bep15
Version:
read and write UDP (BEP 15) tracker packets
359 lines (265 loc) • 8.15 kB
JavaScript
//
// Requires
//
var ipport = require('ipport');
//
// Helpers
//
function bufferEach (buffer, objectLength, offset, eachCb) {
for (var i = offset; i <= (buffer.length - offset / objectLength) - 1; i = i + objectLength) {
eachCb(buffer.slice(i, i + objectLength));
}
}
//
// Connect: Request
//
var writeConnectRequest = function(opts) {
var connectionId = new Buffer([0x41, 0x72, 0x71, 0x01, 0x98, 0x0]);
var action = 0;
var transactionId = opts.transactionId;
var packet = new Buffer(16);
connectionId.copy(packet, 0);
packet.writeInt32BE(action, 8);
packet.writeInt32BE(transactionId, 12);
return packet;
};
var readConnectRequest = function(packet) {
var connectionId = packet.slice(0, 8);
var action = packet.readInt32BE(8);
var transactionId = packet.readInt32BE(12);
return {
connectionId:connectionId,
action:action,
transactionId:transactionId
};
};
//
// Connect: Responce
//
var writeConnectResponse = function(opts) {
var packet = new Buffer(16);
// action
packet.writeInt32BE(0, 0);
// transaction_id
packet.writeInt32BE(opts.transactionId, 4);
// connection_id
opts.connectionId.copy(packet, 8);
return packet;
};
var readConnectResponse = function(packet) {
var action = packet.readInt32BE(0);
var transactionId = packet.readInt32BE(4);
var connectionId = packet.slice(8, 10);
return {
action:action,
transactionId:transactionId,
connectionId:connectionId
};
};
//
// Announce: Request
//
var writeAnnounceRequest = function(opts) {
var connection_id = opts.connectionId;
var action = new Buffer(4);
action.writeInt32BE(1, 0);
var transaction_id = new Buffer(4);
transaction_id.writeInt32BE(opts.transactionId, 0);
var info_hash = new Buffer(opts.infoHash, 'hex');
var peer_id = new Buffer(20);
peer_id.write(opts.peerId, 0, opts.peerId.length, 'hex');
var downloaded = opts.downloaded;
var left = opts.left;
var uploaded = opts.uploaded;
var event = new Buffer(4);
event.writeInt32BE(opts.event || 0, 0);
var ip_address = ipport.toBuffer(opts.ipAddress + ':0').slice(0, 4);
var key = new Buffer(4);
key.writeInt32BE(opts.key, 0);
var num_want = new Buffer(4);
num_want.writeInt32BE(opts.numWant || -1, 0);
var port = new Buffer(2);
port.writeInt16BE(opts.port, 0);
var packet = new Buffer(98);
connection_id.copy(packet);
action.copy(packet, 8);
transaction_id.copy(packet, 12);
info_hash.copy(packet, 16);
peer_id.copy(packet, 36);
downloaded.copy(packet, 56);
left.copy(packet, 64);
uploaded.copy(packet, 72);
event.copy(packet, 80);
ip_address.copy(packet, 84);
key.copy(packet, 88);
num_want.copy(packet, 92);
port.copy(packet, 96);
return packet;
};
var readAnnounceRequest = function(packet) {
var output = {};
output.connectionId = packet.slice(0, 8);
output.action = packet.readInt32BE(8);
output.transactionId = packet.readInt32BE(12);
output.infoHash = packet.slice(16, 36).toString('hex');
output.peerId = packet.slice(36, 56).toString('hex');
output.downloaded = packet.slice(56, 64);
output.left = packet.slice(64, 72);
output.uploaded = packet.slice(72, 80);
output.event = packet.readInt32BE(80);
output.ipAddress = ipport.toString(packet.slice(84, 90)).split(':')[0];
output.key = packet.readInt32BE(88);
output.numWant = packet.readInt32BE(92);
//output.port = packet.readInt32BE(96);
return output;
};
//
// Announce: Responce
//
var writeAnnounceResponce = function(opts) {
var packet = new Buffer(20 + (opts.peers.length * 12));
// action
packet.writeInt32BE(1, 0);
// transaction_id
packet.writeInt32BE(opts.transactionId, 4);
// interval
packet.writeInt32BE(opts.interval, 8);
// leechers
packet.writeInt32BE(opts.leechers, 12);
// seeders
packet.writeInt32BE(opts.seeders, 16);
for (var i = 0; i < opts.peers.length; i++) {
var ip = opts.peers[i];
var ipBuf = ipport.toBuffer(ip);
ipBuf.copy(packet, 24 + (i*6));
}
return packet;
};
var readAnnounceResponce = function(packet) {
var action = packet.readInt32BE(0);
var transactionId = packet.readInt32BE(4);
var interval = packet.readInt32BE(8);
var leechers = packet.readInt32BE(12);
var seeders = packet.readInt32BE(16);
var peers = [];
bufferEach(packet, 6, 20, function(buf) {
peers.push(ipport.toObject(buf));
});
return {
action:action,
transactionId:transactionId,
interval:interval,
leechers:leechers,
seeders:seeders,
peers:peers
};
};
//
// Scrape: Request
//
var writeScrapeRequest = function (opts) {
var packet = new Buffer(16 + ((typeof opts.torrents == 'array') ? opts.torrents.length : 1) * 20);
opts.connectionId.copy(packet);
packet.writeInt32BE(2, 8);
opts.transactionId.copy(packet, 12);
if(typeof opts.torrents == 'object') {
for (var i = opts.torrents.length - 1; i >= 0; i--) {
opts.torrents[i].copy(packet, 16 + (20*i));
}
} else {
opts.torrents.copy(packet, 16);
}
return packet;
};
var readScrapeRequest = function (packet) {
var connectionId = packet.slice(0, 8);
var action = packet.readInt32BE(8);
var transactionId = packet.slice(12, 16);
var torrents = [];
var torrentCount = (packet.length - 16) / 20;
for (var i = 0; i < torrentCount; i++) {
torrents.push(packet.toString('hex', 16+(i*20), 16+(i*20) + 20));
}
return {
connectionId:connectionId,
action:action,
transactionId:transactionId,
torrents:torrents
};
};
//
// Scrape: Responce
//
var writeScrapeResponce = function (opts) {
var packet = new Buffer(8 + (opts.torrents.length * 12));
// action
packet.writeInt32BE(2, 0);
// transaction_id
opts.transactionId.copy(packet, 4);
// torrents
for(var i in opts.torrents) {
// seeders
packet.writeInt32BE(opts.torrents[i].seeders, 8 + (12 * i));
// completed
packet.writeInt32BE(opts.torrents[i].completed, 12 + (12 * i));
// leechers
packet.writeInt32BE(opts.torrents[i].leechers, 16 + (12 * i));
}
return packet;
};
var readScrapeResponce = function (packet) {
var action = packet.readInt32BE(0);
var transactionId = packet.slice(4, 8);
// torrents
var torrents = [];
var torrentCount = (packet.length - 8) / 12;
for(var i = 0; i < torrentCount; i++) {
var seeders = packet.readInt32BE(8 + (12 * i));
var completed = packet.readInt32BE(16 + (12 * i));
var leechers = packet.readInt32BE(16 + (12 * i));
torrents.push({seeders:seeders, completed:completed, leechers:leechers});
}
return {
actions:action,
transactionId:transactionId,
torrents:torrents
};
};
//
// Misc
//
var readErrorResponce = function(packet) {
var action = packet.readInt32BE();
var transactionId = packet.readInt32BE(4);
var message = packet.slice(8, packet.lenth - 8);
return {
action:action,
transactionId:transactionId,
message:message
};
};
var isResponceError = function(packet) {
return (packet.readInt32BE() == 3) ? true : false;
};
var getRequestAction = function(packet) {
return packet.readInt32BE(8);
};
var getResponceAction = function(packet) {
return packet.readInt32BE(0);
};
module.exports.writeConnectRequest = writeConnectRequest;
module.exports.readConnectRequest = readConnectRequest;
module.exports.writeConnectResponse = writeConnectResponse;
module.exports.readConnectResponse = readConnectResponse;
module.exports.writeAnnounceRequest = writeAnnounceRequest;
module.exports.readAnnounceRequest = readAnnounceRequest;
module.exports.writeAnnounceResponce = writeAnnounceResponce;
module.exports.readAnnounceResponce = readAnnounceResponce;
module.exports.writeScrapeRequest = writeScrapeRequest;
module.exports.readScrapeRequest = readScrapeRequest;
module.exports.writeScrapeResponce = writeScrapeResponce;
module.exports.readScrapeResponce = readScrapeResponce;
module.exports.readErrorResponce = readErrorResponce;
module.exports.isResponceError = isResponceError;
module.exports.getRequestAction = getRequestAction;
module.exports.getResponceAction = getResponceAction;