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