murder 0.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. data/.gitignore +1 -0
  2. data/LICENSE +17 -0
  3. data/README +224 -0
  4. data/Rakefile +52 -0
  5. data/VERSION +1 -0
  6. data/dist/BitTornado/BT1/Choker.py +128 -0
  7. data/dist/BitTornado/BT1/Connecter.py +288 -0
  8. data/dist/BitTornado/BT1/Downloader.py +594 -0
  9. data/dist/BitTornado/BT1/DownloaderFeedback.py +155 -0
  10. data/dist/BitTornado/BT1/Encrypter.py +333 -0
  11. data/dist/BitTornado/BT1/FileSelector.py +245 -0
  12. data/dist/BitTornado/BT1/Filter.py +12 -0
  13. data/dist/BitTornado/BT1/HTTPDownloader.py +251 -0
  14. data/dist/BitTornado/BT1/NatCheck.py +95 -0
  15. data/dist/BitTornado/BT1/PiecePicker.py +320 -0
  16. data/dist/BitTornado/BT1/Rerequester.py +426 -0
  17. data/dist/BitTornado/BT1/Statistics.py +177 -0
  18. data/dist/BitTornado/BT1/Storage.py +584 -0
  19. data/dist/BitTornado/BT1/StorageWrapper.py +1045 -0
  20. data/dist/BitTornado/BT1/StreamCheck.py +135 -0
  21. data/dist/BitTornado/BT1/T2T.py +193 -0
  22. data/dist/BitTornado/BT1/Uploader.py +145 -0
  23. data/dist/BitTornado/BT1/__init__.py +1 -0
  24. data/dist/BitTornado/BT1/btformats.py +100 -0
  25. data/dist/BitTornado/BT1/fakeopen.py +89 -0
  26. data/dist/BitTornado/BT1/makemetafile.py +263 -0
  27. data/dist/BitTornado/BT1/track.py +1067 -0
  28. data/dist/BitTornado/ConfigDir.py +401 -0
  29. data/dist/BitTornado/ConfigReader.py +1068 -0
  30. data/dist/BitTornado/ConnChoice.py +31 -0
  31. data/dist/BitTornado/CreateIcons.py +105 -0
  32. data/dist/BitTornado/CurrentRateMeasure.py +37 -0
  33. data/dist/BitTornado/HTTPHandler.py +167 -0
  34. data/dist/BitTornado/PSYCO.py +5 -0
  35. data/dist/BitTornado/RateLimiter.py +153 -0
  36. data/dist/BitTornado/RateMeasure.py +75 -0
  37. data/dist/BitTornado/RawServer.py +195 -0
  38. data/dist/BitTornado/ServerPortHandler.py +188 -0
  39. data/dist/BitTornado/SocketHandler.py +375 -0
  40. data/dist/BitTornado/__init__.py +63 -0
  41. data/dist/BitTornado/bencode.py +319 -0
  42. data/dist/BitTornado/bitfield.py +162 -0
  43. data/dist/BitTornado/clock.py +27 -0
  44. data/dist/BitTornado/download_bt1.py +882 -0
  45. data/dist/BitTornado/inifile.py +169 -0
  46. data/dist/BitTornado/iprangeparse.py +194 -0
  47. data/dist/BitTornado/launchmanycore.py +381 -0
  48. data/dist/BitTornado/natpunch.py +254 -0
  49. data/dist/BitTornado/parseargs.py +137 -0
  50. data/dist/BitTornado/parsedir.py +150 -0
  51. data/dist/BitTornado/piecebuffer.py +86 -0
  52. data/dist/BitTornado/selectpoll.py +109 -0
  53. data/dist/BitTornado/subnetparse.py +218 -0
  54. data/dist/BitTornado/torrentlistparse.py +38 -0
  55. data/dist/BitTornado/zurllib.py +100 -0
  56. data/dist/murder_client.py +291 -0
  57. data/dist/murder_make_torrent.py +46 -0
  58. data/dist/murder_tracker.py +28 -0
  59. data/doc/examples/Capfile +28 -0
  60. data/lib/capistrano/recipes/deploy/strategy/murder.rb +52 -0
  61. data/lib/murder.rb +43 -0
  62. data/lib/murder/admin.rb +47 -0
  63. data/lib/murder/murder.rb +121 -0
  64. data/murder.gemspec +101 -0
  65. metadata +129 -0
