Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -128,10 +128,11 @@ OFHTTPResponse.m \ OFHTTPServer.m \ OFSequencedPacketSocket.m \ OFSocket.m \ OFStreamSocket.m \ + OFSystemInfo+NetworkInterfaces.m \ OFTCPSocket.m \ OFTLSStream.m \ OFUDPSocket.m \ ${USE_SRCS_APPLETALK} \ ${USE_SRCS_IPX} \ ADDED src/OFSystemInfo+NetworkInterfaces.h Index: src/OFSystemInfo+NetworkInterfaces.h ================================================================== --- src/OFSystemInfo+NetworkInterfaces.h +++ src/OFSystemInfo+NetworkInterfaces.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFSystemInfo.h" + +OF_ASSUME_NONNULL_BEGIN + +/** + * @brief A dictionary describing a network interface, as returned by + * @ref networkInterfaces. + * + * Keys are of type @ref OFNetworkInterfaceKey. + */ +typedef OFDictionary OF_GENERIC(OFString *, id) *OFNetworkInterface; + +/** + * @brief A key of an @ref OFNetworkInterface. + * + * Possible values are: + * + * * @ref OFNetworkInterfaceIndex + */ +typedef OFConstantString *OFNetworkInterfaceKey; + +/** + * @brief The index of a network interface. + * + * This maps to an @ref OFNumber. + */ +extern OFNetworkInterfaceKey OFNetworkInterfaceIndex; + +/** + * @brief The IPv4 addresses of a network interface. + * + * This maps to an @ref OFData of @ref OFSocketAddress. + */ +extern OFNetworkInterfaceKey OFNetworkInterfaceIPv4Addresses; + +#ifdef OF_HAVE_IPV6 +/** + * @brief The IPv6 addresses of a network interface. + * + * This maps to an @ref OFData of @ref OFSocketAddress. + */ +extern OFNetworkInterfaceKey OFNetworkInterfaceIPv6Addresses; +#endif + +#ifdef OF_HAVE_IPX +/** + * @brief The IPX addresses of a network interface. + * + * This maps to an @ref OFData of @ref OFSocketAddress. + */ +extern OFNetworkInterfaceKey OFNetworkInterfaceIPXAddresses; +#endif + +#ifdef OF_HAVE_APPLETALK +/** + * @brief The AppleTalk addresses of a network interface. + * + * This maps to an @ref OFData of @ref OFSocketAddress. + */ +extern OFNetworkInterfaceKey OFNetworkInterfaceAppleTalkAddresses; +#endif + +@interface OFSystemInfo (NetworkInterfaces) + +#ifdef OF_HAVE_CLASS_PROPERTIES +@property (class, readonly, nullable, nonatomic) + OFDictionary OF_GENERIC(OFString *, OFNetworkInterface) *networkInterfaces; +#endif + +/** + * @brief Returns the available (though not necessarily configured) network + * interfaces. + * + * @return The available network interfaces + */ ++ (nullable OFDictionary OF_GENERIC(OFString *, OFNetworkInterface) *) + networkInterfaces; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFSystemInfo+NetworkInterfaces.m Index: src/OFSystemInfo+NetworkInterfaces.m ================================================================== --- src/OFSystemInfo+NetworkInterfaces.m +++ src/OFSystemInfo+NetworkInterfaces.m @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFSystemInfo.h" + +OFNetworkInterfaceKey OFNetworkInterfaceIndex = @"OFNetworkInterfaceIndex"; +OFNetworkInterfaceKey OFNetworkInterfaceIPv4Addresses = + @"OFNetworkInterfaceIPv4Addresses"; +#ifdef OF_HAVE_IPV6 +OFNetworkInterfaceKey OFNetworkInterfaceIPv6Addresses = + @"OFNetworkInterfaceIPv6Addresses"; +#endif +#ifdef OF_HAVE_IPX +OFNetworkInterfaceKey OFNetworkInterfaceIPXAddresses = + @"OFNetworkInterfaceIPXAddresses"; +#endif +#ifdef OF_HAVE_APPLETALK +OFNetworkInterfaceKey OFNetworkInterfaceAppleTalkAddresses = + @"OFNetworkInterfaceAppleTalkAddresses"; +#endif + +#ifdef OF_WINDOWS +# include "platform/Windows/OFSystemInfo+NetworkInterfaces.m" +#else +# include "platform/POSIX/OFSystemInfo+NetworkInterfaces.m" +#endif Index: src/OFSystemInfo.h ================================================================== --- src/OFSystemInfo.h +++ src/OFSystemInfo.h @@ -19,70 +19,10 @@ OF_ASSUME_NONNULL_BEGIN @class OFDictionary OF_GENERIC(KeyType, ObjectType); @class OFIRI; -#ifdef OF_HAVE_SOCKETS -/** - * @brief A dictionary describing a network interface, as returned by - * @ref networkInterfaces. - * - * Keys are of type @ref OFNetworkInterfaceKey. - */ -typedef OFDictionary OF_GENERIC(OFString *, id) *OFNetworkInterface; - -/** - * @brief A key of an @ref OFNetworkInterface. - * - * Possible values are: - * - * * @ref OFNetworkInterfaceIndex - */ -typedef OFConstantString *OFNetworkInterfaceKey; - -/** - * @brief The index of a network interface. - * - * This maps to an @ref OFNumber. - */ -extern OFNetworkInterfaceKey OFNetworkInterfaceIndex; - -/** - * @brief The IPv4 addresses of a network interface. - * - * This maps to an @ref OFData of @ref OFSocketAddress. - */ -extern OFNetworkInterfaceKey OFNetworkInterfaceIPv4Addresses; - -# ifdef OF_HAVE_IPV6 -/** - * @brief The IPv6 addresses of a network interface. - * - * This maps to an @ref OFData of @ref OFSocketAddress. - */ -extern OFNetworkInterfaceKey OFNetworkInterfaceIPv6Addresses; -# endif - -# ifdef OF_HAVE_IPX -/** - * @brief The IPX addresses of a network interface. - * - * This maps to an @ref OFData of @ref OFSocketAddress. - */ -extern OFNetworkInterfaceKey OFNetworkInterfaceIPXAddresses; -# endif - -# ifdef OF_HAVE_APPLETALK -/** - * @brief The AppleTalk addresses of a network interface. - * - * This maps to an @ref OFData of @ref OFSocketAddress. - */ -extern OFNetworkInterfaceKey OFNetworkInterfaceAppleTalkAddresses; -# endif -#endif - /** * @class OFSystemInfo OFSystemInfo.h ObjFW/OFSystemInfo.h * * @brief A class for querying information about the system. */ @@ -121,14 +61,10 @@ @property (class, readonly, nonatomic) bool supportsAltiVec; # endif # ifdef OF_WINDOWS @property (class, readonly, nonatomic, getter=isWindowsNT) bool windowsNT; # endif -# ifdef OF_HAVE_SOCKETS -@property (class, readonly, nullable, nonatomic) - OFDictionary OF_GENERIC(OFString *, OFNetworkInterface) *networkInterfaces; -# endif #endif /** * @brief Returns the size of a page. * @@ -401,21 +337,14 @@ * @return Whether the application is running on Windows NT */ + (bool)isWindowsNT; #endif -#ifdef OF_HAVE_SOCKETS -/** - * @brief Returns the available (though not necessarily configured) network - * interfaces. - * - * @return The available network interfaces - */ -+ (nullable OFDictionary OF_GENERIC(OFString *, OFNetworkInterface) *) - networkInterfaces; -#endif - + (instancetype)alloc OF_UNAVAILABLE; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END + +#ifdef OF_HAVE_SOCKETS +# import "OFSystemInfo+NetworkInterfaces.h" +#endif Index: src/OFSystemInfo.m ================================================================== --- src/OFSystemInfo.m +++ src/OFSystemInfo.m @@ -28,26 +28,10 @@ #endif #if defined(OF_MACOS) || defined(OF_IOS) || defined(OF_NETBSD) # include #endif -#ifdef HAVE_NET_IF_H -# include -#endif -#ifdef HAVE_NET_IF_TYPES_H -# include -#endif -#ifdef HAVE_NET_IF_DL_H -# include -#endif -#ifdef HAVE_NETPACKET_PACKET_H -# include -#endif -#ifdef HAVE_SYS_IOCTL_H -# include -#endif - #ifdef OF_AMIGAOS # define Class IntuitionClass # include # include # undef Class @@ -79,14 +63,10 @@ #endif #import "OFIRI.h" #import "OFLocale.h" #import "OFNumber.h" #import "OFOnce.h" -#ifdef OF_HAVE_SOCKETS -# import "OFSocket.h" -# import "OFSocket+Private.h" -#endif #import "OFString.h" #import "OFInvalidFormatException.h" #import "OFOpenItemFailedException.h" @@ -93,16 +73,10 @@ #if defined(OF_MACOS) || defined(OF_IOS) # ifdef HAVE_SYSDIR_H # include # endif #endif -#ifdef OF_WINDOWS -# include -# define interface struct -# include -# undef interface -#endif #ifdef OF_HAIKU # include #endif #ifdef OF_QNX # include @@ -136,28 +110,10 @@ NSSearchPathDirectory, NSSearchPathDomainMask); extern NSSearchPathEnumerationState NSGetNextSearchPathEnumeration( NSSearchPathEnumerationState, char *); #endif -#ifdef OF_HAVE_SOCKETS -OFNetworkInterfaceKey OFNetworkInterfaceIndex = @"OFNetworkInterfaceIndex"; -OFNetworkInterfaceKey OFNetworkInterfaceIPv4Addresses = - @"OFNetworkInterfaceIPv4Addresses"; -# ifdef OF_HAVE_IPV6 -OFNetworkInterfaceKey OFNetworkInterfaceIPv6Addresses = - @"OFNetworkInterfaceIPv6Addresses"; -# endif -# ifdef OF_HAVE_IPX -OFNetworkInterfaceKey OFNetworkInterfaceIPXAddresses = - @"OFNetworkInterfaceIPXAddresses"; -# endif -# ifdef OF_HAVE_APPLETALK -OFNetworkInterfaceKey OFNetworkInterfaceAppleTalkAddresses = - @"OFNetworkInterfaceAppleTalkAddresses"; -# endif -#endif - #if defined(OF_AMD64) || defined(OF_X86) struct X86Regs { uint32_t eax, ebx, ecx, edx; }; #endif @@ -865,372 +821,12 @@ #ifdef OF_WINDOWS + (bool)isWindowsNT { return !(GetVersion() & 0x80000000); } -#endif - -#ifdef OF_HAVE_SOCKETS -static bool -queryNetworkInterfaceIndices(OFMutableDictionary *ret) -{ -# ifdef HAVE_IF_NAMEINDEX - OFStringEncoding encoding = [OFLocale encoding]; - struct if_nameindex *nameindex = if_nameindex(); - - if (nameindex == NULL) - return false; - - @try { - for (size_t i = 0; nameindex[i].if_index != 0; i++) { - OFString *name = [OFString - stringWithCString: nameindex[i].if_name - encoding: encoding]; - OFNumber *index = [OFNumber - numberWithUnsignedInt: nameindex[i].if_index]; - OFMutableDictionary *interface = - [ret objectForKey: name]; - - if (interface == nil) { - interface = [OFMutableDictionary dictionary]; - [ret setObject: interface forKey: name]; - } - - [interface setObject: index - forKey: OFNetworkInterfaceIndex]; - } - } @finally { - if_freenameindex(nameindex); - } - - return true; -# else - return false; -# endif -} - -# if defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) -static bool -queryNetworkInterfaceAddresses(OFMutableDictionary *ret, - OFNetworkInterfaceKey key, OFSocketAddressFamily addressFamily, int family, - size_t sockaddrSize) -{ - OFStringEncoding encoding = [OFLocale encoding]; - int sock = socket(family, SOCK_DGRAM, 0); - struct ifconf ifc; - struct ifreq *ifrs; - OFMutableDictionary *interface; - OFEnumerator *enumerator; - - if (sock < 0) - return false; - - ifrs = malloc(128 * sizeof(struct ifreq)); - if (ifrs == NULL) { - closesocket(sock); - return false; - } - - @try { - char *buffer; - - memset(&ifc, 0, sizeof(ifc)); - ifc.ifc_buf = (void *)ifrs; - ifc.ifc_len = 128 * sizeof(struct ifreq); - if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) - return false; - - buffer = ifc.ifc_buf; - while (buffer < (char *)ifc.ifc_buf + ifc.ifc_len) { - struct ifreq *current = (struct ifreq *)(void *)buffer; - OFString *name; - OFMutableData *addresses; - OFSocketAddress address; - - if (current->ifr_addr.sa_family != family) - goto next; - - name = [OFString stringWithCString: current->ifr_name - encoding: encoding]; - if ((interface = [ret objectForKey: name]) == nil) { - interface = [OFMutableDictionary dictionary]; - [ret setObject: interface forKey: name]; - } - - addresses = [interface objectForKey: key]; - if (addresses == nil) { - addresses = [OFMutableData - dataWithItemSize: sizeof(OFSocketAddress)]; - [interface setObject: addresses forKey: key]; - } - - memset(&address, 0, sizeof(address)); - address.family = addressFamily; - memcpy(&address.sockaddr.in, ¤t->ifr_addr, - sockaddrSize); - - [addresses addItem: &address]; - -next: -# ifdef _SIZEOF_ADDR_IFREQ - buffer += _SIZEOF_ADDR_IFREQ(*current); -# else - buffer += sizeof(struct ifreq); -# endif - } - } @finally { - free(ifrs); - closesocket(sock); - } - - enumerator = [ret objectEnumerator]; - while ((interface = [enumerator nextObject]) != nil) - [[interface objectForKey: key] makeImmutable]; - - return true; -} -# endif - -static bool -queryNetworkInterfaceIPv4Addresses(OFMutableDictionary *ret) -{ -# if defined(OF_WINDOWS) - OFStringEncoding encoding = [OFLocale encoding]; - ULONG adapterInfoSize = sizeof(IP_ADAPTER_INFO); - PIP_ADAPTER_INFO adapterInfo = malloc(adapterInfoSize); - OFMutableDictionary *interface; - OFEnumerator *enumerator; - - if (adapterInfo == NULL) - return false; - - @try { - ULONG error = GetAdaptersInfo(adapterInfo, &adapterInfoSize); - - if (error == ERROR_BUFFER_OVERFLOW) { - PIP_ADAPTER_INFO newAdapterInfo = realloc( - adapterInfo, adapterInfoSize); - - if (newAdapterInfo == NULL) - return false; - - adapterInfo = newAdapterInfo; - error = GetAdaptersInfo(adapterInfo, &adapterInfoSize); - } - - if (error != ERROR_SUCCESS) - return false; - - for (PIP_ADAPTER_INFO iter = adapterInfo; iter != NULL; - iter = iter->Next) { - OFString *name, *IPString; - OFMutableData *addresses; - OFSocketAddress address; - - name = [OFString stringWithCString: iter->AdapterName - encoding: encoding]; - - if ((interface = [ret objectForKey: name]) == nil) { - interface = [OFMutableDictionary dictionary]; - [ret setObject: interface forKey: name]; - } - - IPString = [OFString - stringWithCString: iter->IpAddressList.IpAddress - .String - encoding: encoding]; - - if ([IPString isEqual: @"0.0.0.0"]) - continue; - - if ((addresses = [interface objectForKey: - OFNetworkInterfaceIPv4Addresses]) == nil) { - addresses = [OFMutableData - dataWithItemSize: sizeof(OFSocketAddress)]; - [interface - setObject: addresses - forKey: OFNetworkInterfaceIPv4Addresses]; - } - - address = OFSocketAddressParseIPv4(IPString, 0); - [addresses addItem: &address]; - } - } @finally { - free(adapterInfo); - } - - enumerator = [ret objectEnumerator]; - while ((interface = [enumerator nextObject]) != nil) - [[interface objectForKey: OFNetworkInterfaceIPv4Addresses] - makeImmutable]; - - return true; -# elif defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) - return queryNetworkInterfaceAddresses(ret, - OFNetworkInterfaceIPv4Addresses, OFSocketAddressFamilyIPv4, - AF_INET, sizeof(struct sockaddr_in)); -# else - return false; -# endif -} - -# ifdef OF_HAVE_IPV6 -static bool -queryNetworkInterfaceIPv6Addresses(OFMutableDictionary *ret) -{ -# if defined(OF_LINUX) && defined(OF_HAVE_FILES) - OFFile *file; - OFString *line; - OFMutableDictionary *interface; - OFEnumerator *enumerator; - - @try { - file = [OFFile fileWithPath: @"/proc/net/if_inet6" mode: @"r"]; - } @catch (OFOpenItemFailedException *e) { - return false; - } - - while ((line = [file readLine]) != nil) { - OFArray *components = [line - componentsSeparatedByString: @" " - options: OFStringSkipEmptyComponents]; - OFString *addressString, *name; - OFSocketAddress address; - OFMutableData *addresses; - - if (components.count < 6) - continue; - - addressString = [components objectAtIndex: 0]; - name = [components objectAtIndex: 5]; - - if (addressString.length != 32) - continue; - - if ((interface = [ret objectForKey: name]) == nil) { - interface = [OFMutableDictionary dictionary]; - [ret setObject: interface forKey: name]; - } - - memset(&address, 0, sizeof(address)); - address.family = OFSocketAddressFamilyIPv6; - - for (size_t i = 0; i < 32; i += 2) { - unsigned long long byte; - - @try { - byte = [[addressString - substringWithRange: OFMakeRange(i, 2)] - unsignedLongLongValueWithBase: 16]; - } @catch (OFInvalidFormatException *e) { - goto next_line; - } - - if (byte > 0xFF) - goto next_line; - - address.sockaddr.in6.sin6_addr.s6_addr[i / 2] = - (unsigned char)byte; - } - - if ((addresses = [interface - objectForKey: OFNetworkInterfaceIPv6Addresses]) == nil) { - addresses = [OFMutableData - dataWithItemSize: sizeof(OFSocketAddress)]; - [interface setObject: addresses - forKey: OFNetworkInterfaceIPv6Addresses]; - } - - [addresses addItem: &address]; - -next_line: - continue; - } - - enumerator = [ret objectEnumerator]; - while ((interface = [enumerator nextObject]) != nil) - [[interface objectForKey: OFNetworkInterfaceIPv6Addresses] - makeImmutable]; - - return false; -# elif defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) - return queryNetworkInterfaceAddresses(ret, - OFNetworkInterfaceIPv6Addresses, OFSocketAddressFamilyIPv6, - AF_INET6, sizeof(struct sockaddr_in6)); -# else - return false; -# endif -} -# endif - -# ifdef OF_HAVE_IPX -static bool -queryNetworkInterfaceIPXAddresses(OFMutableDictionary *ret) -{ -# if defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) - return queryNetworkInterfaceAddresses(ret, - OFNetworkInterfaceIPXAddresses, OFSocketAddressFamilyIPX, - AF_IPX, sizeof(struct sockaddr_ipx)); -# else - return false; -# endif -} -# endif - -# ifdef OF_HAVE_APPLETALK -static bool -queryNetworkInterfaceAppleTalkAddresses(OFMutableDictionary *ret) -{ -# if defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) - return queryNetworkInterfaceAddresses(ret, - OFNetworkInterfaceAppleTalkAddresses, - OFSocketAddressFamilyAppleTalk, AF_APPLETALK, - sizeof(struct sockaddr_at)); -# else - return false; -# endif -} -# endif - -+ (OFDictionary OF_GENERIC(OFString *, OFNetworkInterface) *)networkInterfaces -{ - void *pool = objc_autoreleasePoolPush(); - OFMutableDictionary *ret = [OFMutableDictionary dictionary]; - bool success = false; - OFEnumerator *enumerator; - OFMutableDictionary *interface; - - success |= queryNetworkInterfaceIndices(ret); - success |= queryNetworkInterfaceIPv4Addresses(ret); -# ifdef OF_HAVE_IPV6 - success |= queryNetworkInterfaceIPv6Addresses(ret); -# endif -# ifdef OF_HAVE_IPX - success |= queryNetworkInterfaceIPXAddresses(ret); -# endif -# ifdef OF_HAVE_APPLETALK - success |= queryNetworkInterfaceAppleTalkAddresses(ret); -# endif - - if (!success) { - objc_autoreleasePoolPop(pool); - return nil; - } - - enumerator = [ret objectEnumerator]; - while ((interface = [enumerator nextObject]) != nil) - [interface makeImmutable]; - - [ret makeImmutable]; - [ret retain]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} #endif - (instancetype)init { OF_INVALID_INIT_METHOD } @end ADDED src/platform/POSIX/OFSystemInfo+NetworkInterfaces.m Index: src/platform/POSIX/OFSystemInfo+NetworkInterfaces.m ================================================================== --- src/platform/POSIX/OFSystemInfo+NetworkInterfaces.m +++ src/platform/POSIX/OFSystemInfo+NetworkInterfaces.m @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#ifdef HAVE_NET_IF_H +# include +#endif +#ifdef HAVE_NET_IF_TYPES_H +# include +#endif +#ifdef HAVE_NET_IF_DL_H +# include +#endif +#ifdef HAVE_NETPACKET_PACKET_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#import "OFSystemInfo.h" +#import "OFSystemInfo+NetworkInterfaces.h" +#import "OFData.h" +#import "OFDictionary.h" +#ifdef OF_HAVE_FILES + #import "OFFile.h" +#endif +#import "OFLocale.h" +#import "OFNumber.h" +#import "OFSocket.h" +#import "OFSocket+Private.h" +#import "OFString.h" + +#import "OFInvalidFormatException.h" +#import "OFOpenItemFailedException.h" + +@implementation OFSystemInfo (NetworkInterfaces) +static bool +queryNetworkInterfaceIndices(OFMutableDictionary *ret) +{ +#ifdef HAVE_IF_NAMEINDEX + OFStringEncoding encoding = [OFLocale encoding]; + struct if_nameindex *nameindex = if_nameindex(); + + if (nameindex == NULL) + return false; + + @try { + for (size_t i = 0; nameindex[i].if_index != 0; i++) { + OFString *name = [OFString + stringWithCString: nameindex[i].if_name + encoding: encoding]; + OFNumber *index = [OFNumber + numberWithUnsignedInt: nameindex[i].if_index]; + OFMutableDictionary *interface = + [ret objectForKey: name]; + + if (interface == nil) { + interface = [OFMutableDictionary dictionary]; + [ret setObject: interface forKey: name]; + } + + [interface setObject: index + forKey: OFNetworkInterfaceIndex]; + } + } @finally { + if_freenameindex(nameindex); + } + + return true; +#else + return false; +#endif +} + +#if defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) +static bool +queryNetworkInterfaceAddresses(OFMutableDictionary *ret, + OFNetworkInterfaceKey key, OFSocketAddressFamily addressFamily, int family, + size_t sockaddrSize) +{ + OFStringEncoding encoding = [OFLocale encoding]; + int sock = socket(family, SOCK_DGRAM, 0); + struct ifconf ifc; + struct ifreq *ifrs; + OFMutableDictionary *interface; + OFEnumerator *enumerator; + + if (sock < 0) + return false; + + ifrs = malloc(128 * sizeof(struct ifreq)); + if (ifrs == NULL) { + closesocket(sock); + return false; + } + + @try { + char *buffer; + + memset(&ifc, 0, sizeof(ifc)); + ifc.ifc_buf = (void *)ifrs; + ifc.ifc_len = 128 * sizeof(struct ifreq); + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) + return false; + + buffer = ifc.ifc_buf; + while (buffer < (char *)ifc.ifc_buf + ifc.ifc_len) { + struct ifreq *current = (struct ifreq *)(void *)buffer; + OFString *name; + OFMutableData *addresses; + OFSocketAddress address; + + if (current->ifr_addr.sa_family != family) + goto next; + + name = [OFString stringWithCString: current->ifr_name + encoding: encoding]; + if ((interface = [ret objectForKey: name]) == nil) { + interface = [OFMutableDictionary dictionary]; + [ret setObject: interface forKey: name]; + } + + addresses = [interface objectForKey: key]; + if (addresses == nil) { + addresses = [OFMutableData + dataWithItemSize: sizeof(OFSocketAddress)]; + [interface setObject: addresses forKey: key]; + } + + memset(&address, 0, sizeof(address)); + address.family = addressFamily; + memcpy(&address.sockaddr.in, ¤t->ifr_addr, + sockaddrSize); + + [addresses addItem: &address]; + +next: +# ifdef _SIZEOF_ADDR_IFREQ + buffer += _SIZEOF_ADDR_IFREQ(*current); +# else + buffer += sizeof(struct ifreq); +# endif + } + } @finally { + free(ifrs); + closesocket(sock); + } + + enumerator = [ret objectEnumerator]; + while ((interface = [enumerator nextObject]) != nil) + [[interface objectForKey: key] makeImmutable]; + + return true; +} +#endif + +static bool +queryNetworkInterfaceIPv4Addresses(OFMutableDictionary *ret) +{ +#if defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) + return queryNetworkInterfaceAddresses(ret, + OFNetworkInterfaceIPv4Addresses, OFSocketAddressFamilyIPv4, + AF_INET, sizeof(struct sockaddr_in)); +#else + return false; +#endif +} + +#ifdef OF_HAVE_IPV6 +static bool +queryNetworkInterfaceIPv6Addresses(OFMutableDictionary *ret) +{ +# if defined(OF_LINUX) && defined(OF_HAVE_FILES) + OFFile *file; + OFString *line; + OFMutableDictionary *interface; + OFEnumerator *enumerator; + + @try { + file = [OFFile fileWithPath: @"/proc/net/if_inet6" mode: @"r"]; + } @catch (OFOpenItemFailedException *e) { + return false; + } + + while ((line = [file readLine]) != nil) { + OFArray *components = [line + componentsSeparatedByString: @" " + options: OFStringSkipEmptyComponents]; + OFString *addressString, *name; + OFSocketAddress address; + OFMutableData *addresses; + + if (components.count < 6) + continue; + + addressString = [components objectAtIndex: 0]; + name = [components objectAtIndex: 5]; + + if (addressString.length != 32) + continue; + + if ((interface = [ret objectForKey: name]) == nil) { + interface = [OFMutableDictionary dictionary]; + [ret setObject: interface forKey: name]; + } + + memset(&address, 0, sizeof(address)); + address.family = OFSocketAddressFamilyIPv6; + + for (size_t i = 0; i < 32; i += 2) { + unsigned long long byte; + + @try { + byte = [[addressString + substringWithRange: OFMakeRange(i, 2)] + unsignedLongLongValueWithBase: 16]; + } @catch (OFInvalidFormatException *e) { + goto next_line; + } + + if (byte > 0xFF) + goto next_line; + + address.sockaddr.in6.sin6_addr.s6_addr[i / 2] = + (unsigned char)byte; + } + + if ((addresses = [interface + objectForKey: OFNetworkInterfaceIPv6Addresses]) == nil) { + addresses = [OFMutableData + dataWithItemSize: sizeof(OFSocketAddress)]; + [interface setObject: addresses + forKey: OFNetworkInterfaceIPv6Addresses]; + } + + [addresses addItem: &address]; + +next_line: + continue; + } + + enumerator = [ret objectEnumerator]; + while ((interface = [enumerator nextObject]) != nil) + [[interface objectForKey: OFNetworkInterfaceIPv6Addresses] + makeImmutable]; + + return false; +# elif defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) + return queryNetworkInterfaceAddresses(ret, + OFNetworkInterfaceIPv6Addresses, OFSocketAddressFamilyIPv6, + AF_INET6, sizeof(struct sockaddr_in6)); +# else + return false; +# endif +} +#endif + +#ifdef OF_HAVE_IPX +static bool +queryNetworkInterfaceIPXAddresses(OFMutableDictionary *ret) +{ +# if defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) + return queryNetworkInterfaceAddresses(ret, + OFNetworkInterfaceIPXAddresses, OFSocketAddressFamilyIPX, + AF_IPX, sizeof(struct sockaddr_ipx)); +# else + return false; +# endif +} +#endif + +#ifdef OF_HAVE_APPLETALK +static bool +queryNetworkInterfaceAppleTalkAddresses(OFMutableDictionary *ret) +{ +# if defined(HAVE_IOCTL) && defined(HAVE_NET_IF_H) + return queryNetworkInterfaceAddresses(ret, + OFNetworkInterfaceAppleTalkAddresses, + OFSocketAddressFamilyAppleTalk, AF_APPLETALK, + sizeof(struct sockaddr_at)); +# else + return false; +# endif +} +#endif + ++ (OFDictionary OF_GENERIC(OFString *, OFNetworkInterface) *)networkInterfaces +{ + void *pool = objc_autoreleasePoolPush(); + OFMutableDictionary *ret = [OFMutableDictionary dictionary]; + bool success = false; + OFEnumerator *enumerator; + OFMutableDictionary *interface; + + success |= queryNetworkInterfaceIndices(ret); + success |= queryNetworkInterfaceIPv4Addresses(ret); +#ifdef OF_HAVE_IPV6 + success |= queryNetworkInterfaceIPv6Addresses(ret); +#endif +#ifdef OF_HAVE_IPX + success |= queryNetworkInterfaceIPXAddresses(ret); +#endif +#ifdef OF_HAVE_APPLETALK + success |= queryNetworkInterfaceAppleTalkAddresses(ret); +#endif + + if (!success) { + objc_autoreleasePoolPop(pool); + return nil; + } + + enumerator = [ret objectEnumerator]; + while ((interface = [enumerator nextObject]) != nil) + [interface makeImmutable]; + + [ret makeImmutable]; + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} +@end ADDED src/platform/Windows/OFSystemInfo+NetworkInterfaces.m Index: src/platform/Windows/OFSystemInfo+NetworkInterfaces.m ================================================================== --- src/platform/Windows/OFSystemInfo+NetworkInterfaces.m +++ src/platform/Windows/OFSystemInfo+NetworkInterfaces.m @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFSystemInfo.h" +#import "OFSystemInfo+NetworkInterfaces.h" +#import "OFData.h" +#import "OFDictionary.h" +#import "OFLocale.h" +#import "OFSocket.h" +#import "OFString.h" + +#include +#define interface struct +#include +#undef interface + +@implementation OFSystemInfo (NetworkInterfaces) ++ (OFDictionary OF_GENERIC(OFString *, OFNetworkInterface) *)networkInterfaces +{ + void *pool = objc_autoreleasePoolPush(); + OFMutableDictionary *ret = [OFMutableDictionary dictionary]; + OFStringEncoding encoding = [OFLocale encoding]; + ULONG adapterInfoSize; + PIP_ADAPTER_INFO adapterInfo; + OFEnumerator *enumerator; + OFMutableDictionary *interface; + + adapterInfoSize = sizeof(IP_ADAPTER_INFO); + if ((adapterInfo = malloc(adapterInfoSize)) == NULL) { + objc_autoreleasePoolPop(pool); + return nil; + } + + @try { + ULONG error = GetAdaptersInfo(adapterInfo, &adapterInfoSize); + + if (error == ERROR_BUFFER_OVERFLOW) { + PIP_ADAPTER_INFO newAdapterInfo = realloc( + adapterInfo, adapterInfoSize); + + if (newAdapterInfo == NULL) { + objc_autoreleasePoolPop(pool); + return nil; + } + + adapterInfo = newAdapterInfo; + error = GetAdaptersInfo(adapterInfo, &adapterInfoSize); + } + + if (error != ERROR_SUCCESS) { + objc_autoreleasePoolPop(pool); + return nil; + } + + for (PIP_ADAPTER_INFO iter = adapterInfo; iter != NULL; + iter = iter->Next) { + OFString *name, *IPString; + OFMutableData *addresses; + OFSocketAddress address; + + name = [OFString stringWithCString: iter->AdapterName + encoding: encoding]; + + if ((interface = [ret objectForKey: name]) == nil) { + interface = [OFMutableDictionary dictionary]; + [ret setObject: interface forKey: name]; + } + + IPString = [OFString + stringWithCString: iter->IpAddressList.IpAddress + .String + encoding: encoding]; + + if ([IPString isEqual: @"0.0.0.0"]) + continue; + + if ((addresses = [interface objectForKey: + OFNetworkInterfaceIPv4Addresses]) == nil) { + addresses = [OFMutableData + dataWithItemSize: sizeof(OFSocketAddress)]; + [interface + setObject: addresses + forKey: OFNetworkInterfaceIPv4Addresses]; + } + + address = OFSocketAddressParseIPv4(IPString, 0); + [addresses addItem: &address]; + } + } @finally { + free(adapterInfo); + } + + enumerator = [ret objectEnumerator]; + while ((interface = [enumerator nextObject]) != nil) + [interface makeImmutable]; + + [ret makeImmutable]; + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} +@end