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));