Skip to content

Commit 0557ce6

Browse files
mafintoshfeross
authored andcommitted
basic udp tracker support
1 parent 8bc3024 commit 0557ce6

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

index.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ var http = require('http')
1010
var inherits = require('inherits')
1111
var querystring = require('querystring')
1212
var string2compact = require('string2compact')
13+
var dgram = require('dgram')
14+
var parseUrl = require('url').parse
15+
16+
var CONNECTION_ID = Buffer.concat([fromInt32(0x417), fromInt32(0x27101980)]);
17+
var CONNECT = fromInt32(0);
18+
var ANNOUNCE = fromInt32(1);
19+
var EVENTS = {completed:1, started:2, stopped:3};
1320

1421
inherits(Client, EventEmitter)
1522

@@ -77,6 +84,77 @@ Client.prototype.setInterval = function (intervalMs) {
7784
}
7885
}
7986

87+
Client.prototype._requestUdp = function(url, opts) {
88+
var parsed = parseUrl(url)
89+
var socket = dgram.createSocket('udp4')
90+
var self = this
91+
92+
var timeout = setTimeout(function() {
93+
socket.close()
94+
}, 5000)
95+
96+
socket.on('error', function(err) {
97+
self.emit('error', err)
98+
})
99+
100+
socket.on('message', function(message, rinfo) {
101+
var action = message.readUInt32BE(0)
102+
103+
switch (action) {
104+
case 0:
105+
if (message.length < 16) return self.emit('error', new Error('invalid udp handshake'))
106+
announce(message.slice(8, 16), opts)
107+
return;
108+
109+
case 1:
110+
if (message.length < 20) return self.emit('error', new Error('invalid announce message'))
111+
112+
self.emit('update', {
113+
announce: url,
114+
complete: message.readUInt32BE(16),
115+
incomplete: message.readUInt32BE(12)
116+
})
117+
118+
for (var i = 20; i < message.length; i += 6) {
119+
self.emit('peer', compact2string(message.slice(i, i+6)))
120+
}
121+
122+
clearTimeout(timeout)
123+
socket.close()
124+
}
125+
});
126+
127+
function announce(connectionId, opts) {
128+
opts = opts || {}
129+
130+
send(Buffer.concat([
131+
connectionId,
132+
ANNOUNCE,
133+
new Buffer(hat(32), 'hex'),
134+
new Buffer(self._infoHash, 'hex'),
135+
new Buffer(self._peerId, 'utf-8'),
136+
fromInt32(0), fromInt32(opts.downloaded || 0), // fromUint32(0) to expand this to 64bit
137+
fromInt32(0), fromInt32(opts.left || 0),
138+
fromInt32(0), fromInt32(opts.uploaded || 0),
139+
fromInt32(EVENTS[opts.event] || 0),
140+
fromInt32(0),
141+
fromInt32(0),
142+
fromInt32(self._numWant),
143+
fromInt16(self._port || 0)
144+
]));
145+
};
146+
147+
function send(message) {
148+
socket.send(message, 0, message.length, parsed.port, parsed.hostname)
149+
};
150+
151+
send(Buffer.concat([
152+
CONNECTION_ID,
153+
CONNECT,
154+
new Buffer(hat(32), 'hex')
155+
]));
156+
};
157+
80158
/**
81159
* Send a request to the tracker
82160
*/
@@ -100,6 +178,8 @@ Client.prototype._request = function (opts) {
100178
var q = querystring.stringify(opts)
101179

102180
self._announce.forEach(function (announce) {
181+
if (announce.indexOf('udp:') === 0) return self._requestUdp(announce, opts)
182+
103183
var url = announce + '?' + q
104184
var req = http.get(url, function (res) {
105185
var data = ''
@@ -356,6 +436,18 @@ Server.prototype._getPeersCompact = function (swarm) {
356436
// HELPERS
357437
//
358438

439+
function fromInt16(n) {
440+
var buf = new Buffer(2);
441+
buf.writeUInt16BE(n, 0);
442+
return buf;
443+
};
444+
445+
function fromInt32(n) {
446+
var buf = new Buffer(4);
447+
buf.writeUInt32BE(n, 0);
448+
return buf;
449+
};
450+
359451
function bytewiseEncodeURIComponent (buf) {
360452
if (!Buffer.isBuffer(buf)) {
361453
buf = new Buffer(buf, 'hex')

0 commit comments

Comments
 (0)