# 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. # # Written by Matt Chisholm # Client list updated by Ed Savage-Jones - May 28th 2005 import re v64p = '[\da-zA-Z.-]{3}' matches = ( ('-AZ(?P\d+)-+.+$' , "Azureus" ), ('M(?P\d-\d-\d)--.+$' , "BitTorrent" ), ('T(?P%s)0?-+.+$'%v64p , "BitTornado" ), ('-UT(?P[\dA-F]+)-+.+$' , u"\xb5Torrent" ), ('-TS(?P\d+)-+.+$' , "TorrentStorm" ), ('exbc(?P.+)LORD.+$' , "BitLord" ), ('exbc(?P[^-][^-]+)(?!---).+$', "BitComet" ), ('-BC0(?P\d+)-.+$' , "BitComet" ), ('FUTB(?P.+).+$' , "BitComet Mod1" ), ('xUTB(?P.+).+$' , "BitComet Mod2" ), ('A(?P%s)-+.+$'%v64p , "ABC" ), ('S(?P%s)-+.+$'%v64p , "Shadow's" ), (chr(0)*12 + 'aa.+$' , "Experimental 3.2.1b2" ), (chr(0)*12 + '.+$' , "BitTorrent (obsolete)"), ('-G3.+$' , "G3Torrent" ), ('-[Ll][Tt](?P\d+)-+.+$' , "libtorrent" ), ('Mbrst(?P\d-\d-\d).+$' , "burst!" ), ('eX.+$' , "eXeem" ), ('\x00\x02BS.+(?PUDP0|HTTPBT)$', "BitSpirit v2" ), ('\x00[\x02|\x00]BS.+$' , "BitSpirit v2" ), ('.*(?PUDP0|HTTPBT)$' , "BitSpirit" ), ('-BOWP?(?P[\dA-F]+)-.+$', "Bits on Wheels" ), ('(?P.+)RSAnonymous.+$' , "Rufus Anonymous" ), ('(?P.+)RS.+$' , "Rufus" ), ('-ML(?P(\d\.)+\d)(?:\.(?PCVS))?-+.+$',"MLDonkey"), ('346------.+$' , "TorrentTopia 1.70" ), ('OP(?P\d{4}).+$' , "Opera" ), ('-KT(?P\d+)(?PR\d+)-+.+$', "KTorrent" ), # Unknown but seen in peer lists: ('-S(?P10059)-+.+$' , "S (unknown)" ), ('-TR(?P\d+)-+.+$' , "TR (unknown)" ), ('S\x05\x07\x06\x00{7}.+' , "S 576 (unknown)" ), # Clients I've never actually seen in a peer list: ('exbc..---.+$' , "BitVampire 1.3.1" ), ('-BB(?P\d+)-+.+$' , "BitBuddy" ), ('-CT(?P\d+)-+.+$' , "CTorrent" ), ('-MT(?P\d+)-+.+$' , "MoonlightTorrent" ), ('-BX(?P\d+)-+.+$' , "BitTorrent X" ), ('-TN(?P\d+)-+.+$' , "TorrentDotNET" ), ('-SS(?P\d+)-+.+$' , "SwarmScope" ), ('-XT(?P\d+)-+.+$' , "XanTorrent" ), ('U(?P\d+)-+.+$' , "UPnP NAT Bit Torrent" ), ('-AR(?P\d+)-+.+$' , "Arctic" ), ('(?P.+)BM.+$' , "BitMagnet" ), ('BG(?P\d+).+$' , "BTGetit" ), ('-eX(?P[\dA-Fa-f]+)-.+$',"eXeem beta" ), ('Plus12(?P[\dR]+)-.+$' , "Plus! II" ), ('XBT(?P\d+)[d-]-.+$' , "XBT" ), ('-ZT(?P\d+)-+.+$' , "ZipTorrent" ), ('-BitE\?(?P\d+)-.+$' , "BitEruct" ), ('O(?P%s)-+.+$'%v64p , "Osprey Permaseed" ), # Guesses based on Rufus source code, never seen in the wild: ('-BS(?P\d+)-+.+$' , "BTSlave" ), ('-SB(?P\d+)-+.+$' , "SwiftBit" ), ('-SN(?P\d+)-+.+$' , "ShareNET" ), ('-bk(?P\d+)-+.+$' , "BitKitten" ), ('-SZ(?P\d+)-+.+$' , "Shareaza" ), ('-MP(?P\d+)-+.+$' , "MooPolice" ), ('Deadman Walking-.+$' , "Deadman" ), ('270------.+$' , "GreedBT 2.7.0" ), ('XTORR302.+$' , "TorrenTres 0.0.2" ), ('turbobt(?P\d\.\d).+$' , "TurboBT" ), ('DansClient.+$' , "XanTorrent" ), ('-PO(?P\d+)-+.+$' , "PO (unknown)" ), ('-UR(?P\d+)-+.+$' , "UR (unknown)" ), # Patterns that should be executed last ('.*Azureus.*' , "Azureus 2.0.3.2" ), ) matches = [(re.compile(pattern, re.DOTALL), name) for pattern, name in matches] unknown_clients = {} def identify_client(peerid, log=None): client = 'unknown' version = '' for pat, name in matches: m = pat.match(peerid) if m: client = name d = m.groupdict() if d.has_key('version'): version = d['version'] version = version.replace('-','.') if version.find('.') >= 0: version = ''.join(version.split('.')) version = list(version) for i,c in enumerate(version): if '0' <= c <= '9': version[i] = c elif 'A' <= c <= 'Z': version[i] = str(ord(c) - 55) elif 'a' <= c <= 'z': version[i] = str(ord(c) - 61) elif c == '.': version[i] = '62' elif c == '-': version[i] = '63' else: break version = '.'.join(version) elif d.has_key('bcver'): bcver = d['bcver'] version += str(ord(bcver[0])) + '.' if len(bcver) > 1: version += str(ord(bcver[1])/10) version += str(ord(bcver[1])%10) elif d.has_key('rsver'): rsver = d['rsver'] version += str(ord(rsver[0])) + '.' if len(rsver) > 1: version += str(ord(rsver[1])/10) + '.' version += str(ord(rsver[1])%10) if d.has_key('strver'): if d['strver'] is not None: version += d['strver'] if d.has_key('rc'): rc = 'RC ' + d['rc'][1:] if version: version += ' ' version += rc break if client == 'unknown': # identify Shareaza 2.0 - 2.1 if len(peerid) == 20 and chr(0) not in peerid[:15]: for i in range(16,20): if ord(peerid[i]) != (ord(peerid[i - 16]) ^ ord(peerid[31 - i])): break else: client = "Shareaza" if log is not None and 'unknown' in client: if not unknown_clients.has_key(peerid): unknown_clients[peerid] = True log.write('%s\n'%peerid) log.write('------------------------------\n') return client, version