aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy/protocol2/root_context.py
blob: f0e5b9a749e80839352e9945a897434d25483268 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from __future__ import (absolute_import, print_function, division)
import string

from libmproxy.protocol2.layer import Kill
from .rawtcp import RawTcpLayer
from .tls import TlsLayer
from .http import Http1Layer, Http2Layer, HttpLayer


class RootContext(object):
    """
    The outmost context provided to the root layer.
    As a consequence, every layer has .client_conn, .channel, .next_layer() and .config.
    """

    def __init__(self, client_conn, config, channel):
        self.client_conn = client_conn  # Client Connection
        self.channel = channel  # provides .ask() method to communicate with FlowMaster
        self.config = config  # Proxy Configuration

    def next_layer(self, top_layer):
        """
        This function determines the next layer in the protocol stack.
        :param top_layer: the current top layer
        :return: The next layer.
        """

        d = top_layer.client_conn.rfile.peek(3)

        # TODO: Handle ignore and tcp passthrough

        # TLS ClientHello magic, see http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html#client-hello
        is_tls_client_hello = (
            len(d) == 3 and
            d[0] == '\x16' and
            d[1] == '\x03' and
            d[2] in ('\x00', '\x01', '\x02', '\x03')
        )

        is_ascii = all(x in string.ascii_uppercase for x in d)

        # TODO: build is_http2_magic check here, maybe this is an easy way to detect h2c

        if not d:
            return iter([])

        if is_tls_client_hello:
            return TlsLayer(top_layer, True, True)
        elif isinstance(top_layer, TlsLayer) and is_ascii:
            if top_layer.client_conn.get_alpn_proto_negotiated() == 'h2':
                return Http2Layer(top_layer, 'transparent')
            else:
                return Http1Layer(top_layer, "transparent")
        else:
            return RawTcpLayer(top_layer)

    @property
    def layers(self):
        return []

    def __repr__(self):
        return "RootContext"