2478 lines
56 KiB
C
2478 lines
56 KiB
C
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Simpsvc.c
|
||
|
||
Abstract:
|
||
|
||
Supports several simple TCP/IP services in a single thread: TCP Echo,
|
||
UDP Echo, Daytime, Null, Chargen.
|
||
|
||
Author:
|
||
|
||
David Treadwell (davidtr) 3-Mar-1993
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "simptcp.h"
|
||
|
||
#define MAX_UDP_CHARGEN_RESPONSE 7030
|
||
#define MAX_DATE_BUFFER_SIZE 2000
|
||
|
||
// Number of services, counting tcp and udp versions as separate
|
||
#define NUM_SERVICES 10
|
||
|
||
typedef struct _FAMILY {
|
||
short family;
|
||
SOCKET tcpEcho;
|
||
SOCKET udpEcho;
|
||
SOCKET tcpDaytime;
|
||
SOCKET udpDaytime;
|
||
SOCKET tcpDiscard;
|
||
SOCKET udpDiscard;
|
||
SOCKET tcpChargen;
|
||
SOCKET udpChargen;
|
||
SOCKET tcpQotd;
|
||
SOCKET udpQotd;
|
||
} FAMILY;
|
||
|
||
FAMILY family[] = {
|
||
{ AF_INET, INVALID_SOCKET,INVALID_SOCKET,INVALID_SOCKET,INVALID_SOCKET,
|
||
INVALID_SOCKET,INVALID_SOCKET,INVALID_SOCKET,INVALID_SOCKET,
|
||
INVALID_SOCKET,INVALID_SOCKET },
|
||
|
||
{ AF_INET6, INVALID_SOCKET,INVALID_SOCKET,INVALID_SOCKET,INVALID_SOCKET,
|
||
INVALID_SOCKET,INVALID_SOCKET,INVALID_SOCKET,INVALID_SOCKET,
|
||
INVALID_SOCKET,INVALID_SOCKET },
|
||
};
|
||
|
||
#define NUM_FAMILIES (sizeof(family) / sizeof(FAMILY))
|
||
|
||
DWORD IoBufferSize = 4096;
|
||
PCHAR IoBuffer = NULL;
|
||
|
||
WSADATA WsaData;
|
||
|
||
RTL_CRITICAL_SECTION CriticalSection;
|
||
BOOL InitializedCriticalSection = FALSE;
|
||
|
||
typedef struct _TCP_CLIENT_INFO {
|
||
SOCKET SocketHandle;
|
||
SOCKADDR_STORAGE RemoteAddress;
|
||
INT RemoteAddressLen;
|
||
HANDLE ThreadHandle;
|
||
SHORT ServicePort;
|
||
} TCP_CLIENT_INFO, *PTCP_CLIENT_INFO;
|
||
|
||
#define MAX_TCP_CLIENTS 1000
|
||
PTCP_CLIENT_INFO TcpClients = NULL;
|
||
|
||
#define LISTEN_BACKLOG 5
|
||
|
||
#define MAX_IDLE_TICKS 10 * 60 * 1000 // 10 minutes
|
||
#define SELECT_TIMEOUT 5 * 60 // 5 minutes
|
||
|
||
DWORD MaxTcpClients = MAX_TCP_CLIENTS;
|
||
DWORD MaxIdleTicks = MAX_IDLE_TICKS;
|
||
DWORD SelectTimeout = SELECT_TIMEOUT;
|
||
|
||
HMODULE SimptcpModuleHandle=NULL;
|
||
|
||
PFD_SET ReadfdsStore, Readfds;
|
||
|
||
SHORT TcpEchoPort;
|
||
SHORT UdpEchoPort;
|
||
SHORT TcpDiscardPort;
|
||
SHORT UdpDiscardPort;
|
||
SHORT TcpChargenPort;
|
||
SHORT UdpChargenPort;
|
||
SHORT TcpDaytimePort;
|
||
SHORT UdpDaytimePort;
|
||
SHORT TcpQotdPort;
|
||
SHORT UdpQotdPort;
|
||
|
||
#define INVALID_PORT 0
|
||
|
||
BOOL DoTcpEcho = TRUE;
|
||
BOOL DoUdpEcho = TRUE;
|
||
BOOL DoTcpDiscard = TRUE;
|
||
BOOL DoUdpDiscard = TRUE;
|
||
BOOL DoTcpChargen = TRUE;
|
||
BOOL DoUdpChargen = TRUE;
|
||
BOOL DoTcpDaytime = TRUE;
|
||
BOOL DoUdpDaytime = TRUE;
|
||
BOOL DoTcpQotd = TRUE;
|
||
BOOL DoUdpQotd = TRUE;
|
||
|
||
struct {
|
||
PBOOL Boolean;
|
||
PWSTR ValueName;
|
||
} RegistryBooleans[] = {
|
||
&DoTcpEcho, L"EnableTcpEcho",
|
||
&DoUdpEcho, L"EnableUdpEcho",
|
||
&DoTcpDiscard, L"EnableTcpDiscard",
|
||
&DoUdpDiscard, L"EnableUdpDiscard",
|
||
&DoTcpChargen, L"EnableTcpChargen",
|
||
&DoUdpChargen, L"EnableUdpChargen",
|
||
&DoTcpDaytime, L"EnableTcpDaytime",
|
||
&DoUdpDaytime, L"EnableUdpDaytime",
|
||
&DoTcpQotd, L"EnableTcpQotd",
|
||
&DoUdpQotd, L"EnableUdpQotd",
|
||
NULL, NULL
|
||
};
|
||
|
||
struct {
|
||
PDWORD Dword;
|
||
PWSTR ValueName;
|
||
} RegistryDwords[] = {
|
||
&MaxTcpClients, L"MaxTcpClients",
|
||
&MaxIdleTicks, L"MaxIdleTicks",
|
||
&SelectTimeout, L"SelectTimeout",
|
||
&IoBufferSize, L"IoBufferSize",
|
||
NULL, NULL
|
||
};
|
||
|
||
SERVICE_STATUS SimpServiceStatus;
|
||
SERVICE_STATUS_HANDLE SimpServiceStatusHandle;
|
||
|
||
HANDLE SimpPauseEvent;
|
||
SOCKET SimpQuitSocket;
|
||
|
||
BOOL SimpServiceExit = FALSE;
|
||
|
||
PVOID ChargenBuffer = NULL;
|
||
DWORD ChargenBufferSize;
|
||
|
||
PVOID QotdBuffer = NULL;
|
||
DWORD QotdQuoteCount;
|
||
struct {
|
||
DWORD QuoteLength;
|
||
PCHAR Quote;
|
||
} *QotdStrings = NULL;
|
||
PWSTR QotdFileName = NULL;
|
||
HANDLE QotdFileHandle = NULL;
|
||
HANDLE QotdFileMapping = NULL;
|
||
|
||
VOID
|
||
AnnounceServiceStatus (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
ControlResponse(
|
||
DWORD opCode
|
||
);
|
||
|
||
VOID
|
||
AbortTcpClient (
|
||
IN SOCKET Socket
|
||
);
|
||
|
||
INT
|
||
AcceptTcpClient (
|
||
IN SOCKET ListenSocket,
|
||
IN SHORT Port
|
||
);
|
||
|
||
VOID
|
||
DeleteTcpClient (
|
||
IN DWORD ArraySlot,
|
||
IN BOOLEAN Graceful
|
||
);
|
||
|
||
VOID
|
||
DoSimpleServices (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
DoSingleClient (
|
||
IN SOCKET s,
|
||
IN USHORT port
|
||
);
|
||
|
||
VOID
|
||
FormatDaytimeResponse (
|
||
IN PCHAR Buffer,
|
||
IN PDWORD BufferLength
|
||
);
|
||
|
||
VOID
|
||
FormatQotdResponse (
|
||
IN PCHAR Buffer,
|
||
IN PDWORD BufferLength
|
||
);
|
||
|
||
SHORT
|
||
GetServicePort (
|
||
IN PCHAR Service,
|
||
IN PCHAR Protocol
|
||
);
|
||
|
||
INT
|
||
InitializeChargen (
|
||
VOID
|
||
);
|
||
|
||
INT
|
||
InitializeQotdQuotes (
|
||
VOID
|
||
);
|
||
|
||
INT
|
||
ReadRegistry (
|
||
VOID
|
||
);
|
||
|
||
BOOL
|
||
OpenTcpSocket (
|
||
OUT SOCKET *pSocket,
|
||
IN INT FamIdx,
|
||
IN SHORT Port
|
||
);
|
||
|
||
BOOL
|
||
OpenUdpSocket (
|
||
OUT SOCKET *pSocket,
|
||
IN INT FamIdx,
|
||
IN SHORT Port
|
||
);
|
||
|
||
INT
|
||
SimpInitializeEventLog (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
SimpTerminateEventLog(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
SimpLogEvent(
|
||
DWORD Message,
|
||
WORD SubStringCount,
|
||
CHAR *SubStrings[],
|
||
DWORD ErrorCode
|
||
);
|
||
|
||
DWORD
|
||
ThreadEntry (
|
||
LPVOID lpThreadParameter
|
||
);
|
||
|
||
INT
|
||
ProcessFamily(
|
||
IN INT FamIdx)
|
||
{
|
||
INT err=NO_ERROR;
|
||
INT i;
|
||
SOCKADDR_STORAGE remoteAddr;
|
||
INT remoteAddrLength;
|
||
u_long one = 1;
|
||
|
||
if ( family[FamIdx].tcpEcho != INVALID_SOCKET && FD_ISSET( family[FamIdx].tcpEcho, Readfds ) ) {
|
||
i = AcceptTcpClient( family[FamIdx].tcpEcho, TcpEchoPort );
|
||
}
|
||
|
||
if ( family[FamIdx].tcpDiscard != INVALID_SOCKET && FD_ISSET( family[FamIdx].tcpDiscard, Readfds ) ) {
|
||
i = AcceptTcpClient( family[FamIdx].tcpDiscard, TcpDiscardPort );
|
||
}
|
||
|
||
if ( family[FamIdx].tcpDaytime != INVALID_SOCKET && FD_ISSET( family[FamIdx].tcpDaytime, Readfds ) ) {
|
||
|
||
SOCKET acceptSocket;
|
||
DWORD length=IoBufferSize;
|
||
|
||
//
|
||
// A client is making a TCP daytime request. First accept
|
||
// the connection, then send the current time-of-day string
|
||
// to the client, then close the socket.
|
||
//
|
||
|
||
acceptSocket = accept( family[FamIdx].tcpDaytime, NULL, NULL );
|
||
|
||
if ( acceptSocket != INVALID_SOCKET ) {
|
||
FormatDaytimeResponse( IoBuffer, &length );
|
||
send( acceptSocket, IoBuffer, length, 0 );
|
||
err = closesocket( acceptSocket );
|
||
ASSERT( err != SOCKET_ERROR );
|
||
}
|
||
}
|
||
|
||
if ( family[FamIdx].tcpChargen != INVALID_SOCKET && FD_ISSET( family[FamIdx].tcpChargen, Readfds ) ) {
|
||
i = AcceptTcpClient( family[FamIdx].tcpChargen, TcpChargenPort );
|
||
if ( i != -1 ) {
|
||
one = 1;
|
||
err = ioctlsocket( TcpClients[i].SocketHandle, FIONBIO, &one );
|
||
if ( err == SOCKET_ERROR ) {
|
||
DeleteTcpClient( i, FALSE );
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( family[FamIdx].tcpQotd != INVALID_SOCKET && FD_ISSET( family[FamIdx].tcpQotd, Readfds ) ) {
|
||
|
||
SOCKET acceptSocket;
|
||
DWORD length;
|
||
|
||
//
|
||
// A client is making a TCP Qotd request. First accept
|
||
// the connection, then send the current time-of-day string
|
||
// to the client, then close the socket.
|
||
//
|
||
|
||
acceptSocket = accept( family[FamIdx].tcpQotd, NULL, NULL );
|
||
|
||
if ( acceptSocket != INVALID_SOCKET ) {
|
||
FormatQotdResponse( IoBuffer, &length );
|
||
send( acceptSocket, IoBuffer, length, 0 );
|
||
err = closesocket( acceptSocket );
|
||
ASSERT( err != SOCKET_ERROR );
|
||
}
|
||
}
|
||
|
||
// ================================================================
|
||
// Udp services.
|
||
|
||
if ( family[FamIdx].udpEcho != INVALID_SOCKET && FD_ISSET( family[FamIdx].udpEcho, Readfds ) ) {
|
||
|
||
remoteAddrLength = sizeof(remoteAddr);
|
||
|
||
err = recvfrom(
|
||
family[FamIdx].udpEcho,
|
||
IoBuffer,
|
||
IoBufferSize,
|
||
0,
|
||
(PSOCKADDR)&remoteAddr,
|
||
&remoteAddrLength
|
||
);
|
||
|
||
if( ntohs(SS_PORT(&remoteAddr)) > IPPORT_RESERVED
|
||
&& err != SOCKET_ERROR )
|
||
{
|
||
err = sendto(
|
||
family[FamIdx].udpEcho,
|
||
IoBuffer,
|
||
err,
|
||
0,
|
||
(PSOCKADDR)&remoteAddr,
|
||
remoteAddrLength
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( family[FamIdx].udpDiscard != INVALID_SOCKET && FD_ISSET( family[FamIdx].udpDiscard, Readfds ) ) {
|
||
err = recvfrom(
|
||
family[FamIdx].udpDiscard,
|
||
IoBuffer,
|
||
IoBufferSize,
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
ASSERT( err != SOCKET_ERROR );
|
||
|
||
// Nothing to sendto in this case.
|
||
}
|
||
|
||
if ( family[FamIdx].udpDaytime != INVALID_SOCKET && FD_ISSET( family[FamIdx].udpDaytime, Readfds ) ) {
|
||
|
||
DWORD length;
|
||
|
||
remoteAddrLength = sizeof(remoteAddr);
|
||
|
||
err = recvfrom(
|
||
family[FamIdx].udpDaytime,
|
||
IoBuffer,
|
||
IoBufferSize,
|
||
0,
|
||
(PSOCKADDR)&remoteAddr,
|
||
&remoteAddrLength
|
||
);
|
||
|
||
|
||
if( (ntohs(SS_PORT(&remoteAddr)) > IPPORT_RESERVED) && (err != SOCKET_ERROR) )
|
||
{
|
||
length=IoBufferSize;
|
||
FormatDaytimeResponse( IoBuffer, &length );
|
||
|
||
err = sendto(
|
||
family[FamIdx].udpDaytime,
|
||
IoBuffer,
|
||
length,
|
||
0,
|
||
(PSOCKADDR)&remoteAddr,
|
||
remoteAddrLength
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( family[FamIdx].udpChargen != INVALID_SOCKET && FD_ISSET( family[FamIdx].udpChargen, Readfds ) ) {
|
||
|
||
DWORD length;
|
||
|
||
remoteAddrLength = sizeof(remoteAddr);
|
||
|
||
err = recvfrom(
|
||
family[FamIdx].udpChargen,
|
||
IoBuffer,
|
||
IoBufferSize,
|
||
0,
|
||
(PSOCKADDR)&remoteAddr,
|
||
&remoteAddrLength
|
||
);
|
||
|
||
|
||
// Infinite loop attack, when we get a request from
|
||
// another service. - MohsinA, 30-Jun-97.
|
||
|
||
if( (ntohs(SS_PORT(&remoteAddr)) > IPPORT_RESERVED) && (err != SOCKET_ERROR) )
|
||
{
|
||
srand( GetTickCount( ) );
|
||
|
||
length = (rand( ) * MAX_UDP_CHARGEN_RESPONSE) / RAND_MAX;
|
||
if (length > ChargenBufferSize) {
|
||
length=ChargenBufferSize;
|
||
}
|
||
err = sendto(
|
||
family[FamIdx].udpChargen,
|
||
ChargenBuffer,
|
||
length,
|
||
0,
|
||
(PSOCKADDR)&remoteAddr,
|
||
remoteAddrLength
|
||
);
|
||
|
||
}
|
||
}
|
||
|
||
if ( family[FamIdx].udpQotd != INVALID_SOCKET && FD_ISSET( family[FamIdx].udpQotd, Readfds ) ) {
|
||
|
||
DWORD length;
|
||
|
||
remoteAddrLength = sizeof(remoteAddr);
|
||
|
||
err = recvfrom(
|
||
family[FamIdx].udpQotd,
|
||
IoBuffer,
|
||
IoBufferSize,
|
||
0,
|
||
(PSOCKADDR)&remoteAddr,
|
||
&remoteAddrLength
|
||
);
|
||
|
||
if( (ntohs(SS_PORT(&remoteAddr)) > IPPORT_RESERVED) && (err != SOCKET_ERROR) )
|
||
{
|
||
FormatQotdResponse( IoBuffer, &length );
|
||
|
||
err = sendto(
|
||
family[FamIdx].udpQotd,
|
||
IoBuffer,
|
||
length,
|
||
0,
|
||
(PSOCKADDR)&remoteAddr,
|
||
remoteAddrLength
|
||
);
|
||
}
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
VOID
|
||
ServiceEntry (
|
||
IN DWORD argc,
|
||
IN LPWSTR argv[],
|
||
IN PTCPSVCS_GLOBAL_DATA pGlobalData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the "main" routine for the simple TCP/IP services. The
|
||
containing process will call this routine when we're supposed to
|
||
start up.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
INT err=ERROR_GEN_FAILURE;
|
||
TIMEVAL timeout;
|
||
INT i, FamIdx;
|
||
DWORD maxFdSetSize;
|
||
NTSTATUS status;
|
||
BOOL bOk;
|
||
|
||
//
|
||
// Initialize all the status fields so that subsequent calls to
|
||
// SetServiceStatus need to only update fields that changed.
|
||
//
|
||
|
||
SimpServiceStatus.dwServiceType = SERVICE_WIN32;
|
||
SimpServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
||
SimpServiceStatus.dwControlsAccepted = 0;
|
||
SimpServiceStatus.dwCheckPoint = 1;
|
||
SimpServiceStatus.dwWaitHint = 30000; // 30 seconds
|
||
|
||
SimpServiceStatus.dwWin32ExitCode = NO_ERROR;
|
||
SimpServiceStatus.dwServiceSpecificExitCode = NO_ERROR;
|
||
|
||
//
|
||
// Initialize server to receive service requests by registering the
|
||
// control handler.
|
||
//
|
||
|
||
SimpServiceStatusHandle = RegisterServiceCtrlHandler(
|
||
TEXT("SimpTcp"),
|
||
ControlResponse
|
||
);
|
||
|
||
if ( SimpServiceStatusHandle == 0 ) {
|
||
err = GetLastError();
|
||
goto exit;
|
||
}
|
||
|
||
AnnounceServiceStatus( );
|
||
|
||
//
|
||
// Initialize our critical section.
|
||
//
|
||
|
||
status = RtlInitializeCriticalSection( &CriticalSection );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
goto exit;
|
||
}
|
||
|
||
InitializedCriticalSection = TRUE;
|
||
|
||
//
|
||
// Initialize the eventlog.
|
||
//
|
||
|
||
err = SimpInitializeEventLog( );
|
||
ASSERT( err == NO_ERROR );
|
||
|
||
//
|
||
// Read all registry information.
|
||
//
|
||
|
||
err = ReadRegistry( );
|
||
if ( err != NO_ERROR ) {
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Allocate memory for the IO buffer.
|
||
//
|
||
|
||
IoBuffer = RtlAllocateHeap( RtlProcessHeap( ), 0, IoBufferSize );
|
||
|
||
//
|
||
// Allocate memory for the array of TCP clients.
|
||
//
|
||
|
||
TcpClients = RtlAllocateHeap(
|
||
RtlProcessHeap( ),
|
||
0,
|
||
MaxTcpClients * sizeof(TcpClients[0])
|
||
);
|
||
if ( TcpClients == NULL ) {
|
||
err = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Initialize the chargen data buffer.
|
||
//
|
||
|
||
if ( DoTcpChargen || DoUdpChargen ) {
|
||
err = InitializeChargen( );
|
||
if ( err != NO_ERROR ) {
|
||
DoUdpChargen = FALSE;
|
||
DoTcpChargen = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Initialize the quote of the day quotes.
|
||
//
|
||
|
||
if ( DoTcpQotd || DoUdpQotd ) {
|
||
err = InitializeQotdQuotes( );
|
||
if ( err != NO_ERROR ) {
|
||
DoUdpQotd = FALSE;
|
||
DoTcpQotd = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Initialize client socket array.
|
||
//
|
||
|
||
for ( i = 0; (DWORD)i < MaxTcpClients; i++ ) {
|
||
TcpClients[i].SocketHandle = INVALID_SOCKET;
|
||
TcpClients[i].ThreadHandle = NULL;
|
||
}
|
||
|
||
//
|
||
// Determine how large our FD_SET structures must be, then allocate
|
||
// space for them. We have 1 quit socket, plus 10 services * 2 families.
|
||
//
|
||
|
||
maxFdSetSize = sizeof(u_int) + ( (1 + NUM_FAMILIES * NUM_SERVICES)
|
||
* sizeof(SOCKET) );
|
||
|
||
|
||
Readfds = RtlAllocateHeap( RtlProcessHeap( ), 0, maxFdSetSize );
|
||
ReadfdsStore = RtlAllocateHeap( RtlProcessHeap( ), 0, maxFdSetSize );
|
||
|
||
if ( Readfds == NULL || ReadfdsStore == NULL ) {
|
||
err = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Initialize the pause event. We use this event to stop activity
|
||
// when the service is paused.
|
||
//
|
||
|
||
SimpPauseEvent = CreateEvent( NULL, TRUE, TRUE, NULL );
|
||
if ( SimpPauseEvent == NULL ) {
|
||
err = GetLastError( );
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Initialize the Windows Sockets DLL.
|
||
//
|
||
|
||
err = WSAStartup( 0x0101, &WsaData );
|
||
if ( err == SOCKET_ERROR ) {
|
||
err = GetLastError( );
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Initialize the FD sets we'll use.
|
||
//
|
||
|
||
FD_ZERO( ReadfdsStore );
|
||
|
||
//
|
||
// Open the "quit" socket. We close this socket when we need to
|
||
// shut down in order to wake up the main thread from it's select()
|
||
// and begin shutdown.
|
||
//
|
||
|
||
SimpQuitSocket = socket( AF_INET, SOCK_DGRAM, 0 );
|
||
if ( SimpQuitSocket != INVALID_SOCKET ) {
|
||
FD_SET( SimpQuitSocket, ReadfdsStore );
|
||
} else {
|
||
err = GetLastError( );
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// First find the port numbers for all our services.
|
||
//
|
||
|
||
TcpEchoPort = GetServicePort( "echo", "tcp" );
|
||
if ( TcpEchoPort == INVALID_PORT && DoTcpEcho ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_TCP_ECHO_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoTcpEcho = FALSE;
|
||
}
|
||
|
||
UdpEchoPort = GetServicePort( "echo", "udp" );
|
||
if ( UdpEchoPort == INVALID_PORT && DoUdpEcho ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_UDP_ECHO_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoUdpEcho = FALSE;
|
||
}
|
||
|
||
TcpDiscardPort = GetServicePort( "discard", "tcp" );
|
||
if ( TcpDiscardPort == INVALID_PORT && DoTcpDiscard ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_TCP_DISCARD_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoTcpDiscard = FALSE;
|
||
}
|
||
|
||
UdpDiscardPort = GetServicePort( "discard", "udp" );
|
||
if ( UdpDiscardPort == INVALID_PORT && DoUdpDiscard ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_UDP_DISCARD_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoUdpDiscard = FALSE;
|
||
}
|
||
|
||
TcpDaytimePort = GetServicePort( "daytime", "tcp" );
|
||
if ( TcpDaytimePort == INVALID_PORT && DoTcpDaytime ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_TCP_DAYTIME_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoTcpDaytime = FALSE;
|
||
}
|
||
|
||
UdpDaytimePort = GetServicePort( "daytime", "udp" );
|
||
if ( UdpDaytimePort == INVALID_PORT && DoUdpDaytime ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_UDP_DAYTIME_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoUdpDaytime = FALSE;
|
||
}
|
||
|
||
TcpChargenPort = GetServicePort( "chargen", "tcp" );
|
||
if ( TcpChargenPort == INVALID_PORT && DoTcpChargen ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_TCP_CHARGEN_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoTcpChargen = FALSE;
|
||
}
|
||
|
||
UdpChargenPort = GetServicePort( "chargen", "udp" );
|
||
if ( UdpChargenPort == INVALID_PORT && DoUdpChargen ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_UDP_CHARGEN_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoUdpChargen = FALSE;
|
||
}
|
||
|
||
TcpQotdPort = GetServicePort( "qotd", "tcp" );
|
||
if ( TcpQotdPort == INVALID_PORT && DoTcpQotd ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_TCP_QOTD_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoTcpQotd = FALSE;
|
||
}
|
||
|
||
UdpQotdPort = GetServicePort( "qotd", "udp" );
|
||
if ( UdpQotdPort == INVALID_PORT && DoUdpQotd ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_FIND_UDP_QOTD_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
|
||
DoUdpQotd = FALSE;
|
||
}
|
||
|
||
//
|
||
// Open, bind, and listen on the necessary ports.
|
||
//
|
||
|
||
if ( DoTcpEcho ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenTcpSocket( &family[i].tcpEcho, i, TcpEchoPort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_TCP_ECHO_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( DoUdpEcho ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenUdpSocket( &family[i].udpEcho, i, UdpEchoPort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_UDP_ECHO_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( DoTcpDiscard ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenTcpSocket( &family[i].tcpDiscard, i, TcpDiscardPort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_TCP_DISCARD_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( DoUdpDiscard ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenUdpSocket( &family[i].udpDiscard, i, UdpDiscardPort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_UDP_DISCARD_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( DoTcpDaytime ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenTcpSocket( &family[i].tcpDaytime, i, TcpDaytimePort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_TCP_DAYTIME_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( DoUdpDaytime ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenUdpSocket( &family[i].udpDaytime, i, UdpDaytimePort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_UDP_DAYTIME_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( DoTcpChargen ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenTcpSocket( &family[i].tcpChargen, i, TcpChargenPort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_TCP_CHARGEN_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( DoUdpChargen ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenUdpSocket( &family[i].udpChargen, i, UdpChargenPort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_UDP_CHARGEN_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( DoTcpQotd ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenTcpSocket( &family[i].tcpQotd, i, TcpQotdPort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_TCP_QOTD_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
if ( DoUdpQotd ) {
|
||
bOk = FALSE;
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
bOk |= OpenUdpSocket( &family[i].udpQotd, i, UdpQotdPort );
|
||
}
|
||
if ( !bOk ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_UDP_QOTD_PORT,
|
||
0,
|
||
NULL,
|
||
WSAGetLastError( )
|
||
);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Announce that we have successfully started.
|
||
//
|
||
|
||
SimpServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
||
SimpServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
||
SERVICE_ACCEPT_PAUSE_CONTINUE;
|
||
SimpServiceStatus.dwCheckPoint = 0;
|
||
SimpServiceStatus.dwWaitHint = 0;
|
||
|
||
SimptcpModuleHandle=GetModuleHandle("simptcp.dll");
|
||
|
||
AnnounceServiceStatus( );
|
||
|
||
//
|
||
// Loop waiting for connect attempts or datagrams, and service them
|
||
// when they arrive.
|
||
//
|
||
|
||
while ( TRUE ) {
|
||
|
||
//
|
||
// First initialize the FD sets we'll actually use for select().
|
||
//
|
||
|
||
RtlCopyMemory( Readfds, ReadfdsStore, maxFdSetSize );
|
||
|
||
//
|
||
// Now wait for something to happen. Timeout occaisonally
|
||
// so that we can kill idle TCP clients.
|
||
//
|
||
|
||
timeout.tv_sec = SelectTimeout;
|
||
timeout.tv_usec = 0;
|
||
|
||
err = select( 0, Readfds, NULL, NULL, &timeout );
|
||
|
||
//
|
||
// If the service is shutting down, stop processing requests
|
||
// and exit.
|
||
//
|
||
|
||
if ( SimpServiceExit ) {
|
||
err = NO_ERROR;
|
||
goto exit;
|
||
}
|
||
|
||
if ( err == SOCKET_ERROR ) {
|
||
|
||
//
|
||
// This is bad. We should do something intelligent here.
|
||
//
|
||
int MappedErr;
|
||
MappedErr= WSAGetLastError();
|
||
switch(MappedErr) {
|
||
case WSAENOBUFS:
|
||
Sleep(1000);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// If the service is paused, wait for it to become unpaused.
|
||
//
|
||
|
||
err = WaitForSingleObject( SimpPauseEvent, INFINITE );
|
||
ASSERT( err != WAIT_FAILED );
|
||
|
||
//
|
||
// Figure out what happened and act accordingly.
|
||
//
|
||
for (FamIdx=0; FamIdx<NUM_FAMILIES; FamIdx++)
|
||
{
|
||
err = ProcessFamily(FamIdx);
|
||
}
|
||
|
||
|
||
} // infinite loop.
|
||
|
||
exit:
|
||
|
||
//
|
||
// Announce that we're going down.
|
||
//
|
||
|
||
SimpServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
SimpServiceStatus.dwCheckPoint = 1;
|
||
SimpServiceStatus.dwWaitHint = 20000; // 20 seconds
|
||
|
||
SimpServiceStatus.dwWin32ExitCode = err;
|
||
SimpServiceStatus.dwServiceSpecificExitCode = err;
|
||
|
||
AnnounceServiceStatus( );
|
||
|
||
//
|
||
// Delete our critical section.
|
||
//
|
||
|
||
if ( InitializedCriticalSection ) {
|
||
InitializedCriticalSection = FALSE;
|
||
RtlDeleteCriticalSection( &CriticalSection );
|
||
}
|
||
|
||
//
|
||
// Close all opened listening sockets.
|
||
//
|
||
|
||
for (i=0; i<NUM_FAMILIES; i++) {
|
||
if ( family[i].tcpEcho != INVALID_SOCKET ) {
|
||
closesocket( family[i].tcpEcho );
|
||
}
|
||
if ( family[i].udpEcho != INVALID_SOCKET ) {
|
||
closesocket( family[i].udpEcho );
|
||
}
|
||
if ( family[i].tcpDiscard != INVALID_SOCKET ) {
|
||
closesocket( family[i].tcpDiscard );
|
||
}
|
||
if ( family[i].udpDiscard != INVALID_SOCKET ) {
|
||
closesocket( family[i].udpDiscard );
|
||
}
|
||
if ( family[i].tcpDaytime != INVALID_SOCKET ) {
|
||
closesocket( family[i].tcpDaytime );
|
||
}
|
||
if ( family[i].udpDaytime != INVALID_SOCKET ) {
|
||
closesocket( family[i].udpDaytime );
|
||
}
|
||
if ( family[i].tcpChargen != INVALID_SOCKET ) {
|
||
closesocket( family[i].tcpChargen );
|
||
}
|
||
if ( family[i].udpChargen != INVALID_SOCKET ) {
|
||
closesocket( family[i].udpChargen );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Close all connected TCP sockets.
|
||
//
|
||
|
||
for ( i = 0; TcpClients != NULL && (DWORD)i < MaxTcpClients; i++ ) {
|
||
|
||
if ( TcpClients[i].SocketHandle != INVALID_SOCKET ) {
|
||
AbortTcpClient( TcpClients[i].SocketHandle );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Should wait here for all threads to exit!
|
||
//
|
||
|
||
//
|
||
// Deinitialize the eventlog.
|
||
//
|
||
|
||
SimpTerminateEventLog( );
|
||
|
||
//
|
||
// Free allocated memory.
|
||
//
|
||
|
||
if ( IoBuffer != NULL ) {
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, IoBuffer );
|
||
}
|
||
|
||
if ( TcpClients != NULL ) {
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, TcpClients );
|
||
}
|
||
|
||
if ( Readfds != NULL ) {
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, Readfds );
|
||
}
|
||
|
||
if ( ReadfdsStore != NULL ) {
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, ReadfdsStore );
|
||
}
|
||
|
||
if ( ChargenBuffer != NULL ) {
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, ChargenBuffer );
|
||
}
|
||
|
||
if ( QotdBuffer != NULL ) {
|
||
UnmapViewOfFile( QotdBuffer );
|
||
}
|
||
|
||
if ( QotdFileMapping != NULL ) {
|
||
CloseHandle( QotdFileMapping );
|
||
}
|
||
|
||
if ( QotdFileHandle != NULL ) {
|
||
CloseHandle( QotdFileHandle );
|
||
}
|
||
|
||
if ( QotdFileName != NULL ) {
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, QotdFileName );
|
||
}
|
||
|
||
if ( QotdStrings != NULL ) {
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, QotdStrings );
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Announce that we're down.
|
||
//
|
||
|
||
SimpServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||
SimpServiceStatus.dwControlsAccepted = 0;
|
||
SimpServiceStatus.dwCheckPoint = 0;
|
||
SimpServiceStatus.dwWaitHint = 0;
|
||
|
||
SimpServiceStatus.dwWin32ExitCode = err;
|
||
SimpServiceStatus.dwServiceSpecificExitCode = err;
|
||
|
||
AnnounceServiceStatus( );
|
||
|
||
return;
|
||
|
||
} // ServiceEntry
|
||
|
||
|
||
BOOL
|
||
OpenTcpSocket (
|
||
OUT SOCKET *pSocket,
|
||
IN INT FamIdx,
|
||
IN SHORT Port
|
||
)
|
||
{
|
||
SOCKADDR_STORAGE localAddr;
|
||
INT localAddrLen;
|
||
INT err;
|
||
INT one = 1;
|
||
|
||
*pSocket = socket( family[FamIdx].family, SOCK_STREAM, 0 );
|
||
if ( *pSocket == INVALID_SOCKET ) {
|
||
return FALSE;
|
||
}
|
||
|
||
RtlZeroMemory( &localAddr, sizeof(localAddr) );
|
||
SS_PORT(&localAddr) = Port;
|
||
localAddr.ss_family = family[FamIdx].family;
|
||
|
||
|
||
err =
|
||
setsockopt( *pSocket,
|
||
SOL_SOCKET,
|
||
SO_EXCLUSIVEADDRUSE,
|
||
(char *) &one,
|
||
sizeof( one )
|
||
);
|
||
if( err ){
|
||
DEBUG_PRINT(("simptcp: OpenTcpSocket: ExclusiveAddressUse failed %d\n",
|
||
GetLastError() ));
|
||
closesocket(*pSocket);
|
||
*pSocket = INVALID_SOCKET;
|
||
return FALSE;
|
||
}
|
||
|
||
err = bind( *pSocket, (PSOCKADDR)&localAddr, sizeof(localAddr) );
|
||
if ( err ==SOCKET_ERROR ) {
|
||
closesocket(*pSocket);
|
||
*pSocket = INVALID_SOCKET;
|
||
return FALSE;
|
||
}
|
||
|
||
err = listen( *pSocket, LISTEN_BACKLOG );
|
||
if ( err == SOCKET_ERROR ) {
|
||
closesocket(*pSocket);
|
||
*pSocket = INVALID_SOCKET;
|
||
return FALSE;
|
||
}
|
||
|
||
err = setsockopt( *pSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one) );
|
||
if ( err == INVALID_SOCKET ) {
|
||
closesocket(*pSocket);
|
||
*pSocket = INVALID_SOCKET;
|
||
return FALSE;
|
||
}
|
||
|
||
FD_SET( *pSocket, ReadfdsStore );
|
||
return TRUE;
|
||
|
||
} // OpenTcpSocket
|
||
|
||
|
||
BOOL
|
||
OpenUdpSocket (
|
||
OUT SOCKET *pSocket,
|
||
IN INT FamIdx,
|
||
IN SHORT Port
|
||
)
|
||
{
|
||
SOCKADDR_STORAGE localAddr;
|
||
INT localAddrLen;
|
||
INT err;
|
||
DWORD broadcast_off = 0;
|
||
DWORD on = 1;
|
||
|
||
*pSocket = socket( family[FamIdx].family, SOCK_DGRAM, 0 );
|
||
if ( *pSocket == INVALID_SOCKET ) {
|
||
return FALSE;
|
||
}
|
||
|
||
RtlZeroMemory( &localAddr, sizeof(localAddr) );
|
||
SS_PORT(&localAddr) = Port;
|
||
localAddr.ss_family = family[FamIdx].family;
|
||
|
||
err =
|
||
setsockopt( *pSocket,
|
||
SOL_SOCKET,
|
||
SO_EXCLUSIVEADDRUSE,
|
||
(LPBYTE) &on,
|
||
sizeof( on )
|
||
);
|
||
if( err ){
|
||
DEBUG_PRINT(("simptcp: OpenUdpSocket: ExclusiveAddressUse failed %d\n",
|
||
GetLastError() ));
|
||
closesocket(*pSocket);
|
||
*pSocket = INVALID_SOCKET;
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
err = bind( *pSocket, (PSOCKADDR)&localAddr, sizeof(localAddr) );
|
||
if ( err == SOCKET_ERROR ) {
|
||
closesocket(*pSocket);
|
||
*pSocket = INVALID_SOCKET;
|
||
return FALSE;
|
||
}
|
||
|
||
err =
|
||
setsockopt( *pSocket,
|
||
SOL_SOCKET,
|
||
SO_BROADCAST,
|
||
(LPBYTE) &broadcast_off,
|
||
sizeof( broadcast_off )
|
||
);
|
||
|
||
if( err ){
|
||
DEBUG_PRINT(("simptcp: OpenUdpSocket: broadcast_off failed %d\n",
|
||
GetLastError() ));
|
||
closesocket(*pSocket);
|
||
*pSocket = INVALID_SOCKET;
|
||
return FALSE;
|
||
}
|
||
|
||
FD_SET( *pSocket, ReadfdsStore );
|
||
return TRUE;
|
||
|
||
} // OpenUdpSocket
|
||
|
||
|
||
INT
|
||
AcceptTcpClient (
|
||
IN SOCKET ListenSocket,
|
||
IN SHORT Port
|
||
)
|
||
{
|
||
SOCKADDR_STORAGE remoteSockaddr;
|
||
INT remoteSockaddrLength;
|
||
DWORD i;
|
||
SOCKET acceptSocket;
|
||
DWORD threadId;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Always accept the socket first.
|
||
//
|
||
|
||
remoteSockaddrLength = sizeof(remoteSockaddr);
|
||
|
||
acceptSocket =
|
||
accept( ListenSocket, (PSOCKADDR)&remoteSockaddr, &remoteSockaddrLength );
|
||
if ( acceptSocket == INVALID_SOCKET ) {
|
||
return -1;
|
||
}
|
||
|
||
//
|
||
// Use a critical section to protect access to our database of
|
||
// TCP clients.
|
||
//
|
||
|
||
status = RtlEnterCriticalSection( &CriticalSection );
|
||
ASSERT( NT_SUCCESS(status) );
|
||
|
||
//
|
||
// Attempt to find a TCP client slot.
|
||
//
|
||
|
||
for ( i = 0; i < MaxTcpClients; i++ ) {
|
||
if ( TcpClients[i].SocketHandle == INVALID_SOCKET ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we're at the max count of TCP sockets, abort this new
|
||
// socket.
|
||
//
|
||
|
||
if ( i >= MaxTcpClients ) {
|
||
AbortTcpClient( acceptSocket );
|
||
status = RtlLeaveCriticalSection( &CriticalSection );
|
||
ASSERT( NT_SUCCESS(status) );
|
||
return -1;
|
||
}
|
||
|
||
//
|
||
// Initialize info about this client.
|
||
//
|
||
|
||
TcpClients[i].SocketHandle = acceptSocket;
|
||
RtlCopyMemory(
|
||
&TcpClients[i].RemoteAddress,
|
||
&remoteSockaddr,
|
||
sizeof(remoteSockaddr)
|
||
);
|
||
TcpClients[i].ServicePort = Port;
|
||
|
||
//
|
||
// We're in multi-threaded mode, so we'll create a separate thread
|
||
// to handle this client.
|
||
//
|
||
|
||
TcpClients[i].ThreadHandle = CreateThread(
|
||
NULL,
|
||
0,
|
||
ThreadEntry,
|
||
UlongToPtr(i),
|
||
0,
|
||
&threadId
|
||
);
|
||
if ( TcpClients[i].ThreadHandle == NULL ) {
|
||
AbortTcpClient( acceptSocket );
|
||
TcpClients[i].SocketHandle = INVALID_SOCKET;
|
||
status = RtlLeaveCriticalSection( &CriticalSection );
|
||
ASSERT( NT_SUCCESS(status) );
|
||
return -1;
|
||
}
|
||
|
||
//
|
||
// The created thread will handle the connected client.
|
||
//
|
||
|
||
status = RtlLeaveCriticalSection( &CriticalSection );
|
||
ASSERT( NT_SUCCESS(status) );
|
||
|
||
return -1;
|
||
|
||
} // AcceptTcpClient
|
||
|
||
|
||
VOID
|
||
AbortTcpClient (
|
||
IN SOCKET Socket
|
||
)
|
||
{
|
||
LINGER lingerInfo;
|
||
INT err;
|
||
|
||
//
|
||
// First set the linger timeout on the socket to 0. This will cause
|
||
// the connection to be reset.
|
||
//
|
||
|
||
lingerInfo.l_onoff = 1;
|
||
lingerInfo.l_linger = 0;
|
||
|
||
err = setsockopt(
|
||
Socket,
|
||
SOL_SOCKET,
|
||
SO_LINGER,
|
||
(char *)&lingerInfo,
|
||
sizeof(lingerInfo)
|
||
);
|
||
|
||
if ( err == SOCKET_ERROR ) {
|
||
|
||
//
|
||
// There's not too much we can do. Just close the socket.
|
||
//
|
||
|
||
ASSERT(FALSE);
|
||
closesocket( Socket );
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Now close the socket.
|
||
//
|
||
|
||
err = closesocket( Socket );
|
||
ASSERT( err != SOCKET_ERROR );
|
||
|
||
return;
|
||
|
||
} // AbortTcpClient
|
||
|
||
|
||
VOID
|
||
DeleteTcpClient (
|
||
IN DWORD ArraySlot,
|
||
IN BOOLEAN Graceful
|
||
)
|
||
{
|
||
INT err;
|
||
NTSTATUS status;
|
||
|
||
status = RtlEnterCriticalSection( &CriticalSection );
|
||
ASSERT( NT_SUCCESS(status) );
|
||
|
||
ASSERT( TcpClients[ArraySlot].SocketHandle != INVALID_SOCKET );
|
||
|
||
//
|
||
// If this is to be an abortive disconnect, reset the connection.
|
||
// Otherwise just close it normally.
|
||
//
|
||
|
||
if ( !Graceful ) {
|
||
|
||
AbortTcpClient( TcpClients[ArraySlot].SocketHandle );
|
||
|
||
} else {
|
||
|
||
LINGER lingerInfo;
|
||
INT one;
|
||
|
||
//
|
||
// Set the socket to blocking.
|
||
//
|
||
|
||
one = 0;
|
||
ioctlsocket( TcpClients[ArraySlot].SocketHandle, FIONBIO, &one );
|
||
|
||
//
|
||
// Set the socket to linger no more than 60 seconds.
|
||
//
|
||
|
||
lingerInfo.l_onoff = 1;
|
||
lingerInfo.l_linger = 60;
|
||
|
||
setsockopt( TcpClients[ArraySlot].SocketHandle, SOL_SOCKET,
|
||
SO_LINGER, (char *)&lingerInfo, sizeof(lingerInfo) );
|
||
|
||
err = closesocket( TcpClients[ArraySlot].SocketHandle );
|
||
ASSERT( err != SOCKET_ERROR );
|
||
}
|
||
|
||
//
|
||
// Close the thread handle, if appropriate.
|
||
//
|
||
|
||
if ( TcpClients[ArraySlot].ThreadHandle != NULL ) {
|
||
CloseHandle( TcpClients[ArraySlot].ThreadHandle );
|
||
TcpClients[ArraySlot].ThreadHandle = NULL;
|
||
}
|
||
|
||
//
|
||
// Set the handle in the TCP clients array to INVALID_SOCKET so that we
|
||
// know that it is free.
|
||
//
|
||
|
||
TcpClients[ArraySlot].SocketHandle = INVALID_SOCKET;
|
||
|
||
status = RtlLeaveCriticalSection( &CriticalSection );
|
||
ASSERT( NT_SUCCESS(status) );
|
||
|
||
return;
|
||
|
||
} // DeleteTcpClient
|
||
|
||
PCHAR Months[] =
|
||
{
|
||
"January",
|
||
"February",
|
||
"March",
|
||
"April",
|
||
"May",
|
||
"June",
|
||
"July",
|
||
"August",
|
||
"September",
|
||
"October",
|
||
"November",
|
||
"December"
|
||
};
|
||
|
||
PCHAR Days[] =
|
||
{
|
||
"Sunday",
|
||
"Monday",
|
||
"Tuesday",
|
||
"Wednesday",
|
||
"Thursday",
|
||
"Friday",
|
||
"Saturday"
|
||
};
|
||
|
||
|
||
|
||
|
||
VOID
|
||
FormatDaytimeResponse (
|
||
IN PCHAR Buffer,
|
||
IN PDWORD BufferLength
|
||
)
|
||
{
|
||
SYSTEMTIME timeStruct;
|
||
int Status;
|
||
int StringSize;
|
||
|
||
char Buf1[MAX_DATE_BUFFER_SIZE];
|
||
char Buf2[MAX_DATE_BUFFER_SIZE];
|
||
|
||
|
||
*BufferLength=sprintf(Buffer,"");
|
||
|
||
GetLocalTime( &timeStruct );
|
||
Status = GetDateFormatA((LCID)LOCALE_SYSTEM_DEFAULT,
|
||
0,
|
||
&timeStruct,
|
||
NULL,
|
||
Buf1,
|
||
MAX_DATE_BUFFER_SIZE);
|
||
|
||
if (Status == 0) {
|
||
return;
|
||
}
|
||
|
||
Status = GetTimeFormatA((LCID)LOCALE_SYSTEM_DEFAULT,
|
||
0,
|
||
&timeStruct,
|
||
NULL,
|
||
Buf2,
|
||
MAX_DATE_BUFFER_SIZE);
|
||
|
||
if (Status == 0) {
|
||
return;
|
||
}
|
||
|
||
*BufferLength=sprintf(Buffer,"%s %s\n",Buf2,Buf1);
|
||
|
||
return;
|
||
|
||
} // FormatDaytimeResponse
|
||
|
||
|
||
|
||
VOID
|
||
FormatQotdResponse (
|
||
IN PCHAR Buffer,
|
||
IN PDWORD BufferLength
|
||
)
|
||
{
|
||
INT index;
|
||
|
||
if (QotdQuoteCount == 0) {
|
||
sprintf(Buffer,"");
|
||
*BufferLength=strlen(Buffer);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Choose a random quote index.
|
||
//
|
||
|
||
index = (rand( ) * (QotdQuoteCount - 1)) / RAND_MAX;
|
||
|
||
//
|
||
// Copy the quote into the output buffer.
|
||
//
|
||
|
||
strncpy( Buffer, QotdStrings[index].Quote, QotdStrings[index].QuoteLength );
|
||
*BufferLength = QotdStrings[index].QuoteLength;
|
||
|
||
return;
|
||
|
||
} // FormatDaytimeResponse
|
||
|
||
|
||
INT
|
||
InitializeQotdQuotes (
|
||
VOID
|
||
)
|
||
{
|
||
|
||
BY_HANDLE_FILE_INFORMATION fileInformation;
|
||
PCHAR buffer;
|
||
DWORD i,CurQuoteIndex;
|
||
|
||
|
||
if ( QotdFileName == NULL ) {
|
||
return ERROR_FILE_NOT_FOUND;
|
||
}
|
||
|
||
//
|
||
// Open the file containing quote information.
|
||
//
|
||
|
||
QotdFileHandle = CreateFileW(
|
||
QotdFileName,
|
||
GENERIC_READ,
|
||
FILE_SHARE_READ,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
NULL
|
||
);
|
||
if ( QotdFileHandle == INVALID_HANDLE_VALUE ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_QUOTE_FILE,
|
||
0,
|
||
NULL,
|
||
GetLastError( )
|
||
);
|
||
return GetLastError( );
|
||
}
|
||
|
||
//
|
||
// Determine the size of the QOTD file.
|
||
//
|
||
|
||
if ( !GetFileInformationByHandle( QotdFileHandle, &fileInformation ) ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_QUOTE_FILE,
|
||
0,
|
||
NULL,
|
||
GetLastError( )
|
||
);
|
||
return GetLastError( );
|
||
}
|
||
|
||
//
|
||
// Create a file mapping for the quotes file and map it into
|
||
// the address space of this process.
|
||
//
|
||
|
||
QotdFileMapping = CreateFileMapping(
|
||
QotdFileHandle,
|
||
NULL,
|
||
PAGE_READONLY,
|
||
0,
|
||
0,
|
||
NULL
|
||
);
|
||
if ( QotdFileMapping == NULL ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_QUOTE_FILE,
|
||
0,
|
||
NULL,
|
||
GetLastError( )
|
||
);
|
||
return GetLastError( );
|
||
}
|
||
|
||
QotdBuffer = MapViewOfFile(
|
||
QotdFileMapping,
|
||
FILE_MAP_READ,
|
||
0,
|
||
0,
|
||
0
|
||
);
|
||
if ( QotdBuffer == NULL ) {
|
||
SimpLogEvent(
|
||
SIMPTCP_CANT_OPEN_QUOTE_FILE,
|
||
0,
|
||
NULL,
|
||
GetLastError( )
|
||
);
|
||
return GetLastError( );
|
||
}
|
||
|
||
//
|
||
// Count the number of lines in the file. The number of lines
|
||
// corresponds to the number of quotes.
|
||
//
|
||
|
||
QotdQuoteCount = 0;
|
||
buffer = (PCHAR)QotdBuffer;
|
||
|
||
for ( i = 0; i < fileInformation.nFileSizeLow; i++ ) {
|
||
if ( *buffer++ == '%' ) {
|
||
QotdQuoteCount++;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Allocate a buffer to hold the quote array.
|
||
//
|
||
|
||
QotdStrings = RtlAllocateHeap(
|
||
RtlProcessHeap( ),
|
||
0,
|
||
sizeof(QotdStrings[0]) * QotdQuoteCount
|
||
);
|
||
|
||
if ( QotdStrings == NULL ) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
|
||
//
|
||
// Initialize the quote array.
|
||
//
|
||
buffer = (PCHAR)QotdBuffer;
|
||
|
||
CurQuoteIndex=0;
|
||
for ( i = 0; i < QotdQuoteCount; i++ ) {
|
||
|
||
QotdStrings[CurQuoteIndex].Quote = buffer;
|
||
|
||
while ( (DWORD_PTR)buffer < (DWORD_PTR)QotdBuffer +
|
||
fileInformation.nFileSizeLow &&
|
||
*buffer++ != '%' );
|
||
|
||
QotdStrings[CurQuoteIndex].QuoteLength =
|
||
(DWORD)((DWORD_PTR)buffer - (DWORD_PTR)QotdStrings[CurQuoteIndex].Quote) - 1;
|
||
buffer += 2;
|
||
|
||
//
|
||
// If this quote if longer than the IO buffer size, skip over
|
||
// it. We can't use it.
|
||
//
|
||
|
||
if ( QotdStrings[CurQuoteIndex].QuoteLength < IoBufferSize ) {
|
||
// Got a valid one
|
||
CurQuoteIndex++;
|
||
}
|
||
}
|
||
|
||
QotdQuoteCount=CurQuoteIndex;
|
||
|
||
//
|
||
// Initialize the random-number generator.
|
||
//
|
||
|
||
srand( GetTickCount( ) );
|
||
|
||
return NO_ERROR;
|
||
|
||
} // InitializeQotdQuotes
|
||
|
||
|
||
#define CHARGEN_LINE_LENGTH 72
|
||
#define CHARGEN_REAL_LINE_LENGTH (CHARGEN_LINE_LENGTH + 2)
|
||
#define CHARGEN_MIN_CHAR ' '
|
||
#define CHARGEN_MAX_CHAR '~'
|
||
#define CHARGEN_DIFFERENCE (CHARGEN_MAX_CHAR - CHARGEN_MIN_CHAR)
|
||
#define CHARGEN_LINE_COUNT (CHARGEN_DIFFERENCE)
|
||
#define CHARGEN_BUFFER_LENGTH ((CHARGEN_LINE_LENGTH + 2) * (CHARGEN_LINE_COUNT))
|
||
|
||
|
||
INT
|
||
InitializeChargen (
|
||
VOID
|
||
)
|
||
{
|
||
DWORD line;
|
||
BYTE startChar = 0;
|
||
DWORD i;
|
||
|
||
//
|
||
// Allocate a buffer for the chargen data.
|
||
//
|
||
|
||
ChargenBufferSize = CHARGEN_BUFFER_LENGTH;
|
||
|
||
ChargenBuffer = RtlAllocateHeap(
|
||
RtlProcessHeap( ),
|
||
0,
|
||
ChargenBufferSize
|
||
);
|
||
if ( ChargenBuffer == NULL ) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
//
|
||
// Fill in the buffer with the required pattern.
|
||
//
|
||
|
||
for ( line = 0; line < CHARGEN_LINE_COUNT; line++ ) {
|
||
|
||
for ( i = 0; i < CHARGEN_LINE_LENGTH; i++ ) {
|
||
|
||
*((PCHAR)ChargenBuffer + (line * CHARGEN_REAL_LINE_LENGTH) + i) =
|
||
(CHAR)( ((startChar + i) % CHARGEN_DIFFERENCE) + CHARGEN_MIN_CHAR);
|
||
}
|
||
|
||
*((PCHAR)ChargenBuffer + (line * CHARGEN_REAL_LINE_LENGTH) + i) = 0x0D;
|
||
*((PCHAR)ChargenBuffer + (line * CHARGEN_REAL_LINE_LENGTH) + i + 1) = 0x0A;
|
||
|
||
startChar++;
|
||
}
|
||
|
||
return NO_ERROR;
|
||
|
||
} // InitializeQotdQuotes
|
||
|
||
|
||
VOID
|
||
DoSingleClient (
|
||
IN SOCKET s,
|
||
IN USHORT port
|
||
)
|
||
{
|
||
INT err;
|
||
|
||
if ( port == TcpEchoPort ) {
|
||
|
||
while ( TRUE ) {
|
||
|
||
err = recv( s, IoBuffer, IoBufferSize, 0 );
|
||
if ( err == SOCKET_ERROR ) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// If the remote closed gracefully, close the socket.
|
||
//
|
||
|
||
if ( err == 0 ) {
|
||
|
||
closesocket( s );
|
||
return;
|
||
|
||
} else {
|
||
|
||
err = send( s, IoBuffer, err, 0 );
|
||
if ( err == SOCKET_ERROR ) {
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
} else if ( port == TcpDiscardPort ) {
|
||
|
||
while ( TRUE ) {
|
||
|
||
err = recv( s, IoBuffer, IoBufferSize, 0 );
|
||
if ( err == SOCKET_ERROR ) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// If the remote closed gracefully, close the socket.
|
||
//
|
||
|
||
if ( err == 0 ) {
|
||
closesocket( s );
|
||
return;
|
||
} else if ( err == SOCKET_ERROR ) {
|
||
closesocket( s );
|
||
return;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Something bad has happened. Internal data
|
||
// structures are corrupt.
|
||
//
|
||
|
||
ASSERT( FALSE );
|
||
}
|
||
|
||
} // DoSingleClient
|
||
|
||
|
||
SHORT
|
||
GetServicePort (
|
||
IN PCHAR Service,
|
||
IN PCHAR Protocol
|
||
)
|
||
{
|
||
PSERVENT serviceEntry;
|
||
|
||
//
|
||
// Get a servent structure for the specified service.
|
||
//
|
||
|
||
serviceEntry = getservbyname( Service, Protocol );
|
||
|
||
if ( serviceEntry == NULL ) {
|
||
// log an error!
|
||
return INVALID_PORT;
|
||
}
|
||
|
||
//
|
||
// Return the port for the specified service.
|
||
//
|
||
|
||
return serviceEntry->s_port;
|
||
|
||
} // GetServicePort
|
||
|
||
|
||
VOID
|
||
AnnounceServiceStatus (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Announces the service's status to the service controller.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Service status handle is NULL if RegisterServiceCtrlHandler failed.
|
||
//
|
||
|
||
if ( SimpServiceStatusHandle == 0 ) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Call SetServiceStatus, ignoring any errors.
|
||
//
|
||
|
||
SetServiceStatus(SimpServiceStatusHandle, &SimpServiceStatus);
|
||
|
||
} // AnnounceServiceStatus
|
||
|
||
|
||
VOID
|
||
ControlResponse(
|
||
DWORD opCode
|
||
)
|
||
|
||
{
|
||
BOOL announce = TRUE;
|
||
BOOL err;
|
||
|
||
//
|
||
// Determine the type of service control message and modify the
|
||
// service status, if necessary.
|
||
//
|
||
|
||
switch( opCode ) {
|
||
|
||
case SERVICE_CONTROL_STOP:
|
||
|
||
//
|
||
// Announce that we are in the process of stopping.
|
||
//
|
||
|
||
SimpServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
AnnounceServiceStatus( );
|
||
|
||
//
|
||
// Remember that we're stopping.
|
||
//
|
||
|
||
SimpServiceExit = TRUE;
|
||
|
||
//
|
||
// Close a socket that the main select()er thread is
|
||
// waiting on. This will cause the select to wake up
|
||
// and shutdown processing to commence.
|
||
//
|
||
|
||
closesocket( SimpQuitSocket );
|
||
|
||
//
|
||
// Let the main thread announce when the stop is done.
|
||
//
|
||
|
||
announce = FALSE;
|
||
|
||
break;
|
||
|
||
case SERVICE_CONTROL_PAUSE:
|
||
|
||
//
|
||
// Announce that we are in the process of pausing.
|
||
//
|
||
|
||
SimpServiceStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
|
||
AnnounceServiceStatus( );
|
||
|
||
//
|
||
// Remember that we're paused.
|
||
//
|
||
|
||
err = ResetEvent( SimpPauseEvent );
|
||
ASSERT( err );
|
||
|
||
//
|
||
// Announce that we're now paused.
|
||
//
|
||
|
||
SimpServiceStatus.dwCurrentState = SERVICE_PAUSED;
|
||
|
||
break;
|
||
|
||
case SERVICE_CONTROL_CONTINUE:
|
||
|
||
//
|
||
// Announce that continue is pending.
|
||
//
|
||
|
||
SimpServiceStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
|
||
AnnounceServiceStatus( );
|
||
|
||
//
|
||
// Remember that we're no longer paused.
|
||
//
|
||
|
||
err = SetEvent( SimpPauseEvent );
|
||
ASSERT( err );
|
||
|
||
//
|
||
// Announce that we're active now.
|
||
//
|
||
|
||
SimpServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
||
|
||
break;
|
||
|
||
case SERVICE_CONTROL_INTERROGATE:
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
break;
|
||
}
|
||
|
||
if ( announce ) {
|
||
AnnounceServiceStatus( );
|
||
}
|
||
|
||
} // ControlResponse
|
||
|
||
|
||
INT
|
||
ReadRegistry (
|
||
VOID
|
||
)
|
||
{
|
||
HKEY simptcpKey = NULL;
|
||
ULONG error;
|
||
ULONG i;
|
||
DWORD dwordBuffer;
|
||
DWORD bufferLength;
|
||
DWORD type;
|
||
DWORD qotdFileNameLength;
|
||
PWSTR fileName;
|
||
|
||
//
|
||
// First open our parameters key.
|
||
//
|
||
|
||
error = RegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE,
|
||
L"SYSTEM\\CurrentControlSet\\Services\\SimpTcp\\Parameters",
|
||
0,
|
||
MAXIMUM_ALLOWED,
|
||
&simptcpKey
|
||
);
|
||
if ( error != NO_ERROR ) {
|
||
return error;
|
||
}
|
||
|
||
//
|
||
// Read BOOLEANs from the registry.
|
||
//
|
||
|
||
for ( i = 0; RegistryBooleans[i].Boolean != NULL; i++ ) {
|
||
|
||
bufferLength = sizeof(dwordBuffer);
|
||
|
||
error = RegQueryValueExW(
|
||
simptcpKey,
|
||
RegistryBooleans[i].ValueName,
|
||
NULL,
|
||
&type,
|
||
(PVOID)&dwordBuffer,
|
||
&bufferLength
|
||
);
|
||
|
||
//
|
||
// If we fail to read one of these for some reason, just skip it
|
||
// and move on to the next one.
|
||
//
|
||
|
||
if ( error != NO_ERROR ) {
|
||
continue;
|
||
}
|
||
|
||
if ( dwordBuffer == 0 ) {
|
||
*RegistryBooleans[i].Boolean = FALSE;
|
||
} else {
|
||
*RegistryBooleans[i].Boolean = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Read DWORDs from the registry.
|
||
//
|
||
|
||
for ( i = 0; RegistryDwords[i].Dword != NULL; i++ ) {
|
||
|
||
bufferLength = sizeof(RegistryDwords[i].Dword);
|
||
|
||
RegQueryValueExW(
|
||
simptcpKey,
|
||
RegistryDwords[i].ValueName,
|
||
NULL,
|
||
&type,
|
||
(PVOID)RegistryDwords[i].Dword,
|
||
&bufferLength
|
||
);
|
||
}
|
||
|
||
//
|
||
// Read other known values from the registry. Determine the size
|
||
// of the QOTD file name. We need this so that we can allocate
|
||
// enough memory to hold it.
|
||
//
|
||
|
||
qotdFileNameLength = 0;
|
||
|
||
error = RegQueryValueExW(
|
||
simptcpKey,
|
||
L"QotdFileName",
|
||
NULL,
|
||
&type,
|
||
NULL,
|
||
&qotdFileNameLength
|
||
);
|
||
|
||
if ( error == ERROR_MORE_DATA || error == NO_ERROR ) {
|
||
|
||
fileName = RtlAllocateHeap(
|
||
RtlProcessHeap( ),
|
||
0,
|
||
qotdFileNameLength
|
||
);
|
||
if ( fileName == NULL ) {
|
||
return NO_ERROR;
|
||
}
|
||
|
||
error = RegQueryValueExW(
|
||
simptcpKey,
|
||
L"QotdFileName",
|
||
NULL,
|
||
&type,
|
||
(PVOID)fileName,
|
||
&qotdFileNameLength
|
||
);
|
||
if ( error != NO_ERROR ) {
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, fileName );
|
||
return NO_ERROR;
|
||
}
|
||
|
||
//
|
||
// Expand the file name.
|
||
//
|
||
|
||
qotdFileNameLength = ExpandEnvironmentStringsW( fileName, NULL, 0 );
|
||
|
||
QotdFileName = RtlAllocateHeap(
|
||
RtlProcessHeap( ),
|
||
0,
|
||
qotdFileNameLength * 2
|
||
);
|
||
if ( QotdFileName == NULL ) {
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, fileName );
|
||
return NO_ERROR;
|
||
}
|
||
|
||
ExpandEnvironmentStringsW( fileName, QotdFileName, qotdFileNameLength );
|
||
}
|
||
|
||
return NO_ERROR;
|
||
|
||
} // ReadRegistry
|
||
|
||
|
||
DWORD
|
||
ThreadEntry (
|
||
LPVOID lpThreadParameter
|
||
)
|
||
{
|
||
DWORD i = PtrToUlong(lpThreadParameter);
|
||
PVOID ioBuffer;
|
||
INT err;
|
||
BOOLEAN graceful = TRUE;
|
||
|
||
//
|
||
// First, set the send and receive timeouts for the socket. This
|
||
// prevents a dead client from tying up our resources for too long.
|
||
//
|
||
|
||
err = setsockopt( TcpClients[i].SocketHandle, SOL_SOCKET, SO_SNDTIMEO,
|
||
(char *)&MaxIdleTicks, sizeof(MaxIdleTicks) );
|
||
if ( err == SOCKET_ERROR ) {
|
||
DeleteTcpClient( i, FALSE );
|
||
return 0;
|
||
}
|
||
|
||
err = setsockopt( TcpClients[i].SocketHandle, SOL_SOCKET, SO_RCVTIMEO,
|
||
(char *)&MaxIdleTicks, sizeof(MaxIdleTicks) );
|
||
if ( err == SOCKET_ERROR ) {
|
||
DeleteTcpClient( i, FALSE );
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Get a buffer to use locally for IO on the socket.
|
||
//
|
||
|
||
ioBuffer = RtlAllocateHeap( RtlProcessHeap( ), 0, IoBufferSize );
|
||
if ( ioBuffer == NULL ) {
|
||
DeleteTcpClient( i, FALSE );
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Now service the client as appropriate.
|
||
//
|
||
|
||
if ( TcpClients[i].ServicePort == TcpEchoPort ) {
|
||
|
||
//
|
||
// If there is data on a client's echo socket,
|
||
// receive some data and send it back.
|
||
//
|
||
|
||
do {
|
||
|
||
err = recv(
|
||
TcpClients[i].SocketHandle,
|
||
ioBuffer,
|
||
IoBufferSize,
|
||
0
|
||
);
|
||
if ( err == SOCKET_ERROR ) {
|
||
graceful = FALSE;
|
||
}
|
||
|
||
if ( err > 0 ) {
|
||
|
||
err = send(
|
||
TcpClients[i].SocketHandle,
|
||
ioBuffer,
|
||
err,
|
||
0
|
||
);
|
||
if ( err == SOCKET_ERROR ) {
|
||
graceful = FALSE;
|
||
}
|
||
|
||
} else if ( err < 0 ) {
|
||
|
||
graceful = FALSE;
|
||
}
|
||
|
||
} while ( err > 0 );
|
||
|
||
} else if ( TcpClients[i].ServicePort == TcpChargenPort ) {
|
||
|
||
INT one;
|
||
INT error;
|
||
TIMEVAL timeout;
|
||
|
||
//
|
||
// Set the socket to nonblocking.
|
||
//
|
||
|
||
one = 1;
|
||
err = ioctlsocket( TcpClients[i].SocketHandle, FIONBIO, &one );
|
||
if ( err == SOCKET_ERROR ) {
|
||
graceful = FALSE;
|
||
}
|
||
|
||
//
|
||
// Calculate the select() timeout.
|
||
//
|
||
|
||
timeout.tv_sec = MaxIdleTicks / 1000;
|
||
timeout.tv_usec = MaxIdleTicks % 1000;
|
||
|
||
//
|
||
// Loop sending data.
|
||
//
|
||
|
||
do {
|
||
|
||
err = send(
|
||
TcpClients[i].SocketHandle,
|
||
ChargenBuffer,
|
||
ChargenBufferSize,
|
||
0
|
||
);
|
||
|
||
if ( err == SOCKET_ERROR ) {
|
||
|
||
error = GetLastError( );
|
||
|
||
if ( error != WSAEWOULDBLOCK ) {
|
||
|
||
graceful = FALSE;
|
||
|
||
} else {
|
||
|
||
struct {
|
||
INT Count;
|
||
SOCKET Handle;
|
||
} readfds = { 0, 0 };
|
||
struct {
|
||
INT Count;
|
||
SOCKET Handle;
|
||
} writefds = { 0, 0 };
|
||
|
||
//
|
||
// The socket's send queue is blocked. Wait for it to
|
||
// become unblocked.
|
||
//
|
||
|
||
FD_SET( TcpClients[i].SocketHandle, (PFD_SET)&readfds );
|
||
FD_SET( TcpClients[i].SocketHandle, (PFD_SET)&writefds );
|
||
|
||
err = select(
|
||
1,
|
||
(PFD_SET)&readfds,
|
||
(PFD_SET)&writefds,
|
||
NULL,
|
||
&timeout
|
||
);
|
||
if ( err <= 0 ) {
|
||
graceful = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
err = recv(
|
||
TcpClients[i].SocketHandle,
|
||
ioBuffer,
|
||
IoBufferSize,
|
||
0
|
||
);
|
||
if ( err == SOCKET_ERROR ) {
|
||
if ( WSAGetLastError( ) != WSAEWOULDBLOCK ) {
|
||
graceful = FALSE;
|
||
} else {
|
||
err = 1;
|
||
}
|
||
}
|
||
|
||
} while ( err > 0 );
|
||
|
||
} else if ( TcpClients[i].ServicePort == TcpDiscardPort ) {
|
||
|
||
//
|
||
// If there is data on a client's socket, just
|
||
// receive some data and discard it.
|
||
//
|
||
|
||
do {
|
||
|
||
err = recv(
|
||
TcpClients[i].SocketHandle,
|
||
ioBuffer,
|
||
IoBufferSize,
|
||
0
|
||
);
|
||
if ( err == SOCKET_ERROR ) {
|
||
graceful = FALSE;
|
||
}
|
||
|
||
} while ( err > 0 );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Something bad has happened. Internal data
|
||
// structures are corrupt.
|
||
//
|
||
|
||
ASSERT( FALSE );
|
||
}
|
||
|
||
//
|
||
// Free the socket and the IO buffer and return.
|
||
//
|
||
|
||
DeleteTcpClient( i, graceful );
|
||
RtlFreeHeap( RtlProcessHeap( ), 0, ioBuffer );
|
||
|
||
return 0;
|
||
|
||
} // ThreadEntry
|
||
|