From e09c2b65a18dae09a4a3fffa81ffc909a1851a0b Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 7 Feb 2017 08:52:49 +0000 Subject: [PATCH] Added IPv6 support to the IPAddress class --- modules/juce_core/juce_core.cpp | 4 - .../native/juce_BasicNativeHeaders.h | 3 + .../juce_core/native/juce_win32_Network.cpp | 136 +++++++-- modules/juce_core/network/juce_IPAddress.cpp | 269 ++++++++++++++---- modules/juce_core/network/juce_IPAddress.h | 69 +++-- 5 files changed, 378 insertions(+), 103 deletions(-) diff --git a/modules/juce_core/juce_core.cpp b/modules/juce_core/juce_core.cpp index 17b7157718..279840e444 100644 --- a/modules/juce_core/juce_core.cpp +++ b/modules/juce_core/juce_core.cpp @@ -55,10 +55,6 @@ #if JUCE_WINDOWS #include - #define _WINSOCK_DEPRECATED_NO_WARNINGS 1 - #include - #include - #if ! JUCE_MINGW #pragma warning (push) #pragma warning (disable: 4091) diff --git a/modules/juce_core/native/juce_BasicNativeHeaders.h b/modules/juce_core/native/juce_BasicNativeHeaders.h index 86f9589a17..18f0ef0edf 100644 --- a/modules/juce_core/native/juce_BasicNativeHeaders.h +++ b/modules/juce_core/native/juce_BasicNativeHeaders.h @@ -121,6 +121,7 @@ #define NOMINMAX + #define _WINSOCK_DEPRECATED_NO_WARNINGS 1 #define STRICT 1 #define WIN32_LEAN_AND_MEAN 1 #if JUCE_MINGW @@ -141,6 +142,8 @@ #include #include #include + #include + #include #include #include #include diff --git a/modules/juce_core/native/juce_win32_Network.cpp b/modules/juce_core/native/juce_win32_Network.cpp index 58cc184489..0c76e6063d 100644 --- a/modules/juce_core/native/juce_win32_Network.cpp +++ b/modules/juce_core/native/juce_win32_Network.cpp @@ -400,26 +400,26 @@ private: //============================================================================== -struct GetAdaptersInfoHelper +struct GetAdaptersAddressesHelper { - bool callGetAdaptersInfo() + bool callGetAdaptersAddresses() { DynamicLibrary dll ("iphlpapi.dll"); - JUCE_LOAD_WINAPI_FUNCTION (dll, GetAdaptersInfo, getAdaptersInfo, DWORD, (PIP_ADAPTER_INFO, PULONG)) + JUCE_LOAD_WINAPI_FUNCTION (dll, GetAdaptersAddresses, getAdaptersAddresses, DWORD, (ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG)) - if (getAdaptersInfo == nullptr) + if (getAdaptersAddresses == nullptr) return false; - adapterInfo.malloc (1); - ULONG len = sizeof (IP_ADAPTER_INFO); + adaptersAddresses.malloc (1); + ULONG len = sizeof (IP_ADAPTER_ADDRESSES); - if (getAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW) - adapterInfo.malloc (len, 1); + if (getAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, adaptersAddresses, &len) == ERROR_BUFFER_OVERFLOW) + adaptersAddresses.malloc (len, 1); - return getAdaptersInfo (adapterInfo, &len) == NO_ERROR; + return getAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, adaptersAddresses, &len) == NO_ERROR; } - HeapBlock adapterInfo; + HeapBlock adaptersAddresses; }; namespace MACAddressHelpers @@ -430,15 +430,17 @@ namespace MACAddressHelpers result.addIfNotAlreadyThere (ma); } - static void getViaGetAdaptersInfo (Array& result) + static void getViaGetAdaptersAddresses (Array& result) { - GetAdaptersInfoHelper gah; + GetAdaptersAddressesHelper addressesHelper; - if (gah.callGetAdaptersInfo()) + if (addressesHelper.callGetAdaptersAddresses()) { - for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next) - if (adapter->AddressLength >= 6) - addAddress (result, MACAddress (adapter->Address)); + for (PIP_ADAPTER_ADDRESSES adapter = addressesHelper.adaptersAddresses; adapter != nullptr; adapter = adapter->Next) + { + if (adapter->PhysicalAddressLength >= 6) + addAddress (result, MACAddress (adapter->PhysicalAddress)); + } } } @@ -493,24 +495,108 @@ namespace MACAddressHelpers void MACAddress::findAllAddresses (Array& result) { - MACAddressHelpers::getViaGetAdaptersInfo (result); + MACAddressHelpers::getViaGetAdaptersAddresses (result); MACAddressHelpers::getViaNetBios (result); } -void IPAddress::findAllAddresses (Array& result) +void IPAddress::findAllAddresses (Array& result, bool includeIPv6) { - result.addIfNotAlreadyThere (IPAddress::local()); + result.addIfNotAlreadyThere (IPAddress::local ()); - GetAdaptersInfoHelper gah; + if (includeIPv6) + result.addIfNotAlreadyThere (IPAddress::local (true)); - if (gah.callGetAdaptersInfo()) + GetAdaptersAddressesHelper addressesHelper; + if (addressesHelper.callGetAdaptersAddresses()) { - for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next) + for (PIP_ADAPTER_ADDRESSES adapter = addressesHelper.adaptersAddresses; adapter != nullptr; adapter = adapter->Next) { - IPAddress ip (adapter->IpAddressList.IpAddress.String); + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; + for (pUnicast = adapter->FirstUnicastAddress; pUnicast != nullptr; pUnicast = pUnicast->Next) + { + if (pUnicast->Address.lpSockaddr->sa_family == AF_INET) + { + const sockaddr_in* sa_in = (sockaddr_in*)pUnicast->Address.lpSockaddr; + IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false); + result.addIfNotAlreadyThere (ip); + } + else if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6) + { + const sockaddr_in6* sa_in6 = (sockaddr_in6*)pUnicast->Address.lpSockaddr; + + ByteUnion temp; + uint16 arr[8]; + + for (int i = 0; i < 8; ++i) + { + temp.split[0] = sa_in6->sin6_addr.u.Byte[i * 2 + 1]; + temp.split[1] = sa_in6->sin6_addr.u.Byte[i * 2]; + + arr[i] = temp.combined; + } + + IPAddress ip (arr); + result.addIfNotAlreadyThere (ip); + } + } + + PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = nullptr; + for (pAnycast = adapter->FirstAnycastAddress; pAnycast != nullptr; pAnycast = pAnycast->Next) + { + if (pAnycast->Address.lpSockaddr->sa_family == AF_INET) + { + const sockaddr_in* sa_in = (sockaddr_in*)pAnycast->Address.lpSockaddr; + IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false); + result.addIfNotAlreadyThere (ip); + } + else if (pAnycast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6) + { + const sockaddr_in6* sa_in6 = (sockaddr_in6*)pAnycast->Address.lpSockaddr; + + ByteUnion temp; + uint16 arr[8]; - if (ip != IPAddress::any()) - result.addIfNotAlreadyThere (ip); + for (int i = 0; i < 8; ++i) + { + temp.split[0] = sa_in6->sin6_addr.u.Byte[i * 2 + 1]; + temp.split[1] = sa_in6->sin6_addr.u.Byte[i * 2]; + + arr[i] = temp.combined; + } + + IPAddress ip (arr); + result.addIfNotAlreadyThere (ip); + } + } + + PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = nullptr; + for (pMulticast = adapter->FirstMulticastAddress; pMulticast != nullptr; pMulticast = pMulticast->Next) + { + if (pMulticast->Address.lpSockaddr->sa_family == AF_INET) + { + const sockaddr_in* sa_in = (sockaddr_in*)pMulticast->Address.lpSockaddr; + IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false); + result.addIfNotAlreadyThere (ip); + } + else if (pMulticast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6) + { + const sockaddr_in6* sa_in6 = (sockaddr_in6*)pMulticast->Address.lpSockaddr; + + ByteUnion temp; + uint16 arr[8]; + + for (int i = 0; i < 8; ++i) + { + temp.split[0] = sa_in6->sin6_addr.u.Byte[i * 2 + 1]; + temp.split[1] = sa_in6->sin6_addr.u.Byte[i * 2]; + + arr[i] = temp.combined; + } + + IPAddress ip (arr); + result.addIfNotAlreadyThere (ip); + } + } } } } diff --git a/modules/juce_core/network/juce_IPAddress.cpp b/modules/juce_core/network/juce_IPAddress.cpp index 5fcfd3f6d7..388ee6fc8c 100644 --- a/modules/juce_core/network/juce_IPAddress.cpp +++ b/modules/juce_core/network/juce_IPAddress.cpp @@ -28,61 +28,222 @@ ============================================================================== */ -IPAddress::IPAddress() noexcept +IPAddress::IPAddress (bool IPv6) noexcept : isIPv6 (IPv6) { - address[0] = 0; address[1] = 0; - address[2] = 0; address[3] = 0; + for (int i = 0; i < 16; ++i) + address[i] = 0; } -IPAddress::IPAddress (const uint8 bytes[4]) noexcept +IPAddress::IPAddress (const uint8 bytes[], bool IPv6) noexcept : isIPv6 (IPv6) { - address[0] = bytes[0]; address[1] = bytes[1]; - address[2] = bytes[2]; address[3] = bytes[3]; + for (int i = 0; i < (isIPv6 ? 16 : 4); ++i) + address[i] = bytes[i]; + + if (! isIPv6) + zeroUnusedBytes(); } -IPAddress::IPAddress (uint8 a0, uint8 a1, uint8 a2, uint8 a3) noexcept +IPAddress::IPAddress (const uint16 bytes[8]) noexcept : isIPv6 (true) +{ + ByteUnion temp; + + for (int i = 0; i < 8; ++i) + { + temp.combined = bytes[i]; + + address[i * 2] = temp.split[0]; + address[i * 2 + 1] = temp.split[1]; + } +} + +IPAddress::IPAddress (uint8 a0, uint8 a1, uint8 a2, uint8 a3) noexcept : isIPv6 (false) { address[0] = a0; address[1] = a1; address[2] = a2; address[3] = a3; + + zeroUnusedBytes(); } -IPAddress::IPAddress (uint32 n) noexcept +IPAddress::IPAddress (uint16 a1, uint16 a2, uint16 a3, uint16 a4, + uint16 a5, uint16 a6, uint16 a7, uint16 a8) noexcept : isIPv6 (true) + +{ + uint16 array[8] = { a1, a2, a3, a4, a5, a6, a7, a8 }; + + ByteUnion temp; + + for (int i = 0; i < 8; ++i) + { + temp.combined = array[i]; + address[i * 2] = temp.split[0]; + address[i * 2 + 1] = temp.split[1]; + } +} + +IPAddress::IPAddress (uint32 n) noexcept : isIPv6 (false) { address[0] = (n >> 24); address[1] = (n >> 16) & 255; - address[2] = (n >> 8) & 255; + address[2] = (n >> 8) & 255; address[3] = (n & 255); + + zeroUnusedBytes(); } IPAddress::IPAddress (const String& adr) { - StringArray tokens; - tokens.addTokens (adr, ".", String()); + isIPv6 = adr.contains (":"); + + if (! isIPv6) + { + StringArray tokens; + tokens.addTokens (adr, ".", String()); + + for (int i = 0; i < 4; ++i) + address[i] = (uint8) tokens[i].getIntValue(); + } + else + { + StringArray tokens; + tokens.addTokens (adr.removeCharacters ("[]"), ":", String()); + + if (tokens.contains (StringRef())) // if :: shorthand has been used + { + int idx = tokens.indexOf (StringRef()); + tokens.set (idx, "0"); + + while (tokens.size() < 8) + tokens.insert (idx, "0"); + } + + for (int i = 0; i < 8; ++i) + { + ByteUnion temp; + temp.combined = (uint16) CharacterFunctions::HexParser::parse (tokens[i].getCharPointer()); - for (int i = 0; i < 4; ++i) - address[i] = (uint8) tokens[i].getIntValue(); + address[i * 2] = temp.split[0]; + address[i * 2 + 1] = temp.split[1]; + } + } } String IPAddress::toString() const { - String s ((int) address[0]); + if (! isIPv6) + { + String s ((int) address[0]); + + for (int i = 1; i < 4; ++i) + s << '.' << (int) address[i]; + + return s; + } - for (int i = 1; i < 4; ++i) - s << '.' << (int) address[i]; + String addressString; + ByteUnion temp; + + temp.split[0] = address[0]; + temp.split[1] = address[1]; + + addressString = String (String::toHexString (temp.combined)); + + for (int i = 1; i < 8; ++i) + { + temp.split[0] = address[i * 2]; + temp.split[1] = address[i * 2 + 1]; - return s; + addressString << ':' << String (String::toHexString (temp.combined)); + } + + return getFormattedAddress (addressString); } -IPAddress IPAddress::any() noexcept { return IPAddress(); } -IPAddress IPAddress::broadcast() noexcept { return IPAddress (255, 255, 255, 255); } -IPAddress IPAddress::local() noexcept { return IPAddress (127, 0, 0, 1); } +IPAddress IPAddress::any (bool IPv6) noexcept { return IPAddress (IPv6); } +IPAddress IPAddress::broadcast() noexcept { return IPAddress (255, 255, 255, 255); } +IPAddress IPAddress::local (bool IPv6) noexcept { return IPv6 ? IPAddress (0, 0, 0, 0, 0, 0, 0, 1) + : IPAddress (127, 0, 0, 1); } + +String IPAddress::getFormattedAddress (const String& unformattedAddress) +{ + jassert (unformattedAddress.contains (":") && ! unformattedAddress.contains ("::")); // needs to be an unformatted IPv6 address! + + String portString = unformattedAddress.fromFirstOccurrenceOf ("]", false, true); + String addressString = unformattedAddress.dropLastCharacters (portString.length()).removeCharacters ("[]"); + + StringArray tokens; + tokens.addTokens (addressString, ":", String()); + + int numZeros = 0; + int numZerosTemp = 0; + bool isFirst = false; + bool isLast = false; + + for (int i = 0; i < tokens.size(); ++i) + { + String t = tokens.getReference (i); + + if (t.getHexValue32() == 0x0000) + { + ++numZeros; + + if (i == 0) + isFirst = true; + else if (i == tokens.size() - 1 && numZeros > numZerosTemp) + isLast = true; + + if (t.length() > 1) + addressString = addressString.replace (String::repeatedString ("0", t.length()), "0"); + + if (isFirst && numZerosTemp != 0 && numZeros > numZerosTemp) + isFirst = false; + } + else + { + addressString = addressString.replace (t, t.trimCharactersAtStart ("0").toLowerCase()); + + if (numZeros > 0) + { + if (numZeros > numZerosTemp) + numZerosTemp = numZeros; + + numZeros = 0; + } + } + } + + if (numZerosTemp > numZeros) + numZeros = numZerosTemp; + + if (numZeros > 1) + { + if (numZeros == tokens.size()) + addressString = "::,"; + else + { + String zeroString = isFirst ? String ("0") + String::repeatedString (":0", numZeros - 1) + : String::repeatedString (":0", numZeros); + + addressString = addressString.replaceFirstOccurrenceOf (zeroString, ":"); + + if (isLast) + addressString += String (":"); + } + } + + if (portString.isNotEmpty()) + addressString = String ("[") + addressString + String ("]") + portString; + + return addressString; +} bool IPAddress::operator== (const IPAddress& other) const noexcept { - return address[0] == other.address[0] - && address[1] == other.address[1] - && address[2] == other.address[2] - && address[3] == other.address[3]; + for (int i = 0; i < (isIPv6 ? 16 : 4); ++i) + if (address[i] != other.address[i]) + return false; + + return true; + } bool IPAddress::operator!= (const IPAddress& other) const noexcept @@ -99,53 +260,47 @@ static void addAddress (const sockaddr_in* addr_in, Array& result) result.addIfNotAlreadyThere (IPAddress (ntohl (addr))); } -static void findIPAddresses (int sock, Array& result) +static void addAddress (const sockaddr_in6* addr_in, Array& result) { - ifconf cfg; - HeapBlock buffer; - int bufferSize = 1024; + in6_addr addr = addr_in->sin6_addr; - do + typedef union { - bufferSize *= 2; - buffer.calloc ((size_t) bufferSize); - - cfg.ifc_len = bufferSize; - cfg.ifc_buf = buffer; + uint16 combined; + uint8 split[2]; + } ByteUnion; - if (ioctl (sock, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL) - return; + ByteUnion temp; + uint16 arr[8]; - } while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6))); - - #if JUCE_MAC || JUCE_IOS - while (cfg.ifc_len >= (int) (IFNAMSIZ + sizeof (struct sockaddr_in))) + for (int i = 0; i < 8; ++i) // Swap bytes from network to host order { - if (cfg.ifc_req->ifr_addr.sa_family == AF_INET) // Skip non-internet addresses - addAddress ((const sockaddr_in*) &cfg.ifc_req->ifr_addr, result); + temp.split[0] = addr.s6_addr[i * 2 + 1]; + temp.split[1] = addr.s6_addr[i * 2]; - cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len; - cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len; + arr[i] = temp.combined; } - #else - for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i) - { - const ifreq& item = cfg.ifc_req[i]; - if (item.ifr_addr.sa_family == AF_INET) - addAddress ((const sockaddr_in*) &item.ifr_addr, result); - } - #endif + IPAddress ip (arr); + result.addIfNotAlreadyThere (ip); } -void IPAddress::findAllAddresses (Array& result) +void IPAddress::findAllAddresses (Array& result, bool includeIPv6) { - const int sock = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control + struct ifaddrs *ifaddr, *ifa; + + if (getifaddrs (&ifaddr) == -1) + return; - if (sock >= 0) + for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { - findIPAddresses (sock, result); - ::close (sock); + if (ifa->ifa_addr == nullptr) + continue; + + if (ifa->ifa_addr->sa_family == AF_INET) addAddress ((const sockaddr_in*) ifa->ifa_addr, result); + else if (ifa->ifa_addr->sa_family == AF_INET6 && includeIPv6) addAddress ((const sockaddr_in6*) ifa->ifa_addr, result); } + + freeifaddrs (ifaddr); } #endif diff --git a/modules/juce_core/network/juce_IPAddress.h b/modules/juce_core/network/juce_IPAddress.h index c074991b18..478c0390fa 100644 --- a/modules/juce_core/network/juce_IPAddress.h +++ b/modules/juce_core/network/juce_IPAddress.h @@ -33,48 +33,83 @@ //============================================================================== /** - An IPV4 address. + Represents an IP address. */ class JUCE_API IPAddress { public: //============================================================================== /** Populates a list of all the IP addresses that this machine is using. */ - static void findAllAddresses (Array& results); + static void findAllAddresses (Array& results, bool includeIPv6 = false); //============================================================================== - /** Creates a null address (0.0.0.0). */ - IPAddress() noexcept; + /** Creates a null address - 0.0.0.0 (IPv4) or ::, (IPv6) + @param IPv6 if true indicates that this is an IPv6 address + */ + IPAddress (bool IPv6 = false) noexcept; + + /** Creates an IPv4 or IPv6 address by reading 4 or 16 bytes from an array. + @param IPv6 if true indicates that 16 bytes should be read instead of 4. + */ + explicit IPAddress (const uint8 bytes[], bool IPv6 = false) noexcept; - /** Creates an address from 4 bytes. */ - explicit IPAddress (const uint8 bytes[4]) noexcept; + /** Creates an IPv6 address from an array of 8 16-bit integers */ + explicit IPAddress (const uint16 bytes[8]) noexcept; - /** Creates an address from 4 bytes. */ + /** Creates an IPv4 address from 4 bytes. */ IPAddress (uint8 address1, uint8 address2, uint8 address3, uint8 address4) noexcept; - /** Creates an address from a packed 32-bit integer, where the MSB is - the first number in the address, and the LSB is the last. + /** Creates an IPv6 address from 8 16-bit integers */ + IPAddress (uint16 address1, uint16 address2, uint16 address3, uint16 address4, + uint16 address5, uint16 address6, uint16 address7, uint16 address8) noexcept; + + /** Creates an IPv4 address from a packed 32-bit integer, where the + MSB is the first number in the address, and the LSB is the last. */ explicit IPAddress (uint32 asNativeEndian32Bit) noexcept; - /** Parses a string IP address of the form "a.b.c.d". */ + /** Parses a string IP address of the form "1.2.3.4" (IPv4) or "1:2:3:4:5:6:7:8" (IPv6). */ explicit IPAddress (const String& address); - /** Returns a dot-separated string in the form "1.2.3.4" */ + /** Returns a dot- or colon-separated string in the form "1.2.3.4" (IPv4) or "1:2:3:4:5:6:7:8" (IPv6). */ String toString() const; - /** Returns an address meaning "any" (0.0.0.0) */ - static IPAddress any() noexcept; + /** Returns an IPv4 or IPv6 address meaning "any", equivalent to 0.0.0.0 (IPv4) or ::, (IPv6) */ + static IPAddress any (bool IPv6 = false) noexcept; - /** Returns an address meaning "broadcast" (255.255.255.255) */ + /** Returns an IPv4 address meaning "broadcast" (255.255.255.255) */ static IPAddress broadcast() noexcept; - /** Returns an address meaning "localhost" (127.0.0.1) */ - static IPAddress local() noexcept; + /** Returns an IPv4 or IPv6 address meaning "localhost", equivalent to 127.0.0.1 (IPv4) or ::1 (IPv6) */ + static IPAddress local (bool IPv6 = false) noexcept; + + /** Returns a formatted version of the provided IPv6 address conforming to RFC 5952 with leading zeros suppressed, + lower case characters, and double-colon notation used to represent contiguous 16-bit fields of zeros. + + @param unformattedAddress the IPv6 address to be formatted + */ + static String getFormattedAddress (const String& unformattedAddress); bool operator== (const IPAddress& other) const noexcept; bool operator!= (const IPAddress& other) const noexcept; /** The elements of the IP address. */ - uint8 address[4]; + uint8 address[16]; + + bool isIPv6; + +private: + /** Union used to split a 16-bit unsigned integer into 2 8-bit unsigned integers or vice-versa */ + typedef union + { + uint16 combined; + uint8 split[2]; + } ByteUnion; + + /** Method used to zero the remaining bytes of the address array when creating IPv4 addresses */ + void zeroUnusedBytes() + { + for (int i = 4; i < 16; ++i) + address[i] = 0; + } };