~ubuntu-branches/ubuntu/oneiric/bittornado/oneiric

« back to all changes in this revision

Viewing changes to .pc/17_fix_NatCheck_bufferlen_error.dpatch/BitTornado/BT1/NatCheck.py

  • Committer: Bazaar Package Importer
  • Author(s): Cameron Dale
  • Date: 2010-03-21 14:36:30 UTC
  • Revision ID: james.westby@ubuntu.com-20100321143630-d1zk1zdasaf8125s
Tags: 0.3.18-10
* New patch from upstream's CVS to allow torrents that only have an
  announce list: 30_announce_list_only_torrents.dpatch (Closes: #551766)
* Fix a lot of lintian warnings
  - Update standards version to 3.8.4 (no changes)
* Fix for when compact_reqd is turned off:
  31_fix_for_compact_reqd_off.dpatch (Closes: #574860)
* Switch to the new "3.0 (quilt)" source format

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Written by Bram Cohen
 
2
# see LICENSE.txt for license information
 
3
 
 
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
 
8
 
 
9
try:
 
10
    True
 
11
except:
 
12
    True = 1
 
13
    False = 0
 
14
 
 
15
CHECK_PEER_ID_ENCRYPTED = True
 
16
 
 
17
protocol_name = 'BitTorrent protocol'
 
18
 
 
19
# header, reserved, download id, my id, [length, message]
 
20
 
 
21
class NatCheck:
 
22
    def __init__(self, resultfunc, downloadid, peerid, ip, port, rawserver,
 
23
                 encrypted = False):
 
24
        self.resultfunc = resultfunc
 
25
        self.downloadid = downloadid
 
26
        self.peerid = peerid
 
27
        self.ip = ip
 
28
        self.port = port
 
29
        self.encrypted = encrypted
 
30
        self.closed = False
 
31
        self.buffer = ''
 
32
        self.read = self._read
 
33
        self.write = self._write
 
34
        try:
 
35
            self.connection = rawserver.start_connection((ip, port), self)
 
36
            if encrypted:
 
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())
 
40
            else:
 
41
                self.encrypter = None
 
42
                self.write(chr(len(protocol_name)) + protocol_name +
 
43
                    (chr(0) * 8) + downloadid)
 
44
        except socketerror:
 
45
            self.answer(False)
 
46
        except IOError:
 
47
            self.answer(False)
 
48
        self.next_len, self.next_func = 1+len(protocol_name), self.read_header
 
49
 
 
50
    def answer(self, result):
 
51
        self.closed = True
 
52
        try:
 
53
            self.connection.close()
 
54
        except AttributeError:
 
55
            pass
 
56
        self.resultfunc(result, self.downloadid, self.peerid, self.ip, self.port)
 
57
 
 
58
    def _read_header(self, s):
 
59
        if s == chr(len(protocol_name))+protocol_name:
 
60
            return 8, self.read_options
 
61
        return None
 
62
 
 
63
    def read_header(self, s):
 
64
        if self._read_header(s):
 
65
            if self.encrypted:
 
66
                return None
 
67
            return 8, self.read_options
 
68
        if not self.encrypted:
 
69
            return None
 
70
        self._write_buffer(s)
 
71
        return self.encrypter.keylength, self.read_crypto_header
 
72
 
 
73
    ################## ENCRYPTION SUPPORT ######################
 
74
 
 
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
 
79
        if self.buffer:
 
80
            self.buffer = self.encrypter.decrypt(self.buffer)
 
81
 
 
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(
 
90
                        ('\x00'*8)            # VC
 
91
                      + cryptmode             # acceptable crypto modes
 
92
                      + tobinary16(len(padc))
 
93
                      + padc                  # PadC
 
94
                      + '\x00\x00' ) )        # no initial payload data
 
95
        self._max_search = 520
 
96
        return 1, self.read_crypto_block4a
 
97
 
 
98
    def _search_for_pattern(self, s, pat):
 
99
        p = s.find(pat)
 
100
        if p < 0:
 
101
            if len(s) >= len(pat):
 
102
                self._max_search -= len(s)+1-len(pat)
 
103
            if self._max_search < 0:
 
104
                self.close()
 
105
                return False
 
106
            self._write_buffer(s[1-len(pat):])
 
107
            return False
 
108
        self._write_buffer(s[p+len(pat):])
 
109
        return True
 
110
 
 
111
    ### OUTGOING CONNECTION ###
 
112
 
 
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
 
117
            self.answer(True)
 
118
            return None
 
119
        self._start_crypto()
 
120
        return 6, self.read_crypto_block4b
 
121
 
 
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])
 
127
        if padlen > 512:
 
128
            return None
 
129
        if padlen:
 
130
            return padlen, self.read_crypto_pad4
 
131
        return self.read_crypto_block4done()
 
132
 
 
133
    def read_crypto_pad4(self, s):
 
134
        # discard data
 
135
        return self.read_crypto_block4done()
 
136
 
 
137
    def read_crypto_block4done(self):
 
138
        if DEBUG:
 
139
            self._log_start()
 
140
        if self.cryptmode == 1:     # only handshake encryption
 
141
            if not self.buffer:  # oops; check for exceptions to this
 
142
                return None
 
143
            self._end_crypto()
 
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
 
147
 
 
148
    ### START PROTOCOL OVER ENCRYPTED CONNECTION ###
 
149
 
 
150
    def read_encrypted_header(self, s):
 
151
        return self._read_header(s)
 
152
 
 
153
    ################################################
 
154
 
 
155
    def read_options(self, s):
 
156
        return 20, self.read_download_id
 
157
 
 
158
    def read_download_id(self, s):
 
159
        if s != self.downloadid:
 
160
            return None
 
161
        return 20, self.read_peer_id
 
162
 
 
163
    def read_peer_id(self, s):
 
164
        if s != self.peerid:
 
165
            return None
 
166
        self.answer(True)
 
167
        return None
 
168
 
 
169
    def _write(self, message):
 
170
        if not self.closed:
 
171
            self.connection.write(message)
 
172
 
 
173
    def data_came_in(self, connection, s):
 
174
        self.read(s)
 
175
 
 
176
    def _write_buffer(self, s):
 
177
        self.buffer = s+self.buffer
 
178
 
 
179
    def _read(self, s):
 
180
        self.buffer += s
 
181
        while True:
 
182
            if self.closed:
 
183
                return
 
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:
 
189
                m = self.buffer
 
190
                self.buffer = ''
 
191
            elif len(self.buffer) >= self.next_len:
 
192
                m = self.buffer[:self.next_len]
 
193
                self.buffer = self.buffer[self.next_len:]
 
194
            else:
 
195
                return
 
196
            try:
 
197
                x = self.next_func(m)
 
198
            except:
 
199
                if not self.closed:
 
200
                    self.answer(False)
 
201
                return
 
202
            if x is None:
 
203
                if not self.closed:
 
204
                    self.answer(False)
 
205
                return
 
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:
 
210
                self._read2('')
 
211
                return
 
212
 
 
213
    def connection_lost(self, connection):
 
214
        if not self.closed:
 
215
            self.closed = True
 
216
            self.resultfunc(False, self.downloadid, self.peerid, self.ip, self.port)
 
217
 
 
218
    def connection_flushed(self, connection):
 
219
        pass