def create_length_header(length, mask): """Creates a length header. Args: length: Frame length. Must be less than 2^63. mask: Mask bit. Must be boolean. Raises: ValueError: when bad data is given. """ if mask: mask_bit = 1 << 7 else: mask_bit = 0 if length < 0: raise ValueError('length must be non negative integer') elif length <= 125: return util.pack_byte(mask_bit | length) elif length < (1 << 16): return util.pack_byte(mask_bit | 126) + struct.pack('!H', length) elif length < (1 << 63): return util.pack_byte(mask_bit | 127) + struct.pack('!Q', length) else: raise ValueError('Payload is too big for one frame')
def web_socket_transfer_data(request): match = re.search(r'\?case=(\d+_\d+)$', request.ws_resource) if match is None: msgutil.send_message(request, 'FAIL: Query value is incorrect or missing') return payload_length, extended_length = (match.group(1)).split('_', 1) payload_length = int(payload_length) extended_length = int(extended_length) # pywebsocket refuses to create a frame with error encode length. # Thus, we need to build a frame manually. header = util.pack_byte(0x80 | common.OPCODE_TEXT) # 0x80 is for "fin" bit. # No Mask and two bytes extended payload length. header += util.pack_byte(payload_length) if payload_length == 126: header += struct.pack('!H', extended_length) elif payload_length == 127: header += struct.pack('!Q', extended_length) else: msgutil.send_message(request, 'FAIL: Query value is incorrect or missing') return request.connection.write(header) request.connection.write(b'X' * extended_length)
def _build_close_frame(self, code, reason, mask): frame = util.pack_byte(1 << 7 | OPCODE_CLOSE) if code is not None: body = struct.pack('!H', code) + reason.encode('utf-8') else: body = b'' if mask: frame += util.pack_byte(1 << 7 | len(body)) + self._mask_hybi(body) else: frame += util.pack_byte(len(body)) + body return frame
def create_header(opcode, payload_length, fin, rsv1, rsv2, rsv3, mask): """Creates a frame header. Raises: Exception: when bad data is given. """ if opcode < 0 or 0xf < opcode: raise ValueError('Opcode out of range') if payload_length < 0 or (1 << 63) <= payload_length: raise ValueError('payload_length out of range') if (fin | rsv1 | rsv2 | rsv3) & ~1: raise ValueError('FIN bit and Reserved bit parameter must be 0 or 1') header = b'' first_byte = ((fin << 7) | (rsv1 << 6) | (rsv2 << 5) | (rsv3 << 4) | opcode) header += util.pack_byte(first_byte) header += create_length_header(payload_length, mask) return header
def web_socket_transfer_data(request): length = 0x8000000000000000 # pywebsocket refuses to send a frame with too long payload. # Thus, we need to build a frame manually. header = util.pack_byte(0x80 | common.OPCODE_TEXT) # 0x80 is for "fin" bit. header += util.pack_byte(127) header += struct.pack('!Q', length) request.connection.write(header) # Send data indefinitely to simulate a real (broken) server sending a big # frame. A client should ignore these bytes and abort the connection. while True: request.connection.write(b'X' * 4096) time.sleep(1)
def send_data(self, payload, frame_type, end=True, mask=True, rsv1=0, rsv2=0, rsv3=0): if self._outgoing_frame_filter is not None: payload = self._outgoing_frame_filter.filter(payload) if self._fragmented: opcode = OPCODE_CONTINUATION else: opcode = frame_type if end: self._fragmented = False fin = 1 else: self._fragmented = True fin = 0 if mask: mask_bit = 1 << 7 else: mask_bit = 0 header = util.pack_byte(fin << 7 | rsv1 << 6 | rsv2 << 5 | rsv3 << 4 | opcode) payload_length = len(payload) if payload_length <= 125: header += util.pack_byte(mask_bit | payload_length) elif payload_length < 1 << 16: header += util.pack_byte(mask_bit | 126) + struct.pack( '!H', payload_length) elif payload_length < 1 << 63: header += util.pack_byte(mask_bit | 127) + struct.pack( '!Q', payload_length) else: raise Exception('Too long payload (%d byte)' % payload_length) if mask: payload = self._mask_hybi(payload) self._socket.sendall(header + payload)
def _mask_hybi(self, s): # TODO(tyoshino): os.urandom does open/read/close for every call. If # performance matters, change this to some library call that generates # cryptographically secure pseudo random number sequence. masking_nonce = os.urandom(4) result = [masking_nonce] count = 0 for c in iterbytes(s): result.append(util.pack_byte(c ^ indexbytes(masking_nonce, count))) count = (count + 1) % len(masking_nonce) return b''.join(result)
def test_mask_large_data(self): masker = util.RepeatedXorMasker(b'mASk') original = b''.join([util.pack_byte(i % 256) for i in range(1000)]) result = masker.mask(original) expected = b''.join([ util.pack_byte((i % 256) ^ ord('mASk'[i % 4])) for i in range(1000) ]) self.assertEqual(expected, result) masker = util.RepeatedXorMasker(b'MaSk') first_part = b'The WebSocket Protocol enables two-way communication.' result = masker.mask(first_part) self.assertEqual( b'\x19\t6K\x1a\x0418"\x028\x0e9A\x03\x19"\x15<\x08"\rs\x0e#' b'\x001\x07(\x12s\x1f:\x0e~\x1c,\x18s\x08"\x0c>\x1e#\x080\n9' b'\x08<\x05c', result) second_part = b'It has two parts: a handshake and the data transfer.' result = masker.mask(second_part) self.assertEqual( b"('K%\x00 K9\x16<K=\x00!\x1f>[s\nm\t2\x05)\x12;\n&\x04s\n#" b"\x05s\x1f%\x04s\x0f,\x152K9\x132\x05>\x076\x19c", result)
def web_socket_transfer_data(request): # pyformat: disable messages_to_send = [[b'Hello, ', b'world!'], [b'', b'Hello, ', b'', b'world!', b''], [b'', b'', b''], [util.pack_byte(i) for i in range(256)]] # pyformat: enable for message_list in messages_to_send: for index, message in enumerate(message_list): # FIXME: Should use better API to send binary messages when # pywebsocket supports it. if index == 0: opcode = common.OPCODE_BINARY else: opcode = common.OPCODE_CONTINUATION if index < len(message_list) - 1: final = 0 else: final = 1 header = stream.create_header(opcode, len(message), final, 0, 0, 0, 0) request.connection.write(header + message)
def all_distinct_bytes(): return b''.join([util.pack_byte(i) for i in range(256)])