Example #1
0
    async def handle(self, event: Event) -> None:
        if isinstance(event, RawData):
            try:
                header = pull_quic_header(Buffer(data=event.data), host_cid_length=8)
            except ValueError:
                return
            if (
                header.version is not None
                and header.version not in self.quic_config.supported_versions
            ):
                data = encode_quic_version_negotiation(
                    source_cid=header.destination_cid,
                    destination_cid=header.source_cid,
                    supported_versions=self.quic_config.supported_versions,
                )
                await self.send(RawData(data=data, address=event.address))
                return

            connection = self.connections.get(header.destination_cid)
            if (
                connection is None
                and len(event.data) >= 1200
                and header.packet_type == PACKET_TYPE_INITIAL
            ):
                connection = QuicConnection(
                    configuration=self.quic_config, original_connection_id=None
                )
                self.connections[header.destination_cid] = connection
                self.connections[connection.host_cid] = connection

            if connection is not None:
                connection.receive_datagram(event.data, event.address, now=self.now())
                await self._handle_events(connection, event.address)
        elif isinstance(event, Closed):
            pass
Example #2
0
 def test_encode_quic_version_negotiation(self):
     data = encode_quic_version_negotiation(
         destination_cid=binascii.unhexlify("9aac5a49ba87a849"),
         source_cid=binascii.unhexlify("f92f4336fa951ba1"),
         supported_versions=[0x45474716, QuicProtocolVersion.DRAFT_22],
     )
     self.assertEqual(data[1:], load("version_negotiation.bin")[1:])
Example #3
0
    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
Example #4
0
    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)