socks 2.3.1 → 2.4.1
Sign up to get free protection for your applications and to get access to all the features.
- package/.prettierrc.yaml +3 -1
- package/.travis.yml +3 -2
- package/README.md +1 -1
- package/build/client/socksclient.js +163 -161
- package/build/client/socksclient.js.map +1 -1
- package/build/common/constants.js +2 -1
- package/build/common/constants.js.map +1 -1
- package/build/common/helpers.js +1 -0
- package/build/common/helpers.js.map +1 -1
- package/build/common/receivebuffer.js +17 -16
- package/build/common/receivebuffer.js.map +1 -1
- package/build/common/util.js +3 -1
- package/build/common/util.js.map +1 -1
- package/build/index.js +11 -4
- package/build/index.js.map +1 -1
- package/package.json +18 -16
- package/typings/client/socksclient.d.ts +34 -36
- package/typings/common/constants.d.ts +9 -8
- package/typings/common/receiveBuffer.d.ts +4 -4
- package/yarn-error.log +0 -2416
- package/yarn.lock +0 -2300
@@ -1,13 +1,15 @@
|
|
1
1
|
"use strict";
|
2
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
3
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
4
5
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
5
6
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
6
|
-
function step(result) { result.done ? resolve(result.value) :
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
7
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
8
9
|
});
|
9
10
|
};
|
10
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.SocksClientError = exports.SocksClient = void 0;
|
11
13
|
const events_1 = require("events");
|
12
14
|
const net = require("net");
|
13
15
|
const ip = require("ip");
|
@@ -16,14 +18,15 @@ const constants_1 = require("../common/constants");
|
|
16
18
|
const helpers_1 = require("../common/helpers");
|
17
19
|
const receivebuffer_1 = require("../common/receivebuffer");
|
18
20
|
const util_1 = require("../common/util");
|
21
|
+
Object.defineProperty(exports, "SocksClientError", { enumerable: true, get: function () { return util_1.SocksClientError; } });
|
19
22
|
class SocksClient extends events_1.EventEmitter {
|
20
23
|
constructor(options) {
|
21
24
|
super();
|
22
|
-
this.
|
25
|
+
this.options = Object.assign({}, options);
|
23
26
|
// Validate SocksClientOptions
|
24
27
|
helpers_1.validateSocksClientOptions(options);
|
25
28
|
// Default state
|
26
|
-
this.
|
29
|
+
this.setState(constants_1.SocksClientState.Created);
|
27
30
|
}
|
28
31
|
/**
|
29
32
|
* Creates a new SOCKS connection.
|
@@ -81,6 +84,7 @@ class SocksClient extends events_1.EventEmitter {
|
|
81
84
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
82
85
|
let sock;
|
83
86
|
try {
|
87
|
+
// tslint:disable-next-line:no-increment-decrement
|
84
88
|
for (let i = 0; i < options.proxies.length; i++) {
|
85
89
|
const nextProxy = options.proxies[i];
|
86
90
|
// If we've reached the last proxy in the chain, the destination is the actual destination, otherwise it's the next proxy.
|
@@ -88,14 +92,13 @@ class SocksClient extends events_1.EventEmitter {
|
|
88
92
|
? options.destination
|
89
93
|
: {
|
90
94
|
host: options.proxies[i + 1].ipaddress,
|
91
|
-
port: options.proxies[i + 1].port
|
95
|
+
port: options.proxies[i + 1].port,
|
92
96
|
};
|
93
97
|
// Creates the next connection in the chain.
|
94
98
|
const result = yield SocksClient.createConnection({
|
95
99
|
command: 'connect',
|
96
100
|
proxy: nextProxy,
|
97
|
-
destination: nextDestination
|
98
|
-
// Initial connection ignores this as sock is undefined. Subsequent connections re-use the first proxy socket to form a chain.
|
101
|
+
destination: nextDestination,
|
99
102
|
});
|
100
103
|
// If sock is undefined, assign it here.
|
101
104
|
if (!sock) {
|
@@ -173,75 +176,73 @@ class SocksClient extends events_1.EventEmitter {
|
|
173
176
|
frameNumber,
|
174
177
|
remoteHost: {
|
175
178
|
host: remoteHost,
|
176
|
-
port: remotePort
|
179
|
+
port: remotePort,
|
177
180
|
},
|
178
|
-
data: buff.readBuffer()
|
181
|
+
data: buff.readBuffer(),
|
179
182
|
};
|
180
183
|
}
|
181
|
-
/**
|
182
|
-
* Gets the SocksClient internal state.
|
183
|
-
*/
|
184
|
-
get state() {
|
185
|
-
return this._state;
|
186
|
-
}
|
187
184
|
/**
|
188
185
|
* Internal state setter. If the SocksClient is in an error state, it cannot be changed to a non error state.
|
189
186
|
*/
|
190
|
-
|
191
|
-
if (this.
|
192
|
-
this.
|
187
|
+
setState(newState) {
|
188
|
+
if (this.state !== constants_1.SocksClientState.Error) {
|
189
|
+
this.state = newState;
|
193
190
|
}
|
194
191
|
}
|
195
192
|
/**
|
196
193
|
* Starts the connection establishment to the proxy and destination.
|
197
|
-
* @param
|
194
|
+
* @param existingSocket Connected socket to use instead of creating a new one (internal use).
|
198
195
|
*/
|
199
|
-
connect(
|
200
|
-
this.
|
201
|
-
this.
|
202
|
-
this.
|
203
|
-
this.
|
196
|
+
connect(existingSocket) {
|
197
|
+
this.onDataReceived = (data) => this.onDataReceivedHandler(data);
|
198
|
+
this.onClose = () => this.onCloseHandler();
|
199
|
+
this.onError = (err) => this.onErrorHandler(err);
|
200
|
+
this.onConnect = () => this.onConnectHandler();
|
204
201
|
// Start timeout timer (defaults to 30 seconds)
|
205
|
-
const timer = setTimeout(() => this.onEstablishedTimeout(), this.
|
202
|
+
const timer = setTimeout(() => this.onEstablishedTimeout(), this.options.timeout || constants_1.DEFAULT_TIMEOUT);
|
206
203
|
// check whether unref is available as it differs from browser to NodeJS (#33)
|
207
204
|
if (timer.unref && typeof timer.unref === 'function') {
|
208
205
|
timer.unref();
|
209
206
|
}
|
210
207
|
// If an existing socket is provided, use it to negotiate SOCKS handshake. Otherwise create a new Socket.
|
211
|
-
if (
|
212
|
-
this.
|
208
|
+
if (existingSocket) {
|
209
|
+
this.socket = existingSocket;
|
213
210
|
}
|
214
211
|
else {
|
215
|
-
this.
|
212
|
+
this.socket = new net.Socket();
|
216
213
|
}
|
217
214
|
// Attach Socket error handlers.
|
218
|
-
this.
|
219
|
-
this.
|
220
|
-
this.
|
221
|
-
this.
|
222
|
-
this.
|
223
|
-
this.
|
224
|
-
if (
|
225
|
-
this.
|
215
|
+
this.socket.once('close', this.onClose);
|
216
|
+
this.socket.once('error', this.onError);
|
217
|
+
this.socket.once('connect', this.onConnect);
|
218
|
+
this.socket.on('data', this.onDataReceived);
|
219
|
+
this.setState(constants_1.SocksClientState.Connecting);
|
220
|
+
this.receiveBuffer = new receivebuffer_1.ReceiveBuffer();
|
221
|
+
if (existingSocket) {
|
222
|
+
this.socket.emit('connect');
|
226
223
|
}
|
227
224
|
else {
|
228
|
-
this.
|
229
|
-
if (this.
|
230
|
-
this.
|
231
|
-
this.
|
225
|
+
this.socket.connect(this.getSocketOptions());
|
226
|
+
if (this.options.set_tcp_nodelay !== undefined &&
|
227
|
+
this.options.set_tcp_nodelay !== null) {
|
228
|
+
this.socket.setNoDelay(!!this.options.set_tcp_nodelay);
|
232
229
|
}
|
233
230
|
}
|
234
231
|
// Listen for established event so we can re-emit any excess data received during handshakes.
|
235
|
-
this.prependOnceListener('established', info => {
|
232
|
+
this.prependOnceListener('established', (info) => {
|
236
233
|
setImmediate(() => {
|
237
|
-
if (this.
|
238
|
-
const excessData = this.
|
234
|
+
if (this.receiveBuffer.length > 0) {
|
235
|
+
const excessData = this.receiveBuffer.get(this.receiveBuffer.length);
|
239
236
|
info.socket.emit('data', excessData);
|
240
237
|
}
|
241
238
|
info.socket.resume();
|
242
239
|
});
|
243
240
|
});
|
244
241
|
}
|
242
|
+
// Socket options (defaults host/port to options.proxy.host/options.proxy.port)
|
243
|
+
getSocketOptions() {
|
244
|
+
return Object.assign(Object.assign({}, this.options.socket_options), { host: this.options.proxy.host || this.options.proxy.ipaddress, port: this.options.proxy.port });
|
245
|
+
}
|
245
246
|
/**
|
246
247
|
* Handles internal Socks timeout callback.
|
247
248
|
* Note: If the Socks client is not BoundWaitingForConnection or Established, the connection will be closed.
|
@@ -249,33 +250,33 @@ class SocksClient extends events_1.EventEmitter {
|
|
249
250
|
onEstablishedTimeout() {
|
250
251
|
if (this.state !== constants_1.SocksClientState.Established &&
|
251
252
|
this.state !== constants_1.SocksClientState.BoundWaitingForConnection) {
|
252
|
-
this.
|
253
|
+
this.closeSocket(constants_1.ERRORS.ProxyConnectionTimedOut);
|
253
254
|
}
|
254
255
|
}
|
255
256
|
/**
|
256
257
|
* Handles Socket connect event.
|
257
258
|
*/
|
258
|
-
|
259
|
-
this.
|
259
|
+
onConnectHandler() {
|
260
|
+
this.setState(constants_1.SocksClientState.Connected);
|
260
261
|
// Send initial handshake.
|
261
|
-
if (this.
|
262
|
+
if (this.options.proxy.type === 4) {
|
262
263
|
this.sendSocks4InitialHandshake();
|
263
264
|
}
|
264
265
|
else {
|
265
266
|
this.sendSocks5InitialHandshake();
|
266
267
|
}
|
267
|
-
this.
|
268
|
+
this.setState(constants_1.SocksClientState.SentInitialHandshake);
|
268
269
|
}
|
269
270
|
/**
|
270
271
|
* Handles Socket data event.
|
271
272
|
* @param data
|
272
273
|
*/
|
273
|
-
|
274
|
+
onDataReceivedHandler(data) {
|
274
275
|
/*
|
275
276
|
All received data is appended to a ReceiveBuffer.
|
276
277
|
This makes sure that all the data we need is received before we attempt to process it.
|
277
278
|
*/
|
278
|
-
this.
|
279
|
+
this.receiveBuffer.append(data);
|
279
280
|
// Process data that we have.
|
280
281
|
this.processData();
|
281
282
|
}
|
@@ -284,10 +285,10 @@ class SocksClient extends events_1.EventEmitter {
|
|
284
285
|
*/
|
285
286
|
processData() {
|
286
287
|
// If we have enough data to process the next step in the SOCKS handshake, proceed.
|
287
|
-
if (this.
|
288
|
+
if (this.receiveBuffer.length >= this.nextRequiredPacketBufferSize) {
|
288
289
|
// Sent initial handshake, waiting for response.
|
289
290
|
if (this.state === constants_1.SocksClientState.SentInitialHandshake) {
|
290
|
-
if (this.
|
291
|
+
if (this.options.proxy.type === 4) {
|
291
292
|
// Socks v4 only has one handshake response.
|
292
293
|
this.handleSocks4FinalHandshakeResponse();
|
293
294
|
}
|
@@ -306,7 +307,7 @@ class SocksClient extends events_1.EventEmitter {
|
|
306
307
|
// Socks BIND established. Waiting for remote connection via proxy.
|
307
308
|
}
|
308
309
|
else if (this.state === constants_1.SocksClientState.BoundWaitingForConnection) {
|
309
|
-
if (this.
|
310
|
+
if (this.options.proxy.type === 4) {
|
310
311
|
this.handleSocks4IncomingConnectionResponse();
|
311
312
|
}
|
312
313
|
else {
|
@@ -317,7 +318,7 @@ class SocksClient extends events_1.EventEmitter {
|
|
317
318
|
// do nothing (prevents closing of the socket)
|
318
319
|
}
|
319
320
|
else {
|
320
|
-
this.
|
321
|
+
this.closeSocket(constants_1.ERRORS.InternalError);
|
321
322
|
}
|
322
323
|
}
|
323
324
|
}
|
@@ -325,56 +326,56 @@ class SocksClient extends events_1.EventEmitter {
|
|
325
326
|
* Handles Socket close event.
|
326
327
|
* @param had_error
|
327
328
|
*/
|
328
|
-
|
329
|
-
this.
|
329
|
+
onCloseHandler() {
|
330
|
+
this.closeSocket(constants_1.ERRORS.SocketClosed);
|
330
331
|
}
|
331
332
|
/**
|
332
333
|
* Handles Socket error event.
|
333
334
|
* @param err
|
334
335
|
*/
|
335
|
-
|
336
|
-
this.
|
336
|
+
onErrorHandler(err) {
|
337
|
+
this.closeSocket(err.message);
|
337
338
|
}
|
338
339
|
/**
|
339
340
|
* Removes internal event listeners on the underlying Socket.
|
340
341
|
*/
|
341
342
|
removeInternalSocketHandlers() {
|
342
343
|
// Pauses data flow of the socket (this is internally resumed after 'established' is emitted)
|
343
|
-
this.
|
344
|
-
this.
|
345
|
-
this.
|
346
|
-
this.
|
347
|
-
this.
|
344
|
+
this.socket.pause();
|
345
|
+
this.socket.removeListener('data', this.onDataReceived);
|
346
|
+
this.socket.removeListener('close', this.onClose);
|
347
|
+
this.socket.removeListener('error', this.onError);
|
348
|
+
this.socket.removeListener('connect', this.onConnect);
|
348
349
|
}
|
349
350
|
/**
|
350
351
|
* Closes and destroys the underlying Socket. Emits an error event.
|
351
352
|
* @param err { String } An error string to include in error event.
|
352
353
|
*/
|
353
|
-
|
354
|
+
closeSocket(err) {
|
354
355
|
// Make sure only one 'error' event is fired for the lifetime of this SocksClient instance.
|
355
356
|
if (this.state !== constants_1.SocksClientState.Error) {
|
356
357
|
// Set internal state to Error.
|
357
|
-
this.
|
358
|
+
this.setState(constants_1.SocksClientState.Error);
|
358
359
|
// Destroy Socket
|
359
|
-
this.
|
360
|
+
this.socket.destroy();
|
360
361
|
// Remove internal listeners
|
361
362
|
this.removeInternalSocketHandlers();
|
362
363
|
// Fire 'error' event.
|
363
|
-
this.emit('error', new util_1.SocksClientError(err, this.
|
364
|
+
this.emit('error', new util_1.SocksClientError(err, this.options));
|
364
365
|
}
|
365
366
|
}
|
366
367
|
/**
|
367
368
|
* Sends initial Socks v4 handshake request.
|
368
369
|
*/
|
369
370
|
sendSocks4InitialHandshake() {
|
370
|
-
const userId = this.
|
371
|
+
const userId = this.options.proxy.userId || '';
|
371
372
|
const buff = new smart_buffer_1.SmartBuffer();
|
372
373
|
buff.writeUInt8(0x04);
|
373
|
-
buff.writeUInt8(constants_1.SocksCommand[this.
|
374
|
-
buff.writeUInt16BE(this.
|
374
|
+
buff.writeUInt8(constants_1.SocksCommand[this.options.command]);
|
375
|
+
buff.writeUInt16BE(this.options.destination.port);
|
375
376
|
// Socks 4 (IPv4)
|
376
|
-
if (net.isIPv4(this.
|
377
|
-
buff.writeBuffer(ip.toBuffer(this.
|
377
|
+
if (net.isIPv4(this.options.destination.host)) {
|
378
|
+
buff.writeBuffer(ip.toBuffer(this.options.destination.host));
|
378
379
|
buff.writeStringNT(userId);
|
379
380
|
// Socks 4a (hostname)
|
380
381
|
}
|
@@ -384,42 +385,42 @@ class SocksClient extends events_1.EventEmitter {
|
|
384
385
|
buff.writeUInt8(0x00);
|
385
386
|
buff.writeUInt8(0x01);
|
386
387
|
buff.writeStringNT(userId);
|
387
|
-
buff.writeStringNT(this.
|
388
|
+
buff.writeStringNT(this.options.destination.host);
|
388
389
|
}
|
389
|
-
this.
|
390
|
+
this.nextRequiredPacketBufferSize =
|
390
391
|
constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks4Response;
|
391
|
-
this.
|
392
|
+
this.socket.write(buff.toBuffer());
|
392
393
|
}
|
393
394
|
/**
|
394
395
|
* Handles Socks v4 handshake response.
|
395
396
|
* @param data
|
396
397
|
*/
|
397
398
|
handleSocks4FinalHandshakeResponse() {
|
398
|
-
const data = this.
|
399
|
+
const data = this.receiveBuffer.get(8);
|
399
400
|
if (data[1] !== constants_1.Socks4Response.Granted) {
|
400
|
-
this.
|
401
|
+
this.closeSocket(`${constants_1.ERRORS.Socks4ProxyRejectedConnection} - (${constants_1.Socks4Response[data[1]]})`);
|
401
402
|
}
|
402
403
|
else {
|
403
404
|
// Bind response
|
404
|
-
if (constants_1.SocksCommand[this.
|
405
|
+
if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.bind) {
|
405
406
|
const buff = smart_buffer_1.SmartBuffer.fromBuffer(data);
|
406
407
|
buff.readOffset = 2;
|
407
408
|
const remoteHost = {
|
408
409
|
port: buff.readUInt16BE(),
|
409
|
-
host: ip.fromLong(buff.readUInt32BE())
|
410
|
+
host: ip.fromLong(buff.readUInt32BE()),
|
410
411
|
};
|
411
412
|
// If host is 0.0.0.0, set to proxy host.
|
412
413
|
if (remoteHost.host === '0.0.0.0') {
|
413
|
-
remoteHost.host = this.
|
414
|
+
remoteHost.host = this.options.proxy.ipaddress;
|
414
415
|
}
|
415
|
-
this.
|
416
|
-
this.emit('bound', { socket: this.
|
416
|
+
this.setState(constants_1.SocksClientState.BoundWaitingForConnection);
|
417
|
+
this.emit('bound', { remoteHost, socket: this.socket });
|
417
418
|
// Connect response
|
418
419
|
}
|
419
420
|
else {
|
420
|
-
this.
|
421
|
+
this.setState(constants_1.SocksClientState.Established);
|
421
422
|
this.removeInternalSocketHandlers();
|
422
|
-
this.emit('established', { socket: this.
|
423
|
+
this.emit('established', { socket: this.socket });
|
423
424
|
}
|
424
425
|
}
|
425
426
|
}
|
@@ -428,20 +429,20 @@ class SocksClient extends events_1.EventEmitter {
|
|
428
429
|
* @param data
|
429
430
|
*/
|
430
431
|
handleSocks4IncomingConnectionResponse() {
|
431
|
-
const data = this.
|
432
|
+
const data = this.receiveBuffer.get(8);
|
432
433
|
if (data[1] !== constants_1.Socks4Response.Granted) {
|
433
|
-
this.
|
434
|
+
this.closeSocket(`${constants_1.ERRORS.Socks4ProxyRejectedIncomingBoundConnection} - (${constants_1.Socks4Response[data[1]]})`);
|
434
435
|
}
|
435
436
|
else {
|
436
437
|
const buff = smart_buffer_1.SmartBuffer.fromBuffer(data);
|
437
438
|
buff.readOffset = 2;
|
438
439
|
const remoteHost = {
|
439
440
|
port: buff.readUInt16BE(),
|
440
|
-
host: ip.fromLong(buff.readUInt32BE())
|
441
|
+
host: ip.fromLong(buff.readUInt32BE()),
|
441
442
|
};
|
442
|
-
this.
|
443
|
+
this.setState(constants_1.SocksClientState.Established);
|
443
444
|
this.removeInternalSocketHandlers();
|
444
|
-
this.emit('established', { socket: this.
|
445
|
+
this.emit('established', { remoteHost, socket: this.socket });
|
445
446
|
}
|
446
447
|
}
|
447
448
|
/**
|
@@ -452,7 +453,7 @@ class SocksClient extends events_1.EventEmitter {
|
|
452
453
|
buff.writeUInt8(0x05);
|
453
454
|
// We should only tell the proxy we support user/pass auth if auth info is actually provided.
|
454
455
|
// 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.
|
455
|
-
if (this.
|
456
|
+
if (this.options.proxy.userId || this.options.proxy.password) {
|
456
457
|
buff.writeUInt8(2);
|
457
458
|
buff.writeUInt8(constants_1.Socks5Auth.NoAuth);
|
458
459
|
buff.writeUInt8(constants_1.Socks5Auth.UserPass);
|
@@ -461,22 +462,22 @@ class SocksClient extends events_1.EventEmitter {
|
|
461
462
|
buff.writeUInt8(1);
|
462
463
|
buff.writeUInt8(constants_1.Socks5Auth.NoAuth);
|
463
464
|
}
|
464
|
-
this.
|
465
|
+
this.nextRequiredPacketBufferSize =
|
465
466
|
constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5InitialHandshakeResponse;
|
466
|
-
this.
|
467
|
-
this.
|
467
|
+
this.socket.write(buff.toBuffer());
|
468
|
+
this.setState(constants_1.SocksClientState.SentInitialHandshake);
|
468
469
|
}
|
469
470
|
/**
|
470
471
|
* Handles initial Socks v5 handshake response.
|
471
472
|
* @param data
|
472
473
|
*/
|
473
474
|
handleInitialSocks5HandshakeResponse() {
|
474
|
-
const data = this.
|
475
|
+
const data = this.receiveBuffer.get(2);
|
475
476
|
if (data[0] !== 0x05) {
|
476
|
-
this.
|
477
|
+
this.closeSocket(constants_1.ERRORS.InvalidSocks5IntiailHandshakeSocksVersion);
|
477
478
|
}
|
478
479
|
else if (data[1] === 0xff) {
|
479
|
-
this.
|
480
|
+
this.closeSocket(constants_1.ERRORS.InvalidSocks5InitialHandshakeNoAcceptedAuthType);
|
480
481
|
}
|
481
482
|
else {
|
482
483
|
// If selected Socks v5 auth method is no auth, send final handshake request.
|
@@ -488,7 +489,7 @@ class SocksClient extends events_1.EventEmitter {
|
|
488
489
|
this.sendSocks5UserPassAuthentication();
|
489
490
|
}
|
490
491
|
else {
|
491
|
-
this.
|
492
|
+
this.closeSocket(constants_1.ERRORS.InvalidSocks5InitialHandshakeUnknownAuthType);
|
492
493
|
}
|
493
494
|
}
|
494
495
|
}
|
@@ -498,28 +499,28 @@ class SocksClient extends events_1.EventEmitter {
|
|
498
499
|
* Note: No auth and user/pass are currently supported.
|
499
500
|
*/
|
500
501
|
sendSocks5UserPassAuthentication() {
|
501
|
-
const userId = this.
|
502
|
-
const password = this.
|
502
|
+
const userId = this.options.proxy.userId || '';
|
503
|
+
const password = this.options.proxy.password || '';
|
503
504
|
const buff = new smart_buffer_1.SmartBuffer();
|
504
505
|
buff.writeUInt8(0x01);
|
505
506
|
buff.writeUInt8(Buffer.byteLength(userId));
|
506
507
|
buff.writeString(userId);
|
507
508
|
buff.writeUInt8(Buffer.byteLength(password));
|
508
509
|
buff.writeString(password);
|
509
|
-
this.
|
510
|
+
this.nextRequiredPacketBufferSize =
|
510
511
|
constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5UserPassAuthenticationResponse;
|
511
|
-
this.
|
512
|
-
this.
|
512
|
+
this.socket.write(buff.toBuffer());
|
513
|
+
this.setState(constants_1.SocksClientState.SentAuthentication);
|
513
514
|
}
|
514
515
|
/**
|
515
516
|
* Handles Socks v5 auth handshake response.
|
516
517
|
* @param data
|
517
518
|
*/
|
518
519
|
handleInitialSocks5AuthenticationHandshakeResponse() {
|
519
|
-
this.
|
520
|
-
const data = this.
|
520
|
+
this.setState(constants_1.SocksClientState.ReceivedAuthenticationResponse);
|
521
|
+
const data = this.receiveBuffer.get(2);
|
521
522
|
if (data[1] !== 0x00) {
|
522
|
-
this.
|
523
|
+
this.closeSocket(constants_1.ERRORS.Socks5AuthenticationFailed);
|
523
524
|
}
|
524
525
|
else {
|
525
526
|
this.sendSocks5CommandRequest();
|
@@ -531,27 +532,27 @@ class SocksClient extends events_1.EventEmitter {
|
|
531
532
|
sendSocks5CommandRequest() {
|
532
533
|
const buff = new smart_buffer_1.SmartBuffer();
|
533
534
|
buff.writeUInt8(0x05);
|
534
|
-
buff.writeUInt8(constants_1.SocksCommand[this.
|
535
|
+
buff.writeUInt8(constants_1.SocksCommand[this.options.command]);
|
535
536
|
buff.writeUInt8(0x00);
|
536
537
|
// ipv4, ipv6, domain?
|
537
|
-
if (net.isIPv4(this.
|
538
|
+
if (net.isIPv4(this.options.destination.host)) {
|
538
539
|
buff.writeUInt8(constants_1.Socks5HostType.IPv4);
|
539
|
-
buff.writeBuffer(ip.toBuffer(this.
|
540
|
+
buff.writeBuffer(ip.toBuffer(this.options.destination.host));
|
540
541
|
}
|
541
|
-
else if (net.isIPv6(this.
|
542
|
+
else if (net.isIPv6(this.options.destination.host)) {
|
542
543
|
buff.writeUInt8(constants_1.Socks5HostType.IPv6);
|
543
|
-
buff.writeBuffer(ip.toBuffer(this.
|
544
|
+
buff.writeBuffer(ip.toBuffer(this.options.destination.host));
|
544
545
|
}
|
545
546
|
else {
|
546
547
|
buff.writeUInt8(constants_1.Socks5HostType.Hostname);
|
547
|
-
buff.writeUInt8(this.
|
548
|
-
buff.writeString(this.
|
548
|
+
buff.writeUInt8(this.options.destination.host.length);
|
549
|
+
buff.writeString(this.options.destination.host);
|
549
550
|
}
|
550
|
-
buff.writeUInt16BE(this.
|
551
|
-
this.
|
551
|
+
buff.writeUInt16BE(this.options.destination.port);
|
552
|
+
this.nextRequiredPacketBufferSize =
|
552
553
|
constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseHeader;
|
553
|
-
this.
|
554
|
-
this.
|
554
|
+
this.socket.write(buff.toBuffer());
|
555
|
+
this.setState(constants_1.SocksClientState.SentFinalHandshake);
|
555
556
|
}
|
556
557
|
/**
|
557
558
|
* Handles Socks v5 final handshake response.
|
@@ -559,9 +560,9 @@ class SocksClient extends events_1.EventEmitter {
|
|
559
560
|
*/
|
560
561
|
handleSocks5FinalHandshakeResponse() {
|
561
562
|
// Peek at available data (we need at least 5 bytes to get the hostname length)
|
562
|
-
const header = this.
|
563
|
+
const header = this.receiveBuffer.peek(5);
|
563
564
|
if (header[0] !== 0x05 || header[1] !== constants_1.Socks5Response.Granted) {
|
564
|
-
this.
|
565
|
+
this.closeSocket(`${constants_1.ERRORS.InvalidSocks5FinalHandshakeRejected} - ${constants_1.Socks5Response[header[1]]}`);
|
565
566
|
}
|
566
567
|
else {
|
567
568
|
// Read address type
|
@@ -572,18 +573,18 @@ class SocksClient extends events_1.EventEmitter {
|
|
572
573
|
if (addressType === constants_1.Socks5HostType.IPv4) {
|
573
574
|
// Check if data is available.
|
574
575
|
const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseIPv4;
|
575
|
-
if (this.
|
576
|
-
this.
|
576
|
+
if (this.receiveBuffer.length < dataNeeded) {
|
577
|
+
this.nextRequiredPacketBufferSize = dataNeeded;
|
577
578
|
return;
|
578
579
|
}
|
579
|
-
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.
|
580
|
+
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(4));
|
580
581
|
remoteHost = {
|
581
582
|
host: ip.fromLong(buff.readUInt32BE()),
|
582
|
-
port: buff.readUInt16BE()
|
583
|
+
port: buff.readUInt16BE(),
|
583
584
|
};
|
584
585
|
// If given host is 0.0.0.0, assume remote proxy ip instead.
|
585
586
|
if (remoteHost.host === '0.0.0.0') {
|
586
|
-
remoteHost.host = this.
|
587
|
+
remoteHost.host = this.options.proxy.ipaddress;
|
587
588
|
}
|
588
589
|
// Hostname
|
589
590
|
}
|
@@ -591,55 +592,57 @@ class SocksClient extends events_1.EventEmitter {
|
|
591
592
|
const hostLength = header[4];
|
592
593
|
const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseHostname(hostLength); // header + host length + host + port
|
593
594
|
// Check if data is available.
|
594
|
-
if (this.
|
595
|
-
this.
|
595
|
+
if (this.receiveBuffer.length < dataNeeded) {
|
596
|
+
this.nextRequiredPacketBufferSize = dataNeeded;
|
596
597
|
return;
|
597
598
|
}
|
598
|
-
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.
|
599
|
-
);
|
599
|
+
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(5));
|
600
600
|
remoteHost = {
|
601
601
|
host: buff.readString(hostLength),
|
602
|
-
port: buff.readUInt16BE()
|
602
|
+
port: buff.readUInt16BE(),
|
603
603
|
};
|
604
604
|
// IPv6
|
605
605
|
}
|
606
606
|
else if (addressType === constants_1.Socks5HostType.IPv6) {
|
607
607
|
// Check if data is available.
|
608
608
|
const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseIPv6;
|
609
|
-
if (this.
|
610
|
-
this.
|
609
|
+
if (this.receiveBuffer.length < dataNeeded) {
|
610
|
+
this.nextRequiredPacketBufferSize = dataNeeded;
|
611
611
|
return;
|
612
612
|
}
|
613
|
-
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.
|
613
|
+
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(4));
|
614
614
|
remoteHost = {
|
615
615
|
host: ip.toString(buff.readBuffer(16)),
|
616
|
-
port: buff.readUInt16BE()
|
616
|
+
port: buff.readUInt16BE(),
|
617
617
|
};
|
618
618
|
}
|
619
619
|
// We have everything we need
|
620
|
-
this.
|
620
|
+
this.setState(constants_1.SocksClientState.ReceivedFinalResponse);
|
621
621
|
// If using CONNECT, the client is now in the established state.
|
622
|
-
if (constants_1.SocksCommand[this.
|
623
|
-
this.
|
622
|
+
if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.connect) {
|
623
|
+
this.setState(constants_1.SocksClientState.Established);
|
624
624
|
this.removeInternalSocketHandlers();
|
625
|
-
this.emit('established', { socket: this.
|
625
|
+
this.emit('established', { socket: this.socket });
|
626
626
|
}
|
627
|
-
else if (constants_1.SocksCommand[this.
|
627
|
+
else if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.bind) {
|
628
628
|
/* If using BIND, the Socks client is now in BoundWaitingForConnection state.
|
629
629
|
This means that the remote proxy server is waiting for a remote connection to the bound port. */
|
630
|
-
this.
|
631
|
-
this.
|
630
|
+
this.setState(constants_1.SocksClientState.BoundWaitingForConnection);
|
631
|
+
this.nextRequiredPacketBufferSize =
|
632
632
|
constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseHeader;
|
633
|
-
this.emit('bound', { socket: this.
|
633
|
+
this.emit('bound', { remoteHost, socket: this.socket });
|
634
634
|
/*
|
635
635
|
If using Associate, the Socks client is now Established. And the proxy server is now accepting UDP packets at the
|
636
636
|
given bound port. This initial Socks TCP connection must remain open for the UDP relay to continue to work.
|
637
637
|
*/
|
638
638
|
}
|
639
|
-
else if (constants_1.SocksCommand[this.
|
640
|
-
this.
|
639
|
+
else if (constants_1.SocksCommand[this.options.command] === constants_1.SocksCommand.associate) {
|
640
|
+
this.setState(constants_1.SocksClientState.Established);
|
641
641
|
this.removeInternalSocketHandlers();
|
642
|
-
this.emit('established', {
|
642
|
+
this.emit('established', {
|
643
|
+
remoteHost,
|
644
|
+
socket: this.socket,
|
645
|
+
});
|
643
646
|
}
|
644
647
|
}
|
645
648
|
}
|
@@ -648,9 +651,9 @@ class SocksClient extends events_1.EventEmitter {
|
|
648
651
|
*/
|
649
652
|
handleSocks5IncomingConnectionResponse() {
|
650
653
|
// Peek at available data (we need at least 5 bytes to get the hostname length)
|
651
|
-
const header = this.
|
654
|
+
const header = this.receiveBuffer.peek(5);
|
652
655
|
if (header[0] !== 0x05 || header[1] !== constants_1.Socks5Response.Granted) {
|
653
|
-
this.
|
656
|
+
this.closeSocket(`${constants_1.ERRORS.Socks5ProxyRejectedIncomingBoundConnection} - ${constants_1.Socks5Response[header[1]]}`);
|
654
657
|
}
|
655
658
|
else {
|
656
659
|
// Read address type
|
@@ -661,18 +664,18 @@ class SocksClient extends events_1.EventEmitter {
|
|
661
664
|
if (addressType === constants_1.Socks5HostType.IPv4) {
|
662
665
|
// Check if data is available.
|
663
666
|
const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseIPv4;
|
664
|
-
if (this.
|
665
|
-
this.
|
667
|
+
if (this.receiveBuffer.length < dataNeeded) {
|
668
|
+
this.nextRequiredPacketBufferSize = dataNeeded;
|
666
669
|
return;
|
667
670
|
}
|
668
|
-
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.
|
671
|
+
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(4));
|
669
672
|
remoteHost = {
|
670
673
|
host: ip.fromLong(buff.readUInt32BE()),
|
671
|
-
port: buff.readUInt16BE()
|
674
|
+
port: buff.readUInt16BE(),
|
672
675
|
};
|
673
676
|
// If given host is 0.0.0.0, assume remote proxy ip instead.
|
674
677
|
if (remoteHost.host === '0.0.0.0') {
|
675
|
-
remoteHost.host = this.
|
678
|
+
remoteHost.host = this.options.proxy.ipaddress;
|
676
679
|
}
|
677
680
|
// Hostname
|
678
681
|
}
|
@@ -680,38 +683,37 @@ class SocksClient extends events_1.EventEmitter {
|
|
680
683
|
const hostLength = header[4];
|
681
684
|
const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseHostname(hostLength); // header + host length + port
|
682
685
|
// Check if data is available.
|
683
|
-
if (this.
|
684
|
-
this.
|
686
|
+
if (this.receiveBuffer.length < dataNeeded) {
|
687
|
+
this.nextRequiredPacketBufferSize = dataNeeded;
|
685
688
|
return;
|
686
689
|
}
|
687
|
-
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.
|
688
|
-
);
|
690
|
+
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(5));
|
689
691
|
remoteHost = {
|
690
692
|
host: buff.readString(hostLength),
|
691
|
-
port: buff.readUInt16BE()
|
693
|
+
port: buff.readUInt16BE(),
|
692
694
|
};
|
693
695
|
// IPv6
|
694
696
|
}
|
695
697
|
else if (addressType === constants_1.Socks5HostType.IPv6) {
|
696
698
|
// Check if data is available.
|
697
699
|
const dataNeeded = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5ResponseIPv6;
|
698
|
-
if (this.
|
699
|
-
this.
|
700
|
+
if (this.receiveBuffer.length < dataNeeded) {
|
701
|
+
this.nextRequiredPacketBufferSize = dataNeeded;
|
700
702
|
return;
|
701
703
|
}
|
702
|
-
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.
|
704
|
+
buff = smart_buffer_1.SmartBuffer.fromBuffer(this.receiveBuffer.get(dataNeeded).slice(4));
|
703
705
|
remoteHost = {
|
704
706
|
host: ip.toString(buff.readBuffer(16)),
|
705
|
-
port: buff.readUInt16BE()
|
707
|
+
port: buff.readUInt16BE(),
|
706
708
|
};
|
707
709
|
}
|
708
|
-
this.
|
710
|
+
this.setState(constants_1.SocksClientState.Established);
|
709
711
|
this.removeInternalSocketHandlers();
|
710
|
-
this.emit('established', { socket: this.
|
712
|
+
this.emit('established', { remoteHost, socket: this.socket });
|
711
713
|
}
|
712
714
|
}
|
713
715
|
get socksClientOptions() {
|
714
|
-
return Object.assign({}, this.
|
716
|
+
return Object.assign({}, this.options);
|
715
717
|
}
|
716
718
|
}
|
717
719
|
exports.SocksClient = SocksClient;
|