Skip to content

Commit 8f989f7

Browse files
committed
WIP
1 parent a0a30c0 commit 8f989f7

File tree

5 files changed

+317
-124
lines changed

5 files changed

+317
-124
lines changed

packages/devtools-proxy-support/src/agent.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ import type { TcpNetConnectOpts } from 'net';
77
import type { ConnectionOptions } from 'tls';
88
import type { Duplex } from 'stream';
99
import { SSHAgent } from './ssh';
10+
import type { ProxyLogEmitter } from './logging';
11+
import type { EventEmitter } from 'events';
1012

1113
export type AgentWithInitialize = Agent & {
1214
// This is genuinely custom for our usage (to allow establishing an SSH tunnel
1315
// first before starting to push connections through it)
1416
initialize?(): Promise<void>;
17+
logger?: ProxyLogEmitter;
1518

1619
// This is just part of the regular Agent interface, used by Node.js itself,
1720
// but missing from @types/node
@@ -20,7 +23,9 @@ export type AgentWithInitialize = Agent & {
2023
options: TcpNetConnectOpts | ConnectionOptions,
2124
cb: (err: Error | null, s?: Duplex) => void
2225
): void;
23-
};
26+
27+
// http.Agent is an EventEmitter
28+
} & Partial<EventEmitter>;
2429

