161 lines
5.1 KiB
JavaScript
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));
|