Files
raven/testdata/native_loader/interface_client.c
T
Clansty 244bbb8104 feat: report the host's interfaces in the MIB_IFROW calls
- GetIfEntry, GetIfTable and GetNumberOfInterfaces answer from the host's interfaces. GetIfEntry names one by the index the record handed in carries, GetIfTable puts the rows behind a count, and a table that does not fit is refused with ERROR_INSUFFICIENT_BUFFER and the size it needs.

- The interface walk moved out of host_api.cpp into interfaces.cpp, so the records GetAdaptersAddresses reports and the rows these calls report come from one enumeration of the host rather than two.

- interfaces_test checks the record against the MIB_IFROW offsets Windows declares and the cutting of a long name. The new guest fixture interface_client.c reads the tables through the runtime, in 64-bit and 32-bit builds: it checks that the count, the rows and GetIfEntry agree, that a sorted table lists the rows by index, and that an unknown index and a short table fail as Windows fails them.

Assisted-by: Claude Code:deepseek-v4.1-flash-1017

AI-Scope: Read the MIB_IFROW and MIB_IFTABLE layouts, moved the interface enumeration into interfaces.cpp, wrote the three handlers and both tests, and checked them with ctest (native_interface_table_contract, native_interface_table_client, native_interface_table_client_x32).
2026-11-02 12:03:56 +08:00

88 lines
2.9 KiB
C

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iphlpapi.h>
/* A program reads how the machine is networked before it opens its first socket: how many interfaces there are, a row for each, and one
row by index. Every answer has to agree with the others, and a bad request has to fail the way Windows fails it. */
typedef char mib_if_row_is_860_bytes[sizeof(MIB_IFROW) == 860 ? 1 : -1];
static int failure;
static void expect(int condition, int code)
{
if (!condition && !failure)
failure = code;
}
static void zero(void *buffer, unsigned size)
{
BYTE *bytes = buffer;
for (unsigned at = 0; at < size; ++at)
bytes[at] = 0;
}
static int same_name(const WCHAR *left, const WCHAR *right)
{
for (unsigned at = 0; at < 256; ++at)
if (left[at] != right[at])
return 0;
return 1;
}
int raven_entry(void)
{
ULONG count = 0;
expect(GetNumberOfInterfaces(&count) == NO_ERROR && count > 0, 1);
ULONG size = 0;
expect(GetIfTable(NULL, &size, 0) == ERROR_INSUFFICIENT_BUFFER && size >= sizeof(MIB_IFTABLE), 2);
MIB_IFTABLE *table = LocalAlloc(LMEM_FIXED, size);
expect(table != NULL, 3);
if (!table)
return failure;
expect(GetIfTable(table, &size, 0) == NO_ERROR, 4);
expect(table->dwNumEntries == count, 5);
expect(size == 4 + count * sizeof(MIB_IFROW), 6);
for (DWORD at = 0; at < table->dwNumEntries; ++at) {
const MIB_IFROW *row = &table->table[at];
expect(row->dwIndex != 0, 7);
expect(row->dwDescrLen <= MAXLEN_IFDESCR, 8);
expect(row->dwPhysAddrLen <= MAXLEN_PHYSADDR, 9);
// The name is a wide string in a fixed field, so a terminator has to be somewhere in it.
int terminated = 0;
for (unsigned character = 0; character < 256; ++character)
terminated |= row->wszName[character] == 0;
expect(terminated, 10);
// The same interface, asked for by the index the row carries, is described the same way.
MIB_IFROW one;
zero(&one, sizeof one);
one.dwIndex = row->dwIndex;
expect(GetIfEntry(&one) == NO_ERROR, 11);
expect(same_name(one.wszName, row->wszName), 12);
expect(one.dwType == row->dwType && one.dwMtu == row->dwMtu, 13);
expect(one.dwDescrLen == row->dwDescrLen, 14);
for (DWORD character = 0; character < one.dwDescrLen; ++character)
expect(one.bDescr[character] == row->bDescr[character], 15);
}
// An index no interface has is refused, as Windows refuses it.
MIB_IFROW missing;
zero(&missing, sizeof missing);
missing.dwIndex = 0x7fffffffu;
expect(GetIfEntry(&missing) == ERROR_INVALID_PARAMETER, 16);
// A table one byte short of the rows is refused, and the size it needs comes back.
ULONG short_size = size - 1;
expect(GetIfTable(table, &short_size, 0) == ERROR_INSUFFICIENT_BUFFER, 17);
expect(short_size == size, 18);
// The sorted order lists the rows by index.
expect(GetIfTable(table, &size, 1) == NO_ERROR, 19);
for (DWORD at = 1; at < table->dwNumEntries; ++at)
expect(table->table[at - 1].dwIndex < table->table[at].dwIndex, 20);
LocalFree(table);
return failure;
}