• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * Module dependencies.
3 */
4
5var tls; // lazy-loaded...
6var url = require('url');
7var dns = require('dns');
8var Agent = require('agent-base');
9var SocksClient = require('socks').SocksClient;
10var inherits = require('util').inherits;
11
12/**
13 * Module exports.
14 */
15
16module.exports = SocksProxyAgent;
17
18/**
19 * The `SocksProxyAgent`.
20 *
21 * @api public
22 */
23
24function SocksProxyAgent(opts) {
25  if (!(this instanceof SocksProxyAgent)) return new SocksProxyAgent(opts);
26  if ('string' == typeof opts) opts = url.parse(opts);
27  if (!opts)
28    throw new Error(
29      'a SOCKS proxy server `host` and `port` must be specified!'
30    );
31  Agent.call(this, opts);
32
33  var proxy = Object.assign({}, opts);
34
35  // prefer `hostname` over `host`, because of `url.parse()`
36  proxy.host = proxy.hostname || proxy.host;
37
38  // SOCKS doesn't *technically* have a default port, but this is
39  // the same default that `curl(1)` uses
40  proxy.port = +proxy.port || 1080;
41
42  if (proxy.host && proxy.path) {
43    // if both a `host` and `path` are specified then it's most likely the
44    // result of a `url.parse()` call... we need to remove the `path` portion so
45    // that `net.connect()` doesn't attempt to open that as a unix socket file.
46    delete proxy.path;
47    delete proxy.pathname;
48  }
49
50  // figure out if we want socks v4 or v5, based on the "protocol" used.
51  // Defaults to 5.
52  proxy.lookup = false;
53  switch (proxy.protocol) {
54    case 'socks4:':
55      proxy.lookup = true;
56    // pass through
57    case 'socks4a:':
58      proxy.version = 4;
59      break;
60    case 'socks5:':
61      proxy.lookup = true;
62    // pass through
63    case 'socks:': // no version specified, default to 5h
64    case 'socks5h:':
65      proxy.version = 5;
66      break;
67    default:
68      throw new TypeError(
69        'A "socks" protocol must be specified! Got: ' + proxy.protocol
70      );
71  }
72
73  if (proxy.auth) {
74    var auth = proxy.auth.split(':');
75    proxy.authentication = { username: auth[0], password: auth[1] };
76    proxy.userid = auth[0];
77  }
78  this.proxy = proxy;
79}
80inherits(SocksProxyAgent, Agent);
81
82/**
83 * Initiates a SOCKS connection to the specified SOCKS proxy server,
84 * which in turn connects to the specified remote host and port.
85 *
86 * @api public
87 */
88
89SocksProxyAgent.prototype.callback = function connect(req, opts, fn) {
90  var proxy = this.proxy;
91
92  // called once the SOCKS proxy has connected to the specified remote endpoint
93  function onhostconnect(err, result) {
94    if (err) return fn(err);
95
96    var socket = result.socket;
97
98    var s = socket;
99    if (opts.secureEndpoint) {
100      // since the proxy is connecting to an SSL server, we have
101      // to upgrade this socket connection to an SSL connection
102      if (!tls) tls = require('tls');
103      opts.socket = socket;
104      opts.servername = opts.host;
105      opts.host = null;
106      opts.hostname = null;
107      opts.port = null;
108      s = tls.connect(opts);
109    }
110
111    fn(null, s);
112  }
113
114  // called for the `dns.lookup()` callback
115  function onlookup(err, ip) {
116    if (err) return fn(err);
117    options.destination.host = ip;
118    SocksClient.createConnection(options, onhostconnect);
119  }
120
121  var options = {
122    proxy: {
123      ipaddress: proxy.host,
124      port: +proxy.port,
125      type: proxy.version
126    },
127    destination: {
128      port: +opts.port
129    },
130    command: 'connect'
131  };
132
133  if (proxy.authentication) {
134    options.proxy.userId = proxy.userid;
135    options.proxy.password = proxy.authentication.password;
136  }
137
138  if (proxy.lookup) {
139    // client-side DNS resolution for "4" and "5" socks proxy versions
140    dns.lookup(opts.host, onlookup);
141  } else {
142    // proxy hostname DNS resolution for "4a" and "5h" socks proxy servers
143    onlookup(null, opts.host);
144  }
145}
146