@scrypted/server 0.56.0 → 0.58.0
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/dist/cluster/cluster-hash.d.ts +2 -0
- package/dist/cluster/cluster-hash.js +13 -0
- package/dist/cluster/cluster-hash.js.map +1 -0
- package/dist/cluster/connect-rpc-object.d.ts +8 -0
- package/dist/cluster/connect-rpc-object.js +3 -0
- package/dist/cluster/connect-rpc-object.js.map +1 -0
- package/dist/plugin/plugin-remote-worker.js +30 -24
- package/dist/plugin/plugin-remote-worker.js.map +1 -1
- package/dist/rpc.d.ts +2 -2
- package/dist/rpc.js +18 -8
- package/dist/rpc.js.map +1 -1
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +92 -1
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
- package/python/plugin_remote.py +21 -11
- package/python/rpc.py +10 -8
- package/src/cluster/cluster-hash.ts +7 -0
- package/src/cluster/connect-rpc-object.ts +9 -0
- package/src/plugin/plugin-remote-worker.ts +33 -34
- package/src/rpc.ts +19 -8
- package/src/runtime.ts +99 -1
package/src/runtime.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import net from 'net';
|
1
2
|
import { Device, DeviceInformation, DeviceProvider, EngineIOHandler, HttpRequest, HttpRequestHandler, ScryptedDevice, ScryptedInterface, ScryptedInterfaceMethod, ScryptedInterfaceProperty, ScryptedNativeId, ScryptedUser as SU } from '@scrypted/types';
|
2
3
|
import AdmZip from 'adm-zip';
|
3
4
|
import crypto from 'crypto';
|
@@ -34,6 +35,7 @@ import { getPluginVolume } from './plugin/plugin-volume';
|
|
34
35
|
import { NodeForkWorker } from './plugin/runtime/node-fork-worker';
|
35
36
|
import { PythonRuntimeWorker } from './plugin/runtime/python-worker';
|
36
37
|
import { RuntimeWorker, RuntimeWorkerOptions } from './plugin/runtime/runtime-worker';
|
38
|
+
import { ClusterObject } from './cluster/connect-rpc-object';
|
37
39
|
import { getIpAddress, SCRYPTED_INSECURE_PORT, SCRYPTED_SECURE_PORT } from './server-settings';
|
38
40
|
import { AddressSettings } from './services/addresses';
|
39
41
|
import { Alerts } from './services/alerts';
|
@@ -43,6 +45,7 @@ import { PluginComponent } from './services/plugin';
|
|
43
45
|
import { ServiceControl } from './services/service-control';
|
44
46
|
import { UsersService } from './services/users';
|
45
47
|
import { getState, ScryptedStateManager, setState } from './state';
|
48
|
+
import { computeClusterObjectHash } from './cluster/cluster-hash';
|
46
49
|
|
47
50
|
interface DeviceProxyPair {
|
48
51
|
handler: PluginDeviceProxyHandler;
|
@@ -82,6 +85,17 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
82
85
|
})
|
83
86
|
},
|
84
87
|
});
|
88
|
+
connectRPCObjectIO: IOServer = new io.Server({
|
89
|
+
pingTimeout: 120000,
|
90
|
+
perMessageDeflate: true,
|
91
|
+
cors: (req, callback) => {
|
92
|
+
const header = this.getAccessControlAllowOrigin(req.headers);
|
93
|
+
callback(undefined, {
|
94
|
+
origin: header,
|
95
|
+
credentials: true,
|
96
|
+
})
|
97
|
+
},
|
98
|
+
});
|
85
99
|
pluginComponent = new PluginComponent(this);
|
86
100
|
serviceControl = new ServiceControl(this);
|
87
101
|
alerts = new Alerts(this);
|
@@ -120,14 +134,71 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
120
134
|
const cp = spawn(process.env.SHELL, [], {
|
121
135
|
});
|
122
136
|
cp.onData(data => connection.send(data));
|
123
|
-
connection.on('message', message =>
|
137
|
+
connection.on('message', message => {
|
138
|
+
if (Buffer.isBuffer(message)) {
|
139
|
+
cp.write(message.toString());
|
140
|
+
return;
|
141
|
+
}
|
142
|
+
|
143
|
+
try {
|
144
|
+
const parsed = JSON.parse(message.toString());
|
145
|
+
if (parsed.dim) {
|
146
|
+
cp.resize(parsed.dim.cols, parsed.dim.rows);
|
147
|
+
}
|
148
|
+
} catch (e) {
|
149
|
+
// we should only get here if an outdated core plugin
|
150
|
+
// is sending us string data instead of buffer data
|
151
|
+
cp.write(message.toString());
|
152
|
+
}
|
153
|
+
});
|
124
154
|
connection.on('close', () => cp.kill());
|
155
|
+
cp.onExit(() => connection.close());
|
125
156
|
}
|
126
157
|
catch (e) {
|
127
158
|
connection.close();
|
128
159
|
}
|
129
160
|
});
|
130
161
|
|
162
|
+
app.all('/engine.io/connectRPCObject', (req, res) => this.connectRPCObjectHandler(req, res));
|
163
|
+
|
164
|
+
/*
|
165
|
+
* Handle incoming connections that will be
|
166
|
+
* proxied to a connectRPCObject socket.
|
167
|
+
*
|
168
|
+
* Note that the clusterObject hash must be
|
169
|
+
* verified before connecting to the target port.
|
170
|
+
*/
|
171
|
+
this.connectRPCObjectIO.on('connection', connection => {
|
172
|
+
try {
|
173
|
+
const clusterObject: ClusterObject = JSON.parse((connection.request as Request).query.clusterObject as string);
|
174
|
+
const sha256 = computeClusterObjectHash(clusterObject, this.clusterSecret);
|
175
|
+
if (sha256 != clusterObject.sha256) {
|
176
|
+
connection.send({
|
177
|
+
error: 'invalid signature'
|
178
|
+
});
|
179
|
+
connection.close();
|
180
|
+
return;
|
181
|
+
}
|
182
|
+
|
183
|
+
const socket = net.connect(clusterObject.port, '127.0.0.1');
|
184
|
+
socket.on('error', () => connection.close());
|
185
|
+
socket.on('close', () => connection.close());
|
186
|
+
socket.on('data', data => connection.send(data));
|
187
|
+
connection.on('close', () => socket.destroy());
|
188
|
+
connection.on('message', message => {
|
189
|
+
if (typeof message !== 'string') {
|
190
|
+
socket.write(message);
|
191
|
+
}
|
192
|
+
else {
|
193
|
+
console.warn('unexpected string data on engine.io rpc connection. terminating.')
|
194
|
+
connection.close();
|
195
|
+
}
|
196
|
+
});
|
197
|
+
} catch {
|
198
|
+
connection.close();
|
199
|
+
}
|
200
|
+
});
|
201
|
+
|
131
202
|
insecure.on('upgrade', (req, socket, upgradeHead) => {
|
132
203
|
(req as any).upgradeHead = upgradeHead;
|
133
204
|
(app as any).handle(req, {
|
@@ -262,6 +333,33 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
262
333
|
this.shellio.handleRequest(req, res);
|
263
334
|
}
|
264
335
|
|
336
|
+
async connectRPCObjectHandler(req: Request, res: Response) {
|
337
|
+
const isUpgrade = isConnectionUpgrade(req.headers);
|
338
|
+
|
339
|
+
const end = (code: number, message: string) => {
|
340
|
+
if (isUpgrade) {
|
341
|
+
const socket = res.socket;
|
342
|
+
socket.write(`HTTP/1.1 ${code} ${message}\r\n` +
|
343
|
+
'\r\n');
|
344
|
+
socket.destroy();
|
345
|
+
}
|
346
|
+
else {
|
347
|
+
res.status(code);
|
348
|
+
res.send(message);
|
349
|
+
}
|
350
|
+
};
|
351
|
+
|
352
|
+
if (!res.locals.username) {
|
353
|
+
end(401, 'Not Authorized');
|
354
|
+
return;
|
355
|
+
}
|
356
|
+
|
357
|
+
if ((req as any).upgradeHead)
|
358
|
+
this.connectRPCObjectIO.handleUpgrade(req, res.socket, (req as any).upgradeHead)
|
359
|
+
else
|
360
|
+
this.connectRPCObjectIO.handleRequest(req, res);
|
361
|
+
}
|
362
|
+
|
265
363
|
async getEndpointPluginData(req: Request, endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise<HttpPluginData> {
|
266
364
|
const ret = await this.getPluginForEndpoint(endpoint);
|
267
365
|
if (req.url.indexOf('/engine.io/api') !== -1)
|