From fde44cba0815f626253c0d352cd0d782eec94328 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 11 Jan 2015 11:21:18 +0100 Subject: [PATCH] cNetwork: Implemented HostnameToIP lookups. --- src/OSSupport/Network.cpp | 136 +++++++++++++++++++++++++++++++++-- src/OSSupport/Network.h | 11 ++- tests/Network/CMakeLists.txt | 9 +++ tests/Network/EchoServer.cpp | 19 +++++ tests/Network/Google.cpp | 23 +++++- tests/Network/NameLookup.cpp | 67 +++++++++++++++++ 6 files changed, 256 insertions(+), 9 deletions(-) create mode 100644 tests/Network/EchoServer.cpp create mode 100644 tests/Network/NameLookup.cpp diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 0f532624d..8e2d060b4 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -123,6 +123,27 @@ public: //////////////////////////////////////////////////////////////////////////////// // Class definitions: +/** Holds information about an in-progress hostname lookup. */ +class cHostnameLookup +{ + /** 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); + +public: + cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks); +}; +typedef SharedPtr cHostnameLookupPtr; +typedef std::vector cHostnameLookupPtrs; + + + + + /** Implements the cTCPLink details so that it can represent the single connection between two endpoints. */ class cTCPLinkImpl: public cTCPLink @@ -214,7 +235,8 @@ typedef std::vector cServerHandleImplPtrs; class cNetworkSingleton { - friend class cTCPLinkImpl; + friend class cHostnameLookup; // Needs access to m_DNSBase + friend class cTCPLinkImpl; // Needs access to m_EventBase and m_DNSBase public: /** Returns the singleton instance of this class */ @@ -280,6 +302,9 @@ protected: /** Container for all servers that are currently active. */ cServerHandleImplPtrs m_Servers; + /** Container for all pending hostname lookups. */ + cHostnameLookupPtrs m_HostnameLookups; + /** Initializes the LibEvent internals. */ cNetworkSingleton(void); @@ -289,12 +314,94 @@ protected: /** Implements the thread that runs LibEvent's event dispatcher loop. */ static void RunEventLoop(cNetworkSingleton * a_Self); + + /** Removes the specified hostname lookup from m_HostnameLookups. */ + void RemoveHostnameLookup(cHostnameLookup * a_HostnameLookup); }; +//////////////////////////////////////////////////////////////////////////////// +// cHostnameLookup: + +cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks), + m_Hostname(a_Hostname) +{ + 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().m_DNSBase, a_Hostname.c_str(), nullptr, &hints, Callback, this); +} + + + + + +void cHostnameLookup::Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self) +{ + // 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); + cNetworkSingleton::Get().RemoveHostnameLookup(Self); + return; + } + + // Call the success handler for each entry received: + bool HasResolved = false; + for (;a_Addr != nullptr; a_Addr = a_Addr->ai_next) + { + char IP[128]; + switch (a_Addr->ai_family) + { + case AF_INET: // IPv4 + { + sockaddr_in * sin = reinterpret_cast(a_Addr->ai_addr); + evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); + break; + } + case AF_INET6: // IPv6 + { + sockaddr_in6 * sin = reinterpret_cast(a_Addr->ai_addr); + evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); + break; + } + default: + { + // Unknown address family, handle as error + continue; + } + } + Self->m_Callbacks->OnNameResolved(Self->m_Hostname, IP); + HasResolved = true; + } + + // If only unsupported families were reported, call the Error handler: + if (!HasResolved) + { + Self->m_Callbacks->OnError(1); + } + else + { + Self->m_Callbacks->OnFinished(); + } + cNetworkSingleton::Get().RemoveHostnameLookup(Self); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cTCPLinkImpl: @@ -716,9 +823,15 @@ bool cNetworkSingleton::HostnameToIP( cNetwork::cResolveNameCallbacksPtr a_Callbacks ) { - // TODO - ASSERT(!"Not implemented yet!"); - return false; + try + { + m_HostnameLookups.push_back(std::make_shared(a_Hostname, a_Callbacks)); + } + catch (...) + { + return false; + } + return true; } @@ -765,3 +878,18 @@ void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) +void cNetworkSingleton::RemoveHostnameLookup(cHostnameLookup * a_HostnameLookup) +{ + 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[] +} + + + + diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 5cca511dc..3f60b03a7 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -148,11 +148,18 @@ public: // Force a virtual destructor for all descendants: virtual ~cResolveNameCallbacks() {} - /** Called when the hostname is successfully resolved into an IP address. */ + /** Called when the hostname is successfully resolved into an IP address. + May be called multiple times if an address resolves to multiple addresses. + a_IP may be either an IPv4 or an IPv6 address with their proper formatting. */ virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) = 0; - /** Called when an error is encountered while resolving. */ + /** Called when an error is encountered while resolving. + If an error is reported, the OnFinished() callback is not called. */ virtual void OnError(int a_ErrorCode) = 0; + + /** Called when all the addresses resolved have been reported via the OnNameResolved() callback. + Only called if there was no error reported. */ + virtual void OnFinished(void) = 0; }; typedef SharedPtr cResolveNameCallbacksPtr; diff --git a/tests/Network/CMakeLists.txt b/tests/Network/CMakeLists.txt index 8aae84590..a63ce7dfe 100644 --- a/tests/Network/CMakeLists.txt +++ b/tests/Network/CMakeLists.txt @@ -8,7 +8,16 @@ include_directories(${CMAKE_SOURCE_DIR}/lib/libevent/include) add_definitions(-DTEST_GLOBALS=1) add_library(Network ${CMAKE_SOURCE_DIR}/src/OSSupport/Network.cpp ${CMAKE_SOURCE_DIR}/src/OSSupport/Event.cpp ${CMAKE_SOURCE_DIR}/src/StringUtils.cpp) target_link_libraries(Network event_core event_extra) +if (MSVC) + target_link_libraries(Network ws2_32.lib) +endif() add_executable(Google-exe Google.cpp) target_link_libraries(Google-exe Network) add_test(NAME Google-test COMMAND Google-exe) + +add_executable(EchoServer EchoServer.cpp) +target_link_libraries(EchoServer Network) + +add_executable(NameLookup NameLookup.cpp) +target_link_libraries(NameLookup Network) diff --git a/tests/Network/EchoServer.cpp b/tests/Network/EchoServer.cpp new file mode 100644 index 000000000..def064083 --- /dev/null +++ b/tests/Network/EchoServer.cpp @@ -0,0 +1,19 @@ + +// EchoServer.cpp + +// Implements an Echo server using the LibEvent-based cNetwork API, as a test of that API + + + + + + +int main() +{ + // TODO + return 0; +} + + + + diff --git a/tests/Network/Google.cpp b/tests/Network/Google.cpp index 0aa52d025..de5f95e1f 100644 --- a/tests/Network/Google.cpp +++ b/tests/Network/Google.cpp @@ -1,11 +1,17 @@ +// Google.cpp + +// Implements a HTTP download of the google's front page using the LibEvent-based cNetwork API + #include "Globals.h" - #include #include "OSSupport/Event.h" - #include "OSSupport/Network.h" + + + + /** Connect callbacks that send a HTTP GET request for google.com when connected. */ class cHTTPConnectCallbacks: public cNetwork::cConnectCallbacks @@ -35,6 +41,9 @@ public: }; + + + /** cTCPLink callbacks that dump everything it received to the log. */ class cDumpCallbacks: public cTCPLink::cCallbacks @@ -69,7 +78,11 @@ public: }; -int main() { + + + +int main() +{ cEvent evtFinish; LOGD("Network test: Connecting to google.com:80, reading front page via HTTP."); @@ -83,3 +96,7 @@ int main() { evtFinish.Wait(); LOGD("Network test finished"); } + + + + diff --git a/tests/Network/NameLookup.cpp b/tests/Network/NameLookup.cpp new file mode 100644 index 000000000..daa72a3cb --- /dev/null +++ b/tests/Network/NameLookup.cpp @@ -0,0 +1,67 @@ + +// NameLookup.cpp + +// Implements a DNS name lookup using the LibEvent-based cNetwork API + +#include "Globals.h" +#include +#include "OSSupport/Event.h" +#include "OSSupport/Network.h" + + + + + +class cFinishLookupCallbacks: + public cNetwork::cResolveNameCallbacks +{ + cEvent & m_Event; + + virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) override + { + LOGD("%s resolves to IP %s", a_Name.c_str(), a_IP.c_str()); + } + + virtual void OnError(int a_ErrorCode) override + { + LOGD("Error %d while performing lookup!", a_ErrorCode); + abort(); + } + + virtual void OnFinished(void) override + { + LOGD("Resolving finished."); + m_Event.Set(); + } + +public: + cFinishLookupCallbacks(cEvent & a_Event): + m_Event(a_Event) + { + } +}; + + + + + +int main() +{ + cEvent evtFinish; + + LOGD("Network test: Looking up google.com"); + if (!cNetwork::HostnameToIP("google.com", std::make_shared(evtFinish))) + { + LOGWARNING("Cannot resolve google.com"); + abort(); + } + LOGD("Name lookup has been successfully queued"); + + evtFinish.Wait(); + LOGD("Network test finished"); + return 0; +} + + + +