Author: axeld Date: 2010-08-09 18:31:40 +0200 (Mon, 09 Aug 2010) New Revision: 37988 Changeset: http://dev.haiku-os.org/changeset/37988 Modified: haiku/trunk/headers/os/net/NetworkAddress.h haiku/trunk/headers/os/net/NetworkInterface.h haiku/trunk/headers/os/net/NetworkRoster.h haiku/trunk/src/kits/network/libnetapi/Jamfile haiku/trunk/src/kits/network/libnetapi/NetworkAddress.cpp haiku/trunk/src/kits/network/libnetapi/NetworkInterface.cpp haiku/trunk/src/kits/network/libnetapi/NetworkRoster.cpp Log: * Adapted API as needed, and implemented most of the C++ API - it's completely untested at this point, though. * Will port ifconfig, NetworkStatus, and the Network preferences application later in order to test the API. Modified: haiku/trunk/headers/os/net/NetworkAddress.h =================================================================== --- haiku/trunk/headers/os/net/NetworkAddress.h 2010-08-09 16:30:28 UTC (rev 37987) +++ haiku/trunk/headers/os/net/NetworkAddress.h 2010-08-09 16:31:40 UTC (rev 37988) @@ -12,6 +12,7 @@ #include <sys/socket.h> #include <Archivable.h> +#include <String.h> class BNetworkAddress : public BArchivable { @@ -21,10 +22,12 @@ const char* address, uint16 port = 0); BNetworkAddress(const char* address, uint16 port = 0); - BNetworkAddress(const sockaddr* address); - BNetworkAddress(const sockaddr_in* address); - BNetworkAddress(const sockaddr_in6* address); - BNetworkAddress(const sockaddr_dl* address); + BNetworkAddress(const sockaddr& address); + BNetworkAddress( + const sockaddr_storage& address); + BNetworkAddress(const sockaddr_in& address); + BNetworkAddress(const sockaddr_in6& address); + BNetworkAddress(const sockaddr_dl& address); BNetworkAddress(const in_addr_t address); BNetworkAddress(const in6_addr* address); BNetworkAddress(const BNetworkAddress& other); @@ -38,10 +41,11 @@ status_t SetTo(int family, const char* address, uint16 port = 0); status_t SetTo(const char* address, uint16 port = 0); - void SetTo(const sockaddr* address); - void SetTo(const sockaddr_in* address); - void SetTo(const sockaddr_in6* address); - void SetTo(const sockaddr_dl* address); + void SetTo(const sockaddr& address); + void SetTo(const sockaddr_storage& address); + void SetTo(const sockaddr_in& address); + void SetTo(const sockaddr_in6& address); + void SetTo(const sockaddr_dl& address); void SetTo(const in_addr_t address); void SetTo(const in6_addr* address); void SetTo(const BNetworkAddress& other); @@ -63,6 +67,7 @@ int Family() const; uint16 Port() const; size_t Length() const; + const sockaddr& SockAddr() const; bool IsEmpty() const; bool IsWildcard() const; @@ -84,17 +89,20 @@ uint8* LinkLevelAddress() const; size_t LinkLevelAddressLength() const; - BNetworkAddress ResolvedForDestination( - const BNetworkAddress& destination) const; - void ResolveTo(const BNetworkAddress& address); + status_t ResolveForDestination( + const BNetworkAddress& destination); + status_t ResolveTo(const BNetworkAddress& address); - BString ToString() const; + BString ToString(bool includePort = true) const; BString HostName() const; BString PortName() const; virtual status_t Archive(BMessage* into, bool deep = true) const; static BArchivable* Instantiate(BMessage* archive); + bool Equals(const BNetworkAddress& other, + bool includePort = true) const; + BNetworkAddress& operator=(const BNetworkAddress& other); bool operator==(const BNetworkAddress& other) const; Modified: haiku/trunk/headers/os/net/NetworkInterface.h =================================================================== --- haiku/trunk/headers/os/net/NetworkInterface.h 2010-08-09 16:30:28 UTC (rev 37987) +++ haiku/trunk/headers/os/net/NetworkInterface.h 2010-08-09 16:31:40 UTC (rev 37988) @@ -21,9 +21,12 @@ BNetworkInterfaceAddress(); ~BNetworkInterfaceAddress(); + status_t SetTo(BNetworkInterface& interface, + int32 index); + void SetAddress(BNetworkAddress& address); void SetMask(BNetworkAddress& mask); - void SetBroadcast(BNetworkAddress& mask); + void SetBroadcast(BNetworkAddress& broadcast); BNetworkAddress& Address() { return fAddress; } BNetworkAddress& Mask() { return fMask; } @@ -36,9 +39,10 @@ void SetFlags(uint32 flags); uint32 Flags() const { return fFlags; } + int32 Index() const { return fIndex; } + private: - BNetworkInterface* fInterface; - uint32 fIndex; + int32 fIndex; BNetworkAddress fAddress; BNetworkAddress fMask; BNetworkAddress fBroadcast; @@ -53,9 +57,14 @@ BNetworkInterface(uint32 index); ~BNetworkInterface(); + void Unset(); + void SetTo(const char* name); + status_t SetTo(uint32 index); + bool Exists() const; const char* Name() const; + uint32 Index() const; uint32 Flags() const; uint32 MTU() const; uint32 Type() const; @@ -66,10 +75,13 @@ status_t SetMTU(uint32 mtu); int32 CountAddresses() const; - BNetworkInterfaceAddress* AddressAt(int32 index); + status_t GetAddressAt(int32 index, + BNetworkInterfaceAddress& address); status_t AddAddress( const BNetworkInterfaceAddress& address); + status_t SetAddress( + const BNetworkInterfaceAddress& address); status_t RemoveAddress( const BNetworkInterfaceAddress& address); status_t RemoveAddressAt(int32 index); @@ -78,7 +90,6 @@ private: char fName[IF_NAMESIZE]; - uint32 fIndex; BList fAddresses; }; Modified: haiku/trunk/headers/os/net/NetworkRoster.h =================================================================== --- haiku/trunk/headers/os/net/NetworkRoster.h 2010-08-09 16:30:28 UTC (rev 37987) +++ haiku/trunk/headers/os/net/NetworkRoster.h 2010-08-09 16:31:40 UTC (rev 37988) @@ -6,7 +6,7 @@ #define _NETWORK_ROSTER_H -#include <SupportDefs.h> +#include <Locker.h> class BMessenger; @@ -17,14 +17,14 @@ public: static BNetworkRoster& Default(); - int32 CountInterfaces(); - BNetworkInterface* InterfaceAt(int32 index); + size_t CountInterfaces() const; + status_t GetNextInterface(uint32* cookie, + BNetworkInterface& interface) const; status_t AddInterface( const BNetworkInterface& interface); status_t RemoveInterface( const BNetworkInterface& interface); - status_t RemoveInterfaceAt(int32 index); status_t StartWatching(const BMessenger& target, uint32 eventMask); @@ -33,6 +33,9 @@ private: BNetworkRoster(); ~BNetworkRoster(); + +private: + static BNetworkRoster sDefault; }; Modified: haiku/trunk/src/kits/network/libnetapi/Jamfile =================================================================== --- haiku/trunk/src/kits/network/libnetapi/Jamfile 2010-08-09 16:30:28 UTC (rev 37987) +++ haiku/trunk/src/kits/network/libnetapi/Jamfile 2010-08-09 16:31:40 UTC (rev 37988) @@ -1,6 +1,6 @@ SubDir HAIKU_TOP src kits network libnetapi ; -UsePrivateHeaders net ; +UsePrivateHeaders net shared ; SharedLibrary libbnetapi.so : init.cpp Modified: haiku/trunk/src/kits/network/libnetapi/NetworkAddress.cpp =================================================================== --- haiku/trunk/src/kits/network/libnetapi/NetworkAddress.cpp 2010-08-09 16:30:28 UTC (rev 37987) +++ haiku/trunk/src/kits/network/libnetapi/NetworkAddress.cpp 2010-08-09 16:31:40 UTC (rev 37988) @@ -6,3 +6,972 @@ #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 <sys/sockio.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; +} + + +static status_t +resolve_address(int family, const char* host, const char* port, + int type, sockaddr_storage& address) +{ + 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; +} + + +// #pragma mark - + + +BNetworkAddress::BNetworkAddress(int family, const char* host, uint16 port) +{ + SetTo(family, host, port); +} + + +BNetworkAddress::BNetworkAddress(const char* host, uint16 port) +{ + SetTo(host, port); +} + + +BNetworkAddress::BNetworkAddress(const sockaddr& address) +{ + SetTo(address); +} + + +BNetworkAddress::BNetworkAddress(const sockaddr_storage& address) +{ + SetTo(address); +} + + +BNetworkAddress::BNetworkAddress(const sockaddr_in& address) +{ + SetTo(address); +} + + +BNetworkAddress::BNetworkAddress(const sockaddr_in6& address) +{ + SetTo(address); +} + + +BNetworkAddress::BNetworkAddress(const sockaddr_dl& address) +{ + SetTo(address); +} + + +BNetworkAddress::BNetworkAddress(const in_addr_t address) +{ + SetTo(address); +} + + +BNetworkAddress::BNetworkAddress(const in6_addr* address) +{ + SetTo(address); +} + + +BNetworkAddress::BNetworkAddress(const BNetworkAddress& other) + : + fAddress(other.fAddress), + fStatus(other.fStatus) +{ +} + + +BNetworkAddress::BNetworkAddress(BMessage* archive) +{ + // TODO: implement me + fStatus = B_NO_INIT; +} + + +BNetworkAddress::BNetworkAddress() +{ + Unset(); +} + + +BNetworkAddress::~BNetworkAddress() +{ +} + + +status_t +BNetworkAddress::InitCheck() const +{ + return fStatus; +} + + +void +BNetworkAddress::Unset() +{ + fAddress.ss_family = AF_UNSPEC; + fAddress.ss_len = 2; + fStatus = B_OK; +} + + +status_t +BNetworkAddress::SetTo(int family, const char* host, uint16 port) +{ + // Check if the address contains a port + + BString hostAddress(host); + + BString portString; + if (strip_port(hostAddress, portString) && port == 0) + port = strtoul(portString.String(), NULL, 0); + + // 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; +} + + +status_t +BNetworkAddress::SetTo(const char* host, uint16 port) +{ + // Check if the address contains a port + + BString hostAddress(host); + + BString portString; + strip_port(hostAddress, portString); + + // Resolve address + + 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; +} + + +void +BNetworkAddress::SetTo(const sockaddr& address) +{ + if (address.sa_family == AF_UNSPEC) { + Unset(); + return; + } + + size_t length = min_c(sizeof(sockaddr_storage), address.sa_len); + switch (address.sa_family) { + case AF_INET: + length = sizeof(sockaddr_in); + break; + case AF_INET6: + length = sizeof(sockaddr_in6); + break; + case AF_LINK: + length = sizeof(sockaddr_dl); + break; + } + + memcpy(&fAddress, &address, length); + fAddress.ss_len = length; + fStatus = B_OK; +} + + +void +BNetworkAddress::SetTo(const sockaddr_storage& address) +{ + SetTo((sockaddr&)address); +} + + +void +BNetworkAddress::SetTo(const sockaddr_in& address) +{ + SetTo((sockaddr&)address); +} + + +void +BNetworkAddress::SetTo(const sockaddr_in6& address) +{ + SetTo((sockaddr&)address); +} + + +void +BNetworkAddress::SetTo(const sockaddr_dl& address) +{ + SetTo((sockaddr&)address); +} + + +void +BNetworkAddress::SetTo(const in_addr_t inetAddress) +{ + sockaddr_in& address = (sockaddr_in&)fAddress; + memset(&fAddress, 0, sizeof(sockaddr_storage)); + + address.sin_family = AF_INET; + address.sin_len = sizeof(sockaddr_in); + address.sin_addr.s_addr = inetAddress; + fStatus = B_OK; +} + + +void +BNetworkAddress::SetTo(const in6_addr* inet6Address) +{ + sockaddr_in6& address = (sockaddr_in6&)fAddress; + memset(&fAddress, 0, sizeof(sockaddr_storage)); + + address.sin6_family = AF_INET6; + address.sin6_len = sizeof(sockaddr_in6); + memcpy(address.sin6_addr.s6_addr, inet6Address, + sizeof(address.sin6_addr.s6_addr)); + fStatus = B_OK; +} + + +void +BNetworkAddress::SetTo(const BNetworkAddress& other) +{ + fAddress = other.fAddress; + fStatus = other.fStatus; +} + + +status_t +BNetworkAddress::SetToBroadcast(int family, uint16 port) +{ + if (family != AF_INET) + return fStatus = B_NOT_SUPPORTED; + + memset(&fAddress, 0, sizeof(sockaddr_storage)); + fAddress.ss_family = family; + ((sockaddr_in&)fAddress).sin_addr.s_addr = INADDR_BROADCAST; + + SetPort(port); + return fStatus = B_OK; +} + + +status_t +BNetworkAddress::SetToLocal() +{ + // TODO: choose a local address from the network interfaces + return B_NOT_SUPPORTED; +} + + +status_t +BNetworkAddress::SetToLoopback() +{ + return SetTo("localhost"); +} + + +status_t +BNetworkAddress::SetToMask(int family, uint32 prefixLength) +{ + // TODO: implement SetToMask() + return B_NOT_SUPPORTED; +} + + +status_t +BNetworkAddress::SetToWildcard(int family) +{ + return SetTo(family, NULL); +} + + +void +BNetworkAddress::SetPort(uint16 port) +{ + switch (fAddress.ss_family) { + case AF_INET: + ((sockaddr_in&)fAddress).sin_port = htons(port); + break; + + case AF_INET6: + ((sockaddr_in6&)fAddress).sin6_port = htons(port); + break; + + default: + break; + } +} + + +void +BNetworkAddress::SetToLinkLevel(uint8* address, size_t length) +{ + // TODO: implement me! +} + + +void +BNetworkAddress::SetToLinkLevel(const char* name) +{ + // TODO: implement me! +} + + +void +BNetworkAddress::SetToLinkLevel(uint32 index) +{ + // TODO: implement me! +} + + +void +BNetworkAddress::SetLinkLevelIndex(uint32 index) +{ + // TODO: implement me! +} + + +void +BNetworkAddress::SetLinkLevelType(uint32 type) +{ + // TODO: implement me! +} + + +void +BNetworkAddress::SetLinkLevelFrameType(uint32 frameType) +{ + // TODO: implement me! +} + + +int +BNetworkAddress::Family() const +{ + return fAddress.ss_family; +} + + +uint16 +BNetworkAddress::Port() const +{ + switch (fAddress.ss_family) { + case AF_INET: + return ntohs(((sockaddr_in&)fAddress).sin_port); + + case AF_INET6: + return ntohs(((sockaddr_in6&)fAddress).sin6_port); + + default: + return 0; + } +} + + +size_t +BNetworkAddress::Length() const +{ + return fAddress.ss_len; +} + + +const sockaddr& +BNetworkAddress::SockAddr() const +{ + return (const sockaddr&)fAddress; +} + + +bool +BNetworkAddress::IsEmpty() const +{ + return fAddress.ss_len == 0 || fAddress.ss_family == AF_UNSPEC; +} + + +bool +BNetworkAddress::IsWildcard() const +{ + switch (fAddress.ss_family) { + case AF_INET: + return ((sockaddr_in&)fAddress).sin_addr.s_addr == INADDR_ANY; + + case AF_INET6: + return !memcmp(&((sockaddr_in6&)fAddress).sin6_addr, &in6addr_any, + sizeof(in6_addr)); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsBroadcast() const +{ + switch (fAddress.ss_family) { + case AF_INET: + return ((sockaddr_in&)fAddress).sin_addr.s_addr == INADDR_BROADCAST; + + case AF_INET6: + // There is no broadcast in IPv6, only multicast/anycast + return IN6_IS_ADDR_MULTICAST(&((sockaddr_in6&)fAddress).sin6_addr); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsMulticast() const +{ + switch (fAddress.ss_family) { + case AF_INET: + return IN_MULTICAST(((sockaddr_in&)fAddress).sin_addr.s_addr); + + case AF_INET6: + return IN6_IS_ADDR_MULTICAST(&((sockaddr_in6&)fAddress).sin6_addr); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsMulticastGlobal() const +{ + switch (fAddress.ss_family) { + case AF_INET6: + return IN6_IS_ADDR_MC_GLOBAL(&((sockaddr_in6&)fAddress).sin6_addr); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsMulticastNodeLocal() const +{ + switch (fAddress.ss_family) { + case AF_INET6: + return IN6_IS_ADDR_MC_NODELOCAL( + &((sockaddr_in6&)fAddress).sin6_addr); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsMulticastLinkLocal() const +{ + switch (fAddress.ss_family) { + case AF_INET6: + return IN6_IS_ADDR_MC_LINKLOCAL( + &((sockaddr_in6&)fAddress).sin6_addr); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsMulticastSiteLocal() const +{ + switch (fAddress.ss_family) { + case AF_INET6: + return IN6_IS_ADDR_MC_SITELOCAL( + &((sockaddr_in6&)fAddress).sin6_addr); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsMulticastOrgLocal() const +{ + switch (fAddress.ss_family) { + case AF_INET6: + return IN6_IS_ADDR_MC_ORGLOCAL( + &((sockaddr_in6&)fAddress).sin6_addr); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsLinkLocal() const +{ + // TODO: ipv4 + switch (fAddress.ss_family) { + case AF_INET6: + return IN6_IS_ADDR_LINKLOCAL(&((sockaddr_in6&)fAddress).sin6_addr); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsSiteLocal() const +{ + switch (fAddress.ss_family) { + case AF_INET6: + return IN6_IS_ADDR_SITELOCAL(&((sockaddr_in6&)fAddress).sin6_addr); + + default: + return false; + } +} + + +bool +BNetworkAddress::IsLocal() const +{ + BNetworkRoster& roster = BNetworkRoster::Default(); + + BNetworkInterface interface; + uint32 cookie = 0; + + while (roster.GetNextInterface(&cookie, interface) == B_OK) { + int32 count = interface.CountAddresses(); + for (int32 j = 0; j < count; j++) { + BNetworkInterfaceAddress address; + if (interface.GetAddressAt(j, address) != B_OK) + break; + + if (Equals(address.Address(), false)) + return true; + } + } + + return false; +} + + +uint32 +BNetworkAddress::LinkLevelIndex() const +{ + return ((sockaddr_dl&)fAddress).sdl_index; +} + + +BString +BNetworkAddress::LinkLevelInterface() const +{ + sockaddr_dl& address = (sockaddr_dl&)fAddress; + if (address.sdl_nlen == 0) + return ""; + + BString name; + name.SetTo((const char*)address.sdl_data, address.sdl_nlen); + + return name; +} + + +uint32 +BNetworkAddress::LinkLevelType() const +{ + return ((sockaddr_dl&)fAddress).sdl_type; +} + + +uint32 +BNetworkAddress::LinkLevelFrameType() const +{ + return ((sockaddr_dl&)fAddress).sdl_e_type; +} + + +uint8* +BNetworkAddress::LinkLevelAddress() const +{ + return LLADDR(&(sockaddr_dl&)fAddress); +} + + +size_t +BNetworkAddress::LinkLevelAddressLength() const +{ + return ((sockaddr_dl&)fAddress).sdl_alen; +} + + +status_t +BNetworkAddress::ResolveForDestination(const BNetworkAddress& destination) +{ + if (!IsWildcard()) + return B_OK; + if (destination.fAddress.ss_family != fAddress.ss_family) + return B_BAD_VALUE; + + char buffer[2048]; + memset(buffer, 0, sizeof(buffer)); + + route_entry* route = (route_entry*)buffer; + route->destination = (sockaddr*)&destination.fAddress; + + int socket = ::socket(fAddress.ss_family, SOCK_DGRAM, 0); + if (socket < 0) + return errno; + + if (ioctl(socket, SIOCGETRT, route, sizeof(buffer)) != 0) { + close(socket); + return errno; + } + + uint16 port = Port(); + memcpy(&fAddress, route->source, sizeof(sockaddr_storage)); + SetPort(port); + + return B_OK; +} + + +status_t +BNetworkAddress::ResolveTo(const BNetworkAddress& address) +{ + if (!IsWildcard()) + return B_OK; [... truncated: 664 lines follow ...]