Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@applitools/eg-socks5-proxy-server

Package Overview
Dependencies
Maintainers
30
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@applitools/eg-socks5-proxy-server - npm Package Compare versions

Comparing version 0.1.7 to 0.2.0-beta

src/authenticate.js

10

package.json
{
"name": "@applitools/eg-socks5-proxy-server",
"version": "0.1.7",
"version": "0.2.0-beta",
"description": "",

@@ -17,3 +17,3 @@ "main": "src/index.js",

"test": "npm run eslint && npm run test:mocha-parallel",
"test:mocha": "mocha --no-timeouts --trace-warnings --exit 'test/unit/*.test.js' 'test/it/*.test.js' 'test/e2e/*.test.js'",
"test:mocha": "mocha --no-timeouts --trace-warnings --exit 'test/it/*.test.js'",
"eslint": "eslint '**/*.js'",

@@ -44,5 +44,7 @@ "build": "#"

"eslint-plugin-prettier": "^4.0.0",
"mocha": "^9.1.3",
"prettier": "^2.5.0"
"mocha": "^10.0.0",
"node-fetch": "^2.6.7",
"prettier": "^2.5.0",
"socks-proxy-agent": "^7.0.0"
}
}

440

src/index.js

@@ -11,3 +11,7 @@ const {

const {createRequestExecuter} = require('./create-request-executer')
const EVENTS = require('./events')
const {handshake} = require('./handshake')
const {authenticate} = require('./authenticate')
const {executeRequest} = require('./execute-request')
const {executeRequestThroughRemoteProxy} = require('./execute-request-through-remote-proxy')

@@ -18,27 +22,6 @@ const binary = require('binary')

// module specific events
const EVENTS = {
AUTHENTICATION: 'authenticate',
AUTHENTICATION_ERROR: 'authenticateError',
CONNECTION_FILTER: 'connectionFilter',
HANDSHAKE: 'handshake',
PROXY_CONNECT: 'proxyConnect',
PROXY_DATA: 'proxyData',
PROXY_DISCONNECT: 'proxyDisconnect',
PROXY_END: 'proxyEnd',
PROXY_ERROR: 'proxyError',
const _alwaysTrue = () => true
ACCEPT_NEW_REQUEST: 'accept-new-request',
CONNECTED_TO_REMOTE_ADDRESS: 'connected-to-remote-address',
CONNECTED_TO_REMOTE_PROXY: 'connected-to-remote-proxy',
REMOTE_PROXY_HANDSHAKE_COMPLETED: 'remote-proxy-handshake-completed',
REMOTE_PROXY_HANDSHAKE_TIMEOUT: 'remote-proxy-handshake-timeout',
const LENGTH_RFC_1928_ATYP = 4
ORIGIN_SOCKET_ERROR: 'orgin-socket-error',
REMOTE_SOCKET_ERROR: 'remote-socket-error',
REMOTE_CONNECTION_TIMEOUT_ERROR: 'remote-connection-timeout-error',
},
LENGTH_RFC_1928_ATYP = 4
/**

@@ -70,64 +53,14 @@ * The following RFCs may be useful as background:

/**
* +----+------+----------+------+----------+
* |VER | ULEN | UNAME | PLEN | PASSWD |
* +----+------+----------+------+----------+
* | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
* +----+------+----------+------+----------+
*
*
* @param {Buffer} buffer - a buffer
* @returns {undefined}
**/
function authenticate(buffer) {
let authDomain = domain.create()
binary
.stream(buffer)
.word8('ver')
.word8('ulen')
.buffer('uname', 'ulen')
.word8('plen')
.buffer('passwd', 'plen')
.tap((args) => {
// capture the raw buffer
args.requestBuffer = buffer
// verify version is appropriate
if (args.ver !== RFC_1929_VERSION) {
return end(RFC_1929_REPLIES.GENERAL_FAILURE, args)
}
authDomain.on('error', (err) => {
// emit failed authentication event
self.server.emit(EVENTS.AUTHENTICATION_ERROR, args.uname.toString(), err)
// respond with auth failure
return end(RFC_1929_REPLIES.GENERAL_FAILURE, args)
})
// perform authentication
self.options.authenticate(
args.uname.toString(),
args.passwd.toString(),
socket,
authDomain.intercept(() => {
// emit successful authentication event
self.server.emit(EVENTS.AUTHENTICATION, args.uname.toString())
// respond with success...
let responseBuffer = Buffer.allocUnsafe(2)
responseBuffer[0] = RFC_1929_VERSION
responseBuffer[1] = RFC_1929_REPLIES.SUCCEEDED
// respond then listen for cmd and dst info
socket.write(responseBuffer, () => {
// now listen for more details
socket.once('data', connect)
})
}),
)
})
function _authenticate(buffer) {
authenticate({
socket,
buffer,
options: self.options,
connect,
end,
onAuthenticated: (args) => self.server.emit(EVENTS.AUTHENTICATION, args.uname.toString()),
onFailed: (args, err) =>
self.server.emit(EVENTS.AUTHENTICATION_ERROR, args.uname.toString(), err),
})
}
/**

@@ -212,35 +145,20 @@ * +----+-----+-------+------+----------+----------+

.tap((args) => {
if (args.cmd === RFC_1928_COMMANDS.CONNECT) {
let connectionFilter = self.options.connectionFilter,
connectionFilterDomain = domain.create()
if (args.cmd !== RFC_1928_COMMANDS.CONNECT) {
// bind and udp associate commands
return end(RFC_1928_REPLIES.SUCCEEDED, args)
}
// if no connection filter is provided, stub one
if (!connectionFilter || typeof connectionFilter !== 'function') {
connectionFilter = (destination, origin, callback) => setImmediate(callback)
}
let connectionFilter = self.options.connectionFilter,
connectionFilterDomain = domain.create()
// capture connection filter errors
connectionFilterDomain.on('error', (err) => {
// emit failed destination connection event
self.server.emit(
EVENTS.CONNECTION_FILTER,
// destination
{
address: args.dst.addr,
port: args.dst.port,
},
// origin
{
address: socket.remoteAddress,
port: socket.remotePort,
},
err,
)
// if no connection filter is provided, stub one
if (!connectionFilter || typeof connectionFilter !== 'function') {
connectionFilter = (destination, origin, callback) => setImmediate(callback)
}
// respond with failure
return end(RFC_1929_REPLIES.CONNECTION_NOT_ALLOWED, args)
})
// perform connection
return connectionFilter(
// capture connection filter errors
connectionFilterDomain.on('error', (err) => {
// emit failed destination connection event
self.server.emit(
EVENTS.CONNECTION_FILTER,
// destination

@@ -256,25 +174,128 @@ {

},
connectionFilterDomain.intercept(() => {
const destinationInfo = {
address: args.dst.addr,
port: args.dst.port,
err,
)
// respond with failure
return end(RFC_1929_REPLIES.CONNECTION_NOT_ALLOWED, args)
})
// perform connection
return connectionFilter(
// destination
{
address: args.dst.addr,
port: args.dst.port,
},
// origin
{
address: socket.remoteAddress,
port: socket.remotePort,
},
connectionFilterDomain.intercept(() => {
const destinationInfo = {
address: args.dst.addr,
port: args.dst.port,
}
const originInfo = {
address: socket.remoteAddress,
port: socket.remotePort,
}
let createExecuterAndRegisterToEvents
const proxyServer = self.options.proxyServer
? {
...self.options.proxyServer,
shouldUseProxy: self.options.proxyServer.shouldUseProxy || _alwaysTrue
}
: undefined
const originInfo = {
address: socket.remoteAddress,
port: socket.remotePort,
const shouldUseProxy = proxyServer && proxyServer.shouldUseProxy({socket, destinationInfo, originInfo})
const proxyInfo = shouldUseProxy? {
address: proxyServer.address,
port: proxyServer.port
}: undefined
self.server.emit(EVENTS.ACCEPT_NEW_REQUEST, {
originInfo,
destinationInfo,
proxyInfo,
})
createExecuterAndRegisterToEvents = () => {
let connectionTimeout = setTimeout(
() =>
self.server.emit(EVENTS.REMOTE_CONNECTION_TIMEOUT_ERROR, {
originInfo,
destinationInfo,
proxyInfo,
}),
120000,
)
const clearConnectionTimeout = () => {
connectionTimeout && clearTimeout(connectionTimeout)
connectionTimeout = null
}
const proxyServer = self.options.proxyServer ? {...self.options.proxyServer} : undefined
const onConnect = (destinationSocket) => {
connectionFilterDomain.exit()
clearConnectionTimeout()
let createExecuterAndRegisterToEvents
// close destination socket when after original socket was closed
socket.on('close', () => {
destinationSocket.destroy()
self.destinationSockets.splice(self.destinationSockets.indexOf(destinationSocket), 1)
})
if (proxyServer){
const event = shouldUseProxy
? EVENTS.CONNECTED_TO_REMOTE_PROXY
: EVENTS.CONNECTED_TO_REMOTE_ADDRESS
self.server.emit(event, {destinationInfo, originInfo, proxyInfo})
}
const onError = (err) => {
// exit the connection filter domain
connectionFilterDomain.exit()
self.server.emit(EVENTS.REMOTE_SOCKET_ERROR, {
err,
destination,
args,
originInfo,
destinationInfo,
proxyInfo,
})
}
let destination
if (!shouldUseProxy) {
destination = executeRequest({
originalSocket: socket,
args,
end,
onConnect,
onError,
})
} else {
proxyServer.onHandShakeCompleted = () => {
self.server.emit(EVENTS.REMOTE_PROXY_HANDSHAKE_COMPLETED, {originInfo, destinationInfo, proxyServer})
self.server.emit(EVENTS.REMOTE_PROXY_HANDSHAKE_COMPLETED, {
originInfo,
destinationInfo,
proxyInfo,
})
}
let retry = 0
proxyServer.onHandshakeTimeout = () => {
self.server.emit(EVENTS.REMOTE_PROXY_HANDSHAKE_TIMEOUT, {originInfo, destinationInfo, proxyServer})
self.server.emit(EVENTS.REMOTE_PROXY_HANDSHAKE_TIMEOUT, {
originInfo,
destinationInfo,
proxyInfo,
})
retry++

@@ -284,27 +305,4 @@ if (retry <= 3) createExecuterAndRegisterToEvents()

}
}
self.server.emit(EVENTS.ACCEPT_NEW_REQUEST, {
originInfo,
destinationInfo,
proxyServer,
})
createExecuterAndRegisterToEvents = () => {
let connectionTimeout = setTimeout(
() =>
self.server.emit(EVENTS.REMOTE_CONNECTION_TIMEOUT_ERROR, {
originInfo,
destinationInfo,
proxyServer,
}),
120000,
)
const clearConnectionTimeout = () => {
connectionTimeout && clearTimeout(connectionTimeout)
connectionTimeout = null
}
let destination = createRequestExecuter({
destination = executeRequestThroughRemoteProxy({
originalSocket: socket,

@@ -314,60 +312,14 @@ clientHandshakeBuffer,

args,
end,
onConnect,
onError,
})
self.destinationSockets.push(socket)
destination.on('connect', () => {
connectionFilterDomain.exit()
clearConnectionTimeout()
// close destination socket when after original socket was closed
socket.on('close', () => {
destination.destroy()
self.destinationSockets.splice(self.destinationSockets.indexOf(socket), 1)
})
const event = proxyServer
? EVENTS.CONNECTED_TO_REMOTE_PROXY
: EVENTS.CONNECTED_TO_REMOTE_ADDRESS
self.server.emit(event, {destinationInfo, originInfo, proxyServer})
})
destination.on('error', (err) => {
// exit the connection filter domain
connectionFilterDomain.exit()
clearConnectionTimeout()
// notify of connection error
err.addr = args.dst.addr
err.atyp = args.atyp
err.port = args.dst.port
self.server.emit(EVENTS.REMOTE_SOCKET_ERROR, {
err,
destination,
args,
originInfo,
destinationInfo,
proxyServer,
})
if (err.code && err.code === 'EADDRNOTAVAIL') {
return end(RFC_1928_REPLIES.HOST_UNREACHABLE, args)
}
if (err.code && err.code === 'ECONNREFUSED') {
return end(RFC_1928_REPLIES.CONNECTION_REFUSED, args)
}
return end(RFC_1928_REPLIES.NETWORK_UNREACHABLE, args)
})
}
createExecuterAndRegisterToEvents()
}),
)
} else {
// bind and udp associate commands
return end(RFC_1928_REPLIES.SUCCEEDED, args)
}
self.destinationSockets.push(destinationSocket)
}
createExecuterAndRegisterToEvents()
}),
)
})

@@ -418,58 +370,22 @@ }

**/
function handshake(buffer) {
function _handshake(buffer) {
clientHandshakeBuffer = buffer
binary
.stream(buffer)
.word8('ver')
.word8('nmethods')
.buffer('methods', 'nmethods')
.tap((args) => {
// verify version is appropriate
if (args.ver !== RFC_1928_VERSION) {
return end(RFC_1928_REPLIES.GENERAL_FAILURE, args)
}
// convert methods buffer to an array
let acceptedMethods = [].slice.call(args.methods).reduce((methods, method) => {
methods[method] = true
return methods
}, {}),
basicAuth = typeof self.options.authenticate === 'function',
next = connect,
noAuth =
!basicAuth && typeof acceptedMethods[0] !== 'undefined' && acceptedMethods[0],
responseBuffer = Buffer.allocUnsafe(2)
const onHandshakeCompleted = (buffer) => {
self.server.emit(EVENTS.HANDSHAKE, socket)
}
// form response Buffer
responseBuffer[0] = RFC_1928_VERSION
responseBuffer[1] = RFC_1928_METHODS.NO_AUTHENTICATION_REQUIRED
// check for basic auth configuration
if (basicAuth) {
responseBuffer[1] = RFC_1928_METHODS.BASIC_AUTHENTICATION
next = authenticate
// if NO AUTHENTICATION REQUIRED and
} else if (!basicAuth && noAuth) {
responseBuffer[1] = RFC_1928_METHODS.NO_AUTHENTICATION_REQUIRED
next = connect
// basic auth callback not provided and no auth is not supported
} else {
return end(RFC_1928_METHODS.NO_ACCEPTABLE_METHODS, args)
}
// respond then listen for cmd and dst info
socket.write(responseBuffer, () => {
// emit handshake event
self.server.emit(EVENTS.HANDSHAKE, socket)
// now listen for more details
socket.once('data', next)
})
})
handshake({
socket,
buffer,
options: self.options,
connect,
authenticate: _authenticate,
onHandshakeCompleted,
end,
})
}
// capture the client handshake
socket.once('data', handshake)
socket.once('data', _handshake)

@@ -484,8 +400,14 @@ // capture socket closure

on = (...args) => this.server.on(...args)
listen = (...args) => this.server.listen(...args)
close = (...args) => this.server.close(...args)
setProxyServer = (proxyServer) => {
this.options.proxyServer = proxyServer
}
getProxyServer = () => this.options.proxyServer
}
const createSocks5ProxyServer = ({options} = {}) => {
const createSocks5ProxyServer = (options) => {
let socksServer = new SocksServer(options)

@@ -492,0 +414,0 @@ return socksServer

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc