1"""
2A state machine for using TLS Lite with asynchronous I/O.
3"""
4
5class AsyncStateMachine:
6    """
7    This is an abstract class that's used to integrate TLS Lite with
8    asyncore and Twisted.
9
10    This class signals wantsReadsEvent() and wantsWriteEvent().  When
11    the underlying socket has become readable or writeable, the event
12    should be passed to this class by calling inReadEvent() or
13    inWriteEvent().  This class will then try to read or write through
14    the socket, and will update its state appropriately.
15
16    This class will forward higher-level events to its subclass.  For
17    example, when a complete TLS record has been received,
18    outReadEvent() will be called with the decrypted data.
19    """
20
21    def __init__(self):
22        self._clear()
23
24    def _clear(self):
25        #These store the various asynchronous operations (i.e.
26        #generators).  Only one of them, at most, is ever active at a
27        #time.
28        self.handshaker = None
29        self.closer = None
30        self.reader = None
31        self.writer = None
32
33        #This stores the result from the last call to the
34        #currently active operation.  If 0 it indicates that the
35        #operation wants to read, if 1 it indicates that the
36        #operation wants to write.  If None, there is no active
37        #operation.
38        self.result = None
39
40    def _checkAssert(self, maxActive=1):
41        #This checks that only one operation, at most, is
42        #active, and that self.result is set appropriately.
43        activeOps = 0
44        if self.handshaker:
45            activeOps += 1
46        if self.closer:
47            activeOps += 1
48        if self.reader:
49            activeOps += 1
50        if self.writer:
51            activeOps += 1
52
53        if self.result == None:
54            if activeOps != 0:
55                raise AssertionError()
56        elif self.result in (0,1):
57            if activeOps != 1:
58                raise AssertionError()
59        else:
60            raise AssertionError()
61        if activeOps > maxActive:
62            raise AssertionError()
63
64    def wantsReadEvent(self):
65        """If the state machine wants to read.
66
67        If an operation is active, this returns whether or not the
68        operation wants to read from the socket.  If an operation is
69        not active, this returns None.
70
71        @rtype: bool or None
72        @return: If the state machine wants to read.
73        """
74        if self.result != None:
75            return self.result == 0
76        return None
77
78    def wantsWriteEvent(self):
79        """If the state machine wants to write.
80
81        If an operation is active, this returns whether or not the
82        operation wants to write to the socket.  If an operation is
83        not active, this returns None.
84
85        @rtype: bool or None
86        @return: If the state machine wants to write.
87        """
88        if self.result != None:
89            return self.result == 1
90        return None
91
92    def outConnectEvent(self):
93        """Called when a handshake operation completes.
94
95        May be overridden in subclass.
96        """
97        pass
98
99    def outCloseEvent(self):
100        """Called when a close operation completes.
101
102        May be overridden in subclass.
103        """
104        pass
105
106    def outReadEvent(self, readBuffer):
107        """Called when a read operation completes.
108
109        May be overridden in subclass."""
110        pass
111
112    def outWriteEvent(self):
113        """Called when a write operation completes.
114
115        May be overridden in subclass."""
116        pass
117
118    def inReadEvent(self):
119        """Tell the state machine it can read from the socket."""
120        try:
121            self._checkAssert()
122            if self.handshaker:
123                self._doHandshakeOp()
124            elif self.closer:
125                self._doCloseOp()
126            elif self.reader:
127                self._doReadOp()
128            elif self.writer:
129                self._doWriteOp()
130            else:
131                self.reader = self.tlsConnection.readAsync(16384)
132                self._doReadOp()
133        except:
134            self._clear()
135            raise
136
137    def inWriteEvent(self):
138        """Tell the state machine it can write to the socket."""
139        try:
140            self._checkAssert()
141            if self.handshaker:
142                self._doHandshakeOp()
143            elif self.closer:
144                self._doCloseOp()
145            elif self.reader:
146                self._doReadOp()
147            elif self.writer:
148                self._doWriteOp()
149            else:
150                self.outWriteEvent()
151        except:
152            self._clear()
153            raise
154
155    def _doHandshakeOp(self):
156        try:
157            self.result = self.handshaker.next()
158        except StopIteration:
159            self.handshaker = None
160            self.result = None
161            self.outConnectEvent()
162
163    def _doCloseOp(self):
164        try:
165            self.result = self.closer.next()
166        except StopIteration:
167            self.closer = None
168            self.result = None
169            self.outCloseEvent()
170
171    def _doReadOp(self):
172        self.result = self.reader.next()
173        if not self.result in (0,1):
174            readBuffer = self.result
175            self.reader = None
176            self.result = None
177            self.outReadEvent(readBuffer)
178
179    def _doWriteOp(self):
180        try:
181            self.result = self.writer.next()
182        except StopIteration:
183            self.writer = None
184            self.result = None
185
186    def setHandshakeOp(self, handshaker):
187        """Start a handshake operation.
188
189        @type handshaker: generator
190        @param handshaker: A generator created by using one of the
191        asynchronous handshake functions (i.e. handshakeServerAsync, or
192        handshakeClientxxx(..., async=True).
193        """
194        try:
195            self._checkAssert(0)
196            self.handshaker = handshaker
197            self._doHandshakeOp()
198        except:
199            self._clear()
200            raise
201
202    def setServerHandshakeOp(self, **args):
203        """Start a handshake operation.
204
205        The arguments passed to this function will be forwarded to
206        L{tlslite.TLSConnection.TLSConnection.handshakeServerAsync}.
207        """
208        handshaker = self.tlsConnection.handshakeServerAsync(**args)
209        self.setHandshakeOp(handshaker)
210
211    def setCloseOp(self):
212        """Start a close operation.
213        """
214        try:
215            self._checkAssert(0)
216            self.closer = self.tlsConnection.closeAsync()
217            self._doCloseOp()
218        except:
219            self._clear()
220            raise
221
222    def setWriteOp(self, writeBuffer):
223        """Start a write operation.
224
225        @type writeBuffer: str
226        @param writeBuffer: The string to transmit.
227        """
228        try:
229            self._checkAssert(0)
230            self.writer = self.tlsConnection.writeAsync(writeBuffer)
231            self._doWriteOp()
232        except:
233            self._clear()
234            raise
235
236