예제 #1
0
    def get_connection(self,
                       event: GetHttpConnection,
                       *,
                       reuse: bool = True) -> layer.CommandGenerator[None]:
        # Do we already have a connection we can re-use?
        if reuse:
            for connection in self.connections:
                # see "tricky multiplexing edge case" in make_http_connection for an explanation
                conn_is_pending_or_h2 = (connection.alpn == b"h2" or connection
                                         in self.waiting_for_establishment)
                h2_to_h1 = self.context.client.alpn == b"h2" and not conn_is_pending_or_h2
                connection_suitable = (
                    event.connection_spec_matches(connection) and not h2_to_h1)
                if connection_suitable:
                    if connection in self.waiting_for_establishment:
                        self.waiting_for_establishment[connection].append(
                            event)
                    else:
                        stream = self.command_sources.pop(event)
                        yield from self.event_to_child(
                            stream,
                            GetHttpConnectionReply(event, (connection, None)))
                    return

        can_use_context_connection = (
            self.context.server not in self.connections
            and self.context.server.connected
            and event.connection_spec_matches(self.context.server))
        context = self.context.fork()

        stack = tunnel.LayerStack()

        if not can_use_context_connection:

            context.server = Server(event.address)
            if event.tls:
                context.server.sni = event.address[0].encode()

            if event.via:
                assert event.via.scheme in ("http", "https")
                http_proxy = Server(event.via.address)

                if event.via.scheme == "https":
                    http_proxy.alpn_offers = tls.HTTP_ALPNS
                    http_proxy.sni = event.via.address[0].encode()
                    stack /= tls.ServerTLSLayer(context, http_proxy)

                send_connect = not (self.mode == HTTPMode.upstream
                                    and not event.tls)
                stack /= _upstream_proxy.HttpUpstreamProxy(
                    context, http_proxy, send_connect)
            if event.tls:
                stack /= tls.ServerTLSLayer(context)

        stack /= HttpClient(context)

        self.connections[context.server] = stack[0]
        self.waiting_for_establishment[context.server].append(event)

        yield from self.event_to_child(stack[0], events.Start())
예제 #2
0
def test_tunnel_handshake_start(tctx: Context, success):
    server = Server(("proxy", 1234))
    server.state = ConnectionState.OPEN

    tl = TTunnelLayer(tctx, server, tctx.server)
    tl.child_layer = TChildLayer(tctx)
    assert repr(tl)

    playbook = Playbook(tl, logs=True)
    (playbook << SendData(server, b"handshake-hello") >> DataReceived(
        tctx.client, b"client-hello") >> DataReceived(
            server, b"handshake-" + success.encode()) << SendData(
                server, b"handshake-" + success.encode()))
    if success == "success":
        playbook << Log("Got start. Server state: OPEN")
    else:
        playbook << CloseConnection(server)
        playbook << Log("Got start. Server state: CLOSED")

    playbook << SendData(tctx.client, b"client-hello-reply")
    if success == "success":
        playbook >> DataReceived(server, b"tunneled-server-hello")
        playbook << SendData(server, b"tunneled-server-hello-reply")

    assert playbook
예제 #3
0
    def handle_connect_upstream(self):
        assert self.context.server.via.scheme in ("http", "https")

        http_proxy = Server(self.context.server.via.address)

        stack = tunnel.LayerStack()
        if self.context.server.via.scheme == "https":
            http_proxy.sni = self.context.server.via.address[0].encode()
            stack /= tls.ServerTLSLayer(self.context, http_proxy)
        stack /= _upstream_proxy.HttpUpstreamProxy(self.context, http_proxy, True)

        self.child_layer = stack[0]
        yield from self.handle_connect_finish()
예제 #4
0
def test_tunnel_handshake_command(tctx: Context, success):
    server = Server(("proxy", 1234))

    tl = TTunnelLayer(tctx, server, tctx.server)
    tl.child_layer = TChildLayer(tctx)

    playbook = Playbook(tl, logs=True)
    (playbook << Log("Got start. Server state: CLOSED") >> DataReceived(
        tctx.client, b"client-hello") << SendData(
            tctx.client, b"client-hello-reply") >> DataReceived(
                tctx.client, b"open") << OpenConnection(server) >> reply(None)
     << SendData(server, b"handshake-hello") >> DataReceived(
         server, b"handshake-" + success.encode()) << SendData(
             server, b"handshake-" + success.encode()))
    if success == "success":
        assert (playbook << Log(f"Opened: err=None. Server state: OPEN") >>
                DataReceived(server, b"tunneled-server-hello") << SendData(
                    server, b"tunneled-server-hello-reply") >>
                ConnectionClosed(tctx.client) << Log("Got client close.") <<
                CloseConnection(tctx.client))
        assert tl.tunnel_state is tunnel.TunnelState.OPEN
        assert (playbook >> ConnectionClosed(server) <<
                Log("Got server close.") << CloseConnection(server))
        assert tl.tunnel_state is tunnel.TunnelState.CLOSED
    else:
        assert (playbook << CloseConnection(server) <<
                Log("Opened: err='handshake error'. Server state: CLOSED"))
        assert tl.tunnel_state is tunnel.TunnelState.CLOSED
