@push.rocks/smartnetwork 4.10.0 → 5.0.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/.smartconfig.json +19 -8
- package/dist_rust/rustnetwork_linux_amd64_musl +0 -0
- package/dist_rust/rustnetwork_linux_amd64_musl.tsrust-build.json +14 -0
- package/dist_rust/rustnetwork_linux_arm64_musl +0 -0
- package/dist_rust/rustnetwork_linux_arm64_musl.tsrust-build.json +14 -0
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/smartnetwork.classes.domainintelligence.d.ts +10 -4
- package/dist_ts/smartnetwork.classes.domainintelligence.js +69 -39
- package/dist_ts/smartnetwork.classes.ipintelligence.d.ts +8 -2
- package/dist_ts/smartnetwork.classes.ipintelligence.js +67 -37
- package/dist_ts/smartnetwork.classes.rustbridge.d.ts +12 -8
- package/dist_ts/smartnetwork.classes.rustbridge.js +82 -32
- package/dist_ts/smartnetwork.classes.smartnetwork.d.ts +46 -8
- package/dist_ts/smartnetwork.classes.smartnetwork.js +165 -84
- package/native-notices/crates/aho-corasick-1.1.4/copying +3 -0
- package/native-notices/crates/aho-corasick-1.1.4/license-mit +21 -0
- package/native-notices/crates/aho-corasick-1.1.4/unlicense +24 -0
- package/native-notices/crates/anstream-1.0.0/license-apache +202 -0
- package/native-notices/crates/anstream-1.0.0/license-mit +19 -0
- package/native-notices/crates/anstyle-1.0.14/license-apache +202 -0
- package/native-notices/crates/anstyle-1.0.14/license-mit +19 -0
- package/native-notices/crates/anstyle-parse-1.0.0/license-apache +202 -0
- package/native-notices/crates/anstyle-parse-1.0.0/license-mit +19 -0
- package/native-notices/crates/anstyle-query-1.1.5/license-apache +202 -0
- package/native-notices/crates/anstyle-query-1.1.5/license-mit +19 -0
- package/native-notices/crates/bytes-1.11.1/license +25 -0
- package/native-notices/crates/cfg-if-1.0.4/license-apache +201 -0
- package/native-notices/crates/cfg-if-1.0.4/license-mit +25 -0
- package/native-notices/crates/clap-4.6.1/license-apache +202 -0
- package/native-notices/crates/clap-4.6.1/license-mit +19 -0
- package/native-notices/crates/clap_builder-4.6.0/license-apache +202 -0
- package/native-notices/crates/clap_builder-4.6.0/license-mit +19 -0
- package/native-notices/crates/clap_derive-4.6.1/license-apache +202 -0
- package/native-notices/crates/clap_derive-4.6.1/license-mit +19 -0
- package/native-notices/crates/clap_lex-1.1.0/license-apache +202 -0
- package/native-notices/crates/clap_lex-1.1.0/license-mit +19 -0
- package/native-notices/crates/colorchoice-1.0.5/license-apache +202 -0
- package/native-notices/crates/colorchoice-1.0.5/license-mit +19 -0
- package/native-notices/crates/errno-0.3.14/license-apache +201 -0
- package/native-notices/crates/errno-0.3.14/license-mit +25 -0
- package/native-notices/crates/getrandom-0.3.4/license-apache +201 -0
- package/native-notices/crates/getrandom-0.3.4/license-mit +26 -0
- package/native-notices/crates/glob-0.3.3/license-apache +201 -0
- package/native-notices/crates/glob-0.3.3/license-mit +25 -0
- package/native-notices/crates/heck-0.5.0/license-apache +201 -0
- package/native-notices/crates/heck-0.5.0/license-mit +25 -0
- package/native-notices/crates/hex-0.4.3/license-apache +202 -0
- package/native-notices/crates/hex-0.4.3/license-mit +20 -0
- package/native-notices/crates/is_terminal_polyfill-1.70.2/license-apache +202 -0
- package/native-notices/crates/is_terminal_polyfill-1.70.2/license-mit +19 -0
- package/native-notices/crates/itoa-1.0.18/license-apache +176 -0
- package/native-notices/crates/itoa-1.0.18/license-mit +23 -0
- package/native-notices/crates/lazy_static-1.5.0/license-apache +201 -0
- package/native-notices/crates/lazy_static-1.5.0/license-mit +25 -0
- package/native-notices/crates/libc-0.2.186/license-apache +176 -0
- package/native-notices/crates/libc-0.2.186/license-mit +25 -0
- package/native-notices/crates/lock_api-0.4.14/license-apache +201 -0
- package/native-notices/crates/lock_api-0.4.14/license-mit +25 -0
- package/native-notices/crates/log-0.4.29/license-apache +201 -0
- package/native-notices/crates/log-0.4.29/license-mit +25 -0
- package/native-notices/crates/matchers-0.2.0/license +19 -0
- package/native-notices/crates/memchr-2.8.0/copying +3 -0
- package/native-notices/crates/memchr-2.8.0/license-mit +21 -0
- package/native-notices/crates/memchr-2.8.0/unlicense +24 -0
- package/native-notices/crates/mio-1.2.0/license +19 -0
- package/native-notices/crates/no-std-net-0.6.0/license +21 -0
- package/native-notices/crates/nu-ansi-term-0.50.3/license +22 -0
- package/native-notices/crates/once_cell-1.21.4/license-apache +201 -0
- package/native-notices/crates/once_cell-1.21.4/license-mit +23 -0
- package/native-notices/crates/parking_lot-0.12.5/license-apache +201 -0
- package/native-notices/crates/parking_lot-0.12.5/license-mit +25 -0
- package/native-notices/crates/parking_lot_core-0.9.12/license-apache +201 -0
- package/native-notices/crates/parking_lot_core-0.9.12/license-mit +25 -0
- package/native-notices/crates/pin-project-lite-0.2.17/license-apache +177 -0
- package/native-notices/crates/pin-project-lite-0.2.17/license-mit +23 -0
- package/native-notices/crates/pnet_base-0.34.0/license-apache +201 -0
- package/native-notices/crates/pnet_base-0.34.0/license-mit +25 -0
- package/native-notices/crates/pnet_macros-0.34.0/license-apache +201 -0
- package/native-notices/crates/pnet_macros-0.34.0/license-mit +25 -0
- package/native-notices/crates/pnet_macros_support-0.34.0/license-apache +201 -0
- package/native-notices/crates/pnet_macros_support-0.34.0/license-mit +25 -0
- package/native-notices/crates/pnet_packet-0.34.0/license-apache +201 -0
- package/native-notices/crates/pnet_packet-0.34.0/license-mit +25 -0
- package/native-notices/crates/ppv-lite86-0.2.21/license-apache +201 -0
- package/native-notices/crates/ppv-lite86-0.2.21/license-mit +25 -0
- package/native-notices/crates/proc-macro2-1.0.106/license-apache +176 -0
- package/native-notices/crates/proc-macro2-1.0.106/license-mit +23 -0
- package/native-notices/crates/quote-1.0.45/license-apache +176 -0
- package/native-notices/crates/quote-1.0.45/license-mit +23 -0
- package/native-notices/crates/rand-0.9.4/copyright +12 -0
- package/native-notices/crates/rand-0.9.4/license-apache +176 -0
- package/native-notices/crates/rand-0.9.4/license-mit +26 -0
- package/native-notices/crates/rand_chacha-0.9.0/copyright +12 -0
- package/native-notices/crates/rand_chacha-0.9.0/license-apache +176 -0
- package/native-notices/crates/rand_chacha-0.9.0/license-mit +26 -0
- package/native-notices/crates/rand_core-0.9.5/copyright +12 -0
- package/native-notices/crates/rand_core-0.9.5/license-apache +187 -0
- package/native-notices/crates/rand_core-0.9.5/license-mit +26 -0
- package/native-notices/crates/regex-1.12.3/license-apache +201 -0
- package/native-notices/crates/regex-1.12.3/license-mit +25 -0
- package/native-notices/crates/regex-automata-0.4.14/license-apache +201 -0
- package/native-notices/crates/regex-automata-0.4.14/license-mit +25 -0
- package/native-notices/crates/regex-syntax-0.8.10/license-apache +201 -0
- package/native-notices/crates/regex-syntax-0.8.10/license-mit +25 -0
- package/native-notices/crates/scopeguard-1.2.0/license-apache +201 -0
- package/native-notices/crates/scopeguard-1.2.0/license-mit +25 -0
- package/native-notices/crates/serde-1.0.228/license-apache +176 -0
- package/native-notices/crates/serde-1.0.228/license-mit +23 -0
- package/native-notices/crates/serde_core-1.0.228/license-apache +176 -0
- package/native-notices/crates/serde_core-1.0.228/license-mit +23 -0
- package/native-notices/crates/serde_derive-1.0.228/license-apache +176 -0
- package/native-notices/crates/serde_derive-1.0.228/license-mit +23 -0
- package/native-notices/crates/serde_json-1.0.149/license-apache +176 -0
- package/native-notices/crates/serde_json-1.0.149/license-mit +23 -0
- package/native-notices/crates/sharded-slab-0.1.7/license +19 -0
- package/native-notices/crates/signal-hook-registry-1.4.8/license-apache +201 -0
- package/native-notices/crates/signal-hook-registry-1.4.8/license-mit +25 -0
- package/native-notices/crates/smallvec-1.15.1/license-apache +201 -0
- package/native-notices/crates/smallvec-1.15.1/license-mit +25 -0
- package/native-notices/crates/socket2-0.5.10/license-apache +201 -0
- package/native-notices/crates/socket2-0.5.10/license-mit +25 -0
- package/native-notices/crates/socket2-0.6.3/license-apache +201 -0
- package/native-notices/crates/socket2-0.6.3/license-mit +25 -0
- package/native-notices/crates/strsim-0.11.1/license +23 -0
- package/native-notices/crates/surge-ping-0.8.4/license +21 -0
- package/native-notices/crates/syn-2.0.117/license-apache +176 -0
- package/native-notices/crates/syn-2.0.117/license-mit +23 -0
- package/native-notices/crates/thiserror-1.0.69/license-apache +176 -0
- package/native-notices/crates/thiserror-1.0.69/license-mit +23 -0
- package/native-notices/crates/thiserror-impl-1.0.69/license-apache +176 -0
- package/native-notices/crates/thiserror-impl-1.0.69/license-mit +23 -0
- package/native-notices/crates/thread_local-1.1.9/license-apache +201 -0
- package/native-notices/crates/thread_local-1.1.9/license-mit +25 -0
- package/native-notices/crates/tokio-1.52.3/license +21 -0
- package/native-notices/crates/tokio-macros-2.7.0/license +22 -0
- package/native-notices/crates/tracing-0.1.44/license +25 -0
- package/native-notices/crates/tracing-attributes-0.1.31/license +25 -0
- package/native-notices/crates/tracing-core-0.1.36/license +25 -0
- package/native-notices/crates/tracing-log-0.2.0/license +25 -0
- package/native-notices/crates/tracing-subscriber-0.3.23/license +25 -0
- package/native-notices/crates/unicode-ident-1.0.24/license-apache +176 -0
- package/native-notices/crates/unicode-ident-1.0.24/license-mit +23 -0
- package/native-notices/crates/unicode-ident-1.0.24/license-unicode +39 -0
- package/native-notices/crates/utf8parse-0.2.2/license-apache +176 -0
- package/native-notices/crates/utf8parse-0.2.2/license-mit +25 -0
- package/native-notices/crates/zerocopy-0.8.48/license-apache +202 -0
- package/native-notices/crates/zerocopy-0.8.48/license-bsd +24 -0
- package/native-notices/crates/zerocopy-0.8.48/license-mit +26 -0
- package/native-notices/crates/zmij-1.0.21/license-mit +23 -0
- package/native-notices/manifest.json +1890 -0
- package/native-notices/readme.md +42 -0
- package/native-notices/runtime/gcc/gpl-3.0.txt +674 -0
- package/native-notices/runtime/gcc/runtime-library-exception-3.1.txt +73 -0
- package/native-notices/runtime/musl-1.2.5/copyright +193 -0
- package/native-notices/rust/compiler-builtins-license.txt +275 -0
- package/native-notices/rust/copyright-library.html +8266 -0
- package/native-notices/rust/crates/addr2line-0.25.1/license-apache +201 -0
- package/native-notices/rust/crates/addr2line-0.25.1/license-mit +25 -0
- package/native-notices/rust/crates/adler2-2.0.1/license-0bsd +12 -0
- package/native-notices/rust/crates/adler2-2.0.1/license-apache +201 -0
- package/native-notices/rust/crates/adler2-2.0.1/license-mit +23 -0
- package/native-notices/rust/crates/memchr-2.7.6/copying +3 -0
- package/native-notices/rust/crates/memchr-2.7.6/license-mit +21 -0
- package/native-notices/rust/crates/memchr-2.7.6/unlicense +24 -0
- package/native-notices/rust/crates/miniz_oxide-0.8.9/license +25 -0
- package/native-notices/rust/crates/miniz_oxide-0.8.9/license-apache.md +177 -0
- package/native-notices/rust/crates/miniz_oxide-0.8.9/license-mit.md +24 -0
- package/native-notices/rust/crates/miniz_oxide-0.8.9/license-zlib.md +14 -0
- package/native-notices/rust/crates/object-0.37.3/license-apache +201 -0
- package/native-notices/rust/crates/object-0.37.3/license-mit +25 -0
- package/native-notices/rust/licenses/apache-2.0.txt +73 -0
- package/native-notices/rust/licenses/bsd-2-clause.txt +9 -0
- package/native-notices/rust/licenses/cc-by-sa-4.0.txt +427 -0
- package/native-notices/rust/licenses/gcc-exception-3.1.txt +30 -0
- package/native-notices/rust/licenses/gpl-2.0-only.txt +133 -0
- package/native-notices/rust/licenses/gpl-3.0-or-later.txt +202 -0
- package/native-notices/rust/licenses/isc.txt +7 -0
- package/native-notices/rust/licenses/llvm-exception.txt +15 -0
- package/native-notices/rust/licenses/mit.txt +9 -0
- package/native-notices/rust/licenses/ncsa.txt +29 -0
- package/native-notices/rust/licenses/ofl-1.1.txt +43 -0
- package/native-notices/rust/licenses/unicode-3.0.txt +39 -0
- package/package.json +18 -9
- package/readme.md +56 -13
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +1 -1
- package/ts/smartnetwork.classes.domainintelligence.ts +73 -40
- package/ts/smartnetwork.classes.ipintelligence.ts +71 -37
- package/ts/smartnetwork.classes.rustbridge.ts +83 -34
- package/ts/smartnetwork.classes.smartnetwork.ts +208 -89
- package/dist_rust/rustnetwork_linux_amd64 +0 -0
- package/dist_rust/rustnetwork_linux_arm64 +0 -0
- package/readme.hints.md +0 -106
- package/ts/tsconfig.json +0 -5
- /package/{license → license.md} +0 -0
|
@@ -5,7 +5,10 @@ import { IpIntelligence, type IIpIntelligenceResult } from './smartnetwork.class
|
|
|
5
5
|
import { DomainIntelligence, type IDomainIntelligenceResult } from './smartnetwork.classes.domainintelligence.js';
|
|
6
6
|
import { getLogger } from './logging.js';
|
|
7
7
|
import { NetworkError } from './errors.js';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
RustNetworkBridge,
|
|
10
|
+
RustNetworkBridgeActivationCleanupError,
|
|
11
|
+
} from './smartnetwork.classes.rustbridge.js';
|
|
9
12
|
|
|
10
13
|
/** Type alias for the shared Smartdns client instance */
|
|
11
14
|
type TSmartdnsClient = InstanceType<typeof plugins.smartdns.dnsClientMod.Smartdns>;
|
|
@@ -29,6 +32,37 @@ export interface Hop {
|
|
|
29
32
|
rtt: number | null;
|
|
30
33
|
}
|
|
31
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Options for the ping method
|
|
37
|
+
*/
|
|
38
|
+
export interface IPingOptions {
|
|
39
|
+
/** Time to wait for each echo reply in milliseconds. Default: 5000 */
|
|
40
|
+
timeout?: number;
|
|
41
|
+
/** Number of echo requests to send. Default: 1 */
|
|
42
|
+
count?: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Result of the ping method. Round-trip times are in milliseconds and `null`
|
|
47
|
+
* where no reply arrived.
|
|
48
|
+
*/
|
|
49
|
+
export interface IPingResult {
|
|
50
|
+
host: string;
|
|
51
|
+
count: number;
|
|
52
|
+
/** True when at least one echo reply arrived */
|
|
53
|
+
alive: boolean;
|
|
54
|
+
/** Round-trip time of the first reply */
|
|
55
|
+
time: number | null;
|
|
56
|
+
/** Round-trip time per echo request */
|
|
57
|
+
times: (number | null)[];
|
|
58
|
+
min: number | null;
|
|
59
|
+
max: number | null;
|
|
60
|
+
avg: number | null;
|
|
61
|
+
stddev: number | null;
|
|
62
|
+
/** Lost echo requests in percent */
|
|
63
|
+
packetLoss: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
32
66
|
/**
|
|
33
67
|
* Options for the findFreePort method
|
|
34
68
|
*/
|
|
@@ -39,6 +73,8 @@ export interface IFindFreePortOptions {
|
|
|
39
73
|
exclude?: number[];
|
|
40
74
|
}
|
|
41
75
|
|
|
76
|
+
type TBridgeLifecycleState = 'idle' | 'active' | 'release-failed';
|
|
77
|
+
|
|
42
78
|
/**
|
|
43
79
|
* SmartNetwork simplifies actions within the network.
|
|
44
80
|
* Uses a Rust binary for system-dependent operations (ping, traceroute, port scanning, gateway detection).
|
|
@@ -58,7 +94,11 @@ export class SmartNetwork {
|
|
|
58
94
|
private options: SmartNetworkOptions;
|
|
59
95
|
private cache: Map<string, { value: any; expiry: number }>;
|
|
60
96
|
private rustBridge: RustNetworkBridge;
|
|
61
|
-
private
|
|
97
|
+
private bridgeLifecycleState: TBridgeLifecycleState = 'idle';
|
|
98
|
+
private bridgeLifecycleQueue: Promise<void> = Promise.resolve();
|
|
99
|
+
private activeDnsOperations = 0;
|
|
100
|
+
private dnsDrainPromise: Promise<void> | null = null;
|
|
101
|
+
private resolveDnsDrain: (() => void) | null = null;
|
|
62
102
|
private ipIntelligence: IpIntelligence | null = null;
|
|
63
103
|
private domainIntelligence: DomainIntelligence | null = null;
|
|
64
104
|
private dnsClient: TSmartdnsClient | null = null;
|
|
@@ -73,11 +113,21 @@ export class SmartNetwork {
|
|
|
73
113
|
* Start the Rust binary bridge. Must be called before using ping, traceroute,
|
|
74
114
|
* port scanning, or gateway operations. Safe to call multiple times.
|
|
75
115
|
*/
|
|
76
|
-
public
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
116
|
+
public start(): Promise<void> {
|
|
117
|
+
return this.enqueueBridgeLifecycle(async () => {
|
|
118
|
+
if (this.bridgeLifecycleState === 'release-failed') {
|
|
119
|
+
throw new Error('SmartNetwork cleanup must succeed before it can start again');
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
await this.rustBridge.start(this);
|
|
123
|
+
this.bridgeLifecycleState = 'active';
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (error instanceof RustNetworkBridgeActivationCleanupError) {
|
|
126
|
+
this.bridgeLifecycleState = 'release-failed';
|
|
127
|
+
}
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
81
131
|
}
|
|
82
132
|
|
|
83
133
|
/**
|
|
@@ -86,35 +136,100 @@ export class SmartNetwork {
|
|
|
86
136
|
* Rust-backed features, otherwise the smartdns Rust backend may keep
|
|
87
137
|
* the event loop alive.
|
|
88
138
|
*/
|
|
89
|
-
public
|
|
90
|
-
|
|
91
|
-
await this.
|
|
92
|
-
|
|
139
|
+
public stop(): Promise<void> {
|
|
140
|
+
return this.enqueueBridgeLifecycle(async () => {
|
|
141
|
+
await this.waitForDnsOperations();
|
|
142
|
+
|
|
143
|
+
let bridgeError: unknown;
|
|
144
|
+
if (this.bridgeLifecycleState !== 'idle') {
|
|
145
|
+
try {
|
|
146
|
+
await this.rustBridge.stop(this);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
bridgeError = error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let dnsError: unknown;
|
|
153
|
+
if (this.dnsClient) {
|
|
154
|
+
try {
|
|
155
|
+
await this.dnsClient.terminate();
|
|
156
|
+
this.dnsClient = null;
|
|
157
|
+
// Intelligence instances hold a stale reference to the terminated
|
|
158
|
+
// client; drop them so the next call rebuilds with a fresh one.
|
|
159
|
+
this.ipIntelligence = null;
|
|
160
|
+
this.domainIntelligence = null;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
dnsError = error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (bridgeError || dnsError) {
|
|
167
|
+
this.bridgeLifecycleState = 'release-failed';
|
|
168
|
+
} else {
|
|
169
|
+
this.bridgeLifecycleState = 'idle';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (bridgeError && dnsError) {
|
|
173
|
+
throw new AggregateError([bridgeError, dnsError], 'SmartNetwork cleanup failed');
|
|
174
|
+
}
|
|
175
|
+
if (bridgeError) throw bridgeError;
|
|
176
|
+
if (dnsError) throw dnsError;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private enqueueBridgeLifecycle(operationArg: () => Promise<void>): Promise<void> {
|
|
181
|
+
const result = this.bridgeLifecycleQueue.then(operationArg);
|
|
182
|
+
this.bridgeLifecycleQueue = result.catch(() => {});
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private async withDnsClient<T>(
|
|
187
|
+
operationArg: (dnsClientArg: TSmartdnsClient) => Promise<T>,
|
|
188
|
+
): Promise<T> {
|
|
189
|
+
let dnsClient: TSmartdnsClient;
|
|
190
|
+
await this.enqueueBridgeLifecycle(async () => {
|
|
191
|
+
if (this.bridgeLifecycleState === 'release-failed') {
|
|
192
|
+
throw new Error('SmartNetwork cleanup must succeed before DNS can be used again');
|
|
193
|
+
}
|
|
194
|
+
dnsClient = this.ensureDnsClient();
|
|
195
|
+
this.activeDnsOperations++;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
return await operationArg(dnsClient!);
|
|
200
|
+
} finally {
|
|
201
|
+
this.activeDnsOperations--;
|
|
202
|
+
if (this.activeDnsOperations === 0 && this.resolveDnsDrain) {
|
|
203
|
+
const resolve = this.resolveDnsDrain;
|
|
204
|
+
this.resolveDnsDrain = null;
|
|
205
|
+
this.dnsDrainPromise = null;
|
|
206
|
+
resolve();
|
|
207
|
+
}
|
|
93
208
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
this.
|
|
100
|
-
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private waitForDnsOperations(): Promise<void> {
|
|
212
|
+
if (this.activeDnsOperations === 0) return Promise.resolve();
|
|
213
|
+
if (!this.dnsDrainPromise) {
|
|
214
|
+
this.dnsDrainPromise = new Promise<void>((resolve) => {
|
|
215
|
+
this.resolveDnsDrain = resolve;
|
|
216
|
+
});
|
|
101
217
|
}
|
|
218
|
+
return this.dnsDrainPromise;
|
|
102
219
|
}
|
|
103
220
|
|
|
104
221
|
/**
|
|
105
222
|
* Ensure the Rust bridge is running before sending commands.
|
|
106
223
|
*/
|
|
107
224
|
private async ensureBridge(): Promise<void> {
|
|
108
|
-
|
|
109
|
-
await this.start();
|
|
110
|
-
}
|
|
225
|
+
await this.start();
|
|
111
226
|
}
|
|
112
227
|
|
|
113
228
|
/**
|
|
114
229
|
* Lazily create the shared Smartdns client. The Rust backend inside
|
|
115
230
|
* Smartdns is only spawned on first query that requires it (NS/MX/SOA
|
|
116
231
|
* with prefer-system strategy, or any query with doh/udp strategy).
|
|
117
|
-
* The client is
|
|
232
|
+
* The client is terminated by stop().
|
|
118
233
|
*/
|
|
119
234
|
private ensureDnsClient(): TSmartdnsClient {
|
|
120
235
|
if (!this.dnsClient) {
|
|
@@ -136,9 +251,14 @@ export class SmartNetwork {
|
|
|
136
251
|
}
|
|
137
252
|
|
|
138
253
|
/**
|
|
139
|
-
* Send ICMP
|
|
254
|
+
* Send ICMP echo requests to a host.
|
|
255
|
+
*
|
|
256
|
+
* A host that does not answer or cannot be resolved yields `alive: false`.
|
|
257
|
+
* Rejects with a `NetworkError` with code `EPERM` when the process may not
|
|
258
|
+
* open an ICMP socket (Linux needs `CAP_NET_RAW` or a `net.ipv4.ping_group_range`
|
|
259
|
+
* that includes the process group), because no probe was sent at all.
|
|
140
260
|
*/
|
|
141
|
-
public async ping(host: string, opts?:
|
|
261
|
+
public async ping(host: string, opts?: IPingOptions): Promise<IPingResult> {
|
|
142
262
|
await this.ensureBridge();
|
|
143
263
|
const timeoutMs = opts?.timeout ?? 5000;
|
|
144
264
|
const count = opts?.count && opts.count > 1 ? opts.count : 1;
|
|
@@ -146,48 +266,43 @@ export class SmartNetwork {
|
|
|
146
266
|
let result: Awaited<ReturnType<typeof this.rustBridge.ping>>;
|
|
147
267
|
try {
|
|
148
268
|
result = await this.rustBridge.ping(host, count, timeoutMs);
|
|
149
|
-
} catch {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (error instanceof plugins.smartrust.RustBridgeRequestError) {
|
|
271
|
+
if (error.responseErrorCode === 'ERESOLVE') {
|
|
272
|
+
return {
|
|
273
|
+
host,
|
|
274
|
+
count,
|
|
275
|
+
alive: false,
|
|
276
|
+
time: null,
|
|
277
|
+
times: Array<number | null>(count).fill(null),
|
|
278
|
+
min: null,
|
|
279
|
+
max: null,
|
|
280
|
+
avg: null,
|
|
281
|
+
stddev: null,
|
|
282
|
+
packetLoss: 100,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (error.responseErrorCode === 'EPERM') {
|
|
286
|
+
throw new NetworkError(
|
|
287
|
+
`ICMP ping is not permitted for this process (needs CAP_NET_RAW or a net.ipv4.ping_group_range that includes its group): ${error.message}`,
|
|
288
|
+
'EPERM',
|
|
289
|
+
);
|
|
290
|
+
}
|
|
153
291
|
}
|
|
154
|
-
|
|
155
|
-
host,
|
|
156
|
-
count,
|
|
157
|
-
times: Array(count).fill(NaN),
|
|
158
|
-
min: NaN,
|
|
159
|
-
max: NaN,
|
|
160
|
-
avg: NaN,
|
|
161
|
-
stddev: NaN,
|
|
162
|
-
packetLoss: 100,
|
|
163
|
-
alive: false,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Map times: replace null with NaN for backward compatibility
|
|
168
|
-
const times = result.times.map((t) => (t === null ? NaN : t));
|
|
169
|
-
const min = result.min === null ? NaN : result.min;
|
|
170
|
-
const max = result.max === null ? NaN : result.max;
|
|
171
|
-
const avg = result.avg === null ? NaN : result.avg;
|
|
172
|
-
const stddev = result.stddev === null ? NaN : result.stddev;
|
|
173
|
-
|
|
174
|
-
if (count === 1) {
|
|
175
|
-
return {
|
|
176
|
-
alive: result.alive,
|
|
177
|
-
time: times[0] ?? NaN,
|
|
178
|
-
};
|
|
292
|
+
throw error;
|
|
179
293
|
}
|
|
180
294
|
|
|
181
295
|
return {
|
|
182
296
|
host,
|
|
183
297
|
count,
|
|
184
|
-
times,
|
|
185
|
-
min,
|
|
186
|
-
max,
|
|
187
|
-
avg,
|
|
188
|
-
stddev,
|
|
189
|
-
packetLoss: result.packetLoss,
|
|
190
298
|
alive: result.alive,
|
|
299
|
+
time: result.times.find((time) => time !== null) ?? null,
|
|
300
|
+
times: result.times,
|
|
301
|
+
min: result.min,
|
|
302
|
+
max: result.max,
|
|
303
|
+
avg: result.avg,
|
|
304
|
+
stddev: result.stddev,
|
|
305
|
+
packetLoss: result.packetLoss,
|
|
191
306
|
};
|
|
192
307
|
}
|
|
193
308
|
|
|
@@ -348,31 +463,31 @@ export class SmartNetwork {
|
|
|
348
463
|
/**
|
|
349
464
|
* Resolve DNS records (A, AAAA, MX) via the shared smartdns client.
|
|
350
465
|
* The client is lifecycle-managed by start()/stop() — MX queries spawn
|
|
351
|
-
* the smartdns Rust bridge,
|
|
466
|
+
* the smartdns Rust bridge, whose termination is confirmed by stop().
|
|
352
467
|
*/
|
|
353
468
|
public async resolveDns(
|
|
354
469
|
host: string,
|
|
355
470
|
): Promise<{ A: string[]; AAAA: string[]; MX: { exchange: string; priority: number }[] }> {
|
|
356
471
|
try {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
};
|
|
472
|
+
return await this.withDnsClient(async (dnsClient) => {
|
|
473
|
+
const [aRecords, aaaaRecords, mxRecords] = await Promise.all([
|
|
474
|
+
dnsClient.getRecordsA(host).catch((): any[] => []),
|
|
475
|
+
dnsClient.getRecordsAAAA(host).catch((): any[] => []),
|
|
476
|
+
dnsClient.getRecords(host, 'MX').catch((): any[] => []),
|
|
477
|
+
]);
|
|
478
|
+
|
|
479
|
+
const A = aRecords.map((record: any) => record.value);
|
|
480
|
+
const AAAA = aaaaRecords.map((record: any) => record.value);
|
|
481
|
+
const MX = mxRecords.map((record: any) => {
|
|
482
|
+
const parts = record.value.split(' ');
|
|
483
|
+
return {
|
|
484
|
+
priority: parseInt(parts[0], 10),
|
|
485
|
+
exchange: parts[1] || '',
|
|
486
|
+
};
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
return { A, AAAA, MX };
|
|
373
490
|
});
|
|
374
|
-
|
|
375
|
-
return { A, AAAA, MX };
|
|
376
491
|
} catch (err: any) {
|
|
377
492
|
throw new NetworkError(err.message, err.code);
|
|
378
493
|
}
|
|
@@ -438,13 +553,15 @@ export class SmartNetwork {
|
|
|
438
553
|
* Pure TS, no Rust needed.
|
|
439
554
|
*/
|
|
440
555
|
public async getIpIntelligence(ip: string): Promise<IIpIntelligenceResult> {
|
|
441
|
-
|
|
442
|
-
this.ipIntelligence
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
556
|
+
const fetcher = () => this.withDnsClient(async (dnsClient) => {
|
|
557
|
+
if (!this.ipIntelligence) {
|
|
558
|
+
this.ipIntelligence = new IpIntelligence({
|
|
559
|
+
dnsClient,
|
|
560
|
+
timeout: this.options.ipIntelligenceTimeout,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
return this.ipIntelligence.getIntelligence(ip);
|
|
564
|
+
});
|
|
448
565
|
if (this.options.cacheTtl && this.options.cacheTtl > 0) {
|
|
449
566
|
return this.getCached(`ipIntelligence:${ip}`, fetcher);
|
|
450
567
|
}
|
|
@@ -457,10 +574,12 @@ export class SmartNetwork {
|
|
|
457
574
|
* Rust needed.
|
|
458
575
|
*/
|
|
459
576
|
public async getDomainIntelligence(domain: string): Promise<IDomainIntelligenceResult> {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
577
|
+
const fetcher = () => this.withDnsClient(async (dnsClient) => {
|
|
578
|
+
if (!this.domainIntelligence) {
|
|
579
|
+
this.domainIntelligence = new DomainIntelligence({ dnsClient });
|
|
580
|
+
}
|
|
581
|
+
return this.domainIntelligence.getIntelligence(domain);
|
|
582
|
+
});
|
|
464
583
|
if (this.options.cacheTtl && this.options.cacheTtl > 0) {
|
|
465
584
|
return this.getCached(`domainIntelligence:${domain}`, fetcher);
|
|
466
585
|
}
|
|
Binary file
|
|
Binary file
|
package/readme.hints.md
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# Project Analysis
|
|
2
|
-
|
|
3
|
-
## Architecture Overview
|
|
4
|
-
This is a comprehensive network diagnostics toolkit. The main entry point is the `SmartNetwork` class which orchestrates all functionality. System-dependent operations are delegated to a Rust binary via JSON-over-stdin/stdout IPC using `@push.rocks/smartrust`.
|
|
5
|
-
|
|
6
|
-
Key features:
|
|
7
|
-
- Speed testing via Cloudflare (parallelizable with duration support) — pure TS
|
|
8
|
-
- ICMP ping with statistics — Rust binary (surge-ping)
|
|
9
|
-
- Port availability checks (local and remote) — Rust binary (tokio TCP / socket2)
|
|
10
|
-
- Network gateway discovery — Rust binary (parses /proc/net/route on Linux)
|
|
11
|
-
- Public IP retrieval — pure TS
|
|
12
|
-
- DNS resolution via smartdns — pure TS
|
|
13
|
-
- HTTP endpoint health checks — pure TS
|
|
14
|
-
- Traceroute — Rust binary (raw ICMP sockets via socket2)
|
|
15
|
-
|
|
16
|
-
## Rust Binary Architecture (v4.5.0+)
|
|
17
|
-
|
|
18
|
-
### Binary: `rustnetwork`
|
|
19
|
-
- Located in `rust/crates/rustnetwork/`
|
|
20
|
-
- Cross-compiled for linux_amd64 and linux_arm64 via `@git.zone/tsrust`
|
|
21
|
-
- Output: `dist_rust/rustnetwork_linux_amd64`, `dist_rust/rustnetwork_linux_arm64`
|
|
22
|
-
- IPC mode: `--management` flag for JSON-over-stdin/stdout
|
|
23
|
-
|
|
24
|
-
### IPC Commands
|
|
25
|
-
| Method | Description |
|
|
26
|
-
|--------|-------------|
|
|
27
|
-
| `healthPing` | Liveness check (returns `{ pong: true }`) |
|
|
28
|
-
| `ping` | ICMP ping with count/timeout, returns stats |
|
|
29
|
-
| `traceroute` | Hop-by-hop latency via raw ICMP sockets |
|
|
30
|
-
| `tcpPortCheck` | TCP connect probe to remote host:port |
|
|
31
|
-
| `isLocalPortFree` | Bind test on IPv4 + IPv6 |
|
|
32
|
-
| `defaultGateway` | Parse /proc/net/route for default interface |
|
|
33
|
-
|
|
34
|
-
### TypeScript Bridge
|
|
35
|
-
- `ts/smartnetwork.classes.rustbridge.ts` — Singleton `RustNetworkBridge` wrapping `smartrust.RustBridge`
|
|
36
|
-
- `SmartNetwork` class has `start()`/`stop()` lifecycle for the bridge
|
|
37
|
-
- `ensureBridge()` auto-starts on first use
|
|
38
|
-
|
|
39
|
-
### Build Pipeline
|
|
40
|
-
- `pnpm build` = `tsbuild tsfolders && tsrust`
|
|
41
|
-
- Targets configured in `.smartconfig.json` under `@git.zone/tsrust`
|
|
42
|
-
|
|
43
|
-
## Key Components
|
|
44
|
-
|
|
45
|
-
### SmartNetwork Class
|
|
46
|
-
- Central orchestrator for all network operations
|
|
47
|
-
- Requires `start()` before using ping/traceroute/port/gateway operations
|
|
48
|
-
- Supports caching via `cacheTtl` option for gateway and public IP lookups
|
|
49
|
-
- Plugin architecture for extensibility
|
|
50
|
-
|
|
51
|
-
### RustNetworkBridge Class
|
|
52
|
-
- Singleton pattern via `getInstance()`
|
|
53
|
-
- Binary search: dist_rust/ (platform-suffixed) > rust/target/release/ > rust/target/debug/
|
|
54
|
-
- `searchSystemPath: false` — only looks in local paths
|
|
55
|
-
|
|
56
|
-
### CloudflareSpeed Class
|
|
57
|
-
- Handles internet speed testing using Cloudflare's infrastructure
|
|
58
|
-
- Supports parallel streams and customizable test duration
|
|
59
|
-
- Upload speed measurement uses server-timing header with client-side timing fallback
|
|
60
|
-
|
|
61
|
-
### Error Handling
|
|
62
|
-
- Custom `NetworkError` and `TimeoutError` classes for better error context
|
|
63
|
-
- Error codes follow Node.js conventions (ENOTSUP, EINVAL, ETIMEOUT)
|
|
64
|
-
- Ping/port methods gracefully degrade on errors (return alive=false / false)
|
|
65
|
-
|
|
66
|
-
### Logging
|
|
67
|
-
- Global logger interface for consistent logging across the codebase
|
|
68
|
-
- Replaceable logger implementation (defaults to console)
|
|
69
|
-
|
|
70
|
-
### Statistics Helpers
|
|
71
|
-
- Utility functions for statistical calculations (average, median, quartile, jitter)
|
|
72
|
-
- Used by speed testing; ping statistics now computed server-side in Rust
|
|
73
|
-
|
|
74
|
-
## Dependencies
|
|
75
|
-
- **Runtime**: `@push.rocks/smartrust` (IPC bridge), `@push.rocks/smartdns` (DNS resolution)
|
|
76
|
-
- **Removed in v4.5.0**: `@push.rocks/smartping`, `@push.rocks/smartstring`, `isopen`, `systeminformation`
|
|
77
|
-
- **Dev**: `@git.zone/tsrust` (Rust cross-compilation)
|
|
78
|
-
|
|
79
|
-
## Testing
|
|
80
|
-
- `test/test.ts` — Core smoke tests (speed, ports, gateways, public IPs)
|
|
81
|
-
- `test/test.ping.ts` — ICMP ping tests (requires CAP_NET_RAW for alive=true)
|
|
82
|
-
- `test/test.ports.ts` — Comprehensive port testing (27 tests)
|
|
83
|
-
- `test/test.features.ts` — DNS, HTTP health check, traceroute, multi-ping, plugins, caching
|
|
84
|
-
- All tests require `start()`/`stop()` lifecycle for the Rust bridge
|
|
85
|
-
|
|
86
|
-
## Technical Details
|
|
87
|
-
- ESM-only package (module type)
|
|
88
|
-
- TypeScript with strict typing
|
|
89
|
-
- Node built-in imports use `node:` prefix throughout
|
|
90
|
-
- Uses native Node.js modules for HTTP/HTTPS and os.networkInterfaces()
|
|
91
|
-
- Rust binary requires no elevated privileges for port checks; ICMP ping needs CAP_NET_RAW or appropriate ping_group_range
|
|
92
|
-
|
|
93
|
-
## Design Patterns
|
|
94
|
-
- Singleton pattern for RustNetworkBridge
|
|
95
|
-
- Factory pattern for plugin registration
|
|
96
|
-
- Caching pattern with TTL for expensive operations
|
|
97
|
-
- Promise-based async/await throughout
|
|
98
|
-
- Error propagation with custom error types
|
|
99
|
-
- Graceful degradation when ICMP permissions unavailable
|
|
100
|
-
|
|
101
|
-
## Static Rust Binaries
|
|
102
|
-
|
|
103
|
-
- The Rust binaries in `dist_rust/` are statically linked (static-pie) via `"static": true` in the `@git.zone/tsrust` block of `.smartconfig.json` (tsrust >= 1.4.1). They run on both glibc (Debian/Ubuntu) and musl (Alpine) systems.
|
|
104
|
-
- tsrust injects `RUSTFLAGS="-C target-feature=+crt-static"` only into its own per-target cargo invocations and verifies the result (no `PT_INTERP` ELF header). Keep `rust/.cargo/config.toml` free of `rustflags` entries: the injected env variable would replace them, and a repo-wide `rustflags` would also break plain `cargo test`/`cargo check` (proc-macros cannot build with `+crt-static` on linux-gnu without an explicit `--target`).
|
|
105
|
-
- `pnpm run test:rust` runs the Rust unit tests; plain `cargo test` in `rust/` works as well.
|
|
106
|
-
- Verify linkage manually with `ldd dist_rust/<binary>_linux_amd64` → "statically linked".
|
package/ts/tsconfig.json
DELETED
/package/{license → license.md}
RENAMED
|
File without changes
|