murder 0.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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