def handleTcpResponse(self): if self.tcpReadState == self.STATE_TCP_READ_LEN: if len(self.tcpData) >= 4: self.tcpResponseLen, self.tcpData = Protocol.extractInt( self.tcpData ) self.tcpReadState = self.STATE_TCP_READ_DATA self.handleTcpResponse() elif self.tcpReadState == self.STATE_TCP_READ_DATA: if len(self.tcpData) >= self.tcpResponseLen: # tcpResponseLen should be >= 4 if self.tcpResponseLen < 4: logging.error( 'Cannot handle TLV payload of less than 4 bytes' ) self.tcpData = self.tcpData[self.tcpResponseLen:] self.tcpResponseLen = 0 self.tcpReadState = self.STATE_TCP_READ_LEN self.handleTcpResponse() else: data = self.tcpData[:self.tcpResponseLen] self.tcpData = self.tcpData[self.tcpResponseLen:] seq = Protocol.unpackInt(data[0:4]) self.tcpResponseLen = 0 self.tcpReadState = self.STATE_TCP_READ_LEN self.dispatch(seq, data[4:]) self.handleTcpResponse()
def sendAuth(self, username, password): self.username = username try: port = self.udpSock.getsockname()[1] except: port = 6009 # raise authdata = Protocol.packTLV(username) + Protocol.packTLV(password) + Protocol.packInt(port) + Protocol.packInt(self.__versionNum__) self.sendAndRemember(Protocol.AUTH, authdata)
def dispatchInbandData(self, seq, data): if seq not in self.tcpCommandsWaitingForResponse: logging.error("Sequence {} data {} not matched".format(seq, data)) return origRequest = self.tcpCommandsWaitingForResponse[seq] del self.tcpCommandsWaitingForResponse[seq] if origRequest == Protocol.AUTH: self.parseAuthResponse(data) elif origRequest == Protocol.MOTD: self.parseMotdResponse(data) elif origRequest == Protocol.LIST_CHANNELS: self.parseListChannelsResponse(data) elif origRequest == Protocol.LIST_USERS: self.parseListUsersResponse(data) elif origRequest == Protocol.SPECTATE: status, data = Protocol.extractInt(data) if status != 0: self.emitList.append( { 'state': 'error', 'message': f'Failed to spectate {status}' } ) elif origRequest in [Protocol.WELCOME, Protocol.JOIN_CHANNEL, Protocol.TOGGLE_AFK, Protocol.SEND_CHALLENGE, Protocol.CHAT, Protocol.ACCEPT_CHALLENGE, Protocol.DECLINE_CHALLENGE, Protocol.CANCEL_CHALLENGE]: if len(data) == 4: status, data = Protocol.extractInt(data) if status != 0: codestr = Protocol.codeToString(origRequest) logging.error( "{} failed, data {}".format(codestr, repr(data)) ) if codestr == "SEND_CHALLENGE": self.emitList.append( { 'state': 'error', 'message': 'SEND_CHALLANGE failed' } ) elif codestr == "CANCEL_CHALLENGE": pass else: self.emitList.append( { 'state': 'error', 'message': f'codestr: {codestr}' } ) else: logging.error("Unknown response for {}; seq {}; data {}".format( Protocol.codeToString(origRequest), seq, repr(data))) else: logging.error("Not handling {} response; seq {}; data {}".format( Protocol.codeToString(origRequest), seq, repr(data)))
def parseListChannelsResponse(self, data): if len(data) <= 8: logging.error('No channels found') self.stateChannelIsLoaded = True return status1, data = Protocol.extractInt(data) status2, data = Protocol.extractInt(data) logging.debug("Load channels header " + repr(status1) + repr(status2)) while len(data) > 4: room, data = Protocol.extractTLV(data) romname, data = Protocol.extractTLV(data) title, data = Protocol.extractTLV(data) users, data = Protocol.extractInt(data) port, data = Protocol.extractInt(data) index, data = Protocol.extractInt(data) channel = { 'rom': romname.decode("utf-8").split(':')[0], 'room': room.decode("utf-8"), 'title': title.decode("utf-8"), 'users': users, 'port': port, } self.channels[room.decode("utf-8")] = channel logging.debug(repr(self.channels)) self.stateChannelIsLoaded = True if len(data) > 0: logging.error('Channel REMAINING DATA len {} {}'.format( len(data), repr(data)) )
def parseMotdResponse(self, data): if not data: return status, data = Protocol.extractInt(data) channel, data = Protocol.extractTLV(data) topic, data = Protocol.extractTLV(data) msg, data = Protocol.extractTLV(data) self.emitList.append( { 'state': 'motdReceived', 'channel': str(channel), 'topic': str(topic), 'message': str(msg) } )
def parseChatResponse(self, data): name, data = Protocol.extractTLV(data) msg, data = Protocol.extractTLV(data) try: msg = msg.decode('utf-8') name = name.decode('utf-8') except ValueError: pass logging.debug(u"<{}> {}".format(name, msg)) self.emitList.append( { 'state': 'chatReceived', 'message': str(msg), 'name': str(name) } )
def parseStateChangesResponse(self, data): count, data = Protocol.extractInt(data) while count > 0 and len(data) >= 4: state, p1, p2, playerinfo, data = self.__class__.extractStateChangesResponse( data ) msg = f'State: {state}, p1: {p1}, p2: {p2}, playerInfo: {playerinfo}, data: {data}' logging.debug(msg) count -= 1
def parseAuthResponse(self, data): if len(data) < 4: logging.error("Unknown auth response {}".format(repr(data))) return result, data = Protocol.extractInt(data) if result == 0: self.selectTimeout = 15 self.emitList.append( { 'state': 'logingSuccess' } ) else: if self.tcpSock: self.tcpSock.close() self.tcpConnected = False self.emitList.append( { 'state': 'statusMessage', 'message': f'login failed {result}' } ) if result == 6: self.emitList.append( { 'state': 'statusMessage', 'message': 'login failed wrong password' } ) elif result == 9: self.emitList.append( { 'state': 'statusMessage', 'message': 'too many connections' } ) elif result == 4: self.emitList.append( { 'state': 'statusMessage', 'message': 'Username doesns exist in database' } ) elif result == 8: self.emitList.append( { 'state': 'statusMessage', 'message': 'Clone connection closed, please login again' } ) else: self.emitList.append( { 'state': 'statusMessage', 'message': f'login failed {result}' } )
def sendJoinChannelRequest(self, channel=None): if channel: self.channel = channel if channel in self.channels: if channel != 'lobby': self.rom = self.channels[channel]['rom'] else: self.rom = '' else: logging.error("Invalid channel {}".format(channel)) if (int(self.channelport) != int(self.channels[channel]['port'])): self.switchingServer = True self.channelport = int(self.channels[channel]['port']) self.tcpSock.close() self.sequence = 0x1 self.connectTcp() self.sendWelcome() self.sendAuth(self.username, self.password) self.sendAndRemember(Protocol.JOIN_CHANNEL, Protocol.packTLV(self.channel) )
def dispatch(self, seq, data): logging.debug( 'Dispatch ' + Protocol.outOfBandCodeToString(seq) + ' ' + repr(data) ) # out of band data if seq == Protocol.CHAT_DATA: self.parseChatResponse(data) elif seq == Protocol.PLAYER_STATE_CHANGE: self.parseStateChangesResponse(data) elif seq == Protocol.CHALLENGE_DECLINED: pass elif seq == Protocol.CHALLENGE_RECEIVED: pass elif seq == Protocol.CHALLENGE_RETRACTED: pass elif seq == Protocol.JOINING_A_CHANNEL: self.parseJoinChannelResponse(data) elif seq == Protocol.SPECTATE_GRANTED: pass else: # in band response to our previous request self.dispatchInbandData(seq, data)
def sendChat(self, line): if self.channel == 'unsupported' and self.unsupportedRom: line = '[' + self.unsupportedRom + '] ' + line self.sendAndRemember(Protocol.CHAT, Protocol.packTLV(line))
def sendAndForget(self, command, data=b''): logging.debug('Sending {} seq {} {}'.format( Protocol.codeToString(command), self.sequence, repr(data)) ) self.sendtcp(struct.pack('!I', command) + data)
def parseListUsersResponse(self, data): self.resetPlayers() if not data: return status, data = Protocol.extractInt(data) status2, data = Protocol.extractInt(data) while len(data) > 8: p1, data = Protocol.extractTLV(data) # if len(data) <= 4: break state, data = Protocol.extractInt(data) p2, data = Protocol.extractTLV(data) ip, data = Protocol.extractTLV(data) unk1, data = Protocol.extractInt(data) unk2, data = Protocol.extractInt(data) city, data = Protocol.extractTLV(data) cc, data = Protocol.extractTLV(data) if cc: cc = cc.lower() country, data = Protocol.extractTLV(data) port, data = Protocol.extractInt(data) spectators, data = Protocol.extractInt(data) self.addUser( player=p1, ip=ip, port=port, city=city, cc=cc, country=country, spectators=spectators+1, ) if state == PlayerStates.AVAILABLE: self.available[p1] = True elif state == PlayerStates.AFK: self.awayfromkb[p1] = True elif state == PlayerStates.PLAYING: if not p2: p2 = 'null' self.playing[p1] = p2 self.emitList.append( { 'state': 'playersLoaded', } ) if len(data) > 0: logging.error( 'List users - REMAINING DATA len {} {}'.format(len(data), repr(data)) )
def extractStateChangesResponse(data): if len(data) >= 4: code, data = Protocol.extractInt(data) p1, data = Protocol.extractTLV(data) if code == 0: p2 = '' return PlayerStates.QUIT, p1, p2, None, data elif code != 1: logging.error( "Unknown player state change code {}".format(code) ) state, data = Protocol.extractInt(data) p2, data = Protocol.extractTLV(data) if not p2: p2 = "null" ip, data = Protocol.extractTLV(data) # \xff\xff\xff\x9f # \x00\x00\x00& unknown1, data = Protocol.extractInt(data) unknown2, data = Protocol.extractInt(data) city, data = Protocol.extractTLV(data) cc, data = Protocol.extractTLV(data) if cc: cc = cc.lower() country, data = Protocol.extractTLV(data) # \x00\x00\x17y marker, data = Protocol.extractInt(data) playerinfo = dict( player=p1, ip=ip, city=city, cc=cc, country=country, spectators=0, ) return state, p1, p2, playerinfo, data