/******************************************************************* * * 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; iCurrentLocation -= (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()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; iType==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; iClosed) { 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(BytesAvailableRecvDatagramCallback(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; iEaName + TDI_CONNECTION_CONTEXT_LENGTH + 1); DEBUGMSG(DBG_FUNC, (DTEXT("+CtdiConnect\n"))); ASSERT(KeGetCurrentIrql()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; iIpAddress && 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; iAddress[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; }