* 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.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38039 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2010-08-12 11:27:14 +00:00
parent e05f4ded2d
commit 836c43f2b8
5 changed files with 387 additions and 175 deletions

View File

@ -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 @@ 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;

View File

@ -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

View File

@ -11,6 +11,7 @@ SharedLibrary libbnetapi.so :
NetDebug.cpp
NetworkAddress.cpp
NetworkAddressResolver.cpp
NetworkInterface.cpp
NetworkRoster.cpp

View File

@ -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 @@ 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;
}

View File

@ -0,0 +1,221 @@
/*
* Copyright 2010, Axel Dörfler, axeld@pinc-software.de.
* 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;
}