/*
 * 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'.