1"use strict"; 2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 return new (P || (P = Promise))(function (resolve, reject) { 5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 }); 10}; 11Object.defineProperty(exports, "__esModule", { value: true }); 12exports.SocksClientError = exports.SocksClient = void 0; 13const events_1 = require("events"); 14const net = require("net"); 15const ip = require("ip"); 16const smart_buffer_1 = require("smart-buffer"); 17const constants_1 = require("../common/constants"); 18const helpers_1 = require("../common/helpers"); 19const receivebuffer_1 = require("../common/receivebuffer"); 20const util_1 = require("../common/util"); 21Object.defineProperty(exports, "SocksClientError", { enumerable: true, get: function () { return util_1.SocksClientError; } }); 22class SocksClient extends events_1.EventEmitter { 23 constructor(options) { 24 super(); 25 this.options = Object.assign({}, options); 26 // Validate SocksClientOptions 27 (0, helpers_1.validateSocksClientOptions)(options); 28 // Default state 29 this.setState(constants_1.SocksClientState.Created); 30 } 31 /** 32 * Creates a new SOCKS connection. 33 * 34 * Note: Supports callbacks and promises. Only supports the connect command. 35 * @param options { SocksClientOptions } Options. 36 * @param callback { Function } An optional callback function. 37 * @returns { Promise } 38 */ 39 static createConnection(options, callback) { 40 return new Promise((resolve, reject) => { 41 // Validate SocksClientOptions 42 try { 43 (0, helpers_1.validateSocksClientOptions)(options, ['connect']); 44 } 45 catch (err) { 46 if (typeof callback === 'function') { 47 callback(err); 48 // eslint-disable-next-line @typescript-eslint/no-explicit-any 49 return resolve(err); // Resolves pending promise (prevents memory leaks). 50 } 51 else { 52 return reject(err); 53 } 54 } 55 const client = new SocksClient(options); 56 client.connect(options.existing_socket); 57 client.once('established', (info) => { 58 client.removeAllListeners(); 59 if (typeof callback === 'function') { 60 callback(null, info); 61 resolve(info); // Resolves pending promise (prevents memory leaks). 62 } 63 else { 64 resolve(info); 65 } 66 }); 67 // Error occurred, failed to establish connection. 68 client.once('error', (err) => { 69 client.removeAllListeners(); 70 if (typeof callback === 'function') { 71 callback(err); 72 // eslint-disable-next-line @typescript-eslint/no-explicit-any 73 resolve(err); // Resolves pending promise (prevents memory leaks). 74 } 75 else { 76 reject(err); 77 } 78 }); 79 }); 80 } 81 /** 82 * Creates a new SOCKS connection chain to a destination host through 2 or more SOCKS proxies. 83 * 84 * Note: Supports callbacks and promises. Only supports the connect method. 85 * Note: Implemented via createConnection() factory function. 86 * @param options { SocksClientChainOptions } Options 87 * @param callback { Function } An optional callback function. 88 * @returns { Promise } 89 */ 90 static createConnectionChain(options, callback) { 91 // eslint-disable-next-line no-async-promise-executor 92 return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { 93 // Validate SocksClientChainOptions 94 try { 95 (0, helpers_1.validateSocksClientChainOptions)(options); 96 } 97 catch (err) { 98 if (typeof callback === 'function') { 99 callback(err); 100 // eslint-disable-next-line @typescript-eslint/no-explicit-any 101 return resolve(err); // Resolves pending promise (prevents memory leaks). 102 } 103 else { 104 return reject(err); 105 } 106 } 107 // Shuffle proxies 108 if (options.randomizeChain) { 109 (0, util_1.shuffleArray)(options.proxies); 110 } 111 try { 112 let sock; 113 for (let i = 0; i < options.proxies.length; i++) { 114 const nextProxy = options.proxies[i]; 115 // If we've reached the last proxy in the chain, the destination is the actual destination, otherwise it's the next proxy. 116 const nextDestination = i === options.proxies.length - 1 117 ? options.destination 118 : { 119 host: options.proxies[i + 1].host || 120 options.proxies[i + 1].ipaddress, 121 port: options.proxies[i + 1].port, 122 }; 123 // Creates the next connection in the chain. 124 const result = yield SocksClient.createConnection({ 125 command: 'connect', 126 proxy: nextProxy, 127 destination: nextDestination, 128 existing_socket: sock, 129 }); 130 // If sock is undefined, assign it here. 131 sock = sock || result.socket; 132 } 133 if (typeof callback === 'function') { 134 callback(null, { socket: sock }); 135 resolve({ socket: sock }); // Resolves pending promise (prevents memory leaks). 136 } 137 else { 138 resolve({ socket: sock }); 139 } 140 } 141 catch (err) { 142 if (typeof callback === 'function') { 143 callback(err); 144 // eslint-disable-next-line @typescript-eslint/no-explicit-any 145 resolve(err); // Resolves pending promise (prevents memory leaks). 146 } 147 else { 148 reject(err); 149 } 150 } 151 })); 152 } 153 /** 154 * Creates a SOCKS UDP Frame. 155 * @param options 156 */ 157 static createUDPFrame(options) { 158 const buff = new smart_buffer_1.SmartBuffer(); 159 buff.writeUInt16BE(0); 160 buff.writeUInt8(options.frameNumber || 0); 161 // IPv4/IPv6/Hostname 162 if (net.isIPv4(options.remoteHost.host)) { 163 buff.writeUInt8(constants_1.Socks5HostType.IPv4); 164 buff.writeUInt32BE(ip.toLong(options.remoteHost.host)); 165 } 166 else if (net.isIPv6(options.remoteHost.host)) { 167 buff.writeUInt8(constants_1.Socks5HostType.IPv6); 168 buff.writeBuffer(ip.toBuffer(options.remoteHost.host)); 169 } 170 else { 171 buff.writeUInt8(constants_1.Socks5HostType.Hostname); 172 buff.writeUInt8(Buffer.byteLength(options.remoteHost.host)); 173 buff.writeString(options.remoteHost.host); 174 } 175 // Port 176 buff.writeUInt16BE(options.remoteHost.port); 177 // Data 178 buff.writeBuffer(options.data); 179 return buff.toBuffer(); 180 } 181 /** 182 * Parses a SOCKS UDP frame. 183 * @param data 184 */ 185 static parseUDPFrame(data) { 186 const buff = smart_buffer_1.SmartBuffer.fromBuffer(data); 187 buff.readOffset = 2; 188 const frameNumber = buff.readUInt8(); 189 const hostType = buff.readUInt8(); 190 let remoteHost; 191 if (hostType === constants_1.Socks5HostType.IPv4) { 192 remoteHost = ip.fromLong(buff.readUInt32BE()); 193 } 194 else if (hostType === constants_1.Socks5HostType.IPv6) { 195 remoteHost = ip.toString(buff.readBuffer(16)); 196 } 197 else { 198 remoteHost = buff.readString(buff.readUInt8()); 199 } 200 const remotePort = buff.readUInt16BE(); 201 return { 202 frameNumber, 203 remoteHost: { 204 host: remoteHost, 205 port: remotePort, 206 }, 207 data: buff.readBuffer(), 208 }; 209 } 210 /** 211 * Internal state setter. If the SocksClient is in an error state, it cannot be changed to a non error state. 212 */ 213 setState(newState) { 214 if (this.state !== constants_1.SocksClientState.Error) { 215 this.state = newState; 216 } 217 } 218 /** 219 * Starts the connection establishment to the proxy and destination. 220 * @param existingSocket Connected socket to use instead of creating a new one (internal use). 221 */ 222 connect(existingSocket) { 223 this.onDataReceived = (data) => this.onDataReceivedHandler(data); 224 this.onClose = () => this.onCloseHandler(); 225 this.onError = (err) => this.onErrorHandler(err); 226 this.onConnect = () => this.onConnectHandler(); 227 // Start timeout timer (defaults to 30 seconds) 228 const timer = setTimeout(() => this.onEstablishedTimeout(), this.options.timeout || constants_1.DEFAULT_TIMEOUT); 229 // check whether unref is available as it differs from browser to NodeJS (#33) 230 if (timer.unref && typeof timer.unref === 'function') { 231 timer.unref(); 232 } 233 // If an existing socket is provided, use it to negotiate SOCKS handshake. Otherwise create a new Socket. 234 if (existingSocket) { 235 this.socket = existingSocket; 236 } 237 else { 238 this.socket = new net.Socket(); 239 } 240 // Attach Socket error handlers. 241 this.socket.once('close', this.onClose); 242 this.socket.once('error', this.onError); 243 this.socket.once('connect', this.onConnect); 244 this.socket.on('data', this.onDataReceived); 245 this.setState(constants_1.SocksClientState.Connecting); 246 this.receiveBuffer = new receivebuffer_1.ReceiveBuffer(); 247 if (existingSocket) { 248 this.socket.emit('connect'); 249 } 250 else { 251 this.socket.connect(this.getSocketOptions()); 252 if (this.options.set_tcp_nodelay !== undefined && 253 this.options.set_tcp_nodelay !== null) { 254 this.socket.setNoDelay(!!this.options.set_tcp_nodelay); 255 } 256 } 257 // Listen for established event so we can re-emit any excess data received during handshakes. 258 this.prependOnceListener('established', (info) => { 259 setImmediate(() => { 260 if (this.receiveBuffer.length > 0) { 261 const excessData = this.receiveBuffer.get(this.receiveBuffer.length); 262 info.socket.emit('data', excessData); 263 } 264 info.socket.resume(); 265 }); 266 }); 267 } 268 // Socket options (defaults host/port to options.proxy.host/options.proxy.port) 269 getSocketOptions() { 270 return Object.assign(Object.assign({}, this.options.socket_options), { host: this.options.proxy.host || this.options.proxy.ipaddress, port: this.options.proxy.port }); 271 } 272 /** 273 * Handles internal Socks timeout callback. 274 * Note: If the Socks client is not BoundWaitingForConnection or Established, the connection will be closed. 275 */ 276 onEstablishedTimeout() { 277 if (this.state !== constants_1.SocksClientState.Established && 278 this.state !== constants_1.SocksClientState.BoundWaitingForConnection) { 279 this.closeSocket(constants_1.ERRORS.ProxyConnectionTimedOut); 280 } 281 } 282 /** 283 * Handles Socket connect event. 284 */ 285 onConnectHandler() { 286 this.setState(constants_1.SocksClientState.Connected); 287 // Send initial handshake. 288 if (this.options.proxy.type === 4) { 289 this.sendSocks4InitialHandshake(); 290 } 291 else { 292 this.sendSocks5InitialHandshake(); 293 } 294 this.setState(constants_1.SocksClientState.SentInitialHandshake); 295 } 296 /** 297 * Handles Socket data event. 298 * @param data 299 */ 300 onDataReceivedHandler(data) { 301 /* 302 All received data is appended to a ReceiveBuffer. 303 This makes sure that all the data we need is received before we attempt to process it. 304 */ 305 this.receiveBuffer.append(data); 306 // Process data that we have. 307 this.processData(); 308 } 309 /** 310 * Handles processing of the data we have received. 311 */ 312 processData() { 313 // If we have enough data to process the next step in the SOCKS handshake, proceed. 314 while (this.state !== constants_1.SocksClientState.Established && 315 this.state !== constants_1.SocksClientState.Error && 316 this.receiveBuffer.length >= this.nextRequiredPacketBufferSize) { 317 // Sent initial handshake, waiting for response. 318 if (this.state === constants_1.SocksClientState.SentInitialHandshake) { 319 if (this.options.proxy.type === 4) { 320 // Socks v4 only has one handshake response. 321 this.handleSocks4FinalHandshakeResponse(); 322 } 323 else { 324 // Socks v5 has two handshakes, handle initial one here. 325 this.handleInitialSocks5HandshakeResponse(); 326 } 327 // Sent auth request for Socks v5, waiting for response. 328 } 329 else if (this.state === constants_1.SocksClientState.SentAuthentication) { 330 this.handleInitialSocks5AuthenticationHandshakeResponse(); 331 // Sent final Socks v5 handshake, waiting for final response. 332 } 333 else if (this.state === constants_1.SocksClientState.SentFinalHandshake) { 334 this.handleSocks5FinalHandshakeResponse(); 335 // Socks BIND established. Waiting for remote connection via proxy. 336 } 337 else if (this.state === constants_1.SocksClientState.BoundWaitingForConnection) { 338 if (this.options.proxy.type === 4) { 339 this.handleSocks4IncomingConnectionResponse(); 340 } 341 else { 342 this.handleSocks5IncomingConnectionResponse(); 343 } 344 } 345 else { 346 this.closeSocket(constants_1.ERRORS.InternalError); 347 break; 348 } 349 } 350 } 351 /** 352 * Handles Socket close event. 353 * @param had_error 354 */ 355 onCloseHandler() { 356 this.closeSocket(constants_1.ERRORS.SocketClosed); 357 } 358 /** 359 * Handles Socket error event. 360 * @param err 361 */ 362 onErrorHandler(err) { 363 this.closeSocket(err.message); 364 } 365 /** 366 * Removes internal event listeners on the underlying Socket. 367 */ 368 removeInternalSocketHandlers() { 369 // Pauses data flow of the socket (this is internally resumed after 'established' is emitted) 370 this.socket.pause(); 371 this.socket.removeListener('data', this.onDataReceived); 372 this.socket.removeListener('close', this.onClose); 373 this.socket.removeListener('error', this.onError); 374 this.socket.removeListener('connect', this.onConnect); 375 } 376 /** 377 * Closes and destroys the underlying Socket. Emits an error event. 378 * @param err { String } An error string to include in error event. 379 */ 380 closeSocket(err) { 381 // Make sure only one 'error' event is fired for the lifetime of this SocksClient instance. 382 if (this.state !== constants_1.SocksClientState.Error) { 383 // Set internal state to Error. 384 this.setState(constants_1.SocksClientState.Error); 385 // Destroy Socket 386 this.socket.destroy(); 387 // Remove internal listeners 388 this.removeInternalSocketHandlers(); 389 // Fire 'error' event. 390 this.emit('error', new util_1.SocksClientError(err, this.options)); 391 } 392 } 393 /** 394 * Sends initial Socks v4 handshake request. 395 */ 396 sendSocks4InitialHandshake() { 397 const userId = this.options.proxy.userId || ''; 398 const buff = new smart_buffer_1.SmartBuffer(); 399 buff.writeUInt8(0x04); 400 buff.writeUInt8(constants_1.SocksCommand[this.options.command]); 401 buff.writeUInt16BE(this.options.destination.port); 402 // Socks 4 (IPv4) 403 if (net.isIPv4(this.options.destination.host)) { 404 buff.writeBuffer(ip.toBuffer(this.options.destination.host)); 405 buff.writeStringNT(userId); 406 // Socks 4a (hostname) 407 } 408 else { 409 buff.writeUInt8(0x00); 410 buff.writeUInt8(0x00); 411 buff.writeUInt8(0x00); 412 buff.writeUInt8(0x01); 413 buff.writeStringNT(userId); 414 buff.writeStringNT(this.options.destination.host); 415 } 416 this.nextRequiredPacketBufferSize = 417 constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks4Response; 418 this.socket.write(buff.toBuffer()); 419 } 420 /** 421 * Handles Socks v4 handshake response. 422 * @param data 423 */ 424 handleSocks4FinalHandshakeResponse() { 425 const data = this.receiveBuffer.get(8); 426 if (data[1] !== constants_1.Socks4Response.Granted) { 427 this.closeSocket(`${constants_1.ERRORS.Socks4ProxyRejectedConnection} - (${constants_1.Socks4Response[data[1]]})`); 428 } 429 else { 430 // Bind response 431 if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.bind) { 432 const buff = smart_buffer_1.SmartBuffer.fromBuffer(data); 433 buff.readOffset = 2; 434 const remoteHost = { 435 port: buff.readUInt16BE(), 436 host: ip.fromLong(buff.readUInt32BE()), 437 }; 438 // If host is 0.0.0.0, set to proxy host. 439 if (remoteHost.host === '0.0.0.0') { 440 remoteHost.host = this.options.proxy.ipaddress; 441 } 442 this.setState(constants_1.SocksClientState.BoundWaitingForConnection); 443 this.emit('bound', { remoteHost, socket: this.socket }); 444 // Connect response 445 } 446 else { 447 this.setState(constants_1.SocksClientState.Established); 448 this.removeInternalSocketHandlers(); 449 this.emit('established', { socket: this.socket }); 450 } 451 } 452 } 453 /** 454 * Handles Socks v4 incoming connection request (BIND) 455 * @param data 456 */ 457 handleSocks4IncomingConnectionResponse() { 458 const data = this.receiveBuffer.get(8); 459 if (data[1] !== constants_1.Socks4Response.Granted) { 460 this.closeSocket(`${constants_1.ERRORS.Socks4ProxyRejectedIncomingBoundConnection} - (${constants_1.Socks4Response[data[1]]})`); 461 } 462 else { 463 const buff = smart_buffer_1.SmartBuffer.fromBuffer(data); 464 buff.readOffset = 2; 465 const remoteHost = { 466 port: buff.readUInt16BE(), 467 host: ip.fromLong(buff.readUInt32BE()), 468 }; 469 this.setState(constants_1.SocksClientState.Established); 470 this.removeInternalSocketHandlers(); 471 this.emit('established', { remoteHost, socket: this.socket }); 472 } 473 } 474 /** 475 * Sends initial Socks v5 handshake request. 476 */ 477 sendSocks5InitialHandshake() { 478 const buff = new smart_buffer_1.SmartBuffer(); 479 // By default we always support no auth. 480 const supportedAuthMethods = [constants_1.Socks5Auth.NoAuth]; 481 // We should only tell the proxy we support user/pass auth if auth info is actually provided. 482 // Note: As of Tor v0.3.5.7+, if user/pass auth is an option from the client, by default it will always take priority. 483 if (this.options.proxy.userId || this.options.proxy.password) { 484 supportedAuthMethods.push(constants_1.Socks5Auth.UserPass); 485 } 486 // Custom auth method? 487 if (this.options.proxy.custom_auth_method !== undefined) { 488 supportedAuthMethods.push(this.options.proxy.custom_auth_method); 489 } 490 // Build handshake packet 491 buff.writeUInt8(0x05); 492 buff.writeUInt8(supportedAuthMethods.length); 493 for (const authMethod of supportedAuthMethods) { 494 buff.writeUInt8(authMethod); 495 } 496 this.nextRequiredPacketBufferSize = 497 constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5InitialHandshakeResponse; 498 this.socket.write(buff.toBuffer()); 499 this.setState(constants_1.SocksClientState.SentInitialHandshake); 500 } 501 /** 502 * Handles initial Socks v5 handshake response. 503 * @param data 504 */ 505 handleInitialSocks5HandshakeResponse() { 506 const data = this.receiveBuffer.get(2); 507 if (data[0] !== 0x05) { 508 this.closeSocket(constants_1.ERRORS.InvalidSocks5IntiailHandshakeSocksVersion); 509 } 510 else if (data[1] === constants_1.SOCKS5_NO_ACCEPTABLE_AUTH) { 511 this.closeSocket(constants_1.ERRORS.InvalidSocks5InitialHandshakeNoAcceptedAuthType); 512 } 513 else { 514 // If selected Socks v5 auth method is no auth, send final handshake request. 515 if (data[1] === constants_1.Socks5Auth.NoAuth) { 516 this.socks5ChosenAuthType = constants_1.Socks5Auth.NoAuth; 517 this.sendSocks5CommandRequest(); 518 // If selected Socks v5 auth method is user/password, send auth handshake. 519 } 520 else if (data[1] === constants_1.Socks5Auth.UserPass) { 521 this.socks5ChosenAuthType = constants_1.Socks5Auth.UserPass; 522 this.sendSocks5UserPassAuthentication(); 523 // If selected Socks v5 auth method is the custom_auth_method, send custom handshake. 524 } 525 else if (data[1] === this.options.proxy.custom_auth_method) { 526 this.socks5ChosenAuthType = this.options.proxy.custom_auth_method; 527 this.sendSocks5CustomAuthentication(); 528 } 529 else { 530 this.closeSocket(constants_1.ERRORS.InvalidSocks5InitialHandshakeUnknownAuthType); 531 } 532 } 533 } 534 /** 535 * Sends Socks v5 user & password auth handshake. 536 * 537 * Note: No auth and user/pass are currently supported. 538 */ 539 sendSocks5UserPassAuthentication() { 540 const userId = this.options.proxy.userId || ''; 541 const password = this.options.proxy.password || ''; 542 const buff = new smart_buffer_1.SmartBuffer(); 543 buff.writeUInt8(0x01); 544 buff.writeUInt8(Buffer.byteLength(userId)); 545 buff.writeString(userId); 546 buff.writeUInt8(Buffer.byteLength(password)); 547 buff.writeString(password); 548 this.nextRequiredPacketBufferSize = 549 constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5UserPassAuthenticationResponse; 550 this.socket.write(buff.toBuffer()); 551 this.setState(constants_1.SocksClientState.SentAuthentication); 552 } 553 sendSocks5CustomAuthentication() { 554 return __awaiter(this, void 0, void 0, function* () { 555 this.nextRequiredPacketBufferSize = 556 this.options.proxy.custom_auth_response_size; 557 this.socket.write(yield this.options.proxy.custom_auth_request_handler()); 558 this.setState(constants_1.SocksClientState.SentAuthentication); 559 }); 560 } 561 handleSocks5CustomAuthHandshakeResponse(data) { 562 return __awaiter(this, void 0, void 0, function* () { 563 return yield this.options.proxy.custom_auth_response_handler(data); 564 }); 565 } 566 handleSocks5AuthenticationNoAuthHandshakeResponse(data) { 567 return __awaiter(this, void 0, void 0, function* () { 568 return data[1] === 0x00; 569 }); 570 } 571 handleSocks5AuthenticationUserPassHandshakeResponse(data) { 572 return __awaiter(this, void 0, void 0, function* () { 573 return data[1] === 0x00; 574 }); 575 } 576 /** 577 * Handles Socks v5 auth handshake response. 578 * @param data 579 */ 580 handleInitialSocks5AuthenticationHandshakeResponse() { 581 return __awaiter(this, void 0, void 0, function* () { 582 this.setState(constants_1.SocksClientState.ReceivedAuthenticationResponse); 583 let authResult = false; 584 if (this.socks5ChosenAuthType === constants_1.Socks5Auth.NoAuth) { 585 authResult = yield this.handleSocks5AuthenticationNoAuthHandshakeResponse(this.receiveBuffer.get(2)); 586 } 587 else if (this.socks5ChosenAuthType === constants_1.Socks5Auth.UserPass) { 588 authResult = 589 yield this.handleSocks5AuthenticationUserPassHandshakeResponse(this.receiveBuffer.get(2)); 590 } 591 else if (this.socks5ChosenAuthType === this.options.proxy.custom_auth_method) { 592 authResult = yield this.handleSocks5CustomAuthHandshakeResponse(this.receiveBuffer.get(this.options.proxy.custom_auth_response_size)); 593 } 594 if (!authResult) { 595 this.closeSocket(constants_1.ERRORS.Socks5AuthenticationFailed); 596 } 597 else { 598 this.sendSocks5CommandRequest(); 599 } 600 }); 601 } 602 /** 603 * Sends Socks v5 final handshake request. 604 */ 605 sendSocks5CommandRequest() { 606 const buff = new smart_buffer_1.SmartBuffer(); 607 buff.writeUInt8(0x05); 608 buff.writeUInt8(constants_1.SocksCommand[this.options.command]); 609 buff.writeUInt8(0x00); 610 // ipv4, ipv6, domain? 611 if (net.isIPv4(this.options.destination.host)) { 612 buff.writeUInt8(constants_1.Socks5HostType.IPv4); 613 buff.writeBuffer(ip.toBuffer(this.options.destination.host)); 614 } 615 else if (net.isIPv6(this.options.destination.host)) { 616 buff.writeUInt8(constants_1.Socks5HostType.IPv6); 617 buff.writeBuffer(ip.toBuffer(this.options.destination.host)); 618 } 619 else { 620 buff.writeUInt8(constants_1.Socks5HostType.Hostname); 621 buff.writeUInt8(this.options.destination.host.length); 622 buff.writeString(this.options.destination.host); 623 } 624 buff.writeUInt16BE(this.options.destination.port); 625 this.nextRequiredPacketBufferSize = 626 constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseHeader; 627 this.socket.write(buff.toBuffer()); 628 this.setState(constants_1.SocksClientState.SentFinalHandshake); 629 } 630 /** 631 * Handles Socks v5 final handshake response. 632 * @param data 633 */ 634 handleSocks5FinalHandshakeResponse() { 635 // Peek at available data (we need at least 5 bytes to get the hostname length) 636 const header = this.receiveBuffer.peek(5); 637 if (header[0] !== 0x05 || header[1] !== constants_1.Socks5Response.Granted) { 638 this.closeSocket(`${constants_1.ERRORS.InvalidSocks5FinalHandshakeRejected} - ${constants_1.Socks5Response[header[1]]}`); 639 } 640 else { 641 // Read address type 642 const addressType = header[3]; 643 let remoteHost; 644 let buff; 645 // IPv4 646 if (addressType === constants_1.Socks5HostType.IPv4) { 647 // Check if data is available. 648 const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseIPv4; 649 if (this.receiveBuffer.length < dataNeeded) { 650 this.nextRequiredPacketBufferSize = dataNeeded; 651 return; 652 } 653 buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(4)); 654 remoteHost = { 655 host: ip.fromLong(buff.readUInt32BE()), 656 port: buff.readUInt16BE(), 657 }; 658 // If given host is 0.0.0.0, assume remote proxy ip instead. 659 if (remoteHost.host === '0.0.0.0') { 660 remoteHost.host = this.options.proxy.ipaddress; 661 } 662 // Hostname 663 } 664 else if (addressType === constants_1.Socks5HostType.Hostname) { 665 const hostLength = header[4]; 666 const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseHostname(hostLength); // header + host length + host + port 667 // Check if data is available. 668 if (this.receiveBuffer.length < dataNeeded) { 669 this.nextRequiredPacketBufferSize = dataNeeded; 670 return; 671 } 672 buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(5)); 673 remoteHost = { 674 host: buff.readString(hostLength), 675 port: buff.readUInt16BE(), 676 }; 677 // IPv6 678 } 679 else if (addressType === constants_1.Socks5HostType.IPv6) { 680 // Check if data is available. 681 const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseIPv6; 682 if (this.receiveBuffer.length < dataNeeded) { 683 this.nextRequiredPacketBufferSize = dataNeeded; 684 return; 685 } 686 buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(4)); 687 remoteHost = { 688 host: ip.toString(buff.readBuffer(16)), 689 port: buff.readUInt16BE(), 690 }; 691 } 692 // We have everything we need 693 this.setState(constants_1.SocksClientState.ReceivedFinalResponse); 694 // If using CONNECT, the client is now in the established state. 695 if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.connect) { 696 this.setState(constants_1.SocksClientState.Established); 697 this.removeInternalSocketHandlers(); 698 this.emit('established', { remoteHost, socket: this.socket }); 699 } 700 else if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.bind) { 701 /* If using BIND, the Socks client is now in BoundWaitingForConnection state. 702 This means that the remote proxy server is waiting for a remote connection to the bound port. */ 703 this.setState(constants_1.SocksClientState.BoundWaitingForConnection); 704 this.nextRequiredPacketBufferSize = 705 constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseHeader; 706 this.emit('bound', { remoteHost, socket: this.socket }); 707 /* 708 If using Associate, the Socks client is now Established. And the proxy server is now accepting UDP packets at the 709 given bound port. This initial Socks TCP connection must remain open for the UDP relay to continue to work. 710 */ 711 } 712 else if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.associate) { 713 this.setState(constants_1.SocksClientState.Established); 714 this.removeInternalSocketHandlers(); 715 this.emit('established', { 716 remoteHost, 717 socket: this.socket, 718 }); 719 } 720 } 721 } 722 /** 723 * Handles Socks v5 incoming connection request (BIND). 724 */ 725 handleSocks5IncomingConnectionResponse() { 726 // Peek at available data (we need at least 5 bytes to get the hostname length) 727 const header = this.receiveBuffer.peek(5); 728 if (header[0] !== 0x05 || header[1] !== constants_1.Socks5Response.Granted) { 729 this.closeSocket(`${constants_1.ERRORS.Socks5ProxyRejectedIncomingBoundConnection} - ${constants_1.Socks5Response[header[1]]}`); 730 } 731 else { 732 // Read address type 733 const addressType = header[3]; 734 let remoteHost; 735 let buff; 736 // IPv4 737 if (addressType === constants_1.Socks5HostType.IPv4) { 738 // Check if data is available. 739 const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseIPv4; 740 if (this.receiveBuffer.length < dataNeeded) { 741 this.nextRequiredPacketBufferSize = dataNeeded; 742 return; 743 } 744 buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(4)); 745 remoteHost = { 746 host: ip.fromLong(buff.readUInt32BE()), 747 port: buff.readUInt16BE(), 748 }; 749 // If given host is 0.0.0.0, assume remote proxy ip instead. 750 if (remoteHost.host === '0.0.0.0') { 751 remoteHost.host = this.options.proxy.ipaddress; 752 } 753 // Hostname 754 } 755 else if (addressType === constants_1.Socks5HostType.Hostname) { 756 const hostLength = header[4]; 757 const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseHostname(hostLength); // header + host length + port 758 // Check if data is available. 759 if (this.receiveBuffer.length < dataNeeded) { 760 this.nextRequiredPacketBufferSize = dataNeeded; 761 return; 762 } 763 buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(5)); 764 remoteHost = { 765 host: buff.readString(hostLength), 766 port: buff.readUInt16BE(), 767 }; 768 // IPv6 769 } 770 else if (addressType === constants_1.Socks5HostType.IPv6) { 771 // Check if data is available. 772 const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseIPv6; 773 if (this.receiveBuffer.length < dataNeeded) { 774 this.nextRequiredPacketBufferSize = dataNeeded; 775 return; 776 } 777 buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(4)); 778 remoteHost = { 779 host: ip.toString(buff.readBuffer(16)), 780 port: buff.readUInt16BE(), 781 }; 782 } 783 this.setState(constants_1.SocksClientState.Established); 784 this.removeInternalSocketHandlers(); 785 this.emit('established', { remoteHost, socket: this.socket }); 786 } 787 } 788 get socksClientOptions() { 789 return Object.assign({}, this.options); 790 } 791} 792exports.SocksClient = SocksClient; 793//# sourceMappingURL=socksclient.js.map