1# socks [![Build Status](https://travis-ci.org/JoshGlazebrook/socks.svg?branch=master)](https://travis-ci.org/JoshGlazebrook/socks) [![Coverage Status](https://coveralls.io/repos/github/JoshGlazebrook/socks/badge.svg?branch=master)](https://coveralls.io/github/JoshGlazebrook/socks?branch=v2) 2 3Fully featured SOCKS proxy client supporting SOCKSv4, SOCKSv4a, and SOCKSv5. Includes Bind and Associate functionality. 4 5### Features 6 7* Supports SOCKS v4, v4a, and v5 protocols. 8* Supports the CONNECT, BIND, and ASSOCIATE commands. 9* Supports callbacks, promises, and events for proxy connection creation async flow control. 10* Supports proxy chaining (CONNECT only). 11* Supports user/pass authentication. 12* Built in UDP frame creation & parse functions. 13* Created with TypeScript, type definitions are provided. 14 15### Requirements 16 17* Node.js v6.0+ (Please use [v1](https://github.com/JoshGlazebrook/socks/tree/82d83923ad960693d8b774cafe17443ded7ed584) for older versions of Node.js) 18 19### Looking for v1? 20* Docs for v1 are available [here](https://github.com/JoshGlazebrook/socks/tree/82d83923ad960693d8b774cafe17443ded7ed584) 21 22## Installation 23 24`yarn add socks` 25 26or 27 28`npm install --save socks` 29 30## Usage 31 32```typescript 33// TypeScript 34import { SocksClient, SocksClientOptions, SocksClientChainOptions } from 'socks'; 35 36// ES6 JavaScript 37import { SocksClient } from 'socks'; 38 39// Legacy JavaScript 40const SocksClient = require('socks').SocksClient; 41``` 42 43## Quick Start Example 44 45Connect to github.com (192.30.253.113) on port 80, using a SOCKS proxy. 46 47```javascript 48const options = { 49 proxy: { 50 host: '159.203.75.200', // ipv4 or ipv6 or hostname 51 port: 1080, 52 type: 5 // Proxy version (4 or 5) 53 }, 54 55 command: 'connect', // SOCKS command (createConnection factory function only supports the connect command) 56 57 destination: { 58 host: '192.30.253.113', // github.com (hostname lookups are supported with SOCKS v4a and 5) 59 port: 80 60 } 61}; 62 63// Async/Await 64try { 65 const info = await SocksClient.createConnection(options); 66 67 console.log(info.socket); 68 // <Socket ...> (this is a raw net.Socket that is established to the destination host through the given proxy server) 69} catch (err) { 70 // Handle errors 71} 72 73// Promises 74SocksClient.createConnection(options) 75.then(info => { 76 console.log(info.socket); 77 // <Socket ...> (this is a raw net.Socket that is established to the destination host through the given proxy server) 78}) 79.catch(err => { 80 // Handle errors 81}); 82 83// Callbacks 84SocksClient.createConnection(options, (err, info) => { 85 if (!err) { 86 console.log(info.socket); 87 // <Socket ...> (this is a raw net.Socket that is established to the destination host through the given proxy server) 88 } else { 89 // Handle errors 90 } 91}); 92``` 93 94## Chaining Proxies 95 96**Note:** Chaining is only supported when using the SOCKS connect command, and chaining can only be done through the special factory chaining function. 97 98This example makes a proxy chain through two SOCKS proxies to ip-api.com. Once the connection to the destination is established it sends an HTTP request to get a JSON response that returns ip info for the requesting ip. 99 100```javascript 101const options = { 102 destination: { 103 host: 'ip-api.com', // host names are supported with SOCKS v4a and SOCKS v5. 104 port: 80 105 }, 106 command: 'connect', // Only the connect command is supported when chaining proxies. 107 proxies: [ // The chain order is the order in the proxies array, meaning the last proxy will establish a connection to the destination. 108 { 109 host: '159.203.75.235', // ipv4, ipv6, or hostname 110 port: 1081, 111 type: 5 112 }, 113 { 114 host: '104.131.124.203', // ipv4, ipv6, or hostname 115 port: 1081, 116 type: 5 117 } 118 ] 119} 120 121// Async/Await 122try { 123 const info = await SocksClient.createConnectionChain(options); 124 125 console.log(info.socket); 126 // <Socket ...> (this is a raw net.Socket that is established to the destination host through the given proxy servers) 127 128 console.log(info.socket.remoteAddress) // The remote address of the returned socket is the first proxy in the chain. 129 // 159.203.75.235 130 131 info.socket.write('GET /json HTTP/1.1\nHost: ip-api.com\n\n'); 132 info.socket.on('data', (data) => { 133 console.log(data.toString()); // ip-api.com sees that the last proxy in the chain (104.131.124.203) is connected to it. 134 /* 135 HTTP/1.1 200 OK 136 Access-Control-Allow-Origin: * 137 Content-Type: application/json; charset=utf-8 138 Date: Sun, 24 Dec 2017 03:47:51 GMT 139 Content-Length: 300 140 141 { 142 "as":"AS14061 Digital Ocean, Inc.", 143 "city":"Clifton", 144 "country":"United States", 145 "countryCode":"US", 146 "isp":"Digital Ocean", 147 "lat":40.8326, 148 "lon":-74.1307, 149 "org":"Digital Ocean", 150 "query":"104.131.124.203", 151 "region":"NJ", 152 "regionName":"New Jersey", 153 "status":"success", 154 "timezone":"America/New_York", 155 "zip":"07014" 156 } 157 */ 158 }); 159} catch (err) { 160 // Handle errors 161} 162 163// Promises 164SocksClient.createConnectionChain(options) 165.then(info => { 166 console.log(info.socket); 167 // <Socket ...> (this is a raw net.Socket that is established to the destination host through the given proxy server) 168 169 console.log(info.socket.remoteAddress) // The remote address of the returned socket is the first proxy in the chain. 170 // 159.203.75.235 171 172 info.socket.write('GET /json HTTP/1.1\nHost: ip-api.com\n\n'); 173 info.socket.on('data', (data) => { 174 console.log(data.toString()); // ip-api.com sees that the last proxy in the chain (104.131.124.203) is connected to it. 175 /* 176 HTTP/1.1 200 OK 177 Access-Control-Allow-Origin: * 178 Content-Type: application/json; charset=utf-8 179 Date: Sun, 24 Dec 2017 03:47:51 GMT 180 Content-Length: 300 181 182 { 183 "as":"AS14061 Digital Ocean, Inc.", 184 "city":"Clifton", 185 "country":"United States", 186 "countryCode":"US", 187 "isp":"Digital Ocean", 188 "lat":40.8326, 189 "lon":-74.1307, 190 "org":"Digital Ocean", 191 "query":"104.131.124.203", 192 "region":"NJ", 193 "regionName":"New Jersey", 194 "status":"success", 195 "timezone":"America/New_York", 196 "zip":"07014" 197 } 198 */ 199 }); 200}) 201.catch(err => { 202 // Handle errors 203}); 204 205// Callbacks 206SocksClient.createConnectionChain(options, (err, info) => { 207 if (!err) { 208 console.log(info.socket); 209 // <Socket ...> (this is a raw net.Socket that is established to the destination host through the given proxy server) 210 211 console.log(info.socket.remoteAddress) // The remote address of the returned socket is the first proxy in the chain. 212 // 159.203.75.235 213 214 info.socket.write('GET /json HTTP/1.1\nHost: ip-api.com\n\n'); 215 info.socket.on('data', (data) => { 216 console.log(data.toString()); // ip-api.com sees that the last proxy in the chain (104.131.124.203) is connected to it. 217 /* 218 HTTP/1.1 200 OK 219 Access-Control-Allow-Origin: * 220 Content-Type: application/json; charset=utf-8 221 Date: Sun, 24 Dec 2017 03:47:51 GMT 222 Content-Length: 300 223 224 { 225 "as":"AS14061 Digital Ocean, Inc.", 226 "city":"Clifton", 227 "country":"United States", 228 "countryCode":"US", 229 "isp":"Digital Ocean", 230 "lat":40.8326, 231 "lon":-74.1307, 232 "org":"Digital Ocean", 233 "query":"104.131.124.203", 234 "region":"NJ", 235 "regionName":"New Jersey", 236 "status":"success", 237 "timezone":"America/New_York", 238 "zip":"07014" 239 } 240 */ 241 }); 242 } else { 243 // Handle errors 244 } 245}); 246``` 247 248## Bind Example (TCP Relay) 249 250When the bind command is sent to a SOCKS v4/v5 proxy server, the proxy server starts listening on a new TCP port and the proxy relays then remote host information back to the client. When another remote client connects to the proxy server on this port the SOCKS proxy sends a notification that an incoming connection has been accepted to the initial client and a full duplex stream is now established to the initial client and the client that connected to that special port. 251 252```javascript 253const options = { 254 proxy: { 255 host: '159.203.75.235', // ipv4, ipv6, or hostname 256 port: 1081, 257 type: 5 258 }, 259 260 command: 'bind', 261 262 // When using BIND, the destination should be the remote client that is expected to connect to the SOCKS proxy. Using 0.0.0.0 makes the Proxy accept any incoming connection on that port. 263 destination: { 264 host: '0.0.0.0', 265 port: 0 266 } 267}; 268 269// Creates a new SocksClient instance. 270const client = new SocksClient(options); 271 272// When the SOCKS proxy has bound a new port and started listening, this event is fired. 273client.on('bound', info => { 274 console.log(info.remoteHost); 275 /* 276 { 277 host: "159.203.75.235", 278 port: 57362 279 } 280 */ 281}); 282 283// When a client connects to the newly bound port on the SOCKS proxy, this event is fired. 284client.on('established', info => { 285 // info.remoteHost is the remote address of the client that connected to the SOCKS proxy. 286 console.log(info.remoteHost); 287 /* 288 host: 67.171.34.23, 289 port: 49823 290 */ 291 292 console.log(info.socket); 293 // <Socket ...> (This is a raw net.Socket that is a connection between the initial client and the remote client that connected to the proxy) 294 295 // Handle received data... 296 info.socket.on('data', data => { 297 console.log('recv', data); 298 }); 299}); 300 301// An error occurred trying to establish this SOCKS connection. 302client.on('error', err => { 303 console.error(err); 304}); 305 306// Start connection to proxy 307client.connect(); 308``` 309 310## Associate Example (UDP Relay) 311 312When the associate command is sent to a SOCKS v5 proxy server, it sets up a UDP relay that allows the client to send UDP packets to a remote host through the proxy server, and also receive UDP packet responses back through the proxy server. 313 314```javascript 315const options = { 316 proxy: { 317 host: '159.203.75.235', // ipv4, ipv6, or hostname 318 port: 1081, 319 type: 5 320 }, 321 322 command: 'associate', 323 324 // When using associate, the destination should be the remote client that is expected to send UDP packets to the proxy server to be forwarded. This should be your local ip, or optionally the wildcard address (0.0.0.0) UDP Client <-> Proxy <-> UDP Client 325 destination: { 326 host: '0.0.0.0', 327 port: 0 328 } 329}; 330 331// Create a local UDP socket for sending packets to the proxy. 332const udpSocket = dgram.createSocket('udp4'); 333udpSocket.bind(); 334 335// Listen for incoming UDP packets from the proxy server. 336udpSocket.on('message', (message, rinfo) => { 337 console.log(SocksClient.parseUDPFrame(message)); 338 /* 339 { frameNumber: 0, 340 remoteHost: { host: '165.227.108.231', port: 4444 }, // The remote host that replied with a UDP packet 341 data: <Buffer 74 65 73 74 0a> // The data 342 } 343 */ 344}); 345 346let client = new SocksClient(associateOptions); 347 348// When the UDP relay is established, this event is fired and includes the UDP relay port to send data to on the proxy server. 349client.on('established', info => { 350 console.log(info.remoteHost); 351 /* 352 { 353 host: '159.203.75.235', 354 port: 44711 355 } 356 */ 357 358 // Send 'hello' to 165.227.108.231:4444 359 const packet = SocksClient.createUDPFrame({ 360 remoteHost: { host: '165.227.108.231', port: 4444 }, 361 data: Buffer.from(line) 362 }); 363 udpSocket.send(packet, info.remoteHost.port, info.remoteHost.host); 364}); 365 366// Start connection 367client.connect(); 368``` 369 370**Note:** The associate TCP connection to the proxy must remain open for the UDP relay to work. 371 372## Additional Examples 373 374[Documentation](docs/index.md) 375 376 377## Migrating from v1 378 379Looking for a guide to migrate from v1? Look [here](docs/migratingFromV1.md) 380 381## Api Reference: 382 383**Note:** socks includes full TypeScript definitions. These can even be used without using TypeScript as most IDEs (such as VS Code) will use these type definition files for auto completion intellisense even in JavaScript files. 384 385* Class: SocksClient 386 * [new SocksClient(options[, callback])](#new-socksclientoptions) 387 * [Class Method: SocksClient.createConnection(options[, callback])](#class-method-socksclientcreateconnectionoptions-callback) 388 * [Class Method: SocksClient.createConnectionChain(options[, callback])](#class-method-socksclientcreateconnectionchainoptions-callback) 389 * [Class Method: SocksClient.createUDPFrame(options)](#class-method-socksclientcreateudpframedetails) 390 * [Class Method: SocksClient.parseUDPFrame(data)](#class-method-socksclientparseudpframedata) 391 * [Event: 'error'](#event-error) 392 * [Event: 'bound'](#event-bound) 393 * [Event: 'established'](#event-established) 394 * [client.connect()](#clientconnect) 395 * [client.socksClientOptions](#clientconnect) 396 397### SocksClient 398 399SocksClient establishes SOCKS proxy connections to remote destination hosts. These proxy connections are fully transparent to the server and once established act as full duplex streams. SOCKS v4, v4a, and v5 are supported, as well as the connect, bind, and associate commands. 400 401SocksClient supports creating connections using callbacks, promises, and async/await flow control using two static factory functions createConnection and createConnectionChain. It also internally extends EventEmitter which results in allowing event handling based async flow control. 402 403**SOCKS Compatibility Table** 404 405| Socks Version | TCP | UDP | IPv4 | IPv6 | Hostname | 406| --- | :---: | :---: | :---: | :---: | :---: | 407| SOCKS v4 | ✅ | ❌ | ✅ | ❌ | ❌ | 408| SOCKS v4a | ✅ | ❌ | ✅ | ❌ | ✅ | 409| SOCKS v5 | ✅ | ✅ | ✅ | ✅ | ✅ | 410 411### new SocksClient(options) 412 413* ```options``` {SocksClientOptions} - An object describing the SOCKS proxy to use, the command to send and establish, and the destination host to connect to. 414 415### SocksClientOptions 416 417```typescript 418{ 419 proxy: { 420 host: '159.203.75.200', // ipv4, ipv6, or hostname 421 port: 1080, 422 type: 5 // Proxy version (4 or 5). For v4a, just use 4. 423 424 // Optional fields 425 userId: 'some username', // Used for SOCKS4 userId auth, and SOCKS5 user/pass auth in conjunction with password. 426 password: 'some password' // Used in conjunction with userId for user/pass auth for SOCKS5 proxies. 427 }, 428 429 command: 'connect', // connect, bind, associate 430 431 destination: { 432 host: '192.30.253.113', // ipv4, ipv6, hostname. Hostnames work with v4a and v5. 433 port: 80 434 }, 435 436 // Optional fields 437 timeout: 30000, // How long to wait to establish a proxy connection. (defaults to 30 seconds) 438 439 set_tcp_nodelay: true // If true, will turn on the underlying sockets TCP_NODELAY option. 440} 441``` 442 443### Class Method: SocksClient.createConnection(options[, callback]) 444* ```options``` { SocksClientOptions } - An object describing the SOCKS proxy to use, the command to send and establish, and the destination host to connect to. 445* ```callback``` { Function } - Optional callback function that is called when the proxy connection is established, or an error occurs. 446* ```returns``` { Promise } - A Promise is returned that is resolved when the proxy connection is established, or rejected when an error occurs. 447 448Creates a new proxy connection through the given proxy to the given destination host. This factory function supports callbacks and promises for async flow control. 449 450**Note:** If a callback function is provided, the promise will always resolve regardless of an error occurring. Please be sure to exclusively use either promises or callbacks when using this factory function. 451 452```typescript 453const options = { 454 proxy: { 455 host: '159.203.75.200', // ipv4, ipv6, or hostname 456 port: 1080, 457 type: 5 // Proxy version (4 or 5) 458 }, 459 460 command: 'connect', // connect, bind, associate 461 462 destination: { 463 host: '192.30.253.113', // ipv4, ipv6, or hostname 464 port: 80 465 } 466} 467 468// Await/Async (uses a Promise) 469try { 470 const info = await SocksClient.createConnection(options); 471 console.log(info); 472 /* 473 { 474 socket: <Socket ...>, // Raw net.Socket 475 } 476 */ 477 / <Socket ...> (this is a raw net.Socket that is established to the destination host through the given proxy server) 478 479} catch (err) { 480 // Handle error... 481} 482 483// Promise 484SocksClient.createConnection(options) 485.then(info => { 486 console.log(info); 487 /* 488 { 489 socket: <Socket ...>, // Raw net.Socket 490 } 491 */ 492}) 493.catch(err => { 494 // Handle error... 495}); 496 497// Callback 498SocksClient.createConnection(options, (err, info) => { 499 if (!err) { 500 console.log(info); 501 /* 502 { 503 socket: <Socket ...>, // Raw net.Socket 504 } 505 */ 506 } else { 507 // Handle error... 508 } 509}); 510``` 511 512### Class Method: SocksClient.createConnectionChain(options[, callback]) 513* ```options``` { SocksClientChainOptions } - An object describing a list of SOCKS proxies to use, the command to send and establish, and the destination host to connect to. 514* ```callback``` { Function } - Optional callback function that is called when the proxy connection chain is established, or an error occurs. 515* ```returns``` { Promise } - A Promise is returned that is resolved when the proxy connection chain is established, or rejected when an error occurs. 516 517Creates a new proxy connection chain through a list of at least two SOCKS proxies to the given destination host. This factory method supports callbacks and promises for async flow control. 518 519**Note:** If a callback function is provided, the promise will always resolve regardless of an error occurring. Please be sure to exclusively use either promises or callbacks when using this factory function. 520 521**Note:** At least two proxies must be provided for the chain to be established. 522 523```typescript 524const options = { 525 proxies: [ // The chain order is the order in the proxies array, meaning the last proxy will establish a connection to the destination. 526 { 527 host: '159.203.75.235', // ipv4, ipv6, or hostname 528 port: 1081, 529 type: 5 530 }, 531 { 532 host: '104.131.124.203', // ipv4, ipv6, or hostname 533 port: 1081, 534 type: 5 535 } 536 ] 537 538 command: 'connect', // Only connect is supported in chaining mode. 539 540 destination: { 541 host: '192.30.253.113', // ipv4, ipv6, hostname 542 port: 80 543 } 544} 545``` 546 547### Class Method: SocksClient.createUDPFrame(details) 548* ```details``` { SocksUDPFrameDetails } - An object containing the remote host, frame number, and frame data to use when creating a SOCKS UDP frame packet. 549* ```returns``` { Buffer } - A Buffer containing all of the UDP frame data. 550 551Creates a SOCKS UDP frame relay packet that is sent and received via a SOCKS proxy when using the associate command for UDP packet forwarding. 552 553**SocksUDPFrameDetails** 554 555```typescript 556{ 557 frameNumber: 0, // The frame number (used for breaking up larger packets) 558 559 remoteHost: { // The remote host to have the proxy send data to, or the remote host that send this data. 560 host: '1.2.3.4', 561 port: 1234 562 }, 563 564 data: <Buffer 01 02 03 04...> // A Buffer instance of data to include in the packet (actual data sent to the remote host) 565} 566interface SocksUDPFrameDetails { 567 // The frame number of the packet. 568 frameNumber?: number; 569 570 // The remote host. 571 remoteHost: SocksRemoteHost; 572 573 // The packet data. 574 data: Buffer; 575} 576``` 577 578### Class Method: SocksClient.parseUDPFrame(data) 579* ```data``` { Buffer } - A Buffer instance containing SOCKS UDP frame data to parse. 580* ```returns``` { SocksUDPFrameDetails } - An object containing the remote host, frame number, and frame data of the SOCKS UDP frame. 581 582```typescript 583const frame = SocksClient.parseUDPFrame(data); 584console.log(frame); 585/* 586{ 587 frameNumber: 0, 588 remoteHost: { 589 host: '1.2.3.4', 590 port: 1234 591 }, 592 data: <Buffer 01 02 03 04 ...> 593} 594*/ 595``` 596 597Parses a Buffer instance and returns the parsed SocksUDPFrameDetails object. 598 599## Event: 'error' 600* ```err``` { SocksClientError } - An Error object containing an error message and the original SocksClientOptions. 601 602This event is emitted if an error occurs when trying to establish the proxy connection. 603 604## Event: 'bound' 605* ```info``` { SocksClientBoundEvent } An object containing a Socket and SocksRemoteHost info. 606 607This event is emitted when using the BIND command on a remote SOCKS proxy server. This event indicates the proxy server is now listening for incoming connections on a specified port. 608 609**SocksClientBoundEvent** 610```typescript 611{ 612 socket: net.Socket, // The underlying raw Socket 613 remoteHost: { 614 host: '1.2.3.4', // The remote host that is listening (usually the proxy itself) 615 port: 4444 // The remote port the proxy is listening on for incoming connections (when using BIND). 616 } 617} 618``` 619 620## Event: 'established' 621* ```info``` { SocksClientEstablishedEvent } An object containing a Socket and SocksRemoteHost info. 622 623This event is emitted when the following conditions are met: 6241. When using the CONNECT command, and a proxy connection has been established to the remote host. 6252. When using the BIND command, and an incoming connection has been accepted by the proxy and a TCP relay has been established. 6263. When using the ASSOCIATE command, and a UDP relay has been established. 627 628When using BIND, 'bound' is first emitted to indicate the SOCKS server is waiting for an incoming connection, and provides the remote port the SOCKS server is listening on. 629 630When using ASSOCIATE, 'established' is emitted with the remote UDP port the SOCKS server is accepting UDP frame packets on. 631 632**SocksClientEstablishedEvent** 633```typescript 634{ 635 socket: net.Socket, // The underlying raw Socket 636 remoteHost: { 637 host: '1.2.3.4', // The remote host that is listening (usually the proxy itself) 638 port: 52738 // The remote port the proxy is listening on for incoming connections (when using BIND). 639 } 640} 641``` 642 643## client.connect() 644 645Starts connecting to the remote SOCKS proxy server to establish a proxy connection to the destination host. 646 647## client.socksClientOptions 648* ```returns``` { SocksClientOptions } The options that were passed to the SocksClient. 649 650Gets the options that were passed to the SocksClient when it was created. 651 652 653**SocksClientError** 654```typescript 655{ // Subclassed from Error. 656 message: 'An error has occurred', 657 options: { 658 // SocksClientOptions 659 } 660} 661``` 662 663# Further Reading: 664 665Please read the SOCKS 5 specifications for more information on how to use BIND and Associate. 666http://www.ietf.org/rfc/rfc1928.txt 667 668# License 669 670This work is licensed under the [MIT license](http://en.wikipedia.org/wiki/MIT_License). 671