Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove ip package #94

Merged
merged 6 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
node-version: [16.x, 18.x, 20.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v3
Expand Down
2,553 changes: 1,534 additions & 1,019 deletions package-lock.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "socks",
"private": false,
"version": "2.7.1",
"version": "2.7.3",
"description": "Fully featured SOCKS proxy client supporting SOCKSv4, SOCKSv4a, and SOCKSv5. Includes Bind and Associate functionality.",
"main": "build/index.js",
"typings": "typings/index.d.ts",
Expand All @@ -23,7 +23,7 @@
"socks5"
],
"engines": {
"node": ">= 10.13.0",
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
},
"author": "Josh Glazebrook",
Expand All @@ -33,26 +33,26 @@
"license": "MIT",
"readmeFilename": "README.md",
"devDependencies": {
"@types/ip": "1.1.0",
"@types/mocha": "^9.1.1",
"@types/node": "^18.0.6",
"@typescript-eslint/eslint-plugin": "^5.30.6",
"@typescript-eslint/parser": "^5.30.6",
"@types/mocha": "^10.0.6",
"@types/node": "^20.11.17",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"eslint": "^8.20.0",
"mocha": "^10.0.0",
"prettier": "^2.7.1",
"prettier": "^3.2.5",
"ts-node": "^10.9.1",
"typescript": "^4.7.4"
"typescript": "^5.3.3"
},
"dependencies": {
"ip": "^2.0.0",
"ip-address": "^9.0.5",
"smart-buffer": "^4.2.0"
},
"scripts": {
"prepublish": "npm install -g typescript && npm run build",
"test": "NODE_ENV=test mocha --recursive --require ts-node/register test/**/*.ts",
"prettier": "prettier --write ./src/**/*.ts --config .prettierrc.yaml",
"lint": "eslint 'src/**/*.ts'",
"build": "rm -rf build typings && prettier --write ./src/**/*.ts --config .prettierrc.yaml && tsc -p ."
"build": "rm -rf build typings && prettier --write ./src/**/*.ts --config .prettierrc.yaml && tsc -p .",
"build-raw": "rm -rf build typings && tsc -p ."
}
}
37 changes: 23 additions & 14 deletions src/client/socksclient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {EventEmitter} from 'events';
import * as net from 'net';
import * as ip from 'ip';
import {SmartBuffer} from 'smart-buffer';
import {
DEFAULT_TIMEOUT,
Expand All @@ -24,10 +23,14 @@ import {
import {
validateSocksClientOptions,
validateSocksClientChainOptions,
ipv4ToInt32,
ipToBuffer,
int32ToIpv4,
} from '../common/helpers';
import {ReceiveBuffer} from '../common/receivebuffer';
import {SocksClientError, shuffleArray} from '../common/util';
import {Duplex} from 'stream';
import {Address6} from 'ip-address';

// Exposes SocksClient event types
declare interface SocksClient {
Expand Down Expand Up @@ -231,10 +234,10 @@ class SocksClient extends EventEmitter implements SocksClient {
// IPv4/IPv6/Hostname
if (net.isIPv4(options.remoteHost.host)) {
buff.writeUInt8(Socks5HostType.IPv4);
buff.writeUInt32BE(ip.toLong(options.remoteHost.host));
buff.writeUInt32BE(ipv4ToInt32(options.remoteHost.host));
} else if (net.isIPv6(options.remoteHost.host)) {
buff.writeUInt8(Socks5HostType.IPv6);
buff.writeBuffer(ip.toBuffer(options.remoteHost.host));
buff.writeBuffer(ipToBuffer(options.remoteHost.host));
} else {
buff.writeUInt8(Socks5HostType.Hostname);
buff.writeUInt8(Buffer.byteLength(options.remoteHost.host));
Expand Down Expand Up @@ -263,9 +266,11 @@ class SocksClient extends EventEmitter implements SocksClient {
let remoteHost;

if (hostType === Socks5HostType.IPv4) {
remoteHost = ip.fromLong(buff.readUInt32BE());
remoteHost = int32ToIpv4(buff.readUInt32BE());
} else if (hostType === Socks5HostType.IPv6) {
remoteHost = ip.toString(buff.readBuffer(16));
remoteHost = Address6.fromByteArray(
Array.from(buff.readBuffer(16)),
).canonicalForm();
} else {
remoteHost = buff.readString(buff.readUInt8());
}
Expand Down Expand Up @@ -508,7 +513,7 @@ class SocksClient extends EventEmitter implements SocksClient {

// Socks 4 (IPv4)
if (net.isIPv4(this.options.destination.host)) {
buff.writeBuffer(ip.toBuffer(this.options.destination.host));
buff.writeBuffer(ipToBuffer(this.options.destination.host));
buff.writeStringNT(userId);
// Socks 4a (hostname)
} else {
Expand Down Expand Up @@ -546,7 +551,7 @@ class SocksClient extends EventEmitter implements SocksClient {

const remoteHost: SocksRemoteHost = {
port: buff.readUInt16BE(),
host: ip.fromLong(buff.readUInt32BE()),
host: int32ToIpv4(buff.readUInt32BE()),
};

// If host is 0.0.0.0, set to proxy host.
Expand Down Expand Up @@ -584,7 +589,7 @@ class SocksClient extends EventEmitter implements SocksClient {

const remoteHost: SocksRemoteHost = {
port: buff.readUInt16BE(),
host: ip.fromLong(buff.readUInt32BE()),
host: int32ToIpv4(buff.readUInt32BE()),
};

this.setState(SocksClientState.Established);
Expand Down Expand Up @@ -747,10 +752,10 @@ class SocksClient extends EventEmitter implements SocksClient {
// ipv4, ipv6, domain?
if (net.isIPv4(this.options.destination.host)) {
buff.writeUInt8(Socks5HostType.IPv4);
buff.writeBuffer(ip.toBuffer(this.options.destination.host));
buff.writeBuffer(ipToBuffer(this.options.destination.host));
} else if (net.isIPv6(this.options.destination.host)) {
buff.writeUInt8(Socks5HostType.IPv6);
buff.writeBuffer(ip.toBuffer(this.options.destination.host));
buff.writeBuffer(ipToBuffer(this.options.destination.host));
} else {
buff.writeUInt8(Socks5HostType.Hostname);
buff.writeUInt8(this.options.destination.host.length);
Expand Down Expand Up @@ -799,7 +804,7 @@ class SocksClient extends EventEmitter implements SocksClient {
);

remoteHost = {
host: ip.fromLong(buff.readUInt32BE()),
host: int32ToIpv4(buff.readUInt32BE()),
port: buff.readUInt16BE(),
};

Expand Down Expand Up @@ -842,7 +847,9 @@ class SocksClient extends EventEmitter implements SocksClient {
);

remoteHost = {
host: ip.toString(buff.readBuffer(16)),
host: Address6.fromByteArray(
Array.from(buff.readBuffer(16)),
).canonicalForm(),
port: buff.readUInt16BE(),
};
}
Expand Down Expand Up @@ -913,7 +920,7 @@ class SocksClient extends EventEmitter implements SocksClient {
);

remoteHost = {
host: ip.fromLong(buff.readUInt32BE()),
host: int32ToIpv4(buff.readUInt32BE()),
port: buff.readUInt16BE(),
};

Expand Down Expand Up @@ -956,7 +963,9 @@ class SocksClient extends EventEmitter implements SocksClient {
);

remoteHost = {
host: ip.toString(buff.readBuffer(16)),
host: Address6.fromByteArray(
Array.from(buff.readBuffer(16)),
).canonicalForm(),
port: buff.readUInt16BE(),
};
}
Expand Down
48 changes: 22 additions & 26 deletions src/common/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {Duplex} from 'stream';
import {Socket, SocketConnectOpts} from 'net';
import {RequireOnlyOne} from './util';

const DEFAULT_TIMEOUT = 30000;

Expand Down Expand Up @@ -112,32 +111,29 @@ enum SocksClientState {
/**
* Represents a SocksProxy
*/
type SocksProxy = RequireOnlyOne<
{
// The ip address (or hostname) of the proxy. (this is equivalent to the host option)
ipaddress?: string;
// The ip address (or hostname) of the proxy. (this is equivalent to the ipaddress option)
host?: string;
// Numeric port number of the proxy.
port: number;
// 4 or 5 (4 is also used for 4a).
type: SocksProxyType;
/* For SOCKS v4, the userId can be used for authentication.
interface SocksProxy {
// The ip address (or hostname) of the proxy. (this is equivalent to the host option)
ipaddress?: string;
// The ip address (or hostname) of the proxy. (this is equivalent to the ipaddress option)
host?: string;
// Numeric port number of the proxy.
port: number;
// 4 or 5 (4 is also used for 4a).
type: SocksProxyType;
/* For SOCKS v4, the userId can be used for authentication.
For SOCKS v5, userId is used as the username for username/password authentication. */
userId?: string;
// For SOCKS v5, this password is used in username/password authentication.
password?: string;
// If present, this auth method will be sent to the proxy server during the initial handshake.
custom_auth_method?: number;
// If present with custom_auth_method, the payload of the returned Buffer of the provided function is sent during the auth handshake.
custom_auth_request_handler?: () => Promise<Buffer>;
// If present with custom_auth_method, this is the expected total response size of the data returned from the server during custom auth handshake.
custom_auth_response_size?: number;
// If present with custom_auth_method, the response from the server is passed to this function. If true is returned from this function, socks client will continue the handshake process, if false it will disconnect.
custom_auth_response_handler?: (data: Buffer) => Promise<boolean>;
},
'host' | 'ipaddress'
>;
userId?: string;
// For SOCKS v5, this password is used in username/password authentication.
password?: string;
// If present, this auth method will be sent to the proxy server during the initial handshake.
custom_auth_method?: number;
// If present with custom_auth_method, the payload of the returned Buffer of the provided function is sent during the auth handshake.
custom_auth_request_handler?: () => Promise<Buffer>;
// If present with custom_auth_method, this is the expected total response size of the data returned from the server during custom auth handshake.
custom_auth_response_size?: number;
// If present with custom_auth_method, the response from the server is passed to this function. If true is returned from this function, socks client will continue the handshake process, if false it will disconnect.
custom_auth_response_handler?: (data: Buffer) => Promise<boolean>;
}

/**
* Represents a remote host
Expand Down
33 changes: 33 additions & 0 deletions src/common/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
SocksProxy,
} from './constants';
import * as stream from 'stream';
import {Address4, Address6} from 'ip-address';
import * as net from 'net';

/**
* Validates the provided SocksClientOptions
Expand Down Expand Up @@ -208,3 +210,34 @@ function isValidTimeoutValue(value: number) {
}

export {validateSocksClientOptions, validateSocksClientChainOptions};

export function ipv4ToInt32(ip: string): number {
const address = new Address4(ip);
// Convert the IPv4 address parts to an integer
return address.toArray().reduce((acc, part) => (acc << 8) + part, 0);
}

export function int32ToIpv4(int32: number): string {
// Extract each byte (octet) from the 32-bit integer
const octet1 = (int32 >>> 24) & 0xff;
const octet2 = (int32 >>> 16) & 0xff;
const octet3 = (int32 >>> 8) & 0xff;
const octet4 = int32 & 0xff;

// Combine the octets into a string in IPv4 format
return [octet1, octet2, octet3, octet4].join('.');
}

export function ipToBuffer(ip: string): Buffer {
if (net.isIPv4(ip)) {
// Handle IPv4 addresses
const address = new Address4(ip);
return Buffer.from(address.toArray());
} else if (net.isIPv6(ip)) {
// Handle IPv6 addresses
const address = new Address6(ip);
return Buffer.from(address.toByteArray());
} else {
throw new Error('Invalid IP address format');
}
}
12 changes: 1 addition & 11 deletions src/common/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,4 @@ function shuffleArray(array: unknown[]) {
}
}

// Helper type to require one of N keys.
type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Pick<
T,
Exclude<keyof T, Keys>
> &
{
[K in Keys]?: Required<Pick<T, K>> &
Partial<Record<Exclude<Keys, K>, undefined>>;
}[Keys];

export {RequireOnlyOne, SocksClientError, shuffleArray};
export {SocksClientError, shuffleArray};
Loading
Loading