"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SocksProxyAgent = void 0; const socks_1 = require("socks"); const agent_base_1 = require("agent-base"); const debug_1 = __importDefault(require("debug")); const dns = __importStar(require("dns")); const net = __importStar(require("net")); const tls = __importStar(require("tls")); const url_1 = require("url"); const debug = (0, debug_1.default)('socks-proxy-agent'); function parseSocksURL(url) { let lookup = false; let type = 5; const host = url.hostname; // From RFC 1928, Section 3: https://tools.ietf.org/html/rfc1928#section-3 // "The SOCKS service is conventionally located on TCP port 1080" const port = parseInt(url.port, 10) || 1080; // figure out if we want socks v4 or v5, based on the "protocol" used. // Defaults to 5. switch (url.protocol.replace(':', '')) { case 'socks4': lookup = true; type = 4; break; // pass through case 'socks4a': type = 4; break; case 'socks5': lookup = true; type = 5; break; // pass through case 'socks': // no version specified, default to 5h type = 5; break; case 'socks5h': type = 5; break; default: throw new TypeError(`A "socks" protocol must be specified! Got: ${String(url.protocol)}`); } const proxy = { host, port, type, }; if (url.username) { Object.defineProperty(proxy, 'userId', { value: decodeURIComponent(url.username), enumerable: false, }); } if (url.password != null) { Object.defineProperty(proxy, 'password', { value: decodeURIComponent(url.password), enumerable: false, }); } return { lookup, proxy }; } class SocksProxyAgent extends agent_base_1.Agent { constructor(uri, opts) { super(opts); const url = typeof uri === 'string' ? new url_1.URL(uri) : uri; const { proxy, lookup } = parseSocksURL(url); this.shouldLookup = lookup; this.proxy = proxy; this.timeout = opts?.timeout ?? null; this.socketOptions = opts?.socketOptions ?? null; } /** * Initiates a SOCKS connection to the specified SOCKS proxy server, * which in turn connects to the specified remote host and port. */ async connect(req, opts) { const { shouldLookup, proxy, timeout } = this; if (!opts.host) { throw new Error('No `host` defined!'); } let { host } = opts; const { port, lookup: lookupFn = dns.lookup } = opts; if (shouldLookup) { // Client-side DNS resolution for "4" and "5" socks proxy versions. host = await new Promise((resolve, reject) => { // Use the request's custom lookup, if one was configured: lookupFn(host, {}, (err, res) => { if (err) { reject(err); } else { resolve(res); } }); }); } const socksOpts = { proxy, destination: { host, port: typeof port === 'number' ? port : parseInt(port, 10), }, command: 'connect', timeout: timeout ?? undefined, // @ts-expect-error the type supplied by socks for socket_options is wider // than necessary since socks will always override the host and port socket_options: this.socketOptions ?? undefined, }; const cleanup = (tlsSocket) => { req.destroy(); socket.destroy(); if (tlsSocket) tlsSocket.destroy(); }; debug('Creating socks proxy connection: %o', socksOpts); const { socket } = await socks_1.SocksClient.createConnection(socksOpts); debug('Successfully created socks proxy connection'); if (timeout !== null) { socket.setTimeout(timeout); socket.on('timeout', () => cleanup()); } if (opts.secureEndpoint) { // The proxy is connecting to a TLS server, so upgrade // this socket connection to a TLS connection. debug('Upgrading socket connection to TLS'); const servername = opts.servername || opts.host; const tlsSocket = tls.connect({ ...omit(opts, 'host', 'path', 'port'), socket, servername: net.isIP(servername) ? undefined : servername, }); tlsSocket.once('error', (error) => { debug('Socket TLS error', error.message); cleanup(tlsSocket); }); return tlsSocket; } return socket; } } SocksProxyAgent.protocols = [ 'socks', 'socks4', 'socks4a', 'socks5', 'socks5h', ]; exports.SocksProxyAgent = SocksProxyAgent; function omit(obj, ...keys) { const ret = {}; let key; for (key in obj) { if (!keys.includes(key)) { ret[key] = obj[key]; } } return ret; } //# sourceMappingURL=index.js.map