⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 connector.py

📁 bittorrent source by python. please enjoy
💻 PY
📖 第 1 页 / 共 2 页
字号:
# The contents of this file are subject to the BitTorrent Open Source License# Version 1.1 (the License).  You may not copy or use this file, in either# source code or executable form, except in compliance with the License.  You# may obtain a copy of the License at http://www.bittorrent.com/license/.## Software distributed under the License is distributed on an AS IS basis,# WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License# for the specific language governing rights and limitations under the# License.# Originally written by Bram Cohen, heavily modified by Uoti Urpala# Fast extensions added by David Harrisonfrom __future__ import generators# DEBUG# If you think FAST_EXTENSION is causing problems then set the following:#disable_fast_extension = Truedisable_fast_extension = False# END DEBUG# for cryptofrom random import randrangefrom BitTorrent.hash import shafrom Crypto.Cipher import ARC4# urandom comes from obsoletepythonsupportfrom struct import pack, unpackfrom BitTorrent.RawServer_twisted import Handlerfrom BitTorrent.bitfield import Bitfieldfrom BitTorrent.obsoletepythonsupport import *import loggingdef toint(s):    return unpack("!i", s)[0]def tobinary(i):    return pack("!i", i)CHOKE = chr(0)UNCHOKE = chr(1)INTERESTED = chr(2)NOT_INTERESTED = chr(3)# indexHAVE = chr(4)# index, bitfieldBITFIELD = chr(5)# index, begin, lengthREQUEST = chr(6)# index, begin, piecePIECE = chr(7)# index, begin, pieceCANCEL = chr(8)# 2-byte port messagePORT = chr(9)# no argsWANT_METAINFO = chr(10)METAINFO = chr(11)# indexSUSPECT_PIECE = chr(12)# no argsSUGGEST_PIECE =  chr(13)HAVE_ALL =       chr(14)HAVE_NONE =      chr(15)# index, begin, lengthREJECT_REQUEST = chr(16)# indexALLOWED_FAST =   chr(17)message_dict = {chr(0):'CHOKE',                chr(1):'UNCHOKE',                chr(2):'INTERESTED',                chr(3):'NOT_INTERESTED',                chr(4):'HAVE',                chr(5):'BITFIELD',                chr(6):'REQUEST',                chr(7):'PIECE',                chr(8):'CANCEL',                chr(9):'PORT',                chr(13): 'SUGGEST_PIECE',   # FAST_EXTENSION                chr(14): 'HAVE_ALL',        # FAST_EXTENSION                chr(15): 'HAVE_NONE',       # FAST_EXTENSION                chr(16): 'REJECT_REQUEST',  # FAST_EXTENSION                chr(17): 'ALLOWED_FAST'     # FAST_EXTENSION                }# reserved flags:#  reserved[0]#   0x80 Azureus Messaging Protocol#  reserved[5]#   0x10 uTorrent extensions: peer exchange, encrypted connections,#       broadcast listen port.#  reserved[7]DHT = 0x01FAST_EXTENSION = 0x04   # suggest, haveall, havenone, reject request,                        # and allow fast extensions.LAST_BYTE = DHTif not disable_fast_extension:    LAST_BYTE |= FAST_EXTENSIONFLAGS = '\0' * 7 + chr( LAST_BYTE )protocol_name = 'BitTorrent protocol'# for cryptodh_prime = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563PAD_MAX = 200 # less than protocol maximum, and later assumed to be < 256DH_BYTES = 96def bytetonum(x):    return long(x.encode('hex'), 16)def numtobyte(x):    x = hex(x).lstrip('0x').rstrip('Ll')    x = '0'*(192 - len(x)) + x    return x.decode('hex')  noisy = False#noisy = Trueif noisy:    connection_logger = logging.getLogger("BitTorrent.Connector")    log = connection_logger.debug# Dave's comments: Connection is a bad name. class Connection(Handler):    """Implements the syntax of the BitTorrent protocol.        See Upload.py and Download.py for the connection-level        semantics."""    def __init__(self, parent, connection, id, is_local,                 obfuscate_outgoing=False, log_prefix = "", lan=False):        self.parent = parent        self.connection = connection        self.id = id        self.ip = None        self.ip = connection.ip        self.locally_initiated = is_local        self.complete = False        self.lan = lan        self.closed = False        self.got_anything = False        self.next_upload = None        self.upload = None        self.download = None        self._buffer = []        self._buffer_len = 0        self._reader = self._read_messages()        self._next_len = self._reader.next()        self._partial_message = None        self._outqueue = []        self._decrypt = None        self._privkey = None                self.choke_sent = True        self.uses_dht = False        self.uses_fast_extension = False        self.obfuscate_outgoing = obfuscate_outgoing        self.dht_port = None        self.sloppy_pre_connection_counter = 0        self.received_data = False        self.log_prefix = log_prefix        if self.locally_initiated:            # Blech! infohash should be passed as an arg to __init__ when            # initiating.  --Dave            self.logger = logging.getLogger(                self.log_prefix + '.' + repr(self.parent.download_id) +                '.peer_id_not_yet')        else:            self.logger = logging.getLogger(                self.log_prefix + '.infohash_not_yet.peer_id_not_yet' )        if self.locally_initiated:            self.send_handshake()        # Greg's comments: ow ow ow        self.connection.handler = self    def protocol_violation(self, s, c=None):        a = ''        if noisy:            if c is not None:                a = (c.ip, c.port)            log( "FAUX PAS: %s %s" % ( s, a ))        self.logger.info( s )    def send_handshake(self):        flags = FLAGS        if self.obfuscate_outgoing:            privkey = bytetonum(urandom(20))            self._privkey = privkey            pubkey = pow(2, privkey, dh_prime)            out = numtobyte(pubkey) + urandom(randrange(PAD_MAX))            self.connection.write(out)        else:            self.connection.write(chr(len(protocol_name)) + protocol_name +                             flags + self.parent.download_id)            if self.id is not None:                self.connection.write(self.parent.my_id)    def set_parent(self, parent):        self.parent = parent    def close(self):        if not self.closed:            dns = (self.connection.ip, self.connection.port)            self.parent.remove_dns_from_cache(dns)            self.connection.close()    def send_interested(self):        if noisy:            log( "SEND %s" % message_dict[INTERESTED] )        self._send_message(INTERESTED)    def send_not_interested(self):        if noisy:            log( "SEND %s" % message_dict[NOT_INTERESTED] )        self._send_message(NOT_INTERESTED)    def send_choke(self):        if self._partial_message is None:            if noisy:                log( "SEND %s" % message_dict[CHOKE] )            self._send_message(CHOKE)            self.choke_sent = True            self.upload.sent_choke()    def send_unchoke(self):        if self._partial_message is None:            if noisy:                log( "SEND %s" % message_dict[UNCHOKE] )            self._send_message(UNCHOKE)            self.choke_sent = False    def send_port(self, port):        if noisy:            log( "SEND %s" % message_dict[PORT] )        self._send_message(PORT+pack('!H', port))            def send_request(self, index, begin, length):        if noisy:            log( "SEND %s %d %d %d" % (message_dict[REQUEST], index, begin, length) )        self._send_message(pack("!ciii", REQUEST, index, begin, length))    def send_cancel(self, index, begin, length):        self._send_message(pack("!ciii", CANCEL, index, begin, length))    def send_bitfield(self, bitfield):        if noisy:            log( "SEND %s" % message_dict[BITFIELD] )        self._send_message(BITFIELD + bitfield)    def send_have(self, index):        if noisy:            log( "SEND %s" % message_dict[HAVE] )        self._send_message(pack("!ci", HAVE, index))    def send_have_all(self):        assert(self.uses_fast_extension)        if noisy:            log( "SEND %s" % message_dict[HAVE_ALL] )        self._send_message(pack("!c", HAVE_ALL))    def send_have_none(self):        assert(self.uses_fast_extension)        if noisy:            log( "SEND %s" % message_dict[HAVE_NONE] )        self._send_message(pack("!c", HAVE_NONE))    def send_reject_request(self, index, begin, length):        assert(self.uses_fast_extension)        self._send_message(pack("!ciii", REJECT_REQUEST,index,begin,length))    def send_allowed_fast(self, index):        assert(self.uses_fast_extension)        self._send_message(pack("!ci", ALLOWED_FAST, index ))    def send_keepalive(self):        self._send_message('')    def send_partial(self, bytes):        if self.closed:            return 0        if self._partial_message is None and not self.upload.buffer:            return 0        if self._partial_message is None:            total = 0            self._partial_message = []            while self.upload.buffer and total < bytes:                t, piece = self.upload.buffer.pop(0)                index, begin, length = t                msg = pack("!icii%ss" % len(piece), len(piece) + 9, PIECE,                           index, begin, piece)                if noisy: log( "SEND PIECE %d %d" % (index,begin) )                self._partial_message.append(msg)                total += len(msg)            self._partial_message = ''.join(self._partial_message)        if bytes < len(self._partial_message):            self.upload.update_rate(bytes)            self.connection.write(buffer(self._partial_message, 0, bytes))            self._partial_message = buffer(self._partial_message, bytes)            return bytes        queue = [str(self._partial_message)]        self._partial_message = None        if self.choke_sent != self.upload.choked:            if self.upload.choked:                self._outqueue.append(pack("!ic", 1, CHOKE))                self.upload.sent_choke()            else:                self._outqueue.append(pack("!ic", 1, UNCHOKE))            self.choke_sent = self.upload.choked        queue.extend(self._outqueue)        self._outqueue = []        queue = ''.join(queue)        self.upload.update_rate(len(queue))        self.connection.write(queue)        return len(queue)    # yields the number of bytes it wants next, gets those in self._message    def _read_messages(self):        # be compatible with encrypted clients. Thanks Uoti                yield 1 + len(protocol_name)        if self._privkey is not None or \           self._message != chr(len(protocol_name)) + protocol_name:            if self.locally_initiated:                if self._privkey is None:                    return                dhstr = self._message                yield DH_BYTES - len(dhstr)                dhstr += self._message                pub = bytetonum(dhstr)                S = numtobyte(pow(pub, self._privkey, dh_prime))                pub = self._privkey = dhstr = None                SKEY = self.parent.download_id                x = sha('req3' + S).digest()                streamid = sha('req2'+SKEY).digest()                streamid = ''.join([chr(ord(streamid[i]) ^ ord(x[i]))                                    for i in range(20)])                encrypt = ARC4.new(sha('keyA' + S + SKEY).digest()).encrypt                encrypt('x'*1024)                padlen = randrange(PAD_MAX)                x = sha('req1' + S).digest() + streamid + encrypt(                    '\x00'*8 + '\x00'*3+'\x02'+'\x00'+chr(padlen)+                    urandom(padlen)+'\x00\x00')                self.connection.write(x)                self.connection.encrypt = encrypt                decrypt = ARC4.new(sha('keyB' + S + SKEY).digest()).decrypt                decrypt('x'*1024)                VC = decrypt('\x00'*8) # actually encrypt                x = ''                while 1:                    yield 1                    x += self._message                    i = (x + self._rest).find(VC)                    if i >= 0:                        break                    yield len(self._rest)                    x += self._message                    if len(x) >= 520:                        self.protocol_violation('VC not found',                                           self.connection)                        return                yield i + 8 + 4 + 2 - len(x)                x = decrypt((x + self._message)[-6:])                self._decrypt = decrypt                if x[0:4] != '\x00\x00\x00\x02':                    self.protocol_violation('bad crypto method selected, not 2',                                       self.connection)                    return                padlen = (ord(x[4]) << 8) + ord(x[5])                if padlen > 512:                    self.protocol_violation('padlen too long',                                       self.connection)                    return

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -