socks-client.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. var net = require('net');
  2. var ip = require('ip');
  3. var SmartBuffer = require('smart-buffer');
  4. (function () {
  5. var COMMAND = {
  6. Connect: 0x01,
  7. Bind: 0x02,
  8. Associate: 0x03
  9. };
  10. var SOCKS4_RESPONSE = {
  11. Granted: 0x5A,
  12. Failed: 0x5B,
  13. Rejected: 0x5C,
  14. RejectedIdent: 0x5D
  15. };
  16. var SOCKS5_AUTH = {
  17. NoAuth: 0x00,
  18. GSSApi: 0x01,
  19. UserPass: 0x02
  20. };
  21. var SOCKS5_RESPONSE = {
  22. Granted: 0x00,
  23. Failure: 0x01,
  24. NotAllowed: 0x02,
  25. NetworkUnreachable: 0x03,
  26. HostUnreachable: 0x04,
  27. ConnectionRefused: 0x05,
  28. TTLExpired: 0x06,
  29. CommandNotSupported: 0x07,
  30. AddressNotSupported: 0x08
  31. };
  32. exports.createConnection = function (options, callback) {
  33. var socket = new net.Socket(), finished = false, buff = new SmartBuffer();
  34. // Defaults
  35. options.timeout = options.timeout || 10000;
  36. options.proxy.command = commandFromString(options.proxy.command);
  37. options.proxy.userid = options.proxy.userid || "";
  38. var auth = options.proxy.authentication || {};
  39. auth.username = auth.username || "";
  40. auth.password = auth.password || "";
  41. options.proxy.authentication = auth;
  42. // Connect & negotiation timeout
  43. function onTimeout() {
  44. finish(new Error("Connection Timed Out"), socket, null, callback);
  45. }
  46. socket.setTimeout(options.timeout, onTimeout);
  47. // Socket events
  48. socket.once('close', function () {
  49. finish(new Error("Socket Closed"), socket, null, callback);
  50. });
  51. socket.once('error', function (err) {
  52. });
  53. socket.once('connect', function () {
  54. if (options.proxy.type === 4) {
  55. negotiateSocks4(options, socket, callback);
  56. } else if (options.proxy.type === 5) {
  57. negotiateSocks5(options, socket, callback);
  58. } else {
  59. throw new Error("Please specify a proxy type in options.proxy.type");
  60. }
  61. });
  62. socket.connect(options.proxy.port, options.proxy.ipaddress);
  63. // 4/4a (connect, bind) - Supports domains & ipaddress
  64. function negotiateSocks4(options, socket, callback) {
  65. buff.writeUInt8(0x04);
  66. buff.writeUInt8(options.proxy.command);
  67. buff.writeUInt16BE(options.target.port);
  68. // ipv4 or domain?
  69. if (net.isIPv4(options.target.host)) {
  70. buff.writeBuffer(ip.toBuffer(options.target.host));
  71. buff.writeStringNT(options.proxy.userid);
  72. } else {
  73. buff.writeUInt8(0x00);
  74. buff.writeUInt8(0x00);
  75. buff.writeUInt8(0x00);
  76. buff.writeUInt8(0x01);
  77. buff.writeStringNT(options.proxy.userid);
  78. buff.writeStringNT(options.target.host);
  79. }
  80. socket.once('data', receivedResponse);
  81. socket.write(buff.toBuffer());
  82. function receivedResponse(data) {
  83. socket.pause();
  84. if (data.length === 8 && data[1] === SOCKS4_RESPONSE.Granted) {
  85. if (options.proxy.command === COMMAND.Bind) {
  86. buff.clear();
  87. buff.writeBuffer(data);
  88. buff.skip(2);
  89. var info = {
  90. port: buff.readUInt16BE(),
  91. host: buff.readUInt32BE()
  92. };
  93. if (info.host === 0) {
  94. info.host = options.proxy.ipaddress;
  95. } else {
  96. info.host = ip.fromLong(info.host);
  97. }
  98. finish(null, socket, info, callback);
  99. } else {
  100. finish(null, socket, null, callback);
  101. }
  102. } else {
  103. finish(new Error("Rejected (" + data[1] + ")"), socket, null, callback);
  104. }
  105. }
  106. }
  107. // Socks 5 (connect, bind, associate) - Supports domains and ipv4, ipv6.
  108. function negotiateSocks5(options, socket, callback) {
  109. buff.writeUInt8(0x05);
  110. buff.writeUInt8(2);
  111. buff.writeUInt8(SOCKS5_AUTH.NoAuth);
  112. buff.writeUInt8(SOCKS5_AUTH.UserPass);
  113. socket.once('data', handshake);
  114. socket.write(buff.toBuffer());
  115. function handshake(data) {
  116. if (data.length !== 2) {
  117. finish(new Error("Negotiation Error"), socket, null, callback);
  118. } else if (data[0] !== 0x05) {
  119. finish(new Error("Negotiation Error (invalid version)"), socket, null, callback);
  120. } else if (data[1] === 0xFF) {
  121. finish(new Error("Negotiation Error (unacceptable authentication)"), socket, null, callback);
  122. } else {
  123. if (data[1] === SOCKS5_AUTH.NoAuth) {
  124. sendRequest();
  125. } else if (data[1] === SOCKS5_AUTH.UserPass) {
  126. sendAuthentication(options.proxy.authentication);
  127. } else {
  128. finish(new Error("Negotiation Error (unknown authentication type)"), socket, null, callback);
  129. }
  130. }
  131. }
  132. function sendAuthentication(authinfo) {
  133. buff.clear();
  134. buff.writeUInt8(0x01);
  135. buff.writeUInt8(Buffer.byteLength(authinfo.username));
  136. buff.writeString(authinfo.username);
  137. buff.writeUInt8(Buffer.byteLength(authinfo.password));
  138. buff.writeString(authinfo.password);
  139. socket.once('data', authenticationResponse);
  140. socket.write(buff.toBuffer());
  141. function authenticationResponse(data) {
  142. if (data.length === 2 && data[1] === 0x00) {
  143. sendRequest();
  144. } else {
  145. finish(new Error("Negotiation Error (authentication failed)"), socket, null, callback);
  146. }
  147. }
  148. }
  149. function sendRequest() {
  150. buff.clear();
  151. buff.writeUInt8(0x05);
  152. buff.writeUInt8(options.proxy.command);
  153. buff.writeUInt8(0x00);
  154. // ipv4, ipv6, domain?
  155. if (net.isIPv4(options.target.host)) {
  156. buff.writeUInt8(0x01);
  157. buff.writeBuffer(ip.toBuffer(options.target.host));
  158. } else if (net.isIPv6(options.target.host)) {
  159. buff.writeUInt8(0x04);
  160. buff.writeBuffer(ip.toBuffer(options.target.host));
  161. } else {
  162. buff.writeUInt8(0x03);
  163. buff.writeUInt8(options.target.host.length);
  164. buff.writeString(options.target.host);
  165. }
  166. buff.writeUInt16BE(options.target.port);
  167. socket.once('data', receivedResponse);
  168. socket.write(buff.toBuffer());
  169. }
  170. function receivedResponse(data) {
  171. socket.pause();
  172. if (data.length < 4) {
  173. finish(new Error("Negotiation Error"), socket, null, callback);
  174. } else if (data[0] === 0x05 && data[1] === SOCKS5_RESPONSE.Granted) {
  175. if (options.proxy.command === COMMAND.Connect) {
  176. finish(null, socket, null, callback);
  177. } else if (options.proxy.command === COMMAND.Bind || options.proxy.command === COMMAND.Associate) {
  178. buff.clear();
  179. buff.writeBuffer(data);
  180. buff.skip(3);
  181. var info = {};
  182. var addrtype = buff.readUInt8();
  183. try {
  184. if (addrtype === 0x01) {
  185. info.host = buff.readUInt32BE();
  186. if (info.host === 0)
  187. info.host = options.proxy.ipaddress;
  188. else
  189. info.host = ip.fromLong(info.host);
  190. } else if (addrtype === 0x03) {
  191. var len = buff.readUInt8();
  192. info.host = buff.readString(len);
  193. } else if (addrtype === 0x04) {
  194. info.host = buff.readBuffer(16);
  195. } else {
  196. finish(new Error("Negotiation Error (invalid host address)"), socket, null, callback);
  197. }
  198. info.port = buff.readUInt16BE();
  199. finish(null, socket, info, callback);
  200. } catch (ex) {
  201. finish(new Error("Negotiation Error (missing data)"), socket, null, callback);
  202. }
  203. }
  204. } else {
  205. finish(new Error("Negotiation Error (" + data[1] + ")"), socket, null, callback);
  206. }
  207. }
  208. }
  209. function finish(err, socket, info, callback) {
  210. socket.setTimeout(0, onTimeout);
  211. if (!finished) {
  212. finished = true;
  213. if (buff instanceof SmartBuffer)
  214. buff.destroy();
  215. if (err && socket instanceof net.Socket) {
  216. socket.removeAllListeners('close');
  217. socket.removeAllListeners('timeout');
  218. socket.removeAllListeners('data');
  219. socket.destroy();
  220. socket = null;
  221. }
  222. callback(err, socket, info);
  223. }
  224. }
  225. function commandFromString(str) {
  226. var result = COMMAND.Connect;
  227. if (str === "connect") {
  228. result = COMMAND.Connect;
  229. } else if (str === 'associate') {
  230. result = COMMAND.Associate;
  231. } else if (str === 'bind') {
  232. result = COMMAND.Bind;
  233. }
  234. return result;
  235. }
  236. };
  237. exports.createUDPFrame = function (target, data, frame) {
  238. var buff = new SmartBuffer();
  239. buff.writeUInt16BE(0);
  240. buff.writeUInt8(frame || 0x00);
  241. if (net.isIPv4(target.host)) {
  242. buff.writeUInt8(0x01);
  243. buff.writeUInt32BE(ip.toLong(target.host));
  244. } else if (net.isIPv6(target.host)) {
  245. buff.writeUInt8(0x04);
  246. buff.writeBuffer(ip.toBuffer(target.host));
  247. } else {
  248. buff.writeUInt8(0x03);
  249. buff.writeUInt8(Buffer.byteLength(target.host));
  250. buff.writeString(target.host);
  251. }
  252. buff.writeUInt16BE(target.port);
  253. buff.writeBuffer(data);
  254. return buff.toBuffer();
  255. };
  256. })();