/*
* PtokaX - hub server for Direct Connect peer to peer network.
* 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 "ServerManager.h"
#include "utility.h"
//---------------------------------------------------------------------------
#ifdef _WIN32
#pragma hdrstop
#endif
//---------------------------------------------------------------------------
#include "UpdateCheckThread.h"
#ifdef FLYLINKDC_USE_UPDATE_CHECKER_THREAD
//---------------------------------------------------------------------------
#include "../gui.win/MainWindow.h"
//---------------------------------------------------------------------------
clsUpdateCheckThread * clsUpdateCheckThread::mPtr = nullptr;
//---------------------------------------------------------------------------
clsUpdateCheckThread::clsUpdateCheckThread() : hThread(INVALID_HANDLE_VALUE), sRecvBuf(NULL), sSocket(INVALID_SOCKET), ui32FileLen(0), ui32RecvBufLen(0), ui32RecvBufSize(0), ui32BytesRead(0), ui32BytesSent(0), bOk(false), bData(false), bTerminated(false)
{
sMsg[0] = '\0';
}
//---------------------------------------------------------------------------
clsUpdateCheckThread::~clsUpdateCheckThread()
{
ServerManager::ui64BytesRead += (uint64_t)ui32BytesRead;
ServerManager::ui64BytesSent += (uint64_t)ui32BytesSent;
if (sSocket != INVALID_SOCKET)
{
shutdown_and_close(sSocket, SHUT_WR);
}
free(sRecvBuf);
if (hThread != INVALID_HANDLE_VALUE)
{
::CloseHandle(hThread);
}
}
//---------------------------------------------------------------------------
unsigned __stdcall ExecuteUpdateCheck(void * /*pArguments*/)
{
clsUpdateCheckThread::mPtr->Run();
return 0;
}
//---------------------------------------------------------------------------
void clsUpdateCheckThread::Resume()
{
hThread = (HANDLE)_beginthreadex(NULL, 0, ExecuteUpdateCheck, NULL, 0, NULL);
if (hThread == 0)
{
AppendDebugLog("%s - [ERR] Failed to create new UpdateCheckThread\n");
}
}
//---------------------------------------------------------------------------
void clsUpdateCheckThread::Run()
{
struct addrinfo * pResult = nullptr;
struct addrinfo hints;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_socktype = SOCK_STREAM;
if (ServerManager::m_bUseIPv6 == true)
{
hints.ai_family = AF_UNSPEC;
}
else
{
hints.ai_family = AF_INET;
}
if (::getaddrinfo("update.fly-server.ru", "80", &hints, &pResult) != 0 || (pResult->ai_family != AF_INET && pResult->ai_family != AF_INET6))
{
int iError = WSAGetLastError();
const int iMsgLen = sprintf(sMsg, "Update check resolve error %s (%d).", WSErrorStr(iError), iError);
if (CheckSprintf(iMsgLen, 2048, "clsUpdateCheckThread::Run") == true)
{
Message(sMsg, iMsgLen);
}
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
if (pResult != NULL)
{
::freeaddrinfo(pResult);
}
return;
}
// Initialise socket we want to use for connect
#ifdef _WIN32
if ((sSocket = socket(pResult->ai_family, pResult->ai_socktype, pResult->ai_protocol)) == INVALID_SOCKET)
{
int iError = WSAGetLastError();
const int iMsgLen = sprintf(sMsg, "Update check create error %s (%d).", WSErrorStr(iError), iError);
#else
if ((sSocket = socket(pResult->ai_family, pResult->ai_socktype, pResult->ai_protocol)) == -1)
{
const int iMsgLen = sprintf(sMsg, "Update check create error %s (%d).", WSErrorStr(errno), errno);
#endif
if (CheckSprintf(iMsgLen, 2048, "clsUpdateCheckThread::Run1") == true)
{
Message(sMsg, iMsgLen);
}
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
::freeaddrinfo(pResult);
return;
}
// Set the receive buffer
int32_t bufsize = 8192;
#ifdef _WIN32
if (setsockopt(sSocket, SOL_SOCKET, SO_RCVBUF, (char *) &bufsize, sizeof(bufsize)) == SOCKET_ERROR)
{
int iError = WSAGetLastError();
const int iMsgLen = sprintf(sMsg, "Update check recv buff error %s (%d).", WSErrorStr(iError), iError);
#else
if (setsockopt(sSocket, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) == -1)
{
const int iMsgLen = sprintf(sMsg, "Update check recv buff error %s (%d).", WSErrorStr(errno), errno);
#endif
if (CheckSprintf(iMsgLen, 2048, "clsUpdateCheckThread::Run2") == true)
{
Message(sMsg, iMsgLen);
}
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
::freeaddrinfo(pResult);
return;
}
// Set the send buffer
bufsize = 2048;
#ifdef _WIN32
if (setsockopt(sSocket, SOL_SOCKET, SO_SNDBUF, (char *) &bufsize, sizeof(bufsize)) == SOCKET_ERROR)
{
int iError = WSAGetLastError();
const int iMsgLen = sprintf(sMsg, "Update check send buff error %s (%d).", WSErrorStr(iError), iError);
#else
if (setsockopt(sSocket, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) == -1)
{
const int iMsgLen = sprintf(sMsg, "Update check buff error %s (%d).", WSErrorStr(errno), errno);
#endif
if (CheckSprintf(iMsgLen, 2048, "clsUpdateCheckThread::Run3") == true)
{
Message(sMsg, iMsgLen);
}
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
::freeaddrinfo(pResult);
return;
}
Message("Connecting to update.fly-server.ru ...", 28);
// Finally, time to connect ! ;)
#ifdef _WIN32
if (connect(sSocket, pResult->ai_addr, (int)pResult->ai_addrlen) == SOCKET_ERROR)
{
int iError = WSAGetLastError();
if (iError != WSAEWOULDBLOCK)
{
const int iMsgLen = sprintf(sMsg, "Update check connect error %s (%d).", WSErrorStr(iError), iError);
#else
if (connect(sSocket, pResult->ai_addr, (int)pResult->ai_addrlen) == -1)
{
if (errno != EAGAIN)
{
const int iMsgLen = sprintf(sMsg, "Update check connect error %s (%d).", WSErrorStr(errno), errno);
#endif
if (CheckSprintf(iMsgLen, 2048, "clsUpdateCheckThread::Run4") == true)
{
Message(sMsg, iMsgLen);
}
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
::freeaddrinfo(pResult);
return;
}
}
::freeaddrinfo(pResult);
Message("Connected to update.fly-server.ru, sending request...", 43);
if (SendHeader() == false)
{
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
return;
}
Message("Request to update.fly-server.ru sent, receiving data...", 45);
// Set non-blocking mode
#ifdef _WIN32
uint32_t block = 1;
if (ioctlsocket(sSocket, FIONBIO, (unsigned long *)&block) == SOCKET_ERROR)
{
int iError = WSAGetLastError();
const int iMsgLen = sprintf(sMsg, "Update check non-block error %s (%d).", WSErrorStr(iError), iError);
#else
int32_t oldFlag = fcntl(u->s, F_GETFL, 0);
if (fcntl(sSocket, F_SETFL, oldFlag | O_NONBLOCK) == -1)
{
const int iMsgLen = sprintf(sMsg, "Update check non-block error %s (%d).", WSErrorStr(errno), errno);
#endif
if (CheckSprintf(iMsgLen, 2048, "clsUpdateCheckThread::Run5") == true)
{
Message(sMsg, iMsgLen);
}
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
return;
}
sRecvBuf = (char *)malloc(512);
if (sRecvBuf == NULL)
{
AppendDebugLog("%s - [MEM] Cannot allocate 512 bytes for sRecvBuf in clsUpdateCheckThread::Run\n");
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
return;
}
uint16_t iLoops = 0;
while (bTerminated == false && iLoops < 4000)
{
iLoops++;
if (Receive() == false)
{
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
return;
}
::Sleep(75);
}
if (bTerminated == false)
{
Message("Update check timeout.", 21);
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
}
}
//---------------------------------------------------------------------------
void clsUpdateCheckThread::Close()
{
bTerminated = true;
}
//---------------------------------------------------------------------------
void clsUpdateCheckThread::WaitFor()
{
::WaitForSingleObject(hThread, INFINITE);
}
//---------------------------------------------------------------------------
void clsUpdateCheckThread::Message(const char * sMessage, const size_t szLen)
{
char *sMess = (char *)malloc(szLen + 1);
if (sMess == NULL)
{
AppendDebugLogFormat("[MEM] Cannot allocate %zu bytes for sMess in UpdateCheckThread::Message\n", szLen+1);
return;
}
memcpy(sMess, sMessage, szLen);
sMess[szLen] = '\0';
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_MSG, 0, (LPARAM)sMess);
}
//---------------------------------------------------------------------------
bool clsUpdateCheckThread::SendHeader()
{
char * sDataToSend = "GET /version HTTP/1.1\r\nUser-Agent: PtokaX " PtokaXVersionString " [" BUILD_NUMBER "]"
"\r\nHost: update.fly-server.ru\r\nConnection: close\r\nCache-Control: no-cache\r\nAccept: */*\r\nAccept-Language: en\r\n\r\n";
int iBytes = send(sSocket, sDataToSend, (int)strlen(sDataToSend), 0);
#ifdef _WIN32
if (iBytes == SOCKET_ERROR)
{
int iError = WSAGetLastError();
const int iMsgLen = sprintf(sMsg, "Update check send error %s (%d).", WSErrorStr(iError), iError);
#else
if (iBytes == -1)
{
const int iMsgLen = sprintf(sMsg, "Update check send error %s (%d).)", WSErrorStr(errno), errno);
#endif
if (CheckSprintf(iMsgLen, 2048, "clsUpdateCheckThread::SendHeader") == true)
{
Message(sMsg, iMsgLen);
}
return false;
}
ui32BytesSent += iBytes;
return true;
}
//---------------------------------------------------------------------------
bool clsUpdateCheckThread::Receive()
{
u_long ui32bytes = 0;
if (ioctlsocket(sSocket, FIONREAD, &ui32bytes) == SOCKET_ERROR)
{
int iError = WSAGetLastError();
const int iMsgLen = sprintf(sMsg, "Update check ioctlsocket(FIONREAD) error %s (%d).", WSErrorStr(iError), iError);
if (CheckSprintf(iMsgLen, 2048, "clsUpdateCheckThread::Receive") == true)
{
Message(sMsg, iMsgLen);
}
return false;
}
if (ui32bytes == 0)
{
// we need to try receive to catch connection error, or if server closed connection
ui32bytes = 16;
}
else if (ui32bytes > 8192)
{
// receive max. 8192 bytes to receive buffer
ui32bytes = 8192;
}
if (ui32RecvBufSize < ui32RecvBufLen + ui32bytes)
{
size_t szAllignLen = ((ui32RecvBufLen + ui32bytes + 1) & 0xFFFFFE00) + 0x200;
char * pOldBuf = sRecvBuf;
sRecvBuf = (char *)realloc(sRecvBuf, szAllignLen);
if (sRecvBuf == NULL)
{
sRecvBuf = pOldBuf;
AppendDebugLogFormat("[MEM] Cannot reallocate %zu bytes for sRecvBuf in UpdateCheckThread::Receive\n", szAllignLen);
return false;
}
ui32RecvBufSize = (int)szAllignLen - 1;
}
int iBytes = recv(sSocket, sRecvBuf + ui32RecvBufLen, ui32RecvBufSize - ui32RecvBufLen, 0);
#ifdef _WIN32
if (iBytes == SOCKET_ERROR)
{
int iError = WSAGetLastError();
if (iError != WSAEWOULDBLOCK)
{
const int iMsgLen = sprintf(sMsg, "Update check recv error %s (%d).", WSErrorStr(iError), iError);
#else
if (iBytes == -1)
{
if (errno != EAGAIN)
{
const int iMsgLen = sprintf(sMsg, "Update check recv error %s (%d).", WSErrorStr(errno), errno);
#endif
if (CheckSprintf(iMsgLen, 2048, "clsUpdateCheckThread::Receive2") == true)
{
Message(sMsg, iMsgLen);
}
return false;
}
else
{
return true;
}
}
else if (iBytes == 0)
{
Message("Update check closed connection by server.", 41);
return false;
}
ui32BytesRead += iBytes;
ui32RecvBufLen += iBytes;
sRecvBuf[ui32RecvBufLen] = '\0';
if (bData == false)
{
char *sBuffer = sRecvBuf;
for (uint32_t ui32i = 0; ui32i < ui32RecvBufLen; ui32i++)
{
if (sRecvBuf[ui32i] == '\n')
{
sRecvBuf[ui32i] = '\0';
uint32_t ui32iCommandLen = (uint32_t)((sRecvBuf + ui32i) - sBuffer) + 1;
if (ui32iCommandLen > 7 && strncmp(sBuffer, "HTTP", 4) == NULL && strstr(sBuffer, "200") != NULL)
{
bOk = true;
}
else if (ui32iCommandLen == 2 && sBuffer[0] == '\r')
{
if (bOk == true && ui32FileLen != 0)
{
bData = true;
}
else
{
Message("Update check failed.", 20);
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
return false;
}
}
else if (ui32iCommandLen > 16 && strncmp(sBuffer, "Content-Length: ", 16) == NULL)
{
ui32FileLen = atoi(sBuffer + 16);
}
sBuffer += ui32iCommandLen;
if (bData == true)
{
break;
}
}
}
ui32RecvBufLen -= (uint32_t)(sBuffer - sRecvBuf);
if (ui32RecvBufLen == 0)
{
sRecvBuf[0] = '\0';
}
else if (ui32RecvBufLen != 1)
{
memmove(sRecvBuf, sBuffer, ui32RecvBufLen);
sRecvBuf[ui32RecvBufLen] = '\0';
}
else
{
if (sBuffer[0] == '\n')
{
sRecvBuf[0] = '\0';
ui32RecvBufLen = 0;
}
else
{
sRecvBuf[0] = sBuffer[0];
sRecvBuf[1] = '\0';
}
}
}
if (bData == true)
{
if (ui32RecvBufLen == (uint32_t)ui32FileLen)
{
char *sMess = (char *)malloc(ui32RecvBufLen + 1);
if (sMess == NULL)
{
AppendDebugLogFormat("[MEM] Cannot allocate %u bytes for sMess in clsUpdateCheckThread::Receive\n", ui32RecvBufLen + 1);
return false;
}
memcpy(sMess, sRecvBuf, ui32RecvBufLen);
sMess[ui32RecvBufLen] = '\0';
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_DATA, 0, (LPARAM)sMess);
::PostMessage(clsMainWindow::mPtr->m_hWnd, WM_UPDATE_CHECK_TERMINATE, 0, 0);
}
}
return true;
}
//---------------------------------------------------------------------------
#endif // FLYLINKDC_USE_UPDATE_CHECKER_THREAD
↑ V1042 This file is marked with copyleft license, which requires you to open the derived source code.