11import { execCommand } from './utils' ;
2+ import dgram from 'dgram' ;
23import fs from 'fs/promises' ;
34import net from 'net' ;
45import os from 'os' ;
@@ -34,19 +35,19 @@ export type NICInfo = {
3435
3536 /** If the address is internal (e.g. loopback). */
3637 internal : boolean ;
37- } [ ] ;
3838
39- /** Status information about the network interface. */
40- status : {
41- /** Operational status of the interface (e.g. up, down). */
42- operational : 'up' | 'down' | 'dormant' | 'notpresent' | 'lowerlayerdown' | 'testing' | 'unknown' ;
39+ /** If the address is publicly reachable. */
40+ public : boolean ;
4341
44- /** If the interface is enabled in the settings. */
45- admin : boolean ;
42+ /** If the address is the primary address for the system. */
43+ primary : boolean ;
44+ } [ ] ;
4645
47- /** If a network cable is plugged into the interface. */
48- cable : boolean ;
49- } ;
46+ /**
47+ * Checks if the network interface is up, has a speed is defined,
48+ * and is either physical or has the primary address.
49+ */
50+ primary : boolean ;
5051
5152 /** If the interface is a physical (e.g. Ethernet) or virtual (e.g. Loopback, VPN, Hyper-V) interface. */
5253 physical : boolean ;
@@ -66,6 +67,18 @@ export type NICInfo = {
6667 bytes : number ;
6768 } ;
6869
70+ /** Status information about the network interface. */
71+ status : {
72+ /** Operational status of the interface (e.g. up, down). */
73+ operational : 'up' | 'down' | 'dormant' | 'notpresent' | 'lowerlayerdown' | 'testing' | 'unknown' ;
74+
75+ /** If the interface is enabled in the settings. */
76+ admin : boolean ;
77+
78+ /** If a network cable is plugged into the interface. */
79+ cable : boolean ;
80+ } ;
81+
6982 /**
7083 * Current link utilization in percentage (0.0-1.0) of the maximum bandwidth (speed).
7184 *
@@ -168,7 +181,6 @@ export function stopNetworkUtilizationComputation() {
168181 NET_COMPUTE_RUNNING = false ;
169182}
170183
171-
172184/**
173185 * Tries to connect to a given server over TCP.
174186 *
@@ -188,6 +200,32 @@ export async function canConnect(port: number, host: string = '127.0.0.1'): Prom
188200 } ) ;
189201}
190202
203+ /**
204+ * Returns the primary IP address that is configured and
205+ * which is most likely to also be the public IP.
206+ *
207+ * @returns Primary IP address.
208+ */
209+ export async function getPrimaryIp ( ) : Promise < string > {
210+ return new Promise ( ( resolve , reject ) => {
211+ const socket = dgram . createSocket ( 'udp4' ) ;
212+ // Destination doesn't need to be reachable – just needs routing
213+ socket . connect ( 80 , '8.8.8.8' , ( ) => {
214+ try {
215+ const address = socket . address ( ) ;
216+ socket . close ( ) ;
217+ if ( typeof address === 'string' ) {
218+ reject ( new Error ( 'Unexpected address format' ) ) ;
219+ } else {
220+ resolve ( address . address ) ; // local IP used for routing
221+ }
222+ } catch ( err ) {
223+ reject ( err ) ;
224+ }
225+ } ) ;
226+ } ) ;
227+ }
228+
191229/**
192230 * Checks if a process is listening on a given port.
193231 *
@@ -204,7 +242,7 @@ export async function isPortListendedOn(port: number, bindAddress: string = '0.0
204242 server . unref ( ) ;
205243 server . once ( 'error' , ( err ) => {
206244 server . close ( ) ;
207- if ( ( err as any ) . code === 'EADDRINUSE' ) {
245+ if ( ( err as any ) . code === 'EADDRINUSE' ) {
208246 resolve ( true ) ;
209247 } else {
210248 resolve ( false ) ;
@@ -217,7 +255,6 @@ export async function isPortListendedOn(port: number, bindAddress: string = '0.0
217255 } ) ;
218256}
219257
220-
221258/**
222259 * Uses isPortListenedOn() and canConnect() to determine if a port is in use.
223260 * @param port Port number to check.
@@ -226,15 +263,49 @@ export async function isPortListendedOn(port: number, bindAddress: string = '0.0
226263export async function isPortInUse ( port : number , bindAddress : string = '0.0.0.0' ) : Promise < boolean > {
227264 // check first if port can be listened on (way faster if port is really used because just a kernel check needed).
228265 const isListenedOn = await isPortListendedOn ( port , bindAddress ) ;
229- if ( isListenedOn ) return true ;
266+ if ( isListenedOn ) return true ;
230267
231268 // as backup check if can connect
232269 const canConn = await canConnect ( port , bindAddress ) ;
233- if ( canConn ) return true ;
270+ if ( canConn ) return true ;
271+
272+ return false ;
273+ }
274+
275+ /**
276+ * Checks if the given IPv4 or IPv6 address is a public IP address.
277+ * @param ip The IP address to check.
278+ * @returns True if the IP address is public, false otherwise.
279+ */
280+ export function isPublicIp ( ip : string ) : boolean {
281+ if ( ! ip ) return false ;
282+
283+ const ipv4Parts = ip . split ( '.' ) ;
284+ if ( ipv4Parts . length === 4 ) {
285+ const [ a , b ] = ipv4Parts . map ( Number ) ;
286+ // Check for private IP ranges
287+ return ! ( a === 10 || a === 127 || ( a === 172 && b >= 16 && b <= 31 ) || ( a === 192 && b === 168 ) ) ;
288+ }
289+
290+ const ipv6Parts = ip . split ( ':' ) ;
291+ if ( ipv6Parts . length > 2 ) {
292+ const [ first , second ] = ipv6Parts ;
293+ // Check for unique IPv6 ranges
294+ return ! (
295+ first === '::1' || // Loopback
296+ first === 'fc00' ||
297+ first === 'fd00' || // Unique Local Addresses
298+ ( first === 'fe80' && second === '::' ) // Link-Local
299+ ) ;
300+ }
234301
235302 return false ;
236303}
237304
305+ export async function getRealNetworkInterfaces ( ) : Promise < NICInfo [ ] > {
306+ const allNics = await getNetworkInterfaces ( ) ;
307+ return allNics . filter ( ( nic ) => true ) ;
308+ }
238309
239310/**
240311 * Returns information about the network interfaces on the system.
@@ -246,6 +317,7 @@ export async function getNetworkInterfaces(): Promise<NICInfo[]> {
246317 await runNetComputeInterval ( ) ; // runs the first computation immediately
247318 await computeNetworkUtilization ( ) ; // run second computation immediately to get initial values
248319 }
320+ const primaryIp = await getPrimaryIp ( ) ;
249321
250322 const nics : { [ name : string ] : NICInfo } = { } ;
251323 for ( const [ name , addresses ] of Object . entries ( os . networkInterfaces ( ) ) ) {
@@ -259,7 +331,10 @@ export async function getNetworkInterfaces(): Promise<NICInfo[]> {
259331 netmask : addr . netmask ,
260332 cidr : addr . cidr ,
261333 internal : addr . internal ,
334+ public : isPublicIp ( addr . address ) ,
335+ primary : addr . address === primaryIp ,
262336 } ) ) || [ ] ,
337+ primary : false ,
263338 status : {
264339 operational : 'unknown' ,
265340 admin : true , // Default to true, will be updated based on platform
@@ -328,6 +403,7 @@ export async function getNetworkInterfaces(): Promise<NICInfo[]> {
328403 nics [ currNic ] = {
329404 name : currNic ,
330405 addresses : [ ] ,
406+ primary : false ,
331407 status : {
332408 operational : 'unknown' ,
333409 admin : true , // Default to true, will be updated based on platform
@@ -370,5 +446,16 @@ export async function getNetworkInterfaces(): Promise<NICInfo[]> {
370446 break ;
371447 }
372448 }
449+
450+ // post process
451+ for ( const key of Object . keys ( nics ) ) {
452+ nics [ key ] . primary =
453+ nics [ key ] . status . operational === 'up' &&
454+ ( nics [ key ] . physical || nics [ key ] . addresses . find ( ( addr ) => addr . primary ) ) &&
455+ nics [ key ] . speed
456+ ? true
457+ : false ;
458+ }
459+
373460 return Object . values ( nics ) ;
374461}
0 commit comments