def Recv(self): num_received_bytes = len(self.socket_data) try: self.socket_data += self.host_socket.recv(4096) except: return if (len(self.socket_data) == num_received_bytes): return if (self.socket_data.count(DATA_PARTIT_BYTE) == 0): return split_data = self.socket_data.split(DATA_PARTIT_BYTE) data_blobs = split_data[:len(split_data) - 1] final_blob = split_data[len(split_data) - 1:][0] def check_message_timestamp(msg): ctr = str_to_int32(msg) if (ctr <= self.incoming_msg_ctr): return False self.incoming_msg_ctr = ctr return True for raw_data_blob in data_blobs: if (len(raw_data_blob) == 0): continue if (self.use_secure_session()): dec_data_blob = decrypt_auth_message(self.aes_cipher_obj, raw_data_blob, self.use_msg_auth_codes()) ## can only happen in case of an invalid MAC or missing timestamp if (len(dec_data_blob) < 4): continue ## ignore any replayed messages if (not check_message_timestamp(dec_data_blob[0:4])): continue ## after decryption dec_command might represent a batch of ## commands separated by newlines, all of which need to be ## handled successfully split_commands = dec_data_blob[4:].split(DATA_PARTIT_BYTE) strip_commands = [(cmd.rstrip('\r')).lstrip(' ') for cmd in split_commands] for command in strip_commands: self.Handle(command) else: if (raw_data_blob[0] == DATA_MARKER_BYTE): continue ## strips leading spaces and trailing carriage return self.Handle((raw_data_blob.rstrip('\r')).lstrip(' ')) self.socket_data = final_blob
def Recv(self): num_received_bytes = len(self.socket_data) try: self.socket_data += self.host_socket.recv(4096) except: return if (len(self.socket_data) == num_received_bytes): return if (self.socket_data.count(DATA_PARTIT_BYTE) == 0): return split_data = self.socket_data.split(DATA_PARTIT_BYTE) data_blobs = split_data[: len(split_data) - 1 ] final_blob = split_data[ len(split_data) - 1: ][0] def check_message_timestamp(msg): ctr = str_to_int32(msg) if (ctr <= self.incoming_msg_ctr): return False self.incoming_msg_ctr = ctr return True for raw_data_blob in data_blobs: if (len(raw_data_blob) == 0): continue if (self.use_secure_session()): dec_data_blob = decrypt_auth_message(self.aes_cipher_obj, raw_data_blob, self.use_msg_auth_codes()) ## can only happen in case of an invalid MAC or missing timestamp if (len(dec_data_blob) < 4): continue ## ignore any replayed messages if (not check_message_timestamp(dec_data_blob[0: 4])): continue ## after decryption dec_command might represent a batch of ## commands separated by newlines, all of which need to be ## handled successfully split_commands = dec_data_blob[4: ].split(DATA_PARTIT_BYTE) strip_commands = [(cmd.rstrip('\r')).lstrip(' ') for cmd in split_commands] for command in strip_commands: self.Handle(command) else: if (raw_data_blob[0] == DATA_MARKER_BYTE): continue ## strips leading spaces and trailing carriage return self.Handle((raw_data_blob.rstrip('\r')).lstrip(' ')) self.socket_data = final_blob
def HandleProtocolCommands(self, split_data, msg_limits): assert(type(split_data) == list) assert(type(split_data[-1]) == str) msg_length_limit = msg_limits['msglength'] check_msg_limits = (not ('disabled' in msg_limits)) ## either a list of commands, or a list of encrypted data ## blobs which may contain embedded (post-decryption) NLs ## ## note: will be empty if len(split_data) == 1 raw_data_blobs = split_data[: len(split_data) - 1] ## will be a single newline in most cases, or an incomplete ## command which should be saved for a later time when more ## data is in buffer self.data = split_data[-1] commands_buffer = [] def check_message_timestamp(msg): ctr = str_to_int32(msg) if (ctr <= self.incoming_msg_ctr): return False self.incoming_msg_ctr = ctr return True for raw_data_blob in raw_data_blobs: if (len(raw_data_blob) == 0): continue if (self.use_secure_session()): dec_data_blob = decrypt_auth_message(self.aes_cipher_obj, raw_data_blob, self.use_msg_auth_codes()) ## can only happen in case of an invalid MAC or missing timestamp if (len(dec_data_blob) < 4): continue ## handle an encrypted client command, using the AES session key ## previously exchanged between client and server by SETSHAREDKEY ## (this includes LOGIN and REGISTER, key can be set before login) ## ## this assumes (!) a client message to be of the form ## ENCODE(ENCRYPT_AES("CMD ARG1 ARG2 ...", AES_KEY)) ## where ENCODE is the standard base64 encoding scheme ## ## if this is not the case (e.g. if a command was sent UNENCRYPTED ## by client after session-key exchange) the decryption will yield ## garbage and command will be rejected ## ## NOTE: ## blocks of encrypted data are always base64-encoded and will be ## separated by newlines, but after decryption might contain more ## embedded newlines themselves (e.g. if encryption was performed ## over *batches* of plaintext commands) ## ## client --> C=ENCODE(ENCRYPT("CMD1 ARG11 ARG12 ...\nCMD2 ARG21 ...\n")) ## server --> DECRYPT(DECODE(C))="CMD1 ARG11 ARG12 ...\nCMD2 ARG21 ...\n" ## ## ignore any replayed messages if (not check_message_timestamp(dec_data_blob[0: 4])): continue split_commands = dec_data_blob[4: ].split(DATA_PARTIT_BYTE) strip_commands = [(cmd.rstrip('\r')).lstrip(' ') for cmd in split_commands] else: if (raw_data_blob[0] == DATA_MARKER_BYTE): continue ## strips leading spaces and trailing carriage returns strip_commands = [(raw_data_blob.rstrip('\r')).lstrip(' ')] commands_buffer += strip_commands for command in commands_buffer: if (check_msg_limits and (len(command) > msg_length_limit)): self.Send('SERVERMSG message-length limit (%d) exceeded: command \"%s...\" dropped.' % (msg_length_limit, command[0: 8])) else: self.HandleProtocolCommand(command)