diff --git a/lib/common.js b/lib/common.js index aadc705..1e2b792 100644 --- a/lib/common.js +++ b/lib/common.js @@ -4,6 +4,9 @@ var querystring = require('querystring') +exports.IPV4_RE = /^[\d\.]+$/ +exports.IPV6_RE = /^[\da-fA-F:]+$/ + exports.NUM_ANNOUNCE_PEERS = 50 exports.MAX_ANNOUNCE_PEERS = 82 diff --git a/lib/parse_http.js b/lib/parse_http.js index 9a4a95b..fe1c17b 100644 --- a/lib/parse_http.js +++ b/lib/parse_http.js @@ -2,7 +2,7 @@ module.exports = parseHttpRequest var common = require('./common') -var REMOVE_IPV6_RE = /^::ffff:/ +var REMOVE_IPV4_MAPPED_IPV6_RE = /^::ffff:/ function parseHttpRequest (req, options) { var s = req.url.split('?') @@ -30,8 +30,8 @@ function parseHttpRequest (req, options) { params.ip = options.trustProxy ? req.headers['x-forwarded-for'] || req.connection.remoteAddress - : req.connection.remoteAddress.replace(REMOVE_IPV6_RE, '') // force ipv4 - params.addr = params.ip + ':' + params.port // TODO: ipv6 brackets? + : req.connection.remoteAddress.replace(REMOVE_IPV4_MAPPED_IPV6_RE, '') // force ipv4 + params.addr = (common.IPV6_RE.test(params.ip) ? '[' + params.ip + ']' : params.ip) + ':' + params.port } else if (s[0] === '/scrape') { // unofficial scrape message params.action = common.ACTIONS.SCRAPE diff --git a/server.js b/server.js index a194f86..ee3cccd 100644 --- a/server.js +++ b/server.js @@ -94,7 +94,10 @@ Server.prototype.listen = function (port, onlistening) { function onPort (err, port) { if (err) return self.emit('error', err) self.port = port - self._httpServer && self._httpServer.listen(port.http || port) + // ATTENTION: + // binding to :: only receives IPv4 connections if the bindv6only + // sysctl is set 0, which is the default on many operating systems. + self._httpServer && self._httpServer.listen(port.http || port, '::') self._udpServer && self._udpServer.bind(port.udp || port) } @@ -208,11 +211,23 @@ Server.prototype._onAnnounce = function (params, cb) { if (response) { if (!response.action) response.action = common.ACTIONS.ANNOUNCE if (!response.intervalMs) response.intervalMs = self._intervalMs + if (params.compact === 1) { - response.peers = string2compact(response.peers.map(function (peer) { - return peer.ip + ':' + peer.port // TODO: ipv6 brackets + var peers = response.peers + // Find IPv4 peers + response.peers = string2compact(peers.filter(function (peer) { + return common.IPV4_RE.test(peer.ip) + }).map(function (peer) { + return peer.ip + ':' + peer.port + })) + // Find IPv6 peers + response.peers6 = string2compact(peers.filter(function (peer) { + return common.IPV6_RE.test(peer.ip) + }).map(function (peer) { + return '[' + peer.ip + ']:' + peer.port })) } + // IPv6 peers are not separate for non-compact responses } cb(err, response) }) diff --git a/test/server.js b/test/server.js index 0dce512..da0a6e7 100644 --- a/test/server.js +++ b/test/server.js @@ -7,11 +7,14 @@ var peerId = new Buffer('01234567890123456789') var peerId2 = new Buffer('12345678901234567890') var torrentLength = 50000 -function serverTest (t, serverType) { +function serverTest (t, serverType, serverFamily) { t.plan(26) var opts = serverType === 'http' ? { udp: false } : { http: false } var server = new Server(opts) + var serverAddr = serverFamily === 'inet6' ? '[::1]' : '127.0.0.1' + var clientAddr = serverFamily === 'inet6' ? '[::1]' : '127.0.0.1' + var clientIp = serverFamily === 'inet6' ? '::1' : '127.0.0.1' server.on('error', function (err) { t.fail(err.message) @@ -26,7 +29,7 @@ function serverTest (t, serverType) { }) server.listen(function (port) { - var announceUrl = serverType + '://127.0.0.1:' + port + '/announce' + var announceUrl = serverType + '://' + serverAddr + ':' + port + '/announce' var client = new Client(peerId, 6881, { infoHash: infoHash, @@ -49,8 +52,8 @@ function serverTest (t, serverType) { t.equal(server.getSwarm(infoHash).complete, 0) t.equal(server.getSwarm(infoHash).incomplete, 1) t.equal(Object.keys(server.getSwarm(infoHash).peers).length, 1) - t.deepEqual(server.getSwarm(infoHash).peers['127.0.0.1:6881'], { - ip: '127.0.0.1', + t.deepEqual(server.getSwarm(infoHash).peers[clientAddr + ':6881'], { + ip: clientIp, port: 6881, peerId: peerId.toString('hex') }) @@ -83,7 +86,7 @@ function serverTest (t, serverType) { }) client2.once('peer', function (addr) { - t.equal(addr, '127.0.0.1:6881') + t.equal(addr, clientAddr + ':6881') client2.stop() client2.once('update', function (data) { @@ -109,10 +112,14 @@ function serverTest (t, serverType) { }) } -test('http server', function (t) { - serverTest(t, 'http') +test('http ipv4 server', function (t) { + serverTest(t, 'http', 'inet') +}) + +test('http ipv6 server', function (t) { + serverTest(t, 'http', 'inet6') }) test('udp server', function (t) { - serverTest(t, 'udp') + serverTest(t, 'udp', 'inet') })