3719 lines
116 KiB
C
3719 lines
116 KiB
C
/*******************************************************************
|
|
*
|
|
* 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;
|
|
}
|
|
|