| @@ -48,14 +48,40 @@ class ConsoleUnitTestRunner : public UnitTestRunner | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| int main() | |||
| int main (int argc, char **argv) | |||
| { | |||
| ConsoleLogger logger; | |||
| Logger::setCurrentLogger (&logger); | |||
| ConsoleUnitTestRunner runner; | |||
| runner.runAllTests(); | |||
| ArgumentList args (argc, argv); | |||
| if (args.size() == 0) | |||
| { | |||
| runner.runAllTests(); | |||
| } | |||
| else | |||
| { | |||
| if (args.containsOption ("--help|-h")) | |||
| { | |||
| std::cout << argv[0] << " [--help|-h] [--category category] [--list-categories]" << std::endl; | |||
| return 0; | |||
| } | |||
| if (args.containsOption ("--list-categories")) | |||
| { | |||
| for (auto& category : UnitTest::getAllCategories()) | |||
| std::cout << category << std::endl; | |||
| return 0; | |||
| } | |||
| if (args.containsOption ("--category")) | |||
| runner.runTestsInCategory (args.getArgumentAfterOption ("--category").text); | |||
| } | |||
| Logger::setCurrentLogger (nullptr); | |||
| @@ -190,6 +190,7 @@ | |||
| //============================================================================== | |||
| #if JUCE_MAC || JUCE_IOS | |||
| #include "native/juce_mac_Files.mm" | |||
| #include "native/juce_mac_linux_IPAddress.h" | |||
| #include "native/juce_mac_Network.mm" | |||
| #include "native/juce_mac_Strings.mm" | |||
| #include "native/juce_mac_SystemStats.mm" | |||
| @@ -207,6 +208,7 @@ | |||
| #elif JUCE_LINUX | |||
| #include "native/juce_linux_CommonFile.cpp" | |||
| #include "native/juce_linux_Files.cpp" | |||
| #include "native/juce_mac_linux_IPAddress.h" | |||
| #include "native/juce_linux_Network.cpp" | |||
| #if JUCE_USE_CURL | |||
| #include "native/juce_curl_Network.cpp" | |||
| @@ -400,4 +400,10 @@ void IPAddress::findAllAddresses (Array<IPAddress>& result, bool /*includeIPv6*/ | |||
| } | |||
| } | |||
| IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress&) | |||
| { | |||
| return {}; // TODO | |||
| } | |||
| } // namespace juce | |||
| @@ -0,0 +1,140 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2017 - ROLI Ltd. | |||
| JUCE is an open source library subject to commercial or open-source | |||
| licensing. | |||
| The code included in this file is provided under the terms of the ISC license | |||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | |||
| To use, copy, modify, and/or distribute this software for any purpose with or | |||
| without fee is hereby granted provided that the above copyright notice and | |||
| this permission notice appear in all copies. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| namespace juce | |||
| { | |||
| namespace | |||
| { | |||
| static IPAddress makeAddress (const sockaddr_in6* addr_in) | |||
| { | |||
| if (addr_in == nullptr) | |||
| return {}; | |||
| in6_addr addr = addr_in->sin6_addr; | |||
| union ByteUnion | |||
| { | |||
| uint16 combined; | |||
| uint8 split[2]; | |||
| }; | |||
| ByteUnion temp; | |||
| uint16 arr[8]; | |||
| for (int i = 0; i < 8; ++i) // Swap bytes from network to host order | |||
| { | |||
| temp.split[0] = addr.s6_addr[i * 2 + 1]; | |||
| temp.split[1] = addr.s6_addr[i * 2]; | |||
| arr[i] = temp.combined; | |||
| } | |||
| return IPAddress (arr); | |||
| } | |||
| static IPAddress makeAddress (const sockaddr_in *addr_in) | |||
| { | |||
| if (addr_in->sin_addr.s_addr == INADDR_NONE) | |||
| return {}; | |||
| return IPAddress (ntohl (addr_in->sin_addr.s_addr)); | |||
| } | |||
| struct InterfaceInfo | |||
| { | |||
| IPAddress interfaceAddress; | |||
| IPAddress broadcastAddress; | |||
| }; | |||
| bool operator== (const InterfaceInfo& lhs, const InterfaceInfo& rhs) | |||
| { | |||
| return lhs.interfaceAddress == rhs.interfaceAddress | |||
| && lhs.broadcastAddress == rhs.broadcastAddress; | |||
| } | |||
| bool populateInterfaceInfo (struct ifaddrs* ifa, InterfaceInfo& interfaceInfo) | |||
| { | |||
| if (ifa->ifa_addr != nullptr) | |||
| { | |||
| if (ifa->ifa_addr->sa_family == AF_INET) | |||
| { | |||
| auto* interfaceAddressInfo = (sockaddr_in*) ifa->ifa_addr; | |||
| auto* broadcastAddressInfo = (sockaddr_in*) ifa->ifa_dstaddr; | |||
| if (interfaceAddressInfo->sin_addr.s_addr != INADDR_NONE) | |||
| { | |||
| interfaceInfo.interfaceAddress = makeAddress (interfaceAddressInfo); | |||
| interfaceInfo.broadcastAddress = makeAddress (broadcastAddressInfo); | |||
| return true; | |||
| } | |||
| } | |||
| else if (ifa->ifa_addr->sa_family == AF_INET6) | |||
| { | |||
| interfaceInfo.interfaceAddress = makeAddress ((sockaddr_in6*) ifa->ifa_addr); | |||
| interfaceInfo.broadcastAddress = makeAddress ((sockaddr_in6*) ifa->ifa_dstaddr); | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| Array<InterfaceInfo> getAllInterfaceInfo() | |||
| { | |||
| Array<InterfaceInfo> interfaces; | |||
| struct ifaddrs* ifaddr = nullptr; | |||
| if (getifaddrs (&ifaddr) != -1) | |||
| { | |||
| for (auto* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) | |||
| { | |||
| InterfaceInfo i; | |||
| if (populateInterfaceInfo (ifa, i)) | |||
| interfaces.addIfNotAlreadyThere (i); | |||
| } | |||
| freeifaddrs (ifaddr); | |||
| } | |||
| return interfaces; | |||
| } | |||
| } | |||
| void IPAddress::findAllAddresses (Array<IPAddress>& result, bool includeIPv6) | |||
| { | |||
| for (auto& i : getAllInterfaceInfo()) | |||
| if (includeIPv6 || ! i.interfaceAddress.isIPv6) | |||
| result.addIfNotAlreadyThere (i.interfaceAddress); | |||
| } | |||
| IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress& interfaceAddress) | |||
| { | |||
| for (auto& i : getAllInterfaceInfo()) | |||
| if (i.interfaceAddress == interfaceAddress) | |||
| return i.broadcastAddress; | |||
| return {}; | |||
| } | |||
| } // namespace juce | |||
| @@ -618,6 +618,13 @@ void IPAddress::findAllAddresses (Array<IPAddress>& result, bool includeIPv6) | |||
| } | |||
| } | |||
| IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress&) | |||
| { | |||
| // TODO | |||
| return IPAddress {}; | |||
| } | |||
| //============================================================================== | |||
| bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| @@ -85,6 +85,15 @@ IPAddress::IPAddress (uint32 n) noexcept : isIPv6 (false) | |||
| zeroUnusedBytes(); | |||
| } | |||
| bool IPAddress::isNull() const | |||
| { | |||
| for (int i = 0; i < 16; ++i) | |||
| if (address[i] != 0) | |||
| return false; | |||
| return true; | |||
| } | |||
| static String removePort (const String& adr) | |||
| { | |||
| if (adr.containsAnyOf ("[]")) | |||
| @@ -350,57 +359,94 @@ Array<IPAddress> IPAddress::getAllAddresses (bool includeIPv6) | |||
| return addresses; | |||
| } | |||
| #if (! JUCE_WINDOWS) && (! JUCE_ANDROID) | |||
| static void addAddress (const sockaddr_in* addr_in, Array<IPAddress>& result) | |||
| { | |||
| auto addr = addr_in->sin_addr.s_addr; | |||
| if (addr != INADDR_NONE) | |||
| result.addIfNotAlreadyThere (IPAddress (ntohl (addr))); | |||
| } | |||
| //============================================================================== | |||
| #if JUCE_UNIT_TESTS | |||
| static void addAddress (const sockaddr_in6* addr_in, Array<IPAddress>& result) | |||
| struct IPAddressTests : public UnitTest | |||
| { | |||
| in6_addr addr = addr_in->sin6_addr; | |||
| union ByteUnion | |||
| IPAddressTests() | |||
| : UnitTest ("IPAddress", "Networking") | |||
| { | |||
| uint16 combined; | |||
| uint8 split[2]; | |||
| }; | |||
| } | |||
| ByteUnion temp; | |||
| uint16 arr[8]; | |||
| void runTest() override | |||
| { | |||
| testConstructors(); | |||
| testFindAllAddresses(); | |||
| testFindBroadcastAddress(); | |||
| } | |||
| for (int i = 0; i < 8; ++i) // Swap bytes from network to host order | |||
| void testConstructors() | |||
| { | |||
| temp.split[0] = addr.s6_addr[i * 2 + 1]; | |||
| temp.split[1] = addr.s6_addr[i * 2]; | |||
| beginTest ("constructors"); | |||
| arr[i] = temp.combined; | |||
| // Default IPAdress should be null | |||
| IPAddress defaultConstructed; | |||
| expect (defaultConstructed.isNull()); | |||
| auto local = IPAddress::local(); | |||
| expect (! local.isNull()); | |||
| IPAddress ipv4{1, 2, 3, 4}; | |||
| expect (! ipv4.isNull()); | |||
| expect (! ipv4.isIPv6); | |||
| expect (ipv4.toString() == "1.2.3.4"); | |||
| } | |||
| result.addIfNotAlreadyThere (IPAddress (arr)); | |||
| } | |||
| void testFindAllAddresses() | |||
| { | |||
| beginTest ("find all addresses"); | |||
| void IPAddress::findAllAddresses (Array<IPAddress>& result, bool includeIPv6) | |||
| { | |||
| struct ifaddrs* ifaddr = nullptr; | |||
| Array<IPAddress> ipv4Addresses; | |||
| Array<IPAddress> allAddresses; | |||
| if (getifaddrs (&ifaddr) == -1) | |||
| return; | |||
| IPAddress::findAllAddresses (ipv4Addresses, false); | |||
| IPAddress::findAllAddresses (allAddresses, true); | |||
| for (auto* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) | |||
| expect (allAddresses.size() >= ipv4Addresses.size()); | |||
| for (auto& a : ipv4Addresses) | |||
| { | |||
| expect (! a.isNull()); | |||
| expect (! a.isIPv6); | |||
| } | |||
| for (auto& a : allAddresses) | |||
| { | |||
| expect (! a.isNull()); | |||
| } | |||
| } | |||
| void testFindBroadcastAddress() | |||
| { | |||
| if (ifa->ifa_addr == nullptr) | |||
| continue; | |||
| beginTest ("broadcast addresses"); | |||
| Array<IPAddress> addresses; | |||
| // Only IPv4 interfaces have broadcast | |||
| IPAddress::findAllAddresses (addresses, false); | |||
| 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); | |||
| for (auto& a : addresses) | |||
| { | |||
| expect (! a.isNull()); | |||
| auto broadcastAddress = IPAddress::getInterfaceBroadcastAddress (a); | |||
| // If we retrieve an address, it should be an IPv4 address | |||
| if (! broadcastAddress.isNull()) | |||
| { | |||
| expect (! a.isIPv6); | |||
| } | |||
| } | |||
| // Expect to fail to find a broadcast for this address | |||
| IPAddress address{1, 2, 3, 4}; | |||
| expect (IPAddress::getInterfaceBroadcastAddress (address).isNull()); | |||
| } | |||
| }; | |||
| static IPAddressTests iPAddressTests; | |||
| freeifaddrs (ifaddr); | |||
| } | |||
| #endif | |||
| } // namespace juce | |||
| @@ -87,6 +87,9 @@ public: | |||
| /** 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 whether the address contains the null address (e.g. 0.0.0.0). */ | |||
| bool isNull() const; | |||
| //============================================================================== | |||
| /** 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; | |||
| @@ -130,6 +133,11 @@ public: | |||
| /** Converts an IPv4 address to an IPv4-mapped IPv6 address. */ | |||
| static IPAddress convertIPv4AddressToIPv4Mapped (const IPAddress& addressToMap); | |||
| /** If the IPAdress is the address of an interface on the machine, returns the associated broadcast address. | |||
| If the address is not an interface, it will return a null address. | |||
| */ | |||
| static IPAddress getInterfaceBroadcastAddress (const IPAddress& interfaceAddress); | |||
| private: | |||
| /** Union used to split a 16-bit unsigned integer into 2 8-bit unsigned integers or vice-versa */ | |||
| union ByteUnion | |||