Пример #1
0
    def test_connect_with_log(self):
        client_log_file = io.StringIO()
        client = QuicConnection(is_client=True,
                                secrets_log_file=client_log_file)
        server_log_file = io.StringIO()
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
            secrets_log_file=server_log_file,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)
        self.assertEqual(client_transport.sent, 4)
        self.assertEqual(server_transport.sent, 4)

        # check secrets were logged
        client_log = client_log_file.getvalue()
        server_log = server_log_file.getvalue()
        self.assertEqual(client_log, server_log)
        labels = []
        for line in client_log.splitlines():
            labels.append(line.split()[0])
        self.assertEqual(
            labels,
            [
                "QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET",
                "QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET",
                "QUIC_SERVER_TRAFFIC_SECRET_0",
                "QUIC_CLIENT_TRAFFIC_SECRET_0",
            ],
        )
Пример #2
0
    def test_handle_stream_frame_over_max_stream_data(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client receives STREAM frame
        frame_type = QuicFrameType.STREAM_BASE | QuicStreamFlag.OFF
        stream_id = 1
        with self.assertRaises(QuicConnectionError) as cm:
            client._handle_stream_frame(
                tls.Epoch.ONE_RTT,
                frame_type,
                Buffer(
                    data=encode_uint_var(stream_id) +
                    encode_uint_var(client._local_max_stream_data_bidi_remote +
                                    1)),
            )
        self.assertEqual(cm.exception.error_code,
                         QuicErrorCode.FLOW_CONTROL_ERROR)
        self.assertEqual(cm.exception.frame_type, frame_type)
        self.assertEqual(cm.exception.reason_phrase, "Over stream data limit")
Пример #3
0
 def test_handle_ack_frame_ecn(self):
     client = QuicConnection(is_client=True)
     client._handle_ack_frame(
         tls.Epoch.ONE_RTT,
         QuicFrameType.ACK_ECN,
         Buffer(data=b"\x00\x02\x00\x00\x00\x00\x00"),
     )
Пример #4
0
    def _consume_events(self, connection: QuicConnection) -> None:
        # process events
        event = connection.next_event()
        while event is not None:
            if isinstance(event, aioquic.events.HandshakeCompleted):
                if event.alpn_protocol == "h3-20":
                    self._http[connection] = H3Connection(connection)
                elif event.alpn_protocol == "hq-20":
                    self._http[connection] = H0Connection(connection)
            elif isinstance(event, aioquic.events.ConnectionIdIssued):
                self._connections[event.connection_id] = connection
            elif isinstance(event, aioquic.events.ConnectionIdRetired):
                assert self._connections[event.connection_id] == connection
                del self._connections[event.connection_id]

            #  pass event to the HTTP layer
            http = self._http.get(connection)
            if http is not None:
                for http_event in http.handle_event(event):
                    handle_http_event(http, http_event)

            event = connection.next_event()

        # send datagrams
        for data, addr in connection.datagrams_to_send(now=self._loop.time()):
            self._transport.sendto(data, addr)

        # re-arm timer
        """
Пример #5
0
def create_standalone_client(self):
    client = QuicConnection(configuration=QuicConfiguration(is_client=True))
    client._ack_delay = 0

    # kick-off handshake
    client.connect(SERVER_ADDR, now=time.time())
    self.assertEqual(drop(client), 1)

    return client
Пример #6
0
    def test_handle_path_challenge_frame(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # server sends PATH_CHALLENGE
        server._send_path_challenge()
Пример #7
0
    def test_handle_max_stream_data_frame(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client creates bidirectional stream 0
        stream = client.create_stream()[1].transport
        self.assertEqual(stream.max_stream_data_remote, 1048576)

        # client receives MAX_STREAM_DATA raising limit
        client._handle_max_stream_data_frame(
            tls.Epoch.ONE_RTT,
            QuicFrameType.MAX_STREAM_DATA,
            Buffer(data=b"\x00" + encode_uint_var(1048577)),
        )
        self.assertEqual(stream.max_stream_data_remote, 1048577)

        # client receives MAX_STREAM_DATA lowering limit
        client._handle_max_stream_data_frame(
            tls.Epoch.ONE_RTT,
            QuicFrameType.MAX_STREAM_DATA,
            Buffer(data=b"\x00" + encode_uint_var(1048575)),
        )
        self.assertEqual(stream.max_stream_data_remote, 1048577)
Пример #8
0
    def test_handle_stop_sending_frame_receive_only(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # server creates unidirectional stream 3
        server.create_stream(is_unidirectional=True)

        # client receives STOP_SENDING
        with self.assertRaises(QuicConnectionError) as cm:
            client._handle_stop_sending_frame(
                tls.Epoch.ONE_RTT,
                QuicFrameType.STOP_SENDING,
                Buffer(data=b"\x03\x11\x22"),
            )
        self.assertEqual(cm.exception.error_code,
                         QuicErrorCode.STREAM_STATE_ERROR)
        self.assertEqual(cm.exception.frame_type, QuicFrameType.STOP_SENDING)
        self.assertEqual(cm.exception.reason_phrase, "Stream is receive-only")
Пример #9
0
def client_and_server(
    client_options={},
    client_patch=lambda x: None,
    server_options={},
    server_patch=lambda x: None,
    server_stream_handler=None,
    transport_options={},
):
    client = QuicConnection(
        configuration=QuicConfiguration(is_client=True, **client_options))
    client_patch(client)

    server = QuicConnection(
        configuration=QuicConfiguration(is_client=False,
                                        certificate=SERVER_CERTIFICATE,
                                        private_key=SERVER_PRIVATE_KEY,
                                        **server_options),
        stream_handler=server_stream_handler,
    )
    server_patch(server)

    # perform handshake
    create_transport(client, server, **transport_options)

    yield client, server

    # close
    client.close()
    server.close()
Пример #10
0
    def test_handle_retire_connection_id_frame(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client receives RETIRE_CONNECTION_ID
        client._handle_retire_connection_id_frame(
            tls.Epoch.ONE_RTT, QuicFrameType.RETIRE_CONNECTION_ID,
            Buffer(data=b"\x02"))
Пример #11
0
    def test_handle_streams_blocked_uni_frame(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client receives STREAMS_BLOCKED_UNI: 0
        client._handle_streams_blocked_frame(tls.Epoch.ONE_RTT,
                                             QuicFrameType.STREAMS_BLOCKED_UNI,
                                             Buffer(data=b"\x00"))
Пример #12
0
def run(url: str) -> None:
    # parse URL
    parsed = urlparse(url)
    assert parsed.scheme == "https", "Only HTTPS URLs are supported."
    if ":" in parsed.netloc:
        server_name, port_str = parsed.netloc.split(":")
        port = int(port_str)
    else:
        server_name = parsed.netloc
        port = 443

    # prepare socket
    server_addr = (socket.gethostbyname(server_name), port)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # prepare QUIC connection
    quic = QuicConnection(configuration=QuicConfiguration(
        alpn_protocols=["h3-20"],
        is_client=True,
        secrets_log_file=open("/tmp/ssl.log", "w"),
        server_name=server_name,
    ))
    quic.connect(server_addr, now=time.time())

    # send request
    http = H3Connection(quic)
    stream_id = quic.get_next_available_stream_id()
    http.send_headers(
        stream_id=stream_id,
        headers=[
            (b":method", b"GET"),
            (b":scheme", parsed.scheme.encode("utf8")),
            (b":authority", parsed.netloc.encode("utf8")),
            (b":path", parsed.path.encode("utf8")),
        ],
    )
    http.send_data(stream_id=stream_id, data=b"", end_stream=True)
    for data, addr in quic.datagrams_to_send(now=time.time()):
        sock.sendto(data, addr)

    # handle events
    stream_ended = False
    while not stream_ended:
        data, addr = sock.recvfrom(2048)
        quic.receive_datagram(data, addr, now=time.time())
        for event in http.handle_events():
            print(event)
            if isinstance(event, (DataReceived, ResponseReceived)):
                stream_ended = event.stream_ended

        for data, addr in quic.datagrams_to_send(now=time.time()):
            sock.sendto(data, addr)
Пример #13
0
    def test_handle_connection_close_frame_app(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)
        self.assertEqual(client_transport.sent, 4)
        self.assertEqual(server_transport.sent, 4)

        # close
        server.close(error_code=QuicErrorCode.NO_ERROR)
        self.assertEqual(client_transport.sent, 5)
        self.assertEqual(server_transport.sent, 5)
Пример #14
0
    def test_handle_new_token_frame(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client receives NEW_TOKEN
        client._handle_new_token_frame(
            tls.Epoch.ONE_RTT,
            QuicFrameType.NEW_TOKEN,
            Buffer(data=binascii.unhexlify("080102030405060708")),
        )
Пример #15
0
    def test_handle_data_blocked_frame(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client receives DATA_BLOCKED: 12345
        client._handle_data_blocked_frame(
            tls.Epoch.ONE_RTT,
            QuicFrameType.DATA_BLOCKED,
            Buffer(data=encode_uint_var(12345)),
        )
Пример #16
0
    def test_connection_lost_with_exception(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)
        self.assertEqual(client_transport.sent, 4)
        self.assertEqual(server_transport.sent, 4)
        run(client.connect())

        # send data over stream
        client_reader, client_writer = client.create_stream()
        client_writer.write(b"ping")
        run(asyncio.sleep(0))
        self.assertEqual(client_transport.sent, 5)
        self.assertEqual(server_transport.sent, 5)

        # break connection
        exc = Exception("some error")
        client.connection_lost(exc)
        with self.assertRaises(Exception) as cm:
            run(client_reader.read())
        self.assertEqual(cm.exception, exc)
Пример #17
0
    def test_handle_max_streams_uni_frame(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)
        self.assertEqual(client._remote_max_streams_uni, 128)

        # client receives MAX_STREAMS_UNI raising limit
        client._handle_max_streams_uni_frame(
            tls.Epoch.ONE_RTT,
            QuicFrameType.MAX_STREAMS_UNI,
            Buffer(data=encode_uint_var(129)),
        )
        self.assertEqual(client._remote_max_streams_uni, 129)

        # client receives MAX_STREAMS_UNI raising limit
        client._handle_max_streams_uni_frame(
            tls.Epoch.ONE_RTT,
            QuicFrameType.MAX_STREAMS_UNI,
            Buffer(data=encode_uint_var(127)),
        )
        self.assertEqual(client._remote_max_streams_uni, 129)
Пример #18
0
    def test_handle_stream_data_blocked_frame_send_only(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client creates unidirectional stream 2
        client.create_stream(is_unidirectional=True)

        # client receives STREAM_DATA_BLOCKED
        with self.assertRaises(QuicConnectionError) as cm:
            client._handle_stream_data_blocked_frame(
                tls.Epoch.ONE_RTT,
                QuicFrameType.STREAM_DATA_BLOCKED,
                Buffer(data=b"\x02\x01"),
            )
        self.assertEqual(cm.exception.error_code,
                         QuicErrorCode.STREAM_STATE_ERROR)
        self.assertEqual(cm.exception.frame_type,
                         QuicFrameType.STREAM_DATA_BLOCKED)
        self.assertEqual(cm.exception.reason_phrase, "Stream is send-only")
Пример #19
0
    def datagram_received(self, datagram, addr):
        buf = Buffer(data=datagram)
        header = pull_quic_header(buf, host_cid_length=8)

        # version negotiation
        if (header.version is not None
                and header.version not in QuicConnection.supported_versions):
            self._transport.sendto(
                encode_quic_version_negotiation(
                    source_cid=header.destination_cid,
                    destination_cid=header.source_cid,
                    supported_versions=QuicConnection.supported_versions,
                ),
                addr,
            )
            return

        connection = self._connections.get(header.destination_cid, None)
        if connection is None and header.packet_type == PACKET_TYPE_INITIAL:
            # create new connection
            connection = QuicConnection(is_client=False, **self._kwargs)
            connection.connection_made(QuicConnectionTransport(self, addr))
            connection.stream_created_cb = self.stream_created
            self._connections[connection.host_cid] = connection
            logger.info("%s New connection from %s" %
                        (connection_id(connection), addr))

        if connection is not None:
            connection.datagram_received(datagram, addr)
Пример #20
0
    def test_datagram_received_retry(self):
        client = QuicConnection(is_client=True)
        client.host_cid = binascii.unhexlify("c98343fe8f5f0ff4")
        client.peer_cid = binascii.unhexlify("85abb547bf28be97")

        client_transport = FakeTransport()
        client.connection_made(client_transport)
        self.assertEqual(client_transport.sent, 1)

        client.datagram_received(load("retry.bin"), None)
        self.assertEqual(client_transport.sent, 2)
Пример #21
0
def create_standalone_client():
    client = QuicConnection(is_client=True)
    client_transport = FakeTransport(CLIENT_ADDR)
    client.connection_made(client_transport)

    # like connect() but without waiting
    client._network_paths = [QuicNetworkPath(SERVER_ADDR, is_validated=True)]
    client._version = max(client.supported_versions)
    client._connect()

    return client, client_transport
Пример #22
0
    def test_handle_unknown_frame(self):
        client = QuicConnection(is_client=True)

        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client receives unknown frame
        with self.assertRaises(QuicConnectionError) as cm:
            client._payload_received(tls.Epoch.ONE_RTT, b"\x1e")
        self.assertEqual(cm.exception.error_code,
                         QuicErrorCode.PROTOCOL_VIOLATION)
        self.assertEqual(cm.exception.frame_type, 0x1E)
        self.assertEqual(cm.exception.reason_phrase, "Unexpected frame type")
Пример #23
0
def client_and_server():
    client = QuicConnection(is_client=True)
    server = QuicConnection(
        is_client=False, certificate=SERVER_CERTIFICATE, private_key=SERVER_PRIVATE_KEY
    )

    # perform handshake
    create_transport(client, server)

    yield client, server
    client.close()
    server.close()
Пример #24
0
    def test_handle_new_connection_id_frame(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client receives NEW_CONNECTION_ID
        client._handle_new_connection_id_frame(
            tls.Epoch.ONE_RTT,
            QuicFrameType.NEW_CONNECTION_ID,
            Buffer(data=binascii.unhexlify(
                "02117813f3d9e45e0cacbb491b4b66b039f20406f68fede38ec4c31aba8ab1245244e8"
            )),
        )
Пример #25
0
    def test_tls_error(self):
        client = QuicConnection(is_client=True)
        real_initialize = client._initialize

        def patched_initialize(peer_cid: bytes):
            real_initialize(peer_cid)
            client.tls._supported_versions = [tls.TLS_VERSION_1_3_DRAFT_28]

        client._initialize = patched_initialize

        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # fail handshake
        client_transport, server_transport = create_transport(client, server)
        self.assertEqual(client_transport.sent, 2)
        self.assertEqual(server_transport.sent, 1)
Пример #26
0
    def test_handle_stream_frame_wrong_initiator(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # client receives STREAM frame
        with self.assertRaises(QuicConnectionError) as cm:
            client._handle_stream_frame(tls.Epoch.ONE_RTT,
                                        QuicFrameType.STREAM_BASE,
                                        Buffer(data=b"\x00"))
        self.assertEqual(cm.exception.error_code,
                         QuicErrorCode.STREAM_STATE_ERROR)
        self.assertEqual(cm.exception.frame_type, QuicFrameType.STREAM_BASE)
        self.assertEqual(cm.exception.reason_phrase, "Wrong stream initiator")
Пример #27
0
    def test_handle_path_response_frame_bad(self):
        client = QuicConnection(is_client=True)
        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)

        # server receives unsollicited PATH_RESPONSE
        with self.assertRaises(QuicConnectionError) as cm:
            server._handle_path_response_frame(
                tls.Epoch.ONE_RTT,
                QuicFrameType.PATH_RESPONSE,
                Buffer(data=b"\x11\x22\x33\x44\x55\x66\x77\x88"),
            )
        self.assertEqual(cm.exception.error_code,
                         QuicErrorCode.PROTOCOL_VIOLATION)
        self.assertEqual(cm.exception.frame_type, QuicFrameType.PATH_RESPONSE)
Пример #28
0
    def test_decryption_error(self):
        client = QuicConnection(is_client=True)

        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )

        # perform handshake
        client_transport, server_transport = create_transport(client, server)
        self.assertEqual(client_transport.sent, 4)
        self.assertEqual(server_transport.sent, 4)

        # mess with encryption key
        server.spaces[tls.Epoch.ONE_RTT].crypto.send.setup(
            tls.CipherSuite.AES_128_GCM_SHA256, bytes(48))

        # close
        server.close(error_code=QuicErrorCode.NO_ERROR)
        self.assertEqual(client_transport.sent, 4)
        self.assertEqual(server_transport.sent, 5)
Пример #29
0
    def test_datagram_received_wrong_destination_cid(self):
        client = QuicConnection(is_client=True)
        client_transport = FakeTransport()
        client.connection_made(client_transport)
        self.assertEqual(client_transport.sent, 1)

        client.datagram_received(load("retry.bin"), None)
        self.assertEqual(client_transport.sent, 1)
Пример #30
0
    def _test_connect_with_version(self, client_versions, server_versions):
        client = QuicConnection(is_client=True)
        client.supported_versions = client_versions

        server = QuicConnection(
            is_client=False,
            certificate=SERVER_CERTIFICATE,
            private_key=SERVER_PRIVATE_KEY,
        )
        server.supported_versions = server_versions

        # perform handshake
        client_transport, server_transport = create_transport(client, server)
        self.assertEqual(client_transport.sent, 4)
        self.assertEqual(server_transport.sent, 3)

        # check each endpoint has available connection IDs for the peer
        self.assertEqual(
            sequence_numbers(client._peer_cid_available), [1, 2, 3, 4, 5, 6, 7]
        )
        self.assertEqual(
            sequence_numbers(server._peer_cid_available), [1, 2, 3, 4, 5, 6, 7]
        )

        # send data over stream
        client_reader, client_writer = run(client.create_stream())
        client_writer.write(b"ping")
        run(asyncio.sleep(0))
        self.assertEqual(client_transport.sent, 5)
        self.assertEqual(server_transport.sent, 4)

        # FIXME: needs an API
        server_reader, server_writer = (
            server.streams[0].reader,
            server.streams[0].writer,
        )
        self.assertEqual(run(server_reader.read(1024)), b"ping")
        server_writer.write(b"pong")
        run(asyncio.sleep(0))
        self.assertEqual(client_transport.sent, 6)
        self.assertEqual(server_transport.sent, 5)

        # client receives pong
        self.assertEqual(run(client_reader.read(1024)), b"pong")

        # client writes EOF
        client_writer.write_eof()
        run(asyncio.sleep(0))
        self.assertEqual(client_transport.sent, 7)
        self.assertEqual(server_transport.sent, 6)

        # server receives EOF
        self.assertEqual(run(server_reader.read()), b"")