Skip to content

Commit

Permalink
Parse tracker response, try connecting to peers.
Browse files Browse the repository at this point in the history
  • Loading branch information
jackpal committed May 19, 2010
1 parent 0c9fb30 commit 37936df
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 127 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -1,2 +1,5 @@
tests/a.torrent
tests/data
.project
.settings
*.torrent
4 changes: 2 additions & 2 deletions README
Expand Up @@ -11,13 +11,13 @@ which is currently more of a bitTorrent seeder than a full bitTorrent client.
Implemented features:
+ Parse .torrent files.
+ Scan disk to compute good/bad pieces for data files.
+ Request peer list from tracker to request.
+ Request peer list from tracker.
+ Parse tracker response.

TODO:
- UPNP open a port for listening for peers.
- Handle redirects from trackers.
- Create directories for multi-file torrents.
- Listen for peers.
- Parse tracker response.
- Connect to peers.
- Exchange data with peers.
18 changes: 18 additions & 0 deletions listener.js
@@ -0,0 +1,18 @@
var net = require('net'),
peer = require('./peer'),
sys = require('sys');


function create(port, hashes) {
var state = 0, server = net.createServer(function(stream) {
sys.log('createServer callback');
stream.setEncoding('binary');
stream.addListener('connect', function() {
sys.log('New connection');
// TODO: Tell tracker about this connection.
});
});
server.listen(port);
}

exports.create = create;
63 changes: 32 additions & 31 deletions main.js
Expand Up @@ -2,41 +2,42 @@ var sys = require('sys'),
torrent = require('./torrent');

function parseArgs(args) {
var result = {destDir:'.'},
torrentFiles = [],
i, argLen, arg;
for (i = 0, argLen = args.length; i < argLen; i += 1) {
arg = args[i];
if (arg.length == 0) {
throw "Empty argument";
}
if (arg.charAt(0) == '-') {
if (arg === '--destDir') {
result.destDir = args[i+1];
i += 1;
} else {
throw "Unknown flag " + arg;
}
} else {
torrentFiles.push(arg);
}
}
var result = {destDir:'.'},
torrentFiles = [],
i, argLen, arg;
for (i = 0, argLen = args.length; i < argLen; i += 1) {
arg = args[i];
if (arg.length == 0) {
throw "Empty argument";
}
if (arg.charAt(0) == '-') {
if (arg === '--destDir') {
result.destDir = args[i+1];
i += 1;
} else {
throw "Unknown flag " + arg;
}
} else {
torrentFiles.push(arg);
}
}
result.files = torrentFiles;
return result;
}

function main() {
var args = parseArgs(process.argv.slice(2)),
torrentFiles = args.files,
i, iLen, file;
if (torrentFiles.length == 0) {
throw "No torrent files specified.";
} else {
for (i = 0, iLen = torrentFiles.length; i < iLen; i += 1) {
file = torrentFiles[i];
torrent.startTorrent(file, args.destDir);
}
}
var args = parseArgs(process.argv.slice(2)),
torrentFiles = args.files,
i, iLen, file, aTorrent;
if (torrentFiles.length == 0) {
throw "No torrent files specified.";
} else {
for (i = 0, iLen = torrentFiles.length; i < iLen; i += 1) {
file = torrentFiles[i];
aTorrent = torrent.create(file, args.destDir);
aTorrent.start();
}
}
}

main();
main();
30 changes: 30 additions & 0 deletions peer.js
@@ -0,0 +1,30 @@
var net = require('net'),
sys = require('sys');

function create(host, port) {
sys.log('peer.create ' + host + ':' + port);
var peer = {
host: host,
port: port,
exchangedHeader: false,
stream: net.createConnection(port, host),
connect: function() {
sys.log("Connection established to " + this.host + ':' + this.port);
this.stream.write('\x13BitTorrent protocol\0\0\0\0\0\0\0\0');
}
};

peer.stream.setEncoding('binary');
sys.log('peer.create 3 ' + host + ':' + port);
peer.stream.addListener('connect', function() {
sys.log('peer.stream connect ' + host + ':' + port);
peer.connect();
});
peer.stream.addListener('error', function(e) {
sys.log('peer.stream error ' + host + ':' + port + ' ' + e);
})
sys.log('peer.create 4 ' + host + ':' + port);
return peer;
}

