adc: parse user info messages, user<-->sid mapping

--HG--
branch : adc
This commit is contained in:
mappu 2017-11-26 14:55:30 +13:00
parent b1c6a5f56a
commit a5b773952c
4 changed files with 211 additions and 39 deletions

View File

@ -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 {

View File

@ -19,6 +19,7 @@ type HubConnection struct {
usersMut sync.RWMutex
users map[string]UserInfo
userSIDs map[string]string
proto Protocol

View File

@ -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,
}

View File

@ -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 {