def _make_connection(self, channel, data): ctx = channel.server.ctx buf = Buffer(data=data) header = pull_quic_header(buf, host_cid_length=ctx.connection_id_length) # version negotiation if header.version is not None and header.version not in ctx.supported_versions: self.channel.push( encode_quic_version_negotiation( source_cid=header.destination_cid, destination_cid=header.source_cid, supported_versions=ctx.supported_versions, )) return conn = self.conns.get(header.destination_cid) if conn: conn._linked_channel.close() conn._linked_channel = channel self.quic = conn._quic self.conn = conn return if header.packet_type != PACKET_TYPE_INITIAL or len(data) < 1200: return original_connection_id = None if self._retry is not None: if not header.token: # create a retry token channel.push( encode_quic_retry( version=header.version, source_cid=os.urandom(8), destination_cid=header.source_cid, original_destination_cid=header.destination_cid, retry_token=self._retry.create_token( channel.addr, header.destination_cid))) return else: try: original_connection_id = self._retry.validate_token( channel.addr, header.token) except ValueError: return self.quic = QuicConnection( configuration=ctx, logger_connection_id=original_connection_id or header.destination_cid, original_connection_id=original_connection_id, session_ticket_fetcher=channel.server.ticket_store.pop, session_ticket_handler=channel.server.ticket_store.add) self.conn = H3Connection(self.quic) self.conn._linked_channel = channel
def test_pull_retry(self): original_destination_cid = binascii.unhexlify("fbbd219b7363b64b") data = load("retry.bin") buf = Buffer(data=data) header = pull_quic_header(buf, host_cid_length=8) self.assertTrue(header.is_long_header) self.assertEqual(header.version, QuicProtocolVersion.VERSION_1) self.assertEqual(header.packet_type, PACKET_TYPE_RETRY) self.assertEqual(header.destination_cid, binascii.unhexlify("e9d146d8d14cb28e")) self.assertEqual( header.source_cid, binascii.unhexlify("0b0a205a648fcf82d85f128b67bbe08053e6"), ) self.assertEqual( header.token, binascii.unhexlify( "44397a35d698393c134b08a932737859f446d3aadd00ed81540c8d8de172" "906d3e7a111b503f9729b8928e7528f9a86a4581f9ebb4cb3b53c283661e" "8530741a99192ee56914c5626998ec0f"), ) self.assertEqual( header.integrity_tag, binascii.unhexlify("4620aafd42f1d630588b27575a12da5c")) self.assertEqual(header.rest_length, 0) self.assertEqual(buf.tell(), 125) # check integrity if False: self.assertEqual( get_retry_integrity_tag( buf.data_slice(0, 109), original_destination_cid, version=header.version, ), header.integrity_tag, ) # serialize encoded = encode_quic_retry( version=header.version, source_cid=header.source_cid, destination_cid=header.destination_cid, original_destination_cid=original_destination_cid, retry_token=header.token, ) with open("bob.bin", "wb") as fp: fp.write(encoded) self.assertEqual(encoded, data)
def test_pull_retry_draft_28(self): original_destination_cid = binascii.unhexlify("fbbd219b7363b64b") data = load("retry_draft_28.bin") buf = Buffer(data=data) header = pull_quic_header(buf, host_cid_length=8) self.assertTrue(header.is_long_header) self.assertEqual(header.version, QuicProtocolVersion.DRAFT_28) self.assertEqual(header.packet_type, PACKET_TYPE_RETRY) self.assertEqual(header.destination_cid, binascii.unhexlify("e9d146d8d14cb28e")) self.assertEqual( header.source_cid, binascii.unhexlify("0b0a205a648fcf82d85f128b67bbe08053e6"), ) self.assertEqual( header.token, binascii.unhexlify( "44397a35d698393c134b08a932737859f446d3aadd00ed81540c8d8de172" "906d3e7a111b503f9729b8928e7528f9a86a4581f9ebb4cb3b53c283661e" "8530741a99192ee56914c5626998ec0f"), ) self.assertEqual( header.integrity_tag, binascii.unhexlify("f15154a271f10139ef6b129033ac38ae")) self.assertEqual(header.rest_length, 0) self.assertEqual(buf.tell(), 125) # check integrity self.assertEqual( get_retry_integrity_tag(buf.data_slice(0, 109), original_destination_cid, version=header.version), header.integrity_tag, ) # serialize encoded = encode_quic_retry( version=header.version, source_cid=header.source_cid, destination_cid=header.destination_cid, original_destination_cid=original_destination_cid, retry_token=header.token, ) self.assertEqual(encoded, data)
def datagram_received(self, data: Union[bytes, Text], addr: NetworkAddress) -> None: data = cast(bytes, data) buf = Buffer(data=data) # logger.info("datagram received") global totalDatagrams totalDatagrams += 1 # logger.info('total:{} {}'.format(totalDatagrams, addr)) try: header = pull_quic_header( buf, host_cid_length=self._configuration.connection_id_length) except ValueError: return # version negotiation if (header.version is not None and header.version not in self._configuration.supported_versions): self._transport.sendto( encode_quic_version_negotiation( source_cid=header.destination_cid, destination_cid=header.source_cid, supported_versions=self._configuration.supported_versions, ), addr, ) return protocol = self._protocols.get(header.destination_cid, None) original_destination_connection_id: Optional[bytes] = None retry_source_connection_id: Optional[bytes] = None if (protocol is None and len(data) >= 1200 and header.packet_type == PACKET_TYPE_INITIAL): #retry if self._retry is not None: if not header.token: # create a retry token source_cid = os.urandom(8) self._transport.sendto( encode_quic_retry( version=header.version, source_cid=source_cid, destination_cid=header.source_cid, original_destination_cid=header.destination_cid, retry_token=self._retry.create_token( addr, header.destination_cid, source_cid), ), addr, ) return else: # validate retry token try: (original_destination_cid, retry_source_connection_id ) = self._retry.validate_token(addr, header.token) except ValueError: return else: original_destination_connection_id = header.destination_cid # create new connection connection = QuicConnection( configuration=self._configuration, original_destination_connection_id= original_destination_connection_id, retry_source_connection_id=retry_source_connection_id, session_ticket_handler=self._session_ticket_handler, session_ticket_fetcher=self._session_ticket_fetcher, ) # initiate the QuicSocketFactory class with the below call. protocol = self._create_protocol( connection, stream_handler=self._stream_handler) protocol.connection_made(self._transport) # register callbacks protocol._connection_id_issued_handler = partial( self._connection_id_issued, protocol=protocol) protocol._connection_id_retired_handler = partial( self._connection_id_retired, protocol=protocol) protocol._connection_terminated_handler = partial( self._connection_terminated, protocol=protocol) self._protocols[header.destination_cid] = protocol self._protocols[connection.host_cid] = protocol if protocol is not None: protocol.datagram_received(data, addr)