/*
* PtokaX - hub server for Direct Connect peer to peer network.
* Copyright (C) 2002-2005 Ptaczek, Ptaczek at PtokaX dot org
* Copyright (C) 2004-2022 Petr Kozelka, PPK at PtokaX dot org
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3
* as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
#include "stdinc.h"
//---------------------------------------------------------------------------
#include "eventqueue.h"
#include "LanguageManager.h"
#include "ServerManager.h"
#include "serviceLoop.h"
#include "SettingManager.h"
#include "UdpDebug.h"
#include "utility.h"
//---------------------------------------------------------------------------
#ifdef _WIN32
#pragma hdrstop
#endif
//---------------------------------------------------------------------------
#include "ServerThread.h"
//---------------------------------------------------------------------------
ServerThread::AntiConFlood::AntiConFlood(const uint8_t * pIpHash) : m_ui64Time(ServerManager::m_ui64ActualTick), m_pPrev(NULL), m_pNext(NULL), m_ui16Hits(1)
{
memcpy(m_ui128IpHash, pIpHash, 16);
}
//---------------------------------------------------------------------------
ServerThread::ServerThread(const int iAddrFamily, const uint16_t ui16PortNumber) : m_pAntiFloodList(NULL),
#ifdef _WIN32
m_Server(INVALID_SOCKET),
#else
m_ThreadId(0), m_Server(-1),
#endif
m_ui32SuspendTime(0), m_iAdressFamily(iAddrFamily), m_bTerminated(false), m_pPrev(NULL), m_pNext(NULL), m_ui16Port(ui16PortNumber),
m_bActive(false), m_bSuspended(false)
{
#ifdef _WIN32
m_hThreadHandle = INVALID_HANDLE_VALUE;
#endif
}
//---------------------------------------------------------------------------
ServerThread::~ServerThread()
{
#ifndef _WIN32
if (m_ThreadId != 0)
{
Close();
WaitFor();
}
#endif
AntiConFlood * acfcur = NULL,
* acfnext = m_pAntiFloodList;
while (acfnext != NULL)
{
acfcur = acfnext;
acfnext = acfcur->m_pNext;
delete acfcur;
}
#ifdef _WIN32
if (m_hThreadHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hThreadHandle);
}
#endif
}
//---------------------------------------------------------------------------
#ifdef _WIN32
unsigned __stdcall ExecuteServerThread(void * pThread)
{
#else
static void* ExecuteServerThread(void * pThread)
{
#endif
(reinterpret_cast<ServerThread *>(pThread))->Run();
return 0;
}
//---------------------------------------------------------------------------
void ServerThread::Resume()
{
#ifdef _WIN32
m_hThreadHandle = (HANDLE)_beginthreadex(NULL, 0, ExecuteServerThread, this, 0, NULL);
if (m_hThreadHandle == 0)
{
#else
int iRet = pthread_create(&m_ThreadId, NULL, ExecuteServerThread, this);
if (iRet != 0)
{
#endif
AppendDebugLog("%s - [ERR] Failed to create new ServerThread\n");
}
}
//---------------------------------------------------------------------------
void ServerThread::Run()
{
m_bActive = true;
#ifdef _WIN32
SOCKET s = INVALID_SOCKET;
#else
int s = -1;
#endif
sockaddr_storage addr;
socklen_t len = sizeof(addr);
#ifndef _WIN32
struct timespec sleeptime;
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = 1000000;
#endif
while (m_bTerminated == false)
{
s = accept(m_Server, (struct sockaddr *)&addr, &len);
if (m_ui32SuspendTime == 0)
{
if (m_bTerminated == true)
{
shutdown_and_close(s, SHUT_RDWR);
continue;
}
#ifdef _WIN32
if (s == INVALID_SOCKET)
{
if (WSAEWOULDBLOCK != WSAGetLastError())
{
#else
if (s == -1)
{
if (errno != EWOULDBLOCK)
{
if (errno == EMFILE) // max opened file descriptors limit reached
{
sleep(1); // longer sleep give us better chance to have free file descriptor available on next accept call
}
else
{
#endif
EventQueue::m_Ptr->AddThread(EventQueue::EVENT_SRVTHREAD_MSG,
("[ERR] accept() for port " + std::to_string(m_ui16Port) + " has returned error.").c_str());
}
#ifndef _WIN32
}
#endif
}
else
{
if (isFlooder(s, addr) == true)
{
shutdown_and_close(s, SHUT_RDWR);
}
#ifdef _WIN32
::Sleep(1);
#else
nanosleep(&sleeptime, NULL);
#endif
}
}
else
{
uint32_t iSec = 0;
while (m_bTerminated == false)
{
if (m_ui32SuspendTime > iSec)
{
#ifdef _WIN32
::Sleep(1000);
#else
sleep(1);
#endif
if (m_bSuspended == false)
{
iSec++;
}
continue;
}
{
Lock l(m_csServerThread);
m_ui32SuspendTime = 0;
}
if (Listen(true) == true)
{
EventQueue::m_Ptr->AddThread(EventQueue::EVENT_SRVTHREAD_MSG,
("[SYS] Server socket for port " + std::to_string(m_ui16Port) + " sucessfully recovered from suspend state.").c_str());
}
else
{
Close();
}
break;
}
}
}
m_bActive = false;
}
//---------------------------------------------------------------------------
void ServerThread::Close()
{
m_bTerminated = true;
#ifndef _WIN32
shutdown(m_Server, SHUT_RDWR);
#endif
safe_closesocket(m_Server);
}
//---------------------------------------------------------------------------
void ServerThread::WaitFor()
{
#ifdef _WIN32
WaitForSingleObject(m_hThreadHandle, INFINITE);
#else
if (m_ThreadId != 0)
{
pthread_join(m_ThreadId, NULL);
m_ThreadId = 0;
}
#endif
}
//---------------------------------------------------------------------------
bool ServerThread::Listen(const bool bSilent/* = false*/)
{
m_Server = socket(m_iAdressFamily, SOCK_STREAM, IPPROTO_TCP);
#ifdef _WIN32
if (m_Server == INVALID_SOCKET)
{
#else
if (m_Server == -1)
{
#endif
if (bSilent == true)
{
EventQueue::m_Ptr->AddThread(EventQueue::EVENT_SRVTHREAD_MSG,
#ifdef _WIN32
("[ERR] Unable to create server socket for port " + std::to_string(m_ui16Port) + " ! ErrorCode " + std::to_string(WSAGetLastError())).c_str());
#else
("[ERR] Unable to create server socket for port " + std::to_string(m_ui16Port) + " ! ErrorCode " + std::to_string(errno)).c_str());
#endif
}
else
{
#ifdef _BUILD_GUI
::MessageBox(NULL, (std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_UNB_CRT_SRVR_SCK], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_UNB_CRT_SRVR_SCK]) + " " + std::to_string(m_ui16Port) + " ! " + LanguageManager::m_Ptr->m_sTexts[LAN_ERROR_CODE] + " " + std::to_string(WSAGetLastError())).c_str(), g_sPtokaXTitle, MB_OK | MB_ICONERROR);
#else
AppendLog(std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_UNB_CRT_SRVR_SCK], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_UNB_CRT_SRVR_SCK]) + " " + std::to_string(m_ui16Port) + " ! " + LanguageManager::m_Ptr->m_sTexts[LAN_ERROR_CODE] + " " + std::to_string(errno));
#endif
}
return false;
}
#ifndef _WIN32
int on = 1;
if (setsockopt(m_Server, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
{
if (bSilent == true)
{
EventQueue::m_Ptr->AddThread(EventQueue::EVENT_SRVTHREAD_MSG,
("[ERR] Server socket setsockopt error: " + std::to_string(errno) + " for port: " + std::to_string(m_ui16Port)).c_str());
}
else
{
AppendLog(std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_SRV_SCKOPT_ERR], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_SRV_SCKOPT_ERR]) +
": " + std::to_string(ErrnoStr(errno)) + " (" + std::to_string(errno) + ") " +
std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_FOR_PORT_LWR], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_FOR_PORT_LWR]) + ": " + std::to_string(m_ui16Port));
}
close(m_Server);
return false;
}
#endif
// set the socket properties
sockaddr_storage sas;
memset(&sas, 0, sizeof(sockaddr_storage));
socklen_t sas_len;
if (m_iAdressFamily == AF_INET6)
{
((struct sockaddr_in6 *)&sas)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)&sas)->sin6_port = htons(m_ui16Port);
sas_len = sizeof(struct sockaddr_in6);
if (SettingManager::m_Ptr->m_bBools[SETBOOL_BIND_ONLY_SINGLE_IP] == true && ServerManager::m_sHubIP6[0] != '\0')
{
#if defined(_WIN32) && !defined(_WIN64) && !defined(_WIN_IOT)
win_inet_pton(ServerManager::m_sHubIP6, &((struct sockaddr_in6 *)&sas)->sin6_addr);
#else
inet_pton(AF_INET6, ServerManager::m_sHubIP6, &((struct sockaddr_in6 *)&sas)->sin6_addr);
#endif
}
else
{
((struct sockaddr_in6 *)&sas)->sin6_addr = in6addr_any;
if (ServerManager::m_bIPv6DualStack == true && SettingManager::m_Ptr->m_bBools[SETBOOL_BIND_ONLY_SINGLE_IP] == false)
{
#ifdef _WIN32
DWORD dwIPv6 = 0;
setsockopt(m_Server, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&dwIPv6, sizeof(dwIPv6));
#else
int iIPv6 = 0;
setsockopt(m_Server, IPPROTO_IPV6, IPV6_V6ONLY, &iIPv6, sizeof(iIPv6));
#endif
}
}
}
else
{
((struct sockaddr_in *)&sas)->sin_family = AF_INET;
((struct sockaddr_in *)&sas)->sin_port = htons(m_ui16Port);
sas_len = sizeof(struct sockaddr_in);
if (SettingManager::m_Ptr->m_bBools[SETBOOL_BIND_ONLY_SINGLE_IP] == true && ServerManager::m_sHubIP[0] != '\0')
{
((struct sockaddr_in *)&sas)->sin_addr.s_addr = inet_addr(ServerManager::m_sHubIP);
}
else
{
((struct sockaddr_in *)&sas)->sin_addr.s_addr = INADDR_ANY;
}
}
// bind it
#ifdef _WIN32
if (bind(m_Server, (struct sockaddr *)&sas, sas_len) == SOCKET_ERROR)
{
int err = WSAGetLastError();
#else
if (bind(m_Server, (struct sockaddr *)&sas, sas_len) == -1)
{
#endif
if (bSilent == true)
{
EventQueue::m_Ptr->AddThread(EventQueue::EVENT_SRVTHREAD_MSG,
#ifdef _WIN32
("[ERR] Server socket bind error: " + std::to_string(WSErrorStr(err)) + " (" + std::to_string(err) + ") for port: " + std::to_string(m_ui16Port)).c_str());
#else
("[ERR] Server socket bind error: " + std::to_string(ErrnoStr(errno)) + " (" + std::to_string(errno) + ") for port: " + std::to_string(m_ui16Port)).c_str());
#endif
}
else
{
#ifdef _BUILD_GUI
::MessageBox(NULL, (std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_SRV_BIND_ERR], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_SRV_BIND_ERR]) + ": " + std::to_string(WSErrorStr(err)) + " (" + std::to_string(err) + ") " + LanguageManager::m_Ptr->m_sTexts[LAN_FOR_PORT_LWR] + ": " + std::to_string(m_ui16Port)).c_str(),
g_sPtokaXTitle, MB_OK | MB_ICONERROR);
#else
AppendLog(std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_SRV_BIND_ERR], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_SRV_BIND_ERR]) +
#ifdef _WIN32
": " + std::to_string(WSErrorStr(err)) + " (" + std::to_string(err) + ") " +
#else
": " + std::to_string(ErrnoStr(errno)) + " (" + std::to_string(errno) + ") " +
#endif
std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_FOR_PORT_LWR], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_FOR_PORT_LWR]) + ": " + std::to_string(m_ui16Port));
#endif
}
safe_closesocket(m_Server);
return false;
}
// set listen mode
#ifdef _WIN32
if (listen(m_Server, 512) == SOCKET_ERROR)
{
int err = WSAGetLastError();
#else
if (listen(m_Server, 512) == -1)
{
#endif
if (bSilent == true)
{
EventQueue::m_Ptr->AddThread(EventQueue::EVENT_SRVTHREAD_MSG,
#ifdef _WIN32
("[ERR] Server socket listen() error: " + std::to_string(WSErrorStr(err)) + " (" + std::to_string(err) + ") for port: " + std::to_string(m_ui16Port)).c_str());
#else
("[ERR] Server socket listen() error: " + std::to_string(errno) + " for port: " + std::to_string(m_ui16Port)).c_str());
#endif
}
else
{
#ifdef _BUILD_GUI
::MessageBox(NULL, (std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_SRV_LISTEN_ERR], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_SRV_LISTEN_ERR]) + ": " + std::to_string(WSErrorStr(err)) + " (" + std::to_string(err) + ") " + LanguageManager::m_Ptr->m_sTexts[LAN_FOR_PORT_LWR] + ": " + std::to_string(m_ui16Port)).c_str(),
g_sPtokaXTitle, MB_OK | MB_ICONERROR);
#else
AppendLog(std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_SRV_LISTEN_ERR], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_SRV_LISTEN_ERR]) + ": " + std::to_string(errno) + " " + std::to_string(LanguageManager::m_Ptr->m_sTexts[LAN_FOR_PORT_LWR], (size_t)LanguageManager::m_Ptr->m_ui16TextsLens[LAN_FOR_PORT_LWR]) + ": " + std::to_string(m_ui16Port));
#endif
}
safe_closesocket(m_Server);
return false;
}
AppendLog( "Listen port: " + std::to_string(m_ui16Port));
return true;
}
//---------------------------------------------------------------------------
#ifdef _WIN32
bool ServerThread::isFlooder(SOCKET& s, const sockaddr_storage &addr)
{
#else
bool ServerThread::isFlooder(int& s, const sockaddr_storage &addr)
{
#endif
Hash128 ui128IpHash;
if (addr.ss_family == AF_INET6)
{
memcpy(ui128IpHash, &((struct sockaddr_in6 *)&addr)->sin6_addr, 16);
}
else
{
const auto l_ip4 = ((struct sockaddr_in *)&addr)->sin_addr.s_addr;
ui128IpHash[10] = 255;
ui128IpHash[11] = 255;
memcpy(ui128IpHash, &l_ip4, 4);
}
int16_t iConDefloodCount = SettingManager::m_Ptr->GetShort(SETSHORT_NEW_CONNECTIONS_COUNT);
int16_t iConDefloodTime = SettingManager::m_Ptr->GetShort(SETSHORT_NEW_CONNECTIONS_TIME);
AntiConFlood * cur = NULL,
* nxt = m_pAntiFloodList;
while (nxt != NULL)
{
cur = nxt;
nxt = cur->m_pNext;
if (memcmp(ui128IpHash, cur->m_ui128IpHash, 16) == 0)
{
if (cur->m_ui64Time + ((uint64_t)iConDefloodTime) >= ServerManager::m_ui64ActualTick)
{
cur->m_ui16Hits++;
if (cur->m_ui16Hits > iConDefloodCount)
{
return true;
}
else
{
ServiceLoop::m_Ptr->AcceptSocket(s, addr);
return false;
}
}
else
{
RemoveConFlood(cur);
delete cur;
}
}
else if (cur->m_ui64Time + ((uint64_t)iConDefloodTime) < ServerManager::m_ui64ActualTick)
{
RemoveConFlood(cur);
delete cur;
}
}
AntiConFlood * pNewItem = new (std::nothrow) AntiConFlood(ui128IpHash);
if (pNewItem == NULL)
{
AppendDebugLog("%s - [MEM] Cannot allocate pNewItem in theLoop::isFlooder\n");
return true;
}
pNewItem->m_pNext = m_pAntiFloodList;
if (m_pAntiFloodList != NULL)
{
m_pAntiFloodList->m_pPrev = pNewItem;
}
m_pAntiFloodList = pNewItem;
ServiceLoop::m_Ptr->AcceptSocket(s, addr);
return false;
}
//---------------------------------------------------------------------------
void ServerThread::RemoveConFlood(AntiConFlood * pACF)
{
if (pACF->m_pPrev == NULL)
{
if (pACF->m_pNext == NULL)
{
m_pAntiFloodList = NULL;
}
else
{
pACF->m_pNext->m_pPrev = NULL;
m_pAntiFloodList = pACF->m_pNext;
}
}
else if (pACF->m_pNext == NULL)
{
pACF->m_pPrev->m_pNext = NULL;
}
else
{
pACF->m_pPrev->m_pNext = pACF->m_pNext;
pACF->m_pNext->m_pPrev = pACF->m_pPrev;
}
}
//---------------------------------------------------------------------------
void ServerThread::ResumeSck()
{
if (m_bActive == true)
{
Lock l(m_csServerThread);
m_bSuspended = false;
m_ui32SuspendTime = 0;
}
}
//---------------------------------------------------------------------------
void ServerThread::SuspendSck(const uint32_t ui32Time)
{
if (m_bActive == true)
{
Lock l(m_csServerThread);
if (ui32Time != 0)
{
m_ui32SuspendTime = ui32Time;
}
else
{
m_bSuspended = true;
m_ui32SuspendTime = 1;
}
safe_closesocket(m_Server);
}
}
//---------------------------------------------------------------------------
↑ V1042 This file is marked with copyleft license, which requires you to open the derived source code.
↑ V641 The size of the '& sas' buffer is not a multiple of the element size of the type 'struct sockaddr_in6'.
↑ V641 The size of the '& addr' buffer is not a multiple of the element size of the type 'struct sockaddr_in6'.