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.
1306 lines
33 KiB
1306 lines
33 KiB
/*++
|
|
|
|
Copyright (c) 1998 Micros oft Corporation
|
|
|
|
Module Name:
|
|
|
|
spudp.c
|
|
|
|
Abstract:
|
|
|
|
Routines for handling sending and receiving datagram packets to a BINL server.
|
|
|
|
Author:
|
|
|
|
Sean Selitrennikoff (v-seasel) 22-Jun-1998
|
|
|
|
Revision History:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
#include "spprecmp.h"
|
|
#pragma hdrstop
|
|
#include "spcmdcon.h"
|
|
#include <tdi.h>
|
|
#include <tdikrnl.h>
|
|
#include <remboot.h>
|
|
#include <oscpkt.h>
|
|
|
|
//
|
|
// Useful definitions
|
|
//
|
|
#define NULL_IP_ADDR 0
|
|
#define htons( a ) ((((a) & 0xFF00) >> 8) |\
|
|
(((a) & 0x00FF) << 8))
|
|
|
|
//
|
|
// Type definitions
|
|
//
|
|
typedef struct _SPUDP_FSCONTEXT {
|
|
LIST_ENTRY Linkage;
|
|
PFILE_OBJECT FileObject;
|
|
LONG ReferenceCount;
|
|
UCHAR CancelIrps;
|
|
UCHAR Pad[3];
|
|
} SPUDP_FSCONTEXT, *PSPUDP_FSCONTEXT;
|
|
|
|
typedef enum {
|
|
SpUdpNetworkDisconnected,
|
|
SpUdpNetworkDisconnecting,
|
|
SpUdpNetworkConnecting,
|
|
SpUdpNetworkConnected
|
|
} SPUDP_NETWORK_STATE;
|
|
|
|
typedef struct _SPUDP_RECEIVE_ENTRY {
|
|
LIST_ENTRY ListEntry;
|
|
ULONG DataBufferLength;
|
|
PVOID DataBuffer;
|
|
} SPUDP_RECEIVE_ENTRY, *PSPUDP_RECEIVE_ENTRY;
|
|
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
SPUDP_NETWORK_STATE SpUdpNetworkState = SpUdpNetworkDisconnected;
|
|
ULONG SpUdpActiveRefCount = 0;
|
|
HANDLE SpUdpDatagramHandle;
|
|
PFILE_OBJECT SpUdpDatagramFileObject;
|
|
PDEVICE_OBJECT SpUdpDatagramDeviceObject;
|
|
KSPIN_LOCK SpUdpLock;
|
|
KIRQL SpUdpOldIrql;
|
|
LIST_ENTRY SpUdpReceiveList;
|
|
ULONG SpUdpNumReceivePackets = 0;
|
|
ULONG SpUdpSendSequenceNumber = 1;
|
|
|
|
//
|
|
// Function definitions
|
|
//
|
|
|
|
NTSTATUS
|
|
SpUdpTdiErrorHandler(
|
|
IN PVOID TdiEventContext,
|
|
IN NTSTATUS Status
|
|
);
|
|
|
|
NTSTATUS
|
|
SpUdpTdiSetEventHandler(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG EventType,
|
|
IN PVOID EventHandler,
|
|
IN PVOID EventContext
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpIssueDeviceControl (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG IoctlCode,
|
|
IN PVOID InputBuffer,
|
|
IN ULONG InputBufferLength,
|
|
OUT PVOID OutputBuffer,
|
|
IN ULONG OutputBufferLength
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpTdiReceiveDatagramHandler(
|
|
IN PVOID TdiEventContext,
|
|
IN LONG SourceAddressLength,
|
|
IN PVOID SourceAddress,
|
|
IN LONG OptionsLength,
|
|
IN PVOID Options,
|
|
IN ULONG ReceiveDatagramFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP * Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
SpUdpReceivePacketHandler(
|
|
IN ULONG TdiReceiveDatagramFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP * Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
SpUdpCompleteReceivePacket(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
SpUdpProcessReceivePacket(
|
|
IN ULONG TsduSize,
|
|
IN PVOID Tsdu
|
|
);
|
|
|
|
NTSTATUS
|
|
SpUdpSendDatagram(
|
|
IN PVOID SendBuffer,
|
|
IN ULONG SendBufferLength,
|
|
IN ULONG RemoteHostAddress,
|
|
IN USHORT RemoteHostPort
|
|
);
|
|
|
|
NTSTATUS
|
|
SpUdpCompleteSendDatagram(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SpUdpDereferenceFsContext(
|
|
PSPUDP_FSCONTEXT FsContext
|
|
)
|
|
{
|
|
LONG newValue = InterlockedDecrement(&(FsContext->ReferenceCount));
|
|
|
|
|
|
ASSERT(newValue >= 0);
|
|
|
|
if (newValue != 0) {
|
|
return;
|
|
}
|
|
|
|
return;
|
|
} // SpUdpDereferenceFsContext
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpMarkRequestPending(
|
|
PIRP Irp,
|
|
PIO_STACK_LOCATION IrpSp,
|
|
PDRIVER_CANCEL CancelRoutine
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Called with IoCancelSpinLock held.
|
|
|
|
--*/
|
|
{
|
|
PSPUDP_FSCONTEXT fsContext = (PSPUDP_FSCONTEXT) IrpSp->FileObject->FsContext;
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// Set up for cancellation
|
|
//
|
|
ASSERT(Irp->CancelRoutine == NULL);
|
|
|
|
if (!Irp->Cancel) {
|
|
|
|
IoMarkIrpPending(Irp);
|
|
IoSetCancelRoutine(Irp, CancelRoutine);
|
|
|
|
InterlockedIncrement(&(fsContext->ReferenceCount));
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// The IRP has already been cancelled.
|
|
//
|
|
return(STATUS_CANCELLED);
|
|
|
|
} // SpUdpMarkRequestPending
|
|
|
|
|
|
|
|
VOID
|
|
SpUdpCompletePendingRequest(
|
|
IN PIRP Irp,
|
|
IN NTSTATUS Status,
|
|
IN ULONG BytesReturned
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completes a pending request.
|
|
|
|
Arguments:
|
|
|
|
Irp - A pointer to the IRP for this request.
|
|
Status - The final status of the request.
|
|
BytesReturned - Bytes sent/received information.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
Called with IoCancelSpinLock held. Lock Irql is stored in Irp->CancelIrql.
|
|
Releases IoCancelSpinLock before returning.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PSPUDP_FSCONTEXT fsContext;
|
|
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
fsContext = (PSPUDP_FSCONTEXT) irpSp->FileObject->FsContext;
|
|
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
SpUdpDereferenceFsContext(fsContext);
|
|
|
|
if (Irp->Cancel || fsContext->CancelIrps) {
|
|
Status = (unsigned int) STATUS_CANCELLED;
|
|
BytesReturned = 0;
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
Irp->IoStatus.Status = (NTSTATUS) Status;
|
|
Irp->IoStatus.Information = BytesReturned;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return;
|
|
|
|
} // SpUdpCompletePendingRequest
|
|
|
|
|
|
|
|
PFILE_OBJECT
|
|
SpUdpBeginCancelRoutine(
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs common bookkeeping for irp cancellation.
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to I/O request packet
|
|
|
|
Return Value:
|
|
|
|
A pointer to the file object on which the irp was submitted.
|
|
This value must be passed to SpUdpEndCancelRequest().
|
|
|
|
Notes:
|
|
|
|
Called with cancel spinlock held.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PSPUDP_FSCONTEXT fsContext;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PFILE_OBJECT fileObject;
|
|
|
|
|
|
ASSERT(Irp->Cancel);
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
fileObject = irpSp->FileObject;
|
|
fsContext = (PSPUDP_FSCONTEXT) fileObject->FsContext;
|
|
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
//
|
|
// Add a reference so the object can't be closed while the cancel routine
|
|
// is executing.
|
|
//
|
|
InterlockedIncrement(&(fsContext->ReferenceCount));
|
|
|
|
return(fileObject);
|
|
|
|
} // SpUdpBeginCancelRoutine
|
|
|
|
|
|
|
|
VOID
|
|
SpUdpEndCancelRoutine(
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs common bookkeeping for irp cancellation.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
Called with cancel spinlock held.
|
|
|
|
--*/
|
|
{
|
|
|
|
PSPUDP_FSCONTEXT fsContext = (PSPUDP_FSCONTEXT) FileObject->FsContext;
|
|
|
|
//
|
|
// Remove the reference placed on the endpoint by the cancel routine.
|
|
//
|
|
SpUdpDereferenceFsContext(fsContext);
|
|
return;
|
|
|
|
} // SpUdpEndCancelRoutine
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpConnect(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK iosb;
|
|
PFILE_FULL_EA_INFORMATION ea = NULL;
|
|
ULONG eaBufferLength;
|
|
HANDLE addressHandle = NULL;
|
|
PFILE_OBJECT addressFileObject = NULL;
|
|
PDEVICE_OBJECT addressDeviceObject = NULL;
|
|
BOOLEAN attached = FALSE;
|
|
UNICODE_STRING unicodeString;
|
|
TDI_REQUEST_KERNEL_QUERY_INFORMATION queryInfo;
|
|
PTDI_ADDRESS_INFO addressInfo;
|
|
TDI_PROVIDER_INFO providerInfo;
|
|
PWCHAR TdiProviderName;
|
|
ULONG TdiProviderNameLength;
|
|
PTRANSPORT_ADDRESS TransportAddress;
|
|
PTDI_ADDRESS_IP TdiAddressIp;
|
|
|
|
TdiProviderName = L"\\Device\\Udp";
|
|
TdiProviderNameLength = (wcslen(TdiProviderName) + 1) * sizeof(WCHAR);
|
|
|
|
InitializeListHead(&SpUdpReceiveList);
|
|
|
|
//
|
|
// Allocate memory to hold the EA buffer we'll use to specify the
|
|
// transport address to NtCreateFile.
|
|
//
|
|
eaBufferLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
sizeof(TA_IP_ADDRESS);
|
|
|
|
ea = SpMemAlloc(eaBufferLength);
|
|
|
|
if (ea == NULL) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: memory allocation of %u bytes failed.\n", eaBufferLength));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Initialize the EA using the network's transport information.
|
|
//
|
|
ea->NextEntryOffset = 0;
|
|
ea->Flags = 0;
|
|
ea->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
|
|
ea->EaValueLength = (USHORT)sizeof(TA_IP_ADDRESS);
|
|
|
|
RtlMoveMemory(
|
|
ea->EaName,
|
|
TdiTransportAddress,
|
|
ea->EaNameLength + 1
|
|
);
|
|
|
|
TransportAddress = (PTRANSPORT_ADDRESS)(&(ea->EaName[ea->EaNameLength + 1]));
|
|
TransportAddress->TAAddressCount = 1;
|
|
TransportAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
|
|
TransportAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
|
|
TdiAddressIp = (PTDI_ADDRESS_IP)(&(TransportAddress->Address[0].Address[0]));
|
|
TdiAddressIp->sin_port= 0; // Means that you want a port assigned
|
|
TdiAddressIp->in_addr= NULL_IP_ADDR;
|
|
RtlZeroMemory(TdiAddressIp->sin_zero, sizeof(TdiAddressIp->sin_zero));
|
|
|
|
RtlInitUnicodeString(&unicodeString, TdiProviderName);
|
|
|
|
KeAcquireSpinLock(&SpUdpLock, &SpUdpOldIrql);
|
|
|
|
if (SpUdpNetworkState != SpUdpNetworkDisconnected) {
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
SpMemFree(ea);
|
|
return((SpUdpNetworkState == SpUdpNetworkConnected) ? STATUS_SUCCESS : STATUS_PENDING);
|
|
}
|
|
|
|
ASSERT(SpUdpDatagramHandle == NULL);
|
|
ASSERT(SpUdpDatagramFileObject == NULL);
|
|
ASSERT(SpUdpDatagramDeviceObject == NULL);
|
|
ASSERT(SpUdpActiveRefCount == 0);
|
|
|
|
//
|
|
// Set the initial active refcount to 2. One reference will be removed
|
|
// when the network is successfully brought online. The other will be
|
|
// removed when the network is to be taken offline. Also increment the
|
|
// base refcount to account for the active refcount. Change to
|
|
// the online pending state.
|
|
//
|
|
SpUdpActiveRefCount = 2;
|
|
SpUdpNetworkState = SpUdpNetworkConnecting;
|
|
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
|
|
//
|
|
// Prepare for opening the address object.
|
|
//
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE, // attributes
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Perform the actual open of the address object.
|
|
//
|
|
status = ZwCreateFile(
|
|
&addressHandle,
|
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
|
&objectAttributes,
|
|
&iosb, // returned status information.
|
|
0, // block size (unused).
|
|
0, // file attributes.
|
|
0, // not shareable
|
|
FILE_CREATE, // create disposition.
|
|
0, // create options.
|
|
ea,
|
|
eaBufferLength
|
|
);
|
|
|
|
SpMemFree(ea);
|
|
ea = NULL;
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to open address for UDP, status %lx.\n", status));
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Get a pointer to the file object of the address.
|
|
//
|
|
status = ObReferenceObjectByHandle(
|
|
addressHandle,
|
|
0L, // DesiredAccess
|
|
NULL,
|
|
KernelMode,
|
|
&addressFileObject,
|
|
NULL
|
|
);
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to reference address handle, status %lx.\n", status));
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Remember the device object to which we need to give requests for
|
|
// this address object. We can't just use the fileObject->DeviceObject
|
|
// pointer because there may be a device attached to the transport
|
|
// protocol.
|
|
//
|
|
addressDeviceObject = IoGetRelatedDeviceObject(addressFileObject);
|
|
|
|
//
|
|
// Get the transport provider info
|
|
//
|
|
queryInfo.QueryType = TDI_QUERY_PROVIDER_INFO;
|
|
queryInfo.RequestConnectionInformation = NULL;
|
|
|
|
status = SpUdpIssueDeviceControl(
|
|
addressDeviceObject,
|
|
TDI_QUERY_INFORMATION,
|
|
&queryInfo,
|
|
sizeof(queryInfo),
|
|
&providerInfo,
|
|
sizeof(providerInfo)
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to get provider info, status %lx\n", status));
|
|
goto error_exit;
|
|
}
|
|
|
|
if (!(providerInfo.ServiceFlags & TDI_SERVICE_CONNECTIONLESS_MODE)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Provider doesn't support datagrams!\n"));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Set up indication handlers on the address object. We are eligible
|
|
// to receive indications as soon as we do this.
|
|
//
|
|
status = SpUdpTdiSetEventHandler(
|
|
addressFileObject,
|
|
addressDeviceObject,
|
|
TDI_EVENT_ERROR,
|
|
SpUdpTdiErrorHandler,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Setting TDI_EVENT_ERROR failed: %lx\n", status));
|
|
goto error_exit;
|
|
}
|
|
|
|
status = SpUdpTdiSetEventHandler(
|
|
addressFileObject,
|
|
addressDeviceObject,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
SpUdpTdiReceiveDatagramHandler,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Setting TDI_EVENT_RECEIVE_DATAGRAM failed: %lx\n", status));
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Finish transition to online state. Note that an offline request
|
|
// could have been issued in the meantime.
|
|
//
|
|
KeAcquireSpinLock(&SpUdpLock, &SpUdpOldIrql);
|
|
|
|
SpUdpDatagramHandle = addressHandle;
|
|
addressHandle = NULL;
|
|
SpUdpDatagramFileObject = addressFileObject;
|
|
addressFileObject = NULL;
|
|
SpUdpDatagramDeviceObject = addressDeviceObject;
|
|
addressDeviceObject = NULL;
|
|
|
|
ASSERT(SpUdpActiveRefCount == 2);
|
|
SpUdpActiveRefCount--;
|
|
SpUdpNetworkState = SpUdpNetworkConnected;
|
|
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
|
|
error_exit:
|
|
|
|
if (addressFileObject != NULL) {
|
|
ObDereferenceObject(addressFileObject);
|
|
}
|
|
|
|
if (addressHandle != NULL) {
|
|
ZwClose(addressHandle);
|
|
}
|
|
|
|
SpUdpDisconnect();
|
|
|
|
return(status);
|
|
|
|
} // SpUdpConnect
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpDisconnect(
|
|
VOID
|
|
)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PSPUDP_RECEIVE_ENTRY ReceiveEntry;
|
|
|
|
KeAcquireSpinLock(&SpUdpLock, &SpUdpOldIrql);
|
|
|
|
if (SpUdpNetworkState == SpUdpNetworkDisconnected) {
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
SpUdpNetworkState = SpUdpNetworkDisconnecting;
|
|
|
|
if (SpUdpActiveRefCount != 1) {
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
return(STATUS_PENDING);
|
|
}
|
|
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
|
|
if (SpUdpDatagramFileObject != NULL) {
|
|
ObDereferenceObject(SpUdpDatagramFileObject);
|
|
}
|
|
|
|
if (SpUdpDatagramHandle != NULL) {
|
|
ZwClose(SpUdpDatagramHandle);
|
|
}
|
|
|
|
KeAcquireSpinLock(&SpUdpLock, &SpUdpOldIrql);
|
|
|
|
SpUdpDatagramFileObject = NULL;
|
|
SpUdpDatagramHandle = NULL;
|
|
SpUdpDatagramDeviceObject = NULL;
|
|
SpUdpActiveRefCount = 0;
|
|
SpUdpNetworkState = SpUdpNetworkDisconnected;
|
|
|
|
while (!IsListEmpty(&SpUdpReceiveList)) {
|
|
ListEntry = RemoveHeadList(&SpUdpReceiveList);
|
|
ReceiveEntry = CONTAINING_RECORD(ListEntry,
|
|
SPUDP_RECEIVE_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
SpMemFree(ReceiveEntry->DataBuffer);
|
|
SpMemFree(ReceiveEntry);
|
|
}
|
|
|
|
SpUdpNumReceivePackets = 0;
|
|
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // SpUdpDisconnect
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpIssueDeviceControl(
|
|
IN PDEVICE_OBJECT Device,
|
|
IN ULONG IoctlCode,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
IN PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Builds and send an IOCTL to a device and return the results
|
|
|
|
Arguments:
|
|
|
|
Device - a device on the device stack to receive the IOCTL - the
|
|
irp is always sent to the top of the stack
|
|
|
|
IoctlCode - the IOCTL to run
|
|
|
|
InputBuffer - arguments to the IOCTL
|
|
|
|
InputBufferLength - length in bytes of the InputBuffer
|
|
|
|
OutputBuffer - data returned by the IOCTL
|
|
|
|
OnputBufferLength - the size in bytes of the OutputBuffer
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
//
|
|
// Get Io to build the IRP for us
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IoctlCode,
|
|
Device,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
FALSE, // InternalDeviceIoControl
|
|
&event,
|
|
&ioStatus
|
|
);
|
|
|
|
|
|
if (!irp) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Send the IRP and wait for it to complete
|
|
//
|
|
|
|
status = IoCallDriver(Device, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
exit:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpTdiSetEventHandler(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG EventType,
|
|
IN PVOID EventHandler,
|
|
IN PVOID EventContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets up a TDI indication handler on the address object. This is done synchronously, which
|
|
shouldn't usually be an issue since TDI providers can usually complete
|
|
indication handler setups immediately.
|
|
|
|
Arguments:
|
|
|
|
FileObject - a pointer to the file object for an open connection or
|
|
address object.
|
|
|
|
DeviceObject - a pointer to the device object associated with the
|
|
file object.
|
|
|
|
EventType - the event for which the indication handler should be
|
|
called.
|
|
|
|
EventHandler - the routine to call when tghe specified event occurs.
|
|
|
|
EventContext - context which is passed to the indication routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates the status of the request.
|
|
|
|
--*/
|
|
|
|
{
|
|
TDI_REQUEST_KERNEL_SET_EVENT parameters;
|
|
NTSTATUS status;
|
|
|
|
parameters.EventType = EventType;
|
|
parameters.EventHandler = EventHandler;
|
|
parameters.EventContext = EventContext;
|
|
|
|
|
|
status = SpUdpIssueDeviceControl(
|
|
DeviceObject,
|
|
TDI_SET_EVENT_HANDLER,
|
|
¶meters,
|
|
sizeof(parameters),
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
return(status);
|
|
|
|
} // SpUdpTdiSetEventHandler
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpTdiErrorHandler(
|
|
IN PVOID TdiEventContext,
|
|
IN NTSTATUS Status
|
|
)
|
|
{
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // SpUdpTdiErrorHandler
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpTdiReceiveDatagramHandler(
|
|
IN PVOID TdiEventContext,
|
|
IN LONG SourceAddressLength,
|
|
IN PVOID SourceAddress,
|
|
IN LONG OptionsLength,
|
|
IN PVOID Options,
|
|
IN ULONG ReceiveDatagramFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP * Irp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
SPUDP_PACKET UNALIGNED * pHeader = Tsdu;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
if (SpUdpNetworkState != SpUdpNetworkConnected) {
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// Validate the CNP header.
|
|
//
|
|
if (BytesIndicated > sizeof(SPUDP_PACKET)) {
|
|
|
|
//
|
|
// Deliver the packet to the appropriate upper layer protocol.
|
|
//
|
|
status = SpUdpReceivePacketHandler(
|
|
ReceiveDatagramFlags,
|
|
BytesIndicated,
|
|
BytesAvailable,
|
|
BytesTaken,
|
|
Tsdu,
|
|
Irp
|
|
);
|
|
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Something went wrong. Drop the packet by
|
|
// indicating that we consumed it.
|
|
//
|
|
|
|
*BytesTaken = BytesAvailable;
|
|
*Irp = NULL;
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // SpUdpTdiReceiveDatagramHandler
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpReceivePacketHandler(
|
|
IN ULONG TdiReceiveDatagramFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT PULONG BytesTaken,
|
|
IN PVOID Tsdu,
|
|
OUT PIRP * Irp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PVOID DataBuffer;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
if (BytesAvailable == 0) {
|
|
|
|
*Irp = NULL;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// We need to fetch the rest of the packet before we
|
|
// can process it.
|
|
//
|
|
//
|
|
// Allocate a buffer to hold the data.
|
|
//
|
|
DataBuffer = SpMemAllocNonPagedPool(BytesAvailable);
|
|
|
|
if (DataBuffer != NULL) {
|
|
*Irp = IoAllocateIrp(SpUdpDatagramDeviceObject->StackSize, FALSE);
|
|
|
|
if (*Irp != NULL) {
|
|
|
|
PMDL mdl = IoAllocateMdl(
|
|
DataBuffer,
|
|
BytesAvailable,
|
|
FALSE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if (mdl != NULL) {
|
|
|
|
MmBuildMdlForNonPagedPool(mdl);
|
|
|
|
//
|
|
// Build the irp.
|
|
//
|
|
(*Irp)->Flags = 0;
|
|
(*Irp)->RequestorMode = KernelMode;
|
|
(*Irp)->PendingReturned = FALSE;
|
|
(*Irp)->UserIosb = NULL;
|
|
(*Irp)->UserEvent = NULL;
|
|
(*Irp)->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
(*Irp)->AssociatedIrp.SystemBuffer = NULL;
|
|
(*Irp)->UserBuffer = NULL;
|
|
(*Irp)->Tail.Overlay.Thread = 0;
|
|
(*Irp)->Tail.Overlay.OriginalFileObject = SpUdpDatagramFileObject;
|
|
(*Irp)->Tail.Overlay.AuxiliaryBuffer = NULL;
|
|
|
|
TdiBuildReceiveDatagram(
|
|
(*Irp),
|
|
SpUdpDatagramDeviceObject,
|
|
SpUdpDatagramFileObject,
|
|
SpUdpCompleteReceivePacket,
|
|
DataBuffer,
|
|
mdl,
|
|
BytesAvailable,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Make the next stack location current.
|
|
// Normally IoCallDriver would do this, but
|
|
// since we're bypassing that, we do it directly.
|
|
//
|
|
IoSetNextIrpStackLocation( *Irp );
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
}
|
|
|
|
IoFreeIrp(*Irp);
|
|
*Irp = NULL;
|
|
}
|
|
|
|
SpMemFree(DataBuffer);
|
|
DataBuffer = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Something went wrong. Drop the packet.
|
|
//
|
|
*BytesTaken += BytesAvailable;
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // SpUdpReceivePacketHandler
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpCompleteReceivePacket(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
if (Irp->IoStatus.Status == STATUS_SUCCESS) {
|
|
|
|
SpUdpProcessReceivePacket(
|
|
(ULONG)Irp->IoStatus.Information,
|
|
Context
|
|
);
|
|
|
|
IoFreeMdl(Irp->MdlAddress);
|
|
IoFreeIrp(Irp);
|
|
}
|
|
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
} // SpUdpCompleteReceivePacket
|
|
|
|
|
|
VOID
|
|
SpUdpProcessReceivePacket(
|
|
IN ULONG TsduSize,
|
|
IN PVOID Tsdu
|
|
)
|
|
{
|
|
SPUDP_PACKET UNALIGNED * header = Tsdu;
|
|
PSPUDP_RECEIVE_ENTRY ReceiveEntry;
|
|
|
|
ASSERT(TsduSize >= sizeof(SPUDP_PACKET));
|
|
|
|
if ((RtlCompareMemory(header->Signature, SetupResponseSignature, sizeof(SetupResponseSignature)) ==
|
|
sizeof(SetupResponseSignature)) &&
|
|
(SpUdpNumReceivePackets < 100)) {
|
|
|
|
//
|
|
// Put this packet on the receive list
|
|
//
|
|
ReceiveEntry = SpMemAllocNonPagedPool(sizeof(SPUDP_RECEIVE_ENTRY));
|
|
|
|
if (ReceiveEntry == NULL) {
|
|
SpMemFree(Tsdu);
|
|
return;
|
|
}
|
|
|
|
ReceiveEntry->DataBufferLength = TsduSize;
|
|
ReceiveEntry->DataBuffer = Tsdu;
|
|
|
|
KeAcquireSpinLock(&SpUdpLock, &SpUdpOldIrql);
|
|
InsertTailList(&SpUdpReceiveList, &(ReceiveEntry->ListEntry));
|
|
SpUdpNumReceivePackets++;
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
|
|
} else {
|
|
|
|
SpMemFree(Tsdu);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} // SpUdpProcessReceivePacket
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpSendDatagram(
|
|
IN PVOID SendBuffer,
|
|
IN ULONG SendBufferLength,
|
|
IN ULONG RemoteHostAddress,
|
|
IN USHORT RemoteHostPort
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PLIST_ENTRY entry;
|
|
PIRP irp;
|
|
PMDL dataMdl;
|
|
PTDI_CONNECTION_INFORMATION TdiSendDatagramInfo = NULL;
|
|
PTRANSPORT_ADDRESS TaAddress;
|
|
PTDI_ADDRESS_IP TdiAddressIp;
|
|
|
|
TdiSendDatagramInfo = SpMemAllocNonPagedPool(sizeof(TDI_CONNECTION_INFORMATION) +
|
|
sizeof(TA_IP_ADDRESS)
|
|
);
|
|
|
|
if (TdiSendDatagramInfo == NULL) {
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
RtlZeroMemory(TdiSendDatagramInfo,
|
|
sizeof(TDI_CONNECTION_INFORMATION) +
|
|
sizeof(TA_IP_ADDRESS)
|
|
);
|
|
|
|
dataMdl = IoAllocateMdl(
|
|
SendBuffer,
|
|
SendBufferLength,
|
|
FALSE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if (dataMdl == NULL) {
|
|
SpMemFree(TdiSendDatagramInfo);
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool(dataMdl);
|
|
|
|
//
|
|
// Ok, we can send the packet.
|
|
//
|
|
irp = IoAllocateIrp(SpUdpDatagramDeviceObject->StackSize, FALSE);
|
|
|
|
if (irp != NULL) {
|
|
|
|
//
|
|
// Reference the network so it can't disconnect while we are using it.
|
|
//
|
|
KeAcquireSpinLock(&SpUdpLock, &SpUdpOldIrql);
|
|
|
|
if (SpUdpNetworkState != SpUdpNetworkConnected) {
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
SpUdpActiveRefCount++;
|
|
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
|
|
//
|
|
// Set the addressing info
|
|
//
|
|
TdiSendDatagramInfo->RemoteAddressLength = sizeof(TA_IP_ADDRESS);
|
|
TdiSendDatagramInfo->RemoteAddress = (PVOID)(TdiSendDatagramInfo + 1);
|
|
TaAddress = (PTRANSPORT_ADDRESS)(TdiSendDatagramInfo->RemoteAddress);
|
|
TaAddress->TAAddressCount = 1;
|
|
TaAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
|
|
TaAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
|
|
TdiAddressIp = (PTDI_ADDRESS_IP)(&(TaAddress->Address[0].Address[0]));
|
|
TdiAddressIp->in_addr = RemoteHostAddress;
|
|
TdiAddressIp->sin_port= htons(RemoteHostPort);
|
|
RtlZeroMemory(TdiAddressIp->sin_zero, sizeof(TdiAddressIp->sin_zero));
|
|
|
|
//
|
|
// Build the irp.
|
|
//
|
|
irp->Flags = 0;
|
|
irp->RequestorMode = KernelMode;
|
|
irp->PendingReturned = FALSE;
|
|
|
|
irp->UserIosb = NULL;
|
|
irp->UserEvent = NULL;
|
|
|
|
irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
|
|
irp->AssociatedIrp.SystemBuffer = NULL;
|
|
irp->UserBuffer = NULL;
|
|
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
irp->Tail.Overlay.OriginalFileObject = SpUdpDatagramFileObject;
|
|
irp->Tail.Overlay.AuxiliaryBuffer = NULL;
|
|
|
|
TdiBuildSendDatagram(
|
|
irp,
|
|
SpUdpDatagramDeviceObject,
|
|
SpUdpDatagramFileObject,
|
|
SpUdpCompleteSendDatagram,
|
|
TdiSendDatagramInfo,
|
|
dataMdl,
|
|
SendBufferLength,
|
|
TdiSendDatagramInfo
|
|
);
|
|
|
|
//
|
|
// Now send the packet.
|
|
//
|
|
IoCallDriver(
|
|
SpUdpDatagramDeviceObject,
|
|
irp
|
|
);
|
|
|
|
return(STATUS_PENDING);
|
|
}
|
|
|
|
IoFreeMdl(dataMdl);
|
|
SpMemFree(TdiSendDatagramInfo);
|
|
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
|
|
} // SpUdpSendDatagram
|
|
|
|
|
|
NTSTATUS
|
|
SpUdpCompleteSendDatagram(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PMDL dataMdl;
|
|
|
|
dataMdl = Irp->MdlAddress;
|
|
Irp->MdlAddress = NULL;
|
|
|
|
//
|
|
// Remove the active reference we put on.
|
|
//
|
|
KeAcquireSpinLock(&SpUdpLock, &SpUdpOldIrql);
|
|
|
|
SpUdpActiveRefCount--;
|
|
|
|
if (SpUdpNetworkState == SpUdpNetworkDisconnecting) {
|
|
SpUdpDisconnect();
|
|
}
|
|
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
|
|
//
|
|
// Free the TDI address buffer
|
|
//
|
|
SpMemFree(Context);
|
|
|
|
//
|
|
// Free the Irp
|
|
//
|
|
IoFreeIrp(Irp);
|
|
|
|
//
|
|
// Free the MDL chain
|
|
//
|
|
IoFreeMdl(dataMdl);
|
|
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
} // SpUdpCompleteSendPacket
|
|
|
|
NTSTATUS
|
|
SpUdpSendAndReceiveDatagram(
|
|
IN PVOID SendBuffer,
|
|
IN ULONG SendBufferLength,
|
|
IN ULONG RemoteHostAddress,
|
|
IN USHORT RemoteHostPort,
|
|
IN SPUDP_RECEIVE_FN SpUdpReceiveFunc
|
|
)
|
|
{
|
|
LARGE_INTEGER DelayTime;
|
|
ULONG SendTries;
|
|
ULONG RcvTries;
|
|
PLIST_ENTRY ListEntry;
|
|
PSPUDP_RECEIVE_ENTRY ReceiveEntry;
|
|
NTSTATUS Status;
|
|
|
|
DelayTime.QuadPart = -10*1000*1; // 10 millisecond (wake up at next tick)
|
|
|
|
for (SendTries=0; SendTries < 15; SendTries++) {
|
|
|
|
SpUdpSendDatagram(SendBuffer,
|
|
SendBufferLength,
|
|
RemoteHostAddress,
|
|
RemoteHostPort
|
|
);
|
|
|
|
//
|
|
// Wait for 1 second for a response
|
|
//
|
|
for (RcvTries=0; RcvTries < 400; ) {
|
|
|
|
KeAcquireSpinLock(&SpUdpLock, &SpUdpOldIrql);
|
|
if (!IsListEmpty(&SpUdpReceiveList)) {
|
|
|
|
SpUdpNumReceivePackets--;
|
|
ListEntry = RemoveHeadList(&SpUdpReceiveList);
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
|
|
ReceiveEntry = CONTAINING_RECORD(ListEntry,
|
|
SPUDP_RECEIVE_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
Status = (*SpUdpReceiveFunc)(ReceiveEntry->DataBuffer, ReceiveEntry->DataBufferLength);
|
|
|
|
SpMemFree(ReceiveEntry->DataBuffer);
|
|
SpMemFree(ReceiveEntry);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
} else {
|
|
|
|
KeReleaseSpinLock(&SpUdpLock, SpUdpOldIrql);
|
|
|
|
RcvTries++;
|
|
|
|
KeDelayExecutionThread(KernelMode, FALSE, &DelayTime);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
}
|