mini-tor-js/src/index.js
2021-01-11 21:33:45 -08:00

161 lines
5.1 KiB
JavaScript

const fs = require("fs");
const path = require("path");
const url = require("url");
const util = require("util");
const Consensus = require("./tor/Consensus");
const OR = require("./tor/OnionRouter");
const Socket = require("./tor/Socket");
const { get } = require('./utils/http');
const LogLevels = {
off: 0,
error: 1,
warn: 2,
info: 3,
debug: 4
}
let logLevel = LogLevels.error;
function timestamp() {
const d = new Date().toISOString();
return [d.substr(5, 5), d.substr(11, 8)].join(' ')
}
class Logger {
debug(...args) {
if (LogLevels.debug > logLevel) return;
const [format, ...rest] = args;
console.debug(timestamp(), "D", util.format(format, ...rest));
}
info(...args) {
if (LogLevels.info > logLevel) return;
const [format, ...rest] = args;
console.debug(timestamp(), "I", util.format(format, ...rest));
}
warn(...args) {
if (LogLevels.warn > logLevel) return;
const [format, ...rest] = args;
console.debug(timestamp(), "W", util.format(format, ...rest));
}
error(...args) {
if (LogLevels.error > logLevel) return;
const [format, ...rest] = args;
console.debug(timestamp(), "E", util.format(format, ...rest));
}
}
(async function main(args) {
let nb_hops = 3;
let arg_url = '';
for (const arg of args) {
if (arg === '-v') {
logLevel = LogLevels.warn;
} else if (arg === '-vv') {
logLevel = LogLevels.info;
} else if (arg === '-vvv') {
logLevel = LogLevels.debug;
} else if (arg.length === 1 && arg[0] >= '0' && arg[0] <= '9') {
nb_hops = parseInt(arg, 10);
} else {
arg_url = arg;
}
}
if (!arg_url) {
console.log('Wrong usage\nUsage: %s [-v|-vv|-vvv] [nb_hops] url');
return;
}
const uri = url.parse(arg_url);
if (!uri.port) {
uri.port = (uri.protocol === 'https:' ? 443 : 80).toString();
}
const logger = new Logger();
/** @type Socket */
let socket;
/** @type Circuit */
let circuit;
try {
const home = process.env.HOME || process.env.USERPROFILE;
const app_cache_path = path.join(home, '.cache', 'mini-tor-js');
const cached_consensus_path = path.join(app_cache_path, 'cached-consensus')
fs.mkdirSync(app_cache_path, { recursive: true });
// Load consensus
const consensus = new Consensus(logger);
await consensus.fetch(cached_consensus_path);
consensus.set_allowed_dir_ports(80, 443);
// Connect relays path
const hop_count = () => circuit ? circuit.node_list.length : 0;
const forbidden_routers = [];
while(hop_count() < nb_hops) {
if (hop_count() === 0) { // Entry
// Connect to first OR
const entry_or = consensus.get_random_onion_router_by_criteria({
or_ports: [80, 443],
flags: OR.valid | OR.running | OR.fast,
forbidden_routers
});
socket = new Socket(logger);
await socket.connect(entry_or);
if (!socket.is_connected()) {
logger.error("Could not connect to", entry_or.name);
return;
}
circuit = await socket.create_circuit();
if (!circuit || hop_count() < 1) {
logger.error("Could not create circuit to", entry_or.name);
await socket.close();
} else {
forbidden_routers.push(entry_or.identity_fingerprint.toString('hex'));
logger.info("Connected to", entry_or.name);
}
} else if (hop_count() === nb_hops - 1) { // Exit
const exit_or = consensus.get_random_onion_router_by_criteria({
flags: OR.valid | OR.running | OR.fast | OR.exit,
forbidden_routers
});
const before = hop_count();
await circuit.extend(exit_or, 'ntor');
if (hop_count() === before) {
logger.error("Could not create circuit to", exit_or.name);
} else {
logger.info("Extended to exit", exit_or.name);
}
} else { // Middle(s)
const relay_or = consensus.get_random_onion_router_by_criteria({
flags: OR.valid | OR.running | OR.fast,
forbidden_routers
});
const before = hop_count();
await circuit.extend(relay_or, 'ntor');
if (hop_count() === before) {
logger.error("Could not extend circuit to", relay_or.name);
} else {
forbidden_routers.push(relay_or.identity_fingerprint.toString('hex'));
logger.info("Extended to", relay_or.name);
}
}
}
/** @type TorStream */
let tor_stream;
if (uri.hostname.endsWith('.onion')) {
const onion = uri.hostname.substr(0, uri.hostname.length - 6);
tor_stream = await circuit.create_onion_stream(onion, uri.port);
} else {
tor_stream = await circuit.create_stream(uri.hostname, uri.port);
}
if (!tor_stream) {
logger.error("Could not create stream to", uri.href);
return;
}
const stream = tor_stream.get_stream();
const result = await get(uri.protocol, uri.hostname, uri.port, uri.path, stream);
console.log(result);
} catch (err) {
logger.error(err);
} finally {
circuit && await circuit.destroy();
socket && await socket.close();
}
})(process.argv.slice(2));