diff options
Diffstat (limited to 'lib/libUPnP/Neptune/Source/System/Bsd/NptBsdNetwork.cpp')
-rw-r--r-- | lib/libUPnP/Neptune/Source/System/Bsd/NptBsdNetwork.cpp | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/lib/libUPnP/Neptune/Source/System/Bsd/NptBsdNetwork.cpp b/lib/libUPnP/Neptune/Source/System/Bsd/NptBsdNetwork.cpp new file mode 100644 index 0000000..6208185 --- /dev/null +++ b/lib/libUPnP/Neptune/Source/System/Bsd/NptBsdNetwork.cpp @@ -0,0 +1,651 @@ +/***************************************************************** +| +| Neptune - Network :: BSD Implementation +| +| (c) 2001-2016 Gilles Boccon-Gibod +| Author: Gilles Boccon-Gibod (bok@bok.net) +| + ****************************************************************/ + +/*---------------------------------------------------------------------- +| includes ++---------------------------------------------------------------------*/ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <net/if.h> +//#include <net/if_arp.h> +#include <netdb.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include "NptConfig.h" +#include "NptTypes.h" +#include "NptStreams.h" +#include "NptThreads.h" +#include "NptNetwork.h" +#include "NptUtils.h" +#include "NptConstants.h" +#include "NptSockets.h" + +#if defined(NPT_CONFIG_HAVE_NET_IF_DL_H) +#include <net/if_dl.h> +#endif +#if defined(NPT_CONFIG_HAVE_NET_IF_TYPES_H) +#include <net/if_types.h> +#endif + +#if defined(NPT_CONFIG_HAVE_GETIFADDRS) +#include <ifaddrs.h> +#endif + +/*---------------------------------------------------------------------- +| platform adaptation ++---------------------------------------------------------------------*/ +#if !defined(IFHWADDRLEN) +#define IFHWADDRLEN 6 // default to 48 bits +#endif +#if !defined(ARPHRD_ETHER) +#define ARPHRD_ETHER 1 +#endif + +#if defined(_SIZEOF_ADDR_IFREQ) +#define NPT_IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr) +#elif defined(NPT_CONFIG_HAVE_SOCKADDR_SA_LEN) +#define NPT_IFREQ_SIZE(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len) +#else +#define NPT_IFREQ_SIZE(ifr) sizeof(*ifr) +#endif + +/*---------------------------------------------------------------------- +| IPv6 support ++---------------------------------------------------------------------*/ +#if defined(NPT_CONFIG_HAVE_ARPA_INET_H) +#include <arpa/inet.h> +#endif + +/*---------------------------------------------------------------------- +| NPT_IpAddress::Any and NPT_IpAddress::Loopback ++---------------------------------------------------------------------*/ +#if defined(NPT_CONFIG_ENABLE_IPV6) +const NPT_IpAddress NPT_IpAddress::Any(NPT_IpAddress::IPV6, in6addr_any.s6_addr, 16); +const NPT_IpAddress NPT_IpAddress::Loopback(NPT_IpAddress::IPV6, in6addr_loopback.s6_addr, 16); +#else +const NPT_IpAddress NPT_IpAddress::Any; +const NPT_IpAddress NPT_IpAddress::Loopback(127,0,0,1); +#endif + +#if defined(NPT_CONFIG_HAVE_INET_NTOP) +/*---------------------------------------------------------------------- +| NPT_IpAddress::ToString ++---------------------------------------------------------------------*/ +NPT_String +NPT_IpAddress::ToString() const +{ + NPT_String address; + char workspace[128]; + int af = AF_INET; +#if defined(NPT_CONFIG_ENABLE_IPV6) + if (m_Type == IPV6) { + af = AF_INET6; + } +#endif + const char* string = inet_ntop(af, &m_Address[0], workspace, sizeof(workspace)); + if (string) { + address = string; + } + + return address; +} +#else +/*---------------------------------------------------------------------- +| NPT_IpAddress::ToString ++---------------------------------------------------------------------*/ +NPT_String +NPT_IpAddress::ToString() const +{ + NPT_String address; + address.Reserve(16); + address += NPT_String::FromInteger(m_Address[0]); + address += '.'; + address += NPT_String::FromInteger(m_Address[1]); + address += '.'; + address += NPT_String::FromInteger(m_Address[2]); + address += '.'; + address += NPT_String::FromInteger(m_Address[3]); + + return address; +} +#endif + +#if defined(NPT_CONFIG_HAVE_INET_PTON) +/*---------------------------------------------------------------------- +| NPT_IpAddress::Parse ++---------------------------------------------------------------------*/ +NPT_Result +NPT_IpAddress::Parse(const char* name) +{ + int result; + + // check the name + if (name == NULL) return NPT_ERROR_INVALID_PARAMETERS; + + // clear the address + NPT_SetMemory(&m_Address[0], 0, sizeof(m_Address)); + +#if defined(NPT_CONFIG_ENABLE_IPV6) + // try IPv6 first + result = inet_pton(AF_INET6, name, &m_Address[0]); + if (result > 0) { + m_Type = IPV6; + return NPT_SUCCESS; + } +#endif + + // try IPv4 next + result = inet_pton(AF_INET, name, &m_Address[0]); + if (result > 0) { + m_Type = IPV4; + return NPT_SUCCESS; + } + + if (result == 0) { + return NPT_ERROR_INVALID_SYNTAX; + } else { + return NPT_FAILURE; + } +} +#else +/*---------------------------------------------------------------------- +| NPT_IpAddress::Parse ++---------------------------------------------------------------------*/ +NPT_Result +NPT_IpAddress::Parse(const char* name) +{ + // check the name + if (name == NULL) return NPT_ERROR_INVALID_PARAMETERS; + + // clear the address + NPT_SetMemory(&m_Address[0], 0, sizeof(m_Address)); + + // parse + unsigned int fragment; + bool fragment_empty = true; + unsigned char address[4]; + unsigned int accumulator; + for (fragment = 0, accumulator = 0; fragment < 4; ++name) { + if (*name == '\0' || *name == '.') { + // fragment terminator + if (fragment_empty) return NPT_ERROR_INVALID_SYNTAX; + address[fragment++] = accumulator; + if (*name == '\0') break; + accumulator = 0; + fragment_empty = true; + } else if (*name >= '0' && *name <= '9') { + // numerical character + accumulator = accumulator*10 + (*name - '0'); + if (accumulator > 255) return NPT_ERROR_INVALID_SYNTAX; + fragment_empty = false; + } else { + // invalid character + return NPT_ERROR_INVALID_SYNTAX; + } + } + + if (fragment == 4 && *name == '\0' && !fragment_empty) { + m_Address[0] = address[0]; + m_Address[1] = address[1]; + m_Address[2] = address[2]; + m_Address[3] = address[3]; + return NPT_SUCCESS; + } else { + return NPT_ERROR_INVALID_SYNTAX; + } +} +#endif + +#if defined(NPT_CONFIG_HAVE_GETIFADDRS) +/*---------------------------------------------------------------------- +| NPT_NetworkInterface::GetNetworkInterfaces ++---------------------------------------------------------------------*/ +NPT_Result +NPT_NetworkInterface::GetNetworkInterfaces(NPT_List<NPT_NetworkInterface*>& interfaces) +{ + interfaces.Clear(); + + struct ifaddrs* addrs = NULL; + int result = getifaddrs(&addrs); + if (result != 0) { + return NPT_ERROR_BASE_UNIX-errno; + } + + for (struct ifaddrs* addr = addrs; + addr; + addr = addr->ifa_next) { + + // get detailed info about the interface + NPT_Flags flags = 0; + // process the flags + if ((addr->ifa_flags & IFF_UP) == 0) { + // the interface is not up, ignore it + continue; + } + if (addr->ifa_flags & IFF_BROADCAST) { + flags |= NPT_NETWORK_INTERFACE_FLAG_BROADCAST; + } + if (addr->ifa_flags & IFF_LOOPBACK) { + flags |= NPT_NETWORK_INTERFACE_FLAG_LOOPBACK; + } +#if defined(IFF_POINTOPOINT) + if (addr->ifa_flags & IFF_POINTOPOINT) { + flags |= NPT_NETWORK_INTERFACE_FLAG_POINT_TO_POINT; + } +#endif // defined(IFF_POINTOPOINT) + if (addr->ifa_flags & IFF_PROMISC) { + flags |= NPT_NETWORK_INTERFACE_FLAG_PROMISCUOUS; + } + if (addr->ifa_flags & IFF_MULTICAST) { + flags |= NPT_NETWORK_INTERFACE_FLAG_MULTICAST; + } + + // get a pointer to an interface we've looped over before + // or create a new one + NPT_NetworkInterface* interface = NULL; + for (NPT_List<NPT_NetworkInterface*>::Iterator iface_iter = interfaces.GetFirstItem(); + iface_iter; + ++iface_iter) { + if ((*iface_iter)->GetName() == (const char*)addr->ifa_name) { + interface = *iface_iter; + break; + } + } + if (interface == NULL) { + // create a new interface object + interface = new NPT_NetworkInterface(addr->ifa_name, flags); + + // add the interface to the list + interfaces.Add(interface); + } + + if (addr->ifa_addr == NULL) { + continue; + } + switch (addr->ifa_addr->sa_family) { + case AF_INET: { + // primary address + NPT_IpAddress primary_address(ntohl(((struct sockaddr_in*)addr->ifa_addr)->sin_addr.s_addr)); + + // broadcast address + NPT_IpAddress broadcast_address; + if (addr->ifa_broadaddr) { + broadcast_address.Set(ntohl(((struct sockaddr_in*)addr->ifa_broadaddr)->sin_addr.s_addr)); + } + + // point to point address + NPT_IpAddress destination_address; + if (addr->ifa_dstaddr) { + destination_address.Set(ntohl(((struct sockaddr_in*)addr->ifa_dstaddr)->sin_addr.s_addr)); + } + + // netmask + NPT_IpAddress netmask(0xFFFFFFFF); + if (addr->ifa_netmask) { + netmask.Set(ntohl(((struct sockaddr_in*)addr->ifa_netmask)->sin_addr.s_addr)); + } + + // add the address to the interface + NPT_NetworkInterfaceAddress iface_address( + primary_address, + broadcast_address, + destination_address, + netmask); + interface->AddAddress(iface_address); + + break; + } + +#if defined(NPT_CONFIG_ENABLE_IPV6) + case AF_INET6: { + // primary address + const struct sockaddr_in6* ipv6_address = (const struct sockaddr_in6*)addr->ifa_addr; + NPT_IpAddress primary_address(NPT_IpAddress::IPV6, ipv6_address->sin6_addr.s6_addr, 16, ipv6_address->sin6_scope_id); + + // empty broadcast address (no broadcast address for IPv6) + NPT_IpAddress broadcast_address(NPT_IpAddress::IPV6); + + // point to point address + NPT_IpAddress destination_address(NPT_IpAddress::IPV6); + if (flags & NPT_NETWORK_INTERFACE_FLAG_POINT_TO_POINT) { + if (addr->ifa_dstaddr) { + ipv6_address = (const struct sockaddr_in6*)addr->ifa_dstaddr; + destination_address.Set(ipv6_address->sin6_addr.s6_addr, 16, ipv6_address->sin6_scope_id); + } + } + + // empty netmask (does not work for IPv6) + NPT_IpAddress netmask((NPT_IpAddress::IPV6)); + + // add the address to the interface + NPT_NetworkInterfaceAddress iface_address( + primary_address, + broadcast_address, + destination_address, + netmask); + interface->AddAddress(iface_address); + + break; + } +#endif + +#if defined(AF_LINK) && defined(NPT_CONFIG_HAVE_SOCKADDR_DL) + case AF_LINK: { + struct sockaddr_dl* mac_addr = (struct sockaddr_dl*)addr->ifa_addr; + NPT_MacAddress::Type mac_addr_type = NPT_MacAddress::TYPE_UNKNOWN; + switch (mac_addr->sdl_type) { +#if defined(IFT_LOOP) + case IFT_LOOP: mac_addr_type = NPT_MacAddress::TYPE_LOOPBACK; break; +#endif +#if defined(IFT_ETHER) + case IFT_ETHER: mac_addr_type = NPT_MacAddress::TYPE_ETHERNET; break; +#endif +#if defined(IFT_PPP) + case IFT_PPP: mac_addr_type = NPT_MacAddress::TYPE_PPP; break; +#endif + } + interface->SetMacAddress(mac_addr_type, + (const unsigned char*)(&mac_addr->sdl_data[mac_addr->sdl_nlen]), + mac_addr->sdl_alen); + break; + } +#endif + } + } + + freeifaddrs(addrs); + + return NPT_SUCCESS; +} +#else +const unsigned int NPT_BSD_NETWORK_MAX_IFCONF_SIZE = 1<<20; + +/*---------------------------------------------------------------------- +| NPT_NetworkInterface::GetNetworkInterfaces ++---------------------------------------------------------------------*/ +NPT_Result +NPT_NetworkInterface::GetNetworkInterfaces(NPT_List<NPT_NetworkInterface*>& interfaces) +{ +//#if defined(NPT_CONFIG_ENABLE_IPV6) +// int net = socket(PF_INET6, SOCK_DGRAM, 0); +//#else + int net = socket(PF_INET, SOCK_DGRAM, 0); +//#endif + if (net < 0) { + return NPT_ERROR_BASE_UNIX-errno; + } + + // Try to get the config until we have enough memory for it + // According to "Unix Network Programming", some implementations + // do not return an error when the supplied buffer is too small + // so we need to try, increasing the buffer size every time, + // until we get the same size twice. We cannot assume success when + // the returned size is smaller than the supplied buffer, because + // some implementations can return less that the buffer size if + // another structure does not fit. + unsigned int buffer_size = 4096; // initial guess + unsigned int last_size = 0; + struct ifconf config; + unsigned char* buffer = NULL; + for (;buffer_size < NPT_BSD_NETWORK_MAX_IFCONF_SIZE;) { + buffer = new unsigned char[buffer_size]; + config.ifc_len = buffer_size; + config.ifc_buf = (char*)buffer; + if (ioctl(net, SIOCGIFCONF, &config) < 0) { + if (errno != EINVAL || last_size != 0) { + delete[] buffer; + close(net); + return NPT_ERROR_BASE_UNIX-errno; + } + } else { + if ((unsigned int)config.ifc_len == last_size) { + // same size, we can use the buffer + break; + } + // different size, we need to reallocate + last_size = config.ifc_len; + } + + // supply 4096 more bytes more next time around + buffer_size += 4096; + delete[] buffer; + buffer = NULL; + } + if (buffer == NULL) { + close(net); + return NPT_ERROR_NOT_ENOUGH_SPACE; + } + + // iterate over all objects + unsigned char *entries; + for (entries = (unsigned char*)config.ifc_req; entries < (unsigned char*)config.ifc_req+config.ifc_len;) { + struct ifreq* entry = (struct ifreq*)entries; + + // point to the next entry + entries += NPT_IFREQ_SIZE(entry); + + // ignore anything except AF_INET, AF_INET6 (if enabled) and AF_LINK addresses + if (entry->ifr_addr.sa_family != AF_INET +#if defined(NPT_CONFIG_ENABLE_IPV6) + && entry->ifr_addr.sa_family != AF_INET6 +#endif +#if defined(AF_LINK) + && entry->ifr_addr.sa_family != AF_LINK +#endif + ) { + continue; + } + + // get detailed info about the interface + NPT_Flags flags = 0; +#if defined(SIOCGIFFLAGS) + struct ifreq query = *entry; + if (ioctl(net, SIOCGIFFLAGS, &query) < 0) continue; + + // process the flags + if ((query.ifr_flags & IFF_UP) == 0) { + // the interface is not up, ignore it + continue; + } + if (query.ifr_flags & IFF_BROADCAST) { + flags |= NPT_NETWORK_INTERFACE_FLAG_BROADCAST; + } + if (query.ifr_flags & IFF_LOOPBACK) { + flags |= NPT_NETWORK_INTERFACE_FLAG_LOOPBACK; + } +#if defined(IFF_POINTOPOINT) + if (query.ifr_flags & IFF_POINTOPOINT) { + flags |= NPT_NETWORK_INTERFACE_FLAG_POINT_TO_POINT; + } +#endif // defined(IFF_POINTOPOINT) + if (query.ifr_flags & IFF_PROMISC) { + flags |= NPT_NETWORK_INTERFACE_FLAG_PROMISCUOUS; + } + if (query.ifr_flags & IFF_MULTICAST) { + flags |= NPT_NETWORK_INTERFACE_FLAG_MULTICAST; + } +#endif // defined(SIOCGIFFLAGS) + + // get a pointer to an interface we've looped over before + // or create a new one + NPT_NetworkInterface* interface = NULL; + for (NPT_List<NPT_NetworkInterface*>::Iterator iface_iter = interfaces.GetFirstItem(); + iface_iter; + ++iface_iter) { + if ((*iface_iter)->GetName() == (const char*)entry->ifr_name) { + interface = *iface_iter; + break; + } + } + if (interface == NULL) { + // create a new interface object + interface = new NPT_NetworkInterface(entry->ifr_name, flags); + + // add the interface to the list + interfaces.Add(interface); + + // get the mac address +#if defined(SIOCGIFHWADDR) + if (ioctl(net, SIOCGIFHWADDR, &query) == 0) { + NPT_MacAddress::Type mac_addr_type; + unsigned int mac_addr_length = IFHWADDRLEN; + switch (query.ifr_addr.sa_family) { +#if defined(ARPHRD_ETHER) + case ARPHRD_ETHER: + mac_addr_type = NPT_MacAddress::TYPE_ETHERNET; + break; +#endif + +#if defined(ARPHRD_LOOPBACK) + case ARPHRD_LOOPBACK: + mac_addr_type = NPT_MacAddress::TYPE_LOOPBACK; + length = 0; + break; +#endif + +#if defined(ARPHRD_PPP) + case ARPHRD_PPP: + mac_addr_type = NPT_MacAddress::TYPE_PPP; + mac_addr_length = 0; + break; +#endif + +#if defined(ARPHRD_IEEE80211) + case ARPHRD_IEEE80211: + mac_addr_type = NPT_MacAddress::TYPE_IEEE_802_11; + break; +#endif + + default: + mac_addr_type = NPT_MacAddress::TYPE_UNKNOWN; + mac_addr_length = sizeof(query.ifr_addr.sa_data); + break; + } + + interface->SetMacAddress(mac_addr_type, (const unsigned char*)query.ifr_addr.sa_data, mac_addr_length); + } +#endif + } + + switch (entry->ifr_addr.sa_family) { + case AF_INET: { + // primary address + NPT_IpAddress primary_address(ntohl(((struct sockaddr_in*)&entry->ifr_addr)->sin_addr.s_addr)); + + // broadcast address + NPT_IpAddress broadcast_address; +#if defined(SIOCGIFBRDADDR) + if (flags & NPT_NETWORK_INTERFACE_FLAG_BROADCAST) { + if (ioctl(net, SIOCGIFBRDADDR, &query) == 0) { + broadcast_address.Set(ntohl(((struct sockaddr_in*)&query.ifr_addr)->sin_addr.s_addr)); + } + } +#endif + + // point to point address + NPT_IpAddress destination_address; +#if defined(SIOCGIFDSTADDR) + if (flags & NPT_NETWORK_INTERFACE_FLAG_POINT_TO_POINT) { + if (ioctl(net, SIOCGIFDSTADDR, &query) == 0) { + destination_address.Set(ntohl(((struct sockaddr_in*)&query.ifr_addr)->sin_addr.s_addr)); + } + } +#endif + + // netmask + NPT_IpAddress netmask(0xFFFFFFFF); +#if defined(SIOCGIFNETMASK) + if (ioctl(net, SIOCGIFNETMASK, &query) == 0) { + netmask.Set(ntohl(((struct sockaddr_in*)&query.ifr_addr)->sin_addr.s_addr)); + } +#endif + + // add the address to the interface + NPT_NetworkInterfaceAddress iface_address( + primary_address, + broadcast_address, + destination_address, + netmask); + interface->AddAddress(iface_address); + + break; + } + +#if defined(NPT_CONFIG_ENABLE_IPV6) + case AF_INET6: { + // primary address + const struct sockaddr_in6* ipv6_address = (const struct sockaddr_in6*)&entry->ifr_addr; + NPT_IpAddress primary_address(NPT_IpAddress::IPV6, ipv6_address->sin6_addr.s6_addr, 16, ipv6_address->sin6_scope_id); + + // empty broadcast address (no broadcast address for IPv6) + NPT_IpAddress broadcast_address(NPT_IpAddress::IPV6); + + // point to point address + NPT_IpAddress destination_address(NPT_IpAddress::IPV6); +#if defined(SIOCGIFDSTADDR) + if (flags & NPT_NETWORK_INTERFACE_FLAG_POINT_TO_POINT) { + if (ioctl(net, SIOCGIFDSTADDR, &query) == 0) { + ipv6_address = (const struct sockaddr_in6*)&query.ifr_addr; + destination_address.Set(ipv6_address->sin6_addr.s6_addr, 16, ipv6_address->sin6_scope_id); + } + } +#endif + + // empty netmask (does not work for IPv6) + NPT_IpAddress netmask((NPT_IpAddress::IPV6)); + + // add the address to the interface + NPT_NetworkInterfaceAddress iface_address( + primary_address, + broadcast_address, + destination_address, + netmask); + interface->AddAddress(iface_address); + + break; + } +#endif + +#if defined(AF_LINK) && defined(NPT_CONFIG_HAVE_SOCKADDR_DL) + case AF_LINK: { + struct sockaddr_dl* mac_addr = (struct sockaddr_dl*)&entry->ifr_addr; + NPT_MacAddress::Type mac_addr_type = NPT_MacAddress::TYPE_UNKNOWN; + switch (mac_addr->sdl_type) { +#if defined(IFT_LOOP) + case IFT_LOOP: mac_addr_type = NPT_MacAddress::TYPE_LOOPBACK; break; +#endif +#if defined(IFT_ETHER) + case IFT_ETHER: mac_addr_type = NPT_MacAddress::TYPE_ETHERNET; break; +#endif +#if defined(IFT_PPP) + case IFT_PPP: mac_addr_type = NPT_MacAddress::TYPE_PPP; break; +#endif + } + interface->SetMacAddress(mac_addr_type, + (const unsigned char*)(&mac_addr->sdl_data[mac_addr->sdl_nlen]), + mac_addr->sdl_alen); + break; + } +#endif + } + } + + // free resources + delete[] buffer; + close(net); + + return NPT_SUCCESS; +} +#endif |