Author: axeld Date: 2010-08-12 13:27:14 +0200 (Thu, 12 Aug 2010) New Revision: 38039 Changeset: http://dev.haiku-os.org/changeset/38039 Added: haiku/trunk/headers/os/net/NetworkAddressResolver.h haiku/trunk/src/kits/network/libnetapi/NetworkAddressResolver.cpp Modified: haiku/trunk/headers/os/net/NetworkAddress.h haiku/trunk/src/kits/network/libnetapi/Jamfile haiku/trunk/src/kits/network/libnetapi/NetworkAddress.cpp Log: * Factored out a BNetworkAddressResolver from BNetworkAddress, that also allows to iterate over all possible addresses, as suggested privately by Rene. * Added flags to the resolving methods that allow more control over the addresses returned. * Added setters to BNetworkAddress that accept a service name instead of port number, renamed PortName() to ServiceName(). * Made the sockaddr* cast operators return a const sockaddr as it was supposed to be, although I should probably add non-const ones as well. * This also simplified the code somewhat. Modified: haiku/trunk/headers/os/net/NetworkAddress.h =================================================================== --- haiku/trunk/headers/os/net/NetworkAddress.h 2010-08-12 11:26:25 UTC (rev 38038) +++ haiku/trunk/headers/os/net/NetworkAddress.h 2010-08-12 11:27:14 UTC (rev 38039) @@ -12,16 +12,21 @@ #include <sys/socket.h> #include <Archivable.h> +#include <NetworkAddressResolver.h> #include <String.h> class BNetworkAddress : public BArchivable { public: BNetworkAddress(); - BNetworkAddress(int family, - const char* address, uint16 port = 0); BNetworkAddress(const char* address, - uint16 port = 0); + uint16 port = 0, uint32 flags = 0); + BNetworkAddress(const char* address, + const char* service, uint32 flags = 0); + BNetworkAddress(int family, const char* address, + uint16 port = 0, uint32 flags = 0); + BNetworkAddress(int family, const char* address, + const char* service, uint32 flags = 0); BNetworkAddress(const sockaddr& address); BNetworkAddress( const sockaddr_storage& address); @@ -38,10 +43,16 @@ void Unset(); + status_t SetTo(const char* address, uint16 port = 0, + uint32 flags = 0); + status_t SetTo(const char* address, const char* service, + uint32 flags = 0); status_t SetTo(int family, const char* address, - uint16 port = 0); - status_t SetTo(const char* address, uint16 port = 0); + uint16 port = 0, uint32 flags = 0); + status_t SetTo(int family, const char* address, + const char* service, uint32 flags = 0); void SetTo(const sockaddr& address); + void SetTo(const sockaddr& address, size_t length); void SetTo(const sockaddr_storage& address); void SetTo(const sockaddr_in& address); void SetTo(const sockaddr_in6& address); @@ -56,6 +67,7 @@ status_t SetToMask(int family, uint32 prefixLength); status_t SetToWildcard(int family); void SetPort(uint16 port); + status_t SetPort(const char* service); void SetToLinkLevel(uint8* address, size_t length); void SetToLinkLevel(const char* name); @@ -97,7 +109,7 @@ BString ToString(bool includePort = true) const; BString HostName() const; - BString PortName() const; + BString ServiceName() const; virtual status_t Archive(BMessage* into, bool deep = true) const; static BArchivable* Instantiate(BMessage* archive); @@ -111,8 +123,8 @@ bool operator!=(const BNetworkAddress& other) const; bool operator<(const BNetworkAddress& other) const; - operator sockaddr*() const; - operator sockaddr&() const; + operator const sockaddr*() const; + operator const sockaddr&() const; private: sockaddr_storage fAddress; Added: haiku/trunk/headers/os/net/NetworkAddressResolver.h =================================================================== --- haiku/trunk/headers/os/net/NetworkAddressResolver.h (rev 0) +++ haiku/trunk/headers/os/net/NetworkAddressResolver.h 2010-08-12 11:27:14 UTC (rev 38039) @@ -0,0 +1,62 @@ +/* + * Copyright 2010, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _NETWORK_ADDRESS_RESOLVER_H +#define _NETWORK_ADDRESS_RESOLVER_H + + +#include <SupportDefs.h> + + +class BNetworkAddress; +struct addrinfo; + + +// flags for name resolution +enum { + B_NO_ADDRESS_RESOLUTION = 0x0001, + B_UNCONFIGURED_ADDRESS_FAMILIES = 0x0002, +}; + + +class BNetworkAddressResolver { +public: + BNetworkAddressResolver(); + BNetworkAddressResolver(const char* address, + uint16 port = 0, uint32 flags = 0); + BNetworkAddressResolver(const char* address, + const char* service, uint32 flags = 0); + BNetworkAddressResolver(int family, + const char* address, uint16 port = 0, + uint32 flags = 0); + BNetworkAddressResolver(int family, + const char* address, const char* service, + uint32 flags = 0); + ~BNetworkAddressResolver(); + + status_t InitCheck() const; + + void Unset(); + + status_t SetTo(const char* address, uint16 port = 0, + uint32 flags = 0); + status_t SetTo(const char* address, const char* service, + uint32 flags = 0); + status_t SetTo(int family, const char* address, + uint16 port = 0, uint32 flags = 0); + status_t SetTo(int family, const char* address, + const char* service, uint32 flags = 0); + + status_t GetNextAddress(uint32* cookie, + BNetworkAddress& address) const; + status_t GetNextAddress(int family, uint32* cookie, + BNetworkAddress& address) const; + +private: + addrinfo* fInfo; + status_t fStatus; +}; + + +#endif // _NETWORK_ADDRESS_RESOLVER_H Modified: haiku/trunk/src/kits/network/libnetapi/Jamfile =================================================================== --- haiku/trunk/src/kits/network/libnetapi/Jamfile 2010-08-12 11:26:25 UTC (rev 38038) +++ haiku/trunk/src/kits/network/libnetapi/Jamfile 2010-08-12 11:27:14 UTC (rev 38039) @@ -11,6 +11,7 @@ NetDebug.cpp NetworkAddress.cpp + NetworkAddressResolver.cpp NetworkInterface.cpp NetworkRoster.cpp Modified: haiku/trunk/src/kits/network/libnetapi/NetworkAddress.cpp =================================================================== --- haiku/trunk/src/kits/network/libnetapi/NetworkAddress.cpp 2010-08-12 11:26:25 UTC (rev 38038) +++ haiku/trunk/src/kits/network/libnetapi/NetworkAddress.cpp 2010-08-12 11:27:14 UTC (rev 38039) @@ -6,96 +6,40 @@ #include <NetworkAddress.h> -#include <ByteOrder.h> #include <NetworkInterface.h> #include <NetworkRoster.h> #include <arpa/inet.h> #include <errno.h> -#include <net/if.h> -#include <net/route.h> -#include <netdb.h> #include <netinet/in.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> +#include <stdio.h> #include <sys/sockio.h> -static bool -strip_port(BString& host, BString& port) +BNetworkAddress::BNetworkAddress(const char* host, uint16 port, uint32 flags) { - int32 separator = host.FindFirst(':'); - if (separator != -1) { - // looks like there is a port - host.CopyInto(port, separator + 1, -1); - host.Truncate(separator); - - return true; - } - - return false; + SetTo(host, port, flags); } -static status_t -resolve_address(int family, const char* host, const char* port, - int type, sockaddr_storage& address) +BNetworkAddress::BNetworkAddress(const char* host, const char* service, + uint32 flags) { - addrinfo hint = {0}; - hint.ai_family = family; - hint.ai_socktype = type; - hint.ai_protocol = 0; - - if (host == NULL && port == NULL) { - port = "0"; - hint.ai_flags = AI_PASSIVE; - } - - addrinfo* info; - int status = getaddrinfo(host, port, &hint, &info); - if (status != 0) { - // TODO: improve error reporting - return B_ERROR; - } - - bool foundAddress = false; - - if (family == AF_UNSPEC) { - // Prefer IPv6 addresses over IPv4 addresses - - for (const addrinfo* next = info; next != NULL; next = next->ai_next) { - if (next->ai_family == AF_INET6) { - memcpy(&address, next->ai_addr, next->ai_addrlen); - foundAddress = true; - break; - } - } - } - - if (!foundAddress) { - // No preferred, or no IPv6 address found, just take the first one - // that works - memcpy(&address, info->ai_addr, info->ai_addrlen); - } - - freeaddrinfo(info); - return B_OK; + SetTo(host, service, flags); } -// #pragma mark - - - -BNetworkAddress::BNetworkAddress(int family, const char* host, uint16 port) +BNetworkAddress::BNetworkAddress(int family, const char* host, uint16 port, + uint32 flags) { - SetTo(family, host, port); + SetTo(family, host, port, flags); } -BNetworkAddress::BNetworkAddress(const char* host, uint16 port) +BNetworkAddress::BNetworkAddress(int family, const char* host, + const char* service, uint32 flags) { - SetTo(host, port); + SetTo(family, host, service, flags); } @@ -184,119 +128,69 @@ status_t -BNetworkAddress::SetTo(int family, const char* host, uint16 port) +BNetworkAddress::SetTo(const char* host, uint16 port, uint32 flags) { - // Check if the address contains a port + BNetworkAddressResolver resolver; + status_t status = resolver.SetTo(host, port, flags); + if (status != B_OK) + return status; - BString hostAddress(host); + // Prefer IPv6 addresses - BString portString; - if (strip_port(hostAddress, portString) && port == 0) - port = strtoul(portString.String(), NULL, 0); + uint32 cookie = 0; + status = resolver.GetNextAddress(AF_INET6, &cookie, *this); + if (status == B_OK) + return B_OK; - // Resolve address + cookie = 0; + return resolver.GetNextAddress(&cookie, *this); +} - memset(&fAddress, 0, sizeof(sockaddr_storage)); - fAddress.ss_family = family; - if (host != NULL) { - switch (family) { - case AF_INET: - { - hostent* server = gethostbyname(hostAddress.String()); - if (server == NULL) - return errno; +status_t +BNetworkAddress::SetTo(const char* host, const char* service, uint32 flags) +{ + BNetworkAddressResolver resolver; + status_t status = resolver.SetTo(host, service, flags); + if (status != B_OK) + return status; - struct sockaddr_in& address = (sockaddr_in&)fAddress; - address.sin_port = htons(port); - address.sin_addr.s_addr = *(in_addr_t*)server->h_addr_list[0]; - break; - } - - default: - { - fStatus = resolve_address(family, host, "0", 0, fAddress); - if (fStatus != B_OK) - return fStatus; + // Prefer IPv6 addresses - if (family == AF_INET6) { - struct sockaddr_in6& address = (sockaddr_in6&)fAddress; - address.sin6_port = htons(port); - } - break; - } - } - } else { - switch (fAddress.ss_family) { - case AF_INET: - { - struct sockaddr_in& address = (sockaddr_in&)fAddress; - address.sin_port = htons(port); - address.sin_addr.s_addr = INADDR_ANY; - break; - } + uint32 cookie = 0; + status = resolver.GetNextAddress(AF_INET6, &cookie, *this); + if (status == B_OK) + return B_OK; - case AF_INET6: - { - struct sockaddr_in6& address = (sockaddr_in6&)fAddress; - address.sin6_port = htons(port); - address.sin6_addr = in6addr_any; - break; - } - - default: - return B_NOT_SUPPORTED; - } - } - - return fStatus = B_OK; + cookie = 0; + return resolver.GetNextAddress(&cookie, *this); } status_t -BNetworkAddress::SetTo(const char* host, uint16 port) +BNetworkAddress::SetTo(int family, const char* host, uint16 port, uint32 flags) { - // Check if the address contains a port + BNetworkAddressResolver resolver; + status_t status = resolver.SetTo(family, host, port, flags); + if (status != B_OK) + return status; - BString hostAddress(host); + uint32 cookie = 0; + return resolver.GetNextAddress(&cookie, *this); +} - BString portString; - strip_port(hostAddress, portString); - // Resolve address +status_t +BNetworkAddress::SetTo(int family, const char* host, const char* service, + uint32 flags) +{ + BNetworkAddressResolver resolver; + status_t status = resolver.SetTo(family, host, service, flags); + if (status != B_OK) + return status; - memset(&fAddress, 0, sizeof(sockaddr_storage)); - - fStatus = resolve_address(AF_UNSPEC, - host != NULL ? hostAddress.String() : NULL, - portString.Length() == 0 ? NULL : portString.String(), 0, fAddress); - if (fStatus != B_OK) - return fStatus; - - // Set port if specified separately - - switch (fAddress.ss_family) { - case AF_INET: - { - struct sockaddr_in& address = (sockaddr_in&)fAddress; - if (address.sin_port == 0) - address.sin_port = htons(port); - break; - } - - case AF_INET6: - { - struct sockaddr_in6& address = (sockaddr_in6&)fAddress; - if (address.sin6_port == 0) - address.sin6_port = htons(port); - break; - } - - default: - break; - } - - return B_OK; + uint32 cookie = 0; + return resolver.GetNextAddress(&cookie, *this); } @@ -317,10 +211,26 @@ length = sizeof(sockaddr_in6); break; case AF_LINK: - length = sizeof(sockaddr_dl); + { + sockaddr_dl& link = (sockaddr_dl&)address; + length = sizeof(sockaddr_dl) - sizeof(link.sdl_data) + link.sdl_alen + + link.sdl_nlen + link.sdl_slen; break; + } } + SetTo(address, length); +} + + +void +BNetworkAddress::SetTo(const sockaddr& address, size_t length) +{ + if (address.sa_family == AF_UNSPEC || length == 0) { + Unset(); + return; + } + memcpy(&fAddress, &address, length); fAddress.ss_len = length; fStatus = B_OK; @@ -935,7 +845,7 @@ BString -BNetworkAddress::PortName() const +BNetworkAddress::ServiceName() const { // TODO: implement service lookup BString portName; @@ -1062,7 +972,13 @@ } -BNetworkAddress::operator sockaddr*() const +BNetworkAddress::operator const sockaddr*() const { - return (sockaddr*)&fAddress; + return (const sockaddr*)&fAddress; } + + +BNetworkAddress::operator const sockaddr&() const +{ + return (const sockaddr&)fAddress; +} Added: haiku/trunk/src/kits/network/libnetapi/NetworkAddressResolver.cpp =================================================================== --- haiku/trunk/src/kits/network/libnetapi/NetworkAddressResolver.cpp (rev 0) +++ haiku/trunk/src/kits/network/libnetapi/NetworkAddressResolver.cpp 2010-08-12 11:27:14 UTC (rev 38039) @@ -0,0 +1,221 @@ +/* + * Copyright 2010, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include <NetworkAddressResolver.h> + +#include <netdb.h> + +#include <NetworkAddress.h> + + +static bool +strip_port(BString& host, BString& port) +{ + int32 separator = host.FindFirst(':'); + if (separator != -1) { + // looks like there is a port + host.CopyInto(port, separator + 1, -1); + host.Truncate(separator); + + return true; + } + + return false; +} + + +// #pragma mark - + + +BNetworkAddressResolver::BNetworkAddressResolver() + : + fInfo(NULL), + fStatus(B_NO_INIT) +{ +} + + +BNetworkAddressResolver::BNetworkAddressResolver(const char* address, + uint16 port, uint32 flags) + : + fInfo(NULL), + fStatus(B_NO_INIT) +{ + SetTo(address, port, flags); +} + +BNetworkAddressResolver::BNetworkAddressResolver(const char* address, + const char* service, uint32 flags) + : + fInfo(NULL), + fStatus(B_NO_INIT) +{ + SetTo(address, service, flags); +} + + +BNetworkAddressResolver::BNetworkAddressResolver(int family, + const char* address, uint16 port, uint32 flags) + : + fInfo(NULL), + fStatus(B_NO_INIT) +{ + SetTo(family, address, port, flags); +} + + +BNetworkAddressResolver::BNetworkAddressResolver(int family, + const char* address, const char* service, uint32 flags) + : + fInfo(NULL), + fStatus(B_NO_INIT) +{ + SetTo(family, address, service, flags); +} + + +BNetworkAddressResolver::~BNetworkAddressResolver() +{ + Unset(); +} + + +status_t +BNetworkAddressResolver::InitCheck() const +{ + return fStatus; +} + + +void +BNetworkAddressResolver::Unset() +{ + if (fInfo != NULL) { + freeaddrinfo(fInfo); + fInfo = NULL; + } + fStatus = B_NO_INIT; +} + + +status_t +BNetworkAddressResolver::SetTo(const char* address, uint16 port, uint32 flags) +{ + return SetTo(AF_UNSPEC, address, port, flags); +} + + +status_t +BNetworkAddressResolver::SetTo(const char* address, const char* service, + uint32 flags) +{ + return SetTo(AF_UNSPEC, address, service, flags); +} + + +status_t +BNetworkAddressResolver::SetTo(int family, const char* address, uint16 port, + uint32 flags) +{ + BString service; + service << port; + + return SetTo(family, address, port != 0 ? service.String() : NULL, flags); +} + + +status_t +BNetworkAddressResolver::SetTo(int family, const char* host, + const char* service, uint32 flags) +{ + Unset(); + + // Check if the address contains a port + + BString hostString(host); + + BString portString; + if (!strip_port(hostString, portString) && service != NULL) + portString = service; + + // Resolve address + + addrinfo hint = {0}; + hint.ai_family = family; + if ((flags & B_UNCONFIGURED_ADDRESS_FAMILIES) == 0) + hint.ai_flags |= AI_ADDRCONFIG; + if ((flags & B_NO_ADDRESS_RESOLUTION) != 0) + hint.ai_flags |= AI_NUMERICHOST; + + if (host == NULL && portString.Length() == 0) { + portString = "0"; + hint.ai_flags |= AI_PASSIVE; + } + + int status = getaddrinfo(host != NULL ? hostString.String() : NULL, + portString.Length() != 0 ? portString.String() : NULL, &hint, &fInfo); + if (status != 0) { + // TODO: improve error reporting + return fStatus = B_ERROR; + } + + return fStatus = B_OK; +} + + +status_t +BNetworkAddressResolver::GetNextAddress(uint32* cookie, + BNetworkAddress& address) const +{ + if (fStatus != B_OK) + return fStatus; + + // Skip previous info entries + + addrinfo* info = fInfo; + int32 first = *cookie; + for (int32 index = 0; index < first && info != NULL; index++) { + info = info->ai_next; + } + + if (info == NULL) + return B_BAD_VALUE; + + // Return current + + address.SetTo(*info->ai_addr, info->ai_addrlen); + (*cookie)++; + + return B_OK; +} + + +status_t +BNetworkAddressResolver::GetNextAddress(int family, uint32* cookie, + BNetworkAddress& address) const +{ + if (fStatus != B_OK) + return fStatus; + + // Skip previous info entries, and those that have a non-matching family + + addrinfo* info = fInfo; + int32 first = *cookie; + for (int32 index = 0; index < first && info != NULL; index++) { + while (info != NULL && info->ai_family != family) + info = info->ai_next; + } + + if (info == NULL) + return B_BAD_VALUE; + + // Return current + + address.SetTo(*info->ai_addr, info->ai_addrlen); + (*cookie)++; + + return B_OK; +}