2015-01-18 05:57:16 -05:00
|
|
|
|
|
|
|
// 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:
|
|
|
|
|
2015-01-21 15:12:11 -05:00
|
|
|
cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks):
|
2015-01-18 05:57:16 -05:00
|
|
|
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();
|
|
|
|
}
|
2015-01-23 17:01:18 -05:00
|
|
|
|
|
|
|
// Remove the ptr to self, so that the object may be freed:
|
|
|
|
m_SelfPtr.reset();
|
2015-01-27 07:58:46 -05:00
|
|
|
|
|
|
|
// Remove self from cNetworkSingleton:
|
|
|
|
cNetworkSingleton::Get().RemoveServer(this);
|
2015-01-18 05:57:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cServerHandleImplPtr cServerHandleImpl::Listen(
|
|
|
|
UInt16 a_Port,
|
2015-01-21 15:12:11 -05:00
|
|
|
cNetwork::cListenCallbacksPtr a_ListenCallbacks
|
2015-01-18 05:57:16 -05:00
|
|
|
)
|
|
|
|
{
|
2015-01-21 15:12:11 -05:00
|
|
|
cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)};
|
2015-01-23 17:01:18 -05:00
|
|
|
res->m_SelfPtr = res;
|
2015-01-18 05:57:16 -05:00
|
|
|
if (res->Listen(a_Port))
|
|
|
|
{
|
|
|
|
cNetworkSingleton::Get().AddServer(res);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg);
|
2015-01-23 17:01:18 -05:00
|
|
|
res->m_SelfPtr.reset();
|
2015-01-18 05:57:16 -05:00
|
|
|
}
|
|
|
|
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;
|
2015-08-14 16:19:15 -04:00
|
|
|
int err = 0;
|
2015-01-18 05:57:16 -05:00
|
|
|
evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
2015-02-07 05:03:38 -05:00
|
|
|
|
2015-01-18 05:57:16 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-02-08 08:41:24 -05:00
|
|
|
// Allow the port to be reused right after the socket closes:
|
|
|
|
if (evutil_make_listen_socket_reuseable(MainSock) != 0)
|
|
|
|
{
|
|
|
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
|
|
|
Printf(m_ErrorMsg, "Port %d cannot be made reusable: %d (%s). Restarting the server might not work.",
|
|
|
|
a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)
|
|
|
|
);
|
|
|
|
LOG("%s", m_ErrorMsg.c_str());
|
|
|
|
}
|
|
|
|
|
2015-01-18 05:57:16 -05:00
|
|
|
// 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));
|
|
|
|
#else
|
|
|
|
setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
|
|
|
|
#endif
|
|
|
|
|
2015-02-08 08:41:24 -05:00
|
|
|
// Allow the port to be reused right after the socket closes:
|
|
|
|
if (evutil_make_listen_socket_reuseable(MainSock) != 0)
|
|
|
|
{
|
|
|
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
|
|
|
Printf(m_ErrorMsg, "Port %d cannot be made reusable: %d (%s). Restarting the server might not work.",
|
|
|
|
a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)
|
|
|
|
);
|
|
|
|
LOG("%s", m_ErrorMsg.c_str());
|
|
|
|
}
|
|
|
|
|
2015-01-18 05:57:16 -05:00
|
|
|
// 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();
|
2015-01-20 05:27:05 -05:00
|
|
|
Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
|
2015-01-18 05:57:16 -05:00
|
|
|
evutil_closesocket(MainSock);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (evutil_make_socket_nonblocking(MainSock) != 0)
|
|
|
|
{
|
|
|
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
2015-01-20 05:27:05 -05:00
|
|
|
Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
|
2015-01-18 05:57:16 -05:00
|
|
|
evutil_closesocket(MainSock);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (listen(MainSock, 0) != 0)
|
|
|
|
{
|
|
|
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
2015-01-20 05:27:05 -05:00
|
|
|
Printf(m_ErrorMsg, "Cannot listen on port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
|
2015-01-18 05:57:16 -05:00
|
|
|
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;
|
2015-02-07 05:03:38 -05:00
|
|
|
|
2015-01-18 05:57:16 -05:00
|
|
|
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);
|
2015-02-07 05:03:38 -05:00
|
|
|
|
2015-01-18 05:57:16 -05:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-02-08 08:41:24 -05:00
|
|
|
// Allow the port to be reused right after the socket closes:
|
2015-02-18 03:34:33 -05:00
|
|
|
if (evutil_make_listen_socket_reuseable(SecondSock) != 0)
|
2015-02-08 08:41:24 -05:00
|
|
|
{
|
|
|
|
m_ErrorCode = EVUTIL_SOCKET_ERROR();
|
|
|
|
Printf(m_ErrorMsg, "Port %d cannot be made reusable (second socket): %d (%s). Restarting the server might not work.",
|
|
|
|
a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)
|
|
|
|
);
|
|
|
|
LOG("%s", m_ErrorMsg.c_str());
|
|
|
|
}
|
|
|
|
|
2015-01-18 05:57:16 -05:00
|
|
|
// 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);
|
2015-01-20 05:27:05 -05:00
|
|
|
return true; // Report as success, the primary socket is working
|
2015-01-18 05:57:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2015-01-20 05:27:05 -05:00
|
|
|
return true; // Report as success, the primary socket is working
|
2015-01-18 05:57:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (listen(SecondSock, 0) != 0)
|
|
|
|
{
|
|
|
|
err = EVUTIL_SOCKET_ERROR();
|
2015-02-07 05:03:38 -05:00
|
|
|
LOGD("Cannot listen on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
|
2015-01-18 05:57:16 -05:00
|
|
|
evutil_closesocket(SecondSock);
|
2015-01-20 05:27:05 -05:00
|
|
|
return true; // Report as success, the primary socket is working
|
2015-01-18 05:57:16 -05:00
|
|
|
}
|
|
|
|
|
2015-08-14 16:19:15 -04:00
|
|
|
UNUSED(err);
|
|
|
|
|
2015-01-18 05:57:16 -05:00
|
|
|
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);
|
2015-01-23 17:01:18 -05:00
|
|
|
ASSERT(Self->m_SelfPtr != nullptr);
|
2015-01-18 05:57:16 -05:00
|
|
|
|
2015-01-21 15:12:11 -05:00
|
|
|
// Get the textual IP address and port number out of a_Addr:
|
|
|
|
char IPAddress[128];
|
|
|
|
UInt16 Port = 0;
|
|
|
|
switch (a_Addr->sa_family)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
|
|
|
{
|
|
|
|
sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr);
|
2015-07-07 12:10:47 -04:00
|
|
|
evutil_inet_ntop(AF_INET, &(sin->sin_addr), IPAddress, ARRAYCOUNT(IPAddress));
|
2015-01-21 15:12:11 -05:00
|
|
|
Port = ntohs(sin->sin_port);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case AF_INET6:
|
|
|
|
{
|
|
|
|
sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(a_Addr);
|
2015-07-07 12:10:47 -04:00
|
|
|
evutil_inet_ntop(AF_INET6, &(sin6->sin6_addr), IPAddress, ARRAYCOUNT(IPAddress));
|
2015-01-21 15:12:11 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-01-18 05:57:16 -05:00
|
|
|
// Create a new cTCPLink for the incoming connection:
|
2015-01-23 17:01:18 -05:00
|
|
|
cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast<socklen_t>(a_Len));
|
2015-01-18 05:57:16 -05:00
|
|
|
{
|
|
|
|
cCSLock Lock(Self->m_CS);
|
|
|
|
Self->m_Connections.push_back(Link);
|
|
|
|
} // Lock(m_CS)
|
2015-01-22 07:00:32 -05:00
|
|
|
LinkCallbacks->OnLinkCreated(Link);
|
2015-01-23 17:01:18 -05:00
|
|
|
Link->Enable(Link);
|
2015-01-18 05:57:16 -05:00
|
|
|
|
|
|
|
// 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(
|
2015-01-21 15:12:11 -05:00
|
|
|
UInt16 a_Port,
|
|
|
|
cNetwork::cListenCallbacksPtr a_ListenCallbacks
|
2015-01-18 05:57:16 -05:00
|
|
|
)
|
|
|
|
{
|
2015-01-21 15:12:11 -05:00
|
|
|
return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks);
|
2015-01-18 05:57:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|