2530
export function createAgent(
2631
proxyOptions: DevtoolsProxyOptions

packages/devtools-proxy-support/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export {
55
extractProxySecrets,
66
mergeProxySecrets,
77
} from './proxy-options';
8-
export { Tunnel, TunnelOptions, setupSocks5Tunnel } from './tunnel';
8+
export { Tunnel, TunnelOptions, setupSocks5Tunnel } from './socks5';
99
export { createAgent } from './agent';
1010
export { createFetch } from './fetch';
11+
export { ProxyEventMap, ProxyLogEmitter, hookLogger } from './logging';
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
interface BaseSocks5RequestMetadata {
2+
srcAddr: string;
3+
srcPort: number;
4+
dstAddr: string;
5+
dstPort: number;
6+
}
7+
8+
export interface ProxyEventMap {
9+
'socks5:authentication-complete': (ev: { success: boolean }) => void;
10+
'socks5:skip-auth-setup': () => void;
11+
'socks5:start-listening': (ev: {
12+
proxyHost: string;
13+
proxyPort: number;
14+
}) => void;
15+
'socks5:forwarding-error': (
16+
ev: { error: string } & Partial<BaseSocks5RequestMetadata>
17+
) => void;
18+
'socks5:agent-initialized': () => void;
19+
'socks5:closing-tunnel': () => void;
20+
'socks5:got-forwarding-request': (ev: BaseSocks5RequestMetadata) => void;
21+
'socks5:accepted-forwarding-request': (ev: BaseSocks5RequestMetadata) => void;
22+
'socks5:failed-forwarding-request': (
23+
ev: { error: string } & Partial<BaseSocks5RequestMetadata>
24+
) => void;
25+
'socks5:forwarded-socket-closed': (ev: BaseSocks5RequestMetadata) => void;
26+
27+
'ssh:client-closed': () => void;
28+
'ssh:establishing-conection': (ev: {
29+
host: string | undefined;
30+
port: number | undefined;
31+
password: boolean;
32+
passphrase: boolean;
33+
privateKey: boolean;
34+
}) => void;
35+
'ssh:failed-connection': (ev: { error: string }) => void;
36+
'ssh:established-connection': () => void;
37+
'ssh:failed-forward': (ev: {
38+
error: string;
39+
host: string;
40+
retryableError: boolean;
41+
retriesLeft: number;
42+
}) => void;
43+
}
44+
45+
export type ProxyEventArgs<K extends keyof ProxyEventMap> =
46+
ProxyEventMap[K] extends (...args: infer P) => any ? P : never;
47+
48+
export interface ProxyLogEmitter {
49+
// TypeScript uses something like this itself for its EventTarget definitions.
50+
on<K extends keyof ProxyEventMap>(event: K, listener: ProxyEventMap[K]): this;
51+
off?<K extends keyof ProxyEventMap>(
52+
event: K,
53+
listener: ProxyEventMap[K]
54+
): this;
55+
once<K extends keyof ProxyEventMap>(
56+
event: K,
57+
listener: ProxyEventMap[K]
58+
): this;
59+
emit<K extends keyof ProxyEventMap>(
60+
event: K,
61+
...args: ProxyEventArgs<K>
62+
): unknown;
63+
}
64+
65+
interface MongoLogWriter {
66+
info(c: string, id: unknown, ctx: string, msg: string, attr?: any): void;
67+
warn(c: string, id: unknown, ctx: string, msg: string, attr?: any): void;
68+
error(c: string, id: unknown, ctx: string, msg: string, attr?: any): void;
69+
mongoLogId(this: void, id: number): unknown;
70+
}
71+
72+
let idCounter = 0;
73+
export function hookLogger(
74+
emitter: ProxyLogEmitter,
75+
logCtx: string,
76+
log: MongoLogWriter
77+
): void {
78+
logCtx = `${logCtx}-${idCounter++}`;
79+
const { mongoLogId } = log;
80+
81+
emitter.on('socks5:authentication-complete', (ev) => {
82+
log.info(
83+
'DEVTOOLS-PROXY',
84+
mongoLogId(1_001_000_253),
85+
`socks5-${logCtx}`,
86+
'Validated auth parameters',
87+
{ ...ev }
88+
);
89+
});
90+
91+
emitter.on('socks5:skip-auth-setup', () => {
92+
log.info(
93+
'DEVTOOLS-PROXY',
94+
mongoLogId(1_001_000_254),
95+
`socks5-${logCtx}`,
96+
'Skipping auth setup'
97+
);
98+
});
99+
100+
emitter.on('socks5:start-listening', (ev) => {
101+
log.info(
102+
'DEVTOOLS-PROXY',
103+
mongoLogId(1_001_000_255),
104+
`socks5-${logCtx}`,
105+
'Listening for Socks5 connections',
106+
{ ...ev }
107+
);
108+
});
109+
110+
emitter.on('socks5:forwarding-error', (ev) => {
111+
log.error(
112+
'DEVTOOLS-PROXY',
113+
mongoLogId(1_001_000_259),
114+
`socks5-${logCtx}`,
115+
'Failed to establish new SSH connection',
116+
{ ...ev }
117+
);
118+
});
119+
120+
emitter.on('socks5:agent-initialized', () => {
121+
log.info(
122+
'DEVTOOLS-PROXY',
123+
mongoLogId(1_001_000_259),
124+
`socks5-${logCtx}`,
125+
'Finished initializing agent'
126+
);
127+
});
128+
129+
emitter.on('socks5:closing-tunnel', () => {
130+
log.info(
131+
'DEVTOOLS-PROXY',
132+
mongoLogId(1_001_000_256),
133+
`socks5-${logCtx}`,
134+
'Closing Socks5 tunnel'
135+
);
136+
});
137+
138+
emitter.on('socks5:got-forwarding-request', (ev) => {
139+
log.info(
140+
'DEVTOOLS-PROXY',
141+
mongoLogId(1_001_000_260),
142+
`socks5-${logCtx}`,
143+
'Received Socks5 fowarding request',
144+
{ ...ev }
145+
);
146+
});
147+
148+
emitter.on('socks5:accepted-forwarding-request', (ev) => {
149+
log.info(
150+
'DEVTOOLS-PROXY',
151+
mongoLogId(1_001_000_262),
152+
`socks5-${logCtx}`,
153+
'Established outbound connection and accepting socks5 request',
154+
{ ...ev }
155+
);
156+
});
157+
158+
emitter.on('socks5:failed-forwarding-request', (ev) => {
159+
log.error(
160+
'DEVTOOLS-PROXY',
161+
mongoLogId(1_001_000_265),
162+
`socks5-${logCtx}`,
163+
'Error establishing outbound connection for socks5 request',
164+
{ ...ev }
165+
);
166+
});
167+
168+
emitter.on('socks5:forwarded-socket-closed', (ev) => {
169+
log.info(
170+
'DEVTOOLS-PROXY',
171+
mongoLogId(1_001_000_264),
172+
`socks5-${logCtx}`,
173+
'Socks5 stream socket closed',
174+
{ ...ev }
175+
);
176+
});
177+
178+
emitter.on('ssh:client-closed', () => {
179+
log.info(
180+
'DEVTOOLS-PROXY',
181+
mongoLogId(1_001_000_252),
182+
`ssh-${logCtx}`,
183+
'sshClient closed'
184+
);
185+
});
186+
187+
emitter.on('ssh:establishing-conection', (ev) => {
188+
log.info(
189+
'DEVTOOLS-PROXY',
190+
mongoLogId(1_001_000_257),
191+
`ssh-${logCtx}`,
192+
'Establishing new SSH connection',
193+
{ ...ev }
194+
);
195+
});
196+
emitter.on('ssh:failed-connection', (ev) => {
197+
log.info(
198+
'DEVTOOLS-PROXY',
199+
mongoLogId(1_001_000_258),
200+
`ssh-${logCtx}`,
201+
'Failed to establish new SSH connection',
202+
{ ...ev }
203+
);
204+
});
205+
emitter.on('ssh:established-connection', () => {
206+
log.info(
207+
'DEVTOOLS-PROXY',
208+
mongoLogId(1_001_000_259),
209+
`ssh-${logCtx}`,
210+
'Finished establishing new SSH connection'
211+
);
212+
});
213+
214+
emitter.on('ssh:failed-forward', (ev) => {
215+
log.error(
216+
'DEVTOOLS-PROXY',
217+
mongoLogId(1_001_000_261),
218+
`ssh-${logCtx}`,
219+
'Error forwarding Socks5 request, retrying',
220+
{
221+
...ev,
222+
}
223+
);
224+
});
225+
}

0 commit comments

Comments
 (0)