master ohmu/fastfetch-openbsd / src / detection / localip / localip_windows.c
ohmu  ·  2026-10-15
  1#include <ws2tcpip.h>
  2#include <iphlpapi.h>
  3
  4#include "common/netif.h"
  5#include "common/windows/unicode.h"
  6#include "common/debug.h"
  7#include "localip.h"
  8
  9static inline void appendFlag(FFstrbuf* flags, bool enabled, const char* name) {
 10    if (!enabled) {
 11        return;
 12    }
 13
 14    if (flags->length) {
 15        ffStrbufAppendC(flags, ',');
 16    }
 17    ffStrbufAppendS(flags, name);
 18}
 19
 20static void fillInterfaceFlags(FFstrbuf* flags, const MIB_IF_ROW2* row) {
 21    appendFlag(flags, row->InterfaceAndOperStatusFlags.HardwareInterface, "HARDWARE");
 22    appendFlag(flags, row->InterfaceAndOperStatusFlags.FilterInterface, "FILTER");
 23    appendFlag(flags, row->InterfaceAndOperStatusFlags.ConnectorPresent, "CONNECTOR_PRESENT");
 24    appendFlag(flags, row->InterfaceAndOperStatusFlags.NotAuthenticated, "NOT_AUTHENTICATED");
 25    appendFlag(flags, row->InterfaceAndOperStatusFlags.NotMediaConnected, "NO_MEDIA");
 26    appendFlag(flags, row->InterfaceAndOperStatusFlags.Paused, "PAUSED");
 27    appendFlag(flags, row->InterfaceAndOperStatusFlags.LowPower, "LOW_POWER");
 28    appendFlag(flags, row->InterfaceAndOperStatusFlags.EndPointInterface, "ENDPOINT");
 29
 30    switch (row->DirectionType) {
 31    case NET_IF_DIRECTION_SENDONLY:
 32        appendFlag(flags, true, "SEND_ONLY");
 33        break;
 34    case NET_IF_DIRECTION_RECEIVEONLY:
 35        appendFlag(flags, true, "RECEIVE_ONLY");
 36        break;
 37    case NET_IF_DIRECTION_SENDRECEIVE:
 38        appendFlag(flags, true, "SEND_RECEIVE");
 39        break;
 40    default:
 41        break;
 42    }
 43
 44    // Exactly one value of each of these enums is reported per interface, so every value gets
 45    // a flag of its own
 46#define FF_LOCALIP_FLAG_CASE(value, name) \
 47    case value: appendFlag(flags, true, name); break;
 48
 49    switch (row->MediaConnectState) {
 50        FF_LOCALIP_FLAG_CASE(MediaConnectStateUnknown, "MEDIA_UNKNOWN")
 51        FF_LOCALIP_FLAG_CASE(MediaConnectStateConnected, "MEDIA_CONNECTED")
 52        FF_LOCALIP_FLAG_CASE(MediaConnectStateDisconnected, "MEDIA_DISCONNECTED")
 53    }
 54
 55    // The interface type, as registered with IANA and listed in ipifcons.h
 56    switch (row->Type) {
 57        FF_LOCALIP_FLAG_CASE(IF_TYPE_OTHER, "OTHER")
 58        FF_LOCALIP_FLAG_CASE(IF_TYPE_REGULAR_1822, "REGULAR_1822")
 59        FF_LOCALIP_FLAG_CASE(IF_TYPE_HDH_1822, "HDH_1822")
 60        FF_LOCALIP_FLAG_CASE(IF_TYPE_DDN_X25, "DDN_X25")
 61        FF_LOCALIP_FLAG_CASE(IF_TYPE_RFC877_X25, "RFC877_X25")
 62        FF_LOCALIP_FLAG_CASE(IF_TYPE_ETHERNET_CSMACD, "ETHERNET_CSMACD")
 63        FF_LOCALIP_FLAG_CASE(IF_TYPE_IS088023_CSMACD, "IS088023_CSMACD")
 64        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88024_TOKENBUS, "ISO88024_TOKENBUS")
 65        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_TOKENRING, "ISO88025_TOKENRING")
 66        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88026_MAN, "ISO88026_MAN")
 67        FF_LOCALIP_FLAG_CASE(IF_TYPE_STARLAN, "STARLAN")
 68        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROTEON_10MBIT, "PROTEON_10MBIT")
 69        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROTEON_80MBIT, "PROTEON_80MBIT")
 70        FF_LOCALIP_FLAG_CASE(IF_TYPE_HYPERCHANNEL, "HYPERCHANNEL")
 71        FF_LOCALIP_FLAG_CASE(IF_TYPE_FDDI, "FDDI")
 72        FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_B, "LAP_B")
 73        FF_LOCALIP_FLAG_CASE(IF_TYPE_SDLC, "SDLC")
 74        FF_LOCALIP_FLAG_CASE(IF_TYPE_DS1, "DS1")
 75        FF_LOCALIP_FLAG_CASE(IF_TYPE_E1, "E1")
 76        FF_LOCALIP_FLAG_CASE(IF_TYPE_BASIC_ISDN, "BASIC_ISDN")
 77        FF_LOCALIP_FLAG_CASE(IF_TYPE_PRIMARY_ISDN, "PRIMARY_ISDN")
 78        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_POINT2POINT_SERIAL, "PROP_POINT2POINT_SERIAL")
 79        FF_LOCALIP_FLAG_CASE(IF_TYPE_PPP, "PPP")
 80        FF_LOCALIP_FLAG_CASE(IF_TYPE_SOFTWARE_LOOPBACK, "SOFTWARE_LOOPBACK")
 81        FF_LOCALIP_FLAG_CASE(IF_TYPE_EON, "EON")
 82        FF_LOCALIP_FLAG_CASE(IF_TYPE_ETHERNET_3MBIT, "ETHERNET_3MBIT")
 83        FF_LOCALIP_FLAG_CASE(IF_TYPE_NSIP, "NSIP")
 84        FF_LOCALIP_FLAG_CASE(IF_TYPE_SLIP, "SLIP")
 85        FF_LOCALIP_FLAG_CASE(IF_TYPE_ULTRA, "ULTRA")
 86        FF_LOCALIP_FLAG_CASE(IF_TYPE_DS3, "DS3")
 87        FF_LOCALIP_FLAG_CASE(IF_TYPE_SIP, "SIP")
 88        FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY, "FRAMERELAY")
 89        FF_LOCALIP_FLAG_CASE(IF_TYPE_RS232, "RS232")
 90        FF_LOCALIP_FLAG_CASE(IF_TYPE_PARA, "PARA")
 91        FF_LOCALIP_FLAG_CASE(IF_TYPE_ARCNET, "ARCNET")
 92        FF_LOCALIP_FLAG_CASE(IF_TYPE_ARCNET_PLUS, "ARCNET_PLUS")
 93        FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM, "ATM")
 94        FF_LOCALIP_FLAG_CASE(IF_TYPE_MIO_X25, "MIO_X25")
 95        FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET, "SONET")
 96        FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_PLE, "X25_PLE")
 97        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88022_LLC, "ISO88022_LLC")
 98        FF_LOCALIP_FLAG_CASE(IF_TYPE_LOCALTALK, "LOCALTALK")
 99        FF_LOCALIP_FLAG_CASE(IF_TYPE_SMDS_DXI, "SMDS_DXI")
