1# Authors:
2#   Trevor Perrin
3#   Kees Bos - Fixes for compatibility with different Python versions
4#   Martin von Loewis - python 3 port
5#
6# See the LICENSE file for legal information regarding use of this file.
7
8
9"""TLS Lite + xmlrpclib."""
10
11try:
12    import xmlrpclib
13    import httplib
14except ImportError:
15    # Python 3
16    from xmlrpc import client as xmlrpclib
17    from http import client as httplib
18from tlslite.integration.httptlsconnection import HTTPTLSConnection
19from tlslite.integration.clienthelper import ClientHelper
20import tlslite.errors
21
22
23class XMLRPCTransport(xmlrpclib.Transport, ClientHelper):
24    """Handles an HTTPS transaction to an XML-RPC server."""
25
26    # Pre python 2.7, the make_connection returns a HTTP class
27    transport = xmlrpclib.Transport()
28    conn_class_is_http = not hasattr(transport, '_connection')
29    del(transport)
30
31    def __init__(self, use_datetime=0,
32                 username=None, password=None,
33                 certChain=None, privateKey=None,
34                 checker=None,
35                 settings=None,
36                 ignoreAbruptClose=False):
37        """Create a new XMLRPCTransport.
38
39        An instance of this class can be passed to L{xmlrpclib.ServerProxy}
40        to use TLS with XML-RPC calls::
41
42            from tlslite import XMLRPCTransport
43            from xmlrpclib import ServerProxy
44
45            transport = XMLRPCTransport(user="alice", password="abra123")
46            server = ServerProxy("https://localhost", transport)
47
48        For client authentication, use one of these argument
49        combinations:
50         - username, password (SRP)
51         - certChain, privateKey (certificate)
52
53        For server authentication, you can either rely on the
54        implicit mutual authentication performed by SRP or
55        you can do certificate-based server
56        authentication with one of these argument combinations:
57         - x509Fingerprint
58
59        Certificate-based server authentication is compatible with
60        SRP or certificate-based client authentication.
61
62        The constructor does not perform the TLS handshake itself, but
63        simply stores these arguments for later.  The handshake is
64        performed only when this class needs to connect with the
65        server.  Thus you should be prepared to handle TLS-specific
66        exceptions when calling methods of L{xmlrpclib.ServerProxy}.  See the
67        client handshake functions in
68        L{tlslite.TLSConnection.TLSConnection} for details on which
69        exceptions might be raised.
70
71        @type username: str
72        @param username: SRP username.  Requires the
73        'password' argument.
74
75        @type password: str
76        @param password: SRP password for mutual authentication.
77        Requires the 'username' argument.
78
79        @type certChain: L{tlslite.x509certchain.X509CertChain}
80        @param certChain: Certificate chain for client authentication.
81        Requires the 'privateKey' argument.  Excludes the SRP arguments.
82
83        @type privateKey: L{tlslite.utils.rsakey.RSAKey}
84        @param privateKey: Private key for client authentication.
85        Requires the 'certChain' argument.  Excludes the SRP arguments.
86
87        @type checker: L{tlslite.checker.Checker}
88        @param checker: Callable object called after handshaking to
89        evaluate the connection and raise an Exception if necessary.
90
91        @type settings: L{tlslite.handshakesettings.HandshakeSettings}
92        @param settings: Various settings which can be used to control
93        the ciphersuites, certificate types, and SSL/TLS versions
94        offered by the client.
95
96        @type ignoreAbruptClose: bool
97        @param ignoreAbruptClose: ignore the TLSAbruptCloseError on
98        unexpected hangup.
99        """
100
101        # self._connection is new in python 2.7, since we're using it here,
102        # we'll add this ourselves too, just in case we're pre-2.7
103        self._connection = (None, None)
104        xmlrpclib.Transport.__init__(self, use_datetime)
105        self.ignoreAbruptClose = ignoreAbruptClose
106        ClientHelper.__init__(self,
107                 username, password,
108                 certChain, privateKey,
109                 checker,
110                 settings)
111
112    def make_connection(self, host):
113        # return an existing connection if possible.  This allows
114        # HTTP/1.1 keep-alive.
115        if self._connection and host == self._connection[0]:
116            http = self._connection[1]
117        else:
118            # create a HTTPS connection object from a host descriptor
119            chost, extra_headers, x509 = self.get_host_info(host)
120
121            http = HTTPTLSConnection(chost, None,
122                                     username=self.username, password=self.password,
123                                     certChain=self.certChain, privateKey=self.privateKey,
124                                     checker=self.checker,
125                                     settings=self.settings,
126                                     ignoreAbruptClose=self.ignoreAbruptClose)
127            # store the host argument along with the connection object
128            self._connection = host, http
129        if not self.conn_class_is_http:
130            return http
131        http2 = httplib.HTTP()
132        http2._setup(http)
133        return http2
134