iosignal 1.6.0 → 1.7.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/iosignal.cjs +2 -2
- package/dist/iosignal.esm.js +1 -1
- package/dist/iosignal.esm.js.map +1 -1
- package/dist/iosignal.min.js +1 -1
- package/dist/iosignal.min.js.map +1 -1
- package/indexWebBrowser.js +1 -1
- package/package.json +2 -2
- package/src/api/RedisAPI.js +23 -26
- package/src/api/api_reply.js +12 -15
- package/src/api/api_sudo.js +67 -72
- package/src/auth/Auth_Env.js +7 -9
- package/src/auth/Auth_File.js +31 -35
- package/src/auth/Auth_Redis.js +15 -21
- package/src/auth/BohoAuth.js +68 -72
- package/src/client/CongPacket.js +15 -24
- package/src/client/IOCongSocket.js +26 -33
- package/src/client/IOCore.js +6 -35
- package/src/client/IOWS.js +22 -23
- package/src/client/IOWebSocket.js +11 -7
- package/src/common/constants.js +21 -32
- package/src/common/payload.js +47 -48
- package/src/common/quotaTable.js +10 -10
- package/src/common/util.js +38 -38
- package/src/server/FileLogger.js +9 -9
- package/src/server/Manager.js +39 -55
- package/src/server/Metric.js +54 -53
- package/src/server/Remote.js +207 -0
- package/src/server/{ServerRemoteCore.js → RemoteCore.js} +53 -81
- package/src/server/Server.js +22 -25
- package/src/server/serverOption.js +10 -10
- package/test-browser/index.html +30 -26
- package/test-browser/server.js +1 -1
- package/test-client-nodejs/client.cjs +6 -6
- package/test-client-nodejs/client.mjs +6 -6
- package/test-client-nodejs/client_api_reply.mjs +8 -8
- package/test-client-nodejs/stateChange.mjs +7 -7
- package/test-server/server-Auth_Env.js +2 -2
- package/test-server/server-Auth_File.js +1 -1
- package/test-server/server-api_reply.js +2 -2
- package/test-server/server-api_sudo.js +3 -3
- package/test-server/server-esm.mjs +9 -8
- package/test-server/simple-server.mjs +6 -6
- package/test-signal-payloadType/signalPack.js +1 -1
- package/test-signal-payloadType/signaling.js +4 -4
- package/src/server/ServerRemote.js +0 -227
package/src/server/Manager.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MBP } from 'meta-buffer-pack'
|
|
2
|
-
import {
|
|
2
|
+
import { Remote } from './Remote.js'
|
|
3
3
|
import { serverOption } from './serverOption.js'
|
|
4
4
|
import { IOMsg, CLIENT_STATE } from '../common/constants.js'
|
|
5
5
|
import { getSignalPack } from '../common/payload.js';
|
|
@@ -9,35 +9,33 @@ import { Metric } from './Metric.js'
|
|
|
9
9
|
const encoder = new TextEncoder()
|
|
10
10
|
const decoder = new TextDecoder()
|
|
11
11
|
|
|
12
|
-
export class Manager{
|
|
12
|
+
export class Manager {
|
|
13
13
|
|
|
14
|
-
constructor(
|
|
14
|
+
constructor(server, authManager) {
|
|
15
15
|
this.server = server;
|
|
16
16
|
this.txBytes = 0;
|
|
17
17
|
this.rxBytes = 0;
|
|
18
18
|
|
|
19
19
|
this.authManager = authManager;
|
|
20
|
-
// TIP. authManager module is option.
|
|
21
|
-
// if exist: used by ServerRemoteCore for the auth. process.
|
|
22
20
|
|
|
23
21
|
this.connectionLogger;
|
|
24
22
|
if (serverOption.fileLogger.connection.use) {
|
|
25
|
-
this.connectionLogger = new FileLogger(
|
|
26
|
-
console.log('Manager: begin file logger [connection]')
|
|
23
|
+
this.connectionLogger = new FileLogger(serverOption.fileLogger.connection.path)
|
|
24
|
+
// console.log('Manager: begin file logger [connection]')
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
this.attackLogger;
|
|
30
|
-
if(
|
|
31
|
-
this.attackLogger = new FileLogger(
|
|
32
|
-
console.log('Manager: begin file logger.[attack]')
|
|
28
|
+
if (serverOption.fileLogger.attack.use) {
|
|
29
|
+
this.attackLogger = new FileLogger(serverOption.fileLogger.attack.path)
|
|
30
|
+
// console.log('Manager: begin file logger.[attack]')
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
this.remotes = new Set();
|
|
36
|
-
this.
|
|
33
|
+
this.remotes = new Set(); // total remotes
|
|
34
|
+
this.cid2remote = new Map(); // key: cid -> value: remote
|
|
35
|
+
this.retain_messages = new Map() // key: tag, data: signalMessage
|
|
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.cid2remote = new Map(); // map:[ key:cid -> value:remote ]
|
|
40
|
-
this.retain_messages = new Map() // key: tag, data: signalMessage
|
|
41
39
|
this.metric = new Metric(this)
|
|
42
40
|
this.lastSSID = 0;
|
|
43
41
|
|
|
@@ -56,14 +54,14 @@ export class Manager{
|
|
|
56
54
|
} else {
|
|
57
55
|
remo.ping()
|
|
58
56
|
}
|
|
59
|
-
|
|
57
|
+
|
|
60
58
|
});
|
|
61
59
|
}, serverOption.timeout);
|
|
62
60
|
|
|
63
61
|
if (serverOption.showMetric) {
|
|
64
62
|
this.monitIntervalID = setInterval((e) => {
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
this.monitor()
|
|
64
|
+
}, serverOption.monitorPeriod);
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
|
|
@@ -72,7 +70,7 @@ export class Manager{
|
|
|
72
70
|
|
|
73
71
|
addRemote(socket, req) {
|
|
74
72
|
socket.isAlive = true;
|
|
75
|
-
let remote = new
|
|
73
|
+
let remote = new Remote(socket, req, this)
|
|
76
74
|
this.remotes.add(remote)
|
|
77
75
|
remote.send(Buffer.from([IOMsg.SERVER_READY]))
|
|
78
76
|
remote.setState(CLIENT_STATE.SENT_SERVER_READY)
|
|
@@ -84,7 +82,7 @@ export class Manager{
|
|
|
84
82
|
|
|
85
83
|
|
|
86
84
|
removeRemote(remote) {
|
|
87
|
-
|
|
85
|
+
|
|
88
86
|
let remoteInfo = `- IP:${remote.ip} #${remote.ssid} cid:${remote.cid} ${remote?.socket.socketType === 'websocket' ? "WS" : "CS"} `
|
|
89
87
|
if (this.connectionLogger) this.connectionLogger.log(remoteInfo)
|
|
90
88
|
this.deligateSignal(remote, '@state', 'close')
|
|
@@ -97,16 +95,16 @@ export class Manager{
|
|
|
97
95
|
// console.log(`-- channel [ ${ch} ] removes subscriber id: ${remote.cid} `)
|
|
98
96
|
remotes.delete(remote)
|
|
99
97
|
if (remotes.size == 0) this.channel_map.delete(ch)
|
|
100
|
-
}else{
|
|
98
|
+
} else {
|
|
101
99
|
// console.log('no such a ch: ', ch )
|
|
102
100
|
}
|
|
103
101
|
}
|
|
104
102
|
|
|
105
103
|
// retain message policy
|
|
106
|
-
// type 1.
|
|
107
|
-
if(
|
|
104
|
+
// type 1. ( isAuthorizedUser) ? keep : delete.
|
|
105
|
+
if (remote.boho.isAuthorized) {
|
|
108
106
|
//keep retain signal.
|
|
109
|
-
}else{
|
|
107
|
+
} else {
|
|
110
108
|
// anonymouse: remove the cid publish channel
|
|
111
109
|
for (let ch of remote.retain_signal.keys()) {
|
|
112
110
|
// console.log('cid retain_signal.keys:', ch )
|
|
@@ -114,17 +112,17 @@ export class Manager{
|
|
|
114
112
|
if (this.channel_map.has(ch)) {
|
|
115
113
|
this.channel_map.delete(ch)
|
|
116
114
|
// console.log(`-- deleted cid pub ch [ ${ch} ]. cid publisher id: ${remote.cid} `)
|
|
117
|
-
}else{
|
|
115
|
+
} else {
|
|
118
116
|
// console.log('no such a retain_signal tag: ', ch )
|
|
119
117
|
}
|
|
120
118
|
}
|
|
121
119
|
}
|
|
122
|
-
|
|
123
120
|
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
|
|
122
|
+
if (remote.uid && this.server.apiNames.has('account')) {
|
|
123
|
+
let req = { topic: 'detachUserRemote' }
|
|
126
124
|
req.args = ['caller:manager.removeRemote']
|
|
127
|
-
this.server.emit('account', remote, req
|
|
125
|
+
this.server.emit('account', remote, req)
|
|
128
126
|
}
|
|
129
127
|
|
|
130
128
|
this.remotes.delete(remote)
|
|
@@ -134,7 +132,7 @@ export class Manager{
|
|
|
134
132
|
}
|
|
135
133
|
|
|
136
134
|
|
|
137
|
-
// unix wall
|
|
135
|
+
// it's like unix wall command.
|
|
138
136
|
serverSignal(obj) {
|
|
139
137
|
let sigPack = MBP.pack(
|
|
140
138
|
MBP.MB('#MsgType', '8', IOMsg.SERVER_SIGNAL),
|
|
@@ -171,40 +169,38 @@ export class Manager{
|
|
|
171
169
|
this.sender(tag, remote, sigPack)
|
|
172
170
|
}
|
|
173
171
|
|
|
174
|
-
adminSignal(
|
|
172
|
+
adminSignal(cid, message) {
|
|
175
173
|
if (this.cid2remote.has(cid)) {
|
|
176
174
|
this.cid2remote.get(cid).send_enc_mode(message)
|
|
177
|
-
// return ['ok', 1]
|
|
178
175
|
return
|
|
179
|
-
}else{
|
|
176
|
+
} else {
|
|
180
177
|
return "no cid"
|
|
181
178
|
}
|
|
182
179
|
}
|
|
183
180
|
|
|
184
|
-
serverSignalTo(
|
|
181
|
+
serverSignalTo(tag, ...args) {
|
|
185
182
|
let cid = tag.split('@')[0]
|
|
186
183
|
let topic = tag.split('@')[1]
|
|
187
|
-
let sigPack = getSignalPack(
|
|
188
|
-
// console.log('tag split', cid )
|
|
184
|
+
let sigPack = getSignalPack('@' + topic, ...args)
|
|
189
185
|
if (cid && this.cid2remote.has(cid)) {
|
|
190
186
|
let targetRemote = this.cid2remote.get(cid)
|
|
191
|
-
console.log('target', targetRemote.state
|
|
187
|
+
console.log('target', targetRemote.state, targetRemote.cid)
|
|
192
188
|
targetRemote.send_enc_mode(sigPack)
|
|
193
189
|
return
|
|
194
|
-
}else{
|
|
190
|
+
} else {
|
|
195
191
|
return "no cid"
|
|
196
192
|
}
|
|
197
193
|
}
|
|
198
194
|
|
|
199
195
|
sender(tag, remote, message) {
|
|
200
|
-
if(serverOption.memberOnly && !remote.boho.isAuthorized){
|
|
196
|
+
if (serverOption.memberOnly && !remote.boho.isAuthorized) {
|
|
201
197
|
// console.log("## MemberOnly, reject unAuthorized remote." )
|
|
202
198
|
remote.send(Buffer.from([IOMsg.SERVER_CLEAR_AUTH]))
|
|
203
199
|
remote.close()
|
|
204
200
|
return ['err', 'not autrized']
|
|
205
201
|
}
|
|
206
202
|
|
|
207
|
-
if(
|
|
203
|
+
if (tag.indexOf('$') == 0) return ['err', 'prefix $ is reserved for userSet tag.']
|
|
208
204
|
|
|
209
205
|
let cidIndex = tag.indexOf('@');
|
|
210
206
|
if (cidIndex === 0) {
|
|
@@ -214,10 +210,8 @@ export class Manager{
|
|
|
214
210
|
tag = remote.cid + tag;
|
|
215
211
|
message = this.getNewSignalTagMessage(message, tag)
|
|
216
212
|
|
|
217
|
-
// console.log('cid_pub new tag:', tag, this.getSignalTag(message ))
|
|
218
213
|
} else if (cidIndex > 0) {
|
|
219
214
|
// uni-cast.
|
|
220
|
-
// console.log('unicast to:', tag)
|
|
221
215
|
let targetCId = tag.split('@')[0];
|
|
222
216
|
if (this.cid2remote.has(targetCId)) {
|
|
223
217
|
//rm cid from tag.
|
|
@@ -229,8 +223,6 @@ export class Manager{
|
|
|
229
223
|
return ['ok', 1]
|
|
230
224
|
}
|
|
231
225
|
return ['err', 'Invalid cid']
|
|
232
|
-
// pub message from cid.
|
|
233
|
-
// you already subscribe the cid.
|
|
234
226
|
} else {
|
|
235
227
|
// console.log('ch_pub tag:', tag)
|
|
236
228
|
//else channel publish message
|
|
@@ -247,7 +239,7 @@ export class Manager{
|
|
|
247
239
|
// (blank)#topic -> home_channel#topic.
|
|
248
240
|
if (tag.indexOf('#') === 0) {
|
|
249
241
|
tag = remote.HOME_CHANNEL + tag;
|
|
250
|
-
} else if(
|
|
242
|
+
} else if (tag.includes('@')) {
|
|
251
243
|
tag = this.CID_PREFIX + tag;
|
|
252
244
|
} else {
|
|
253
245
|
tag = this.CHANNEL_PREFIX + tag;
|
|
@@ -265,11 +257,9 @@ export class Manager{
|
|
|
265
257
|
// cid retain signal stored inside remote.
|
|
266
258
|
let retainTag = tag.split('@')[1]
|
|
267
259
|
remote.retain_signal.set(retainTag, message)
|
|
268
|
-
// console.log('## cid retain tag, size: ', retainTag, remote.retain_signal.size)
|
|
269
260
|
} else {
|
|
270
261
|
// channel retain signal stored inside manager.
|
|
271
262
|
this.retain_messages.set(tag, message)
|
|
272
|
-
// console.log('## channel retain tag, size: ', tag, this.retain_messages.size)
|
|
273
263
|
}
|
|
274
264
|
|
|
275
265
|
} else {
|
|
@@ -315,11 +305,10 @@ export class Manager{
|
|
|
315
305
|
|
|
316
306
|
|
|
317
307
|
subscribe(chArr, remote) {
|
|
318
|
-
// console.log('ChannelManager:: subscribe: ',chArr)
|
|
319
308
|
chArr.forEach(tag => {
|
|
320
309
|
if (tag.indexOf('#') === 0) {
|
|
321
310
|
tag = remote.HOME_CHANNEL + tag
|
|
322
|
-
} else if(
|
|
311
|
+
} else if (tag.includes('@')) {
|
|
323
312
|
tag = this.CID_PREFIX + tag;
|
|
324
313
|
} else {
|
|
325
314
|
tag = this.CHANNEL_PREFIX + tag
|
|
@@ -331,11 +320,9 @@ export class Manager{
|
|
|
331
320
|
} else {
|
|
332
321
|
this.channel_map.set(tag, new Set([remote]))
|
|
333
322
|
}
|
|
334
|
-
// console.log('Manager::map:', this.channel_map.keys() )
|
|
335
323
|
|
|
336
324
|
// 2.add to remote channels.
|
|
337
325
|
remote.channels.add(tag)
|
|
338
|
-
// console.log('remote.channels set.', remote.channels )
|
|
339
326
|
|
|
340
327
|
// 3. send retain message if available.
|
|
341
328
|
if (tag.includes('$')
|
|
@@ -356,8 +343,7 @@ export class Manager{
|
|
|
356
343
|
} else if (this.retain_messages.has(tag)) {
|
|
357
344
|
retainMessage = this.retain_messages.get(tag)
|
|
358
345
|
}
|
|
359
|
-
|
|
360
|
-
if(retainMessage) remote.send_enc_mode(retainMessage)
|
|
346
|
+
if (retainMessage) remote.send_enc_mode(retainMessage)
|
|
361
347
|
|
|
362
348
|
}
|
|
363
349
|
|
|
@@ -384,7 +370,7 @@ export class Manager{
|
|
|
384
370
|
// substitution home_channel
|
|
385
371
|
if (ch.indexOf('#') === 0) {
|
|
386
372
|
ch = remote.HOME_CHANNEL + ch;
|
|
387
|
-
} else if(
|
|
373
|
+
} else if (ch.includes('@')) {
|
|
388
374
|
ch = this.CID_PREFIX + ch;
|
|
389
375
|
} else {
|
|
390
376
|
ch = this.CHANNEL_PREFIX + ch;
|
|
@@ -418,13 +404,11 @@ export class Manager{
|
|
|
418
404
|
this.metric.channels(serverOption.showChannel);
|
|
419
405
|
}
|
|
420
406
|
let mode = parseInt(serverOption.showMetric)
|
|
421
|
-
console.log('monitor metric type:', mode)
|
|
422
407
|
switch (mode) {
|
|
423
408
|
case 1:
|
|
424
409
|
this.metric.oneline(true);
|
|
425
410
|
break;
|
|
426
411
|
case 2:
|
|
427
|
-
// this.metric.getCIdList(true);
|
|
428
412
|
this.metric.getRemotes(true);
|
|
429
413
|
break;
|
|
430
414
|
case 3:
|
package/src/server/Metric.js
CHANGED
|
@@ -4,52 +4,52 @@ import { ENC_MODE } from '../common/constants.js'
|
|
|
4
4
|
const MAX = 10
|
|
5
5
|
let period = 10000
|
|
6
6
|
|
|
7
|
-
export class Metric{
|
|
8
|
-
constructor(manager){
|
|
7
|
+
export class Metric {
|
|
8
|
+
constructor(manager) {
|
|
9
9
|
this.manager = manager;
|
|
10
10
|
|
|
11
|
-
this.metricsPack= {
|
|
12
|
-
remotes:[0],
|
|
13
|
-
channels:[0],
|
|
14
|
-
txBytes:[0],
|
|
15
|
-
rxBytes:[0],
|
|
16
|
-
unixTime: [Math.floor(
|
|
11
|
+
this.metricsPack = {
|
|
12
|
+
remotes: [0],
|
|
13
|
+
channels: [0],
|
|
14
|
+
txBytes: [0],
|
|
15
|
+
rxBytes: [0],
|
|
16
|
+
unixTime: [Math.floor(Date.now() / 1000)],
|
|
17
17
|
period: 0
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
this.tickId = setInterval(
|
|
20
|
+
this.tickId = setInterval(e => {
|
|
21
21
|
|
|
22
|
-
this.metricsPack.remotes.push(
|
|
23
|
-
if(this.metricsPack.remotes.length > MAX
|
|
22
|
+
this.metricsPack.remotes.push(this.manager.remotes.size)
|
|
23
|
+
if (this.metricsPack.remotes.length > MAX) this.metricsPack.remotes.shift()
|
|
24
24
|
|
|
25
|
-
this.metricsPack.channels.push(
|
|
26
|
-
if(this.metricsPack.channels.length > MAX
|
|
25
|
+
this.metricsPack.channels.push(this.manager.channel_map.size)
|
|
26
|
+
if (this.metricsPack.channels.length > MAX) this.metricsPack.channels.shift()
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
this.metricsPack.txBytes.push( this.manager.txBytes )
|
|
30
|
-
if(this.metricsPack.txBytes.length > MAX ) this.metricsPack.txBytes.shift()
|
|
31
28
|
|
|
32
|
-
this.metricsPack.
|
|
33
|
-
if(this.metricsPack.
|
|
29
|
+
this.metricsPack.txBytes.push(this.manager.txBytes)
|
|
30
|
+
if (this.metricsPack.txBytes.length > MAX) this.metricsPack.txBytes.shift()
|
|
34
31
|
|
|
35
|
-
this.metricsPack.
|
|
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
36
|
this.metricsPack.period = period;
|
|
37
37
|
this.manager.txBytes = 0
|
|
38
38
|
this.manager.rxBytes = 0
|
|
39
|
-
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
}, 10000)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
oneline(prn){
|
|
46
|
+
oneline(prn) {
|
|
47
47
|
let memoryUse = memoryUsage();
|
|
48
48
|
let memoryInfo = [{
|
|
49
49
|
...memoryUse
|
|
50
50
|
}]
|
|
51
51
|
|
|
52
|
-
if (
|
|
52
|
+
if (prn) console.table(memoryInfo, ['rss', 'heapTotal', 'heapUsed', 'external', 'arrayBuffers']);
|
|
53
53
|
|
|
54
54
|
let metric = [{
|
|
55
55
|
lastSSID: this.manager.lastSSID,
|
|
@@ -59,36 +59,37 @@ export class Metric{
|
|
|
59
59
|
rxBytes: this.manager.rxBytes,
|
|
60
60
|
}]
|
|
61
61
|
|
|
62
|
-
if (
|
|
62
|
+
if (prn) console.table(metric, ['lastSSID', 'remotes', 'channels', 'txBytes', 'rxBytes']);
|
|
63
63
|
|
|
64
64
|
return metric[0]
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
getRemotes(
|
|
68
|
-
let remoteList =
|
|
69
|
-
let remoteStates = remoteList.map(v=>{
|
|
70
|
-
return
|
|
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
71
|
})
|
|
72
|
-
if (prn
|
|
72
|
+
if (prn) console.table(remoteStates)
|
|
73
73
|
return remoteStates
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
|
|
76
|
+
getCIdList(prn) {
|
|
77
|
+
let CIdList = Array.from(this.manager.cid2remote.keys())
|
|
78
|
+
if (prn) console.table(CIdList)
|
|
78
79
|
return CIdList
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
getChannelList(prn){
|
|
82
|
-
let chList =
|
|
83
|
-
if (prn
|
|
82
|
+
getChannelList(prn) {
|
|
83
|
+
let chList = Array.from(this.manager.channel_map.keys())
|
|
84
|
+
if (prn) console.table(chList)
|
|
84
85
|
return chList
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
getSubscribers(ch){
|
|
88
|
+
getSubscribers(ch) {
|
|
88
89
|
let list = [];
|
|
89
|
-
if(
|
|
90
|
+
if (this.manager.channel_map.has(ch)) {
|
|
90
91
|
let subscribers = this.manager.channel_map.get(ch) //set
|
|
91
|
-
subscribers.forEach(
|
|
92
|
+
subscribers.forEach(c => {
|
|
92
93
|
list.push(c.cid)
|
|
93
94
|
})
|
|
94
95
|
}
|
|
@@ -96,54 +97,54 @@ export class Metric{
|
|
|
96
97
|
return list
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
getRemoteByCId(
|
|
100
|
+
getRemoteByCId(cid, mode = 1) {
|
|
100
101
|
|
|
101
|
-
if(
|
|
102
|
+
if (this.manager.cid2remote.has(cid)) {
|
|
102
103
|
let remote = this.manager.cid2remote.get(cid)
|
|
103
|
-
if(mode == 1){
|
|
104
|
+
if (mode == 1) {
|
|
104
105
|
return {
|
|
105
106
|
ip: remote.ip,
|
|
106
107
|
id: remote.cid,
|
|
107
108
|
ssid: remote.ssid,
|
|
108
109
|
cid: remote.cid,
|
|
109
|
-
uptime:
|
|
110
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
110
111
|
tx: remote.socket.txCounter,
|
|
111
112
|
rx: remote.socket.rxCounter,
|
|
112
113
|
txBytes: remote.socket.bytesWritten || remote.socket._socket?.bytesWritten,
|
|
113
114
|
rxBytes: remote.socket.bytesRead || remote.socket._socket?.bytesRead
|
|
114
115
|
}
|
|
115
|
-
}else if(
|
|
116
|
+
} else if (mode == 2) {
|
|
116
117
|
return {
|
|
117
118
|
ip: remote.ip,
|
|
118
|
-
uptime:
|
|
119
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
119
120
|
nick: remote.nick,
|
|
120
121
|
ssid: remote.ssid,
|
|
121
122
|
echo: remote.lastEchoMessage
|
|
122
123
|
}
|
|
123
|
-
}else if(
|
|
124
|
+
} else if (mode == 3) {
|
|
124
125
|
return {
|
|
125
126
|
ip: remote.ip,
|
|
126
|
-
uptime:
|
|
127
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
127
128
|
nick: remote.nick,
|
|
128
129
|
ssid: remote.ssid,
|
|
129
130
|
isSecure: remote.TLS,
|
|
130
131
|
isAuth: remote.boho.isAuthorized,
|
|
131
|
-
encMode:
|
|
132
|
+
encMode: ENC_MODE[remote.encMode]
|
|
132
133
|
}
|
|
133
|
-
}else if(
|
|
134
|
-
let channels = Array.from(
|
|
135
|
-
let set_memory = Array.from(
|
|
136
|
-
let retain_signal = Array.from(
|
|
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())
|
|
137
138
|
return {
|
|
138
139
|
ip: remote.ip,
|
|
139
|
-
uptime:
|
|
140
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
140
141
|
channels: channels,
|
|
141
142
|
set: set_memory,
|
|
142
143
|
retain: retain_signal
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
}else{
|
|
147
|
+
} else {
|
|
147
148
|
return { result: "nop" }
|
|
148
149
|
}
|
|
149
150
|
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { pack, CongRx } from '../client/CongPacket.js'
|
|
2
|
+
import { RemoteCore } from './RemoteCore.js';
|
|
3
|
+
import { serverOption } from './serverOption.js';
|
|
4
|
+
import { IOMsg } from '../common/constants.js';
|
|
5
|
+
import * as util from '../common/util.js'
|
|
6
|
+
|
|
7
|
+
const decoder = new TextDecoder()
|
|
8
|
+
|
|
9
|
+
export class Remote extends RemoteCore {
|
|
10
|
+
constructor(socket, req, manager) {
|
|
11
|
+
super(socket, manager);
|
|
12
|
+
this.socketType = socket.socketType
|
|
13
|
+
|
|
14
|
+
if (this.socketType === 'websocket') {
|
|
15
|
+
this.TLS = req.headers["x-forwarded-proto"] === 'https' ? true : false;
|
|
16
|
+
this.ip = this.getRemoteIP(req);
|
|
17
|
+
} else {
|
|
18
|
+
this.TLS = false;
|
|
19
|
+
this.ip = this.getRemoteIP(socket.remoteAddress)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if (util.isPrivateIP(this.ip)) {
|
|
24
|
+
// connection from local or private network has default private channel.
|
|
25
|
+
this.HOME_CHANNEL = 'PRIVATE:'
|
|
26
|
+
this.privateNode = true
|
|
27
|
+
} else {
|
|
28
|
+
// The HOME_CHANNEL is defined by the global IP address value.
|
|
29
|
+
this.HOME_CHANNEL = 'IP:' + util.getIPv4HexString(this.ip)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if (this.socketType === 'websocket') {
|
|
34
|
+
if (serverOption.debug.slow) {
|
|
35
|
+
socket.on("message", this.onTimeDelayMessage.bind(this));
|
|
36
|
+
} else {
|
|
37
|
+
socket.on("message", this.onSocketMessage.bind(this));
|
|
38
|
+
}
|
|
39
|
+
socket.on("pong", this.receiveMonitor.bind(this));
|
|
40
|
+
socket.on("ping", this.receiveMonitor.bind(this));
|
|
41
|
+
socket.on("error", (e) => { console.log('Websocket error', e, e.code) });
|
|
42
|
+
socket.onclose = (e) => {
|
|
43
|
+
this.manager.removeRemote(this);
|
|
44
|
+
};
|
|
45
|
+
} else { // TCP else
|
|
46
|
+
this.congRx = new CongRx();
|
|
47
|
+
socket.on('data', data => { this.congRx.write(data) })
|
|
48
|
+
|
|
49
|
+
this.congRx.on('wrong', this.onWrongCongPacketMessage.bind(this))
|
|
50
|
+
// readable.pipe( destination )
|
|
51
|
+
// tcpSocket -> congRx(parser) -> onMessage
|
|
52
|
+
// socket.pipe( this.congRx )
|
|
53
|
+
|
|
54
|
+
if (serverOption.debug.slow) {
|
|
55
|
+
this.congRx.on('data', this.onTimeDelayMessage.bind(this))
|
|
56
|
+
} else {
|
|
57
|
+
this.congRx.on('data', this.onSocketMessage.bind(this))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
socket.on('error', e => { console.log('TCP Socket error', e) })
|
|
61
|
+
socket.on('close', e => {
|
|
62
|
+
this.manager.removeRemote(this);
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
onWrongCongPacketMessage(message) {
|
|
69
|
+
/*
|
|
70
|
+
* three category CongSocket connection.
|
|
71
|
+
1. anonymouse, tcp connection.
|
|
72
|
+
> no cid_req, no auth_req
|
|
73
|
+
> It's attacker. close it.
|
|
74
|
+
2. not authorized but cid ready connnection.
|
|
75
|
+
> anonymouse remote client.
|
|
76
|
+
> allowed some wrong(broken) message. small quota.
|
|
77
|
+
3. authorized remote connection.
|
|
78
|
+
> allowed some wrong(broken) message. large quota.
|
|
79
|
+
*/
|
|
80
|
+
if (this.cid) { // category 2 and 3.
|
|
81
|
+
// temporary broken message.
|
|
82
|
+
// console.log('wrong congpack from user:', Date(), this.ip, this.ssid, this.cid)
|
|
83
|
+
} else {
|
|
84
|
+
// attacker
|
|
85
|
+
let msg = ""
|
|
86
|
+
try {
|
|
87
|
+
msg = decoder.decode(message)
|
|
88
|
+
} catch (error) {
|
|
89
|
+
msg += 'Len: ' + message.byteLength
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// console.log('attacker message:', attackMsg)
|
|
93
|
+
if (this.manager.attackLogger) {
|
|
94
|
+
let attackMsg = `> ${this.ssid} ${this.ip} ${msg}`
|
|
95
|
+
this.manager.attackLogger.log(attackMsg)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.close(true);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
permissionChecker() {
|
|
104
|
+
// type1.
|
|
105
|
+
// accept some traffic before authorize.
|
|
106
|
+
const limitBytes = 100
|
|
107
|
+
|
|
108
|
+
let txBytes = this.socket.bytesWritten || this.socket._socket?.bytesWritten
|
|
109
|
+
let rxBytes = this.socket.bytesRead || this.socket._socket?.bytesRead
|
|
110
|
+
|
|
111
|
+
if (txBytes == undefined) txBytes = 0
|
|
112
|
+
if (rxBytes == undefined) rxBytes = 0
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// any type of received messages: message, ping, pong
|
|
118
|
+
receiveMonitor() {
|
|
119
|
+
this.socket.rxCounter++
|
|
120
|
+
this.socket.isAlive = true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
isConnectionHTTPS(req) {
|
|
124
|
+
//In case of reverse proxy environment. i.e. using nginx proxy_pass.
|
|
125
|
+
return req.headers["x-forwarded-proto"]; //https or undefined
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getRemoteIP(req) { //req or ip string
|
|
129
|
+
let ip;
|
|
130
|
+
if (req && req.headers) {
|
|
131
|
+
ip = req.headers["x-forwarded-for"];
|
|
132
|
+
if (ip == undefined) ip = req.socket.remoteAddress;
|
|
133
|
+
} else {
|
|
134
|
+
ip = req;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (ip) {
|
|
138
|
+
if (ip.indexOf("::ffff:") == 0) ip = ip.substring(7);
|
|
139
|
+
if (ip == "::1") ip = "127.0.0.1";
|
|
140
|
+
} else {
|
|
141
|
+
ip = "0.0.0.0";
|
|
142
|
+
}
|
|
143
|
+
return ip;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
ping() {
|
|
147
|
+
if (this.socketType === 'websocket') {
|
|
148
|
+
this.socket.ping()
|
|
149
|
+
this.socket.txCounter++
|
|
150
|
+
} else {
|
|
151
|
+
this.send(Buffer.from([IOMsg.PING]))
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
pong() {
|
|
156
|
+
if (this.socketType === 'websocket') {
|
|
157
|
+
this.socket.pong()
|
|
158
|
+
this.socket.txCounter++
|
|
159
|
+
} else {
|
|
160
|
+
this.send(Buffer.from([IOMsg.PONG]))
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
getTraffic() {
|
|
165
|
+
let txb = this.socket.bytesWritten || this.socket._socket?.bytesWritten
|
|
166
|
+
let rxb = this.socket.bytesRead || this.socket._socket?.bytesRead
|
|
167
|
+
let tx = this.socket.txCounter
|
|
168
|
+
let rx = this.socket.rxCounter
|
|
169
|
+
let traffic = { tx, txb, rx, rxb }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
close(terminateNow = false) {
|
|
173
|
+
this.getTraffic()
|
|
174
|
+
if (this.socketType === 'websocket') {
|
|
175
|
+
if (terminateNow) this.socket.terminate();
|
|
176
|
+
else this.socket.close();
|
|
177
|
+
} else {
|
|
178
|
+
if (terminateNow) this.socket.destroy();
|
|
179
|
+
else this.socket.end();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
send(message, isBinary) {
|
|
184
|
+
this.manager.txBytes += message.byteLength;
|
|
185
|
+
this.socket.txCounter++;
|
|
186
|
+
if (this.socketType === 'websocket') {
|
|
187
|
+
if (this.socket.readyState === 1) {
|
|
188
|
+
if (isBinary != undefined) {
|
|
189
|
+
this.socket.send(message, { binary: isBinary });
|
|
190
|
+
} else {
|
|
191
|
+
this.socket.send(message);
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
this.close(true)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
} else { //CongSocket
|
|
198
|
+
if (this.socket.readyState == 'open') {
|
|
199
|
+
this.socket.write(pack(message))
|
|
200
|
+
} else {
|
|
201
|
+
this.close(true)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
}
|