diff --git a/headers/os/net/NetworkAddress.h b/headers/os/net/NetworkAddress.h index ef489a1811..b2738f717e 100644 --- a/headers/os/net/NetworkAddress.h +++ b/headers/os/net/NetworkAddress.h @@ -12,16 +12,21 @@ #include #include +#include #include 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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; diff --git a/headers/os/net/NetworkAddressResolver.h b/headers/os/net/NetworkAddressResolver.h new file mode 100644 index 0000000000..ed507a580e --- /dev/null +++ b/headers/os/net/NetworkAddressResolver.h @@ -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 + + +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 diff --git a/src/kits/network/libnetapi/Jamfile b/src/kits/network/libnetapi/Jamfile index 9f113f0f0e..2ffee488ba 100644 --- a/src/kits/network/libnetapi/Jamfile +++ b/src/kits/network/libnetapi/Jamfile @@ -11,6 +11,7 @@ SharedLibrary libbnetapi.so : NetDebug.cpp NetworkAddress.cpp + NetworkAddressResolver.cpp NetworkInterface.cpp NetworkRoster.cpp diff --git a/src/kits/network/libnetapi/NetworkAddress.cpp b/src/kits/network/libnetapi/NetworkAddress.cpp index 796c11c075..e9d28a7ec1 100644 --- a/src/kits/network/libnetapi/NetworkAddress.cpp +++ b/src/kits/network/libnetapi/NetworkAddress.cpp @@ -6,96 +6,40 @@ #include -#include #include #include #include #include -#include -#include -#include #include -#include -#include -#include +#include #include -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 @@ BNetworkAddress::Unset() 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 - - 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; - - 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; - - 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; - } - - 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(const char* host, const char* service, uint32 flags) { - // Check if the address contains a port + BNetworkAddressResolver resolver; + status_t status = resolver.SetTo(host, service, flags); + if (status != B_OK) + return status; - BString hostAddress(host); + // Prefer IPv6 addresses - BString portString; - strip_port(hostAddress, portString); + 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)); - fStatus = resolve_address(AF_UNSPEC, - host != NULL ? hostAddress.String() : NULL, - portString.Length() == 0 ? NULL : portString.String(), 0, fAddress); - if (fStatus != B_OK) - return fStatus; +status_t +BNetworkAddress::SetTo(int family, const char* host, uint16 port, uint32 flags) +{ + BNetworkAddressResolver resolver; + status_t status = resolver.SetTo(family, host, port, flags); + if (status != B_OK) + return status; - // Set port if specified separately + uint32 cookie = 0; + return resolver.GetNextAddress(&cookie, *this); +} - 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; - } +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; - default: - break; - } - - return B_OK; + uint32 cookie = 0; + return resolver.GetNextAddress(&cookie, *this); } @@ -317,8 +211,24 @@ BNetworkAddress::SetTo(const sockaddr& address) 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); @@ -935,7 +845,7 @@ BNetworkAddress::HostName() const BString -BNetworkAddress::PortName() const +BNetworkAddress::ServiceName() const { // TODO: implement service lookup BString portName; @@ -1062,7 +972,13 @@ BNetworkAddress::operator<(const BNetworkAddress& other) const } -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; } diff --git a/src/kits/network/libnetapi/NetworkAddressResolver.cpp b/src/kits/network/libnetapi/NetworkAddressResolver.cpp new file mode 100644 index 0000000000..6a229439c6 --- /dev/null +++ b/src/kits/network/libnetapi/NetworkAddressResolver.cpp @@ -0,0 +1,221 @@ +/* + * Copyright 2010, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + +#include + + +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; +}