peer-term 1.2.10 → 1.2.12
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/package.json +1 -1
- package/src/index.js +107 -31
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -368,25 +368,54 @@ class Session {
|
|
|
368
368
|
|
|
369
369
|
async _completeKeyExchange(clientPublicKeyBase64) {
|
|
370
370
|
if (!this.keyPair || !this.hostPublicKeyBase64) return;
|
|
371
|
+
if (typeof clientPublicKeyBase64 !== 'string' || clientPublicKeyBase64.length === 0) {
|
|
372
|
+
this._rejectProtocolMessage('Invalid key-exchange public key');
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
371
375
|
|
|
372
|
-
|
|
373
|
-
|
|
376
|
+
try {
|
|
377
|
+
this.logDebug('Deriving shared secret...');
|
|
378
|
+
this.clientPublicKeyBase64 = clientPublicKeyBase64;
|
|
379
|
+
|
|
380
|
+
const peerPublicKey = await importPublicKey(clientPublicKeyBase64);
|
|
381
|
+
this.sharedKey = await deriveSharedKey(this.keyPair.privateKey, peerPublicKey);
|
|
382
|
+
|
|
383
|
+
if (this.secureMode) {
|
|
384
|
+
this.securityFingerprint = await fingerprintPublicKeys(
|
|
385
|
+
this.code,
|
|
386
|
+
this.hostPublicKeyBase64,
|
|
387
|
+
this.clientPublicKeyBase64
|
|
388
|
+
);
|
|
389
|
+
this.fingerprintAuthorized = false;
|
|
390
|
+
this._printFingerprintAuthorization();
|
|
391
|
+
} else {
|
|
392
|
+
this.fingerprintAuthorized = true;
|
|
393
|
+
await this._activateSecureSession();
|
|
394
|
+
}
|
|
395
|
+
} catch (err) {
|
|
396
|
+
this._rejectProtocolMessage(`Invalid key exchange: ${err.message}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
374
399
|
|
|
375
|
-
|
|
376
|
-
this.
|
|
400
|
+
_rejectProtocolMessage(reason) {
|
|
401
|
+
this.log(`Protocol error: ${reason}`);
|
|
402
|
+
this.isClientConnected = false;
|
|
403
|
+
this.awaitingRejoin = false;
|
|
404
|
+
this.sharedKey = null;
|
|
405
|
+
this.keyPair = null;
|
|
406
|
+
this.hostPublicKeyBase64 = null;
|
|
407
|
+
this.clientPublicKeyBase64 = null;
|
|
408
|
+
this.securityFingerprint = null;
|
|
409
|
+
this.fingerprintAuthorized = false;
|
|
410
|
+
this.stopHeartbeat();
|
|
411
|
+
this._cleanupWebRTC();
|
|
412
|
+
this.destroy();
|
|
413
|
+
}
|
|
377
414
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
this.clientPublicKeyBase64
|
|
383
|
-
);
|
|
384
|
-
this.fingerprintAuthorized = false;
|
|
385
|
-
this._printFingerprintAuthorization();
|
|
386
|
-
} else {
|
|
387
|
-
this.fingerprintAuthorized = true;
|
|
388
|
-
await this._activateSecureSession();
|
|
389
|
-
}
|
|
415
|
+
_handleMessageError(err) {
|
|
416
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
417
|
+
this.log(`Protocol handler error: ${message}`);
|
|
418
|
+
this.destroy();
|
|
390
419
|
}
|
|
391
420
|
|
|
392
421
|
_printFingerprintAuthorization() {
|
|
@@ -481,6 +510,7 @@ class Session {
|
|
|
481
510
|
});
|
|
482
511
|
|
|
483
512
|
this.ws.on('message', async (raw) => {
|
|
513
|
+
try {
|
|
484
514
|
let msg;
|
|
485
515
|
try {
|
|
486
516
|
msg = JSON.parse(raw.toString());
|
|
@@ -488,6 +518,10 @@ class Session {
|
|
|
488
518
|
this.log('Invalid message from relay');
|
|
489
519
|
return;
|
|
490
520
|
}
|
|
521
|
+
if (!msg || typeof msg.type !== 'string') {
|
|
522
|
+
this._rejectProtocolMessage('Malformed relay message');
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
491
525
|
|
|
492
526
|
switch (msg.type) {
|
|
493
527
|
case 'code': {
|
|
@@ -614,6 +648,9 @@ class Session {
|
|
|
614
648
|
break;
|
|
615
649
|
}
|
|
616
650
|
}
|
|
651
|
+
} catch (err) {
|
|
652
|
+
this._handleMessageError(err);
|
|
653
|
+
}
|
|
617
654
|
});
|
|
618
655
|
|
|
619
656
|
this.ws.on('close', () => {
|
|
@@ -648,13 +685,10 @@ class Session {
|
|
|
648
685
|
try {
|
|
649
686
|
const resizeJson = JSON.stringify({ type: 'resize', cols, rows });
|
|
650
687
|
const payload = await encrypt(this.sharedKey, resizeJson);
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
} else if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
656
|
-
this.ws.send(JSON.stringify({ type: 'data', payload, meta: 'resize' }));
|
|
657
|
-
}
|
|
688
|
+
this._sendEncryptedToClient(payload, {
|
|
689
|
+
directPayload: JSON.stringify({ _meta: 'resize', payload }),
|
|
690
|
+
meta: 'resize',
|
|
691
|
+
});
|
|
658
692
|
} catch {}
|
|
659
693
|
}
|
|
660
694
|
|
|
@@ -746,14 +780,41 @@ class Session {
|
|
|
746
780
|
const payload = await encrypt(sharedKey, data);
|
|
747
781
|
if (this.destroyed || sharedKey !== this.sharedKey) return;
|
|
748
782
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
783
|
+
this._sendEncryptedToClient(payload, { enforceBackpressure: true });
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
_sendEncryptedToClient(payload, options = {}) {
|
|
787
|
+
const directPayload = options.directPayload || payload;
|
|
788
|
+
|
|
789
|
+
if (this.useDataChannel) {
|
|
790
|
+
if (this.webrtc && this.webrtc.isActive()) {
|
|
791
|
+
let sentDirect = false;
|
|
792
|
+
try {
|
|
793
|
+
sentDirect = this.webrtc.send(directPayload);
|
|
794
|
+
} catch {}
|
|
795
|
+
|
|
796
|
+
if (sentDirect) return true;
|
|
756
797
|
}
|
|
798
|
+
|
|
799
|
+
this.useDataChannel = false;
|
|
800
|
+
this.log('DataChannel send failed — falling back to relay');
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
return this._sendViaRelay(payload, options);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
_sendViaRelay(payload, { meta, enforceBackpressure = false } = {}) {
|
|
807
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return false;
|
|
808
|
+
if (enforceBackpressure && this.ws.bufferedAmount >= 1.25 * 1024 * 1024) return false;
|
|
809
|
+
|
|
810
|
+
const msg = { type: 'data', payload };
|
|
811
|
+
if (meta) msg.meta = meta;
|
|
812
|
+
|
|
813
|
+
try {
|
|
814
|
+
this.ws.send(JSON.stringify(msg));
|
|
815
|
+
return true;
|
|
816
|
+
} catch {
|
|
817
|
+
return false;
|
|
757
818
|
}
|
|
758
819
|
}
|
|
759
820
|
|
|
@@ -886,12 +947,17 @@ class Session {
|
|
|
886
947
|
});
|
|
887
948
|
|
|
888
949
|
newWs.on('message', async (raw) => {
|
|
950
|
+
try {
|
|
889
951
|
let msg;
|
|
890
952
|
try {
|
|
891
953
|
msg = JSON.parse(raw.toString());
|
|
892
954
|
} catch {
|
|
893
955
|
return;
|
|
894
956
|
}
|
|
957
|
+
if (!msg || typeof msg.type !== 'string') {
|
|
958
|
+
this._rejectProtocolMessage('Malformed relay message');
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
895
961
|
|
|
896
962
|
if (msg.type === 'rejoined') {
|
|
897
963
|
// Success — replace the old ws with this new one
|
|
@@ -911,6 +977,9 @@ class Session {
|
|
|
911
977
|
this.destroy();
|
|
912
978
|
try { newWs.close(); } catch {}
|
|
913
979
|
}
|
|
980
|
+
} catch (err) {
|
|
981
|
+
this._handleMessageError(err);
|
|
982
|
+
}
|
|
914
983
|
});
|
|
915
984
|
|
|
916
985
|
newWs.on('error', () => {
|
|
@@ -946,6 +1015,7 @@ class Session {
|
|
|
946
1015
|
*/
|
|
947
1016
|
_attachWsHandlers(newWs) {
|
|
948
1017
|
newWs.on('message', async (raw) => {
|
|
1018
|
+
try {
|
|
949
1019
|
let msg;
|
|
950
1020
|
try {
|
|
951
1021
|
msg = JSON.parse(raw.toString());
|
|
@@ -953,6 +1023,10 @@ class Session {
|
|
|
953
1023
|
this.log('Invalid message from relay');
|
|
954
1024
|
return;
|
|
955
1025
|
}
|
|
1026
|
+
if (!msg || typeof msg.type !== 'string') {
|
|
1027
|
+
this._rejectProtocolMessage('Malformed relay message');
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
956
1030
|
|
|
957
1031
|
switch (msg.type) {
|
|
958
1032
|
case 'client-connected': {
|
|
@@ -1060,6 +1134,9 @@ class Session {
|
|
|
1060
1134
|
break;
|
|
1061
1135
|
}
|
|
1062
1136
|
}
|
|
1137
|
+
} catch (err) {
|
|
1138
|
+
this._handleMessageError(err);
|
|
1139
|
+
}
|
|
1063
1140
|
});
|
|
1064
1141
|
|
|
1065
1142
|
newWs.on('close', () => {
|
|
@@ -1390,7 +1467,6 @@ process.on('uncaughtException', (err) => {
|
|
|
1390
1467
|
process.on('unhandledRejection', (reason) => {
|
|
1391
1468
|
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
1392
1469
|
logger.error(`Unhandled rejection: ${err.message}`, err);
|
|
1393
|
-
process.exit(1);
|
|
1394
1470
|
});
|
|
1395
1471
|
|
|
1396
1472
|
// ─── Run ─────────────────────────────────────────────────────────────────────
|