You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 3.6KB

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