Skip to content

Commit 98a50c1

Browse files
committed
Add a socks proxy to udp client trackers.
1 parent ee7783b commit 98a50c1

File tree

3 files changed

+53
-21
lines changed

3 files changed

+53
-21
lines changed

client.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ inherits(Client, EventEmitter)
3232
* @param {number} opts.rtcConfig RTCPeerConnection configuration object
3333
* @param {number} opts.wrtc custom webrtc impl (useful in node.js)
3434
* @param {object} opts.httpAgent HTTP agent impl (used to proxy http requests in node.js)
35+
* @param {object} opts.socksProxyOpts socks proxy options (used to proxy UDP requests in node.js)
3536
*/
3637
function Client (opts) {
3738
var self = this
@@ -64,6 +65,7 @@ function Client (opts) {
6465
self._rtcConfig = opts.rtcConfig
6566
self._wrtc = opts.wrtc
6667
self._httpAgent = opts.httpAgent
68+
self._socksProxyOpts = opts.socksProxyOpts
6769
self._getAnnounceOpts = opts.getAnnounceOpts
6870

6971
debug('new client %s', self.infoHash)

lib/client/udp-tracker.js

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ var debug = require('debug')('bittorrent-tracker:udp-tracker')
77
var dgram = require('dgram')
88
var inherits = require('inherits')
99
var randombytes = require('randombytes')
10+
var Socks = require('socks')
1011
var url = require('url')
1112

1213
var common = require('../common')
@@ -63,8 +64,27 @@ UDPTracker.prototype._request = function (opts) {
6364
var self = this
6465
if (!opts) opts = {}
6566
var parsedUrl = url.parse(self.announceUrl)
67+
if (!parsedUrl.port) {
68+
parsedUrl.port = 80
69+
}
70+
var timeout
71+
var socket
6672
var transactionId = genTransactionId()
67-
var socket = dgram.createSocket('udp4')
73+
if (self.client._socksProxyOpts) {
74+
// UDP requests uses the associate command
75+
self.client._socksProxyOpts.command = 'associate'
76+
if (!self.client._socksProxyOpts.target) {
77+
// This should contain client IP and port but can be set to 0 if we don't have this information
78+
self.client._socksProxyOpts.target = {
79+
host: '0.0.0.0',
80+
port: 0
81+
}
82+
}
83+
84+
Socks.createConnection(self.client._socksProxyOpts, onGotSocket)
85+
} else {
86+
onGotSocket(null, dgram.createSocket('udp4'))
87+
}
6888

6989
var cleanup = function () {
7090
if (!socket) return
@@ -81,23 +101,29 @@ UDPTracker.prototype._request = function (opts) {
81101
}
82102
self.cleanupFns.push(cleanup)
83103

84-
// does not matter if `stopped` event arrives, so supress errors & cleanup after timeout
85-
var ms = opts.event === 'stopped' ? TIMEOUT / 10 : TIMEOUT
86-
var timeout = setTimeout(function () {
87-
timeout = null
88-
if (opts.event === 'stopped') cleanup()
89-
else onError(new Error('tracker request timed out (' + opts.event + ')'))
90-
}, ms)
91-
if (timeout.unref) timeout.unref()
104+
function onGotSocket (err, s, info) {
105+
if (err) return onError(err)
92106

93-
send(Buffer.concat([
94-
common.CONNECTION_ID,
95-
common.toUInt32(common.ACTIONS.CONNECT),
96-
transactionId
97-
]))
107+
socket = s
98108

99-
socket.on('error', onError)
100-
socket.on('message', onSocketMessage)
109+
// does not matter if `stopped` event arrives, so supress errors & cleanup after timeout
110+
var ms = opts.event === 'stopped' ? TIMEOUT / 10 : TIMEOUT
111+
timeout = setTimeout(function () {
112+
timeout = null
113+
if (opts.event === 'stopped') cleanup()
114+
else onError(new Error('tracker request timed out (' + opts.event + ')'))
115+
}, ms)
116+
if (timeout.unref) timeout.unref()
117+
118+
send(Buffer.concat([
119+
common.CONNECTION_ID,
120+
common.toUInt32(common.ACTIONS.CONNECT),
121+
transactionId
122+
]), info)
123+
124+
socket.on('error', onError)
125+
socket.on('message', onSocketMessage)
126+
}
101127

102128
function onSocketMessage (msg) {
103129
if (self.destroyed) return
@@ -180,11 +206,13 @@ UDPTracker.prototype._request = function (opts) {
180206
self.client.emit('warning', err)
181207
}
182208

183-
function send (message) {
184-
if (!parsedUrl.port) {
185-
parsedUrl.port = 80
209+
function send (message, info) {
210+
if (info) {
211+
var pack = Socks.createUDPFrame({ host: parsedUrl.hostname, port: parsedUrl.port }, message)
212+
socket.send(pack, 0, pack.length, info.port, info.host)
213+
} else {
214+
socket.send(message, 0, message.length, parsedUrl.port, parsedUrl.hostname)
186215
}
187-
socket.send(message, 0, message.length, parsedUrl.port, parsedUrl.hostname)
188216
}
189217

190218
function announce (connectionId, opts) {

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"./lib/common-node.js": false,
1515
"./lib/client/http-tracker.js": false,
1616
"./lib/client/udp-tracker.js": false,
17-
"./server.js": false
17+
"./server.js": false,
18+
"socks": false
1819
},
1920
"bugs": {
2021
"url": "https://github.com/feross/bittorrent-tracker/issues"
@@ -38,6 +39,7 @@
3839
"simple-get": "^2.0.0",
3940
"simple-peer": "^6.0.0",
4041
"simple-websocket": "^4.0.0",
42+
"socks": "^1.1.9",
4143
"string2compact": "^1.1.1",
4244
"uniq": "^1.0.1",
4345
"ws": "^1.0.0",

0 commit comments

Comments
 (0)