bdy 1.7.46-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.yml +23 -0
- package/LICENSE +21 -0
- package/bin/cli.js +5 -0
- package/dockerfile +15 -0
- package/link.sh +3 -0
- package/package.json +39 -0
- package/src/agent/linux.js +127 -0
- package/src/agent/manager.js +404 -0
- package/src/agent/osx.js +150 -0
- package/src/agent/socket/tunnel.js +232 -0
- package/src/agent/socket.js +260 -0
- package/src/agent/system.js +205 -0
- package/src/agent/wait.js +20 -0
- package/src/agent/windows.js +168 -0
- package/src/agent.js +248 -0
- package/src/api/agent.js +95 -0
- package/src/api/buddy.js +131 -0
- package/src/api/socket.js +142 -0
- package/src/cfg.js +228 -0
- package/src/command/agent/disable.js +37 -0
- package/src/command/agent/enable.js +117 -0
- package/src/command/agent/restart.js +28 -0
- package/src/command/agent/run.js +16 -0
- package/src/command/agent/start.js +28 -0
- package/src/command/agent/status.js +45 -0
- package/src/command/agent/stop.js +28 -0
- package/src/command/agent/tunnel/http.js +47 -0
- package/src/command/agent/tunnel/list.js +27 -0
- package/src/command/agent/tunnel/start.js +38 -0
- package/src/command/agent/tunnel/status.js +32 -0
- package/src/command/agent/tunnel/stop.js +30 -0
- package/src/command/agent/tunnel/tcp.js +47 -0
- package/src/command/agent/tunnel/tls.js +47 -0
- package/src/command/agent/tunnel.js +21 -0
- package/src/command/agent/update.js +43 -0
- package/src/command/agent/version.js +23 -0
- package/src/command/agent.js +27 -0
- package/src/command/config/add/http.js +33 -0
- package/src/command/config/add/tcp.js +33 -0
- package/src/command/config/add/tls.js +33 -0
- package/src/command/config/add.js +13 -0
- package/src/command/config/get/region.js +13 -0
- package/src/command/config/get/timeout.js +13 -0
- package/src/command/config/get/token.js +13 -0
- package/src/command/config/get/tunnel.js +21 -0
- package/src/command/config/get/tunnels.js +13 -0
- package/src/command/config/get/whitelist.js +13 -0
- package/src/command/config/get.js +19 -0
- package/src/command/config/remove/tunnel.js +22 -0
- package/src/command/config/remove.js +9 -0
- package/src/command/config/set/region.js +19 -0
- package/src/command/config/set/timeout.js +22 -0
- package/src/command/config/set/token.js +18 -0
- package/src/command/config/set/whitelist.js +19 -0
- package/src/command/config/set.js +15 -0
- package/src/command/config.js +15 -0
- package/src/command/http.js +34 -0
- package/src/command/pre.js +47 -0
- package/src/command/start.js +31 -0
- package/src/command/tcp.js +32 -0
- package/src/command/tls.js +32 -0
- package/src/command/version.js +10 -0
- package/src/format.js +171 -0
- package/src/index.js +32 -0
- package/src/input.js +283 -0
- package/src/logger.js +87 -0
- package/src/output/interactive/tunnel.js +871 -0
- package/src/output/noninteractive/agent/tunnels.js +32 -0
- package/src/output/noninteractive/config/tunnel.js +52 -0
- package/src/output/noninteractive/config/tunnels.js +19 -0
- package/src/output/noninteractive/tunnel.js +79 -0
- package/src/output.js +136 -0
- package/src/server/cert.js +51 -0
- package/src/server/http1.js +79 -0
- package/src/server/http2.js +79 -0
- package/src/server/sftp.js +474 -0
- package/src/server/ssh.js +107 -0
- package/src/server/tls.js +41 -0
- package/src/ssh/client.js +196 -0
- package/src/texts.js +447 -0
- package/src/tunnel/agent.js +100 -0
- package/src/tunnel/compression.js +32 -0
- package/src/tunnel/dns.js +55 -0
- package/src/tunnel/html/404.html +129 -0
- package/src/tunnel/html/503.html +136 -0
- package/src/tunnel/html.js +32 -0
- package/src/tunnel/http/log.js +204 -0
- package/src/tunnel/http/serve.js +127 -0
- package/src/tunnel/http/stream.js +46 -0
- package/src/tunnel/http.js +406 -0
- package/src/tunnel/identification.js +95 -0
- package/src/tunnel/latency.js +63 -0
- package/src/tunnel/tcp.js +71 -0
- package/src/tunnel.js +696 -0
- package/src/utils.js +496 -0
- package/unlink.sh +3 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
const logger = require('../logger');
|
|
2
|
+
const Ssh2 = require('ssh2');
|
|
3
|
+
const fs = require('fs/promises');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const { flagsToString, STATUS_CODE } = Ssh2.utils.sftp;
|
|
7
|
+
|
|
8
|
+
class ServerSftp {
|
|
9
|
+
constructor(sftp) {
|
|
10
|
+
logger.debug('Creating sftp server');
|
|
11
|
+
this.openHandlers = new Map();
|
|
12
|
+
this.count = 0;
|
|
13
|
+
this.sftp = sftp;
|
|
14
|
+
this.sftp.on('OPEN', (reqId, fileName, flags, attrs) => this.open(reqId, fileName, flags, attrs));
|
|
15
|
+
this.sftp.on('READ', (reqId, handle, offset, length) => this.read(reqId, handle, offset, length));
|
|
16
|
+
this.sftp.on('WRITE', (reqId, handle, offset, data) => this.write(reqId, handle, offset, data));
|
|
17
|
+
this.sftp.on('FSTAT', (reqId, handle) => this.fstat(reqId, handle));
|
|
18
|
+
this.sftp.on('FSETSTAT', (reqId, handle, attrs) => this.fsetstat(reqId, handle, attrs));
|
|
19
|
+
this.sftp.on('CLOSE', (reqId, handle) => this.close(reqId, handle));
|
|
20
|
+
this.sftp.on('OPENDIR', (reqId, path) => this.opendir(reqId, path));
|
|
21
|
+
this.sftp.on('READDIR', (reqId, handle) => this.readdir(reqId, handle));
|
|
22
|
+
this.sftp.on('LSTAT', (reqId, path) => this.lstat(reqId, path));
|
|
23
|
+
this.sftp.on('STAT', (reqId, path) => this.stat(reqId, path));
|
|
24
|
+
this.sftp.on('REMOVE', (reqId, path) => this.remove(reqId, path));
|
|
25
|
+
this.sftp.on('RMDIR', (reqId, path) => this.rmdir(reqId, path));
|
|
26
|
+
this.sftp.on('REALPATH', (reqId, path) => this.realpath(reqId, path));
|
|
27
|
+
this.sftp.on('READLINK', (reqId, path) => this.readlink(reqId, path));
|
|
28
|
+
this.sftp.on('SETSTAT', (reqId, path, attrs) => this.setstat(reqId, path, attrs));
|
|
29
|
+
this.sftp.on('MKDIR', (reqId, path, attrs) => this.mkdir(reqId, path, attrs));
|
|
30
|
+
this.sftp.on('RENAME', (reqId, oldPath, newPath) => this.rename(reqId, oldPath, newPath));
|
|
31
|
+
this.sftp.on('SYMLINK', (reqId, linkPath, targetPath) => this.symlink(reqId, linkPath, targetPath));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async open(reqId, fileName, flags, attrs) {
|
|
35
|
+
logger.debug(`SFTP want to open file ${fileName}`);
|
|
36
|
+
try {
|
|
37
|
+
const flag = flagsToString(flags);
|
|
38
|
+
if (!flag) {
|
|
39
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const id = ++this.count;
|
|
43
|
+
const handle = Buffer.alloc(4);
|
|
44
|
+
handle.writeUint32BE(id, 0);
|
|
45
|
+
const fd = await fs.open(fileName, flag);
|
|
46
|
+
this.openHandlers.set(id, fd);
|
|
47
|
+
await this._fsetstat(fd, attrs);
|
|
48
|
+
logger.debug('SFTP open file succeed');
|
|
49
|
+
this.sftp.handle(reqId, handle);
|
|
50
|
+
} catch(err) {
|
|
51
|
+
logger.debug('SFTP open file failed');
|
|
52
|
+
logger.debug(err);
|
|
53
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async read(reqId, handle, offset, length) {
|
|
58
|
+
logger.debug('SFTP want to read file');
|
|
59
|
+
try {
|
|
60
|
+
const id = handle.readUInt32BE(0);
|
|
61
|
+
if (!this.openHandlers.has(id)) {
|
|
62
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const fd = this.openHandlers.get(id);
|
|
66
|
+
const buffer = Buffer.alloc(length);
|
|
67
|
+
const r = await fd.read(buffer, {
|
|
68
|
+
offset: 0,
|
|
69
|
+
length,
|
|
70
|
+
position: offset,
|
|
71
|
+
});
|
|
72
|
+
logger.debug('SFTP read file succeed');
|
|
73
|
+
if (r.bytesRead <= 0) {
|
|
74
|
+
this.sftp.status(reqId, STATUS_CODE.EOF);
|
|
75
|
+
} else if (r.bytesRead < length) {
|
|
76
|
+
// strip buffer
|
|
77
|
+
const buffer2 = Buffer.alloc(r.bytesRead);
|
|
78
|
+
buffer.copy(buffer2, 0, 0, r.bytesRead);
|
|
79
|
+
this.sftp.data(reqId, buffer2);
|
|
80
|
+
} else {
|
|
81
|
+
this.sftp.data(reqId, buffer);
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
logger.debug('SFTP read file failed');
|
|
85
|
+
logger.debug(err);
|
|
86
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async write(reqId, handle, offset, data) {
|
|
91
|
+
logger.debug('SFTP want to write file');
|
|
92
|
+
try {
|
|
93
|
+
const id = handle.readUInt32BE(0);
|
|
94
|
+
if (!this.openHandlers.has(id)) {
|
|
95
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const fd = this.openHandlers.get(id);
|
|
99
|
+
await fd.write(data, 0, data.length, offset);
|
|
100
|
+
logger.debug('SFTP write file succeed');
|
|
101
|
+
this.sftp.status(reqId, STATUS_CODE.OK);
|
|
102
|
+
} catch(err) {
|
|
103
|
+
logger.debug('SFTP write file failed');
|
|
104
|
+
logger.debug(err);
|
|
105
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async close(reqId, handle) {
|
|
110
|
+
logger.debug('SFTP want to close handler');
|
|
111
|
+
try {
|
|
112
|
+
const id = handle.readUInt32BE(0);
|
|
113
|
+
if (!this.openHandlers.has(id)) {
|
|
114
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const fd = this.openHandlers.get(id);
|
|
118
|
+
await fd.close();
|
|
119
|
+
this.openHandlers.delete(id);
|
|
120
|
+
logger.debug('SFTP closed handler succeed');
|
|
121
|
+
this.sftp.status(reqId, STATUS_CODE.OK);
|
|
122
|
+
} catch(err) {
|
|
123
|
+
logger.debug('SFTP close handler failed');
|
|
124
|
+
logger.debug(err);
|
|
125
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async fstat(reqId, handle) {
|
|
130
|
+
logger.debug('SFTP want to stat file');
|
|
131
|
+
try {
|
|
132
|
+
const id = handle.readUInt32BE(0);
|
|
133
|
+
if (!this.openHandlers.has(id)) {
|
|
134
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const fd = this.openHandlers.get(id);
|
|
138
|
+
const attrs = await fd.stat();
|
|
139
|
+
logger.debug('SFTP stat file succeed');
|
|
140
|
+
this.sftp.attrs(reqId, attrs);
|
|
141
|
+
} catch(err) {
|
|
142
|
+
logger.debug('SFTP stat file failed');
|
|
143
|
+
logger.debug(err);
|
|
144
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async _fsetstat(fd, attrs) {
|
|
149
|
+
if (Number.isInteger(attrs.uid) && Number.isInteger(attrs.gid)) {
|
|
150
|
+
try {
|
|
151
|
+
logger.debug(`SFTP want to chown with uid: ${attrs.uid}, gid: ${attrs.gid}`);
|
|
152
|
+
await fd.chown(attrs.uid, attrs.gid);
|
|
153
|
+
logger.debug('SFTP chown succeed');
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logger.debug('SFTP chown failed');
|
|
156
|
+
logger.debug(err);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (attrs.mode) {
|
|
160
|
+
try {
|
|
161
|
+
logger.debug(`SFTP want to chmod with mode: ${attrs.mode}`);
|
|
162
|
+
await fd.chmod(attrs.mode);
|
|
163
|
+
logger.debug('SFTP chmod succeed');
|
|
164
|
+
} catch (err) {
|
|
165
|
+
logger.debug('SFTP chmod failed');
|
|
166
|
+
logger.debug(err);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (attrs.atime && attrs.mtime) {
|
|
170
|
+
try {
|
|
171
|
+
logger.debug(`SFTP want to utimes with atime: ${attrs.atime}, mtime: ${attrs.mtime}`);
|
|
172
|
+
await fd.utimes(attrs.atime, attrs.mtime);
|
|
173
|
+
logger.debug('SFTP utimes succeed');
|
|
174
|
+
} catch (err) {
|
|
175
|
+
logger.debug('SFTP utimes failed');
|
|
176
|
+
logger.debug(err);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async fsetstat(reqId, handle, attrs) {
|
|
182
|
+
logger.debug('SFTP want to set attrs of file');
|
|
183
|
+
try {
|
|
184
|
+
const id = handle.readUInt32BE(0);
|
|
185
|
+
if (!this.openHandlers.has(id)) {
|
|
186
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const fd = this.openHandlers.get(id);
|
|
190
|
+
await this._fsetstat(fd, attrs);
|
|
191
|
+
logger.debug('SFTP set attrs of file succeed');
|
|
192
|
+
this.sftp.status(reqId, STATUS_CODE.OK);
|
|
193
|
+
} catch(err) {
|
|
194
|
+
logger.debug('SFTP set attrs of file failed');
|
|
195
|
+
logger.debug(err);
|
|
196
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async opendir(reqId, path) {
|
|
201
|
+
logger.debug(`SFTP want to open directory ${path}`);
|
|
202
|
+
try {
|
|
203
|
+
const id = ++this.count;
|
|
204
|
+
const handle = Buffer.alloc(4);
|
|
205
|
+
handle.writeUint32BE(id, 0);
|
|
206
|
+
const fd = await fs.opendir(path);
|
|
207
|
+
this.openHandlers.set(id, fd);
|
|
208
|
+
logger.debug('SFTP open directory succeed');
|
|
209
|
+
this.sftp.handle(reqId, handle);
|
|
210
|
+
} catch(err) {
|
|
211
|
+
logger.debug('SFTP open directory failed');
|
|
212
|
+
logger.debug(err);
|
|
213
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
formatMode(mode) {
|
|
219
|
+
const types = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'];
|
|
220
|
+
return [types[mode >> 6 & 7], types[mode >> 3 & 7], types[mode & 7]].join('');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
async longname(fullPath, name, stats) {
|
|
225
|
+
// -rw-r--r-- 1 Michal staff 160543 Jul 29 09:31 cli.log
|
|
226
|
+
const isDir = stats.isDirectory();
|
|
227
|
+
const isLink = stats.isSymbolicLink();
|
|
228
|
+
let displayName = name;
|
|
229
|
+
if (isLink) {
|
|
230
|
+
const link = await fs.readlink(fullPath);
|
|
231
|
+
displayName = `${name} -> ${link}`;
|
|
232
|
+
}
|
|
233
|
+
return [
|
|
234
|
+
isDir ? 'd' : (isLink ? 'l' : '-'),
|
|
235
|
+
this.formatMode(stats.mode),
|
|
236
|
+
' ',
|
|
237
|
+
(stats.nlink || 1).toString().padEnd(4),
|
|
238
|
+
' ',
|
|
239
|
+
stats.uid.toString().padEnd(4),
|
|
240
|
+
' ',
|
|
241
|
+
stats.gid.toString().padEnd(4),
|
|
242
|
+
' ',
|
|
243
|
+
stats.size.toString().padStart(10),
|
|
244
|
+
' ',
|
|
245
|
+
stats.mtime.toISOString().slice(0, 19).replace('T', ' '),
|
|
246
|
+
' ',
|
|
247
|
+
displayName
|
|
248
|
+
].join('');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async readdir(reqId, handle) {
|
|
252
|
+
logger.debug('SFTP want to read directory');
|
|
253
|
+
try {
|
|
254
|
+
const id = handle.readUInt32BE(0);
|
|
255
|
+
if (!this.openHandlers.has(id)) {
|
|
256
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const fd = this.openHandlers.get(id);
|
|
260
|
+
const dir = await fd.read();
|
|
261
|
+
if (!dir) {
|
|
262
|
+
logger.debug('SFTP read directory ended');
|
|
263
|
+
this.sftp.status(reqId, STATUS_CODE.EOF);
|
|
264
|
+
} else {
|
|
265
|
+
const name = dir.name;
|
|
266
|
+
const fullPath = path.join(fd.path, name);
|
|
267
|
+
const stat = await fs.lstat(fullPath);
|
|
268
|
+
const longname = await this.longname(fullPath, name, stat);
|
|
269
|
+
logger.debug('SFTP read directory succeed');
|
|
270
|
+
this.sftp.name(reqId, {
|
|
271
|
+
filename: name,
|
|
272
|
+
longname,
|
|
273
|
+
attrs: stat
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
} catch(err) {
|
|
277
|
+
logger.debug('SFTP read directory failed');
|
|
278
|
+
logger.debug(err);
|
|
279
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async stat(reqId, path) {
|
|
284
|
+
logger.debug(`SFTP want to stat ${path}`);
|
|
285
|
+
try {
|
|
286
|
+
const attrs = await fs.stat(path);
|
|
287
|
+
logger.debug('SFTP stat path succeed');
|
|
288
|
+
this.sftp.attrs(reqId, attrs);
|
|
289
|
+
} catch(err) {
|
|
290
|
+
logger.debug('SFTP stat path failed');
|
|
291
|
+
logger.debug(err);
|
|
292
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async remove(reqId, path) {
|
|
297
|
+
logger.debug(`SFTP want to remove file ${path}`);
|
|
298
|
+
try {
|
|
299
|
+
await fs.rm(path);
|
|
300
|
+
logger.debug('SFTP removed file succeed');
|
|
301
|
+
this.sftp.status(reqId, STATUS_CODE.OK);
|
|
302
|
+
} catch(err) {
|
|
303
|
+
logger.debug('SFTP remove file failed');
|
|
304
|
+
logger.debug(err);
|
|
305
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async rmdir(reqId, path) {
|
|
310
|
+
logger.debug(`SFTP want to remove directory ${path}`);
|
|
311
|
+
try {
|
|
312
|
+
await fs.rmdir(path, {
|
|
313
|
+
recursive: true
|
|
314
|
+
});
|
|
315
|
+
logger.debug('SFTP remove directory succeed');
|
|
316
|
+
this.sftp.status(reqId, STATUS_CODE.OK);
|
|
317
|
+
} catch(err) {
|
|
318
|
+
logger.debug('SFTP remove directory failed');
|
|
319
|
+
logger.debug(err);
|
|
320
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async realpath(reqId, path) {
|
|
325
|
+
logger.debug(`SFTP want realpath ${path}`);
|
|
326
|
+
try {
|
|
327
|
+
const realPath = await fs.realpath(path);
|
|
328
|
+
logger.debug('SFTP realpath succeed');
|
|
329
|
+
this.sftp.name(reqId, [{
|
|
330
|
+
filename: realPath,
|
|
331
|
+
}]);
|
|
332
|
+
} catch(err) {
|
|
333
|
+
logger.debug('SFTP realpath failed');
|
|
334
|
+
logger.debug(err);
|
|
335
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async readlink(reqId, path) {
|
|
340
|
+
logger.debug(`SFTP want readlink ${path}`);
|
|
341
|
+
try {
|
|
342
|
+
const realPath = await fs.readlink(path);
|
|
343
|
+
logger.debug('SFTP readlink succeed');
|
|
344
|
+
this.sftp.name(reqId, [{
|
|
345
|
+
filename: realPath,
|
|
346
|
+
}]);
|
|
347
|
+
} catch(err) {
|
|
348
|
+
logger.debug('SFTP readlink failed');
|
|
349
|
+
logger.debug(err);
|
|
350
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async _setstat(path, attrs) {
|
|
355
|
+
if (Number.isInteger(attrs.uid) && Number.isInteger(attrs.gid)) {
|
|
356
|
+
try {
|
|
357
|
+
logger.debug(`SFTP want chown with uid: ${attrs.uid}, gid: ${attrs.gid}`);
|
|
358
|
+
await fs.chown(path, attrs.uid, attrs.gid);
|
|
359
|
+
logger.debug('SFTP chown succeed');
|
|
360
|
+
} catch (err) {
|
|
361
|
+
logger.debug('SFTP chown failed');
|
|
362
|
+
logger.debug(err);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (attrs.mode) {
|
|
366
|
+
try {
|
|
367
|
+
logger.debug(`SFTP want to chmod with mode: ${attrs.mode}`);
|
|
368
|
+
await fs.chmod(path, attrs.mode);
|
|
369
|
+
logger.debug('SFTP chmod succeed');
|
|
370
|
+
} catch (err) {
|
|
371
|
+
logger.debug('SFTP chmod failed');
|
|
372
|
+
logger.debug(err);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (attrs.atime && attrs.mtime) {
|
|
376
|
+
try {
|
|
377
|
+
logger.debug(`SFTP want to utimes with atime: ${attrs.atime}, mtime: ${attrs.mtime}`);
|
|
378
|
+
await fs.utimes(path, attrs.atime, attrs.mtime);
|
|
379
|
+
logger.debug('SFTP utimes succeed');
|
|
380
|
+
} catch (err) {
|
|
381
|
+
logger.debug('SFTP utimes failed');
|
|
382
|
+
logger.debug(err);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async setstat(reqId, path, attrs) {
|
|
388
|
+
logger.debug(`SFTP want to set attrs ${path}`);
|
|
389
|
+
try {
|
|
390
|
+
await this._setstat(path, attrs);
|
|
391
|
+
logger.debug('SFTP set attrs succeed');
|
|
392
|
+
this.sftp.status(reqId, STATUS_CODE.OK);
|
|
393
|
+
} catch (err) {
|
|
394
|
+
logger.debug('SFTP set attrs failed');
|
|
395
|
+
logger.debug(err);
|
|
396
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async mkdir(reqId, path, attrs) {
|
|
401
|
+
logger.debug(`SFTP want to create directory ${path}`);
|
|
402
|
+
try {
|
|
403
|
+
await fs.mkdir(path);
|
|
404
|
+
await this._setstat(path, attrs);
|
|
405
|
+
logger.debug('SFTP create directory succeed');
|
|
406
|
+
this.sftp.status(reqId, STATUS_CODE.OK);
|
|
407
|
+
} catch (err) {
|
|
408
|
+
logger.debug('SFTP create directory failed');
|
|
409
|
+
logger.debug(err);
|
|
410
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async rename(reqId, oldPath, newPath) {
|
|
415
|
+
logger.debug(`SFTP want to rename path ${oldPath} to ${newPath}`);
|
|
416
|
+
try {
|
|
417
|
+
await fs.rename(oldPath, newPath);
|
|
418
|
+
logger.debug('SFTP rename path succeed');
|
|
419
|
+
this.sftp.status(reqId, STATUS_CODE.OK);
|
|
420
|
+
} catch (err) {
|
|
421
|
+
logger.debug('SFTP rename path failed');
|
|
422
|
+
logger.debug(err);
|
|
423
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async symlink(reqId, linkPath, targetPath) {
|
|
428
|
+
logger.debug(`SFTP want to symlink ${targetPath} at ${linkPath}`);
|
|
429
|
+
try {
|
|
430
|
+
await fs.symlink(targetPath, linkPath);
|
|
431
|
+
logger.debug('SFTP symlink succeed');
|
|
432
|
+
this.sftp.status(reqId, STATUS_CODE.OK);
|
|
433
|
+
} catch (err) {
|
|
434
|
+
logger.debug('SFTP symlink failed');
|
|
435
|
+
logger.debug(err);
|
|
436
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async lstat(reqId, path) {
|
|
441
|
+
logger.debug(`SFTP want to lstat ${path}`);
|
|
442
|
+
try {
|
|
443
|
+
const attrs = await fs.lstat(path);
|
|
444
|
+
logger.debug('SFTP lstat path succeed');
|
|
445
|
+
this.sftp.attrs(reqId, attrs);
|
|
446
|
+
} catch (err) {
|
|
447
|
+
logger.debug('SFTP lstat path failed');
|
|
448
|
+
logger.debug(err);
|
|
449
|
+
this.sftp.status(reqId, STATUS_CODE.FAILURE);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
destroy() {
|
|
454
|
+
if (this.sftp) {
|
|
455
|
+
this.sftp.removeAllListeners();
|
|
456
|
+
this.sftp.end();
|
|
457
|
+
this.sftp = null;
|
|
458
|
+
}
|
|
459
|
+
if (this.openHandlers) {
|
|
460
|
+
this.openHandlers.forEach((fd) => {
|
|
461
|
+
try {
|
|
462
|
+
fd.close();
|
|
463
|
+
} catch {
|
|
464
|
+
// do nothing
|
|
465
|
+
}
|
|
466
|
+
fd = null;
|
|
467
|
+
});
|
|
468
|
+
this.openHandlers = null;
|
|
469
|
+
}
|
|
470
|
+
this.count = 0;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
module.exports = ServerSftp;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
const Ssh2 = require('ssh2');
|
|
3
|
+
const logger = require('../logger.js');
|
|
4
|
+
const { exec } = require('child_process');
|
|
5
|
+
const ServerSftp = require('./sftp');
|
|
6
|
+
|
|
7
|
+
class ServerSsh extends EventEmitter {
|
|
8
|
+
constructor(login, password, hostKey) {
|
|
9
|
+
super();
|
|
10
|
+
this.login = login;
|
|
11
|
+
this.password = password;
|
|
12
|
+
this.server = new Ssh2.Server({
|
|
13
|
+
keepAlive: true,
|
|
14
|
+
hostKeys: [hostKey],
|
|
15
|
+
ident: 'ssh2 server'
|
|
16
|
+
}, (client) => this.processClient(client));
|
|
17
|
+
this.server.listen();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
stop() {
|
|
21
|
+
this.server.close();
|
|
22
|
+
this.server.removeAllListeners();
|
|
23
|
+
this.server = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
handleSshTunnel(stream) {
|
|
27
|
+
this.server.injectSocket(stream);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
processClient(client) {
|
|
31
|
+
client.setNoDelay();
|
|
32
|
+
client.on('authentication', async (ctx) => {
|
|
33
|
+
if (ctx.method !== 'password') {
|
|
34
|
+
// only password
|
|
35
|
+
ctx.reject();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (ctx.username !== this.login) {
|
|
39
|
+
ctx.reject();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (ctx.password !== this.password) {
|
|
43
|
+
ctx.reject();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
ctx.accept();
|
|
47
|
+
});
|
|
48
|
+
client.on('close', () => {
|
|
49
|
+
client.removeAllListeners();
|
|
50
|
+
client = null;
|
|
51
|
+
});
|
|
52
|
+
client.on('error', (err) => {
|
|
53
|
+
logger.debug('Error on ssh server client');
|
|
54
|
+
logger.debug(err);
|
|
55
|
+
client.end();
|
|
56
|
+
});
|
|
57
|
+
client.on('session', (accept) => {
|
|
58
|
+
let session = accept();
|
|
59
|
+
session.env = {};
|
|
60
|
+
const closeSession = () => {
|
|
61
|
+
session.removeAllListeners();
|
|
62
|
+
if (session.sftp) {
|
|
63
|
+
session.sftp.destroy();
|
|
64
|
+
session.sftp = null;
|
|
65
|
+
}
|
|
66
|
+
session = null;
|
|
67
|
+
if (client) client.end();
|
|
68
|
+
};
|
|
69
|
+
session.on('close', closeSession);
|
|
70
|
+
session.on('end', closeSession);
|
|
71
|
+
session.on('exec', (accept, reject, info) => {
|
|
72
|
+
if (!info.command) {
|
|
73
|
+
if (reject) reject();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
let stream;
|
|
77
|
+
if (accept) stream = accept();
|
|
78
|
+
exec(info.command, {
|
|
79
|
+
env: session.env
|
|
80
|
+
}, (err, stdout, stderr) => {
|
|
81
|
+
if (stream) {
|
|
82
|
+
stream.stderr.write(stderr);
|
|
83
|
+
stream.write(stdout);
|
|
84
|
+
stream.exit(!err ? 0 : 1);
|
|
85
|
+
stream.end();
|
|
86
|
+
stream = null;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
session.on('env', (accept, reject, info) => {
|
|
91
|
+
Object.keys(info || {}).forEach((key) => {
|
|
92
|
+
session.env[key] = info[key];
|
|
93
|
+
});
|
|
94
|
+
if (accept) accept();
|
|
95
|
+
});
|
|
96
|
+
session.on('sftp', (accept, reject) => {
|
|
97
|
+
if (!session.sftp) {
|
|
98
|
+
session.sftp = new ServerSftp(accept());
|
|
99
|
+
} else if (reject) {
|
|
100
|
+
reject();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = ServerSsh;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
const tls = require('tls');
|
|
3
|
+
const ServerCert = require('./cert.js');
|
|
4
|
+
const { TLS_SOCKET } = require('../utils');
|
|
5
|
+
|
|
6
|
+
class ServerTls extends EventEmitter {
|
|
7
|
+
constructor(key, cert, ca) {
|
|
8
|
+
super();
|
|
9
|
+
this.key = key;
|
|
10
|
+
this.cert = cert;
|
|
11
|
+
this.ca = ca;
|
|
12
|
+
if (!this.key || !this.cert) {
|
|
13
|
+
const c = new ServerCert();
|
|
14
|
+
this.key = c.key;
|
|
15
|
+
this.cert = c.cert;
|
|
16
|
+
}
|
|
17
|
+
this.server = tls.createServer({
|
|
18
|
+
handshakeTimeout: 60000,
|
|
19
|
+
key: this.key,
|
|
20
|
+
cert: this.cert,
|
|
21
|
+
ca: this.ca,
|
|
22
|
+
rejectUnauthorized: !!this.ca,
|
|
23
|
+
requestCert: !!this.ca
|
|
24
|
+
}, (socket) => this.processSocket(socket));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async processSocket(socket) {
|
|
28
|
+
this.emit(TLS_SOCKET, socket);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
handleSshTunnel(channel) {
|
|
32
|
+
this.server.emit('connection', channel);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
stop() {
|
|
36
|
+
this.server.removeAllListeners();
|
|
37
|
+
this.server = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = ServerTls;
|