Skip to content

Commit 6b5868d

Browse files
committed
Allow base64url encoding of SOCKS password-smuggled metadata
We're getting a bit silly here, but this should make it very widely compatible so it's super easy to use this metadata basically anywhere (at the cost of reducing the max length, since b64 encoding this is not very efficient).
1 parent c7fe5f7 commit 6b5868d

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

src/server/socks-server.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,15 @@ async function handleUsernamePasswordMetadata(socket: net.Socket) {
296296
return false;
297297
}
298298

299-
const passwordString = password.toString('utf8');
300299
let metadataJson: any = {};
301300
try {
302-
metadataJson = JSON.parse(passwordString);
301+
// Base64'd json always starts with 'e' (typically eyI), so we can use this fairly
302+
// reliably to detect base64 (and definitely exclude valid object JSON encoding).
303+
const decoded = password[0] === 'e'.charCodeAt(0)
304+
? Buffer.from(password.toString('utf8'), 'base64url').toString('utf8')
305+
: password.toString('utf8');
306+
307+
metadataJson = JSON.parse(decoded);
303308
} catch (e) {
304309
socket.end(Buffer.from([
305310
0x05,

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,25 @@ nodeOnly(() => {
269269
expect(responseData.tags).to.deep.equal(['socket-metadata:response-test-tag']);
270270
});
271271

272+
it("should accept and use username/password base64 metadata SOCKSv5 connections", async () => {
273+
const responseEventDeferred = getDeferred<CompletedResponse>();
274+
await server.on('response', (res) => responseEventDeferred.resolve(res));
275+
276+
const socksSocket = await openSocksSocket(server, '127.0.0.1', remoteServer.port, {
277+
type: 5,
278+
userId: "mockttp-metadata",
279+
password: Buffer.from(
280+
JSON.stringify({ tags: ['base64d-test-tag'] })
281+
).toString('base64url')
282+
});
283+
284+
const response = await h1RequestOverSocket(socksSocket, remoteServer.url);
285+
expect(response.statusCode).to.equal(200);
286+
287+
const responseData = await responseEventDeferred;
288+
expect(responseData.tags).to.deep.equal(['socket-metadata:base64d-test-tag']);
289+
});
290+
272291
it("to reject username/password auth with unparseable JSON metadata", async () => {
273292
try {
274293
await openSocksSocket(server, '127.0.0.1', remoteServer.port, {

0 commit comments

Comments
 (0)