mirror of
https://github.com/reactos/reactos.git
synced 2024-12-30 19:14:31 +00:00
13e566444d
'extbuff' must be of size _MAX_PATH to comply with its usage in the GetModuleFileName() call made in the code.
2672 lines
86 KiB
C++
2672 lines
86 KiB
C++
/**************************************************************************
|
|
* Copyright (C) 2005 by Achal Dhir *
|
|
* achaldhir@gmail.com *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* 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, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
// TFTPServer.cpp
|
|
|
|
#include <stdio.h>
|
|
#include <winsock2.h>
|
|
#include <process.h>
|
|
#include <time.h>
|
|
#include <tchar.h>
|
|
#include <ws2tcpip.h>
|
|
#include <limits.h>
|
|
#include <iphlpapi.h>
|
|
#include <math.h>
|
|
#include "tftpd.h"
|
|
|
|
//Global Variables
|
|
char serviceName[] = "TFTPServer";
|
|
char displayName[] = "Open TFTP Server, MultiThreaded";
|
|
char sVersion[] = "Open TFTP Server MultiThreaded Version 1.64 Windows Built 2001";
|
|
char iniFile[_MAX_PATH];
|
|
char logFile[_MAX_PATH];
|
|
char lnkFile[_MAX_PATH];
|
|
char tempbuff[256];
|
|
char extbuff[_MAX_PATH];
|
|
char logBuff[512];
|
|
char fileSep = '\\';
|
|
char notFileSep = '/';
|
|
MYWORD blksize = 65464;
|
|
char verbatim = 0;
|
|
MYWORD timeout = 3;
|
|
MYWORD loggingDay;
|
|
data1 network;
|
|
data1 newNetwork;
|
|
data2 cfig;
|
|
//ThreadPool Variables
|
|
HANDLE tEvent;
|
|
HANDLE cEvent;
|
|
HANDLE sEvent;
|
|
HANDLE lEvent;
|
|
MYBYTE currentServer = UCHAR_MAX;
|
|
MYWORD totalThreads=0;
|
|
MYWORD minThreads=1;
|
|
MYWORD activeThreads=0;
|
|
|
|
//Service Variables
|
|
SERVICE_STATUS serviceStatus;
|
|
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
|
|
HANDLE stopServiceEvent = 0;
|
|
|
|
void WINAPI ServiceControlHandler(DWORD controlCode)
|
|
{
|
|
switch (controlCode)
|
|
{
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
break;
|
|
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
case SERVICE_CONTROL_STOP:
|
|
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
SetEvent(stopServiceEvent);
|
|
return ;
|
|
|
|
case SERVICE_CONTROL_PAUSE:
|
|
break;
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
break;
|
|
|
|
default:
|
|
if (controlCode >= 128 && controlCode <= 255)
|
|
break;
|
|
else
|
|
break;
|
|
}
|
|
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
}
|
|
|
|
void WINAPI ServiceMain(DWORD /*argc*/, TCHAR* /*argv*/[])
|
|
{
|
|
serviceStatus.dwServiceType = SERVICE_WIN32;
|
|
serviceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
serviceStatus.dwControlsAccepted = 0;
|
|
serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
|
|
serviceStatus.dwCheckPoint = 0;
|
|
serviceStatus.dwWaitHint = 0;
|
|
|
|
serviceStatusHandle = RegisterServiceCtrlHandler(serviceName, ServiceControlHandler);
|
|
|
|
if (serviceStatusHandle)
|
|
{
|
|
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
//init
|
|
verbatim = false;
|
|
|
|
if (_beginthread(init, 0, 0) == 0)
|
|
{
|
|
if (cfig.logLevel)
|
|
{
|
|
sprintf(logBuff, "Thread Creation Failed");
|
|
logMess(logBuff, 1);
|
|
}
|
|
exit(-1);
|
|
}
|
|
|
|
fd_set readfds;
|
|
timeval tv;
|
|
tv.tv_sec = 20;
|
|
tv.tv_usec = 0;
|
|
|
|
stopServiceEvent = CreateEvent(0, FALSE, FALSE, 0);
|
|
|
|
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
|
|
serviceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
do
|
|
{
|
|
network.busy = false;
|
|
|
|
if (!network.tftpConn[0].ready || !network.ready)
|
|
{
|
|
Sleep(1000);
|
|
continue;
|
|
}
|
|
|
|
FD_ZERO(&readfds);
|
|
|
|
for (int i = 0; i < MAX_SERVERS && network.tftpConn[i].ready; i++)
|
|
FD_SET(network.tftpConn[i].sock, &readfds);
|
|
|
|
int fdsReady = select(network.maxFD, &readfds, NULL, NULL, &tv);
|
|
|
|
for (int i = 0; fdsReady > 0 && i < MAX_SERVERS && network.tftpConn[i].ready; i++)
|
|
{
|
|
if (network.ready)
|
|
{
|
|
network.busy = true;
|
|
|
|
if (FD_ISSET(network.tftpConn[i].sock, &readfds))
|
|
{
|
|
WaitForSingleObject(sEvent, INFINITE);
|
|
|
|
currentServer = i;
|
|
|
|
if (!totalThreads || activeThreads >= totalThreads)
|
|
{
|
|
_beginthread(
|
|
processRequest, // thread function
|
|
0, // default security attributes
|
|
NULL); // argument to thread function
|
|
|
|
}
|
|
|
|
SetEvent(tEvent);
|
|
WaitForSingleObject(sEvent, INFINITE);
|
|
fdsReady--;
|
|
SetEvent(sEvent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (WaitForSingleObject(stopServiceEvent, 0) == WAIT_TIMEOUT);
|
|
|
|
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
sprintf(logBuff, "Closing Network Connections...");
|
|
logMess(logBuff, 1);
|
|
|
|
closeConn();
|
|
|
|
WSACleanup();
|
|
|
|
sprintf(logBuff, "TFTP Server Stopped !\n");
|
|
logMess(logBuff, 1);
|
|
|
|
if (cfig.logfile)
|
|
{
|
|
fclose(cfig.logfile);
|
|
cfig.logfile = NULL;
|
|
}
|
|
|
|
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
|
|
serviceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
CloseHandle(stopServiceEvent);
|
|
stopServiceEvent = 0;
|
|
}
|
|
}
|
|
|
|
void runService()
|
|
{
|
|
SERVICE_TABLE_ENTRY serviceTable[] =
|
|
{
|
|
{serviceName, ServiceMain},
|
|
{0, 0}
|
|
};
|
|
|
|
StartServiceCtrlDispatcher(serviceTable);
|
|
}
|
|
|
|
bool stopService(SC_HANDLE service)
|
|
{
|
|
if (service)
|
|
{
|
|
SERVICE_STATUS serviceStatus;
|
|
QueryServiceStatus(service, &serviceStatus);
|
|
if (serviceStatus.dwCurrentState != SERVICE_STOPPED)
|
|
{
|
|
ControlService(service, SERVICE_CONTROL_STOP, &serviceStatus);
|
|
printf("Stopping Service.");
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
QueryServiceStatus(service, &serviceStatus);
|
|
if (serviceStatus.dwCurrentState == SERVICE_STOPPED)
|
|
{
|
|
printf("Stopped\n");
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Sleep(500);
|
|
printf(".");
|
|
}
|
|
}
|
|
printf("Failed\n");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void installService()
|
|
{
|
|
SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
|
|
|
|
if (serviceControlManager)
|
|
{
|
|
SC_HANDLE service = OpenService(serviceControlManager,
|
|
serviceName, SERVICE_QUERY_STATUS);
|
|
if (service)
|
|
{
|
|
printf("Service Already Exists..\n");
|
|
StartService(service,0,NULL);
|
|
CloseServiceHandle(service);
|
|
}
|
|
else
|
|
{
|
|
TCHAR path[ _MAX_PATH + 1 ];
|
|
if (GetModuleFileName(0, path, sizeof(path) / sizeof(path[0])) > 0)
|
|
{
|
|
SC_HANDLE service = CreateService(serviceControlManager,
|
|
serviceName, displayName,
|
|
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
|
|
SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
|
|
0, 0, 0, 0, 0);
|
|
if (service)
|
|
{
|
|
printf("Successfully installed.. !\n");
|
|
StartService(service,0,NULL);
|
|
CloseServiceHandle(service);
|
|
}
|
|
else
|
|
printf("Installation Failed..\n");
|
|
}
|
|
}
|
|
CloseServiceHandle(serviceControlManager);
|
|
}
|
|
else
|
|
printWindowsError();
|
|
}
|
|
|
|
void uninstallService()
|
|
{
|
|
SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
|
|
|
|
if (serviceControlManager)
|
|
{
|
|
SC_HANDLE service = OpenService(serviceControlManager,
|
|
serviceName, SERVICE_QUERY_STATUS | SERVICE_STOP | DELETE);
|
|
if (service)
|
|
{
|
|
if (stopService(service))
|
|
{
|
|
DeleteService(service);
|
|
printf("Successfully Removed !\n");
|
|
}
|
|
else
|
|
printf("Failed to Stop Service..\n");
|
|
|
|
CloseServiceHandle(service);
|
|
}
|
|
|
|
CloseServiceHandle(serviceControlManager);
|
|
}
|
|
else
|
|
printWindowsError();
|
|
}
|
|
|
|
void printWindowsError()
|
|
{
|
|
MYDWORD dw = GetLastError();
|
|
|
|
if (dw)
|
|
{
|
|
LPVOID lpMsgBuf;
|
|
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
dw,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR) &lpMsgBuf,
|
|
0, NULL );
|
|
|
|
printf("Error: %s\nPress Enter..\n", (LPTSTR)lpMsgBuf);
|
|
getchar();
|
|
}
|
|
}
|
|
|
|
int main(int argc, TCHAR* argv[])
|
|
{
|
|
OSVERSIONINFO osvi;
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
bool result = GetVersionEx(&osvi);
|
|
|
|
if (result && osvi.dwPlatformId >= VER_PLATFORM_WIN32_NT)
|
|
{
|
|
if (argc > 1 && lstrcmpi(argv[1], TEXT("-i")) == 0)
|
|
installService();
|
|
else if (argc > 1 && lstrcmpi(argv[1], TEXT("-u")) == 0)
|
|
uninstallService();
|
|
else if (argc > 1 && lstrcmpi(argv[1], TEXT("-v")) == 0)
|
|
{
|
|
SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
|
|
bool serviceStopped = true;
|
|
|
|
if (serviceControlManager)
|
|
{
|
|
SC_HANDLE service = OpenService(serviceControlManager,
|
|
serviceName, SERVICE_QUERY_STATUS | SERVICE_STOP);
|
|
if (service)
|
|
{
|
|
serviceStopped = stopService(service);
|
|
CloseServiceHandle(service);
|
|
}
|
|
CloseServiceHandle(serviceControlManager);
|
|
}
|
|
else
|
|
printWindowsError();
|
|
|
|
if (serviceStopped)
|
|
runProg();
|
|
else
|
|
printf("Failed to Stop Service\n");
|
|
}
|
|
else
|
|
runService();
|
|
}
|
|
else if (argc == 1 || lstrcmpi(argv[1], TEXT("-v")) == 0)
|
|
runProg();
|
|
else
|
|
printf("This option is not available on Windows95/98/ME\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void runProg()
|
|
{
|
|
verbatim = true;
|
|
|
|
if (_beginthread(init, 0, 0) == 0)
|
|
{
|
|
if (cfig.logLevel)
|
|
{
|
|
sprintf(logBuff, "Thread Creation Failed");
|
|
logMess(logBuff, 1);
|
|
}
|
|
exit(-1);
|
|
}
|
|
|
|
fd_set readfds;
|
|
timeval tv;
|
|
int fdsReady = 0;
|
|
tv.tv_sec = 20;
|
|
tv.tv_usec = 0;
|
|
|
|
printf("\naccepting requests..\n");
|
|
|
|
do
|
|
{
|
|
network.busy = false;
|
|
|
|
//printf("Active=%u Total=%u\n",activeThreads, totalThreads);
|
|
|
|
if (!network.tftpConn[0].ready || !network.ready)
|
|
{
|
|
Sleep(1000);
|
|
continue;
|
|
}
|
|
|
|
FD_ZERO(&readfds);
|
|
|
|
for (int i = 0; i < MAX_SERVERS && network.tftpConn[i].ready; i++)
|
|
FD_SET(network.tftpConn[i].sock, &readfds);
|
|
|
|
fdsReady = select(network.maxFD, &readfds, NULL, NULL, &tv);
|
|
|
|
if (!network.ready)
|
|
continue;
|
|
|
|
//errno = WSAGetLastError();
|
|
|
|
//if (errno)
|
|
// printf("%d\n", errno);
|
|
|
|
for (int i = 0; fdsReady > 0 && i < MAX_SERVERS && network.tftpConn[i].ready; i++)
|
|
{
|
|
if (network.ready)
|
|
{
|
|
network.busy = true;
|
|
|
|
if (FD_ISSET(network.tftpConn[i].sock, &readfds))
|
|
{
|
|
//printf("%d Requests Waiting\n", fdsReady);
|
|
|
|
WaitForSingleObject(sEvent, INFINITE);
|
|
|
|
currentServer = i;
|
|
|
|
if (!totalThreads || activeThreads >= totalThreads)
|
|
{
|
|
_beginthread(
|
|
processRequest, // thread function
|
|
0, // default security attributes
|
|
NULL); // argument to thread function
|
|
}
|
|
SetEvent(tEvent);
|
|
|
|
//printf("thread signalled=%u\n",SetEvent(tEvent));
|
|
|
|
WaitForSingleObject(sEvent, INFINITE);
|
|
fdsReady--;
|
|
SetEvent(sEvent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (true);
|
|
|
|
closeConn();
|
|
|
|
WSACleanup();
|
|
}
|
|
|
|
void closeConn()
|
|
{
|
|
for (int i = 0; i < MAX_SERVERS && network.tftpConn[i].loaded; i++)
|
|
if (network.tftpConn[i].ready)
|
|
closesocket(network.tftpConn[i].sock);
|
|
}
|
|
|
|
void processRequest(void *lpParam)
|
|
{
|
|
//printf("New Thread %u\n",GetCurrentThreadId());
|
|
|
|
request req;
|
|
|
|
WaitForSingleObject(cEvent, INFINITE);
|
|
totalThreads++;
|
|
SetEvent(cEvent);
|
|
|
|
do
|
|
{
|
|
WaitForSingleObject(tEvent, INFINITE);
|
|
//printf("In Thread %u\n",GetCurrentThreadId());
|
|
|
|
WaitForSingleObject(cEvent, INFINITE);
|
|
activeThreads++;
|
|
SetEvent(cEvent);
|
|
|
|
if (currentServer >= MAX_SERVERS || !network.tftpConn[currentServer].port)
|
|
{
|
|
SetEvent(sEvent);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
memset(&req, 0, sizeof(request));
|
|
req.sock = INVALID_SOCKET;
|
|
|
|
req.clientsize = sizeof(req.client);
|
|
req.sockInd = currentServer;
|
|
currentServer = UCHAR_MAX;
|
|
req.knock = network.tftpConn[req.sockInd].sock;
|
|
|
|
if (req.knock == INVALID_SOCKET)
|
|
{
|
|
SetEvent(sEvent);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
errno = 0;
|
|
req.bytesRecd = recvfrom(req.knock, (char*)&req.mesin, sizeof(message), 0, (sockaddr*)&req.client, &req.clientsize);
|
|
errno = WSAGetLastError();
|
|
|
|
//printf("socket Signalled=%u\n",SetEvent(sEvent));
|
|
SetEvent(sEvent);
|
|
|
|
if (!errno && req.bytesRecd > 0)
|
|
{
|
|
if (cfig.hostRanges[0].rangeStart)
|
|
{
|
|
MYDWORD iip = ntohl(req.client.sin_addr.s_addr);
|
|
bool allowed = false;
|
|
|
|
#ifdef __REACTOS__
|
|
for (MYWORD j = 0; j < _countof(cfig.hostRanges) && cfig.hostRanges[j].rangeStart; j++)
|
|
#else
|
|
for (int j = 0; j <= 32 && cfig.hostRanges[j].rangeStart; j++)
|
|
#endif
|
|
{
|
|
if (iip >= cfig.hostRanges[j].rangeStart && iip <= cfig.hostRanges[j].rangeEnd)
|
|
{
|
|
allowed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!allowed)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "Access Denied");
|
|
logMess(&req, 1);
|
|
sendto(req.knock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0, (sockaddr*)&req.client, req.clientsize);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((htons(req.mesin.opcode) == 5))
|
|
{
|
|
sprintf(req.serverError.errormessage, "Error Code %i at Client, %s", ntohs(req.clientError.errorcode), req.clientError.errormessage);
|
|
logMess(&req, 2);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
else if (htons(req.mesin.opcode) != 1 && htons(req.mesin.opcode) != 2)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(5);
|
|
sprintf(req.serverError.errormessage, "Unknown Transfer Id");
|
|
logMess(&req, 2);
|
|
sendto(req.knock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0, (sockaddr*)&req.client, req.clientsize);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
req.blksize = 512;
|
|
req.timeout = timeout;
|
|
req.expiry = time(NULL) + req.timeout;
|
|
bool fetchAck = false;
|
|
|
|
req.sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
|
|
if (req.sock == INVALID_SOCKET)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(0);
|
|
strcpy(req.serverError.errormessage, "Thread Socket Creation Error");
|
|
sendto(req.knock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0, (sockaddr*)&req.client, req.clientsize);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
sockaddr_in service;
|
|
service.sin_family = AF_INET;
|
|
service.sin_addr.s_addr = network.tftpConn[req.sockInd].server;
|
|
|
|
if (cfig.minport)
|
|
{
|
|
for (MYWORD comport = cfig.minport; ; comport++)
|
|
{
|
|
service.sin_port = htons(comport);
|
|
|
|
if (comport > cfig.maxport)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(0);
|
|
strcpy(req.serverError.errormessage, "No port is free");
|
|
sendto(req.knock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0, (sockaddr*)&req.client, req.clientsize);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else if (bind(req.sock, (sockaddr*) &service, sizeof(service)) == -1)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
service.sin_port = 0;
|
|
|
|
if (bind(req.sock, (sockaddr*) &service, sizeof(service)) == -1)
|
|
{
|
|
strcpy(req.serverError.errormessage, "Thread failed to bind");
|
|
sendto(req.knock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0, (sockaddr*)&req.client, req.clientsize);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
}
|
|
}
|
|
|
|
if (req.attempt >= 3)
|
|
continue;
|
|
|
|
if (connect(req.sock, (sockaddr*)&req.client, req.clientsize) == -1)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(0);
|
|
strcpy(req.serverError.errormessage, "Connect Failed");
|
|
sendto(req.knock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0, (sockaddr*)&req.client, req.clientsize);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
//sprintf(req.serverError.errormessage, "In Temp, Socket");
|
|
//logMess(&req, 1);
|
|
|
|
char *inPtr = req.mesin.buffer;
|
|
*(inPtr + (req.bytesRecd - 3)) = 0;
|
|
req.filename = inPtr;
|
|
|
|
if (!strlen(req.filename) || strlen(req.filename) > UCHAR_MAX)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(4);
|
|
strcpy(req.serverError.errormessage, "Malformed Request, Invalid/Missing Filename");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
req.attempt = UCHAR_MAX;
|
|
logMess(&req, 1);
|
|
continue;
|
|
}
|
|
|
|
inPtr += strlen(inPtr) + 1;
|
|
req.mode = inPtr;
|
|
|
|
if (!strlen(req.mode) || strlen(req.mode) > 25)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(4);
|
|
strcpy(req.serverError.errormessage, "Malformed Request, Invalid/Missing Mode");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
req.attempt = UCHAR_MAX;
|
|
logMess(&req, 1);
|
|
continue;
|
|
}
|
|
|
|
inPtr += strlen(inPtr) + 1;
|
|
|
|
for (MYDWORD i = 0; i < strlen(req.filename); i++)
|
|
if (req.filename[i] == notFileSep)
|
|
req.filename[i] = fileSep;
|
|
|
|
tempbuff[0] = '.';
|
|
tempbuff[1] = '.';
|
|
tempbuff[2] = fileSep;
|
|
tempbuff[3] = 0;
|
|
|
|
if (strstr(req.filename, tempbuff))
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "Access violation");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
if (req.filename[0] == fileSep)
|
|
req.filename++;
|
|
|
|
if (!cfig.homes[0].alias[0])
|
|
{
|
|
if (strlen(cfig.homes[0].target) + strlen(req.filename) >= sizeof(req.path))
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(4);
|
|
sprintf(req.serverError.errormessage, "Filename too large");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
strcpy(req.path, cfig.homes[0].target);
|
|
strcat(req.path, req.filename);
|
|
}
|
|
else
|
|
{
|
|
char *bname = strchr(req.filename, fileSep);
|
|
|
|
if (bname)
|
|
{
|
|
*bname = 0;
|
|
bname++;
|
|
}
|
|
else
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
sprintf(req.serverError.errormessage, "Missing directory/alias");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
#ifdef __REACTOS__
|
|
for (int i = 0; i < MAX_SERVERS; i++)
|
|
#else
|
|
for (int i = 0; i < 8; i++)
|
|
#endif
|
|
{
|
|
//printf("%s=%i\n", req.filename, cfig.homes[i].alias[0]);
|
|
if (cfig.homes[i].alias[0] && !strcasecmp(req.filename, cfig.homes[i].alias))
|
|
{
|
|
if (strlen(cfig.homes[i].target) + strlen(bname) >= sizeof(req.path))
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(4);
|
|
sprintf(req.serverError.errormessage, "Filename too large");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
|
|
strcpy(req.path, cfig.homes[i].target);
|
|
strcat(req.path, bname);
|
|
break;
|
|
}
|
|
else if (i == 7 || !cfig.homes[i].alias[0])
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
sprintf(req.serverError.errormessage, "No such directory/alias %s", req.filename);
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (req.attempt >= 3)
|
|
continue;
|
|
|
|
if (ntohs(req.mesin.opcode) == 1)
|
|
{
|
|
if (!cfig.fileRead)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "GET Access Denied");
|
|
logMess(&req, 1);
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
if (*inPtr)
|
|
{
|
|
char *tmp = inPtr;
|
|
|
|
while (*tmp)
|
|
{
|
|
if (!strcasecmp(tmp, "blksize"))
|
|
{
|
|
tmp += strlen(tmp) + 1;
|
|
MYDWORD val = atol(tmp);
|
|
|
|
if (val < 512)
|
|
val = 512;
|
|
else if (val > blksize)
|
|
val = blksize;
|
|
|
|
req.blksize = val;
|
|
break;
|
|
}
|
|
|
|
tmp += strlen(tmp) + 1;
|
|
}
|
|
}
|
|
|
|
errno = 0;
|
|
|
|
if (!strcasecmp(req.mode, "netascii") || !strcasecmp(req.mode, "ascii"))
|
|
req.file = fopen(req.path, "rt");
|
|
else
|
|
req.file = fopen(req.path, "rb");
|
|
|
|
if (errno || !req.file)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(1);
|
|
strcpy(req.serverError.errormessage, "File not found or No Access");
|
|
logMess(&req, 1);
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!cfig.fileWrite && !cfig.fileOverwrite)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "PUT Access Denied");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
req.file = fopen(req.path, "rb");
|
|
|
|
if (req.file)
|
|
{
|
|
fclose(req.file);
|
|
req.file = NULL;
|
|
|
|
if (!cfig.fileOverwrite)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(6);
|
|
strcpy(req.serverError.errormessage, "File already exists");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
}
|
|
else if (!cfig.fileWrite)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "Create File Access Denied");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
errno = 0;
|
|
|
|
if (!strcasecmp(req.mode, "netascii") || !strcasecmp(req.mode, "ascii"))
|
|
req.file = fopen(req.path, "wt");
|
|
else
|
|
req.file = fopen(req.path, "wb");
|
|
|
|
if (errno || !req.file)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "Invalid Path or No Access");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
setvbuf(req.file, NULL, _IOFBF, 5 * req.blksize);
|
|
|
|
if (*inPtr)
|
|
{
|
|
fetchAck = true;
|
|
char *outPtr = req.mesout.buffer;
|
|
req.mesout.opcode = htons(6);
|
|
MYDWORD val;
|
|
while (*inPtr)
|
|
{
|
|
//printf("%s\n", inPtr);
|
|
if (!strcasecmp(inPtr, "blksize"))
|
|
{
|
|
strcpy(outPtr, inPtr);
|
|
outPtr += strlen(outPtr) + 1;
|
|
inPtr += strlen(inPtr) + 1;
|
|
val = atol(inPtr);
|
|
|
|
if (val < 512)
|
|
val = 512;
|
|
else if (val > blksize)
|
|
val = blksize;
|
|
|
|
req.blksize = val;
|
|
sprintf(outPtr, "%u", val);
|
|
outPtr += strlen(outPtr) + 1;
|
|
}
|
|
else if (!strcasecmp(inPtr, "tsize"))
|
|
{
|
|
strcpy(outPtr, inPtr);
|
|
outPtr += strlen(outPtr) + 1;
|
|
inPtr += strlen(inPtr) + 1;
|
|
|
|
if (ntohs(req.mesin.opcode) == 1)
|
|
{
|
|
if (!fseek(req.file, 0, SEEK_END))
|
|
{
|
|
if (ftell(req.file) >= 0)
|
|
{
|
|
req.tsize = ftell(req.file);
|
|
sprintf(outPtr, "%u", req.tsize);
|
|
outPtr += strlen(outPtr) + 1;
|
|
}
|
|
else
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "Invalid Path or No Access");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "Invalid Path or No Access");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
req.tsize = 0;
|
|
sprintf(outPtr, "%u", req.tsize);
|
|
outPtr += strlen(outPtr) + 1;
|
|
}
|
|
}
|
|
else if (!strcasecmp(inPtr, "timeout"))
|
|
{
|
|
strcpy(outPtr, inPtr);
|
|
outPtr += strlen(outPtr) + 1;
|
|
inPtr += strlen(inPtr) + 1;
|
|
val = atoi(inPtr);
|
|
|
|
if (val < 1)
|
|
val = 1;
|
|
else if (val > UCHAR_MAX)
|
|
val = UCHAR_MAX;
|
|
|
|
req.timeout = val;
|
|
req.expiry = time(NULL) + req.timeout;
|
|
sprintf(outPtr, "%u", val);
|
|
outPtr += strlen(outPtr) + 1;
|
|
}
|
|
|
|
inPtr += strlen(inPtr) + 1;
|
|
//printf("=%u\n", val);
|
|
}
|
|
|
|
if (req.attempt >= 3)
|
|
continue;
|
|
|
|
errno = 0;
|
|
req.bytesReady = (const char*)outPtr - (const char*)&req.mesout;
|
|
//printf("Bytes Ready=%u\n", req.bytesReady);
|
|
send(req.sock, (const char*)&req.mesout, req.bytesReady, 0);
|
|
errno = WSAGetLastError();
|
|
}
|
|
else if (htons(req.mesin.opcode) == 2)
|
|
{
|
|
req.acout.opcode = htons(4);
|
|
req.acout.block = htons(0);
|
|
errno = 0;
|
|
req.bytesReady = 4;
|
|
send(req.sock, (const char*)&req.mesout, req.bytesReady, 0);
|
|
errno = WSAGetLastError();
|
|
}
|
|
|
|
if (errno)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
else if (ntohs(req.mesin.opcode) == 1)
|
|
{
|
|
errno = 0;
|
|
req.pkt[0] = (packet*)calloc(1, req.blksize + 4);
|
|
req.pkt[1] = (packet*)calloc(1, req.blksize + 4);
|
|
|
|
if (errno || !req.pkt[0] || !req.pkt[1])
|
|
{
|
|
sprintf(req.serverError.errormessage, "Memory Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
long ftellLoc = ftell(req.file);
|
|
|
|
if (ftellLoc > 0)
|
|
{
|
|
if (fseek(req.file, 0, SEEK_SET))
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "File Access Error");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
}
|
|
else if (ftellLoc < 0)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "File Access Error");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
errno = 0;
|
|
req.pkt[0]->opcode = htons(3);
|
|
req.pkt[0]->block = htons(1);
|
|
req.bytesRead[0] = fread(&req.pkt[0]->buffer, 1, req.blksize, req.file);
|
|
|
|
if (errno)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "Invalid Path or No Access");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
if (req.bytesRead[0] == req.blksize)
|
|
{
|
|
req.pkt[1]->opcode = htons(3);
|
|
req.pkt[1]->block = htons(2);
|
|
req.bytesRead[1] = fread(&req.pkt[1]->buffer, 1, req.blksize, req.file);
|
|
if (req.bytesRead[1] < req.blksize)
|
|
{
|
|
fclose(req.file);
|
|
req.file = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fclose(req.file);
|
|
req.file = 0;
|
|
}
|
|
|
|
if (errno)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(2);
|
|
strcpy(req.serverError.errormessage, "Invalid Path or No Access");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
while (req.attempt <= 3)
|
|
{
|
|
if (fetchAck)
|
|
{
|
|
FD_ZERO(&req.readfds);
|
|
req.tv.tv_sec = 1;
|
|
req.tv.tv_usec = 0;
|
|
FD_SET(req.sock, &req.readfds);
|
|
select(req.sock + 1, &req.readfds, NULL, NULL, &req.tv);
|
|
|
|
if (FD_ISSET(req.sock, &req.readfds))
|
|
{
|
|
errno = 0;
|
|
req.bytesRecd = recv(req.sock, (char*)&req.mesin, sizeof(message), 0);
|
|
errno = WSAGetLastError();
|
|
if (req.bytesRecd <= 0 || errno)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else if(req.bytesRecd >= 4 && ntohs(req.mesin.opcode) == 4)
|
|
{
|
|
if (ntohs(req.acin.block) == req.block)
|
|
{
|
|
req.block++;
|
|
req.fblock++;
|
|
req.attempt = 0;
|
|
}
|
|
else if (req.expiry > time(NULL))
|
|
continue;
|
|
else
|
|
req.attempt++;
|
|
}
|
|
else if (ntohs(req.mesin.opcode) == 5)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Client %s:%u, Error Code %i at Client, %s", inet_ntoa(req.client.sin_addr), ntohs(req.client.sin_port), ntohs(req.clientError.errorcode), req.clientError.errormessage);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(4);
|
|
sprintf(req.serverError.errormessage, "Unexpected Option Code %i", ntohs(req.mesin.opcode));
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
else if (req.expiry > time(NULL))
|
|
continue;
|
|
else
|
|
req.attempt++;
|
|
}
|
|
else
|
|
{
|
|
fetchAck = true;
|
|
req.acin.block = 1;
|
|
req.block = 1;
|
|
req.fblock = 1;
|
|
}
|
|
|
|
if (req.attempt >= 3)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(0);
|
|
|
|
if (req.fblock && !req.block)
|
|
strcpy(req.serverError.errormessage, "Large File, Block# Rollover not supported by Client");
|
|
else
|
|
strcpy(req.serverError.errormessage, "Timeout");
|
|
|
|
logMess(&req, 1);
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else if (!req.fblock)
|
|
{
|
|
errno = 0;
|
|
send(req.sock, (const char*)&req.mesout, req.bytesReady, 0);
|
|
errno = WSAGetLastError();
|
|
if (errno)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
req.expiry = time(NULL) + req.timeout;
|
|
}
|
|
else if (ntohs(req.pkt[0]->block) == req.block)
|
|
{
|
|
errno = 0;
|
|
send(req.sock, (const char*)req.pkt[0], req.bytesRead[0] + 4, 0);
|
|
errno = WSAGetLastError();
|
|
if (errno)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
req.expiry = time(NULL) + req.timeout;
|
|
|
|
if (req.file)
|
|
{
|
|
req.tblock = ntohs(req.pkt[1]->block) + 1;
|
|
if (req.tblock == req.block)
|
|
{
|
|
req.pkt[1]->block = htons(++req.tblock);
|
|
req.bytesRead[1] = fread(&req.pkt[1]->buffer, 1, req.blksize, req.file);
|
|
|
|
if (errno)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(4);
|
|
sprintf(req.serverError.errormessage, strerror(errno));
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else if (req.bytesRead[1] < req.blksize)
|
|
{
|
|
fclose(req.file);
|
|
req.file = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ntohs(req.pkt[1]->block) == req.block)
|
|
{
|
|
errno = 0;
|
|
send(req.sock, (const char*)req.pkt[1], req.bytesRead[1] + 4, 0);
|
|
errno = WSAGetLastError();
|
|
if (errno)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
|
|
req.expiry = time(NULL) + req.timeout;
|
|
|
|
if (req.file)
|
|
{
|
|
req.tblock = ntohs(req.pkt[0]->block) + 1;
|
|
if (req.tblock == req.block)
|
|
{
|
|
req.pkt[0]->block = htons(++req.tblock);
|
|
req.bytesRead[0] = fread(&req.pkt[0]->buffer, 1, req.blksize, req.file);
|
|
if (errno)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(4);
|
|
sprintf(req.serverError.errormessage, strerror(errno));
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else if (req.bytesRead[0] < req.blksize)
|
|
{
|
|
fclose(req.file);
|
|
req.file = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf(req.serverError.errormessage, "%u Blocks Served", req.fblock - 1);
|
|
logMess(&req, 2);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (ntohs(req.mesin.opcode) == 2)
|
|
{
|
|
errno = 0;
|
|
req.pkt[0] = (packet*)calloc(1, req.blksize + 4);
|
|
|
|
if (errno || !req.pkt[0])
|
|
{
|
|
sprintf(req.serverError.errormessage, "Memory Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
continue;
|
|
}
|
|
|
|
while (req.attempt <= 3)
|
|
{
|
|
FD_ZERO(&req.readfds);
|
|
req.tv.tv_sec = 1;
|
|
req.tv.tv_usec = 0;
|
|
FD_SET(req.sock, &req.readfds);
|
|
select(req.sock + 1, &req.readfds, NULL, NULL, &req.tv);
|
|
|
|
if (FD_ISSET(req.sock, &req.readfds))
|
|
{
|
|
errno = 0;
|
|
req.bytesRecd = recv(req.sock, (char*)req.pkt[0], req.blksize + 4, 0);
|
|
errno = WSAGetLastError();
|
|
|
|
if (errno)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
req.bytesRecd = 0;
|
|
|
|
if (req.bytesRecd >= 4)
|
|
{
|
|
if (ntohs(req.pkt[0]->opcode) == 3)
|
|
{
|
|
req.tblock = req.block + 1;
|
|
|
|
if (ntohs(req.pkt[0]->block) == req.tblock)
|
|
{
|
|
req.acout.opcode = htons(4);
|
|
req.acout.block = req.pkt[0]->block;
|
|
req.block++;
|
|
req.fblock++;
|
|
req.bytesReady = 4;
|
|
req.expiry = time(NULL) + req.timeout;
|
|
|
|
errno = 0;
|
|
send(req.sock, (const char*)&req.mesout, req.bytesReady, 0);
|
|
errno = WSAGetLastError();
|
|
|
|
if (errno)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
|
|
if (req.bytesRecd > 4)
|
|
{
|
|
errno = 0;
|
|
if (fwrite(&req.pkt[0]->buffer, req.bytesRecd - 4, 1, req.file) != 1 || errno)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(3);
|
|
strcpy(req.serverError.errormessage, "Disk full or allocation exceeded");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else
|
|
req.attempt = 0;
|
|
}
|
|
else
|
|
req.attempt = 0;
|
|
|
|
if ((MYWORD)req.bytesRecd < req.blksize + 4)
|
|
{
|
|
fclose(req.file);
|
|
req.file = 0;
|
|
sprintf(req.serverError.errormessage, "%u Blocks Received", req.fblock);
|
|
logMess(&req, 2);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
else if (req.expiry > time(NULL))
|
|
continue;
|
|
else if (req.attempt >= 3)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(0);
|
|
|
|
if (req.fblock && !req.block)
|
|
strcpy(req.serverError.errormessage, "Large File, Block# Rollover not supported by Client");
|
|
else
|
|
strcpy(req.serverError.errormessage, "Timeout");
|
|
|
|
logMess(&req, 1);
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
req.expiry = time(NULL) + req.timeout;
|
|
errno = 0;
|
|
send(req.sock, (const char*)&req.mesout, req.bytesReady, 0);
|
|
errno = WSAGetLastError();
|
|
req.attempt++;
|
|
|
|
if (errno)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (req.bytesRecd > (int)sizeof(message))
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(4);
|
|
sprintf(req.serverError.errormessage, "Error: Incoming Packet too large");
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else if (ntohs(req.pkt[0]->opcode) == 5)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Error Code %i at Client, %s", ntohs(req.pkt[0]->block), &req.pkt[0]->buffer);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(4);
|
|
sprintf(req.serverError.errormessage, "Unexpected Option Code %i", ntohs(req.pkt[0]->opcode));
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
else if (req.expiry > time(NULL))
|
|
continue;
|
|
else if (req.attempt >= 3)
|
|
{
|
|
req.serverError.opcode = htons(5);
|
|
req.serverError.errorcode = htons(0);
|
|
|
|
if (req.fblock && !req.block)
|
|
strcpy(req.serverError.errormessage, "Large File, Block# Rollover not supported by Client");
|
|
else
|
|
strcpy(req.serverError.errormessage, "Timeout");
|
|
|
|
logMess(&req, 1);
|
|
send(req.sock, (const char*)&req.serverError, strlen(req.serverError.errormessage) + 5, 0);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
req.expiry = time(NULL) + req.timeout;
|
|
errno = 0;
|
|
send(req.sock, (const char*)&req.mesout, req.bytesReady, 0);
|
|
errno = WSAGetLastError();
|
|
req.attempt++;
|
|
|
|
if (errno)
|
|
{
|
|
sprintf(req.serverError.errormessage, "Communication Error");
|
|
logMess(&req, 1);
|
|
req.attempt = UCHAR_MAX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (cleanReq(&req));
|
|
|
|
WaitForSingleObject(cEvent, INFINITE);
|
|
totalThreads--;
|
|
SetEvent(cEvent);
|
|
|
|
//printf("Thread %u Killed\n",GetCurrentThreadId());
|
|
_endthread();
|
|
return;
|
|
}
|
|
|
|
bool cleanReq(request* req)
|
|
{
|
|
//printf("cleaning\n");
|
|
|
|
if (req->file)
|
|
fclose(req->file);
|
|
|
|
if (!(req->sock == INVALID_SOCKET))
|
|
{
|
|
//printf("Here\n");
|
|
closesocket(req->sock);
|
|
}
|
|
|
|
if (req->pkt[0])
|
|
free(req->pkt[0]);
|
|
|
|
if (req->pkt[1])
|
|
free(req->pkt[1]);
|
|
|
|
WaitForSingleObject(cEvent, INFINITE);
|
|
activeThreads--;
|
|
SetEvent(cEvent);
|
|
|
|
//printf("cleaned\n");
|
|
|
|
return (totalThreads <= minThreads);
|
|
}
|
|
|
|
bool getSection(const char *sectionName, char *buffer, MYBYTE serial, char *fileName)
|
|
{
|
|
//printf("%s=%s\n",fileName,sectionName);
|
|
char section[128];
|
|
sprintf(section, "[%s]", sectionName);
|
|
myUpper(section);
|
|
FILE *f = fopen(fileName, "rt");
|
|
char buff[512];
|
|
MYBYTE found = 0;
|
|
|
|
if (f)
|
|
{
|
|
while (fgets(buff, 511, f))
|
|
{
|
|
myUpper(buff);
|
|
myTrim(buff, buff);
|
|
|
|
if (strstr(buff, section) == buff)
|
|
{
|
|
found++;
|
|
if (found == serial)
|
|
{
|
|
//printf("%s=%s\n",fileName,sectionName);
|
|
while (fgets(buff, 511, f))
|
|
{
|
|
myTrim(buff, buff);
|
|
|
|
if (strstr(buff, "[") == buff)
|
|
break;
|
|
|
|
if (((*buff) >= '0' && (*buff) <= '9') || ((*buff) >= 'A' && (*buff) <= 'Z') || ((*buff) >= 'a' && (*buff) <= 'z') || ((*buff) && strchr("/\\?*", (*buff))))
|
|
{
|
|
buffer += sprintf(buffer, "%s", buff);
|
|
buffer++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
*buffer = 0;
|
|
*(buffer + 1) = 0;
|
|
return (found == serial);
|
|
}
|
|
|
|
FILE *openSection(const char *sectionName, MYBYTE serial, char *fileName)
|
|
{
|
|
//printf("%s=%s\n",fileName,sectionName);
|
|
char section[128];
|
|
sprintf(section, "[%s]", sectionName);
|
|
myUpper(section);
|
|
FILE *f = fopen(fileName, "rt");
|
|
char buff[512];
|
|
MYBYTE found = 0;
|
|
|
|
if (f)
|
|
{
|
|
while (fgets(buff, 511, f))
|
|
{
|
|
myUpper(buff);
|
|
myTrim(buff, buff);
|
|
|
|
if (strstr(buff, section) == buff)
|
|
{
|
|
found++;
|
|
|
|
if (found == serial)
|
|
return f;
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *readSection(char* buff, FILE *f)
|
|
{
|
|
while (fgets(buff, 511, f))
|
|
{
|
|
myTrim(buff, buff);
|
|
|
|
if (*buff == '[')
|
|
break;
|
|
|
|
if (((*buff) >= '0' && (*buff) <= '9') || ((*buff) >= 'A' && (*buff) <= 'Z') || ((*buff) >= 'a' && (*buff) <= 'z') || ((*buff) && strchr("/\\?*", (*buff))))
|
|
return buff;
|
|
}
|
|
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
char* myGetToken(char* buff, MYBYTE index)
|
|
{
|
|
while (*buff)
|
|
{
|
|
if (index)
|
|
index--;
|
|
else
|
|
break;
|
|
|
|
buff += strlen(buff) + 1;
|
|
}
|
|
|
|
return buff;
|
|
}
|
|
|
|
MYWORD myTokenize(char *target, char *source, char *sep, bool whiteSep)
|
|
{
|
|
bool found = true;
|
|
char *dp = target;
|
|
MYWORD kount = 0;
|
|
|
|
while (*source)
|
|
{
|
|
if (sep && sep[0] && strchr(sep, (*source)))
|
|
{
|
|
found = true;
|
|
source++;
|
|
continue;
|
|
}
|
|
else if (whiteSep && (*source) <= 32)
|
|
{
|
|
found = true;
|
|
source++;
|
|
continue;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
if (target != dp)
|
|
{
|
|
*dp = 0;
|
|
dp++;
|
|
}
|
|
kount++;
|
|
}
|
|
|
|
found = false;
|
|
*dp = *source;
|
|
dp++;
|
|
source++;
|
|
}
|
|
|
|
*dp = 0;
|
|
dp++;
|
|
*dp = 0;
|
|
|
|
//printf("%s\n", target);
|
|
|
|
return kount;
|
|
}
|
|
|
|
char* myTrim(char *target, char *source)
|
|
{
|
|
while ((*source) && (*source) <= 32)
|
|
source++;
|
|
|
|
int i = 0;
|
|
|
|
for (; i < 511 && source[i]; i++)
|
|
target[i] = source[i];
|
|
|
|
target[i] = source[i];
|
|
i--;
|
|
|
|
for (; i >= 0 && target[i] <= 32; i--)
|
|
target[i] = 0;
|
|
|
|
return target;
|
|
}
|
|
|
|
/*
|
|
void mySplit(char *name, char *value, char *source, char splitChar)
|
|
{
|
|
char *dp = strchr(source, splitChar);
|
|
|
|
if (dp)
|
|
{
|
|
strncpy(name, source, (dp - source));
|
|
name[dp - source] = 0;
|
|
strcpy(value, dp + 1);
|
|
myTrim(name, name);
|
|
myTrim(value, value);
|
|
}
|
|
else
|
|
{
|
|
strcpy(name, source);
|
|
myTrim(name, name);
|
|
*value = 0;
|
|
}
|
|
}
|
|
*/
|
|
|
|
void mySplit(char *name, char *value, char *source, char splitChar)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
int k = 0;
|
|
|
|
for (; source[i] && j <= 510 && source[i] != splitChar; i++, j++)
|
|
{
|
|
name[j] = source[i];
|
|
}
|
|
|
|
if (source[i])
|
|
{
|
|
i++;
|
|
for (; k <= 510 && source[i]; i++, k++)
|
|
{
|
|
value[k] = source[i];
|
|
}
|
|
}
|
|
|
|
name[j] = 0;
|
|
value[k] = 0;
|
|
|
|
myTrim(name, name);
|
|
myTrim(value, value);
|
|
//printf("%s %s\n", name, value);
|
|
}
|
|
|
|
|
|
char *IP2String(char *target, MYDWORD ip)
|
|
{
|
|
data15 inaddr;
|
|
inaddr.ip = ip;
|
|
sprintf(target, "%u.%u.%u.%u", inaddr.octate[0], inaddr.octate[1], inaddr.octate[2], inaddr.octate[3]);
|
|
return target;
|
|
}
|
|
|
|
bool isIP(char *string)
|
|
{
|
|
int j = 0;
|
|
|
|
for (; *string; string++)
|
|
{
|
|
if (*string == '.' && *(string + 1) != '.')
|
|
j++;
|
|
else if (*string < '0' || *string > '9')
|
|
return 0;
|
|
}
|
|
|
|
if (j == 3)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
char *myUpper(char *string)
|
|
{
|
|
char diff = 'a' - 'A';
|
|
MYWORD len = strlen(string);
|
|
for (int i = 0; i < len; i++)
|
|
if (string[i] >= 'a' && string[i] <= 'z')
|
|
string[i] -= diff;
|
|
return string;
|
|
}
|
|
|
|
char *myLower(char *string)
|
|
{
|
|
char diff = 'a' - 'A';
|
|
MYWORD len = strlen(string);
|
|
for (int i = 0; i < len; i++)
|
|
if (string[i] >= 'A' && string[i] <= 'Z')
|
|
string[i] += diff;
|
|
return string;
|
|
}
|
|
|
|
void init(void *lpParam)
|
|
{
|
|
memset(&cfig, 0, sizeof(cfig));
|
|
|
|
GetModuleFileName(NULL, extbuff, _MAX_PATH);
|
|
char *fileExt = strrchr(extbuff, '.');
|
|
*fileExt = 0;
|
|
sprintf(iniFile, "%s.ini", extbuff);
|
|
sprintf(lnkFile, "%s.url", extbuff);
|
|
fileExt = strrchr(extbuff, '\\');
|
|
*fileExt = 0;
|
|
fileExt++;
|
|
sprintf(logFile, "%s\\log\\%s%%Y%%m%%d.log", extbuff, fileExt);
|
|
|
|
FILE *f = NULL;
|
|
char raw[512];
|
|
char name[512];
|
|
char value[512];
|
|
|
|
if (verbatim)
|
|
{
|
|
cfig.logLevel = 2;
|
|
printf("%s\n\n", sVersion);
|
|
}
|
|
else if ((f = openSection("LOGGING", 1, iniFile)))
|
|
{
|
|
cfig.logLevel = 1;
|
|
tempbuff[0] = 0;
|
|
|
|
while (readSection(raw, f))
|
|
{
|
|
if (!strcasecmp(raw, "None"))
|
|
cfig.logLevel = 0;
|
|
else if (!strcasecmp(raw, "Errors"))
|
|
cfig.logLevel = 1;
|
|
else if (!strcasecmp(raw, "All"))
|
|
cfig.logLevel = 2;
|
|
else
|
|
sprintf(tempbuff, "Section [LOGGING], Invalid LogLevel: %s", raw);
|
|
}
|
|
}
|
|
|
|
if (!verbatim && cfig.logLevel && logFile[0])
|
|
{
|
|
time_t t = time(NULL);
|
|
tm *ttm = localtime(&t);
|
|
loggingDay = ttm->tm_yday;
|
|
strftime(extbuff, sizeof(extbuff), logFile, ttm);
|
|
|
|
cfig.logfile = fopen(extbuff, "at");
|
|
|
|
if (cfig.logfile)
|
|
{
|
|
WritePrivateProfileString("InternetShortcut","URL", extbuff, lnkFile);
|
|
WritePrivateProfileString("InternetShortcut","IconIndex", "0", lnkFile);
|
|
WritePrivateProfileString("InternetShortcut","IconFile", extbuff, lnkFile);
|
|
sprintf(logBuff, "%s Starting..", sVersion);
|
|
logMess(logBuff, 1);
|
|
|
|
if (tempbuff[0])
|
|
logMess(tempbuff, 0);
|
|
}
|
|
}
|
|
|
|
MYWORD wVersionRequested = MAKEWORD(1, 1);
|
|
WSAStartup(wVersionRequested, &cfig.wsaData);
|
|
|
|
if (cfig.wsaData.wVersion != wVersionRequested)
|
|
{
|
|
sprintf(logBuff, "WSAStartup Error");
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
if ((f = openSection("HOME", 1, iniFile)))
|
|
{
|
|
while (readSection(raw, f))
|
|
{
|
|
mySplit(name, value, raw, '=');
|
|
|
|
if (strlen(value))
|
|
{
|
|
if (!cfig.homes[0].alias[0] && cfig.homes[0].target[0])
|
|
{
|
|
sprintf(logBuff, "Section [HOME], alias and bare path mixup, entry %s ignored", raw);
|
|
logMess(logBuff, 1);
|
|
}
|
|
else if (strchr(name, notFileSep) || strchr(name, fileSep) || strchr(name, '>') || strchr(name, '<') || strchr(name, '.'))
|
|
{
|
|
sprintf(logBuff, "Section [HOME], invalid chars in alias %s, entry ignored", name);
|
|
logMess(logBuff, 1);
|
|
}
|
|
else if (name[0] && strlen(name) < 64 && value[0])
|
|
{
|
|
#ifdef __REACTOS__
|
|
for (int i = 0; i < MAX_SERVERS; i++)
|
|
#else
|
|
for (int i = 0; i < 8; i++)
|
|
#endif
|
|
{
|
|
if (cfig.homes[i].alias[0] && !strcasecmp(name, cfig.homes[i].alias))
|
|
{
|
|
sprintf(logBuff, "Section [HOME], Duplicate Entry: %s ignored", raw);
|
|
logMess(logBuff, 1);
|
|
break;
|
|
}
|
|
else if (!cfig.homes[i].alias[0])
|
|
{
|
|
strcpy(cfig.homes[i].alias, name);
|
|
strcpy(cfig.homes[i].target, value);
|
|
|
|
if (cfig.homes[i].target[strlen(cfig.homes[i].target) - 1] != fileSep)
|
|
{
|
|
tempbuff[0] = fileSep;
|
|
tempbuff[1] = 0;
|
|
strcat(cfig.homes[i].target, tempbuff);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "Section [HOME], alias %s too large", name);
|
|
logMess(logBuff, 1);
|
|
}
|
|
}
|
|
else if (!cfig.homes[0].alias[0] && !cfig.homes[0].target[0])
|
|
{
|
|
strcpy(cfig.homes[0].target, name);
|
|
|
|
if (cfig.homes[0].target[strlen(cfig.homes[0].target) - 1] != fileSep)
|
|
{
|
|
tempbuff[0] = fileSep;
|
|
tempbuff[1] = 0;
|
|
strcat(cfig.homes[0].target, tempbuff);
|
|
}
|
|
}
|
|
else if (cfig.homes[0].alias[0])
|
|
{
|
|
sprintf(logBuff, "Section [HOME], alias and bare path mixup, entry %s ignored", raw);
|
|
logMess(logBuff, 1);
|
|
}
|
|
else if (cfig.homes[0].target[0])
|
|
{
|
|
sprintf(logBuff, "Section [HOME], Duplicate Path: %s ignored", raw);
|
|
logMess(logBuff, 1);
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "Section [HOME], missing = sign, Invalid Entry: %s ignored", raw);
|
|
logMess(logBuff, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!cfig.homes[0].target[0])
|
|
{
|
|
GetModuleFileName(NULL, cfig.homes[0].target, UCHAR_MAX);
|
|
char *iniFileExt = strrchr(cfig.homes[0].target, fileSep);
|
|
*(++iniFileExt) = 0;
|
|
}
|
|
|
|
cfig.fileRead = true;
|
|
|
|
if ((f = openSection("TFTP-OPTIONS", 1, iniFile)))
|
|
{
|
|
while (readSection(raw, f))
|
|
{
|
|
mySplit(name, value, raw, '=');
|
|
|
|
if (strlen(value))
|
|
{
|
|
if (!strcasecmp(name, "blksize"))
|
|
{
|
|
MYDWORD tblksize = atol(value);
|
|
|
|
if (tblksize < 512)
|
|
blksize = 512;
|
|
else if (tblksize > USHRT_MAX - 32)
|
|
blksize = USHRT_MAX - 32;
|
|
else
|
|
blksize = tblksize;
|
|
}
|
|
else if (!strcasecmp(name, "threadpoolsize"))
|
|
{
|
|
minThreads = atol(value);
|
|
if (minThreads < 1)
|
|
minThreads = 0;
|
|
else if (minThreads > 100)
|
|
minThreads = 100;
|
|
}
|
|
else if (!strcasecmp(name, "timeout"))
|
|
{
|
|
timeout = atol(value);
|
|
if (timeout < 1)
|
|
timeout = 1;
|
|
else if (timeout > UCHAR_MAX)
|
|
timeout = UCHAR_MAX;
|
|
}
|
|
else if (!strcasecmp(name, "Read"))
|
|
{
|
|
if (strchr("Yy", *value))
|
|
cfig.fileRead = true;
|
|
else
|
|
cfig.fileRead = false;
|
|
}
|
|
else if (!strcasecmp(name, "Write"))
|
|
{
|
|
if (strchr("Yy", *value))
|
|
cfig.fileWrite = true;
|
|
else
|
|
cfig.fileWrite = false;
|
|
}
|
|
else if (!strcasecmp(name, "Overwrite"))
|
|
{
|
|
if (strchr("Yy", *value))
|
|
cfig.fileOverwrite = true;
|
|
else
|
|
cfig.fileOverwrite = false;
|
|
}
|
|
else if (!strcasecmp(name, "port-range"))
|
|
{
|
|
char *ptr = strchr(value, '-');
|
|
if (ptr)
|
|
{
|
|
*ptr = 0;
|
|
cfig.minport = atol(value);
|
|
cfig.maxport = atol(++ptr);
|
|
|
|
if (cfig.minport < 1024 || cfig.minport >= USHRT_MAX || cfig.maxport < 1024 || cfig.maxport >= USHRT_MAX || cfig.minport > cfig.maxport)
|
|
{
|
|
cfig.minport = 0;
|
|
cfig.maxport = 0;
|
|
|
|
sprintf(logBuff, "Invalid port range %s", value);
|
|
logMess(logBuff, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "Invalid port range %s", value);
|
|
logMess(logBuff, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "Warning: unknown option %s, ignored", name);
|
|
logMess(logBuff, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((f = openSection("ALLOWED-CLIENTS", 1, iniFile)))
|
|
{
|
|
#ifdef __REACTOS__
|
|
MYWORD i = 0;
|
|
#else
|
|
int i = 0;
|
|
#endif
|
|
|
|
while (readSection(raw, f))
|
|
{
|
|
#ifdef __REACTOS__
|
|
if (i < _countof(cfig.hostRanges))
|
|
#else
|
|
if (i < 32)
|
|
#endif
|
|
{
|
|
MYDWORD rs = 0;
|
|
MYDWORD re = 0;
|
|
mySplit(name, value, raw, '-');
|
|
rs = htonl(my_inet_addr(name));
|
|
|
|
if (strlen(value))
|
|
re = htonl(my_inet_addr(value));
|
|
else
|
|
re = rs;
|
|
|
|
if (rs && rs != INADDR_NONE && re && re != INADDR_NONE && rs <= re)
|
|
{
|
|
cfig.hostRanges[i].rangeStart = rs;
|
|
cfig.hostRanges[i].rangeEnd = re;
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "Section [ALLOWED-CLIENTS] Invalid entry %s in ini file, ignored", raw);
|
|
logMess(logBuff, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (verbatim)
|
|
{
|
|
printf("starting TFTP...\n");
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "starting TFTP service");
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
for (int i = 0; i < MAX_SERVERS; i++)
|
|
if (cfig.homes[i].target[0])
|
|
{
|
|
sprintf(logBuff, "alias /%s is mapped to %s", cfig.homes[i].alias, cfig.homes[i].target);
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
if (cfig.hostRanges[0].rangeStart)
|
|
{
|
|
char temp[128];
|
|
|
|
#ifdef __REACTOS__
|
|
for (MYWORD i = 0; i < _countof(cfig.hostRanges) && cfig.hostRanges[i].rangeStart; i++)
|
|
#else
|
|
for (MYWORD i = 0; i <= sizeof(cfig.hostRanges) && cfig.hostRanges[i].rangeStart; i++)
|
|
#endif
|
|
{
|
|
sprintf(logBuff, "%s", "permitted clients: ");
|
|
sprintf(temp, "%s-", IP2String(tempbuff, htonl(cfig.hostRanges[i].rangeStart)));
|
|
strcat(logBuff, temp);
|
|
sprintf(temp, "%s", IP2String(tempbuff, htonl(cfig.hostRanges[i].rangeEnd)));
|
|
strcat(logBuff, temp);
|
|
logMess(logBuff, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "%s", "permitted clients: all");
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
if (cfig.minport)
|
|
{
|
|
sprintf(logBuff, "server port range: %u-%u", cfig.minport, cfig.maxport);
|
|
logMess(logBuff, 1);
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "server port range: all");
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
sprintf(logBuff, "max blksize: %u", blksize);
|
|
logMess(logBuff, 1);
|
|
sprintf(logBuff, "default blksize: %u", 512);
|
|
logMess(logBuff, 1);
|
|
sprintf(logBuff, "default timeout: %u", timeout);
|
|
logMess(logBuff, 1);
|
|
sprintf(logBuff, "file read allowed: %s", cfig.fileRead ? "Yes" : "No");
|
|
logMess(logBuff, 1);
|
|
sprintf(logBuff, "file create allowed: %s", cfig.fileWrite ? "Yes" : "No");
|
|
logMess(logBuff, 1);
|
|
sprintf(logBuff, "file overwrite allowed: %s", cfig.fileOverwrite ? "Yes" : "No");
|
|
logMess(logBuff, 1);
|
|
|
|
if (!verbatim)
|
|
{
|
|
sprintf(logBuff, "logging: %s", cfig.logLevel > 1 ? "all" : "errors");
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
lEvent = CreateEvent(
|
|
NULL, // default security descriptor
|
|
FALSE, // ManualReset
|
|
TRUE, // Signalled
|
|
TEXT("AchalTFTServerLogEvent")); // object name
|
|
|
|
if (lEvent == NULL)
|
|
{
|
|
printf("CreateEvent error: %lu\n", GetLastError());
|
|
exit(-1);
|
|
}
|
|
else if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
|
{
|
|
sprintf(logBuff, "CreateEvent opened an existing Event\nServer May already be Running");
|
|
logMess(logBuff, 0);
|
|
exit(-1);
|
|
}
|
|
|
|
tEvent = CreateEvent(
|
|
NULL, // default security descriptor
|
|
FALSE, // ManualReset
|
|
FALSE, // Signalled
|
|
TEXT("AchalTFTServerThreadEvent")); // object name
|
|
|
|
if (tEvent == NULL)
|
|
{
|
|
printf("CreateEvent error: %lu\n", GetLastError());
|
|
exit(-1);
|
|
}
|
|
else if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
|
{
|
|
sprintf(logBuff, "CreateEvent opened an existing Event\nServer May already be Running");
|
|
logMess(logBuff, 0);
|
|
exit(-1);
|
|
}
|
|
|
|
sEvent = CreateEvent(
|
|
NULL, // default security descriptor
|
|
FALSE, // ManualReset
|
|
TRUE, // Signalled
|
|
TEXT("AchalTFTServerSocketEvent")); // object name
|
|
|
|
if (sEvent == NULL)
|
|
{
|
|
printf("CreateEvent error: %lu\n", GetLastError());
|
|
exit(-1);
|
|
}
|
|
else if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
|
{
|
|
sprintf(logBuff, "CreateEvent opened an existing Event\nServer May already be Running");
|
|
logMess(logBuff, 0);
|
|
exit(-1);
|
|
}
|
|
|
|
cEvent = CreateEvent(
|
|
NULL, // default security descriptor
|
|
FALSE, // ManualReset
|
|
TRUE, // Signalled
|
|
TEXT("AchalTFTServerCountEvent")); // object name
|
|
|
|
if (cEvent == NULL)
|
|
{
|
|
printf("CreateEvent error: %lu\n", GetLastError());
|
|
exit(-1);
|
|
}
|
|
else if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
|
{
|
|
sprintf(logBuff, "CreateEvent opened an existing Event\nServer May already be Running");
|
|
logMess(logBuff, 0);
|
|
exit(-1);
|
|
}
|
|
|
|
if (minThreads)
|
|
{
|
|
for (int i = 0; i < minThreads; i++)
|
|
{
|
|
_beginthread(
|
|
processRequest, // thread function
|
|
0, // default security attributes
|
|
NULL); // argument to thread function
|
|
}
|
|
|
|
sprintf(logBuff, "thread pool size: %u", minThreads);
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
for (int i = 0; i < MAX_SERVERS && network.tftpConn[i].port; i++)
|
|
{
|
|
sprintf(logBuff, "listening on: %s:%i", IP2String(tempbuff, network.tftpConn[i].server), network.tftpConn[i].port);
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
do
|
|
{
|
|
memset(&newNetwork, 0, sizeof(data1));
|
|
|
|
bool bindfailed = false;
|
|
|
|
if ((f = openSection("LISTEN-ON", 1, iniFile)))
|
|
{
|
|
MYBYTE i = 0;
|
|
|
|
while (readSection(raw, f))
|
|
{
|
|
MYWORD port = 69;
|
|
|
|
cfig.ifspecified = true;
|
|
mySplit(name, value, raw, ':');
|
|
|
|
if (value[0])
|
|
port = atoi(value);
|
|
|
|
if(i < MAX_SERVERS)
|
|
{
|
|
if (isIP(name))
|
|
{
|
|
MYDWORD addr = my_inet_addr(name);
|
|
|
|
if (!addr)
|
|
{
|
|
newNetwork.listenServers[0] = 0;
|
|
newNetwork.listenPorts[0] = port;
|
|
fclose(f);
|
|
break;
|
|
}
|
|
else if (!findServer(newNetwork.listenServers, addr))
|
|
{
|
|
newNetwork.listenServers[i] = addr;
|
|
newNetwork.listenPorts[i] = port;
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "Warning: Section [LISTEN-ON], Invalid Interface Address %s, ignored", raw);
|
|
logMess(logBuff, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!cfig.ifspecified)
|
|
{
|
|
sprintf(logBuff, "detecting Interfaces..");
|
|
logMess(logBuff, 1);
|
|
getInterfaces(&newNetwork);
|
|
|
|
for (MYBYTE n = 0; n < MAX_SERVERS && newNetwork.staticServers[n]; n++)
|
|
{
|
|
newNetwork.listenServers[n] = newNetwork.staticServers[n];
|
|
newNetwork.listenPorts[n] = 69;
|
|
}
|
|
}
|
|
|
|
MYBYTE i = 0;
|
|
|
|
for (int j = 0; j < MAX_SERVERS && newNetwork.listenPorts[j]; j++)
|
|
{
|
|
int k = 0;
|
|
|
|
for (; k < MAX_SERVERS && network.tftpConn[k].loaded; k++)
|
|
{
|
|
if (network.tftpConn[k].ready && network.tftpConn[k].server == newNetwork.listenServers[j] && network.tftpConn[k].port == newNetwork.listenPorts[j])
|
|
break;
|
|
}
|
|
|
|
if (network.tftpConn[k].ready && network.tftpConn[k].server == newNetwork.listenServers[j] && network.tftpConn[k].port == newNetwork.listenPorts[j])
|
|
{
|
|
memcpy(&(newNetwork.tftpConn[i]), &(network.tftpConn[k]), sizeof(tftpConnType));
|
|
|
|
if (newNetwork.maxFD < newNetwork.tftpConn[i].sock)
|
|
newNetwork.maxFD = newNetwork.tftpConn[i].sock;
|
|
|
|
network.tftpConn[k].ready = false;
|
|
//printf("%d, %s found\n", i, IP2String(tempbuff, newNetwork.tftpConn[i].server));
|
|
i++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
newNetwork.tftpConn[i].sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
|
|
if (newNetwork.tftpConn[i].sock == INVALID_SOCKET)
|
|
{
|
|
bindfailed = true;
|
|
sprintf(logBuff, "Failed to Create Socket");
|
|
logMess(logBuff, 1);
|
|
continue;
|
|
}
|
|
|
|
//printf("Socket %u\n", newNetwork.tftpConn[i].sock);
|
|
|
|
errno = 0;
|
|
newNetwork.tftpConn[i].addr.sin_family = AF_INET;
|
|
newNetwork.tftpConn[i].addr.sin_addr.s_addr = newNetwork.listenServers[j];
|
|
newNetwork.tftpConn[i].addr.sin_port = htons(newNetwork.listenPorts[j]);
|
|
int nRet = bind(newNetwork.tftpConn[i].sock, (sockaddr*)&newNetwork.tftpConn[i].addr, sizeof(struct sockaddr_in));
|
|
|
|
if (nRet == SOCKET_ERROR || errno)
|
|
{
|
|
bindfailed = true;
|
|
closesocket(newNetwork.tftpConn[i].sock);
|
|
sprintf(logBuff, "%s Port %i bind failed", IP2String(tempbuff, newNetwork.listenServers[j]), newNetwork.listenPorts[j]);
|
|
logMess(logBuff, 1);
|
|
continue;
|
|
}
|
|
|
|
newNetwork.tftpConn[i].loaded = true;
|
|
newNetwork.tftpConn[i].ready = true;
|
|
newNetwork.tftpConn[i].server = newNetwork.listenServers[j];
|
|
newNetwork.tftpConn[i].port = newNetwork.listenPorts[j];
|
|
|
|
//printf("%d, %s created\n", i, IP2String(tempbuff, newNetwork.tftpConn[i].server));
|
|
|
|
if (newNetwork.maxFD < newNetwork.tftpConn[i].sock)
|
|
newNetwork.maxFD = newNetwork.tftpConn[i].sock;
|
|
|
|
if (!newNetwork.listenServers[j])
|
|
break;
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (bindfailed)
|
|
cfig.failureCount++;
|
|
else
|
|
cfig.failureCount = 0;
|
|
|
|
closeConn();
|
|
memcpy(&network, &newNetwork, sizeof(data1));
|
|
|
|
//printf("%i %i %i\n", network.tftpConn[0].ready, network.dnsUdpConn[0].ready, network.dnsTcpConn[0].ready);
|
|
|
|
if (!network.tftpConn[0].ready)
|
|
{
|
|
sprintf(logBuff, "No Static Interface ready, Waiting...");
|
|
logMess(logBuff, 1);
|
|
continue;
|
|
}
|
|
|
|
for (int i = 0; i < MAX_SERVERS && network.tftpConn[i].loaded; i++)
|
|
{
|
|
sprintf(logBuff, "Listening On: %s:%d", IP2String(tempbuff, network.tftpConn[i].server), network.tftpConn[i].port);
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
network.ready = true;
|
|
|
|
} while (detectChange());
|
|
|
|
//printf("Exiting Init\n");
|
|
|
|
_endthread();
|
|
return;
|
|
}
|
|
|
|
bool detectChange()
|
|
{
|
|
if (!cfig.failureCount)
|
|
{
|
|
if (cfig.ifspecified)
|
|
return false;
|
|
}
|
|
|
|
MYDWORD eventWait = UINT_MAX;
|
|
|
|
if (cfig.failureCount)
|
|
#ifdef __REACTOS__
|
|
eventWait = 10000 * (1 << cfig.failureCount);
|
|
#else
|
|
eventWait = 10000 * pow(2, cfig.failureCount);
|
|
#endif
|
|
|
|
OVERLAPPED overlap;
|
|
MYDWORD ret;
|
|
HANDLE hand = NULL;
|
|
overlap.hEvent = WSACreateEvent();
|
|
|
|
ret = NotifyAddrChange(&hand, &overlap);
|
|
|
|
if (ret != NO_ERROR)
|
|
{
|
|
if (WSAGetLastError() != WSA_IO_PENDING)
|
|
{
|
|
printf("NotifyAddrChange error...%d\n", WSAGetLastError());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if ( WaitForSingleObject(overlap.hEvent, eventWait) == WAIT_OBJECT_0 )
|
|
WSACloseEvent(overlap.hEvent);
|
|
|
|
network.ready = false;
|
|
|
|
while (network.busy)
|
|
Sleep(1000);
|
|
|
|
if (cfig.failureCount)
|
|
{
|
|
sprintf(logBuff, "Retrying failed Listening Interfaces..");
|
|
logMess(logBuff, 1);
|
|
}
|
|
else
|
|
{
|
|
sprintf(logBuff, "Network changed, re-detecting Interfaces..");
|
|
logMess(logBuff, 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
void getInterfaces(data1 *network)
|
|
{
|
|
memset(network, 0, sizeof(data1));
|
|
|
|
SOCKET sd = WSASocket(PF_INET, SOCK_DGRAM, 0, 0, 0, 0);
|
|
|
|
if (sd == INVALID_SOCKET)
|
|
return;
|
|
|
|
INTERFACE_INFO InterfaceList[MAX_SERVERS];
|
|
unsigned long nBytesReturned;
|
|
|
|
if (WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList,
|
|
sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR)
|
|
return ;
|
|
|
|
int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
|
|
|
|
for (int i = 0; i < nNumInterfaces; ++i)
|
|
{
|
|
sockaddr_in *pAddress = (sockaddr_in*)&(InterfaceList[i].iiAddress);
|
|
u_long nFlags = InterfaceList[i].iiFlags;
|
|
|
|
if (!(nFlags & IFF_POINTTOPOINT))
|
|
{
|
|
//printf("%s\n", IP2String(tempbuff, pAddress->sin_addr.S_un.S_addr));
|
|
addServer(network->staticServers, pAddress->sin_addr.s_addr);
|
|
}
|
|
}
|
|
|
|
closesocket(sd);
|
|
}
|
|
*/
|
|
|
|
|
|
void getInterfaces(data1 *network)
|
|
{
|
|
memset(network, 0, sizeof(data1));
|
|
|
|
SOCKET sd = WSASocket(PF_INET, SOCK_DGRAM, 0, 0, 0, 0);
|
|
|
|
if (sd == INVALID_SOCKET)
|
|
return;
|
|
|
|
INTERFACE_INFO InterfaceList[MAX_SERVERS];
|
|
unsigned long nBytesReturned;
|
|
|
|
if (WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList,
|
|
sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR)
|
|
return ;
|
|
|
|
int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
|
|
|
|
for (int i = 0; i < nNumInterfaces; ++i)
|
|
{
|
|
sockaddr_in *pAddress = (sockaddr_in*)&(InterfaceList[i].iiAddress);
|
|
u_long nFlags = InterfaceList[i].iiFlags;
|
|
|
|
if (pAddress->sin_addr.s_addr)
|
|
{
|
|
addServer(network->allServers, pAddress->sin_addr.s_addr);
|
|
|
|
if (!(nFlags & IFF_POINTTOPOINT) && (nFlags & IFF_UP))
|
|
{
|
|
addServer(network->staticServers, pAddress->sin_addr.s_addr);
|
|
}
|
|
}
|
|
}
|
|
|
|
closesocket(sd);
|
|
}
|
|
|
|
|
|
bool addServer(MYDWORD *array, MYDWORD ip)
|
|
{
|
|
for (MYBYTE i = 0; i < MAX_SERVERS; i++)
|
|
{
|
|
if (!ip || array[i] == ip)
|
|
return 0;
|
|
else if (!array[i])
|
|
{
|
|
array[i] = ip;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
MYDWORD *findServer(MYDWORD *array, MYDWORD ip)
|
|
{
|
|
if (ip)
|
|
{
|
|
for (MYBYTE i = 0; i < MAX_SERVERS && array[i]; i++)
|
|
{
|
|
if (array[i] == ip)
|
|
return &(array[i]);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void logMess(char *logBuff, MYBYTE logLevel)
|
|
{
|
|
WaitForSingleObject(lEvent, INFINITE);
|
|
|
|
if (verbatim)
|
|
printf("%s\n", logBuff);
|
|
else if (cfig.logfile && logLevel <= cfig.logLevel)
|
|
{
|
|
time_t t = time(NULL);
|
|
tm *ttm = localtime(&t);
|
|
|
|
if (ttm->tm_yday != loggingDay)
|
|
{
|
|
loggingDay = ttm->tm_yday;
|
|
strftime(extbuff, sizeof(extbuff), logFile, ttm);
|
|
fprintf(cfig.logfile, "Logging Continued on file %s\n", extbuff);
|
|
fclose(cfig.logfile);
|
|
cfig.logfile = fopen(extbuff, "at");
|
|
|
|
if (cfig.logfile)
|
|
{
|
|
fprintf(cfig.logfile, "%s\n\n", sVersion);
|
|
WritePrivateProfileString("InternetShortcut","URL", extbuff, lnkFile);
|
|
WritePrivateProfileString("InternetShortcut","IconIndex", "0", lnkFile);
|
|
WritePrivateProfileString("InternetShortcut","IconFile", extbuff, lnkFile);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
strftime(extbuff, sizeof(extbuff), "%d-%b-%y %X", ttm);
|
|
fprintf(cfig.logfile, "[%s] %s\n", extbuff, logBuff);
|
|
fflush(cfig.logfile);
|
|
}
|
|
SetEvent(lEvent);
|
|
}
|
|
|
|
void logMess(request *req, MYBYTE logLevel)
|
|
{
|
|
WaitForSingleObject(lEvent, INFINITE);
|
|
|
|
char tempbuff[256];
|
|
|
|
if (verbatim)
|
|
{
|
|
if (!req->serverError.errormessage[0])
|
|
sprintf(req->serverError.errormessage, strerror(errno));
|
|
|
|
if (req->path[0])
|
|
printf("Client %s:%u %s, %s\n", IP2String(tempbuff, req->client.sin_addr.s_addr), ntohs(req->client.sin_port), req->path, req->serverError.errormessage);
|
|
else
|
|
printf("Client %s:%u, %s\n", IP2String(tempbuff, req->client.sin_addr.s_addr), ntohs(req->client.sin_port), req->serverError.errormessage);
|
|
}
|
|
else if (cfig.logfile && logLevel <= cfig.logLevel)
|
|
{
|
|
time_t t = time(NULL);
|
|
tm *ttm = localtime(&t);
|
|
|
|
if (ttm->tm_yday != loggingDay)
|
|
{
|
|
loggingDay = ttm->tm_yday;
|
|
strftime(extbuff, sizeof(extbuff), logFile, ttm);
|
|
fprintf(cfig.logfile, "Logging Continued on file %s\n", extbuff);
|
|
fclose(cfig.logfile);
|
|
cfig.logfile = fopen(extbuff, "at");
|
|
|
|
if (cfig.logfile)
|
|
{
|
|
fprintf(cfig.logfile, "%s\n\n", sVersion);
|
|
WritePrivateProfileString("InternetShortcut","URL", extbuff, lnkFile);
|
|
WritePrivateProfileString("InternetShortcut","IconIndex", "0", lnkFile);
|
|
WritePrivateProfileString("InternetShortcut","IconFile", extbuff, lnkFile);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
strftime(extbuff, sizeof(extbuff), "%d-%b-%y %X", ttm);
|
|
|
|
if (req->path[0])
|
|
fprintf(cfig.logfile, "[%s] Client %s:%u %s, %s\n", extbuff, IP2String(tempbuff, req->client.sin_addr.s_addr), ntohs(req->client.sin_port), req->path, req->serverError.errormessage);
|
|
else
|
|
fprintf(cfig.logfile, "[%s] Client %s:%u, %s\n", extbuff, IP2String(tempbuff, req->client.sin_addr.s_addr), ntohs(req->client.sin_port), req->serverError.errormessage);
|
|
|
|
fflush(cfig.logfile);
|
|
}
|
|
SetEvent(lEvent);
|
|
}
|