pinokiod 3.270.0 → 3.271.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/kernel/index.js +2 -2
- package/kernel/peer.js +186 -19
- package/package.json +1 -1
package/kernel/index.js
CHANGED
|
@@ -803,6 +803,7 @@ class Kernel {
|
|
|
803
803
|
async init(options) {
|
|
804
804
|
|
|
805
805
|
let home = this.store.get("home") || process.env.PINOKIO_HOME
|
|
806
|
+
this.homedir = home
|
|
806
807
|
|
|
807
808
|
// reset shells if they exist
|
|
808
809
|
if (this.shell) {
|
|
@@ -820,10 +821,9 @@ class Kernel {
|
|
|
820
821
|
this.kv = new KV(this)
|
|
821
822
|
this.cloudflare = new Cloudflare()
|
|
822
823
|
this.peer = new Peer(this)
|
|
824
|
+
await this.peer.initialize(this)
|
|
823
825
|
this.git = new Git(this)
|
|
824
826
|
|
|
825
|
-
this.homedir = home
|
|
826
|
-
|
|
827
827
|
// if (home) {
|
|
828
828
|
// this.homedir = home
|
|
829
829
|
// } else {
|
package/kernel/peer.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const dgram = require('dgram');
|
|
2
2
|
const axios = require('axios');
|
|
3
3
|
const os = require('os')
|
|
4
|
+
const systeminformation = require('systeminformation')
|
|
4
5
|
const Environment = require("./environment")
|
|
5
6
|
class PeerDiscovery {
|
|
6
7
|
constructor(kernel, port = 41234, message = 'ping', interval = 1000) {
|
|
@@ -12,9 +13,8 @@ class PeerDiscovery {
|
|
|
12
13
|
this.peers = new Set();
|
|
13
14
|
this.interface_addresses = []
|
|
14
15
|
this.host_candidates = []
|
|
15
|
-
this.host =
|
|
16
|
+
this.host = null
|
|
16
17
|
this.default_port = 42000
|
|
17
|
-
this.peers.add(this.host)
|
|
18
18
|
this.router_info_cache = {}
|
|
19
19
|
// this.start();
|
|
20
20
|
}
|
|
@@ -85,6 +85,13 @@ class PeerDiscovery {
|
|
|
85
85
|
this.active = false
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
+
// Prepare host/peer state before the rest of the kernel bootstraps
|
|
89
|
+
async initialize(kernel) {
|
|
90
|
+
await this.refreshLocalAddress()
|
|
91
|
+
if (kernel) {
|
|
92
|
+
await this.check(kernel)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
88
95
|
async start(kernel) {
|
|
89
96
|
let env = await Environment.get(kernel.homedir, kernel)
|
|
90
97
|
|
|
@@ -576,23 +583,70 @@ class PeerDiscovery {
|
|
|
576
583
|
_isLocalLAN(ip) {
|
|
577
584
|
return this.isRFC1918(ip)
|
|
578
585
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
+
// Refresh LAN/IP selection; keeps peers set in sync with the active address
|
|
587
|
+
async refreshLocalAddress() {
|
|
588
|
+
try {
|
|
589
|
+
const { host, host_candidates, interface_addresses } = await this._getLocalIPAddress()
|
|
590
|
+
this.interface_addresses = interface_addresses
|
|
591
|
+
this.host_candidates = host_candidates
|
|
592
|
+
if (host && this.host !== host) {
|
|
593
|
+
if (this.host) {
|
|
594
|
+
this.peers.delete(this.host)
|
|
595
|
+
}
|
|
596
|
+
this.host = host
|
|
597
|
+
} else if (!this.host) {
|
|
598
|
+
this.host = host
|
|
599
|
+
}
|
|
600
|
+
if (this.host) {
|
|
601
|
+
this.peers.add(this.host)
|
|
602
|
+
}
|
|
603
|
+
return this.host
|
|
604
|
+
} catch (err) {
|
|
605
|
+
console.error('peer refreshLocalAddress error', err)
|
|
606
|
+
if (!this.host) {
|
|
607
|
+
this.host = null
|
|
608
|
+
}
|
|
609
|
+
return this.host
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async _getLocalIPAddress() {
|
|
613
|
+
const interface_addresses = await this._collectInterfaceAddresses()
|
|
614
|
+
const shareable = interface_addresses.filter((entry) => entry.shareable)
|
|
615
|
+
const host_candidates = shareable.map((entry) => ({
|
|
616
|
+
address: entry.address,
|
|
617
|
+
netmask: entry.netmask,
|
|
618
|
+
interface: entry.interface,
|
|
619
|
+
scope: entry.scope,
|
|
620
|
+
shareable: entry.shareable,
|
|
621
|
+
type: entry.type || null,
|
|
622
|
+
operstate: entry.operstate || null,
|
|
623
|
+
virtual: entry.virtual || false,
|
|
624
|
+
default: entry.default || false,
|
|
625
|
+
prefixLength: entry.prefixLength,
|
|
626
|
+
mac: entry.mac || null,
|
|
627
|
+
score: this._scoreCandidate(entry)
|
|
628
|
+
}))
|
|
629
|
+
let selectedHost = null
|
|
630
|
+
let bestScore = -Infinity
|
|
631
|
+
host_candidates.forEach((candidate, index) => {
|
|
632
|
+
const score = typeof candidate.score === 'number' ? candidate.score : -Infinity
|
|
633
|
+
if (score > bestScore) {
|
|
634
|
+
bestScore = score
|
|
635
|
+
selectedHost = candidate.address
|
|
636
|
+
} else if (score === bestScore && selectedHost === null) {
|
|
637
|
+
selectedHost = candidate.address
|
|
638
|
+
}
|
|
639
|
+
})
|
|
640
|
+
if (!selectedHost && shareable.length > 0) {
|
|
641
|
+
selectedHost = shareable[0].address
|
|
586
642
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
return cgnatCandidate.address
|
|
643
|
+
if (!selectedHost && interface_addresses.length > 0) {
|
|
644
|
+
selectedHost = interface_addresses[0].address
|
|
590
645
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
return publicCandidate.address
|
|
646
|
+
if (!selectedHost) {
|
|
647
|
+
selectedHost = '127.0.0.1'
|
|
594
648
|
}
|
|
595
|
-
return
|
|
649
|
+
return { host: selectedHost, host_candidates, interface_addresses }
|
|
596
650
|
}
|
|
597
651
|
isPrivateOrCGNAT(ip) {
|
|
598
652
|
return this.isRFC1918(ip) || this.isCGNAT(ip)
|
|
@@ -626,7 +680,7 @@ class PeerDiscovery {
|
|
|
626
680
|
}
|
|
627
681
|
return secondOctet >= 16 && secondOctet <= 31
|
|
628
682
|
}
|
|
629
|
-
|
|
683
|
+
_collectInterfaceAddressesSync() {
|
|
630
684
|
const interfaces = os.networkInterfaces()
|
|
631
685
|
const results = []
|
|
632
686
|
const seen = new Set()
|
|
@@ -653,12 +707,51 @@ class PeerDiscovery {
|
|
|
653
707
|
interface: ifaceName,
|
|
654
708
|
internal: Boolean(iface.internal),
|
|
655
709
|
scope: classification.scope,
|
|
656
|
-
shareable: classification.shareable
|
|
710
|
+
shareable: classification.shareable,
|
|
711
|
+
mac: typeof iface.mac === 'string' ? iface.mac : null
|
|
657
712
|
})
|
|
658
713
|
}
|
|
659
714
|
}
|
|
660
715
|
return results
|
|
661
716
|
}
|
|
717
|
+
async _collectInterfaceAddresses() {
|
|
718
|
+
const baseEntries = this._collectInterfaceAddressesSync()
|
|
719
|
+
let metadata = []
|
|
720
|
+
try {
|
|
721
|
+
metadata = await systeminformation.networkInterfaces()
|
|
722
|
+
} catch (err) {
|
|
723
|
+
metadata = []
|
|
724
|
+
}
|
|
725
|
+
const metadataMap = new Map()
|
|
726
|
+
if (Array.isArray(metadata)) {
|
|
727
|
+
metadata.forEach((entry) => {
|
|
728
|
+
if (entry && typeof entry.iface === 'string') {
|
|
729
|
+
metadataMap.set(this._normalizeInterfaceName(entry.iface), entry)
|
|
730
|
+
}
|
|
731
|
+
})
|
|
732
|
+
}
|
|
733
|
+
return baseEntries.map((entry) => {
|
|
734
|
+
const key = this._normalizeInterfaceName(entry.interface)
|
|
735
|
+
const meta = key ? metadataMap.get(key) : null
|
|
736
|
+
const prefixLength = this._prefixLengthFromNetmask(entry.netmask)
|
|
737
|
+
return {
|
|
738
|
+
...entry,
|
|
739
|
+
prefixLength,
|
|
740
|
+
type: meta && meta.type ? meta.type : null,
|
|
741
|
+
operstate: meta && meta.operstate ? meta.operstate : null,
|
|
742
|
+
speed: typeof meta?.speed === 'number' ? meta.speed : null,
|
|
743
|
+
virtual: Boolean(meta && meta.virtual),
|
|
744
|
+
default: Boolean(meta && meta.default),
|
|
745
|
+
mac: entry.mac || (meta && meta.mac) || null
|
|
746
|
+
}
|
|
747
|
+
})
|
|
748
|
+
}
|
|
749
|
+
_normalizeInterfaceName(name) {
|
|
750
|
+
if (!name || typeof name !== 'string') {
|
|
751
|
+
return ''
|
|
752
|
+
}
|
|
753
|
+
return name.trim().toLowerCase()
|
|
754
|
+
}
|
|
662
755
|
classifyAddress(address, isInternal = false) {
|
|
663
756
|
if (!address || typeof address !== 'string') {
|
|
664
757
|
return { scope: 'unknown', shareable: false }
|
|
@@ -684,6 +777,80 @@ class PeerDiscovery {
|
|
|
684
777
|
}
|
|
685
778
|
return { scope: 'public', shareable: true }
|
|
686
779
|
}
|
|
780
|
+
_prefixLengthFromNetmask(netmask) {
|
|
781
|
+
if (!netmask || typeof netmask !== 'string') {
|
|
782
|
+
return null
|
|
783
|
+
}
|
|
784
|
+
const octets = this._parseIPv4(netmask)
|
|
785
|
+
if (!octets) {
|
|
786
|
+
return null
|
|
787
|
+
}
|
|
788
|
+
let bits = 0
|
|
789
|
+
for (const octet of octets) {
|
|
790
|
+
bits += this._countBits(octet)
|
|
791
|
+
}
|
|
792
|
+
return bits
|
|
793
|
+
}
|
|
794
|
+
_countBits(value) {
|
|
795
|
+
let count = 0
|
|
796
|
+
let v = value & 255
|
|
797
|
+
while (v) {
|
|
798
|
+
count += v & 1
|
|
799
|
+
v >>= 1
|
|
800
|
+
}
|
|
801
|
+
return count
|
|
802
|
+
}
|
|
803
|
+
// Heuristically rank interface candidates so physical LAN adapters win over VPN/tunnels
|
|
804
|
+
_scoreCandidate(entry) {
|
|
805
|
+
if (!entry || !entry.shareable) {
|
|
806
|
+
return -Infinity
|
|
807
|
+
}
|
|
808
|
+
let score = 0
|
|
809
|
+
switch (entry.scope) {
|
|
810
|
+
case 'lan':
|
|
811
|
+
score += 100
|
|
812
|
+
break
|
|
813
|
+
case 'cgnat':
|
|
814
|
+
score += 60
|
|
815
|
+
break
|
|
816
|
+
case 'public':
|
|
817
|
+
score += 40
|
|
818
|
+
break
|
|
819
|
+
default:
|
|
820
|
+
score -= 50
|
|
821
|
+
break
|
|
822
|
+
}
|
|
823
|
+
if (entry.default) {
|
|
824
|
+
score += 20
|
|
825
|
+
}
|
|
826
|
+
const type = entry.type ? entry.type.toLowerCase() : ''
|
|
827
|
+
if (type === 'wired') {
|
|
828
|
+
score += 25
|
|
829
|
+
} else if (type === 'wireless') {
|
|
830
|
+
score += 18
|
|
831
|
+
} else if (type === 'vpn') {
|
|
832
|
+
score -= 40
|
|
833
|
+
} else if (type === 'cellular') {
|
|
834
|
+
score += 5
|
|
835
|
+
}
|
|
836
|
+
if (entry.virtual) {
|
|
837
|
+
score -= 25
|
|
838
|
+
}
|
|
839
|
+
if (entry.operstate && entry.operstate.toLowerCase() === 'up') {
|
|
840
|
+
score += 5
|
|
841
|
+
} else if (entry.operstate) {
|
|
842
|
+
score -= 10
|
|
843
|
+
}
|
|
844
|
+
if (typeof entry.prefixLength === 'number') {
|
|
845
|
+
if (entry.prefixLength <= 24) {
|
|
846
|
+
score += 5
|
|
847
|
+
}
|
|
848
|
+
if (entry.prefixLength >= 30) {
|
|
849
|
+
score -= 20
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return score
|
|
853
|
+
}
|
|
687
854
|
_buildExternalHostEntries(externalPort) {
|
|
688
855
|
if (!externalPort && externalPort !== 0) {
|
|
689
856
|
return []
|
|
@@ -741,7 +908,7 @@ class PeerDiscovery {
|
|
|
741
908
|
return entries
|
|
742
909
|
}
|
|
743
910
|
_broadcastTargets() {
|
|
744
|
-
const addresses = this.
|
|
911
|
+
const addresses = this._collectInterfaceAddressesSync()
|
|
745
912
|
this.interface_addresses = addresses
|
|
746
913
|
const targets = new Set()
|
|
747
914
|
for (const entry of addresses) {
|