• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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