Merge lp:~cyli/divmod.org/remove-vertex into lp:divmod.org

Proposed by cyli
Status: Merged
Approved by: Allen Short
Approved revision: 2702
Merged at revision: 2702
Proposed branch: lp:~cyli/divmod.org/remove-vertex
Merge into: lp:divmod.org
Diff against target: 9122 lines (+1/-8893)
41 files modified
Divmod.pth (+1/-2)
Vertex/DEPS.txt (+0/-5)
Vertex/LICENSE (+0/-20)
Vertex/MANIFEST.in (+0/-4)
Vertex/NAME.txt (+0/-10)
Vertex/NEWS.txt (+0/-20)
Vertex/README.txt (+0/-12)
Vertex/bin/gvertex (+0/-7)
Vertex/bin/vertex (+0/-7)
Vertex/doc/notes (+0/-61)
Vertex/doc/q2q-standalone.tac (+0/-5)
Vertex/prime/plugins/vertex_client.py (+0/-4)
Vertex/setup.py (+0/-31)
Vertex/vertex/__init__.py (+0/-3)
Vertex/vertex/_version.py (+0/-3)
Vertex/vertex/bits.py (+0/-142)
Vertex/vertex/conncache.py (+0/-160)
Vertex/vertex/depserv.py (+0/-197)
Vertex/vertex/endpoint.py (+0/-56)
Vertex/vertex/gtk2hack.glade (+0/-635)
Vertex/vertex/gtk2hack.py (+0/-270)
Vertex/vertex/ivertex.py (+0/-108)
Vertex/vertex/ptcp.py (+0/-1050)
Vertex/vertex/q2q.py (+0/-2763)
Vertex/vertex/q2qadmin.py (+0/-21)
Vertex/vertex/q2qclient.py (+0/-453)
Vertex/vertex/q2qstandalone.py (+0/-108)
Vertex/vertex/sigma.py (+0/-760)
Vertex/vertex/statemachine.py (+0/-56)
Vertex/vertex/subproducer.py (+0/-125)
Vertex/vertex/tcpdfa.py (+0/-96)
Vertex/vertex/test/__init__.py (+0/-3)
Vertex/vertex/test/mock_data.py (+0/-2)
Vertex/vertex/test/test_bits.py (+0/-68)
Vertex/vertex/test/test_client.py (+0/-30)
Vertex/vertex/test/test_dependencyservice.py (+0/-69)
Vertex/vertex/test/test_ptcp.py (+0/-365)
Vertex/vertex/test/test_q2q.py (+0/-712)
Vertex/vertex/test/test_sigma.py (+0/-210)
Vertex/vertex/test/test_statemachine.py (+0/-67)
Vertex/vertex/test/test_subproducer.py (+0/-173)
To merge this branch: bzr merge lp:~cyli/divmod.org/remove-vertex
Reviewer Review Type Date Requested Status
Allen Short Approve
Review via email: mp+171717@code.launchpad.net

Description of the change

Proposing to remove Vertex from the divmod repos. I found only one branch with a merge proposal for it:

https://code.launchpad.net/~alfred-54/divmod.org/divmod.org/+merge/158833

I've ported trunk as well as alfred-54's branch to https://github.com/cyli/vertex, which I will then transfer to the twistedmatrix organization after/if this branch is merged into divmod.org repo.

