Merge lp:~cyli/divmod.org/remove-vertex into lp:divmod.org
- remove-vertex
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Allen Short | Approve | ||
Review via email: mp+171717@code.launchpad.net |
Commit message
Description of the change
Proposing to remove Vertex from the divmod repos. I found only one branch with a merge proposal for it:
https:/
I've ported trunk as well as alfred-54's branch to https:/
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' |
1779 | Binary 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' |
1781 | Binary 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.