diff --git a/lib/common-node.js b/lib/common-node.js index 12c45b2..b017bc0 100644 --- a/lib/common-node.js +++ b/lib/common-node.js @@ -7,6 +7,7 @@ var querystring = require('querystring') exports.IPV4_RE = /^[\d\.]+$/ exports.IPV6_RE = /^[\da-fA-F:]+$/ +exports.REMOVE_IPV4_MAPPED_IPV6_RE = /^::ffff:/ exports.CONNECTION_ID = Buffer.concat([ toUInt32(0x417), toUInt32(0x27101980) ]) exports.ACTIONS = { CONNECT: 0, ANNOUNCE: 1, SCRAPE: 2, ERROR: 3 } @@ -23,6 +24,11 @@ exports.EVENT_NAMES = { started: 'start', stopped: 'stop' } +exports.PEER_TYPES = { + http: 'http', + udp: 'udp', + websocket: 'ws' +} function toUInt32 (n) { var buf = new Buffer(4) diff --git a/lib/server/parse-http.js b/lib/server/parse-http.js index cb3e481..4681e43 100644 --- a/lib/server/parse-http.js +++ b/lib/server/parse-http.js @@ -2,8 +2,6 @@ module.exports = parseHttpRequest var common = require('../common') -var REMOVE_IPV4_MAPPED_IPV6_RE = /^::ffff:/ - function parseHttpRequest (req, opts) { if (!opts) opts = {} var s = req.url.split('?') @@ -11,6 +9,7 @@ function parseHttpRequest (req, opts) { if (opts.action === 'announce' || s[0] === '/announce') { params.action = common.ACTIONS.ANNOUNCE + params.type = common.PEER_TYPES.http if (typeof params.info_hash !== 'string' || params.info_hash.length !== 20) { throw new Error('invalid info_hash') @@ -34,8 +33,10 @@ function parseHttpRequest (req, opts) { params.ip = opts.trustProxy ? req.headers['x-forwarded-for'] || req.connection.remoteAddress - : req.connection.remoteAddress.replace(REMOVE_IPV4_MAPPED_IPV6_RE, '') // force ipv4 + : req.connection.remoteAddress.replace(common.REMOVE_IPV4_MAPPED_IPV6_RE, '') // force ipv4 params.addr = (common.IPV6_RE.test(params.ip) ? '[' + params.ip + ']' : params.ip) + ':' + params.port + + params.headers = req.headers } else if (opts.action === 'scrape' || s[0] === '/scrape') { params.action = common.ACTIONS.SCRAPE diff --git a/lib/server/parse-udp.js b/lib/server/parse-udp.js index 0eda11d..6524ce2 100644 --- a/lib/server/parse-udp.js +++ b/lib/server/parse-udp.js @@ -10,7 +10,8 @@ function parseUdpRequest (msg, rinfo) { var params = { connectionId: msg.slice(0, 8), // 64-bit action: msg.readUInt32BE(8), - transactionId: msg.readUInt32BE(12) + transactionId: msg.readUInt32BE(12), + type: common.PEER_TYPES.udp } if (!bufferEqual(params.connectionId, common.CONNECTION_ID)) { diff --git a/lib/server/parse-websocket.js b/lib/server/parse-websocket.js index 944843a..a28f5aa 100644 --- a/lib/server/parse-websocket.js +++ b/lib/server/parse-websocket.js @@ -2,10 +2,12 @@ module.exports = parseWebSocketRequest var common = require('../common') -function parseWebSocketRequest (socket, params) { +function parseWebSocketRequest (socket, opts, params) { + if (!opts) opts = {} params = JSON.parse(params) // may throw params.action = common.ACTIONS.ANNOUNCE + params.type = common.PEER_TYPES.websocket params.socket = socket if (typeof params.info_hash !== 'string' || params.info_hash.length !== 20) { @@ -32,5 +34,15 @@ function parseWebSocketRequest (socket, params) { ) params.compact = -1 // return full peer objects (used for websocket responses) + params.ip = opts.trustProxy + ? socket.upgradeReq.headers['x-forwarded-for'] || socket.upgradeReq.connection.remoteAddress + : socket.upgradeReq.connection.remoteAddress.replace(common.REMOVE_IPV4_MAPPED_IPV6_RE, '') // force ipv4 + params.port = socket.upgradeReq.connection.remotePort + if (params.port) { + params.addr = (common.IPV6_RE.test(params.ip) ? '[' + params.ip + ']' : params.ip) + ':' + params.port + } + + params.headers = socket.upgradeReq.headers + return params } diff --git a/lib/server/swarm.js b/lib/server/swarm.js index 212fa1e..e6d51db 100644 --- a/lib/server/swarm.js +++ b/lib/server/swarm.js @@ -2,6 +2,7 @@ module.exports = Swarm var debug = require('debug')('bittorrent-tracker') var randomIterate = require('random-iterate') +var common = require('../common') // Regard this as the default implementation of an interface that you // need to support when overriding Server.createSwarm() and Server.getSwarm() @@ -13,7 +14,8 @@ function Swarm (infoHash, server) { Swarm.prototype.announce = function (params, cb) { var self = this - var peer = self.peers[params.addr || params.peer_id] + var id = params.type === common.PEER_TYPES.websocket ? params.peer_id : params.addr + var peer = self.peers[id] if (params.event === 'started') { self._onAnnounceStarted(params, peer) @@ -49,7 +51,8 @@ Swarm.prototype._onAnnounceStarted = function (params, peer) { if (params.left === 0) this.complete += 1 else this.incomplete += 1 - peer = this.peers[params.addr || params.peer_id] = { + var id = params.type === common.PEER_TYPES.websocket ? params.peer_id : params.addr + peer = this.peers[id] = { complete: params.left === 0, peerId: params.peer_id, // as hex ip: params.ip, // only http, udp @@ -66,7 +69,8 @@ Swarm.prototype._onAnnounceStopped = function (params, peer) { if (peer.complete) this.complete -= 1 else this.incomplete -= 1 - delete this.peers[params.addr || params.peer_id] + var id = params.type === common.PEER_TYPES.websocket ? params.peer_id : params.addr + delete this.peers[id] } Swarm.prototype._onAnnounceCompleted = function (params, peer) { diff --git a/server.js b/server.js index a0e107c..e60a581 100644 --- a/server.js +++ b/server.js @@ -294,21 +294,23 @@ Server.prototype.onUdpRequest = function (msg, rinfo) { }) } -Server.prototype.onWebSocketConnection = function (socket) { +Server.prototype.onWebSocketConnection = function (socket, opts) { var self = this + if (!opts) opts = {} + opts.trustProxy = opts.trustProxy || self._trustProxy socket.peerId = null // as hex socket.infoHashes = [] socket.onSend = self._onWebSocketSend.bind(self, socket) - socket.on('message', self._onWebSocketRequest.bind(self, socket)) + socket.on('message', self._onWebSocketRequest.bind(self, socket, opts)) socket.on('error', self._onWebSocketError.bind(self, socket)) socket.on('close', self._onWebSocketClose.bind(self, socket)) } -Server.prototype._onWebSocketRequest = function (socket, params) { +Server.prototype._onWebSocketRequest = function (socket, opts, params) { var self = this try { - params = parseWebSocketRequest(socket, params) + params = parseWebSocketRequest(socket, opts, params) } catch (err) { socket.send(JSON.stringify({ 'failure reason': err.message