/*++ 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; 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 = NULL; 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 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 = IoBufferSize; // // A client is making a TCP Qotd request. First accept // the connection, then send the quote of the day // 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 = IoBufferSize; 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 = FIELD_OFFSET(fd_set, fd_array[1 + NUM_FAMILIES * NUM_SERVICES]); 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= 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 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; UINT Length; 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. We want to make sure // we don't overflow the "Buffer" passed in. // Length = QotdStrings[index].QuoteLength; if (*BufferLength < Length) { Length = *BufferLength; } else { *BufferLength = Length; } strncpy( Buffer, QotdStrings[index].Quote, Length); 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 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, Length; 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 ); if (qotdFileNameLength == 0) { RtlFreeHeap( RtlProcessHeap( ), 0, fileName ); return GetLastError(); } QotdFileName = RtlAllocateHeap( RtlProcessHeap( ), 0, qotdFileNameLength * sizeof(UNICODE_NULL) ); if ( QotdFileName == NULL ) { RtlFreeHeap( RtlProcessHeap( ), 0, fileName ); return NO_ERROR; } Length = ExpandEnvironmentStringsW( fileName, QotdFileName, qotdFileNameLength ); if (Length == 0) { RtlFreeHeap( RtlProcessHeap( ), 0, fileName ); RtlFreeHeap( RtlProcessHeap( ), 0, QotdFileName ); return GetLastError(); } } 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