@@ -14,13 +14,15 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17- import { Client } from "matrix-org-irc" ;
17+ import { Client , IrcClientOpts } from "matrix-org-irc" ;
1818import * as promiseutil from "../promiseutil" ;
1919import Scheduler from "./Scheduler" ;
2020import * as logging from "../logging" ;
2121import Bluebird from "bluebird" ;
2222import { Defer } from "../promiseutil" ;
2323import { IrcServer } from "./IrcServer" ;
24+ import { Socket } from "net" ;
25+ import { SocksClientOptions , SocksClient } from "socks" ;
2426
2527const 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 ;
0 commit comments