From 035ecdc9e285ad2fd9ccf51e4ed2ac01b53dc3d1 Mon Sep 17 00:00:00 2001 From: peterbell10 Date: Thu, 15 Jun 2017 10:03:49 +0100 Subject: Replace evdns with getaddrinfo and getnameinfo (#3766) --- src/OSSupport/CMakeLists.txt | 3 ++ src/OSSupport/GetAddressInfoError.h | 29 ++++++++++++ src/OSSupport/HostnameLookup.cpp | 68 +++++++++++++-------------- src/OSSupport/HostnameLookup.h | 13 +++--- src/OSSupport/IPLookup.cpp | 91 +++++++++++++++--------------------- src/OSSupport/IPLookup.h | 11 ++--- src/OSSupport/NetworkLookup.cpp | 63 +++++++++++++++++++++++++ src/OSSupport/NetworkLookup.h | 42 +++++++++++++++++ src/OSSupport/NetworkSingleton.cpp | 93 +++---------------------------------- src/OSSupport/NetworkSingleton.h | 43 ++++------------- src/OSSupport/TCPLinkImpl.cpp | 86 +++++++++++++++++++++++----------- tests/Network/CMakeLists.txt | 6 +++ 12 files changed, 300 insertions(+), 248 deletions(-) create mode 100644 src/OSSupport/GetAddressInfoError.h create mode 100644 src/OSSupport/NetworkLookup.cpp create mode 100644 src/OSSupport/NetworkLookup.h diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index 22699f7cd..8e018425b 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -12,6 +12,7 @@ SET (SRCS IPLookup.cpp IsThread.cpp NetworkInterfaceEnum.cpp + NetworkLookup.cpp NetworkSingleton.cpp ServerHandleImpl.cpp StackTrace.cpp @@ -24,11 +25,13 @@ SET (HDRS Errors.h Event.h File.h + GetAddressInfoError.h GZipFile.h HostnameLookup.h IPLookup.h IsThread.h Network.h + NetworkLookup.h NetworkSingleton.h Queue.h ServerHandleImpl.h diff --git a/src/OSSupport/GetAddressInfoError.h b/src/OSSupport/GetAddressInfoError.h new file mode 100644 index 000000000..43869fb63 --- /dev/null +++ b/src/OSSupport/GetAddressInfoError.h @@ -0,0 +1,29 @@ +#pragma once + + + +/** Returns the readable form of a getaddressinfo type error code */ +inline AString ErrorString(int a_ErrorCode) +{ + // Note gai_strerror is not threadsafe on windows + #ifdef _WIN32 + char ErrorStr[GAI_STRERROR_BUFFER_SIZE + 1]; + + int MsgLen = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + nullptr, + a_ErrorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + ErrorStr, + sizeof(ErrorStr) - 1, + nullptr + ); + + return AString(ErrorStr, MsgLen); + #else + return gai_strerror(a_ErrorCode); + #endif +} + diff --git a/src/OSSupport/HostnameLookup.cpp b/src/OSSupport/HostnameLookup.cpp index 0944153be..867472e8c 100644 --- a/src/OSSupport/HostnameLookup.cpp +++ b/src/OSSupport/HostnameLookup.cpp @@ -5,8 +5,8 @@ #include "Globals.h" #include "HostnameLookup.h" -#include #include "NetworkSingleton.h" +#include "GetAddressInfoError.h" @@ -15,8 +15,9 @@ //////////////////////////////////////////////////////////////////////////////// // cHostnameLookup: -cHostnameLookup::cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks): - m_Callbacks(a_Callbacks) +cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks), + m_Hostname(a_Hostname) { } @@ -24,43 +25,45 @@ cHostnameLookup::cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks) -void cHostnameLookup::Lookup(const AString & a_Hostname) +void cHostnameLookup::Lookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks) { - // Store the hostname for the callback: - m_Hostname = a_Hostname; - - // Start the lookup: - // Note that we don't have to store the LibEvent lookup handle, LibEvent will free it on its own. - evutil_addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_protocol = IPPROTO_TCP; - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = AF_UNSPEC; - hints.ai_flags = EVUTIL_AI_CANONNAME; - evdns_getaddrinfo(cNetworkSingleton::Get().GetDNSBase(), a_Hostname.c_str(), nullptr, &hints, Callback, this); + // Cannot use std::make_shared here, constructor is not accessible + cHostnameLookupPtr Lookup{ new cHostnameLookup(a_Hostname, std::move(a_Callbacks)) }; + + // Note the Lookup object is owned solely by this lambda which is destroyed after it runs + cNetworkSingleton::Get().GetLookupThread().ScheduleLookup([=]() + { + // Start the lookup: + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + + addrinfo * Result; + int ErrCode = getaddrinfo(Lookup->m_Hostname.c_str(), nullptr, &hints, &Result); + + Lookup->Callback(ErrCode, Result); + }); } -void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a_Self) +void cHostnameLookup::Callback(int a_ErrCode, addrinfo * a_Addr) { - // Get the Self class: - cHostnameLookup * Self = reinterpret_cast(a_Self); - ASSERT(Self != nullptr); - // If an error has occurred, notify the error callback: if (a_ErrCode != 0) { - Self->m_Callbacks->OnError(a_ErrCode, evutil_socket_error_to_string(a_ErrCode)); - cNetworkSingleton::Get().RemoveHostnameLookup(Self); + m_Callbacks->OnError(a_ErrCode, ErrorString(a_ErrCode)); return; } // Call the success handler for each entry received: bool HasResolved = false; - evutil_addrinfo * OrigAddr = a_Addr; + addrinfo * OrigAddr = a_Addr; for (;a_Addr != nullptr; a_Addr = a_Addr->ai_next) { char IP[128]; @@ -69,7 +72,7 @@ void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a case AF_INET: // IPv4 { sockaddr_in * sin = reinterpret_cast(a_Addr->ai_addr); - if (!Self->m_Callbacks->OnNameResolvedV4(Self->m_Hostname, sin)) + if (!m_Callbacks->OnNameResolvedV4(m_Hostname, sin)) { // Callback indicated that the IP shouldn't be serialized to a string, just continue with the next address: HasResolved = true; @@ -81,7 +84,7 @@ void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a case AF_INET6: // IPv6 { sockaddr_in6 * sin = reinterpret_cast(a_Addr->ai_addr); - if (!Self->m_Callbacks->OnNameResolvedV6(Self->m_Hostname, sin)) + if (!m_Callbacks->OnNameResolvedV6(m_Hostname, sin)) { // Callback indicated that the IP shouldn't be serialized to a string, just continue with the next address: HasResolved = true; @@ -96,21 +99,20 @@ void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a continue; // for (a_Addr) } } - Self->m_Callbacks->OnNameResolved(Self->m_Hostname, IP); + m_Callbacks->OnNameResolved(m_Hostname, IP); HasResolved = true; } // for (a_Addr) // If only unsupported families were reported, call the Error handler: if (!HasResolved) { - Self->m_Callbacks->OnError(DNS_ERR_NODATA, "The name does not resolve to any known address."); + m_Callbacks->OnError(EAI_NODATA, ErrorString(EAI_NODATA)); } else { - Self->m_Callbacks->OnFinished(); + m_Callbacks->OnFinished(); } - evutil_freeaddrinfo(OrigAddr); - cNetworkSingleton::Get().RemoveHostnameLookup(Self); + freeaddrinfo(OrigAddr); } @@ -125,9 +127,7 @@ bool cNetwork::HostnameToIP( cNetwork::cResolveNameCallbacksPtr a_Callbacks ) { - auto Lookup = std::make_shared(a_Callbacks); - cNetworkSingleton::Get().AddHostnameLookup(Lookup); - Lookup->Lookup(a_Hostname); + cHostnameLookup::Lookup(a_Hostname, std::move(a_Callbacks)); return true; } diff --git a/src/OSSupport/HostnameLookup.h b/src/OSSupport/HostnameLookup.h index d69f24707..559dfad5f 100644 --- a/src/OSSupport/HostnameLookup.h +++ b/src/OSSupport/HostnameLookup.h @@ -12,7 +12,6 @@ #pragma once #include "Network.h" -#include @@ -22,21 +21,21 @@ class cHostnameLookup { public: - /** Creates the lookup object. Doesn't start the lookup yet. */ - cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks); - - /** Starts the lookup. */ - void Lookup(const AString & a_Hostname); + /** Creates a lookup object and schedules the lookup. */ + static void Lookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks); protected: + /** Creates the lookup object. Doesn't start the lookup yet. */ + cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks); + /** The callbacks to call for resolved names / errors. */ cNetwork::cResolveNameCallbacksPtr m_Callbacks; /** The hostname that was queried (needed for the callbacks). */ AString m_Hostname; - static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self); + void Callback(int a_ErrCode, struct addrinfo * a_Addr); }; typedef SharedPtr cHostnameLookupPtr; typedef std::vector cHostnameLookupPtrs; diff --git a/src/OSSupport/IPLookup.cpp b/src/OSSupport/IPLookup.cpp index 2722d4722..7b543793d 100644 --- a/src/OSSupport/IPLookup.cpp +++ b/src/OSSupport/IPLookup.cpp @@ -5,8 +5,9 @@ #include "Globals.h" #include "IPLookup.h" -#include +#include #include "NetworkSingleton.h" +#include "GetAddressInfoError.h" @@ -15,8 +16,9 @@ //////////////////////////////////////////////////////////////////////////////// // cIPLookup: -cIPLookup::cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks): - m_Callbacks(a_Callbacks) +cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks), + m_IP(a_IP) { ASSERT(a_Callbacks != nullptr); } @@ -25,68 +27,58 @@ cIPLookup::cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks): -bool cIPLookup::Lookup(const AString & a_IP) +void cIPLookup::Lookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks) { - // Parse the IP address string into a sockaddr structure: - m_IP = a_IP; - sockaddr_storage sa; - int salen = static_cast(sizeof(sa)); - memset(&sa, 0, sizeof(sa)); - if (evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast(&sa), &salen) != 0) - { - LOGD("Failed to parse IP address \"%s\".", a_IP.c_str()); - return false; - } + cIPLookupPtr Lookup{ new cIPLookup(a_IP, std::move(a_Callbacks)) }; // Cannot use std::make_shared here, constructor is not accessible - // Call the proper resolver based on the address family: - // Note that there's no need to store the evdns_request handle returned, LibEvent frees it on its own. - switch (sa.ss_family) + // Note the Lookup object is owned solely by this lambda which is destroyed after it runs + cNetworkSingleton::Get().GetLookupThread().ScheduleLookup([=]() { - case AF_INET: - { - sockaddr_in * sa4 = reinterpret_cast(&sa); - evdns_base_resolve_reverse(cNetworkSingleton::Get().GetDNSBase(), &(sa4->sin_addr), 0, Callback, this); - break; - } - case AF_INET6: - { - sockaddr_in6 * sa6 = reinterpret_cast(&sa); - evdns_base_resolve_reverse_ipv6(cNetworkSingleton::Get().GetDNSBase(), &(sa6->sin6_addr), 0, Callback, this); - break; - } - default: + sockaddr_storage sa; + int salen = sizeof(sa); + memset(&sa, 0, sizeof(sa)); + + int ErrCode = evutil_parse_sockaddr_port(Lookup->m_IP.c_str(), reinterpret_cast(&sa), &salen); + + if (ErrCode != 0) { - LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family); - ASSERT(!"Unknown address family"); - return false; + LOGD("Failed to parse IP address \"%s\".", Lookup->m_IP.c_str()); + Lookup->Callback(ErrCode, nullptr); + return; } - } // switch (address family) - return true; + + char Hostname[NI_MAXHOST]; + char ServInfo[NI_MAXSERV]; + + ErrCode = getnameinfo( + reinterpret_cast(&sa), + static_cast(salen), + Hostname, sizeof(Hostname), + ServInfo, sizeof(ServInfo), + 0 + ); + Lookup->Callback(ErrCode, Hostname); + }); } -void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self) +void cIPLookup::Callback(int a_Result, const char * a_Address) { - // Get the Self class: - cIPLookup * Self = reinterpret_cast(a_Self); - ASSERT(Self != nullptr); - // Call the proper callback based on the event received: - if ((a_Result != 0) || (a_Addresses == nullptr)) + if ((a_Result != 0) || (a_Address == nullptr)) { // An error has occurred, notify the error callback: - Self->m_Callbacks->OnError(a_Result, evutil_socket_error_to_string(a_Result)); + m_Callbacks->OnError(a_Result, ErrorString(a_Result)); } else { // Call the success handler: - Self->m_Callbacks->OnNameResolved(*(reinterpret_cast(a_Addresses)), Self->m_IP); - Self->m_Callbacks->OnFinished(); + m_Callbacks->OnNameResolved(a_Address, m_IP); + m_Callbacks->OnFinished(); } - cNetworkSingleton::Get().RemoveIPLookup(Self); } @@ -101,14 +93,7 @@ bool cNetwork::IPToHostName( cNetwork::cResolveNameCallbacksPtr a_Callbacks ) { - auto res = std::make_shared(a_Callbacks); - cNetworkSingleton::Get().AddIPLookup(res); - if (!res->Lookup(a_IP)) - { - // Lookup failed early on, remove the object completely: - cNetworkSingleton::Get().RemoveIPLookup(res.get()); - return false; - } + cIPLookup::Lookup(a_IP, std::move(a_Callbacks)); return true; } diff --git a/src/OSSupport/IPLookup.h b/src/OSSupport/IPLookup.h index af878cbf1..600ce0af2 100644 --- a/src/OSSupport/IPLookup.h +++ b/src/OSSupport/IPLookup.h @@ -21,12 +21,9 @@ class cIPLookup { public: - /** Creates the lookup object. Doesn't start the lookup yet. */ - cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks); - /** Starts the lookup. - Returns true if lookup started successfully, false on failure (invalid IP format etc.) */ - bool Lookup(const AString & a_IP); + /** Creates a lookup object and schedules the lookup. */ + static void Lookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks); protected: @@ -36,9 +33,11 @@ protected: /** The IP that was queried (needed for the callbacks). */ AString m_IP; + /** Creates the lookup object. Doesn't start the lookup yet. */ + cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks); /** Callback that is called by LibEvent when there's an event for the request. */ - static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self); + void Callback(int a_Result, const char * a_Address); }; typedef SharedPtr cIPLookupPtr; typedef std::vector cIPLookupPtrs; diff --git a/src/OSSupport/NetworkLookup.cpp b/src/OSSupport/NetworkLookup.cpp new file mode 100644 index 000000000..5cd7ecfc4 --- /dev/null +++ b/src/OSSupport/NetworkLookup.cpp @@ -0,0 +1,63 @@ + +// NetworkLookup.cpp + +// Implements the cNetworkLookup class representing an executor for asynchronous lookup tasks + + +#include "Globals.h" +#include "NetworkLookup.h" + + + + +cNetworkLookup::cNetworkLookup() : + cIsThread("NetworkLookup") +{ +} + + + + + +cNetworkLookup::~cNetworkLookup() +{ + Stop(); +} + + + + + +void cNetworkLookup::ScheduleLookup(std::function a_Lookup) +{ + m_WorkQueue.EnqueueItem(std::move(a_Lookup)); +} + + + + + +void cNetworkLookup::Stop() +{ + m_ShouldTerminate = true; + m_WorkQueue.Clear(); + m_WorkQueue.EnqueueItem([](){}); // Dummy work to wake up the thread + cIsThread::Stop(); +} + + + + + +void cNetworkLookup::Execute() +{ + while (!m_ShouldTerminate) + { + // Execute the next task in the queue + auto Work = m_WorkQueue.DequeueItem(); + Work(); + } +} + + + diff --git a/src/OSSupport/NetworkLookup.h b/src/OSSupport/NetworkLookup.h new file mode 100644 index 000000000..e09062f4d --- /dev/null +++ b/src/OSSupport/NetworkLookup.h @@ -0,0 +1,42 @@ + +// NetworkLookup.h + +// Declares the cNetworkLookup class representing an executor for asynchronous lookup tasks + +#pragma once + +#include + +#include "IsThread.h" +#include "Queue.h" + + + + + +class cNetworkLookup : + public cIsThread +{ +public: + + cNetworkLookup(); + ~cNetworkLookup(); + + /** Schedule a lookup task for execution. */ + void ScheduleLookup(std::function a_Lookup); + + /** Cancels any scheduled lookups and joins the lookup thread. */ + void Stop(); + +protected: + + /** Process the queue until the thread is stopped. */ + virtual void Execute() override final; + +private: + + /** The queue of lookup tasks waiting to be executed. */ + cQueue> m_WorkQueue; +}; + + diff --git a/src/OSSupport/NetworkSingleton.cpp b/src/OSSupport/NetworkSingleton.cpp index 3a8dbbdc7..fde39807b 100644 --- a/src/OSSupport/NetworkSingleton.cpp +++ b/src/OSSupport/NetworkSingleton.cpp @@ -8,15 +8,7 @@ #include "NetworkSingleton.h" #include #include -#include #include -#include "IPLookup.h" -#include "HostnameLookup.h" - -#ifdef ANDROID - // For DNS server retrieval - #include -#endif @@ -53,6 +45,9 @@ cNetworkSingleton & cNetworkSingleton::Get(void) void cNetworkSingleton::Initialise(void) { + // Start the lookup thread + m_LookupThread.Start(); + // Windows: initialize networking: #ifdef _WIN32 WSADATA wsaData; @@ -86,24 +81,6 @@ void cNetworkSingleton::Initialise(void) abort(); } - // Create the DNS lookup helper: - m_DNSBase = evdns_base_new(m_EventBase, 1); - if (m_DNSBase == nullptr) - { - LOGERROR("Failed to initialize LibEvent's DNS subsystem. The server will now terminate."); - abort(); - } - - #ifdef ANDROID - char PropertyBuffer[PROP_VALUE_MAX]; - - __system_property_get("net.dns1", PropertyBuffer); - evdns_base_nameserver_ip_add(m_DNSBase, PropertyBuffer); - - __system_property_get("net.dns2", PropertyBuffer); - evdns_base_nameserver_ip_add(m_DNSBase, PropertyBuffer); - #endif - // Create the event loop thread: m_HasTerminated = false; m_EventLoopThread = std::thread(RunEventLoop, this); @@ -118,6 +95,9 @@ void cNetworkSingleton::Terminate(void) { ASSERT(!m_HasTerminated); + // Wait for the lookup thread to stop + m_LookupThread.Stop(); + // Wait for the LibEvent event loop to terminate: event_base_loopbreak(m_EventBase); m_EventLoopThread.join(); @@ -127,12 +107,9 @@ void cNetworkSingleton::Terminate(void) cCSLock Lock(m_CS); m_Connections.clear(); m_Servers.clear(); - m_HostnameLookups.clear(); - m_IPLookups.clear(); } // Free the underlying LibEvent objects: - evdns_base_free(m_DNSBase, true); event_base_free(m_EventBase); libevent_global_shutdown(); @@ -189,64 +166,6 @@ void cNetworkSingleton::SignalizeStartup(evutil_socket_t a_Socket, short a_Event -void cNetworkSingleton::AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup) -{ - ASSERT(!m_HasTerminated); - cCSLock Lock(m_CS); - m_HostnameLookups.push_back(a_HostnameLookup); -} - - - - - -void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup) -{ - ASSERT(!m_HasTerminated); - cCSLock Lock(m_CS); - for (auto itr = m_HostnameLookups.begin(), end = m_HostnameLookups.end(); itr != end; ++itr) - { - if (itr->get() == a_HostnameLookup) - { - m_HostnameLookups.erase(itr); - return; - } - } // for itr - m_HostnameLookups[] -} - - - - - -void cNetworkSingleton::AddIPLookup(cIPLookupPtr a_IPLookup) -{ - ASSERT(!m_HasTerminated); - cCSLock Lock(m_CS); - m_IPLookups.push_back(a_IPLookup); -} - - - - - -void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) -{ - ASSERT(!m_HasTerminated); - cCSLock Lock(m_CS); - for (auto itr = m_IPLookups.begin(), end = m_IPLookups.end(); itr != end; ++itr) - { - if (itr->get() == a_IPLookup) - { - m_IPLookups.erase(itr); - return; - } - } // for itr - m_IPLookups[] -} - - - - - void cNetworkSingleton::AddLink(cTCPLinkImplPtr a_Link) { ASSERT(!m_HasTerminated); diff --git a/src/OSSupport/NetworkSingleton.h b/src/OSSupport/NetworkSingleton.h index 3c8f5f660..0b815af43 100644 --- a/src/OSSupport/NetworkSingleton.h +++ b/src/OSSupport/NetworkSingleton.h @@ -13,8 +13,10 @@ #pragma once +#include #include #include "Network.h" +#include "NetworkLookup.h" #include "CriticalSection.h" #include "Event.h" @@ -24,19 +26,12 @@ // fwd: struct event_base; -struct evdns_base; class cTCPLinkImpl; typedef SharedPtr cTCPLinkImplPtr; typedef std::vector cTCPLinkImplPtrs; class cServerHandleImpl; typedef SharedPtr cServerHandleImplPtr; typedef std::vector cServerHandleImplPtrs; -class cHostnameLookup; -typedef SharedPtr cHostnameLookupPtr; -typedef std::vector cHostnameLookupPtrs; -class cIPLookup; -typedef SharedPtr cIPLookupPtr; -typedef std::vector cIPLookupPtrs; @@ -63,24 +58,8 @@ public: /** Returns the main LibEvent handle for event registering. */ event_base * GetEventBase(void) { return m_EventBase; } - /** Returns the LibEvent handle for DNS lookups. */ - evdns_base * GetDNSBase(void) { return m_DNSBase; } - - /** Adds the specified hostname lookup to m_HostnameLookups. - Used by the underlying lookup implementation when a new lookup is initiated. */ - void AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup); - - /** Removes the specified hostname lookup from m_HostnameLookups. - Used by the underlying lookup implementation when the lookup is finished. */ - void RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup); - - /** Adds the specified IP lookup to M_IPLookups. - Used by the underlying lookup implementation when a new lookup is initiated. */ - void AddIPLookup(cIPLookupPtr a_IPLookup); - - /** Removes the specified IP lookup from m_IPLookups. - Used by the underlying lookup implementation when the lookup is finished. */ - void RemoveIPLookup(const cIPLookup * a_IPLookup); + /** Returns the thread used to perform hostname and IP lookups */ + cNetworkLookup & GetLookupThread() { return m_LookupThread; } /** Adds the specified link to m_Connections. Used by the underlying link implementation when a new link is created. */ @@ -104,26 +83,17 @@ protected: /** The main LibEvent container for driving the event loop. */ event_base * m_EventBase; - /** The LibEvent handle for doing DNS lookups. */ - evdns_base * m_DNSBase; - /** Container for all client connections, including ones with pending-connect. */ cTCPLinkImplPtrs m_Connections; /** Container for all servers that are currently active. */ cServerHandleImplPtrs m_Servers; - /** Container for all pending hostname lookups. */ - cHostnameLookupPtrs m_HostnameLookups; - - /** Container for all pending IP lookups. */ - cIPLookupPtrs m_IPLookups; - /** Mutex protecting all containers against multithreaded access. */ cCriticalSection m_CS; /** Set to true if Terminate has been called. */ - volatile bool m_HasTerminated; + std::atomic m_HasTerminated; /** The thread in which the main LibEvent loop runs. */ std::thread m_EventLoopThread; @@ -131,6 +101,9 @@ protected: /** Event that is signalled once the startup is finished and the LibEvent loop is running. */ cEvent m_StartupEvent; + /** The thread on which hostname and ip address lookup is performed. */ + cNetworkLookup m_LookupThread; + /** Converts LibEvent-generated log events into log messages in MCS log. */ static void LogCallback(int a_Severity, const char * a_Msg); diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index 47be99a48..06eff9b09 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -70,41 +70,75 @@ cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTC res->m_Callbacks->OnLinkCreated(res); res->Enable(res); - // If a_Host is an IP address, schedule a connection immediately: - sockaddr_storage sa; - int salen = static_cast(sizeof(sa)); - if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast(&sa), &salen) == 0) + // Callback to connect after performing lookup: + class cHostnameCallback : + public cNetwork::cResolveNameCallbacks { - // Insert the correct port: - if (sa.ss_family == AF_INET6) + cTCPLinkImplPtr m_Link; + UInt16 m_Port; + bool m_IsConnecting; + + public: + + cHostnameCallback(cTCPLinkImplPtr a_Link, UInt16 a_ConnectPort): + m_Link(std::move(a_Link)), + m_Port(a_ConnectPort), + m_IsConnecting(false) { - reinterpret_cast(&sa)->sin6_port = htons(a_Port); } - else + + void DoConnect(const sockaddr * a_IP, int size) { - reinterpret_cast(&sa)->sin_port = htons(a_Port); + // Make sure connect is only completed once + if (!m_IsConnecting) + { + int ErrCode = bufferevent_socket_connect(m_Link->m_BufferEvent, a_IP, size); + if (ErrCode == 0) + { + m_IsConnecting = true; + } + else + { + m_Link->GetCallbacks()->OnError(ErrCode, evutil_socket_error_to_string(ErrCode)); + } + } } - // Queue the connect request: - if (bufferevent_socket_connect(res->m_BufferEvent, reinterpret_cast(&sa), salen) == 0) + virtual bool OnNameResolvedV4(const AString & a_Name, const sockaddr_in * a_IP) override { - // Success - return res; + sockaddr_in Addr = *a_IP; + Addr.sin_port = htons(m_Port); + DoConnect(reinterpret_cast(&Addr), sizeof(Addr)); + return false; // Don't care about recieving ip as string } - // Failure - cNetworkSingleton::Get().RemoveLink(res.get()); - return nullptr; - } - // a_Host is a hostname, connect after a lookup: - if (bufferevent_socket_connect_hostname(res->m_BufferEvent, cNetworkSingleton::Get().GetDNSBase(), AF_UNSPEC, a_Host.c_str(), a_Port) == 0) - { - // Success - return res; - } - // Failure - cNetworkSingleton::Get().RemoveLink(res.get()); - return nullptr; + virtual bool OnNameResolvedV6(const AString & a_Name, const sockaddr_in6 * a_IP) override + { + sockaddr_in6 Addr = *a_IP; + Addr.sin6_port = htons(m_Port); + DoConnect(reinterpret_cast(&Addr), sizeof(Addr)); + return false; // Don't care about recieving ip as string + } + + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override + { + m_Link->GetCallbacks()->OnError(a_ErrorCode, a_ErrorMsg); + cNetworkSingleton::Get().RemoveLink(m_Link.get()); + } + + // Don't need to do anything for these + virtual void OnFinished() override + { + } + + virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) override + { + } + }; + + // Schedule the host query + cNetwork::HostnameToIP(a_Host, std::make_shared(res, a_Port)); + return res; } diff --git a/tests/Network/CMakeLists.txt b/tests/Network/CMakeLists.txt index f3496d276..617e3ccc9 100644 --- a/tests/Network/CMakeLists.txt +++ b/tests/Network/CMakeLists.txt @@ -12,7 +12,9 @@ set (Network_SRCS ${CMAKE_SOURCE_DIR}/src/OSSupport/Event.cpp ${CMAKE_SOURCE_DIR}/src/OSSupport/HostnameLookup.cpp ${CMAKE_SOURCE_DIR}/src/OSSupport/IPLookup.cpp + ${CMAKE_SOURCE_DIR}/src/OSSupport/IsThread.cpp ${CMAKE_SOURCE_DIR}/src/OSSupport/NetworkInterfaceEnum.cpp + ${CMAKE_SOURCE_DIR}/src/OSSupport/NetworkLookup.cpp ${CMAKE_SOURCE_DIR}/src/OSSupport/NetworkSingleton.cpp ${CMAKE_SOURCE_DIR}/src/OSSupport/ServerHandleImpl.cpp ${CMAKE_SOURCE_DIR}/src/OSSupport/TCPLinkImpl.cpp @@ -27,12 +29,16 @@ set (Network_SRCS set (Network_HDRS ${CMAKE_SOURCE_DIR}/src/OSSupport/CriticalSection.h ${CMAKE_SOURCE_DIR}/src/OSSupport/Event.h + ${CMAKE_SOURCE_DIR}/src/OSSupport/GetAddressInfoError.h ${CMAKE_SOURCE_DIR}/src/OSSupport/HostnameLookup.h ${CMAKE_SOURCE_DIR}/src/OSSupport/IPLookup.h + ${CMAKE_SOURCE_DIR}/src/OSSupport/IsThread.h ${CMAKE_SOURCE_DIR}/src/OSSupport/Network.h + ${CMAKE_SOURCE_DIR}/src/OSSupport/NetworkLookup.h ${CMAKE_SOURCE_DIR}/src/OSSupport/NetworkSingleton.h ${CMAKE_SOURCE_DIR}/src/OSSupport/ServerHandleImpl.h ${CMAKE_SOURCE_DIR}/src/OSSupport/TCPLinkImpl.h + ${CMAKE_SOURCE_DIR}/src/OSSupport/Queue.h ${CMAKE_SOURCE_DIR}/src/PolarSSL++/CtrDrbgContext.h ${CMAKE_SOURCE_DIR}/src/PolarSSL++/CryptoKey.h ${CMAKE_SOURCE_DIR}/src/PolarSSL++/EntropyContext.h -- cgit v1.2.3