iosignal 1.7.0 → 1.8.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +254 -247
  3. package/auth_file.json +13 -13
  4. package/auth_file.mjs +23 -23
  5. package/dist/iosignal-iife.js +11 -0
  6. package/dist/iosignal-iife.js.map +1 -0
  7. package/dist/iosignal.cjs +1 -2
  8. package/dist/iosignal.js +11 -0
  9. package/dist/iosignal.js.map +1 -0
  10. package/index.js +19 -19
  11. package/indexWebBrowser.js +13 -13
  12. package/package.json +56 -59
  13. package/rollup.config.js +72 -69
  14. package/src/api/RedisAPI.js +65 -65
  15. package/src/api/api_constant.js +7 -7
  16. package/src/api/api_reply.js +31 -31
  17. package/src/api/api_sudo.js +91 -91
  18. package/src/auth/Auth_Env.js +76 -76
  19. package/src/auth/Auth_File.js +71 -71
  20. package/src/auth/Auth_Redis.js +44 -44
  21. package/src/auth/BohoAuth.js +164 -164
  22. package/src/client/CongPacket.js +141 -141
  23. package/src/client/IOCongSocket.js +81 -81
  24. package/src/client/IOCore.js +822 -822
  25. package/src/client/IOWS.js +80 -80
  26. package/src/client/IOWebSocket.js +111 -111
  27. package/src/common/constants.js +148 -148
  28. package/src/common/payload.js +81 -81
  29. package/src/common/quotaTable.js +68 -68
  30. package/src/common/util.js +61 -61
  31. package/src/server/FileLogger.js +30 -30
  32. package/src/server/Manager.js +433 -433
  33. package/src/server/Metric.js +155 -155
  34. package/src/server/Remote.js +207 -207
  35. package/src/server/RemoteCore.js +439 -439
  36. package/src/server/Server.js +163 -163
  37. package/src/server/serverOption.js +56 -56
  38. package/test-browser/favicon.ico +0 -0
  39. package/test-browser/index.html +96 -96
  40. package/test-browser/server.js +11 -11
  41. package/test-client-nodejs/client.cjs +17 -17
  42. package/test-client-nodejs/client.mjs +17 -17
  43. package/test-client-nodejs/client_api_reply.mjs +15 -15
  44. package/test-client-nodejs/stateChange.mjs +19 -19
  45. package/test-server/server-Auth_Env.js +19 -19
  46. package/test-server/server-Auth_File.js +12 -12
  47. package/test-server/server-api_reply.js +6 -6
  48. package/test-server/server-api_sudo.js +7 -7
  49. package/test-server/server-commonjs.cjs +8 -8
  50. package/test-server/server-esm.mjs +20 -20
  51. package/test-server/simple-server.mjs +11 -11
  52. package/test-signal-payloadType/signalPack.js +34 -34
  53. package/test-signal-payloadType/signaling.js +60 -60
  54. package/dist/iosignal.esm.js +0 -3
  55. package/dist/iosignal.esm.js.map +0 -1
  56. package/dist/iosignal.min.js +0 -3
  57. package/dist/iosignal.min.js.map +0 -1
