SOCKET.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1992 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
// File: pop3svr.c
//
// Contents:
//
// Classes:
//
// Functions:
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

extern BOOL bServiceTerminating;
extern SOCKET sListener;

#define POP3_PORT 110

PCHAR Welcome = "+OK Welcome to the NT Example POP3 Server (POP3SRV)\r\n";

BOOL
AcceptClients (
HANDLE hCompletionPort
)

/*++

Routine Description:

This routine sets up a listening socket on the POP3 port, then
loops accepting incoming connections from clients. Whenever
it gets a connection, it sends the greeting message and starts
a read on the newly connected socket.

Arguments:

hCompletionPort - the completion port that the POP3SRV worker
threads are waiting on.

Return Value:

BOOL - FALSE if there was an error in starting the listening
socket.

--*/

{
SOCKET s;
SOCKADDR_IN sin;
int err;
BOOL bSuccess;
PCLIENT_CONTEXT lpClientContext;
DWORD dwBytesRead;
int zero;

//
// Create a listening socket that we'll use to accept incoming
// conections.
//

sListener = socket( AF_INET, SOCK_STREAM, 0 );
if ( sListener == INVALID_SOCKET ) {
return FALSE;
}

//
// Bind the socket to the POP3 well-known port.
//

sin.sin_family = AF_INET;
sin.sin_port = htons( POP3_PORT );
sin.sin_addr.s_addr = INADDR_ANY;

err = bind( sListener, (LPSOCKADDR)&sin, sizeof(sin) );
if ( err == SOCKET_ERROR ) {
closesocket( sListener );
return FALSE;
}

//
// Listen for incoming connections on the socket.
//

err = listen( sListener, 5 );
if ( err == SOCKET_ERROR ) {
closesocket( sListener );
return FALSE;
}

//
// Loop forever accepting connections from clients.
//

while ( TRUE ) {

s = accept( sListener, NULL, NULL );
if ( s == INVALID_SOCKET ) {
closesocket( sListener );
return FALSE;
}

//
// If the service if terminating, exit this thread.
//

if ( bServiceTerminating ) {
return FALSE;
}

//
// Allocate a socket context for the new connection. We use
// this context structure to track socket-level information
// about the client.
//

lpClientContext = LocalAlloc( 0, sizeof(*lpClientContext) );
if ( lpClientContext == NULL ) {
closesocket( s );
continue;
}

//
// Initialize the context structure.
//

lpClientContext->Socket = s;
lpClientContext->Context = CreatePop3Context( );
lpClientContext->Overlapped.Internal = 0;
lpClientContext->Overlapped.InternalHigh = 0;
lpClientContext->Overlapped.Offset = 0;
lpClientContext->Overlapped.OffsetHigh = 0;
lpClientContext->Overlapped.hEvent = NULL;

//
// Send the welcome banner.
//

err = send( s, Welcome, strlen(Welcome), 0 );
if ( err == SOCKET_ERROR ) {
LocalFree( lpClientContext );
closesocket( s );
continue;
}

// --- DavidTr: Slide 14(a) ----------------------------------------
//
// Associate the new socket with a completion port.
//

hCompletionPort = CreateIoCompletionPort(
(HANDLE)s,
hCompletionPort,
(DWORD)lpClientContext,
0
);
if ( hCompletionPort == NULL ) {
LocalFree( lpClientContext );
closesocket( s );
closesocket( sListener );
return FALSE;
}

// --- DavidTr: Slide 16 -------------------------------------------
//
// Disable send bufferring on the socket. Setting SO_SNDBUF
// to 0 causes winsock to stop bufferring sends and perform
// sends directly from our buffers, thereby reducing CPU
// usage.
//

zero = 0;
err = setsockopt( s, SOL_SOCKET, SO_SNDBUF, (char *)&zero, sizeof(zero) );
if ( err == SOCKET_ERROR ) {
LocalFree( lpClientContext );
closesocket( s );
closesocket( sListener );
return FALSE;
}

//
// Start an overlapped read on the socket. This read will
// complete in one of the worker threads, which will continue
// processing of the client.
//

lpClientContext->LastClientIo = ClientIoRead;
lpClientContext->BytesReadSoFar = 0;

bSuccess = ReadFile(
(HANDLE)s,
lpClientContext->Buffer,
sizeof(lpClientContext->Buffer),
&dwBytesRead,
&lpClientContext->Overlapped
);
if ( !bSuccess && GetLastError( ) != ERROR_IO_PENDING ) {
LocalFree( lpClientContext );
closesocket( s );
continue;
}
}

} // AcceptClients


VOID
CloseClient (
PCLIENT_CONTEXT lpClientContext,
BOOL bGraceful
)

/*++

Routine Description:

Terminates a connection with a client and frees up resources.

Arguments:

lpClientContext - a pointer to the context structure that contains
information about this client.

bGraceful - TRUE if the close should be graceful, FALSE otherwise.

Return Value:

None.

--*/

{
LINGER lingerStruct;

//
// If we're supposed to abort the connection, set the linger value
// on the socket to 0.
//

if ( !bGraceful ) {
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
setsockopt( lpClientContext->Socket, SOL_SOCKET, SO_LINGER,
(char *)&lingerStruct, sizeof(lingerStruct) );
}

//
// Now close the socket handle. This will do an abortive or
// graceful close, as requested.
//

closesocket( lpClientContext->Socket );

//
// Free context structuyres.
//

LocalFree( lpClientContext->Context );
LocalFree( lpClientContext );

return;

} // CloseClient