|
|
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation
//
// File: ktcore.cxx
//
// Contents: Kerberos Tunneller, core service thread routines
//
// History: 28-Jun-2001 t-ryanj Created
//
//------------------------------------------------------------------------
#include "ktdebug.h"
#include "ktcore.h"
#include "ktcontrol.h"
#include "ktcontext.h"
#include "ktsock.h"
#include "kthttp.h"
#include "ktkerb.h"
VOID KtDispatchPerContext( PKTCONTEXT pContext );
//+-------------------------------------------------------------------------
//
// Function: KtThreadCore
//
// Synopsis: Main loop for service threads.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//--------------------------------------------------------------------------
VOID KtThreadCore( VOID ) { ULONG_PTR CompKey; LPOVERLAPPED pOverlapped = NULL; PKTCONTEXT pContext = NULL; DWORD IocpBytes; BOOL IocpSuccess; DWORD IocpError;
//
// Here's the main service loop. It ends when the service is stopped.
//
while( !KtIsStopped() ) { //
// Wait for a task to queue on the completion port.
//
IocpSuccess = GetQueuedCompletionStatus( KtIocp, &IocpBytes, &CompKey, &pOverlapped, INFINITE );
//
// Extrapolate the address of the context from the address of the
// overlapped struct.
//
if( pOverlapped ) { pContext = CONTAINING_RECORD( pOverlapped, KTCONTEXT, ol ); }
//
// If there's an error on the Iocp, release any associated context if possible.
//
if( !IocpSuccess ) { /* TODO: Event Log. */
DebugLog( DEB_ERROR, "%s(%d): Error from GetQueuedCompletionStatus: 0x%x.\n", __FILE__, __LINE__, GetLastError() ); if( pContext ) { DebugLog( DEB_TRACE, "%s(%d): Releasing context due to GQCS error.\n", __FILE__, __LINE__ ); KtReleaseContext( pContext ); } else { DebugLog( DEB_TRACE, "%s(%d): No completion packet dequeued.\n", __FILE__, __LINE__ ); }
//
// Any completion key we have is invalid, so skip back to top of loop.
//
continue; }
//
// Dispatch the task to the appropriate routine.
//
switch( CompKey ) { case KTCK_SERVICE_CONTROL: //
// The specific control event was passed on the bytes argument.
//
KtServiceControlEvent(IocpBytes); break; case KTCK_CHECK_CONTEXT: //
// The context may not exist. If the service is shutting down, it closes
// all pending connections, which will cause completion to be posted,
// but since the context has already been destroyed, there's nothing to do.
//
if( pContext ) KtDispatchPerContext(pContext); break; default: DebugLog( DEB_WARN, "%s(%d): Unhandled case: 0x%x.\n", __FILE__, __LINE__, CompKey ); break; } } }
//+-------------------------------------------------------------------------
//
// Function: KtDispatchPerContext
//
// Synopsis: This routine handles the sequence of events that happen
// over the lifetime of a connection.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//--------------------------------------------------------------------------
VOID KtDispatchPerContext( PKTCONTEXT pContext ) { switch( pContext->Status ) { case KT_SOCK_CONNECT: //
// First we need to prepare to accept other connections.
// If we can't accept more incoming connections, this will
// impact the entire service.
//
DebugLog( DEB_TRACE, "----==== Preparing to accept new connection ====----\n" );
if( !KtSockAccept() ) goto SvcError;
//
// Now we can complete the acceptance of the socket that
// connected, then issue a read on that socket. If something
// goes wrong here, we'll close the session.
//
if( !KtSockCompleteAccept(pContext) ) goto SessError;
if( !KtSockRead(pContext) ) goto SessError; break;
case KT_SOCK_READ: //
// pContext->ol.InternalHigh holds the bytes transferred after a
// socket operation. If it's zero, the other side has closed the
// connection.
//
if( !pContext->ol.InternalHigh ) goto SessError; pContext->emptybuf->bytesused = (ULONG)pContext->ol.InternalHigh; DebugLog( DEB_PEDANTIC, "%s(%d): %d bytes read from loopback.\n", __FILE__, __LINE__, pContext->emptybuf->bytesused ); //
// If we don't know how many bytes to look for yet, figure it out.
//
if( pContext->ExpectedLength == 0 ) { if( !KtParseExpectedLength( pContext ) ) goto SessError; DebugLog( DEB_TRACE, "%s(%d): Expected message length: %d.\n", __FILE__, __LINE__, pContext->ExpectedLength ); } pContext->TotalBytes += pContext->emptybuf->bytesused; //
// If there might be more to read, get more space and try to read more,
// otherwise, coalesce everything we've read into one mammoth buffer,
// then send that.
//
if( pContext->ExpectedLength > pContext->TotalBytes ) { if( !KtGetMoreSpace( pContext, KTCONTEXT_BUFFER_LENGTH ) ) goto SessError; if( !KtSockRead(pContext) ) goto SessError; } else { DebugLog( DEB_TRACE, "%s(%d): %d bytes total read from loopback.\n", __FILE__, __LINE__, pContext->TotalBytes ); if( !KtCoalesceBuffers( pContext ) ) goto SessError;
if( !KtFindProxy(pContext) ) goto SessError; if( !KtHttpWrite(pContext) ) goto SessError; }
break;
case KT_HTTP_WRITE: //
// And now we read the response to our request.
//
pContext->ExpectedLength = 0; pContext->TotalBytes = 0; if( !KtHttpRead(pContext) ) goto SessError; break;
case KT_HTTP_READ: DebugLog( DEB_PEDANTIC, "%s(%d): %d bytes read from http.\n", __FILE__, __LINE__, pContext->emptybuf->bytesused );
if( pContext->emptybuf->bytesused == 0 ) { DebugLog( DEB_TRACE, "%s(%d): Data incomplete. Dropping connection.\n", __FILE__, __LINE__ ); goto SessError; } //
// If we don't know how many bytes to look for yet, figure it out.
//
if( pContext->ExpectedLength == 0 ) { if( !KtParseExpectedLength( pContext ) ) goto SessError; DebugLog( DEB_TRACE, "%s(%d): Expected message length: %d.\n", __FILE__, __LINE__, pContext->ExpectedLength ); }
//
// Update the byte count
//
pContext->TotalBytes += pContext->emptybuf->bytesused; //
// If we're expecting more, get more space and try to read more,
// otherwise, coalesce everything we've read into one mammoth buffer,
// relay that response back to our client.
//
if( pContext->ExpectedLength > pContext->TotalBytes ) { if( !KtGetMoreSpace( pContext, KTCONTEXT_BUFFER_LENGTH ) ) goto SessError; if( !KtHttpRead(pContext) ) goto SessError; } else { DebugLog( DEB_TRACE, "%s(%d): %d total bytes read from http.\n", __FILE__, __LINE__, pContext->TotalBytes ); if( !KtCoalesceBuffers(pContext) ) goto SessError;
//
// If this is a debug build, let's generate some debug spew if
// we get a kerb-error as a reply to one of our requests.
//
#if DBG
KtParseKerbError(pContext); #endif
if( !KtSockWrite(pContext) ) goto SessError; }
break;
case KT_SOCK_WRITE: //
// Once we've finished relaying our whole request-reponse pair,
// we're done with this session.
//
DebugLog( DEB_TRACE, "%s(%d): %d bytes written to loopback.\n", __FILE__, __LINE__, pContext->ol.InternalHigh );
KtReleaseContext(pContext); break;
default: DebugLog( DEB_WARN, "%s(%d): Unhandled case: 0x%x.\n", __FILE__, __LINE__, pContext->Status ); DsysAssert( pContext->Status == KT_SOCK_CONNECT || pContext->Status == KT_SOCK_READ || pContext->Status == KT_HTTP_WRITE || pContext->Status == KT_HTTP_READ || pContext->Status == KT_SOCK_WRITE ); break; }
return;
SessError: DebugLog( DEB_TRACE, "%s(%d): Dropping connection due to session error.\n", __FILE__, __LINE__ ); KtReleaseContext(pContext); return;
SvcError: /* TODO: Event log. Pause service??? */ return; }
|