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