Created
October 10, 2013 12:55
-
-
Save nkcmr/6917870 to your computer and use it in GitHub Desktop.
It took 2 days for me to figure out how to process torrent files in JavaScript. So I am publishing my findings. Fully commented and helpful links are dropped in. I hope if you find this it saves you a few hours of hair-pulling and cursing at your screen. Spoiler alert: If you don't know a whole lot about encodings, you're gonna have a bad time ;-;
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var debug = require("debug")("torrent:read"); //npm install debug | |
var bencode = require("bencode"); //npm install bencode - https://en.wikipedia.org/wiki/Bencode | |
var fs = require("fs"); | |
var crypto = require("crypto"); | |
var _ = require("underscore"); //npm install underscore | |
var percent_encoding = { | |
encode: function(buffer) { | |
var ret = "", a2z, AtoZ, zero2nine, other_valid_symbols, all_unreserved_symbols; | |
// ASCII table reference - http://www.ascii-code.com/ | |
// Percent encoding reference - https://en.wikipedia.org/wiki/Percent-encoding | |
// ASCII codes for lower case "a" to lower case "z" is 97 - 122 | |
a2z = _.range(97, 123); | |
// ASCII codes for upper case "A" to upper case "Z" is 65 - 90 | |
AtoZ = _.range(65, 91); | |
// Other unreserved symbols are [ - ] , [ _ ] , [ ~ ] and [ . ] | |
other_valid_symbols = [45, 95, 126, 46]; | |
// ASCII codes for 0 to 9 are 48 - 57 | |
zero2nine = _.range(48, 58); | |
// Combine all of those ranges into one handy-dandy array | |
all_unreserved_symbols = _.union(a2z, AtoZ, other_valid_symbols, zero2nine); | |
// Make sure variable being passed is a buffer | |
if (Buffer.isBuffer(buffer)) { | |
// Lets parse across the buffer | |
for (var i = 0; i < buffer.length; i++) { | |
// Store the bytecode | |
var bytecode = buffer[i]; | |
// Is the bytecode NOT in unreserved (Percent encoding) space? | |
if ( ! _.contains(all_unreserved_symbols, bytecode)) { | |
// bytecode is not in unreserved space - convert to hex value and append a '%' | |
// convert base-10 integer to its hexedecimal value - i.e 209 would become "d1" | |
var hex_value = buffer[i].toString(16); | |
// apply a "%" i.e. "d1" becomes "%d1" | |
var url_entity = "%" + hex_value; | |
//append to the return string | |
ret += url_entity; | |
} else { | |
//bytecode is in unreserved space - convert to ASCII representation | |
// Store the single bytecode into a very tiny buffer | |
var buff = new Buffer([bytecode]); | |
// Convert the buffer into ASCII code | |
var ascii_code = buff.toString("ascii"); | |
//Append the friendly character to the return string | |
ret += ascii_code; | |
} | |
} | |
return ret; | |
} else { | |
throw new Error("First parameter must be a buffer!"); | |
} | |
} | |
}; | |
//Official torrent info_hash calculation process! | |
// Step 1 - read in the raw torrent file | |
var torrent_file = fs.readFileSync("~/Desktop/ubuntu-12.04.torrent"); | |
// Step 2 - convert the raw torrent file from bencode to a json object | |
var torrent_metadata = bencode.decode(torrent_file); | |
// Step 3 - grab the VALUE of the info dictionary/object and then re-encode it to bencode | |
var info_section = bencode.encode(torrent_metadata.info); | |
// Very helpful information about BitTorrent files and Trackers and other stuff - https://wiki.theory.org/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol | |
// Step 4 - calculate the sha1 hash of the bencode-ed info dictionary/object | |
var sha1_hash = crypto.createHash("sha1").update(info_section).digest(); | |
// Step 5 - create a new buffer of the calculated sha1 hash of the info dictionary and don't apply any encoding | |
var sha1_buffer = new Buffer(sha1_hash); | |
// Step 6 - convert bytecodes that are in unreserved (precent encoding) space into their appropriate ascii code and reserved entities to a %[hex_value] | |
var info_hash = percent_encoding.encode(sha1_buffer); | |
debug("20-byte sha1 hash buffer: %j", sha1_buffer); | |
debug("40-byte sha1 hash value: %s", sha1_buffer.toString("hex")) | |
debug("Torrent URL-encoded info_hash: %s", percent_encoding.encode(sha1_buffer)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment