diff --git a/src/kits/network/getifaddrs.cpp b/src/kits/network/getifaddrs.cpp index eeb64e7be9..1b8d2e5d11 100644 --- a/src/kits/network/getifaddrs.cpp +++ b/src/kits/network/getifaddrs.cpp @@ -4,74 +4,105 @@ * * Authors: * Adrien Destugues, pulkomandy@pulkomandy.tk + * Axel Dörfler, axeld@pinc-software.de */ +#include +#include + #include #include +#include #include #include #include #include #include -#include - #include #include -int getifaddrs(struct ifaddrs **ifap) +static sockaddr* +copy_address(const sockaddr& address) { - if (ifap == NULL) { + if (address.sa_family == AF_UNSPEC) + return NULL; + + sockaddr_storage* copy = new (std::nothrow) sockaddr_storage; + if (copy == NULL) + return NULL; + + size_t length = std::min(sizeof(sockaddr_storage), (size_t)address.sa_len); + switch (address.sa_family) { + case AF_INET: + length = sizeof(sockaddr_in); + break; + case AF_INET6: + length = sizeof(sockaddr_in6); + break; + } + memcpy(copy, &address, length); + copy->ss_len = length; + return (sockaddr*)copy; +} + + +/*! Returns a chained list of all interfaces. + + We follow BSD semantics, and only return one entry per interface, + not per address; since this is mainly used by NetBSD's netresolv, it's + probably what it expects. +*/ +int +getifaddrs(struct ifaddrs** _ifaddrs) +{ + if (_ifaddrs == NULL) { errno = B_BAD_VALUE; return -1; } -#if 0 - uint32 cookie; -#endif - - // get a list of all interfaces - int socket = ::socket(AF_INET, SOCK_DGRAM, 0); if (socket < 0) - return errno; + return -1; FileDescriptorCloser closer(socket); - // get interface count + // Get interface count ifconf config; config.ifc_len = sizeof(config.ifc_value); if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0) - return errno; + return -1; size_t count = (size_t)config.ifc_value; - if (count == 0) - return B_BAD_VALUE; + if (count == 0) { + errno = B_BAD_VALUE; + return -1; + } - // allocate a buffer for ifreqs for all interfaces + // Allocate a buffer for ifreqs for all interfaces char* buffer = (char*)malloc(count * sizeof(struct ifreq)); - if (buffer == NULL) - return B_NO_MEMORY; + if (buffer == NULL) { + errno = B_NO_MEMORY; + return -1; + } MemoryDeleter deleter(buffer); - // get interfaces configuration + // Get interfaces configuration config.ifc_len = count * sizeof(struct ifreq); config.ifc_buf = buffer; if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0) - return errno; + return -1; ifreq* interfaces = (ifreq*)buffer; ifreq* end = (ifreq*)(buffer + config.ifc_len); - struct ifaddrs* previous = NULL; - struct ifaddrs* current = NULL; for (uint32_t i = 0; interfaces < end; i++) { - current = new(std::nothrow) ifaddrs(); + struct ifaddrs* current = new(std::nothrow) ifaddrs(); if (current == NULL) { freeifaddrs(previous); errno = B_NO_MEMORY; @@ -83,73 +114,45 @@ int getifaddrs(struct ifaddrs **ifap) previous = current; current->ifa_name = strdup(interfaces[0].ifr_name); - current->ifa_addr = new sockaddr(interfaces[0].ifr_addr); - // still needs: flags, netmask, dst:broadcast addr + current->ifa_addr = copy_address(interfaces[0].ifr_addr); + current->ifa_netmask = NULL; + current->ifa_dstaddr = NULL; + current->ifa_data = NULL; - ioctl(socket, SIOCGIFFLAGS, &interfaces[0], sizeof(struct ifconf)); - current->ifa_flags = interfaces[0].ifr_flags; - ioctl(socket, SIOCGIFNETMASK, &interfaces[0], sizeof(struct ifconf)); - current->ifa_netmask = new sockaddr(interfaces[0].ifr_mask); - ioctl(socket, SIOCGIFDSTADDR, &interfaces[0], sizeof(struct ifconf)); - current->ifa_dstaddr = new sockaddr(interfaces[0].ifr_dstaddr); + ifreq request; + strlcpy(request.ifr_name, interfaces[0].ifr_name, IF_NAMESIZE); - // TODO we may also need to get other addresses for the same interface? -#if 0 - BNetworkInterfaceAddress address; - int32 i = 0; - while (interface->GetAddressAt(i++, address) == B_OK) { - if (interface == NULL) { - freeifaddrs(previous); - errno = B_NO_MEMORY; - return -1; - } - - current = new(std::nothrow) ifaddrs(); - if (current == NULL) { - freeifaddrs(previous); - errno = B_NO_MEMORY; - return -1; - } - - // Chain this interface with the next one - current->ifa_next = previous; - previous = current; - - current->ifa_data = interface; - current->ifa_name = interface->Name(); - // Points to the name in the BNetworkInterface instance, which - // is added as ifa_data so freeifaddrs can release it. - current->ifa_flags = address.Flags(); - current->ifa_addr = new sockaddr(address.Address().SockAddr()); - current->ifa_netmask = new sockaddr(address.Mask().SockAddr()); - current->ifa_dstaddr = new sockaddr(address.Destination().SockAddr()); + if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) + current->ifa_flags = request.ifr_flags; + if (ioctl(socket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) + == 0) { + current->ifa_netmask = copy_address(request.ifr_mask); + } + if (ioctl(socket, SIOCGIFDSTADDR, &request, sizeof(struct ifreq)) + == 0) { + current->ifa_dstaddr = copy_address(request.ifr_dstaddr); } - interface = new(std::nothrow) BNetworkInterface(); -#endif - - // move on to next interface + // Move on to next interface interfaces = (ifreq*)((uint8_t*)interfaces + _SIZEOF_ADDR_IFREQ(interfaces[0])); } - *ifap = current; - + *_ifaddrs = previous; return 0; } void -freeifaddrs(struct ifaddrs *ifa) +freeifaddrs(struct ifaddrs* ifa) { - struct ifaddrs* next; while (ifa != NULL) { - free ((void*)ifa->ifa_name); + free((void*)ifa->ifa_name); delete ifa->ifa_addr; delete ifa->ifa_netmask; delete ifa->ifa_dstaddr; - next = ifa->ifa_next; + struct ifaddrs* next = ifa->ifa_next; delete ifa; ifa = next; }