1
# -*- test-case-name: twisted.test.test_amp.TLSTest -*-
2
# Copyright (c) 2008-2009 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
Utilities and helpers for simulating a network
12
from OpenSSL.SSL import Error as NativeOpenSSLError
16
from zope.interface import implements, directlyProvides
18
from twisted.python.failure import Failure
19
from twisted.internet import error
20
from twisted.internet import interfaces
23
def __init__(self, obj, connectState):
25
self.connectState = connectState
27
self.readyToSend = connectState
30
return 'TLSNegotiation(%r)' % (self.obj,)
32
def pretendToVerify(self, other, tpt):
33
# Set the transport problems list here? disconnections?
34
# hmmmmm... need some negative path tests.
36
if not self.obj.iosimVerify(other.obj):
37
tpt.disconnectReason = NativeOpenSSLError()
43
A wrapper around a file-like object to make it behave as a Transport.
45
This doesn't actually stream the file to the attached protocol,
46
and is thus useful mainly as a utility for debugging protocols.
48
implements(interfaces.ITransport,
49
interfaces.ITLSTransport) # ha ha not really
51
_nextserial = itertools.count().next
55
disconnectReason = error.ConnectionDone("Connection done")
62
self.serial = self._nextserial()
65
return 'FakeTransport<%s,%s,%s>' % (
66
self.isServer and 'S' or 'C', self.serial,
67
self.protocol.__class__.__name__)
69
def write(self, data):
70
if self.tls is not None:
71
self.tlsbuf.append(data)
73
self.stream.append(data)
75
def _checkProducer(self):
76
# Cheating; this is called at "idle" times to allow producers to be
77
# found and dealt with
79
self.producer.resumeProducing()
81
def registerProducer(self, producer, streaming):
82
"""From abstract.FileDescriptor
84
self.producer = producer
85
self.streamingProducer = streaming
87
producer.resumeProducing()
89
def unregisterProducer(self):
92
def stopConsuming(self):
93
self.unregisterProducer()
96
def writeSequence(self, iovec):
97
self.write("".join(iovec))
99
def loseConnection(self):
100
self.disconnecting = True
102
def reportDisconnect(self):
103
if self.tls is not None:
104
# We were in the middle of negotiating! Must have been a TLS problem.
105
err = NativeOpenSSLError()
107
err = self.disconnectReason
108
self.protocol.connectionLost(Failure(err))
111
# XXX: According to ITransport, this should return an IAddress!
112
return 'file', 'file'
115
# XXX: According to ITransport, this should return an IAddress!
118
def resumeProducing(self):
119
# Never sends data anyways
122
def pauseProducing(self):
123
# Never sends data anyways
126
def stopProducing(self):
127
self.loseConnection()
129
def startTLS(self, contextFactory, beNormal=True):
130
# Nothing's using this feature yet, but startTLS has an undocumented
131
# second argument which defaults to true; if set to False, servers will
132
# behave like clients and clients will behave like servers.
133
connectState = self.isServer ^ beNormal
134
self.tls = TLSNegotiation(contextFactory, connectState)
137
def getOutBuffer(self):
142
elif self.tls is not None:
143
if self.tls.readyToSend:
144
# Only _send_ the TLS negotiation "packet" if I'm ready to.
152
def bufferReceived(self, buf):
153
if isinstance(buf, TLSNegotiation):
154
assert self.tls is not None # By the time you're receiving a
155
# negotiation, you have to have called
158
self.tls.pretendToVerify(buf, self)
159
self.tls = None # we're done with the handshake if we've gotten
160
# this far... although maybe it failed...?
161
# TLS started! Unbuffer...
162
b, self.tlsbuf = self.tlsbuf, None
163
self.writeSequence(b)
164
directlyProvides(self, interfaces.ISSLTransport)
166
# We haven't sent our own TLS negotiation: time to do that!
167
self.tls.readyToSend = True
169
self.protocol.dataReceived(buf)
173
def makeFakeClient(c):
179
def makeFakeServer(s):
186
"""Utility to pump data between clients and servers for protocol testing.
188
Perhaps this is a utility worthy of being in protocol.py?
190
def __init__(self, client, server, clientIO, serverIO, debug):
193
self.clientIO = clientIO
194
self.serverIO = serverIO
197
def flush(self, debug=False):
198
"""Pump until there is no more input or output.
200
Returns whether any data was moved.
203
for x in range(1000):
213
def pump(self, debug=False):
214
"""Move data back and forth.
216
Returns whether any data was moved.
218
if self.debug or debug:
220
sData = self.serverIO.getOutBuffer()
221
cData = self.clientIO.getOutBuffer()
222
self.clientIO._checkProducer()
223
self.serverIO._checkProducer()
224
if self.debug or debug:
226
# XXX slightly buggy in the face of incremental output
228
print 'C: '+repr(cData)
230
print 'S: '+repr(sData)
232
self.serverIO.bufferReceived(cData)
234
self.clientIO.bufferReceived(sData)
237
if (self.serverIO.disconnecting and
238
not self.serverIO.disconnected):
239
if self.debug or debug:
241
self.serverIO.disconnected = True
242
self.clientIO.disconnecting = True
243
self.clientIO.reportDisconnect()
245
if self.clientIO.disconnecting and not self.clientIO.disconnected:
246
if self.debug or debug:
248
self.clientIO.disconnected = True
249
self.serverIO.disconnecting = True
250
self.serverIO.reportDisconnect()
255
def connectedServerAndClient(ServerClass, ClientClass,
256
clientTransportFactory=makeFakeClient,
257
serverTransportFactory=makeFakeServer,
259
"""Returns a 3-tuple: (client, server, pump)
263
cio = clientTransportFactory(c)
264
sio = serverTransportFactory(s)
265
c.makeConnection(cio)
266
s.makeConnection(sio)
267
pump = IOPump(c, s, cio, sio, debug)
268
# kick off server greeting, etc