1# Authors:
2#   Trevor Perrin
3#   Martin von Loewis - python 3 port
4#
5# See the LICENSE file for legal information regarding use of this file.
6
7"""TLS Lite + asyncore."""
8
9
10import asyncore
11from tlslite.tlsconnection import TLSConnection
12from .asyncstatemachine import AsyncStateMachine
13
14
15class TLSAsyncDispatcherMixIn(AsyncStateMachine):
16    """This class can be "mixed in" with an
17    L{asyncore.dispatcher} to add TLS support.
18
19    This class essentially sits between the dispatcher and the select
20    loop, intercepting events and only calling the dispatcher when
21    applicable.
22
23    In the case of handle_read(), a read operation will be activated,
24    and when it completes, the bytes will be placed in a buffer where
25    the dispatcher can retrieve them by calling recv(), and the
26    dispatcher's handle_read() will be called.
27
28    In the case of handle_write(), the dispatcher's handle_write() will
29    be called, and when it calls send(), a write operation will be
30    activated.
31
32    To use this class, you must combine it with an asyncore.dispatcher,
33    and pass in a handshake operation with setServerHandshakeOp().
34
35    Below is an example of using this class with medusa.  This class is
36    mixed in with http_channel to create http_tls_channel.  Note:
37     1. the mix-in is listed first in the inheritance list
38
39     2. the input buffer size must be at least 16K, otherwise the
40       dispatcher might not read all the bytes from the TLS layer,
41       leaving some bytes in limbo.
42
43     3. IE seems to have a problem receiving a whole HTTP response in a
44     single TLS record, so HTML pages containing '\\r\\n\\r\\n' won't
45     be displayed on IE.
46
47    Add the following text into 'start_medusa.py', in the 'HTTP Server'
48    section::
49
50        from tlslite import *
51        s = open("./serverX509Cert.pem").read()
52        x509 = X509()
53        x509.parse(s)
54        certChain = X509CertChain([x509])
55
56        s = open("./serverX509Key.pem").read()
57        privateKey = parsePEMKey(s, private=True)
58
59        class http_tls_channel(TLSAsyncDispatcherMixIn,
60                               http_server.http_channel):
61            ac_in_buffer_size = 16384
62
63            def __init__ (self, server, conn, addr):
64                http_server.http_channel.__init__(self, server, conn, addr)
65                TLSAsyncDispatcherMixIn.__init__(self, conn)
66                self.tlsConnection.ignoreAbruptClose = True
67                self.setServerHandshakeOp(certChain=certChain,
68                                          privateKey=privateKey)
69
70        hs.channel_class = http_tls_channel
71
72    If the TLS layer raises an exception, the exception will be caught
73    in asyncore.dispatcher, which will call close() on this class.  The
74    TLS layer always closes the TLS connection before raising an
75    exception, so the close operation will complete right away, causing
76    asyncore.dispatcher.close() to be called, which closes the socket
77    and removes this instance from the asyncore loop.
78
79    """
80
81
82    def __init__(self, sock=None):
83        AsyncStateMachine.__init__(self)
84
85        if sock:
86            self.tlsConnection = TLSConnection(sock)
87
88        #Calculate the sibling I'm being mixed in with.
89        #This is necessary since we override functions
90        #like readable(), handle_read(), etc., but we
91        #also want to call the sibling's versions.
92        for cl in self.__class__.__bases__:
93            if cl != TLSAsyncDispatcherMixIn and cl != AsyncStateMachine:
94                self.siblingClass = cl
95                break
96        else:
97            raise AssertionError()
98
99    def readable(self):
100        result = self.wantsReadEvent()
101        if result != None:
102            return result
103        return self.siblingClass.readable(self)
104
105    def writable(self):
106        result = self.wantsWriteEvent()
107        if result != None:
108            return result
109        return self.siblingClass.writable(self)
110
111    def handle_read(self):
112        self.inReadEvent()
113
114    def handle_write(self):
115        self.inWriteEvent()
116
117    def outConnectEvent(self):
118        self.siblingClass.handle_connect(self)
119
120    def outCloseEvent(self):
121        asyncore.dispatcher.close(self)
122
123    def outReadEvent(self, readBuffer):
124        self.readBuffer = readBuffer
125        self.siblingClass.handle_read(self)
126
127    def outWriteEvent(self):
128        self.siblingClass.handle_write(self)
129
130    def recv(self, bufferSize=16384):
131        if bufferSize < 16384 or self.readBuffer == None:
132            raise AssertionError()
133        returnValue = self.readBuffer
134        self.readBuffer = None
135        return returnValue
136
137    def send(self, writeBuffer):
138        self.setWriteOp(writeBuffer)
139        return len(writeBuffer)
140
141    def close(self):
142        if hasattr(self, "tlsConnection"):
143            self.setCloseOp()
144        else:
145            asyncore.dispatcher.close(self)
146