UNPKG

@huggycn/bittorrent-peerid

Version:

Map a BitTorrent peer ID to a human-readable client name and version

429 lines (388 loc) 15.1 kB
/*! bittorrent-peerid. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */ const utils = require('./lib/utils') /** * Parses and returns the client type and version of a bittorrent peer id. * Throws an exception if the peer id is invalid. * * @param {Buffer|string} peerId (as Buffer or hex/utf8 string) */ module.exports = peerId => { let buffer if (Buffer.isBuffer(peerId)) { buffer = peerId } else if (typeof peerId === 'string') { buffer = Buffer.from(peerId, 'utf8') } else { throw new Error(`Invalid peerId must be Buffer or hex string: ${peerId}`) } peerId = buffer.toString('utf8') let client = null // If the client reuses parts of the peer ID of other peers, then try to determine this // first (before we misidentify the client). if (utils.isPossibleSpoofClient(peerId)) { if ((client = utils.decodeBitSpiritClient(peerId, buffer))) return client if ((client = utils.decodeBitCometClient(peerId, buffer))) return client return { client: 'BitSpirit?' } } // See if the client uses Az style identification if (utils.isAzStyle(peerId)) { if ((client = getAzStyleClientName(peerId))) { const version = getAzStyleClientVersion(client, peerId) // Hack for fake ZipTorrent clients - there seems to be some clients // which use the same identifier, but they aren't valid ZipTorrent clients if (client.startsWith('ZipTorrent') && peerId.startsWith('bLAde', 8)) { return { client: 'Unknown [Fake: ZipTorrent]', version } } // BitTorrent 6.0 Beta currently misidentifies itself if (client === '\u00B5Torrent' && version === '6.0 Beta') { return { client: 'Mainline', version: '6.0 Beta' } } // If it's the rakshasa libtorrent, then it's probably rTorrent if (client.startsWith('libTorrent (Rakshasa)')) { return { client: `${client} / rTorrent*`, version } } return { origin: peerId.substring(1, 3), client, version } } } // See if the client uses Shadow style identification if (utils.isShadowStyle(peerId)) { if ((client = getShadowStyleClientName(peerId))) { // TODO: handle shadow style client version numbers return { origin: peerId.substring(0, 1), client } } } // See if the client uses Mainline style identification if (utils.isMainlineStyle(peerId)) { if ((client = getMainlineStyleClientName(peerId))) { // TODO: handle mainline style client version numbers return { origin: peerId.substring(0, 1), client } } } // Check for BitSpirit / BitComet disregarding spoof mode if ((client = utils.decodeBitSpiritClient(peerId, buffer))) return client if ((client = utils.decodeBitCometClient(peerId, buffer))) return client // See if the client identifies itself using a particular substring const data = getSimpleClient(peerId) if (data) { client = data.client // TODO: handle simple client version numbers return { origin: client, client, version: data.version } } // See if client is known to be awkward / nonstandard if ((client = utils.identifyAwkwardClient(peerId, buffer))) { return client } // TODO: handle unknown az-formatted and shadow-formatted clients return { client: 'unknown' } } // Az style two byte code identifiers to real client name const azStyleClients = {} const azStyleClientVersions = {} // Shadow's style one byte code identifiers to real client name const shadowStyleClients = {} const shadowStyleClientVersions = {} // Mainline's new style uses one byte code identifiers too const mainlineStyleClients = {} // Clients with completely custom naming schemes const customStyleClients = [] const VER_AZ_THREE_DIGITS = v => // "1.2.3" `${v[0]}.${v[1]}.${v[2]}` const VER_AZ_DELUGE = v => { const alphabet = 'ABCDE' if (isNaN(v[2])) { return `${v[0]}.${v[1]}.1${alphabet.indexOf(v[2])}` } return `${v[0]}.${v[1]}.${v[2]}` } const VER_AZ_THREE_DIGITS_PLUS_MNEMONIC = v => { // "1.2.3 [4]" let mnemonic = v[3] if (mnemonic === 'B') { mnemonic = 'Beta' } else if (mnemonic === 'A') { mnemonic = 'Alpha' } else { mnemonic = '' } return `${v[0]}.${v[1]}.${v[2]} ${mnemonic}` } const VER_AZ_FOUR_DIGITS = v => // "1.2.3.4" `${v[0]}.${v[1]}.${v[2]}.${v[3]}` const VER_AZ_TWO_MAJ_TWO_MIN = v => // "12.34" `${v[0] + v[1]}.${v[2]}${v[3]}` const VER_AZ_SKIP_FIRST_ONE_MAJ_TWO_MIN = v => // "2.34" `${v[1]}.${v[2]}${v[3]}` const VER_AZ_KTORRENT_STYLE = '1.2.3=[RD].4' const VER_AZ_TRANSMISSION_STYLE = v => { // "transmission" if (v[0] === '0' && v[1] === '0' && v[2] === '0') { return `0.${v[3]}` } else if (v[0] === '0' && v[1] === '0') { return `0.${v[2]}${v[3]}` } return `${v[0]}.${v[1]}${v[2]}${v[3] === 'Z' || v[3] === 'X' ? '+' : ''}` } const VER_AZ_WEBTORRENT_STYLE = v => { // "webtorrent" let version = '' if (v[0] === '0') { version += `${v[1]}.` } else { version += `${v[0]}${v[1]}.` } if (v[2] === '0') { version += v[3] } else { version += `${v[2]}${v[3]}` } return version } const VER_AZ_THREE_ALPHANUMERIC_DIGITS = '2.33.4' const VER_NONE = 'NO_VERSION' function addAzStyle(id, client, version = VER_AZ_FOUR_DIGITS) { azStyleClients[id] = client azStyleClientVersions[client] = version } function addShadowStyle(id, client, version = VER_AZ_THREE_DIGITS) { shadowStyleClients[id] = client shadowStyleClientVersions[client] = version } function addMainlineStyle(id, client) { mainlineStyleClients[id] = client } function addSimpleClient(client, version, id, position) { if (typeof id === 'number' || typeof id === 'undefined') { position = id id = version version = undefined } customStyleClients.push({ id, client, version, position: position || 0 }) } function getAzStyleClientName(peerId) { return azStyleClients[peerId.substring(1, 3)] } function getShadowStyleClientName(peerId) { return shadowStyleClients[peerId.substring(0, 1)] } function getMainlineStyleClientName(peerId) { return mainlineStyleClients[peerId.substring(0, 1)] } function getSimpleClient(peerId) { for (let i = 0; i < customStyleClients.length; ++i) { const client = customStyleClients[i] if (peerId.startsWith(client.id, client.position)) { return client } } return null } function getAzStyleClientVersion(client, peerId) { const version = azStyleClientVersions[client] if (!version) return null return utils.getAzStyleVersionNumber(peerId.substring(3, 7), version) } (() => { // add known clients alphabetically addAzStyle('A~', 'Ares', VER_AZ_THREE_DIGITS) addAzStyle('AG', 'Ares', VER_AZ_THREE_DIGITS) addAzStyle('AN', 'Ares', VER_AZ_FOUR_DIGITS) addAzStyle('AR', 'Ares')// Ares is more likely than ArcticTorrent addAzStyle('AV', 'Avicora') addAzStyle('AX', 'BitPump', VER_AZ_TWO_MAJ_TWO_MIN) addAzStyle('AT', 'Artemis') addAzStyle('AZ', 'Vuze', VER_AZ_FOUR_DIGITS) addAzStyle('BB', 'BitBuddy', '1.234') addAzStyle('BC', 'BitComet', VER_AZ_SKIP_FIRST_ONE_MAJ_TWO_MIN) addAzStyle('BE', 'BitTorrent SDK') addAzStyle('BF', 'BitFlu', VER_NONE) addAzStyle('BG', 'BTG', VER_AZ_FOUR_DIGITS) addAzStyle('bk', 'BitKitten (libtorrent)') addAzStyle('BR', 'BitRocket', '1.2(34)') addAzStyle('BS', 'BTSlave') addAzStyle('BT', 'BitTorrent', VER_AZ_THREE_DIGITS_PLUS_MNEMONIC) addAzStyle('BW', 'BitWombat') addAzStyle('BX', 'BittorrentX') addAzStyle('CB', 'Shareaza Plus') addAzStyle('CD', 'Enhanced CTorrent', VER_AZ_TWO_MAJ_TWO_MIN) addAzStyle('CT', 'CTorrent', '1.2.34') addAzStyle('DP', 'Propogate Data Client') addAzStyle('DE', 'Deluge', VER_AZ_DELUGE) addAzStyle('EB', 'EBit') addAzStyle('ES', 'Electric Sheep', VER_AZ_THREE_DIGITS) addAzStyle('FC', 'FileCroc') addAzStyle('FG', 'FlashGet', VER_AZ_SKIP_FIRST_ONE_MAJ_TWO_MIN) addAzStyle('FX', 'Freebox BitTorrent') addAzStyle('FT', 'FoxTorrent/RedSwoosh') addAzStyle('GR', 'GetRight', '1.2') addAzStyle('GS', 'GSTorrent')// TODO: Format is v"abcd" addAzStyle('HL', 'Halite', VER_AZ_THREE_DIGITS) addAzStyle('HN', 'Hydranode') addAzStyle('KG', 'KGet') addAzStyle('KT', 'KTorrent', VER_AZ_KTORRENT_STYLE) addAzStyle('LC', 'LeechCraft') addAzStyle('LH', 'LH-ABC') addAzStyle('LK', 'linkage', VER_AZ_THREE_DIGITS) addAzStyle('LP', 'Lphant', VER_AZ_TWO_MAJ_TWO_MIN) addAzStyle('LT', 'libtorrent (Rasterbar)', VER_AZ_THREE_ALPHANUMERIC_DIGITS) addAzStyle('lt', 'libTorrent (Rakshasa)', VER_AZ_THREE_ALPHANUMERIC_DIGITS) addAzStyle('LW', 'LimeWire', VER_NONE)// The "0001" bytes found after the LW commonly refers to the version of the BT protocol implemented. Documented here: http://www.limewire.org/wiki/index.php?title=BitTorrentRevision addAzStyle('MO', 'MonoTorrent') addAzStyle('MP', 'MooPolice', VER_AZ_THREE_DIGITS) addAzStyle('MR', 'Miro') addAzStyle('MT', 'MoonlightTorrent') addAzStyle('NE', 'BT Next Evolution', VER_AZ_THREE_DIGITS) addAzStyle('NX', 'Net Transport') addAzStyle('OS', 'OneSwarm', VER_AZ_FOUR_DIGITS) addAzStyle('OT', 'OmegaTorrent') addAzStyle('PC', 'CacheLogic', '12.3-4') addAzStyle('PT', 'Popcorn Time') addAzStyle('PD', 'Pando') addAzStyle('PE', 'PeerProject') addAzStyle('pX', 'pHoeniX') addAzStyle('qB', 'qBittorrent', VER_AZ_DELUGE) addAzStyle('QD', 'qqdownload') addAzStyle('RT', 'Retriever') addAzStyle('RZ', 'RezTorrent') addAzStyle('S~', 'Shareaza alpha/beta') addAzStyle('SB', 'SwiftBit') addAzStyle('SD', '\u8FC5\u96F7\u5728\u7EBF (Xunlei)')// Apparently, the English name of the client is "Thunderbolt". addAzStyle('SG', 'GS Torrent', VER_AZ_FOUR_DIGITS) addAzStyle('SN', 'ShareNET') addAzStyle('SP', 'BitSpirit', VER_AZ_THREE_DIGITS)// >= 3.6 addAzStyle('SS', 'SwarmScope') addAzStyle('ST', 'SymTorrent', '2.34') addAzStyle('st', 'SharkTorrent') addAzStyle('SZ', 'Shareaza') addAzStyle('TG', 'Torrent GO') addAzStyle('TN', 'Torrent.NET') addAzStyle('TR', 'Transmission', VER_AZ_TRANSMISSION_STYLE) addAzStyle('TS', 'TorrentStorm') addAzStyle('TT', 'TuoTu', VER_AZ_THREE_DIGITS) addAzStyle('UL', 'uLeecher!') addAzStyle('UE', '\u00B5Torrent Embedded', VER_AZ_THREE_DIGITS_PLUS_MNEMONIC) addAzStyle('UT', '\u00B5Torrent', VER_AZ_THREE_DIGITS_PLUS_MNEMONIC) addAzStyle('UM', '\u00B5Torrent Mac', VER_AZ_THREE_DIGITS_PLUS_MNEMONIC) addAzStyle('UW', '\u00B5Torrent Web', VER_AZ_THREE_DIGITS_PLUS_MNEMONIC) addAzStyle('WD', 'WebTorrent Desktop', VER_AZ_WEBTORRENT_STYLE)// Go Webtorrent!! :) addAzStyle('WT', 'Bitlet') addAzStyle('WW', 'WebTorrent', VER_AZ_WEBTORRENT_STYLE)// Go Webtorrent!! :) addAzStyle('WY', 'FireTorrent')// formerly Wyzo. addAzStyle('VG', '\u54c7\u560E (Vagaa)', VER_AZ_FOUR_DIGITS) addAzStyle('XL', '\u8FC5\u96F7\u5728\u7EBF (Xunlei)')// Apparently, the English name of the client is "Thunderbolt". addAzStyle('XT', 'XanTorrent') addAzStyle('XF', 'Xfplay', '\u5f71\u97f3\u5148\u950b')//xfplay.com addAzStyle('XX', 'XTorrent', '1.2.34') addAzStyle('XC', 'XTorrent', '1.2.34') addAzStyle('ZT', 'ZipTorrent') addAzStyle('7T', 'aTorrent') addAzStyle('ZO', 'Zona', VER_AZ_FOUR_DIGITS) addAzStyle('#@', 'Invalid PeerID') addShadowStyle('A', 'ABC') addShadowStyle('O', 'Osprey Permaseed') addShadowStyle('Q', 'BTQueue') addShadowStyle('R', 'Tribler') addShadowStyle('S', 'Shad0w') addShadowStyle('T', 'BitTornado') addShadowStyle('U', 'UPnP NAT') addMainlineStyle('M', 'Mainline') addMainlineStyle('Q', 'Queen Bee') // Simple clients with no version number. addSimpleClient('\u00B5Torrent', '1.7.0 RC', '-UT170-')// http://forum.utorrent.com/viewtopic.php?pid=260927#p260927 addSimpleClient('Azureus', '1', 'Azureus') addSimpleClient('Azureus', '2.0.3.2', 'Azureus', 5) addSimpleClient('Aria', '2', '-aria2-') addSimpleClient('BitTorrent Plus!', 'II', 'PRC.P---') addSimpleClient('BitTorrent Plus!', 'P87.P---') addSimpleClient('BitTorrent Plus!', 'S587Plus') addSimpleClient('BitTyrant (Azureus Mod)', 'AZ2500BT') addSimpleClient('Blizzard Downloader', 'BLZ') addSimpleClient('BTGetit', 'BG', 10) addSimpleClient('BTugaXP', 'btuga') addSimpleClient('BTugaXP', 'BTuga', 5) addSimpleClient('BTugaXP', 'oernu') addSimpleClient('Deadman Walking', 'BTDWV-') addSimpleClient('Deadman', 'Deadman Walking-') addSimpleClient('External Webseed', 'Ext') addSimpleClient('G3 Torrent', '-G3') addSimpleClient('GreedBT', '2.7.1', '271-') addSimpleClient('Hurricane Electric', 'arclight') addSimpleClient('HTTP Seed', '-WS') addSimpleClient('JVtorrent', '10-------') addSimpleClient('Limewire', 'LIME') addSimpleClient('Martini Man', 'martini') addSimpleClient('Pando', 'Pando') addSimpleClient('PeerApp', 'PEERAPP') addSimpleClient('SimpleBT', 'btfans', 4) addSimpleClient('Swarmy', 'a00---0') addSimpleClient('Swarmy', 'a02---0') addSimpleClient('Teeweety', 'T00---0') addSimpleClient('TorrentTopia', '346-') addSimpleClient('XanTorrent', 'DansClient') addSimpleClient('MediaGet', '-MG1') addSimpleClient('MediaGet', '2.1', '-MG21') /** * This is interesting - it uses Mainline style, except uses two characters instead of one. * And then - the particular numbering style it uses would actually break the way we decode * version numbers (our code is too hardcoded to "-x-y-z--" style version numbers). * * This should really be declared as a Mainline style peer ID, but I would have to * make my code more generic. Not a bad thing - just something I'm not doing right * now. */ addSimpleClient('Amazon AWS S3', 'S3-') // Simple clients with custom version schemes // TODO: support custom version schemes addSimpleClient('BitTorrent DNA', 'DNA') addSimpleClient('Opera', 'OP')// Pre build 10000 versions addSimpleClient('Opera', 'O')// Post build 10000 versions addSimpleClient('Burst!', 'Mbrst') addSimpleClient('TurboBT', 'turbobt') addSimpleClient('BT Protocol Daemon', 'btpd') addSimpleClient('Plus!', 'Plus') addSimpleClient('XBT', 'XBT') addSimpleClient('BitsOnWheels', '-BOW') addSimpleClient('eXeem', 'eX') addSimpleClient('MLdonkey', '-ML') addSimpleClient('Bitlet', 'BitLet') addSimpleClient('AllPeers', 'AP') addSimpleClient('BTuga Revolution', 'BTM') addSimpleClient('Rufus', 'RS', 2) addSimpleClient('BitMagnet', 'BM', 2)// BitMagnet - predecessor to Rufus addSimpleClient('QVOD', 'QVOD') // Top-BT is based on BitTornado, but doesn't quite stick to Shadow's naming conventions, // so we'll use substring matching instead. addSimpleClient('Top-BT', 'TB') addSimpleClient('Tixati', 'TIX') // seems to have a sub-version encoded in following 3 bytes, not worked out how: "folx/1.0.456.591" : 2D 464C 3130 FF862D 486263574A43585F66314D5A addSimpleClient('folx', '-FL') addSimpleClient('\u00B5Torrent Mac', '-UM') addSimpleClient('\u00B5Torrent', '-UT') // UT 3.4+ })()