/*++
Copyright (c) 1998-1999 Microsoft Corporation
Module Name:
tditest.c
Abstract:
This module implements the (temporary) TDI test module.
Author:
Keith Moore (keithmo) 19-Jun-1998
Revision History:
--*/
#include "precomp.h"
//
// Private types.
//
typedef struct _TEST_SHUTDOWN
{
KEVENT Event;
NTSTATUS Status;
} TEST_SHUTDOWN, *PTEST_SHUTDOWN;
//
// Private prototypes.
//
BOOLEAN
UlpTestConnectionRequest(
IN PVOID pListeningContext,
IN PUL_CONNECTION pConnection,
IN PTRANSPORT_ADDRESS pRemoteAddress,
IN ULONG RemoteAddressLength,
OUT PVOID *ppConnectionContext
);
VOID
UlpTestConnectionComplete(
IN PVOID pListeningContext,
IN PVOID pConnectionContext,
IN NTSTATUS Status
);
VOID
UlpTestConnectionDisconnect(
IN PVOID pListeningContext,
IN PVOID pConnectionContext,
IN NTSTATUS Status
);
VOID
UlpTestConnectionDestroyed(
IN PVOID pListeningContext,
IN PVOID pConnectionContext
);
NTSTATUS
UlpTestReceiveData(
IN PVOID pListeningContext,
IN PVOID pConnectionContext,
IN PVOID pBuffer,
IN ULONG IndicatedLength,
OUT PULONG pTakenLength
);
VOID
UlpCloseEndpointComplete(
IN PVOID pCompletionContext,
IN NTSTATUS Status,
IN ULONG_PTR Information
);
//
// Private globals.
//
BOOLEAN g_TdiTestInitialized;
PUL_ENDPOINT g_TdiTestEndpoint;
PMDL g_TdiTestResponseMdl;
CHAR g_TdiTestResponseBuffer[] =
"HTTP/1.1 404 Object Not Found\r\n"
"Server: Keith & Henry's Excellent Device Driver (Duct-Tape)\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 102\r\n"
"\r\n"
"
Error"
"The system cannot find the file specified. ";
#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, UlInitializeTdiTest )
#pragma alloc_text( PAGE, UlTerminateTdiTest )
#endif // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlpTestConnectionRequest
NOT PAGEABLE -- UlpTestConnectionComplete
NOT PAGEABLE -- UlpTestConnectionDisconnect
NOT PAGEABLE -- UlpTestConnectionDestroyed
NOT PAGEABLE -- UlpTestReceiveData
NOT PAGEABLE -- UlpCloseEndpointComplete
#endif
//
// Public functions.
//
/***************************************************************************++
Routine Description:
Performs global initialization of this module.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlInitializeTdiTest(
VOID
)
{
NTSTATUS status;
TA_IP_ADDRESS localAddress;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( !g_TdiTestInitialized );
//
// Allocate a MDL describing the response buffer.
//
g_TdiTestResponseMdl = IoAllocateMdl(
g_TdiTestResponseBuffer,
sizeof(g_TdiTestResponseBuffer) - 1,
FALSE,
FALSE,
NULL
);
if (g_TdiTestResponseMdl == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool( g_TdiTestResponseMdl );
//
// Create the listening endpoint. Note that our listening endpoint
// context is just a double indirect pointer to the raw UL_ENDPOINT.
//
UlInitializeIpTransportAddress(
&localAddress,
0,
80
);
status = UlCreateListeningEndpoint(
(PTRANSPORT_ADDRESS)&localAddress,
sizeof(localAddress),
10,
&UlpTestConnectionRequest,
&UlpTestConnectionComplete,
&UlpTestConnectionDisconnect,
&UlpTestConnectionDestroyed,
&UlpTestReceiveData,
&g_TdiTestEndpoint,
&g_TdiTestEndpoint
);
if (!NT_SUCCESS(status))
{
IoFreeMdl( g_TdiTestResponseMdl );
return status;
}
g_TdiTestInitialized = TRUE;
return STATUS_SUCCESS;
} // UlInitializeTdiTest
/***************************************************************************++
Routine Description:
Performs global termination of this module.
--***************************************************************************/
VOID
UlTerminateTdiTest(
VOID
)
{
NTSTATUS status;
TEST_SHUTDOWN shutdown;
//
// Sanity check.
//
PAGED_CODE();
if (g_TdiTestInitialized)
{
g_TdiTestInitialized = FALSE;
//
// Close the listening endpoint and wait for it to complete.
//
KeInitializeEvent( &shutdown.Event, SynchronizationEvent, FALSE );
status = UlCloseListeningEndpoint(
g_TdiTestEndpoint,
&UlpCloseEndpointComplete,
&shutdown
);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(
&shutdown.Event, // Object
UserRequest, // WaitReason
KernelMode, // WaitMode
FALSE, // Alertable
NULL // Timeout
);
status = shutdown.Status;
}
if (!NT_SUCCESS(status))
{
IF_DEBUG( TDI_TEST )
{
KdPrint((
"UlTerminateTdiTest: UlCloseListeningEndpoint failed, %08lx\n",
status
));
}
}
//
// Free the response MDL we created above.
//
IoFreeMdl( g_TdiTestResponseMdl );
}
} // UlTerminateTdiTest
//
// Private functions.
//
/***************************************************************************++
Routine Description:
Routine invoked after an incoming TCP/MUX connection has been
received (but not yet accepted).
Arguments:
pListeningContext - Supplies an uninterpreted context value as
passed to the UlCreateListeningEndpoint() API.
pConnection - Supplies the connection being established.
pRemoteAddress - Supplies the remote (client-side) address
requesting the connection.
RemoteAddressLength - Supplies the total byte length of the
pRemoteAddress structure.
ppConnectionContext - Receives a pointer to an uninterpreted
context value to be associated with the new connection if
accepted. If the new connection is not accepted, this
parameter is ignored.
Return Value:
BOOL - TRUE if the connection was accepted, FALSE if not.
--***************************************************************************/
BOOLEAN
UlpTestConnectionRequest(
IN PVOID pListeningContext,
IN PUL_CONNECTION pConnection,
IN PTRANSPORT_ADDRESS pRemoteAddress,
IN ULONG RemoteAddressLength,
OUT PVOID *ppConnectionContext
)
{
PUL_ENDPOINT pEndpoint;
PHTTP_CONNECTION pHttpConnection;
NTSTATUS status;
//
// Sanity check.
//
pEndpoint = *(PUL_ENDPOINT *)pListeningContext;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
ASSERT( IS_VALID_CONNECTION( pConnection ) );
IF_DEBUG( TDI_TEST )
{
KdPrint((
"UlpTestConnectionRequest: conn %p\n",
pConnection
));
}
//
// Create a new HTTP connection.
//
status = UlCreateHttpConnection( &pHttpConnection, pConnection );
if (NT_SUCCESS(status))
{
//
// We the HTTP_CONNECTION pointer as our connection context.
//
*ppConnectionContext = pHttpConnection;
return TRUE;
}
//
// Failed to create new connection.
//
IF_DEBUG( TDI_TEST )
{
KdPrint((
"UlpTestConnectionRequest: cannot create new conn, error %08lx\n",
status
));
}
return FALSE;
} // UlpTestConnectionRequest
/***************************************************************************++
Routine Description:
Routine invoked after an incoming TCP/MUX connection has been
fully accepted.
This routine is also invoked if an incoming connection was not
accepted *after* PUL_CONNECTION_REQUEST returned TRUE. In other
words, if PUL_CONNECTION_REQUEST indicated that the connection
should be accepted but a fatal error occurred later, then
PUL_CONNECTION_COMPLETE is invoked.
Arguments:
pListeningContext - Supplies an uninterpreted context value
as passed to the UlCreateListeningEndpoint() API.
pConnectionContext - Supplies the uninterpreted context value
as returned by PUL_CONNECTION_REQUEST.
Status - Supplies the completion status. If this value is
STATUS_SUCCESS, then the connection is now fully accepted.
Otherwise, the connection has been aborted.
--***************************************************************************/
VOID
UlpTestConnectionComplete(
IN PVOID pListeningContext,
IN PVOID pConnectionContext,
IN NTSTATUS Status
)
{
PUL_CONNECTION pConnection;
PUL_ENDPOINT pEndpoint;
PHTTP_CONNECTION pHttpConnection;
//
// Sanity check.
//
pEndpoint = *(PUL_ENDPOINT *)pListeningContext;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
pHttpConnection = (PHTTP_CONNECTION)pConnectionContext;
pConnection = pHttpConnection->pConnection;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
IF_DEBUG( TDI_TEST )
{
KdPrint((
"UlpTestConnectionComplete: http %p conn %p status %08lx\n",
pHttpConnection,
pConnection,
Status
));
}
//
// Blow away our HTTP connection if the connect failed.
//
if (!NT_SUCCESS(Status))
{
UlDereferenceHttpConnection( pHttpConnection );
}
} // UlpTestConnectionComplete
/***************************************************************************++
Routine Description:
Routine invoked after an established TCP/MUX connection has been
disconnected by the remote (client) side.
Arguments:
pListeningContext - Supplies an uninterpreted context value
as passed to the UlCreateListeningEndpoint() API.
pConnectionContext - Supplies the uninterpreted context value
as returned by PUL_CONNECTION_REQUEST.
Status - Supplies the termination status.
--***************************************************************************/
VOID
UlpTestConnectionDisconnect(
IN PVOID pListeningContext,
IN PVOID pConnectionContext,
IN NTSTATUS Status
)
{
PUL_CONNECTION pConnection;
PUL_ENDPOINT pEndpoint;
PHTTP_CONNECTION pHttpConnection;
//
// Sanity check.
//
pEndpoint = *(PUL_ENDPOINT *)pListeningContext;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
pHttpConnection = (PHTTP_CONNECTION)pConnectionContext;
pConnection = pHttpConnection->pConnection;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
IF_DEBUG( TDI_TEST )
{
KdPrint((
"UlpTestConnectionDisconnect: http %p conn %p\n",
pHttpConnection,
pConnection
));
}
} // UlpTestConnectionDisconnect
/***************************************************************************++
Routine Description:
Routine invoked after an established TCP/MUX connection has been
destroyed.
Arguments:
pListeningContext - Supplies an uninterpreted context value
as passed to the UlCreateListeningEndpoint() API.
pConnectionContext - Supplies the uninterpreted context value
as returned by PUL_CONNECTION_REQUEST.
--***************************************************************************/
VOID
UlpTestConnectionDestroyed(
IN PVOID pListeningContext,
IN PVOID pConnectionContext
)
{
PUL_CONNECTION pConnection;
PUL_ENDPOINT pEndpoint;
PHTTP_CONNECTION pHttpConnection;
NTSTATUS status;
//
// Sanity check.
//
pEndpoint = *(PUL_ENDPOINT *)pListeningContext;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
pHttpConnection = (PHTTP_CONNECTION)pConnectionContext;
pConnection = pHttpConnection->pConnection;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
IF_DEBUG( TDI_TEST )
{
KdPrint((
"UlpTestConnectionDestroyed: http %p conn %p\n",
pHttpConnection,
pConnection
));
}
//
// Tear it down.
//
UlDereferenceHttpConnection( pHttpConnection );
} // UlpTestConnectionDestroyed
/***************************************************************************++
Routine Description:
Routine invoked after data has been received on an established
TCP/MUX connection.
Arguments:
pListeningContext - Supplies an uninterpreted context value
as passed to the UlCreateListeningEndpoint() API.
pConnectionContext - Supplies an uninterpreted context value
as returend from the PUL_CONNECTION_REQUEST callback.
pBuffer - Supplies a pointer to the received data.
IndicatedLength - Supplies the length of the received data
available in pBuffer.
pTakenLength - Receives the number of bytes consumed by
the receive handler.
Return Value:
NTSTATUS - The status of the consumed data. The behavior of
the TDI/MUX component is dependent on the return value
and the value set in *pTakenLength, and is defined as
follows:
STATUS_SUCCESS, *pTakenLength == IndicatedLength -
All indicated data was consumed by the receive
handler. Additional incoming data will cause
subsequent receive indications.
STATUS_SUCCESS, *pTakenLength < IndicatedLength -
Part of the indicated data was consumed by the
receive handler. The network transport will
buffer data and no further indications will be
made until the UlReceiveData() is called.
STATUS_MORE_PROCESSING_REQUIRED - Part of the
indicated data was consumed by the receive handler.
A subsequent receive indication will be made
when additional data is available. The subsequent
indication will include the unconsumed data from
the current indication plus any additional data
received.
Any other status - Indicates a fatal error in the
receive handler. The connection will be aborted.
*pTakenLength > IndicatedLength - This is an error
condition and should never occur.
--***************************************************************************/
NTSTATUS
UlpTestReceiveData(
IN PVOID pListeningContext,
IN PVOID pConnectionContext,
IN PVOID pBuffer,
IN ULONG IndicatedLength,
OUT PULONG pTakenLength
)
{
PUL_CONNECTION pConnection;
PUL_ENDPOINT pEndpoint;
PHTTP_CONNECTION pHttpConnection;
NTSTATUS status;
//
// Sanity check.
//
pEndpoint = *(PUL_ENDPOINT *)pListeningContext;
ASSERT( IS_VALID_ENDPOINT( pEndpoint ) );
pHttpConnection = (PHTTP_CONNECTION)pConnectionContext;
pConnection = pHttpConnection->pConnection;
ASSERT( IS_VALID_CONNECTION( pConnection ) );
IF_DEBUG( TDI_TEST )
{
KdPrint((
"UlpTestReceiveData: http %p conn %p\n",
pHttpConnection,
pConnection
));
}
//
// Call through to the receive handler.
//
status = HttpReceive(
pHttpConnection,
(PUCHAR)pBuffer,
(SIZE_T)IndicatedLength,
(SIZE_T *)pTakenLength // Bogus! FIX THIS
);
if (!NT_SUCCESS(status))
{
IF_DEBUG( TDI_TEST )
{
KdPrint((
"UlpTestReceiveData: HttpReceive failed, error %08lx\n",
status
));
}
}
return status;
} // UlpTestReceiveData
/***************************************************************************++
Routine Description:
Completion handler for UlCloseListeningEndpoint().
Arguments:
pCompletionContext - Supplies an uninterpreted context value
as passed to the asynchronous API. This is actually a
pointer to a TEST_SHUTDOWN structure.
Status - Supplies the final completion status of the
asynchronous API.
Information - Optionally supplies additional information about
the completed operation, such as the number of bytes
transferred. This field is unused for UlCloseListeningEndpoint().
--***************************************************************************/
VOID
UlpCloseEndpointComplete(
IN PVOID pCompletionContext,
IN NTSTATUS Status,
IN ULONG_PTR Information
)
{
PTEST_SHUTDOWN pShutdown;
IF_DEBUG( TDI_TEST )
{
KdPrint((
"UlpCloseEndpointComplete: %08lx %p\n",
Status,
Information
));
}
//
// Save the completion status, signal the event.
//
pShutdown = (PTEST_SHUTDOWN)pCompletionContext;
pShutdown->Status = Status;
KeSetEvent( &pShutdown->Event, 0, FALSE );
} // UlpCloseEndpointComplete