iosignal 0.0.1 → 1.1.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 (48) hide show
  1. package/README.md +171 -0
  2. package/auth_file.json +13 -0
  3. package/auth_file.mjs +23 -0
  4. package/dist/iosignal.cjs +2 -0
  5. package/dist/iosignal.esm.js +3 -0
  6. package/dist/iosignal.esm.js.map +1 -0
  7. package/dist/iosignal.min.js +3 -0
  8. package/dist/iosignal.min.js.map +1 -0
  9. package/img/iosignal_stack.png +0 -0
  10. package/index.js +19 -0
  11. package/indexWebBrowser.js +13 -0
  12. package/package.json +48 -6
  13. package/rollup.config.js +69 -0
  14. package/src/api/RedisAPI.js +68 -0
  15. package/src/api/api_constant.js +7 -0
  16. package/src/api/api_reply.js +34 -0
  17. package/src/api/api_sudo.js +96 -0
  18. package/src/auth/Auth_Env.js +78 -0
  19. package/src/auth/Auth_File.js +75 -0
  20. package/src/auth/Auth_Redis.js +50 -0
  21. package/src/auth/BohoAuth.js +168 -0
  22. package/src/client/CongPacket.js +150 -0
  23. package/src/client/IOCongSocket.js +88 -0
  24. package/src/client/IOCore.js +959 -0
  25. package/src/client/IOWS.js +81 -0
  26. package/src/client/IOWebSocket.js +96 -0
  27. package/src/common/constants.js +159 -0
  28. package/src/common/quotaTable.js +61 -0
  29. package/src/common/util.js +61 -0
  30. package/src/server/FileLogger.js +30 -0
  31. package/src/server/Manager.js +521 -0
  32. package/src/server/Metric.js +154 -0
  33. package/src/server/Server.js +136 -0
  34. package/src/server/ServerRemote.js +235 -0
  35. package/src/server/ServerRemoteCore.js +467 -0
  36. package/src/server/serverOption.js +55 -0
  37. package/test-browser/favicon.ico +0 -0
  38. package/test-browser/index.html +93 -0
  39. package/test-browser/server.js +8 -0
  40. package/test-client-nodejs/client.cjs +17 -0
  41. package/test-client-nodejs/client.mjs +17 -0
  42. package/test-client-nodejs/stateChange.mjs +19 -0
  43. package/test-server/server-commonjs.cjs +8 -0
  44. package/test-server/server-esm.mjs +19 -0
  45. package/test-server/test-Auth_Env.js +17 -0
  46. package/test-server/test-Auth_File.js +9 -0
  47. package/test-server/test-api_reply.js +10 -0
  48. package/test-server/test-api_sudo.js +11 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * BohoAuth
