|
|
/*******************************************************************
* * Copyright (c) 1998-1999 Microsoft Corporation * * DESCRIPTION: CTDI.C - Common TDI layer, for NT * * AUTHOR: Stan Adermann (StanA) * * DATE:9/29/1998 * *******************************************************************/
/** include files **/
#include "raspptp.h"
#include "bpool.h"
#if VER_PRODUCTVERSION_W >= 0x0500
#define IP_ROUTE_REFCOUNT
#endif
/** local definitions **/
typedef enum { CTDI_REF_CONNECT = 0, CTDI_REF_ASSOADDR, CTDI_REF_SETEVENT, CTDI_REF_ADDRREF, CTDI_REF_LIST, CTDI_REF_REPLENISH, CTDI_REF_DISASSO, CTDI_REF_DISCONNECT, CTDI_REF_RECVDG, CTDI_REF_SEND, CTDI_REF_SENDDG, CTDI_REF_QUERY, CTDI_REF_INLISTEN, CTDI_REF_INITIAL, CTDI_REF_UNKNOWN, CTDI_REF_MAX } CTDI_REF;
#if DBG
#define REFERENCE_OBJECT_EX(o, index) \
{ \ NdisInterlockedIncrement(&(o)->arrRef[index]); \ REFERENCE_OBJECT(o); \ }
#define DEREFERENCE_OBJECT_EX(o, index) \
{ \ NdisInterlockedDecrement(&(o)->arrRef[index]); \ DEREFERENCE_OBJECT(o); \ }
#define CTDI_F_BUILD_ASSOCADDR 0x00000001
#define CTDI_F_ASSOCADDR_CALLBACK 0x00000002
#define CTDI_F_ACCEPT 0x00000004
#define CTDI_F_CONNECTCOMP_CALLBACK 0x00000008
#define CTDI_F_DISCONNECT_CALLBACK 0x00000010
#define CTDI_F_DISCONNECT 0x00000020
#define CTDI_F_BUILD_DISCONNECT_1 0x00000040
#define CTDI_F_BUILD_DISCONNECT_2 0x00000080
#define CTDI_F_DISCONNECTCOMP_CALLBACK 0x00000100
#define CTDI_F_DISCONNECT_CLEANUP 0x00000200
#define CTDI_F_BUILD_DISASSOC 0x00001000
#define CTDI_F_DISASSOC_CALLBACK 0x00002000
#else
#define REFERENCE_OBJECT_EX(o, index) \
{ \ REFERENCE_OBJECT(o); \ }
#define DEREFERENCE_OBJECT_EX(o, index) \
{ \ DEREFERENCE_OBJECT(o); \ } #endif
#define CTDI_SIGNATURE 'IDTC'
#define NUM_TCP_LISTEN 5
#define CTDI_UNKNOWN 'NKNU'
#define CTDI_ENDPOINT 'PDNE'
#define CTDI_DATAGRAM 'MRGD'
#define CTDI_LISTEN 'TSIL'
#define CTDI_CONNECTION 'NNOC'
#define PROBE 0
#define IS_CTDI(c) ((c) && (c)->Signature==CTDI_SIGNATURE)
typedef struct CTDI_DATA * PCTDI_DATA;
typedef struct CTDI_DATA { LIST_ENTRY ListEntry; ULONG Signature; ULONG Type; REFERENCE_COUNT Reference; HANDLE hFile; PFILE_OBJECT pFileObject; NDIS_SPIN_LOCK Lock; BOOLEAN Closed; BOOLEAN CloseReqPending;
CTDI_EVENT_CONNECT_QUERY ConnectQueryCallback; CTDI_EVENT_CONNECT_COMPLETE ConnectCompleteCallback; CTDI_EVENT_DISCONNECT DisconnectCallback; CTDI_EVENT_RECEIVE RecvCallback; PVOID RecvContext; CTDI_EVENT_RECEIVE_DATAGRAM RecvDatagramCallback; CTDI_EVENT_SEND_COMPLETE SendCompleteCallback; CTDI_EVENT_QUERY_COMPLETE QueryCompleteCallback; CTDI_EVENT_SET_COMPLETE SetCompleteCallback;
union { struct { PVOID Context; LIST_ENTRY ConnectList; ULONG NumConnection; } Listen; struct { PVOID Context; PCTDI_DATA LocalEndpoint; PVOID ConnectInfo; TA_IP_ADDRESS RemoteAddress; LIST_ENTRY ListEntry; ULONG DisconnectCount; union { BOOLEAN Disconnect; ULONG_PTR Padding1; }; union { BOOLEAN Abort; ULONG_PTR Padding2; }; } Connection; struct { BUFFERPOOL RxPool; } Datagram; };
#if DBG
ULONG arrRef[16]; ULONG DbgFlags; BOOLEAN bRef; #endif
} CTDI_DATA, *PCTDI_DATA;
#if DBG
#define SET_DBGFLAG(_p, _f) (_p)->DbgFlags |= (_f)
#else
#define SET_DBGFLAG(_p, _f)
#endif
typedef struct { PVOID Context; CTDI_EVENT_SEND_COMPLETE pSendCompleteCallback; } CTDI_SEND_CONTEXT, *PCTDI_SEND_CONTEXT;
typedef struct { PVOID Context; CTDI_EVENT_QUERY_COMPLETE pQueryCompleteCallback; } CTDI_QUERY_CONTEXT, *PCTDI_QUERY_CONTEXT;
typedef struct { PVOID Context; PVOID DatagramContext; CTDI_EVENT_SEND_COMPLETE pSendCompleteCallback; TDI_CONNECTION_INFORMATION TdiConnectionInfo; TA_IP_ADDRESS Ip; } CTDI_SEND_DATAGRAM_CONTEXT, *PCTDI_SEND_DATAGRAM_CONTEXT;
#define BLOCKS_NEEDED_FOR_SIZE(BlockSize, Size) ((Size)/(BlockSize) + ((((Size)/(BlockSize))*(BlockSize) < (Size)) ? 1 : 0 ))
#define NUM_STACKS_FOR_CONTEXT(ContextSize) \
BLOCKS_NEEDED_FOR_SIZE(sizeof(IO_STACK_LOCATION), (ContextSize))
STATIC PVOID __inline GetContextArea( PIRP pIrp, ULONG ContextSize ) { #if 0
ULONG i; for (i=0; i<BLOCKS_NEEDED_FOR_SIZE(sizeof(IO_STACK_LOCATION), ContextSize); i++) IoSetNextIrpStackLocation(pIrp); #else
ULONG NumStacks = BLOCKS_NEEDED_FOR_SIZE(sizeof(IO_STACK_LOCATION), ContextSize); pIrp->CurrentLocation -= (CHAR)NumStacks; pIrp->Tail.Overlay.CurrentStackLocation -= NumStacks; #endif
ASSERT(BLOCKS_NEEDED_FOR_SIZE(sizeof(IO_STACK_LOCATION), ContextSize)<=2); return IoGetCurrentIrpStackLocation(pIrp); }
#define GET_CONTEXT(Irp, Context) (Context*)GetContextArea((Irp), sizeof(Context))
STATIC VOID __inline ReleaseContextArea( PIRP pIrp, ULONG ContextSize ) { ULONG NumStacks = BLOCKS_NEEDED_FOR_SIZE(sizeof(IO_STACK_LOCATION), ContextSize) - 1; pIrp->CurrentLocation += (CHAR)NumStacks; pIrp->Tail.Overlay.CurrentStackLocation += NumStacks; }
#define RELEASE_CONTEXT(Irp, Context) ReleaseContextArea((Irp), sizeof(Context))
typedef struct { LIST_ENTRY ListEntry; REFERENCE_COUNT Reference; ULONG IpAddress; BOOLEAN ExternalRoute; } CTDI_ROUTE, *PCTDI_ROUTE;
typedef struct { LIST_ENTRY ListEntry; IPNotifyData Data; } CTDI_ROUTE_NOTIFY, *PCTDI_ROUTE_NOTIFY; /* default settings */
/** external functions **/
/** external data **/
/** public data **/
LIST_ENTRY CtdiList; LIST_ENTRY CtdiFreeList; LIST_ENTRY CtdiRouteList; LIST_ENTRY CtdiRouteNotifyList; NDIS_SPIN_LOCK CtdiListLock; HANDLE hTcp = 0; PFILE_OBJECT pFileTcp = NULL; HANDLE hIp = 0; PFILE_OBJECT pFileIp = NULL;
ULONG CtdiTcpDisconnectTimeout = 30; // Seconds
ULONG CtdiTcpConnectTimeout = 30;
/** private data **/ BOOLEAN fCtdiInitialized = FALSE;
CSHORT CtdiMdlFlags = 0;
/** private functions **/
NDIS_STATUS CtdiAddHostRoute( IN PTA_IP_ADDRESS pIpAddress );
NDIS_STATUS CtdiDeleteHostRoute( IN PTA_IP_ADDRESS pIpAddress );
STATIC VOID CtdipIpRequestRoutingNotification( IN ULONG IpAddress );
STATIC VOID CtdipCloseProtocol( HANDLE hFile, PFILE_OBJECT pFileObject ) { NTSTATUS NtStatus; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipCloseProtocol\n")));
ASSERT(KeGetCurrentIrql()<DISPATCH_LEVEL); if (pFileObject) { ObDereferenceObject(pFileObject); } NtStatus = ZwClose(hFile); if (NtStatus!=STATUS_SUCCESS) { DEBUGMSG(DBG_ERROR, (DTEXT("ZwClose(hFile) failed %08x\n"), NtStatus)); }
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipCloseProtocol\n"))); }
STATIC VOID CtdipDataFreeWorker( IN PPPTP_WORK_ITEM pWorkItem ) { PCTDI_DATA pCtdi; NTSTATUS NtStatus; PLIST_ENTRY ListEntry; BOOLEAN FoundEntry = FALSE; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipDataFreeWorker\n")));
while (ListEntry = MyInterlockedRemoveHeadList(&CtdiFreeList, &CtdiListLock)) { pCtdi = CONTAINING_RECORD(ListEntry, CTDI_DATA, ListEntry); if (pCtdi->Type==CTDI_DATAGRAM) { FreeBufferPool(&pCtdi->Datagram.RxPool); }
if (pCtdi->hFile) { CtdipCloseProtocol(pCtdi->hFile, pCtdi->pFileObject); pCtdi->pFileObject = NULL; pCtdi->hFile = NULL; }
NdisFreeSpinLock(&pCtdi->Lock); pCtdi->Signature = 0;
if(pCtdi->Type == CTDI_LISTEN) { if(pCtdi->CloseReqPending) { // TapiClose pended this request, complete it now
DEBUGMSG(DBG_TDI, (DTEXT("Complete TapiClose request\n"))); ASSERT(pgAdapter); NdisMSetInformationComplete(pgAdapter->hMiniportAdapter, NDIS_STATUS_SUCCESS); } } MyMemFree(pCtdi, sizeof(CTDI_DATA)); }
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipDataFreeWorker\n"))); }
STATIC VOID CtdipDataFree( PCTDI_DATA pCtdi ) // This should only be called by DEREFERENCE_OBJECT
{ DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipDataFree\n"))); NdisAcquireSpinLock(&CtdiListLock); RemoveEntryList(&pCtdi->ListEntry); InsertTailList(&CtdiFreeList, &pCtdi->ListEntry);
#if DBG
if(pCtdi->bRef) { ASSERT(pCtdi->DbgFlags & CTDI_F_DISASSOC_CALLBACK); } #endif
pCtdi->Signature = 0; NdisReleaseSpinLock(&CtdiListLock); ScheduleWorkItem(CtdipDataFreeWorker, NULL, NULL, 0); DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipDataFree\n"))); }
STATIC PCTDI_DATA CtdipDataAlloc() { PCTDI_DATA pCtdi;
pCtdi = MyMemAlloc(sizeof(CTDI_DATA), TAG_CTDI_DATA);
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipDataAlloc\n")));
if (pCtdi) { NdisZeroMemory(pCtdi, sizeof(CTDI_DATA)); pCtdi->Signature = CTDI_SIGNATURE; pCtdi->Type = CTDI_UNKNOWN; INIT_REFERENCE_OBJECT(pCtdi, CtdipDataFree); // pair in CtdiClose
NdisAllocateSpinLock(&pCtdi->Lock); MyInterlockedInsertHeadList(&CtdiList, &pCtdi->ListEntry, &CtdiListLock); }
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipDataAlloc %08x\n"), pCtdi)); return pCtdi; }
STATIC NDIS_STATUS CtdipIpQueryRouteTable( OUT IPRouteEntry **ppQueryBuffer, OUT PULONG pQuerySize, OUT PULONG pNumRoutes ) { NDIS_STATUS Status = NDIS_STATUS_SUCCESS; ULONG NumRoutes = 20; ULONG QuerySize = 0; TCP_REQUEST_QUERY_INFORMATION_EX QueryRoute; IPRouteEntry *pQueryBuffer = NULL; PIO_STACK_LOCATION IrpSp; IO_STATUS_BLOCK IoStatusBlock; PIRP pIrp; KEVENT Event;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipIpQueryRouteTable\n")));
if (!fCtdiInitialized) { Status = NDIS_STATUS_FAILURE; goto ciqrtDone; }
// Query TCPfor the current routing table
QueryRoute.ID.toi_entity.tei_entity = CL_NL_ENTITY; QueryRoute.ID.toi_entity.tei_instance = 0; QueryRoute.ID.toi_class = INFO_CLASS_PROTOCOL; QueryRoute.ID.toi_type = INFO_TYPE_PROVIDER;
do {
QuerySize = sizeof(IPRouteEntry) * NumRoutes; QueryRoute.ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; NdisZeroMemory(&QueryRoute.Context, CONTEXT_SIZE);
pQueryBuffer = MyMemAlloc(QuerySize, TAG_CTDI_ROUTE); if (!pQueryBuffer) { // ToDo: free the new pRoute
Status = NDIS_STATUS_RESOURCES; goto ciqrtDone; }
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest(IOCTL_TCP_QUERY_INFORMATION_EX, pFileTcp->DeviceObject, &QueryRoute, sizeof(QueryRoute), pQueryBuffer, QuerySize, FALSE, &Event, &IoStatusBlock);
if (!pIrp) { Status = NDIS_STATUS_RESOURCES; goto ciqrtDone; }
IrpSp = IoGetNextIrpStackLocation(pIrp); IrpSp->FileObject = pFileTcp;
Status = IoCallDriver(pFileTcp->DeviceObject, pIrp);
if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; }
if (Status==STATUS_BUFFER_OVERFLOW) { // We have no idea of the size of the routing table and no good
// way to find out, so we just loop, increasing our buffer until
// we win or die
MyMemFree(pQueryBuffer, QuerySize); pQueryBuffer = NULL; NumRoutes *= 2; } else if (Status!=STATUS_SUCCESS) { DEBUGMSG(DBG_TDI, (DTEXT("Failed to query complete routing table %08x\n"), Status)); goto ciqrtDone; }
} while ( Status==STATUS_BUFFER_OVERFLOW );
NumRoutes = (ULONG)(IoStatusBlock.Information / sizeof(IPRouteEntry));
ciqrtDone: if (pQueryBuffer) { ASSERT(Status==NDIS_STATUS_SUCCESS); *ppQueryBuffer = pQueryBuffer; *pNumRoutes = NumRoutes; *pQuerySize = QuerySize; } else { ASSERT(Status!=NDIS_STATUS_SUCCESS); *ppQueryBuffer = NULL; *pNumRoutes = 0; *pQuerySize = 0; } DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipIpQueryRouteTable\n"))); return Status; }
NTSTATUS CtdipRouteChangeEvent( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID pContext ) { NDIS_STATUS Status; PCTDI_ROUTE_NOTIFY pNotify = pContext; ENUM_CONTEXT Enum; PLIST_ENTRY pListEntry; PCTDI_DATA pCtdi; ULONG IpAddress = pNotify->Data.Add; KIRQL Irql; IPRouteEntry *pQueryBuffer = NULL; ULONG NumRoutes = 20; ULONG QuerySize = 0; ULONG i; BOOLEAN RouteWentAway = TRUE; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipRouteChangeEvent\n")));
DEBUGMSG(DBG_TDI, (DTEXT("Route change irp for %d.%d.%d.%d completed with status %08x\n"), IPADDR(IpAddress), pIrp->IoStatus.Status));
NdisAcquireSpinLock(&CtdiListLock); RemoveEntryList(&pNotify->ListEntry); NdisReleaseSpinLock(&CtdiListLock);
if (!fCtdiInitialized) { goto crceDone; } if (pIrp->IoStatus.Status==STATUS_SUCCESS) { Status = CtdipIpQueryRouteTable(&pQueryBuffer, &QuerySize, &NumRoutes); if (Status!=NDIS_STATUS_SUCCESS) { goto crceDone; }
for (i=0; i<NumRoutes; i++) { if (pQueryBuffer[i].ire_dest == IpAddress && pQueryBuffer[i].ire_proto == IRE_PROTO_NETMGMT && pQueryBuffer[i].ire_mask == 0xFFFFFFFF) { RouteWentAway = FALSE; break; } } MyMemFree(pQueryBuffer, QuerySize);
if (RouteWentAway) { InitEnumContext(&Enum); while (pListEntry = EnumListEntry(&CtdiList, &Enum, &CtdiListLock)) { pCtdi = CONTAINING_RECORD(pListEntry, CTDI_DATA, ListEntry); if (IS_CTDI(pCtdi) && pCtdi->Type==CTDI_CONNECTION && !pCtdi->Closed && pCtdi->Connection.RemoteAddress.Address[0].Address[0].in_addr==IpAddress && pCtdi->DisconnectCallback) { DEBUGMSG(DBG_TDI, (DTEXT("Disconnecting Ctdi:%08x due to route change.\n"), pCtdi)); pCtdi->DisconnectCallback(pCtdi->Connection.Context, TRUE); } } EnumComplete(&Enum, &CtdiListLock); } else { CtdipIpRequestRoutingNotification(IpAddress); } }
crceDone: RELEASE_CONTEXT(pIrp, CTDI_ROUTE_NOTIFY); IoFreeIrp(pIrp);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipRouteChangeEvent\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
STATIC VOID CtdipIpRequestRoutingNotification( IN ULONG IpAddress ) { PLIST_ENTRY pListEntry; PIRP pIrp = NULL; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PIO_STACK_LOCATION IrpSp; PCTDI_ROUTE_NOTIFY pNotify = NULL; BOOLEAN NotifyActive = FALSE; BOOLEAN LockHeld;
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipIpRequestRoutingNotification\n")));
if (!fCtdiInitialized) { return; } NdisAcquireSpinLock(&CtdiListLock); LockHeld = TRUE; for (pListEntry = CtdiRouteNotifyList.Flink; pListEntry!=&CtdiRouteNotifyList; pListEntry = pListEntry->Flink) { pNotify = CONTAINING_RECORD(pListEntry, CTDI_ROUTE_NOTIFY, ListEntry);
if (IpAddress==pNotify->Data.Add) { DEBUGMSG(DBG_TDI, (DTEXT("Routing notification already active on %d.%d.%d.%d\n"), IPADDR(IpAddress))); NotifyActive = TRUE; } } if (!NotifyActive) { DEBUGMSG(DBG_TDI, (DTEXT("Requesting routing notification on %d.%d.%d.%d\n"), IPADDR(IpAddress)));
pIrp = IoAllocateIrp((CCHAR)(pFileIp->DeviceObject->StackSize + NUM_STACKS_FOR_CONTEXT(sizeof(CTDI_ROUTE_NOTIFY))), FALSE);
if (!pIrp) { Status = NDIS_STATUS_RESOURCES; goto crrnDone; }
pNotify = GET_CONTEXT(pIrp, CTDI_ROUTE_NOTIFY);
//
// Setup IRP stack location to forward IRP to IP
// Must be METHOD_BUFFERED or we are not setting it up correctly
//
ASSERT ( (IOCTL_IP_RTCHANGE_NOTIFY_REQUEST & 0x03)==METHOD_BUFFERED ); pIrp->AssociatedIrp.SystemBuffer = &pNotify->Data;
IrpSp = IoGetNextIrpStackLocation(pIrp); IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; IrpSp->MinorFunction = 0; IrpSp->Flags = 0; IrpSp->Control = 0; IrpSp->FileObject = pFileIp; IrpSp->DeviceObject = pFileIp->DeviceObject; IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(IPNotifyData); IrpSp->Parameters.DeviceIoControl.OutputBufferLength = 0; IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_IP_RTCHANGE_NOTIFY_REQUEST; IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
IoSetCompletionRoutine(pIrp, CtdipRouteChangeEvent, pNotify, TRUE, TRUE, TRUE);
pNotify->Data.Version = 0; pNotify->Data.Add = IpAddress;
InsertTailList(&CtdiRouteNotifyList, &pNotify->ListEntry); LockHeld = FALSE; NdisReleaseSpinLock(&CtdiListLock); (void)IoCallDriver(pFileIp->DeviceObject, pIrp); pIrp = NULL; }
crrnDone: if (LockHeld) { NdisReleaseSpinLock(&CtdiListLock); } if (pIrp) { IoFreeIrp(pIrp); } DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipIpRequestRoutingNotification\n"))); }
STATIC VOID CtdipScheduleAddHostRoute( PPPTP_WORK_ITEM pWorkItem ) { PCTDI_DATA pCtdi = pWorkItem->Context; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipScheduleAddHostRoute\n")));
CtdiAddHostRoute(&pCtdi->Connection.RemoteAddress);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipScheduleAddHostRoute\n"))); }
STATIC NTSTATUS CtdipConnectCompleteCallback( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context ) { PCTDI_DATA pCtdi = Context; NDIS_STATUS NdisStatus;
PTDI_CONNECTION_INFORMATION pRequestInfo = NULL; PTA_IP_ADDRESS pRequestAddress = NULL; PTDI_CONNECTION_INFORMATION pReturnInfo = NULL; PTA_IP_ADDRESS pReturnAddress = NULL; PBOOLEAN pInboundFlag = NULL; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipConnectCompleteCallback %08x\n"), pIrp->IoStatus.Status));
SET_DBGFLAG(pCtdi, CTDI_F_CONNECTCOMP_CALLBACK);
pRequestInfo = pCtdi->Connection.ConnectInfo;
pRequestAddress = (PTA_IP_ADDRESS)((PUCHAR)(pRequestInfo + 1) + sizeof(PVOID)); (ULONG_PTR)pRequestAddress &= ~((ULONG_PTR)sizeof(PVOID) - 1);
pReturnInfo = (PTDI_CONNECTION_INFORMATION) ((PUCHAR)(pRequestAddress + 1) + sizeof(PVOID));
(ULONG_PTR)pReturnInfo &= ~((ULONG_PTR)sizeof(PVOID) - 1);
pReturnAddress = (PTA_IP_ADDRESS)((PUCHAR)(pReturnInfo + 1) + sizeof(PVOID));
(ULONG_PTR)pReturnAddress &= ~((ULONG_PTR)sizeof(PVOID) - 1);
pInboundFlag = (PBOOLEAN)(pReturnAddress + 1);
// Connection complete. Tell the client.
if (pIrp->IoStatus.Status==STATUS_SUCCESS) { pCtdi->Connection.RemoteAddress = *pReturnAddress; ScheduleWorkItem(CtdipScheduleAddHostRoute, pCtdi, NULL, 0);
if (*pInboundFlag) { NdisInterlockedIncrement(&Counters.InboundConnectComplete); } else { NdisInterlockedIncrement(&Counters.OutboundConnectComplete); } }
MyMemFree(pRequestInfo, 2*(sizeof(TDI_CONNECTION_INFORMATION)+sizeof(TA_IP_ADDRESS)) + sizeof(BOOLEAN) + 3*sizeof(PVOID) ); pCtdi->Connection.ConnectInfo = NULL;
if (pCtdi->ConnectCompleteCallback) { // Report status and give them the new handle if we succeeded.
NdisStatus = pCtdi->ConnectCompleteCallback(pCtdi->Connection.Context, (pIrp->IoStatus.Status ? 0 : (HANDLE)pCtdi), pIrp->IoStatus.Status); if (NdisStatus!=NDIS_STATUS_SUCCESS || pIrp->IoStatus.Status!=STATUS_SUCCESS) { CtdiDisconnect(pCtdi, FALSE); CtdiClose(pCtdi); } } else { // We assume that if there's no ConnectCompleteCallback, that this is
// probably a listen, we've already given the handle for this, and
// we don't want to close it ourselves. Instead, we'll do a disconnect
// indication and allow the upper layer to clean up.
if (pIrp->IoStatus.Status!=STATUS_SUCCESS && !pCtdi->Closed && pCtdi->DisconnectCallback) { pCtdi->DisconnectCallback(pCtdi->Connection.Context, TRUE); } }
IoFreeIrp(pIrp);
DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_CONNECT); // Pair in CtdiConnect
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipConnectCompleteCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
STATIC NTSTATUS CtdipAssociateAddressCallback( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context ) { PCTDI_DATA pConnect = Context; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipAssociateAddressCallback\n")));
DEBUGMSG(DBG_TDI, (DTEXT("TDI_ASSOCIATE_ADDRESS Sts:%08x\n"), pIrp->IoStatus.Status));
// ToDo: What cleanup do we need to do if this fails?
SET_DBGFLAG(pConnect, CTDI_F_ASSOCADDR_CALLBACK); //ASSERT(NT_SUCCESS(pIrp->IoStatus.Status));
IoFreeIrp(pIrp); DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_ASSOADDR); // Pair in CtdipAddListenConnection and also in CtdiConnect
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipAssociateAddressCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
// This function expects the CtdiListLock to be held.
PCTDI_ROUTE CtdipFindRoute( ULONG IpAddress ) { PCTDI_ROUTE pRoute = NULL; PLIST_ENTRY pListEntry;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipFindRoute\n")));
for (pListEntry = CtdiRouteList.Flink; pListEntry != &CtdiRouteList; pListEntry = pListEntry->Flink) { pRoute = CONTAINING_RECORD(pListEntry, CTDI_ROUTE, ListEntry); if (pRoute->IpAddress==IpAddress) { // Found the route, return it.
goto cfrDone; } } pRoute = NULL;
cfrDone: DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipFindRoute %08x\n"), pRoute)); return pRoute; }
STATIC NTSTATUS CtdipSetEventCallback( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context ) { PCTDI_DATA pConnect = Context; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipSetEventCallback\n")));
DEBUGMSG(DBG_TDI, (DTEXT("TDI_SET_EVENT_HANDLER Sts:%08x\n"), pIrp->IoStatus.Status));
// ToDo: What cleanup do we need to do if this fails?
IoFreeIrp(pIrp); DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_SETEVENT); // Pair in CtdipSetEventHandler
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipSetEventCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
STATIC NDIS_STATUS CtdipSetEventHandler( IN PCTDI_DATA pCtdi, IN ULONG ulEventType, IN PVOID pEventHandler ) { PIRP pIrp; NDIS_STATUS ReturnStatus = NDIS_STATUS_SUCCESS; NTSTATUS NtStatus;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipSetEventHandler\n"))); if (!IS_CTDI(pCtdi)) { DEBUGMSG(DBG_ERROR, (DTEXT("Ctdi: Bad handle %08x\n"), pCtdi)); ReturnStatus = NDIS_STATUS_FAILURE; goto cpsehDone; }
// This should be the Address context ToDo: is this always true?
pIrp = IoAllocateIrp(pCtdi->pFileObject->DeviceObject->StackSize, FALSE); if (!pIrp) { ReturnStatus = NDIS_STATUS_RESOURCES; goto cpsehDone; }
REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_SETEVENT); // Pair in CtdipSetEventCallback
TdiBuildSetEventHandler(pIrp, pCtdi->pFileObject->DeviceObject, pCtdi->pFileObject, CtdipSetEventCallback, pCtdi, ulEventType, pEventHandler, pCtdi);
DEBUGMSG(DBG_TDI, (DTEXT("IoCallDriver TDI_SET_EVENT_HANDLER\n")));
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pCtdi->pFileObject->DeviceObject, pIrp);
ReturnStatus = STATUS_SUCCESS;
cpsehDone: DEBUGMSG(DBG_FUNC|DBG_ERR(ReturnStatus), (DTEXT("-CtdipSetEventHandler\n"))); return ReturnStatus; }
STATIC NDIS_STATUS CtdipAddListenConnection( IN PCTDI_DATA pEndpoint ) { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; PIRP pIrp; UNICODE_STRING DeviceName;
UCHAR EaBuffer[sizeof(FILE_FULL_EA_INFORMATION) + TDI_CONNECTION_CONTEXT_LENGTH + sizeof(PVOID)]; PFILE_FULL_EA_INFORMATION pEa = (PFILE_FULL_EA_INFORMATION)EaBuffer; PVOID UNALIGNED *ppContext;
NDIS_STATUS ReturnStatus = NDIS_STATUS_SUCCESS; PCTDI_DATA pConnect = CtdipDataAlloc();
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipAddListenConnection\n")));
if (!pConnect) { ReturnStatus = NDIS_STATUS_RESOURCES; goto calcDone; }
pConnect->Type = CTDI_CONNECTION; pConnect->Connection.LocalEndpoint = pEndpoint;
pConnect->RecvCallback = pEndpoint->RecvCallback; pConnect->DisconnectCallback = pEndpoint->DisconnectCallback;
DeviceName.Length = sizeof(DD_TCP_DEVICE_NAME) - sizeof(WCHAR); DeviceName.Buffer = DD_TCP_DEVICE_NAME;
InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL);
NdisZeroMemory(pEa, sizeof(EaBuffer)); pEa->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH; pEa->EaValueLength = sizeof(PVOID); NdisMoveMemory(pEa->EaName, TdiConnectionContext, TDI_CONNECTION_CONTEXT_LENGTH);
ppContext = (PVOID UNALIGNED*) (pEa->EaName + TDI_CONNECTION_CONTEXT_LENGTH + 1);
*ppContext = pConnect;
NdisZeroMemory(&IoStatusBlock, sizeof(IoStatusBlock));
NtStatus = ZwCreateFile(&pConnect->hFile, /* FileHandle */ FILE_READ_DATA | FILE_WRITE_DATA, /* Desired Access */ &ObjectAttributes, /* Object Attributes */ &IoStatusBlock, /* IO Status Block */ NULL, /* Allocation Size */ FILE_ATTRIBUTE_NORMAL, /* File Attributes */ 0, /* Share Access */ FILE_OPEN, /* Create Disposition */ 0, /* Create Options */ pEa, /* EaBuffer */ sizeof(EaBuffer) /* EaLength */ );
if (NtStatus!=STATUS_SUCCESS) { ReturnStatus = NtStatus; goto calcDone; }
// Convert the address file handle to a FILE_OBJECT
NtStatus = ObReferenceObjectByHandle(pConnect->hFile, /* Handle */ 0, /* DesiredAccess */ NULL, /* ObjectType */ KernelMode, /* AccessMode */ &pConnect->pFileObject, /* Object */ NULL /* HandleInfo */ );
if (NtStatus != STATUS_SUCCESS) { ReturnStatus = NtStatus; goto calcDone; }
// Make an irp to associate the endpoint and connection.
pIrp = IoAllocateIrp(pConnect->pFileObject->DeviceObject->StackSize, FALSE); if (!pIrp) { ReturnStatus = NDIS_STATUS_RESOURCES; goto calcDone; }
REFERENCE_OBJECT_EX(pConnect, CTDI_REF_ASSOADDR); // Pair in CtdipAssociateAddressCallback
TdiBuildAssociateAddress(pIrp, pConnect->pFileObject->DeviceObject, pConnect->pFileObject, CtdipAssociateAddressCallback, pConnect, pEndpoint->hFile);
DEBUGMSG(DBG_TDI, (DTEXT("IoCallDriver TDI_ASSOCIATE_ADDRESS\n")));
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pConnect->pFileObject->DeviceObject, pIrp);
// Associate address creates a reference from the connection to the endpoint.
REFERENCE_OBJECT_EX(pEndpoint, CTDI_REF_ADDRREF); // Pair in CtdipDisassociateAddressCallback
SET_DBGFLAG(pConnect, CTDI_F_BUILD_ASSOCADDR); #if DBG
pConnect->bRef = TRUE; #endif
// It's ready. Put it on the list.
REFERENCE_OBJECT_EX(pEndpoint, CTDI_REF_LIST); //Pair in CtdipConnectCallback
REFERENCE_OBJECT_EX(pConnect, CTDI_REF_LIST); //Pair in CtdipConnectCallback
MyInterlockedInsertTailList(&pEndpoint->Listen.ConnectList, &pConnect->Connection.ListEntry, &pEndpoint->Lock);
NdisInterlockedIncrement(&pEndpoint->Listen.NumConnection); // This pConnect should now be an active TCP listen.
calcDone: if (!NT_SUCCESS(ReturnStatus)) { if (pConnect) { // Any failure means no associate address. don't disassociate.
// It also means it's not attached to the listen.
CtdiClose(pConnect); } } DEBUGMSG(DBG_FUNC|DBG_ERR(ReturnStatus), (DTEXT("-CtdipAddListenConnection %08x\n"), ReturnStatus)); return ReturnStatus; }
STATIC VOID CtdipReplenishListens( IN PPPTP_WORK_ITEM pWorkItem ) { PCTDI_DATA pEndpoint = pWorkItem->Context; ULONG i; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipReplenishListens\n")));
for (i=pEndpoint->Listen.NumConnection; i<NUM_TCP_LISTEN; i++) { CtdipAddListenConnection(pEndpoint); }
DEREFERENCE_OBJECT_EX(pEndpoint, CTDI_REF_REPLENISH); // Pair in CtdipConnectCallback
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipReplenishListens\n"))); }
STATIC NTSTATUS CtdipConnectCallback( IN PVOID TdiEventContext, IN LONG RemoteAddressLength, IN PVOID RemoteAddress, IN LONG UserDataLength, IN PVOID UserData, IN LONG OptionsLength, IN PVOID Options, OUT CONNECTION_CONTEXT *ConnectionContext, OUT PIRP *AcceptIrp ) { NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED; NDIS_STATUS NdisStatus; PTRANSPORT_ADDRESS pAddress = (PTRANSPORT_ADDRESS)RemoteAddress; PCTDI_DATA pCtdi = (PCTDI_DATA)TdiEventContext; PCTDI_DATA pConnect = NULL; UINT i; PIRP pIrp = NULL; PTDI_CONNECTION_INFORMATION pRequestInfo = NULL; PTDI_CONNECTION_INFORMATION pReturnInfo = NULL; PTA_IP_ADDRESS pRemoteAddress; PVOID pNewContext; PLIST_ENTRY pListEntry = NULL; PBOOLEAN pInboundFlag;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipConnectCallback\n")));
NdisInterlockedIncrement(&Counters.InboundConnectAttempts);
if (RemoteAddressLength<sizeof(TA_IP_ADDRESS) || !RemoteAddress || pCtdi->Closed) { Status = STATUS_CONNECTION_REFUSED; goto cccDone; }
ASSERT(UserDataLength==0); ASSERT(OptionsLength==0);
// Do all the allocation we'll need at one shot.
pIrp = IoAllocateIrp(pCtdi->pFileObject->DeviceObject->StackSize, FALSE);
// No sign saying we can't allocate the request info, return info and address buffers
// in one shot.
pRequestInfo = MyMemAlloc(2*(sizeof(TDI_CONNECTION_INFORMATION)+ sizeof(TA_IP_ADDRESS)) + 3*sizeof(PVOID) + sizeof(BOOLEAN), TAG_CTDI_CONNECT_INFO); if (!pIrp || !pRequestInfo) { Status = STATUS_INSUFFICIENT_RESOURCES; goto cccDone; }
pListEntry = MyInterlockedRemoveHeadList(&pCtdi->Listen.ConnectList, &pCtdi->Lock); if (!pListEntry) { DEBUGMSG(DBG_ERROR, (DTEXT("No listen connections available.\n"))); Status = STATUS_CONNECTION_REFUSED;
REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_REPLENISH); // pair in CtdipReplenishListens
if (ScheduleWorkItem(CtdipReplenishListens, pCtdi, NULL, 0)!=NDIS_STATUS_SUCCESS) { DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_REPLENISH); // pair for above if Schedule fails
} goto cccDone; } NdisInterlockedDecrement(&pCtdi->Listen.NumConnection);
pConnect = CONTAINING_RECORD(pListEntry, CTDI_DATA, Connection.ListEntry);
// We have a double reference when an object is on the list of another object,
// and we want to release them both when we remove the item from the list,
// but in this case we also want to take a reference on the connection object,
// so one of them cancels out.
//REFERENCE_OBJECT(pConnect); // Pair in CtdiDisconnect
//DEREFERENCE_OBJECT(pConnect); // Pair in CtdipAddListenConnection
DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_LIST); // Pair in CtdipAddListenConnection
if (!pCtdi->ConnectQueryCallback || pCtdi->Closed) { Status = STATUS_CONNECTION_REFUSED; goto cccDone; } NdisStatus = pCtdi->ConnectQueryCallback(pCtdi->Listen.Context, pAddress, pConnect, &pNewContext); if (NdisStatus!=NDIS_STATUS_SUCCESS) { Status = STATUS_CONNECTION_REFUSED; goto cccDone; }
// We've got the go-ahead to accept this connection, at the TCP level.
pConnect->Connection.ConnectInfo = pRequestInfo; pConnect->Connection.Context = pNewContext; pConnect->Connection.RemoteAddress = *(PTA_IP_ADDRESS)pAddress;
NdisZeroMemory(pRequestInfo, 2*(sizeof(TDI_CONNECTION_INFORMATION)+sizeof(TA_IP_ADDRESS)) + sizeof(BOOLEAN) + 3*sizeof(PVOID));
pRequestInfo->RemoteAddressLength = sizeof(TA_IP_ADDRESS);
pRemoteAddress = (PTA_IP_ADDRESS)((PUCHAR)(pRequestInfo + 1) + sizeof(PVOID)); (ULONG_PTR)pRemoteAddress &= ~((ULONG_PTR)sizeof(PVOID) - 1);
pRequestInfo->RemoteAddress = pRemoteAddress;
*pRemoteAddress = *(PTA_IP_ADDRESS)pAddress;
pReturnInfo = (PTDI_CONNECTION_INFORMATION) ((PUCHAR)(pRemoteAddress + 1) + sizeof(PVOID));
(ULONG_PTR)pReturnInfo &= ~((ULONG_PTR)sizeof(PVOID) - 1);
pReturnInfo->RemoteAddressLength = sizeof(TA_IP_ADDRESS);
pRemoteAddress = (PTA_IP_ADDRESS)((PUCHAR)(pReturnInfo + 1) + sizeof(PVOID));
(ULONG_PTR)pRemoteAddress &= ~((ULONG_PTR)sizeof(PVOID) - 1);
pReturnInfo->RemoteAddress = pRemoteAddress;
pInboundFlag = (PBOOLEAN)(pRemoteAddress + 1); *pInboundFlag = TRUE;
// ToDo: the old PPTP driver filled in the ReturnInfo remote address.
//
pRemoteAddress->TAAddressCount = 1; pRemoteAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP; pRemoteAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
SET_DBGFLAG(pConnect, CTDI_F_ACCEPT);
TdiBuildAccept(pIrp, pConnect->pFileObject->DeviceObject, pConnect->pFileObject, CtdipConnectCompleteCallback, pConnect, // Context
pRequestInfo, pReturnInfo);
IoSetNextIrpStackLocation(pIrp);
*ConnectionContext = pConnect; *AcceptIrp = pIrp;
REFERENCE_OBJECT_EX(pConnect->Connection.LocalEndpoint, CTDI_REF_REPLENISH); // pair in CtdipReplenishListens
if (ScheduleWorkItem(CtdipReplenishListens, pConnect->Connection.LocalEndpoint, NULL, 0)!=NDIS_STATUS_SUCCESS) { DEREFERENCE_OBJECT_EX(pConnect->Connection.LocalEndpoint, CTDI_REF_REPLENISH); // pair for above if Schedule fails
}
cccDone: if (Status!=STATUS_MORE_PROCESSING_REQUIRED) { // We lose. Clean up.
if (pConnect) { // We haven't used this connection, so it is still valid. return it
// to the list, and reapply the references.
REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_LIST); //REFERENCE_OBJECT(pConnect);
MyInterlockedInsertTailList(&pCtdi->Listen.ConnectList, &pConnect->Connection.ListEntry, &pCtdi->Lock); NdisInterlockedIncrement(&pCtdi->Listen.NumConnection); } if (pIrp) { IoFreeIrp(pIrp); } if (pRequestInfo) { MyMemFree(pRequestInfo, 2*(sizeof(TDI_CONNECTION_INFORMATION)+sizeof(TA_IP_ADDRESS))); } }
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtdipConnectCallback %08x\n"), Status)); return Status; }
STATIC NTSTATUS CtdipDisassociateAddressCallback( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context ) { PCTDI_DATA pConnect = Context; PCTDI_DATA pEndpoint; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipDisassociateAddressCallback\n")));
DEBUGMSG(DBG_TDI, (DTEXT("TDI_DISASSOCIATE_ADDRESS Sts:%08x\n"), pIrp->IoStatus.Status));
// ToDo: What cleanup do we need to do if this fails?
SET_DBGFLAG(pConnect, CTDI_F_DISASSOC_CALLBACK);
IoFreeIrp(pIrp); pEndpoint = pConnect->Connection.LocalEndpoint; DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_DISASSO); // Pair in CtdipDisconnectCleanup
DEREFERENCE_OBJECT_EX(pEndpoint, CTDI_REF_ADDRREF); // Pair in CtdipAddListenConnection and CtdiConnect
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipDisassociateAddressCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
STATIC VOID CtdipDisconnectCleanup( IN PPPTP_WORK_ITEM pWorkItem ) { PCTDI_DATA pConnect = pWorkItem->Context; PIRP pIrp = NULL; NTSTATUS Status; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipDisconnectCleanup\n")));
SET_DBGFLAG(pConnect, CTDI_F_DISCONNECT_CLEANUP);
pIrp = IoAllocateIrp(pConnect->pFileObject->DeviceObject->StackSize, FALSE); if (!pIrp) { goto cdaDone; }
// Normally we would reference pConnect for making an irp, but we already
// have one for this work item, & we'll just keep it.
SET_DBGFLAG(pConnect, CTDI_F_BUILD_DISASSOC);
TdiBuildDisassociateAddress(pIrp, pConnect->pFileObject->DeviceObject, pConnect->pFileObject, CtdipDisassociateAddressCallback, pConnect); DEBUGMSG(DBG_TDI, (DTEXT("IoCallDriver TDI_DISASSOCIATE_ADDRESS\n"))); REFERENCE_OBJECT_EX(pConnect, CTDI_REF_DISASSO);
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pConnect->pFileObject->DeviceObject, pIrp);
CtdiDeleteHostRoute(&pConnect->Connection.RemoteAddress);
if (!pConnect->Closed && pConnect->DisconnectCallback) { pConnect->DisconnectCallback(pConnect->Connection.Context, pConnect->Connection.Abort); }
DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_DISCONNECT); // Pair CtdipDisconnectCallback and CtdiDisconnect
cdaDone: DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipDisconnectCleanup\n"))); }
STATIC NTSTATUS CtdipDisconnectCompleteCallback( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, PVOID Context ) { PCTDI_DATA pConnect = Context; PIO_STACK_LOCATION pIrpSp = IoGetNextIrpStackLocation(pIrp); PTDI_REQUEST_KERNEL pRequest = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters; BOOLEAN CleanupNow = FALSE;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipDisconnectCompleteCallback %08x\n"), pIrp->IoStatus.Status));
if (pRequest->RequestConnectionInformation) { // We don't do anything with this info yet
} if (pRequest->ReturnConnectionInformation) { // We don't do anything with this info yet
} if (pRequest->RequestSpecific) { // Allocated as part of irp, don't free it.
}
if (IS_CTDI(pConnect)) {
SET_DBGFLAG(pConnect, CTDI_F_DISCONNECTCOMP_CALLBACK);
// Possible to do a release AND and abort, so we'll get called here twice.
// We only want to cleanup once.
NdisAcquireSpinLock(&pConnect->Lock); CleanupNow = ((--pConnect->Connection.DisconnectCount)==0) ? TRUE : FALSE; NdisReleaseSpinLock(&pConnect->Lock);
if (!CleanupNow || ScheduleWorkItem(CtdipDisconnectCleanup, pConnect, NULL, 0)!=NDIS_STATUS_SUCCESS) { DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_DISCONNECT); // Pair CtdipDisconnectCallback and CtdiDisconnect
} }
IoFreeIrp(pIrp);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipDisconnectCompleteCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
STATIC NTSTATUS CtdipDisconnectCallback( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN LONG DisconnectDataLength, IN PVOID DisconnectData, IN LONG DisconnectInformationLength, IN PVOID DisconnectInformation, IN ULONG DisconnectFlags ) { PCTDI_DATA pConnect = (PCTDI_DATA)ConnectionContext; PCTDI_DATA pEndpoint; PIRP pIrp = NULL; PTIME pTimeout = NULL; PTDI_CONNECTION_INFORMATION pConnectInfo = NULL; NTSTATUS Status = STATUS_SUCCESS;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipDisconnectCallback\n")));
SET_DBGFLAG(pConnect, CTDI_F_DISCONNECT_CALLBACK);
if (DisconnectFlags==0) { DisconnectFlags = TDI_DISCONNECT_ABORT; } ASSERT(DisconnectFlags==TDI_DISCONNECT_RELEASE || DisconnectFlags==TDI_DISCONNECT_ABORT);
NdisAcquireSpinLock(&pConnect->Lock); if (DisconnectFlags==TDI_DISCONNECT_ABORT) { BOOLEAN CleanupNow;
pConnect->Connection.Disconnect = TRUE; pConnect->Connection.Abort = TRUE; CleanupNow = (pConnect->Connection.DisconnectCount==0) ? TRUE : FALSE; REFERENCE_OBJECT_EX(pConnect, CTDI_REF_DISCONNECT); // Pair in
NdisReleaseSpinLock(&pConnect->Lock); if (IS_CTDI(pConnect) && CleanupNow) { if (ScheduleWorkItem(CtdipDisconnectCleanup, pConnect, NULL, 0)!=NDIS_STATUS_SUCCESS) { // Schedule failed, deref now
DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_DISCONNECT); // Pair above
} } else { DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_DISCONNECT); // Pair above
} } else { if (pConnect->Connection.Disconnect) { // We've already disconnected. Ignore.
NdisReleaseSpinLock(&pConnect->Lock); } else { pConnect->Connection.Disconnect = TRUE; pConnect->Connection.DisconnectCount++;
REFERENCE_OBJECT_EX(pConnect, CTDI_REF_DISCONNECT); // Pair in CtdipDisconnectCompleteCallback
NdisReleaseSpinLock(&pConnect->Lock);
pIrp = IoAllocateIrp((CCHAR)(pConnect->pFileObject->DeviceObject->StackSize + NUM_STACKS_FOR_CONTEXT(sizeof(TIME)+sizeof(TDI_CONNECTION_INFORMATION))), FALSE);
if (!pIrp) { Status = STATUS_INSUFFICIENT_RESOURCES; DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_DISCONNECT); // Pair above
goto cdcDone; }
pTimeout = (PTIME)GetContextArea(pIrp, sizeof(TIME)+sizeof(TDI_CONNECTION_INFORMATION)); pConnectInfo = (PTDI_CONNECTION_INFORMATION)(pTimeout + 1);
pTimeout->LowPart = CtdiTcpDisconnectTimeout * -10000000L; pTimeout->HighPart = (pTimeout->LowPart) ? -1 : 0;
// Responding to a controlled disconnect, we don't provide
// TDI_CONNECTION_INFORMATION, but we request it from the peer.
SET_DBGFLAG(pConnect, CTDI_F_BUILD_DISCONNECT_1);
TdiBuildDisconnect(pIrp, pConnect->pFileObject->DeviceObject, pConnect->pFileObject, CtdipDisconnectCompleteCallback, pConnect, pTimeout, TDI_DISCONNECT_RELEASE, NULL, pConnectInfo);
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pConnect->pFileObject->DeviceObject, pIrp); } }
cdcDone: if (!NT_SUCCESS(Status)) { if (pIrp) { IoFreeIrp(pIrp); } } DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipDisconnectCallback\n"))); return STATUS_SUCCESS; }
STATIC NTSTATUS CtdipOpenProtocol( IN PUNICODE_STRING pDeviceName, IN PTRANSPORT_ADDRESS pAddress, OUT PHANDLE phFile, OUT PFILE_OBJECT *ppFileObject ) { NTSTATUS NtStatus = STATUS_SUCCESS; UCHAR EaBuffer[sizeof(FILE_FULL_EA_INFORMATION) + TDI_TRANSPORT_ADDRESS_LENGTH + sizeof(TA_IP_ADDRESS)]; PFILE_FULL_EA_INFORMATION pEa = (PFILE_FULL_EA_INFORMATION)EaBuffer; TA_IP_ADDRESS UNALIGNED *pEaTaIp; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipOpenProtocol %wZ\n"), pDeviceName));
*phFile = 0; *ppFileObject = NULL;
InitializeObjectAttributes(&ObjectAttributes, pDeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL);
NdisZeroMemory(pEa, sizeof(EaBuffer)); pEa->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; pEa->EaValueLength = sizeof(TA_IP_ADDRESS); NdisMoveMemory(pEa->EaName, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH);
pEaTaIp = (TA_IP_ADDRESS UNALIGNED*) (pEa->EaName + TDI_TRANSPORT_ADDRESS_LENGTH + 1);
*pEaTaIp = *(PTA_IP_ADDRESS)pAddress;
DEBUGMSG(DBG_TDI, (DTEXT("Endpoint: sin_port = %Xh in_addr = %Xh\n"), pEaTaIp->Address[0].Address[0].sin_port, pEaTaIp->Address[0].Address[0].in_addr));
NdisZeroMemory(&IoStatusBlock, sizeof(IoStatusBlock));
NtStatus = ZwCreateFile( phFile, /* FileHandle */ FILE_READ_DATA | FILE_WRITE_DATA, /* Desired Access */ &ObjectAttributes, /* Object Attributes */ &IoStatusBlock, /* IO Status Block */ NULL, /* Allocation Size */ FILE_ATTRIBUTE_NORMAL, /* File Attributes */ 0, /* Share Access */ FILE_OPEN, /* Create Disposition */ 0, /* Create Options */ pEa, /* EaBuffer */ sizeof(EaBuffer) /* EaLength */ );
if (NtStatus!=STATUS_SUCCESS) { DEBUGMSG(DBG_ERROR, (DTEXT("ZwCreateFile failed %08x\n"), NtStatus)); goto copDone; }
// Convert the address file handle to a FILE_OBJECT
NtStatus = ObReferenceObjectByHandle( *phFile, /* Handle */ 0, /* DesiredAccess */ NULL, /* ObjectType */ KernelMode, /* AccessMode */ ppFileObject, /* Object */ NULL /* HandleInfo */ );
copDone: if (NtStatus!=STATUS_SUCCESS && *phFile) { ZwClose(*phFile); *phFile = 0; *ppFileObject = NULL; } DEBUGMSG(DBG_FUNC|DBG_ERR(NtStatus), (DTEXT("-CtdipOpenProtocol %08x\n"), NtStatus)); return NtStatus; }
STATIC NTSTATUS CtdipReceiveCompleteCallback( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context ) { PCTDI_DATA pCtdi = Context; PUCHAR pData; ULONG Length; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipReceiveCompleteCallback\n")));
pData = MmGetMdlVirtualAddress(pIrp->MdlAddress); Length = MmGetMdlByteCount(pIrp->MdlAddress); if (pIrp->IoStatus.Status==STATUS_SUCCESS && pCtdi->RecvCallback && !pCtdi->Closed) { pCtdi->RecvCallback(pCtdi->Connection.Context, pData, Length); }
#if PROBE
MmUnlockPages(pIrp->MdlAddress); #endif
IoFreeMdl(pIrp->MdlAddress); MyMemFree(pData, Length); IoFreeIrp(pIrp);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipReceiveCompleteCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
STATIC NTSTATUS CtdipReceiveCallback( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, OUT PIRP *IoRequestPacket ) { PCTDI_DATA pCtdi = ConnectionContext; NTSTATUS NtStatus = STATUS_DATA_NOT_ACCEPTED; NDIS_STATUS Status; PUCHAR pBuffer; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipReceiveCallback\n")));
if (pCtdi->RecvCallback && !pCtdi->Closed) { if (ReceiveFlags&TDI_RECEIVE_ENTIRE_MESSAGE || BytesIndicated==BytesAvailable) { Status = pCtdi->RecvCallback(pCtdi->Connection.Context, Tsdu, BytesIndicated); // Data must be used in this call
ASSERT(Status==NDIS_STATUS_SUCCESS); NtStatus = STATUS_SUCCESS; *BytesTaken = BytesIndicated; } else { // We need an irp to receive all the data.
PIRP pIrp = IoAllocateIrp(pCtdi->pFileObject->DeviceObject->StackSize, FALSE); PUCHAR pBuffer = MyMemAlloc(BytesAvailable, TAG_CTDI_MESSAGE); PMDL pMdl = NULL;
if (pBuffer && pIrp) { pMdl = IoAllocateMdl(pBuffer, BytesAvailable, FALSE, FALSE, pIrp); if (pMdl) { #if PROBE
__try { MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess); } __except (EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl(pMdl); pMdl = NULL; } #else
MmBuildMdlForNonPagedPool(pMdl); #endif
} }
if (pMdl) { TdiBuildReceive(pIrp, pCtdi->pFileObject->DeviceObject, pCtdi->pFileObject, CtdipReceiveCompleteCallback, pCtdi, pMdl, 0, BytesAvailable);
// We're not calling IoCallDriver, so we need to set the proper
// stack location.
IoSetNextIrpStackLocation(pIrp);
*IoRequestPacket = pIrp;
*BytesTaken = 0; NtStatus = STATUS_MORE_PROCESSING_REQUIRED; } else { // Some alloc failure occurred, free everything.
NtStatus = STATUS_DATA_NOT_ACCEPTED; *BytesTaken = 0; if (pBuffer) { MyMemFree(pBuffer, BytesAvailable); } if (pIrp) { IoFreeIrp(pIrp); } } } }
DEBUGMSG(DBG_FUNC|DBG_ERR(NtStatus), (DTEXT("-CtdipReceiveCallback %08x\n"), NtStatus)); return NtStatus; }
typedef struct { TA_IP_ADDRESS SourceAddress; ULONG Length; PVOID pBuffer; } RECV_DATAGRAM_CONTEXT, *PRECV_DATAGRAM_CONTEXT;
STATIC NTSTATUS CtdipReceiveDatagramCompleteCallback( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context ) { PRECV_DATAGRAM_CONTEXT pRecvContext; PCTDI_DATA pCtdi = Context; NDIS_STATUS Status = (NDIS_STATUS)pIrp->IoStatus.Status; PNDIS_BUFFER pNdisBuffer;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipReceiveDatagramCompleteCallback\n")));
pRecvContext = (PRECV_DATAGRAM_CONTEXT)IoGetCurrentIrpStackLocation(pIrp);
pNdisBuffer = NdisBufferFromBuffer(pRecvContext->pBuffer); ASSERT(MmGetMdlVirtualAddress(pNdisBuffer)==pRecvContext->pBuffer);
if (pCtdi->RecvDatagramCallback && !pCtdi->Closed && Status==NDIS_STATUS_SUCCESS) { // We took a reference for the buffer when we created the irp.
(void)// ToDo: We don't care about the return value?
pCtdi->RecvDatagramCallback(pCtdi->RecvContext, (PTRANSPORT_ADDRESS)&pRecvContext->SourceAddress, pRecvContext->pBuffer, pRecvContext->Length);
// The above layer owns the buffer now.
} else { FreeBufferToPool(&pCtdi->Datagram.RxPool, pRecvContext->pBuffer, TRUE); DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_RECVDG); }
RELEASE_CONTEXT(pIrp, RECV_DATAGRAM_CONTEXT); IoFreeIrp(pIrp);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipReceiveDatagramCompleteCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS CtdipReceiveDatagramCallback( 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 ULONG* BytesTaken, IN PVOID Tsdu, OUT PIRP* IoRequestPacket ) { PUCHAR pBuffer = NULL; PNDIS_BUFFER pNdisBuffer; PCTDI_DATA pCtdi = TdiEventContext; NTSTATUS NtStatus = STATUS_SUCCESS;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipReceiveDatagramCallback\n"))); if (pCtdi->RecvDatagramCallback==NULL) { DEBUGMSG(DBG_ERROR, (DTEXT("Datagram received, no handler registered. Drop it\n"))); goto crdcDone; }
pBuffer = GetBufferFromPool(&pCtdi->Datagram.RxPool); if (!pBuffer) { DEBUGMSG(DBG_ERROR, (DTEXT("No buffers, dropping datagram\n"))); goto crdcDone; }
pNdisBuffer = NdisBufferFromBuffer(pBuffer);
if (pCtdi->RecvDatagramCallback && !pCtdi->Closed) { if (BytesAvailable>PPTP_MAX_RECEIVE_SIZE) { DEBUGMSG(DBG_ERROR, (DTEXT("WAY too many bytes received. %d\n"), BytesAvailable)); ASSERT(BytesAvailable<PPTP_MAX_RECEIVE_SIZE); } else if (ReceiveDatagramFlags&TDI_RECEIVE_ENTIRE_MESSAGE || BytesAvailable==BytesIndicated) { ULONG BytesCopied;
// Let's just do a copy here.
TdiCopyBufferToMdl(Tsdu, 0, BytesIndicated, pNdisBuffer, 0, &BytesCopied);
ASSERT(BytesCopied==BytesIndicated);
REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_RECVDG); // pair in CtdiReceiveComplete
(void)// ToDo: We don't care about the return value?
pCtdi->RecvDatagramCallback(pCtdi->RecvContext, SourceAddress, pBuffer, BytesIndicated);
// We've handed the buffer to the layer above. Clear the var so we don't
// free it when we leave here.
pBuffer = NULL; *BytesTaken = BytesIndicated; } else { PRECV_DATAGRAM_CONTEXT pContext; PIRP pIrp = IoAllocateIrp((CCHAR)(pCtdi->pFileObject->DeviceObject->StackSize + NUM_STACKS_FOR_CONTEXT(sizeof(RECV_DATAGRAM_CONTEXT))), FALSE);
if (pIrp) { pContext = GET_CONTEXT(pIrp, RECV_DATAGRAM_CONTEXT);
pContext->SourceAddress = *(PTA_IP_ADDRESS)SourceAddress; pContext->Length = BytesAvailable; pContext->pBuffer = pBuffer;
TdiBuildReceiveDatagram(pIrp, pCtdi->pFileObject->DeviceObject, pCtdi->pFileObject, CtdipReceiveDatagramCompleteCallback, pCtdi, pNdisBuffer, PPTP_MAX_RECEIVE_SIZE, NULL, NULL, 0);
IoSetNextIrpStackLocation(pIrp); // Required by TDI
*BytesTaken = 0; *IoRequestPacket = pIrp; NtStatus = STATUS_MORE_PROCESSING_REQUIRED; pBuffer = NULL; // to keep us from freeing it here.
REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_RECVDG); // pair in CtdiReceiveComplete
} } } else { NtStatus = STATUS_DATA_NOT_ACCEPTED; }
crdcDone: if (pBuffer) { FreeBufferToPool(&pCtdi->Datagram.RxPool, pBuffer, TRUE); } DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipReceiveDatagramCallback %08x\n"), NtStatus)); return NtStatus; }
STATIC NTSTATUS CtdipSendCallback( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context ) { PCTDI_DATA pCtdi = Context; PVOID pData = NULL; NDIS_STATUS Status = (NDIS_STATUS)pIrp->IoStatus.Status; PCTDI_SEND_CONTEXT pSendContext; CTDI_EVENT_SEND_COMPLETE pSendCompleteCallback; PVOID CtdiContext;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipSendCallback %08x\n"), Status));
pSendContext = (PCTDI_SEND_CONTEXT)IoGetCurrentIrpStackLocation(pIrp); CtdiContext = pSendContext->Context; pSendCompleteCallback = pSendContext->pSendCompleteCallback;
// ToDo: take action if the irp returns failure.
if (!pIrp->MdlAddress) { DEBUGMSG(DBG_WARN, (DTEXT("MdlAddress NULL\n"))); } else { ASSERT(pIrp->MdlAddress->Next == NULL); pData = MmGetMdlVirtualAddress(pIrp->MdlAddress); #if PROBE
MmUnlockPages(pIrp->MdlAddress); #endif
IoFreeMdl(pIrp->MdlAddress); } RELEASE_CONTEXT(pIrp, CTDI_SEND_CONTEXT); IoFreeIrp(pIrp);
pSendCompleteCallback(CtdiContext, NULL, pData, Status);
DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_SEND); // Pair in CtdiSend
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipSendCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
STATIC NTSTATUS CtdipSendDatagramCallback( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context ) { PCTDI_DATA pCtdi = Context; PVOID pData = NULL; NDIS_STATUS Status = (NDIS_STATUS)pIrp->IoStatus.Status; PCTDI_SEND_DATAGRAM_CONTEXT pSendContext; CTDI_EVENT_SEND_COMPLETE pSendCompleteCallback; PVOID CtdiContext, DatagramContext;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipSendDatagramCallback %08x\n"), Status));
pSendContext = (PCTDI_SEND_DATAGRAM_CONTEXT)IoGetCurrentIrpStackLocation(pIrp); CtdiContext = pSendContext->Context; DatagramContext = pSendContext->DatagramContext; pSendCompleteCallback = pSendContext->pSendCompleteCallback;
// ToDo: take action if the irp returns failure.
if (!pIrp->MdlAddress) { DEBUGMSG(DBG_WARN, (DTEXT("MdlAddress NULL\n"))); } else { ASSERT(pIrp->MdlAddress->Next == NULL); pData = MmGetMdlVirtualAddress(pIrp->MdlAddress); #if PROBE
MmUnlockPages(pIrp->MdlAddress); #endif
IoFreeMdl(pIrp->MdlAddress); } RELEASE_CONTEXT(pIrp, CTDI_SEND_DATAGRAM_CONTEXT); IoFreeIrp(pIrp);
if (pSendCompleteCallback) { pSendCompleteCallback(CtdiContext, DatagramContext, pData, Status); } else { ASSERT(!"No SendCompleteHandler for datagram"); }
DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_SENDDG); // Pair in CtdiSendDatagram
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipSendDatagramCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
/** public functions **/
NDIS_STATUS CtdiInitialize( IN ULONG ulFlags ) { TA_IP_ADDRESS Local; UNICODE_STRING DeviceName; NTSTATUS Status = STATUS_SUCCESS; DEBUGMSG(DBG_FUNC|DBG_TDI, (DTEXT("+CtdiInitialize\n")));
if( fCtdiInitialized ){ goto ciDone; }
InitializeListHead(&CtdiList); InitializeListHead(&CtdiFreeList); InitializeListHead(&CtdiRouteList); InitializeListHead(&CtdiRouteNotifyList); NdisAllocateSpinLock(&CtdiListLock); fCtdiInitialized = TRUE;
if (ulFlags&CTDI_FLAG_NETWORK_HEADER) { CtdiMdlFlags |= MDL_NETWORK_HEADER; }
if (ulFlags&CTDI_FLAG_ENABLE_ROUTING) { NdisZeroMemory(&Local, sizeof(Local));
Local.TAAddressCount = 1; Local.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP; Local.Address[0].AddressType = TDI_ADDRESS_TYPE_IP; Local.Address[0].Address[0].sin_port = 0; Local.Address[0].Address[0].in_addr = 0;
RtlInitUnicodeString(&DeviceName, DD_TCP_DEVICE_NAME);
Status = CtdipOpenProtocol(&DeviceName, (PTRANSPORT_ADDRESS)&Local, &hTcp, &pFileTcp);
if (Status!=STATUS_SUCCESS) { goto ciDone; } RtlInitUnicodeString(&DeviceName, DD_IP_DEVICE_NAME);
Status = CtdipOpenProtocol(&DeviceName, (PTRANSPORT_ADDRESS)&Local, &hIp, &pFileIp);
if (Status!=STATUS_SUCCESS) { goto ciDone; }
}
ciDone: if (Status!=STATUS_SUCCESS) { if (hTcp) { CtdipCloseProtocol(hTcp, pFileTcp); hTcp = 0; pFileTcp = NULL; } if (hIp) { CtdipCloseProtocol(hIp, pFileIp); hIp = 0; pFileIp = NULL; } NdisFreeSpinLock(&CtdiListLock); fCtdiInitialized = FALSE; } DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtdiInitialize %08x\n"), Status)); return (NDIS_STATUS)Status; }
VOID CtdiShutdown( ) { HANDLE h; PFILE_OBJECT pFile; UINT i;
DEBUGMSG(DBG_FUNC|DBG_TDI, (DTEXT("+CtdiShutdown\n"))); if (fCtdiInitialized) { fCtdiInitialized = FALSE; NdisMSleep(30000); // Allow code using these handles on other processors to complete
// before we close them.
if (hIp || pFileIp) { h = hIp; hIp = 0; pFile = pFileIp; pFileIp = NULL; CtdipCloseProtocol(h, pFile); } if (hTcp || pFileTcp) { h = hTcp; hTcp = 0; pFile = pFileTcp; pFileTcp = NULL; CtdipCloseProtocol(h, pFile); } // Some irps seem very slow to be cancelled by TCP.
for (i=0; i<300; i++) { if (IsListEmpty(&CtdiList) && IsListEmpty(&CtdiRouteList) && IsListEmpty(&CtdiRouteNotifyList) && IsListEmpty(&CtdiFreeList)) { break; } NdisMSleep(10000); // Small window to allow irps to complete after closing their handles.
} ASSERT(IsListEmpty(&CtdiList)); ASSERT(IsListEmpty(&CtdiRouteList)); ASSERT(IsListEmpty(&CtdiRouteNotifyList)); NdisFreeSpinLock(&CtdiListLock); }
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdiShutdown\n"))); }
NDIS_STATUS CtdiClose( IN HANDLE hCtdi ) { PCTDI_DATA pCtdi = (PCTDI_DATA)hCtdi; NTSTATUS Status;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiClose\n")));
if (!IS_CTDI(pCtdi)) { return NDIS_STATUS_SUCCESS; }
NdisAcquireSpinLock(&pCtdi->Lock); if (!pCtdi->Closed) { pCtdi->Closed = TRUE; switch (pCtdi->Type) { case CTDI_ENDPOINT: { break; } case CTDI_CONNECTION: { ASSERT(!pCtdi->Connection.ConnectInfo); break; } case CTDI_LISTEN: { while (!IsListEmpty(&pCtdi->Listen.ConnectList)) { PLIST_ENTRY pListEntry; PCTDI_DATA pConnect; PIRP pIrp; NDIS_STATUS Status;
pListEntry = RemoveHeadList(&pCtdi->Listen.ConnectList); pConnect = CONTAINING_RECORD(pListEntry, CTDI_DATA, Connection.ListEntry);
NdisReleaseSpinLock(&pCtdi->Lock);
// these derefs are for the double references placed when these are placed on
// the list
DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_LIST); DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_LIST);
pIrp = IoAllocateIrp(pConnect->pFileObject->DeviceObject->StackSize, FALSE); if (pIrp) { // Normally we would take a reference to pConnect for this irp, but
// these handles won't be getting a close from above, which means they
// are in need of one extra dereference.
SET_DBGFLAG(pConnect, CTDI_F_BUILD_DISASSOC);
TdiBuildDisassociateAddress(pIrp, pConnect->pFileObject->DeviceObject, pConnect->pFileObject, CtdipDisassociateAddressCallback, pConnect); DEBUGMSG(DBG_TDI, (DTEXT("IoCallDriver TDI_DISASSOCIATE_ADDRESS\n")));
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pConnect->pFileObject->DeviceObject, pIrp); } else { DEREFERENCE_OBJECT_EX(pConnect, CTDI_REF_UNKNOWN); } NdisAcquireSpinLock(&pCtdi->Lock);
}
CtlpCleanupCtls(pgAdapter);
} default: break; } NdisReleaseSpinLock(&pCtdi->Lock); DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_INITIAL); // This derefs the initial reference
} else { NdisReleaseSpinLock(&pCtdi->Lock); }
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdiClose\n"))); return NDIS_STATUS_SUCCESS; }
NDIS_STATUS CtdiListen( IN HANDLE hCtdi, IN ULONG_PTR NumListen, IN CTDI_EVENT_CONNECT_QUERY pConnectQueryHandler, IN CTDI_EVENT_RECEIVE pReceiveHandler, IN CTDI_EVENT_DISCONNECT pDisconnectHandler, IN PVOID pContext ) { UINT i; NDIS_STATUS ReturnStatus = NDIS_STATUS_SUCCESS; PCTDI_DATA pCtdi = (PCTDI_DATA)hCtdi; BOOLEAN Reference = FALSE;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiListen\n")));
if (!IS_CTDI(pCtdi)) { DEBUGMSG(DBG_ERROR, (DTEXT("Ctdi: Bad handle %08x\n"), pCtdi)); ReturnStatus = NDIS_STATUS_FAILURE; goto clDone; }
NdisAcquireSpinLock(&pCtdi->Lock);
pCtdi->Type = CTDI_LISTEN; pCtdi->Listen.Context = pContext;
pCtdi->RecvCallback = pReceiveHandler; pCtdi->ConnectQueryCallback = pConnectQueryHandler; pCtdi->DisconnectCallback = pDisconnectHandler;
InitializeListHead(&pCtdi->Listen.ConnectList);
REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_INLISTEN); // Pair in this func.
Reference = TRUE; NdisReleaseSpinLock(&pCtdi->Lock);
for (i=0; i<NumListen; i++) { ReturnStatus = CtdipAddListenConnection(pCtdi); if (ReturnStatus!=NDIS_STATUS_SUCCESS) { goto clDone; } }
ReturnStatus = CtdipSetEventHandler(pCtdi, TDI_EVENT_CONNECT, CtdipConnectCallback); if (ReturnStatus!=NDIS_STATUS_SUCCESS) { DEBUGMSG(DBG_ERROR, (DTEXT("CtdiSetEventHandler TDI_EVENT_CONNECT failed\n"))); goto clDone; }
ReturnStatus = CtdipSetEventHandler(pCtdi, TDI_EVENT_RECEIVE, CtdipReceiveCallback); if (ReturnStatus!=NDIS_STATUS_SUCCESS) { DEBUGMSG(DBG_ERROR, (DTEXT("CtdiSetEventHandler TDI_EVENT_RECEIVE failed\n"))); goto clDone; }
ReturnStatus = CtdipSetEventHandler(pCtdi, TDI_EVENT_DISCONNECT, CtdipDisconnectCallback); if (ReturnStatus!=NDIS_STATUS_SUCCESS) { DEBUGMSG(DBG_ERROR, (DTEXT("CtdiSetEventHandler TDI_EVENT_DISCONNECT failed\n"))); goto clDone; }
clDone: if (Reference) { DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_INLISTEN); // Pair in this func.
} if (ReturnStatus!=NDIS_STATUS_SUCCESS) { // ToDo: cleanup on failure.
// Figure out how to undo address association, if necessary.
}
DEBUGMSG(DBG_FUNC|DBG_ERR(ReturnStatus), (DTEXT("-CtdiListen %08x\n"), ReturnStatus)); return ReturnStatus; }
NDIS_STATUS CtdiConnect( IN HANDLE hCtdi, IN PTRANSPORT_ADDRESS pAddress, IN CTDI_EVENT_CONNECT_COMPLETE pConnectCompleteHandler, IN CTDI_EVENT_RECEIVE pReceiveHandler, IN CTDI_EVENT_DISCONNECT pDisconnectHandler, IN PVOID pContext ) { UNICODE_STRING DeviceName; OBJECT_ATTRIBUTES ObjectAttributes; NDIS_STATUS ReturnStatus = NDIS_STATUS_SUCCESS; NTSTATUS NtStatus; PTIME pTimeout = NULL; PIRP pIrp; IO_STATUS_BLOCK IoStatusBlock; PCTDI_DATA pEndpoint = (PCTDI_DATA)hCtdi; PCTDI_DATA pConnect = NULL; PTDI_CONNECTION_INFORMATION pRequestInfo = NULL; PTDI_CONNECTION_INFORMATION pReturnInfo = NULL; PTA_IP_ADDRESS pRemoteAddress; PBOOLEAN pInboundFlag; BOOLEAN CloseConnection = FALSE;
UCHAR EaBuffer[sizeof(FILE_FULL_EA_INFORMATION) + TDI_CONNECTION_CONTEXT_LENGTH + sizeof(PVOID)]; PFILE_FULL_EA_INFORMATION pEa = (PFILE_FULL_EA_INFORMATION)EaBuffer; PVOID *ppContext = (PVOID*)(pEa->EaName + TDI_CONNECTION_CONTEXT_LENGTH + 1);
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiConnect\n")));
ASSERT(KeGetCurrentIrql()<DISPATCH_LEVEL);
if (!IS_CTDI(pEndpoint)) { DEBUGMSG(DBG_ERROR, (DTEXT("Ctdi: Bad handle %08x\n"), pEndpoint)); ReturnStatus = NDIS_STATUS_FAILURE; goto ccDone; }
pConnect = CtdipDataAlloc(); if (!pConnect) { ReturnStatus = NDIS_STATUS_RESOURCES; goto ccDone; }
pConnect->Type = CTDI_CONNECTION; pConnect->Connection.Context = pContext; pConnect->Connection.LocalEndpoint = pEndpoint; pConnect->ConnectCompleteCallback = pConnectCompleteHandler; pConnect->RecvCallback = pReceiveHandler; pConnect->DisconnectCallback = pDisconnectHandler;
DeviceName.Length = sizeof(DD_TCP_DEVICE_NAME) - sizeof(WCHAR); DeviceName.Buffer = DD_TCP_DEVICE_NAME;
InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL);
NdisZeroMemory(pEa, sizeof(EaBuffer)); pEa->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH; pEa->EaValueLength = sizeof(PVOID); NdisMoveMemory(pEa->EaName, TdiConnectionContext, TDI_CONNECTION_CONTEXT_LENGTH);
*ppContext = pConnect;
NdisZeroMemory(&IoStatusBlock, sizeof(IoStatusBlock));
NtStatus = ZwCreateFile(&pConnect->hFile, /* FileHandle */ FILE_READ_DATA | FILE_WRITE_DATA, /* Desired Access */ &ObjectAttributes, /* Object Attributes */ &IoStatusBlock, /* IO Status Block */ NULL, /* Allocation Size */ FILE_ATTRIBUTE_NORMAL, /* File Attributes */ 0, /* Share Access */ FILE_OPEN, /* Create Disposition */ 0, /* Create Options */ pEa, /* EaBuffer */ sizeof(EaBuffer) /* EaLength */ );
if (NtStatus!=STATUS_SUCCESS) { ReturnStatus = NtStatus; goto ccDone; }
// Convert the address file handle to a FILE_OBJECT
NtStatus = ObReferenceObjectByHandle(pConnect->hFile, /* Handle */ 0, /* DesiredAccess */ NULL, /* ObjectType */ KernelMode, /* AccessMode */ &pConnect->pFileObject, /* Object */ NULL /* HandleInfo */ );
if (NtStatus != STATUS_SUCCESS) { ReturnStatus = NtStatus; goto ccDone; }
// Make an irp to associate the endpoint and connection.
pIrp = IoAllocateIrp(pConnect->pFileObject->DeviceObject->StackSize, FALSE); if (!pIrp) { ReturnStatus = NDIS_STATUS_RESOURCES; goto ccDone; }
REFERENCE_OBJECT_EX(pConnect, CTDI_REF_ASSOADDR); // Pair in CtdipAssociateAddressCallback
TdiBuildAssociateAddress(pIrp, pConnect->pFileObject->DeviceObject, pConnect->pFileObject, CtdipAssociateAddressCallback, pConnect, pEndpoint->hFile); // Associate address creates a reference from the connection to the endpoint.
REFERENCE_OBJECT_EX(pEndpoint, CTDI_REF_ADDRREF); // Pair in CtdipDisassociateAddressCallback
DEBUGMSG(DBG_TDI, (DTEXT("IoCallDriver TDI_ASSOCIATE_ADDRESS\n")));
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pConnect->pFileObject->DeviceObject, pIrp);
ReturnStatus = CtdipSetEventHandler(pEndpoint, TDI_EVENT_RECEIVE, CtdipReceiveCallback); if (ReturnStatus!=NDIS_STATUS_SUCCESS) { DEBUGMSG(DBG_ERROR, (DTEXT("CtdiSetEventHandler TDI_EVENT_RECEIVE failed\n"))); goto ccDone; }
ReturnStatus = CtdipSetEventHandler(pEndpoint, TDI_EVENT_DISCONNECT, CtdipDisconnectCallback); if (ReturnStatus!=NDIS_STATUS_SUCCESS) { DEBUGMSG(DBG_ERROR, (DTEXT("CtdiSetEventHandler TDI_EVENT_DISCONNECT failed\n"))); goto ccDone; }
// Make an irp to establish the connection
pIrp = IoAllocateIrp(pConnect->pFileObject->DeviceObject->StackSize, FALSE);
// No sign saying we can't allocate the request info, return info and address buffers
// in one shot.
pRequestInfo = MyMemAlloc(2*(sizeof(TDI_CONNECTION_INFORMATION)+ sizeof(TA_IP_ADDRESS)) + 3*sizeof(PVOID) + sizeof(BOOLEAN), TAG_CTDI_CONNECT_INFO);
if (!pIrp || !pRequestInfo) { ReturnStatus = NDIS_STATUS_RESOURCES;
goto ccDone; }
NdisZeroMemory(pRequestInfo, 2*(sizeof(TDI_CONNECTION_INFORMATION)+sizeof(TA_IP_ADDRESS)) + sizeof(BOOLEAN) + 3*sizeof(PVOID));
pConnect->Connection.ConnectInfo = pRequestInfo;
pRequestInfo->RemoteAddressLength = sizeof(TA_IP_ADDRESS);
pRemoteAddress = (PTA_IP_ADDRESS)((PUCHAR)(pRequestInfo + 1) + sizeof(PVOID)); (ULONG_PTR)pRemoteAddress &= ~((ULONG_PTR)sizeof(PVOID) - 1);
pRequestInfo->RemoteAddress = pRemoteAddress;
*pRemoteAddress = *(PTA_IP_ADDRESS)pAddress;
pReturnInfo = (PTDI_CONNECTION_INFORMATION) ((PUCHAR)(pRemoteAddress + 1) + sizeof(PVOID));
(ULONG_PTR)pReturnInfo &= ~((ULONG_PTR)sizeof(PVOID) - 1);
pReturnInfo->RemoteAddressLength = sizeof(TA_IP_ADDRESS);
pRemoteAddress = (PTA_IP_ADDRESS)((PUCHAR)(pReturnInfo + 1) + sizeof(PVOID));
(ULONG_PTR)pRemoteAddress &= ~((ULONG_PTR)sizeof(PVOID) - 1);
pReturnInfo->RemoteAddress = pRemoteAddress;
pInboundFlag = (PBOOLEAN)(pRemoteAddress + 1); *pInboundFlag = FALSE;
pRemoteAddress->TAAddressCount = 1; pRemoteAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP; pRemoteAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
REFERENCE_OBJECT_EX(pConnect, CTDI_REF_CONNECT); // Pair in CtdipConnectCompleteCallback
TdiBuildConnect(pIrp, pConnect->pFileObject->DeviceObject, pConnect->pFileObject, CtdipConnectCompleteCallback, pConnect, NULL, // ToDo: allow them to specify timeout
pRequestInfo, pReturnInfo);
DEBUGMSG(DBG_TDI, (DTEXT("IoCallDriver TDI_CONNECT\n")));
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pConnect->pFileObject->DeviceObject, pIrp); ReturnStatus = STATUS_PENDING;
NdisInterlockedIncrement(&Counters.OutboundConnectAttempts);
ccDone:; if (!NT_SUCCESS(ReturnStatus) && pConnectCompleteHandler) { pConnectCompleteHandler(pContext, 0, ReturnStatus); ReturnStatus = NDIS_STATUS_PENDING; CtdiDisconnect(pConnect, TRUE); CtdiClose(pConnect); } DEBUGMSG(DBG_FUNC|DBG_ERR(ReturnStatus), (DTEXT("-CtdiConnect %08x\n"), ReturnStatus)); return ReturnStatus; }
NDIS_STATUS CtdiDisconnect( IN HANDLE hCtdi, IN BOOLEAN Abort ) { PCTDI_DATA pCtdi = (PCTDI_DATA)hCtdi; NDIS_STATUS Status; PIRP pIrp = NULL; PTIME pTimeout; PTDI_CONNECTION_INFORMATION pConnectInfo; BOOLEAN Disconnected = FALSE;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiDisconnect\n")));
if (!IS_CTDI(pCtdi)) { Status = NDIS_STATUS_SUCCESS; goto cdDone; }
SET_DBGFLAG(pCtdi, CTDI_F_DISCONNECT);
NdisAcquireSpinLock(&pCtdi->Lock); if ((Abort && pCtdi->Connection.Abort) || (!Abort && pCtdi->Connection.Disconnect)) { // Already disconnecting, bail out.
NdisReleaseSpinLock(&pCtdi->Lock); Status = NDIS_STATUS_SUCCESS; goto cdDone; } if (Abort) { pCtdi->Connection.Abort = TRUE; } pCtdi->Connection.Disconnect = TRUE; pCtdi->Connection.DisconnectCount++; if (pCtdi->pFileObject) { pIrp = IoAllocateIrp((CCHAR)(pCtdi->pFileObject->DeviceObject->StackSize + NUM_STACKS_FOR_CONTEXT(sizeof(TIME)+sizeof(TDI_CONNECTION_INFORMATION))), FALSE); } NdisReleaseSpinLock(&pCtdi->Lock);
if (!pIrp) { Status = STATUS_INSUFFICIENT_RESOURCES; goto cdDone; }
pTimeout = (PTIME)GetContextArea(pIrp, sizeof(TIME)+sizeof(TDI_CONNECTION_INFORMATION)); pConnectInfo = (PTDI_CONNECTION_INFORMATION)(pTimeout + 1);
pTimeout->LowPart = CtdiTcpDisconnectTimeout * -10000000L; pTimeout->HighPart = (pTimeout->LowPart) ? -1 : 0;
// Responding to a controlled disconnect, we don't provide
// TDI_CONNECTION_INFORMATION, but we request it from the peer.
SET_DBGFLAG(pCtdi, CTDI_F_BUILD_DISCONNECT_2);
REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_DISCONNECT); // Pair in CtdipDisconnectCompleteCallback
TdiBuildDisconnect(pIrp, pCtdi->pFileObject->DeviceObject, pCtdi->pFileObject, CtdipDisconnectCompleteCallback, pCtdi, pTimeout, (Abort ? TDI_DISCONNECT_ABORT : TDI_DISCONNECT_RELEASE), NULL, pConnectInfo);
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pCtdi->pFileObject->DeviceObject, pIrp);
Status = NDIS_STATUS_SUCCESS;
cdDone: if (!NT_SUCCESS(Status)) { if (pIrp) { IoFreeIrp(pIrp); } } DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtdiDisconnect %08x\n"), Status)); return Status; }
NDIS_STATUS CtdiReceiveComplete( IN HANDLE hCtdi, IN PUCHAR pBuffer ) { PCTDI_DATA pCtdi = (PCTDI_DATA)hCtdi; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiReceiveComplete\n"))); FreeBufferToPool(&pCtdi->Datagram.RxPool, pBuffer, TRUE); DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_RECVDG); // Pair in CtdiReceiveComplete
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdiReceiveComplete\n"))); return NDIS_STATUS_SUCCESS; }
NDIS_STATUS CtdiSend( IN HANDLE hCtdi, IN CTDI_EVENT_SEND_COMPLETE pSendCompleteHandler, IN PVOID pContext, IN PVOID pvBuffer, IN ULONG ulLength ) // We require that pBuffer not be temporary storage, as we will use it to send
// the data in an async call.
{ PCTDI_DATA pCtdi = (PCTDI_DATA)hCtdi; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PIRP pIrp = NULL; PMDL pMdl = NULL; PUCHAR pBuffer = pvBuffer; PCTDI_SEND_CONTEXT pSendContext;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiSend\n"))); if (!IS_CTDI(pCtdi)) { DEBUGMSG(DBG_ERROR, (DTEXT("Ctdi: Bad handle %08x\n"), pCtdi)); Status = NDIS_STATUS_FAILURE; goto csDone; }
// Allocate one extra stack location for context data.
pIrp = IoAllocateIrp((CCHAR)(pCtdi->pFileObject->DeviceObject->StackSize + NUM_STACKS_FOR_CONTEXT(sizeof(CTDI_SEND_CONTEXT))), FALSE);
pMdl = IoAllocateMdl(pBuffer, ulLength, FALSE, FALSE, pIrp);
if (pMdl) { #if PROBE
__try { MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess); } __except (EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl(pMdl); pMdl = NULL; } #else
MmBuildMdlForNonPagedPool(pMdl); #endif
}
if (!pIrp || !pMdl) { DEBUGMSG(DBG_ERROR, (DTEXT("Failed to allocate irp or mdl\n"))); Status = NDIS_STATUS_RESOURCES; goto csDone; }
// Get the first stack location for our own context use
pSendContext = GET_CONTEXT(pIrp, CTDI_SEND_CONTEXT);
pSendContext->Context = pContext; pSendContext->pSendCompleteCallback = pSendCompleteHandler;
TdiBuildSend(pIrp, pCtdi->pFileObject->DeviceObject, pCtdi->pFileObject, CtdipSendCallback, pCtdi, pMdl, 0, ulLength);
REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_SEND); // pair in CtdipSendCallback
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pCtdi->pFileObject->DeviceObject, pIrp);
Status = STATUS_PENDING; csDone: if (!NT_SUCCESS(Status) && pSendCompleteHandler) { pSendCompleteHandler(pContext, NULL, pBuffer, Status); Status = NDIS_STATUS_PENDING; if (pMdl) { IoFreeMdl(pMdl); } if (pIrp) { IoFreeIrp(pIrp); } } DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtdiSend %08x\n"), Status)); return Status; }
NDIS_STATUS CtdiSendDatagram( IN HANDLE hCtdi, IN CTDI_EVENT_SEND_COMPLETE pSendCompleteHandler, IN PVOID pContext, IN PVOID pDatagramContext, IN PTRANSPORT_ADDRESS pDestination, IN PUCHAR pBuffer, IN ULONG ulLength ) { PCTDI_DATA pCtdi = (PCTDI_DATA)hCtdi; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PIRP pIrp = NULL; PMDL pMdl = NULL; CTDI_SEND_DATAGRAM_CONTEXT *pSendContext;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiSendDatagram\n")));
if (!IS_CTDI(pCtdi) || pCtdi->Closed) { Status = NDIS_STATUS_CLOSED; goto csdDone; }
pIrp = IoAllocateIrp((CCHAR)(pCtdi->pFileObject->DeviceObject->StackSize + NUM_STACKS_FOR_CONTEXT(sizeof(CTDI_SEND_DATAGRAM_CONTEXT))), FALSE); ASSERT(pCtdi->pFileObject->DeviceObject->StackSize + NUM_STACKS_FOR_CONTEXT(sizeof(CTDI_SEND_DATAGRAM_CONTEXT))<7);
pMdl = IoAllocateMdl(pBuffer, ulLength, FALSE, FALSE, NULL);
if (pMdl) { #if PROBE
__try { MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess); } __except (EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl(pMdl); pMdl = NULL; } #else
MmBuildMdlForNonPagedPool(pMdl); #endif
}
if (!pIrp || !pMdl) { Status = NDIS_STATUS_RESOURCES; goto csdDone; }
pMdl->MdlFlags |= CtdiMdlFlags;
// Get the first stack location for our own context use
pSendContext = GET_CONTEXT(pIrp, CTDI_SEND_DATAGRAM_CONTEXT);
NdisZeroMemory(pSendContext, sizeof(CTDI_SEND_DATAGRAM_CONTEXT));
pSendContext->pSendCompleteCallback = pSendCompleteHandler; pSendContext->Context = pContext; pSendContext->DatagramContext = pDatagramContext;
pSendContext->TdiConnectionInfo.RemoteAddressLength = sizeof(pSendContext->Ip); pSendContext->TdiConnectionInfo.RemoteAddress = &pSendContext->Ip;
pSendContext->Ip = *(PTA_IP_ADDRESS)pDestination;
if (pSendContext->Ip.Address[0].AddressLength!=TDI_ADDRESS_LENGTH_IP || pSendContext->Ip.Address[0].AddressType!=TDI_ADDRESS_TYPE_IP) { DEBUGMSG(DBG_WARN, (DTEXT("Misformed transmit address on %08x\n"), pCtdi)); }
TdiBuildSendDatagram(pIrp, pCtdi->pFileObject->DeviceObject, pCtdi->pFileObject, CtdipSendDatagramCallback, pCtdi, pMdl, ulLength, &pSendContext->TdiConnectionInfo);
REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_SENDDG); // Pair in CtdipSendDatagramCallback
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pCtdi->pFileObject->DeviceObject, pIrp);
Status = STATUS_PENDING; csdDone: if (!NT_SUCCESS(Status)) { if (pSendCompleteHandler) { pSendCompleteHandler(pContext, pDatagramContext, pBuffer, Status); Status = NDIS_STATUS_PENDING; } if (pMdl) { IoFreeMdl(pMdl); } if (pIrp) { IoFreeIrp(pIrp); } }
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtdiSendDatagram %08x\n"), Status)); return Status; }
STATIC VOID CtdipDeleteHostRoute( PCTDI_ROUTE pRoute ) { PFILE_OBJECT pFileObject = pFileTcp; BOOLEAN NewRoute = FALSE; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; IPRouteEntry *pQueryBuffer = NULL; IPRouteEntry *pNewRoute = NULL; IPRouteEntry BestRoute; BOOLEAN BestRouteFound = FALSE; PIRP pIrp; IO_STATUS_BLOCK IoStatusBlock; PIO_STACK_LOCATION IrpSp; TCP_REQUEST_QUERY_INFORMATION_EX QueryRoute; TCP_REQUEST_SET_INFORMATION_EX *pSetRoute = NULL; ULONG NumRoutes = 20; ULONG Size = 0, QuerySize = 0; ULONG i; KEVENT Event; #ifdef IP_ROUTE_REFCOUNT
OBJECT_ATTRIBUTES ObjectAttributes; HANDLE IpFileHandle = 0; #endif
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipDeleteHostRoute\n")));
if (!fCtdiInitialized) { Status = NDIS_STATUS_FAILURE; goto cdhrDone; } if (!pRoute->ExternalRoute) { // Query TCPfor the current routing table
Status = CtdipIpQueryRouteTable(&pQueryBuffer, &QuerySize, &NumRoutes); if (Status!=NDIS_STATUS_SUCCESS) { goto cdhrDone; }
BestRoute.ire_mask = 0; BestRoute.ire_metric1 = (ULONG)-1;
for (i=0; i<NumRoutes; i++) { DEBUGMSG(DBG_TDI, (DTEXT("Route %d.%d.%d.%d Type %d NextHop %d.%d.%d.%d Mask %d.%d.%d.%d Metric %d Index %d\n"), IPADDR(pQueryBuffer[i].ire_dest), pQueryBuffer[i].ire_type, IPADDR(pQueryBuffer[i].ire_nexthop), IPADDR(pQueryBuffer[i].ire_mask), pQueryBuffer[i].ire_metric1, pQueryBuffer[i].ire_index)); if (pQueryBuffer[i].ire_dest == pRoute->IpAddress && pQueryBuffer[i].ire_proto == IRE_PROTO_NETMGMT) { BestRoute = pQueryBuffer[i]; BestRouteFound = TRUE; break; } }
// We've taken what we need from the route list. Free it.
MyMemFree(pQueryBuffer, QuerySize); pQueryBuffer = NULL;
if (BestRouteFound) {
#ifdef IP_ROUTE_REFCOUNT
Size = sizeof(IPRouteEntry); pNewRoute = MyMemAlloc(Size, TAG_CTDI_ROUTE); pSetRoute = (PVOID)pNewRoute; if (!pNewRoute) { Status = NDIS_STATUS_RESOURCES; goto cdhrDone; } NdisZeroMemory(pNewRoute, Size); #else
Size = sizeof(TCP_REQUEST_SET_INFORMATION_EX) + sizeof(IPRouteEntry); pSetRoute = MyMemAlloc(Size, TAG_CTDI_ROUTE); if (!pSetRoute) { Status = NDIS_STATUS_RESOURCES; goto cdhrDone; }
NdisZeroMemory(pSetRoute, Size);
pSetRoute->ID.toi_entity.tei_entity = CL_NL_ENTITY; pSetRoute->ID.toi_entity.tei_instance = 0; pSetRoute->ID.toi_class = INFO_CLASS_PROTOCOL; pSetRoute->ID.toi_type = INFO_TYPE_PROVIDER; pSetRoute->ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; pSetRoute->BufferSize = sizeof(IPRouteEntry);
pNewRoute = (IPRouteEntry*)&pSetRoute->Buffer[0]; #endif
*pNewRoute = BestRoute;
pNewRoute->ire_type = IRE_TYPE_INVALID;
DEBUGMSG(DBG_TDI, (DTEXT("DeleteHostRoute %d.%d.%d.%d Type %d NextHop %d.%d.%d.%d Index %d\n"), IPADDR(pNewRoute->ire_dest), pNewRoute->ire_type, IPADDR(pNewRoute->ire_nexthop), pNewRoute->ire_index));
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
#ifdef IP_ROUTE_REFCOUNT
pFileObject = pFileIp;
pIrp = IoBuildDeviceIoControlRequest( IOCTL_IP_SET_ROUTEWITHREF, pFileObject->DeviceObject, pNewRoute, Size, NULL, 0, FALSE, &Event, &IoStatusBlock); #else
pIrp = IoBuildDeviceIoControlRequest( IOCTL_TCP_SET_INFORMATION_EX, pFileObject->DeviceObject, pSetRoute, Size, NULL, 0, FALSE, &Event, &IoStatusBlock); #endif
if (pIrp == NULL) { goto cdhrDone; }
IrpSp = IoGetNextIrpStackLocation(pIrp); IrpSp->FileObject = pFileObject;
Status = IoCallDriver(pFileObject->DeviceObject, pIrp);
if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status;
}
if (Status != STATUS_SUCCESS) { DEBUGMSG(DBG_TDI, (DTEXT("Create host route failed %08x\n"), Status)); goto cdhrDone; } } }
cdhrDone: if (pRoute) { MyInterlockedRemoveEntryList(&pRoute->ListEntry, &CtdiListLock); MyMemFree(pRoute, sizeof(CTDI_ROUTE)); } if (pSetRoute) { MyMemFree(pSetRoute, Size); } if (pQueryBuffer) { MyMemFree(pQueryBuffer, QuerySize); } DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipDeleteHostRoute\n"))); }
NDIS_STATUS CtdiAddHostRoute( IN PTA_IP_ADDRESS pIpAddress ) { PFILE_OBJECT pFileObject = pFileTcp; PCTDI_ROUTE pRoute = NULL; BOOLEAN NewRoute = FALSE; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; IPRouteEntry *pQueryBuffer = NULL; IPRouteEntry *pNewRoute = NULL; IPRouteEntry BestRoute; BOOLEAN BestRouteFound = FALSE; PIRP pIrp = NULL; IO_STATUS_BLOCK IoStatusBlock; PIO_STACK_LOCATION IrpSp; TCP_REQUEST_QUERY_INFORMATION_EX QueryRoute; TCP_REQUEST_SET_INFORMATION_EX *pSetRoute = NULL; ULONG NumRoutes = 20; ULONG Size = 0, QuerySize = 0; ULONG i; KEVENT Event;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiAddHostRoute %d.%d.%d.%d\n"), IPADDR(pIpAddress->Address[0].Address[0].in_addr)));
NdisAcquireSpinLock(&CtdiListLock); pRoute = CtdipFindRoute(pIpAddress->Address[0].Address[0].in_addr); if (pRoute) { REFERENCE_OBJECT(pRoute); // Pair in CtdiDeleteHostRoute
} else { NewRoute = TRUE; pRoute = MyMemAlloc(sizeof(CTDI_ROUTE), TAG_CTDI_ROUTE); if (!pRoute) { Status = NDIS_STATUS_RESOURCES; NdisReleaseSpinLock(&CtdiListLock); goto cahrDone; } NdisZeroMemory(pRoute, sizeof(CTDI_ROUTE)); pRoute->IpAddress = pIpAddress->Address[0].Address[0].in_addr; INIT_REFERENCE_OBJECT(pRoute, CtdipDeleteHostRoute); // Pair in CtdiDeleteHostRoute
InsertTailList(&CtdiRouteList, &pRoute->ListEntry); } NdisReleaseSpinLock(&CtdiListLock);
if (NewRoute) { // Query TCPfor the current routing table
Status = CtdipIpQueryRouteTable(&pQueryBuffer, &QuerySize, &NumRoutes); if (Status!=NDIS_STATUS_SUCCESS) { goto cahrDone; }
BestRoute.ire_mask = 0; BestRoute.ire_metric1 = (ULONG)-1;
for (i=0; i<NumRoutes; i++) { DEBUGMSG(DBG_TDI, (DTEXT("Route %d.%d.%d.%d Type %d NextHop %d.%d.%d.%d Mask %d.%d.%d.%d Metric %d Index %d\n"), IPADDR(pQueryBuffer[i].ire_dest), pQueryBuffer[i].ire_type, IPADDR(pQueryBuffer[i].ire_nexthop), IPADDR(pQueryBuffer[i].ire_mask), pQueryBuffer[i].ire_metric1, pQueryBuffer[i].ire_index)); if (pQueryBuffer[i].ire_dest == (pIpAddress->Address[0].Address[0].in_addr & pQueryBuffer[i].ire_mask)) { if ((BestRoute.ire_mask == pQueryBuffer[i].ire_mask && BestRoute.ire_metric1 > pQueryBuffer[i].ire_metric1) || ntohl(pQueryBuffer[i].ire_mask) > ntohl(BestRoute.ire_mask)) { BestRoute = pQueryBuffer[i]; BestRouteFound = TRUE; } } }
// We've taken what we need from the route list. Free it.
MyMemFree(pQueryBuffer, QuerySize); pQueryBuffer = NULL;
if (!BestRouteFound) { DEBUGMSG(DBG_WARN, (DTEXT("Add host route. No route found\n"))); } else { // If we're using the IP refcounts, always add and delete the route.
#ifndef IP_ROUTE_REFCOUNT
if (BestRoute.ire_dest == pIpAddress->Address[0].Address[0].in_addr && BestRoute.ire_mask == 0xFFFFFFFF) { //
// A route already exists so don't add
//
pRoute->ExternalRoute = TRUE; Status = NDIS_STATUS_SUCCESS; goto cahrDone; } #endif
#ifdef IP_ROUTE_REFCOUNT
Size = sizeof(IPRouteEntry); pNewRoute = MyMemAlloc(Size, TAG_CTDI_ROUTE); pSetRoute = (PVOID)pNewRoute; if (!pNewRoute) { Status = NDIS_STATUS_RESOURCES; goto cahrDone; } NdisZeroMemory(pNewRoute, Size); #else
Size = sizeof(TCP_REQUEST_SET_INFORMATION_EX) + sizeof(IPRouteEntry); pSetRoute = MyMemAlloc(Size, TAG_CTDI_ROUTE); if (!pSetRoute) { Status = NDIS_STATUS_RESOURCES; goto cahrDone; }
NdisZeroMemory(pSetRoute, Size);
pSetRoute->ID.toi_entity.tei_entity = CL_NL_ENTITY; pSetRoute->ID.toi_entity.tei_instance = 0; pSetRoute->ID.toi_class = INFO_CLASS_PROTOCOL; pSetRoute->ID.toi_type = INFO_TYPE_PROVIDER; pSetRoute->ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; pSetRoute->BufferSize = sizeof(IPRouteEntry);
pNewRoute = (IPRouteEntry*)&pSetRoute->Buffer[0]; #endif
*pNewRoute = BestRoute;
pNewRoute->ire_dest = pIpAddress->Address[0].Address[0].in_addr; pNewRoute->ire_mask = 0xFFFFFFFF; pNewRoute->ire_proto = IRE_PROTO_NETMGMT;
// Check DIRECT/INDIRECT only if this is not a host route
if(BestRoute.ire_mask != 0xFFFFFFFF) { if ((BestRoute.ire_mask & pIpAddress->Address[0].Address[0].in_addr) == (BestRoute.ire_mask & BestRoute.ire_nexthop)) { pNewRoute->ire_type = IRE_TYPE_DIRECT; } else { pNewRoute->ire_type = IRE_TYPE_INDIRECT; } }
DEBUGMSG(DBG_TDI, (DTEXT("AddHostRoute %d.%d.%d.%d Type %d NextHop %d.%d.%d.%d Index %d\n"), IPADDR(pNewRoute->ire_dest), pNewRoute->ire_type, IPADDR(pNewRoute->ire_nexthop), pNewRoute->ire_index));
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
#ifdef IP_ROUTE_REFCOUNT
pFileObject = pFileIp;
pIrp = IoBuildDeviceIoControlRequest( IOCTL_IP_SET_ROUTEWITHREF, pFileObject->DeviceObject, pNewRoute, Size, NULL, 0, FALSE, &Event, &IoStatusBlock); #else
pIrp = IoBuildDeviceIoControlRequest( IOCTL_TCP_SET_INFORMATION_EX, pFileObject->DeviceObject, pSetRoute, Size, NULL, 0, FALSE, &Event, &IoStatusBlock); #endif
if (pIrp == NULL) { goto cahrDone; }
IrpSp = IoGetNextIrpStackLocation(pIrp); IrpSp->FileObject = pFileObject;
Status = IoCallDriver(pFileObject->DeviceObject, pIrp);
if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; }
if (Status != STATUS_SUCCESS) { DEBUGMSG(DBG_TDI, (DTEXT("Create host route failed %08x\n"), Status)); goto cahrDone; }
//CtdipIpRequestRoutingNotification(pIpAddress->Address[0].Address[0].in_addr);
// The route's a keeper. Set the var to null so we don't free it
pRoute = NULL; } }
cahrDone: if (pRoute) { MyInterlockedRemoveEntryList(&pRoute->ListEntry, &CtdiListLock); MyMemFree(pRoute, sizeof(CTDI_ROUTE)); } if (pSetRoute) { MyMemFree(pSetRoute, Size); } if (pQueryBuffer) { MyMemFree(pQueryBuffer, QuerySize); } DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtdiAddHostRoute %08x\n"), Status)); return Status; }
NDIS_STATUS CtdiDeleteHostRoute( IN PTA_IP_ADDRESS pIpAddress ) { PCTDI_ROUTE pRoute = NULL; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiDeleteHostRoute\n"))); NdisAcquireSpinLock(&CtdiListLock); pRoute = CtdipFindRoute(pIpAddress->Address[0].Address[0].in_addr); NdisReleaseSpinLock(&CtdiListLock); if (pRoute) { DEREFERENCE_OBJECT(pRoute); // Pair in CtdiAddHostRoute
} DEBUGMSG(DBG_FUNC, (DTEXT("-CtdiDeleteHostRoute\n"))); return NDIS_STATUS_SUCCESS; }
NDIS_STATUS CtdiCreateEndpoint( OUT PHANDLE phCtdi, IN ULONG_PTR ulAddressFamily, IN ULONG_PTR ulType, IN PTRANSPORT_ADDRESS pAddress, IN ULONG_PTR ulRxPadding ) { UNICODE_STRING DeviceName; OBJECT_ATTRIBUTES ObjectAttributes; NDIS_STATUS ReturnStatus = NDIS_STATUS_SUCCESS; NTSTATUS NtStatus; IO_STATUS_BLOCK IoStatusBlock; PCTDI_DATA pCtdi = NULL;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiCreateEndpoint\n"))); DBG_D(DBG_TAPI, KeGetCurrentIrql());
// Validate TDI initialized
if ( !fCtdiInitialized ) { DEBUGMSG(DBG_ERROR | DBG_TDI, (DTEXT("CtdiCreateEndpoint: TDI interface hasn't been initialized!\n"))); ReturnStatus = NDIS_STATUS_FAILURE; goto cceDone; }
ASSERT(ulAddressFamily==AF_INET); if (ulAddressFamily!=AF_INET) { DEBUGMSG(DBG_ERROR|DBG_TDI, (DTEXT("unsupported family\n"))); ReturnStatus = NDIS_STATUS_OPEN_FAILED; goto cceDone; }
// Alloc our endpoint structure.
pCtdi = CtdipDataAlloc(); if (!pCtdi) { ReturnStatus = NDIS_STATUS_RESOURCES; goto cceDone; }
pCtdi->Type = CTDI_ENDPOINT;
switch (ulType) { case SOCK_RAW: { WCHAR DeviceNameBuffer[sizeof(DD_RAW_IP_DEVICE_NAME) + 16]; WCHAR ProtocolNumberBuffer[8]; UNICODE_STRING ProtocolNumber; TA_IP_ADDRESS TmpAddress = *(PTA_IP_ADDRESS)pAddress;
pCtdi->Type = CTDI_DATAGRAM;
InitBufferPool(&pCtdi->Datagram.RxPool, ALIGN_UP(PPTP_MAX_RECEIVE_SIZE+ulRxPadding, ULONG_PTR), 0, // MaxBuffers, no limit
10, // Buffers per block
0, // Frees per collection
TRUE, // These are MDLs
TAG_CTDI_DGRAM);
NdisZeroMemory(DeviceNameBuffer, sizeof(DeviceNameBuffer)); DeviceName.Buffer = DeviceNameBuffer; DeviceName.MaximumLength = sizeof(DeviceNameBuffer); DeviceName.Length = 0;
RtlAppendUnicodeToString(&DeviceName, DD_RAW_IP_DEVICE_NAME); RtlAppendUnicodeToString(&DeviceName, L"\\");
ProtocolNumber.Buffer = ProtocolNumberBuffer; ProtocolNumber.MaximumLength = sizeof(ProtocolNumberBuffer); ProtocolNumber.Length = 0;
RtlIntegerToUnicodeString(((PTA_IP_ADDRESS)pAddress)->Address[0].Address[0].sin_port, 10, &ProtocolNumber); RtlAppendUnicodeStringToString(&DeviceName, &ProtocolNumber);
TmpAddress.Address[0].Address[0].sin_port = 0; TmpAddress.Address[0].Address[0].in_addr = 0; NdisZeroMemory(TmpAddress.Address[0].Address[0].sin_zero, sizeof(TmpAddress.Address[0].Address[0].sin_zero));
NtStatus = CtdipOpenProtocol(&DeviceName, pAddress, &pCtdi->hFile, &pCtdi->pFileObject);
if (NtStatus!=STATUS_SUCCESS) { ReturnStatus = NtStatus; goto cceDone; }
break; } case SOCK_DGRAM: // for UDP
{ DeviceName.Length = sizeof(DD_UDP_DEVICE_NAME) - sizeof(WCHAR); DeviceName.Buffer = DD_UDP_DEVICE_NAME;
pCtdi->Type = CTDI_DATAGRAM;
InitBufferPool(&pCtdi->Datagram.RxPool, ALIGN_UP(PPTP_MAX_RECEIVE_SIZE+ulRxPadding, ULONG_PTR), 0, // MaxBuffers, no limit
10, // Buffers per block
0, // Frees per collection
TRUE, // These are MDLs
TAG_CTDI_DGRAM);
NtStatus = CtdipOpenProtocol(&DeviceName, pAddress, &pCtdi->hFile, &pCtdi->pFileObject);
if (NtStatus!=STATUS_SUCCESS) { ReturnStatus = NtStatus; goto cceDone; }
break; } case SOCK_STREAM: { RtlInitUnicodeString(&DeviceName, DD_TCP_DEVICE_NAME);
NtStatus = CtdipOpenProtocol(&DeviceName, pAddress, &pCtdi->hFile, &pCtdi->pFileObject);
if (NtStatus!=STATUS_SUCCESS) { ReturnStatus = NtStatus; goto cceDone; }
break; } default: DEBUGMSG(DBG_ERROR|DBG_TDI, (DTEXT("unsupported Type\n"))); ReturnStatus = NDIS_STATUS_OPEN_FAILED; goto cceDone; }
cceDone: if (ReturnStatus!=NDIS_STATUS_SUCCESS) { if (pCtdi) { CtdipDataFree(pCtdi); pCtdi = NULL; } }
// Return the CTDI_DATA as a handle.
*phCtdi = (HANDLE)pCtdi;
DEBUGMSG(DBG_FUNC|DBG_ERR(ReturnStatus), (DTEXT("-CtdiCreateEndpoint Sts:%08x hCtdi:%08x\n"), ReturnStatus, pCtdi)); return ReturnStatus; }
NDIS_STATUS CtdiSetEventHandler( IN HANDLE hCtdi, IN ULONG ulEventType, IN PVOID pEventHandler, IN PVOID pContext ) { NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PCTDI_DATA pCtdi = (PCTDI_DATA)hCtdi; PVOID PrivateCallback = NULL;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiSetEventHandler Type:%d\n"), ulEventType));
switch (ulEventType) { case TDI_EVENT_RECEIVE_DATAGRAM: { if (pCtdi->Type==CTDI_DATAGRAM) { PrivateCallback = CtdipReceiveDatagramCallback; pCtdi->RecvDatagramCallback = pEventHandler; pCtdi->RecvContext = pContext; } else { DEBUGMSG(DBG_ERROR, (DTEXT("Tried to register RecvDgram handler on wrong handle.\n"))); Status = NDIS_STATUS_FAILURE; } break; } default: Status = NDIS_STATUS_NOT_SUPPORTED; break; }
if (Status==NDIS_STATUS_SUCCESS && PrivateCallback!=NULL) { Status = CtdipSetEventHandler(pCtdi, ulEventType, PrivateCallback); }
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiSetEventHandler %08x\n"), Status)); return Status; }
NDIS_STATUS CtdiSetInformation( IN HANDLE hCtdi, IN ULONG_PTR ulSetType, IN PTDI_CONNECTION_INFORMATION pConnectionInformation, IN CTDI_EVENT_SET_COMPLETE pSetCompleteHandler, IN PVOID pContext ) { DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiSetInformation\n"))); DEBUGMSG(DBG_FUNC, (DTEXT("-CtdiSetInformation\n"))); return NDIS_STATUS_FAILURE; }
STATIC NTSTATUS CtdipQueryInformationCallback( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, PVOID Context ) { PCTDI_DATA pCtdi = Context; NDIS_STATUS Status = (NDIS_STATUS)pIrp->IoStatus.Status; PCTDI_QUERY_CONTEXT pQuery; CTDI_EVENT_QUERY_COMPLETE pQueryCompleteCallback; PVOID CtdiContext; PVOID pBuffer;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdipQueryInformationCallback\n")));
pQuery = (PCTDI_QUERY_CONTEXT)IoGetCurrentIrpStackLocation(pIrp); CtdiContext = pQuery->Context; pQueryCompleteCallback = pQuery->pQueryCompleteCallback;
pBuffer = MmGetMdlVirtualAddress(pIrp->MdlAddress); #if PROBE
MmUnlockPages(pIrp->MdlAddress); #endif
IoFreeMdl(pIrp->MdlAddress); RELEASE_CONTEXT(pIrp, CTDI_QUERY_CONTEXT); IoFreeIrp(pIrp);
pQueryCompleteCallback(CtdiContext, pBuffer, Status);
DEREFERENCE_OBJECT_EX(pCtdi, CTDI_REF_QUERY);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtdipQueryInformationCallback\n"))); return STATUS_MORE_PROCESSING_REQUIRED; }
NDIS_STATUS CtdiQueryInformation( IN HANDLE hCtdi, IN ULONG ulQueryType, IN OUT PVOID pBuffer, IN ULONG Length, IN CTDI_EVENT_QUERY_COMPLETE pQueryCompleteHandler, IN PVOID pContext ) { PIRP pIrp = NULL; PMDL pMdl = NULL; PCTDI_DATA pCtdi = (PCTDI_DATA) hCtdi; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PCTDI_QUERY_CONTEXT pQuery; DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiQueryInformation\n")));
pIrp = IoAllocateIrp((CCHAR)(pCtdi->pFileObject->DeviceObject->StackSize + NUM_STACKS_FOR_CONTEXT(sizeof(CTDI_QUERY_CONTEXT))), FALSE); if (pIrp) { pMdl = IoAllocateMdl(pBuffer, Length, FALSE, FALSE, pIrp); if (pMdl) { #if PROBE
__try { MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess); } __except (EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl(pMdl); pMdl = NULL; Status = NDIS_STATUS_RESOURCES; } #else
MmBuildMdlForNonPagedPool(pMdl); #endif
} } else { Status = NDIS_STATUS_RESOURCES; }
if (pMdl) { pQuery = GET_CONTEXT(pIrp, CTDI_QUERY_CONTEXT); pQuery->Context = pContext; pQuery->pQueryCompleteCallback = pQueryCompleteHandler;
TdiBuildQueryInformation(pIrp, pCtdi->pFileObject->DeviceObject, pCtdi->pFileObject, CtdipQueryInformationCallback, pCtdi, ulQueryType, pMdl); REFERENCE_OBJECT_EX(pCtdi, CTDI_REF_QUERY);
// Completion handler always called, don't care on return value.
(void)IoCallDriver(pCtdi->pFileObject->DeviceObject, pIrp); } else { if (pIrp) { IoFreeIrp(pIrp); Status = NDIS_STATUS_RESOURCES; } }
if (pQueryCompleteHandler && !NT_SUCCESS(Status)) { pQueryCompleteHandler(pContext, pBuffer, Status); Status = STATUS_PENDING; } DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtdiQueryInformation %08x\n"), Status)); return Status; }
VOID CtdiCleanupLooseEnds() { PLIST_ENTRY ListEntry;
if (!fCtdiInitialized) { return; }
DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiCleanupLooseEnds\n")));
if (!IsListEmpty(&CtdiFreeList)) { ScheduleWorkItem(CtdipDataFreeWorker, NULL, NULL, 0); } DEBUGMSG(DBG_FUNC, (DTEXT("-CtdiCleanupLooseEnds\n"))); }
VOID CtdiSetRequestPending( IN HANDLE hCtdi ) { PCTDI_DATA pCtdi = (PCTDI_DATA) hCtdi; pCtdi->CloseReqPending = TRUE; }
|