@@ -1,439 +1,439 @@
1
- import { webcrypto } from 'crypto';
2
- import { MBP } from 'meta-buffer-pack'
3
- import { Boho, BohoMsg, MetaSize } from 'boho'
4
- import { serverOption } from './serverOption.js';
5
- import { IOMsg, ENC_MODE, CLIENT_STATE } from '../common/constants.js'
6
- import { quotaTable } from '../common/quotaTable.js'
7
-
8
- const decoder = new TextDecoder()
9
-
10
- const NB = MBP.NB
11
- const MB = MBP.MB
12
-
13
-
14
- export class RemoteCore {
15
- constructor(socket, manager) {
16
-
17
- this.socket = socket;
18
- socket.isAlive = true;
19
- socket.txCounter = 0;
20
- socket.rxCounter = 0;
21
- socket.openTime = Date.now()
22
-
23
- this.boho = new Boho()
24
- this.encMode = ENC_MODE.AUTO;
25
- this.channels = new Set(); // subscribed tags
26
- this.retain_signal = new Map();
27
-
28
- this.ssid = RemoteCore.ssid++; // ordered index number
29
- this.manager = manager;
30
-
31
- this.cid; // Communication Id
32
- this.did; // Device Id
33
- this.uid; // User Id
34
- this.nick = ""
35
-
36
-
37
- this.lastEchoMessage = "N"
38
- this.privateNode = false
39
- this.HOME_CHANNEL = ""
40
- this.level = serverOption.defaultQuotaIndex;
41
- this.quota = quotaTable[this.level];
42
- this.isAdmin = false;
43
-
44
- this.state;
45
- this.stateLog = [];
46
- this.setState(CLIENT_STATE.INIT)
47
-
48
- }
49
-
50
- static ssid = 1;
51
-
52
- setState(state) {
53
- this.state = state
54
- if (serverOption.debug.showAuthInfo) {
55
- this.stateLog.push(state)
56
- console.log(this.stateLog.join('>'))
57
- }
58
- }
59
-
60
- getState() { // <Number>
61
- return this.state
62
- }
63
-
64
- getStateName() { // <String>
65
- return (CLIENT_STATE[this.state]).toLowerCase()
66
- }
67
-
68
-
69
- showMessageLog(message, isBinary) {
70
- let from = this.boho.isAuthorized ? `did: ${this.did} ${this.cid}@` : `${this.cid}@`
71
- if (isBinary) {
72
- let msgTypeName = IOMsg[message[0]]
73
- if (!msgTypeName) msgTypeName = BohoMsg[message[0]]
74
- msgTypeName = ' [' + msgTypeName + ']';
75
- if (message.byteLength > 40) {
76
- console.log(from + msgTypeName + ' LEN:', message.length);
77
- } else {
78
- console.log(from + msgTypeName, message);
79
- }
80
-
81
- } else {
82
- console.log(from + ' [TEXT] %s', message);
83
- }
84
- }
85
-
86
- rxQuotaChecker(message) {
87
- let rxBytes = message.byteLength
88
- this.manager.rxBytes += rxBytes;
89
-
90
- if (serverOption.useQuota.signalSize && (rxBytes > this.quota.signalSize)) {
91
- console.log('## quota: size over')
92
- this.send(Buffer.from([IOMsg.OVER_SIZE]))
93
- if (serverOption.useQuota.disconnect) this.close();
94
- return false;
95
- }
96
-
97
- // todo:
98
- // traffic rate :
99
- return true;
100
- }
101
-
102
- onTimeDelayMessage(message, isBinary = true) {
103
- setTimeout(() => {
104
- this.onSocketMessage(message, isBinary)
105
- }
106
- , serverOption.debug.delay)
107
- }
108
-
109
- // CongSocket or WebSocket
110
- onSocketMessage(message, isBinary = true) {
111
-
112
- this.receiveMonitor() // rx-data, ping/pong and timeout check
113
- if (!this.rxQuotaChecker(message)) return
114
-
115
- if (serverOption.showMessage === 'message') this.showMessageLog(message, isBinary)
116
-
117
- let msgType, decoded
118
-
119
- if (isBinary) {
120
- msgType = message[0]
121
-
122
- if (msgType === BohoMsg.ENC_488) {
123
-
124
- try {
125
- decoded = this.boho.decrypt_488(message)
126
- } catch (err) {
127
- console.log('-- E488 DEC_FAIL', err)
128
- return
129
- }
130
-
131
- if (decoded) {
132
- msgType = decoded[0]
133
- message = decoded
134
- } else {
135
- return
136
- }
137
-
138
- } else if (msgType === BohoMsg.ENC_E2E) { // symetric E2EE Signal
139
- try {
140
- decoded = this.boho.decrypt_488(message)
141
- } catch (err) {
142
- // console.log('-- E2E DEC_FAIL', err)
143
- return
144
- }
145
- // console.log('e2e unpack:', decoded )
146
- if (decoded) {
147
- msgType = decoded[0]
148
- message.set(decoded, MetaSize.ENC_488) // set decoded signal_e2e headaer.
149
- message = message.subarray(MetaSize.ENC_488) // reset offset.
150
- } else {
151
- return
152
- }
153
-
154
- } else {
155
- // console.log( `[P] ${ IOMsg[ msgType] || BohoMsg[cmd]} LEN: ${message.byteLength}` )
156
- }
157
-
158
- switch (msgType) {
159
- case IOMsg.PING:
160
- this.pong();
161
- break;
162
-
163
- case IOMsg.PONG:
164
- break;
165
-
166
- case IOMsg.CID_REQ:
167
- if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
168
- // Protocol violation: CID_REQ was sent before receiving the SERVER_READY signal.
169
- this.close()
170
- }
171
-
172
- if (!this.cid) {
173
- this.cid = '?' + webcrypto.getRandomValues(Buffer.alloc(3)).toString('base64url')
174
- this.manager.cid2remote.set(this.cid, this)
175
- }
176
-
177
- // console.log('<< SENDING CID_RES:', this.cid)
178
- this.send_enc_mode(MBP.pack(
179
- MB('#cid_ack', '8', IOMsg.CID_RES),
180
- MB('#cid', this.cid)
181
- ))
182
-
183
- this.setState(CLIENT_STATE.CID_READY)
184
- break;
185
-
186
-
187
- case IOMsg.ECHO: // TEXT only
188
- try {
189
- let msg = decoder.decode(message.subarray(1))
190
- this.lastEchoMessage = msg
191
- } catch (error) {
192
- // console.log('ECHO message is not a text')
193
- }
194
- this.send(message, isBinary)
195
- break;
196
-
197
- case IOMsg.IAM:
198
- if (message.byteLength > 1) {
199
- let iamInfo = message.subarray(1)
200
- this.nick = decoder.decode(iamInfo)
201
- console.log('iam nick reset', this.nick)
202
- }
203
- this.iamResponse()
204
- break;
205
-
206
- case IOMsg.SIGNAL_E2E:
207
- case IOMsg.SIGNAL:
208
- if (message.byteLength >= 3) {
209
- let tagLen = message.readUInt8(1)
210
- if (message.byteLength >= tagLen + 2) {
211
- let tag = message.subarray(2, 2 + tagLen)
212
- tag = decoder.decode(tag)
213
- this.manager.sender(tag, this, message)
214
- }
215
- }
216
- break;
217
-
218
-
219
-
220
- case IOMsg.UNSUBSCRIBE:
221
- if (message.byteLength == 2) {
222
- this.manager.unsubscribe([""], this)
223
- } else if (message.byteLength >= 3) {
224
- let tagLen = message.readUInt8(1)
225
-
226
- if (message.byteLength == tagLen + 2) {
227
- let tag = message.subarray(2, 2 + tagLen)
228
- tag = decoder.decode(tag)
229
- let tagList = tag.split(',')
230
- this.manager.unsubscribe(tagList, this)
231
- }
232
- }
233
- break;
234
-
235
-
236
- case IOMsg.SUBSCRIBE: // 1byte tagLen
237
- if (message.byteLength >= 3) {
238
- let tagLen = message.readUInt8(1)
239
- if (message.byteLength == tagLen + 2) {
240
- let tag = message.subarray(2, 2 + tagLen)
241
- tag = decoder.decode(tag)
242
- let tagList = tag.split(',')
243
- this.manager.subscribe(tagList, this)
244
- }
245
- }
246
- break;
247
-
248
- case IOMsg.SUBSCRIBE_REQ: // 2bytes tagLen
249
- if (message.byteLength >= 6) {
250
- let msgID = message.readUInt16BE(1)
251
- let tagLen = message.readUInt16BE(3)
252
- if (message.byteLength == tagLen + 5) {
253
- let tag = message.subarray(5, 5 + tagLen)
254
- tag = decoder.decode(tag)
255
- let tagList = tag.split(',')
256
- // console.log('>> SUBSCRIBE_REQ from:', this.cid, tagList)
257
- this.manager.subscribe(tagList, this)
258
- this.response(msgID, 0)
259
- } else {
260
- this.response(msgID, 255)
261
- }
262
- }
263
- break;
264
-
265
-
266
- case IOMsg.REQUEST: //api request
267
- try {
268
- let req = MBP.unpack(message)
269
- if (req) {
270
- if (!req.target || !req.topic) return
271
- if (this.manager.server.apiNames.has(req.target)) {
272
- this.manager.server.emit(req.target, this, req)
273
- } else {
274
- // console.log('UnKnown API req: ', req.target)
275
- }
276
- }
277
- } catch (e) {
278
- console.log('request catch error', e)
279
- }
280
- break;
281
-
282
-
283
- case IOMsg.CLOSE:
284
- if (message.byteLength > 1) {
285
- let reason = decoder.decode(message.subarray(1))
286
- console.log('>> CLOSE reason:', reason)
287
- this.close();
288
- }
289
- break;
290
-
291
-
292
- // Auth
293
- case BohoMsg.AUTH_REQ:
294
- if (!this.manager.authManager) return
295
- if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
296
- // console.log('protocol error. auth_req before server_ready')
297
- this.close();
298
- }
299
- this.setState(CLIENT_STATE.RECV_AUTH_REQ)
300
- let auth_nonce_pack = this.boho.auth_nonce()
301
- // console.log('## auth_nonce_pack', auth_nonce_pack )
302
- this.send(auth_nonce_pack)
303
- this.setState(CLIENT_STATE.SENT_SERVER_NONCE)
304
- break;
305
-
306
- case BohoMsg.AUTH_HMAC:
307
- if (!this.manager.authManager) return
308
- if (this.state < CLIENT_STATE.SENT_SERVER_NONCE) {
309
- // console.log('protocol error. auth_hmac before server_nonce')
310
- this.close();
311
- }
312
- this.setState(CLIENT_STATE.RECV_AUTH_HMAC)
313
- //async
314
- this.manager.authManager.verify_auth_hmac(message, this)
315
- .then(authInfo => {
316
- if (authInfo) {
317
- this.manager.deligateSignal(remote, '@$name', authInfo.name)
318
- // console.log('device login success authInfo', authInfo)
319
- // this.manager.sender('@$name', authInfo.name )
320
- }
321
-
322
- })
323
- .catch(e => {
324
-
325
- })
326
-
327
- return;
328
-
329
- default:
330
- // console.log('unknown MsgType:',msgType,' from:', this.ip, this.ssid, this.cid )
331
-
332
- }
333
-
334
- } else {
335
- // TEXT FRAME
336
- try {
337
- let textMessage = decoder.decode(message)
338
- // console.log('text buffer:',textMessage )
339
- // console.log('text buffer:',message.toString('hex') )
340
- this.manager.server.emit('text_message', textMessage, this)
341
- } catch (error) {
342
-
343
- }
344
- }
345
-
346
- }
347
-
348
-
349
- response(msgId, statusCode, body) {
350
- // console.log('response body', body)
351
- if (body) {
352
-
353
- this.send_enc_mode(MBP.pack(
354
- MB('#type', '8', IOMsg.RESPONSE_MBP),
355
- MB('status', '8', statusCode),
356
- MB('mid', '16', msgId),
357
- MB('body', body)
358
- ))
359
-
360
- } else {
361
- this.send_enc_mode(MBP.pack(
362
- MB('#type', '8', IOMsg.RESPONSE_MBP),
363
- MB('status', '8', statusCode),
364
- MB('mid', '16', msgId)
365
- ))
366
-
367
- }
368
-
369
- }
370
-
371
-
372
- send_enc_mode(data, useEncryption = false) {
373
-
374
- if (this.encMode === ENC_MODE.YES ||
375
- this.encMode === ENC_MODE.AUTO &&
376
- !this.TLS && this.boho.isAuthorized
377
- ) useEncryption = true;
378
-
379
- // console.log('svr useEnc', useEncryption, data )
380
-
381
- if (useEncryption && data[0] == IOMsg.SIGNAL_E2E) {
382
- // When the server delivers end-to-end encrypted messages,
383
- // it should only encrypt the header part and pass the data part as is.
384
- let tagLen = data[1];
385
-
386
- // let tagInfo = decoder.decode( data.subarray( 2, 2 + tagLen) )
387
- // console.log('server bypass E2E signal taginfo:', tagInfo )
388
-
389
- let encHeader = this.boho.encrypt_488(data.subarray(0, 3 + tagLen))
390
- encHeader[0] = BohoMsg.ENC_E2E
391
- let newEncBuffer = Buffer.concat([encHeader, data.subarray(3 + tagLen)])
392
- this.send(newEncBuffer)
393
-
394
- } else if (useEncryption) {
395
- let encPack = this.boho.encrypt_488(data)
396
- if (encPack) {
397
- this.send(encPack)
398
- } else {
399
- // console.log('encryption FAIL: NO DATA TRANSIT')
400
- }
401
- } else {
402
- // console.log('send -N-')
403
- this.send(data)
404
-
405
- }
406
-
407
- }
408
-
409
-
410
-
411
- iamResponse(info = "") {
412
-
413
- if (info == "") {
414
- let channels = []
415
- for (let tag of this.channels.keys()) {
416
- channels.push(tag)
417
- }
418
-
419
- info = {
420
- "ssid": this.ssid,
421
- "uid": this.uid,
422
- "cid": this.cid,
423
- "did": this.did,
424
- "nick": this.nick,
425
- "ip": this.ip
426
- , 'tag': channels
427
- }
428
- }
429
-
430
- let pack = MBP.pack(
431
- MB('#MsgType', '8', IOMsg.IAM_RES),
432
- MB('#info', info)
433
- )
434
- this.send_enc_mode(pack)
435
- }
436
-
437
-
438
- }
439
-
1
+ import { webcrypto } from 'crypto';
2
+ import { MBP } from 'meta-buffer-pack'
3
+ import { Boho, BohoMsg, MetaSize } from 'boho'
4
+ import { serverOption } from './serverOption.js';
5
+ import { IOMsg, ENC_MODE, CLIENT_STATE } from '../common/constants.js'
6
+ import { quotaTable } from '../common/quotaTable.js'
7
+
8
+ const decoder = new TextDecoder()
9
+
10
+ const NB = MBP.NB
11
+ const MB = MBP.MB
12
+
13
+
14
+ export class RemoteCore {
15
+ constructor(socket, manager) {
16
+
17
+ this.socket = socket;
18
+ socket.isAlive = true;
19
+ socket.txCounter = 0;
20
+ socket.rxCounter = 0;
21
+ socket.openTime = Date.now()
22
+
23
+ this.boho = new Boho()
24
+ this.encMode = ENC_MODE.AUTO;
25
+ this.channels = new Set(); // subscribed tags
26
+ this.retain_signal = new Map();
27
+
28
+ this.ssid = RemoteCore.ssid++; // ordered index number
29
+ this.manager = manager;
30
+
31
+ this.cid; // Communication Id
32
+ this.did; // Device Id
33
+ this.uid; // User Id
34
+ this.nick = ""
35
+
36
+
37
+ this.lastEchoMessage = "N"
38
+ this.privateNode = false
39
+ this.HOME_CHANNEL = ""
40
+ this.level = serverOption.defaultQuotaIndex;
41
+ this.quota = quotaTable[this.level];
42
+ this.isAdmin = false;
43
+
44
+ this.state;
45
+ this.stateLog = [];
46
+ this.setState(CLIENT_STATE.INIT)
47
+
48
+ }
49
+
50
+ static ssid = 1;
51
+
52
+ setState(state) {
53
+ this.state = state
54
+ if (serverOption.debug.showAuthInfo) {
55
+ this.stateLog.push(state)
56
+ console.log(this.stateLog.join('>'))
57
+ }
58
+ }
59
+
60
+ getState() { // <Number>
61
+ return this.state
62
+ }
63
+
64
+ getStateName() { // <String>
65
+ return (CLIENT_STATE[this.state]).toLowerCase()
66
+ }
67
+
68
+
69
+ showMessageLog(message, isBinary) {
70
+ let from = this.boho.isAuthorized ? `did: ${this.did} ${this.cid}@` : `${this.cid}@`
71
+ if (isBinary) {
72
+ let msgTypeName = IOMsg[message[0]]
73
+ if (!msgTypeName) msgTypeName = BohoMsg[message[0]]
74
+ msgTypeName = ' [' + msgTypeName + ']';
75
+ if (message.byteLength > 40) {
76
+ console.log(from + msgTypeName + ' LEN:', message.length);
77
+ } else {
78
+ console.log(from + msgTypeName, message);
79
+ }
80
+
81
+ } else {
82
+ console.log(from + ' [TEXT] %s', message);
83
+ }
84
+ }
85
+
86
+ rxQuotaChecker(message) {
87
+ let rxBytes = message.byteLength
88
+ this.manager.rxBytes += rxBytes;
89
+
90
+ if (serverOption.useQuota.signalSize && (rxBytes > this.quota.signalSize)) {
91
+ console.log('## quota: size over')
92
+ this.send(Buffer.from([IOMsg.OVER_SIZE]))
93
+ if (serverOption.useQuota.disconnect) this.close();
94
+ return false;
95
+ }
96
+
97
+ // todo:
98
+ // traffic rate :
99
+ return true;
100
+ }
101
+
102
+ onTimeDelayMessage(message, isBinary = true) {
103
+ setTimeout(() => {
104
+ this.onSocketMessage(message, isBinary)
105
+ }
106
+ , serverOption.debug.delay)
107
+ }
108
+
109
+ // CongSocket or WebSocket
110
+ onSocketMessage(message, isBinary = true) {
111
+
112
+ this.receiveMonitor() // rx-data, ping/pong and timeout check
113
+ if (!this.rxQuotaChecker(message)) return
114
+
115
+ if (serverOption.showMessage === 'message') this.showMessageLog(message, isBinary)
116
+
117
+ let msgType, decoded
118
+
119
+ if (isBinary) {
120
+ msgType = message[0]
121
+
122
+ if (msgType === BohoMsg.ENC_488) {
123
+
124
+ try {
125
+ decoded = this.boho.decrypt_488(message)
126
+ } catch (err) {
127
+ console.log('-- E488 DEC_FAIL', err)
128
+ return
129
+ }
130
+
131
+ if (decoded) {
132
+ msgType = decoded[0]
133
+ message = decoded
134
+ } else {
135
+ return
136
+ }
137
+
138
+ } else if (msgType === BohoMsg.ENC_E2E) { // symetric E2EE Signal
139
+ try {
140
+ decoded = this.boho.decrypt_488(message)
141
+ } catch (err) {
142
+ // console.log('-- E2E DEC_FAIL', err)
143
+ return
144
+ }
145
+ // console.log('e2e unpack:', decoded )
146
+ if (decoded) {
147
+ msgType = decoded[0]
148
+ message.set(decoded, MetaSize.ENC_488) // set decoded signal_e2e headaer.
149
+ message = message.subarray(MetaSize.ENC_488) // reset offset.
150
+ } else {
151
+ return
152
+ }
153
+
154
+ } else {
155
+ // console.log( `[P] ${ IOMsg[ msgType] || BohoMsg[cmd]} LEN: ${message.byteLength}` )
156
+ }
157
+
158
+ switch (msgType) {
159
+ case IOMsg.PING:
160
+ this.pong();
161
+ break;
162
+
163
+ case IOMsg.PONG:
164
+ break;
165
+
166
+ case IOMsg.CID_REQ:
167
+ if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
168
+ // Protocol violation: CID_REQ was sent before receiving the SERVER_READY signal.
169
+ this.close()
170
+ }
171
+
172
+ if (!this.cid) {
173
+ this.cid = '?' + webcrypto.getRandomValues(Buffer.alloc(3)).toString('base64url')
174
+ this.manager.cid2remote.set(this.cid, this)
175
+ }
176
+
177
+ // console.log('<< SENDING CID_RES:', this.cid)
178
+ this.send_enc_mode(MBP.pack(
179
+ MB('#cid_ack', '8', IOMsg.CID_RES),
180
+ MB('#cid', this.cid)
181
+ ))
182
+
183
+ this.setState(CLIENT_STATE.CID_READY)
184
+ break;
185
+
186
+
187
+ case IOMsg.ECHO: // TEXT only
188
+ try {
189
+ let msg = decoder.decode(message.subarray(1))
190
+ this.lastEchoMessage = msg
191
+ } catch (error) {
192
+ // console.log('ECHO message is not a text')
193
+ }
194
+ this.send(message, isBinary)
195
+ break;
196
+
197
+ case IOMsg.IAM:
198
+ if (message.byteLength > 1) {
199
+ let iamInfo = message.subarray(1)
200
+ this.nick = decoder.decode(iamInfo)
201
+ console.log('iam nick reset', this.nick)
202
+ }
203
+ this.iamResponse()
204
+ break;
205
+
206
+ case IOMsg.SIGNAL_E2E:
207
+ case IOMsg.SIGNAL:
208
+ if (message.byteLength >= 3) {
209
+ let tagLen = message.readUInt8(1)
210
+ if (message.byteLength >= tagLen + 2) {
211
+ let tag = message.subarray(2, 2 + tagLen)
212
+ tag = decoder.decode(tag)
213
+ this.manager.sender(tag, this, message)
214
+ }
215
+ }
216
+ break;
217
+
218
+
219
+
220
+ case IOMsg.UNSUBSCRIBE:
221
+ if (message.byteLength == 2) {
222
+ this.manager.unsubscribe([""], this)
223
+ } else if (message.byteLength >= 3) {
224
+ let tagLen = message.readUInt8(1)
225
+
226
+ if (message.byteLength == tagLen + 2) {
227
+ let tag = message.subarray(2, 2 + tagLen)
228
+ tag = decoder.decode(tag)
229
+ let tagList = tag.split(',')
230
+ this.manager.unsubscribe(tagList, this)
231
+ }
232
+ }
233
+ break;
234
+
235
+
236
+ case IOMsg.SUBSCRIBE: // 1byte tagLen
237
+ if (message.byteLength >= 3) {
238
+ let tagLen = message.readUInt8(1)
239
+ if (message.byteLength == tagLen + 2) {
240
+ let tag = message.subarray(2, 2 + tagLen)
241
+ tag = decoder.decode(tag)
242
+ let tagList = tag.split(',')
243
+ this.manager.subscribe(tagList, this)
244
+ }
245
+ }
246
+ break;
247
+
248
+ case IOMsg.SUBSCRIBE_REQ: // 2bytes tagLen
249
+ if (message.byteLength >= 6) {
250
+ let msgID = message.readUInt16BE(1)
251
+ let tagLen = message.readUInt16BE(3)
252
+ if (message.byteLength == tagLen + 5) {
253
+ let tag = message.subarray(5, 5 + tagLen)
254
+ tag = decoder.decode(tag)
255
+ let tagList = tag.split(',')
256
+ // console.log('>> SUBSCRIBE_REQ from:', this.cid, tagList)
257
+ this.manager.subscribe(tagList, this)
258
+ this.response(msgID, 0)
259
+ } else {
260
+ this.response(msgID, 255)
261
+ }
262
+ }
263
+ break;
264
+
265
+
266
+ case IOMsg.REQUEST: //api request
267
+ try {
268
+ let req = MBP.unpack(message)
269
+ if (req) {
270
+ if (!req.target || !req.topic) return
271
+ if (this.manager.server.apiNames.has(req.target)) {
272
+ this.manager.server.emit(req.target, this, req)
273
+ } else {
274
+ // console.log('UnKnown API req: ', req.target)
275
+ }
276
+ }
277
+ } catch (e) {
278
+ console.log('request catch error', e)
279
+ }
280
+ break;
281
+
282
+
283
+ case IOMsg.CLOSE:
284
+ if (message.byteLength > 1) {
285
+ let reason = decoder.decode(message.subarray(1))
286
+ console.log('>> CLOSE reason:', reason)
287
+ this.close();
288
+ }
289
+ break;
290
+
291
+
292
+ // Auth
293
+ case BohoMsg.AUTH_REQ:
294
+ if (!this.manager.authManager) return
295
+ if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
296
+ // console.log('protocol error. auth_req before server_ready')
297
+ this.close();
298
+ }
299
+ this.setState(CLIENT_STATE.RECV_AUTH_REQ)
300
+ let auth_nonce_pack = this.boho.auth_nonce()
301
+ // console.log('## auth_nonce_pack', auth_nonce_pack )
302
+ this.send(auth_nonce_pack)
303
+ this.setState(CLIENT_STATE.SENT_SERVER_NONCE)
304
+ break;
305
+
306
+ case BohoMsg.AUTH_HMAC:
307
+ if (!this.manager.authManager) return
308
+ if (this.state < CLIENT_STATE.SENT_SERVER_NONCE) {
309
+ // console.log('protocol error. auth_hmac before server_nonce')
310
+ this.close();
311
+ }
312
+ this.setState(CLIENT_STATE.RECV_AUTH_HMAC)
313
+ //async
314
+ this.manager.authManager.verify_auth_hmac(message, this)
315
+ .then(authInfo => {
316
+ if (authInfo) {
317
+ this.manager.deligateSignal(remote, '@$name', authInfo.name)
318
+ // console.log('device login success authInfo', authInfo)
319
+ // this.manager.sender('@$name', authInfo.name )
320
+ }
321
+
322
+ })
323
+ .catch(e => {
324
+
325
+ })
326
+
327
+ return;
328
+
329
+ default:
330
+ // console.log('unknown MsgType:',msgType,' from:', this.ip, this.ssid, this.cid )
331
+
332
+ }
333
+
334
+ } else {
335
+ // TEXT FRAME
336
+ try {
337
+ let textMessage = decoder.decode(message)
338
+ // console.log('text buffer:',textMessage )
339
+ // console.log('text buffer:',message.toString('hex') )
340
+ this.manager.server.emit('text_message', textMessage, this)
341
+ } catch (error) {
342
+
343
+ }
344
+ }
345
+
346
+ }
347
+
348
+
349
+ response(msgId, statusCode, body) {
350
+ // console.log('response body', body)
351
+ if (body) {
352
+
353
+ this.send_enc_mode(MBP.pack(
354
+ MB('#type', '8', IOMsg.RESPONSE_MBP),
355
+ MB('status', '8', statusCode),
356
+ MB('mid', '16', msgId),
357
+ MB('body', body)
358
+ ))
359
+
360
+ } else {
361
+ this.send_enc_mode(MBP.pack(
362
+ MB('#type', '8', IOMsg.RESPONSE_MBP),
363
+ MB('status', '8', statusCode),
364
+ MB('mid', '16', msgId)
365
+ ))
366
+
367
+ }
368
+
369
+ }
370
+
371
+
372
+ send_enc_mode(data, useEncryption = false) {
373
+
374
+ if (this.encMode === ENC_MODE.YES ||
375
+ this.encMode === ENC_MODE.AUTO &&
376
+ !this.TLS && this.boho.isAuthorized
377
+ ) useEncryption = true;
378
+
379
+ // console.log('svr useEnc', useEncryption, data )
380
+
381
+ if (useEncryption && data[0] == IOMsg.SIGNAL_E2E) {
382
+ // When the server delivers end-to-end encrypted messages,
383
+ // it should only encrypt the header part and pass the data part as is.
384
+ let tagLen = data[1];
385
+
386
+ // let tagInfo = decoder.decode( data.subarray( 2, 2 + tagLen) )
387
+ // console.log('server bypass E2E signal taginfo:', tagInfo )
388
+
389
+ let encHeader = this.boho.encrypt_488(data.subarray(0, 3 + tagLen))
390
+ encHeader[0] = BohoMsg.ENC_E2E
391
+ let newEncBuffer = Buffer.concat([encHeader, data.subarray(3 + tagLen)])
392
+ this.send(newEncBuffer)
393
+
394
+ } else if (useEncryption) {
395
+ let encPack = this.boho.encrypt_488(data)
396
+ if (encPack) {
397
+ this.send(encPack)
398
+ } else {
399
+ // console.log('encryption FAIL: NO DATA TRANSIT')
400
+ }
401
+ } else {
402
+ // console.log('send -N-')
403
+ this.send(data)
404
+
405
+ }
406
+
407
+ }
408
+
409
+
410
+
411
+ iamResponse(info = "") {
412
+
413
+ if (info == "") {
414
+ let channels = []
415
+ for (let tag of this.channels.keys()) {
416
+ channels.push(tag)
417
+ }
418
+
419
+ info = {
420
+ "ssid": this.ssid,
421
+ "uid": this.uid,
422
+ "cid": this.cid,
423
+ "did": this.did,
424
+ "nick": this.nick,
425
+ "ip": this.ip
426
+ , 'tag': channels
427
+ }
428
+ }
429
+
430
+ let pack = MBP.pack(
431
+ MB('#MsgType', '8', IOMsg.IAM_RES),
432
+ MB('#info', info)
433
+ )
434
+ this.send_enc_mode(pack)
435
+ }
436
+
437
+
438
+ }
439
+