@push.rocks/smartnetwork 4.1.2 → 4.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.
@@ -1,5 +1,6 @@
1
1
  import * as plugins from './smartnetwork.plugins.js';
2
2
  import { CloudflareSpeed } from './smartnetwork.classes.cloudflarespeed.js';
3
+ import { PublicIp } from './smartnetwork.classes.publicip.js';
3
4
  import { getLogger } from './logging.js';
4
5
  import { NetworkError } from './errors.js';
5
6
  import * as stats from './helpers/stats.js';
@@ -23,6 +24,15 @@ export interface Hop {
23
24
  ip: string;
24
25
  rtt: number | null;
25
26
  }
27
+
28
+ /**
29
+ * Options for the findFreePort method
30
+ */
31
+ export interface IFindFreePortOptions {
32
+ /** If true, selects a random available port within the range instead of the first one */
33
+ randomize?: boolean;
34
+ }
35
+
26
36
  export class SmartNetwork {
27
37
  /** Static registry for external plugins */
28
38
  public static pluginsRegistry: Map<string, any> = new Map();
@@ -147,9 +157,10 @@ export class SmartNetwork {
147
157
  * Find the first available port within a given range
148
158
  * @param startPort The start of the port range (inclusive)
149
159
  * @param endPort The end of the port range (inclusive)
150
- * @returns The first available port number, or null if no ports are available
160
+ * @param options Optional configuration for port selection behavior
161
+ * @returns The first available port number (or random if options.randomize is true), or null if no ports are available
151
162
  */
152
- public async findFreePort(startPort: number, endPort: number): Promise<number | null> {
163
+ public async findFreePort(startPort: number, endPort: number, options?: IFindFreePortOptions): Promise<number | null> {
153
164
  // Validate port range
154
165
  if (startPort < 1 || startPort > 65535 || endPort < 1 || endPort > 65535) {
155
166
  throw new NetworkError('Port numbers must be between 1 and 65535', 'EINVAL');
@@ -158,16 +169,38 @@ export class SmartNetwork {
158
169
  throw new NetworkError('Start port must be less than or equal to end port', 'EINVAL');
159
170
  }
160
171
 
161
- // Check each port in the range
162
- for (let port = startPort; port <= endPort; port++) {
163
- const isUnused = await this.isLocalPortUnused(port);
164
- if (isUnused) {
165
- return port;
172
+ // If randomize option is true, collect all available ports and select randomly
173
+ if (options?.randomize) {
174
+ const availablePorts: number[] = [];
175
+
176
+ // Scan the range to find all available ports
177
+ for (let port = startPort; port <= endPort; port++) {
178
+ const isUnused = await this.isLocalPortUnused(port);
179
+ if (isUnused) {
180
+ availablePorts.push(port);
181
+ }
182
+ }
183
+
184
+ // If there are available ports, select one randomly
185
+ if (availablePorts.length > 0) {
186
+ const randomIndex = Math.floor(Math.random() * availablePorts.length);
187
+ return availablePorts[randomIndex];
166
188
  }
189
+
190
+ // No free port found in the range
191
+ return null;
192
+ } else {
193
+ // Default behavior: return the first available port (sequential search)
194
+ for (let port = startPort; port <= endPort; port++) {
195
+ const isUnused = await this.isLocalPortUnused(port);
196
+ if (isUnused) {
197
+ return port;
198
+ }
199
+ }
200
+
201
+ // No free port found in the range
202
+ return null;
167
203
  }
168
-
169
- // No free port found in the range
170
- return null;
171
204
  }
172
205
 
173
206
  /**
@@ -259,10 +292,10 @@ export class SmartNetwork {
259
292
  * Lookup public IPv4 and IPv6
260
293
  */
261
294
  public async getPublicIps(): Promise<{ v4: string | null; v6: string | null }> {
262
- const fetcher = async () => ({
263
- v4: await plugins.publicIp.publicIpv4({ timeout: 1000, onlyHttps: true }).catch(() => null),
264
- v6: await plugins.publicIp.publicIpv6({ timeout: 1000, onlyHttps: true }).catch(() => null),
265
- });
295
+ const fetcher = async () => {
296
+ const publicIp = new PublicIp({ timeout: 1000 });
297
+ return publicIp.getPublicIps();
298
+ };
266
299
  if (this.options.cacheTtl && this.options.cacheTtl > 0) {
267
300
  return this.getCached('publicIps', fetcher);
268
301
  }
@@ -276,13 +309,30 @@ export class SmartNetwork {
276
309
  host: string,
277
310
  ): Promise<{ A: string[]; AAAA: string[]; MX: { exchange: string; priority: number }[] }> {
278
311
  try {
279
- const dns = await import('dns');
280
- const { resolve4, resolve6, resolveMx } = dns.promises;
281
- const [A, AAAA, MX] = await Promise.all([
282
- resolve4(host).catch(() => []),
283
- resolve6(host).catch(() => []),
284
- resolveMx(host).catch(() => []),
312
+ const dnsClient = new plugins.smartdns.dnsClientMod.Smartdns({
313
+ strategy: 'prefer-system', // Try system resolver first (handles localhost), fallback to DoH
314
+ allowDohFallback: true,
315
+ });
316
+
317
+ const [aRecords, aaaaRecords, mxRecords] = await Promise.all([
318
+ dnsClient.getRecordsA(host).catch((): any[] => []),
319
+ dnsClient.getRecordsAAAA(host).catch((): any[] => []),
320
+ dnsClient.getRecords(host, 'MX').catch((): any[] => []),
285
321
  ]);
322
+
323
+ // Extract values from the record objects
324
+ const A = aRecords.map((record: any) => record.value);
325
+ const AAAA = aaaaRecords.map((record: any) => record.value);
326
+
327
+ // Parse MX records - the value contains "priority exchange"
328
+ const MX = mxRecords.map((record: any) => {
329
+ const parts = record.value.split(' ');
330
+ return {
331
+ priority: parseInt(parts[0], 10),
332
+ exchange: parts[1] || '',
333
+ };
334
+ });
335
+
286
336
  return { A, AAAA, MX };
287
337
  } catch (err: any) {
288
338
  throw new NetworkError(err.message, err.code);
@@ -6,17 +6,16 @@ import * as perfHooks from 'perf_hooks';
6
6
  export { os, https, perfHooks };
7
7
 
8
8
  // @pushrocks scope
9
+ import * as smartdns from '@push.rocks/smartdns';
9
10
  import * as smartping from '@push.rocks/smartping';
10
11
  import * as smartpromise from '@push.rocks/smartpromise';
11
12
  import * as smartstring from '@push.rocks/smartstring';
12
13
 
13
- export { smartpromise, smartping, smartstring };
14
+ export { smartdns, smartpromise, smartping, smartstring };
14
15
 
15
16
  // @third party scope
16
17
  // @ts-ignore
17
18
  import isopen from 'isopen';
18
- // @ts-ignore
19
- import * as publicIp from 'public-ip';
20
19
  import * as systeminformation from 'systeminformation';
21
20
 
22
- export { isopen, publicIp, systeminformation };
21
+ export { isopen, systeminformation };