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

📄 track.py

📁 一个多点下载且源码公开的P2P软件
💻 PY
📖 第 1 页 / 共 4 页
字号:
# Written by Bram Cohen# see LICENSE.txt for license informationfrom BitTornado.parseargs import parseargs, formatDefinitionsfrom BitTornado.RawServer import RawServer, autodetect_ipv6, autodetect_socket_stylefrom BitTornado.HTTPHandler import HTTPHandler, months, weekdaysfrom BitTornado.parsedir import parsedirfrom NatCheck import NatCheck, CHECK_PEER_ID_ENCRYPTEDfrom BitTornado.BTcrypto import CRYPTO_OKfrom T2T import T2TListfrom BitTornado.subnetparse import IP_List, ipv6_to_ipv4, to_ipv4, is_valid_ip, is_ipv4from BitTornado.iprangeparse import IP_List as IP_Range_Listfrom BitTornado.torrentlistparse import parsetorrentlistfrom threading import Event, Threadfrom BitTornado.bencode import bencode, bdecode, Bencachedfrom BitTornado.zurllib import urlopen, quote, unquotefrom Filter import Filterfrom urlparse import urlparsefrom os import rename, getpidfrom os.path import exists, isfilefrom cStringIO import StringIOfrom traceback import print_excfrom time import time, gmtime, strftime, localtimefrom BitTornado.clock import clockfrom random import shuffle, seed, randrangefrom sha import shafrom types import StringType, IntType, LongType, ListType, DictTypefrom binascii import b2a_hex, a2b_hex, a2b_base64from string import lowerimport sys, osimport signalimport reimport BitTornado.__init__from BitTornado.__init__ import version, createPeerIDtry:    Trueexcept:    True = 1    False = 0    bool = lambda x: not not xdefaults = [    ('port', 80, "Port to listen on."),    ('dfile', None, 'file to store recent downloader info in'),    ('bind', '', 'comma-separated list of ips/hostnames to bind to locally'),#    ('ipv6_enabled', autodetect_ipv6(),    ('ipv6_enabled', 0,         'allow the client to connect to peers via IPv6'),    ('ipv6_binds_v4', autodetect_socket_style(),        'set if an IPv6 server socket will also field IPv4 connections'),    ('socket_timeout', 15, 'timeout for closing connections'),    ('save_dfile_interval', 5 * 60, 'seconds between saving dfile'),    ('timeout_downloaders_interval', 45 * 60, 'seconds between expiring downloaders'),    ('reannounce_interval', 30 * 60, 'seconds downloaders should wait between reannouncements'),    ('response_size', 50, 'number of peers to send in an info message'),    ('timeout_check_interval', 5,        'time to wait between checking if any connections have timed out'),    ('nat_check', 3,        "how many times to check if a downloader is behind a NAT (0 = don't check)"),    ('log_nat_checks', 0,        "whether to add entries to the log for nat-check results"),    ('min_time_between_log_flushes', 3.0,        'minimum time it must have been since the last flush to do another one'),    ('min_time_between_cache_refreshes', 600.0,        'minimum time in seconds before a cache is considered stale and is flushed'),    ('allowed_dir', '', 'only allow downloads for .torrents in this dir'),    ('allowed_list', '', 'only allow downloads for hashes in this list (hex format, one per line)'),    ('allowed_controls', 0, 'allow special keys in torrents in the allowed_dir to affect tracker access'),    ('multitracker_enabled', 0, 'whether to enable multitracker operation'),    ('multitracker_allowed', 'autodetect', 'whether to allow incoming tracker announces (can be none, autodetect or all)'),    ('multitracker_reannounce_interval', 2 * 60, 'seconds between outgoing tracker announces'),    ('multitracker_maxpeers', 20, 'number of peers to get in a tracker announce'),    ('aggregate_forward', '', 'format: <url>[,<password>] - if set, forwards all non-multitracker to this url with this optional password'),    ('aggregator', '0', 'whether to act as a data aggregator rather than a tracker.  If enabled, may be 1, or <password>; ' +             'if password is set, then an incoming password is required for access'),    ('hupmonitor', 0, 'whether to reopen the log file upon receipt of HUP signal'),    ('http_timeout', 60,         'number of seconds to wait before assuming that an http connection has timed out'),    ('parse_dir_interval', 60, 'seconds between reloading of allowed_dir or allowed_file ' +             'and allowed_ips and banned_ips lists'),    ('show_infopage', 1, "whether to display an info page when the tracker's root dir is loaded"),    ('infopage_redirect', '', 'a URL to redirect the info page to'),    ('show_names', 1, 'whether to display names from allowed dir'),    ('favicon', '', 'file containing x-icon data to return when browser requests favicon.ico'),    ('allowed_ips', '', 'only allow connections from IPs specified in the given file; '+             'file contains subnet data in the format: aa.bb.cc.dd/len'),    ('banned_ips', '', "don't allow connections from IPs specified in the given file; "+             'file contains IP range data in the format: xxx:xxx:ip1-ip2'),    ('only_local_override_ip', 2, "ignore the ip GET parameter from machines which aren't on local network IPs " +             "(0 = never, 1 = always, 2 = ignore if NAT checking is not enabled)"),    ('logfile', '', 'file to write the tracker logs, use - for stdout (default)'),    ('allow_get', 0, 'use with allowed_dir; adds a /file?hash={hash} url that allows users to download the torrent file'),    ('keep_dead', 0, 'keep dead torrents after they expire (so they still show up on your /scrape and web page)'),    ('scrape_allowed', 'full', 'scrape access allowed (can be none, specific or full)'),    ('dedicated_seed_id', '', 'allows tracker to monitor dedicated seed(s) and flag torrents as seeded'),    ('compact_reqd', 1, "only allow peers that accept a compact response"),  ]def statefiletemplate(x):    if type(x) != DictType:        raise ValueError    for cname, cinfo in x.items():        if cname == 'peers':            for y in cinfo.values():      # The 'peers' key is a dictionary of SHA hashes (torrent ids)                if type(y) != DictType:   # ... for the active torrents, and each is a dictionary                    raise ValueError                for id, info in y.items(): # ... of client ids interested in that torrent                    if (len(id) != 20):                        raise ValueError                    if type(info) != DictType:  # ... each of which is also a dictionary                        raise ValueError # ... which has an IP, a Port, and a Bytes Left count for that client for that torrent                    if type(info.get('ip', '')) != StringType:                        raise ValueError                    port = info.get('port')                    if type(port) not in (IntType,LongType) or port < 0:                        raise ValueError                    left = info.get('left')                    if type(left) not in (IntType,LongType) or left < 0:                        raise ValueError                    if type(info.get('supportcrypto')) not in (IntType,LongType):                        raise ValueError                    if type(info.get('requirecrypto')) not in (IntType,LongType):                        raise ValueError        elif cname == 'completed':            if (type(cinfo) != DictType): # The 'completed' key is a dictionary of SHA hashes (torrent ids)                raise ValueError          # ... for keeping track of the total completions per torrent            for y in cinfo.values():      # ... each torrent has an integer value                if type(y) not in (IntType,LongType):                    raise ValueError      # ... for the number of reported completions for that torrent        elif cname == 'allowed':            if (type(cinfo) != DictType): # a list of info_hashes and included data                raise ValueError            if x.has_key('allowed_dir_files'):                adlist = [z[1] for z in x['allowed_dir_files'].values()]                for y in cinfo.keys():        # and each should have a corresponding key here                    if not y in adlist:                        raise ValueError        elif cname == 'allowed_dir_files':            if (type(cinfo) != DictType): # a list of files, their attributes and info hashes                raise ValueError            dirkeys = {}            for y in cinfo.values():      # each entry should have a corresponding info_hash                if not y[1]:                    continue                if not x['allowed'].has_key(y[1]):                    raise ValueError                if dirkeys.has_key(y[1]): # and each should have a unique info_hash                    raise ValueError                dirkeys[y[1]] = 1            alas = 'your file may exist elsewhere in the universe\nbut alas, not here\n'local_IPs = IP_List()local_IPs.set_intranet_addresses()def isotime(secs = None):    if secs == None:        secs = time()    return strftime('%Y-%m-%d %H:%M UTC', gmtime(secs))http_via_filter = re.compile(' for ([0-9.]+)\Z')def _get_forwarded_ip(headers):    header = headers.get('x-forwarded-for')    if header:        try:            x,y = header.split(',')        except:            return header        if is_valid_ip(x) and not local_IPs.includes(x):            return x        return y    header = headers.get('client-ip')    if header:        return header    header = headers.get('via')    if header:        x = http_via_filter.search(header)        try:            return x.group(1)        except:            pass    header = headers.get('from')    #if header:    #    return header    #return None    return headerdef get_forwarded_ip(headers):    x = _get_forwarded_ip(headers)    if not is_valid_ip(x) or local_IPs.includes(x):        return None    return xdef compact_peer_info(ip, port):    try:        s = ( ''.join([chr(int(i)) for i in ip.split('.')])              + chr((port & 0xFF00) >> 8) + chr(port & 0xFF) )        if len(s) != 6:            raise ValueError    except:        s = ''  # not a valid IP, must be a domain name    return sclass Tracker:    def __init__(self, config, rawserver):        self.config = config        self.response_size = config['response_size']        self.dfile = config['dfile']        self.natcheck = config['nat_check']        favicon = config['favicon']        self.parse_dir_interval = config['parse_dir_interval']        self.favicon = None        if favicon:            try:                h = open(favicon,'r')                self.favicon = h.read()                h.close()            except:                print "**warning** specified favicon file -- %s -- does not exist." % favicon        self.rawserver = rawserver        self.cached = {}    # format: infohash: [[time1, l1, s1], [time2, l2, s2], ...]        self.cached_t = {}  # format: infohash: [time, cache]        self.times = {}        self.state = {}        self.seedcount = {}        self.allowed_IPs = None        self.banned_IPs = None        if config['allowed_ips'] or config['banned_ips']:            self.allowed_ip_mtime = 0            self.banned_ip_mtime = 0            self.read_ip_lists()                        self.only_local_override_ip = config['only_local_override_ip']        if self.only_local_override_ip == 2:            self.only_local_override_ip = not config['nat_check']        if CHECK_PEER_ID_ENCRYPTED and not CRYPTO_OK:            print ('**warning** crypto library not installed,' +                   ' cannot completely verify encrypted peers')        if exists(self.dfile):            try:                h = open(self.dfile, 'rb')                ds = h.read()                h.close()                tempstate = bdecode(ds)                if not tempstate.has_key('peers'):                    tempstate = {'peers': tempstate}                statefiletemplate(tempstate)                self.state = tempstate            except:                print '**warning** statefile '+self.dfile+' corrupt; resetting'        self.downloads = self.state.setdefault('peers', {})        self.completed = self.state.setdefault('completed', {})        self.becache = {}        ''' format: infohash: [[l0, s0], [l1, s1], ...]                l0,s0 = compact, not requirecrypto=1                l1,s1 = compact, only supportcrypto=1                l2,s2 = [compact, crypto_flag], all peers            if --compact_reqd 0:                l3,s3 = [ip,port,id]                l4,l4 = [ip,port] nopeerid        '''        if config['compact_reqd']:            x = 3        else:            x = 5        self.cache_default = [({},{}) for i in xrange(x)]        for infohash, ds in self.downloads.items():            self.seedcount[infohash] = 0            for x,y in ds.items():                ip = y['ip']                if ( (self.allowed_IPs and not self.allowed_IPs.includes(ip))                     or (self.banned_IPs and self.banned_IPs.includes(ip)) ):                    del ds[x]                    continue                if not y['left']:                    self.seedcount[infohash] += 1                if y.get('nat',-1):                    continue

⌨️ 快捷键说明

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