Пример #1
0
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
Пример #2
0
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
Пример #3
0
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()
Пример #4
0
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()