exports.create = create;
149 changes: 93 additions & 56 deletions torrent.js
@@ -1,63 +1,100 @@
var sys = require('sys'),
fs = require('fs'),
bencode = require('./bencode'),
filestore = require('./filestore'),
tracker = require("./tracker"),
var bencode = require('./bencode'),
crypto = require('crypto');
filestore = require('./filestore'),
fs = require('fs'),
listener = require('./listener'),
peer = require('./peer'),
sys = require('sys'),
tracker = require("./tracker");

function log(msg) {
sys.puts(msg);
}
function create(torrentPath, destDir) {
return {
torrentPath: torrentPath,
destDir: destDir,
listenerPort: 6881,
peers: {},
pingTracker: function () {
var that = this,
params = {
info_hash: this.metaInfo.info_hash,
peer_id: '01234567890123456789',
port: this.listenerPort,
uploaded: 0,
downloaded: 0,
left: this.store.left,
compact:1,
event:'started'
};
tracker.ping(this.trackerClient, params, function (error, response) {
var newPeers, numPeers, i, interval = 3600;
if (!error) {
interval = Math.max(interval, response.interval);
newPeers = response.peers;
numPeers = Math.floor(newPeers.length / 6);
sys.log('Tracker gave us ' + numPeers + ' peers.');
for (i = 0; i < numPeers; i++ ) {
that.addPeer(newPeers.substring(i*6,(i+1)*6));
}
}
that.pingTimer = setTimeout(function () {
that.pingTracker();
}, interval * 1000);
});
},

function pingTracker(metaInfo, store, trackerClient) {
var params = {
info_hash: metaInfo.info_hash,
peer_id: '01234567890123456789',
port: 6881,
uploaded: 0,
downloaded: 0,
left: store.left,
compact:1,
event:'started'
};
tracker.ping(trackerClient, params, function (error, response) {
system.log('pingTracker callback ' + error + ' ' + JSON.stringify(response));
});
}
addPeer : function (peerAddress) {
if ( ! (peerAddress in this.peers) ) {
this.peers[peerAddress] = peer.create(
this.decodeHost(peerAddress),
this.decodePort(peerAddress));
}
},

function computeHash(info) {
var encoded = bencode.encode(info),
hash = crypto.createHash('sha1');
hash.update(encoded);
return hash.digest('binary');
}
computeHash: function (info) {
var encoded = bencode.encode(info),
hash = crypto.createHash('sha1');
hash.update(encoded);
return hash.digest('binary');
},

decodeHost: function (address) {
return address.charCodeAt(0) + '.' + address.charCodeAt(1) + '.' + address.charCodeAt(2) + '.' + address.charCodeAt(3);
},

decodePort: function (address) {
return (address.charCodeAt(4) << 8) + address.charCodeAt(5);
},

function startTorrent(torrentPath, destDir) {
var metaInfo, store, error2, trackerClient;
sys.puts('Starting torrent ' + torrentPath);
fs.readFile(torrentPath, 'binary', function startTorrentCallback(error, contents) {
var aFileStore;
if (error) {
log('Could not open torrent file ' + torrentPath + ': ' + error);
} else {
metaInfo = bencode.decode(contents);
if ('comment' in metaInfo) {
sys.puts('Torrent \'' + metaInfo.comment + '\'');
}
metaInfo.info_hash = computeHash(metaInfo.info);
store = filestore.create(metaInfo, destDir);
sys.log('inspecting files');
filestore.inspect(store,
function inspectCallback(error) {
if (error) {
log('Could not inspect torrent files ' + error);
} else {
trackerClient = tracker.create(metaInfo);
pingTracker(metaInfo, store, trackerClient);
}
});
}
});
start : function() {
var that = this;
sys.puts('Starting torrent ' + this.torrentPath);
fs.readFile(this.torrentPath, 'binary',
function startTorrentCallback(error, contents) {
if (error) {
sys.log('Could not open torrent file ' + that.torrentPath + ': ' + error);
} else {
that.metaInfo = bencode.decode(contents);
if ('comment' in that.metaInfo) {
sys.log('Torrent \'' + that.metaInfo.comment + '\'');
}
that.metaInfo.info_hash = that.computeHash(that.metaInfo.info);
that.store = filestore.create(that.metaInfo, that.destDir);
sys.log('inspecting files');
filestore.inspect(that.store,
function inspectCallback(error) {
if (error) {
sys.log('Could not inspect torrent files ' + error);
} else {
sys.log('finished inspecting files.');
listener.create(that.listenerPort, [that.metaInfo.info_hash]);
that.trackerClient = tracker.create(that.metaInfo);
that.pingTracker();
}
});
}
});
}
};
}

exports.startTorrent = startTorrent;
exports.create = create;

0 comments on commit 37936df

Please sign in to comment.