예제 #5
0
def test_disconnect_during_handshake_command(tctx: Context, disconnect):
    server = Server(("proxy", 1234))

    tl = TTunnelLayer(tctx, server, tctx.server)
    tl.child_layer = TChildLayer(tctx)

    playbook = Playbook(tl, logs=True)
    assert (playbook << Log("Got start. Server state: CLOSED") >> DataReceived(
        tctx.client, b"client-hello") << SendData(
            tctx.client, b"client-hello-reply") >> DataReceived(
                tctx.client, b"open") << OpenConnection(server) >> reply(None)
            << SendData(server, b"handshake-hello"))
    if disconnect == "client":
        assert (
            playbook >> ConnectionClosed(tctx.client) >> ConnectionClosed(
                server
            )  # proxyserver will cancel all other connections as well.
            << CloseConnection(server) <<
            Log("Opened: err='connection closed without notice'. Server state: CLOSED"
                ) << Log("Got client close.") << CloseConnection(tctx.client))
    else:
        assert (playbook >> ConnectionClosed(server) << CloseConnection(
            server
        ) << Log(
            "Opened: err='connection closed without notice'. Server state: CLOSED"
        ))
예제 #6
0
def test_disconnect_during_handshake_start(tctx: Context, disconnect):
    server = Server(("proxy", 1234))
    server.state = ConnectionState.OPEN

    tl = TTunnelLayer(tctx, server, tctx.server)
    tl.child_layer = TChildLayer(tctx)

    playbook = Playbook(tl, logs=True)

    assert (playbook << SendData(server, b"handshake-hello"))
    if disconnect == "client":
        assert (playbook >> ConnectionClosed(tctx.client) >> ConnectionClosed(
            server)  # proxyserver will cancel all other connections as well.
                << CloseConnection(server) <<
                Log("Got start. Server state: CLOSED") <<
                Log("Got client close.") << CloseConnection(tctx.client))
    else:
        assert (playbook >> ConnectionClosed(server) << CloseConnection(server)
                << Log("Got start. Server state: CLOSED"))
예제 #7
0
def test_tunnel_openconnection_error(tctx: Context):
    server = Server(("proxy", 1234))

    tl = TTunnelLayer(tctx, server, tctx.server)
    tl.child_layer = TChildLayer(tctx)

    playbook = Playbook(tl, logs=True)
    assert (playbook << Log("Got start. Server state: CLOSED") >> DataReceived(
        tctx.client, b"open") << OpenConnection(server))
    assert tl.tunnel_state is tunnel.TunnelState.ESTABLISHING
    assert (playbook >> reply("IPoAC packet dropped.") <<
            Log("Opened: err='IPoAC packet dropped.'. Server state: CLOSED"))
    assert tl.tunnel_state is tunnel.TunnelState.CLOSED
예제 #8
0
def test_tunnel_default_impls(tctx: Context):
    """
    Some tunnels don't need certain features, so the default behaviour
    should be to be transparent.
    """
    server = Server(None)
    server.state = ConnectionState.OPEN
    tl = tunnel.TunnelLayer(tctx, server, tctx.server)
    tl.child_layer = TChildLayer(tctx)
    playbook = Playbook(tl, logs=True)
    assert (playbook << Log("Got start. Server state: OPEN") >> DataReceived(
        server, b"server-hello") << SendData(server, b"server-hello-reply"))
    assert tl.tunnel_state is tunnel.TunnelState.OPEN
    assert (playbook >> ConnectionClosed(server) << Log("Got server close.") <<
            CloseConnection(server))
    assert tl.tunnel_state is tunnel.TunnelState.CLOSED

    assert (
        playbook >> DataReceived(
            tctx.client, b"open") << OpenConnection(server) >> reply(None) <<
        Log("Opened: err=None. Server state: OPEN") >> DataReceived(
            server, b"half-close") << CloseConnection(server, half_close=True))
예제 #9
0
    def __init__(self, flow: http.HTTPFlow, options: Options) -> None:
        client = flow.client_conn.copy()
        client.state = ConnectionState.OPEN

        context = Context(client, options)
        context.server = Server((flow.request.host, flow.request.port))
        context.server.tls = flow.request.scheme == "https"
        if options.mode.startswith("upstream:"):
            context.server.via = server_spec.parse_with_mode(options.mode)[1]

        super().__init__(context)

        self.layer = layers.HttpLayer(context, HTTPMode.transparent)
        self.layer.connections[client] = MockServer(flow, context.fork())
        self.flow = flow
        self.done = asyncio.Event()