From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- libdnet-stripped/src/intf-win32.c | 621 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 621 insertions(+) create mode 100644 libdnet-stripped/src/intf-win32.c (limited to 'libdnet-stripped/src/intf-win32.c') diff --git a/libdnet-stripped/src/intf-win32.c b/libdnet-stripped/src/intf-win32.c new file mode 100644 index 0000000..245289f --- /dev/null +++ b/libdnet-stripped/src/intf-win32.c @@ -0,0 +1,621 @@ +/* + * intf-win32.c + * + * Copyright (c) 2002 Dug Song + * + * $Id: intf-win32.c 632 2006-08-10 04:36:52Z dugsong $ + */ + +#ifdef _WIN32 +#include "dnet_winconfig.h" +#else +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include + +#include "dnet.h" +#include "pcap.h" +#include +#include + +int g_has_npcap_loopback = 0; +#define _DEVICE_PREFIX "\\Device\\" + +struct ifcombo { + struct { + DWORD ipv4; + DWORD ipv6; + } *idx; + int cnt; + int max; +}; + +/* XXX - ipifcons.h incomplete, use IANA ifTypes MIB */ +#define MIB_IF_TYPE_TUNNEL 131 +#define MIB_IF_TYPE_MAX MAX_IF_TYPE + +struct intf_handle { + struct ifcombo ifcombo[MIB_IF_TYPE_MAX]; + IP_ADAPTER_ADDRESSES *iftable; +}; + +static char * +_ifcombo_name(int type) +{ + char *name = NULL; + + switch (type) { + case IF_TYPE_ETHERNET_CSMACD: + case IF_TYPE_IEEE80211: + name = "eth"; + break; + case IF_TYPE_ISO88025_TOKENRING: + name = "tr"; + break; + case IF_TYPE_PPP: + name = "ppp"; + break; + case IF_TYPE_SOFTWARE_LOOPBACK: + name = "lo"; + break; + case IF_TYPE_TUNNEL: + name = "tun"; + break; + default: + name = "unk"; + break; + } + return (name); +} + +static int +_ifcombo_type(const char *device) +{ + int type = INTF_TYPE_OTHER; + + if (strncmp(device, "eth", 3) == 0) { + type = INTF_TYPE_ETH; + } else if (strncmp(device, "tr", 2) == 0) { + type = INTF_TYPE_TOKENRING; + } else if (strncmp(device, "ppp", 3) == 0) { + type = INTF_TYPE_PPP; + } else if (strncmp(device, "lo", 2) == 0) { + type = INTF_TYPE_LOOPBACK; + } else if (strncmp(device, "tun", 3) == 0) { + type = INTF_TYPE_TUN; + } + return (type); +} + +static void +_ifcombo_add(struct ifcombo *ifc, DWORD ipv4_idx, DWORD ipv6_idx) +{ + void* pmem = NULL; + if (ifc->cnt == ifc->max) { + if (ifc->idx) { + ifc->max *= 2; + pmem = realloc(ifc->idx, + sizeof(ifc->idx[0]) * ifc->max); + } else { + ifc->max = 8; + pmem = malloc(sizeof(ifc->idx[0]) * ifc->max); + } + if (!pmem) { + /* malloc or realloc failed. Restore state. + * TODO: notify caller. */ + ifc->max = ifc->cnt; + return; + } + ifc->idx = pmem; + } + ifc->idx[ifc->cnt].ipv4 = ipv4_idx; + ifc->idx[ifc->cnt].ipv6 = ipv6_idx; + ifc->cnt++; +} + +/* Map an MIB interface type into an internal interface type. The + internal types are never exposed to users of this library; they exist + only for the sake of ordering interface types within an intf_handle, + which has an array of ifcombo structures ordered by type. Entries in + an intf_handle must not be stored or accessed by a raw MIB type + number because they will not be able to be found by a device name + such as "net0" if the device name does not map exactly to the type. */ +static int +_if_type_canonicalize(int type) +{ + return _ifcombo_type(_ifcombo_name(type)); +} + +static void +_adapter_address_to_entry(intf_t *intf, IP_ADAPTER_ADDRESSES *a, + struct intf_entry *entry) +{ + struct addr *ap, *lap; + int i; + int type; + IP_ADAPTER_UNICAST_ADDRESS *addr; + + /* The total length of the entry may be passed inside entry. + Remember it and clear the entry. */ + u_int intf_len = entry->intf_len; + memset(entry, 0, sizeof(*entry)); + entry->intf_len = intf_len; + + type = _if_type_canonicalize(a->IfType); + for (i = 0; i < intf->ifcombo[type].cnt; i++) { + if (intf->ifcombo[type].idx[i].ipv4 == a->IfIndex && + intf->ifcombo[type].idx[i].ipv6 == a->Ipv6IfIndex) { + break; + } + } + /* XXX - type matches MIB-II ifType. */ + snprintf(entry->intf_name, sizeof(entry->intf_name), "%s%lu", + _ifcombo_name(a->IfType), i); + entry->intf_type = (uint16_t)type; + + /* Get interface flags. */ + entry->intf_flags = 0; + if (a->OperStatus == IfOperStatusUp) + entry->intf_flags |= INTF_FLAG_UP; + if (a->IfType == IF_TYPE_SOFTWARE_LOOPBACK) + entry->intf_flags |= INTF_FLAG_LOOPBACK; + else + entry->intf_flags |= INTF_FLAG_MULTICAST; + + /* Get interface MTU. */ + entry->intf_mtu = a->Mtu; + + /* Get hardware address. */ + if (a->PhysicalAddressLength == ETH_ADDR_LEN) { + entry->intf_link_addr.addr_type = ADDR_TYPE_ETH; + entry->intf_link_addr.addr_bits = ETH_ADDR_BITS; + memcpy(&entry->intf_link_addr.addr_eth, a->PhysicalAddress, + ETH_ADDR_LEN); + } + /* Get addresses. */ + ap = entry->intf_alias_addrs; + lap = ap + ((entry->intf_len - sizeof(*entry)) / + sizeof(entry->intf_alias_addrs[0])); + for (addr = a->FirstUnicastAddress; addr != NULL; addr = addr->Next) { + IP_ADAPTER_PREFIX *prefix; + unsigned short bits; + + /* Find the netmask length. This is stored in a parallel list. + We just take the first one with a matching address family, + but that may not be right. Windows Vista and later has an + OnLinkPrefixLength member that is stored right with the + unicast address. */ + bits = 0; + if (addr->Length >= 48) { + /* "The size of the IP_ADAPTER_UNICAST_ADDRESS structure changed on + * Windows Vista and later. The Length member should be used to determine + * which version of the IP_ADAPTER_UNICAST_ADDRESS structure is being + * used." + * Empirically, 48 is the value on Windows 8.1, so should include the + * OnLinkPrefixLength member.*/ + bits = addr->OnLinkPrefixLength; + } + else { + for (prefix = a->FirstPrefix; prefix != NULL; prefix = prefix->Next) { + if (prefix->Address.lpSockaddr->sa_family == addr->Address.lpSockaddr->sa_family) { + bits = (unsigned short) prefix->PrefixLength; + break; + } + } + } + + if (entry->intf_addr.addr_type == ADDR_TYPE_NONE) { + /* Set primary address if unset. */ + addr_ston(addr->Address.lpSockaddr, &entry->intf_addr); + entry->intf_addr.addr_bits = bits; + } else if (ap < lap) { + /* Set aliases. */ + addr_ston(addr->Address.lpSockaddr, ap); + ap->addr_bits = bits; + ap++; + entry->intf_alias_num++; + } + } + entry->intf_len = (u_int) ((u_char *)ap - (u_char *)entry); +} + +#define NPCAP_SERVICE_REGISTRY_KEY "SYSTEM\\CurrentControlSet\\Services\\npcap" + +/* The name of the Npcap loopback adapter is stored in the npcap service's + * Registry key in the LoopbackAdapter value. For legacy loopback support, this + * is a name like "NPF_{GUID}", but for newer Npcap the name is "NPF_Loopback" + */ +int intf_get_loopback_name(char *buffer, int buf_size) +{ + HKEY hKey; + DWORD type; + int size = buf_size; + int res = 0; + + memset(buffer, 0, buf_size); + + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, NPCAP_SERVICE_REGISTRY_KEY "\\Parameters", 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + if (RegQueryValueExA(hKey, "LoopbackAdapter", 0, &type, (LPBYTE)buffer, &size) == ERROR_SUCCESS && type == REG_SZ) + { + res = 1; + } + else + { + res = 0; + } + + RegCloseKey(hKey); + } + else + { + res = 0; + } + + return res; +} + +static IP_ADAPTER_ADDRESSES* +_update_tables_for_npcap_loopback(IP_ADAPTER_ADDRESSES *p) +{ + IP_ADAPTER_ADDRESSES *a_prev = NULL; + IP_ADAPTER_ADDRESSES *a; + IP_ADAPTER_ADDRESSES *a_original_loopback_prev = NULL; + IP_ADAPTER_ADDRESSES *a_original_loopback = NULL; + IP_ADAPTER_ADDRESSES *a_npcap_loopback = NULL; + static char npcap_loopback_name[1024] = {0}; + + /* Don't bother hitting the registry every time. Not ideal for long-running + * processes, but works for Nmap. */ + if (npcap_loopback_name[0] == '\0') + g_has_npcap_loopback = intf_get_loopback_name(npcap_loopback_name, 1024); + else if (g_has_npcap_loopback == 0) + return p; + + if (!p) + return p; + + /* Loop through the addresses looking for the dummy loopback interface from Windows. */ + for (a = p; a != NULL; a = a->Next) { + if (a->IfType == IF_TYPE_SOFTWARE_LOOPBACK) { + /* Dummy loopback. Keep track of it. */ + a_original_loopback = a; + a_original_loopback_prev = a_prev; + } + else if (strcmp(a->AdapterName, npcap_loopback_name + strlen(_DEVICE_PREFIX) - 1) == 0) { + /* Legacy loopback adapter. The modern one doesn't show up in GetAdaptersAddresses. */ + a_npcap_loopback = a; + } + a_prev = a; + } + + /* If there's no loopback on this system, something's wrong. Windows is + * supposed to create this. */ + if (!a_original_loopback) + return p; + g_has_npcap_loopback = 1; + /* If we didn't find the legacy adapter, use the modern adapter name. */ + if (!a_npcap_loopback) { + /* Overwrite the name we got from the Registry, in case it's a broken legacy + * install, in which case we'll never find the legacy adapter anyway. */ + strlcpy(npcap_loopback_name, _DEVICE_PREFIX "NPF_Loopback", 1024); + /* Overwrite the AdapterName from the system's own loopback adapter with + * the NPF_Loopback name. This is what we use to open the adapter with + * Packet.dll later. */ + a_original_loopback->AdapterName = npcap_loopback_name + sizeof(_DEVICE_PREFIX) - 1; + return p; + } + else { + /* Legacy loopback adapter was found. Copy some key info from the system's + * loopback adapter. */ + a_npcap_loopback->IfType = a_original_loopback->IfType; + a_npcap_loopback->FirstUnicastAddress = a_original_loopback->FirstUnicastAddress; + a_npcap_loopback->FirstPrefix = a_original_loopback->FirstPrefix; + memset(a_npcap_loopback->PhysicalAddress, 0, ETH_ADDR_LEN); + /* Unlink the original loopback adapter from the list. We'll use Npcap's instead. */ + if (a_original_loopback_prev) { + a_original_loopback_prev->Next = a_original_loopback_prev->Next->Next; + return p; + } + else if (a_original_loopback == p) { + return a_original_loopback->Next; + } + else { + return p; + } + } +} + +static int +_refresh_tables(intf_t *intf) +{ + IP_ADAPTER_ADDRESSES *p; + DWORD ret; + ULONG len; + + p = NULL; + /* GetAdaptersAddresses is supposed to return ERROR_BUFFER_OVERFLOW and + * set len to the required size when len is too small. So normally we + * would call the function once with a small len, and then again with + * the longer len. But, on Windows 2003, apparently you only get + * ERROR_BUFFER_OVERFLOW the *first* time you call the function with a + * too-small len--the next time you get ERROR_INVALID_PARAMETER. So this + * function would fail the second and later times it is called. + * + * So, make the first call using a large len. On Windows 2003, this will + * work the first time as long as there are not too many adapters. (It + * will still fail with ERROR_INVALID_PARAMETER if there are too many + * adapters, but this will happen infrequently because of the large + * buffer.) Other systems that always return ERROR_BUFFER_OVERFLOW when + * appropriate will enlarge the buffer if the initial len is too short. */ + len = 16384; + do { + free(p); + p = malloc(len); + if (p == NULL) + return (-1); + ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST, NULL, p, &len); + } while (ret == ERROR_BUFFER_OVERFLOW); + + if (ret != NO_ERROR) { + free(p); + return (-1); + } + p = _update_tables_for_npcap_loopback(p); + intf->iftable = p; + + /* + * Map "unfriendly" win32 interface indices to ours. + * XXX - like IP_ADAPTER_INFO ComboIndex + */ + for (p = intf->iftable; p != NULL; p = p->Next) { + int type; + type = _if_type_canonicalize(p->IfType); + if (type < MIB_IF_TYPE_MAX) + _ifcombo_add(&intf->ifcombo[type], p->IfIndex, p->Ipv6IfIndex); + else + return (-1); + } + return (0); +} + +static IP_ADAPTER_ADDRESSES * +_find_adapter_address(intf_t *intf, const char *device) +{ + IP_ADAPTER_ADDRESSES *a; + char *p = (char *)device; + int n, type = _ifcombo_type(device); + + while (isalpha((int) (unsigned char) *p)) p++; + n = atoi(p); + + for (a = intf->iftable; a != NULL; a = a->Next) { + if ( intf->ifcombo[type].idx != NULL && + intf->ifcombo[type].idx[n].ipv4 == a->IfIndex && + intf->ifcombo[type].idx[n].ipv6 == a->Ipv6IfIndex) { + return a; + } + } + + return NULL; +} + +static IP_ADAPTER_ADDRESSES * +_find_adapter_address_by_index(intf_t *intf, int af, unsigned int index) +{ + IP_ADAPTER_ADDRESSES *a; + + for (a = intf->iftable; a != NULL; a = a->Next) { + if (af == AF_INET && index == a->IfIndex) + return a; + if (af == AF_INET6 && index == a->Ipv6IfIndex) + return a; + } + + return NULL; +} + +intf_t * +intf_open(void) +{ + return (calloc(1, sizeof(intf_t))); +} + +int +intf_get(intf_t *intf, struct intf_entry *entry) +{ + IP_ADAPTER_ADDRESSES *a; + + if (_refresh_tables(intf) < 0) + return (-1); + + a = _find_adapter_address(intf, entry->intf_name); + if (a == NULL) + return (-1); + + _adapter_address_to_entry(intf, a, entry); + + return (0); +} + +/* Look up an interface from an index, such as a sockaddr_in6.sin6_scope_id. */ +int +intf_get_index(intf_t *intf, struct intf_entry *entry, int af, unsigned int index) +{ + IP_ADAPTER_ADDRESSES *a; + + if (_refresh_tables(intf) < 0) + return (-1); + + a = _find_adapter_address_by_index(intf, af, index); + if (a == NULL) + return (-1); + + _adapter_address_to_entry(intf, a, entry); + + return (0); +} + +int +intf_get_src(intf_t *intf, struct intf_entry *entry, struct addr *src) +{ + IP_ADAPTER_ADDRESSES *a; + IP_ADAPTER_UNICAST_ADDRESS *addr; + + if (src->addr_type != ADDR_TYPE_IP) { + errno = EINVAL; + return (-1); + } + if (_refresh_tables(intf) < 0) + return (-1); + + for (a = intf->iftable; a != NULL; a = a->Next) { + for (addr = a->FirstUnicastAddress; addr != NULL; addr = addr->Next) { + struct addr dnet_addr; + + addr_ston(addr->Address.lpSockaddr, &dnet_addr); + if (addr_cmp(&dnet_addr, src) == 0) { + _adapter_address_to_entry(intf, a, entry); + return (0); + } + } + } + errno = ENXIO; + return (-1); +} + +int +intf_get_dst(intf_t *intf, struct intf_entry *entry, struct addr *dst) +{ + errno = ENOSYS; + SetLastError(ERROR_NOT_SUPPORTED); + return (-1); +} + +int +intf_set(intf_t *intf, const struct intf_entry *entry) +{ + /* + * XXX - could set interface up/down via SetIfEntry(), + * but what about the rest of the configuration? :-( + * {Add,Delete}IPAddress for 2000/XP only + */ + errno = ENOSYS; + SetLastError(ERROR_NOT_SUPPORTED); + return (-1); +} + +int +intf_loop(intf_t *intf, intf_handler callback, void *arg) +{ + IP_ADAPTER_ADDRESSES *a; + struct intf_entry *entry; + u_char ebuf[1024]; + int ret = 0; + + if (_refresh_tables(intf) < 0) + return (-1); + + entry = (struct intf_entry *)ebuf; + + for (a = intf->iftable; a != NULL; a = a->Next) { + entry->intf_len = sizeof(ebuf); + _adapter_address_to_entry(intf, a, entry); + if ((ret = (*callback)(entry, arg)) != 0) + break; + } + return (ret); +} + +intf_t * +intf_close(intf_t *intf) +{ + int i; + + if (intf != NULL) { + for (i = 0; i < MIB_IF_TYPE_MAX; i++) { + if (intf->ifcombo[i].idx) + free(intf->ifcombo[i].idx); + } + if (intf->iftable) + free(intf->iftable); + free(intf); + } + return (NULL); +} + +/* Converts a libdnet interface name to its pcap equivalent. The pcap name is + stored in pcapdev up to a length of pcapdevlen, including the terminating + '\0'. Returns -1 on error. */ +int +intf_get_pcap_devname_cached(const char *intf_name, char *pcapdev, int pcapdevlen, int refresh) +{ + IP_ADAPTER_ADDRESSES *a; + static pcap_if_t *pcapdevs = NULL; + pcap_if_t *pdev; + intf_t *intf; + char errbuf[PCAP_ERRBUF_SIZE]; + + if ((intf = intf_open()) == NULL) + return (-1); + if (_refresh_tables(intf) < 0) { + intf_close(intf); + return (-1); + } + a = _find_adapter_address(intf, intf_name); + + if (a == NULL) { + intf_close(intf); + return (-1); + } + + if (refresh) { + pcap_freealldevs(pcapdevs); + pcapdevs = NULL; + } + + if (pcapdevs == NULL) { + if (pcap_findalldevs(&pcapdevs, errbuf) == -1) { + intf_close(intf); + return (-1); + } + } + + /* Loop through all the pcap devices until we find a match. */ + for (pdev = pcapdevs; pdev != NULL; pdev = pdev->next) { + char *name; + + if (pdev->name == NULL || strlen(pdev->name) < sizeof(_DEVICE_PREFIX)) + continue; + /* "\\Device\\NPF_{GUID}" + * "\\Device\\NPF_Loopback" + * Find the '{'after device prefix. + */ + name = strchr(pdev->name + sizeof(_DEVICE_PREFIX) - 1, '{'); + if (name == NULL) { + /* If no GUID, just match the whole device name */ + name = pdev->name + sizeof(_DEVICE_PREFIX) - 1; + } + if (strcmp(name, a->AdapterName) == 0) + break; + } + if (pdev != NULL) + strlcpy(pcapdev, pdev->name, pcapdevlen); + intf_close(intf); + if (pdev == NULL) + return -1; + else + return 0; +} +int +intf_get_pcap_devname(const char *intf_name, char *pcapdev, int pcapdevlen) +{ + return intf_get_pcap_devname_cached(intf_name, pcapdev, pcapdevlen, 0); +} -- cgit v1.2.3