Skip to content

Commit 0ccff16

Browse files
author
3nprob
committed
Add SOCKS proxy support
1 parent d2fafaf commit 0ccff16

File tree

3 files changed

+57
-6
lines changed

3 files changed

+57
-6
lines changed

src/irc/BridgedClient.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ export class BridgedClient extends EventEmitter {
250250
this.server.getIpv6Prefix() ? this.clientConfig.getIpv6Address() : undefined
251251
),
252252
encodingFallback: this.encodingFallback,
253+
socksProxy: this.server.config.proxy
253254
}, (inst: ConnectionInstance) => {
254255
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
255256
this.onConnectionCreated(inst, nameInfo, identResolver!);

src/irc/ConnectionInstance.ts

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { Client } from "matrix-org-irc";
17+
import { Client, IrcClientOpts } from "matrix-org-irc";
1818
import * as promiseutil from "../promiseutil";
1919
import Scheduler from "./Scheduler";
2020
import * as logging from "../logging";
2121
import Bluebird from "bluebird";
2222
import { Defer } from "../promiseutil";
2323
import { IrcServer } from "./IrcServer";
24+
import { Socket } from "net";
25+
import { SocksClientOptions, SocksClient } from "socks";
2426

2527
const log = logging.get("client-connection");
2628

@@ -67,6 +69,7 @@ export interface ConnectionOpts {
6769
secure?: {
6870
ca?: string;
6971
};
72+
socksProxy?: string;
7073
encodingFallback: string;
7174
}
7275

@@ -348,6 +351,45 @@ export class ConnectionInstance {
348351
this.resetPingSendTimer();
349352
}, this.pingOpts.pingRateMs);
350353
}
354+
private static async connectSocksProxy(proxyAddress: string, server: IrcServer): Promise<Socket> {
355+
const proxyParts = new URL(proxyAddress);
356+
let socksType: 4|5;
357+
switch (proxyParts.protocol) {
358+
case 'socks4:':
359+
case 'socks4a:':
360+
socksType = 4;
361+
break;
362+
case 'socks5:':
363+
socksType = 5;
364+
break;
365+
default:
366+
throw new Error(`Unsupported proxy protocol ${proxyParts.protocol}`);
367+
}
368+
const options: SocksClientOptions = {
369+
proxy: {
370+
host: proxyParts.hostname,
371+
port: parseInt(proxyParts.port, 10),
372+
type: socksType,
373+
userId: proxyParts.username,
374+
password: proxyParts.password,
375+
},
376+
command: 'connect',
377+
destination: {
378+
host: server.randomDomain(),
379+
port: server.getPort() || 6667,
380+
},
381+
timeout: CONNECT_TIMEOUT_MS,
382+
};
383+
try {
384+
const info = await SocksClient.createConnection(options);
385+
log.info('Connected to socks proxy', info.socket);
386+
return info.socket;
387+
}
388+
catch (err) {
389+
log.error('Failed to connect to proxy', err);
390+
throw err;
391+
}
392+
}
351393

352394
/**
353395
* Create an IRC client connection and connect to it.
@@ -366,7 +408,7 @@ export class ConnectionInstance {
366408
if (!opts.nick || !server) {
367409
throw new Error("Bad inputs. Nick: " + opts.nick);
368410
}
369-
const connectionOpts = {
411+
const connectionOpts: IrcClientOpts = {
370412
userName: opts.username,
371413
realName: opts.realname,
372414
password: opts.password,
@@ -382,12 +424,19 @@ export class ConnectionInstance {
382424
family: (server.getIpv6Prefix() || server.getIpv6Only() ? 6 : null) as 6|null,
383425
bustRfc3484: true,
384426
sasl: opts.password ? server.useSasl() : false,
385-
secure: server.useSsl() ? server.getSecureOptions() : undefined,
386-
encodingFallback: opts.encodingFallback
427+
secure: server.useSsl() ? {
428+
...server.getSecureOptions(),
429+
...opts.secure
430+
} : undefined,
431+
encodingFallback: opts.encodingFallback,
387432
};
388433

389434
// Returns: A promise which resolves to a ConnectionInstance
390-
const retryConnection = () => {
435+
const retryConnection = async () => {
436+
if (opts.socksProxy) {
437+
const socket = await this.connectSocksProxy(opts.socksProxy, server);
438+
connectionOpts.socket = socket;
439+
}
391440
const nodeClient = new Client(
392441
server.randomDomain(), opts.nick, connectionOpts
393442
);
@@ -400,7 +449,7 @@ export class ConnectionInstance {
400449
if (onCreatedCallback) {
401450
onCreatedCallback(inst);
402451
}
403-
return inst.connect();
452+
return await inst.connect();
404453
};
405454

406455
let connAttempts = 0;

src/irc/IrcServer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export interface IrcServerConfig {
4040
allowExpiredCerts?: boolean;
4141
additionalAddresses?: string[];
4242
onlyAdditionalAddresses: boolean;
43+
proxy?: string;
4344
dynamicChannels: {
4445
enabled: boolean;
4546
published: boolean;

0 commit comments

Comments
 (0)