windows-nt/Source/XPSP1/NT/net/tcpip/services/simple/simptcp.c
2020-09-26 16:20:57 +08:00

2478 lines
56 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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