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.
- package/README.md +163 -0
- package/dist/browser/esm/io.d.ts +8 -9
- package/dist/browser/esm/io.js +5 -5
- package/dist/browser/esm/io.js.map +1 -1
- package/dist/browser/iife/io.js +5 -5
- package/dist/browser/iife/io.js.map +1 -1
- package/dist/node/iosignal.cjs +124 -119
- package/dist/node/iosignal.js +123 -118
- package/dist/types/client/IOCore.d.ts +2 -2
- package/dist/types/client/browser/IOWebSocket.d.ts +4 -4
- package/dist/types/common/payload.d.ts +2 -2
- package/package.json +21 -3
- package/.vscode/settings.json +0 -3
- package/index.js +0 -26
- package/rollup.config.js +0 -59
- package/src/auth/BohoAuth.js +0 -196
- package/src/auth/key_providers/FileKeyProvider.js +0 -59
- package/src/auth/key_providers/RedisKeyProvider.js +0 -46
- package/src/auth/key_providers/StringKeyProvider.js +0 -54
- package/src/client/CongPacket.js +0 -143
- package/src/client/IOCongSocket.js +0 -92
- package/src/client/IOCore.js +0 -1202
- package/src/client/IOWS.js +0 -78
- package/src/client/browser/IOWebSocket.js +0 -202
- package/src/common/constants.js +0 -210
- package/src/common/payload.js +0 -82
- package/src/common/quotaTable.js +0 -68
- package/src/common/util.js +0 -64
- package/src/server/FileLogger.js +0 -30
- package/src/server/Manager.js +0 -450
- package/src/server/Metrics.js +0 -155
- package/src/server/Remote.js +0 -216
- package/src/server/RemoteCore.js +0 -427
- package/src/server/Server.js +0 -236
- package/src/server/serverOption.js +0 -56
- package/src/services/RedisService.js +0 -53
- package/src/services/constant.js +0 -7
- package/src/services/replyService.js +0 -39
- package/src/services/sudoService.js +0 -85
- package/tsconfig.json +0 -25
package/src/server/Remote.js
DELETED
|
@@ -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
|
-
}
|
package/src/server/RemoteCore.js
DELETED
|
@@ -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
|
-
|