iosignal 4.10.0 → 5.0.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/io.d.ts +3 -1
- package/dist/io.js +3 -3
- package/dist/io.js.map +1 -1
- package/dist/io.min.js +4 -4
- package/dist/io.min.js.map +1 -1
- package/dist/iosignal.js +87 -482
- package/dist/types/client/IOCore.d.ts +1 -0
- package/dist/types/client/browser/IOWebSocket.d.ts +1 -1
- package/dist/types/common/constants.d.ts +7 -6
- package/package.json +2 -2
- package/src/auth/BohoAuth.js +12 -8
- package/src/client/IOCore.js +21 -21
- package/src/common/constants.js +10 -17
- package/src/server/Manager.js +7 -7
- package/src/server/Metrics.js +155 -0
- package/src/server/RemoteCore.js +6 -19
- package/src/server/Server.js +13 -13
- package/src/services/sudoService.js +5 -5
|
@@ -74,6 +74,6 @@ export default class IO extends IOCore {
|
|
|
74
74
|
socket_send(data: BufferSource): void;
|
|
75
75
|
}
|
|
76
76
|
export type Boho = import("boho").Boho;
|
|
77
|
-
export type MBP =
|
|
77
|
+
export type MBP = typeof import("meta-buffer-pack");
|
|
78
78
|
export type Buffer = import("boho").Buffer;
|
|
79
79
|
import { IOCore } from "../IOCore.js";
|
|
@@ -2,11 +2,14 @@ export type STATE = {
|
|
|
2
2
|
CONNECTING: number;
|
|
3
3
|
OPEN: number;
|
|
4
4
|
SERVER_READY: number;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
READY: number;
|
|
5
|
+
AUTH_REQ: number;
|
|
6
|
+
AUTH_RES: number;
|
|
8
7
|
AUTH_FAIL: number;
|
|
9
8
|
AUTH_CLEAR: number;
|
|
9
|
+
CID_REQ: number;
|
|
10
|
+
CID_RES: number;
|
|
11
|
+
READY: number;
|
|
12
|
+
CLOSING: number;
|
|
10
13
|
CLOSED: number;
|
|
11
14
|
STOP: number;
|
|
12
15
|
REDIRECTING: number;
|
|
@@ -16,9 +19,7 @@ export namespace STATE {
|
|
|
16
19
|
let OPEN: number;
|
|
17
20
|
let SERVER_READY: number;
|
|
18
21
|
let AUTH_REQ: number;
|
|
19
|
-
let
|
|
20
|
-
let AUTH_HMAC: number;
|
|
21
|
-
let AUTH_ACK: number;
|
|
22
|
+
let AUTH_RES: number;
|
|
22
23
|
let AUTH_FAIL: number;
|
|
23
24
|
let AUTH_CLEAR: number;
|
|
24
25
|
let CID_REQ: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iosignal",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "iosignal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"typescript": "^5.8.3"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"boho": "^
|
|
40
|
+
"boho": "^2.0.0",
|
|
41
41
|
"eventemitter3": "^5.0.1",
|
|
42
42
|
"meta-buffer-pack": "^2.1.0",
|
|
43
43
|
"ws": "^8.18.3"
|
package/src/auth/BohoAuth.js
CHANGED
|
@@ -34,10 +34,10 @@ export class BohoAuth {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
async
|
|
37
|
+
async verify_auth_req(auth_req, peer) {
|
|
38
38
|
try {
|
|
39
39
|
//1. unpack
|
|
40
|
-
let infoPack = MBP.unpack(
|
|
40
|
+
let infoPack = MBP.unpack(auth_req, Boho.Meta.AUTH_REQ)
|
|
41
41
|
if (!infoPack) {
|
|
42
42
|
this.send_auth_fail(peer, 'unpack auth_pack fail');
|
|
43
43
|
return
|
|
@@ -75,8 +75,8 @@ export class BohoAuth {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
//3. check hmac
|
|
78
|
-
let
|
|
79
|
-
if (!
|
|
78
|
+
let auth_res = peer.boho.verify_auth_req(infoPack)
|
|
79
|
+
if (!auth_res) {
|
|
80
80
|
this.send_auth_fail(peer, 'hmac dismatched');
|
|
81
81
|
return
|
|
82
82
|
}
|
|
@@ -99,8 +99,12 @@ export class BohoAuth {
|
|
|
99
99
|
)
|
|
100
100
|
if (this.keepOldConnection) { // default true
|
|
101
101
|
// keep old connection. reject new connection by sending auth_fail signal.
|
|
102
|
-
this.send_auth_fail(peer, 'duplicate login')
|
|
103
102
|
// peer.send(authClearSignal)
|
|
103
|
+
if( peer.socketType == 'websocket'){
|
|
104
|
+
this.send_auth_fail(peer, 'duplicate login')
|
|
105
|
+
}else{
|
|
106
|
+
peer.close(); //arduino
|
|
107
|
+
}
|
|
104
108
|
} else {
|
|
105
109
|
// accept new connection. stop the old connection by sending auth_clear signal.
|
|
106
110
|
old.send(authClearSignal)
|
|
@@ -145,8 +149,8 @@ export class BohoAuth {
|
|
|
145
149
|
//9. set cid2remote
|
|
146
150
|
peer.manager.cid2remote.set(peer.cid, peer)
|
|
147
151
|
//10. send ack.
|
|
148
|
-
peer.send(
|
|
149
|
-
peer.setState(STATE.
|
|
152
|
+
peer.send(auth_res)
|
|
153
|
+
peer.setState(STATE.AUTH_RES)
|
|
150
154
|
if (this.authLogger) {
|
|
151
155
|
let peerInfo = `OK #${peer.ssid} cid: ${peer.cid} did:${peer.did}`
|
|
152
156
|
if (peer.isAdmin) peerInfo = "#ADMIN# " + peerInfo
|
|
@@ -154,7 +158,7 @@ export class BohoAuth {
|
|
|
154
158
|
}
|
|
155
159
|
return authInfo
|
|
156
160
|
} catch (error) {
|
|
157
|
-
console.log('
|
|
161
|
+
console.log('verify_auth_req error', error)
|
|
158
162
|
this.send_auth_fail(peer, '[BohoAuth]caught: unknown error:' + error);
|
|
159
163
|
}
|
|
160
164
|
}
|
package/src/client/IOCore.js
CHANGED
|
@@ -114,6 +114,8 @@ export class IOCore extends EventEmitter {
|
|
|
114
114
|
* @type {Boho}
|
|
115
115
|
*/
|
|
116
116
|
this.boho = new Boho()
|
|
117
|
+
|
|
118
|
+
this.serverTimeNonce = Buffer.alloc( Boho.MetaSize.SERVER_TIME_NONCE );
|
|
117
119
|
/**
|
|
118
120
|
* Indicates if the connection is TLS (wss).
|
|
119
121
|
* @type {boolean}
|
|
@@ -345,10 +347,13 @@ export class IOCore extends EventEmitter {
|
|
|
345
347
|
* @returns {this}
|
|
346
348
|
*/
|
|
347
349
|
login(id, key) {
|
|
348
|
-
this.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
350
|
+
if( this.serverTimeNonce ){
|
|
351
|
+
console.log('iosignal.login serverTimeNonce', this.serverTimeNonce)
|
|
352
|
+
this.auth(id, key)
|
|
353
|
+
this.useAuth = true
|
|
354
|
+
let auth_pack = this.boho.auth_req(this.serverTimeNonce )
|
|
355
|
+
this.send(auth_pack)
|
|
356
|
+
}
|
|
352
357
|
return this
|
|
353
358
|
}
|
|
354
359
|
|
|
@@ -497,12 +502,15 @@ export class IOCore extends EventEmitter {
|
|
|
497
502
|
this.redirect(url)
|
|
498
503
|
break;
|
|
499
504
|
|
|
500
|
-
case
|
|
505
|
+
case Boho.BohoMsg.SERVER_TIME_NONCE: // SERVER_READY
|
|
501
506
|
this.stateChange('server_ready', 'server_ready')
|
|
502
507
|
if (this.useAuth) {
|
|
503
|
-
this.send(this.boho.auth_req())
|
|
504
|
-
|
|
508
|
+
this.send(this.boho.auth_req(buffer))
|
|
509
|
+
this.stateChange('auth_req','auth_req')
|
|
510
|
+
// CID_REQ will be called, after auth_res.
|
|
505
511
|
} else {
|
|
512
|
+
// keep server_time_nonce for manual login()
|
|
513
|
+
buffer.copy( this.serverTimeNonce);
|
|
506
514
|
// CID_REQ here, if not using auth.
|
|
507
515
|
this.send(Buffer.from([IOMsg.CID_REQ]))
|
|
508
516
|
}
|
|
@@ -549,6 +557,7 @@ export class IOCore extends EventEmitter {
|
|
|
549
557
|
> cid_sub message: tag includes cid and @ both : 'cid@*'
|
|
550
558
|
> ch_sub message: else.
|
|
551
559
|
*/
|
|
560
|
+
// console.log('payloadType', payloadType )
|
|
552
561
|
switch (payloadType) {
|
|
553
562
|
|
|
554
563
|
case PAYLOAD_TYPE.EMPTY:
|
|
@@ -625,26 +634,16 @@ export class IOCore extends EventEmitter {
|
|
|
625
634
|
this.testPromise(buffer)
|
|
626
635
|
break;
|
|
627
636
|
|
|
628
|
-
case Boho.BohoMsg.AUTH_NONCE:
|
|
629
|
-
this.stateChange('auth_nonce','server sent auth_nonce.')
|
|
630
|
-
let auth_hmac = this.boho.auth_hmac(buffer)
|
|
631
|
-
if (auth_hmac) {
|
|
632
|
-
this.send(auth_hmac)
|
|
633
|
-
} else {
|
|
634
|
-
this.stateChange('auth_fail', 'boho.auth_hmac() invalid auth_nonce.')
|
|
635
|
-
}
|
|
636
|
-
break;
|
|
637
|
-
|
|
638
637
|
case Boho.BohoMsg.AUTH_FAIL:
|
|
639
638
|
this.stateChange('auth_fail', 'auth_fail from server.')
|
|
640
639
|
break;
|
|
641
640
|
|
|
642
|
-
case Boho.BohoMsg.
|
|
643
|
-
if (this.boho.
|
|
644
|
-
this.stateChange('
|
|
641
|
+
case Boho.BohoMsg.AUTH_RES:
|
|
642
|
+
if (this.boho.verify_auth_res(buffer)) {
|
|
643
|
+
this.stateChange('auth_res', 'server sent auth_res')
|
|
645
644
|
this.send(Buffer.from([IOMsg.CID_REQ]))
|
|
646
645
|
} else {
|
|
647
|
-
this.stateChange('auth_fail', '
|
|
646
|
+
this.stateChange('auth_fail', 'verify_auth_res() invalid server_hmac')
|
|
648
647
|
}
|
|
649
648
|
break;
|
|
650
649
|
|
|
@@ -872,6 +871,7 @@ export class IOCore extends EventEmitter {
|
|
|
872
871
|
*/
|
|
873
872
|
signal_e2e(tag, data, key) {
|
|
874
873
|
|
|
874
|
+
if( !this.boho.isAuthorized ) return;
|
|
875
875
|
if (typeof tag !== 'string') throw TypeError('tag should be string.')
|
|
876
876
|
let tagEncoded = encoder.encode(tag)
|
|
877
877
|
let dataPack = MBP.B8(data)
|
package/src/common/constants.js
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* @property {number} CONNECTING
|
|
5
5
|
* @property {number} OPEN
|
|
6
6
|
* @property {number} SERVER_READY
|
|
7
|
-
* @property {number}
|
|
8
|
-
* @property {number}
|
|
9
|
-
* @property {number} READY
|
|
7
|
+
* @property {number} AUTH_REQ
|
|
8
|
+
* @property {number} AUTH_RES
|
|
10
9
|
* @property {number} AUTH_FAIL
|
|
11
10
|
* @property {number} AUTH_CLEAR
|
|
11
|
+
* @property {number} CID_REQ
|
|
12
|
+
* @property {number} CID_RES
|
|
13
|
+
* @property {number} READY
|
|
12
14
|
* @property {number} CLOSING
|
|
13
15
|
* @property {number} CLOSED
|
|
14
16
|
* @property {number} STOP
|
|
@@ -20,11 +22,10 @@ export const STATE = {
|
|
|
20
22
|
OPEN: 1,
|
|
21
23
|
SERVER_READY: 10,
|
|
22
24
|
AUTH_REQ: 11,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
AUTH_CLEAR: 16,
|
|
25
|
+
AUTH_RES: 12,
|
|
26
|
+
AUTH_FAIL: 13,
|
|
27
|
+
AUTH_CLEAR: 14,
|
|
28
|
+
|
|
28
29
|
CID_REQ: 17,
|
|
29
30
|
CID_RES: 18,
|
|
30
31
|
READY: 19,
|
|
@@ -139,15 +140,7 @@ export let IOMsg = {
|
|
|
139
140
|
|
|
140
141
|
// DO NOT USE: 0xB0~ 0xBF
|
|
141
142
|
// Boho module using this numbers.
|
|
142
|
-
|
|
143
|
-
// AUTH_NONCE: 0xB1,
|
|
144
|
-
// AUTH_HMAC: 0xB2,
|
|
145
|
-
// AUTH_ACK: 0xB3,
|
|
146
|
-
// AUTH_FAIL: 0xB4,
|
|
147
|
-
// AUTH_EXT: 0xB5,
|
|
148
|
-
// ENC_PACK : 0xB6,
|
|
149
|
-
// ENC_E2E : 0xB7,
|
|
150
|
-
// ENC_488 : 0xB8
|
|
143
|
+
|
|
151
144
|
// reserved ~0xBF
|
|
152
145
|
|
|
153
146
|
// C. IO status contorl.
|
package/src/server/Manager.js
CHANGED
|
@@ -4,7 +4,7 @@ import { serverOption } from './serverOption.js'
|
|
|
4
4
|
import { IOMsg, STATE } from '../common/constants.js'
|
|
5
5
|
import { getSignalPack } from '../common/payload.js';
|
|
6
6
|
import { FileLogger } from './FileLogger.js'
|
|
7
|
-
import {
|
|
7
|
+
import { Metrics } from './Metrics.js'
|
|
8
8
|
|
|
9
9
|
const encoder = new TextEncoder()
|
|
10
10
|
const decoder = new TextDecoder()
|
|
@@ -36,7 +36,7 @@ export class Manager {
|
|
|
36
36
|
this.channel_map = new Map() // key: channelName -> value: < remote : Set >
|
|
37
37
|
this.CHANNEL_PREFIX = 'CH:'
|
|
38
38
|
this.CID_PREFIX = 'CID:'
|
|
39
|
-
this.
|
|
39
|
+
this.metrics = new Metrics(this)
|
|
40
40
|
this.lastSSID = 0;
|
|
41
41
|
|
|
42
42
|
this.pingIntervalID = setInterval(e => {
|
|
@@ -72,7 +72,7 @@ export class Manager {
|
|
|
72
72
|
socket.isAlive = true;
|
|
73
73
|
let remote = new Remote(socket, req, this)
|
|
74
74
|
this.remotes.add(remote)
|
|
75
|
-
remote.send(
|
|
75
|
+
remote.send( remote.boho.server_time_nonce() )
|
|
76
76
|
remote.setState(STATE.SERVER_READY)
|
|
77
77
|
this.lastSSID = remote.ssid;
|
|
78
78
|
let connectionInfo = `+ IP:${remote.ip} #${remote.ssid} ${socket.socketType === 'websocket' ? "WS" : "CS"} `;
|
|
@@ -402,18 +402,18 @@ export class Manager {
|
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
if (serverOption.showChannel > 0) {
|
|
405
|
-
this.
|
|
405
|
+
this.metrics.channels(serverOption.showChannel);
|
|
406
406
|
}
|
|
407
407
|
let mode = parseInt(serverOption.showMetric)
|
|
408
408
|
switch (mode) {
|
|
409
409
|
case 1:
|
|
410
|
-
this.
|
|
410
|
+
this.metrics.oneline(true);
|
|
411
411
|
break;
|
|
412
412
|
case 2:
|
|
413
|
-
this.
|
|
413
|
+
this.metrics.getRemotes(true);
|
|
414
414
|
break;
|
|
415
415
|
case 3:
|
|
416
|
-
this.
|
|
416
|
+
this.metrics.getChannelList(true);
|
|
417
417
|
break;
|
|
418
418
|
default:
|
|
419
419
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { memoryUsage } from 'process';
|
|
2
|
+
import { ENC_MODE } from '../common/constants.js'
|
|
3
|
+
|
|
4
|
+
const MAX = 10
|
|
5
|
+
let period = 10000
|
|
6
|
+
|
|
7
|
+
export class Metrics {
|
|
8
|
+
constructor(manager) {
|
|
9
|
+
this.manager = manager;
|
|
10
|
+
|
|
11
|
+
this.metricsPack = {
|
|
12
|
+
remotes: [0],
|
|
13
|
+
channels: [0],
|
|
14
|
+
txBytes: [0],
|
|
15
|
+
rxBytes: [0],
|
|
16
|
+
unixTime: [Math.floor(Date.now() / 1000)],
|
|
17
|
+
period: 0
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
this.tickId = setInterval(e => {
|
|
21
|
+
|
|
22
|
+
this.metricsPack.remotes.push(this.manager.remotes.size)
|
|
23
|
+
if (this.metricsPack.remotes.length > MAX) this.metricsPack.remotes.shift()
|
|
24
|
+
|
|
25
|
+
this.metricsPack.channels.push(this.manager.channel_map.size)
|
|
26
|
+
if (this.metricsPack.channels.length > MAX) this.metricsPack.channels.shift()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
this.metricsPack.txBytes.push(this.manager.txBytes)
|
|
30
|
+
if (this.metricsPack.txBytes.length > MAX) this.metricsPack.txBytes.shift()
|
|
31
|
+
|
|
32
|
+
this.metricsPack.rxBytes.push(this.manager.rxBytes)
|
|
33
|
+
if (this.metricsPack.rxBytes.length > MAX) this.metricsPack.rxBytes.shift()
|
|
34
|
+
|
|
35
|
+
this.metricsPack.unixTime = Math.floor(Date.now() / 1000)
|
|
36
|
+
this.metricsPack.period = period;
|
|
37
|
+
this.manager.txBytes = 0
|
|
38
|
+
this.manager.rxBytes = 0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
}, 10000)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
oneline(prn) {
|
|
47
|
+
let memoryUse = memoryUsage();
|
|
48
|
+
let memoryInfo = [{
|
|
49
|
+
...memoryUse
|
|
50
|
+
}]
|
|
51
|
+
|
|
52
|
+
if (prn) console.table(memoryInfo, ['rss', 'heapTotal', 'heapUsed', 'external', 'arrayBuffers']);
|
|
53
|
+
|
|
54
|
+
let metric = [{
|
|
55
|
+
lastSSID: this.manager.lastSSID,
|
|
56
|
+
remotes: this.manager.remotes.size,
|
|
57
|
+
channels: this.manager.channel_map.size,
|
|
58
|
+
txBytes: this.manager.txBytes,
|
|
59
|
+
rxBytes: this.manager.rxBytes,
|
|
60
|
+
}]
|
|
61
|
+
|
|
62
|
+
if (prn) console.table(metric, ['lastSSID', 'remotes', 'channels', 'txBytes', 'rxBytes']);
|
|
63
|
+
|
|
64
|
+
return metric[0]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getRemotes(prn) {
|
|
68
|
+
let remoteList = Array.from(this.manager.remotes.values())
|
|
69
|
+
let remoteStates = remoteList.map(v => {
|
|
70
|
+
return "#" + v.ssid + ":" + v.cid + "(" + v.state + ")"
|
|
71
|
+
})
|
|
72
|
+
if (prn) console.table(remoteStates)
|
|
73
|
+
return remoteStates
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getCIdList(prn) {
|
|
77
|
+
let CIdList = Array.from(this.manager.cid2remote.keys())
|
|
78
|
+
if (prn) console.table(CIdList)
|
|
79
|
+
return CIdList
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getChannelList(prn) {
|
|
83
|
+
let chList = Array.from(this.manager.channel_map.keys())
|
|
84
|
+
if (prn) console.table(chList)
|
|
85
|
+
return chList
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getSubscribers(ch) {
|
|
89
|
+
let list = [];
|
|
90
|
+
if (this.manager.channel_map.has(ch)) {
|
|
91
|
+
let subscribers = this.manager.channel_map.get(ch) //set
|
|
92
|
+
subscribers.forEach(c => {
|
|
93
|
+
list.push(c.cid)
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
// console.log(list)
|
|
97
|
+
return list
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getRemoteByCId(cid, mode = 1) {
|
|
101
|
+
|
|
102
|
+
if (this.manager.cid2remote.has(cid)) {
|
|
103
|
+
let remote = this.manager.cid2remote.get(cid)
|
|
104
|
+
if (mode == 1) {
|
|
105
|
+
return {
|
|
106
|
+
ip: remote.ip,
|
|
107
|
+
id: remote.cid,
|
|
108
|
+
ssid: remote.ssid,
|
|
109
|
+
cid: remote.cid,
|
|
110
|
+
uid: remote.uid,
|
|
111
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
112
|
+
tx: remote.socket.txCounter,
|
|
113
|
+
rx: remote.socket.rxCounter,
|
|
114
|
+
txBytes: remote.socket.bytesWritten || remote.socket._socket?.bytesWritten,
|
|
115
|
+
rxBytes: remote.socket.bytesRead || remote.socket._socket?.bytesRead
|
|
116
|
+
}
|
|
117
|
+
} else if (mode == 2) {
|
|
118
|
+
return {
|
|
119
|
+
ip: remote.ip,
|
|
120
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
121
|
+
nick: remote.nick,
|
|
122
|
+
ssid: remote.ssid
|
|
123
|
+
}
|
|
124
|
+
} else if (mode == 3) {
|
|
125
|
+
return {
|
|
126
|
+
ip: remote.ip,
|
|
127
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
128
|
+
nick: remote.nick,
|
|
129
|
+
ssid: remote.ssid,
|
|
130
|
+
isSecure: remote.TLS,
|
|
131
|
+
isAuth: remote.boho.isAuthorized,
|
|
132
|
+
encMode: ENC_MODE[remote.encMode]
|
|
133
|
+
}
|
|
134
|
+
} else if (mode == 4) {
|
|
135
|
+
let channels = Array.from(remote.channels.keys())
|
|
136
|
+
let set_memory = Array.from(remote.memory.keys())
|
|
137
|
+
let retain_signal = Array.from(remote.retain_signal.keys())
|
|
138
|
+
return {
|
|
139
|
+
ip: remote.ip,
|
|
140
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
141
|
+
channels: channels,
|
|
142
|
+
set: set_memory,
|
|
143
|
+
retain: retain_signal
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
} else {
|
|
148
|
+
return { result: "nop" }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|
package/src/server/RemoteCore.js
CHANGED
|
@@ -27,7 +27,7 @@ export class RemoteCore {
|
|
|
27
27
|
this.ssid = RemoteCore.ssid++; // ordered index number
|
|
28
28
|
this.manager = manager;
|
|
29
29
|
|
|
30
|
-
this.cid = ''; //
|
|
30
|
+
this.cid = ''; // Client Id
|
|
31
31
|
this.did = ''; // Device Id
|
|
32
32
|
this.uid = ''; // User Id
|
|
33
33
|
this.nick = ''
|
|
@@ -50,7 +50,7 @@ export class RemoteCore {
|
|
|
50
50
|
this.state = state
|
|
51
51
|
if (serverOption.debug.showAuthInfo) {
|
|
52
52
|
this.stateLog.push( state + ":" + STATE[ state] )
|
|
53
|
-
if( state == STATE.
|
|
53
|
+
if( state == STATE.AUTH_RES){
|
|
54
54
|
console.log('[auth success]', this.cid, this.stateLog.join('>'))
|
|
55
55
|
}else if( state == STATE.AUTH_FAIL){
|
|
56
56
|
console.log('[auth_fail]',this.stateLog.join('>'))
|
|
@@ -291,29 +291,16 @@ export class RemoteCore {
|
|
|
291
291
|
break;
|
|
292
292
|
|
|
293
293
|
|
|
294
|
-
//
|
|
294
|
+
// client's auth requst
|
|
295
295
|
case Boho.BohoMsg.AUTH_REQ:
|
|
296
296
|
if (!this.manager.authManager) return
|
|
297
297
|
if (this.state < STATE.SERVER_READY) {
|
|
298
|
-
console.log('protocol error. auth_req
|
|
298
|
+
console.log('protocol error. must called auth_req after server_ready')
|
|
299
299
|
this.close();
|
|
300
300
|
}
|
|
301
301
|
this.setState(STATE.AUTH_REQ)
|
|
302
|
-
let auth_nonce_pack = this.boho.auth_nonce()
|
|
303
|
-
// console.log('## auth_nonce_pack', auth_nonce_pack )
|
|
304
|
-
this.send(auth_nonce_pack)
|
|
305
|
-
this.setState(STATE.AUTH_NONCE)
|
|
306
|
-
break;
|
|
307
|
-
|
|
308
|
-
case Boho.BohoMsg.AUTH_HMAC:
|
|
309
|
-
if (!this.manager.authManager) return
|
|
310
|
-
if (this.state < STATE.AUTH_NONCE) {
|
|
311
|
-
console.log('protocol error. auth_hmac before server_nonce')
|
|
312
|
-
this.close();
|
|
313
|
-
}
|
|
314
|
-
this.setState(STATE.AUTH_HMAC)
|
|
315
302
|
//async
|
|
316
|
-
this.manager.authManager.
|
|
303
|
+
this.manager.authManager.verify_auth_req(message, this)
|
|
317
304
|
.then(authInfo => {
|
|
318
305
|
if (authInfo?.name) {
|
|
319
306
|
this.manager.deligateSignal(this, '@$name', authInfo.name)
|
|
@@ -321,7 +308,7 @@ export class RemoteCore {
|
|
|
321
308
|
// this.manager.sender('@$name', authInfo.name )
|
|
322
309
|
}
|
|
323
310
|
|
|
324
|
-
// console.log('[VERIFY
|
|
311
|
+
// console.log('[VERIFY AUTH_REQ] SubClass of BohoAuth return authInfo:', authInfo )
|
|
325
312
|
})
|
|
326
313
|
.catch(e => {
|
|
327
314
|
console.log('authInfo return fail.', e )
|
package/src/server/Server.js
CHANGED
|
@@ -131,25 +131,25 @@ export class Server extends EventEmitter {
|
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
133
|
* RPC service register.
|
|
134
|
-
*
|
|
135
|
-
*
|
|
134
|
+
* service: service_name <string>
|
|
135
|
+
* service_module <function module|class instance>
|
|
136
136
|
* return this
|
|
137
137
|
*/
|
|
138
|
-
attach(
|
|
138
|
+
attach(service, service_module) {
|
|
139
139
|
|
|
140
|
-
if( typeof
|
|
141
|
-
throw new Error(`
|
|
140
|
+
if( typeof service !== 'string'){
|
|
141
|
+
throw new Error(`attach service name is not a string.`)
|
|
142
142
|
}
|
|
143
143
|
if (!service_module.checkPermission || typeof service_module.checkPermission != 'function') {
|
|
144
|
-
throw new Error(`Service ${
|
|
144
|
+
throw new Error(`Service ${service} : no checkPermission or not a function.`)
|
|
145
145
|
}
|
|
146
146
|
if(!service_module.commands || !Array.isArray(service_module.commands) ){
|
|
147
|
-
throw new Error(`Service ${
|
|
147
|
+
throw new Error(`Service ${service} : no commands or !Array.`)
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// Service TYPE 1. single call() function.
|
|
151
151
|
if ( service_module.call && typeof service_module.call == 'function' ) {
|
|
152
|
-
this.on(
|
|
152
|
+
this.on(service, (remote, req) => {
|
|
153
153
|
try {
|
|
154
154
|
if (!service_module.checkPermission(remote, req)) {
|
|
155
155
|
remote.response(req.mid, STATUS.ERROR, "NO_PERMISSION.")
|
|
@@ -162,13 +162,13 @@ export class Server extends EventEmitter {
|
|
|
162
162
|
remote.response(req.mid, STATUS.ERROR, "UNKNOWN_COMMAND");
|
|
163
163
|
}
|
|
164
164
|
} catch (error) {
|
|
165
|
-
console.error(`Unhandled Service Error in
|
|
165
|
+
console.error(`Unhandled Service Error in service [${service}]:`, error);
|
|
166
166
|
remote.response(req.mid, STATUS.ERROR, "INTERNAL_SERVER_ERROR");
|
|
167
167
|
}
|
|
168
168
|
})
|
|
169
169
|
} else {
|
|
170
170
|
// Service TYPE 2. multiple functions.
|
|
171
|
-
this.on(
|
|
171
|
+
this.on(service, (remote, req) => {
|
|
172
172
|
try {
|
|
173
173
|
if (!service_module.checkPermission(remote, req)) {
|
|
174
174
|
remote.response(req.mid, STATUS.ERROR, "NO_PERMISSION.")
|
|
@@ -180,13 +180,13 @@ export class Server extends EventEmitter {
|
|
|
180
180
|
remote.response(req.mid, STATUS.ERROR, "UNKNOWN_COMMAND");
|
|
181
181
|
}
|
|
182
182
|
} catch (error) {
|
|
183
|
-
console.error(`Unhandled Service Error in
|
|
183
|
+
console.error(`Unhandled Service Error in service [${service}]:`, error);
|
|
184
184
|
remote.response(req.mid, STATUS.ERROR, "INTERNAL_SERVER_ERROR");
|
|
185
185
|
}
|
|
186
186
|
})
|
|
187
187
|
}
|
|
188
|
-
this.serviceNames.add(
|
|
189
|
-
// console.log(`[Service attached: ${
|
|
188
|
+
this.serviceNames.add(service)
|
|
189
|
+
// console.log(`[Service attached: ${service} ] accept commands: ${service_module.commands}`)
|
|
190
190
|
return this
|
|
191
191
|
}
|
|
192
192
|
|
|
@@ -21,18 +21,18 @@ export async function call(remote, req) {
|
|
|
21
21
|
let cmd = req.topic;
|
|
22
22
|
cmd = cmd.toLowerCase()
|
|
23
23
|
if (cmd == 'cid') {
|
|
24
|
-
result = remote.manager.
|
|
24
|
+
result = remote.manager.metrics.getCIdList()
|
|
25
25
|
} else if (cmd == 'remotes' || cmd == 'clients') {
|
|
26
|
-
result = remote.manager.
|
|
26
|
+
result = remote.manager.metrics.getRemotes()
|
|
27
27
|
} else if (cmd == 'channels') {
|
|
28
|
-
result = remote.manager.
|
|
28
|
+
result = remote.manager.metrics.getChannelList()
|
|
29
29
|
} else if (cmd == 'subscribers') {
|
|
30
30
|
let ch = req.$[0]
|
|
31
|
-
if (ch) result = remote.manager.
|
|
31
|
+
if (ch) result = remote.manager.metrics.getSubscribers(ch)
|
|
32
32
|
} else if (cmd == 'remote' || cmd == 'client') {
|
|
33
33
|
let cid = req.$[0]
|
|
34
34
|
let mode = req.$[1]
|
|
35
|
-
if (cid) result = remote.manager.
|
|
35
|
+
if (cid) result = remote.manager.metrics.getRemoteByCId(cid, mode)
|
|
36
36
|
|
|
37
37
|
} else if (cmd == 'close') {
|
|
38
38
|
if (req.$[0]) result = remote.manager.closeRemoteByCId(req.$[0])
|