class NatCheck: def __init__(self, resultfunc, downloadid, peerid, ip, port, rawserver, encrypted = False): self.resultfunc = resultfunc self.downloadid = downloadid self.peerid = peerid self.ip = ip self.port = port self.encrypted = encrypted self.closed = False self.buffer = '' self.read = self._read self.write = self._write try: self.connection = rawserver.start_connection((ip, port), self) if encrypted: self._dc = not(CRYPTO_OK and CHECK_PEER_ID_ENCRYPTED) self.encrypter = Crypto(True, disable_crypto = self._dc) self.write(self.encrypter.pubkey+self.encrypter.padding()) else: self.encrypter = None self.write(chr(len(protocol_name)) + protocol_name + (chr(0) * 8) + downloadid) except socketerror: self.answer(False) except IOError: self.answer(False) self.next_len, self.next_func = 1+len(protocol_name), self.read_header def answer(self, result): self.closed = True try: self.connection.close() except AttributeError: pass self.resultfunc(result, self.downloadid, self.peerid, self.ip, self.port) def _read_header(self, s): if s == chr(len(protocol_name))+protocol_name: return 8, self.read_options return None def read_header(self, s): if self._read_header(s): if self.encrypted: return None return 8, self.read_options if not self.encrypted: return None self._write_buffer(s) return self.encrypter.keylength, self.read_crypto_header ################## ENCRYPTION SUPPORT ###################### def _start_crypto(self): self.encrypter.setrawaccess(self._read,self._write) self.write = self.encrypter.write self.read = self.encrypter.read if self.buffer: self.buffer = self.encrypter.decrypt(self.buffer) def read_crypto_header(self, s): self.encrypter.received_key(s) self.encrypter.set_skey(self.downloadid) cryptmode = '\x00\x00\x00\x02' # full stream encryption padc = self.encrypter.padding() self.write( self.encrypter.block3a + self.encrypter.block3b + self.encrypter.encrypt( ('\x00'*8) # VC + cryptmode # acceptable crypto modes + tobinary16(len(padc)) + padc # PadC + '\x00\x00' ) ) # no initial payload data self._max_search = 520 return 1, self.read_crypto_block4a def _search_for_pattern(self, s, pat): p = s.find(pat) if p < 0: if len(s) >= len(pat): self._max_search -= len(s)+1-len(pat) if self._max_search < 0: self.close() return False self._write_buffer(s[1-len(pat):]) return False self._write_buffer(s[p+len(pat):]) return True ### OUTGOING CONNECTION ### def read_crypto_block4a(self, s): if not self._search_for_pattern(s,self.encrypter.VC_pattern()): return -1, self.read_crypto_block4a # wait for more data if self._dc: # can't or won't go any further self.answer(True) return None self._start_crypto() return 6, self.read_crypto_block4b def read_crypto_block4b(self, s): self.cryptmode = toint(s[:4]) % 4 if self.cryptmode != 2: return None # unknown encryption padlen = (ord(s[4])<<8)+ord(s[5]) if padlen > 512: return None if padlen: return padlen, self.read_crypto_pad4 return self.read_crypto_block4done() def read_crypto_pad4(self, s): # discard data return self.read_crypto_block4done() def read_crypto_block4done(self): if DEBUG: self._log_start() if self.cryptmode == 1: # only handshake encryption if not self.buffer: # oops; check for exceptions to this return None self._end_crypto() self.write(chr(len(protocol_name)) + protocol_name + option_pattern + self.Encoder.download_id) return 1+len(protocol_name), self.read_encrypted_header ### START PROTOCOL OVER ENCRYPTED CONNECTION ### def read_encrypted_header(self, s): return self._read_header(s) ################################################ def read_options(self, s): return 20, self.read_download_id def read_download_id(self, s): if s != self.downloadid: return None return 20, self.read_peer_id def read_peer_id(self, s): if s != self.peerid: return None self.answer(True) return None def _write(self, message): if not self.closed: self.connection.write(message) def data_came_in(self, connection, s): self.read(s) def _write_buffer(self, s): self.buffer = s+self.buffer def _read(self, s): self.buffer += s while True: if self.closed: return # self.next_len = # of characters function expects # or 0 = all characters in the buffer # or -1 = wait for next read, then all characters in the buffer # not compatible w/ keepalives, switch out after all negotiation complete if self.next_len <= 0: m = self.buffer self.buffer = '' elif len(self.buffer) >= self.next_len: m = self.buffer[:self.next_len] self.buffer = self.buffer[self.next_len:] else: return try: x = self.next_func(m) except: if not self.closed: self.answer(False) return if x is None: if not self.closed: self.answer(False) return self.next_len, self.next_func = x if self.next_len < 0: # already checked buffer return # wait for additional data if self.bufferlen is not None: self._read2('') return def connection_lost(self, connection): if not self.closed: self.closed = True self.resultfunc(False, self.downloadid, self.peerid, self.ip, self.port) def connection_flushed(self, connection): pass
class NatCheck: def __init__(self, resultfunc, downloadid, peerid, ip, port, rawserver, encrypted=False): self.resultfunc = resultfunc self.downloadid = downloadid self.peerid = peerid self.ip = ip self.port = port self.encrypted = encrypted self.closed = False self.buffer = '' self.read = self._read self.write = self._write try: self.connection = rawserver.start_connection((ip, port), self) if encrypted: self._dc = not (CRYPTO_OK and CHECK_PEER_ID_ENCRYPTED) self.encrypter = Crypto(True, disable_crypto=self._dc) self.write(self.encrypter.pubkey + self.encrypter.padding()) else: self.encrypter = None self.write( chr(len(protocol_name)) + protocol_name + (chr(0) * 8) + downloadid) except socketerror: self.answer(False) except IOError: self.answer(False) self.next_len = 1 + len(protocol_name) self.next_func = self.read_header def answer(self, result): self.closed = True try: self.connection.close() except AttributeError: pass self.resultfunc(result, self.downloadid, self.peerid, self.ip, self.port) def _read_header(self, s): if s == chr(len(protocol_name)) + protocol_name: return 8, self.read_options return None def read_header(self, s): if self._read_header(s): if self.encrypted: return None return 8, self.read_options if not self.encrypted: return None self._write_buffer(s) return self.encrypter.keylength, self.read_crypto_header ################## ENCRYPTION SUPPORT ###################### def _start_crypto(self): self.encrypter.setrawaccess(self._read, self._write) self.write = self.encrypter.write self.read = self.encrypter.read if self.buffer: self.buffer = self.encrypter.decrypt(self.buffer) def read_crypto_header(self, s): self.encrypter.received_key(s) self.encrypter.set_skey(self.downloadid) cryptmode = '\x00\x00\x00\x02' # full stream encryption padc = self.encrypter.padding() self.write( self.encrypter.block3a + self.encrypter.block3b + self.encrypter.encrypt(('\x00' * 8) # VC + cryptmode # acceptable crypto modes + tobinary16(len(padc)) + padc # PadC + '\x00\x00')) # no initial payload data self._max_search = 520 return 1, self.read_crypto_block4a def _search_for_pattern(self, s, pat): p = s.find(pat) if p < 0: if len(s) >= len(pat): self._max_search -= len(s) + 1 - len(pat) if self._max_search < 0: self.close() return False self._write_buffer(s[1 - len(pat):]) return False self._write_buffer(s[p + len(pat):]) return True ### OUTGOING CONNECTION ### def read_crypto_block4a(self, s): if not self._search_for_pattern(s, self.encrypter.VC_pattern()): return -1, self.read_crypto_block4a # wait for more data if self._dc: # can't or won't go any further self.answer(True) return None self._start_crypto() return 6, self.read_crypto_block4b def read_crypto_block4b(self, s): self.cryptmode = toint(s[:4]) % 4 if self.cryptmode != 2: return None # unknown encryption padlen = (ord(s[4]) << 8) + ord(s[5]) if padlen > 512: return None if padlen: return padlen, self.read_crypto_pad4 return self.read_crypto_block4done() def read_crypto_pad4(self, s): # discard data return self.read_crypto_block4done() def read_crypto_block4done(self): if self.cryptmode == 1: # only handshake encryption if not self.buffer: # oops; check for exceptions to this return None self._end_crypto() self.write( chr(len(protocol_name)) + protocol_name + option_pattern + self.Encoder.download_id) return 1 + len(protocol_name), self.read_encrypted_header ### START PROTOCOL OVER ENCRYPTED CONNECTION ### def read_encrypted_header(self, s): return self._read_header(s) ################################################ def read_options(self, s): return 20, self.read_download_id def read_download_id(self, s): if s != self.downloadid: return None return 20, self.read_peer_id def read_peer_id(self, s): if s != self.peerid: return None self.answer(True) return None def _write(self, message): if not self.closed: self.connection.write(message) def data_came_in(self, connection, s): self.read(s) def _write_buffer(self, s): self.buffer = s + self.buffer def _read(self, s): self.buffer += s while True: if self.closed: return # self.next_len = # of characters function expects # or 0 = all characters in the buffer # or -1 = wait for next read, then all characters in the buffer # not compatible w/ keepalives, switch out after all negotiation # complete if self.next_len <= 0: m = self.buffer self.buffer = '' elif len(self.buffer) >= self.next_len: m = self.buffer[:self.next_len] self.buffer = self.buffer[self.next_len:] else: return try: x = self.next_func(m) except: if not self.closed: self.answer(False) return if x is None: if not self.closed: self.answer(False) return self.next_len, self.next_func = x if self.next_len < 0: # already checked buffer return # wait for additional data if self.bufferlen is not None: self._read2('') return def connection_lost(self, connection): if not self.closed: self.closed = True self.resultfunc(False, self.downloadid, self.peerid, self.ip, self.port) def connection_flushed(self, connection): pass
class Connection: def __init__(self, Encoder, connection, id, ext_handshake=False, encrypted=None, options=None): self.Encoder = Encoder self.connection = connection self.connecter = Encoder.connecter self.id = id self.locally_initiated = (id is not None) self.readable_id = make_readable(id) self.complete = False self.keepalive = lambda: None self.closed = False self.buffer = '' self.bufferlen = None self.log = None self.read = self._read self.write = self._write self.cryptmode = 0 self.encrypter = None if self.locally_initiated: incompletecounter.increment() if encrypted: self.encrypted = True self.encrypter = Crypto(True) self.write(self.encrypter.pubkey + self.encrypter.padding()) else: self.encrypted = False self.write(chr(len(protocol_name)) + protocol_name + option_pattern + self.Encoder.download_id) self.next_len = 1 + len(protocol_name) self.next_func = self.read_header elif ext_handshake: self.Encoder.connecter.external_connection_made += 1 if encrypted: # passed an already running encrypter self.encrypter = encrypted self.encrypted = True self._start_crypto() self.next_len, self.next_func = 14, self.read_crypto_block3c else: self.encrypted = False self.options = options self.write(self.Encoder.my_id) self.next_len, self.next_func = 20, self.read_peer_id else: self.encrypted = None # don't know yet self.next_len = 1 + len(protocol_name) self.next_func = self.read_header self.Encoder.raw_server.add_task(self._auto_close, 30) def _log_start(self): # only called with DEBUG = True self.log = open('peerlog.' + self.get_ip() + '.txt', 'a') self.log.write('connected - ') if self.locally_initiated: self.log.write('outgoing\n') else: self.log.write('incoming\n') self._logwritefunc = self.write self.write = self._log_write def _log_write(self, s): self.log.write('w:' + hexlify(s) + '\n') self._logwritefunc(s) def get_ip(self, real=False): return self.connection.get_ip(real) def get_id(self): return self.id def get_readable_id(self): return self.readable_id def is_locally_initiated(self): return self.locally_initiated def is_encrypted(self): return bool(self.encrypted) def is_flushed(self): return self.connection.is_flushed() def _read_header(self, s): if s == chr(len(protocol_name)) + protocol_name: return 8, self.read_options return None def read_header(self, s): if self._read_header(s): if self.encrypted or self.Encoder.config['crypto_stealth']: return None return 8, self.read_options if self.locally_initiated and not self.encrypted: return None elif not self.Encoder.config['crypto_allowed']: return None if not self.encrypted: self.encrypted = True self.encrypter = Crypto(self.locally_initiated) self._write_buffer(s) return self.encrypter.keylength, self.read_crypto_header ################## ENCRYPTION SUPPORT ###################### def _start_crypto(self): self.encrypter.setrawaccess(self._read, self._write) self.write = self.encrypter.write self.read = self.encrypter.read if self.buffer: self.buffer = self.encrypter.decrypt(self.buffer) def _end_crypto(self): self.read = self._read self.write = self._write self.encrypter = None def read_crypto_header(self, s): self.encrypter.received_key(s) self.encrypter.set_skey(self.Encoder.download_id) if self.locally_initiated: if self.Encoder.config['crypto_only']: cryptmode = '\x00\x00\x00\x02' # full stream encryption else: cryptmode = '\x00\x00\x00\x03' # header or full stream padc = self.encrypter.padding() self.write(self.encrypter.block3a + self.encrypter.block3b + self.encrypter.encrypt( ('\x00' * 8) # VC + cryptmode # acceptable crypto modes + tobinary16(len(padc)) + padc # PadC + '\x00\x00')) # no initial payload data self._max_search = 520 return 1, self.read_crypto_block4a self.write(self.encrypter.pubkey + self.encrypter.padding()) self._max_search = 520 return 0, self.read_crypto_block3a def _search_for_pattern(self, s, pat): p = s.find(pat) if p < 0: if len(s) >= len(pat): self._max_search -= len(s) + 1 - len(pat) if self._max_search < 0: self.close() return False self._write_buffer(s[1 - len(pat):]) return False self._write_buffer(s[p + len(pat):]) return True ### INCOMING CONNECTION ### def read_crypto_block3a(self, s): if not self._search_for_pattern(s, self.encrypter.block3a): return -1, self.read_crypto_block3a # wait for more data return len(self.encrypter.block3b), self.read_crypto_block3b def read_crypto_block3b(self, s): if s != self.encrypter.block3b: return None self.Encoder.connecter.external_connection_made += 1 self._start_crypto() return 14, self.read_crypto_block3c def read_crypto_block3c(self, s): if s[:8] != ('\x00' * 8): # check VC return None self.cryptmode = toint(s[8:12]) % 4 if self.cryptmode == 0: return None # no encryption selected # only header encryption if self.cryptmode == 1 and self.Encoder.config['crypto_only']: return None padlen = (ord(s[12]) << 8) + ord(s[13]) if padlen > 512: return None return padlen + 2, self.read_crypto_pad3 def read_crypto_pad3(self, s): s = s[-2:] ialen = (ord(s[0]) << 8) + ord(s[1]) if ialen > 65535: return None if self.cryptmode == 1: cryptmode = '\x00\x00\x00\x01' # header only encryption else: cryptmode = '\x00\x00\x00\x02' # full stream encryption padd = self.encrypter.padding() self.write(('\x00' * 8) # VC + cryptmode # encryption mode + tobinary16(len(padd)) + padd) # PadD if ialen: return ialen, self.read_crypto_ia return self.read_crypto_block3done() def read_crypto_ia(self, s): if DEBUG: self._log_start() self.log.write('r:' + hexlify(s) + '(ia)\n') if self.buffer: self.log.write('r:' + hexlify(self.buffer) + '(buffer)\n') return self.read_crypto_block3done(s) def read_crypto_block3done(self, ia=''): if DEBUG: if not self.log: self._log_start() if self.cryptmode == 1: # only handshake encryption assert not self.buffer # oops; check for exceptions to this self._end_crypto() if ia: self._write_buffer(ia) return 1 + len(protocol_name), self.read_encrypted_header ### OUTGOING CONNECTION ### def read_crypto_block4a(self, s): if not self._search_for_pattern(s, self.encrypter.VC_pattern()): return -1, self.read_crypto_block4a # wait for more data self._start_crypto() return 6, self.read_crypto_block4b def read_crypto_block4b(self, s): self.cryptmode = toint(s[:4]) % 4 if self.cryptmode == 1: # only header encryption if self.Encoder.config['crypto_only']: return None elif self.cryptmode != 2: return None # unknown encryption padlen = (ord(s[4]) << 8) + ord(s[5]) if padlen > 512: return None if padlen: return padlen, self.read_crypto_pad4 return self.read_crypto_block4done() def read_crypto_pad4(self, s): # discard data return self.read_crypto_block4done() def read_crypto_block4done(self): if DEBUG: self._log_start() if self.cryptmode == 1: # only handshake encryption if not self.buffer: # oops; check for exceptions to this return None self._end_crypto() self.write(chr(len(protocol_name)) + protocol_name + option_pattern + self.Encoder.download_id) return 1 + len(protocol_name), self.read_encrypted_header ### START PROTOCOL OVER ENCRYPTED CONNECTION ### def read_encrypted_header(self, s): return self._read_header(s) ################################################ def read_options(self, s): self.options = s return 20, self.read_download_id def read_download_id(self, s): if s != self.Encoder.download_id or \ not self.Encoder.check_ip(ip=self.get_ip()): return None if not self.locally_initiated: if not self.encrypted: self.Encoder.connecter.external_connection_made += 1 self.write(chr(len(protocol_name)) + protocol_name + option_pattern + self.Encoder.download_id + self.Encoder.my_id) return 20, self.read_peer_id def read_peer_id(self, s): if not self.encrypted and self.Encoder.config['crypto_only']: return None # allows older trackers to ping, # but won't proceed w/ connections if not self.id: self.id = s self.readable_id = make_readable(s) else: if s != self.id: return None self.complete = self.Encoder.got_id(self) if not self.complete: return None if self.locally_initiated: self.write(self.Encoder.my_id) incompletecounter.decrement() self._switch_to_read2() c = self.Encoder.connecter.connection_made(self) self.keepalive = c.send_keepalive return 4, self.read_len def read_len(self, s): l = toint(s) if l > self.Encoder.max_len: return None return l, self.read_message def read_message(self, s): if s != '': self.connecter.got_message(self, s) return 4, self.read_len def read_dead(self, s): return None def _auto_close(self): if not self.complete: self.close() def close(self): if not self.closed: self.connection.close() self.sever() def sever(self): if self.log: self.log.write('closed\n') self.log.close() self.closed = True del self.Encoder.connections[self.connection] if self.complete: self.connecter.connection_lost(self) elif self.locally_initiated: incompletecounter.decrement() def send_message_raw(self, message): self.write(message) def _write(self, message): if not self.closed: self.connection.write(message) def data_came_in(self, connection, s): self.read(s) def _write_buffer(self, s): self.buffer = s + self.buffer def _read(self, s): if self.log: self.log.write('r:' + hexlify(s) + '\n') self.Encoder.measurefunc(len(s)) self.buffer += s while True: if self.closed: return # self.next_len = # of characters function expects # or 0 = all characters in the buffer # or -1 = wait for next read, then all characters in the buffer # not compatible w/ keepalives, switch out after all negotiation # complete if self.next_len <= 0: m = self.buffer self.buffer = '' elif len(self.buffer) >= self.next_len: m = self.buffer[:self.next_len] self.buffer = self.buffer[self.next_len:] else: return try: x = self.next_func(m) except: self.next_len, self.next_func = 1, self.read_dead raise if x is None: self.close() return self.next_len, self.next_func = x if self.next_len < 0: # already checked buffer return # wait for additional data if self.bufferlen is not None: self._read2('') return def _switch_to_read2(self): self._write_buffer = None if self.encrypter: self.encrypter.setrawaccess(self._read2, self._write) else: self.read = self._read2 self.bufferlen = len(self.buffer) self.buffer = [self.buffer] def _read2(self, s): # more efficient, requires buffer['',''] & bufferlen if self.log: self.log.write('r:' + hexlify(s) + '\n') self.Encoder.measurefunc(len(s)) while True: if self.closed: return p = self.next_len - self.bufferlen if self.next_len == 0: m = '' elif s: if p > len(s): self.buffer.append(s) self.bufferlen += len(s) return self.bufferlen = len(s) - p self.buffer.append(s[:p]) m = ''.join(self.buffer) if p == len(s): self.buffer = [] else: self.buffer = [s[p:]] s = '' elif p <= 0: # assert len(self.buffer) == 1 s = self.buffer[0] self.bufferlen = len(s) - self.next_len m = s[:self.next_len] if p == 0: self.buffer = [] else: self.buffer = [s[self.next_len:]] s = '' else: return try: x = self.next_func(m) except: self.next_len, self.next_func = 1, self.read_dead raise if x is None: self.close() return self.next_len, self.next_func = x if self.next_len < 0: # already checked buffer return # wait for additional data def connection_flushed(self, connection): if self.complete: self.connecter.connection_flushed(self) def connection_lost(self, connection): if connection in self.Encoder.connections: self.sever()
class Connection: count = 0 def __init__(self, Encoder, connection, id, ext_handshake=False, encrypted=None, options=None, port=None): self.Encoder = Encoder self.connection = connection self.connecter = Encoder.connecter self.id = id self.locally_initiated = id != None self.readable_id = make_readable(id) self.complete = False self.keepalive = lambda: None self.closed = False self.port = port self.buffer = "" self.bufferlen = None self.log = None self.read = self._read self.write = self._write self.cryptmode = 0 self.encrypter = None if DEBUG2: Connection.count += 1 if self.locally_initiated: if self.port: self.Encoder.peerscache.append({"ip": self.get_ip(), "port": self.port}) incompletecounter.increment() if encrypted: if DEBUG2: print Connection.count, "L+" self.encrypted = True self.encrypter = Crypto(True) self.write(self.encrypter.pubkey + self.encrypter.padding()) else: if DEBUG2: print Connection.count, "L" self.encrypted = False self.write(chr(len(PROTOCOL_NAME)) + PROTOCOL_NAME + str(OPTION_PATTERN) + self.Encoder.download_id) self.next_len, self.next_func = 1 + len(PROTOCOL_NAME), self.read_header elif ext_handshake: self.Encoder.connecter.external_connection_made += 1 if encrypted: # passed an already running encrypter if DEBUG2: print Connection.count, "R" self.encrypter = encrypted self.encrypted = True self.next_len, self.next_func = 14, self.read_crypto_block3c else: if DEBUG2: print Connection.count, "R+" self.encrypted = False self.options = options self.write( chr(len(PROTOCOL_NAME)) + PROTOCOL_NAME + str(OPTION_PATTERN) + self.Encoder.download_id + self.Encoder.my_id ) self.next_len, self.next_func = 20, self.read_peer_id else: self.encrypted = None # don't know yet self.next_len, self.next_func = 1 + len(PROTOCOL_NAME), self.read_header self.Encoder.raw_server.add_task(self._auto_close, self.Encoder.config["negotiation_time"]) def _log_start(self): # only called with DEBUG1 = True self.log = open("peerlog." + self.get_ip() + ".txt", "a") self.log.write("connected - ") if self.locally_initiated: self.log.write("outgoing\n") else: self.log.write("incoming\n") self._logwritefunc = self.write self.write = self._log_write def _log_write(self, s): self.log.write("w:" + b2a_hex(s) + "\n") self._logwritefunc(s) def get_ip(self, real=False): return self.connection.get_ip(real) def get_port(self): return self.port def set_port(self, port): if self.port != port: self.port = port self.Encoder.peerscache.append({"ip": self.get_ip(), "port": self.port}) def get_id(self): return self.id def get_readable_id(self): return self.readable_id def is_locally_initiated(self): return self.locally_initiated def is_encrypted(self): return bool(self.encrypted) def is_flushed(self): return self.connection.is_flushed() def _read_header(self, s): if s == chr(len(PROTOCOL_NAME)) + PROTOCOL_NAME: return 8, self.read_options return None def read_header(self, s): if DEBUG2: print Connection.count, "read_header" if self._read_header(s): if self.encrypted or self.Encoder.config["crypto_stealth"]: return None return 8, self.read_options if self.locally_initiated and not self.encrypted: return None elif not self.Encoder.config["crypto_allowed"]: return None if not self.encrypted: self.encrypted = True self.encrypter = Crypto(self.locally_initiated) self._write_buffer(s) return self.encrypter.keylength, self.read_crypto_header ################## ENCRYPTION SUPPORT ###################### def _start_crypto(self): self.encrypter.setrawaccess(self._read, self._write) self.write = self.encrypter.write self.read = self.encrypter.read if self.buffer: self.buffer = self.encrypter.decrypt(self.buffer) def read_crypto_header(self, s): self.encrypter.received_key(s) self.encrypter.set_skey(self.Encoder.download_id) if self.locally_initiated: if self.Encoder.config["crypto_only"]: cryptmode = "\x00\x00\x00\x02" # full stream encryption else: cryptmode = "\x00\x00\x00\x03" # header or full stream padc = self.encrypter.padding() self.write( self.encrypter.block3a + self.encrypter.block3b + self.encrypter.encrypt( ("\x00" * 8) # VC + cryptmode # acceptable crypto modes + tobinary16(len(padc)) + padc # PadC + "\x00\x00" ) ) # no initial payload data self._max_search = 520 return 1, self.read_crypto_block4a self.write(self.encrypter.pubkey + self.encrypter.padding()) self._max_search = 520 return 0, self.read_crypto_block3a def _search_for_pattern(self, s, pat): p = s.find(pat) if p < 0: if len(s) >= len(pat): self._max_search -= len(s) + 1 - len(pat) if self._max_search < 0: self.close() return False self._write_buffer(s[1 - len(pat) :]) return False self._write_buffer(s[p + len(pat) :]) return True ### INCOMING CONNECTION ### def read_crypto_block3a(self, s): if not self._search_for_pattern(s, self.encrypter.block3a): return -1, self.read_crypto_block3a # wait for more data return len(self.encrypter.block3b), self.read_crypto_block3b def read_crypto_block3b(self, s): if s != self.encrypter.block3b: return None self.Encoder.connecter.external_connection_made += 1 return 14, self.read_crypto_block3c def read_crypto_block3c(self, s): s = self.encrypter.decrypt(s) if s[:8] != ("\x00" * 8): # check VC return None self.cryptmode = toint(s[8:12]) % 4 if self.cryptmode == 0: return None # no encryption selected if self.cryptmode == 1 and self.Encoder.config["crypto_only"]: # only header encryption return None padlen = (ord(s[12]) << 8) + ord(s[13]) if padlen > 512: return None return padlen + 2, self.read_crypto_pad3 def read_crypto_pad3(self, s): s = self.encrypter.decrypt(s)[-2:] ialen = (ord(s[0]) << 8) + ord(s[1]) if ialen > 65535: return None if self.cryptmode == 1: cryptmode = "\x00\x00\x00\x01" # header only encryption else: cryptmode = "\x00\x00\x00\x02" # full stream encryption self.cryptmode = 2 padd = self.encrypter.padding() self.write( self.encrypter.encrypt(("\x00" * 8) + cryptmode + tobinary16(len(padd)) + padd) # VC # encryption mode ) # PadD if ialen: return ialen, self.read_crypto_ia return self.read_crypto_block3done() def read_crypto_ia(self, s): s = self.encrypter.decrypt(s) if DEBUG1: self._log_start() self.log.write("r:" + b2a_hex(s) + "(ia)\n") if self.buffer: self.log.write("r:" + b2a_hex(self.buffer) + "(buffer)\n") return self.read_crypto_block3done(s) def read_crypto_block3done(self, ia=""): if DEBUG1: if not self.log: self._log_start() if self.cryptmode == 2: # full stream encryption self._start_crypto() else: self.encrypter = None if ia: self._write_buffer(ia) return 1 + len(PROTOCOL_NAME), self.read_encrypted_header ### OUTGOING CONNECTION ### def read_crypto_block4a(self, s): if not self._search_for_pattern(s, self.encrypter.VC_pattern()): return -1, self.read_crypto_block4a # wait for more data return 6, self.read_crypto_block4b def read_crypto_block4b(self, s): s = self.encrypter.decrypt(s) self.cryptmode = toint(s[:4]) % 4 if self.cryptmode == 1: # only header encryption if self.Encoder.config["crypto_only"]: return None elif self.cryptmode != 2: return None # unknown encryption padlen = (ord(s[4]) << 8) + ord(s[5]) if padlen > 512: return None if padlen: return padlen, self.read_crypto_pad4 return self.read_crypto_block4done() def read_crypto_pad4(self, s): self.encrypter.decrypt(s) # discard data return self.read_crypto_block4done() def read_crypto_block4done(self): if DEBUG1: self._log_start() if self.cryptmode == 2: # full stream encryption self._start_crypto() else: self.encrypter = None self.write(chr(len(PROTOCOL_NAME)) + PROTOCOL_NAME + str(OPTION_PATTERN) + self.Encoder.download_id) return 1 + len(PROTOCOL_NAME), self.read_encrypted_header ### START PROTOCOL OVER ENCRYPTED CONNECTION ### def read_encrypted_header(self, s): return self._read_header(s) ################################################ def read_options(self, s): if DEBUG2: print Connection.count, "read_options" self.options = Bitfield(64, s) return 20, self.read_download_id def read_download_id(self, s): if DEBUG2: print Connection.count, "read_download_id" if s != self.Encoder.download_id or not self.Encoder.check_ip(ip=self.get_ip()): return None if not self.locally_initiated: if not self.encrypted: self.Encoder.connecter.external_connection_made += 1 self.write( chr(len(PROTOCOL_NAME)) + PROTOCOL_NAME + str(OPTION_PATTERN) + self.Encoder.download_id + self.Encoder.my_id ) return 20, self.read_peer_id def read_peer_id(self, s): if DEBUG2: print Connection.count, "read_peer_id" if not self.encrypted and self.Encoder.config["crypto_only"]: return None # allows older trackers to ping, # but won't proceed w/ connections if not self.id: self.id = s self.readable_id = make_readable(s) else: if s != self.id: return None self.complete = self.Encoder.got_id(self) if not self.complete: return None if self.locally_initiated: self.write(self.Encoder.my_id) incompletecounter.decrement() self._switch_to_read2() c = self.Encoder.connecter.connection_made(self) self.keepalive = c.send_keepalive return 4, self.read_len def read_len(self, s): l = toint(s) if l > self.Encoder.max_len: return None return l, self.read_message def read_message(self, s): if s != "": self.connecter.got_message(self, s) return 4, self.read_len def read_dead(self, s): return None def _auto_close(self): if not self.complete: self.close() def close(self): if not self.closed: self.connection.close() self.sever() def sever(self): if self.log: self.log.write("closed\n") self.log.close() self.closed = True del self.Encoder.connections[self.connection] if self.complete: self.connecter.connection_lost(self) elif self.locally_initiated: incompletecounter.decrement() def send_message_raw(self, message): self.write(message) def _write(self, message): if not self.closed: self.connection.write(message) def data_came_in(self, connection, s): self.read(s) def _write_buffer(self, s): self.buffer = s + self.buffer def _read(self, s): if self.log: self.log.write("r:" + b2a_hex(s) + "\n") self.Encoder.measurefunc(len(s)) self.buffer += s while True: if self.closed: return # self.next_len = # of characters function expects # or 0 = all characters in the buffer # or -1 = wait for next read, then all characters in the buffer # not compatible w/ keepalives, switch out after all negotiation complete if self.next_len <= 0: m = self.buffer self.buffer = "" elif len(self.buffer) >= self.next_len: m = self.buffer[: self.next_len] self.buffer = self.buffer[self.next_len :] else: return try: x = self.next_func(m) except: self.next_len, self.next_func = 1, self.read_dead raise if x is None: self.close() return self.next_len, self.next_func = x if self.next_len < 0: # already checked buffer return # wait for additional data if self.bufferlen is not None: self._read2("") return def _switch_to_read2(self): self._write_buffer = None if self.encrypter: self.encrypter.setrawaccess(self._read2, self._write) else: self.read = self._read2 self.bufferlen = len(self.buffer) self.buffer = [self.buffer] def _read2(self, s): # more efficient, requires buffer['',''] & bufferlen if self.log: self.log.write("r:" + b2a_hex(s) + "\n") self.Encoder.measurefunc(len(s)) while True: if self.closed: return p = self.next_len - self.bufferlen if self.next_len == 0: m = "" elif s: if p > len(s): self.buffer.append(s) self.bufferlen += len(s) return self.bufferlen = len(s) - p self.buffer.append(s[:p]) m = "".join(self.buffer) if p == len(s): self.buffer = [] else: self.buffer = [s[p:]] s = "" elif p <= 0: # assert len(self.buffer) == 1 s = self.buffer[0] self.bufferlen = len(s) - self.next_len m = s[: self.next_len] if p == 0: self.buffer = [] else: self.buffer = [s[self.next_len :]] s = "" else: return try: x = self.next_func(m) except: self.next_len, self.next_func = 1, self.read_dead raise if x is None: self.close() return self.next_len, self.next_func = x def connection_flushed(self, connection): if self.complete: self.connecter.connection_flushed(self) def connection_lost(self, connection): if self.Encoder.connections.has_key(connection): self.sever()