1
# Written by Bram Cohen
2
# see LICENSE.txt for license information
4
from cStringIO import StringIO
5
from socket import error as socketerror
6
from traceback import print_exc
7
from BitTornado.BTcrypto import Crypto, CRYPTO_OK
15
CHECK_PEER_ID_ENCRYPTED = True
17
protocol_name = 'BitTorrent protocol'
19
# header, reserved, download id, my id, [length, message]
22
def __init__(self, resultfunc, downloadid, peerid, ip, port, rawserver,
24
self.resultfunc = resultfunc
25
self.downloadid = downloadid
29
self.encrypted = encrypted
32
self.read = self._read
33
self.write = self._write
35
self.connection = rawserver.start_connection((ip, port), self)
37
self._dc = not(CRYPTO_OK and CHECK_PEER_ID_ENCRYPTED)
38
self.encrypter = Crypto(True, disable_crypto = self._dc)
39
self.write(self.encrypter.pubkey+self.encrypter.padding())
42
self.write(chr(len(protocol_name)) + protocol_name +
43
(chr(0) * 8) + downloadid)
48
self.next_len, self.next_func = 1+len(protocol_name), self.read_header
50
def answer(self, result):
53
self.connection.close()
54
except AttributeError:
56
self.resultfunc(result, self.downloadid, self.peerid, self.ip, self.port)
58
def _read_header(self, s):
59
if s == chr(len(protocol_name))+protocol_name:
60
return 8, self.read_options
63
def read_header(self, s):
64
if self._read_header(s):
67
return 8, self.read_options
68
if not self.encrypted:
71
return self.encrypter.keylength, self.read_crypto_header
73
################## ENCRYPTION SUPPORT ######################
75
def _start_crypto(self):
76
self.encrypter.setrawaccess(self._read,self._write)
77
self.write = self.encrypter.write
78
self.read = self.encrypter.read
80
self.buffer = self.encrypter.decrypt(self.buffer)
82
def read_crypto_header(self, s):
83
self.encrypter.received_key(s)
84
self.encrypter.set_skey(self.downloadid)
85
cryptmode = '\x00\x00\x00\x02' # full stream encryption
86
padc = self.encrypter.padding()
87
self.write( self.encrypter.block3a
88
+ self.encrypter.block3b
89
+ self.encrypter.encrypt(
91
+ cryptmode # acceptable crypto modes
92
+ tobinary16(len(padc))
94
+ '\x00\x00' ) ) # no initial payload data
95
self._max_search = 520
96
return 1, self.read_crypto_block4a
98
def _search_for_pattern(self, s, pat):
101
if len(s) >= len(pat):
102
self._max_search -= len(s)+1-len(pat)
103
if self._max_search < 0:
106
self._write_buffer(s[1-len(pat):])
108
self._write_buffer(s[p+len(pat):])
111
### OUTGOING CONNECTION ###
113
def read_crypto_block4a(self, s):
114
if not self._search_for_pattern(s,self.encrypter.VC_pattern()):
115
return -1, self.read_crypto_block4a # wait for more data
116
if self._dc: # can't or won't go any further
120
return 6, self.read_crypto_block4b
122
def read_crypto_block4b(self, s):
123
self.cryptmode = toint(s[:4]) % 4
124
if self.cryptmode != 2:
125
return None # unknown encryption
126
padlen = (ord(s[4])<<8)+ord(s[5])
130
return padlen, self.read_crypto_pad4
131
return self.read_crypto_block4done()
133
def read_crypto_pad4(self, s):
135
return self.read_crypto_block4done()
137
def read_crypto_block4done(self):
140
if self.cryptmode == 1: # only handshake encryption
141
if not self.buffer: # oops; check for exceptions to this
144
self.write(chr(len(protocol_name)) + protocol_name +
145
option_pattern + self.Encoder.download_id)
146
return 1+len(protocol_name), self.read_encrypted_header
148
### START PROTOCOL OVER ENCRYPTED CONNECTION ###
150
def read_encrypted_header(self, s):
151
return self._read_header(s)
153
################################################
155
def read_options(self, s):
156
return 20, self.read_download_id
158
def read_download_id(self, s):
159
if s != self.downloadid:
161
return 20, self.read_peer_id
163
def read_peer_id(self, s):
169
def _write(self, message):
171
self.connection.write(message)
173
def data_came_in(self, connection, s):
176
def _write_buffer(self, s):
177
self.buffer = s+self.buffer
184
# self.next_len = # of characters function expects
185
# or 0 = all characters in the buffer
186
# or -1 = wait for next read, then all characters in the buffer
187
# not compatible w/ keepalives, switch out after all negotiation complete
188
if self.next_len <= 0:
191
elif len(self.buffer) >= self.next_len:
192
m = self.buffer[:self.next_len]
193
self.buffer = self.buffer[self.next_len:]
197
x = self.next_func(m)
206
self.next_len, self.next_func = x
207
if self.next_len < 0: # already checked buffer
208
return # wait for additional data
209
if self.bufferlen is not None:
213
def connection_lost(self, connection):
216
self.resultfunc(False, self.downloadid, self.peerid, self.ip, self.port)
218
def connection_flushed(self, connection):