Merge pull request #1701 from mc-server/libevent
LibEvent-based socket API
This commit is contained in:
commit
f91de20ca1
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -25,3 +25,6 @@
|
|||||||
[submodule "lib/SQLiteCpp"]
|
[submodule "lib/SQLiteCpp"]
|
||||||
path = lib/SQLiteCpp
|
path = lib/SQLiteCpp
|
||||||
url = https://github.com/mc-server/SQLiteCpp.git
|
url = https://github.com/mc-server/SQLiteCpp.git
|
||||||
|
[submodule "lib/libevent"]
|
||||||
|
path = lib/libevent
|
||||||
|
url = https://github.com/mc-server/libevent.git
|
||||||
|
@ -95,6 +95,13 @@ set(SQLITECPP_BUILD_EXAMPLES OFF CACHE BOOL "Build examples."
|
|||||||
set(SQLITECPP_BUILD_TESTS OFF CACHE BOOL "Build and run tests." FORCE)
|
set(SQLITECPP_BUILD_TESTS OFF CACHE BOOL "Build and run tests." FORCE)
|
||||||
set(SQLITECPP_INTERNAL_SQLITE OFF CACHE BOOL "Add the internal SQLite3 source to the project." FORCE)
|
set(SQLITECPP_INTERNAL_SQLITE OFF CACHE BOOL "Add the internal SQLite3 source to the project." FORCE)
|
||||||
|
|
||||||
|
# Set options for LibEvent, disable all their tests and benchmarks:
|
||||||
|
set(EVENT__DISABLE_OPENSSL YES CACHE BOOL "Disable OpenSSL in LibEvent" FORCE)
|
||||||
|
set(EVENT__DISABLE_BENCHMARK YES CACHE BOOL "Disable LibEvent benchmarks" FORCE)
|
||||||
|
set(EVENT__DISABLE_TESTS YES CACHE BOOL "Disable LibEvent tests" FORCE)
|
||||||
|
set(EVENT__DISABLE_REGRESS YES CACHE BOOL "Disable LibEvent regression tests" FORCE)
|
||||||
|
set(EVENT__DISABLE_SAMPLES YES CACHE BOOL "Disable LibEvent samples" FORCE)
|
||||||
|
|
||||||
# Check that the libraries are present:
|
# Check that the libraries are present:
|
||||||
if (NOT EXISTS ${CMAKE_SOURCE_DIR}/lib/SQLiteCpp/CMakeLists.txt)
|
if (NOT EXISTS ${CMAKE_SOURCE_DIR}/lib/SQLiteCpp/CMakeLists.txt)
|
||||||
message(FATAL_ERROR "SQLiteCpp is missing in folder lib/SQLiteCpp. Have you initialized the submodules / downloaded the extra libraries?")
|
message(FATAL_ERROR "SQLiteCpp is missing in folder lib/SQLiteCpp. Have you initialized the submodules / downloaded the extra libraries?")
|
||||||
@ -102,6 +109,9 @@ endif()
|
|||||||
if (NOT EXISTS ${CMAKE_SOURCE_DIR}/lib/polarssl/CMakeLists.txt)
|
if (NOT EXISTS ${CMAKE_SOURCE_DIR}/lib/polarssl/CMakeLists.txt)
|
||||||
message(FATAL_ERROR "PolarSSL is missing in folder lib/polarssl. Have you initialized the submodules / downloaded the extra libraries?")
|
message(FATAL_ERROR "PolarSSL is missing in folder lib/polarssl. Have you initialized the submodules / downloaded the extra libraries?")
|
||||||
endif()
|
endif()
|
||||||
|
if (NOT EXISTS ${CMAKE_SOURCE_DIR}/lib/libevent/CMakeLists.txt)
|
||||||
|
message(FATAL_ERROR "LibEvent is missing in folder lib/libevent. Have you initialized and updated the submodules / downloaded the extra libraries?")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Include all the libraries:
|
# Include all the libraries:
|
||||||
add_subdirectory(lib/jsoncpp/)
|
add_subdirectory(lib/jsoncpp/)
|
||||||
@ -112,6 +122,7 @@ add_subdirectory(lib/sqlite/)
|
|||||||
add_subdirectory(lib/SQLiteCpp/)
|
add_subdirectory(lib/SQLiteCpp/)
|
||||||
add_subdirectory(lib/expat/)
|
add_subdirectory(lib/expat/)
|
||||||
add_subdirectory(lib/luaexpat/)
|
add_subdirectory(lib/luaexpat/)
|
||||||
|
add_subdirectory(lib/libevent/)
|
||||||
|
|
||||||
# Add proper include directories so that SQLiteCpp can find SQLite3:
|
# Add proper include directories so that SQLiteCpp can find SQLite3:
|
||||||
get_property(SQLITECPP_INCLUDES DIRECTORY "lib/SQLiteCpp/" PROPERTY INCLUDE_DIRECTORIES)
|
get_property(SQLITECPP_INCLUDES DIRECTORY "lib/SQLiteCpp/" PROPERTY INCLUDE_DIRECTORIES)
|
||||||
@ -119,6 +130,9 @@ set(SQLITECPP_INCLUDES "${SQLITECPP_INCLUDES}" "${CMAKE_CURRENT_SOURCE_DIR}/lib/
|
|||||||
set_property(DIRECTORY lib/SQLiteCpp/ PROPERTY INCLUDE_DIRECTORIES "${SQLITECPP_INCLUDES}")
|
set_property(DIRECTORY lib/SQLiteCpp/ PROPERTY INCLUDE_DIRECTORIES "${SQLITECPP_INCLUDES}")
|
||||||
set_property(TARGET SQLiteCpp PROPERTY INCLUDE_DIRECTORIES "${SQLITECPP_INCLUDES}")
|
set_property(TARGET SQLiteCpp PROPERTY INCLUDE_DIRECTORIES "${SQLITECPP_INCLUDES}")
|
||||||
|
|
||||||
|
# Add proper includes for LibEvent's event-config.h header:
|
||||||
|
include_directories(SYSTEM ${LIBEVENT_INCLUDE_DIRS})
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
add_subdirectory(lib/luaproxy/)
|
add_subdirectory(lib/luaproxy/)
|
||||||
endif()
|
endif()
|
||||||
@ -126,13 +140,14 @@ endif()
|
|||||||
|
|
||||||
# We use EXCLUDE_FROM_ALL so that only the explicit dependencies are used
|
# We use EXCLUDE_FROM_ALL so that only the explicit dependencies are used
|
||||||
# (PolarSSL also has test and example programs in their CMakeLists.txt, we don't want those)
|
# (PolarSSL also has test and example programs in their CMakeLists.txt, we don't want those)
|
||||||
include(lib/polarssl.cmake)
|
include(lib/polarssl.cmake EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
set_exe_flags()
|
set_exe_flags()
|
||||||
|
|
||||||
add_subdirectory (src)
|
add_subdirectory (src)
|
||||||
|
|
||||||
if(${SELF_TEST})
|
if(${SELF_TEST})
|
||||||
|
message("Tests enabled")
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory (tests)
|
add_subdirectory (tests)
|
||||||
endif()
|
endif()
|
||||||
|
1
lib/libevent
Submodule
1
lib/libevent
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 0b49ae34594533daa82c06a506078de9e336a013
|
@ -5,6 +5,7 @@ project (MCServer)
|
|||||||
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/")
|
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/")
|
||||||
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/jsoncpp/include")
|
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/jsoncpp/include")
|
||||||
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include")
|
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include")
|
||||||
|
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/libevent/include")
|
||||||
|
|
||||||
set(FOLDERS
|
set(FOLDERS
|
||||||
OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings
|
OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings
|
||||||
@ -323,4 +324,4 @@ endif ()
|
|||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib)
|
target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib)
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(${EXECUTABLE} luaexpat jsoncpp polarssl zlib sqlite lua SQLiteCpp)
|
target_link_libraries(${EXECUTABLE} luaexpat jsoncpp polarssl zlib sqlite lua SQLiteCpp event_core event_extra)
|
||||||
|
@ -268,33 +268,47 @@ template class SizeChecker<UInt16, 2>;
|
|||||||
#include "OSSupport/StackTrace.h"
|
#include "OSSupport/StackTrace.h"
|
||||||
#else
|
#else
|
||||||
// Logging functions
|
// Logging functions
|
||||||
void inline LOGERROR(const char* a_Format, ...) FORMATSTRING(1, 2);
|
void inline LOGERROR(const char * a_Format, ...) FORMATSTRING(1, 2);
|
||||||
|
|
||||||
void inline LOGERROR(const char* a_Format, ...)
|
void inline LOGERROR(const char * a_Format, ...)
|
||||||
{
|
{
|
||||||
va_list argList;
|
va_list argList;
|
||||||
va_start(argList, a_Format);
|
va_start(argList, a_Format);
|
||||||
vprintf(a_Format, argList);
|
vprintf(a_Format, argList);
|
||||||
|
putchar('\n');
|
||||||
va_end(argList);
|
va_end(argList);
|
||||||
}
|
}
|
||||||
|
|
||||||
void inline LOGWARNING(const char* a_Format, ...) FORMATSTRING(1, 2);
|
void inline LOGWARNING(const char * a_Format, ...) FORMATSTRING(1, 2);
|
||||||
|
|
||||||
void inline LOGWARNING(const char* a_Format, ...)
|
void inline LOGWARNING(const char * a_Format, ...)
|
||||||
{
|
{
|
||||||
va_list argList;
|
va_list argList;
|
||||||
va_start(argList, a_Format);
|
va_start(argList, a_Format);
|
||||||
vprintf(a_Format, argList);
|
vprintf(a_Format, argList);
|
||||||
|
putchar('\n');
|
||||||
va_end(argList);
|
va_end(argList);
|
||||||
}
|
}
|
||||||
|
|
||||||
void inline LOGD(const char* a_Format, ...) FORMATSTRING(1, 2);
|
void inline LOGD(const char * a_Format, ...) FORMATSTRING(1, 2);
|
||||||
|
|
||||||
void inline LOGD(const char* a_Format, ...)
|
void inline LOGD(const char * a_Format, ...)
|
||||||
{
|
{
|
||||||
va_list argList;
|
va_list argList;
|
||||||
va_start(argList, a_Format);
|
va_start(argList, a_Format);
|
||||||
vprintf(a_Format, argList);
|
vprintf(a_Format, argList);
|
||||||
|
putchar('\n');
|
||||||
|
va_end(argList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void inline LOG(const char * a_Format, ...) FORMATSTRING(1, 2);
|
||||||
|
|
||||||
|
void inline LOG(const char * a_Format, ...)
|
||||||
|
{
|
||||||
|
va_list argList;
|
||||||
|
va_start(argList, a_Format);
|
||||||
|
vprintf(a_Format, argList);
|
||||||
|
putchar('\n');
|
||||||
va_end(argList);
|
va_end(argList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,12 +10,17 @@ SET (SRCS
|
|||||||
Event.cpp
|
Event.cpp
|
||||||
File.cpp
|
File.cpp
|
||||||
GZipFile.cpp
|
GZipFile.cpp
|
||||||
|
HostnameLookup.cpp
|
||||||
|
IPLookup.cpp
|
||||||
IsThread.cpp
|
IsThread.cpp
|
||||||
ListenThread.cpp
|
ListenThread.cpp
|
||||||
|
NetworkSingleton.cpp
|
||||||
Semaphore.cpp
|
Semaphore.cpp
|
||||||
|
ServerHandleImpl.cpp
|
||||||
Socket.cpp
|
Socket.cpp
|
||||||
SocketThreads.cpp
|
SocketThreads.cpp
|
||||||
StackTrace.cpp
|
StackTrace.cpp
|
||||||
|
TCPLinkImpl.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
SET (HDRS
|
SET (HDRS
|
||||||
@ -24,13 +29,19 @@ SET (HDRS
|
|||||||
Event.h
|
Event.h
|
||||||
File.h
|
File.h
|
||||||
GZipFile.h
|
GZipFile.h
|
||||||
|
HostnameLookup.h
|
||||||
|
IPLookup.h
|
||||||
IsThread.h
|
IsThread.h
|
||||||
ListenThread.h
|
ListenThread.h
|
||||||
|
Network.h
|
||||||
|
NetworkSingleton.h
|
||||||
Queue.h
|
Queue.h
|
||||||
Semaphore.h
|
Semaphore.h
|
||||||
|
ServerHandleImpl.h
|
||||||
Socket.h
|
Socket.h
|
||||||
SocketThreads.h
|
SocketThreads.h
|
||||||
StackTrace.h
|
StackTrace.h
|
||||||
|
TCPLinkImpl.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT MSVC)
|
if(NOT MSVC)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||||
|
#include "CriticalSection.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
124
src/OSSupport/HostnameLookup.cpp
Normal file
124
src/OSSupport/HostnameLookup.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
|
||||||
|
// HostnameLookup.cpp
|
||||||
|
|
||||||
|
// Implements the cHostnameLookup class representing an in-progress hostname-to-IP lookup
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "HostnameLookup.h"
|
||||||
|
#include <event2/dns.h>
|
||||||
|
#include "NetworkSingleton.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cHostnameLookup:
|
||||||
|
|
||||||
|
cHostnameLookup::cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks):
|
||||||
|
m_Callbacks(a_Callbacks)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cHostnameLookup::Lookup(const AString & a_Hostname)
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a_Self)
|
||||||
|
{
|
||||||
|
// Get the Self class:
|
||||||
|
cHostnameLookup * Self = reinterpret_cast<cHostnameLookup *>(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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the success handler for each entry received:
|
||||||
|
bool HasResolved = false;
|
||||||
|
evutil_addrinfo * OrigAddr = a_Addr;
|
||||||
|
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<sockaddr_in *>(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<sockaddr_in6 *>(a_Addr->ai_addr);
|
||||||
|
evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// Unknown address family, handle as if this entry wasn't received
|
||||||
|
continue; // for (a_Addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self->m_Callbacks->OnNameResolved(Self->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.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Self->m_Callbacks->OnFinished();
|
||||||
|
}
|
||||||
|
evutil_freeaddrinfo(OrigAddr);
|
||||||
|
cNetworkSingleton::Get().RemoveHostnameLookup(Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cNetwork API:
|
||||||
|
|
||||||
|
bool cNetwork::HostnameToIP(
|
||||||
|
const AString & a_Hostname,
|
||||||
|
cNetwork::cResolveNameCallbacksPtr a_Callbacks
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto Lookup = std::make_shared<cHostnameLookup>(a_Callbacks);
|
||||||
|
cNetworkSingleton::Get().AddHostnameLookup(Lookup);
|
||||||
|
Lookup->Lookup(a_Hostname);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
47
src/OSSupport/HostnameLookup.h
Normal file
47
src/OSSupport/HostnameLookup.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
// HostnameLookup.h
|
||||||
|
|
||||||
|
// Declares the cHostnameLookup class representing an in-progress hostname-to-IP lookup
|
||||||
|
|
||||||
|
// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Network.h"
|
||||||
|
#include <event2/util.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Holds information about an in-progress Hostname-to-IP lookup. */
|
||||||
|
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);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/** 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);
|
||||||
|
};
|
||||||
|
typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr;
|
||||||
|
typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
111
src/OSSupport/IPLookup.cpp
Normal file
111
src/OSSupport/IPLookup.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
|
||||||
|
// IPLookup.cpp
|
||||||
|
|
||||||
|
// Implements the cIPLookup class representing an IP-to-hostname lookup in progress.
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "IPLookup.h"
|
||||||
|
#include <event2/dns.h>
|
||||||
|
#include "NetworkSingleton.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cIPLookup:
|
||||||
|
|
||||||
|
cIPLookup::cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks):
|
||||||
|
m_Callbacks(a_Callbacks)
|
||||||
|
{
|
||||||
|
ASSERT(a_Callbacks != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool cIPLookup::Lookup(const AString & a_IP)
|
||||||
|
{
|
||||||
|
// Parse the IP address string into a sockaddr structure:
|
||||||
|
m_IP = a_IP;
|
||||||
|
sockaddr_storage sa;
|
||||||
|
int salen = static_cast<int>(sizeof(sa));
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
if (evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) != 0)
|
||||||
|
{
|
||||||
|
LOGD("Failed to parse IP address \"%s\".", a_IP.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
case AF_INET:
|
||||||
|
{
|
||||||
|
sockaddr_in * sa4 = reinterpret_cast<sockaddr_in *>(&sa);
|
||||||
|
evdns_base_resolve_reverse(cNetworkSingleton::Get().GetDNSBase(), &(sa4->sin_addr), 0, Callback, this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AF_INET6:
|
||||||
|
{
|
||||||
|
sockaddr_in6 * sa6 = reinterpret_cast<sockaddr_in6 *>(&sa);
|
||||||
|
evdns_base_resolve_reverse_ipv6(cNetworkSingleton::Get().GetDNSBase(), &(sa6->sin6_addr), 0, Callback, this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family);
|
||||||
|
ASSERT(!"Unknown address family");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // switch (address family)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self)
|
||||||
|
{
|
||||||
|
// Get the Self class:
|
||||||
|
cIPLookup * Self = reinterpret_cast<cIPLookup *>(a_Self);
|
||||||
|
ASSERT(Self != nullptr);
|
||||||
|
|
||||||
|
// Call the proper callback based on the event received:
|
||||||
|
if ((a_Result != 0) || (a_Addresses == nullptr))
|
||||||
|
{
|
||||||
|
// An error has occurred, notify the error callback:
|
||||||
|
Self->m_Callbacks->OnError(a_Result, evutil_socket_error_to_string(a_Result));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Call the success handler:
|
||||||
|
Self->m_Callbacks->OnNameResolved(*(reinterpret_cast<char **>(a_Addresses)), Self->m_IP);
|
||||||
|
Self->m_Callbacks->OnFinished();
|
||||||
|
}
|
||||||
|
cNetworkSingleton::Get().RemoveIPLookup(Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cNetwork API:
|
||||||
|
|
||||||
|
bool cNetwork::IPToHostName(
|
||||||
|
const AString & a_IP,
|
||||||
|
cNetwork::cResolveNameCallbacksPtr a_Callbacks
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto res = std::make_shared<cIPLookup>(a_Callbacks);
|
||||||
|
cNetworkSingleton::Get().AddIPLookup(res);
|
||||||
|
return res->Lookup(a_IP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
49
src/OSSupport/IPLookup.h
Normal file
49
src/OSSupport/IPLookup.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
// IPLookup.h
|
||||||
|
|
||||||
|
// Declares the cIPLookup class representing an IP-to-hostname lookup in progress.
|
||||||
|
|
||||||
|
// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Network.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Holds information about an in-progress IP-to-Hostname lookup. */
|
||||||
|
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);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/** The callbacks to call for resolved names / errors. */
|
||||||
|
cNetwork::cResolveNameCallbacksPtr m_Callbacks;
|
||||||
|
|
||||||
|
/** The IP that was queried (needed for the callbacks). */
|
||||||
|
AString m_IP;
|
||||||
|
|
||||||
|
|
||||||
|
/** 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);
|
||||||
|
};
|
||||||
|
typedef SharedPtr<cIPLookup> cIPLookupPtr;
|
||||||
|
typedef std::vector<cIPLookupPtr> cIPLookupPtrs;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
246
src/OSSupport/Network.h
Normal file
246
src/OSSupport/Network.h
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
|
||||||
|
// Network.h
|
||||||
|
|
||||||
|
// Declares the classes used for the Network API
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// fwd:
|
||||||
|
class cTCPLink;
|
||||||
|
typedef SharedPtr<cTCPLink> cTCPLinkPtr;
|
||||||
|
typedef std::vector<cTCPLinkPtr> cTCPLinkPtrs;
|
||||||
|
class cServerHandle;
|
||||||
|
typedef SharedPtr<cServerHandle> cServerHandlePtr;
|
||||||
|
typedef std::vector<cServerHandlePtr> cServerHandlePtrs;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Interface that provides the methods available on a single TCP connection. */
|
||||||
|
class cTCPLink
|
||||||
|
{
|
||||||
|
friend class cNetwork;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class cCallbacks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Force a virtual destructor for all descendants:
|
||||||
|
virtual ~cCallbacks() {}
|
||||||
|
|
||||||
|
/** Called when the cTCPLink for the connection is created.
|
||||||
|
The callback may store the cTCPLink instance for later use, but it should remove it in OnError(), OnRemoteClosed() or right after Close(). */
|
||||||
|
virtual void OnLinkCreated(cTCPLinkPtr a_Link) = 0;
|
||||||
|
|
||||||
|
/** Called when there's data incoming from the remote peer. */
|
||||||
|
virtual void OnReceivedData(const char * a_Data, size_t a_Length) = 0;
|
||||||
|
|
||||||
|
/** Called when the remote end closes the connection.
|
||||||
|
The link is still available for connection information query (IP / port).
|
||||||
|
Sending data on the link is not an error, but the data won't be delivered. */
|
||||||
|
virtual void OnRemoteClosed(void) = 0;
|
||||||
|
|
||||||
|
/** Called when an error is detected on the connection. */
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0;
|
||||||
|
};
|
||||||
|
typedef SharedPtr<cCallbacks> cCallbacksPtr;
|
||||||
|
|
||||||
|
|
||||||
|
// Force a virtual destructor for all descendants:
|
||||||
|
virtual ~cTCPLink() {}
|
||||||
|
|
||||||
|
/** Queues the specified data for sending to the remote peer.
|
||||||
|
Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */
|
||||||
|
virtual bool Send(const void * a_Data, size_t a_Length) = 0;
|
||||||
|
|
||||||
|
/** Queues the specified data for sending to the remote peer.
|
||||||
|
Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */
|
||||||
|
bool Send(const AString & a_Data)
|
||||||
|
{
|
||||||
|
return Send(a_Data.data(), a_Data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the IP address of the local endpoint of the connection. */
|
||||||
|
virtual AString GetLocalIP(void) const = 0;
|
||||||
|
|
||||||
|
/** Returns the port used by the local endpoint of the connection. */
|
||||||
|
virtual UInt16 GetLocalPort(void) const = 0;
|
||||||
|
|
||||||
|
/** Returns the IP address of the remote endpoint of the connection. */
|
||||||
|
virtual AString GetRemoteIP(void) const = 0;
|
||||||
|
|
||||||
|
/** Returns the port used by the remote endpoint of the connection. */
|
||||||
|
virtual UInt16 GetRemotePort(void) const = 0;
|
||||||
|
|
||||||
|
/** Closes the link gracefully.
|
||||||
|
The link will send any queued outgoing data, then it will send the FIN packet.
|
||||||
|
The link will still receive incoming data from remote until the remote closes the connection. */
|
||||||
|
virtual void Shutdown(void) = 0;
|
||||||
|
|
||||||
|
/** Drops the connection without any more processing.
|
||||||
|
Sends the RST packet, queued outgoing and incoming data is lost. */
|
||||||
|
virtual void Close(void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Callbacks to be used for the various situations. */
|
||||||
|
cCallbacksPtr m_Callbacks;
|
||||||
|
|
||||||
|
|
||||||
|
/** Creates a new link, with the specified callbacks. */
|
||||||
|
cTCPLink(cCallbacksPtr a_Callbacks):
|
||||||
|
m_Callbacks(a_Callbacks)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Interface that provides the methods available on a listening server socket. */
|
||||||
|
class cServerHandle
|
||||||
|
{
|
||||||
|
friend class cNetwork;
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Force a virtual destructor for all descendants:
|
||||||
|
virtual ~cServerHandle() {}
|
||||||
|
|
||||||
|
/** Stops the server, no more incoming connections will be accepted.
|
||||||
|
All current connections will be shut down (cTCPLink::Shutdown()). */
|
||||||
|
virtual void Close(void) = 0;
|
||||||
|
|
||||||
|
/** Returns true if the server has been started correctly and is currently listening for incoming connections. */
|
||||||
|
virtual bool IsListening(void) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class cNetwork
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Callbacks used for connecting to other servers as a client. */
|
||||||
|
class cConnectCallbacks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Force a virtual destructor for all descendants:
|
||||||
|
virtual ~cConnectCallbacks() {}
|
||||||
|
|
||||||
|
/** Called when the Connect call succeeds.
|
||||||
|
Provides the newly created link that can be used for communication. */
|
||||||
|
virtual void OnConnected(cTCPLink & a_Link) = 0;
|
||||||
|
|
||||||
|
/** Called when the Connect call fails. */
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0;
|
||||||
|
};
|
||||||
|
typedef SharedPtr<cConnectCallbacks> cConnectCallbacksPtr;
|
||||||
|
|
||||||
|
|
||||||
|
/** Callbacks used when listening for incoming connections as a server. */
|
||||||
|
class cListenCallbacks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Force a virtual destructor for all descendants:
|
||||||
|
virtual ~cListenCallbacks() {}
|
||||||
|
|
||||||
|
/** Called when the TCP server created with Listen() receives a new incoming connection.
|
||||||
|
Returns the link callbacks that the server should use for the newly created link.
|
||||||
|
If a nullptr is returned, the connection is dropped immediately;
|
||||||
|
otherwise a new cTCPLink instance is created and OnAccepted() is called. */
|
||||||
|
virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) = 0;
|
||||||
|
|
||||||
|
/** Called when the TCP server created with Listen() creates a new link for an incoming connection.
|
||||||
|
Provides the newly created Link that can be used for communication.
|
||||||
|
Called right after a successful OnIncomingConnection(). */
|
||||||
|
virtual void OnAccepted(cTCPLink & a_Link) = 0;
|
||||||
|
|
||||||
|
/** Called when the socket fails to listen on the specified port. */
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0;
|
||||||
|
};
|
||||||
|
typedef SharedPtr<cListenCallbacks> cListenCallbacksPtr;
|
||||||
|
|
||||||
|
|
||||||
|
/** Callbacks used when resolving names to IPs. */
|
||||||
|
class cResolveNameCallbacks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Force a virtual destructor for all descendants:
|
||||||
|
virtual ~cResolveNameCallbacks() {}
|
||||||
|
|
||||||
|
/** Called when the hostname is successfully resolved into an IP address.
|
||||||
|
May be called multiple times if a name 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.
|
||||||
|
If an error is reported, the OnFinished() callback is not called. */
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 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<cResolveNameCallbacks> cResolveNameCallbacksPtr;
|
||||||
|
|
||||||
|
|
||||||
|
/** Queues a TCP connection to be made to the specified host.
|
||||||
|
Calls one the connection callbacks (success, error) when the connection is successfully established, or upon failure.
|
||||||
|
The a_LinkCallbacks is passed to the newly created cTCPLink.
|
||||||
|
Returns true if queueing was successful, false on failure to queue.
|
||||||
|
Note that the return value doesn't report the success of the actual connection; the connection is established asynchronously in the background.
|
||||||
|
Implemented in TCPLinkImpl.cpp. */
|
||||||
|
static bool Connect(
|
||||||
|
const AString & a_Host,
|
||||||
|
UInt16 a_Port,
|
||||||
|
cConnectCallbacksPtr a_ConnectCallbacks,
|
||||||
|
cTCPLink::cCallbacksPtr a_LinkCallbacks
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/** Opens up the specified port for incoming connections.
|
||||||
|
Calls an OnAccepted callback for each incoming connection.
|
||||||
|
A cTCPLink with the specified link callbacks is created for each connection.
|
||||||
|
Returns a cServerHandle that can be used to query the operation status and close the server.
|
||||||
|
Implemented in ServerHandleImpl.cpp. */
|
||||||
|
static cServerHandlePtr Listen(
|
||||||
|
UInt16 a_Port,
|
||||||
|
cListenCallbacksPtr a_ListenCallbacks
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/** Queues a DNS query to resolve the specified hostname to IP address.
|
||||||
|
Calls one of the callbacks when the resolving succeeds, or when it fails.
|
||||||
|
Returns true if queueing was successful, false if not.
|
||||||
|
Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background.
|
||||||
|
Implemented in HostnameLookup.cpp. */
|
||||||
|
static bool HostnameToIP(
|
||||||
|
const AString & a_Hostname,
|
||||||
|
cResolveNameCallbacksPtr a_Callbacks
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/** Queues a DNS query to resolve the specified IP address to a hostname.
|
||||||
|
Calls one of the callbacks when the resolving succeeds, or when it fails.
|
||||||
|
Returns true if queueing was successful, false if not.
|
||||||
|
Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background.
|
||||||
|
Implemented in IPLookup.cpp. */
|
||||||
|
static bool IPToHostName(
|
||||||
|
const AString & a_IP,
|
||||||
|
cResolveNameCallbacksPtr a_Callbacks
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
245
src/OSSupport/NetworkSingleton.cpp
Normal file
245
src/OSSupport/NetworkSingleton.cpp
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
|
||||||
|
// NetworkSingleton.cpp
|
||||||
|
|
||||||
|
// Implements the cNetworkSingleton class representing the storage for global data pertaining to network API
|
||||||
|
// such as a list of all connections, all listening sockets and the LibEvent dispatch thread.
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "NetworkSingleton.h"
|
||||||
|
#include <event2/event.h>
|
||||||
|
#include <event2/thread.h>
|
||||||
|
#include <event2/bufferevent.h>
|
||||||
|
#include <event2/dns.h>
|
||||||
|
#include <event2/listener.h>
|
||||||
|
#include "IPLookup.h"
|
||||||
|
#include "HostnameLookup.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cNetworkSingleton::cNetworkSingleton(void)
|
||||||
|
{
|
||||||
|
// Windows: initialize networking:
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA wsaData;
|
||||||
|
memset(&wsaData, 0, sizeof(wsaData));
|
||||||
|
int res = WSAStartup (MAKEWORD(2, 2), &wsaData);
|
||||||
|
if (res != 0)
|
||||||
|
{
|
||||||
|
int err = WSAGetLastError();
|
||||||
|
LOGWARNING("WSAStartup failed: %d, WSAGLE = %d (%s)", res, err, evutil_socket_error_to_string(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// Initialize LibEvent logging:
|
||||||
|
event_set_log_callback(LogCallback);
|
||||||
|
|
||||||
|
// Initialize threading:
|
||||||
|
#if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED)
|
||||||
|
evthread_use_windows_threads();
|
||||||
|
#elif defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED)
|
||||||
|
evthread_use_pthreads();
|
||||||
|
#else
|
||||||
|
#error No threading implemented for EVTHREAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create the main event_base:
|
||||||
|
m_EventBase = event_base_new();
|
||||||
|
if (m_EventBase == nullptr)
|
||||||
|
{
|
||||||
|
LOGERROR("Failed to initialize LibEvent. The server will now terminate.");
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the event loop thread:
|
||||||
|
std::thread EventLoopThread(RunEventLoop, this);
|
||||||
|
EventLoopThread.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cNetworkSingleton::~cNetworkSingleton()
|
||||||
|
{
|
||||||
|
// Wait for the LibEvent event loop to terminate:
|
||||||
|
event_base_loopbreak(m_EventBase);
|
||||||
|
m_EventLoopTerminated.Wait();
|
||||||
|
|
||||||
|
// Remove all objects:
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cNetworkSingleton & cNetworkSingleton::Get(void)
|
||||||
|
{
|
||||||
|
static cNetworkSingleton Instance;
|
||||||
|
return Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg)
|
||||||
|
{
|
||||||
|
switch (a_Severity)
|
||||||
|
{
|
||||||
|
case _EVENT_LOG_DEBUG: LOGD ("LibEvent: %s", a_Msg); break;
|
||||||
|
case _EVENT_LOG_MSG: LOG ("LibEvent: %s", a_Msg); break;
|
||||||
|
case _EVENT_LOG_WARN: LOGWARNING("LibEvent: %s", a_Msg); break;
|
||||||
|
case _EVENT_LOG_ERR: LOGERROR ("LibEvent: %s", a_Msg); break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
LOGWARNING("LibEvent: Unknown log severity (%d): %s", a_Severity, a_Msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self)
|
||||||
|
{
|
||||||
|
event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY);
|
||||||
|
a_Self->m_EventLoopTerminated.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cNetworkSingleton::AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CS);
|
||||||
|
m_HostnameLookups.push_back(a_HostnameLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CS);
|
||||||
|
m_IPLookups.push_back(a_IPLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CS);
|
||||||
|
m_Connections.push_back(a_Link);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CS);
|
||||||
|
for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
|
||||||
|
{
|
||||||
|
if (itr->get() == a_Link)
|
||||||
|
{
|
||||||
|
m_Connections.erase(itr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} // for itr - m_Connections[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cNetworkSingleton::AddServer(cServerHandleImplPtr a_Server)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CS);
|
||||||
|
m_Servers.push_back(a_Server);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cNetworkSingleton::RemoveServer(const cServerHandleImpl * a_Server)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CS);
|
||||||
|
for (auto itr = m_Servers.begin(), end = m_Servers.end(); itr != end; ++itr)
|
||||||
|
{
|
||||||
|
if (itr->get() == a_Server)
|
||||||
|
{
|
||||||
|
m_Servers.erase(itr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} // for itr - m_Servers[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
134
src/OSSupport/NetworkSingleton.h
Normal file
134
src/OSSupport/NetworkSingleton.h
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
|
||||||
|
// NetworkSingleton.h
|
||||||
|
|
||||||
|
// Declares the cNetworkSingleton class representing the storage for global data pertaining to network API
|
||||||
|
// such as a list of all connections, all listening sockets and the LibEvent dispatch thread.
|
||||||
|
|
||||||
|
// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Network.h"
|
||||||
|
#include "CriticalSection.h"
|
||||||
|
#include "Event.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// fwd:
|
||||||
|
struct event_base;
|
||||||
|
struct evdns_base;
|
||||||
|
class cTCPLinkImpl;
|
||||||
|
typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr;
|
||||||
|
typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs;
|
||||||
|
class cServerHandleImpl;
|
||||||
|
typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
|
||||||
|
typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs;
|
||||||
|
class cHostnameLookup;
|
||||||
|
typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr;
|
||||||
|
typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs;
|
||||||
|
class cIPLookup;
|
||||||
|
typedef SharedPtr<cIPLookup> cIPLookupPtr;
|
||||||
|
typedef std::vector<cIPLookupPtr> cIPLookupPtrs;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class cNetworkSingleton
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~cNetworkSingleton();
|
||||||
|
|
||||||
|
/** Returns the singleton instance of this class */
|
||||||
|
static cNetworkSingleton & Get(void);
|
||||||
|
|
||||||
|
/** 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);
|
||||||
|
|
||||||
|
/** Adds the specified link to m_Connections.
|
||||||
|
Used by the underlying link implementation when a new link is created. */
|
||||||
|
void AddLink(cTCPLinkImplPtr a_Link);
|
||||||
|
|
||||||
|
/** Removes the specified link from m_Connections.
|
||||||
|
Used by the underlying link implementation when the link is closed / errored. */
|
||||||
|
void RemoveLink(const cTCPLinkImpl * a_Link);
|
||||||
|
|
||||||
|
/** Adds the specified link to m_Servers.
|
||||||
|
Used by the underlying server handle implementation when a new listening server is created.
|
||||||
|
Only servers that succeed in listening are added. */
|
||||||
|
void AddServer(cServerHandleImplPtr a_Server);
|
||||||
|
|
||||||
|
/** Removes the specified server from m_Servers.
|
||||||
|
Used by the underlying server handle implementation when the server is closed. */
|
||||||
|
void RemoveServer(const cServerHandleImpl * a_Server);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/** Event that gets signalled when the event loop terminates. */
|
||||||
|
cEvent m_EventLoopTerminated;
|
||||||
|
|
||||||
|
|
||||||
|
/** Initializes the LibEvent internals. */
|
||||||
|
cNetworkSingleton(void);
|
||||||
|
|
||||||
|
/** Converts LibEvent-generated log events into log messages in MCS log. */
|
||||||
|
static void LogCallback(int a_Severity, const char * a_Msg);
|
||||||
|
|
||||||
|
/** Implements the thread that runs LibEvent's event dispatcher loop. */
|
||||||
|
static void RunEventLoop(cNetworkSingleton * a_Self);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
334
src/OSSupport/ServerHandleImpl.cpp
Normal file
334
src/OSSupport/ServerHandleImpl.cpp
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
|
||||||
|
// ServerHandleImpl.cpp
|
||||||
|
|
||||||
|
// Implements the cServerHandleImpl class implementing the TCP server functionality
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "ServerHandleImpl.h"
|
||||||
|
#include "TCPLinkImpl.h"
|
||||||
|
#include "NetworkSingleton.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Globals:
|
||||||
|
|
||||||
|
static bool IsValidSocket(evutil_socket_t a_Socket)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return (a_Socket != INVALID_SOCKET);
|
||||||
|
#else // _WIN32
|
||||||
|
return (a_Socket >= 0);
|
||||||
|
#endif // else _WIN32
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cServerHandleImpl:
|
||||||
|
|
||||||
|
cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks):
|
||||||
|
m_ListenCallbacks(a_ListenCallbacks),
|
||||||
|
m_ConnListener(nullptr),
|
||||||
|
m_SecondaryConnListener(nullptr),
|
||||||
|
m_IsListening(false),
|
||||||
|
m_ErrorCode(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cServerHandleImpl::~cServerHandleImpl()
|
||||||
|
{
|
||||||
|
if (m_ConnListener != nullptr)
|
||||||
|
{
|
||||||
|
evconnlistener_free(m_ConnListener);
|
||||||
|
}
|
||||||
|
if (m_SecondaryConnListener != nullptr)
|
||||||
|
{
|
||||||
|
evconnlistener_free(m_SecondaryConnListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cServerHandleImpl::Close(void)
|
||||||
|
{
|
||||||
|
// Stop the listener sockets:
|
||||||
|
evconnlistener_disable(m_ConnListener);
|
||||||
|
if (m_SecondaryConnListener != nullptr)
|
||||||
|
{
|
||||||
|
evconnlistener_disable(m_SecondaryConnListener);
|
||||||
|
}
|
||||||
|
m_IsListening = false;
|
||||||
|
|
||||||
|
// Shutdown all connections:
|
||||||
|
cTCPLinkImplPtrs Conns;
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CS);
|
||||||
|
std::swap(Conns, m_Connections);
|
||||||
|
}
|
||||||
|
for (auto conn: Conns)
|
||||||
|
{
|
||||||
|
conn->Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the ptr to self, so that the object may be freed:
|
||||||
|
m_SelfPtr.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cServerHandleImplPtr cServerHandleImpl::Listen(
|
||||||
|
UInt16 a_Port,
|
||||||
|
cNetwork::cListenCallbacksPtr a_ListenCallbacks
|
||||||
|
)
|
||||||
|
{
|
||||||
|
cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)};
|
||||||
|
res->m_SelfPtr = res;
|
||||||
|
if (res->Listen(a_Port))
|
||||||
|
{
|
||||||
|
cNetworkSingleton::Get().AddServer(res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg);
|
||||||
|
res->m_SelfPtr.reset();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool cServerHandleImpl::Listen(UInt16 a_Port)
|
||||||
|
{
|
||||||
|
// Make sure the cNetwork internals are innitialized:
|
||||||
|
cNetworkSingleton::Get();
|
||||||
|
|
||||||
|
// Set up the main socket:
|
||||||
|
// It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available.
|
||||||
|
bool NeedsTwoSockets = false;
|
||||||
|
int err;
|
||||||
|
evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (!IsValidSocket(MainSock))
|
||||||
|
{
|
||||||
|
// Failed to create IPv6 socket, create an IPv4 one instead:
|
||||||
|
err = EVUTIL_SOCKET_ERROR();
|
||||||
|
LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
|
||||||
|
MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (!IsValidSocket(MainSock))
|
||||||
|
{
|
||||||
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
||||||
|
Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind to all interfaces:
|
||||||
|
sockaddr_in name;
|
||||||
|
memset(&name, 0, sizeof(name));
|
||||||
|
name.sin_family = AF_INET;
|
||||||
|
name.sin_port = ntohs(a_Port);
|
||||||
|
if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
|
||||||
|
{
|
||||||
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
||||||
|
Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
|
||||||
|
evutil_closesocket(MainSock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// IPv6 socket created, switch it into "dualstack" mode:
|
||||||
|
UInt32 Zero = 0;
|
||||||
|
#ifdef _WIN32
|
||||||
|
// WinXP doesn't support this feature, so if the setting fails, create another socket later on:
|
||||||
|
int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
|
||||||
|
err = EVUTIL_SOCKET_ERROR();
|
||||||
|
NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
|
||||||
|
LOGD("setsockopt(IPV6_V6ONLY) returned %d, err is %d (%s). %s",
|
||||||
|
res, err, evutil_socket_error_to_string(err),
|
||||||
|
NeedsTwoSockets ? "Second socket will be created" : "Second socket not needed"
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Bind to all interfaces:
|
||||||
|
sockaddr_in6 name;
|
||||||
|
memset(&name, 0, sizeof(name));
|
||||||
|
name.sin6_family = AF_INET6;
|
||||||
|
name.sin6_port = ntohs(a_Port);
|
||||||
|
if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
|
||||||
|
{
|
||||||
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
||||||
|
Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
|
||||||
|
evutil_closesocket(MainSock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (evutil_make_socket_nonblocking(MainSock) != 0)
|
||||||
|
{
|
||||||
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
||||||
|
Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
|
||||||
|
evutil_closesocket(MainSock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (listen(MainSock, 0) != 0)
|
||||||
|
{
|
||||||
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
||||||
|
Printf(m_ErrorMsg, "Cannot listen on port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
|
||||||
|
evutil_closesocket(MainSock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock);
|
||||||
|
m_IsListening = true;
|
||||||
|
if (!NeedsTwoSockets)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a secondary socket is required (WinXP dual-stack), create it here:
|
||||||
|
LOGD("Creating a second socket for IPv4");
|
||||||
|
evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (!IsValidSocket(SecondSock))
|
||||||
|
{
|
||||||
|
err = EVUTIL_SOCKET_ERROR();
|
||||||
|
LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
|
||||||
|
return true; // Report as success, the primary socket is working
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the secondary socket nonblocking:
|
||||||
|
if (evutil_make_socket_nonblocking(SecondSock) != 0)
|
||||||
|
{
|
||||||
|
err = EVUTIL_SOCKET_ERROR();
|
||||||
|
LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
|
||||||
|
evutil_closesocket(SecondSock);
|
||||||
|
return true; // Report as success, the primary socket is working
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind to all IPv4 interfaces:
|
||||||
|
sockaddr_in name;
|
||||||
|
memset(&name, 0, sizeof(name));
|
||||||
|
name.sin_family = AF_INET;
|
||||||
|
name.sin_port = ntohs(a_Port);
|
||||||
|
if (bind(SecondSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
|
||||||
|
{
|
||||||
|
err = EVUTIL_SOCKET_ERROR();
|
||||||
|
LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
|
||||||
|
evutil_closesocket(SecondSock);
|
||||||
|
return true; // Report as success, the primary socket is working
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(SecondSock, 0) != 0)
|
||||||
|
{
|
||||||
|
err = EVUTIL_SOCKET_ERROR();
|
||||||
|
LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
|
||||||
|
evutil_closesocket(SecondSock);
|
||||||
|
return true; // Report as success, the primary socket is working
|
||||||
|
}
|
||||||
|
|
||||||
|
m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self)
|
||||||
|
{
|
||||||
|
// Cast to true self:
|
||||||
|
cServerHandleImpl * Self = reinterpret_cast<cServerHandleImpl *>(a_Self);
|
||||||
|
ASSERT(Self != nullptr);
|
||||||
|
ASSERT(Self->m_SelfPtr != nullptr);
|
||||||
|
|
||||||
|
// Get the textual IP address and port number out of a_Addr:
|
||||||
|
char IPAddress[128];
|
||||||
|
evutil_inet_ntop(a_Addr->sa_family, a_Addr->sa_data, IPAddress, ARRAYCOUNT(IPAddress));
|
||||||
|
UInt16 Port = 0;
|
||||||
|
switch (a_Addr->sa_family)
|
||||||
|
{
|
||||||
|
case AF_INET:
|
||||||
|
{
|
||||||
|
sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr);
|
||||||
|
Port = ntohs(sin->sin_port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AF_INET6:
|
||||||
|
{
|
||||||
|
sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(a_Addr);
|
||||||
|
Port = ntohs(sin6->sin6_port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the OnIncomingConnection callback to get the link callbacks to use:
|
||||||
|
cTCPLink::cCallbacksPtr LinkCallbacks = Self->m_ListenCallbacks->OnIncomingConnection(IPAddress, Port);
|
||||||
|
if (LinkCallbacks == nullptr)
|
||||||
|
{
|
||||||
|
// Drop the connection:
|
||||||
|
evutil_closesocket(a_Socket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new cTCPLink for the incoming connection:
|
||||||
|
cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast<socklen_t>(a_Len));
|
||||||
|
{
|
||||||
|
cCSLock Lock(Self->m_CS);
|
||||||
|
Self->m_Connections.push_back(Link);
|
||||||
|
} // Lock(m_CS)
|
||||||
|
LinkCallbacks->OnLinkCreated(Link);
|
||||||
|
Link->Enable(Link);
|
||||||
|
|
||||||
|
// Call the OnAccepted callback:
|
||||||
|
Self->m_ListenCallbacks->OnAccepted(*Link);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CS);
|
||||||
|
for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
|
||||||
|
{
|
||||||
|
if (itr->get() == a_Link)
|
||||||
|
{
|
||||||
|
m_Connections.erase(itr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} // for itr - m_Connections[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cNetwork API:
|
||||||
|
|
||||||
|
cServerHandlePtr cNetwork::Listen(
|
||||||
|
UInt16 a_Port,
|
||||||
|
cNetwork::cListenCallbacksPtr a_ListenCallbacks
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
105
src/OSSupport/ServerHandleImpl.h
Normal file
105
src/OSSupport/ServerHandleImpl.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
|
||||||
|
// ServerHandleImpl.h
|
||||||
|
|
||||||
|
// Declares the cServerHandleImpl class implementing the TCP server functionality
|
||||||
|
|
||||||
|
// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Network.h"
|
||||||
|
#include <event2/listener.h>
|
||||||
|
#include "CriticalSection.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// fwd:
|
||||||
|
class cTCPLinkImpl;
|
||||||
|
typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr;
|
||||||
|
typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs;
|
||||||
|
class cServerHandleImpl;
|
||||||
|
typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
|
||||||
|
typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class cServerHandleImpl:
|
||||||
|
public cServerHandle
|
||||||
|
{
|
||||||
|
typedef cServerHandle super;
|
||||||
|
friend class cTCPLinkImpl;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Closes the server, dropping all the connections. */
|
||||||
|
~cServerHandleImpl();
|
||||||
|
|
||||||
|
/** Creates a new server instance listening on the specified port.
|
||||||
|
Both IPv4 and IPv6 interfaces are used, if possible.
|
||||||
|
Always returns a server instance; in the event of a failure, the instance holds the error details. Use IsListening() to query success. */
|
||||||
|
static cServerHandleImplPtr Listen(
|
||||||
|
UInt16 a_Port,
|
||||||
|
cNetwork::cListenCallbacksPtr a_ListenCallbacks
|
||||||
|
);
|
||||||
|
|
||||||
|
// cServerHandle overrides:
|
||||||
|
virtual void Close(void) override;
|
||||||
|
virtual bool IsListening(void) const override { return m_IsListening; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** The callbacks used to notify about incoming connections. */
|
||||||
|
cNetwork::cListenCallbacksPtr m_ListenCallbacks;
|
||||||
|
|
||||||
|
/** The LibEvent handle representing the main listening socket. */
|
||||||
|
evconnlistener * m_ConnListener;
|
||||||
|
|
||||||
|
/** The LibEvent handle representing the secondary listening socket (only when side-by-side listening is needed, such as WinXP). */
|
||||||
|
evconnlistener * m_SecondaryConnListener;
|
||||||
|
|
||||||
|
/** Set to true when the server is initialized successfully and is listening for incoming connections. */
|
||||||
|
bool m_IsListening;
|
||||||
|
|
||||||
|
/** Container for all currently active connections on this server. */
|
||||||
|
cTCPLinkImplPtrs m_Connections;
|
||||||
|
|
||||||
|
/** Mutex protecting m_Connections againt multithreaded access. */
|
||||||
|
cCriticalSection m_CS;
|
||||||
|
|
||||||
|
/** Contains the error code for the failure to listen. Only valid for non-listening instances. */
|
||||||
|
int m_ErrorCode;
|
||||||
|
|
||||||
|
/** Contains the error message for the failure to listen. Only valid for non-listening instances. */
|
||||||
|
AString m_ErrorMsg;
|
||||||
|
|
||||||
|
/** The SharedPtr to self, so that it can be passed to created links. */
|
||||||
|
cServerHandleImplPtr m_SelfPtr;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Creates a new instance with the specified callbacks.
|
||||||
|
Initializes the internals, but doesn't start listening yet. */
|
||||||
|
cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks);
|
||||||
|
|
||||||
|
/** Starts listening on the specified port.
|
||||||
|
Returns true if successful, false on failure. On failure, sets m_ErrorCode and m_ErrorMsg. */
|
||||||
|
bool Listen(UInt16 a_Port);
|
||||||
|
|
||||||
|
/** The callback called by LibEvent upon incoming connection. */
|
||||||
|
static void Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self);
|
||||||
|
|
||||||
|
/** Removes the specified link from m_Connections.
|
||||||
|
Called by cTCPLinkImpl when the link is terminated. */
|
||||||
|
void RemoveLink(const cTCPLinkImpl * a_Link);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
331
src/OSSupport/TCPLinkImpl.cpp
Normal file
331
src/OSSupport/TCPLinkImpl.cpp
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
|
||||||
|
// TCPLinkImpl.cpp
|
||||||
|
|
||||||
|
// Implements the cTCPLinkImpl class implementing the TCP link functionality
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "TCPLinkImpl.h"
|
||||||
|
#include "NetworkSingleton.h"
|
||||||
|
#include "ServerHandleImpl.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cTCPLinkImpl:
|
||||||
|
|
||||||
|
cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks):
|
||||||
|
super(a_LinkCallbacks),
|
||||||
|
m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen):
|
||||||
|
super(a_LinkCallbacks),
|
||||||
|
m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE)),
|
||||||
|
m_Server(a_Server)
|
||||||
|
{
|
||||||
|
// Update the endpoint addresses:
|
||||||
|
UpdateLocalAddress();
|
||||||
|
UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cTCPLinkImpl::~cTCPLinkImpl()
|
||||||
|
{
|
||||||
|
bufferevent_free(m_BufferEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks)
|
||||||
|
{
|
||||||
|
ASSERT(a_LinkCallbacks != nullptr);
|
||||||
|
ASSERT(a_ConnectCallbacks != nullptr);
|
||||||
|
|
||||||
|
// Create a new link:
|
||||||
|
cTCPLinkImplPtr res{new cTCPLinkImpl(a_LinkCallbacks)}; // Cannot use std::make_shared here, constructor is not accessible
|
||||||
|
res->m_ConnectCallbacks = a_ConnectCallbacks;
|
||||||
|
cNetworkSingleton::Get().AddLink(res);
|
||||||
|
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<int>(sizeof(sa));
|
||||||
|
if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) == 0)
|
||||||
|
{
|
||||||
|
// Insert the correct port:
|
||||||
|
if (sa.ss_family == AF_INET6)
|
||||||
|
{
|
||||||
|
reinterpret_cast<sockaddr_in6 *>(&sa)->sin6_port = htons(a_Port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reinterpret_cast<sockaddr_in *>(&sa)->sin_port = htons(a_Port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue the connect request:
|
||||||
|
if (bufferevent_socket_connect(res->m_BufferEvent, reinterpret_cast<sockaddr *>(&sa), salen) == 0)
|
||||||
|
{
|
||||||
|
// Success
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cTCPLinkImpl::Enable(cTCPLinkImplPtr a_Self)
|
||||||
|
{
|
||||||
|
// Take hold of a shared copy of self, to keep as long as the callbacks are coming:
|
||||||
|
m_Self = a_Self;
|
||||||
|
|
||||||
|
// Set the LibEvent callbacks and enable processing:
|
||||||
|
bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this);
|
||||||
|
bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length)
|
||||||
|
{
|
||||||
|
return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cTCPLinkImpl::Shutdown(void)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND);
|
||||||
|
#else
|
||||||
|
shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR);
|
||||||
|
#endif
|
||||||
|
bufferevent_disable(m_BufferEvent, EV_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cTCPLinkImpl::Close(void)
|
||||||
|
{
|
||||||
|
// Disable all events on the socket, but keep it alive:
|
||||||
|
bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE);
|
||||||
|
if (m_Server == nullptr)
|
||||||
|
{
|
||||||
|
cNetworkSingleton::Get().RemoveLink(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_Server->RemoveLink(this);
|
||||||
|
}
|
||||||
|
m_Self.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self)
|
||||||
|
{
|
||||||
|
ASSERT(a_Self != nullptr);
|
||||||
|
cTCPLinkImpl * Self = static_cast<cTCPLinkImpl *>(a_Self);
|
||||||
|
ASSERT(Self->m_Callbacks != nullptr);
|
||||||
|
|
||||||
|
// Read all the incoming data, in 1024-byte chunks:
|
||||||
|
char data[1024];
|
||||||
|
size_t length;
|
||||||
|
while ((length = bufferevent_read(a_BufferEvent, data, sizeof(data))) > 0)
|
||||||
|
{
|
||||||
|
Self->m_Callbacks->OnReceivedData(data, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self)
|
||||||
|
{
|
||||||
|
ASSERT(a_Self != nullptr);
|
||||||
|
cTCPLinkImplPtr Self = static_cast<cTCPLinkImpl *>(a_Self)->m_Self;
|
||||||
|
|
||||||
|
// If an error is reported, call the error callback:
|
||||||
|
if (a_What & BEV_EVENT_ERROR)
|
||||||
|
{
|
||||||
|
// Choose the proper callback to call based on whether we were waiting for connection or not:
|
||||||
|
int err = EVUTIL_SOCKET_ERROR();
|
||||||
|
if (Self->m_ConnectCallbacks != nullptr)
|
||||||
|
{
|
||||||
|
if (err == 0)
|
||||||
|
{
|
||||||
|
// This could be a DNS failure
|
||||||
|
err = bufferevent_socket_get_dns_error(a_BufferEvent);
|
||||||
|
}
|
||||||
|
Self->m_ConnectCallbacks->OnError(err, evutil_socket_error_to_string(err));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Self->m_Callbacks->OnError(err, evutil_socket_error_to_string(err));
|
||||||
|
if (Self->m_Server == nullptr)
|
||||||
|
{
|
||||||
|
cNetworkSingleton::Get().RemoveLink(Self.get());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Self->m_Server->RemoveLink(Self.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self->m_Self.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pending connection succeeded, call the connection callback:
|
||||||
|
if (a_What & BEV_EVENT_CONNECTED)
|
||||||
|
{
|
||||||
|
if (Self->m_ConnectCallbacks != nullptr)
|
||||||
|
{
|
||||||
|
Self->m_ConnectCallbacks->OnConnected(*Self);
|
||||||
|
// Reset the connect callbacks so that later errors get reported through the link callbacks:
|
||||||
|
Self->m_ConnectCallbacks.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Self->UpdateLocalAddress();
|
||||||
|
Self->UpdateRemoteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the connection has been closed, call the link callback and remove the connection:
|
||||||
|
if (a_What & BEV_EVENT_EOF)
|
||||||
|
{
|
||||||
|
Self->m_Callbacks->OnRemoteClosed();
|
||||||
|
if (Self->m_Server != nullptr)
|
||||||
|
{
|
||||||
|
Self->m_Server->RemoveLink(Self.get());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cNetworkSingleton::Get().RemoveLink(Self.get());
|
||||||
|
}
|
||||||
|
Self->m_Self.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unknown event, report it:
|
||||||
|
LOGWARNING("cTCPLinkImpl: Unhandled LibEvent event %d (0x%x)", a_What, a_What);
|
||||||
|
ASSERT(!"cTCPLinkImpl: Unhandled LibEvent event");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, socklen_t a_AddrLen, AString & a_IP, UInt16 & a_Port)
|
||||||
|
{
|
||||||
|
// Based on the family specified in the address, use the correct datastructure to convert to IP string:
|
||||||
|
char IP[128];
|
||||||
|
switch (a_Address->sa_family)
|
||||||
|
{
|
||||||
|
case AF_INET: // IPv4:
|
||||||
|
{
|
||||||
|
const sockaddr_in * sin = reinterpret_cast<const sockaddr_in *>(a_Address);
|
||||||
|
evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP));
|
||||||
|
a_Port = ntohs(sin->sin_port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AF_INET6: // IPv6
|
||||||
|
{
|
||||||
|
const sockaddr_in6 * sin = reinterpret_cast<const sockaddr_in6 *>(a_Address);
|
||||||
|
evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP));
|
||||||
|
a_Port = ntohs(sin->sin6_port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
LOGWARNING("%s: Unknown socket address family: %d", __FUNCTION__, a_Address->sa_family);
|
||||||
|
ASSERT(!"Unknown socket address family");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a_IP.assign(IP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cTCPLinkImpl::UpdateLocalAddress(void)
|
||||||
|
{
|
||||||
|
sockaddr_storage sa;
|
||||||
|
socklen_t salen = static_cast<socklen_t>(sizeof(sa));
|
||||||
|
getsockname(bufferevent_getfd(m_BufferEvent), reinterpret_cast<sockaddr *>(&sa), &salen);
|
||||||
|
UpdateAddress(reinterpret_cast<const sockaddr *>(&sa), salen, m_LocalIP, m_LocalPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cTCPLinkImpl::UpdateRemoteAddress(void)
|
||||||
|
{
|
||||||
|
sockaddr_storage sa;
|
||||||
|
socklen_t salen = static_cast<socklen_t>(sizeof(sa));
|
||||||
|
getpeername(bufferevent_getfd(m_BufferEvent), reinterpret_cast<sockaddr *>(&sa), &salen);
|
||||||
|
UpdateAddress(reinterpret_cast<const sockaddr *>(&sa), salen, m_RemoteIP, m_RemotePort);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cNetwork API:
|
||||||
|
|
||||||
|
bool cNetwork::Connect(
|
||||||
|
const AString & a_Host,
|
||||||
|
UInt16 a_Port,
|
||||||
|
cNetwork::cConnectCallbacksPtr a_ConnectCallbacks,
|
||||||
|
cTCPLink::cCallbacksPtr a_LinkCallbacks
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Add a connection request to the queue:
|
||||||
|
cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks);
|
||||||
|
return (Conn != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
122
src/OSSupport/TCPLinkImpl.h
Normal file
122
src/OSSupport/TCPLinkImpl.h
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
|
||||||
|
// TCPLinkImpl.h
|
||||||
|
|
||||||
|
// Declares the cTCPLinkImpl class implementing the TCP link functionality
|
||||||
|
|
||||||
|
// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Network.h"
|
||||||
|
#include <event2/event.h>
|
||||||
|
#include <event2/bufferevent.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// fwd:
|
||||||
|
class cServerHandleImpl;
|
||||||
|
typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
|
||||||
|
class cTCPLinkImpl;
|
||||||
|
typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr;
|
||||||
|
typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class cTCPLinkImpl:
|
||||||
|
public cTCPLink
|
||||||
|
{
|
||||||
|
typedef cTCPLink super;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Creates a new link based on the given socket.
|
||||||
|
Used for connections accepted in a server using cNetwork::Listen().
|
||||||
|
a_Address and a_AddrLen describe the remote peer that has connected.
|
||||||
|
The link is created disabled, you need to call Enable() to start the regular communication. */
|
||||||
|
cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen);
|
||||||
|
|
||||||
|
/** Destroys the LibEvent handle representing the link. */
|
||||||
|
~cTCPLinkImpl();
|
||||||
|
|
||||||
|
/** Queues a connection request to the specified host.
|
||||||
|
a_ConnectCallbacks must be valid.
|
||||||
|
Returns a link that has the connection request queued, or NULL for failure. */
|
||||||
|
static cTCPLinkImplPtr Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks);
|
||||||
|
|
||||||
|
/** Enables communication over the link.
|
||||||
|
Links are created with communication disabled, so that creation callbacks can be called first.
|
||||||
|
This function then enables the regular communication to be reported.
|
||||||
|
The a_Self parameter is used so that the socket can keep itself alive as long as the callbacks are coming. */
|
||||||
|
void Enable(cTCPLinkImplPtr a_Self);
|
||||||
|
|
||||||
|
// cTCPLink overrides:
|
||||||
|
virtual bool Send(const void * a_Data, size_t a_Length) override;
|
||||||
|
virtual AString GetLocalIP(void) const override { return m_LocalIP; }
|
||||||
|
virtual UInt16 GetLocalPort(void) const override { return m_LocalPort; }
|
||||||
|
virtual AString GetRemoteIP(void) const override { return m_RemoteIP; }
|
||||||
|
virtual UInt16 GetRemotePort(void) const override { return m_RemotePort; }
|
||||||
|
virtual void Shutdown(void) override;
|
||||||
|
virtual void Close(void) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/** Callbacks to call when the connection is established.
|
||||||
|
May be NULL if not used. Only used for outgoing connections (cNetwork::Connect()). */
|
||||||
|
cNetwork::cConnectCallbacksPtr m_ConnectCallbacks;
|
||||||
|
|
||||||
|
/** The LibEvent handle representing this connection. */
|
||||||
|
bufferevent * m_BufferEvent;
|
||||||
|
|
||||||
|
/** The server handle that has created this link.
|
||||||
|
Only valid for incoming connections, nullptr for outgoing connections. */
|
||||||
|
cServerHandleImplPtr m_Server;
|
||||||
|
|
||||||
|
/** The IP address of the local endpoint. Valid only after the socket has been connected. */
|
||||||
|
AString m_LocalIP;
|
||||||
|
|
||||||
|
/** The port of the local endpoint. Valid only after the socket has been connected. */
|
||||||
|
UInt16 m_LocalPort;
|
||||||
|
|
||||||
|
/** The IP address of the remote endpoint. Valid only after the socket has been connected. */
|
||||||
|
AString m_RemoteIP;
|
||||||
|
|
||||||
|
/** The port of the remote endpoint. Valid only after the socket has been connected. */
|
||||||
|
UInt16 m_RemotePort;
|
||||||
|
|
||||||
|
/** SharedPtr to self, used to keep this object alive as long as the callbacks are coming.
|
||||||
|
Initialized in Enable(), cleared in Close() and EventCallback(RemoteClosed). */
|
||||||
|
cTCPLinkImplPtr m_Self;
|
||||||
|
|
||||||
|
|
||||||
|
/** Creates a new link to be queued to connect to a specified host:port.
|
||||||
|
Used for outgoing connections created using cNetwork::Connect().
|
||||||
|
To be used only by the Connect() factory function.
|
||||||
|
The link is created disabled, you need to call Enable() to start the regular communication. */
|
||||||
|
cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks);
|
||||||
|
|
||||||
|
/** Callback that LibEvent calls when there's data available from the remote peer. */
|
||||||
|
static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self);
|
||||||
|
|
||||||
|
/** Callback that LibEvent calls when there's a non-data-related event on the socket. */
|
||||||
|
static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self);
|
||||||
|
|
||||||
|
/** Sets a_IP and a_Port to values read from a_Address, based on the correct address family. */
|
||||||
|
static void UpdateAddress(const sockaddr * a_Address, socklen_t a_AddrLen, AString & a_IP, UInt16 & a_Port);
|
||||||
|
|
||||||
|
/** Updates m_LocalIP and m_LocalPort based on the metadata read from the socket. */
|
||||||
|
void UpdateLocalAddress(void);
|
||||||
|
|
||||||
|
/** Updates m_RemoteIP and m_RemotePort based on the metadata read from the socket. */
|
||||||
|
void UpdateRemoteAddress(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,3 +5,4 @@ enable_testing()
|
|||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
add_subdirectory(ChunkData)
|
add_subdirectory(ChunkData)
|
||||||
|
add_subdirectory(Network)
|
||||||
|
60
tests/Network/CMakeLists.txt
Normal file
60
tests/Network/CMakeLists.txt
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
cmake_minimum_required (VERSION 2.6)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/src/)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/lib/libevent/include)
|
||||||
|
|
||||||
|
add_definitions(-DTEST_GLOBALS=1)
|
||||||
|
|
||||||
|
# Create a single Network library that contains all the networking code:
|
||||||
|
set (Network_SRCS
|
||||||
|
${CMAKE_SOURCE_DIR}/src/OSSupport/CriticalSection.cpp
|
||||||
|
${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/NetworkSingleton.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/OSSupport/ServerHandleImpl.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/OSSupport/TCPLinkImpl.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/StringUtils.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set (Network_HDRS
|
||||||
|
${CMAKE_SOURCE_DIR}/src/OSSupport/CriticalSection.h
|
||||||
|
${CMAKE_SOURCE_DIR}/src/OSSupport/Event.h
|
||||||
|
${CMAKE_SOURCE_DIR}/src/OSSupport/HostnameLookup.h
|
||||||
|
${CMAKE_SOURCE_DIR}/src/OSSupport/IPLookup.h
|
||||||
|
${CMAKE_SOURCE_DIR}/src/OSSupport/Network.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/StringUtils.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(Network
|
||||||
|
${Network_SRCS}
|
||||||
|
${Network_HDRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(Network event_core event_extra)
|
||||||
|
if (MSVC)
|
||||||
|
target_link_libraries(Network ws2_32.lib)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Define individual tests:
|
||||||
|
|
||||||
|
# Google: download the google.com frontpage using http client socket:
|
||||||
|
add_executable(Google-exe Google.cpp)
|
||||||
|
target_link_libraries(Google-exe Network)
|
||||||
|
add_test(NAME Google-test COMMAND Google-exe)
|
||||||
|
|
||||||
|
# EchoServer: Listen on port 9876, echo everything back:
|
||||||
|
add_executable(EchoServer EchoServer.cpp)
|
||||||
|
target_link_libraries(EchoServer Network)
|
||||||
|
|
||||||
|
# NameLookup: Lookup hostname-to-IP and IP-to-hostname:
|
||||||
|
add_executable(NameLookup NameLookup.cpp)
|
||||||
|
target_link_libraries(NameLookup Network)
|
132
tests/Network/EchoServer.cpp
Normal file
132
tests/Network/EchoServer.cpp
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
|
||||||
|
// EchoServer.cpp
|
||||||
|
|
||||||
|
// Implements an Echo server using the LibEvent-based cNetwork API, as a test of that API
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include "OSSupport/Network.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** cTCPLink callbacks that echo everything they receive back to the remote peer. */
|
||||||
|
class cEchoLinkCallbacks:
|
||||||
|
public cTCPLink::cCallbacks
|
||||||
|
{
|
||||||
|
// cTCPLink::cCallbacks overrides:
|
||||||
|
virtual void OnLinkCreated(cTCPLinkPtr a_Link) override
|
||||||
|
{
|
||||||
|
ASSERT(m_Link == nullptr);
|
||||||
|
m_Link = a_Link;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual void OnReceivedData(const char * a_Data, size_t a_Size) override
|
||||||
|
{
|
||||||
|
ASSERT(m_Link != nullptr);
|
||||||
|
|
||||||
|
// Echo the incoming data back to outgoing data:
|
||||||
|
LOGD("%p (%s:%d): Data received (%u bytes), echoing back.", m_Link.get(), m_Link->GetRemoteIP().c_str(), m_Link->GetRemotePort(), static_cast<unsigned>(a_Size));
|
||||||
|
m_Link->Send(a_Data, a_Size);
|
||||||
|
LOGD("Echo queued");
|
||||||
|
|
||||||
|
// Search for a Ctrl+Z, if found, drop the connection:
|
||||||
|
for (size_t i = 0; i < a_Size; i++)
|
||||||
|
{
|
||||||
|
if (a_Data[i] == '\x1a')
|
||||||
|
{
|
||||||
|
m_Link->Close();
|
||||||
|
m_Link.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual void OnRemoteClosed(void) override
|
||||||
|
{
|
||||||
|
ASSERT(m_Link != nullptr);
|
||||||
|
|
||||||
|
LOGD("%p (%s:%d): Remote has closed the connection.", m_Link.get(), m_Link->GetRemoteIP().c_str(), m_Link->GetRemotePort());
|
||||||
|
m_Link.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
|
||||||
|
{
|
||||||
|
ASSERT(m_Link != nullptr);
|
||||||
|
|
||||||
|
LOGD("%p (%s:%d): Error %d in the cEchoLinkCallbacks: %s", m_Link.get(), m_Link->GetRemoteIP().c_str(), m_Link->GetRemotePort(), a_ErrorCode, a_ErrorMsg.c_str());
|
||||||
|
m_Link.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The link attached to this callbacks instance. */
|
||||||
|
cTCPLinkPtr m_Link;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class cEchoServerCallbacks:
|
||||||
|
public cNetwork::cListenCallbacks
|
||||||
|
{
|
||||||
|
virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort)
|
||||||
|
{
|
||||||
|
LOGD("New incoming connection(%s:%d).", a_RemoteIPAddress.c_str(), a_RemotePort);
|
||||||
|
return std::make_shared<cEchoLinkCallbacks>();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnAccepted(cTCPLink & a_Link) override
|
||||||
|
{
|
||||||
|
LOGD("New client accepted (%s:%d), sending welcome message.", a_Link.GetRemoteIP().c_str(), a_Link.GetRemotePort());
|
||||||
|
// Send a welcome message to each connecting client:
|
||||||
|
a_Link.Send("Welcome to the simple echo server.\r\n");
|
||||||
|
LOGD("Welcome message queued.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
|
||||||
|
{
|
||||||
|
LOGWARNING("An error occured while listening for connections: %d (%s).", a_ErrorCode, a_ErrorMsg.c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
LOGD("EchoServer: starting up");
|
||||||
|
cServerHandlePtr Server = cNetwork::Listen(9876, std::make_shared<cEchoServerCallbacks>());
|
||||||
|
if (!Server->IsListening())
|
||||||
|
{
|
||||||
|
LOGWARNING("Cannot listen on port 9876");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
ASSERT(Server->IsListening());
|
||||||
|
|
||||||
|
// Wait for the user to terminate the server:
|
||||||
|
printf("Press enter to close the server.\n");
|
||||||
|
AString line;
|
||||||
|
std::getline(std::cin, line);
|
||||||
|
|
||||||
|
// Close the server and all its active connections:
|
||||||
|
LOG("Server terminating.");
|
||||||
|
Server->Close();
|
||||||
|
ASSERT(!Server->IsListening());
|
||||||
|
LOGD("Server has been closed.");
|
||||||
|
|
||||||
|
printf("Press enter to exit test.\n");
|
||||||
|
std::getline(std::cin, line);
|
||||||
|
|
||||||
|
LOG("Network test finished.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
118
tests/Network/Google.cpp
Normal file
118
tests/Network/Google.cpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
|
||||||
|
// Google.cpp
|
||||||
|
|
||||||
|
// Implements a HTTP download of the google's front page using the LibEvent-based cNetwork API
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include <thread>
|
||||||
|
#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
|
||||||
|
{
|
||||||
|
cEvent & m_Event;
|
||||||
|
virtual void OnConnected(cTCPLink & a_Link) override
|
||||||
|
{
|
||||||
|
LOGD("Connected, sending HTTP GET");
|
||||||
|
if (!a_Link.Send("GET / HTTP/1.0\r\nHost:google.com\r\n\r\n"))
|
||||||
|
{
|
||||||
|
LOGWARNING("Sending HTTP GET failed");
|
||||||
|
}
|
||||||
|
LOGD("HTTP GET queued.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
|
||||||
|
{
|
||||||
|
LOGD("Error while connecting HTTP: %d (%s)", a_ErrorCode, a_ErrorMsg.c_str());
|
||||||
|
m_Event.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
cHTTPConnectCallbacks(cEvent & a_Event):
|
||||||
|
m_Event(a_Event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** cTCPLink callbacks that dump everything it received to the log. */
|
||||||
|
class cDumpCallbacks:
|
||||||
|
public cTCPLink::cCallbacks
|
||||||
|
{
|
||||||
|
cEvent & m_Event;
|
||||||
|
cTCPLinkPtr m_Link;
|
||||||
|
|
||||||
|
virtual void OnLinkCreated(cTCPLinkPtr a_Link) override
|
||||||
|
{
|
||||||
|
ASSERT(m_Link == nullptr);
|
||||||
|
m_Link = a_Link;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnReceivedData(const char * a_Data, size_t a_Size) override
|
||||||
|
{
|
||||||
|
ASSERT(m_Link != nullptr);
|
||||||
|
|
||||||
|
// Log the incoming data size:
|
||||||
|
AString Hex;
|
||||||
|
CreateHexDump(Hex, a_Data, a_Size, 16);
|
||||||
|
LOGD("Incoming data: %u bytes:\n%s", static_cast<unsigned>(a_Size), Hex.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnRemoteClosed(void) override
|
||||||
|
{
|
||||||
|
ASSERT(m_Link != nullptr);
|
||||||
|
|
||||||
|
LOGD("Remote has closed the connection.");
|
||||||
|
m_Link.reset();
|
||||||
|
m_Event.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
|
||||||
|
{
|
||||||
|
ASSERT(m_Link != nullptr);
|
||||||
|
|
||||||
|
LOGD("Error %d (%s) in the cDumpCallbacks.", a_ErrorCode, a_ErrorMsg.c_str());
|
||||||
|
m_Link.reset();
|
||||||
|
m_Event.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
cDumpCallbacks(cEvent & a_Event):
|
||||||
|
m_Event(a_Event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
cEvent evtFinish;
|
||||||
|
|
||||||
|
LOGD("Network test: Connecting to google.com:80, reading front page via HTTP.");
|
||||||
|
if (!cNetwork::Connect("google.com", 80, std::make_shared<cHTTPConnectCallbacks>(evtFinish), std::make_shared<cDumpCallbacks>(evtFinish)))
|
||||||
|
{
|
||||||
|
LOGWARNING("Cannot queue connection to google.com");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
LOGD("Connect request has been queued.");
|
||||||
|
|
||||||
|
evtFinish.Wait();
|
||||||
|
LOGD("Network test finished");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
80
tests/Network/NameLookup.cpp
Normal file
80
tests/Network/NameLookup.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
|
||||||
|
// NameLookup.cpp
|
||||||
|
|
||||||
|
// Implements a DNS name lookup using the LibEvent-based cNetwork API
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include <thread>
|
||||||
|
#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, const AString & a_ErrorMsg) override
|
||||||
|
{
|
||||||
|
LOGD("Error %d (%s) while performing lookup!", a_ErrorCode, a_ErrorMsg.c_str());
|
||||||
|
exit(a_ErrorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnFinished(void) override
|
||||||
|
{
|
||||||
|
LOGD("Resolving finished.");
|
||||||
|
m_Event.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
cFinishLookupCallbacks(cEvent & a_Event):
|
||||||
|
m_Event(a_Event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
cEvent evtFinish;
|
||||||
|
|
||||||
|
// Look up google.com (has multiple IP addresses):
|
||||||
|
LOGD("Network test: Looking up google.com");
|
||||||
|
if (!cNetwork::HostnameToIP("google.com", std::make_shared<cFinishLookupCallbacks>(evtFinish)))
|
||||||
|
{
|
||||||
|
LOGWARNING("Cannot resolve google.com to IP");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
LOGD("Name lookup has been successfully queued");
|
||||||
|
evtFinish.Wait();
|
||||||
|
LOGD("Lookup finished.");
|
||||||
|
|
||||||
|
// Look up 8.8.8.8 (Google free DNS):
|
||||||
|
LOGD("Network test: Looking up IP 8.8.8.8");
|
||||||
|
if (!cNetwork::IPToHostName("8.8.8.8", std::make_shared<cFinishLookupCallbacks>(evtFinish)))
|
||||||
|
{
|
||||||
|
LOGWARNING("Cannot resolve 8.8.8.8 to name");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
LOGD("IP lookup has been successfully queued");
|
||||||
|
evtFinish.Wait();
|
||||||
|
LOGD("IP lookup finished.");
|
||||||
|
|
||||||
|
LOGD("Network test finished");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user