100        FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_SERVICE, "FRAMERELAY_SERVICE")
101        FF_LOCALIP_FLAG_CASE(IF_TYPE_V35, "V35")
102        FF_LOCALIP_FLAG_CASE(IF_TYPE_HSSI, "HSSI")
103        FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPPI, "HIPPI")
104        FF_LOCALIP_FLAG_CASE(IF_TYPE_MODEM, "MODEM")
105        FF_LOCALIP_FLAG_CASE(IF_TYPE_AAL5, "AAL5")
106        FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_PATH, "SONET_PATH")
107        FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_VT, "SONET_VT")
108        FF_LOCALIP_FLAG_CASE(IF_TYPE_SMDS_ICIP, "SMDS_ICIP")
109        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_VIRTUAL, "PROP_VIRTUAL")
110        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_MULTIPLEXOR, "PROP_MULTIPLEXOR")
111        FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80212, "IEEE80212")
112        FF_LOCALIP_FLAG_CASE(IF_TYPE_FIBRECHANNEL, "FIBRECHANNEL")
113        FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPPIINTERFACE, "HIPPIINTERFACE")
114        FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_INTERCONNECT, "FRAMERELAY_INTERCONNECT")
115        FF_LOCALIP_FLAG_CASE(IF_TYPE_AFLANE_8023, "AFLANE_8023")
116        FF_LOCALIP_FLAG_CASE(IF_TYPE_AFLANE_8025, "AFLANE_8025")
117        FF_LOCALIP_FLAG_CASE(IF_TYPE_CCTEMUL, "CCTEMUL")
118        FF_LOCALIP_FLAG_CASE(IF_TYPE_FASTETHER, "FASTETHER")
119        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN, "ISDN")
120        FF_LOCALIP_FLAG_CASE(IF_TYPE_V11, "V11")
121        FF_LOCALIP_FLAG_CASE(IF_TYPE_V36, "V36")
122        FF_LOCALIP_FLAG_CASE(IF_TYPE_G703_64K, "G703_64K")
123        FF_LOCALIP_FLAG_CASE(IF_TYPE_G703_2MB, "G703_2MB")
124        FF_LOCALIP_FLAG_CASE(IF_TYPE_QLLC, "QLLC")
125        FF_LOCALIP_FLAG_CASE(IF_TYPE_FASTETHER_FX, "FASTETHER_FX")
126        FF_LOCALIP_FLAG_CASE(IF_TYPE_CHANNEL, "CHANNEL")
127        FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80211, "IEEE80211")
128        FF_LOCALIP_FLAG_CASE(IF_TYPE_IBM370PARCHAN, "IBM370PARCHAN")
129        FF_LOCALIP_FLAG_CASE(IF_TYPE_ESCON, "ESCON")
130        FF_LOCALIP_FLAG_CASE(IF_TYPE_DLSW, "DLSW")
131        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN_S, "ISDN_S")
132        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN_U, "ISDN_U")
133        FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_D, "LAP_D")
134        FF_LOCALIP_FLAG_CASE(IF_TYPE_IPSWITCH, "IPSWITCH")
135        FF_LOCALIP_FLAG_CASE(IF_TYPE_RSRB, "RSRB")
136        FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_LOGICAL, "ATM_LOGICAL")
137        FF_LOCALIP_FLAG_CASE(IF_TYPE_DS0, "DS0")
138        FF_LOCALIP_FLAG_CASE(IF_TYPE_DS0_BUNDLE, "DS0_BUNDLE")
139        FF_LOCALIP_FLAG_CASE(IF_TYPE_BSC, "BSC")
140        FF_LOCALIP_FLAG_CASE(IF_TYPE_ASYNC, "ASYNC")
141        FF_LOCALIP_FLAG_CASE(IF_TYPE_CNR, "CNR")
142        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025R_DTR, "ISO88025R_DTR")
143        FF_LOCALIP_FLAG_CASE(IF_TYPE_EPLRS, "EPLRS")
144        FF_LOCALIP_FLAG_CASE(IF_TYPE_ARAP, "ARAP")
145        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_CNLS, "PROP_CNLS")
146        FF_LOCALIP_FLAG_CASE(IF_TYPE_HOSTPAD, "HOSTPAD")
147        FF_LOCALIP_FLAG_CASE(IF_TYPE_TERMPAD, "TERMPAD")
148        FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_MPI, "FRAMERELAY_MPI")
149        FF_LOCALIP_FLAG_CASE(IF_TYPE_X213, "X213")
150        FF_LOCALIP_FLAG_CASE(IF_TYPE_ADSL, "ADSL")
151        FF_LOCALIP_FLAG_CASE(IF_TYPE_RADSL, "RADSL")
152        FF_LOCALIP_FLAG_CASE(IF_TYPE_SDSL, "SDSL")
153        FF_LOCALIP_FLAG_CASE(IF_TYPE_VDSL, "VDSL")
154        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_CRFPRINT, "ISO88025_CRFPRINT")
155        FF_LOCALIP_FLAG_CASE(IF_TYPE_MYRINET, "MYRINET")
156        FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_EM, "VOICE_EM")
157        FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_FXO, "VOICE_FXO")
158        FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_FXS, "VOICE_FXS")
159        FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_ENCAP, "VOICE_ENCAP")
160        FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_OVERIP, "VOICE_OVERIP")
161        FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_DXI, "ATM_DXI")
162        FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_FUNI, "ATM_FUNI")
163        FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_IMA, "ATM_IMA")
164        FF_LOCALIP_FLAG_CASE(IF_TYPE_PPPMULTILINKBUNDLE, "PPPMULTILINKBUNDLE")
165        FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_CDLC, "IPOVER_CDLC")
166        FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_CLAW, "IPOVER_CLAW")
167        FF_LOCALIP_FLAG_CASE(IF_TYPE_STACKTOSTACK, "STACKTOSTACK")
168        FF_LOCALIP_FLAG_CASE(IF_TYPE_VIRTUALIPADDRESS, "VIRTUALIPADDRESS")
169        FF_LOCALIP_FLAG_CASE(IF_TYPE_MPC, "MPC")
170        FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_ATM, "IPOVER_ATM")
171        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_FIBER, "ISO88025_FIBER")
172        FF_LOCALIP_FLAG_CASE(IF_TYPE_TDLC, "TDLC")
173        FF_LOCALIP_FLAG_CASE(IF_TYPE_GIGABITETHERNET, "GIGABITETHERNET")
174        FF_LOCALIP_FLAG_CASE(IF_TYPE_HDLC, "HDLC")
175        FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_F, "LAP_F")
176        FF_LOCALIP_FLAG_CASE(IF_TYPE_V37, "V37")
177        FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_MLP, "X25_MLP")
178        FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_HUNTGROUP, "X25_HUNTGROUP")
179        FF_LOCALIP_FLAG_CASE(IF_TYPE_TRANSPHDLC, "TRANSPHDLC")
180        FF_LOCALIP_FLAG_CASE(IF_TYPE_INTERLEAVE, "INTERLEAVE")
181        FF_LOCALIP_FLAG_CASE(IF_TYPE_FAST, "FAST")
182        FF_LOCALIP_FLAG_CASE(IF_TYPE_IP, "IP")
183        FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_MACLAYER, "DOCSCABLE_MACLAYER")
184        FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_DOWNSTREAM, "DOCSCABLE_DOWNSTREAM")
185        FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_UPSTREAM, "DOCSCABLE_UPSTREAM")
186        FF_LOCALIP_FLAG_CASE(IF_TYPE_A12MPPSWITCH, "A12MPPSWITCH")
187        FF_LOCALIP_FLAG_CASE(IF_TYPE_TUNNEL, "TUNNEL")
188        FF_LOCALIP_FLAG_CASE(IF_TYPE_COFFEE, "COFFEE")
189        FF_LOCALIP_FLAG_CASE(IF_TYPE_CES, "CES")
190        FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_SUBINTERFACE, "ATM_SUBINTERFACE")
191        FF_LOCALIP_FLAG_CASE(IF_TYPE_L2_VLAN, "L2_VLAN")
192        FF_LOCALIP_FLAG_CASE(IF_TYPE_L3_IPVLAN, "L3_IPVLAN")
193        FF_LOCALIP_FLAG_CASE(IF_TYPE_L3_IPXVLAN, "L3_IPXVLAN")
194        FF_LOCALIP_FLAG_CASE(IF_TYPE_DIGITALPOWERLINE, "DIGITALPOWERLINE")
195        FF_LOCALIP_FLAG_CASE(IF_TYPE_MEDIAMAILOVERIP, "MEDIAMAILOVERIP")
196        FF_LOCALIP_FLAG_CASE(IF_TYPE_DTM, "DTM")
197        FF_LOCALIP_FLAG_CASE(IF_TYPE_DCN, "DCN")
198        FF_LOCALIP_FLAG_CASE(IF_TYPE_IPFORWARD, "IPFORWARD")
199        FF_LOCALIP_FLAG_CASE(IF_TYPE_MSDSL, "MSDSL")
200        FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE1394, "IEEE1394")
201        FF_LOCALIP_FLAG_CASE(IF_TYPE_IF_GSN, "IF_GSN")
202        FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_MACLAYER, "DVBRCC_MACLAYER")
203        FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_DOWNSTREAM, "DVBRCC_DOWNSTREAM")
204        FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_UPSTREAM, "DVBRCC_UPSTREAM")
205        FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_VIRTUAL, "ATM_VIRTUAL")
206        FF_LOCALIP_FLAG_CASE(IF_TYPE_MPLS_TUNNEL, "MPLS_TUNNEL")
207        FF_LOCALIP_FLAG_CASE(IF_TYPE_SRP, "SRP")
208        FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICEOVERATM, "VOICEOVERATM")
209        FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICEOVERFRAMERELAY, "VOICEOVERFRAMERELAY")
210        FF_LOCALIP_FLAG_CASE(IF_TYPE_IDSL, "IDSL")
211        FF_LOCALIP_FLAG_CASE(IF_TYPE_COMPOSITELINK, "COMPOSITELINK")
212        FF_LOCALIP_FLAG_CASE(IF_TYPE_SS7_SIGLINK, "SS7_SIGLINK")
213        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_WIRELESS_P2P, "PROP_WIRELESS_P2P")
214        FF_LOCALIP_FLAG_CASE(IF_TYPE_FR_FORWARD, "FR_FORWARD")
215        FF_LOCALIP_FLAG_CASE(IF_TYPE_RFC1483, "RFC1483")
216        FF_LOCALIP_FLAG_CASE(IF_TYPE_USB, "USB")
217        FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE8023AD_LAG, "IEEE8023AD_LAG")
218        FF_LOCALIP_FLAG_CASE(IF_TYPE_BGP_POLICY_ACCOUNTING, "BGP_POLICY_ACCOUNTING")
219        FF_LOCALIP_FLAG_CASE(IF_TYPE_FRF16_MFR_BUNDLE, "FRF16_MFR_BUNDLE")
220        FF_LOCALIP_FLAG_CASE(IF_TYPE_H323_GATEKEEPER, "H323_GATEKEEPER")
221        FF_LOCALIP_FLAG_CASE(IF_TYPE_H323_PROXY, "H323_PROXY")
222        FF_LOCALIP_FLAG_CASE(IF_TYPE_MPLS, "MPLS")
223        FF_LOCALIP_FLAG_CASE(IF_TYPE_MF_SIGLINK, "MF_SIGLINK")
224        FF_LOCALIP_FLAG_CASE(IF_TYPE_HDSL2, "HDSL2")
225        FF_LOCALIP_FLAG_CASE(IF_TYPE_SHDSL, "SHDSL")
226        FF_LOCALIP_FLAG_CASE(IF_TYPE_DS1_FDL, "DS1_FDL")
227        FF_LOCALIP_FLAG_CASE(IF_TYPE_POS, "POS")
228        FF_LOCALIP_FLAG_CASE(IF_TYPE_DVB_ASI_IN, "DVB_ASI_IN")
229        FF_LOCALIP_FLAG_CASE(IF_TYPE_DVB_ASI_OUT, "DVB_ASI_OUT")
230        FF_LOCALIP_FLAG_CASE(IF_TYPE_PLC, "PLC")
231        FF_LOCALIP_FLAG_CASE(IF_TYPE_NFAS, "NFAS")
232        FF_LOCALIP_FLAG_CASE(IF_TYPE_TR008, "TR008")
233        FF_LOCALIP_FLAG_CASE(IF_TYPE_GR303_RDT, "GR303_RDT")
234        FF_LOCALIP_FLAG_CASE(IF_TYPE_GR303_IDT, "GR303_IDT")
235        FF_LOCALIP_FLAG_CASE(IF_TYPE_ISUP, "ISUP")
236        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_MACLAYER, "PROP_DOCS_WIRELESS_MACLAYER")
237        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_DOWNSTREAM, "PROP_DOCS_WIRELESS_DOWNSTREAM")
238        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_UPSTREAM, "PROP_DOCS_WIRELESS_UPSTREAM")
239        FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPERLAN2, "HIPERLAN2")
240        FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_BWA_P2MP, "PROP_BWA_P2MP")
241        FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_OVERHEAD_CHANNEL, "SONET_OVERHEAD_CHANNEL")
242        FF_LOCALIP_FLAG_CASE(IF_TYPE_DIGITAL_WRAPPER_OVERHEAD_CHANNEL, "DIGITAL_WRAPPER_OVERHEAD_CHANNEL")
243        FF_LOCALIP_FLAG_CASE(IF_TYPE_AAL2, "AAL2")
244        FF_LOCALIP_FLAG_CASE(IF_TYPE_RADIO_MAC, "RADIO_MAC")
245        FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_RADIO, "ATM_RADIO")
246        FF_LOCALIP_FLAG_CASE(IF_TYPE_IMT, "IMT")
247        FF_LOCALIP_FLAG_CASE(IF_TYPE_MVL, "MVL")
248        FF_LOCALIP_FLAG_CASE(IF_TYPE_REACH_DSL, "REACH_DSL")
249        FF_LOCALIP_FLAG_CASE(IF_TYPE_FR_DLCI_ENDPT, "FR_DLCI_ENDPT")
250        FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_VCI_ENDPT, "ATM_VCI_ENDPT")
251        FF_LOCALIP_FLAG_CASE(IF_TYPE_OPTICAL_CHANNEL, "OPTICAL_CHANNEL")
252        FF_LOCALIP_FLAG_CASE(IF_TYPE_OPTICAL_TRANSPORT, "OPTICAL_TRANSPORT")
253        FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80216_WMAN, "IEEE80216_WMAN")
254        FF_LOCALIP_FLAG_CASE(IF_TYPE_WWANPP, "WWANPP")
255        FF_LOCALIP_FLAG_CASE(IF_TYPE_WWANPP2, "WWANPP2")
256        FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE802154, "IEEE802154")
257        FF_LOCALIP_FLAG_CASE(IF_TYPE_XBOX_WIRELESS, "XBOX_WIRELESS")
258    }
259
260#undef FF_LOCALIP_FLAG_CASE
261}
262
263static bool isDefaultRouteInterface(const FFLocalIpOptions* options, NET_IFINDEX ifIndex) {
264    // Both route lookups are cached, so this is cheap even inside the per interface loop
265    return ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == ifIndex) ||
266        ((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == ifIndex);
267}
268
269const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
270    FF_DEBUG("Starting local IP detection with showType=0x%X, namePrefix='%.*s'",
271        options->showType,
272        (int) options->namePrefix.length,
273        options->namePrefix.chars);
274
275    ADDRESS_FAMILY family = options->showType & FF_LOCALIP_TYPE_IPV4_BIT
276        ? options->showType & FF_LOCALIP_TYPE_IPV6_BIT ? AF_UNSPEC : AF_INET
277        : AF_INET6;
278    bool queryV4 = (options->showType & FF_LOCALIP_TYPE_IPV4_BIT) != 0;
279    bool queryV6 = (options->showType & FF_LOCALIP_TYPE_IPV6_BIT) != 0;
280
281    // Every interface is queried with GetIfEntry2() rather than by walking a table of all of them:
282    // GetIfTable2() costs about 0.95 ms on a machine with a few dozen adapters however few of them
283    // are reported, because it marshals every registered one, while GetIfEntry2() costs about 24 us
284    // per interface. With 52 adapters registered and 8 interfaces carrying an address the per
285    // interface API is 3.6 times faster overall; the table only wins past roughly 40 interfaces with
286    // an address, and with FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT (the default) at most one interface
287    // can pass the filter below anyway.
288    bool defaultRouteOnly = (options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) != 0;
289    if (defaultRouteOnly) {
290        if (queryV4) {
291            FF_DEBUG("Querying the IPv4 default route interface only: ifIndex=%u", (unsigned) ffNetifGetDefaultRouteV4()->ifIndex);
292        }
293        if (queryV6) {
294            FF_DEBUG("Querying the IPv6 default route interface only: ifIndex=%u", (unsigned) ffNetifGetDefaultRouteV6()->ifIndex);
295        }
296    }
297
298    PMIB_UNICASTIPADDRESS_TABLE table = nullptr;
299    const char* errorMessage = nullptr;
300
301    // The unicast address table is the only API that reports per interface addresses and it cannot
302    // be filtered by interface, so it is queried once and only the rows of the interfaces which pass
303    // the filter below are read
304    if (!NETIO_SUCCESS(GetUnicastIpAddressTable(family, &table))) {
305        FF_DEBUG("GetUnicastIpAddressTable failed");
306        errorMessage = "GetUnicastIpAddressTable() failed";
307        goto cleanup;
308    }
309
310    [[maybe_unused]] int adapterCount = 0, processedCount = 0;
311
312    // Rows of the same interface are not adjacent (IPv4 rows come first),
313    // so each interface is handled at its first row
314    for (ULONG i = 0; i < table->NumEntries; ++i) {
315        NET_IFINDEX ifIndex = table->Table[i].InterfaceIndex;
316
317        bool seen = false;
318        for (ULONG j = 0; j < i && !seen; ++j) {
319            seen = table->Table[j].InterfaceIndex == ifIndex;
320        }
321        if (seen) {
322            continue;
323        }
324
325        adapterCount++;
326
327        // Checked first to avoid querying interfaces we won't show
328        if (defaultRouteOnly && !isDefaultRouteInterface(options, ifIndex)) {
329            FF_DEBUG("Skipping interface %u (not default route interface)", (unsigned) ifIndex);
330            continue;
331        }
332
333        MIB_IF_ROW2 ifRow = { .InterfaceIndex = ifIndex };
334        if (!NETIO_SUCCESS(GetIfEntry2(&ifRow))) {
335            FF_DEBUG("GetIfEntry2(%u) failed", (unsigned) ifIndex);
336            continue;
337        }
338
339        FF_DEBUG("Processing adapter %d: IfIndex=%u, IfType=%u, OperStatus=%u",
340            adapterCount,
341            (unsigned) ifIndex,
342            (unsigned) ifRow.Type,
343            (unsigned) ifRow.OperStatus);
344
345        if (ifRow.OperStatus != IfOperStatusUp) {
346            FF_DEBUG("Skipping adapter %u (not operational, status=%d)", (unsigned) ifIndex, ifRow.OperStatus);
347            continue;
348        }
349
350        bool isLoop = ifRow.Type == IF_TYPE_SOFTWARE_LOOPBACK;
351        FF_DEBUG("Adapter %u: isLoopback=%s", (unsigned) ifIndex, isLoop ? "true" : "false");
352
353        if (isLoop && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) {
354            FF_DEBUG("Skipping loopback adapter %u (loopback not requested)", (unsigned) ifIndex);
355            continue;
356        }
357
358        FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(ifRow.Alias);
359        FF_DEBUG("Adapter %u name: '%s'", (unsigned) ifIndex, name.chars);
360
361        if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) {
362            FF_DEBUG("Skipping adapter %u (name doesn't match prefix '%.*s')",
363                (unsigned) ifIndex,
364                (int) options->namePrefix.length,
365                options->namePrefix.chars);
366            continue;
367        }
368
369        processedCount++;
370        FF_DEBUG("Creating result item for adapter %u ('%s')", (unsigned) ifIndex, name.chars);
371
372        FFLocalIpResult* item = FF_LIST_ADD(FFLocalIpResult, *results);
373        ffStrbufInitMove(&item->name, &name);
374        ffStrbufInit(&item->ipv4);
375        ffStrbufInit(&item->ipv6);
376        ffStrbufInit(&item->mac);
377        ffStrbufInit(&item->flags);
378        item->defaultRoute = FF_LOCALIP_TYPE_NONE;
379        item->speed = -1;
380        item->mtu = -1;
381
382        if (options->showType & FF_LOCALIP_TYPE_FLAGS_BIT) {
383            fillInterfaceFlags(&item->flags, &ifRow);
384            FF_DEBUG("Adapter %u flags: '%s'", (unsigned) ifIndex, item->flags.chars);
385        }
386
387        uint32_t typesToAdd = options->showType & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT);
388        FF_DEBUG("Types to add for adapter %u: 0x%X", (unsigned) ifIndex, typesToAdd);
389
390        [[maybe_unused]] int ipv4Count = 0, ipv6Count = 0;
391
392        for (ULONG j = i; j < table->NumEntries; ++j) {
393            MIB_UNICASTIPADDRESS_ROW* ifa = &table->Table[j];
394            if (ifa->InterfaceIndex != ifIndex) {
395                continue;
396            }
397
398            FF_DEBUG("Processing unicast address: prefix origin=%d, suffix origin=%d, family=%d, DadState=%d",
399                ifa->PrefixOrigin,
400                ifa->SuffixOrigin,
401                ifa->Address.si_family,
402                ifa->DadState);
403
404            if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT)) {
405                if (ifa->DadState != IpDadStatePreferred) {
406                    FF_DEBUG("Skipping address (not preferred)");
407                    continue;
408                }
409
410                if (ifa->SuffixOrigin == IpSuffixOriginRandom) {
411                    FF_DEBUG("Skipping temporary address (random suffix)");
412                    continue;
413                }
414
415                // MIB_UNICASTIPADDRESS_ROW::SkipAsSource
416            }
417
418            if (ifa->Address.si_family == AF_INET) {
419                if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) {
420                    FF_DEBUG("Skipping IPv4 address (not requested in typesToAdd=0x%X)", typesToAdd);
421                    continue;
422                }
423
424                bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == ifIndex);
425                if (defaultRouteOnly && !isDefaultRoute) {
426                    FF_DEBUG("Skipping IPv4 address (not on default route interface)");
427                    continue;
428                }
429
430                SOCKADDR_IN* ipv4 = &ifa->Address.Ipv4;
431                char addressBuffer[INET_ADDRSTRLEN + 10];
432                char* end = RtlIpv4AddressToStringA(&ipv4->sin_addr, addressBuffer);
433
434                if ((options->showType & FF_LOCALIP_TYPE_PREFIX_LEN_BIT) && ifa->OnLinkPrefixLength) {
435                    end += snprintf(end, 10, "/%u", (unsigned) ifa->OnLinkPrefixLength);
436                }
437
438                FF_DEBUG("Adding IPv4 address: %s (isDefaultRoute=%s)", addressBuffer, isDefaultRoute ? "true" : "false");
439
440                if (item->ipv4.length) {
441                    ffStrbufAppendC(&item->ipv4, ',');
442                }
443                ffStrbufAppendNS(&item->ipv4, (uint32_t) (end - addressBuffer), addressBuffer);
444                if (isDefaultRoute) {
445                    item->defaultRoute |= FF_LOCALIP_TYPE_IPV4_BIT;
446                }
447
448                ipv4Count++;
449                typesToAdd &= ~(unsigned) FF_LOCALIP_TYPE_IPV4_BIT;
450                if (typesToAdd == 0) {
451                    break;
452                }
453            } else if (ifa->Address.si_family == AF_INET6) {
454                if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) {
455                    FF_DEBUG("Skipping IPv6 address (not requested in typesToAdd=0x%X)", typesToAdd);
456                    continue;
457                }
458
459                SOCKADDR_IN6* ipv6 = &ifa->Address.Ipv6;
460
461                FFLocalIpIpv6Type ipv6Type = FF_LOCALIP_IPV6_TYPE_NONE;
462                if (IN6_IS_ADDR_GLOBAL(&ipv6->sin6_addr)) {
463                    ipv6Type |= FF_LOCALIP_IPV6_TYPE_GUA_BIT;
464                } else if (IN6_IS_ADDR_UNIQUE_LOCAL(&ipv6->sin6_addr)) {
465                    ipv6Type |= FF_LOCALIP_IPV6_TYPE_ULA_BIT;
466                } else if (IN6_IS_ADDR_LINKLOCAL(&ipv6->sin6_addr)) {
467                    ipv6Type |= FF_LOCALIP_IPV6_TYPE_LLA_BIT;
468                } else {
469                    ipv6Type |= FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT;
470                }
471
472                if (!(options->ipv6Type & ipv6Type)) {
473                    FF_DEBUG("Skipping IPv6 address (doesn't match requested type 0x%X)", options->ipv6Type);
474                    continue;
475                }
476
477                bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == ifIndex);
478                if (defaultRouteOnly && !isDefaultRoute) {
479                    FF_DEBUG("Skipping IPv6 address (not on default route interface)");
480                    continue;
481                }
482
483                char addressBuffer[INET6_ADDRSTRLEN + 10];
484                char* end = RtlIpv6AddressToStringA(&ipv6->sin6_addr, addressBuffer);
485
486                if ((options->showType & FF_LOCALIP_TYPE_PREFIX_LEN_BIT) && ifa->OnLinkPrefixLength) {
487                    end += snprintf(end, 10, "/%u", (unsigned) ifa->OnLinkPrefixLength);
488                }
489
490                FF_DEBUG("Adding IPv6 address: %s (isDefaultRoute=%s)", addressBuffer, isDefaultRoute ? "true" : "false");
491
492                if (item->ipv6.length) {
493                    ffStrbufAppendC(&item->ipv6, ',');
494                }
495                ffStrbufAppendNS(&item->ipv6, (uint32_t) (end - addressBuffer), addressBuffer);
496                if (isDefaultRoute) {
497                    item->defaultRoute |= FF_LOCALIP_TYPE_IPV6_BIT;
498                }
499
500                ipv6Count++;
501                typesToAdd &= ~(unsigned) FF_LOCALIP_TYPE_IPV6_BIT;
502                if (typesToAdd == 0) {
503                    break;
504                }
505            }
506        }
507
508        FF_DEBUG("Adapter %u: collected %d IPv4 and %d IPv6 addresses", (unsigned) ifIndex, ipv4Count, ipv6Count);
509
510        if (options->showType & FF_LOCALIP_TYPE_SPEED_BIT) {
511            item->speed = (int32_t) (ifRow.ReceiveLinkSpeed / 1000000);
512            FF_DEBUG("Adapter %u speed: %d Mbps (raw: %llu)", (unsigned) ifIndex, item->speed, ifRow.ReceiveLinkSpeed);
513        }
514        if (options->showType & FF_LOCALIP_TYPE_MTU_BIT) {
515            item->mtu = (int32_t) ifRow.Mtu;
516            FF_DEBUG("Adapter %u MTU: %d", (unsigned) ifIndex, item->mtu);
517        }
518        if (options->showType & FF_LOCALIP_TYPE_MAC_BIT && ifRow.PhysicalAddressLength == 6) {
519            const uint8_t* ptr = ifRow.PhysicalAddress;
520            ffStrbufSetF(&item->mac, "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
521            FF_DEBUG("Adapter %u MAC: %s", (unsigned) ifIndex, item->mac.chars);
522        }
523    }
524
525    FF_DEBUG("Local IP detection completed: scanned %d adapters, processed %d, results count: %u",
526        adapterCount,
527        processedCount,
528        results->length);
529
530cleanup:
531    if (table) {
532        FreeMibTable(table);
533    }
534    return errorMessage;
535}