Compare commits

...

10 Commits

Author SHA1 Message Date
Carter Li 70d2de4aee Update build-windows-hosts.yml 2026-10-02 23:18:25 +08:00
dependabot[bot] 790b21cda4 CI: Bump the github-actions group with 5 updates
Bumps the github-actions group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [benchmark-action/github-action-benchmark](https://github.com/benchmark-action/github-action-benchmark) | `1.22.1` | `1.22.2` |
| [github/codeql-action/init](https://github.com/github/codeql-action) | `4.38.0` | `4.38.1` |
| [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.38.0` | `4.38.1` |
| [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) | `4.3.0` | `4.4.0` |
| [signpath/github-action-submit-signing-request](https://github.com/signpath/github-action-submit-signing-request) | `2.3` | `3.0` |


Updates `benchmark-action/github-action-benchmark` from 1.22.1 to 1.22.2
- [Release notes](https://github.com/benchmark-action/github-action-benchmark/releases)
- [Changelog](https://github.com/benchmark-action/github-action-benchmark/blob/master/CHANGELOG.md)
- [Commits](https://github.com/benchmark-action/github-action-benchmark/compare/52576c92bccf6ac60c8223ec7eb2565637cae9ba...4322e5726e6334590d251fc4f92bec0efafc45dc)

Updates `github/codeql-action/init` from 4.38.0 to 4.38.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

Updates `github/codeql-action/analyze` from 4.38.0 to 4.38.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

Updates `docker/setup-qemu-action` from 4.3.0 to 4.4.0
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/1f40c72289eff860ee54a304f1438e3cff362e0a...99012661954931238ded8c8b007157a8430204e1)

Updates `signpath/github-action-submit-signing-request` from 2.3 to 3.0
- [Release notes](https://github.com/signpath/github-action-submit-signing-request/releases)
- [Commits](https://github.com/signpath/github-action-submit-signing-request/compare/c92b958760219087e01f8d67a1669ed57afe2627...f6d04783b4569d051e0c80105fe66e82819d0092)

---
updated-dependencies:
- dependency-name: benchmark-action/github-action-benchmark
  dependency-version: 1.22.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: signpath/github-action-submit-signing-request
  dependency-version: '3.0'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-27 18:07:11 +00:00
李通洲 80632920bb Doc: uses the temp domain of repology [ci skip] 2026-09-27 09:04:10 +08:00
李通洲 5246f149eb Doc: updates info about image logo on Windows 2026-09-27 09:03:44 +08:00
Joshua Rogers f00a021e27 Completion (Bash): fixes --config/-c path completion splitting on spaces (#2621) [ci skip]
Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-09-27 09:00:11 +08:00
İhsan Deniz 676dbcfee2 Vulkan: fixes driver name concatenation on multi-GPU systems (#2620)
* Vulkan: fixes driver name concatenation on multi-GPU systems

applyDriverName appended to the result buffer without clearing it. When
a later device reported a higher API version, its driver name was
appended to the name of the earlier device instead of replacing it, as
the comment in detectVulkan intends.

* CHANGELOG: moves the #2615 entry to Unreleased

#2616 was opened after 2.69.0 had been released, but its entry was
written under 2.69.0 Bugfixes, so the changelog listed the fix as part
of 2.69.0.
2026-09-27 08:35:20 +08:00
İhsan Deniz 9801d89316 Brightness (Linux): fixes random built-in flag and stdout leak in DDC/CI detection (#2616)
DDC/CI results never set `builtin`, and FF_LIST_ADD does not zero the new
element, so external monitors were reported as built-in or external at
random. Set it to false, as the BSD DDC/CI backend does.

FF_SUPPRESS_IO() was declared inside the `if (ffddca_init)` block, so its
cleanup restored stdout before display detection ran and libddcutil trace
lines could reach the output, ahead of `--format json` too. Declare it at
function scope so it covers the whole detection, including the
FF_DISABLE_DLOPEN build.

Closes #2615
2026-09-26 18:18:43 +08:00
Carter Li fb52d32452 Packages (Linux): uses getdirent64(2) directly 2026-09-26 14:18:00 +08:00
李通洲 36dc82e926 Bootmgr: cleans code 2026-09-26 14:17:24 +08:00
李通洲 7fdd1d8e93 LocalIP / NetIO / DNS (Windows): improves performance
Fixes #2614
2026-09-26 14:17:24 +08:00
16 changed files with 768 additions and 257 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ jobs:
run: cp .github/benchmark-index.html "$RUNNER_TEMP/benchmark-index.html"
- name: store benchmark result
uses: benchmark-action/github-action-benchmark@52576c92bccf6ac60c8223ec7eb2565637cae9ba # v1.22.1
uses: benchmark-action/github-action-benchmark@4322e5726e6334590d251fc4f92bec0efafc45dc # v1.22.2
with:
name: Fastfetch
tool: customSmallerIsBetter
+2 -2
View File
@@ -52,7 +52,7 @@ jobs:
- name: Initialize CodeQL
if: inputs.arch == 'amd64'
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
with:
languages: c
@@ -64,7 +64,7 @@ jobs:
- name: perform CodeQL analysis
if: inputs.arch == 'amd64'
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
- name: list features
run: ./fastfetch --list-features
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: set up QEMU for loong64
uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
- name: build in loong64 container
run: |
+13
View File
@@ -1,3 +1,16 @@
# Unreleased
Changes:
* LocalIP flags on Windows now describe the interface itself — its operational status, its media connection state and its type, for example `ETHERNET_CSMACD`, `IEEE80211` or `SOFTWARE_LOOPBACK` — instead of the `IP_ADAPTER_*` bits.
Features:
* Improved performance of LocalIP, NetIO and DNS on Windows
Bugfixes:
* Fixed `--config`/`-c` bash completion offering one candidate per word instead of one candidate per file for paths containing spaces. (Completion, Bash)
* Fixed external monitors detected via DDC/CI being randomly reported as built-in, and libddcutil trace messages occasionally leaking into the output and breaking `--format json`. (#2615, Brightness / Linux)
* Fixed the Vulkan driver name being concatenated with the driver name of a previously enumerated device on systems with more than one GPU. (#2619, Vulkan)
# 2.69.0
Changes:
+19 -37
View File
@@ -9,8 +9,8 @@
[![homebrew downloads](https://img.shields.io/homebrew/installs/dm/fastfetch?logo=homebrew)](https://formulae.brew.sh/formula/fastfetch#default)
[![GitHub all releases](https://img.shields.io/github/downloads/fastfetch-cli/fastfetch/total?logo=github)](https://github.com/fastfetch-cli/fastfetch/releases)
[![GitHub release (with filter)](https://img.shields.io/github/v/release/fastfetch-cli/fastfetch?logo=github)](https://github.com/fastfetch-cli/fastfetch/releases)
[![latest packaged version(s)](https://repology.org/badge/latest-versions/fastfetch.svg)](https://repology.org/project/fastfetch/versions)
[![Packaging status](https://repology.org/badge/tiny-repos/fastfetch.svg)](https://repology.org/project/fastfetch/versions)
[![latest packaged version(s)](https://repology.amdmi3.ru/badge/latest-versions/fastfetch.svg)](https://repology.amdmi3.ru/project/fastfetch/versions)
[![Packaging status](https://repology.amdmi3.ru/badge/tiny-repos/fastfetch.svg)](https://repology.amdmi3.ru/project/fastfetch/versions)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/fastfetch-cli/fastfetch)
[![中文README](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-README-red)](README-cn.md)
@@ -36,8 +36,8 @@ There are [screenshots on different platforms](https://github.com/fastfetch-cli/
Some distributions package outdated versions of fastfetch. Older versions receive no support, so please always try to use the latest version.
<a href="https://repology.org/project/fastfetch/versions">
<img src="https://repology.org/badge/vertical-allrepos/fastfetch.svg?columns=2" alt="Packaging status" align="right">
<a href="https://repology.amdmi3.ru/project/fastfetch/versions">
<img src="https://repology.amdmi3.ru/badge/vertical-allrepos/fastfetch.svg?columns=2" alt="Packaging status" align="right">
</a>
* Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (Ubuntu 22.04 or newer; latest version)
@@ -248,7 +248,20 @@ Set the key to a white space.
### Q: How can I display images on Windows?
As of April 2025:
#### Windows Terminal
Windows Terminal supports the sixel image protocol only.
```jsonc
{
"logo": {
"type": "sixel", // DO NOT USE "auto"
"source": "C:/path/to/image.png", // ~ can be used to refer to the Windows user home directory
"width": <image-width-in-chars>, // Optional
"height": <image-height-in-chars> // Optional
}
}
```
#### mintty and Wezterm
@@ -260,38 +273,7 @@ In `config.jsonc`:
"logo": {
"type": "iterm",
"source": "C:/path/to/image.png",
"width": <num-in-chars>
}
}
```
#### Windows Terminal
Windows Terminal supports the sixel image protocol only.
* If you installed fastfetch through MSYS2:
1. Install imagemagick: `pacman -S mingw-w64-<subsystem>-x86_64-imagemagick`
2. In `config.jsonc`:
```jsonc
{
"logo": {
"type": "sixel", // DO NOT USE "auto"
"source": "C:/path/to/image.png", // Do NOT use `~` as fastfetch is a native Windows program and doesn't apply cygwin path conversion
"width": <image-width-in-chars>, // Optional
"height": <image-height-in-chars> // Optional
}
}
```
* If you installed fastfetch via scoop or downloaded the binary directly from the GitHub Releases page:
1. Convert your image manually to sixel format using [any online image conversion service](https://www.google.com/search?q=convert+image+to+sixel)
2. In `config.jsonc`:
```jsonc
{
"logo": {
"type": "raw", // DO NOT USE "auto"
"source": "C:/path/to/image.sixel",
"width": <image-width-in-chars>, // Required
"height": <image-height-in-chars> // Required
"width": <image-width-in-chars>
}
}
```
+3 -1
View File
@@ -33,7 +33,9 @@ _fastfetch() {
if type _filedir &>/dev/null; then
_filedir
elif type compgen &>/dev/null; then
COMPREPLY+=($(compgen -f -- "$cur"))
local -a files
readarray -t files < <(compgen -f -- "$cur")
COMPREPLY+=("${files[@]}")
fi
return
;;
-3
View File
@@ -171,9 +171,6 @@ NTSTATUS NTAPI NtQuerySystemEnvironmentValueEx(
_Out_opt_ PULONG Attributes // EFI_VARIABLE_*
);
NTSTATUS NTAPI RtlGUIDFromString(IN PCUNICODE_STRING GuidString, OUT GUID* Guid);
NTSTATUS NTAPI RtlStringFromGUIDEx(IN const GUID* Guid, OUT PUNICODE_STRING GuidString, IN BOOLEAN AllocateGuidString);
typedef struct _SYSTEM_SECUREBOOT_INFORMATION {
BOOLEAN SecureBootEnabled;
BOOLEAN SecureBootCapable;
+1
View File
@@ -5,6 +5,7 @@
#include <stdalign.h>
#define FF_EFIVARS_PATH_PREFIX "/sys/firmware/efi/efivars/"
#define FF_EFI_GLOBAL_GUID "8be4df61-93ca-11d2-aa0d-00e098032b8c"
const char* ffDetectBootmgr(FFBootmgrResult* result) {
alignas(uint16_t) uint8_t buffer[2048];
+6 -4
View File
@@ -39,10 +39,12 @@ const char* ffDetectBootmgr(FFBootmgrResult* result) {
return err;
}
GUID efiGlobalGuid;
if (!NT_SUCCESS(RtlGUIDFromString(&(UNICODE_STRING) RTL_CONSTANT_STRING(L"{" FF_EFI_GLOBAL_GUID L"}"), &efiGlobalGuid))) {
return "RtlGUIDFromString() failed";
}
GUID efiGlobalGuid = {
.Data1 = 0x8be4df61,
.Data2 = 0x93ca,
.Data3 = 0x11d2,
.Data4 = { 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c },
};
ULONG size = sizeof(result->order);
if (!NT_SUCCESS(NtQuerySystemEnvironmentValueEx(&(UNICODE_STRING) RTL_CONSTANT_STRING(L"BootCurrent"), &efiGlobalGuid, &result->order, &size, nullptr))) {
-2
View File
@@ -18,5 +18,3 @@ typedef struct FFEfiLoadOption {
} FFEfiLoadOption;
bool ffEfiFillLoadOption(const FFEfiLoadOption* efiOption, FFBootmgrResult* result);
#define FF_EFI_GLOBAL_GUID "8be4df61-93ca-11d2-aa0d-00e098032b8c"
+4 -1
View File
@@ -98,10 +98,12 @@ static const char* detectWithDdcci([[maybe_unused]] FFBrightnessOptions* options
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_free_any_vcp_value)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_close_display)
// libddcutil may print trace messages to stdout during display detection, not only in ddca_init
FF_SUPPRESS_IO();
#ifndef FF_DISABLE_DLOPEN
FF_LIBRARY_LOAD_SYMBOL_LAZY(libddcutil, ddca_init)
if (ffddca_init) {
FF_SUPPRESS_IO();
// Ref: https://github.com/rockowitz/ddcutil/issues/344
if (ffddca_init(nullptr, -1 /*DDCA_SYSLOG_NOT_SET*/, 1 /*DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE*/) < 0) {
return "ddca_init() failed";
@@ -148,6 +150,7 @@ static const char* detectWithDdcci([[maybe_unused]] FFBrightnessOptions* options
brightness->max = max;
brightness->min = 0;
brightness->current = current;
brightness->builtin = false;
ffStrbufInitS(&brightness->name, display->model_name);
}
ffddca_close_display(handle);
+229 -55
View File
@@ -1,67 +1,241 @@
#include "detection/dns/dns.h"
#include "common/netif.h"
#include "common/mallocHelper.h"
#include "common/debug.h"
#include "common/windows/registry.h"
#include <ws2tcpip.h>
#include <iphlpapi.h>
// The resolvers of an interface are stored in the registry, under a key named after its GUID:
// HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{GUID} IPv4
// HKLM\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\{GUID} IPv6
// A statically configured `NameServer` takes precedence over the value handed out by DHCP, which is
// the precedence the IP stack applies as well - verified against GetAdaptersAddresses() on a machine
// with both set, where only the static list was reported.
//
// This replaces GetAdaptersAddresses(), which builds the unicast, anycast, multicast and DNS server
// lists of every adapter - about 9 ms on a machine with 52 of them - even though the module only
// reports the resolvers of the interface that carries the default route. Reading the one key that
// matters costs about 16 us (12 us to open it, 4 us to read the value, see
// src/common/windows/registry.c), and the module as a whole goes from about 11 ms to about 0.3 ms.
//
// Known limitation: a resolver learned from a router advertisement (RDNSS) is not written to the
// registry by any Windows version, so on a SLAAC-only network (no DHCPv6 lease, no static
// `NameServer`) IPv6 resolvers are not reported at all. GetAdaptersAddresses() reported them.
#define FF_DNS_REGISTRY_KEY_V4 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\"
#define FF_DNS_REGISTRY_KEY_V6 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces\\"
// Enough for either key path plus the 38 characters of a GUID
#define FF_DNS_REGISTRY_PATH_SIZE 256
static void appendAddress(FFlist* results, FFstrbuf* servers, uint32_t start, uint32_t end, FFDNSShowType showType) {
// A link local address may carry a zone index, which the reported address never includes
char address[INET6_ADDRSTRLEN];
uint32_t length = end - start;
if (length >= sizeof(address)) {
length = sizeof(address) - 1;
}
uint32_t i = 0;
for (; i < length && servers->chars[start + i] != '%'; ++i) {
address[i] = servers->chars[start + i];
}
address[i] = '\0';
if (showType & FF_DNS_TYPE_IPV4_BIT) {
IN_ADDR ipv4;
if (InetPtonA(AF_INET, address, &ipv4) == 1) {
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv4AddressToStringA(&ipv4, item->chars) - item->chars);
return;
}
}
if (showType & FF_DNS_TYPE_IPV6_BIT) {
IN6_ADDR ipv6;
if (InetPtonA(AF_INET6, address, &ipv6) == 1) {
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET6_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv6AddressToStringA(&ipv6, item->chars) - item->chars);
}
}
}
static void appendAddressList(FFstrbuf* servers, FFDNSShowType showType, FFlist* results) {
uint32_t start = 0;
while (start < servers->length) {
while (start < servers->length && (servers->chars[start] == ',' || servers->chars[start] == ' ' || servers->chars[start] == '\t')) {
++start;
}
uint32_t end = start;
while (end < servers->length && servers->chars[end] != ',' && servers->chars[end] != ' ' && servers->chars[end] != '\t') {
++end;
}
if (end > start) {
appendAddress(results, servers, start, end, showType);
}
start = end;
}
}
// A DHCPv6 lease stores its resolvers as a plain list of 16 byte addresses
static void appendAddressArray(const BYTE* data, uint32_t dataLength, FFDNSShowType showType, FFlist* results) {
if (!(showType & FF_DNS_TYPE_IPV6_BIT)) {
return;
}
for (uint32_t offset = 0; offset + sizeof(IN6_ADDR) <= dataLength; offset += sizeof(IN6_ADDR)) {
IN6_ADDR ipv6;
memcpy(&ipv6, data + offset, sizeof(ipv6));
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET6_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv6AddressToStringA(&ipv6, item->chars) - item->chars);
}
}
// `Dhcpv6InterfaceOptions` stores the DHCPv6 options of the last lease as an array of records: a 20
// byte header (option code, 0, payload length, 0, 0xFFFFFFFF) followed by the payload, padded to 16
// bytes. Option 23 is OPTION_DNS_SERVERS (RFC 3646).
static void appendDhcpv6Options(const BYTE* data, uint32_t dataLength, FFDNSShowType showType, FFlist* results) {
for (uint32_t offset = 0; offset + 20 <= dataLength;) {
uint32_t code, length;
memcpy(&code, data + offset, sizeof(code));
memcpy(&length, data + offset + 8, sizeof(length));
uint32_t payloadLength = dataLength - (offset + 20);
if (payloadLength > length) {
payloadLength = length;
}
if (code == 23) {
appendAddressArray(data + offset + 20, payloadLength, showType, results);
return;
}
if (payloadLength < length) {
return;
}
offset += 20 + ((payloadLength + 15) & ~(uint32_t) 15);
}
}
static void detectV4(const wchar_t* guid, FFDNSOptions* options, FFlist* results) {
wchar_t keyPath[FF_DNS_REGISTRY_PATH_SIZE] = FF_DNS_REGISTRY_KEY_V4;
wcscat(keyPath, guid);
FF_DEBUG("v4 key '%ls'", keyPath);
FF_AUTO_CLOSE_FD HANDLE hKey = nullptr;
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, keyPath, &hKey, nullptr)) {
FF_DEBUG("v4 key open failed");
return;
}
FF_STRBUF_AUTO_DESTROY servers = ffStrbufCreate();
// `NameServer` holds the statically configured list and wins over the one handed out by DHCP. A
// value that exists but holds no address must not shadow the fallback, hence the length checks.
bool got = (ffRegReadStrbuf(hKey, L"NameServer", &servers, nullptr) && servers.length) ||
(ffRegReadStrbuf(hKey, L"DhcpNameServer", &servers, nullptr) && servers.length);
FF_DEBUG("v4 got=%d servers='%s'", got, servers.chars);
if (!got) {
return;
}
appendAddressList(&servers, options->showType, results);
FF_DEBUG("v4 results=%u", results->length);
}
static void detectV6(const wchar_t* guid, FFDNSOptions* options, FFlist* results) {
wchar_t keyPath[FF_DNS_REGISTRY_PATH_SIZE] = FF_DNS_REGISTRY_KEY_V6;
wcscat(keyPath, guid);
FF_DEBUG("v6 key '%ls'", keyPath);
FF_AUTO_CLOSE_FD HANDLE hKey = nullptr;
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, keyPath, &hKey, nullptr)) {
FF_DEBUG("v6 key open failed");
return;
}
FF_STRBUF_AUTO_DESTROY servers = ffStrbufCreate();
if (ffRegReadStrbuf(hKey, L"NameServer", &servers, nullptr) && servers.length) {
FF_DEBUG("v6 servers='%s'", servers.chars);
appendAddressList(&servers, options->showType, results);
FF_DEBUG("v6 results=%u", results->length);
return;
}
// The two DHCPv6 values below are documented as REG_BINARY and are always written that way; the
// registry reader reports the bytes without their type, so the parsing below trusts that.
BYTE data[4096];
FFArgBuffer dhcpv6Servers = { .data = data, .length = sizeof(data) };
if (ffRegReadData(hKey, L"Dhcpv6DNSServers", &dhcpv6Servers, nullptr)) {
FF_DEBUG("v6 Dhcpv6DNSServers cb=%u", dhcpv6Servers.length);
appendAddressArray(data, dhcpv6Servers.length, options->showType, results);
return;
}
FFArgBuffer dhcpv6Options = { .data = data, .length = sizeof(data) };
if (ffRegReadData(hKey, L"Dhcpv6InterfaceOptions", &dhcpv6Options, nullptr)) {
FF_DEBUG("v6 Dhcpv6InterfaceOptions cb=%u", dhcpv6Options.length);
appendDhcpv6Options(data, dhcpv6Options.length, options->showType, results);
return;
}
FF_DEBUG("v6 no resolver");
}
const char* ffDetectDNS(FFDNSOptions* options, FFlist* results) {
FF_AUTO_FREE IP_ADAPTER_ADDRESSES* adapter_addresses = nullptr;
// Multiple attempts in case interfaces change while
// we are in the middle of querying them.
DWORD adapter_addresses_buffer_size = 0;
for (int attempts = 0;; ++attempts) {
if (adapter_addresses_buffer_size) {
adapter_addresses = (IP_ADAPTER_ADDRESSES*) realloc(adapter_addresses, adapter_addresses_buffer_size);
assert(adapter_addresses);
}
DWORD error = GetAdaptersAddresses(
options->showType & FF_DNS_TYPE_IPV4_BIT
? options->showType & FF_DNS_TYPE_IPV6_BIT ? AF_UNSPEC : AF_INET
: AF_INET6,
GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_FRIENDLY_NAME,
nullptr,
adapter_addresses,
&adapter_addresses_buffer_size);
if (error == ERROR_SUCCESS) {
break;
} else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) {
continue;
} else {
return "GetAdaptersAddresses() failed";
}
FF_DEBUG("enter, showType=%u", (unsigned) options->showType);
// The interface that carries the default route is the one the IP stack resolves names through.
// ffNetifGetDefaultRouteV4() already ignores the interfaces that are not operational.
NET_IFINDEX ifIndex = (NET_IFINDEX) ffNetifGetDefaultRouteV4()->ifIndex;
FF_DEBUG("ifIndex=%u", (unsigned) ifIndex);
if (!ifIndex) {
ifIndex = (NET_IFINDEX) ffNetifGetDefaultRouteV6()->ifIndex;
}
if (!ifIndex) {
return "Failed to detect default route";
}
uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteV4()->ifIndex;
// Iterate through all of the adapters
for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) {
if (adapter->IfIndex != defaultRouteIfIndex) {
continue;
}
if (adapter->OperStatus != IfOperStatusUp) {
continue;
}
for (IP_ADAPTER_DNS_SERVER_ADDRESS_XP* ifa = adapter->FirstDnsServerAddress; ifa; ifa = ifa->Next) {
// The element must only be added once it can be filled: `FF_LIST_ADD` does not zero the
// new element, so an unrecognized address family used to leave an uninitialized FFstrbuf
// in the list, which the callers then read and free
if (ifa->Address.lpSockaddr->sa_family == AF_INET) {
SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr;
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv4AddressToStringA(&ipv4->sin_addr, item->chars) - item->chars);
} else if (ifa->Address.lpSockaddr->sa_family == AF_INET6) {
SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET6_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv6AddressToStringA(&ipv6->sin6_addr, item->chars) - item->chars);
}
}
break;
NET_LUID luid;
if (ConvertInterfaceIndexToLuid(ifIndex, &luid) != NO_ERROR) {
return "ConvertInterfaceIndexToLuid() failed";
}
GUID guid;
if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) {
return "ConvertInterfaceLuidToGuid() failed";
}
wchar_t guidStr[64];
// clang-format off
swprintf(guidStr, ARRAY_SIZE(guidStr), L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
(unsigned long) guid.Data1,
(unsigned int) guid.Data2,
(unsigned int) guid.Data3,
(unsigned int) guid.Data4[0],
(unsigned int) guid.Data4[1],
(unsigned int) guid.Data4[2],
(unsigned int) guid.Data4[3],
(unsigned int) guid.Data4[4],
(unsigned int) guid.Data4[5],
(unsigned int) guid.Data4[6],
(unsigned int) guid.Data4[7]);
// clang-format on
// IPv4 first, so the list is grouped by address family the same way the printed output is
if (options->showType & FF_DNS_TYPE_IPV4_BIT) {
detectV4(guidStr, options, results);
}
if (options->showType & FF_DNS_TYPE_IPV6_BIT) {
detectV6(guidStr, options, results);
}
FF_DEBUG("return, results=%u", results->length);
return nullptr;
}
+359 -94
View File
@@ -2,27 +2,269 @@
#include <iphlpapi.h>
#include "common/netif.h"
#include "common/mallocHelper.h"
#include "common/windows/unicode.h"
#include "common/debug.h"
#include "localip.h"
#define FF_LOCALIP_NIFLAG(name) { IP_ADAPTER_##name, #name }
static inline void appendFlag(FFstrbuf* flags, bool enabled, const char* name) {
if (!enabled) {
return;
}
static const FFLocalIpNIFlag niFlagOptions[] = {
FF_LOCALIP_NIFLAG(DDNS_ENABLED),
FF_LOCALIP_NIFLAG(REGISTER_ADAPTER_SUFFIX),
FF_LOCALIP_NIFLAG(DHCP_ENABLED),
FF_LOCALIP_NIFLAG(RECEIVE_ONLY),
FF_LOCALIP_NIFLAG(NO_MULTICAST),
FF_LOCALIP_NIFLAG(IPV6_OTHER_STATEFUL_CONFIG),
FF_LOCALIP_NIFLAG(NETBIOS_OVER_TCPIP_ENABLED),
FF_LOCALIP_NIFLAG(IPV4_ENABLED),
FF_LOCALIP_NIFLAG(IPV6_ENABLED),
FF_LOCALIP_NIFLAG(IPV6_MANAGE_ADDRESS_CONFIG),
// sentinel
{},
};
if (flags->length) {
ffStrbufAppendC(flags, ',');
}
ffStrbufAppendS(flags, name);
}
static void fillInterfaceFlags(FFstrbuf* flags, const MIB_IF_ROW2* row) {
appendFlag(flags, row->InterfaceAndOperStatusFlags.HardwareInterface, "HARDWARE");
appendFlag(flags, row->InterfaceAndOperStatusFlags.FilterInterface, "FILTER");
appendFlag(flags, row->InterfaceAndOperStatusFlags.ConnectorPresent, "CONNECTOR_PRESENT");
appendFlag(flags, row->InterfaceAndOperStatusFlags.NotAuthenticated, "NOT_AUTHENTICATED");
appendFlag(flags, row->InterfaceAndOperStatusFlags.NotMediaConnected, "NO_MEDIA");
appendFlag(flags, row->InterfaceAndOperStatusFlags.Paused, "PAUSED");
appendFlag(flags, row->InterfaceAndOperStatusFlags.LowPower, "LOW_POWER");
appendFlag(flags, row->InterfaceAndOperStatusFlags.EndPointInterface, "ENDPOINT");
switch (row->DirectionType) {
case NET_IF_DIRECTION_SENDONLY:
appendFlag(flags, true, "SEND_ONLY");
break;
case NET_IF_DIRECTION_RECEIVEONLY:
appendFlag(flags, true, "RECEIVE_ONLY");
break;
case NET_IF_DIRECTION_SENDRECEIVE:
appendFlag(flags, true, "SEND_RECEIVE");
break;
default:
break;
}
// Exactly one value of each of these enums is reported per interface, so every value gets
// a flag of its own
#define FF_LOCALIP_FLAG_CASE(value, name) \
case value: appendFlag(flags, true, name); break;
switch (row->MediaConnectState) {
FF_LOCALIP_FLAG_CASE(MediaConnectStateUnknown, "MEDIA_UNKNOWN")
FF_LOCALIP_FLAG_CASE(MediaConnectStateConnected, "MEDIA_CONNECTED")
FF_LOCALIP_FLAG_CASE(MediaConnectStateDisconnected, "MEDIA_DISCONNECTED")
}
// The interface type, as registered with IANA and listed in ipifcons.h
switch (row->Type) {
FF_LOCALIP_FLAG_CASE(IF_TYPE_OTHER, "OTHER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_REGULAR_1822, "REGULAR_1822")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HDH_1822, "HDH_1822")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DDN_X25, "DDN_X25")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RFC877_X25, "RFC877_X25")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ETHERNET_CSMACD, "ETHERNET_CSMACD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IS088023_CSMACD, "IS088023_CSMACD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88024_TOKENBUS, "ISO88024_TOKENBUS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_TOKENRING, "ISO88025_TOKENRING")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88026_MAN, "ISO88026_MAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_STARLAN, "STARLAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROTEON_10MBIT, "PROTEON_10MBIT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROTEON_80MBIT, "PROTEON_80MBIT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HYPERCHANNEL, "HYPERCHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FDDI, "FDDI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_B, "LAP_B")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SDLC, "SDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS1, "DS1")
FF_LOCALIP_FLAG_CASE(IF_TYPE_E1, "E1")
FF_LOCALIP_FLAG_CASE(IF_TYPE_BASIC_ISDN, "BASIC_ISDN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PRIMARY_ISDN, "PRIMARY_ISDN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_POINT2POINT_SERIAL, "PROP_POINT2POINT_SERIAL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PPP, "PPP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SOFTWARE_LOOPBACK, "SOFTWARE_LOOPBACK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_EON, "EON")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ETHERNET_3MBIT, "ETHERNET_3MBIT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_NSIP, "NSIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SLIP, "SLIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ULTRA, "ULTRA")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS3, "DS3")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SIP, "SIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY, "FRAMERELAY")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RS232, "RS232")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PARA, "PARA")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ARCNET, "ARCNET")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ARCNET_PLUS, "ARCNET_PLUS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM, "ATM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MIO_X25, "MIO_X25")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET, "SONET")
FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_PLE, "X25_PLE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88022_LLC, "ISO88022_LLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_LOCALTALK, "LOCALTALK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SMDS_DXI, "SMDS_DXI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_SERVICE, "FRAMERELAY_SERVICE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_V35, "V35")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HSSI, "HSSI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPPI, "HIPPI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MODEM, "MODEM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_AAL5, "AAL5")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_PATH, "SONET_PATH")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_VT, "SONET_VT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SMDS_ICIP, "SMDS_ICIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_VIRTUAL, "PROP_VIRTUAL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_MULTIPLEXOR, "PROP_MULTIPLEXOR")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80212, "IEEE80212")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FIBRECHANNEL, "FIBRECHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPPIINTERFACE, "HIPPIINTERFACE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_INTERCONNECT, "FRAMERELAY_INTERCONNECT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_AFLANE_8023, "AFLANE_8023")
FF_LOCALIP_FLAG_CASE(IF_TYPE_AFLANE_8025, "AFLANE_8025")
FF_LOCALIP_FLAG_CASE(IF_TYPE_CCTEMUL, "CCTEMUL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FASTETHER, "FASTETHER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN, "ISDN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_V11, "V11")
FF_LOCALIP_FLAG_CASE(IF_TYPE_V36, "V36")
FF_LOCALIP_FLAG_CASE(IF_TYPE_G703_64K, "G703_64K")
FF_LOCALIP_FLAG_CASE(IF_TYPE_G703_2MB, "G703_2MB")
FF_LOCALIP_FLAG_CASE(IF_TYPE_QLLC, "QLLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FASTETHER_FX, "FASTETHER_FX")
FF_LOCALIP_FLAG_CASE(IF_TYPE_CHANNEL, "CHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80211, "IEEE80211")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IBM370PARCHAN, "IBM370PARCHAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ESCON, "ESCON")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DLSW, "DLSW")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN_S, "ISDN_S")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN_U, "ISDN_U")
FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_D, "LAP_D")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPSWITCH, "IPSWITCH")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RSRB, "RSRB")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_LOGICAL, "ATM_LOGICAL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS0, "DS0")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS0_BUNDLE, "DS0_BUNDLE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_BSC, "BSC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ASYNC, "ASYNC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_CNR, "CNR")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025R_DTR, "ISO88025R_DTR")
FF_LOCALIP_FLAG_CASE(IF_TYPE_EPLRS, "EPLRS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ARAP, "ARAP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_CNLS, "PROP_CNLS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HOSTPAD, "HOSTPAD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TERMPAD, "TERMPAD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_MPI, "FRAMERELAY_MPI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_X213, "X213")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ADSL, "ADSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RADSL, "RADSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SDSL, "SDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VDSL, "VDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_CRFPRINT, "ISO88025_CRFPRINT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MYRINET, "MYRINET")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_EM, "VOICE_EM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_FXO, "VOICE_FXO")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_FXS, "VOICE_FXS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_ENCAP, "VOICE_ENCAP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_OVERIP, "VOICE_OVERIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_DXI, "ATM_DXI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_FUNI, "ATM_FUNI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_IMA, "ATM_IMA")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PPPMULTILINKBUNDLE, "PPPMULTILINKBUNDLE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_CDLC, "IPOVER_CDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_CLAW, "IPOVER_CLAW")
FF_LOCALIP_FLAG_CASE(IF_TYPE_STACKTOSTACK, "STACKTOSTACK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VIRTUALIPADDRESS, "VIRTUALIPADDRESS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MPC, "MPC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_ATM, "IPOVER_ATM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_FIBER, "ISO88025_FIBER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TDLC, "TDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_GIGABITETHERNET, "GIGABITETHERNET")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HDLC, "HDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_F, "LAP_F")
FF_LOCALIP_FLAG_CASE(IF_TYPE_V37, "V37")
FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_MLP, "X25_MLP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_HUNTGROUP, "X25_HUNTGROUP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TRANSPHDLC, "TRANSPHDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_INTERLEAVE, "INTERLEAVE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FAST, "FAST")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IP, "IP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_MACLAYER, "DOCSCABLE_MACLAYER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_DOWNSTREAM, "DOCSCABLE_DOWNSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_UPSTREAM, "DOCSCABLE_UPSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_A12MPPSWITCH, "A12MPPSWITCH")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TUNNEL, "TUNNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_COFFEE, "COFFEE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_CES, "CES")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_SUBINTERFACE, "ATM_SUBINTERFACE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_L2_VLAN, "L2_VLAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_L3_IPVLAN, "L3_IPVLAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_L3_IPXVLAN, "L3_IPXVLAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DIGITALPOWERLINE, "DIGITALPOWERLINE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MEDIAMAILOVERIP, "MEDIAMAILOVERIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DTM, "DTM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DCN, "DCN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPFORWARD, "IPFORWARD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MSDSL, "MSDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE1394, "IEEE1394")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IF_GSN, "IF_GSN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_MACLAYER, "DVBRCC_MACLAYER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_DOWNSTREAM, "DVBRCC_DOWNSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_UPSTREAM, "DVBRCC_UPSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_VIRTUAL, "ATM_VIRTUAL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MPLS_TUNNEL, "MPLS_TUNNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SRP, "SRP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICEOVERATM, "VOICEOVERATM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICEOVERFRAMERELAY, "VOICEOVERFRAMERELAY")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IDSL, "IDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_COMPOSITELINK, "COMPOSITELINK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SS7_SIGLINK, "SS7_SIGLINK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_WIRELESS_P2P, "PROP_WIRELESS_P2P")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FR_FORWARD, "FR_FORWARD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RFC1483, "RFC1483")
FF_LOCALIP_FLAG_CASE(IF_TYPE_USB, "USB")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE8023AD_LAG, "IEEE8023AD_LAG")
FF_LOCALIP_FLAG_CASE(IF_TYPE_BGP_POLICY_ACCOUNTING, "BGP_POLICY_ACCOUNTING")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRF16_MFR_BUNDLE, "FRF16_MFR_BUNDLE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_H323_GATEKEEPER, "H323_GATEKEEPER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_H323_PROXY, "H323_PROXY")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MPLS, "MPLS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MF_SIGLINK, "MF_SIGLINK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HDSL2, "HDSL2")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SHDSL, "SHDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS1_FDL, "DS1_FDL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_POS, "POS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVB_ASI_IN, "DVB_ASI_IN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVB_ASI_OUT, "DVB_ASI_OUT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PLC, "PLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_NFAS, "NFAS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TR008, "TR008")
FF_LOCALIP_FLAG_CASE(IF_TYPE_GR303_RDT, "GR303_RDT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_GR303_IDT, "GR303_IDT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISUP, "ISUP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_MACLAYER, "PROP_DOCS_WIRELESS_MACLAYER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_DOWNSTREAM, "PROP_DOCS_WIRELESS_DOWNSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_UPSTREAM, "PROP_DOCS_WIRELESS_UPSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPERLAN2, "HIPERLAN2")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_BWA_P2MP, "PROP_BWA_P2MP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_OVERHEAD_CHANNEL, "SONET_OVERHEAD_CHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DIGITAL_WRAPPER_OVERHEAD_CHANNEL, "DIGITAL_WRAPPER_OVERHEAD_CHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_AAL2, "AAL2")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RADIO_MAC, "RADIO_MAC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_RADIO, "ATM_RADIO")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IMT, "IMT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MVL, "MVL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_REACH_DSL, "REACH_DSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FR_DLCI_ENDPT, "FR_DLCI_ENDPT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_VCI_ENDPT, "ATM_VCI_ENDPT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_OPTICAL_CHANNEL, "OPTICAL_CHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_OPTICAL_TRANSPORT, "OPTICAL_TRANSPORT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80216_WMAN, "IEEE80216_WMAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_WWANPP, "WWANPP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_WWANPP2, "WWANPP2")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE802154, "IEEE802154")
FF_LOCALIP_FLAG_CASE(IF_TYPE_XBOX_WIRELESS, "XBOX_WIRELESS")
}
#undef FF_LOCALIP_FLAG_CASE
}
static bool isDefaultRouteInterface(const FFLocalIpOptions* options, NET_IFINDEX ifIndex) {
// Both route lookups are cached, so this is cheap even inside the per interface loop
return ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == ifIndex) ||
((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == ifIndex);
}
const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
FF_DEBUG("Starting local IP detection with showType=0x%X, namePrefix='%.*s'",
@@ -30,89 +272,102 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
(int) options->namePrefix.length,
options->namePrefix.chars);
FF_AUTO_FREE IP_ADAPTER_ADDRESSES* adapter_addresses = nullptr;
ADDRESS_FAMILY family = options->showType & FF_LOCALIP_TYPE_IPV4_BIT
? options->showType & FF_LOCALIP_TYPE_IPV6_BIT ? AF_UNSPEC : AF_INET
: AF_INET6;
bool queryV4 = (options->showType & FF_LOCALIP_TYPE_IPV4_BIT) != 0;
bool queryV6 = (options->showType & FF_LOCALIP_TYPE_IPV6_BIT) != 0;
// Multiple attempts in case interfaces change while
// we are in the middle of querying them.
DWORD adapter_addresses_buffer_size = 0;
for (int attempts = 0;; ++attempts) {
FF_DEBUG("Attempt %d to get adapter addresses, buffer size: %lu", attempts + 1, adapter_addresses_buffer_size);
if (adapter_addresses_buffer_size) {
adapter_addresses = (IP_ADAPTER_ADDRESSES*) realloc(adapter_addresses, adapter_addresses_buffer_size);
assert(adapter_addresses);
// Every interface is queried with GetIfEntry2() rather than by walking a table of all of them:
// GetIfTable2() costs about 0.95 ms on a machine with a few dozen adapters however few of them
// are reported, because it marshals every registered one, while GetIfEntry2() costs about 24 us
// per interface. With 52 adapters registered and 8 interfaces carrying an address the per
// interface API is 3.6 times faster overall; the table only wins past roughly 40 interfaces with
// an address, and with FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT (the default) at most one interface
// can pass the filter below anyway.
bool defaultRouteOnly = (options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) != 0;
if (defaultRouteOnly) {
if (queryV4) {
FF_DEBUG("Querying the IPv4 default route interface only: ifIndex=%u", (unsigned) ffNetifGetDefaultRouteV4()->ifIndex);
}
DWORD family = options->showType & FF_LOCALIP_TYPE_IPV4_BIT
? options->showType & FF_LOCALIP_TYPE_IPV6_BIT ? AF_UNSPEC : AF_INET
: AF_INET6;
FF_DEBUG("Calling GetAdaptersAddresses with family=%u, flags=0x%X", (unsigned) family, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER);
DWORD error = GetAdaptersAddresses(
family,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
nullptr,
adapter_addresses,
&adapter_addresses_buffer_size);
if (error == ERROR_SUCCESS) {
FF_DEBUG("GetAdaptersAddresses succeeded on attempt %d", attempts + 1);
break;
} else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) {
FF_DEBUG("Buffer overflow, need %lu bytes, retrying", adapter_addresses_buffer_size);
continue;
} else {
FF_DEBUG("GetAdaptersAddresses failed with error %lu after %d attempts", error, attempts + 1);
return "GetAdaptersAddresses() failed";
if (queryV6) {
FF_DEBUG("Querying the IPv6 default route interface only: ifIndex=%u", (unsigned) ffNetifGetDefaultRouteV6()->ifIndex);
}
}
PMIB_UNICASTIPADDRESS_TABLE table = nullptr;
const char* errorMessage = nullptr;
// The unicast address table is the only API that reports per interface addresses and it cannot
// be filtered by interface, so it is queried once and only the rows of the interfaces which pass
// the filter below are read
if (!NETIO_SUCCESS(GetUnicastIpAddressTable(family, &table))) {
FF_DEBUG("GetUnicastIpAddressTable failed");
errorMessage = "GetUnicastIpAddressTable() failed";
goto cleanup;
}
[[maybe_unused]] int adapterCount = 0, processedCount = 0;
// Iterate through all of the adapters
for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) {
// Rows of the same interface are not adjacent (IPv4 rows come first),
// so each interface is handled at its first row
for (ULONG i = 0; i < table->NumEntries; ++i) {
NET_IFINDEX ifIndex = table->Table[i].InterfaceIndex;
bool seen = false;
for (ULONG j = 0; j < i && !seen; ++j) {
seen = table->Table[j].InterfaceIndex == ifIndex;
}
if (seen) {
continue;
}
adapterCount++;
// Checked first to avoid querying interfaces we won't show
if (defaultRouteOnly && !isDefaultRouteInterface(options, ifIndex)) {
FF_DEBUG("Skipping interface %u (not default route interface)", (unsigned) ifIndex);
continue;
}
MIB_IF_ROW2 ifRow = { .InterfaceIndex = ifIndex };
if (!NETIO_SUCCESS(GetIfEntry2(&ifRow))) {
FF_DEBUG("GetIfEntry2(%u) failed", (unsigned) ifIndex);
continue;
}
FF_DEBUG("Processing adapter %d: IfIndex=%u, IfType=%u, OperStatus=%u",
adapterCount,
(unsigned) adapter->IfIndex,
(unsigned) adapter->IfType,
(unsigned) adapter->OperStatus);
(unsigned) ifIndex,
(unsigned) ifRow.Type,
(unsigned) ifRow.OperStatus);
if (adapter->OperStatus != IfOperStatusUp) {
FF_DEBUG("Skipping adapter %u (not operational, status=%d)", (unsigned) adapter->IfIndex, adapter->OperStatus);
if (ifRow.OperStatus != IfOperStatusUp) {
FF_DEBUG("Skipping adapter %u (not operational, status=%d)", (unsigned) ifIndex, ifRow.OperStatus);
continue;
}
bool isLoop = adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK;
FF_DEBUG("Adapter %u: isLoopback=%s", (unsigned) adapter->IfIndex, isLoop ? "true" : "false");
bool isLoop = ifRow.Type == IF_TYPE_SOFTWARE_LOOPBACK;
FF_DEBUG("Adapter %u: isLoopback=%s", (unsigned) ifIndex, isLoop ? "true" : "false");
if (isLoop && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) {
FF_DEBUG("Skipping loopback adapter %u (loopback not requested)", (unsigned) adapter->IfIndex);
FF_DEBUG("Skipping loopback adapter %u (loopback not requested)", (unsigned) ifIndex);
continue;
}
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(adapter->FriendlyName);
FF_DEBUG("Adapter %u name: '%s'", (unsigned) adapter->IfIndex, name.chars);
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(ifRow.Alias);
FF_DEBUG("Adapter %u name: '%s'", (unsigned) ifIndex, name.chars);
if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) {
FF_DEBUG("Skipping adapter %u (name doesn't match prefix '%.*s')",
(unsigned) adapter->IfIndex,
(unsigned) ifIndex,
(int) options->namePrefix.length,
options->namePrefix.chars);
continue;
}
if (options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) {
if (!((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == adapter->IfIndex) &&
!((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == adapter->IfIndex)) {
FF_DEBUG("Skipping interface %u (not default route interface)", (unsigned) adapter->IfIndex);
continue;
}
}
processedCount++;
FF_DEBUG("Creating result item for adapter %u ('%s')", (unsigned) adapter->IfIndex, name.chars);
FF_DEBUG("Creating result item for adapter %u ('%s')", (unsigned) ifIndex, name.chars);
FFLocalIpResult* item = FF_LIST_ADD(FFLocalIpResult, *results);
ffStrbufInitMove(&item->name, &name);
@@ -124,16 +379,26 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
item->speed = -1;
item->mtu = -1;
if (options->showType & FF_LOCALIP_TYPE_FLAGS_BIT) {
fillInterfaceFlags(&item->flags, &ifRow);
FF_DEBUG("Adapter %u flags: '%s'", (unsigned) ifIndex, item->flags.chars);
}
uint32_t typesToAdd = options->showType & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT);
FF_DEBUG("Types to add for adapter %u: 0x%X", (unsigned) adapter->IfIndex, typesToAdd);
FF_DEBUG("Types to add for adapter %u: 0x%X", (unsigned) ifIndex, typesToAdd);
[[maybe_unused]] int ipv4Count = 0, ipv6Count = 0;
for (IP_ADAPTER_UNICAST_ADDRESS* ifa = adapter->FirstUnicastAddress; ifa; ifa = ifa->Next) {
for (ULONG j = i; j < table->NumEntries; ++j) {
MIB_UNICASTIPADDRESS_ROW* ifa = &table->Table[j];
if (ifa->InterfaceIndex != ifIndex) {
continue;
}
FF_DEBUG("Processing unicast address: prefix origin=%d, suffix origin=%d, family=%d, DadState=%d",
ifa->PrefixOrigin,
ifa->SuffixOrigin,
ifa->Address.lpSockaddr->sa_family,
ifa->Address.si_family,
ifa->DadState);
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT)) {
@@ -150,19 +415,19 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
// MIB_UNICASTIPADDRESS_ROW::SkipAsSource
}
if (ifa->Address.lpSockaddr->sa_family == AF_INET) {
if (ifa->Address.si_family == AF_INET) {
if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) {
FF_DEBUG("Skipping IPv4 address (not requested in typesToAdd=0x%X)", typesToAdd);
continue;
}
bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == adapter->IfIndex);
if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute) {
bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == ifIndex);
if (defaultRouteOnly && !isDefaultRoute) {
FF_DEBUG("Skipping IPv4 address (not on default route interface)");
continue;
}
SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr;
SOCKADDR_IN* ipv4 = &ifa->Address.Ipv4;
char addressBuffer[INET_ADDRSTRLEN + 10];
char* end = RtlIpv4AddressToStringA(&ipv4->sin_addr, addressBuffer);
@@ -185,13 +450,13 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
if (typesToAdd == 0) {
break;
}
} else if (ifa->Address.lpSockaddr->sa_family == AF_INET6) {
} else if (ifa->Address.si_family == AF_INET6) {
if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) {
FF_DEBUG("Skipping IPv6 address (not requested in typesToAdd=0x%X)", typesToAdd);
continue;
}
SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
SOCKADDR_IN6* ipv6 = &ifa->Address.Ipv6;
FFLocalIpIpv6Type ipv6Type = FF_LOCALIP_IPV6_TYPE_NONE;
if (IN6_IS_ADDR_GLOBAL(&ipv6->sin6_addr)) {
@@ -209,8 +474,8 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
continue;
}
bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == adapter->IfIndex);
if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute) {
bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == ifIndex);
if (defaultRouteOnly && !isDefaultRoute) {
FF_DEBUG("Skipping IPv6 address (not on default route interface)");
continue;
}
@@ -240,24 +505,20 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
}
}
FF_DEBUG("Adapter %u: collected %d IPv4 and %d IPv6 addresses", (unsigned) adapter->IfIndex, ipv4Count, ipv6Count);
FF_DEBUG("Adapter %u: collected %d IPv4 and %d IPv6 addresses", (unsigned) ifIndex, ipv4Count, ipv6Count);
if (options->showType & FF_LOCALIP_TYPE_SPEED_BIT) {
item->speed = (int32_t) (adapter->ReceiveLinkSpeed / 1000000);
FF_DEBUG("Adapter %u speed: %d Mbps (raw: %llu)", (unsigned) adapter->IfIndex, item->speed, adapter->ReceiveLinkSpeed);
item->speed = (int32_t) (ifRow.ReceiveLinkSpeed / 1000000);
FF_DEBUG("Adapter %u speed: %d Mbps (raw: %llu)", (unsigned) ifIndex, item->speed, ifRow.ReceiveLinkSpeed);
}
if (options->showType & FF_LOCALIP_TYPE_MTU_BIT) {
item->mtu = (int32_t) adapter->Mtu;
FF_DEBUG("Adapter %u MTU: %d", (unsigned) adapter->IfIndex, item->mtu);
item->mtu = (int32_t) ifRow.Mtu;
FF_DEBUG("Adapter %u MTU: %d", (unsigned) ifIndex, item->mtu);
}
if (options->showType & FF_LOCALIP_TYPE_FLAGS_BIT) {
ffLocalIpFillNIFlags(&item->flags, adapter->Flags, niFlagOptions);
FF_DEBUG("Adapter %u flags: 0x%lX -> '%s'", (unsigned) adapter->IfIndex, adapter->Flags, item->flags.chars);
}
if (options->showType & FF_LOCALIP_TYPE_MAC_BIT && adapter->PhysicalAddressLength == 6) {
uint8_t* ptr = adapter->PhysicalAddress;
if (options->showType & FF_LOCALIP_TYPE_MAC_BIT && ifRow.PhysicalAddressLength == 6) {
const uint8_t* ptr = ifRow.PhysicalAddress;
ffStrbufSetF(&item->mac, "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
FF_DEBUG("Adapter %u MAC: %s", (unsigned) adapter->IfIndex, item->mac.chars);
FF_DEBUG("Adapter %u MAC: %s", (unsigned) ifIndex, item->mac.chars);
}
}
@@ -266,5 +527,9 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
processedCount,
results->length);
return nullptr;
cleanup:
if (table) {
FreeMibTable(table);
}
return errorMessage;
}
+63 -55
View File
@@ -1,71 +1,79 @@
#include "netio.h"
#include "common/netif.h"
#include "common/mallocHelper.h"
#include "common/windows/unicode.h"
#include <ws2tcpip.h>
#include <iphlpapi.h>
const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) {
FF_AUTO_FREE IP_ADAPTER_ADDRESSES* adapter_addresses = nullptr;
// Multiple attempts in case interfaces change while
// we are in the middle of querying them.
DWORD adapter_addresses_buffer_size = 0;
for (int attempts = 0;; ++attempts) {
if (adapter_addresses_buffer_size) {
adapter_addresses = (IP_ADAPTER_ADDRESSES*) realloc(adapter_addresses, adapter_addresses_buffer_size);
assert(adapter_addresses);
}
DWORD error = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
nullptr,
adapter_addresses,
&adapter_addresses_buffer_size);
if (error == ERROR_SUCCESS) {
break;
} else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) {
continue;
} else {
return "GetAdaptersAddresses() failed";
}
static void addInterface(FFlist* result, const FFNetIOOptions* options, NET_IFINDEX ifIndex, bool isDefaultRoute) {
MIB_IF_ROW2 ifRow = { .InterfaceIndex = ifIndex };
if (!NETIO_SUCCESS(GetIfEntry2(&ifRow))) {
return;
}
// localip reports the interfaces that are operational only, so the same filter is applied here
if (ifRow.OperStatus != IfOperStatusUp) {
return;
}
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(ifRow.Alias);
if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) {
return;
}
FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result);
*counters = (FFNetIOResult) {
.name = ffStrbufCreateMove(&name),
.txBytes = ifRow.OutOctets,
.rxBytes = ifRow.InOctets,
.txPackets = (ifRow.OutUcastPkts + ifRow.OutNUcastPkts),
.rxPackets = (ifRow.InUcastPkts + ifRow.InNUcastPkts),
.rxErrors = ifRow.InErrors,
.txErrors = ifRow.OutErrors,
.rxDrops = ifRow.InDiscards,
.txDrops = ifRow.OutDiscards,
.defaultRoute = isDefaultRoute,
};
}
const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) {
uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteV4()->ifIndex;
// Iterate through all of the adapters
for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) {
bool isDefaultRoute = adapter->IfIndex == defaultRouteIfIndex;
if (options->defaultRouteOnly && !isDefaultRoute) {
continue;
}
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(adapter->FriendlyName);
if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) {
continue;
}
MIB_IF_ROW2 ifRow = { .InterfaceIndex = adapter->IfIndex };
if (GetIfEntry2(&ifRow) == NO_ERROR) {
FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result);
*counters = (FFNetIOResult) {
.name = ffStrbufCreateMove(&name),
.txBytes = ifRow.OutOctets,
.rxBytes = ifRow.InOctets,
.txPackets = (ifRow.OutUcastPkts + ifRow.OutNUcastPkts),
.rxPackets = (ifRow.InUcastPkts + ifRow.InNUcastPkts),
.rxErrors = ifRow.InErrors,
.txErrors = ifRow.OutErrors,
.rxDrops = ifRow.InDiscards,
.txDrops = ifRow.OutDiscards,
.defaultRoute = isDefaultRoute,
};
}
// With `defaultRouteOnly` (the default) the interface carrying the default route is the only one
// that can be reported, so it is queried with the single entry API instead of walking a table of
// every interface: GetIfEntry2() on one interface costs about 12 us.
if (options->defaultRouteOnly) {
addInterface(result, options, (NET_IFINDEX) defaultRouteIfIndex, true);
return nullptr;
}
// The interface set is taken from the unicast address table, the same source localip uses, so
// both modules report the same interfaces. Walking the interface table instead would also report
// the NDIS filter layers of every adapter (Npcap, QoS, WFP, ...) and the adapters that carry no
// address at all, and those cannot be told apart from real adapters by any MIB_IF_ROW2 field.
// GetUnicastIpAddressTable() costs about 72 us, GetIfTable2() about 860 us.
PMIB_UNICASTIPADDRESS_TABLE addressTable = nullptr;
if (!NETIO_SUCCESS(GetUnicastIpAddressTable(AF_UNSPEC, &addressTable))) {
return "GetUnicastIpAddressTable() failed";
}
for (ULONG i = 0; i < addressTable->NumEntries; ++i) {
NET_IFINDEX ifIndex = addressTable->Table[i].InterfaceIndex;
// Rows of the same interface are not adjacent (IPv4 rows come first),
// so each interface is handled at its first row
bool seen = false;
for (ULONG j = 0; j < i && !seen; ++j) {
seen = addressTable->Table[j].InterfaceIndex == ifIndex;
}
if (seen) {
continue;
}
addInterface(result, options, ifIndex, ifIndex == defaultRouteIfIndex);
}
FreeMibTable(addressTable);
return nullptr;
}
+65 -1
View File
@@ -70,7 +70,71 @@ bool ffPackagesWriteCache(FFstrbuf* cacheDir, FFstrbuf* cacheContent, uint32_t n
return ffWriteFileBuffer(cacheDir->chars, cacheContent);
}
#ifndef _WIN32
#if __linux__
#include <sys/syscall.h>
struct linux_dirent64 {
uint64_t d_ino; /* 64-bit inode number */
int64_t d_off; /* Not an offset; see getdents() */
unsigned short d_reclen; /* Size of this dirent */
unsigned char d_type; /* File type */
char d_name[]; /* Filename (null-terminated) */
};
uint32_t ffPackagesGetNumElements(const char* dirname, bool isdir) {
FF_AUTO_CLOSE_FD int fd = open(dirname, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (fd < 0) {
return 0;
}
alignas(struct linux_dirent64) uint8_t bytes[64 * 1024];
uint32_t num_elements = 0;
const size_t nameOffset = offsetof(struct linux_dirent64, d_name);
for (;;) {
long bytesRead = syscall(SYS_getdents64, fd, bytes, sizeof(bytes));
if (bytesRead < 0) {
if (errno == EINTR) {
continue;
}
break;
}
if (bytesRead == 0) {
break;
}
size_t remaining = (size_t) bytesRead;
struct linux_dirent64* entry = (struct linux_dirent64*) bytes;
while (remaining >= nameOffset + 1) {
bool ok = false;
if (entry->d_name[0] != '.') {
if (__builtin_expect(entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK, true)) {
ok = entry->d_type == (isdir ? DT_DIR : DT_REG);
} else {
struct stat stbuf;
if (fstatat(fd, entry->d_name, &stbuf, 0) == 0) {
ok = isdir ? S_ISDIR(stbuf.st_mode) : S_ISREG(stbuf.st_mode);
}
}
}
num_elements += ok;
size_t recordLength = entry->d_reclen;
remaining -= recordLength;
entry = (struct linux_dirent64*) ((uint8_t*) entry + recordLength);
}
if (remaining != 0) {
break;
}
}
return num_elements;
}
#elif !_WIN32
uint32_t ffPackagesGetNumElements(const char* dirname, bool isdir) {
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(dirname);
if (dirp == nullptr) {
+2
View File
@@ -19,6 +19,8 @@ static inline void applyVulkanVersion(uint32_t vulkanVersion, FFVersion* ffVersi
}
static void applyDriverName(VkPhysicalDeviceDriverPropertiesKHR* properties, FFstrbuf* result) {
ffStrbufClear(result);
if (!ffStrSet(properties->driverName)) {
return;
}