Skip to content

Commit 05db347

Browse files
committed
Fix crash on bad inbound SOCKS connections
1 parent f20ae7f commit 05db347

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

src/server/socks-server.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export function buildSocksServer(options: SocksServerOptions): SocksServer {
7979
return net.createServer(handleSocksConnect);
8080

8181

82-
async function handleSocksConnect(this: net.Server, socket: net.Socket) {
82+
async function handleSocksConnect(this: net.Server, socket: net.Socket): Promise<void> {
8383
const server = this;
8484
// Until we pass this socket onwards, we handle (and drop) any errors on it:
8585
socket.on('error', ignoreError);
@@ -88,18 +88,18 @@ export function buildSocksServer(options: SocksServerOptions): SocksServer {
8888
const firstByte = await readBytes(socket, 1);;
8989
const version = firstByte[0];
9090
if (version === 0x04) {
91-
return handleSocksV4(socket, (address: SocksTcpAddress) => {
91+
await handleSocksV4(socket, (address: SocksTcpAddress) => {
9292
socket.removeListener('error', ignoreError);
9393
server.emit('socks-tcp-connect', socket, address);
9494
});
9595
} else if (version === 0x05) {
96-
return handleSocksV5(socket, (address: SocksTcpAddress) => {
96+
await handleSocksV5(socket, (address: SocksTcpAddress) => {
9797
socket.removeListener('error', ignoreError);
9898
server.emit('socks-tcp-connect', socket, address);
9999
});
100100
} else {
101101
// Should never happen, since this is sniffed by Httpolyglot, but just in case:
102-
return resetOrDestroy(socket);
102+
resetOrDestroy(socket);
103103
}
104104
} catch (err) {
105105
// We log but otherwise ignore failures, e.g. if the client closes the
@@ -329,7 +329,7 @@ async function handleUsernamePasswordMetadata(socket: net.Socket) {
329329
async function readBytes(socket: net.Socket, length?: number | undefined): Promise<Buffer> {
330330
const buffer = socket.read(length);
331331
if (buffer === null) {
332-
return new Promise((resolve, reject) => {
332+
return new Promise<Buffer>((resolve, reject) => {
333333
socket.once('readable', () => resolve(readBytes(socket, length)));
334334
socket.once('close', () => reject(new Error('Socket closed')));
335335
socket.once('error', reject);

test/integration/proxying/socks-proxying.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import {
1010
getLocal
1111
} from "../../..";
1212
import {
13+
delay,
1314
expect,
1415
getDeferred,
1516
nodeOnly,
17+
openRawSocket,
1618
openSocksSocket,
1719
sendRawRequest
1820
} from "../../test-utils";
@@ -141,6 +143,30 @@ nodeOnly(() => {
141143
expect((await passthroughEvent).port).to.equal(remoteServer.port.toString());
142144
});
143145

146+
it("should not crash given a failed SOCKS handshake", async () => {
147+
const events = [];
148+
await server.on('request-initiated', (req) => events.push(req));
149+
await server.on('client-error', (err) => events.push(err));
150+
await server.on('tls-client-error', (err) => events.push(err));
151+
await server.on('raw-passthrough-opened', (err) => events.push(err));
152+
153+
const socket = await openRawSocket(server);
154+
socket.write(Buffer.from([0x05, 0x01, 0x00])); // Version 5, 1 method, no auth
155+
156+
const result = await new Promise((resolve, reject) => {
157+
socket.once('data', resolve);
158+
socket.on('error', reject);
159+
})
160+
expect(result).to.deep.equal(Buffer.from([0x05, 0x0])); // Server accepts no auth
161+
162+
// Server is now waiting for destination - we reset the connection instead
163+
socket.resetAndDestroy();
164+
165+
// No crash! And no events.
166+
await delay(10);
167+
expect(events.length).to.equal(0);
168+
});
169+
144170
});
145171

146172
describe("with only custom metadata auth supported", () => {

0 commit comments

Comments
 (0)