@@ -0,0 +1,155 @@
1
+ # Written by Bram Cohen
2
+ # see LICENSE.txt for license information
3
+
4
+ from cStringIO import StringIO
5
+ from urllib import quote
6
+ from threading import Event
7
+
8
+ try:
9
+ True
10
+ except:
11
+ True = 1
12
+ False = 0
13
+
14
+ class DownloaderFeedback:
15
+ def __init__(self, choker, httpdl, add_task, upfunc, downfunc,
16
+ ratemeasure, leftfunc, file_length, finflag, sp, statistics,
17
+ statusfunc = None, interval = None):
18
+ self.choker = choker
19
+ self.httpdl = httpdl
20
+ self.add_task = add_task
21
+ self.upfunc = upfunc
22
+ self.downfunc = downfunc
23
+ self.ratemeasure = ratemeasure
24
+ self.leftfunc = leftfunc
25
+ self.file_length = file_length
26
+ self.finflag = finflag
27
+ self.sp = sp
28
+ self.statistics = statistics
29
+ self.lastids = []
30
+ self.spewdata = None
31
+ self.doneprocessing = Event()
32
+ self.doneprocessing.set()
33
+ if statusfunc:
34
+ self.autodisplay(statusfunc, interval)
35
+
36
+
37
+ def _rotate(self):
38
+ cs = self.choker.connections
39
+ for id in self.lastids:
40
+ for i in xrange(len(cs)):
41
+ if cs[i].get_id() == id:
42
+ return cs[i:] + cs[:i]
43
+ return cs
44
+
45
+ def spews(self):
46
+ l = []
47
+ cs = self._rotate()
48
+ self.lastids = [c.get_id() for c in cs]
49
+ for c in cs:
50
+ a = {}
51
+ a['id'] = c.get_readable_id()
52
+ a['ip'] = c.get_ip()
53
+ a['optimistic'] = (c is self.choker.connections[0])
54
+ if c.is_locally_initiated():
55
+ a['direction'] = 'L'
56
+ else:
57
+ a['direction'] = 'R'
58
+ u = c.get_upload()
59
+ a['uprate'] = int(u.measure.get_rate())
60
+ a['uinterested'] = u.is_interested()
61
+ a['uchoked'] = u.is_choked()
62
+ d = c.get_download()
63
+ a['downrate'] = int(d.measure.get_rate())
64
+ a['dinterested'] = d.is_interested()
65
+ a['dchoked'] = d.is_choked()
66
+ a['snubbed'] = d.is_snubbed()
67
+ a['utotal'] = d.connection.upload.measure.get_total()
68
+ a['dtotal'] = d.connection.download.measure.get_total()
69
+ if len(d.connection.download.have) > 0:
70
+ a['completed'] = float(len(d.connection.download.have)-d.connection.download.have.numfalse)/float(len(d.connection.download.have))
71
+ else:
72
+ a['completed'] = 1.0
73
+ a['speed'] = d.connection.download.peermeasure.get_rate()
74
+
75
+ l.append(a)
76
+
77
+ for dl in self.httpdl.get_downloads():
78
+ if dl.goodseed:
79
+ a = {}
80
+ a['id'] = 'http seed'
81
+ a['ip'] = dl.baseurl
82
+ a['optimistic'] = False
83
+ a['direction'] = 'L'
84
+ a['uprate'] = 0
85
+ a['uinterested'] = False
86
+ a['uchoked'] = False
87
+ a['downrate'] = int(dl.measure.get_rate())
88
+ a['dinterested'] = True
89
+ a['dchoked'] = not dl.active
90
+ a['snubbed'] = not dl.active
91
+ a['utotal'] = None
92
+ a['dtotal'] = dl.measure.get_total()
93
+ a['completed'] = 1.0
94
+ a['speed'] = None
95
+
96
+ l.append(a)
97
+
98
+ return l
99
+
100
+
101
+ def gather(self, displayfunc = None):
102
+ s = {'stats': self.statistics.update()}
103
+ if self.sp.isSet():
104
+ s['spew'] = self.spews()
105
+ else:
106
+ s['spew'] = None
107
+ s['up'] = self.upfunc()
108
+ if self.finflag.isSet():
109
+ s['done'] = self.file_length
110
+ return s
111
+ s['down'] = self.downfunc()
112
+ obtained, desired = self.leftfunc()
113
+ s['done'] = obtained
114
+ s['wanted'] = desired
115
+ if desired > 0:
116
+ s['frac'] = float(obtained)/desired
117
+ else:
118
+ s['frac'] = 1.0
119
+ if desired == obtained:
120
+ s['time'] = 0
121
+ else:
122
+ s['time'] = self.ratemeasure.get_time_left(desired-obtained)
123
+ return s
124
+
125
+
126
+ def display(self, displayfunc):
127
+ if not self.doneprocessing.isSet():
128
+ return
129
+ self.doneprocessing.clear()
130
+ stats = self.gather()
131
+ if self.finflag.isSet():
132
+ displayfunc(dpflag = self.doneprocessing,
133
+ upRate = stats['up'],
134
+ statistics = stats['stats'], spew = stats['spew'])
135
+ elif stats['time'] is not None:
136
+ displayfunc(dpflag = self.doneprocessing,
137
+ fractionDone = stats['frac'], sizeDone = stats['done'],
138
+ downRate = stats['down'], upRate = stats['up'],
139
+ statistics = stats['stats'], spew = stats['spew'],
140
+ timeEst = stats['time'])
141
+ else:
142
+ displayfunc(dpflag = self.doneprocessing,
143
+ fractionDone = stats['frac'], sizeDone = stats['done'],
144
+ downRate = stats['down'], upRate = stats['up'],
145
+ statistics = stats['stats'], spew = stats['spew'])
146
+
147
+
148
+ def autodisplay(self, displayfunc, interval):
149
+ self.displayfunc = displayfunc
150
+ self.interval = interval
151
+ self._autodisplay()
152
+
153
+ def _autodisplay(self):
154
+ self.add_task(self._autodisplay, self.interval)
155
+ self.display(self.displayfunc)
@@ -0,0 +1,333 @@
1
+ # Written by Bram Cohen
2
+ # see LICENSE.txt for license information
3
+
4
+ from cStringIO import StringIO
5
+ from binascii import b2a_hex
6
+ from socket import error as socketerror
7
+ from urllib import quote
8
+ from traceback import print_exc
9
+ try:
10
+ True
11
+ except:
12
+ True = 1
13
+ False = 0
14
+
15
+ MAX_INCOMPLETE = 8
16
+
17
+ protocol_name = 'BitTorrent protocol'
18
+ option_pattern = chr(0)*8
19
+
20
+ def toint(s):
21
+ return long(b2a_hex(s), 16)
22
+
23
+ def tobinary(i):
24
+ return (chr(i >> 24) + chr((i >> 16) & 0xFF) +
25
+ chr((i >> 8) & 0xFF) + chr(i & 0xFF))
26
+
27
+ hexchars = '0123456789ABCDEF'
28
+ hexmap = []
29
+ for i in xrange(256):
30
+ hexmap.append(hexchars[(i&0xF0)/16]+hexchars[i&0x0F])
31
+
32
+ def tohex(s):
33
+ r = []
34
+ for c in s:
35
+ r.append(hexmap[ord(c)])
36
+ return ''.join(r)
37
+
38
+ def make_readable(s):
39
+ if not s:
40
+ return ''
41
+ if quote(s).find('%') >= 0:
42
+ return tohex(s)
43
+ return '"'+s+'"'
44
+
45
+
46
+ class IncompleteCounter:
47
+ def __init__(self):
48
+ self.c = 0
49
+ def increment(self):
50
+ self.c += 1
51
+ def decrement(self):
52
+ self.c -= 1
53
+ def toomany(self):
54
+ return self.c >= MAX_INCOMPLETE
55
+
56
+ incompletecounter = IncompleteCounter()
57
+
58
+
59
+ # header, reserved, download id, my id, [length, message]
60
+
61
+ class Connection:
62
+ def __init__(self, Encoder, connection, id, ext_handshake=False):
63
+ self.Encoder = Encoder
64
+ self.connection = connection
65
+ self.connecter = Encoder.connecter
66
+ self.id = id
67
+ self.readable_id = make_readable(id)
68
+ self.locally_initiated = (id != None)
69
+ self.complete = False
70
+ self.keepalive = lambda: None
71
+ self.closed = False
72
+ self.buffer = StringIO()
73
+ if self.locally_initiated:
74
+ incompletecounter.increment()
75
+ if self.locally_initiated or ext_handshake:
76
+ self.connection.write(chr(len(protocol_name)) + protocol_name +
77
+ option_pattern + self.Encoder.download_id)
78
+ if ext_handshake:
79
+ self.Encoder.connecter.external_connection_made += 1
80
+ self.connection.write(self.Encoder.my_id)
81
+ self.next_len, self.next_func = 20, self.read_peer_id
82
+ else:
83
+ self.next_len, self.next_func = 1, self.read_header_len
84
+ self.Encoder.raw_server.add_task(self._auto_close, 15)
85
+
86
+ def get_ip(self, real=False):
87
+ return self.connection.get_ip(real)
88
+
89
+ def get_id(self):
90
+ return self.id
91
+
92
+ def get_readable_id(self):
93
+ return self.readable_id
94
+
95
+ def is_locally_initiated(self):
96
+ return self.locally_initiated
97
+
98
+ def is_flushed(self):
99
+ return self.connection.is_flushed()
100
+
101
+ def read_header_len(self, s):
102
+ if ord(s) != len(protocol_name):
103
+ return None
104
+ return len(protocol_name), self.read_header
105
+
106
+ def read_header(self, s):
107
+ if s != protocol_name:
108
+ return None
109
+ return 8, self.read_reserved
110
+
111
+ def read_reserved(self, s):
112
+ return 20, self.read_download_id
113
+
114
+ def read_download_id(self, s):
115
+ if s != self.Encoder.download_id:
116
+ return None
117
+ if not self.locally_initiated:
118
+ self.Encoder.connecter.external_connection_made += 1
119
+ self.connection.write(chr(len(protocol_name)) + protocol_name +
120
+ option_pattern + self.Encoder.download_id + self.Encoder.my_id)
121
+ return 20, self.read_peer_id
122
+
123
+ def read_peer_id(self, s):
124
+ if not self.id:
125
+ self.id = s
126
+ self.readable_id = make_readable(s)
127
+ else:
128
+ if s != self.id:
129
+ return None
130
+ self.complete = self.Encoder.got_id(self)
131
+ if not self.complete:
132
+ return None
133
+ if self.locally_initiated:
134
+ self.connection.write(self.Encoder.my_id)
135
+ incompletecounter.decrement()
136
+ c = self.Encoder.connecter.connection_made(self)
137
+ self.keepalive = c.send_keepalive
138
+ return 4, self.read_len
139
+
140
+ def read_len(self, s):
141
+ l = toint(s)
142
+ if l > self.Encoder.max_len:
143
+ return None
144
+ return l, self.read_message
145
+
146
+ def read_message(self, s):
147
+ if s != '':
148
+ self.connecter.got_message(self, s)
149
+ return 4, self.read_len
150
+
151
+ def read_dead(self, s):
152
+ return None
153
+
154
+ def _auto_close(self):
155
+ if not self.complete:
156
+ self.close()
157
+
158
+ def close(self):
159
+ if not self.closed:
160
+ self.connection.close()
161
+ self.sever()
162
+
163
+ def sever(self):
164
+ self.closed = True
165
+ del self.Encoder.connections[self.connection]
166
+ if self.complete:
167
+ self.connecter.connection_lost(self)
168
+ elif self.locally_initiated:
169
+ incompletecounter.decrement()
170
+
171
+ def send_message_raw(self, message):
172
+ if not self.closed:
173
+ self.connection.write(message)
174
+
175
+ def data_came_in(self, connection, s):
176
+ self.Encoder.measurefunc(len(s))
177
+ while True:
178
+ if self.closed:
179
+ return
180
+ i = self.next_len - self.buffer.tell()
181
+ if i > len(s):
182
+ self.buffer.write(s)
183
+ return
184
+ self.buffer.write(s[:i])
185
+ s = s[i:]
186
+ m = self.buffer.getvalue()
187
+ self.buffer.reset()
188
+ self.buffer.truncate()
189
+ try:
190
+ x = self.next_func(m)
191
+ except:
192
+ self.next_len, self.next_func = 1, self.read_dead
193
+ raise
194
+ if x is None:
195
+ self.close()
196
+ return
197
+ self.next_len, self.next_func = x
198
+
199
+ def connection_flushed(self, connection):
200
+ if self.complete:
201
+ self.connecter.connection_flushed(self)
202
+
203
+ def connection_lost(self, connection):
204
+ if self.Encoder.connections.has_key(connection):
205
+ self.sever()
206
+
207
+
208
+ class Encoder:
209
+ def __init__(self, connecter, raw_server, my_id, max_len,
210
+ schedulefunc, keepalive_delay, download_id,
211
+ measurefunc, config):
212
+ self.raw_server = raw_server
213
+ self.connecter = connecter
214
+ self.my_id = my_id
215
+ self.max_len = max_len
216
+ self.schedulefunc = schedulefunc
217
+ self.keepalive_delay = keepalive_delay
218
+ self.download_id = download_id
219
+ self.measurefunc = measurefunc
220
+ self.config = config
221
+ self.connections = {}
222
+ self.banned = {}
223
+ self.to_connect = []
224
+ self.paused = False
225
+ if self.config['max_connections'] == 0:
226
+ self.max_connections = 2 ** 30
227
+ else:
228
+ self.max_connections = self.config['max_connections']
229
+ schedulefunc(self.send_keepalives, keepalive_delay)
230
+
231
+ def send_keepalives(self):
232
+ self.schedulefunc(self.send_keepalives, self.keepalive_delay)
233
+ if self.paused:
234
+ return
235
+ for c in self.connections.values():
236
+ c.keepalive()
237
+
238
+ def start_connections(self, list):
239
+ if not self.to_connect:
240
+ self.raw_server.add_task(self._start_connection_from_queue)
241
+ self.to_connect = list
242
+
243
+ def _start_connection_from_queue(self):
244
+ if self.connecter.external_connection_made:
245
+ max_initiate = self.config['max_initiate']
246
+ else:
247
+ max_initiate = int(self.config['max_initiate']*1.5)
248
+ cons = len(self.connections)
249
+ if cons >= self.max_connections or cons >= max_initiate:
250
+ delay = 60
251
+ elif self.paused or incompletecounter.toomany():
252
+ delay = 1
253
+ else:
254
+ delay = 0
255
+ dns, id = self.to_connect.pop(0)
256
+ self.start_connection(dns, id)
257
+ if self.to_connect:
258
+ self.raw_server.add_task(self._start_connection_from_queue, delay)
259
+
260
+ def start_connection(self, dns, id):
261
+ if ( self.paused
262
+ or len(self.connections) >= self.max_connections
263
+ or id == self.my_id
264
+ or self.banned.has_key(dns[0]) ):
265
+ return True
266
+ for v in self.connections.values():
267
+ if v is None:
268
+ continue
269
+ if id and v.id == id:
270
+ return True
271
+ ip = v.get_ip(True)
272
+ if self.config['security'] and ip != 'unknown' and ip == dns[0]:
273
+ return True
274
+ try:
275
+ c = self.raw_server.start_connection(dns)
276
+ con = Connection(self, c, id)
277
+ self.connections[c] = con
278
+ c.set_handler(con)
279
+ except socketerror:
280
+ return False
281
+ return True
282
+
283
+ def _start_connection(self, dns, id):
284
+ def foo(self=self, dns=dns, id=id):
285
+ self.start_connection(dns, id)
286
+
287
+ self.schedulefunc(foo, 0)
288
+
289
+ def got_id(self, connection):
290
+ if connection.id == self.my_id:
291
+ self.connecter.external_connection_made -= 1
292
+ return False
293
+ ip = connection.get_ip(True)
294
+ if self.config['security'] and self.banned.has_key(ip):
295
+ return False
296
+ for v in self.connections.values():
297
+ if connection is not v:
298
+ if connection.id == v.id:
299
+ return False
300
+ if self.config['security'] and ip != 'unknown' and ip == v.get_ip(True):
301
+ v.close()
302
+ return True
303
+
304
+ def external_connection_made(self, connection):
305
+ if self.paused or len(self.connections) >= self.max_connections:
306
+ connection.close()
307
+ return False
308
+ con = Connection(self, connection, None)
309
+ self.connections[connection] = con
310
+ connection.set_handler(con)
311
+ return True
312
+
313
+ def externally_handshaked_connection_made(self, connection, options, already_read):
314
+ if self.paused or len(self.connections) >= self.max_connections:
315
+ connection.close()
316
+ return False
317
+ con = Connection(self, connection, None, True)
318
+ self.connections[connection] = con
319
+ connection.set_handler(con)
320
+ if already_read:
321
+ con.data_came_in(con, already_read)
322
+ return True
323
+
324
+ def close_all(self):
325
+ for c in self.connections.values():
326
+ c.close()
327
+ self.connections = {}
328
+
329
+ def ban(self, ip):
330
+ self.banned[ip] = 1
331
+
332
+ def pause(self, flag):
333
+ self.paused = flag