3
+ */
4
+
5
+ import { MBP } from 'meta-buffer-pack'
6
+ import { BohoMsg , Meta } from 'boho'
7
+ import { quotaTable } from '../common/quotaTable.js'
8
+ import { serverOption } from '../server/serverOption.js'
9
+ import { IOMsg ,CLIENT_STATE } from '../common/constants.js'
10
+ import { FileLogger } from '../server/FileLogger.js'
11
+
12
+ const decoder = new TextDecoder()
13
+ export class BohoAuth{
14
+ constructor(){
15
+ this.authLogger;
16
+ if( serverOption.fileLogger.auth.use ){
17
+ this.authLogger = new FileLogger( serverOption.fileLogger.auth.path)
18
+ console.log('Auth: begin file logger.[auth]')
19
+ }
20
+ }
21
+
22
+ send_auth_fail( peer , reason){
23
+ if( this.authLogger ){
24
+ let peerInfo = `FAIL #${peer.ssid} reason:${reason} `
25
+ this.authLogger.log( peerInfo )
26
+ }
27
+
28
+ console.log('## AUTH_FAIL reason:', reason )
29
+ // peer.send( Buffer.from( [BohoMsg.AUTH_FAIL] ))
30
+ peer.setState( CLIENT_STATE.AUTH_FAIL )
31
+ setTimeout(e=>{
32
+ peer.send( Buffer.from( [BohoMsg.AUTH_FAIL] ))
33
+ }, serverOption.auth.delay_auth_fail )
34
+ }
35
+
36
+ async verify_auth_hmac( auth_hmac , peer ){
37
+ try {
38
+ //1. unpack
39
+ let infoPack = MBP.unpack( auth_hmac , Meta.AUTH_HMAC )
40
+ if(!infoPack){
41
+ this.send_auth_fail( peer ,'unpack auth_pack fail');
42
+ return
43
+ }
44
+
45
+ let id = ""
46
+ if( infoPack.id8.includes(0)){
47
+ id = decoder.decode( infoPack.id8.subarray( 0, infoPack.id8.indexOf(0) ) );
48
+ }else{
49
+ id = decoder.decode( infoPack.id8 );
50
+ }
51
+
52
+ //2. get key of id from DB
53
+ let authInfo = await this.getAuth( id )
54
+
55
+ // console.log('##### authInfo',authInfo )
56
+ if(serverOption.debug.showAuthInfo ){
57
+ }
58
+
59
+ if( !authInfo ){
60
+ this.send_auth_fail( peer , 'NO ID:'+ id);
61
+ return
62
+ }
63
+
64
+ // console.log('db authInfo.key: ', authInfo.key)
65
+ peer.boho.copy_id8( infoPack.id8 )
66
+ // type of key
67
+ let authKey ;
68
+ const SHA256_HASH_BASE64_LEN = 44
69
+ if( authInfo.key.length == SHA256_HASH_BASE64_LEN){
70
+ // hashed key
71
+ authKey = Buffer.from( authInfo.key, 'base64')
72
+ peer.boho.copy_key( authKey )
73
+ }else{
74
+ // plain key
75
+ peer.boho.set_key( authInfo.key )
76
+ }
77
+
78
+ //3. check hmac
79
+ // console.log('-- found: authKey of id: ', id, authKey.toString('hex'))
80
+ let auth_ack = peer.boho.check_auth_hmac( infoPack )
81
+
82
+ // console.log('auth_ack', auth_ack )
83
+ if( !auth_ack ){
84
+ this.send_auth_fail( peer, 'hmac dismatched' );
85
+ return
86
+ }
87
+
88
+ //4. get info
89
+ // console.log('#### auth success' )
90
+
91
+ //5. check duplicate login.
92
+ // current policy. Deny duplicate login
93
+
94
+ // duplicate login policy.
95
+ // 1. send clear_auth signal to the old connection.
96
+ // 2. close old connection.
97
+ // 3. close new connection. retry from begining.
98
+ if( peer.manager.cid2remote.has( authInfo.cid ) ){
99
+ let old = peer.manager.cid2remote.get(authInfo.cid )
100
+ if( old == peer ){
101
+ console.log('## trying RELOGIN with SAME ID. ignored.')
102
+ return
103
+ }
104
+ console.log('### clear_auth and close old connection:', old.cid )
105
+ old.send( Buffer.from( [ IOMsg.SERVER_CLEAR_AUTH ]))
106
+ old.close()
107
+ peer.close() // auto relogin
108
+ return
109
+ }
110
+
111
+
112
+ //6. delete current (temp rand)cid if exist.
113
+ if( peer.cid ){
114
+ peer.manager.cid2remote.delete( peer.cid )
115
+ }
116
+
117
+ //7. setting info.
118
+ peer.did = id
119
+ peer.cid = authInfo.cid
120
+ peer.nick = authInfo.cid // temporary: nick as cid
121
+
122
+ //8. setting quota level.
123
+ let quotaLevel = serverOption.defaultQuotaIndex;
124
+ if(authInfo.level) quotaLevel = authInfo.level;
125
+ quotaLevel = parseInt( quotaLevel )
126
+ let newQuota = quotaTable[ quotaLevel ];
127
+ if(!newQuota){
128
+ let err = 'no index quotaTable for auth.level: ' + quotaLevel
129
+ console.log('##AUTH:DATA ERROR##', err)
130
+ this.send_auth_fail( peer, err );
131
+ return
132
+ }else{
133
+ peer.level = quotaLevel;
134
+ peer.quota = newQuota;
135
+ }
136
+
137
+ if( peer.level === serverOption.adminLevel){
138
+ console.log('## LOGIN ADMIN CID:', peer.cid )
139
+ peer.isAdmin = true;
140
+ }
141
+ // console.log('auth: peer.quota', peer.quota )
142
+ // send quota.level
143
+ peer.send( Buffer.from( [IOMsg.QUOTA_LEVEL, quotaLevel] ))
144
+
145
+ //9. set cid2remote
146
+ peer.manager.cid2remote.set( peer.cid , peer )
147
+ // console.log( 'done: cid2remote:', peer.manager.cid2remote.keys() )
148
+ // console.log("LOGIN: ", `id: ${ peer.did}(${peer.cid})` )
149
+ //10. send ack.
150
+ peer.send( auth_ack )
151
+ peer.setState( CLIENT_STATE.AUTH_READY )
152
+ if( this.authLogger ){
153
+ let peerInfo = `OK #${peer.ssid} cid: ${peer.cid} did:${ peer.did }`
154
+ if( peer.isAdmin ) peerInfo = "#ADMIN# "+ peerInfo
155
+ this.authLogger.log( peerInfo )
156
+ }
157
+ return authInfo
158
+ } catch (error) {
159
+ this.send_auth_fail( peer , 'caught: unknown error' + error );
160
+ }
161
+
162
+ }
163
+
164
+
165
+
166
+
167
+ }
168
+
@@ -0,0 +1,150 @@
1
+ import { Transform } from 'stream'
2
+ import { MBP } from 'meta-buffer-pack'
3
+
4
+ export const CongType = {
5
+ TYPE_LEN1: 1,
6
+ TYPE_LEN2: 2,
7
+ TYPE_LEN3: 3,
8
+ TYPE_LEN4: 4
9
+ }
10
+
11
+ // support LittleEndian system.
12
+ export function pack(payload) {
13
+
14
+ if (payload.byteLength < 256) { //one byte len
15
+ return MBP.pack(
16
+ MBP.MB('#type', '8', CongType.TYPE_LEN1),
17
+ MBP.MB('#payloadLen1', '8', payload.byteLength),
18
+ MBP.MB('#payload', payload)
19
+ )
20
+
21
+ } else if (payload.byteLength < 65536) { // 2bytes len
22
+ return MBP.pack(
23
+ MBP.MB('#type', '8', CongType.TYPE_LEN2),
24
+ MBP.MB('#payloadLen2', '16L', payload.byteLength),
25
+ MBP.MB('#payload', payload)
26
+ )
27
+
28
+ } else if (payload.byteLength < 2 ** 24) { // 3bytes len
29
+ let len4Buffer = Buffer.alloc(4)
30
+ len4Buffer.writeUint32LE( payload.byteLength )
31
+ let cropLen3 = len4Buffer.subarray(0,3)
32
+ return MBP.pack(
33
+ MBP.MB('#type', '8', CongType.TYPE_LEN3),
34
+ MBP.MB('#payloadLen3', cropLen3),
35
+ MBP.MB('#payload', payload)
36
+ )
37
+
38
+ } else { //use 4 bytes.
39
+ return MBP.pack(
40
+ MBP.MB('#type', '8', CongType.TYPE_LEN4),
41
+ MBP.MB('#payloadLen4', '32L', payload.byteLength),
42
+ MBP.MB('#payload', payload)
43
+ )
44
+ }
45
+
46
+ }
47
+
48
+ // let totalCong = 0;
49
+
50
+ export class CongRx extends Transform {
51
+ constructor(options) {
52
+ super(options)
53
+ // console.log('new congRx. totalCong:', ++totalCong )
54
+ this.buffer = Buffer.alloc(0)
55
+ this.frames = []
56
+ this.rxi = 0;
57
+ this.rxi_zero = 0;
58
+ }
59
+
60
+
61
+
62
+ _transform(chunk, encoding, callback) {
63
+ this.addData(chunk)
64
+ if (this.frames.length > 0) {
65
+ this.frames.forEach(frame => {
66
+ // console.log('emit frame:', frame)
67
+ this.push(frame)
68
+ })
69
+ this.frames = []
70
+ }
71
+ callback()
72
+ }
73
+
74
+
75
+ addData(chunk) {
76
+ // console.log('congpack chunk:', chunk )
77
+ let c = chunk.byteLength;
78
+ let i = 0;
79
+ while( c-- ){
80
+ this.rxi++
81
+ if( chunk[ i++ ] == 0 ){
82
+ this.rxi_zero++
83
+ }
84
+ }
85
+
86
+ if (this.buffer.byteLength > 0) {
87
+ this.buffer = Buffer.concat([this.buffer, chunk])
88
+ } else {
89
+ this.buffer = chunk
90
+ }
91
+ // console.log('buffer before parse', this.buffer )
92
+ this.parse()
93
+ }
94
+
95
+
96
+ parse() {
97
+ let head = this.buffer[0]
98
+ let headerLen;
99
+ let payloadSize;
100
+ // find header
101
+ // console.log('>> parser head, buffer:', head , this.buffer )
102
+
103
+ if (head == CongType.TYPE_LEN1) {
104
+ headerLen = 2;
105
+ if (this.buffer.byteLength < headerLen) return;
106
+ payloadSize = this.buffer.readUint8(1)
107
+
108
+ } else if (head == CongType.TYPE_LEN2) {
109
+ headerLen = 3;
110
+ if (this.buffer.byteLength < headerLen) return;
111
+ payloadSize = this.buffer.readUint16LE(1)
112
+
113
+ } else if (head == CongType.TYPE_LEN3) {
114
+ headerLen = 4;
115
+ if (this.buffer.byteLength < headerLen) return;
116
+ payloadSize = this.buffer.readUint16LE(1) + this.buffer.readUint8(3) * 65536
117
+
118
+ } else if (head == CongType.TYPE_LEN4) {
119
+ headerLen = 5;
120
+ if (this.buffer.byteLength < headerLen) return;
121
+ payloadSize = this.buffer.readUint32LE(1)
122
+
123
+ } else {
124
+ // console.log('CongPacket: UNKNOWN_HEAD drop', this.buffer)
125
+ this.emit('wrong',this.buffer)
126
+ this.buffer = Buffer.alloc(0)
127
+ }
128
+
129
+
130
+ if (payloadSize == this.buffer.byteLength - headerLen) {
131
+ this.frames.push(this.buffer.subarray(headerLen))
132
+ this.buffer = Buffer.alloc(0)
133
+ return
134
+ } else if (payloadSize < this.buffer.byteLength - headerLen) {
135
+ this.frames.push(this.buffer.subarray(headerLen, headerLen + payloadSize))
136
+ this.buffer = this.buffer.subarray(headerLen + payloadSize)
137
+ this.parse()
138
+ } else {
139
+ //not ready
140
+ // console.log('+')
141
+ return
142
+ }
143
+
144
+
145
+ }
146
+
147
+
148
+ }
149
+
150
+
@@ -0,0 +1,88 @@
1
+ import { IOCore } from "./IOCore.js"
2
+ import { pack, CongRx } from './CongPacket.js'
3
+ import net from'net'
4
+
5
+
6
+ export class IOCongSocket extends IOCore{
7
+ constructor( url ) {
8
+ super( url );
9
+ if(url) this.open();
10
+ }
11
+
12
+ close() {
13
+ this.socket?.end();
14
+ this.socket = null;
15
+ }
16
+
17
+ stop() {
18
+ this.close()
19
+ clearInterval(this.connectionCheckerIntervalID);
20
+ this.connectionCheckerIntervalID = null
21
+ }
22
+
23
+ keepAlive() {
24
+ let state = this.socket?.readyState;
25
+ if ( !this.socket || !(state === 'open' || state === 'opening') ) {
26
+ this.open();
27
+ }
28
+ }
29
+
30
+
31
+ createConnection(url){
32
+ // TCP Socket
33
+ let urlObj = new URL( url )
34
+ // console.log(urlObj)
35
+ if( urlObj.protocol != "cong:"){
36
+ urlObj = new URL( 'cong://'+ url )
37
+ }
38
+ // console.log('congSocket url:', urlObj )
39
+ this.socket = net.createConnection( urlObj.port, urlObj.hostname )
40
+ this.stateChange('opening')
41
+
42
+ this.socket.on('connect' , ()=>{
43
+ // console.log('cong connected' )
44
+ this.congRx = new CongRx();
45
+ this.socket.pipe( this.congRx )
46
+ this.congRx.on("data", this.onTCPSocketMessage.bind(this));
47
+ this.emit('open')
48
+ })
49
+
50
+ this.socket.on('error', e=>{
51
+ this.emit('error', e)
52
+ })
53
+
54
+ this.socket.on('close', ()=>{
55
+ this.emit('close');
56
+ })
57
+
58
+ }
59
+
60
+ onTCPSocketMessage( data ) {
61
+ this.rxCounter++;
62
+ this.rxBytes += data.byteLength
63
+ this.lastTxRxTime = Date.now();
64
+ // console.log('>> congRx data.len:', data.byteLength )
65
+ // console.log('>> cong.rxi, rxi_zero:', this.congRx.rxi, this.congRx.rxi_zero )
66
+ this.emit('socket_data', data );
67
+
68
+ }
69
+
70
+ socket_send(data) {
71
+ if( this.socket?.readyState === 'open'){
72
+ // console.log('tcp send raw payload:', data)
73
+ let packData = pack(data);
74
+ // console.log('tcp send cong packed:', packData)
75
+ this.socket.write( packData )
76
+ this.txCounter++;
77
+ this.txBytes += packData.byteLength;
78
+ this.lastTxRxTime = Date.now();
79
+ }else{
80
+ console.log('.')
81
+ }
82
+ }
83
+
84
+ }
85
+
86
+
87
+
88
+