@@ -296,75 +296,40 @@ Server.prototype._onHttpRequest = function (req, res) {
296296Server . prototype . _onUdpRequest = function ( msg , rinfo ) {
297297 var self = this
298298
299- if ( msg . length < 16 ) {
300- return error ( 'received packet is too short' )
301- }
302-
303- if ( rinfo . family !== 'IPv4' ) {
304- return error ( 'udp tracker does not support IPv6' )
305- }
306-
307- var connectionId = msg . slice ( 0 , 8 ) // 64-bit
308- var action = msg . readUInt32BE ( 8 )
309- var transactionId = msg . readUInt32BE ( 12 )
310-
311- if ( ! bufferEqual ( connectionId , common . CONNECTION_ID ) ) {
312- return error ( 'received packet with invalid connection id' )
299+ var params
300+ try {
301+ params = parseUdpRequest ( msg , rinfo )
302+ } catch ( err ) {
303+ console . error ( err . stack )
304+ return error ( err . message )
313305 }
314-
306+
315307 var socket = dgram . createSocket ( 'udp4' )
316308
317- var infoHash , swarm
318- if ( action === common . ACTIONS . CONNECT ) {
309+ var swarm
310+ if ( params && params . request === 'connect' ) {
319311 send ( Buffer . concat ( [
320312 common . toUInt32 ( common . ACTIONS . CONNECT ) ,
321- common . toUInt32 ( transactionId ) ,
322- connectionId
313+ common . toUInt32 ( params . transactionId ) ,
314+ params . connectionId
323315 ] ) )
324- } else if ( action === common . ACTIONS . ANNOUNCE ) {
325- infoHash = msg . slice ( 16 , 36 ) . toString ( 'binary' ) // 20 bytes
326- var peerId = msg . slice ( 36 , 56 ) . toString ( 'utf8' ) // 20 bytes
327- var downloaded = fromUInt64 ( msg . slice ( 56 , 64 ) ) // TODO: track this?
328- var left = fromUInt64 ( msg . slice ( 64 , 72 ) )
329- var uploaded = fromUInt64 ( msg . slice ( 72 , 80 ) ) // TODO: track this?
330- var event = msg . readUInt32BE ( 80 )
331- var ip = msg . readUInt32BE ( 84 ) // optional
332- var key = msg . readUInt32BE ( 88 ) // TODO: what is this for?
333- var numwant = msg . readUInt32BE ( 92 ) // optional
334- var port = msg . readUInt16BE ( 96 ) // optional
335-
336- if ( ip ) {
337- ip = ipLib . toString ( ip )
338- } else {
339- ip = rinfo . address
340- }
341-
342- if ( ! port ) {
343- port = rinfo . port
344- }
345-
346- var addr = ip + ':' + port
347-
348- swarm = self . _getSwarm ( infoHash )
349- var peer = swarm . peers [ addr ]
350-
351- // never send more than MAX_ANNOUNCE_PEERS or else the UDP packet will get bigger than
352- // 512 bytes which is not safe
353- numwant = Math . min ( numwant || NUM_ANNOUNCE_PEERS , MAX_ANNOUNCE_PEERS )
316+ } else if ( params && params . request === 'announce' ) {
317+ swarm = self . _getSwarm ( params . info_hash )
318+ var peer = swarm . peers [ params . addr ]
354319
355320 var start = function ( ) {
356321 if ( peer ) {
357322 debug ( 'unexpected `started` event from peer that is already in swarm' )
358323 return update ( ) // treat as an update
359324 }
360- if ( left === 0 ) swarm . complete += 1
325+ if ( params . left === 0 ) swarm . complete += 1
361326 else swarm . incomplete += 1
362- peer = swarm . peers [ addr ] = {
363- ip : ip ,
364- port : port ,
365- peerId : peerId
327+ peer = swarm . peers [ params . addr ] = {
328+ ip : params . ip ,
329+ port : params . port ,
330+ peerId : params . peer_id
366331 }
367- self . emit ( 'start' , addr )
332+ self . emit ( 'start' , params . addr )
368333 }
369334
370335 var stop = function ( ) {
@@ -374,8 +339,8 @@ Server.prototype._onUdpRequest = function (msg, rinfo) {
374339 }
375340 if ( peer . complete ) swarm . complete -= 1
376341 else swarm . incomplete -= 1
377- swarm . peers [ addr ] = null
378- self . emit ( 'stop' , addr )
342+ swarm . peers [ params . addr ] = null
343+ self . emit ( 'stop' , params . addr )
379344 }
380345
381346 var complete = function ( ) {
@@ -390,61 +355,54 @@ Server.prototype._onUdpRequest = function (msg, rinfo) {
390355 swarm . complete += 1
391356 swarm . incomplete -= 1
392357 peer . complete = true
393- self . emit ( 'complete' , addr )
358+ self . emit ( 'complete' , params . addr )
394359 }
395360
396361 var update = function ( ) {
397362 if ( ! peer ) {
398363 debug ( 'unexpected `update` event from peer that is not in swarm' )
399364 return start ( ) // treat as a start
400365 }
401- self . emit ( 'update' , addr )
366+ self . emit ( 'update' , params . addr )
402367 }
403368
404- switch ( event ) {
405- case common . EVENTS . started :
369+ switch ( params . event ) {
370+ case ' started' :
406371 start ( )
407372 break
408- case common . EVENTS . stopped :
373+ case ' stopped' :
409374 stop ( )
410375 break
411- case common . EVENTS . completed :
376+ case ' completed' :
412377 complete ( )
413378 break
414- case common . EVENTS . update : // update
379+ case ' update' :
415380 update ( )
416381 break
417382 default :
418383 return error ( 'invalid event' ) // early return
419384 }
420385
421- if ( left === 0 && peer ) peer . complete = true
386+ if ( params . left === 0 && peer ) peer . complete = true
422387
423388 // send peers
424- var peers = self . _getPeersCompact ( swarm , numwant )
389+ var peers = self . _getPeersCompact ( swarm , params . numwant )
425390
426391 send ( Buffer . concat ( [
427392 common . toUInt32 ( common . ACTIONS . ANNOUNCE ) ,
428- common . toUInt32 ( transactionId ) ,
393+ common . toUInt32 ( params . transactionId ) ,
429394 common . toUInt32 ( self . _intervalMs ) ,
430395 common . toUInt32 ( swarm . incomplete ) ,
431396 common . toUInt32 ( swarm . complete ) ,
432397 peers
433398 ] ) )
434399
435- } else if ( action === common . ACTIONS . SCRAPE ) { // scrape message
436- infoHash = msg . slice ( 16 , 36 ) . toString ( 'binary' ) // 20 bytes
437-
438- // TODO: support multiple info_hash scrape
439- if ( msg . length > 36 ) {
440- error ( 'multiple info_hash scrape not supported' )
441- }
442-
443- swarm = self . _getSwarm ( infoHash )
400+ } else if ( params && params . request === 'scrape' ) { // scrape message
401+ swarm = self . _getSwarm ( params . info_hash )
444402
445403 send ( Buffer . concat ( [
446404 common . toUInt32 ( common . ACTIONS . SCRAPE ) ,
447- common . toUInt32 ( transactionId ) ,
405+ common . toUInt32 ( params . transactionId ) ,
448406 common . toUInt32 ( swarm . complete ) ,
449407 common . toUInt32 ( swarm . complete ) , // TODO: this only provides a lower-bound
450408 common . toUInt32 ( swarm . incomplete )
@@ -464,7 +422,7 @@ Server.prototype._onUdpRequest = function (msg, rinfo) {
464422 debug ( 'sent error %s' , message )
465423 send ( Buffer . concat ( [
466424 common . toUInt32 ( common . ACTIONS . ERROR ) ,
467- common . toUInt32 ( transactionId || 0 ) ,
425+ common . toUInt32 ( params . transactionId || 0 ) ,
468426 new Buffer ( message , 'utf8' )
469427 ] ) )
470428 self . emit ( 'warning' , new Error ( message ) )
@@ -553,6 +511,65 @@ function parseHttpRequest (req, options) {
553511 }
554512}
555513
514+ function parseUdpRequest ( msg , rinfo ) {
515+ if ( msg . length < 16 ) {
516+ throw new Error ( 'received packet is too short' )
517+ }
518+
519+ if ( rinfo . family !== 'IPv4' ) {
520+ throw new Error ( 'udp tracker does not support IPv6' )
521+ }
522+
523+ var params = {
524+ connectionId : msg . slice ( 0 , 8 ) , // 64-bit
525+ action : msg . readUInt32BE ( 8 ) ,
526+ transactionId : msg . readUInt32BE ( 12 )
527+ }
528+
529+ // TODO: randomize:
530+ if ( ! bufferEqual ( params . connectionId , common . CONNECTION_ID ) ) {
531+ throw new Error ( 'received packet with invalid connection id' )
532+ }
533+
534+ if ( params . action === common . ACTIONS . CONNECT ) {
535+ params . request = 'connect'
536+ } else if ( params . action === common . ACTIONS . ANNOUNCE ) {
537+ params . request = 'announce'
538+ params . info_hash = msg . slice ( 16 , 36 ) . toString ( 'binary' ) // 20 bytes
539+ params . peer_id = msg . slice ( 36 , 56 ) . toString ( 'utf8' ) // 20 bytes
540+ params . downloaded = fromUInt64 ( msg . slice ( 56 , 64 ) ) // TODO: track this?
541+ params . left = fromUInt64 ( msg . slice ( 64 , 72 ) )
542+ params . uploaded = fromUInt64 ( msg . slice ( 72 , 80 ) ) // TODO: track this?
543+ params . event = msg . readUInt32BE ( 80 )
544+ params . event = common . EVENT_IDS [ params . event ]
545+ if ( ! params . event ) throw new Error ( 'invalid event' ) // early return
546+ params . ip = msg . readUInt32BE ( 84 ) // optional
547+ params . ip = params . ip ?
548+ ipLib . toString ( params . ip ) :
549+ params . ip = rinfo . address
550+ params . key = msg . readUInt32BE ( 88 ) // TODO: what is this for?
551+ params . numwant = msg . readUInt32BE ( 92 ) // optional
552+ // never send more than MAX_ANNOUNCE_PEERS or else the UDP packet will get bigger than
553+ // 512 bytes which is not safe
554+ params . numwant = Math . min ( params . numwant || NUM_ANNOUNCE_PEERS , MAX_ANNOUNCE_PEERS )
555+ params . port = msg . readUInt16BE ( 96 ) || rinfo . port // optional
556+ params . addr = params . ip + ':' + params . port // TODO: ipv6 brackets
557+
558+ } else if ( params . action === common . ACTIONS . SCRAPE ) { // scrape message
559+ params . request = 'scrape'
560+ params . info_hash = msg . slice ( 16 , 36 ) . toString ( 'binary' ) // 20 bytes
561+
562+ // TODO: support multiple info_hash scrape
563+ if ( msg . length > 36 ) {
564+ throw new Error ( 'multiple info_hash scrape not supported' )
565+ }
566+ } else {
567+ return null
568+ }
569+
570+ return params
571+ }
572+
556573
557574// HELPER FUNCTIONS
558575
0 commit comments