iosignal 5.2.0 → 5.3.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 (40) hide show
  1. package/README.md +163 -0
  2. package/dist/browser/esm/io.d.ts +8 -9
  3. package/dist/browser/esm/io.js +5 -5
  4. package/dist/browser/esm/io.js.map +1 -1
  5. package/dist/browser/iife/io.js +5 -5
  6. package/dist/browser/iife/io.js.map +1 -1
  7. package/dist/node/iosignal.cjs +124 -119
  8. package/dist/node/iosignal.js +123 -118
  9. package/dist/types/client/IOCore.d.ts +2 -2
  10. package/dist/types/client/browser/IOWebSocket.d.ts +4 -4
  11. package/dist/types/common/payload.d.ts +2 -2
  12. package/package.json +21 -3
  13. package/.vscode/settings.json +0 -3
  14. package/index.js +0 -26
  15. package/rollup.config.js +0 -59
  16. package/src/auth/BohoAuth.js +0 -196
  17. package/src/auth/key_providers/FileKeyProvider.js +0 -59
  18. package/src/auth/key_providers/RedisKeyProvider.js +0 -46
  19. package/src/auth/key_providers/StringKeyProvider.js +0 -54
  20. package/src/client/CongPacket.js +0 -143
  21. package/src/client/IOCongSocket.js +0 -92
  22. package/src/client/IOCore.js +0 -1202
  23. package/src/client/IOWS.js +0 -78
  24. package/src/client/browser/IOWebSocket.js +0 -202
  25. package/src/common/constants.js +0 -210
  26. package/src/common/payload.js +0 -82
  27. package/src/common/quotaTable.js +0 -68
  28. package/src/common/util.js +0 -64
  29. package/src/server/FileLogger.js +0 -30
  30. package/src/server/Manager.js +0 -450
  31. package/src/server/Metrics.js +0 -155
  32. package/src/server/Remote.js +0 -216
  33. package/src/server/RemoteCore.js +0 -427
  34. package/src/server/Server.js +0 -236
  35. package/src/server/serverOption.js +0 -56
  36. package/src/services/RedisService.js +0 -53
  37. package/src/services/constant.js +0 -7
  38. package/src/services/replyService.js +0 -39
  39. package/src/services/sudoService.js +0 -85
  40. package/tsconfig.json +0 -25
