1
1
import { contextBridge } from 'electron'
2
2
import { electronAPI } from '@electron-toolkit/preload'
3
+ import { SSH , SSHConnection , SSHEvent , SSHEmitter , SSHConnectionAuth } from './index.d'
3
4
import net from 'net'
4
- import { Channel } from 'ssh2'
5
- import { NodeSSH } from 'node-ssh'
6
5
import socks from 'socksv5'
6
+ import ssh2 from 'ssh2'
7
7
8
8
// Custom APIs for renderer
9
9
const api = { }
10
10
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 ( )
18
14
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 )
25
19
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
+ }
30
31
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
+ } )
51
35
52
- return net
53
- . createServer ( ( client : net . Socket ) => {
54
- client . pipe ( connection ) . pipe ( client )
36
+ server . on ( 'close' , ( ) => {
37
+ this . eventEmitter . emit ( SSHEvent . Disconnect )
55
38
} )
56
- . listen ( settings . sourcePort , settings . sourceIP , ( ) => {
39
+
40
+ server . listen ( settings . sourcePort , settings . sourceAddr , ( ) => {
57
41
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 } .`
59
43
)
44
+ this . eventEmitter . emit ( SSHEvent . Connect , settings . sourcePort , settings . destinationAddr )
60
45
} )
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
+ }
73
108
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
+ )
88
113
} )
89
114
90
115
server . on ( 'error' , ( err ) => {
91
116
console . log ( 'Server error:' , err )
117
+ this . eventEmitter . emit ( SSHEvent . Error , err )
92
118
} )
93
119
94
120
server . useAuth ( socks . auth . None ( ) )
95
121
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 )
98
125
} )
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 )
101
191
}
102
192
}
103
193
}
@@ -118,4 +208,6 @@ if (process.contextIsolated) {
118
208
window . electron = electronAPI
119
209
// @ts -ignore (define in dts)
120
210
window . api = api
211
+ // @ts -ignore (define in dts)
212
+ window . ssh = ssh
121
213
}
0 commit comments