Skip to content
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: 2 additions & 0 deletions lib/dispatcher/socks5-proxy-agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class Socks5ProxyAgent extends DispatcherBase {
const authenticationReady = Promise.withResolvers()

const authenticationTimeout = setTimeout(() => {
socks5Client.destroy()
authenticationReady.reject(new Error('SOCKS5 authentication timeout'))
}, 5000)

Expand Down Expand Up @@ -148,6 +149,7 @@ class Socks5ProxyAgent extends DispatcherBase {
const connectionReady = Promise.withResolvers()

const connectionTimeout = setTimeout(() => {
socks5Client.destroy()
connectionReady.reject(new Error('SOCKS5 connection timeout'))
}, 5000)

Expand Down
39 changes: 39 additions & 0 deletions test/socks5-proxy-agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,45 @@ test('Socks5ProxyAgent - connection failure', async (t) => {
await p.completed
})

test('Socks5ProxyAgent - destroys socket when negotiation times out', async (t) => {
const p = tspl(t, { plan: 2 })

// SOCKS5 proxy that accepts the TCP connection but never replies to the greeting
const stalledProxy = net.createServer(() => {})
await new Promise((resolve) => {
stalledProxy.listen(0, '127.0.0.1', resolve)
})

let connectorSocket
const agent = new Socks5ProxyAgent(`socks5://127.0.0.1:${stalledProxy.address().port}`, {
connect (opts, callback) {
const socket = net.connect({ host: opts.hostname, port: opts.port })
connectorSocket = socket
socket.once('connect', () => callback(null, socket))
socket.once('error', callback)
return socket
}
})

try {
await request('http://example.invalid/', {
dispatcher: agent
})
p.fail('should have thrown an error')
} catch (err) {
p.ok(err, 'should throw error when SOCKS5 negotiation stalls')
}

await agent.close()
p.ok(connectorSocket.destroyed, 'socket left by the timed out negotiation should be destroyed')

t.after(async () => {
stalledProxy.close()
})

await p.completed
})

test('Socks5ProxyAgent - proxy connection refused', async (t) => {
const p = tspl(t, { plan: 1 })

Expand Down
Loading