Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ var optionalOpts = {
uploaded: 0,
downloaded: 0,
left: 0,
ip: 'custom.ip.address.or.hostname',
customParam: 'blah' // custom parameters supported
}
}
Expand Down Expand Up @@ -252,6 +253,7 @@ $ bittorrent-tracker --help

Options:
-p, --port [number] change the port [default: 8000]
--trust-ip trust 'ip' parameter in GET requests
--trust-proxy trust 'x-forwarded-for' header from reverse proxy
--interval client announce interval (ms) [default: 600000]
--http enable http server
Expand Down
3 changes: 3 additions & 0 deletions bin/cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var argv = minimist(process.argv.slice(2), {
'http',
'quiet',
'silent',
'trust-ip',
'trust-proxy',
'udp',
'version',
Expand Down Expand Up @@ -53,6 +54,7 @@ if (argv.help) {
--http-hostname [string] change the http server hostname [default: '::']
--udp-hostname [string] change the udp hostname [default: '0.0.0.0']
--udp6-hostname [string] change the udp6 hostname [default: '::']
--trust-ip trust 'ip' parameter in GET requests
--trust-proxy trust 'x-forwarded-for' header from reverse proxy
--interval client announce interval (ms) [default: 600000]
--http enable http server
Expand Down Expand Up @@ -82,6 +84,7 @@ var server = new Server({
http: argv.http,
interval: argv.interval,
stats: argv.stats,
trustIp: argv['trust-ip'],
trustProxy: argv['trust-proxy'],
udp: argv.udp,
ws: argv.ws
Expand Down
15 changes: 12 additions & 3 deletions lib/server/parse-http.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,18 @@ function parseHttpRequest (req, opts) {
common.MAX_ANNOUNCE_PEERS
)

params.ip = opts.trustProxy
? req.headers['x-forwarded-for'] || req.connection.remoteAddress
: req.connection.remoteAddress.replace(common.REMOVE_IPV4_MAPPED_IPV6_RE, '') // force ipv4
// If we're trusting IPs supplied in the GET parameters, simply use that if available
if (opts.trustIp && params.ip) {
// No operation needed
// Else, if we're trusting proxied headers, use that value if available,
} else if (opts.trustProxy && req.headers['x-forwarded-for']) {
params.ip = req.headers['x-forwarded-for']
// Otherwise, simply use the connection's remote host address.
} else {
params.ip = req.connection.remoteAddress
// Remove IPv4-Mapped address prefix from IPv6 address if present
.replace(common.REMOVE_IPV4_MAPPED_IPV6_RE, '')
}
params.addr = (common.IPV6_RE.test(params.ip) ? '[' + params.ip + ']' : params.ip) + ':' + params.port

params.headers = req.headers
Expand Down
4 changes: 4 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ inherits(Server, EventEmitter)
*
* @param {Object} opts options object
* @param {Number} opts.interval tell clients to announce on this interval (ms)
* @param {Number} opts.trustIp trust 'ip' parameter in GET requests
* @param {Number} opts.trustProxy trust 'x-forwarded-for' header from reverse proxy
* @param {boolean} opts.http start an http server? (default: true)
* @param {boolean} opts.udp start a udp server? (default: true)
Expand All @@ -49,6 +50,7 @@ function Server (opts) {
: 10 * 60 * 1000 // 10 min

self._trustProxy = !!opts.trustProxy
self._trustIp = !!opts.trustIp
if (typeof opts.filter === 'function') self._filter = opts.filter

self.peersCacheLength = opts.peersCacheLength
Expand Down Expand Up @@ -366,6 +368,7 @@ Server.prototype.onHttpRequest = function (req, res, opts) {
var self = this
if (!opts) opts = {}
opts.trustProxy = opts.trustProxy || self._trustProxy
opts.trustIp = opts.trustIp || self._trustIp

var params
try {
Expand Down Expand Up @@ -445,6 +448,7 @@ Server.prototype.onWebSocketConnection = function (socket, opts) {
var self = this
if (!opts) opts = {}
opts.trustProxy = opts.trustProxy || self._trustProxy
opts.trustIp = opts.trustIp || self._trustIp

socket.peerId = null // as hex
socket.infoHashes = [] // swarms that this socket is participating in
Expand Down
3 changes: 2 additions & 1 deletion test/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,8 @@ function testClientGetAnnounceOpts (t, serverType) {
port: port,
getAnnounceOpts: function () {
return {
testParam: 'this is a test'
testParam: 'this is a test',
ip: 'test.bittorrenttest.xyz'
}
},
wrtc: {}
Expand Down
157 changes: 157 additions & 0 deletions test/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,160 @@ test('http ipv6 server', function (t) {
test('udp server', function (t) {
serverTest(t, 'udp', 'inet')
})

function serverTestTrustIP (t, serverType, serverFamily) {
t.plan(32)

var hostname = serverFamily === 'inet6'
? '[::1]'
: '127.0.0.1'
var customIp = 'custom.ip.address.or.hostname'

var opts = {
serverType: serverType,
peersCacheLength: 2,
trustIp: true
}

common.createServer(t, opts, function (server) {
var port = server[serverType].address().port
var announceUrl = serverType + '://' + hostname + ':' + port + '/announce'

var client1 = new Client({
infoHash: infoHash,
announce: [ announceUrl ],
peerId: peerId,
port: 6881,
getAnnounceOpts: function () {
// provide a callback that will be called whenever announce() is called
// internally (on timer), or by the user
return {
uploaded: 0,
downloaded: 0,
left: 0,
ip: customIp,
customParam: 'blah' // custom parameters supported
}
}
// wrtc: wrtc
})

client1.start()

server.once('start', function () {
t.pass('got start message from client1')
})

client1.once('update', function (data) {
t.equal(data.announce, announceUrl)
t.equal(data.complete, 0)
t.equal(data.incomplete, 1)

server.getSwarm(infoHash, function (err, swarm) {
t.error(err)

t.equal(Object.keys(server.torrents).length, 1)
t.equal(swarm.complete, 0)
t.equal(swarm.incomplete, 1)
t.equal(swarm.peers.length, 1)

var id = serverType === 'ws'
? peerId.toString('hex')
: customIp + ':6881'

var peer = swarm.peers.peek(id)

t.equal(peer.type, serverType)
t.equal(peer.ip, customIp)
t.equal(peer.peerId, peerId.toString('hex'))
t.equal(peer.complete, false)
if (serverType === 'ws') {
t.equal(typeof peer.port, 'number')
t.ok(peer.socket)
} else {
t.equal(peer.port, 6881)
t.notOk(peer.socket)
}

client1.complete()

client1.once('update', function (data) {
t.equal(data.announce, announceUrl)
t.equal(data.complete, 1)
t.equal(data.incomplete, 0)

client1.scrape()

client1.once('scrape', function (data) {
t.equal(data.announce, announceUrl)
t.equal(typeof data.complete, 'number')
t.equal(typeof data.incomplete, 'number')
t.equal(typeof data.downloaded, 'number')

var client2 = new Client({
infoHash: infoHash,
announce: [ announceUrl ],
peerId: peerId2,
port: 6882
// wrtc: wrtc
})

client2.start()

server.once('start', function () {
t.pass('got start message from client2')
})

client2.once('peer', function (addr) {
t.ok(addr === hostname + ':6881' || addr === hostname + ':6882' || addr.id === peerId.toString('hex'))

swarm.peers.once('evict', function (evicted) {
t.equals(evicted.value.peerId, peerId.toString('hex'))
})
var client3 = new Client({
infoHash: infoHash,
announce: [ announceUrl ],
peerId: peerId3,
port: 6880
// wrtc: wrtc
})
client3.start()

server.once('start', function () {
t.pass('got start message from client3')
})

client3.once('update', function () {
client2.stop()
client2.once('update', function (data) {
t.equal(data.announce, announceUrl)
t.equal(data.complete, 1)
t.equal(data.incomplete, 1)
client2.destroy()

client3.stop()
client3.once('update', function (data) {
t.equal(data.announce, announceUrl)
t.equal(data.complete, 1)
t.equal(data.incomplete, 0)

client3.destroy(function () {
client1.destroy(function () {
server.close()
})
// if (serverType === 'ws') wrtc.close()
})
})
})
})
})
})
})
})
})
})
}

test('http ipv4 server', function (t) {
serverTestTrustIP(t, 'http', 'inet')
})