@@ -1,216 +0,0 @@
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
- // Clean up all listeners on the socket to prevent leaks
44
- socket.removeAllListeners();
45
- this.manager.removeRemote(this);
46
- };
47
- } else { // TCP else
48
- this.congRx = new CongRx();
49
- socket.on('data', data => { this.congRx.write(data) })
50
-
51
- this.congRx.on('wrong', this.onWrongCongPacketMessage.bind(this))
52
- // readable.pipe( destination )
53
- // tcpSocket -> congRx(parser) -> onMessage
54
- // socket.pipe( this.congRx )
55
-
56
- if (serverOption.debug.slow) {
57
- this.congRx.on('data', this.onTimeDelayMessage.bind(this))
58
- } else {
59
- this.congRx.on('data', this.onSocketMessage.bind(this))
60
- }
61
-
62
- socket.on('error', e => { console.log('TCP Socket error', e) })
63
- socket.on('close', e => {
64
- // Clean up listeners on both the socket and the parser
65
- socket.removeAllListeners();
66
- this.congRx.removeAllListeners();
67
- this.manager.removeRemote(this);
68
- })
69
-
70
- }
71
- }
72
-
73
- onWrongCongPacketMessage(message) {
74
- /*
75
- * three category CongSocket connection.
76
- 1. anonymouse, tcp connection.
77
- > no cid_req, no auth_req
78
- > It's attacker. close it.
79
- 2. not authorized but cid ready connnection.
80
- > anonymouse remote client.
81
- > allowed some wrong(broken) message. small quota.
82
- 3. authorized remote connection.
83
- > allowed some wrong(broken) message. large quota.
84
- */
85
- if (this.cid) { // category 2 and 3.
86
- // temporary broken message.
87
- // console.log('wrong congpack from user:', Date(), this.ip, this.ssid, this.cid)
88
- } else {
89
- // attacker
90
- let msg = ""
91
- try {
92
- msg = decoder.decode(message)
93
- } catch (error) {
94
- msg += 'Len: ' + message.byteLength
95
- }
96
-
97
- // console.log('attacker message:', attackMsg)
98
- if (this.manager.attackLogger) {
99
- let attackMsg = `> ${this.ssid} ${this.ip} ${msg}`
100
- this.manager.attackLogger.log(attackMsg)
101
- }
102
-
103
- this.close(true);
104
- }
105
- }
106
-
107
-
108
- permissionChecker() {
109
- // type1.
110
- // accept some traffic before authorize.
111
- const limitBytes = 100
112
-
113
- let txBytes = this.socket.bytesWritten || this.socket._socket?.bytesWritten
114
- let rxBytes = this.socket.bytesRead || this.socket._socket?.bytesRead
115
-
116
- if (txBytes == undefined) txBytes = 0
117
- if (rxBytes == undefined) rxBytes = 0
118
-
119
- }
120
-
121
-
122
- // any type of received messages: message, ping, pong
123
- receiveMonitor() {
124
- this.socket.rxCounter++
125
- this.socket.isAlive = true;
126
- }
127
-
128
- isConnectionHTTPS(req) {
129
- //In case of reverse proxy environment. i.e. using nginx proxy_pass.
130
- return req.headers["x-forwarded-proto"]; //https or undefined
131
- }
132
-
133
- getRemoteIP(req) { //req or ip string
134
- let ip;
135
- if (req && req.headers) {
136
- ip = req.headers["x-forwarded-for"];
137
- if (ip == undefined) ip = req.socket.remoteAddress;
138
- } else {
139
- ip = req;
140
- }
141
-
142
- if (ip) {
143
- if (ip.indexOf("::ffff:") == 0) ip = ip.substring(7);
144
- if (ip == "::1") ip = "127.0.0.1";
145
- } else {
146
- ip = "0.0.0.0";
147
- }
148
- return ip;
149
- }
150
-
151
- ping() {
152
- if (this.socketType === 'websocket') {
153
- this.socket.ping()
154
- this.socket.txCounter++
155
- } else {
156
- this.send(Buffer.from([IOMsg.PING]))
157
- }
158
- }
159
-
160
- pong() {
161
- if (this.socketType === 'websocket') {
162
- this.socket.pong()
163
- this.socket.txCounter++
164
- } else {
165
- this.send(Buffer.from([IOMsg.PONG]))
166
- }
167
- }
168
-
169
- getTraffic() {
170
- let txb = this.socket.bytesWritten || this.socket._socket?.bytesWritten
171
- let rxb = this.socket.bytesRead || this.socket._socket?.bytesRead
172
- let tx = this.socket.txCounter
173
- let rx = this.socket.rxCounter
174
- let traffic = { tx, txb, rx, rxb }
175
- }
176
-
177
- close(terminateNow = false) {
178
- this.getTraffic()
179
- if (this.socketType === 'websocket') {
180
- if (terminateNow) this.socket.terminate();
181
- else this.socket.close();
182
- } else {
183
- if (terminateNow) this.socket.destroy();
184
- else this.socket.end();
185
- }
186
- }
187
-
188
- // node.js server side remote.
189
- send(message, isBinary) {
190
- // console.log(`<-S [${IOMsg[ message[0]]}]`)
191
- this.manager.txBytes += message.byteLength;
192
- this.socket.txCounter++;
193
- if (this.socketType === 'websocket') {
194
- if (this.socket.readyState === 1) {
195
- if (isBinary != undefined) {
196
- this.socket.send(message, { binary: isBinary });
197
- } else {
198
- this.socket.send(message);
199
- }
200
- } else {
201
- console.log('server(ws)Remote.send() called. not open state. current readyState:', this.socket.readyState )
202
- this.close(true)
203
- }
204
-
205
- } else { //CongSocket
206
- if (this.socket.readyState == 'open') {
207
- this.socket.write(pack(message))
208
- } else {
209
- console.log('server(cong)Remote.send() called. not open state. current readyState:', this.socket.readyState )
210
- this.close(true)
211
- }
212
- }
213
-
214
- }
215
-
216
- }
@@ -1,427 +0,0 @@
1
- import MBP from 'meta-buffer-pack'
2
- import Boho from 'boho'
3
- import { serverOption } from './serverOption.js';
4
- import { IOMsg, ENC_MODE, STATE } from '../common/constants.js'
5
- import { quotaTable } from '../common/quotaTable.js'
6
-
7
- const decoder = new TextDecoder()
8
-
9
- const NB = MBP.NB
10
- const MB = MBP.MB
11
-
12
-
13
- export class RemoteCore {
14
- constructor(socket, manager) {
15
-
16
- this.socket = socket;
17
- socket.isAlive = true;
18
- socket.txCounter = 0;
19
- socket.rxCounter = 0;
20
- socket.openTime = Date.now()
21
-
22
- this.boho = new Boho()
23
- this.encMode = ENC_MODE.AUTO;
24
- this.channels = new Set(); // subscribed tags
25
- this.retain_signal = new Map();
26
-
27
- this.ssid = RemoteCore.ssid++; // ordered index number
28
- this.manager = manager;
29
-
30
- this.cid = ''; // Client Id
31
- this.did = ''; // Device Id
32
- this.uid = ''; // User Id
33
- this.nick = ''
34
-
35
- this.privateNode = false
36
- this.HOME_CHANNEL = ''
37
- this.level = serverOption.defaultQuotaIndex;
38
- this.quota = quotaTable[this.level];
39
- this.isAdmin = false;
40
-
41
- this.state;
42
- this.stateLog = [];
43
- this.setState(STATE.OPEN)
44
-
45
- }
46
-
47
- static ssid = 1;
48
-
49
- setState(state) {
50
- this.state = state
51
- if (serverOption.debug.showAuthInfo) {
52
- this.stateLog.push( state + ":" + STATE[ state] )
53
- if( state == STATE.AUTH_RES){
54
- console.log('[auth success]', this.cid, this.stateLog.join('>'))
55
- }else if( state == STATE.AUTH_FAIL){
56
- console.log('[auth_fail]',this.stateLog.join('>'))
57
- }
58
- }
59
- }
60
-
61
- getState() { // <Number>
62
- return this.state
63
- }
64
-
65
- getStateName() { // <String>
66
- return (STATE[this.state]).toLowerCase()
67
- }
68
-
69
-
70
- showMessageLog(message, isBinary) {
71
- let h = '->S '
72
- let from = this.boho.isAuthorized ? ` did: ${this.did} cid: ${this.cid}` : ` cid: ${this.cid}`
73
- let prnLimit = 20
74
- if (isBinary) {
75
- let msgTypeName = IOMsg[message[0]]
76
- if (!msgTypeName) msgTypeName = Boho.BohoMsg[message[0]]
77
-
78
- msgTypeName = '[' + msgTypeName + ']';
79
- let size = message.byteLength;
80
- if (size < prnLimit) {
81
- console.log( h + msgTypeName + from +` [${size}]`, message);
82
- } else {
83
- let elseLen = size - prnLimit
84
- let some = message.subarray(0,prnLimit)
85
- console.log( h + msgTypeName + from +` [${size}]` , some , ' ... else: ', elseLen);
86
- }
87
-
88
- } else {
89
- console.log( h + ' [TEXT] %s' + from, message);
90
- }
91
- }
92
-
93
- rxQuotaChecker(message) {
94
- let rxBytes = message.byteLength
95
- this.manager.rxBytes += rxBytes;
96
-
97
- if (serverOption.useQuota.signalSize && (rxBytes > this.quota.signalSize)) {
98
- console.log('## quota: size over')
99
- this.send(Buffer.from([IOMsg.OVER_SIZE]))
100
- if (serverOption.useQuota.disconnect) this.close();
101
- return false;
102
- }
103
-
104
- // todo:
105
- // traffic rate :
106
- return true;
107
- }
108
-
109
- onTimeDelayMessage(message, isBinary = true) {
110
- setTimeout(() => {
111
- this.onSocketMessage(message, isBinary)
112
- }
113
- , serverOption.debug.delay)
114
- }
115
-
116
- // CongSocket or WebSocket
117
- onSocketMessage(message, isBinary = true) {
118
-
119
- this.receiveMonitor() // rx-data, ping/pong and timeout check
120
- if (!this.rxQuotaChecker(message)) return
121
-
122
- if (serverOption.showMessage === 'message') this.showMessageLog(message, isBinary)
123
-
124
- let msgType, decoded
125
-
126
- if (isBinary) {
127
- msgType = message[0]
128
-
129
- if (msgType === Boho.BohoMsg.ENC_488) {
130
-
131
- try {
132
- decoded = this.boho.decrypt_488(message)
133
- } catch (err) {
134
- console.log('-- E488 DEC_FAIL', err)
135
- return
136
- }
137
-
138
- if (decoded) {
139
- msgType = decoded[0]
140
- message = decoded
141
- } else {
142
- return
143
- }
144
-
145
- } else if (msgType === Boho.BohoMsg.ENC_E2E) { // symetric E2EE Signal
146
- try {
147
- decoded = this.boho.decrypt_488(message)
148
- } catch (err) {
149
- // console.log('-- E2E DEC_FAIL', err)
150
- return
151
- }
152
- // console.log('e2e unpack:', decoded )
153
- if (decoded) {
154
- msgType = decoded[0]
155
- message.set(decoded, Boho.MetaSize.ENC_488) // set decoded signal_e2e headaer.
156
- message = message.subarray(Boho.MetaSize.ENC_488) // reset offset.
157
- } else {
158
- return
159
- }
160
-
161
- } else {
162
- // console.log( `[P] ${ IOMsg[ msgType] || Boho.BohoMsg[cmd]} LEN: ${message.byteLength}` )
163
- }
164
-
165
- switch (msgType) {
166
- case IOMsg.PING:
167
- this.pong();
168
- break;
169
-
170
- case IOMsg.PONG:
171
- break;
172
-
173
- case IOMsg.CID_REQ:
174
- if (this.state < STATE.SERVER_READY) {
175
- // Protocol violation: CID_REQ was sent before receiving the SERVER_READY signal.
176
- console.log('CID_REQ before SERVER_READY')
177
- this.close()
178
- }
179
-
180
- if (!this.cid) {
181
- this.cid = '?' + globalThis.crypto.getRandomValues(Buffer.alloc(3)).toString('base64url')
182
- this.manager.cid2remote.set(this.cid, this)
183
- }
184
-
185
- // console.log('<< SENDING CID_RES:', this.cid)
186
- this.send_enc_mode(MBP.pack(
187
- MB('#cid_ack', '8', IOMsg.CID_RES),
188
- MB('#cid', this.cid)
189
- ))
190
-
191
- this.setState(STATE.CID_RES)
192
- break;
193
-
194
- case IOMsg.ECHO:
195
- if( message.byteLength < 30) {
196
- this.send(message, isBinary)
197
- }
198
- break;
199
-
200
- case IOMsg.IAM:
201
- if (message.byteLength > 1 && message.byteLength < 30) {
202
- // if iam request has a text message then reset the nick property.
203
- this.nick = decoder.decode(message.subarray(1))
204
- }
205
- this.iamResponse()
206
- break;
207
-
208
- case IOMsg.SIGNAL_E2E:
209
- case IOMsg.SIGNAL:
210
- if (message.byteLength >= 3) {
211
- let tagLen = message.readUInt8(1)
212
- if (message.byteLength >= tagLen + 2) {
213
- let tag = message.subarray(2, 2 + tagLen)
214
- tag = decoder.decode(tag)
215
- this.manager.sender(tag, this, message)
216
- }
217
- }
218
- break;
219
-
220
-
221
-
222
- case IOMsg.UNSUBSCRIBE:
223
- if (message.byteLength == 2) {
224
- this.manager.unsubscribe([''], this)
225
- } else if (message.byteLength >= 3) {
226
- let tagLen = message.readUInt8(1)
227
-
228
- if (message.byteLength == tagLen + 2) {
229
- let tag = message.subarray(2, 2 + tagLen)
230
- tag = decoder.decode(tag)
231
- let tagList = tag.split(',')
232
- this.manager.unsubscribe(tagList, this)
233
- }
234
- }
235
- break;
236
-
237
-
238
- case IOMsg.SUBSCRIBE: // 1byte tagLen
239
- if (message.byteLength >= 3) {
240
- let tagLen = message.readUInt8(1)
241
- if (message.byteLength == tagLen + 2) {
242
- let tag = message.subarray(2, 2 + tagLen)
243
- tag = decoder.decode(tag)
244
- let tagList = tag.split(',')
245
- this.manager.subscribe(tagList, this)
246
- }
247
- }
248
- break;
249
-
250
- case IOMsg.SUBSCRIBE_REQ: // 2bytes tagLen
251
- if (message.byteLength >= 6) {
252
- let msgID = message.readUInt16BE(1)
253
- let tagLen = message.readUInt16BE(3)
254
- if (message.byteLength == tagLen + 5) {
255
- let tag = message.subarray(5, 5 + tagLen)
256
- tag = decoder.decode(tag)
257
- let tagList = tag.split(',')
258
- // console.log('>> SUBSCRIBE_REQ from:', this.cid, tagList)
259
- this.manager.subscribe(tagList, this)
260
- this.response(msgID, 0)
261
- } else {
262
- this.response(msgID, 255)
263
- }
264
- }
265
- break;
266
-
267
-
268
- case IOMsg.CALL: //service request
269
- try {
270
- let req = MBP.unpack(message)
271
- if (req) {
272
- if (!req.target || !req.topic) return
273
- if (this.manager.server.serviceNames.has(req.target)) {
274
- this.manager.server.emit(req.target, this, req)
275
- } else {
276
- // console.log('UnKnown Service call: ', req.target)
277
- }
278
- }
279
- } catch (e) {
280
- console.log('request catch error', e)
281
- }
282
- break;
283
-
284
-
285
- case IOMsg.CLOSE:
286
- if (message.byteLength > 1) {
287
- let reason = decoder.decode(message.subarray(1))
288
- console.log('>> CLOSE reason:', reason)
289
- this.close();
290
- }
291
- break;
292
-
293
-
294
- // client's auth requst
295
- case Boho.BohoMsg.AUTH_REQ:
296
- if (!this.manager.authManager) return
297
- if (this.state < STATE.SERVER_READY) {
298
- console.log('protocol error. must called auth_req after server_ready')
299
- this.close();
300
- }
301
- this.setState(STATE.AUTH_REQ)
302
- //async
303
- this.manager.authManager.verify_auth_req(message, this)
304
- .then(authInfo => {
305
- if (authInfo?.name) {
306
- this.manager.deligateSignal(this, '@$name', authInfo.name)
307
- // console.log('device login success authInfo', authInfo)
308
- // this.manager.sender('@$name', authInfo.name )
309
- }
310
-
311
- // console.log('[VERIFY AUTH_REQ] SubClass of BohoAuth return authInfo:', authInfo )
312
- })
313
- .catch(e => {
314
- console.log('authInfo return fail.', e )
315
- })
316
-
317
- return;
318
-
319
- default:
320
- // console.log('unknown MsgType:',msgType,' from:', this.ip, this.ssid, this.cid )
321
-
322
- }
323
-
324
- } else {
325
- // TEXT FRAME
326
- try {
327
- let textMessage = decoder.decode(message)
328
- // console.log('text buffer:',textMessage )
329
- // console.log('text buffer:',message.toString('hex') )
330
- this.manager.server.emit('text_message', textMessage, this)
331
- } catch (error) {
332
-
333
- }
334
- }
335
-
336
- }
337
-
338
-
339
- response(msgId, statusCode, body) {
340
- // console.log('response body', body)
341
- if (body) {
342
- this.send_enc_mode(MBP.pack(
343
- MB('#type', '8', IOMsg.RESPONSE_MBP),
344
- MB('status', '8', statusCode),
345
- MB('mid', '16', msgId),
346
- MB('body', body)
347
- ))
348
- } else {
349
- this.send_enc_mode(MBP.pack(
350
- MB('#type', '8', IOMsg.RESPONSE_MBP),
351
- MB('status', '8', statusCode),
352
- MB('mid', '16', msgId)
353
- ))
354
- }
355
- }
356
-
357
-
358
- send_enc_mode(data, useEncryption = false) {
359
-
360
- if (this.encMode === ENC_MODE.YES ||
361
- this.encMode === ENC_MODE.AUTO &&
362
- !this.TLS && this.boho.isAuthorized
363
- ) useEncryption = true;
364
-
365
- // console.log('svr useEnc', useEncryption, data )
366
-
367
- if (useEncryption && data[0] == IOMsg.SIGNAL_E2E) {
368
- // When the server delivers end-to-end encrypted messages,
369
- // it should only encrypt the header part and pass the data part as is.
370
- let tagLen = data[1];
371
-
372
- // let tagInfo = decoder.decode( data.subarray( 2, 2 + tagLen) )
373
- // console.log('server bypass E2E signal taginfo:', tagInfo )
374
-
375
- let encHeader = this.boho.encrypt_488(data.subarray(0, 3 + tagLen))
376
- encHeader[0] = Boho.BohoMsg.ENC_E2E
377
- let newEncBuffer = Buffer.concat([encHeader, data.subarray(3 + tagLen)])
378
- this.send(newEncBuffer)
379
-
380
- } else if (useEncryption) {
381
- let encPack = this.boho.encrypt_488(data)
382
- if (encPack) {
383
- this.send(encPack)
384
- } else {
385
- // console.log('encryption FAIL: NO DATA TRANSIT')
386
- }
387
- } else {
388
- // console.log('send -N-')
389
- this.send(data)
390
-
391
- }
392
-
393
- }
394
-
395
-
396
-
397
- iamResponse(info = '') {
398
-
399
- if (info == '') {
400
- let channels = []
401
- for (let tag of this.channels.keys()) {
402
- channels.push(tag)
403
- }
404
-
405
- info = {
406
- "ssid": this.ssid,
407
- "cid": this.cid,
408
- "did": this.did,
409
- "uid": this.uid,
410
- "level": this.level,
411
- "nick": this.nick,
412
- "ip": this.ip
413
- , 'tag': channels
414
- }
415
- }
416
-
417
- // console.log( 'iam res info', info )
418
- let pack = MBP.pack(
419
- MB('#MsgType', '8', IOMsg.IAM_RES),
420
- MB('#info', info)
421
- )
422
- this.send_enc_mode(pack)
423
- }
424
-
425
-
426
- }
427
-