summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/npm-registry-fetch/node_modules/socks-proxy-agent/index.js
blob: 3dac18d5645438c42bfc6e009093397e9d57fa07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
 * Module dependencies.
 */

var tls; // lazy-loaded...
var url = require('url');
var dns = require('dns');
var Agent = require('agent-base');
var SocksClient = require('socks');
var inherits = require('util').inherits;

/**
 * Module exports.
 */

module.exports = SocksProxyAgent;

/**
 * The `SocksProxyAgent`.
 *
 * @api public
 */

function SocksProxyAgent(opts) {
  if (!(this instanceof SocksProxyAgent)) return new SocksProxyAgent(opts);
  if ('string' == typeof opts) opts = url.parse(opts);
  if (!opts)
    throw new Error(
      'a SOCKS proxy server `host` and `port` must be specified!'
    );
  Agent.call(this, opts);

  var proxy = Object.assign({}, opts);

  // prefer `hostname` over `host`, because of `url.parse()`
  proxy.host = proxy.hostname || proxy.host;

  // SOCKS doesn't *technically* have a default port, but this is
  // the same default that `curl(1)` uses
  proxy.port = +proxy.port || 1080;

  if (proxy.host && proxy.path) {
    // if both a `host` and `path` are specified then it's most likely the
    // result of a `url.parse()` call... we need to remove the `path` portion so
    // that `net.connect()` doesn't attempt to open that as a unix socket file.
    delete proxy.path;
    delete proxy.pathname;
  }

  // figure out if we want socks v4 or v5, based on the "protocol" used.
  // Defaults to 5.
  proxy.lookup = false;
  switch (proxy.protocol) {
    case 'socks4:':
      proxy.lookup = true;
    // pass through
    case 'socks4a:':
      proxy.version = 4;
      break;
    case 'socks5:':
      proxy.lookup = true;
    // pass through
    case 'socks:': // no version specified, default to 5h
    case 'socks5h:':
      proxy.version = 5;
      break;
    default:
      throw new TypeError(
        'A "socks" protocol must be specified! Got: ' + proxy.protocol
      );
  }

  if (proxy.auth) {
    var auth = proxy.auth.split(':');
    proxy.authentication = { username: auth[0], password: auth[1] };
    proxy.userid = auth[0];
  }
  this.proxy = proxy;
}
inherits(SocksProxyAgent, Agent);

/**
 * Initiates a SOCKS connection to the specified SOCKS proxy server,
 * which in turn connects to the specified remote host and port.
 *
 * @api public
 */

SocksProxyAgent.prototype.callback = function connect(req, opts, fn) {
  var proxy = this.proxy;

  // called once the SOCKS proxy has connected to the specified remote endpoint
  function onhostconnect(err, socket) {
    if (err) return fn(err);
    var s = socket;
    if (opts.secureEndpoint) {
      // since the proxy is connecting to an SSL server, we have
      // to upgrade this socket connection to an SSL connection
      if (!tls) tls = require('tls');
      opts.socket = socket;
      opts.servername = opts.host;
      opts.host = null;
      opts.hostname = null;
      opts.port = null;
      s = tls.connect(opts);
    }
    socket.resume();
    fn(null, s);
  }

  // called for the `dns.lookup()` callback
  function onlookup(err, ip) {
    if (err) return fn(err);
    options.target.host = ip;
    SocksClient.createConnection(options, onhostconnect);
  }

  var options = {
    proxy: {
      ipaddress: proxy.host,
      port: +proxy.port,
      type: proxy.version
    },
    target: {
      port: +opts.port
    },
    command: 'connect'
  };
  if (proxy.authentication) {
    options.proxy.authentication = proxy.authentication;
    options.proxy.userid = proxy.userid;
  }

  if (proxy.lookup) {
    // client-side DNS resolution for "4" and "5" socks proxy versions
    dns.lookup(opts.host, onlookup);
  } else {
    // proxy hostname DNS resolution for "4a" and "5h" socks proxy servers
    onlookup(null, opts.host);
  }
}