示例#1
0
    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)
示例#2
0
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)
示例#3
0
 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)
示例#4
0
    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)
示例#5
0
    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
示例#6
0
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)
示例#7
0
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
示例#8
0
    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
示例#9
0
    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