To post a comment you must log in.
Revision history for this message
Allen Short (washort) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Divmod.pth'
2--- Divmod.pth 2013-01-02 10:08:46 +0000
3+++ Divmod.pth 2013-06-27 06:07:28 +0000
4@@ -1,4 +1,4 @@
5-# -*- test-case-name: axiom,combinator,epsilon,xmantissa,nevow,formless,xquotient,reverend,sine,vertex,hyperbola,imaginary,examplegame -*-
6+# -*- test-case-name: axiom,combinator,epsilon,xmantissa,nevow,formless,xquotient,reverend,sine,hyperbola,imaginary,examplegame -*-
7 Axiom
8 Combinator
9 Epsilon
10@@ -7,7 +7,6 @@
11 Quotient
12 Reverend
13 Sine
14-Vertex
15 Hyperbola
16 Imaginary
17 Imaginary/ExampleGame
18
19=== removed directory 'Vertex'
20=== removed file 'Vertex/DEPS.txt'
21--- Vertex/DEPS.txt 2006-06-14 11:54:41 +0000
22+++ Vertex/DEPS.txt 1970-01-01 00:00:00 +0000
23@@ -1,5 +0,0 @@
24-Python 2.4
25-Twisted 2.4.0
26-PyOpenSSL 0.6
27-OpenSSL 0.9.7
28-Epsilon 0.5.0
29
30=== removed file 'Vertex/LICENSE'
31--- Vertex/LICENSE 2005-12-10 22:31:51 +0000
32+++ Vertex/LICENSE 1970-01-01 00:00:00 +0000
33@@ -1,20 +0,0 @@
34-Copyright (c) 2005 Divmod Inc.
35-
36-Permission is hereby granted, free of charge, to any person obtaining
37-a copy of this software and associated documentation files (the
38-"Software"), to deal in the Software without restriction, including
39-without limitation the rights to use, copy, modify, merge, publish,
40-distribute, sublicense, and/or sell copies of the Software, and to
41-permit persons to whom the Software is furnished to do so, subject to
42-the following conditions:
43-
44-The above copyright notice and this permission notice shall be
45-included in all copies or substantial portions of the Software.
46-
47-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
48-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
49-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
50-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
51-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
52-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
53-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
54\ No newline at end of file
55
56=== removed file 'Vertex/MANIFEST.in'
57--- Vertex/MANIFEST.in 2006-06-14 11:54:41 +0000
58+++ Vertex/MANIFEST.in 1970-01-01 00:00:00 +0000
59@@ -1,4 +0,0 @@
60-include LICENSE
61-include NAME.txt
62-include DEPS.txt
63-include doc/q2q-standalone.tac
64
65=== removed file 'Vertex/NAME.txt'
66--- Vertex/NAME.txt 2005-08-27 23:09:07 +0000
67+++ Vertex/NAME.txt 1970-01-01 00:00:00 +0000
68@@ -1,10 +0,0 @@
69-
70-See: http://mathworld.wolfram.com/Vertex.html
71-
72-A vertex in mathematics is a location where two or more lines or edges meet.
73-
74-Divmod Vertex is an implementation of and interface to the Q2Q protocol. It is
75-named for a vertext because it provides a way for peers on the internet to
76-establish connections with each other, e.g. to make their connection lines
77-meet, regardless of intermediary interference such as network address
78-translators and lack of naming information.
79
80=== removed file 'Vertex/NEWS.txt'
81--- Vertex/NEWS.txt 2009-11-30 01:08:55 +0000
82+++ Vertex/NEWS.txt 1970-01-01 00:00:00 +0000
83@@ -1,20 +0,0 @@
84-0.3.0 (2009-11-25):
85- - Remove use of deprecated Twisted APIs from the test suite and improve
86- some error handling as a necessary consequence.
87- - Use twisted.internet.ssl instead of epsilon.sslverify.
88- - Remove an implementation of deferLater.
89-
90-0.2.0 (2006-06-12):
91- - Moved JUICE implementation into Epsilon.
92- - Removed dependency on Nevow's formless.
93- - Clarify licensing terms.
94- - Fix bugs on 64-bit platforms.
95- - removed buggy legacy non-TLS options which would break negotiation with
96- OpenSSL 0.9.8a.
97- - Deprecated twisted test APIs removed.
98- - First phase of integration with twisted.cred; vertex endpoints can now be
99- authenticated against a Twisted UsernamePassword cred authenticator.
100-
101-0.1.0 (2005-10-10):
102-
103- - Initial release.
104
105=== removed file 'Vertex/README.txt'
106--- Vertex/README.txt 2006-06-14 11:54:41 +0000
107+++ Vertex/README.txt 1970-01-01 00:00:00 +0000
108@@ -1,12 +0,0 @@
109-
110-Divmod Vertex
111-=============
112-
113-Divmod Vertex is the first implementation of the Q2Q protocol, which is a
114-peer-to-peer communication protocol for establishing stream-based communication
115-between named endpoints.
116-
117-It is also a P2P application client and server platform in the early stages of
118-development. It is currently quite usable for knocking holes in firewalls, but
119-requires some polish to really be usable.
120-
121
122=== removed directory 'Vertex/bin'
123=== removed file 'Vertex/bin/gvertex'
124--- Vertex/bin/gvertex 2006-03-08 04:10:37 +0000
125+++ Vertex/bin/gvertex 1970-01-01 00:00:00 +0000
126@@ -1,7 +0,0 @@
127-#!/usr/bin/python
128-# Copyright 2005 Divmod, Inc. See LICENSE file for details
129-
130-from vertex.gtk2hack import main
131-
132-if __name__ == '__main__':
133- main()
134
135=== removed file 'Vertex/bin/vertex'
136--- Vertex/bin/vertex 2005-08-10 22:20:03 +0000
137+++ Vertex/bin/vertex 1970-01-01 00:00:00 +0000
138@@ -1,7 +0,0 @@
139-#!/usr/bin/python
140-# Copyright 2005 Divmod, Inc. See LICENSE file for details
141-
142-from vertex.q2qclient import Q2QClientProgram
143-
144-if __name__ == '__main__':
145- Q2QClientProgram().parseOptions()
146
147=== removed directory 'Vertex/doc'
148=== removed file 'Vertex/doc/notes'
149--- Vertex/doc/notes 2005-08-05 03:13:02 +0000
150+++ Vertex/doc/notes 1970-01-01 00:00:00 +0000
151@@ -1,61 +0,0 @@
152-
153-Gin
154-===
155-
156-TCP-alike over UDP
157-
158-Packet Format
159-=============
160-
161-Connection ID - 32 bits
162-Sequence Number - 32 bits
163-Ack number - 32 bits
164-Window - 32 bits
165-Flags (SYN, ACK, FIN, IGN) - 8 bits
166-Checksum - 32 bits
167-Data length in bytes - 16 bits
168-Data - See previous
169-
170-SEMANTICS
171-=========
172-Different types of packets:
173-
174-just SYN:
175-SYN+ACK:
176-just ACK: normal tcp packet meanings
177-
178-SYN+NAT: Request for information about the sender's public address
179-
180-ACK+NAT: Response with information about the recipient's public address. The
181-data is the IP address and port number to which the packet is being sent,
182-formatted as a dotted-quad formatted IP address followed by a colon followed by
183-the base-10 string representation of the port number..
184-
185-STB: Size Too Big - a packet with a dlen greater than the length of its data
186-was received.
187-
188-Other Stuff:
189-
190-Connection IDs uniquely identify a stream for a protocol. All bytes
191-associated with a particular connectionID will be delivered to the
192-same protocol instance.
193-
194-Sequence Numbers indicate the senders notion of how far into its
195-outgoing stream this packet is. Sequence numbers start from a
196-pseudo-random value within the allowed range and are incremented by
197-the number of bytes in each packet transmitted (re-transmits
198-discounted). This indicates to the peer where the bytes in each
199-packet lie in the stream, allowing ordered delivery.
200-
201-Ack numbers indicate the senders notion of how far into its incoming
202-stream all data has been received. This value is always what the
203-sender expects the receiver to use as its next sequence number.
204-
205-Window indicates the number of bytes in advance of the senders ack
206-number the receiver may proceed in sending. This receiver's sequence
207-numbers must never be greater than the sender's last ack number plus
208-their window number.
209-
210-Checksum is a CRC 32 of the data (and only the data - wait maybe this
211-should be the header less the checksum, too, in case things get
212-corrupted there)
213
214=== removed file 'Vertex/doc/q2q-standalone.tac'
215--- Vertex/doc/q2q-standalone.tac 2005-08-10 22:20:03 +0000
216+++ Vertex/doc/q2q-standalone.tac 1970-01-01 00:00:00 +0000
217@@ -1,5 +0,0 @@
218-# Copyright 2005 Divmod, Inc. See LICENSE file for details
219-from vertex.q2qstandalone import defaultConfig
220-
221-application = defaultConfig()
222-
223
224=== removed directory 'Vertex/prime'
225=== removed directory 'Vertex/prime/plugins'
226=== removed file 'Vertex/prime/plugins/vertex_client.py'
227--- Vertex/prime/plugins/vertex_client.py 2006-06-01 04:57:02 +0000
228+++ Vertex/prime/plugins/vertex_client.py 1970-01-01 00:00:00 +0000
229@@ -1,4 +0,0 @@
230-
231-from vertex.gtk2hack import PlugEntry
232-
233-pe = PlugEntry()
234
235=== removed file 'Vertex/setup.py'
236--- Vertex/setup.py 2009-11-30 01:08:55 +0000
237+++ Vertex/setup.py 1970-01-01 00:00:00 +0000
238@@ -1,31 +0,0 @@
239-from epsilon import setuphelper
240-
241-from vertex import version
242-
243-setuphelper.autosetup(
244- name="Vertex",
245- version=version.short(),
246- maintainer="Divmod, Inc.",
247- maintainer_email="support@divmod.org",
248- url="http://divmod.org/trac/wiki/DivmodVertex",
249- license="MIT",
250- platforms=["any"],
251- description=
252- """
253- Divmod Vertex is the first implementation of the Q2Q protocol, which
254- is a peer-to-peer communication protocol for establishing
255- stream-based communication between named endpoints.
256- """,
257- classifiers=[
258- "Development Status :: 2 - Pre-Alpha",
259- "Framework :: Twisted",
260- "Intended Audience :: Developers",
261- "License :: OSI Approved :: MIT License",
262- "Programming Language :: Python",
263- "Topic :: Communications",
264- "Topic :: Internet",
265- "Topic :: Internet :: File Transfer Protocol (FTP)",
266- "Topic :: Internet :: Name Service (DNS)",
267- "Topic :: Software Development :: Libraries :: Python Modules",
268- ],
269- )
270
271=== removed directory 'Vertex/vertex'
272=== removed file 'Vertex/vertex/__init__.py'
273--- Vertex/vertex/__init__.py 2008-08-11 11:19:59 +0000
274+++ Vertex/vertex/__init__.py 1970-01-01 00:00:00 +0000
275@@ -1,3 +0,0 @@
276-# -*- test-case-name: vertex.test -*-
277-
278-from vertex._version import version
279
280=== removed file 'Vertex/vertex/_version.py'
281--- Vertex/vertex/_version.py 2009-11-30 01:08:55 +0000
282+++ Vertex/vertex/_version.py 1970-01-01 00:00:00 +0000
283@@ -1,3 +0,0 @@
284-# This is an auto-generated file. Use admin/change-versions to update.
285-from twisted.python import versions
286-version = versions.Version(__name__[:__name__.rfind('.')], 0, 3, 0)
287
288=== removed file 'Vertex/vertex/bits.py'
289--- Vertex/vertex/bits.py 2005-08-05 06:02:56 +0000
290+++ Vertex/vertex/bits.py 1970-01-01 00:00:00 +0000
291@@ -1,142 +0,0 @@
292-# Copyright 2005 Divmod, Inc. See LICENSE file for details
293-# -*- test-case-name: vertex.test.test_bits -*-
294-""" The purpose of this module is to provide the class BitArray, a compact
295-overlay onto an array of bytes which is instead bit-addressable. It also
296-includes several bitwise operators.
297-
298-It does not include all array operations yet, most notably those related to
299-slicing, since it is written primarily for use by the swarming implementation
300-and swarming only requires fixed-size bit masks.
301-
302-"""
303-
304-__metaclass__ = type
305-
306-import array
307-import operator
308-import math
309-
310-BITS_PER_BYTE = 8
311-
312-def operate(operation):
313- # XXX TODO: optimize this and countbits later
314- def __x__(self, other):
315- if len(self) < len(other):
316- return operation(other, self)
317- new = BitArray(size=len(self))
318- for offt, (mybit, hisbit) in enumerate(zip(self, other)):
319- result = new[offt] = operation(mybit, hisbit)
320-
321- for j in range(offt+1, len(self)):
322- new[j] = operation(self[j], 0)
323- return new
324- return __x__
325-
326-
327-class BitArray:
328- """
329- A large mutable array of bits.
330- """
331-
332- def __init__(self, bytes=None, size=None, default=0):
333- if bytes is None and size is None:
334- size = 0
335- if bytes is None:
336- bytes = array.array("B")
337- bytesize = int(math.ceil(float(size) / BITS_PER_BYTE))
338- if default:
339- padbyte = 255
340- else:
341- padbyte = 0
342- bytes.fromlist([padbyte] * bytesize)
343- self.bytes = bytes
344- if size is None:
345- size = len(self.bytes) * self.bytes.itemsize * BITS_PER_BYTE
346- self.size = size
347-
348- # initialize 'on' and 'off' lists to optimize various things
349- self.on = []
350- self.off = []
351- blists = self.blists = self.off, self.on
352-
353- for index, bit in enumerate(self):
354- blists[bit].append(index)
355-
356- def append(self, bit):
357- offt = self.size
358- self.size += 1
359- if (len(self.bytes) * self.bytes.itemsize * BITS_PER_BYTE) < self.size:
360- self.bytes.append(0)
361- self[offt] = bit
362-
363- def any(self, req=1):
364- return bool(self.blists[req])
365-
366- def percent(self):
367- """
368- debugging method; returns a string indicating percentage completion
369- """
370- if not len(self):
371- return 'Inf%'
372- return '%0.2f%%'% ((float(self.countbits()) / len(self)) * 100,)
373-
374- def __getitem__(self, bitcount):
375- if bitcount < 0:
376- bitcount += self.size
377- if bitcount >= self.size:
378- raise IndexError("%r >= %r" % (bitcount, self.size))
379- div, mod = divmod(bitcount, self.bytes.itemsize * BITS_PER_BYTE)
380- byte = self.bytes[div]
381- return (byte >> mod) & 1
382-
383- def __setitem__(self, bitcount, bit):
384- if bitcount < 0:
385- bitcount += self.size
386- if bitcount >= self.size:
387- raise IndexError("bitcount too big")
388- div, mod = divmod(bitcount, self.bytes.itemsize * BITS_PER_BYTE)
389- if bit:
390- self.bytes[div] |= 1 << mod
391- else:
392- self.bytes[div] &= ~(1 << mod)
393-
394- # change updating
395- notbitlist = self.blists[not bit]
396- try:
397- notbitlist.remove(bitcount)
398- except ValueError:
399- pass
400- bitlist = self.blists[bit]
401- if bitcount not in bitlist:
402- bitlist.append(bitcount)
403-
404- def __len__(self):
405- return self.size
406-
407- def __repr__(self):
408- l = []
409- l.append('[')
410- for b in self:
411- if b:
412- c = 'X'
413- else:
414- c = ' '
415- l.append(c)
416- l.append(']')
417- return ''.join(l)
418-
419- def countbits(self, on=True):
420- return len(self.blists[on])
421-
422- def positions(self, bit):
423- """
424- An iterator of all positions that a bit holds in this BitArray.
425-
426- @param bit: 1 or 0
427- """
428- return self.blists[bit][:]
429-
430- __xor__ = operate(operator.xor)
431- __and__ = operate(operator.and_)
432- __or__ = operate(operator.or_)
433-
434
435=== removed file 'Vertex/vertex/conncache.py'
436--- Vertex/vertex/conncache.py 2009-07-06 11:40:18 +0000
437+++ Vertex/vertex/conncache.py 1970-01-01 00:00:00 +0000
438@@ -1,160 +0,0 @@
439-# Copyright 2005 Divmod, Inc. See LICENSE file for details
440-# -*- test-case-name: vertex.test.test_q2q.TCPConnection.testSendingFiles -*-
441-
442-"""
443-Connect between two endpoints using a message-based protocol to exchange
444-messages lazily in response to UI events, caching the protocol as necessary.
445-Using connection-oriented protocols, you will most likely not want to use this
446-class - you might end up retrieving a cached connection in the middle of a
447-chunk of data being sent. For the purposes of this distinction, a
448-'message-oriented' protocol is one which has an API which either::
449-
450- a) writes only whole messages to its transport so there is never an
451- opportunity to insert data into the middle of a message, or
452-
453- b) provides an API on the Protocol instance for queuing whole messages such
454- that if partial messages are sent, calling the API multiple times will
455- queue them internally so that clients do not need to care whether the
456- connection is made or not.
457-
458-It is worth noting that all Juice-derived protocols meet constraint (b).
459-"""
460-
461-from zope.interface import implements
462-
463-from twisted.internet.defer import maybeDeferred, DeferredList, Deferred
464-from twisted.internet.main import CONNECTION_LOST
465-from twisted.internet import interfaces
466-from twisted.internet.protocol import ClientFactory
467-
468-
469-class ConnectionCache:
470- def __init__(self):
471- """
472- """
473- # map (fromAddress, toAddress, protoName): protocol instance
474- self.cachedConnections = {}
475- # map (fromAddress, toAddress, protoName): list of Deferreds
476- self.inProgress = {}
477-
478- def connectCached(self, endpoint, protocolFactory,
479- extraWork=lambda x: x,
480- extraHash=None):
481- """See module docstring
482- """
483- key = endpoint, extraHash
484- D = Deferred()
485- if key in self.cachedConnections:
486- D.callback(self.cachedConnections[key])
487- elif key in self.inProgress:
488- self.inProgress[key].append(D)
489- else:
490- self.inProgress[key] = [D]
491- endpoint.connect(
492- _CachingClientFactory(
493- self, key, protocolFactory,
494- extraWork))
495- return D
496-
497- def cacheUnrequested(self, endpoint, extraHash, protocol):
498- self.connectionMadeForKey((endpoint, extraHash), protocol)
499-
500- def connectionMadeForKey(self, key, protocol):
501- deferreds = self.inProgress.pop(key, [])
502- self.cachedConnections[key] = protocol
503- for d in deferreds:
504- d.callback(protocol)
505-
506- def connectionLostForKey(self, key):
507- if key in self.cachedConnections:
508- del self.cachedConnections[key]
509-
510- def connectionFailedForKey(self, key, reason):
511- deferreds = self.inProgress.pop(key)
512- for d in deferreds:
513- d.errback(reason)
514-
515- def shutdown(self):
516- return DeferredList(
517- [maybeDeferred(p.transport.loseConnection)
518- for p in self.cachedConnections.values()])
519-
520-
521-class _CachingClientFactory(ClientFactory):
522- debug = False
523-
524- def __init__(self, cache, key, subFactory, extraWork):
525- """
526- @param cache: a Q2QService
527-
528- @param key: a 2-tuple of (endpoint, extra) that represents what
529- connections coming from this factory are for.
530-
531- @param subFactory: a ClientFactory which I forward methods to.
532-
533- @param extraWork: extraWork(proto) -> Deferred which fires when the
534- connection has been prepared sufficiently to be used by subsequent
535- connections and can be counted as a success.
536- """
537-
538- self.cache = cache
539- self.key = key
540- self.subFactory = subFactory
541- self.finishedExtraWork = False
542- self.extraWork = extraWork
543-
544- lostAsFailReason = CONNECTION_LOST
545-
546- def clientConnectionMade(self, protocol):
547- def success(reason):
548- self.cache.connectionMadeForKey(self.key, protocol)
549- self.finishedExtraWork = True
550- return protocol
551-
552- def failed(reason):
553- self.lostAsFailReason = reason
554- protocol.transport.loseConnection()
555- return reason
556- maybeDeferred(self.extraWork, protocol).addCallbacks(
557- success, failed)
558-
559- def clientConnectionLost(self, connector, reason):
560- if self.finishedExtraWork:
561- self.cache.connectionLostForKey(self.key)
562- else:
563- self.cache.connectionFailedForKey(self.key,
564- self.lostAsFailReason)
565- self.subFactory.clientConnectionLost(connector, reason)
566-
567- def clientConnectionFailed(self, connector, reason):
568- self.cache.connectionFailedForKey(self.key, reason)
569- self.subFactory.clientConnectionFailed(connector, reason)
570-
571- def buildProtocol(self, addr):
572- return _CachingTransportShim(self, self.subFactory.buildProtocol(addr))
573-
574-
575-class _CachingTransportShim:
576- disconnecting = property(lambda self: self.transport.disconnecting)
577-
578- implements(interfaces.IProtocol)
579-
580- def __init__(self, factory, protocol):
581- self.factory = factory
582- self.protocol = protocol
583-
584- # IProtocol
585- self.dataReceived = protocol.dataReceived
586- self.connectionLost = protocol.connectionLost
587-
588-
589- def makeConnection(self, transport):
590- self.transport = transport
591- self.protocol.makeConnection(transport)
592- self.factory.clientConnectionMade(self.protocol)
593-
594-
595- def __repr__(self):
596- return 'Q2Q-Cached<%r, %r>' % (self.transport,
597- self.protocol)
598-
599
600=== removed file 'Vertex/vertex/depserv.py'
601--- Vertex/vertex/depserv.py 2009-07-06 11:40:18 +0000
602+++ Vertex/vertex/depserv.py 1970-01-01 00:00:00 +0000
603@@ -1,197 +0,0 @@
604-# Copyright 2005 Divmod, Inc. See LICENSE file for details
605-
606-"""
607-This module is no longer supported for use outside Vertex.
608-"""
609-
610-from twisted.python import log
611-from sets import Set
612-from twisted.persisted import sob
613-from twisted.application import service, internet
614-
615-from zope.interface import implements
616-
617-class Conf(dict):
618- """A class to help in construction the configuration for delpoy().
619-
620- Typical usage::
621-
622- from vertex.depserv import Conf
623- conf = Conf()
624- s = conf.section
625- s('pop',
626- port = 110,
627- sslPort = 995)
628- ...
629- """
630- def section(self, name, **kw):
631- self.setdefault(name, {}).update(kw)
632-
633-
634-class NotPersistable:
635- implements(sob.IPersistable)
636- def __init__(self, original):
637- self.original = original
638-
639- def setStyle(self, style):
640- self.style = style
641-
642- def save(self, tag=None, filename=None, passphrase=None):
643- pass
644-
645-
646-class StartupError(Exception):
647- pass
648-
649-
650-class DependencyService(service.MultiService):
651- """A MultiService that can start multiple services with interdependencies.
652-
653- Each keyword parameter is a dict which serves as the options for that
654- service.
655-
656- Each service defines a method setup_SERVICE, which is called with the
657- matching parameters (the service name must be all caps). If there is no key
658- for SERVICE in the class parameters, the setup method is not called. The
659- return value is ignored, and DependencyService makes no assumptions about
660- any side effects.
661-
662- Each service may also optionally define depends_SERVICE which is called
663- before the setup method with the same parameters as the setup method. This
664- method returns a list of names of services on which SERVICE depends.
665- DependencyService will then initialize the service is the correct order. If
666- circular dependencies result, or a service depends on another service which
667- does not exist or is not configured to run, StartupError is raised.
668-
669- The class can define required services by setting 'requiredServices' to a
670- list of service names. These services will be initialized first in the
671- order they appear in the list, ignoring all dependency information. If
672- there are no parameters for a required service (consequently, the setup
673- method would not normally be called), StartupError is raised.
674- """
675-
676-
677- requiredServices = []
678-
679-
680- def __init__(self, **kw):
681- service.MultiService.__init__(self)
682-
683- # this makes it possible for one service to change the configuration of
684- # another. Avoid if possible, there if you need it. Be sure to properly
685- # set the dependencies.
686- self.config = kw
687- self.servers = []
688-
689- services = kw.keys()
690- initedServices = Set()
691- uninitedServices = Set(services)
692-
693- # build dependencies
694- dependencies = {}
695- for serv in services:
696- try:
697- dependMethod = self._getDependsMethod(serv)
698- except AttributeError:
699- continue
700- dependencies[serv] = dependMethod(**kw[serv])
701-
702- def initializeService(svc):
703- self._getServiceMethod(svc)(**kw[svc])
704- initedServices.add(svc)
705- uninitedServices.remove(svc)
706-
707- for svc in self.requiredServices:
708- if dependencies.get(svc):
709- raise StartupError(
710- '%r is a required service but has unsatisfied '
711- 'dependency on %r' % (svc, dependencies[svc]))
712- initializeService(svc)
713-
714- while uninitedServices:
715- # iterate over the uninitialized services, adding those with no
716- # outstanding dependencies to initThisRound.
717- initThisRound = []
718- for serv in uninitedServices:
719- for dep in dependencies.get(serv, []):
720- if dep not in initedServices:
721- if dep not in uninitedServices:
722- raise StartupError(
723- 'service %r depends on service %r, which is not '
724- 'configured or does not exist.' % (serv, dep))
725- break
726- else:
727- initThisRound.append(serv)
728- if not initThisRound:
729- raise StartupError(
730- 'Can not initialize all services. Circular dependencies '
731- 'between setup methods?')
732- for svc in initThisRound:
733- initializeService(svc)
734-
735-
736- def _getServiceMethod(self, service):
737- return getattr(self, 'setup_%s' % (service.upper(),))
738-
739-
740- def _getDependsMethod(self, service):
741- return getattr(self, 'depends_%s' % (service.upper(),))
742-
743-
744- def deploy(Class, name=None, uid=None, gid=None, **kw):
745- """Create an application with the give name, uid, and gid.
746-
747- The application has one child service, an instance of Class
748- configured based on the additional keyword arguments passed.
749-
750- The application is not persistable.
751- """
752- svc = Class(**kw)
753-
754- if name is None:
755- name = Class.__name__
756- # Make it easier (possible) to find this service by name later on
757- svc.setName(name)
758-
759- app = service.Application(name, uid=uid, gid=gid)
760- app.addComponent(NotPersistable(app), ignoreClass=True)
761- svc.setServiceParent(app)
762-
763- return app
764- deploy = classmethod(deploy)
765-
766- def attach(self, subservice):
767- subservice.setServiceParent(self)
768- return subservice
769-
770- def detach(self, subservice):
771- subservice.disownServiceParent()
772-
773- def addServer(self, normalPort, sslPort, f, name):
774- """Add a TCP and an SSL server. Name them `name` and `name`+'s'."""
775- tcp = internet.TCPServer(normalPort,f)
776- tcp.setName(name)
777- self.servers.append(tcp)
778- if sslPort is not None:
779- ssl = internet.SSLServer(sslPort, f, contextFactory=self.sslfac)
780- ssl.setName(name+'s')
781- self.servers.append(ssl)
782-
783- def discernPrivilegedServers(self):
784- return [srv for srv in self.servers if srv.args[0] <= 1024]
785-
786- def discernUnprivilegedServers(self):
787- return [srv for srv in self.servers if srv.args[0] > 1024]
788-
789- def privilegedStartService(self):
790- for server in self.discernPrivilegedServers():
791- log.msg("privileged attach %r" % server)
792- self.attach(server)
793- return service.MultiService.privilegedStartService(self)
794-
795- def startService(self):
796- for server in self.discernUnprivilegedServers():
797- log.msg("attaching %r" % server)
798- self.attach(server)
799-
800- return service.MultiService.startService(self)
801
802=== removed file 'Vertex/vertex/endpoint.py'
803--- Vertex/vertex/endpoint.py 2005-08-05 06:02:56 +0000
804+++ Vertex/vertex/endpoint.py 1970-01-01 00:00:00 +0000
805@@ -1,56 +0,0 @@
806-# Copyright 2005 Divmod, Inc. See LICENSE file for details
807-
808-def stablesort(self, other):
809- return cmp(self.__class__, getattr(other, '__class__', type(other)))
810-
811-class TCPEndpoint:
812- def __init__(self, host, port):
813- self.host = host
814- self.port = port
815-
816- def __hash__(self):
817- return hash((self.host, self.port)) + 5
818-
819- def connect(self, protocolFactory):
820- from twisted.internet import reactor
821- return reactor.connectTCP(self.host, self.port, protocolFactory)
822-
823- def __repr__(self):
824- return '<TCP@%s,%d>' % (self.host, self.port)
825-
826- def __cmp__(self, other):
827- if isinstance(other, TCPEndpoint):
828- return cmp((self.host, self.port),
829- (other.host, other.port))
830- return stablesort(self, other)
831-
832-
833-class Q2QEndpoint:
834- def __init__(self, service, fromAddress, toAddress, protocolName):
835- self.service = service
836- self.fromAddress = fromAddress
837- self.toAddress = toAddress
838- self.protocolName = protocolName
839-
840- def __repr__(self):
841- return '<Q2Q from <%s> to <%s> on %r>' % (
842- self.fromAddress, self.toAddress, self.protocolName)
843-
844- def __cmp__(self, other):
845- if isinstance(other, Q2QEndpoint):
846- return cmp((self.fromAddress, self.toAddress, self.protocolName),
847- (other.fromAddress, other.toAddress, other.protocolName))
848- return stablesort(self, other)
849-
850- def __hash__(self):
851- return hash((self.fromAddress,
852- self.toAddress,
853- self.protocolName)) + 7
854-
855- def connect(self, protocolFactory):
856- # from twisted.python.context import get
857- # get("q2q-service")
858- return self.service.connectQ2Q(
859- self.fromAddress, self.toAddress, self.protocolName,
860- protocolFactory)
861-
862
863=== removed file 'Vertex/vertex/gtk2hack.glade'
864--- Vertex/vertex/gtk2hack.glade 2006-06-01 04:57:02 +0000
865+++ Vertex/vertex/gtk2hack.glade 1970-01-01 00:00:00 +0000
866@@ -1,635 +0,0 @@
867-<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
868-<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
869-
870-<glade-interface>
871-<requires lib="gnome"/>
872-
873-<widget class="GtkMenu" id="notification_popup">
874-
875- <child>
876- <widget class="GtkImageMenuItem" id="add_contact1">
877- <property name="visible">True</property>
878- <property name="label" translatable="yes">Add Contact</property>
879- <property name="use_underline">True</property>
880- <signal name="activate" handler="addContact" last_modification_time="Sun, 22 May 2005 01:24:07 GMT"/>
881-
882- <child internal-child="image">
883- <widget class="GtkImage" id="image9">
884- <property name="visible">True</property>
885- <property name="stock">gtk-add</property>
886- <property name="icon_size">1</property>
887- <property name="xalign">0.5</property>
888- <property name="yalign">0.5</property>
889- <property name="xpad">0</property>
890- <property name="ypad">0</property>
891- </widget>
892- </child>
893- </widget>
894- </child>
895-
896- <child>
897- <widget class="GtkMenuItem" id="identifymenuitem">
898- <property name="visible">True</property>
899- <property name="label" translatable="yes">Identify</property>
900- <property name="use_underline">True</property>
901- <signal name="activate" handler="identifyDialog" last_modification_time="Sun, 22 May 2005 03:20:18 GMT"/>
902- </widget>
903- </child>
904-
905- <child>
906- <widget class="GtkSeparatorMenuItem" id="contacts_begin">
907- <property name="visible">True</property>
908- </widget>
909- </child>
910-
911- <child>
912- <widget class="GtkSeparatorMenuItem" id="separator3">
913- <property name="visible">True</property>
914- </widget>
915- </child>
916-
917- <child>
918- <widget class="GtkMenuItem" id="animate">
919- <property name="visible">True</property>
920- <property name="label" translatable="yes">Animate</property>
921- <property name="use_underline">True</property>
922- <signal name="activate" handler="toggleAnimate" last_modification_time="Sun, 22 May 2005 01:44:10 GMT"/>
923- </widget>
924- </child>
925-
926- <child>
927- <widget class="GtkSeparatorMenuItem" id="separator1">
928- <property name="visible">True</property>
929- </widget>
930- </child>
931-
932- <child>
933- <widget class="GtkImageMenuItem" id="quit1">
934- <property name="visible">True</property>
935- <property name="stock_item">GNOMEUIINFO_MENU_EXIT_ITEM</property>
936- <signal name="activate" handler="quit" last_modification_time="Sun, 22 May 2005 01:24:07 GMT"/>
937- </widget>
938- </child>
939-</widget>
940-
941-<widget class="GtkDialog" id="ident_dialog">
942- <property name="visible">True</property>
943- <property name="title" translatable="yes">Identify Yourself</property>
944- <property name="type">GTK_WINDOW_TOPLEVEL</property>
945- <property name="window_position">GTK_WIN_POS_CENTER</property>
946- <property name="modal">False</property>
947- <property name="resizable">False</property>
948- <property name="destroy_with_parent">False</property>
949- <property name="decorated">True</property>
950- <property name="skip_taskbar_hint">False</property>
951- <property name="skip_pager_hint">False</property>
952- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
953- <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
954- <property name="focus_on_map">True</property>
955- <property name="urgency_hint">False</property>
956- <property name="has_separator">True</property>
957-
958- <child internal-child="vbox">
959- <widget class="GtkVBox" id="dialog-vbox1">
960- <property name="visible">True</property>
961- <property name="homogeneous">False</property>
962- <property name="spacing">0</property>
963-
964- <child internal-child="action_area">
965- <widget class="GtkHButtonBox" id="dialog-action_area1">
966- <property name="visible">True</property>
967- <property name="layout_style">GTK_BUTTONBOX_END</property>
968-
969- <child>
970- <widget class="GtkButton" id="cancelbutton1">
971- <property name="visible">True</property>
972- <property name="can_default">True</property>
973- <property name="can_focus">True</property>
974- <property name="label">gtk-cancel</property>
975- <property name="use_stock">True</property>
976- <property name="relief">GTK_RELIEF_NORMAL</property>
977- <property name="focus_on_click">True</property>
978- <property name="response_id">-6</property>
979- <signal name="clicked" handler="identifyCancel" last_modification_time="Sun, 22 May 2005 03:14:51 GMT"/>
980- </widget>
981- </child>
982-
983- <child>
984- <widget class="GtkButton" id="okbutton1">
985- <property name="visible">True</property>
986- <property name="can_default">True</property>
987- <property name="can_focus">True</property>
988- <property name="label">gtk-ok</property>
989- <property name="use_stock">True</property>
990- <property name="relief">GTK_RELIEF_NORMAL</property>
991- <property name="focus_on_click">True</property>
992- <property name="response_id">-5</property>
993- <signal name="clicked" handler="identifyOK" last_modification_time="Sun, 22 May 2005 03:15:00 GMT"/>
994- </widget>
995- </child>
996- </widget>
997- <packing>
998- <property name="padding">0</property>
999- <property name="expand">False</property>
1000- <property name="fill">True</property>
1001- <property name="pack_type">GTK_PACK_END</property>
1002- </packing>
1003- </child>
1004-
1005- <child>
1006- <widget class="GtkVBox" id="vbox1">
1007- <property name="visible">True</property>
1008- <property name="homogeneous">False</property>
1009- <property name="spacing">0</property>
1010-
1011- <child>
1012- <widget class="GtkTable" id="table1">
1013- <property name="border_width">5</property>
1014- <property name="visible">True</property>
1015- <property name="n_rows">2</property>
1016- <property name="n_columns">2</property>
1017- <property name="homogeneous">False</property>
1018- <property name="row_spacing">2</property>
1019- <property name="column_spacing">5</property>
1020-
1021- <child>
1022- <widget class="GtkLabel" id="label4">
1023- <property name="visible">True</property>
1024- <property name="label" translatable="yes">Address</property>
1025- <property name="use_underline">False</property>
1026- <property name="use_markup">False</property>
1027- <property name="justify">GTK_JUSTIFY_LEFT</property>
1028- <property name="wrap">False</property>
1029- <property name="selectable">False</property>
1030- <property name="xalign">0</property>
1031- <property name="yalign">0.5</property>
1032- <property name="xpad">0</property>
1033- <property name="ypad">0</property>
1034- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
1035- <property name="width_chars">-1</property>
1036- <property name="single_line_mode">False</property>
1037- <property name="angle">0</property>
1038- </widget>
1039- <packing>
1040- <property name="left_attach">0</property>
1041- <property name="right_attach">1</property>
1042- <property name="top_attach">0</property>
1043- <property name="bottom_attach">1</property>
1044- <property name="x_options">fill</property>
1045- <property name="y_options"></property>
1046- </packing>
1047- </child>
1048-
1049- <child>
1050- <widget class="GtkLabel" id="label5">
1051- <property name="visible">True</property>
1052- <property name="label" translatable="yes">Password</property>
1053- <property name="use_underline">False</property>
1054- <property name="use_markup">False</property>
1055- <property name="justify">GTK_JUSTIFY_LEFT</property>
1056- <property name="wrap">False</property>
1057- <property name="selectable">False</property>
1058- <property name="xalign">0</property>
1059- <property name="yalign">0.5</property>
1060- <property name="xpad">0</property>
1061- <property name="ypad">0</property>
1062- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
1063- <property name="width_chars">-1</property>
1064- <property name="single_line_mode">False</property>
1065- <property name="angle">0</property>
1066- </widget>
1067- <packing>
1068- <property name="left_attach">0</property>
1069- <property name="right_attach">1</property>
1070- <property name="top_attach">1</property>
1071- <property name="bottom_attach">2</property>
1072- <property name="x_options">fill</property>
1073- <property name="y_options"></property>
1074- </packing>
1075- </child>
1076-
1077- <child>
1078- <widget class="GtkEntry" id="addressEntry">
1079- <property name="visible">True</property>
1080- <property name="can_focus">True</property>
1081- <property name="has_focus">True</property>
1082- <property name="editable">True</property>
1083- <property name="visibility">True</property>
1084- <property name="max_length">0</property>
1085- <property name="text" translatable="yes"></property>
1086- <property name="has_frame">True</property>
1087- <property name="invisible_char">*</property>
1088- <property name="activates_default">False</property>
1089- </widget>
1090- <packing>
1091- <property name="left_attach">1</property>
1092- <property name="right_attach">2</property>
1093- <property name="top_attach">0</property>
1094- <property name="bottom_attach">1</property>
1095- <property name="y_options"></property>
1096- </packing>
1097- </child>
1098-
1099- <child>
1100- <widget class="GtkEntry" id="passwordEntry">
1101- <property name="visible">True</property>
1102- <property name="can_focus">True</property>
1103- <property name="editable">True</property>
1104- <property name="visibility">False</property>
1105- <property name="max_length">0</property>
1106- <property name="text" translatable="yes"></property>
1107- <property name="has_frame">True</property>
1108- <property name="invisible_char">*</property>
1109- <property name="activates_default">True</property>
1110- </widget>
1111- <packing>
1112- <property name="left_attach">1</property>
1113- <property name="right_attach">2</property>
1114- <property name="top_attach">1</property>
1115- <property name="bottom_attach">2</property>
1116- <property name="y_options"></property>
1117- </packing>
1118- </child>
1119- </widget>
1120- <packing>
1121- <property name="padding">0</property>
1122- <property name="expand">True</property>
1123- <property name="fill">True</property>
1124- </packing>
1125- </child>
1126-
1127- <child>
1128- <widget class="GtkProgressBar" id="identifyProgressBar">
1129- <property name="visible">True</property>
1130- <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
1131- <property name="fraction">0</property>
1132- <property name="pulse_step">0.10000000149</property>
1133- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
1134- </widget>
1135- <packing>
1136- <property name="padding">0</property>
1137- <property name="expand">False</property>
1138- <property name="fill">False</property>
1139- </packing>
1140- </child>
1141-
1142- <child>
1143- <widget class="GtkLabel" id="identifyProgressLabel">
1144- <property name="visible">True</property>
1145- <property name="label" translatable="yes"></property>
1146- <property name="use_underline">False</property>
1147- <property name="use_markup">False</property>
1148- <property name="justify">GTK_JUSTIFY_LEFT</property>
1149- <property name="wrap">False</property>
1150- <property name="selectable">False</property>
1151- <property name="xalign">0.5</property>
1152- <property name="yalign">0.5</property>
1153- <property name="xpad">0</property>
1154- <property name="ypad">0</property>
1155- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
1156- <property name="width_chars">-1</property>
1157- <property name="single_line_mode">False</property>
1158- <property name="angle">0</property>
1159- </widget>
1160- <packing>
1161- <property name="padding">3</property>
1162- <property name="expand">False</property>
1163- <property name="fill">False</property>
1164- </packing>
1165- </child>
1166- </widget>
1167- <packing>
1168- <property name="padding">0</property>
1169- <property name="expand">False</property>
1170- <property name="fill">True</property>
1171- </packing>
1172- </child>
1173- </widget>
1174- </child>
1175-</widget>
1176-
1177-<widget class="GtkDialog" id="add_contact_dialog">
1178- <property name="visible">True</property>
1179- <property name="title" translatable="yes">Add Contact</property>
1180- <property name="type">GTK_WINDOW_TOPLEVEL</property>
1181- <property name="window_position">GTK_WIN_POS_NONE</property>
1182- <property name="modal">False</property>
1183- <property name="resizable">True</property>
1184- <property name="destroy_with_parent">False</property>
1185- <property name="decorated">True</property>
1186- <property name="skip_taskbar_hint">False</property>
1187- <property name="skip_pager_hint">False</property>
1188- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
1189- <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
1190- <property name="focus_on_map">True</property>
1191- <property name="urgency_hint">False</property>
1192- <property name="has_separator">True</property>
1193-
1194- <child internal-child="vbox">
1195- <widget class="GtkVBox" id="dialog-vbox2">
1196- <property name="visible">True</property>
1197- <property name="homogeneous">False</property>
1198- <property name="spacing">0</property>
1199-
1200- <child internal-child="action_area">
1201- <widget class="GtkHButtonBox" id="dialog-action_area2">
1202- <property name="visible">True</property>
1203- <property name="layout_style">GTK_BUTTONBOX_END</property>
1204-
1205- <child>
1206- <widget class="GtkButton" id="cancelbutton2">
1207- <property name="visible">True</property>
1208- <property name="can_default">True</property>
1209- <property name="can_focus">True</property>
1210- <property name="label">gtk-cancel</property>
1211- <property name="use_stock">True</property>
1212- <property name="relief">GTK_RELIEF_NORMAL</property>
1213- <property name="focus_on_click">True</property>
1214- <property name="response_id">-6</property>
1215- <signal name="activate" handler="popdownDialog" last_modification_time="Tue, 28 Feb 2006 09:34:42 GMT"/>
1216- </widget>
1217- </child>
1218-
1219- <child>
1220- <widget class="GtkButton" id="okbutton2">
1221- <property name="visible">True</property>
1222- <property name="can_default">True</property>
1223- <property name="can_focus">True</property>
1224- <property name="label">gtk-ok</property>
1225- <property name="use_stock">True</property>
1226- <property name="relief">GTK_RELIEF_NORMAL</property>
1227- <property name="focus_on_click">True</property>
1228- <property name="response_id">-5</property>
1229- <signal name="activate" handler="doAddContact" last_modification_time="Tue, 28 Feb 2006 09:34:23 GMT"/>
1230- <signal name="clicked" handler="doAddContact" last_modification_time="Tue, 28 Feb 2006 10:36:25 GMT"/>
1231- </widget>
1232- </child>
1233- </widget>
1234- <packing>
1235- <property name="padding">0</property>
1236- <property name="expand">False</property>
1237- <property name="fill">True</property>
1238- <property name="pack_type">GTK_PACK_END</property>
1239- </packing>
1240- </child>
1241-
1242- <child>
1243- <widget class="GtkFrame" id="frame1">
1244- <property name="visible">True</property>
1245- <property name="label_xalign">0</property>
1246- <property name="label_yalign">0.5</property>
1247- <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
1248-
1249- <child>
1250- <widget class="GtkAlignment" id="alignment1">
1251- <property name="visible">True</property>
1252- <property name="xalign">0.5</property>
1253- <property name="yalign">0.5</property>
1254- <property name="xscale">1</property>
1255- <property name="yscale">1</property>
1256- <property name="top_padding">0</property>
1257- <property name="bottom_padding">0</property>
1258- <property name="left_padding">12</property>
1259- <property name="right_padding">0</property>
1260-
1261- <child>
1262- <widget class="GtkTable" id="table2">
1263- <property name="border_width">16</property>
1264- <property name="visible">True</property>
1265- <property name="n_rows">2</property>
1266- <property name="n_columns">2</property>
1267- <property name="homogeneous">False</property>
1268- <property name="row_spacing">16</property>
1269- <property name="column_spacing">16</property>
1270-
1271- <child>
1272- <widget class="GtkEntry" id="nameentry">
1273- <property name="visible">True</property>
1274- <property name="can_focus">True</property>
1275- <property name="editable">True</property>
1276- <property name="visibility">True</property>
1277- <property name="max_length">0</property>
1278- <property name="text" translatable="yes"></property>
1279- <property name="has_frame">True</property>
1280- <property name="invisible_char">*</property>
1281- <property name="activates_default">False</property>
1282- </widget>
1283- <packing>
1284- <property name="left_attach">1</property>
1285- <property name="right_attach">2</property>
1286- <property name="top_attach">0</property>
1287- <property name="bottom_attach">1</property>
1288- <property name="y_options"></property>
1289- </packing>
1290- </child>
1291-
1292- <child>
1293- <widget class="GtkEntry" id="q2qidentry">
1294- <property name="visible">True</property>
1295- <property name="can_focus">True</property>
1296- <property name="editable">True</property>
1297- <property name="visibility">True</property>
1298- <property name="max_length">0</property>
1299- <property name="text" translatable="yes"></property>
1300- <property name="has_frame">True</property>
1301- <property name="invisible_char">*</property>
1302- <property name="activates_default">False</property>
1303- </widget>
1304- <packing>
1305- <property name="left_attach">1</property>
1306- <property name="right_attach">2</property>
1307- <property name="top_attach">1</property>
1308- <property name="bottom_attach">2</property>
1309- <property name="y_options"></property>
1310- </packing>
1311- </child>
1312-
1313- <child>
1314- <widget class="GtkLabel" id="label7">
1315- <property name="visible">True</property>
1316- <property name="label" translatable="yes">Name</property>
1317- <property name="use_underline">False</property>
1318- <property name="use_markup">False</property>
1319- <property name="justify">GTK_JUSTIFY_LEFT</property>
1320- <property name="wrap">False</property>
1321- <property name="selectable">False</property>
1322- <property name="xalign">0</property>
1323- <property name="yalign">0.5</property>
1324- <property name="xpad">0</property>
1325- <property name="ypad">0</property>
1326- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
1327- <property name="width_chars">-1</property>
1328- <property name="single_line_mode">False</property>
1329- <property name="angle">0</property>
1330- </widget>
1331- <packing>
1332- <property name="left_attach">0</property>
1333- <property name="right_attach">1</property>
1334- <property name="top_attach">0</property>
1335- <property name="bottom_attach">1</property>
1336- <property name="x_options">fill</property>
1337- <property name="y_options"></property>
1338- </packing>
1339- </child>
1340-
1341- <child>
1342- <widget class="GtkLabel" id="label8">
1343- <property name="visible">True</property>
1344- <property name="label" translatable="yes">Q2QID</property>
1345- <property name="use_underline">False</property>
1346- <property name="use_markup">False</property>
1347- <property name="justify">GTK_JUSTIFY_LEFT</property>
1348- <property name="wrap">False</property>
1349- <property name="selectable">False</property>
1350- <property name="xalign">0</property>
1351- <property name="yalign">0.5</property>
1352- <property name="xpad">0</property>
1353- <property name="ypad">0</property>
1354- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
1355- <property name="width_chars">-1</property>
1356- <property name="single_line_mode">False</property>
1357- <property name="angle">0</property>
1358- </widget>
1359- <packing>
1360- <property name="left_attach">0</property>
1361- <property name="right_attach">1</property>
1362- <property name="top_attach">1</property>
1363- <property name="bottom_attach">2</property>
1364- <property name="x_options">fill</property>
1365- <property name="y_options"></property>
1366- </packing>
1367- </child>
1368- </widget>
1369- </child>
1370- </widget>
1371- </child>
1372-
1373- <child>
1374- <widget class="GtkLabel" id="label6">
1375- <property name="visible">True</property>
1376- <property name="label" translatable="yes">Contact Information</property>
1377- <property name="use_underline">False</property>
1378- <property name="use_markup">True</property>
1379- <property name="justify">GTK_JUSTIFY_LEFT</property>
1380- <property name="wrap">False</property>
1381- <property name="selectable">False</property>
1382- <property name="xalign">0.5</property>
1383- <property name="yalign">0.5</property>
1384- <property name="xpad">0</property>
1385- <property name="ypad">0</property>
1386- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
1387- <property name="width_chars">-1</property>
1388- <property name="single_line_mode">False</property>
1389- <property name="angle">0</property>
1390- </widget>
1391- <packing>
1392- <property name="type">label_item</property>
1393- </packing>
1394- </child>
1395- </widget>
1396- <packing>
1397- <property name="padding">0</property>
1398- <property name="expand">True</property>
1399- <property name="fill">True</property>
1400- </packing>
1401- </child>
1402- </widget>
1403- </child>
1404-</widget>
1405-
1406-<widget class="GtkDialog" id="accept_connection_dialog">
1407- <property name="visible">True</property>
1408- <property name="title" translatable="yes">Accept Connection?</property>
1409- <property name="type">GTK_WINDOW_TOPLEVEL</property>
1410- <property name="window_position">GTK_WIN_POS_NONE</property>
1411- <property name="modal">False</property>
1412- <property name="resizable">True</property>
1413- <property name="destroy_with_parent">False</property>
1414- <property name="decorated">True</property>
1415- <property name="skip_taskbar_hint">False</property>
1416- <property name="skip_pager_hint">False</property>
1417- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
1418- <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
1419- <property name="focus_on_map">True</property>
1420- <property name="urgency_hint">False</property>
1421- <property name="has_separator">True</property>
1422- <signal name="destroy" handler="rejectConnectionEvt" last_modification_time="Tue, 28 Feb 2006 10:00:37 GMT"/>
1423-
1424- <child internal-child="vbox">
1425- <widget class="GtkVBox" id="dialog-vbox3">
1426- <property name="visible">True</property>
1427- <property name="homogeneous">False</property>
1428- <property name="spacing">0</property>
1429-
1430- <child internal-child="action_area">
1431- <widget class="GtkHButtonBox" id="dialog-action_area3">
1432- <property name="visible">True</property>
1433- <property name="layout_style">GTK_BUTTONBOX_END</property>
1434-
1435- <child>
1436- <widget class="GtkButton" id="cancelbutton3">
1437- <property name="visible">True</property>
1438- <property name="can_default">True</property>
1439- <property name="can_focus">True</property>
1440- <property name="label">gtk-cancel</property>
1441- <property name="use_stock">True</property>
1442- <property name="relief">GTK_RELIEF_NORMAL</property>
1443- <property name="focus_on_click">True</property>
1444- <property name="response_id">-6</property>
1445- <signal name="activate" handler="destroyit" last_modification_time="Tue, 28 Feb 2006 10:00:55 GMT"/>
1446- <signal name="clicked" handler="destroyit" last_modification_time="Tue, 28 Feb 2006 10:36:09 GMT"/>
1447- </widget>
1448- </child>
1449-
1450- <child>
1451- <widget class="GtkButton" id="okbutton3">
1452- <property name="visible">True</property>
1453- <property name="can_default">True</property>
1454- <property name="can_focus">True</property>
1455- <property name="label">gtk-ok</property>
1456- <property name="use_stock">True</property>
1457- <property name="relief">GTK_RELIEF_NORMAL</property>
1458- <property name="focus_on_click">True</property>
1459- <property name="response_id">-5</property>
1460- <signal name="activate" handler="acceptConnectionEvt" last_modification_time="Tue, 28 Feb 2006 09:59:48 GMT"/>
1461- <signal name="clicked" handler="acceptConnectionEvt" last_modification_time="Tue, 28 Feb 2006 10:36:51 GMT"/>
1462- </widget>
1463- </child>
1464- </widget>
1465- <packing>
1466- <property name="padding">0</property>
1467- <property name="expand">False</property>
1468- <property name="fill">True</property>
1469- <property name="pack_type">GTK_PACK_END</property>
1470- </packing>
1471- </child>
1472-
1473- <child>
1474- <widget class="GtkLabel" id="accept_connection_label">
1475- <property name="visible">True</property>
1476- <property name="label" translatable="yes">Accept connection?</property>
1477- <property name="use_underline">False</property>
1478- <property name="use_markup">False</property>
1479- <property name="justify">GTK_JUSTIFY_LEFT</property>
1480- <property name="wrap">False</property>
1481- <property name="selectable">False</property>
1482- <property name="xalign">0.5</property>
1483- <property name="yalign">0.5</property>
1484- <property name="xpad">0</property>
1485- <property name="ypad">0</property>
1486- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
1487- <property name="width_chars">-1</property>
1488- <property name="single_line_mode">False</property>
1489- <property name="angle">0</property>
1490- </widget>
1491- <packing>
1492- <property name="padding">0</property>
1493- <property name="expand">False</property>
1494- <property name="fill">False</property>
1495- </packing>
1496- </child>
1497- </widget>
1498- </child>
1499-</widget>
1500-
1501-</glade-interface>
1502
1503=== removed file 'Vertex/vertex/gtk2hack.py'
1504--- Vertex/vertex/gtk2hack.py 2006-06-01 04:57:02 +0000
1505+++ Vertex/vertex/gtk2hack.py 1970-01-01 00:00:00 +0000
1506@@ -1,270 +0,0 @@
1507-
1508-import os
1509-import rfc822
1510-
1511-from twisted.python.filepath import FilePath
1512-
1513-# import gtk ### pyflakes complains about this, due to the next line
1514-import gtk.glade
1515-
1516-from vertex.q2qclient import ClientQ2QService
1517-from vertex.q2q import Q2QAddress
1518-
1519-class _NullCb:
1520- def __init__(self, name):
1521- self.name = name
1522-
1523- def __call__(self, *a, **kw):
1524- print 'No callback provided for', self.name, a, kw
1525-
1526-class _SignalAttacher:
1527- def __init__(self, original):
1528- self.original = original
1529-
1530- def __getitem__(self, callbackName):
1531- return getattr(self.original, callbackName, None) or _NullCb(callbackName)
1532-
1533-GLADE_FILE = os.path.splitext(__file__)[0] + '.glade'
1534-
1535-class IdentificationDialog:
1536- def __init__(self, clientService, plug):
1537- self.xml = gtk.glade.XML(GLADE_FILE, "ident_dialog")
1538- self.clientService = clientService
1539- self.xml.signal_autoconnect(_SignalAttacher(self))
1540- self.addressEntry = self.xml.get_widget('addressEntry')
1541- self.passwordEntry = self.xml.get_widget('passwordEntry')
1542- self.progressBar = self.xml.get_widget('identifyProgressBar')
1543- self.progressLabel = self.xml.get_widget('identifyProgressLabel')
1544- self.identifyWindow = self.xml.get_widget("ident_dialog")
1545- self.cancelButton = self.xml.get_widget('cancelbutton1')
1546- self.okButton = self.xml.get_widget('okbutton1')
1547- self.plug = plug
1548-
1549- def identifyCancel(self, event):
1550- self.identifyWindow.destroy()
1551-
1552- def identifyOK(self, event):
1553- idstr = self.addressEntry.get_text()
1554- D = self.clientService.authorize(
1555- Q2QAddress.fromString(idstr),
1556- self.passwordEntry.get_text())
1557-
1558- sensitiveWidgets = [self.addressEntry,
1559- self.passwordEntry,
1560- self.okButton,
1561- self.cancelButton]
1562- for widget in sensitiveWidgets:
1563- widget.set_sensitive(False)
1564- self.progressLabel.set_text("Authenticating...")
1565- def itWorked(workedNone):
1566- self.identifyWindow.destroy()
1567- self.plug.setCurrentID(idstr)
1568- def itDidntWork(error):
1569- self.progressLabel.set_text(error.getErrorMessage())
1570- for widget in sensitiveWidgets:
1571- widget.set_sensitive(True)
1572- D.addCallbacks(itWorked, itDidntWork)
1573-
1574-class AddContactDialog:
1575- def __init__(self, plug):
1576- self.xml = gtk.glade.XML(GLADE_FILE, "add_contact_dialog")
1577- self.xml.signal_autoconnect(_SignalAttacher(self))
1578- self.window = self.xml.get_widget("add_contact_dialog")
1579- self.window.show_all()
1580- self.plug = plug
1581-
1582- def doAddContact(self, evt):
1583- name = self.xml.get_widget("nameentry").get_text()
1584- addr = self.xml.get_widget("q2qidentry").get_text()
1585- self.plug.addBuddy(name, addr)
1586- self.popdownDialog()
1587-
1588- def popdownDialog(self, evt=None):
1589- self.window.destroy()
1590-
1591-class AcceptConnectionDialog:
1592- def __init__(self, d, From, to, protocol):
1593- self.d = d
1594- self.xml = gtk.glade.XML(GLADE_FILE, "accept_connection_dialog")
1595- self.xml.signal_autoconnect(_SignalAttacher(self))
1596- self.label = self.xml.get_widget("accept_connection_label")
1597- self.label.set_text(
1598- "Accept connection from %s for %s?" % (From, protocol))
1599- self.window = self.xml.get_widget("accept_connection_dialog")
1600- self.window.show_all()
1601-
1602- done = False
1603-
1604- def destroyit(self, evt):
1605- self.window.destroy()
1606-
1607- def acceptConnectionEvt(self, evt):
1608- self.done = True
1609- print "YES"
1610- self.d.callback(1)
1611- print "WHAT"
1612- self.window.destroy()
1613-
1614- def rejectConnectionEvt(self, evt):
1615- print "DSTRY"
1616- if not self.done:
1617- print "DIE!"
1618- from twisted.python import failure
1619- self.d.errback(failure.Failure(KeyError("Connection rejected by user")))
1620- else:
1621- print "OK"
1622-
1623-from twisted.internet.protocol import ServerFactory
1624-from twisted.internet.protocol import Protocol
1625-
1626-class VertexDemoProtocol(Protocol):
1627-
1628- def connectionMade(self):
1629- print 'CONN MADE'
1630-
1631- def dataReceived(self, data):
1632- print 'HOLY SHNIKIES', data
1633-
1634-class VertexFactory(ServerFactory):
1635- protocol = VertexDemoProtocol
1636-
1637- def __init__(self, plug):
1638- self.plug = plug
1639-
1640- def startFactory(self):
1641- #self.plug.animator.stop(1)
1642- pass
1643-
1644- def stopFactory(self):
1645- #self.plug.animator.stop(0)
1646- pass
1647-
1648-
1649-class BuddyItem:
1650- def __init__(self, plug, alias, q2qaddress):
1651- mi = self.menuItem = gtk.MenuItem(alias + " <"+q2qaddress+">")
1652- mi.connect("activate", self.initiateFileTransfer)
1653- mi.show_all()
1654- self.plug = plug
1655- self.alias = alias
1656- self.q2qaddress = q2qaddress
1657- self.plug.loadedBuddies[q2qaddress] = self
1658-
1659- def initiateFileTransfer(self, evt):
1660- print 'Initiate transfer with ' + self.alias + self.q2qaddress
1661-
1662- def addToMenu(self):
1663- self.plug.section.append(self.menuItem)
1664-
1665- def removeFromMenu(self):
1666- self.plug.section.remove(self.menuItem)
1667-
1668-from twisted.plugin import IPlugin
1669-from prime.iprime import IMenuApplication
1670-from zope.interface import implements
1671-
1672-class PlugEntry:
1673- implements(IMenuApplication, IPlugin)
1674-
1675- def __init__(self):
1676- self.xml = gtk.glade.XML(GLADE_FILE, "notification_popup")
1677-
1678- def register(self, section):
1679- print 'REGISTER'
1680- self.section = section
1681-
1682- workingdir = FilePath(os.path.expanduser("~/.vertex"))
1683- self.clientService = ClientQ2QService(
1684- workingdir.child("q2q-certificates").path,
1685- verifyHook=self.displayVerifyDialog,
1686- inboundTCPPortnum=8172,
1687- # q2qPortnum=8173,
1688- udpEnabled=False)
1689- self.setCurrentID(self.clientService.getDefaultFrom())
1690- self.buddiesfile = workingdir.child("q2q-buddies.txt")
1691- self.loadedBuddies = {}
1692- self.parseBuddies()
1693-
1694- def parseBuddies(self):
1695- try:
1696- self.buddyList = rfc822.AddressList(self.buddiesfile.open().read())
1697- except IOError:
1698- return
1699- self.clearContactMenu()
1700- for dispn, addr in self.buddyList:
1701- if addr not in self.loadedBuddies:
1702- BuddyItem(self, dispn, addr)
1703- self.buildContactMenu()
1704-
1705- def clearContactMenu(self):
1706- for bud in self.loadedBuddies.values():
1707- bud.removeFromMenu()
1708-
1709- def buildContactMenu(self):
1710- l = self.loadedBuddies.values()
1711- l.sort(key=lambda x: x.alias)
1712- l.reverse()
1713- for bud in l:
1714- bud.addToMenu()
1715-
1716- def addBuddy(self, alias, q2qaddr):
1717- temp = self.buddiesfile.temporarySibling()
1718- try:
1719- origdata = self.buddiesfile.open().read()
1720- except IOError:
1721- origdata = ''
1722- moredata = '\n%s <%s>' % (alias, q2qaddr)
1723- ftemp = temp.open('w')
1724- ftemp.write(origdata)
1725- ftemp.write(moredata)
1726- ftemp.close()
1727- temp.moveTo(self.buddiesfile)
1728- self.parseBuddies()
1729-
1730- def displayVerifyDialog(self, From, to, protocol):
1731- from twisted.internet import defer
1732- d = defer.Deferred()
1733- AcceptConnectionDialog(d, From, to, protocol)
1734- return d
1735-
1736- def setCurrentID(self, idName):
1737-
1738- if idName is not None:
1739- currentID = Q2QAddress.fromString(idName)
1740- # log in?
1741- # self.animator.start()
1742- SL = self.xml.get_widget("identifymenuitem").get_children()[0].set_label
1743- def loggedIn(result):
1744- SL(str(currentID))
1745- self.currentID = currentID
1746- def notLoggedIn(error):
1747- SL("Identify")
1748- # self.animator.stop(0)
1749- # This following order is INSANE - you should definitely not have
1750- # to wait until the LISTEN succeeds to start the service; quite the
1751- # opposite, you should wait until the service has started, then
1752- # issue the LISTEN!! For some reason, the connection drops
1753- # immediately if you do that, and I have no idea why. As soon as I
1754- # can fix that issue the startService should be moved up previous
1755- # to listenQ2Q.
1756- self.clientService.listenQ2Q(currentID,
1757- {'vertex': VertexFactory(self)},
1758- "desktop vertex UI").addCallbacks(
1759- loggedIn, notLoggedIn).addCallback(
1760- lambda ign: self.clientService.startService())
1761-
1762- # XXX event handlers
1763-
1764- def toggleAnimate(self, event):
1765- if self.animator.animating:
1766- # SL("Animate")
1767- self.animator.stop()
1768- else:
1769- # SL("Stop Animating")
1770- self.animator.start()
1771-
1772- def identifyDialog(self, event):
1773- IdentificationDialog(self.clientService, self)
1774-
1775- def addContact(self, event):
1776- AddContactDialog(self)
1777
1778=== removed file 'Vertex/vertex/icon-active.png'
1779Binary files Vertex/vertex/icon-active.png 2006-06-01 04:57:02 +0000 and Vertex/vertex/icon-active.png 1970-01-01 00:00:00 +0000 differ
1780=== removed file 'Vertex/vertex/icon-inactive.png'
1781Binary files Vertex/vertex/icon-inactive.png 2006-06-01 04:57:02 +0000 and Vertex/vertex/icon-inactive.png 1970-01-01 00:00:00 +0000 differ
1782=== removed file 'Vertex/vertex/ivertex.py'
1783--- Vertex/vertex/ivertex.py 2012-03-14 16:23:22 +0000
1784+++ Vertex/vertex/ivertex.py 1970-01-01 00:00:00 +0000
1785@@ -1,108 +0,0 @@
1786-# Copyright 2005 Divmod, Inc. See LICENSE file for details
1787-
1788-from zope.interface import Interface
1789-
1790-class IQ2QTransport(Interface):
1791- """
1792- I am a byte-stream-oriented transport which has Q2Q identifiers associated
1793- with the endpoints, and possibly some cryptographic verification of the
1794- authenticity of those endpoints.
1795- """
1796-
1797- def getQ2QHost():
1798- """ Returns a Q2QAddress object representing the user on this end of the
1799- connection.
1800- """
1801-
1802- def getQ2QPeer():
1803- """ Returns a Q2QAddress object representing the user on the other end of the
1804- connection.
1805- """
1806-
1807-class IQ2QUser(Interface):
1808- """
1809- A cred interface for Q2Q users.
1810- """
1811- def signCertificateRequest(certificateRequest, domainCert, suggestedSerial):
1812- """
1813- Return a signed certificate object if the subject fields in the
1814- certificateRequest are valid.
1815- """
1816-
1817-class IFileTransfer(Interface):
1818-
1819- def getUploadSink(self, path):
1820- """
1821- @param path: a PathFragment that the client wishes to upload to.
1822-
1823- @return: a DataSink where we'll save the data to.
1824- """
1825-
1826- def getDownloadSource(self, path):
1827- """
1828- @param path: a PathFragment that the client wishes to download.
1829-
1830- @return: a DataSource to download data from.
1831- """
1832-
1833- def listChildren(self, path):
1834- """
1835- @param path: a PathFragment that the client wishes to get a list of.
1836-
1837- @return: a list of dictionaries mapping::
1838- {'name': str,
1839- 'size': int,
1840- 'type': vertex.filexfer.MIMEType,
1841- 'modified': datetime.datetime}
1842- """
1843-
1844-class ISessionTokenStorage(Interface):
1845- def idFromCookie(self, cookie, domain):
1846- """Look up a user ID from the given cookie in the given domain.
1847- """
1848-
1849-class ICertificateStorage(Interface):
1850- def getSelfSignedCertificate(self, domainName):
1851- """
1852- @return: a Deferred which will fire with the certificate for the given
1853- domain name.
1854- """
1855-
1856- def storeSelfSignedCertificate(self, domainName, mainCert):
1857- """
1858- @type mainCert: C{str}
1859- @param mainCert: Serialized, self-signed certificate to associate
1860- with the given domain.
1861-
1862- @return: a Deferred which will fire when the certificate has been
1863- stored successfully.
1864- """
1865-
1866- def getPrivateCertificate(self, domainName):
1867- """
1868- @return: a PrivateCertificate instance, e.g. a certificate including a
1869- private key, for 'domainName'.
1870- """
1871-
1872- def addPrivateCertificate(self, domainName, existingCertificate=None):
1873- """
1874- """
1875-
1876-class IOfferUp(Interface):
1877- """
1878- Sharing control database storage.
1879- """
1880-
1881-class IPlugin(Interface):
1882- """
1883- """
1884-
1885-class ITestPlugin(Interface):
1886- """
1887- Dummy plug-in interface for unit testing.
1888- """
1889-
1890-class ITestPlugin2(Interface):
1891- """
1892- Dummy plug-in interface for unit testing.
1893- """
1894
1895=== removed file 'Vertex/vertex/ptcp.py'
1896--- Vertex/vertex/ptcp.py 2012-03-14 16:23:22 +0000
1897+++ Vertex/vertex/ptcp.py 1970-01-01 00:00:00 +0000
1898@@ -1,1050 +0,0 @@
1899-# -*- test-case-name: vertex.test.test_ptcp -*-
1900-
1901-import struct
1902-
1903-from binascii import crc32 # used to use zlib.crc32 - but that gives different
1904- # results on 64-bit platforms!!
1905-
1906-import itertools
1907-
1908-from twisted.python.failure import Failure
1909-from twisted.internet.defer import Deferred
1910-from twisted.internet import protocol, error, reactor, defer
1911-from twisted.internet.main import CONNECTION_DONE
1912-from twisted.python import log, util
1913-
1914-from vertex import tcpdfa
1915-from vertex.statemachine import StateError
1916-
1917-
1918-genConnID = itertools.count(8).next
1919-
1920-MAX_PSEUDO_PORT = (2 ** 16)
1921-
1922-_packetFormat = ('!' # WTF did you think
1923- 'H' # sourcePseudoPort
1924- 'H' # destPseudoPort
1925- 'L' # sequenceNumber
1926- 'L' # acknowledgementNumber
1927- 'L' # window
1928- 'B' # flags
1929- 'l' # checksum
1930- # (signed because of binascii.crc32)
1931- 'H' # dlen
1932- )
1933-_fixedSize = struct.calcsize(_packetFormat)
1934-
1935-_SYN, _ACK, _FIN, _RST, _STB = [1 << n for n in range(5)]
1936-
1937-def _flagprop(flag):
1938- def setter(self, value):
1939- if value:
1940- self.flags |= flag
1941- else:
1942- self.flags &= ~flag
1943- return property(lambda self: bool(self.flags & flag), setter)
1944-
1945-def relativeSequence(wireSequence, initialSequence, lapNumber):
1946- """ Compute a relative sequence number from a wire sequence number so that we
1947- can use natural Python comparisons on it, such as <, >, ==.
1948-
1949- @param wireSequence: the sequence number received on the wire.
1950-
1951- @param initialSequence: the ISN for this sequence, negotiated at SYN time.
1952-
1953- @param lapNumber: the number of times that this value has wrapped around
1954- 2**32.
1955- """
1956- return (wireSequence + (lapNumber * (2**32))) - initialSequence
1957-
1958-class PTCPPacket(util.FancyStrMixin, object):
1959- showAttributes = (
1960- ('sourcePseudoPort', 'sourcePseudoPort', '%d'),
1961- ('destPseudoPort', 'destPseudoPort', '%d'),
1962- ('shortdata', 'data', '%r'),
1963- ('niceflags', 'flags', '%s'),
1964- ('dlen', 'dlen', '%d'),
1965- ('seqNum', 'seq', '%d'),
1966- ('ackNum', 'ack', '%d'),
1967- ('checksum', 'checksum', '%x'),
1968- ('peerAddressTuple', 'peerAddress', '%r'),
1969- ('retransmitCount', 'retransmitCount', '%d'),
1970- )
1971-
1972- syn = _flagprop(_SYN)
1973- ack = _flagprop(_ACK)
1974- fin = _flagprop(_FIN)
1975- rst = _flagprop(_RST)
1976- stb = _flagprop(_STB)
1977-
1978- # Number of retransmit attempts left for this segment. When it reaches
1979- # zero, this segment is dead.
1980- retransmitCount = 50
1981-
1982- def shortdata():
1983- def get(self):
1984- if len(self.data) > 13:
1985- return self.data[:5] + '...' + self.data[-5:]
1986- else:
1987- return self.data
1988- return get,
1989- shortdata = property(*shortdata())
1990-
1991- def niceflags():
1992- def get(self):
1993- res = []
1994- for (f, v) in [
1995- (self.syn, 'S'), (self.ack, 'A'), (self.fin, 'F'),
1996- (self.rst, 'R'), (self.stb, 'T')]:
1997- res.append(f and v or '.')
1998- return ''.join(res)
1999- return get,
2000- niceflags = property(*niceflags())
2001-
2002- def create(cls,
2003- sourcePseudoPort, destPseudoPort,
2004- seqNum, ackNum, data,
2005- window=(1 << 15),
2006- syn=False, ack=False, fin=False,
2007- rst=False, stb=False,
2008- destination=None):
2009- i = cls(sourcePseudoPort, destPseudoPort,
2010- seqNum, ackNum, window,
2011- 0, 0, len(data), data)
2012- i.syn = syn
2013- i.ack = ack
2014- i.fin = fin
2015- i.rst = rst
2016- i.stb = stb
2017- i.checksum = i.computeChecksum()
2018- i.destination = destination
2019- return i
2020- create = classmethod(create)
2021-
2022-
2023- def __init__(self,
2024- sourcePseudoPort,
2025- destPseudoPort,
2026- seqNum, ackNum, window, flags,
2027- checksum, dlen, data, peerAddressTuple=None,
2028- seqOffset=0, ackOffset=0, seqLaps=0, ackLaps=0):
2029- self.sourcePseudoPort = sourcePseudoPort
2030- self.destPseudoPort = destPseudoPort
2031- self.seqNum = seqNum
2032- self.ackNum = ackNum
2033- self.window = window
2034- self.flags = flags
2035- self.checksum = checksum
2036- self.dlen = dlen
2037- self.data = data
2038- self.peerAddressTuple = peerAddressTuple # None if local
2039-
2040- self.seqOffset = seqOffset
2041- self.ackOffset = ackOffset
2042- self.seqLaps = seqLaps
2043- self.ackLaps = ackLaps
2044-
2045- def segmentLength(self):
2046- """RFC page 26: 'The segment length (SEG.LEN) includes both data and sequence
2047- space occupying controls'
2048- """
2049- return self.dlen + self.syn + self.fin
2050-
2051- def relativeSeq(self):
2052- return relativeSequence(self.seqNum, self.seqOffset, self.seqLaps)
2053-
2054- def relativeAck(self):
2055- return relativeSequence(self.ackNum, self.ackOffset, self.ackLaps)
2056-
2057-
2058- def verifyChecksum(self):
2059- if len(self.data) != self.dlen:
2060- if len(self.data) > self.dlen:
2061- raise GarbageDataError(self)
2062- else:
2063- raise TruncatedDataError(self)
2064- expected = self.computeChecksum()
2065- received = self.checksum
2066- if expected != received:
2067- raise ChecksumMismatchError(expected, received)
2068-
2069- def computeChecksum(self):
2070- return crc32(self.data)
2071-
2072- def decode(cls, bytes, hostPortPair):
2073- fields = struct.unpack(_packetFormat, bytes[:_fixedSize])
2074- sourcePseudoPort, destPseudoPort, seq, ack, window, flags, checksum, dlen = fields
2075- data = bytes[_fixedSize:]
2076- pkt = cls(sourcePseudoPort, destPseudoPort, seq, ack, window, flags,
2077- checksum, dlen, data, hostPortPair)
2078- return pkt
2079- decode = classmethod(decode)
2080-
2081- def mustRetransmit(self):
2082- """Check to see if this packet must be retransmitted until it was received.
2083- """
2084- if self.syn or self.fin or self.dlen:
2085- return True
2086- return False
2087-
2088- def encode(self):
2089- dlen = len(self.data)
2090- checksum = self.computeChecksum()
2091- return struct.pack(
2092- _packetFormat,
2093- self.sourcePseudoPort, self.destPseudoPort,
2094- self.seqNum, self.ackNum, self.window,
2095- self.flags, checksum, dlen) + self.data
2096-
2097- def fragment(self, mtu):
2098- if self.dlen < mtu:
2099- return [self]
2100- assert not self.syn, "should not be originating syn packets w/ data"
2101- seqOfft = 0
2102- L = []
2103- # XXX TODO: need to take seqLaps into account, etc.
2104- for chunk in iterchunks(self.data, mtu):
2105- last = self.create(self.sourcePseudoPort,
2106- self.destPseudoPort,
2107- self.seqNum + seqOfft,
2108- self.ackNum,
2109- chunk,
2110- self.window,
2111- destination=self.destination,
2112- ack=self.ack)
2113- L.append(last)
2114- seqOfft += len(chunk)
2115- if self.fin:
2116- last.fin = self.fin
2117- last.checksum = last.computeChecksum()
2118- return L
2119-
2120-
2121-def iterchunks(data, chunksize):
2122- """iterate chunks of data
2123- """
2124- offt = 0
2125- while offt < len(data):
2126- yield data[offt:offt+chunksize]
2127- offt += chunksize
2128-
2129-
2130-def ISN():
2131- """
2132- Initial Sequence Number generator.
2133- """
2134- # return int((time.time() * 1000000) / 4) % 2**32
2135- return 0
2136-
2137-
2138-def segmentAcceptable(RCV_NXT, RCV_WND, SEG_SEQ, SEG_LEN):
2139- # RFC page 26.
2140- if SEG_LEN == 0 and RCV_WND == 0:
2141- return SEG_SEQ == RCV_NXT
2142- if SEG_LEN == 0 and RCV_WND > 0:
2143- return ((RCV_NXT <= SEG_SEQ) and (SEG_SEQ < RCV_NXT + RCV_WND))
2144- if SEG_LEN > 0 and RCV_WND == 0:
2145- return False
2146- if SEG_LEN > 0 and RCV_WND > 0:
2147- return (( (RCV_NXT <= SEG_SEQ) and (SEG_SEQ < RCV_NXT + RCV_WND))
2148- or ((RCV_NXT <= SEG_SEQ+SEG_LEN-1) and
2149- (SEG_SEQ+SEG_LEN-1 < RCV_NXT + RCV_WND)))
2150- assert 0, 'Should be impossible to get here.'
2151- return False
2152-
2153-class BadPacketError(Exception):
2154- """
2155- A packet was bad for some reason.
2156- """
2157-
2158-class ChecksumMismatchError(Exception):
2159- """
2160- The checksum and data received did not match.
2161- """
2162-
2163-class TruncatedDataError(Exception):
2164- """
2165- The packet was truncated in transit, and all of the data did not arrive.
2166- """
2167-
2168-class GarbageDataError(Exception):
2169- """
2170- Too much data was received (???)
2171- """
2172-
2173-class PTCPConnection(tcpdfa.TCP):
2174- """
2175- Implementation of RFC 793 state machine.
2176-
2177- @ivar oldestUnackedSendSeqNum: (TCP RFC: SND.UNA) The oldest (relative)
2178- sequence number referring to an octet which we have sent or may send which
2179- is unacknowledged. This begins at 0, which is special because it is not
2180- for an octet, but rather for the initial SYN packet. Unless it is 0, this
2181- represents the sequence number of self._outgoingBytes[0].
2182-
2183- @ivar nextSendSeqNum: (TCP RFC: SND.NXT) The next (relative) sequence
2184- number that we will send to our peer after the current buffered segments
2185- have all been acknowledged. This is the sequence number of the
2186- not-yet-extant octet in the stream at
2187- self._outgoingBytes[len(self._outgoingBytes)].
2188-
2189- @ivar nextRecvSeqNum: (TCP RFC: RCV.NXT) The next (relative) sequence
2190- number that the peer should send to us if they want to send more data;
2191- their first unacknowledged sequence number as far as we are concerned; the
2192- left or lower edge of the receive window; the sequence number of the first
2193- octet that has not been delivered to the application. changed whenever we
2194- receive an appropriate ACK.
2195-
2196- @ivar peerSendISN: the initial sequence number that the peer sent us during
2197- the negotiation phase. All peer-relative sequence numbers are computed
2198- using this. (see C{relativeSequence}).
2199-
2200- @ivar hostSendISN: the initial sequence number that the we sent during the
2201- negotiation phase. All host-relative sequence numbers are computed using
2202- this. (see C{relativeSequence})
2203-
2204- @ivar retransmissionQueue: a list of packets to be re-sent until their
2205- acknowledgements come through.
2206-
2207- @ivar recvWindow: (TCP RFC: RCV.WND) - the size [in octets] of the current
2208- window allowed by this host, to be in transit from the other host.
2209-
2210- @ivar sendWindow: (TCP RFC: SND.WND) - the size [in octets] of the current
2211- window allowed by our peer, to be in transit from us.
2212-
2213- """
2214-
2215- mtu = 512 - _fixedSize
2216-
2217- recvWindow = mtu
2218- sendWindow = mtu
2219- sendWindowRemaining = mtu * 2
2220-
2221- protocol = None
2222-
2223- def __init__(self,
2224- hostPseudoPort, peerPseudoPort,
2225- ptcp, factory, peerAddressTuple):
2226- tcpdfa.TCP.__init__(self)
2227- self.hostPseudoPort = hostPseudoPort
2228- self.peerPseudoPort = peerPseudoPort
2229- self.ptcp = ptcp
2230- self.factory = factory
2231- self._receiveBuffer = []
2232- self.retransmissionQueue = []
2233- self.peerAddressTuple = peerAddressTuple
2234-
2235- self.oldestUnackedSendSeqNum = 0
2236- self.nextSendSeqNum = 0
2237- self.hostSendISN = 0
2238- self.nextRecvSeqNum = 0
2239- self.peerSendISN = 0
2240- self.setPeerISN = False
2241-
2242- peerSendISN = None
2243-
2244- def packetReceived(self, packet):
2245- # XXX TODO: probably have to do something to the packet here to
2246- # identify its relative sequence number.
2247-
2248- # print 'received', self, packet
2249-
2250- if packet.stb:
2251- # Shrink the MTU
2252- [self.mtu] = struct.unpack('!H', packet.data)
2253- rq = []
2254- for pkt in self.retransmissionQueue:
2255- rq.extend(pkt.fragment(self.mtu))
2256- self.retransmissionQueue = rq
2257- return
2258-
2259- if self._paused:
2260- return
2261-
2262- generatedStateMachineInput = False
2263- if packet.syn:
2264- if packet.dlen:
2265- # Whoops, what? SYNs probably can contain data, I think, but I
2266- # certainly don't see anything in the spec about how to deal
2267- # with this or in ethereal for how linux deals with it -glyph
2268- raise BadPacketError(
2269- "currently no data allowed in SYN packets: %r"
2270- % (packet,))
2271- else:
2272- assert packet.segmentLength() == 1
2273- if self.peerAddressTuple is None:
2274- # we're a server
2275- assert self.wasEverListen, "Clients must specify a connect address."
2276- self.peerAddressTuple = packet.peerAddressTuple
2277- else:
2278- # we're a client
2279- assert self.peerAddressTuple == packet.peerAddressTuple
2280- if self.setPeerISN:
2281- if self.peerSendISN != packet.seqNum:
2282- raise BadPacketError(
2283- "Peer ISN was already set to %s but incoming packet "
2284- "tried to set it to %s" % (
2285- self.peerSendISN, packet.seqNum))
2286- if not self.retransmissionQueue:
2287- # If our retransmissionQueue is hot, we are going to send
2288- # them an ACK to this with the next packet we send them
2289- # anyway; as a bonus, this will properly determine whether
2290- # we're sending a SYN+ACK or merely an ACK; the only time
2291- # we send an ACK is when we have nothing to say to them and
2292- # they're blocked on getting a response to their SYN+ACK
2293- # from us. -glyph
2294- self.originate(ack=True)
2295- return
2296- self.setPeerISN = True
2297- self.peerSendISN = packet.seqNum
2298- # syn, fin, and data are mutually exclusive, so this relative
2299- # sequence-number increment is done both here, and below in the
2300- # data/fin processing block.
2301- self.nextRecvSeqNum += packet.segmentLength()
2302- if not packet.ack:
2303- generatedStateMachineInput = True
2304- self.input(tcpdfa.SYN)
2305-
2306- SEG_ACK = packet.relativeAck() # aliasing this for easier reading w/
2307- # the RFC
2308- if packet.ack:
2309- if (self.oldestUnackedSendSeqNum < SEG_ACK and
2310- SEG_ACK <= self.nextSendSeqNum):
2311- # According to the spec, an 'acceptable ack
2312- rq = self.retransmissionQueue
2313- while rq:
2314- segmentOnQueue = rq[0]
2315- qSegSeq = segmentOnQueue.relativeSeq()
2316- if qSegSeq + segmentOnQueue.segmentLength() <= SEG_ACK:
2317- # fully acknowledged, as per RFC!
2318- rq.pop(0)
2319- sminput = None
2320- self.sendWindowRemaining += segmentOnQueue.segmentLength()
2321- # print 'inc send window', self, self.sendWindowRemaining
2322- if segmentOnQueue.syn:
2323- if packet.syn:
2324- sminput = tcpdfa.SYN_ACK
2325- else:
2326- sminput = tcpdfa.ACK
2327- elif segmentOnQueue.fin:
2328- sminput = tcpdfa.ACK
2329- if sminput is not None:
2330- # print 'ack input:', segmentOnQueue, packet, sminput
2331- generatedStateMachineInput = True
2332- self.input(sminput)
2333- else:
2334- break
2335- else:
2336- # write buffer is empty; alert the application layer.
2337- self._writeBufferEmpty()
2338- self.oldestUnackedSendSeqNum = SEG_ACK
2339-
2340- if packet.syn:
2341- assert generatedStateMachineInput
2342- return
2343-
2344- # XXX TODO: examine 'window' field and adjust sendWindowRemaining
2345- # is it 'occupying a portion of valid receive sequence space'? I think
2346- # this means 'packet which might acceptably contain useful data'
2347- if not packet.segmentLength():
2348- assert packet.ack, "What the _HELL_ is wrong with this packet:" +str(packet)
2349- return
2350-
2351- if not segmentAcceptable(self.nextRecvSeqNum,
2352- self.recvWindow,
2353- packet.relativeSeq(),
2354- packet.segmentLength()):
2355- # We have to transmit an ack here since it's old data. We probably
2356- # need to ack in more states than just ESTABLISHED... but which
2357- # ones?
2358- if not self.retransmissionQueue:
2359- self.originate(ack=True)
2360- return
2361-
2362- # OK! It's acceptable! Let's process the various bits of data.
2363- # Where is the useful data in the packet?
2364- if packet.relativeSeq() > self.nextRecvSeqNum:
2365- # XXX: Here's what's going on. Data can be 'in the window', but
2366- # still in the future. For example, if I have a window of length 3
2367- # and I send segments DATA1(len 1) DATA2(len 1) FIN and you receive
2368- # them in the order FIN DATA1 DATA2, you don't actually want to
2369- # process the FIN until you've processed the data.
2370-
2371- # For the moment we are just dropping anything that isn't exactly
2372- # the next thing we want to process. This is perfectly valid;
2373- # these packets might have been dropped, so the other end will have
2374- # to retransmit them anyway.
2375- return
2376-
2377- if packet.dlen:
2378- assert not packet.syn, 'no seriously I _do not_ know how to handle this'
2379- usefulData = packet.data[self.nextRecvSeqNum - packet.relativeSeq():]
2380- # DONT check/slice the window size here, the acceptability code
2381- # checked it, we can over-ack if the other side is buggy (???)
2382- if self.protocol is not None:
2383- try:
2384- self.protocol.dataReceived(usefulData)
2385- except:
2386- log.err()
2387- self.loseConnection()
2388-
2389- self.nextRecvSeqNum += packet.segmentLength()
2390- if self.state == tcpdfa.ESTABLISHED:
2391- # In all other states, the state machine takes care of sending ACKs
2392- # in its output process.
2393- self.originate(ack=True)
2394-
2395- if packet.fin:
2396- self.input(tcpdfa.FIN)
2397-
2398-
2399- def getHost(self):
2400- tupl = self.ptcp.transport.getHost()
2401- return PTCPAddress((tupl.host, tupl.port),
2402- self.pseudoPortPair)
2403-
2404- def getPeer(self):
2405- return PTCPAddress(self.peerAddressTuple,
2406- self.pseudoPortPair)
2407-
2408- _outgoingBytes = ''
2409- _nagle = None
2410-
2411- def write(self, bytes):
2412- assert not self.disconnected, 'Writing to a transport that was already disconnected.'
2413- self._outgoingBytes += bytes
2414- self._writeLater()
2415-
2416-
2417- def writeSequence(self, seq):
2418- self.write(''.join(seq))
2419-
2420-
2421- def _writeLater(self):
2422- if self._nagle is None:
2423- self._nagle = reactor.callLater(0.001, self._reallyWrite)
2424-
2425- def _originateOneData(self):
2426- amount = min(self.sendWindowRemaining, self.mtu)
2427- sendOut = self._outgoingBytes[:amount]
2428- # print 'originating data packet', len(sendOut)
2429- self._outgoingBytes = self._outgoingBytes[amount:]
2430- self.sendWindowRemaining -= len(sendOut)
2431- self.originate(ack=True, data=sendOut)
2432-
2433- def _reallyWrite(self):
2434- # print self, 'really writing', self._paused
2435- self._nagle = None
2436- if self._outgoingBytes:
2437- # print 'window and bytes', self.sendWindowRemaining, len(self._outgoingBytes)
2438- while self.sendWindowRemaining and self._outgoingBytes:
2439- self._originateOneData()
2440-
2441- _retransmitter = None
2442- _retransmitTimeout = 0.5
2443-
2444- def _retransmitLater(self):
2445- assert self.state != tcpdfa.CLOSED
2446- if self._retransmitter is None:
2447- self._retransmitter = reactor.callLater(self._retransmitTimeout, self._reallyRetransmit)
2448-
2449- def _stopRetransmitting(self):
2450- # used both as a quick-and-dirty test shutdown hack and a way to shut
2451- # down when we die...
2452- if self._retransmitter is not None:
2453- self._retransmitter.cancel()
2454- self._retransmitter = None
2455- if self._nagle is not None:
2456- self._nagle.cancel()
2457- self._nagle = None
2458- if self._closeWaitLoseConnection is not None:
2459- self._closeWaitLoseConnection.cancel()
2460- self._closeWaitLoseConnection = None
2461-
2462- def _reallyRetransmit(self):
2463- # XXX TODO: packet fragmentation & coalescing.
2464- # print 'Wee a retransmit! What I got?', self.retransmissionQueue
2465- self._retransmitter = None
2466- if self.retransmissionQueue:
2467- for packet in self.retransmissionQueue:
2468- packet.retransmitCount -= 1
2469- if packet.retransmitCount:
2470- packet.ackNum = self.currentAckNum()
2471- self.ptcp.sendPacket(packet)
2472- else:
2473- self.input(tcpdfa.TIMEOUT)
2474- return
2475- self._retransmitLater()
2476-
2477- disconnecting = False # This is *TWISTED* level state-machine stuff,
2478- # not TCP-level.
2479-
2480- def loseConnection(self):
2481- if not self.disconnecting:
2482- self.disconnecting = True
2483- if not self._outgoingBytes:
2484- self._writeBufferEmpty()
2485-
2486-
2487- def _writeBufferEmpty(self):
2488- if self._outgoingBytes:
2489- self._reallyWrite()
2490- elif self.producer is not None:
2491- if (not self.streamingProducer) or self.producerPaused:
2492- self.producerPaused = False
2493- self.producer.resumeProducing()
2494- elif self.disconnecting and (not self.disconnected
2495- or self.state == tcpdfa.CLOSE_WAIT):
2496- self.input(tcpdfa.APP_CLOSE)
2497-
2498-
2499- def _writeBufferFull(self):
2500- # print 'my write buffer is full'
2501- if (self.producer is not None
2502- and not self.producerPaused):
2503- self.producerPaused = True
2504- # print 'producer pausing'
2505- self.producer.pauseProducing()
2506- # print 'producer paused'
2507- else:
2508- # print 'but I am not telling my producer to pause!'
2509- # print ' ', self.producer, self.streamingProducer, self.producerPaused
2510- pass
2511-
2512-
2513- disconnected = False
2514- producer = None
2515- producerPaused = False
2516- streamingProducer = False
2517-
2518- def registerProducer(self, producer, streaming):
2519- if self.producer is not None:
2520- raise RuntimeError(
2521- "Cannot register producer %s, "
2522- "because producer %s was never unregistered."
2523- % (producer, self.producer))
2524- if self.disconnected:
2525- producer.stopProducing()
2526- else:
2527- self.producer = producer
2528- self.streamingProducer = streaming
2529- if not streaming and not self._outgoingBytes:
2530- producer.resumeProducing()
2531-
2532- def unregisterProducer(self):
2533- self.producer = None
2534- if not self._outgoingBytes:
2535- self._writeBufferEmpty()
2536-
2537- _paused = False
2538- def pauseProducing(self):
2539- self._paused = True
2540-
2541- def resumeProducing(self):
2542- self._paused = False
2543-
2544- def currentAckNum(self):
2545- return (self.nextRecvSeqNum + self.peerSendISN) % (2**32)
2546-
2547- def originate(self, data='', syn=False, ack=False, fin=False):
2548- if syn:
2549- # We really should be randomizing the ISN but until we finish the
2550- # implementations of the various bits of wraparound logic that were
2551- # started with relativeSequence
2552- assert self.nextSendSeqNum == 0
2553- assert self.hostSendISN == 0
2554- p = PTCPPacket.create(self.hostPseudoPort,
2555- self.peerPseudoPort,
2556- seqNum=(self.nextSendSeqNum + self.hostSendISN) % (2**32),
2557- ackNum=self.currentAckNum(),
2558- data=data,
2559- window=self.recvWindow,
2560- syn=syn, ack=ack, fin=fin,
2561- destination=self.peerAddressTuple)
2562- # do we want to enqueue this packet for retransmission?
2563- sl = p.segmentLength()
2564- self.nextSendSeqNum += sl
2565-
2566- if p.mustRetransmit():
2567- # print self, 'originating retransmittable packet', len(self.retransmissionQueue)
2568- if self.retransmissionQueue:
2569- if self.retransmissionQueue[-1].fin:
2570- raise AssertionError("Sending %r after FIN??!" % (p,))
2571- # print 'putting it on the queue'
2572- self.retransmissionQueue.append(p)
2573- # print 'and sending it later'
2574- self._retransmitLater()
2575- if not self.sendWindowRemaining: # len(self.retransmissionQueue) > 5:
2576- # print 'oh no my queue is too big'
2577- # This is a random number (5) because I ought to be summing the
2578- # packet lengths or something.
2579- self._writeBufferFull()
2580- else:
2581- # print 'my queue is still small enough', len(self.retransmissionQueue), self, self.sendWindowRemaining
2582- pass
2583- self.ptcp.sendPacket(p)
2584-
2585- # State machine transition definitions, hooray.
2586- def transition_SYN_SENT_to_CLOSED(self):
2587- """
2588- The connection never got anywhere. Goodbye.
2589- """
2590- # XXX CONNECTOR API OMFG
2591- self.factory.clientConnectionFailed(None, error.TimeoutError())
2592-
2593-
2594- wasEverListen = False
2595-
2596- def enter_LISTEN(self):
2597- # Spec says this is necessary for RST handling; we need it for making
2598- # sure it's OK to bind port numbers.
2599- self.wasEverListen = True
2600-
2601- def enter_CLOSED(self):
2602- self.ptcp.connectionClosed(self)
2603- self._stopRetransmitting()
2604- if self._timeWaitCall is not None:
2605- self._timeWaitCall.cancel()
2606- self._timeWaitCall = None
2607-
2608- _timeWaitCall = None
2609- _timeWaitTimeout = 0.01 # REALLY fast timeout, right now this is for
2610- # the tests...
2611-
2612- def enter_TIME_WAIT(self):
2613- self._stopRetransmitting()
2614- self._timeWaitCall = reactor.callLater(self._timeWaitTimeout, self._do2mslTimeout)
2615-
2616- def _do2mslTimeout(self):
2617- self._timeWaitCall = None
2618- self.input(tcpdfa.TIMEOUT)
2619-
2620- peerAddressTuple = None
2621-
2622- def transition_LISTEN_to_SYN_SENT(self):
2623- """
2624- Uh, what? We were listening and we tried to send some bytes.
2625- This is an error for PTCP.
2626- """
2627- raise StateError("You can't write anything until someone connects to you.")
2628-
2629-# def invalidInput(self, datum):
2630-# print self, self.protocol, 'invalid input', datum
2631-
2632- def pseudoPortPair():
2633- def get(self):
2634- return (self.hostPseudoPort,
2635- self.peerPseudoPort)
2636- return get,
2637- pseudoPortPair = property(*pseudoPortPair())
2638-
2639- def enter_ESTABLISHED(self):
2640- """
2641- We sent out SYN, they acknowledged it. Congratulations, you
2642- have a new baby connection.
2643- """
2644- assert not self.disconnecting
2645- assert not self.disconnected
2646- try:
2647- p = self.factory.buildProtocol(PTCPAddress(
2648- self.peerAddressTuple, self.pseudoPortPair))
2649- p.makeConnection(self)
2650- except:
2651- log.msg("Exception during PTCP connection setup.")
2652- log.err()
2653- self.loseConnection()
2654- else:
2655- self.protocol = p
2656-
2657- def exit_ESTABLISHED(self):
2658- assert not self.disconnected
2659- self.disconnected = True
2660- try:
2661- self.protocol.connectionLost(Failure(CONNECTION_DONE))
2662- except:
2663- log.err()
2664- self.protocol = None
2665-
2666- if self.producer is not None:
2667- try:
2668- self.producer.stopProducing()
2669- except:
2670- log.err()
2671- self.producer = None
2672-
2673-
2674- _closeWaitLoseConnection = None
2675-
2676- def enter_CLOSE_WAIT(self):
2677- # Twisted automatically reacts to network half-close by issuing a full
2678- # close.
2679- self._closeWaitLoseConnection = reactor.callLater(0.01, self._loseConnectionBecauseOfCloseWait)
2680-
2681- def _loseConnectionBecauseOfCloseWait(self):
2682- self._closeWaitLoseConnection = None
2683- self.loseConnection()
2684-
2685- def immediateShutdown(self):
2686- """_IMMEDIATELY_ shut down this connection, sending one (non-retransmitted)
2687- app-close packet, emptying our buffers, clearing our producer and
2688- getting ready to die right after this call.
2689- """
2690- self._outgoingBytes = ''
2691- if self.state == tcpdfa.ESTABLISHED:
2692- self.input(tcpdfa.APP_CLOSE)
2693- self._stopRetransmitting()
2694- self._reallyRetransmit()
2695-
2696- # All states that we can reasonably be in handle a timeout; force our
2697- # connection to think that it's become desynchronized with the other
2698- # end so that it will totally shut itself down.
2699-
2700- self.input(tcpdfa.TIMEOUT)
2701- assert self._retransmitter is None
2702- assert self._nagle is None
2703-
2704- def output_ACK(self):
2705- self.originate(ack=True)
2706-
2707- def output_FIN(self):
2708- self.originate(fin=True)
2709-
2710- def output_SYN_ACK(self):
2711- self.originate(syn=True, ack=True)
2712-
2713- def output_SYN(self):
2714- self.originate(syn=True)
2715-
2716-class PTCPAddress(object):
2717- # garbage
2718-
2719- def __init__(self, (host, port), (pseudoHostPort, pseudoPeerPort)):
2720- self.host = host
2721- self.port = port
2722- self.pseudoHostPort = pseudoHostPort
2723- self.pseudoPeerPort = pseudoPeerPort
2724-
2725- def __repr__(self):
2726- return 'PTCPAddress((%r, %r), (%r, %r))' % (
2727- self.host, self.port,
2728- self.pseudoHostPort,
2729- self.pseudoPeerPort)
2730-
2731-
2732-
2733-class _PendingEvent(object):
2734- def __init__(self):
2735- self.listeners = []
2736-
2737-
2738- def deferred(self):
2739- d = Deferred()
2740- self.listeners.append(d)
2741- return d
2742-
2743-
2744- def callback(self, result):
2745- l = self.listeners
2746- self.listeners = []
2747- for d in l:
2748- d.callback(result)
2749-
2750-
2751- def errback(self, result=None):
2752- if result is None:
2753- result = Failure()
2754- l = self.listeners
2755- self.listeners = []
2756- for d in l:
2757- d.errback(result)
2758-
2759-
2760-
2761-class PTCP(protocol.DatagramProtocol):
2762- """
2763- L{PTCP} implements a strongly TCP-like protocol on top of UDP. It
2764- provides a transport which is connection-oriented, streaming,
2765- ordered, and reliable.
2766-
2767- @ivar factory: A L{ServerFactory} which is used to create
2768- L{IProtocol} providers whenever a new PTCP connection is made
2769- to this port.
2770-
2771- @ivar _connections: A mapping of endpoint addresses to connection
2772- objects. These are the active connections being multiplexed
2773- over this UDP port. Many PTCP connections may run over the
2774- same L{PTCP} instance, communicating with many different
2775- remote hosts as well as multiplexing different PTCP
2776- connections to the same remote host. The mapping keys,
2777- endpoint addresses, are three-tuples of:
2778-
2779- - The destination pseudo-port which is always C{1}
2780- - The source pseudo-port
2781- - A (host, port) tuple giving the UDP address of a PTCP
2782- peer holding the other side of the connection
2783-
2784- The mapping values, connection objects, are L{PTCPConnection}
2785- instances.
2786- @type _connections: C{dict}
2787-
2788- """
2789- # External API
2790-
2791- def __init__(self, factory):
2792- self.factory = factory
2793- self._allConnectionsClosed = _PendingEvent()
2794-
2795-
2796- def connect(self, factory, host, port, pseudoPort=1):
2797- """
2798- Attempt to establish a new connection via PTCP to the given
2799- remote address.
2800-
2801- @param factory: A L{ClientFactory} which will be used to
2802- create an L{IProtocol} provider if the connection is
2803- successfully set up, or which will have failure callbacks
2804- invoked on it otherwise.
2805-
2806- @param host: The IP address of another listening PTCP port to
2807- connect to.
2808- @type host: C{str}
2809-
2810- @param port: The port number of that other listening PTCP port
2811- to connect to.
2812- @type port: C{int}
2813-
2814- @param pseudoPort: Not really implemented. Do not pass a
2815- value for this parameter or things will break.
2816-
2817- @return: A L{PTCPConnection} instance representing the new
2818- connection, but you really shouldn't use this for
2819- anything. Write a protocol!
2820- """
2821- sourcePseudoPort = genConnID() % MAX_PSEUDO_PORT
2822- conn = self._connections[(pseudoPort, sourcePseudoPort, (host, port))
2823- ] = PTCPConnection(
2824- sourcePseudoPort, pseudoPort, self, factory, (host, port))
2825- conn.input(tcpdfa.APP_ACTIVE_OPEN)
2826- return conn
2827-
2828- def sendPacket(self, packet):
2829- if self.transportGoneAway:
2830- return
2831- self.transport.write(packet.encode(), packet.destination)
2832-
2833-
2834- # Internal stuff
2835- def startProtocol(self):
2836- self.transportGoneAway = False
2837- self._lastConnID = 10 # random.randrange(2 ** 32)
2838- self._connections = {}
2839-
2840- def _finalCleanup(self):
2841- """
2842- Clean up all of our connections by issuing application-level close and
2843- stop notifications, sending hail-mary final FIN packets (which may not
2844- reach the other end, but nevertheless can be useful) when possible.
2845- """
2846- for conn in self._connections.values():
2847- conn.immediateShutdown()
2848- assert not self._connections
2849-
2850- def stopProtocol(self):
2851- """
2852- Notification from twisted that our underlying port has gone away;
2853- make sure we're not going to try to send any packets through our
2854- transport and blow up, then shut down all of our protocols, issuing
2855- appr
2856- opriate application-level messages.
2857- """
2858- self.transportGoneAway = True
2859- self._finalCleanup()
2860-
2861- def cleanupAndClose(self):
2862- """
2863- Clean up all remaining connections, then close our transport.
2864-
2865- Although in a pinch we will do cleanup after our socket has gone away
2866- (if it does so unexpectedly, above in stopProtocol), we would really
2867- prefer to do cleanup while we still have access to a transport, since
2868- that way we can force out a few final packets and save the remote
2869- application an awkward timeout (if it happens to get through, which
2870- is generally likely).
2871- """
2872- self._finalCleanup()
2873- return self._stop()
2874-
2875- def datagramReceived(self, bytes, addr):
2876- if len(bytes) < _fixedSize:
2877- # It can't be any good.
2878- return
2879-
2880- pkt = PTCPPacket.decode(bytes, addr)
2881- try:
2882- pkt.verifyChecksum()
2883- except TruncatedDataError:
2884-# print '(ptcp packet truncated: %r)' % (pkt,)
2885- self.sendPacket(
2886- PTCPPacket.create(
2887- pkt.destPseudoPort,
2888- pkt.sourcePseudoPort,
2889- 0,
2890- 0,
2891- struct.pack('!H', len(pkt.data)),
2892- stb=True,
2893- destination=addr))
2894- except GarbageDataError:
2895- print "garbage data!", pkt
2896- except ChecksumMismatchError, cme:
2897- print "bad checksum", pkt, cme
2898- print repr(pkt.data)
2899- print hex(pkt.checksum), hex(pkt.computeChecksum())
2900- else:
2901- self.packetReceived(pkt)
2902-
2903- stopped = False
2904- def _stop(self, result=None):
2905- if not self.stopped:
2906- self.stopped = True
2907- return self.transport.stopListening()
2908- else:
2909- return defer.succeed(None)
2910-
2911- def waitForAllConnectionsToClose(self):
2912- """
2913- Wait for all currently-open connections to enter the 'CLOSED' state.
2914- Currently this is only usable from test fixtures.
2915- """
2916- if not self._connections:
2917- return self._stop()
2918- return self._allConnectionsClosed.deferred().addBoth(self._stop)
2919-
2920- def connectionClosed(self, ptcpConn):
2921- packey = (ptcpConn.peerPseudoPort, ptcpConn.hostPseudoPort,
2922- ptcpConn.peerAddressTuple)
2923- del self._connections[packey]
2924- if ((not self.transportGoneAway) and
2925- (not self._connections) and
2926- self.factory is None):
2927- self._stop()
2928- if not self._connections:
2929- self._allConnectionsClosed.callback(None)
2930-
2931- def packetReceived(self, packet):
2932- packey = (packet.sourcePseudoPort, packet.destPseudoPort, packet.peerAddressTuple)
2933- if packey not in self._connections:
2934- if packet.flags == _SYN and packet.destPseudoPort == 1: # SYN and _ONLY_ SYN set.
2935- conn = PTCPConnection(packet.destPseudoPort,
2936- packet.sourcePseudoPort, self,
2937- self.factory, packet.peerAddressTuple)
2938- conn.input(tcpdfa.APP_PASSIVE_OPEN)
2939- self._connections[packey] = conn
2940- else:
2941- log.msg("corrupted packet? %r %r %r" % (packet,packey, self._connections))
2942- return
2943- try:
2944- self._connections[packey].packetReceived(packet)
2945- except:
2946- log.msg("PTCPConnection error on %r:" % (packet,))
2947- log.err()
2948- del self._connections[packey]
2949
2950=== removed file 'Vertex/vertex/q2q.py'
2951--- Vertex/vertex/q2q.py 2012-03-14 23:42:53 +0000
2952+++ Vertex/vertex/q2q.py 1970-01-01 00:00:00 +0000
2953@@ -1,2763 +0,0 @@
2954-# -*- test-case-name: vertex.test.test_q2q -*-
2955-# Copyright 2005-2008 Divmod, Inc. See LICENSE file for details
2956-
2957-"""
2958-I{Quotient to Quotient} protocol implementation.
2959-"""
2960-
2961-# stdlib
2962-import itertools
2963-from hashlib import md5
2964-import struct
2965-import datetime
2966-import time
2967-from collections import namedtuple
2968-
2969-from pprint import pformat
2970-
2971-from zope.interface import implements
2972-
2973-# twisted
2974-from twisted.internet import reactor, defer, interfaces, protocol, error
2975-from twisted.internet.main import CONNECTION_DONE
2976-from twisted.internet.ssl import (
2977- CertificateRequest, Certificate, PrivateCertificate, KeyPair,
2978- DistinguishedName)
2979-from twisted.python import log
2980-from twisted.python.failure import Failure
2981-from twisted.application import service
2982-
2983-# twisted.cred
2984-from twisted.cred.checkers import ICredentialsChecker
2985-from twisted.cred.portal import IRealm, Portal
2986-from twisted.cred.credentials import IUsernamePassword, UsernamePassword
2987-from twisted.cred.error import UnauthorizedLogin
2988-
2989-from twisted.protocols.amp import Argument, Boolean, Integer, String, Unicode, ListOf, AmpList
2990-from twisted.protocols.amp import AmpBox, Command, StartTLS, ProtocolSwitchCommand, AMP
2991-from twisted.protocols.amp import _objectsToStrings
2992-
2993-# vertex
2994-from vertex import subproducer, ptcp
2995-from vertex import endpoint, ivertex
2996-from vertex.conncache import ConnectionCache
2997-
2998-MESSAGE_PROTOCOL = 'q2q-message'
2999-port = 8788
3000-
3001-class ConnectionError(Exception):
3002- pass
3003-
3004-class AttemptsFailed(ConnectionError):
3005- pass
3006-
3007-class NoAttemptsMade(ConnectionError):
3008- pass
3009-
3010-class VerifyError(Exception):
3011- pass
3012-
3013-class BadCertificateRequest(VerifyError):
3014- pass
3015-
3016-class IgnoreConnectionFailed(protocol.ClientFactory):
3017- def __init__(self, realFactory):
3018- self.realFactory = realFactory
3019-
3020- def clientConnectionLost(self, connector, reason):
3021- self.realFactory.clientConnectionLost(connector, reason)
3022-
3023- def clientConnectionFailed(self, connector, reason):
3024- pass
3025-
3026- def buildProtocol(self, addr):
3027- return self.realFactory.buildProtocol(addr)
3028-
3029-class Q2QAddress(object):
3030- def __init__(self, domain, resource=None):
3031- self.resource = resource
3032- self.domain = domain
3033-
3034- def domainAddress(self):
3035- """ Return an Address object which is the same as this one with ONLY the
3036- 'domain' attribute set, not 'resource'.
3037-
3038- May return 'self' if 'resource' is already None.
3039- """
3040- if self.resource is None:
3041- return self
3042- else:
3043- return Q2QAddress(self.domain)
3044-
3045- def claimedAsIssuerOf(self, cert):
3046- """
3047- Check if the information in a provided certificate *CLAIMS* to be issued by
3048- this address.
3049-
3050- PLEASE NOTE THAT THIS METHOD IS IN NO WAY AUTHORITATIVE. It does not
3051- perform any cryptographic checks.
3052-
3053- Currently this check is if L{Q2QAddress.__str__}C{(self)} is equivalent
3054- to the commonName on the certificate's issuer.
3055- """
3056- return cert.getIssuer().commonName == str(self)
3057-
3058- def claimedAsSubjectOf(self, cert):
3059- """
3060- Check if the information in a provided certificate *CLAIMS* to be
3061- provided for use by this address.
3062-
3063- PLEASE NOTE THAT THIS METHOD IS IN NO WAY AUTHORITATIVE. It does not
3064- perform any cryptographic checks.
3065-
3066- Currently this check is if L{Q2QAddress.__str__}C{(self)} is equivalent
3067- to the commonName on the certificate's subject.
3068- """
3069- return cert.getSubject().commonName == str(self)
3070-
3071- def __cmp__(self, other):
3072- if not isinstance(other, Q2QAddress):
3073- return cmp(self.__class__, other.__class__)
3074- return cmp((self.domain, self.resource), (other.domain, other.resource))
3075-
3076- def __iter__(self):
3077- return iter((self.resource, self.domain))
3078-
3079- def __str__(self):
3080- """
3081- Return a string of the normalized form of this address. e.g.::
3082-
3083- glyph@divmod.com # for a user
3084- divmod.com # for a domain
3085- """
3086- if self.resource:
3087- resource = self.resource + '@'
3088- else:
3089- resource = ''
3090- return (resource + self.domain).encode('utf-8')
3091-
3092- def __repr__(self):
3093- return '<Q2Q at %s>' % self.__str__()
3094-
3095- def __hash__(self):
3096- return hash(str(self))
3097-
3098- def fromString(cls, string):
3099- args = string.split("@",1)
3100- args.reverse()
3101- return cls(*args)
3102- fromString = classmethod(fromString)
3103-
3104-
3105-class VirtualTransportAddress:
3106- def __init__(self, underlying):
3107- self.underlying = underlying
3108-
3109- def __repr__(self):
3110- return 'VirtualTransportAddress(%r)' % (self.underlying,)
3111-
3112-class Q2QTransportAddress:
3113- """
3114- The return value of getPeer() and getHost() for Q2Q-enabled transports.
3115- Passed to buildProtocol of factories passed to listenQ2Q.
3116-
3117- @ivar underlying: The return value of the underlying transport's getPeer()
3118- or getHost(); an address which indicates the path which the bytes carrying
3119- Q2Q traffic are travelling over. It is tempting to think of this as a
3120- 'physical' layer but that it not necessarily accurate; there are
3121- potentially multiple layers of wrapping on any Q2Q transport, as an SSL
3122- transport may be tunnelled over a UDP NAT-traversal layer. Implements
3123- C{IAddress} from Twisted, for all the good that will do you.
3124-
3125- @ivar logical: a L{Q2QAddress}, The logical peer; the user ostensibly
3126- listening to data on the other end of this transport.
3127-
3128- @ivar protocol: a L{str}, the name of the protocol that is connected.
3129- """
3130-
3131- def __init__(self, underlying, logical, protocol):
3132- self.underlying = underlying
3133- self.logical = logical
3134- self.protocol = protocol
3135-
3136- def __repr__(self):
3137- return 'Q2QTransportAddress(%r, %r, %r)' % (
3138- self.underlying,
3139- self.logical,
3140- self.protocol)
3141-
3142-
3143-class AmpTime(Argument):
3144- def toString(self, inObject):
3145- return inObject.strftime("%Y-%m-%dT%H:%M:%S")
3146-
3147-
3148- def fromString(self, inString):
3149- return datetime.datetime.strptime(inString, "%Y-%m-%dT%H:%M:%S")
3150-
3151-
3152-
3153-class Q2QAddressArgument(Argument):
3154- fromString = Q2QAddress.fromString
3155- toString = Q2QAddress.__str__
3156-
3157-class HostPort(Argument):
3158- def toString(self, inObj):
3159- return "%s:%d" % tuple(inObj)
3160-
3161- def fromString(self, inStr):
3162- host, sPort = inStr.split(":")
3163- return (host, int(sPort))
3164-
3165-
3166-
3167-class _BinaryLoadable(String):
3168- def toString(self, arg):
3169- assert isinstance(arg, self.loader), "%r not %r" % (arg, self.loader)
3170- return String.toString(self, arg.dump())
3171-
3172- def fromString(self, arg):
3173- return self.loader.load(String.fromString(self, arg))
3174-
3175-class CertReq(_BinaryLoadable):
3176- loader = CertificateRequest
3177-
3178-class Cert(_BinaryLoadable):
3179- loader = Certificate
3180-
3181-from twisted.internet import protocol
3182-
3183-class Q2QClientProtocolFactoryWrapper:
3184-
3185- def __init__(self, service, cpf, fromAddress, toAddress, protocolName,
3186- connectionEstablishedDeferred):
3187- self.service = service
3188- self.cpf = cpf
3189- self.fromAddress = fromAddress
3190- self.toAddress = toAddress
3191- self.protocolName = protocolName
3192- self.connectionEstablishedDeferred = connectionEstablishedDeferred
3193- connectionEstablishedDeferred.addCallback(self.setMyClient)
3194-
3195- myClient = None
3196- def setMyClient(self, myClient):
3197- # print '***CLIENT SET***', self, self.fromAddress, self.toAddress, self.cpf
3198- self.myClient = myClient
3199- return myClient
3200-
3201- def buildProtocol(self, addr):
3202- # xxx modify addr to include q2q information.
3203- subProto = self.cpf.buildProtocol(self.toAddress)
3204- myProto = SeparateConnectionTransport(self.service, subProto, self.fromAddress,
3205- self.toAddress, self.protocolName,
3206- self.connectionEstablishedDeferred)
3207- return myProto
3208-
3209- def clientConnectionFailed(self, connector, reason):
3210- # DON'T forward this to our client protocol factory; only one attempt
3211- # has failed; let that happen later, when _ALL_ attempts have failed.
3212- assert self.myClient is None
3213- self.connectionEstablishedDeferred.errback(reason)
3214-
3215- def clientConnectionLost(self, connector, reason):
3216- # as in clientConnectionFailed, don't bother to forward; this
3217- # clientConnectionLost is actually a clientConnectionFailed for the
3218- # underlying transport.
3219- if self.myClient is not None:
3220- # forward in this case because it's likely that we need to pass it
3221- # along...
3222- self.cpf.clientConnectionLost(connector, reason)
3223-
3224- def doStart(self):
3225- self.cpf.doStart()
3226-
3227- def doStop(self):
3228- self.cpf.doStop()
3229-
3230-class ImmediatelyLoseConnection(protocol.Protocol):
3231- def connectionMade(self):
3232- self.transport.loseConnection()
3233-
3234-class AbstractConnectionAttempt(protocol.ClientFactory):
3235-
3236-
3237- def __init__(self, method, q2qproto, connectionID, fromAddress, toAddress,
3238- protocolName, clientProtocolFactory, issueGreeting=False):
3239- self.method = method
3240- self.q2qproto = q2qproto
3241- assert isinstance(connectionID, str)
3242- self.connectionID = connectionID
3243- self.q2qproto = q2qproto
3244- self.fromAddress = fromAddress
3245- self.toAddress = toAddress
3246- self.protocolName = protocolName
3247- self.deferred = defer.Deferred()
3248- self.clientProtocolFactory = Q2QClientProtocolFactoryWrapper(
3249- q2qproto.service,
3250- clientProtocolFactory, fromAddress, toAddress, protocolName,
3251- self.deferred)
3252- self.issueGreeting = issueGreeting
3253-
3254-
3255- def startAttempt(self):
3256- """
3257- +-+
3258- |?|
3259- +-+
3260- """
3261- raise NotImplementedError()
3262-
3263-
3264- q2qb = None
3265-
3266- cancelled = False
3267-
3268- def buildProtocol(self, addr):
3269- if self.cancelled:
3270- return ImmediatelyLoseConnection()
3271- assert self.q2qb is None
3272- self.q2qb = Q2QBootstrap(
3273- self.connectionID, self.clientProtocolFactory)
3274- return self.q2qb
3275-
3276- def clientConnectionFailed(self, connector, reason):
3277- """
3278- """
3279- # Don't bother forwarding. In fact this should probably never be
3280- # called because we're not bothering to forward them along from
3281- # Q2QClientProtocolFactoryWrapper
3282-
3283- def clientConnectionLost(self, connector, reason):
3284- """
3285- """
3286- # we don't care - this will be handled by Q2QBootstrap.
3287-
3288- def cancel(self):
3289- """
3290- - Stop attempting to connect.
3291-
3292- - If a connection is somehow made after this has been cancelled, reject
3293- it.
3294-
3295- - Clean up any resources, such as listening UDP or TCP ports,
3296- associated with this connection attempt [obviously, that are unshared
3297- by other connection attempt]
3298-
3299- """
3300- self.cancelled = True
3301-
3302-
3303-class TCPConnectionAttempt(AbstractConnectionAttempt):
3304- attempted = False
3305- def startAttempt(self):
3306- assert not self.attempted
3307- self.attempted = True
3308- reactor.connectTCP(self.method.host, self.method.port, self)
3309- return self.deferred
3310-
3311-
3312-class TCPMethod:
3313- def __init__(self, hostport):
3314- self.host, port = hostport.split(':')
3315- self.port = int(port)
3316-
3317- attemptFactory = TCPConnectionAttempt
3318- relayable = True
3319- ptype = 'tcp'
3320-
3321- def toString(self):
3322- return '%s@%s:%d' % (self.ptype, self.host, self.port)
3323-
3324- def __repr__(self):
3325- return '<%s>'%self.toString()
3326-
3327- def attempt(self, *a):
3328- return [self.attemptFactory(self, *a)]
3329-
3330-connectionCounter = itertools.count().next
3331-connectionCounter()
3332-
3333-class VirtualConnectionAttempt(AbstractConnectionAttempt):
3334- attempted = False
3335- def startAttempt(self):
3336- assert not self.attempted
3337- self.attempted = True
3338- cid = connectionCounter()
3339- if self.q2qproto.isServer:
3340- cid = -cid
3341- innerTransport = VirtualTransport(self.q2qproto, cid, self, True)
3342- def startit(result):
3343- proto = innerTransport.startProtocol()
3344- return self.deferred
3345-
3346- d = self.q2qproto.callRemote(Virtual, id=cid)
3347- d.addCallback(startit)
3348- return d
3349-
3350-
3351-class VirtualMethod:
3352- def __init__(self, virt=None):
3353- pass
3354-
3355- relayable = False
3356-
3357- def toString(self):
3358- return 'virtual'
3359-
3360- def __repr__(self):
3361- return '<%s>' % (self.toString(),)
3362-
3363- def attempt(self, *a):
3364- return [VirtualConnectionAttempt(self, *a)]
3365-
3366-
3367-class _PTCPConnectionAttempt1NoPress(AbstractConnectionAttempt):
3368- attempted = False
3369- def startAttempt(self):
3370- assert not self.attempted
3371- self.attempted = True
3372- svc = self.q2qproto.service
3373- dsp = svc.dispatcher
3374- dsp.connectPTCP(
3375- self.method.host, self.method.port, self,
3376- svc.sharedUDPPortnum)
3377- return self.deferred
3378-
3379-class _PTCPConnectionAttemptPress(AbstractConnectionAttempt):
3380- attempted = False
3381- def startAttempt(self):
3382- assert not self.attempted
3383- self.attempted = True
3384-
3385- svc = self.q2qproto.service
3386- dsp = svc.dispatcher
3387- newPort = self.newPort = dsp.bindNewPort()
3388- dsp.connectPTCP(
3389- self.method.host, self.method.port, self,
3390- newPort)
3391-
3392- return self.deferred
3393-
3394- def cancel(self):
3395- if not self.cancelled:
3396- self.q2qproto.service.dispatcher.unbindPort(self.newPort)
3397- else:
3398- print 'totally wacky, [press] cancelled twice!'
3399- AbstractConnectionAttempt.cancel(self)
3400-
3401-class PTCPMethod(TCPMethod):
3402- """Pseudo-TCP method.
3403- """
3404- ptype = 'ptcp'
3405-
3406- def attempt(self, *a):
3407- return [_PTCPConnectionAttempt1NoPress(self, *a),
3408- _PTCPConnectionAttemptPress(self, *a)]
3409-
3410-class RPTCPConnectionAttempt(AbstractConnectionAttempt):
3411- attempted = False
3412- def startAttempt(self):
3413- assert not self.attempted
3414- self.attempted = True
3415-
3416- realLocalUDP = self.newPort = self.q2qproto.service.dispatcher.seedNAT((self.method.host, self.method.port))
3417- # self.host and self.port are remote host and port
3418- # realLocalUDP is a local port
3419-
3420- # The arguments here are given from the perspective of the recipient of
3421- # the command. we are asking the recipient of the connection to map a
3422- # NAT entry of a pre-existing listening UDP socket on their end of the
3423- # connection by sending us some traffic. therefore the src is their
3424- # endpoint, the dst is our endpoint, the user we are asking them to
3425- # send TO is us, the user we are asking them to accept this FROM is us.
3426-
3427- # we include protocol as an arg because this is helpful for relaying.
3428-
3429- def enbinden(boundereded):
3430- if not self.cancelled:
3431- self.q2qproto.service.dispatcher.connectPTCP(
3432- self.method.host, self.method.port, self, realLocalUDP
3433- )
3434- return self.deferred
3435-
3436- def swallowKnown(error):
3437- error.trap(ConnectionError)
3438- self.deferred.errback(CONNECTION_DONE)
3439- return self.deferred
3440-
3441- d = self.q2qproto.callRemote(
3442- BindUDP,
3443- q2qsrc=self.toAddress,
3444- q2qdst=self.fromAddress,
3445- protocol=self.protocolName,
3446- udpsrc=(self.method.host, self.method.port),
3447- udpdst=(self.q2qproto._determinePublicIP(), realLocalUDP))
3448- d.addCallbacks(enbinden, swallowKnown)
3449- return d
3450-
3451- def cancel(self):
3452- if not self.cancelled:
3453- self.q2qproto.service.dispatcher.unbindPort(self.newPort)
3454- else:
3455- print 'totally wacky, [rptcp] cancelled twice!'
3456- AbstractConnectionAttempt.cancel(self)
3457-
3458-
3459-
3460-
3461-class RPTCPMethod(TCPMethod):
3462- """ Certain NATs respond very poorly to seed traffic: e.g. if they receive
3463- unsolicited traffic to a particular port, they will make that outbound port
3464- unavailable for outbound traffic originated internally. The
3465- Reverse-Pseudo-TCP method is a way to have the *sender* send the first UDP
3466- packet, so they will bind it.
3467-
3468- This is a worst-case scenario: if both ends of the connection have NATs
3469- which behave this way, there is no way to establish a connection.
3470- """
3471-
3472- ptype = 'rptcp'
3473- attemptFactory = RPTCPConnectionAttempt
3474-
3475-
3476-class UnknownMethod:
3477-
3478- relayable = True
3479-
3480- def __init__(self, S):
3481- self.string = S
3482-
3483- def attemptConnect(self, q2qproto, connectionID, From, to,
3484- protocolName, protocolFactory):
3485- return defer.fail(Failure(ConnectionError(
3486- "unknown connection method: %s" % (self.string,))))
3487-
3488-
3489-_methodFactories = {'virtual': VirtualMethod,
3490- 'tcp': TCPMethod,
3491- 'ptcp': PTCPMethod,
3492- 'rptcp': RPTCPMethod}
3493-
3494-class Method(Argument):
3495- def toString(self, inObj):
3496- return inObj.toString()
3497-
3498-
3499- def fromString(self, inString):
3500- f = inString.split("@", 1)
3501- factoryName = f[0]
3502- if len(f) > 1:
3503- factoryData = f[1]
3504- else:
3505- factoryData = ''
3506- methodFactory = _methodFactories.get(factoryName, None)
3507- if methodFactory is None:
3508- factory = UnknownMethod(inString)
3509- else:
3510- factory = methodFactory(factoryData)
3511- return factory
3512-
3513-
3514-class Secure(StartTLS):
3515-
3516- commandName = "secure"
3517- arguments = StartTLS.arguments + [
3518- ('From', Q2QAddressArgument(optional=True)),
3519- ('to', Q2QAddressArgument()),
3520- ('authorize', Boolean())
3521- ]
3522-
3523-
3524-
3525-class Listen(Command):
3526- """
3527- A simple command for registering interest with an active Q2Q connection
3528- to hear from a server when others come calling. An occurrence of this
3529- command might have this appearance on the wire::
3530-
3531- C: -Command: Listen
3532- C: -Ask: 1
3533- C: From: glyph@divmod.com
3534- C: Protocols: q2q-example, q2q-example2
3535- C: Description: some simple protocols
3536- C:
3537- S: -Answer: 1
3538- S:
3539-
3540- This puts some state on the server side that will affect any Connect
3541- commands with q2q-example or q2q-example2 in the Protocol: header.
3542- """
3543-
3544- commandName = 'listen'
3545- arguments = [
3546- ('From', Q2QAddressArgument()),
3547- ('protocols', ListOf(String())),
3548- ('description', Unicode())]
3549-
3550- result = []
3551-
3552-class ConnectionStartBox(AmpBox):
3553- def __init__(self, __transport):
3554- super(ConnectionStartBox, self).__init__()
3555- self.virtualTransport = __transport
3556-
3557- # XXX Overriding a private interface
3558- def _sendTo(self, proto):
3559- super(ConnectionStartBox, self)._sendTo(proto)
3560- self.virtualTransport.startProtocol()
3561-
3562-class Virtual(Command):
3563- commandName = 'virtual'
3564- result = []
3565-
3566- arguments = [('id', Integer())]
3567-
3568- def makeResponse(cls, objects, proto):
3569- tpt = objects.pop('__transport__')
3570- # XXX Using a private API
3571- return _objectsToStrings(
3572- objects, cls.response,
3573- ConnectionStartBox(tpt),
3574- proto)
3575-
3576- makeResponse = classmethod(makeResponse)
3577-
3578-class Identify(Command):
3579- """
3580- Respond to an IDENTIFY command with a self-signed certificate for the
3581- domain requested, assuming we are an authority for said domain. An
3582- occurrence of this command might have this appearance on the wire::
3583-
3584- C: -Command: Identify
3585- C: -Ask: 1
3586- C: Domain: divmod.com
3587- C:
3588- S: -Answer: 1
3589- S: Certificate: <<<base64-encoded self-signed certificate of divmod.com>>>
3590- S:
3591-
3592- """
3593-
3594- commandName = 'identify'
3595-
3596- arguments = [('subject', Q2QAddressArgument())]
3597-
3598- response = [('certificate', Cert())]
3599-
3600-class BindUDP(Command):
3601- """
3602- See UDPXMethod
3603- """
3604-
3605- commandName = 'bind-udp'
3606-
3607- arguments = [
3608- ('protocol', String()),
3609- ('q2qsrc', Q2QAddressArgument()),
3610- ('q2qdst', Q2QAddressArgument()),
3611- ('udpsrc', HostPort()),
3612- ('udpdst', HostPort()),
3613- ]
3614-
3615- errors = {ConnectionError: 'ConnectionError'}
3616-
3617- response = []
3618-
3619-class SourceIP(Command):
3620- """
3621- Ask a server on the public internet what my public IP probably is. An
3622- occurrence of this command might have this appearance on the wire::
3623-
3624- C: -Command: Source-IP
3625- C: -Ask: 1
3626- C:
3627- S: -Answer: 1
3628- S: IP: 4.3.2.1
3629- S:
3630-
3631- """
3632-
3633- commandName = 'source-ip'
3634-
3635- arguments = []
3636-
3637- response = [('ip', String())]
3638-
3639-class Inbound(Command):
3640- """
3641- Request information about where to connect to a particular resource.
3642-
3643- Generally speaking this is an "I want to connect to you" request.
3644-
3645- The format of this request is transport neutral except for the optional
3646- 'Udp_Source' header, which specifies an IP/Port pair for all receiving peers to
3647- send an almost-empty (suggested value of '\\r\\n') UDP packet to to help
3648- with NAT traversal issues.
3649-
3650- See L{Q2QService.connectQ2Q} for details.
3651-
3652- An occurrence of this command might have this appearance on the wire::
3653-
3654- C: -Command: Inbound
3655- C: -Ask: 1
3656- C: From: glyph@divmod.com
3657- C: Id: 681949ffa3be@twistedmatrix.com
3658- C: To: radix@twistedmatrix.com
3659- C: Protocol: q2q-example
3660- C: Udp_Source: 1.2.3.4:4321
3661- C:
3662- S: -Answer: 1
3663- S: Listeners:
3664- S: Description: at lab
3665- S: Methods: tcp@18.38.12.4:3827, virtual
3666- S:
3667- S: Description: my home machine
3668- S: Methods: tcp@187.48.38.3:49812, udp@187.48.38.3:49814, virtual
3669-
3670- Now the connection-id has been registered and either client or server can
3671- issue WRITE or CLOSE commands.
3672-
3673- Failure modes::
3674-
3675- - "NotFound": the toResource or toDomain is invalid, or the resource does
3676- not speak that protocol.
3677-
3678- - "VerifyError": Authenticity or security for the requested connection
3679- could not be authorized. This is a fatal error: the connection will be
3680- dropped.
3681-
3682- The "Udp_Source" header indicates the address from which this Inbound chain
3683- originated. It is to be used to establish connections where possible
3684- between NATs which require traffic between two host/port pairs to be
3685- bidirectional before a "hole" is established, such as port restricted cone
3686- and symmetric NATs. (Note, this only has about a 30% probability of
3687- working on a symmetric NAT, but it's worth trying sometimes anyway). Any
3688- UDP-based connection methods (currently only Gin, but in principle others
3689- such as RTP, RTCP, SIP and Quake traffic) that wish to use this connection
3690- must first send some garbage traffic to the host/port specified by the
3691- "Udp_Source" header.
3692-
3693- The response is a list of "listeners" - a small (unicode) textual
3694- description of a host, plus a list of methods describing how to connect to
3695- it.
3696- """
3697-
3698- commandName = 'inbound'
3699- arguments = [('From', Q2QAddressArgument()),
3700- ('to', Q2QAddressArgument()),
3701- ('protocol', String()),
3702- ('udp_source', HostPort(optional=True))]
3703-
3704- response = [('listeners', AmpList(
3705- [('id', String()),
3706- ('certificate', Cert(optional=True)),
3707- ('methods', ListOf(Method())),
3708- ('expires', AmpTime()),
3709- ('description', Unicode())]))]
3710-
3711- errors = {KeyError: "NotFound"}
3712- fatalErrors = {VerifyError: "VerifyError"}
3713-
3714-class Outbound(Command):
3715- """Similar to Inbound, but _requires that the recipient already has the
3716- id parameter as an outgoing connection attempt_.
3717- """
3718- commandName = 'outbound'
3719-
3720- arguments = [('From', Q2QAddressArgument()),
3721- ('to', Q2QAddressArgument()),
3722- ('protocol', String()),
3723- ('id', String()),
3724- ('methods', ListOf(Method()))]
3725-
3726- response = []
3727-
3728- errors = {AttemptsFailed: 'AttemptsFailed'}
3729-
3730-class Sign(Command):
3731- commandName = 'sign'
3732- arguments = [('certificate_request', CertReq()),
3733- ('password', String())]
3734-
3735- response = [('certificate', Cert())]
3736-
3737- errors = {KeyError: "NoSuchUser",
3738- BadCertificateRequest: "BadCertificateRequest"}
3739-
3740-class Choke(Command):
3741- """Ask our peer to be quiet for a while.
3742- """
3743- commandName = 'Choke'
3744- arguments = [('id', Integer())]
3745- requiresAnswer = False
3746-
3747-
3748-class Unchoke(Command):
3749- """Reverse the effects of a choke.
3750- """
3751- commandName = 'Unchoke'
3752- arguments = [('id', Integer())]
3753- requiresAnswer = False
3754-
3755-
3756-def safely(f, *a, **k):
3757- """try/except around something, w/ twisted error handling.
3758- """
3759- try:
3760- f(*a,**k)
3761- except:
3762- log.err()
3763-
3764-class Q2Q(AMP, subproducer.SuperProducer):
3765- """ Quotient to Quotient protocol.
3766-
3767- At a low level, this uses a protocol called 'Juice' (JUice Is Concurrent
3768- Events), which is a simple rfc2822-inspired (although not -compliant)
3769- protocol for request/response pair hookup.
3770-
3771- At a higher level, it provides a mechanism for SSL certificate exchange,
3772- looking up physical locations of users' data, and switching into other
3773- protocols after an initial handshake.
3774-
3775- @ivar publicIP: The IP that the other end of the connection claims to know
3776- us by. This will be used when responding to L{Inbound} commands if the Q2Q
3777- service I am attached to does not specify a public IP to use.
3778-
3779- @ivar authorized: A boolean indicating whether SSL verification has taken
3780- place to ensure that this connection's peer has claimed an accurate identity.
3781- """
3782-
3783- protocolName = 'q2q'
3784- service = None
3785- publicIP = None
3786- authorized = False
3787-
3788- def __init__(self, *a, **kw):
3789- """ Q2Q instances should only be created by Q2QService. See
3790- L{Q2QService.connectQ2Q} and L{Q2QService.listenQ2Q}.
3791- """
3792- subproducer.SuperProducer.__init__(self)
3793- AMP.__init__(self, *a, **kw)
3794-
3795- def connectionMade(self):
3796- self.producingTransports = {}
3797- self.connections = {}
3798- self.listeningClient = []
3799- self.connectionObservers = []
3800- if self.service.publicIP is None:
3801- log.msg("Service has no public IP: determining")
3802- self.service.publicIP = self.transport.getHost().host
3803- self.service._publicIPIsReallyPrivate = True
3804- def rememberPublicIP(pubip):
3805- ip = pubip['ip']
3806- log.msg('remembering public ip as %r' % ip)
3807- self.publicIP = ip
3808- self.service.publicIP = ip
3809- self.service._publicIPIsReallyPrivate = False
3810- self.callRemote(SourceIP).addCallback(rememberPublicIP)
3811- else:
3812- log.msg("Using existing public IP: %r" % (self.service.publicIP,))
3813-
3814- def connectionLost(self, reason):
3815- ""
3816- AMP.connectionLost(self, reason)
3817- self._uncacheMe()
3818- self.producingTransports = {}
3819- for key, value in self.listeningClient:
3820- log.msg("removing remote listener for %r" % (key,))
3821- self.service.listeningClients[key].remove(value)
3822- self.listeningClient = []
3823- for xport in self.connections.values():
3824- safely(xport.connectionLost, reason)
3825- for observer in self.connectionObservers:
3826- safely(observer)
3827-
3828- def notifyOnConnectionLost(self, observer):
3829- ""
3830- self.connectionObservers.append(observer)
3831-
3832- def _bindUDP(self, q2qsrc, q2qdst, udpsrc, udpdst, protocol):
3833-
3834- # we are representing the src, because they are the ones being told to
3835- # originate a UDP packet.
3836-
3837- self.verifyCertificateAllowed(q2qsrc, q2qdst)
3838-
3839- # if I've got a local factory for this 3-tuple, do the bind if I own
3840- # this IP...
3841- srchost, srcport = udpsrc
3842-
3843- lcget = self.service.listeningClients.get((q2qsrc, protocol), ())
3844-
3845- bindery = []
3846-
3847- for (listener, listenCert, desc
3848- ) in lcget:
3849- # print 'looking at listener', listener
3850- # print listener.transport.getPeer().host, srchost
3851- if listener.transport.getPeer().host == srchost:
3852- # print 'bound in clients loop'
3853-
3854- d = listener.callRemote(
3855- BindUDP,
3856- q2qsrc=q2qsrc,
3857- q2qdst=q2qdst,
3858- udpsrc=udpsrc,
3859- udpdst=udpdst,
3860- protocol=protocol)
3861- def swallowKnown(err):
3862- err.trap(error.ConnectionDone, error.ConnectionLost)
3863- d.addErrback(swallowKnown)
3864- bindery.append(d)
3865- if bindery:
3866- # print 'bindery return', len(bindery)
3867- def _justADict(ign):
3868- return dict()
3869- return defer.DeferredList(bindery).addCallback(_justADict)
3870-
3871- # print 'what?', lcget
3872- if (self.service.getLocalFactories(q2qdst, q2qsrc, protocol)
3873- and srchost == self._determinePublicIP()):
3874- self.service.dispatcher.seedNAT(udpdst, srcport, conditional=True)
3875- # print 'bound locally'
3876- return dict()
3877- # print 'conn-error'
3878- raise ConnectionError("unable to find appropriate UDP binder")
3879-
3880- BindUDP.responder(_bindUDP)
3881-
3882- def _identify(self, subject):
3883- """
3884- Implementation of L{Identify}.
3885- """
3886- ourCA = self.service.certificateStorage.getPrivateCertificate(str(subject))
3887- return dict(certificate=ourCA)
3888- Identify.responder(_identify)
3889-
3890-
3891- def verifyCertificateAllowed(self,
3892- ourAddress,
3893- theirAddress):
3894- """
3895- Check that the certificate currently in use by this transport is valid to
3896- claim that the connection offers authorization for this host speaking
3897- for C{ourAddress}, to a host speaking for C{theirAddress}. The remote
3898- host (the one claiming to use theirAddress) may have a certificate
3899- which is issued for the domain for theirAddress or the full address
3900- given in theirAddress.
3901-
3902- This method runs B{after} cryptographic verification of the validity of
3903- certificates, although it does not perform any cryptographic checks
3904- itself. It depends on SSL connection handshaking - *and* the
3905- particular certificate lookup logic which prevents spoofed Issuer
3906- fields, to work properly. However, all it checks is the X509 names
3907- present in the certificates matching with the application-level
3908- security claims being made by our peer.
3909-
3910- An example of successful verification, because both parties have
3911- properly signed certificates for their usage from the domain they
3912- have been issued::
3913-
3914- our current certficate:
3915- issuer: divmod.com
3916- subject: glyph@divmod.com
3917- their current certificate:
3918- issuer: twistedmatrix.com
3919- subject: exarkun@twistedmatrix.com
3920- Arguments to verifyCertificateAllowed:
3921- ourAddress: glyph@divmod.com
3922- theirAddress: exarkun@twistedmatrix.com
3923- Result of verifyCertificateAllowed: None
3924-
3925- An example of rejected verification, because domain certificates are
3926- always B{self}-signed in Q2Q; verisign is not a trusted certificate
3927- authority for the entire internet as with some other TLS
3928- implementations::
3929-
3930- our current certificate:
3931- issuer: divmod.com
3932- subject: divmod.com
3933- their current certificate:
3934- issuer: verisign.com
3935- subject: twistedmatrix.com
3936- Arguments to verifyCertificateAllowed:
3937- ourAddress: divmod.com
3938- theirAddress: twistedmatrix.com
3939- Result of verifyCertificateAllowed: exception VerifyError raised
3940-
3941- Another example of successful verification, because we assume our
3942- current certificate is under the control of this side of the
3943- connection, so *any* claimed subject is considered acceptable::
3944-
3945- our current certificate:
3946- issuer: divmod.com
3947- subject: divmod.com
3948- their current certificate:
3949- issuer: divmod.com
3950- subject: glyph@twistedmatrix.com
3951- Arguments to verifyCertificateAllowed:
3952- ourAddress: divmod.com
3953- theirAddress: glyph@twistedmatrix.com
3954- Result of verifyCertificateAllowed: None
3955-
3956- Another example of successful verification, because the user is
3957- claiming to be anonymous; there is also a somewhat looser
3958- cryptographic check applied to signatures for anonymous
3959- connections::
3960-
3961- our current certificate:
3962- issuer: divmod.com
3963- subject: divmod.com
3964- their current certificate:
3965- issuer: @
3966- subject: @
3967- arguments to verifyCertificateAllowed:
3968- ourAddress: divmod.com
3969- theirAddress: @
3970- Result of verifyCertificateAllowed: None
3971-
3972- Accept anonymous connections with caution.
3973-
3974- @param ourAddress: a L{Q2QAddress} representing the address that we are
3975- supposed to have authority for, requested by our peer.
3976-
3977- @param theirAddress: a L{Q2QAddress} representing the address that our
3978- network peer claims to be communicating on behalf of. For example, if
3979- our peer is foobar.com they may claim to be operating on behalf of any
3980- user @foobar.com.
3981-
3982- @raise: L{VerifyError} if the certificates do not match the
3983- claimed addresses.
3984- """
3985-
3986- # XXX TODO: Somehow, it's got to be possible for a single cluster to
3987- # internally claim to be agents of any other host when issuing a
3988- # CONNECT; in other words, we always implicitly trust ourselves. Also,
3989- # we might want to issue anonymous CONNECTs over unencrypted
3990- # connections.
3991-
3992- # IOW: *we* can sign a certificate to be whoever, but the *peer* can
3993- # only sign the certificate to be the peer.
3994-
3995- # The easiest way to make this work is to issue ourselves a wildcard
3996- # certificate.
3997-
3998- if not self.authorized:
3999- if theirAddress.domain == '':
4000- # XXX TODO: document this rule, anonymous connections are
4001- # allowed to not be authorized because they are not making any
4002- # claims about who they are
4003-
4004- # XXX also TODO: make it so that anonymous connections are
4005- # disabled by default for most protocols
4006- return True
4007- raise VerifyError("No official negotiation has taken place.")
4008-
4009- peerCert = Certificate.peerFromTransport(self.transport)
4010- ourCert = self.hostCertificate
4011-
4012- ourClaimedDomain = ourAddress.domainAddress()
4013- theirClaimedDomain = theirAddress.domainAddress()
4014-
4015- # Sanity check #1: did we pick the right certificate on our end?
4016- if not ourClaimedDomain.claimedAsIssuerOf(ourCert):
4017- raise VerifyError(
4018- "Something has gone horribly wrong: local domain mismatch "
4019- "claim: %s actual: %s" % (ourClaimedDomain,
4020- ourCert.getIssuer()))
4021- if theirClaimedDomain.claimedAsIssuerOf(peerCert):
4022- # Their domain issued their certificate.
4023- if theirAddress.claimedAsSubjectOf(peerCert) or theirClaimedDomain.claimedAsSubjectOf(peerCert):
4024- return
4025- elif ourClaimedDomain.claimedAsIssuerOf(peerCert):
4026- # *our* domain can spoof *anything*
4027- return
4028- elif ourAddress.claimedAsIssuerOf(peerCert):
4029- # Neither our domain nor their domain signed this. Did *we*?
4030- # (Useful in peer-to-peer persistent transactions where we don't
4031- # want the server involved: exarkun@twistedmatrix.com can sign
4032- # glyph@divmod.com's certificate).
4033- return
4034-
4035- raise VerifyError(
4036- "Us: %s Them: %s "
4037- "TheyClaimWeAre: %s TheyClaimTheyAre: %s" %
4038- (ourCert, peerCert,
4039- ourAddress, theirAddress))
4040-
4041- def _listen(self, protocols, From, description):
4042- """
4043- Implementation of L{Listen}.
4044- """
4045- # The peer is coming from a client-side representation of the user
4046- # described by 'From', and talking *to* a server-side representation of
4047- # the user described by 'From'.
4048- self.verifyCertificateAllowed(From, From)
4049- theirCert = Certificate.peerFromTransport(self.transport)
4050- for protocolName in protocols:
4051- if protocolName.startswith('.'):
4052- raise VerifyError(
4053- "Internal protocols are for server-server use _only_: %r" %
4054- protocolName)
4055-
4056- key = (From, protocolName)
4057- value = (self, theirCert, description)
4058- log.msg("%r listening for %r" % key)
4059- self.listeningClient.append((key, value))
4060- self.service.listeningClients.setdefault(key, []).append(value)
4061- return {}
4062- Listen.responder(_listen)
4063-
4064-
4065- def _inbound(self, From, to, protocol, udp_source=None):
4066- """
4067- Implementation of L{Inbound}.
4068- """
4069- # Verify stuff!
4070-
4071- self.verifyCertificateAllowed(to, From)
4072- return self.service.verifyHook(From, to, protocol
4073- ).addCallback(self._inboundimpl,
4074- From,
4075- to,
4076- protocol,
4077- udp_source).addErrback(
4078- lambda f: f.trap(KeyError) and dict(listeners=[]))
4079- Inbound.responder(_inbound)
4080-
4081- def _inboundimpl(self, ign, From, to, protocol, udp_source):
4082-
4083- # 2-tuples of factory, description
4084- srvfacts = self.service.getLocalFactories(From, to, protocol)
4085-
4086- result = [] # list of listener dicts
4087-
4088- if srvfacts:
4089- log.msg("local factories found for inbound request: %r" % (srvfacts,))
4090- localMethods = []
4091- publicIP = self._determinePublicIP()
4092- privateIP = self._determinePrivateIP()
4093- if self.service.inboundTCPPort is not None:
4094- tcpPort = self.service.inboundTCPPort.getHost().port
4095- localMethods.append(TCPMethod(
4096- '%s:%d' %
4097- (publicIP, tcpPort)))
4098- if publicIP != privateIP:
4099- localMethods.append(TCPMethod(
4100- '%s:%d' %
4101- (privateIP, tcpPort)))
4102-
4103- if not self.service.udpEnabled:
4104- log.msg("udp not enabled -- but I so want to send udp traffic!")
4105- elif udp_source is None:
4106- log.msg("udp_source was none on inbound")
4107- else:
4108- if self.service.dispatcher is None:
4109- log.msg("udp_source %s:%d, but dispatcher not running" %
4110- udp_source)
4111- else:
4112- remoteUDPHost, remoteUDPPort = udp_source
4113- log.msg(
4114- "remote PTCP: %s:%d, "
4115- "local public IP: %s, local private IP: %s"
4116- % (remoteUDPHost, remoteUDPPort, publicIP, privateIP) )
4117-
4118- # Seed my NAT from my shared UDP port
4119- udpPort = self.service.dispatcher.seedNAT(udp_source, self.service.sharedUDPPortnum)
4120-
4121- if remoteUDPHost == publicIP and publicIP != privateIP:
4122- log.msg(
4123- "Remote IP matches local, public IP %r;"
4124- " preferring internal IP %r" % (publicIP, privateIP))
4125- localMethods.append(
4126- PTCPMethod("%s:%d" % (privateIP, udpPort)))
4127- localMethods.append(
4128- PTCPMethod("%s:%d" % (publicIP, udpPort)))
4129-
4130- # XXX CLEANUP!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4131- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4132- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4133- privateUDPPort = self.service.dispatcher.seedNAT(udp_source)
4134- localMethods.append(
4135- PTCPMethod('%s:%d' % (publicIP, privateUDPPort)))
4136-
4137- udpxPort = self.service.dispatcher.seedNAT(udp_source)
4138- localMethods.append(
4139- RPTCPMethod("%s:%d" % (publicIP, udpxPort)))
4140-
4141- if self.service.virtualEnabled:
4142- localMethods.append(VirtualMethod())
4143- log.msg('responding to inbound with local methods: %r' % (localMethods,))
4144-
4145- for serverFactory, description in srvfacts:
4146- expiryTime, listenID = self.service.mapListener(
4147- to, From, protocol, serverFactory)
4148- result.append(dict(id=listenID,
4149- expires=expiryTime,
4150- methods=localMethods,
4151- description=description))
4152-
4153- # We've looked for our local factory. Let's see if we have any
4154- # listening protocols elsewhere.
4155-
4156-
4157- key = (to, protocol)
4158- if key in self.service.listeningClients:
4159- args = dict(From=From,
4160- to=to,
4161- protocol=protocol,
4162- udp_source=udp_source)
4163- DL = []
4164- lclients = self.service.listeningClients[key]
4165- log.msg("listeners found for %s:%r" % (to, protocol))
4166- for listener, listenCert, desc in lclients:
4167- log.msg("relaying inbound to %r via %r" % (to, listener))
4168- DL.append(listener.callRemote(Inbound, **args).addCallback(
4169- self._massageClientInboundResponse, listener, result))
4170-
4171- def allListenerResponses(x):
4172- log.msg("all inbound responses received: %s" % (pformat(result),))
4173- return dict(listeners=result)
4174- return defer.DeferredList(DL).addCallback(allListenerResponses)
4175- else:
4176- log.msg("no listenening clients for %s:%r. local methods: %r" % (to,protocol, result))
4177- return dict(listeners=result)
4178-
4179-
4180- def _massageClientInboundResponse(self, inboundResponse, listener, result):
4181- irl = inboundResponse['listeners']
4182- log.msg("received relayed inbound response: %r via %r" %
4183- (inboundResponse, listener))
4184-
4185- for listenerInfo in irl:
4186- # inboundResponse['description'] = ??? trust client version for
4187- # now... maybe the server doesn't even need to know about
4188- # descriptions...?
4189- listenerInfo['methods'] = [
4190- meth for meth in listenerInfo['methods'] if meth.relayable]
4191- # make sure that the certificate that we're relaying matches the
4192- # certificate that they gave us!
4193- if listenerInfo['methods']:
4194- allowedCertificate = Certificate.peerFromTransport(
4195- listener.transport)
4196- listenerInfo['certificate'] = allowedCertificate
4197- result.append(listenerInfo)
4198-
4199- def _determinePublicIP(self):
4200- reservePublicIP = None
4201- if self.service.publicIP is not None:
4202- if self.service._publicIPIsReallyPrivate:
4203- reservePublicIP = self.service.publicIP
4204- else:
4205- return self.service.publicIP
4206- if self.publicIP is not None:
4207- return self.publicIP
4208- if reservePublicIP is not None:
4209- return reservePublicIP
4210- return self._determinePrivateIP()
4211-
4212- def _determinePrivateIP(self):
4213- return self.transport.getHost().host
4214-
4215- def _sourceIP(self):
4216- result = {'ip': self.transport.getPeer().host}
4217- return result
4218- SourceIP.responder(_sourceIP)
4219-
4220- def _resume(self, connection, data, writeDeferred):
4221- try:
4222- connection.dataReceived(data)
4223- except:
4224- writeDeferred.errback()
4225- else:
4226- writeDeferred.callback({})
4227-
4228-
4229- def _choke(self, id):
4230- connection = self.connections[id]
4231- connection.choke()
4232- return {}
4233- Choke.responder(_choke)
4234-
4235-
4236- def _unchoke(self, id):
4237- connection = self.connections[id]
4238- connection.unchoke()
4239- return {}
4240- Unchoke.responder(_unchoke)
4241-
4242-
4243- def amp_WRITE(self, box):
4244- """
4245- Respond to a WRITE command, sending some data over a virtual channel
4246- created by VIRTUAL. The answer is simply an acknowledgement, as it is
4247- simply meant to note that the write went through without errors.
4248-
4249- An occurrence of I{Write} on the wire, together with the response
4250- generated by this method, might have this apperance::
4251-
4252- C: -Command: Write
4253- C: -Ask: 1
4254- C: -Length: 13
4255- C: Id: glyph@divmod.com->radix@twistedmatrix.com:q2q-example:0
4256- C:
4257- C: HELLO WORLD
4258- C:
4259- S: -Answer: 1
4260- S:
4261-
4262- """
4263- id = int(box['id'])
4264- if id not in self.connections:
4265- raise error.ConnectionDone()
4266- connection = self.connections[id]
4267- data = box['body']
4268- connection.dataReceived(data)
4269- return AmpBox()
4270-
4271- def amp_CLOSE(self, box):
4272- """
4273- Respond to a CLOSE command, dumping some data onto the stream. As with
4274- WRITE, this returns an empty acknowledgement.
4275-
4276- An occurrence of I{Close} on the wire, together with the response
4277- generated by this method, might have this apperance::
4278-
4279- C: -Command: Close
4280- C: -Ask: 1
4281- C: Id: glyph@divmod.com->radix@twistedmatrix.com:q2q-example:0
4282- C:
4283- S: -Answer: 1
4284- S:
4285-
4286- """
4287- # The connection is removed from the mapping by connectionLost.
4288- connection = self.connections[int(box['id'])]
4289- connection.connectionLost(Failure(CONNECTION_DONE))
4290- return AmpBox()
4291-
4292-
4293- def _sign(self, certificate_request, password):
4294- """
4295- Respond to a request to sign a CSR for a user or agent located within
4296- our domain.
4297- """
4298- if self.service.portal is None:
4299- raise BadCertificateRequest("This agent cannot sign certificates.")
4300-
4301- subj = certificate_request.getSubject()
4302-
4303- sk = subj.keys()
4304- if 'commonName' not in sk:
4305- raise BadCertificateRequest(
4306- "Certificate requested with bad subject: %s" % (sk,))
4307-
4308- uandd = subj.commonName.split("@")
4309- if len(uandd) != 2:
4310- raise BadCertificateRequest("Won't sign certificates for other domains")
4311- domain = uandd[1]
4312-
4313- CS = self.service.certificateStorage
4314- ourCert = CS.getPrivateCertificate(domain)
4315-
4316- D = self.service.portal.login(
4317- UsernamePassword(subj.commonName,
4318- password),
4319- self,
4320- ivertex.IQ2QUser)
4321-
4322- def _(ial):
4323- (iface, aspect, logout) = ial
4324- ser = CS.genSerial(domain)
4325- return dict(certificate=aspect.signCertificateRequest(
4326- certificate_request, ourCert, ser))
4327-
4328- return D.addCallback(_)
4329- Sign.responder(_sign)
4330-
4331-
4332- def _secure(self, to, From, authorize):
4333- """
4334- Response to a SECURE command, starting TLS when necessary, and using a
4335- certificate identified by the I{To} header.
4336-
4337- An occurrence of I{Secure} on the wire, together with the response
4338- generated by this method, might have the following appearance::
4339-
4340- C: -Command: Secure
4341- C: -Ask: 1
4342- C: To: divmod.com
4343- C: From: twistedmatrix.com
4344- C: Authorize: True
4345- C:
4346- Client Starts TLS here with twistedmatrix.com certificate
4347- S: -Answer: 1
4348- S:
4349- Server Starts TLS here with divmod.com certificate
4350-
4351- """
4352- if self.hostCertificate is not None:
4353- raise RuntimeError("Re-encrypting already encrypted connection")
4354- CS = self.service.certificateStorage
4355- ourCert = CS.getPrivateCertificate(str(to.domainAddress()))
4356- if authorize:
4357- D = CS.getSelfSignedCertificate(str(From.domainAddress()))
4358- else:
4359- self.authorized = False
4360- return {'tls_localCertificate': ourCert}
4361-
4362- def hadCert(peerSigned):
4363- self.authorized = True
4364- self._cacheMeNow(From, to, authorize)
4365- return {'tls_localCertificate': ourCert,
4366- 'tls_verifyAuthorities': [peerSigned]}
4367-
4368- def didNotHaveCert(err):
4369- err.trap(KeyError)
4370- return self._retrieveRemoteCertificate(From, port)
4371-
4372- D.addErrback(didNotHaveCert)
4373- D.addCallback(hadCert)
4374-
4375- return D
4376- Secure.responder(_secure)
4377-
4378- _cachedUnrequested = False
4379-
4380- def _cacheMeNow(self, From, to, authorize):
4381- tcpeer = self.transport.getPeer()
4382- # XXX 'port' is insane here, but we lack a better number to hash
4383- # against. perhaps the SECURE request should give a reciprocal
4384- # connection identifier...?
4385- self.service.secureConnectionCache.cacheUnrequested(
4386- endpoint.TCPEndpoint(tcpeer.host, port),
4387- (From, to.domain, authorize), self)
4388- assert not self._cachedUnrequested
4389- self._cachedUnrequested = (From, to, authorize, tcpeer)
4390-
4391- def _uncacheMe(self):
4392- if self._cachedUnrequested:
4393- # If this is a client connection, this will never be called, since
4394- # _cacheMeNow is called from the _server_ half of this business.
4395- # The uncaching API here is a bit of a ragged edge of conncache.py;
4396- # the interface should probably be cleaned up, but I don't think
4397- # there are any functional problems with it.
4398- From, to, authorize, tcpeer = self._cachedUnrequested
4399- self.service.secureConnectionCache.connectionLostForKey(
4400- (endpoint.TCPEndpoint(tcpeer.host, port),
4401- (From, to.domain, authorize)))
4402-
4403- def _retrieveRemoteCertificate(self, From, port=port):
4404- """
4405- The entire conversation, starting with TCP handshake and ending at
4406- disconnect, to retrieve a foreign domain's certificate for the first
4407- time.
4408- """
4409- CS = self.service.certificateStorage
4410- host = str(From.domainAddress())
4411- p = AMP()
4412- p.wrapper = self.wrapper
4413- f = protocol.ClientCreator(reactor, lambda: p)
4414- connD = f.connectTCP(host, port)
4415-
4416- def connected(proto):
4417- dhost = From.domainAddress()
4418- iddom = proto.callRemote(Identify, subject=dhost)
4419- def gotCert(identifyBox):
4420- theirCert = identifyBox['certificate']
4421- theirIssuer = theirCert.getIssuer().commonName
4422- theirName = theirCert.getSubject().commonName
4423- if (theirName != str(dhost)):
4424- raise VerifyError(
4425- "%r claimed it was %r in IDENTIFY response"
4426- % (theirName, dhost))
4427- if (theirIssuer != str(dhost)):
4428- raise VerifyError(
4429- "self-signed %r claimed it was issued by "
4430- "%r in IDENTIFY response" % (dhost, theirIssuer))
4431- def storedCert(ignored):
4432- return theirCert
4433- return CS.storeSelfSignedCertificate(
4434- str(dhost), theirCert).addCallback(storedCert)
4435- def nothingify(x):
4436- proto.transport.loseConnection()
4437- return x
4438- return iddom.addCallback(gotCert).addBoth(nothingify)
4439- connD.addCallback(connected)
4440- return connD
4441-
4442-
4443- def secure(self, fromAddress, toAddress,
4444- fromCertificate, foreignCertificateAuthority=None,
4445- authorize=True):
4446- """Return a Deferred which fires True when this connection has been secured as
4447- a channel between fromAddress (locally) and toAddress (remotely).
4448- Raises an error if this is not possible.
4449- """
4450- if self.hostCertificate is not None:
4451- raise RuntimeError("Re-securing already secured connection.")
4452-
4453- def _cbSecure(response):
4454- if foreignCertificateAuthority is not None:
4455- self.authorized = True
4456- return True
4457- extra = {'tls_localCertificate': fromCertificate}
4458- if foreignCertificateAuthority is not None:
4459- extra['tls_verifyAuthorities'] = [foreignCertificateAuthority]
4460-
4461- return self.callRemote(
4462- Secure,
4463- From=fromAddress,
4464- to=toAddress,
4465- authorize=authorize, **extra).addCallback(_cbSecure)
4466-
4467- def _virtual(self, id):
4468- if self.isServer:
4469- assert id > 0
4470- else:
4471- assert id < 0
4472- # We are double-deferring here so that we only start writing data to
4473- # our client _after_ they have processed our ACK.
4474- tpt = VirtualTransport(self, id, self.service._bootstrapFactory, False)
4475-
4476-
4477- return dict(__transport__=tpt)
4478-
4479- Virtual.responder(_virtual)
4480-
4481-
4482- # Client/Support methods.
4483-
4484- def attemptConnectionMethods(self, methods, connectionID, From, to,
4485- protocolName, protocolFactory):
4486- attemptObjects = []
4487- for meth in methods:
4488- atts = meth.attempt(self, connectionID, From, to,
4489- protocolName, protocolFactory)
4490- attemptObjects.extend(atts)
4491-
4492- attemptDeferreds = [att.startAttempt() for att in attemptObjects]
4493-
4494- d = defer.DeferredList(attemptDeferreds,
4495- fireOnOneCallback=True,
4496- fireOnOneErrback=False)
4497- def dontLogThat(e):
4498- e.trap(error.ConnectionLost, error.ConnectionDone)
4499-
4500- for attDef in attemptDeferreds:
4501- attDef.addErrback(dontLogThat)
4502-
4503- def _unfortunate_defer_hack(results):
4504- #Do you see what you've made me become?
4505- if isinstance(results, tuple):
4506- stuff = [(False, None)] * len(attemptObjects)
4507- stuff[results[1]] = (True, results[0])
4508- return stuff
4509- return results
4510-
4511-
4512- def gotResults(results):
4513- theResult = None
4514- anyResult = False
4515- for index, (success, result) in enumerate(results):
4516- if success:
4517- # woohoo! home free.
4518- # XXX Cancel outstanding attempts, maybe. They'll fail anyway,
4519- # because the factory will return None from buildProtocol().
4520- theResult = result
4521- anyResult = True
4522- else:
4523- attemptObjects[index].cancel()
4524- if anyResult:
4525- # theResult will be a SeparateConnectionTransport
4526- return theResult.subProtocol
4527- else:
4528- reason = Failure(AttemptsFailed([fobj for (f, fobj) in results]))
4529- return reason
4530-
4531- d.addCallback(_unfortunate_defer_hack)
4532- d.addCallback(gotResults)
4533- return d
4534-
4535-
4536- def listen(self, fromAddress, protocols, serverDescription):
4537- return self.callRemote(
4538- Listen, From=fromAddress,
4539- protocols=protocols, description=serverDescription)
4540-
4541-
4542- def connect(self, From, to,
4543- protocolName, clientFactory,
4544- chooser):
4545- """
4546- Issue an INBOUND command, creating a virtual connection to the peer,
4547- given identifying information about the endpoint to connect to, and a
4548- protocol factory.
4549-
4550- @param clientFactory: a *Client* ProtocolFactory instance which will
4551- generate a protocol upon connect.
4552-
4553- @return: a Deferred which fires with the protocol instance that was
4554- connected, or fails with AttemptsFailed if the connection was not
4555- possible.
4556- """
4557-
4558- publicIP = self._determinePublicIP()
4559-
4560- A = dict(From=From,
4561- to=to,
4562- protocol=protocolName)
4563-
4564- if self.service.dispatcher is not None:
4565- # tell them exactly where they can shove it
4566- A['udp_source'] = (publicIP,
4567- self.service.sharedUDPPortnum)
4568- else:
4569- # don't tell them because we don't know
4570- log.msg("dispatcher unavailable when connecting")
4571-
4572- D = self.callRemote(Inbound, **A)
4573-
4574- def _connected(answer):
4575- listenersD = defer.maybeDeferred(chooser, answer['listeners'])
4576- def gotListeners(listeners):
4577- allConnectionAttempts = []
4578- for listener in listeners:
4579- d = self.attemptConnectionMethods(
4580- listener['methods'],
4581- listener['id'],
4582- From, to,
4583- protocolName, clientFactory,
4584- )
4585- allConnectionAttempts.append(d)
4586- return defer.DeferredList(allConnectionAttempts)
4587- listenersD.addCallback(gotListeners)
4588- def finishedAllAttempts(results):
4589- succeededAny = False
4590- failures = []
4591- if not results:
4592- return Failure(NoAttemptsMade(
4593- "there was no available path for connections "
4594- "(%r->%r/%s)" % (From, to, protocolName)))
4595- for succeeded, result in results:
4596- if succeeded:
4597- succeededAny = True
4598- randomConnection = result
4599- break
4600- else:
4601- failures.append(result)
4602- if not succeededAny:
4603- return Failure(AttemptsFailed(
4604- [failure.getBriefTraceback() for failure in failures]))
4605-
4606- # XXX TODO: this connection is really random; connectQ2Q should
4607- # not return one of the connections it's made, put it into your
4608- # protocol's connectionMade handler
4609-
4610- return randomConnection
4611-
4612- return listenersD.addCallback(finishedAllAttempts)
4613- return D.addCallback(_connected)
4614-
4615-
4616-class SeparateConnectionTransport(object):
4617- def __init__(self,
4618- service,
4619- subProtocol,
4620- q2qhost,
4621- q2qpeer,
4622- protocolName,
4623- connectionEstablishedDeferred=None):
4624- self.service = service
4625- self.subProtocol = subProtocol
4626- self.q2qhost = q2qhost
4627- self.q2qpeer = q2qpeer
4628- self.protocolName = protocolName
4629- self.connectionEstablishedDeferred = connectionEstablishedDeferred
4630-
4631- subProtocol = None
4632- q2qhost = None
4633- q2qpeer = None
4634- protocolName = 'unknown'
4635-
4636- # ITransport
4637- disconnecting = property(lambda self: self.transport.disconnecting)
4638-
4639- # IQ2QTransport
4640-
4641- def getQ2QHost(self):
4642- return self.q2qhost
4643-
4644- def getQ2QPeer(self):
4645- return self.q2qpeer
4646-
4647- def makeConnection(self, tpt):
4648- self.transport = tpt
4649- self.service.subConnections.append(self)
4650- self.subProtocol.makeConnection(self)
4651- if self.connectionEstablishedDeferred is not None:
4652- self.connectionEstablishedDeferred.callback(self)
4653-
4654- def getPeer(self):
4655- return Q2QTransportAddress(self.getQ2QPeer(),
4656- self.transport.getPeer(),
4657- self.protocolName)
4658-
4659- def getHost(self):
4660- return Q2QTransportAddress(self.getQ2QHost(),
4661- self.transport.getHost(),
4662- self.protocolName)
4663-
4664- def dataReceived(self, data):
4665- self.subProtocol.dataReceived(data)
4666-
4667- def write(self, data):
4668- self.transport.write(data)
4669-
4670- def writeSequence(self, data):
4671- self.transport.writeSequence(data)
4672-
4673- def registerProducer(self, producer, streaming):
4674- self.transport.registerProducer(producer, streaming)
4675-
4676- def unregisterProducer(self):
4677- self.transport.unregisterProducer()
4678-
4679- def loseConnection(self):
4680- self.transport.loseConnection()
4681-
4682- def connectionLost(self, reason):
4683- self.service.subConnections.remove(self)
4684- if self.subProtocol is not None:
4685- self.subProtocol.connectionLost(reason)
4686- self.subProtocol = None
4687-
4688-class WhoAmI(Command):
4689- commandName = 'Who-Am-I'
4690-
4691- response = [
4692- ('address', HostPort()),
4693- ]
4694-
4695-class RetrieveConnection(ProtocolSwitchCommand):
4696- commandName = 'Retrieve-Connection'
4697-
4698- arguments = [
4699- ('identifier', String()),
4700- ]
4701-
4702- fatalErrors = {KeyError: "NoSuchConnection"}
4703-
4704-class Q2QBootstrap(AMP):
4705- def __init__(self, connIdentifier=None, protoFactory=None):
4706- AMP.__init__(self)
4707- assert connIdentifier is None or isinstance(connIdentifier, (str))
4708- self.connIdentifier = connIdentifier
4709- self.protoFactory = protoFactory
4710-
4711- def connectionMade(self):
4712- if self.connIdentifier is not None:
4713- def swallowKnown(err):
4714- err.trap(error.ConnectionDone, KeyError)
4715- self.retrieveConnection(self.connIdentifier, self.protoFactory).addErrback(swallowKnown)
4716-
4717- def whoami(self):
4718- """Return a Deferred which fires with a 2-tuple of (dotted quad ip, port
4719- number).
4720- """
4721- def cbWhoAmI(result):
4722- return result['address']
4723- return self.callRemote(WhoAmI).addCallback(cbWhoAmI)
4724-
4725-
4726- def _whoami(self):
4727- peer = self.transport.getPeer()
4728- return {
4729- 'address': (peer.host, peer.port),
4730- }
4731- WhoAmI.responder(_whoami)
4732-
4733-
4734- def retrieveConnection(self, identifier, factory):
4735- return self.callRemote(RetrieveConnection, factory, identifier=identifier)
4736-
4737-
4738- def _retrieveConnection(self, identifier):
4739- listenerInfo = self.service.lookupListener(identifier)
4740- if listenerInfo is None:
4741- raise KeyError(identifier)
4742- else:
4743- proto = listenerInfo.protocolFactory.buildProtocol(listenerInfo.From)
4744- return SeparateConnectionTransport(
4745- self.service,
4746- proto,
4747- listenerInfo.to,
4748- listenerInfo.From,
4749- listenerInfo.protocolName)
4750-
4751- RetrieveConnection.responder(_retrieveConnection)
4752-
4753-
4754-
4755-class Q2QBootstrapFactory(protocol.Factory):
4756- protocol = Q2QBootstrap
4757-
4758- def __init__(self, service):
4759- self.service = service
4760-
4761- def buildProtocol(self, addr):
4762- q2etc = protocol.Factory.buildProtocol(self, addr)
4763- q2etc.service = self.service
4764- return q2etc
4765-
4766-class VirtualTransport(subproducer.SubProducer):
4767- implements(interfaces.IProducer, interfaces.ITransport, interfaces.IConsumer)
4768- disconnecting = False
4769-
4770- def __init__(self, q2q, connectionID, protocolFactory, isClient):
4771- """
4772- @param q2q: a Q2Q Protocol instance.
4773-
4774- @param connectionID: an integer identifier, unique to the q2q instance
4775- that I am wrapping (my underlying physical connection).
4776-
4777- @param protocolFactory: an IProtocolFactory implementor which returns a
4778- protocol instance for me to use. I'll use it to build the protocol,
4779- and if the 'client' flag is True, also use it to notify
4780- connectionLost/connectionFailed.
4781-
4782- @param isClient: a boolean describing whether my protocol is the
4783- initiating half of this connection or not.
4784- """
4785- subproducer.SubProducer.__init__(self, q2q)
4786- self.q2q = q2q
4787-
4788- self.id = connectionID
4789- self.isClient = isClient
4790- self.q2q.connections[self.id] = self
4791- self.protocolFactory = protocolFactory
4792-
4793- protocol = None
4794-
4795- def startProtocol(self):
4796- self.protocol = self.protocolFactory.buildProtocol(self.getPeer())
4797- self.protocol.makeConnection(self)
4798- return self.protocol
4799-
4800- def pauseProducing(self):
4801- self.q2q.callRemote(Choke, id=self.id)
4802-
4803- def resumeProducing(self):
4804- self.q2q.callRemote(Unchoke, id=self.id)
4805-
4806- def writeSequence(self, iovec):
4807- self.write(''.join(iovec))
4808-
4809- def loseConnection(self):
4810- if self.disconnecting:
4811- # print 'omg wtf loseConnection!???!'
4812- return
4813- self.disconnecting = True
4814- d = self.q2q.callRemoteString('close', id=str(self.id))
4815- def cbClosed(ignored):
4816- self.connectionLost(Failure(CONNECTION_DONE))
4817- def ebClosed(reason):
4818- if self.id in self.q2q.connections:
4819- self.connectionLost(reason)
4820- elif not reason.check(error.ConnectionDone):
4821- # Anything but a ConnectionDone (or similar things, perhaps)
4822- # is fishy. Like an IndexError, that'd be wacko. But a
4823- # ConnectionDone when self.id is already out of the Q2Q's
4824- # connections mapping means the connection was closed after
4825- # we thought it was supposed to be closed. No harm there.
4826- log.err(reason, "Close virtual #%d failed" % (self.id,))
4827- d.addCallbacks(cbClosed, ebClosed)
4828-
4829-
4830- def connectionLost(self, reason):
4831- del self.q2q.connections[self.id]
4832- if self.protocol is not None:
4833- self.protocol.connectionLost(reason)
4834- if self.isClient:
4835- self.protocolFactory.clientConnectionLost(None, reason)
4836-
4837-
4838- def dataReceived(self, data):
4839- try:
4840- self.protocol.dataReceived(data)
4841- except:
4842- # XXX: unconditionally logging errors from user code makes it hard
4843- # to write tests, and is not always the right thing to do. we
4844- # should revamp Twisted to have some kind of control over this
4845- # behavior, and add that control back in to this code path as well
4846- # (although logging exceptions from dataReceived is _by default_
4847- # certainly the right thing to do) --glyph+exarkun
4848- reason = Failure()
4849- log.err(reason)
4850- self.connectionLost(reason)
4851-
4852- def write(self, data):
4853- self.q2q.callRemoteString(
4854- 'write', False, body=data, id=str(self.id))
4855-
4856- def getHost(self):
4857- return VirtualTransportAddress(self.q2q.transport.getHost())
4858-
4859- def getPeer(self):
4860- return VirtualTransportAddress(self.q2q.transport.getPeer())
4861-
4862-
4863-_counter = 0
4864-def _nextJuiceLog():
4865- global _counter
4866- try:
4867- return str(_counter)
4868- finally:
4869- _counter = _counter + 1
4870-
4871-class DefaultQ2QAvatar:
4872- implements(ivertex.IQ2QUser)
4873-
4874- def __init__(self, username, domain):
4875- self.username = username
4876- self.domain = domain
4877-
4878- def signCertificateRequest(self, certificateRequest,
4879- domainCert, suggestedSerial):
4880- keyz = certificateRequest.getSubject().keys()
4881- if keyz != ['commonName']:
4882- raise BadCertificateRequest(
4883- "Don't know how to verify fields other than CN: " +
4884- repr(keyz))
4885- newCert = domainCert.signRequestObject(
4886- certificateRequest,
4887- suggestedSerial)
4888- log.msg('signing certificate for user %s@%s: %s' % (
4889- self.username, self.domain, newCert.digest()))
4890- return newCert
4891-
4892-
4893-
4894-class DefaultCertificateStore:
4895-
4896- implements(ICredentialsChecker, IRealm)
4897-
4898- credentialInterfaces = [IUsernamePassword]
4899-
4900- def requestAvatar(self, avatarId, mind, interface):
4901- assert interface is ivertex.IQ2QUser, (
4902- "default certificate store only supports one interface")
4903- return interface, DefaultQ2QAvatar(*avatarId.split("@")), lambda : None
4904-
4905- def requestAvatarId(self, credentials):
4906- username, domain = credentials.username.split("@")
4907- pw = self.users.get((domain, username))
4908- if pw is None:
4909- return defer.fail(UnauthorizedLogin())
4910- def _(passwordIsCorrect):
4911- if passwordIsCorrect:
4912- return username + '@' + domain
4913- else:
4914- raise UnauthorizedLogin()
4915- return defer.maybeDeferred(
4916- credentials.checkPassword, pw).addCallback(_)
4917-
4918- def __init__(self):
4919- self.remoteStore = {}
4920- self.localStore = {}
4921- self.users = {}
4922-
4923- def getSelfSignedCertificate(self, domainName):
4924- return defer.maybeDeferred(self.remoteStore.__getitem__, domainName)
4925-
4926- def addUser(self, domain, username, privateSecret):
4927- self.users[domain, username] = privateSecret
4928-
4929- def checkUser(self, domain, username, privateSecret):
4930- if self.users.get((domain, username)) != privateSecret:
4931- return defer.fail(KeyError())
4932- return defer.succeed(True)
4933-
4934- def storeSelfSignedCertificate(self, domainName, mainCert):
4935- """
4936-
4937- @return: a Deferred which will fire when the certificate has been
4938- stored successfully.
4939- """
4940- assert not isinstance(mainCert, str)
4941- return defer.maybeDeferred(self.remoteStore.__setitem__, domainName, mainCert)
4942-
4943- def getPrivateCertificate(self, domainName):
4944- """
4945-
4946- @return: a PrivateCertificate instance, e.g. a certificate including a
4947- private key, for 'domainName'.
4948- """
4949- return self.localStore[domainName]
4950-
4951-
4952- def genSerial(self, name):
4953- return abs(struct.unpack('!i', md5(name).digest()[:4])[0])
4954-
4955- def addPrivateCertificate(self, subjectName, existingCertificate=None):
4956- """
4957- Add a PrivateCertificate object to this store for this subjectName.
4958-
4959- If existingCertificate is None, add a new self-signed certificate.
4960- """
4961- if existingCertificate is None:
4962- assert '@' not in subjectName, "Don't self-sign user certs!"
4963- mainDN = DistinguishedName(commonName=subjectName)
4964- mainKey = KeyPair.generate()
4965- mainCertReq = mainKey.certificateRequest(mainDN)
4966- mainCertData = mainKey.signCertificateRequest(mainDN, mainCertReq,
4967- lambda dn: True,
4968- self.genSerial(subjectName))
4969- mainCert = mainKey.newCertificate(mainCertData)
4970- else:
4971- mainCert = existingCertificate
4972- self.localStore[subjectName] = mainCert
4973-
4974-import os
4975-
4976-class _pemmap(object):
4977- def __init__(self, pathname, certclass):
4978- self.pathname = pathname
4979- try:
4980- os.makedirs(pathname)
4981- except (OSError, IOError):
4982- pass
4983- self.certclass = certclass
4984-
4985- def file(self, name, mode):
4986- try:
4987- return file(os.path.join(self.pathname, name)+'.pem', mode)
4988- except IOError, ioe:
4989- raise KeyError(name, ioe)
4990-
4991- def __setitem__(self, key, cert):
4992- kn = cert.getSubject().commonName
4993- assert kn == key
4994- self.file(kn, 'wb').write(cert.dumpPEM())
4995-
4996- def __getitem__(self, cn):
4997- return self.certclass.loadPEM(self.file(cn, 'rb').read())
4998-
4999- def iteritems(self):
5000- files = os.listdir(self.pathname)
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: