@@ -13,14 +13,9 @@ var string2compact = require('string2compact')
1313var dgram = require ( 'dgram' )
1414var parseUrl = require ( 'url' ) . parse
1515
16- var CONNECTION_ID = Buffer . concat ( [ toUInt32 ( 0x417 ) , toUInt32 ( 0x27101980 ) ] )
17- var CONNECT = toUInt32 ( 0 )
18- var ANNOUNCE = toUInt32 ( 1 )
19- var EVENTS = {
20- completed : 1 ,
21- started : 2 ,
22- stopped : 3
23- }
16+ var CONNECTION_ID = Buffer . concat ( [ toUInt32 ( 0x417 ) , toUInt32 ( 0x27101980 ) ] )
17+ var ACTIONS = { CONNECT : 0 , ANNOUNCE : 1 }
18+ var EVENTS = { completed : 1 , started : 2 , stopped : 3 }
2419
2520inherits ( Client , EventEmitter )
2621
@@ -31,9 +26,13 @@ function Client (peerId, port, torrent, opts) {
3126 self . _opts = opts || { }
3227
3328 // required
34- self . _peerId = peerId
29+ self . _peerId = Buffer . isBuffer ( peerId )
30+ ? peerId
31+ : new Buffer ( torrent . infoHash , 'utf8' )
3532 self . _port = port
36- self . _infoHash = torrent . infoHash
33+ self . _infoHash = Buffer . isBuffer ( torrent . infoHash )
34+ ? torrent . infoHash
35+ : new Buffer ( torrent . infoHash , 'hex' )
3736 self . _torrentLength = torrent . length
3837 self . _announce = torrent . announce
3938
@@ -142,21 +141,26 @@ Client.prototype._requestHttp = function (announceUrl, opts) {
142141}
143142
144143Client . prototype . _requestUdp = function ( announceUrl , opts ) {
145- var parsed = parseUrl ( announceUrl )
146- var socket = dgram . createSocket ( 'udp4' )
147144 var self = this
145+ var parsedUrl = parseUrl ( announceUrl )
146+ var socket = dgram . createSocket ( 'udp4' )
147+ var transactionId = new Buffer ( hat ( 32 ) , 'hex' )
148148
149- var timeout = setTimeout ( function ( ) {
149+ var timeout = setTimeout ( function ( ) {
150150 socket . close ( )
151151 } , 5000 )
152152
153- socket . on ( 'error' , function ( err ) {
153+ socket . on ( 'error' , function ( err ) {
154154 self . emit ( 'error' , err )
155155 } )
156156
157- socket . on ( 'message' , function ( message , rinfo ) {
158- var action = message . readUInt32BE ( 0 )
157+ socket . on ( 'message' , function ( message , rinfo ) {
158+
159+ if ( message . length < 8 || message . readUInt32BE ( 4 ) !== transactionId . readUInt32BE ( 0 ) ) {
160+ return self . emit ( 'error' , new Error ( 'tracker sent back invalid transaction id' ) )
161+ }
159162
163+ var action = message . readUInt32BE ( 0 )
160164 switch ( action ) {
161165 case 0 :
162166 if ( message . length < 16 ) {
@@ -170,49 +174,58 @@ Client.prototype._requestUdp = function (announceUrl, opts) {
170174 return self . emit ( 'error' , new Error ( 'invalid announce message' ) )
171175 }
172176
177+ // TODO: this should be stored per tracker, not globally for all trackers
178+ var interval = message . readUInt32BE ( 8 )
179+ if ( interval && ! self . _opts . interval && self . _intervalMs !== 0 ) {
180+ // use the interval the tracker recommends, UNLESS the user manually specifies an
181+ // interval they want to use
182+ self . setInterval ( interval * 1000 )
183+ }
184+
173185 self . emit ( 'update' , {
174186 announce : announceUrl ,
175187 complete : message . readUInt32BE ( 16 ) ,
176188 incomplete : message . readUInt32BE ( 12 )
177189 } )
178190
179- for ( var i = 20 ; i < message . length ; i += 6 ) {
180- self . emit ( 'peer' , compact2string ( message . slice ( i , i + 6 ) ) )
181- }
191+ compact2string . multi ( message . slice ( 20 ) ) . forEach ( function ( addr ) {
192+ self . emit ( 'peer' , addr )
193+ } )
182194
183195 clearTimeout ( timeout )
184196 socket . close ( )
185197 }
186198 } )
187199
200+ function send ( message ) {
201+ socket . send ( message , 0 , message . length , parsedUrl . port , parsedUrl . hostname )
202+ }
203+
188204 function announce ( connectionId , opts ) {
189205 opts = opts || { }
206+ transactionId = new Buffer ( hat ( 32 ) , 'hex' )
190207
191208 send ( Buffer . concat ( [
192209 connectionId ,
193- ANNOUNCE ,
194- new Buffer ( hat ( 32 ) , 'hex' ) ,
195- new Buffer ( self . _infoHash , 'hex' ) ,
196- new Buffer ( self . _peerId , 'utf-8' ) ,
197- toUInt32 ( 0 ) , toUInt32 ( opts . downloaded || 0 ) , // fromUint32(0) to expand this to 64bit
198- toUInt32 ( 0 ) , toUInt32 ( opts . left || 0 ) ,
199- toUInt32 ( 0 ) , toUInt32 ( opts . uploaded || 0 ) ,
210+ toUInt32 ( ACTIONS . ANNOUNCE ) ,
211+ transactionId ,
212+ self . _infoHash ,
213+ self . _peerId ,
214+ toUInt32 ( 0 ) , toUInt32 ( opts . downloaded || 0 ) , // 64bit
215+ toUInt32 ( 0 ) , toUInt32 ( opts . left || 0 ) , // 64bit
216+ toUInt32 ( 0 ) , toUInt32 ( opts . uploaded || 0 ) , // 64bit
200217 toUInt32 ( EVENTS [ opts . event ] || 0 ) ,
201- toUInt32 ( 0 ) ,
202- toUInt32 ( 0 ) ,
218+ toUInt32 ( 0 ) , // ip address (optional)
219+ toUInt32 ( 0 ) , // key (optional)
203220 toUInt32 ( self . _numWant ) ,
204221 toUInt16 ( self . _port || 0 )
205222 ] ) )
206223 }
207224
208- function send ( message ) {
209- socket . send ( message , 0 , message . length , parsed . port , parsed . hostname )
210- }
211-
212225 send ( Buffer . concat ( [
213226 CONNECTION_ID ,
214- CONNECT ,
215- new Buffer ( hat ( 32 ) , 'hex' )
227+ toUInt32 ( ACTIONS . CONNECT ) ,
228+ transactionId
216229 ] ) )
217230}
218231
@@ -234,13 +247,15 @@ Client.prototype._handleResponse = function (data, announceUrl) {
234247 self . emit ( 'warning' , warning ) ;
235248 }
236249
250+ // TODO: this should be stored per tracker, not globally for all trackers
237251 var interval = data . interval || data [ 'min interval' ]
238252 if ( interval && ! self . _opts . interval && self . _intervalMs !== 0 ) {
239253 // use the interval the tracker recommends, UNLESS the user manually specifies an
240254 // interval they want to use
241255 self . setInterval ( interval * 1000 )
242256 }
243257
258+ // TODO: this should be stored per tracker, not globally for all trackers
244259 var trackerId = data [ 'tracker id' ]
245260 if ( trackerId ) {
246261 // If absent, do not discard previous trackerId value
@@ -255,8 +270,7 @@ Client.prototype._handleResponse = function (data, announceUrl) {
255270
256271 if ( Buffer . isBuffer ( data . peers ) ) {
257272 // tracker returned compact response
258- var addrs = compact2string . multi ( data . peers )
259- addrs . forEach ( function ( addr ) {
273+ compact2string . multi ( data . peers ) . forEach ( function ( addr ) {
260274 self . emit ( 'peer' , addr )
261275 } )
262276 } else if ( Array . isArray ( data . peers ) ) {
@@ -463,15 +477,9 @@ function toUInt32 (n) {
463477}
464478
465479function bytewiseEncodeURIComponent ( buf ) {
466- if ( ! Buffer . isBuffer ( buf ) ) {
467- buf = new Buffer ( buf , 'hex' )
468- }
469480 return encodeURIComponent ( buf . toString ( 'binary' ) )
470481}
471482
472483function bytewiseDecodeURIComponent ( str ) {
473- if ( Buffer . isBuffer ( str ) ) {
474- str = str . toString ( 'utf8' )
475- }
476484 return ( new Buffer ( decodeURIComponent ( str ) , 'binary' ) . toString ( 'hex' ) )
477485}
0 commit comments