11import { contextBridge } from 'electron'
22import { electronAPI } from '@electron-toolkit/preload'
3+ import { SSH , SSHConnection , SSHEvent , SSHEmitter , SSHConnectionAuth } from './index.d'
34import net from 'net'
4- import { Channel } from 'ssh2'
5- import { NodeSSH } from 'node-ssh'
65import socks from 'socksv5'
6+ import ssh2 from 'ssh2'
77
88// Custom APIs for renderer
99const api = { }
1010
11- type SSHAuth = {
12- host : string
13- namespace : string
14- device : string
15- username : string
16- password : string
17- }
11+ export class SSHConnectionLocalPortForward implements SSHConnection {
12+ eventEmitter = new SSHEmitter ( )
13+ client = new ssh2 . Client ( )
1814
19- type SSHLocalPortForwardSettings = {
20- sourceIP : string
21- sourcePort : number
22- destinationIP : string
23- destinationPort : number
24- }
15+ connect ( auth : SSHConnectionAuth , settings : any ) {
16+ this . client . on ( 'ready' , ( ) => {
17+ console . info ( 'SSH connection established' )
18+ this . eventEmitter . emit ( SSHEvent . Auth )
2519
26- type SSHDynamicPortForwardSettings = {
27- destinationIP : string
28- destinationPort : number
29- }
20+ this . client . forwardOut (
21+ settings . sourceAddr ,
22+ settings . sourcePort ,
23+ settings . destinationAddr ,
24+ settings . destinationPort ,
25+ ( err , stream ) => {
26+ if ( err ) {
27+ console . error ( 'SSH forwarding error:' , err )
28+ this . eventEmitter . emit ( SSHEvent . Error , err )
29+ return
30+ }
3031
31- const ssh = {
32- localPortForward : ( auth : SSHAuth , settings : SSHLocalPortForwardSettings ) => {
33- new NodeSSH ( )
34- . connect ( {
35- host : auth . host ,
36- username : `${ auth . username } @${ auth . namespace } .${ auth . device } ` ,
37- password : auth . password
38- } )
39- . then ( ( ssh : NodeSSH ) => {
40- console . info ( 'SSH connection stablished' )
41-
42- return ssh . forwardOut (
43- settings . sourceIP ,
44- settings . sourcePort ,
45- settings . destinationIP ,
46- settings . destinationPort
47- )
48- } )
49- . then ( ( connection : Channel ) => {
50- console . info ( 'Local port forward started' )
32+ const server = net . createServer ( ( client ) => {
33+ client . pipe ( stream ) . pipe ( client )
34+ } )
5135
52- return net
53- . createServer ( ( client : net . Socket ) => {
54- client . pipe ( connection ) . pipe ( client )
36+ server . on ( 'close' , ( ) => {
37+ this . eventEmitter . emit ( SSHEvent . Disconnect )
5538 } )
56- . listen ( settings . sourcePort , settings . sourceIP , ( ) => {
39+
40+ server . listen ( settings . sourcePort , settings . sourceAddr , ( ) => {
5741 console . log (
58- `Local port forward started from ${ settings . sourceIP } :${ settings . sourcePort } to ${ settings . destinationIP } :${ settings . destinationPort } .`
42+ `Local port forward started from ${ settings . sourceAddr } :${ settings . sourcePort } to ${ settings . destinationAddr } :${ settings . destinationPort } .`
5943 )
44+ this . eventEmitter . emit ( SSHEvent . Connect , settings . sourcePort , settings . destinationAddr )
6045 } )
61- } )
62- . catch ( ( e ) => {
63- console . error ( 'SSH connection error:' , e )
64- } )
65- } ,
66- dynamicPortForward : ( auth : SSHAuth , settings : SSHDynamicPortForwardSettings ) => {
67- try {
68- const client = new NodeSSH ( ) . connect ( {
69- host : auth . host ,
70- username : `${ auth . username } @${ auth . namespace } .${ auth . device } ` ,
71- password : auth . password
72- } )
46+ }
47+ )
48+ } )
49+
50+ this . client . on ( 'error' , ( err ) => {
51+ console . error ( 'SSH connection error:' , err )
52+ this . eventEmitter . emit ( SSHEvent . Error , err )
53+ } )
54+
55+ this . client . connect ( {
56+ host : auth . host ,
57+ username : `${ auth . username } @${ auth . namespace } .${ auth . device } ` ,
58+ password : auth . password
59+ } )
60+ }
61+
62+ disconnect ( ) {
63+ if ( this . client ) {
64+ this . client . end ( )
65+ this . eventEmitter . emit ( SSHEvent . Disconnect )
66+ }
67+ }
68+
69+ onAuth ( callback : any ) {
70+ this . eventEmitter . on ( SSHEvent . Auth , callback )
71+ }
72+
73+ onConnect ( callback : any ) {
74+ this . eventEmitter . on ( SSHEvent . Connect , callback )
75+ }
76+
77+ onError ( callback : any ) {
78+ this . eventEmitter . on ( SSHEvent . Error , callback )
79+ }
80+
81+ onDisconnect ( callback : any ) {
82+ this . eventEmitter . on ( SSHEvent . Disconnect , callback )
83+ }
84+ }
85+
86+ export class SSHConnectionDynamicPortForward implements SSHConnection {
87+ eventEmitter = new SSHEmitter ( )
88+ client = new ssh2 . Client ( )
89+
90+ connect ( auth : SSHConnectionAuth , settings : any ) {
91+ this . client . on ( 'ready' , ( ) => {
92+ console . info ( 'SSH connection established' )
93+ this . eventEmitter . emit ( SSHEvent . Auth )
94+
95+ const server = socks . createServer ( ( info , accept , deny ) => {
96+ this . client . forwardOut (
97+ info . srcAddr ,
98+ info . srcPort ,
99+ info . dstAddr ,
100+ info . dstPort ,
101+ ( err , stream ) => {
102+ if ( err ) {
103+ console . error ( 'SSH forwarding error:' , err )
104+ this . eventEmitter . emit ( SSHEvent . Error , err )
105+ deny ( )
106+ return
107+ }
73108
74- const server = socks . createServer ( ( info , accept , _ ) => {
75- client . then ( ( connection : NodeSSH ) => {
76- connection
77- . forwardOut ( info . srcAddr , info . srcPort , info . dstAddr , info . dstPort )
78- . then ( ( channel : Channel ) => {
79- console . info ( info )
80- const client = accept ( true )
81-
82- channel . pipe ( client ) . pipe ( channel )
83- } )
84- . catch ( ( e ) => {
85- console . error ( e )
86- } )
87- } )
109+ const client = accept ( true )
110+ stream . pipe ( client ) . pipe ( stream )
111+ }
112+ )
88113 } )
89114
90115 server . on ( 'error' , ( err ) => {
91116 console . log ( 'Server error:' , err )
117+ this . eventEmitter . emit ( SSHEvent . Error , err )
92118 } )
93119
94120 server . useAuth ( socks . auth . None ( ) )
95121
96- server . listen ( settings . destinationPort , settings . destinationIP , ( ) => {
97- console . log ( 'Server listening on' , settings . destinationPort , settings . destinationIP )
122+ server . listen ( settings . destinationPort , settings . destinationAddr , ( ) => {
123+ console . log ( 'Server listening on' , settings . destinationPort , settings . destinationAddr )
124+ this . eventEmitter . emit ( SSHEvent . Connect , settings . destinationPort , settings . destinationAddr )
98125 } )
99- } catch ( e ) {
100- console . log ( 'Failed to create server' , e )
126+ } )
127+
128+ this . client . on ( 'error' , ( err ) => {
129+ console . error ( 'SSH connection error:' , err )
130+ this . eventEmitter . emit ( SSHEvent . Error , err )
131+ } )
132+
133+ this . client . connect ( {
134+ host : auth . host ,
135+ username : `${ auth . username } @${ auth . namespace } .${ auth . device } ` ,
136+ password : auth . password
137+ } )
138+ }
139+
140+ disconnect ( ) {
141+ if ( this . client ) {
142+ this . client . end ( )
143+ this . eventEmitter . emit ( SSHEvent . Disconnect )
144+ }
145+ }
146+
147+ onAuth ( callback : any ) {
148+ this . eventEmitter . on ( SSHEvent . Auth , callback )
149+ }
150+
151+ onConnect ( callback : any ) {
152+ this . eventEmitter . on ( SSHEvent . Connect , callback )
153+ }
154+
155+ onError ( callback : any ) {
156+ this . eventEmitter . on ( SSHEvent . Error , callback )
157+ }
158+
159+ onDisconnect ( callback : any ) {
160+ this . eventEmitter . on ( SSHEvent . Disconnect , callback )
161+ }
162+ }
163+
164+ const ssh : SSH = {
165+ localPortForward : ( ) : SSHConnection => {
166+ const localPortForwardInstance = new SSHConnectionLocalPortForward ( )
167+
168+ return {
169+ eventEmitter : localPortForwardInstance . eventEmitter ,
170+ client : localPortForwardInstance . client ,
171+ connect : localPortForwardInstance . connect . bind ( this ) ,
172+ disconnect : localPortForwardInstance . disconnect . bind ( this ) ,
173+ onAuth : localPortForwardInstance . onAuth . bind ( this ) ,
174+ onConnect : localPortForwardInstance . onConnect . bind ( this ) ,
175+ onError : localPortForwardInstance . onError . bind ( this ) ,
176+ onDisconnect : localPortForwardInstance . onDisconnect . bind ( this )
177+ }
178+ } ,
179+ dynamicPortForward : ( ) : SSHConnection => {
180+ const dynamicPortForwardInstance = new SSHConnectionDynamicPortForward ( )
181+
182+ return {
183+ eventEmitter : dynamicPortForwardInstance . eventEmitter ,
184+ client : dynamicPortForwardInstance . client ,
185+ connect : dynamicPortForwardInstance . connect . bind ( this ) ,
186+ disconnect : dynamicPortForwardInstance . disconnect . bind ( this ) ,
187+ onAuth : dynamicPortForwardInstance . onAuth . bind ( this ) ,
188+ onConnect : dynamicPortForwardInstance . onConnect . bind ( this ) ,
189+ onError : dynamicPortForwardInstance . onError . bind ( this ) ,
190+ onDisconnect : dynamicPortForwardInstance . onDisconnect . bind ( this )
101191 }
102192 }
103193}
@@ -118,4 +208,6 @@ if (process.contextIsolated) {
118208 window . electron = electronAPI
119209 // @ts -ignore (define in dts)
120210 window . api = api
211+ // @ts -ignore (define in dts)
212+ window . ssh = ssh
121213}
0 commit comments