Index: Doxyfile ================================================================== --- Doxyfile +++ Doxyfile @@ -21,10 +21,11 @@ OF_FILE_MANAGER_SUPPORTS_PERMISSIONS \ OF_FILE_MANAGER_SUPPORTS_SYMLINKS \ OF_GENERIC(...)= \ OF_HAVE_BLOCKS \ OF_HAVE_FILES \ + OF_HAVE_GETIFADDRS \ OF_HAVE_SANDBOX \ OF_HAVE_SOCKETS \ OF_HAVE_THREADS \ OF_KINDOF(...)= \ OF_NO_RETURN= \ Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -1746,10 +1746,15 @@ AC_SUBST(OF_SELECT_KERNEL_EVENT_OBSERVER_M, "OFSelectKernelEventObserver.m") ]) ;; esac + + AC_CHECK_HEADERS(ifaddrs.h) + AC_CHECK_FUNC(getifaddrs, [ + AC_DEFINE(OF_HAVE_GETIFADDRS, 1, [Whether we have getifaddrs()]) + ]) AC_ARG_WITH(tls, AS_HELP_STRING([--with-tls], [ enable TLS support using the specified library (yes, openssl, gnutls, securetransport or no)])) Index: src/OFSystemInfo.h ================================================================== --- src/OFSystemInfo.h +++ src/OFSystemInfo.h @@ -16,11 +16,31 @@ #import "OFObject.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN +@class OFDictionary OF_GENERIC(KeyType, ObjectType); @class OFIRI; + +#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_GETIFADDRS) +/** + * @brief A key in the per-interface dictionary returned by + * @ref networkInterfaces. + * + * Possible keys are: + * + * * @ref OFNetworkInterfaceAddresses + */ +typedef OFString *OFNetworkInterfaceInfoKey; + +/** + * @brief The addresses of a network interface. + * + * This maps to an @ref OFData of @ref OFSocketAddress. + */ +extern const OFConstantString *OFNetworkInterfaceAddresses; +#endif /** * @class OFSystemInfo OFSystemInfo.h ObjFW/OFSystemInfo.h * * @brief A class for querying information about the system. @@ -59,10 +79,15 @@ # if defined(OF_POWERPC) || defined(OF_POWERPC64) || defined(DOXYGEN) @property (class, readonly, nonatomic) bool supportsAltiVec; # endif # ifdef OF_WINDOWS @property (class, readonly, nonatomic, getter=isWindowsNT) bool windowsNT; +# endif +# if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_GETIFADDRS) +@property (class, readonly, nonatomic) OFDictionary OF_GENERIC(OFString *, + OFDictionary OF_GENERIC(OFNetworkInterfaceInfoKey, id) *) + *networkInterfaces; # endif #endif /** * @brief Returns the size of a page. @@ -335,11 +360,22 @@ * * @return Whether the application is running on Windows NT */ + (bool)isWindowsNT; #endif + +#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_GETIFADDRS) +/** + * @brief Returns the available (though not necessarily configured) network + * interfaces and information about them. + * + * @return The available network interfaces and information about them + */ ++ (OFDictionary OF_GENERIC(OFString *, OFDictionary + OF_GENERIC(OFNetworkInterfaceInfoKey, id) *) *)networkInterfaces; +#endif + (instancetype)alloc OF_UNAVAILABLE; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/OFSystemInfo.m ================================================================== --- src/OFSystemInfo.m +++ src/OFSystemInfo.m @@ -54,14 +54,18 @@ #endif #import "OFSystemInfo.h" #import "OFApplication.h" #import "OFArray.h" +#import "OFData.h" #import "OFDictionary.h" #import "OFIRI.h" #import "OFLocale.h" #import "OFOnce.h" +#ifdef OF_HAVE_SOCKETS +# import "OFSocket.h" +#endif #import "OFString.h" #if defined(OF_MACOS) || defined(OF_IOS) # ifdef HAVE_SYSDIR_H # include @@ -78,10 +82,14 @@ #endif #if !defined(PATH_MAX) && defined(MAX_PATH) # define PATH_MAX MAX_PATH #endif + +#ifdef HAVE_IFADDRS_H +# include +#endif #if defined(OF_MACOS) || defined(OF_IOS) /* * These have been dropped from newer iOS SDKs, however, their replacements are * not available on iOS < 10. This means it's impossible to search for the @@ -110,10 +118,15 @@ #if defined(OF_X86_64) || defined(OF_X86) struct X86Regs { uint32_t eax, ebx, ecx, edx; }; #endif + +#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_GETIFADDRS) +const OFConstantString *OFNetworkInterfaceAddresses = + @"OFNetworkInterfaceAddresses"; +#endif static size_t pageSize = 4096; static size_t numberOfCPUs = 1; static OFString *operatingSystemName = nil; static OFString *operatingSystemVersion = nil; @@ -817,11 +830,105 @@ + (bool)isWindowsNT { return !(GetVersion() & 0x80000000); } #endif + +#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_GETIFADDRS) +static OFSocketAddress +wrapSockaddr(struct sockaddr *sa) +{ + OFSocketAddress address; + + switch (sa->sa_family) { + case AF_INET: + address.family = OFSocketAddressFamilyIPv4; + memcpy(&address.sockaddr.ipx, sa, sizeof(struct sockaddr_in)); + address.length = (socklen_t)sizeof(struct sockaddr_in); + break; +# ifdef AF_INET6 + case AF_INET6: + address.family = OFSocketAddressFamilyIPv6; + memcpy(&address.sockaddr.ipx, sa, sizeof(struct sockaddr_in6)); + address.length = (socklen_t)sizeof(struct sockaddr_in6); + break; +# endif +# ifdef AF_IPX + case AF_IPX: + address.family = OFSocketAddressFamilyIPX; + memcpy(&address.sockaddr.ipx, sa, sizeof(struct sockaddr_ipx)); + address.length = (socklen_t)sizeof(struct sockaddr_ipx); + break; +# endif +# ifdef AF_APPLETALK + case AF_APPLETALK: + address.family = OFSocketAddressFamilyAppleTalk; + memcpy(&address.sockaddr.at, sa, sizeof(struct sockaddr_at)); + address.length = (socklen_t)sizeof(struct sockaddr_at); + break; +# endif + default: + address.family = OFSocketAddressFamilyUnknown; + memcpy(&address.sockaddr, sa, sizeof(struct sockaddr)); + address.length = sizeof(struct sockaddr); + break; + } + + return address; +} + ++ (OFDictionary OF_GENERIC(OFString *, OFDictionary + OF_GENERIC(OFNetworkInterfaceInfoKey, id) *) *)networkInterfaces +{ + OFMutableDictionary *interfaces = [OFMutableDictionary dictionary]; + OFStringEncoding encoding = [OFLocale encoding]; + struct ifaddrs *ifaddrs; + + if (getifaddrs(&ifaddrs) != 0) + return nil; + + @try { + for (struct ifaddrs *iter = ifaddrs; iter != NULL; + iter = iter->ifa_next) { + OFString *interfaceName = + [OFString stringWithCString: iter->ifa_name + encoding: encoding]; + OFMutableDictionary *interface; + OFMutableData *addresses; + OFSocketAddress address; + + interface = [interfaces objectForKey: interfaceName]; + if (interface == nil) { + interface = [OFMutableDictionary dictionary]; + [interfaces setObject: interface + forKey: interfaceName]; + } + + if (iter->ifa_addr == NULL) + continue; + + addresses = [interface + objectForKey: OFNetworkInterfaceAddresses]; + if (addresses == nil) { + addresses = [OFMutableData + dataWithItemSize: sizeof(OFSocketAddress)]; + [interface + setObject: addresses + forKey: OFNetworkInterfaceAddresses]; + } + + address = wrapSockaddr(iter->ifa_addr); + [addresses addItem: &address]; + } + } @finally { + freeifaddrs(ifaddrs); + } + + return interfaces; +} +#endif - (instancetype)init { OF_INVALID_INIT_METHOD } @end Index: src/objfw-defs.h.in ================================================================== --- src/objfw-defs.h.in +++ src/objfw-defs.h.in @@ -12,10 +12,11 @@ #undef OF_HAVE_BUILTIN_BSWAP64 #undef OF_HAVE_CHMOD #undef OF_HAVE_CHOWN #undef OF_HAVE_FILES #undef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR +#undef OF_HAVE_GETIFADDRS #undef OF_HAVE_IPV6 #undef OF_HAVE_IPX #undef OF_HAVE_LIMITS_H #undef OF_HAVE_LINK #undef OF_HAVE_MAX_ALIGN_T