def test_mask(self): # Sample input e6,97,a5 is U+65e5 in UTF-8 masker = util.RepeatedXorMasker(b'\xff\xff\xff\xff') result = masker.mask(b'\xe6\x97\xa5') self.assertEqual(b'\x19\x68\x5a', result) masker = util.RepeatedXorMasker(b'\x00\x00\x00\x00') result = masker.mask(b'\xe6\x97\xa5') self.assertEqual(b'\xe6\x97\xa5', result) masker = util.RepeatedXorMasker(b'\xe6\x97\xa5\x20') result = masker.mask(b'\xe6\x97\xa5') self.assertEqual(b'\x00\x00\x00', result)
def _build_frame(header, body, mask): if not mask: return header + body masking_nonce = os.urandom(4) masker = util.RepeatedXorMasker(masking_nonce) return header + masking_nonce + masker.mask(body)
def test_mask_twice(self): masker = util.RepeatedXorMasker(b'\x00\x7f\xff\x20') # mask[0], mask[1], ... will be used. result = masker.mask(b'\x00\x00\x00\x00\x00') self.assertEqual(b'\x00\x7f\xff\x20\x00', result) # mask[2], mask[0], ... will be used for the next call. result = masker.mask(b'\x00\x00\x00\x00\x00') self.assertEqual(b'\x7f\xff\x20\x00\x7f', result)
def test_mask_large_data(self): masker = util.RepeatedXorMasker('mASk') original = ''.join([chr(i % 256) for i in xrange(1000)]) result = masker.mask(original) expected = ''.join( [chr((i % 256) ^ ord('mASk'[i % 4])) for i in xrange(1000)]) self.assertEqual(expected, result) masker = util.RepeatedXorMasker('MaSk') first_part = 'The WebSocket Protocol enables two-way communication.' result = masker.mask(first_part) self.assertEqual( '\x19\t6K\x1a\x0418"\x028\x0e9A\x03\x19"\x15<\x08"\rs\x0e#' '\x001\x07(\x12s\x1f:\x0e~\x1c,\x18s\x08"\x0c>\x1e#\x080\n9' '\x08<\x05c', result) second_part = 'It has two parts: a handshake and the data transfer.' result = masker.mask(second_part) self.assertEqual( "('K%\x00 K9\x16<K=\x00!\x1f>[s\nm\t2\x05)\x12;\n&\x04s\n#" "\x05s\x1f%\x04s\x0f,\x152K9\x132\x05>\x076\x19c", result)
def _receive_frame(self): """Receives a frame and return data in the frame as a tuple containing each header field and payload separately. Raises: ConnectionTerminatedException: when read returns empty string. InvalidFrameException: when the frame contains invalid data. """ received = self.receive_bytes(2) first_byte = ord(received[0]) fin = (first_byte >> 7) & 1 rsv1 = (first_byte >> 6) & 1 rsv2 = (first_byte >> 5) & 1 rsv3 = (first_byte >> 4) & 1 opcode = first_byte & 0xf second_byte = ord(received[1]) mask = (second_byte >> 7) & 1 payload_length = second_byte & 0x7f if (mask == 1) != self._options.unmask_receive: raise InvalidFrameException( 'Mask bit on the received frame did\'nt match masking ' 'configuration for received frames') # The spec doesn't disallow putting a value in 0x0-0xFFFF into the # 8-octet extended payload length field (or 0x0-0xFD in 2-octet field). # So, we don't check the range of extended_payload_length. if payload_length == 127: extended_payload_length = self.receive_bytes(8) payload_length = struct.unpack('!Q', extended_payload_length)[0] if payload_length > 0x7FFFFFFFFFFFFFFF: raise InvalidFrameException('Extended payload length >= 2^63') elif payload_length == 126: extended_payload_length = self.receive_bytes(2) payload_length = struct.unpack('!H', extended_payload_length)[0] if mask == 1: masking_nonce = self.receive_bytes(4) masker = util.RepeatedXorMasker(masking_nonce) else: masker = _NOOP_MASKER bytes = masker.mask(self.receive_bytes(payload_length)) return opcode, bytes, fin, rsv1, rsv2, rsv3
def _build_frame(header, body, mask): if not mask: return header + body # TODO: adapt original library code # original code uses os.urandom which is not available on digi gateways # get 4 bytes out of the int range 33..126 (readable ascii codes) import random random_int_list = random.sample(range(33, 127), 4) masking_nonce = ''.join(chr(i) for i in random_int_list) # was # masking_nonce = os.urandom(4) masker = util.RepeatedXorMasker(masking_nonce) return header + masking_nonce + masker.mask(body)
def parse_frame(receive_bytes, logger=None, ws_version=common.VERSION_HYBI_LATEST, unmask_receive=True): """Parses a frame. Returns a tuple containing each header field and payload. Args: receive_bytes: a function that reads frame data from a stream or something similar. The function takes length of the bytes to be read. The function must raise ConnectionTerminatedException if there is not enough data to be read. logger: a logging object. ws_version: the version of WebSocket protocol. unmask_receive: unmask received frames. When received unmasked frame, raises InvalidFrameException. Raises: ConnectionTerminatedException: when receive_bytes raises it. InvalidFrameException: when the frame contains invalid data. """ if not logger: logger = logging.getLogger() logger.log(common.LOGLEVEL_FINE, 'Receive the first 2 octets of a frame') first_byte = ord(receive_bytes(1)) fin = (first_byte >> 7) & 1 rsv1 = (first_byte >> 6) & 1 rsv2 = (first_byte >> 5) & 1 rsv3 = (first_byte >> 4) & 1 opcode = first_byte & 0xf second_byte = ord(receive_bytes(1)) mask = (second_byte >> 7) & 1 payload_length = second_byte & 0x7f logger.log( common.LOGLEVEL_FINE, 'FIN=%s, RSV1=%s, RSV2=%s, RSV3=%s, opcode=%s, ' 'Mask=%s, Payload_length=%s', fin, rsv1, rsv2, rsv3, opcode, mask, payload_length) if (mask == 1) != unmask_receive: raise InvalidFrameException( 'Mask bit on the received frame did\'nt match masking ' 'configuration for received frames') # The HyBi and later specs disallow putting a value in 0x0-0xFFFF # into the 8-octet extended payload length field (or 0x0-0xFD in # 2-octet field). valid_length_encoding = True length_encoding_bytes = 1 if payload_length == 127: logger.log(common.LOGLEVEL_FINE, 'Receive 8-octet extended payload length') extended_payload_length = receive_bytes(8) payload_length = struct.unpack('!Q', extended_payload_length)[0] if payload_length > 0x7FFFFFFFFFFFFFFF: raise InvalidFrameException('Extended payload length >= 2^63') if ws_version >= 13 and payload_length < 0x10000: valid_length_encoding = False length_encoding_bytes = 8 logger.log(common.LOGLEVEL_FINE, 'Decoded_payload_length=%s', payload_length) elif payload_length == 126: logger.log(common.LOGLEVEL_FINE, 'Receive 2-octet extended payload length') extended_payload_length = receive_bytes(2) payload_length = struct.unpack('!H', extended_payload_length)[0] if ws_version >= 13 and payload_length < 126: valid_length_encoding = False length_encoding_bytes = 2 logger.log(common.LOGLEVEL_FINE, 'Decoded_payload_length=%s', payload_length) if not valid_length_encoding: logger.warning( 'Payload length is not encoded using the minimal number of ' 'bytes (%d is encoded using %d bytes)', payload_length, length_encoding_bytes) if mask == 1: logger.log(common.LOGLEVEL_FINE, 'Receive mask') masking_nonce = receive_bytes(4) masker = util.RepeatedXorMasker(masking_nonce) logger.log(common.LOGLEVEL_FINE, 'Mask=%r', masking_nonce) else: masker = _NOOP_MASKER logger.log(common.LOGLEVEL_FINE, 'Receive payload data') if logger.isEnabledFor(common.LOGLEVEL_FINE): receive_start = time.time() raw_payload_bytes = receive_bytes(payload_length) if logger.isEnabledFor(common.LOGLEVEL_FINE): logger.log( common.LOGLEVEL_FINE, 'Done receiving payload data at %s MB/s', payload_length / (time.time() - receive_start) / 1000 / 1000) logger.log(common.LOGLEVEL_FINE, 'Unmask payload data') if logger.isEnabledFor(common.LOGLEVEL_FINE): unmask_start = time.time() unmasked_bytes = masker.mask(raw_payload_bytes) if logger.isEnabledFor(common.LOGLEVEL_FINE): logger.log(common.LOGLEVEL_FINE, 'Done unmasking payload data at %s MB/s', payload_length / (time.time() - unmask_start) / 1000 / 1000) return opcode, unmasked_bytes, fin, rsv1, rsv2, rsv3
def _receive_frame(self): """Receives a frame and return data in the frame as a tuple containing each header field and payload separately. Raises: ConnectionTerminatedException: when read returns empty string. InvalidFrameException: when the frame contains invalid data. """ received = self.receive_bytes(2) first_byte = ord(received[0]) fin = (first_byte >> 7) & 1 rsv1 = (first_byte >> 6) & 1 rsv2 = (first_byte >> 5) & 1 rsv3 = (first_byte >> 4) & 1 opcode = first_byte & 0xf second_byte = ord(received[1]) mask = (second_byte >> 7) & 1 payload_length = second_byte & 0x7f if (mask == 1) != self._options.unmask_receive: raise InvalidFrameException( 'Mask bit on the received frame did\'nt match masking ' 'configuration for received frames') # The Hybi-13 and later specs disallow putting a value in 0x0-0xFFFF # into the 8-octet extended payload length field (or 0x0-0xFD in # 2-octet field). valid_length_encoding = True length_encoding_bytes = 1 if payload_length == 127: extended_payload_length = self.receive_bytes(8) payload_length = struct.unpack('!Q', extended_payload_length)[0] if payload_length > 0x7FFFFFFFFFFFFFFF: raise InvalidFrameException('Extended payload length >= 2^63') if self._request.ws_version >= 13 and payload_length < 0x10000: valid_length_encoding = False length_encoding_bytes = 8 elif payload_length == 126: extended_payload_length = self.receive_bytes(2) payload_length = struct.unpack('!H', extended_payload_length)[0] if self._request.ws_version >= 13 and payload_length < 126: valid_length_encoding = False length_encoding_bytes = 2 if not valid_length_encoding: self._logger.warning( 'Payload length is not encoded using the minimal number of ' 'bytes (%d is encoded using %d bytes)', payload_length, length_encoding_bytes) if mask == 1: masking_nonce = self.receive_bytes(4) masker = util.RepeatedXorMasker(masking_nonce) else: masker = _NOOP_MASKER bytes = masker.mask(self.receive_bytes(payload_length)) return opcode, bytes, fin, rsv1, rsv2, rsv3
def _receive_frame(self): """Receives a frame and return data in the frame as a tuple containing each header field and payload separately. Raises: ConnectionTerminatedException: when read returns empty string. InvalidFrameException: when the frame contains invalid data. """ self._logger.log(common.LOGLEVEL_FINE, 'Receive the first 2 octets of a frame') received = self.receive_bytes(2) first_byte = ord(received[0]) fin = (first_byte >> 7) & 1 rsv1 = (first_byte >> 6) & 1 rsv2 = (first_byte >> 5) & 1 rsv3 = (first_byte >> 4) & 1 opcode = first_byte & 0xf second_byte = ord(received[1]) mask = (second_byte >> 7) & 1 payload_length = second_byte & 0x7f self._logger.log( common.LOGLEVEL_FINE, 'FIN=%s, RSV1=%s, RSV2=%s, RSV3=%s, opcode=%s, ' 'Mask=%s, Payload_length=%s', fin, rsv1, rsv2, rsv3, opcode, mask, payload_length) if (mask == 1) != self._options.unmask_receive: raise InvalidFrameException( 'Mask bit on the received frame did\'nt match masking ' 'configuration for received frames') # The Hybi-13 and later specs disallow putting a value in 0x0-0xFFFF # into the 8-octet extended payload length field (or 0x0-0xFD in # 2-octet field). valid_length_encoding = True length_encoding_bytes = 1 if payload_length == 127: self._logger.log(common.LOGLEVEL_FINE, 'Receive 8-octet extended payload length') extended_payload_length = self.receive_bytes(8) payload_length = struct.unpack('!Q', extended_payload_length)[0] if payload_length > 0x7FFFFFFFFFFFFFFF: raise InvalidFrameException('Extended payload length >= 2^63') if self._request.ws_version >= 13 and payload_length < 0x10000: valid_length_encoding = False length_encoding_bytes = 8 self._logger.log(common.LOGLEVEL_FINE, 'Decoded_payload_length=%s', payload_length) elif payload_length == 126: self._logger.log(common.LOGLEVEL_FINE, 'Receive 2-octet extended payload length') extended_payload_length = self.receive_bytes(2) payload_length = struct.unpack('!H', extended_payload_length)[0] if self._request.ws_version >= 13 and payload_length < 126: valid_length_encoding = False length_encoding_bytes = 2 self._logger.log(common.LOGLEVEL_FINE, 'Decoded_payload_length=%s', payload_length) if not valid_length_encoding: self._logger.warning( 'Payload length is not encoded using the minimal number of ' 'bytes (%d is encoded using %d bytes)', payload_length, length_encoding_bytes) if mask == 1: self._logger.log(common.LOGLEVEL_FINE, 'Receive mask') masking_nonce = self.receive_bytes(4) masker = util.RepeatedXorMasker(masking_nonce) self._logger.log(common.LOGLEVEL_FINE, 'Mask=%r', masking_nonce) else: masker = _NOOP_MASKER self._logger.log(common.LOGLEVEL_FINE, 'Receive payload data') if self._logger.isEnabledFor(common.LOGLEVEL_FINE): receive_start = time.time() raw_payload_bytes = self.receive_bytes(payload_length) if self._logger.isEnabledFor(common.LOGLEVEL_FINE): self._logger.log( common.LOGLEVEL_FINE, 'Done receiving payload data at %s MB/s', payload_length / (time.time() - receive_start) / 1000 / 1000) self._logger.log(common.LOGLEVEL_FINE, 'Unmask payload data') if self._logger.isEnabledFor(common.LOGLEVEL_FINE): unmask_start = time.time() bytes = masker.mask(raw_payload_bytes) if self._logger.isEnabledFor(common.LOGLEVEL_FINE): self._logger.log( common.LOGLEVEL_FINE, 'Done unmasking payload data at %s MB/s', payload_length / (time.time() - unmask_start) / 1000 / 1000) return opcode, bytes, fin, rsv1, rsv2, rsv3