From a5b773952c10feb3cbcefb6ad4a19100fbb87228 Mon Sep 17 00:00:00 2001 From: mappu Date: Sun, 26 Nov 2017 14:55:30 +1300 Subject: [PATCH] adc: parse user info messages, user<-->sid mapping --HG-- branch : adc --- AdcProtocol.go | 233 ++++++++++++++++++++++++++++++++++------ HubConnection.go | 1 + HubConnectionOptions.go | 9 +- UserInfo.go | 7 +- 4 files changed, 211 insertions(+), 39 deletions(-) diff --git a/AdcProtocol.go b/AdcProtocol.go index 6e661c1..ce50d86 100644 --- a/AdcProtocol.go +++ b/AdcProtocol.go @@ -77,6 +77,7 @@ func (this *AdcProtocol) ProcessCommand(msg string) { return } + this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg}) parts := strings.Split(msg, " ") switch parts[0] { @@ -112,33 +113,104 @@ func (this *AdcProtocol) ProcessCommand(msg string) { this.sid = parts[1] case "IINF": - flags := make(map[string]string) - for _, flag := range parts[1:] { - if len(flag) < 2 { - this.malformed(parts) - return - } - flags[flag[0:2]] = flag[2:] + // Hub telling information about itself + + flags, err := this.parts2flags(parts[1:]) + if err != nil { + this.logError(err) + return } - // Possibilities: User MyINFO (Normal state), or, hub telling information about itself (Identify->Normal state) - if (this.state == adcStateIdentify || this.state == adcStateVerify) && flags["CT"] == "32" { - // Hub telling information about itself - this.handleInfo(flags) + if flags["CT"] != "32" { + this.malformed(parts) + return + } + err = this.handleHubInfo(flags) + if err != nil { + this.logError(err) + return + } + + if this.state == adcStateIdentify { // Transition to state VERIFY and send our own info this.hc.SayRaw("BINF " + this.escape(this.sid) + " " + this.ourINFO(true) + "\n") this.state = adcStateVerify - } else if this.state == adcStateNormal { + } else if this.state == adcStateNormal || this.state == adcStateVerify { // OK - this.handleInfo(flags) } else { + // Bad state to be in this.malformed(parts) return } + case "BINF": + if this.state != adcStateNormal { + this.enterNormalState() // successful login + } + + sid := parts[1] + flags, err := this.parts2flags(parts[2:]) + if err != nil { + this.logError(err) + return + } + + uinfo, err := this.handleUserInfo(flags) + if err != nil { + this.logError(err) + return + } + + // Log this user in, and associate this SID with this user + this.hc.usersMut.Lock() + defer this.hc.usersMut.Unlock() + + newNick := uinfo.Nick + + oldNick, sidExists := this.hc.userSIDs[sid] + + handleNewUser := func() { + // Install this SID as pointing to this nick + this.hc.userSIDs[sid] = uinfo.Nick + + // Check if this nick was in use by any other SID already + for otherSid, otherSidNick := range this.hc.userSIDs { + if otherSidNick == newNick && otherSid != sid { + this.hc.processEvent(HubEvent{ + EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, + Message: fmt.Sprintf("Hub connection corrupted (duplicate SIDs '%s' and '%s' for nick '%s'), disconnecting", sid, otherSid, newNick), + }) + this.hc.Disconnect() + return + } + } + + // Notifications + this.hc.users[newNick] = *uinfo + this.hc.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: newNick}) + } + + if sidExists && oldNick != newNick { + // Nick change = delete all trace of this user first, treat as new + + delete(this.hc.users, oldNick) + delete(this.hc.userSIDs, sid) + this.hc.processEvent(HubEvent{EventType: EVENT_USER_PART, Nick: oldNick}) + handleNewUser() + + } else if sidExists && oldNick == newNick { + // Updating existing user + this.hc.users[newNick] = *uinfo + this.hc.processEvent(HubEvent{EventType: EVENT_USER_UPDATED_INFO, Nick: newNick}) + + } else if !sidExists { + // User joined + handleNewUser() + } + case "ISTA": // Message from the hub if len(parts) < 3 { @@ -154,7 +226,6 @@ func (this *AdcProtocol) ProcessCommand(msg string) { // default: - this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg}) this.malformed(parts) } } @@ -167,7 +238,6 @@ func (this *AdcProtocol) infoFlagsFor(u *UserInfo) map[string]string { "US": fmt.Sprintf("%d", u.UploadSpeedBps), "DS": fmt.Sprintf("%d", u.DownloadSpeedBps), "SL": fmt.Sprintf("%d", u.Slots), - "FS": fmt.Sprintf("%d", u.Slots), // Free slots - NOT IN ADC DOCUMENTATION "HN": fmt.Sprintf("%d", u.HubsUnregistered), "HR": fmt.Sprintf("%d", u.HubsRegistered), "HO": fmt.Sprintf("%d", u.HubsOperator), @@ -215,30 +285,123 @@ func (this *AdcProtocol) ourINFO(includePid bool) string { return ret[1:] } -func (this *AdcProtocol) handleInfo(flags map[string]string) { - if flags["CT"] == "32" { +func (this *AdcProtocol) parts2flags(parts []string) (map[string]string, error) { + flags := make(map[string]string, len(parts)) + for _, flag := range parts { + if len(flag) < 2 { + return nil, fmt.Errorf("Malformed flag '%s'", flag) + } + flags[flag[0:2]] = this.unescape(flag[2:]) + } - // IINF DEADCH++\sTest\shub VE2.12.1\s(r"[unknown]")\sRelease HI1 NIADCH++ APADCH++ CT32 - // AP: extension 3.24 "Application and version separation in INF" - // HI: + return flags, nil +} - // Hub properties updated +func (this *AdcProtocol) handleHubInfo(flags map[string]string) error { + if flags["CT"] != "32" { + return fmt.Errorf("Expected CT==32") + } - // Special SUPPORT that is only indicated in IINF - if _, ok := flags["AP"]; ok { - this.supports[adcSeparateApVe] = struct{}{} + // IINF DEADCH++\sTest\shub VE2.12.1\s(r"[unknown]")\sRelease HI1 NIADCH++ APADCH++ CT32 + // AP: extension 3.24 "Application and version separation in INF" + // HI: + + // Hub properties updated + + // Special SUPPORT that is only indicated in IINF + if _, ok := flags["AP"]; ok { + this.supports[adcSeparateApVe] = struct{}{} + } + + // Hub's name is in "NI", hub description in "DE" + hubName, ok := flags["NI"] + if ok { + if hubDesc, ok := flags["DE"]; ok && len(hubDesc) > 0 { + hubName += " - " + hubDesc } - hubName, ok := flags["DE"] - if ok { - this.hc.HubName = this.unescape(hubName) - this.hc.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: this.hc.HubName}) - } + this.hc.HubName = hubName + this.hc.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: this.hc.HubName}) + } + return nil +} + +func (this *AdcProtocol) handleUserInfo(flags map[string]string) (*UserInfo, error) { + + // User MyINFO + // BINF GUPR IDFEARIFD33NTGC4YBEZ3UFQS5R4ZXXTFL2QN2GRY PDZMIFLG5EKZG3BDRRMIJPG7ARNA6KW3JVIH3DF7Q NIivysaur5 SL3 FS3 SS0 SF0 HN1 HR0 HO0 VEEiskaltDC++\s2.2.9 US2621440 KPSHA256/3UPRORG4BLJ4CG6TO6R3G75A67LXOGD437NALQALRWJF6XBOECTA I40.0.0.0 U418301 + // BINF GUPR I4172.17.0.1 U418301 IDFEARIFD33NTGC4YBEZ3UFQS5R4ZXXTFL2QN2GRY VEEiskaltDC++\s2.2.9 SF0 NIivysaur5 SL3 HN1 HO0 KPSHA256/3UPRORG4BLJ4CG6TO6R3G75A67LXOGD437NALQALRWJF6XBOECTA HR0 FS3 SS0 US2621440 SUSEGA,ADC0,TCP4,UDP4 + // TODO + + u := UserInfo{} + for prop, val := range flags { + switch prop { + case "ID": + u.CID = val + case "PD": + // ignore PID - it will only appear if we're talking about our own user + case "NI": + u.Nick = val + case "SL": + u.Slots, _ = strconv.ParseUint(val, 10, 64) + case "SS": + u.ShareSize, _ = strconv.ParseUint(val, 10, 64) + case "SF": + u.SharedFiles, _ = strconv.ParseUint(val, 10, 64) + case "HN": + u.HubsUnregistered, _ = strconv.ParseUint(val, 10, 64) + case "HR": + u.HubsRegistered, _ = strconv.ParseUint(val, 10, 64) + case "HO": + u.HubsOperator, _ = strconv.ParseUint(val, 10, 64) + case "US": + u.UploadSpeedBps, _ = strconv.ParseUint(val, 10, 64) + case "DS": + u.DownloadSpeedBps, _ = strconv.ParseUint(val, 10, 64) + case "KP": + u.Keyprint = val + case "I4": + u.IPv4Address = val + case "I6": + u.IPv6Address = val + case "U4": + u.IPv4UDPPort, _ = strconv.ParseUint(val, 10, 64) + case "U6": + u.IPv6UDPPort, _ = strconv.ParseUint(val, 10, 64) + case "SU": + u.SupportFlags = make(map[string]struct{}) + for _, supportFlag := range strings.Split(val, ",") { + u.SupportFlags[supportFlag] = struct{}{} + } + } + } + + // VE / AP + AP, hasAP := flags["AP"] + VE, hasVE := flags["VE"] + if hasAP && hasVE { + u.ClientTag = AP + u.ClientVersion = VE + } else if hasAP && !hasVE { + u.ClientTag, u.ClientVersion = this.getAPVEFromSingle(AP) + } else if !hasAP && hasVE { + u.ClientTag, u.ClientVersion = this.getAPVEFromSingle(VE) + } + + if u.Nick == "" { + return nil, fmt.Errorf("Malformed user missing nick") + } + + return &u, nil +} + +func (this *AdcProtocol) getAPVEFromSingle(term string) (string, string) { + words := strings.Split(term, " ") + if len(words) > 1 { + return strings.Join(words[0:len(words)-1], " "), words[len(words)-1] } else { - // User MyINFO - // TODO - + return term, "0" } } @@ -249,7 +412,11 @@ func (this *AdcProtocol) enterNormalState() { } func (this *AdcProtocol) malformed(parts []string) { - this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Ignoring malformed, unhandled, or out-of-state protocol command '" + parts[0] + "'"}) + this.logError(fmt.Errorf("Ignoring malformed, unhandled, or out-of-state protocol command %v", parts)) +} + +func (this *AdcProtocol) logError(e error) { + this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Protocol error: " + e.Error()}) } func (this *AdcProtocol) escape(plaintext string) string { diff --git a/HubConnection.go b/HubConnection.go index 5e6b58a..5780cdc 100644 --- a/HubConnection.go +++ b/HubConnection.go @@ -19,6 +19,7 @@ type HubConnection struct { usersMut sync.RWMutex users map[string]UserInfo + userSIDs map[string]string proto Protocol diff --git a/HubConnectionOptions.go b/HubConnectionOptions.go index 1111788..063e775 100644 --- a/HubConnectionOptions.go +++ b/HubConnectionOptions.go @@ -43,10 +43,11 @@ func (this *HubConnectionOptions) prepareConnection() *HubConnection { } hc := HubConnection{ - Hco: this, - HubName: DEFAULT_HUB_NAME, - State: CONNECTIONSTATE_DISCONNECTED, - users: make(map[string]UserInfo), + Hco: this, + HubName: DEFAULT_HUB_NAME, + State: CONNECTIONSTATE_DISCONNECTED, + users: make(map[string]UserInfo), + userSIDs: make(map[string]string), autoReconnect: !this.SkipAutoReconnect, } diff --git a/UserInfo.go b/UserInfo.go index f1690cd..9450e9e 100644 --- a/UserInfo.go +++ b/UserInfo.go @@ -35,8 +35,11 @@ type UserInfo_ADCOnly struct { IsHubOwner bool IPv4Address string // Passive <==> these fields are not set IPv6Address string - IPv4UDPPort uint - IPv6UDPPort uint + IPv4UDPPort uint64 + IPv6UDPPort uint64 + Keyprint string + CID string + SupportFlags map[string]struct{} } func NewUserInfo(username string) *UserInfo {