You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
359 lines
8.8 KiB
359 lines
8.8 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
|