Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2299 lines
82 KiB

/*****************************************************************************
*
* Copyright (c) 1998-1999 Microsoft Corporation
*
* CONTROL.C - PPTP Control Layer functionality
*
* Author: Stan Adermann (stana)
*
* Created: 7/28/1998
*
*****************************************************************************/
#include "raspptp.h"
#include "control.tmh"
typedef struct {
LIST_ENTRY ListEntry;
ULONG Length;
} MESSAGE_HEADER, *PMESSAGE_HEADER;
USHORT PptpControlPort = PPTP_TCP_PORT;
USHORT PptpProtocolNumber = PPTP_IP_GRE_PROTOCOL;
ULONG PptpWanEndpoints = OS_DEFAULT_WAN_ENDPOINTS;
BOOLEAN PptpClientSide = TRUE;
ULONG PptpBaseCallId = 0;
ULONG PptpMaxCallId = 0;
ULONG PptpCallIdMask = 0;
ULONG PptpListensPending = OS_LISTENS_PENDING;
BOOLEAN PptpEchoAlways = TRUE;
ULONG PptpEchoTimeout = PPTP_ECHO_TIMEOUT_DEFAULT;
ULONG PptpMessageTimeout = PPTP_MESSAGE_TIMEOUT_DEFAULT;
ULONG PptpMaxTransmit = PPTP_MAX_TRANSMIT;
BOOLEAN PptpInitialized = FALSE;
LONG PptpSendRecursionLimit = PPTP_SEND_RECURSION_LIMIT_DEFAULT;
BOOLEAN PptpAuthenticateIncomingCalls = FALSE;
PCLIENT_ADDRESS g_AcceptClientList = NULL;
ULONG g_ulAcceptClientAddresses = 0;
// Added for security
ULONG PptpValidateAddress = TRUE; // Validate the source ip address
ULONG PptpMaxTunnelsPerIpAddress = -1; // Maximum tunnels from each source ip address
PCLIENT_ADDRESS g_TrustedClientList = NULL; // Trusted client address list
ULONG g_ulTrustedClientAddresses = 0;
//CHAR PptpHostName[MAX_HOSTNAME_LENGTH];
static ULONG PptpMessageLength_v1[NUM_MESSAGE_TYPES] =
{
0, // invalid
sizeof(PPTP_CONTROL_START_PACKET), // Request
sizeof(PPTP_CONTROL_START_PACKET), // Reply
sizeof(PPTP_CONTROL_STOP_PACKET), // Request
sizeof(PPTP_CONTROL_STOP_PACKET), // Reply
sizeof(PPTP_CONTROL_ECHO_REQUEST_PACKET),
sizeof(PPTP_CONTROL_ECHO_REPLY_PACKET),
sizeof(PPTP_CALL_OUT_REQUEST_PACKET),
sizeof(PPTP_CALL_OUT_REPLY_PACKET),
sizeof(PPTP_CALL_IN_REQUEST_PACKET),
sizeof(PPTP_CALL_IN_REPLY_PACKET),
sizeof(PPTP_CALL_IN_CONNECT_PACKET),
sizeof(PPTP_CALL_CLEAR_REQUEST_PACKET),
sizeof(PPTP_CALL_DISCONNECT_NOTIFY_PACKET),
sizeof(PPTP_WAN_ERROR_NOTIFY_PACKET),
sizeof(PPTP_SET_LINK_INFO_PACKET)
};
static PCHAR aszCtlStateType[NUM_CONTROL_STATES+1] =
{
"INVALID",
"LISTEN",
"DIALING",
"WAIT_REQUEST",
"WAIT_REPLY",
"ESTABLISHED",
"WAIT_STOP",
"CLEANUP",
"UNKNOWN"
};
__inline PCHAR szCtlState(IN CONTROL_STATE state)
{
if (state >= 0 && state < NUM_CONTROL_STATES)
{
return aszCtlStateType[state];
}
else
{
return aszCtlStateType[NUM_CONTROL_STATES];
}
}
VOID
CtlCleanup(
PCONTROL_TUNNEL pCtl,
BOOLEAN Locked
);
VOID
CtlpEchoTimeout(
IN PVOID SystemSpecific1,
IN PVOID pContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
);
VOID
CtlpDeathTimeout(
IN PVOID SystemSpecific1,
IN PVOID pContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
);
CONTROL_STATE
CtlSetState(
IN PCONTROL_TUNNEL pCtl,
IN CONTROL_STATE State,
IN ULONG_PTR StateParam,
IN BOOLEAN Locked
);
CHAR *
ControlMsgToString(
ULONG Message
);
PCONTROL_TUNNEL
CtlAlloc(
PPPTP_ADAPTER pAdapter
)
{
PCONTROL_TUNNEL pCtl;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlAlloc\n")));
pCtl = MyMemAlloc(sizeof(CONTROL_TUNNEL), TAG_PPTP_TUNNEL);
if(!pCtl)
{
WPLOG(LL_A, LM_Res, ("Failed to alloc CONTROL_TUNNEL"));
return NULL;
}
#if LIST_CHECKING
NdisAcquireSpinLock(&pAdapter->Lock);
ASSERT(!CheckListEntry(&pAdapter->ControlTunnelList, &pCtl->ListEntry));
NdisReleaseSpinLock(&pAdapter->Lock);
#endif
NdisZeroMemory(pCtl, sizeof(CONTROL_TUNNEL));
pCtl->Signature = TAG_PPTP_TUNNEL;
pCtl->pAdapter = pAdapter;
pCtl->PptpMessageLength = PptpMessageLength_v1;
NdisAllocateSpinLock(&pCtl->Lock);
NdisInitializeListHead(&pCtl->CallList);
NdisInitializeListHead(&pCtl->MessageList);
NdisMInitializeTimer(&pCtl->Echo.Timer,
pAdapter->hMiniportAdapter,
CtlpEchoTimeout,
pCtl);
NdisMInitializeTimer(&pCtl->WaitTimeout,
pAdapter->hMiniportAdapter,
CtlpDeathTimeout,
pCtl);
NdisMInitializeTimer(&pCtl->StopTimeout,
pAdapter->hMiniportAdapter,
CtlpDeathTimeout,
pCtl);
INIT_REFERENCE_OBJECT(pCtl, CtlFree);
MyInterlockedInsertTailList(&pAdapter->ControlTunnelList,
&pCtl->ListEntry,
&pAdapter->Lock);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlAlloc %08x\n"), pCtl));
return pCtl;
}
VOID
CtlFree(PCONTROL_TUNNEL pCtl)
{
BOOLEAN NotUsed;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlFree %08x\n"), pCtl));
NdisAcquireSpinLock(&pgAdapter->Lock);
#if LIST_CHECKING
ASSERT(CheckListEntry(&pgAdapter->ControlTunnelList, &pCtl->ListEntry));
#endif
if (!IS_CTL(pCtl))
{
NdisReleaseSpinLock(&pgAdapter->Lock);
return;
}
ASSERT(REFERENCE_COUNT(pCtl) == 0);
ASSERT(IsListEmpty(&pCtl->CallList));
ASSERT(IsListEmpty(&pCtl->MessageList));
pCtl->Signature = TAG_FREE;
RemoveEntryList(&pCtl->ListEntry);
InitializeListHead(&pCtl->ListEntry);
NdisReleaseSpinLock(&pCtl->pAdapter->Lock);
// This duplicates some of the cleanup code, but attempting to stop
// the driver without first stopping tapi can result in an ungraceful
// shutdown.
NdisMCancelTimer(&pCtl->Echo.Timer, &NotUsed);
NdisMCancelTimer(&pCtl->WaitTimeout, &NotUsed);
NdisMCancelTimer(&pCtl->StopTimeout, &NotUsed);
if (pCtl->hCtdi)
{
WPLOG(LL_M, LM_TUNNEL, ("TCP disconnecting %!IPADDR!",
pCtl->Remote.Address.Address[0].Address[0].in_addr));
CtdiDisconnect(pCtl->hCtdi, FALSE);
CtdiClose(pCtl->hCtdi);
pCtl->hCtdi = NULL;
}
if (pCtl->hCtdiEndpoint)
{
CtdiClose(pCtl->hCtdiEndpoint);
pCtl->hCtdiEndpoint = NULL;
}
NdisFreeSpinLock(&pCtl->Lock);
MyMemFree(pCtl, sizeof(CONTROL_TUNNEL));
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlFree\n")));
}
PVOID
CtlpAllocPacketLocked(
PCONTROL_TUNNEL pCtl,
PPTP_MESSAGE_TYPE Message
)
{
PMESSAGE_HEADER pMsg;
ULONG PacketLength;
PPTP_HEADER *pHeader;
DEBUGMSG(DBG_TUNNEL, (DTEXT("+CtlpAllocPacketLocked %d\n"), Message));
ASSERT(IS_CTL(pCtl));
ASSERT(Message>=CONTROL_START_REQUEST && Message<NUM_MESSAGE_TYPES);
if (pCtl->State == STATE_CTL_CLEANUP)
{
WPLOG(LL_A, LM_TUNNEL, ("pCtl %p in CLEANUP state", pCtl));
return NULL;
}
PacketLength = pCtl->PptpMessageLength[Message];
pMsg = MyMemAlloc(sizeof(MESSAGE_HEADER)+PacketLength, TAG_CTL_PACKET);
if (!pMsg)
{
DEBUGMSG(DBG_ERROR, (DTEXT("Failed to alloc control packet\n")));
WPLOG(LL_A, LM_TUNNEL, ("Failed to alloc control packet"));
return NULL;
}
pHeader = (PVOID)&pMsg[1];
NdisZeroMemory(pMsg, sizeof(MESSAGE_HEADER)+PacketLength);
pMsg->Length = sizeof(MESSAGE_HEADER)+PacketLength;
pHeader->Length = htons((USHORT)PacketLength);
pHeader->PacketType = htons(PPTP_CONTROL_MESSAGE);
pHeader->Cookie = htonl(PPTP_MAGIC_COOKIE);
pHeader->MessageType = htons(Message);
InsertTailList(&pCtl->MessageList, &pMsg->ListEntry);
REFERENCE_OBJECT_EX(pCtl, CTL_REF_PACKET); // pair in CtlFreePacket
DEBUGMSG(DBG_TUNNEL, (DTEXT("-CtlpAllocPacketLocked %08x\n"), &pMsg[1]));
return &pMsg[1];
}
PVOID
CtlAllocPacket(
PCONTROL_TUNNEL pCtl,
PPTP_MESSAGE_TYPE Message
)
{
PVOID pPacket = NULL;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlAllocPacket\n")));
if (IS_CTL(pCtl))
{
NdisAcquireSpinLock(&pCtl->Lock);
pPacket = CtlpAllocPacketLocked(pCtl, Message);
NdisReleaseSpinLock(&pCtl->Lock);
}
if(!pPacket)
{
gCounters.ulCtlAllocPacketNull++;
}
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlAllocPacket\n")));
return pPacket;
}
VOID
CtlFreePacket(
PCONTROL_TUNNEL pCtl,
PVOID pPacket
)
{
PMESSAGE_HEADER pMsg = ((PMESSAGE_HEADER)pPacket) - 1;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlFreePacket %08x\n"), pPacket));
ASSERT(IS_CTL(pCtl));
NdisAcquireSpinLock(&pCtl->Lock);
RemoveEntryList(&pMsg->ListEntry);
if (pCtl->State==STATE_CTL_CLEANUP && IsListEmpty(&pCtl->MessageList))
{
CtlCleanup(pCtl, LOCKED);
}
NdisReleaseSpinLock(&pCtl->Lock);
MyMemFree(pMsg, pMsg->Length);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_PACKET); // pair in CtlpAllocPacket
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlFreePacket\n")));
}
STATIC VOID
CtlpCleanup(
IN PPPTP_WORK_ITEM pWorkItem
)
{
PCONTROL_TUNNEL pCtl = pWorkItem->Context;
BOOLEAN CleanupComplete = FALSE;
BOOLEAN TimeoutStopped;
DEBUGMSG(DBG_FUNC|DBG_TUNNEL, (DTEXT("+CtlpCleanup %08x\n"), pCtl));
ASSERT(IS_CTL(pCtl));
NdisAcquireSpinLock(&pCtl->Lock);
if (!IsListEmpty(&pCtl->CallList))
{
ENUM_CONTEXT Enum;
PCALL_SESSION pCall;
PLIST_ENTRY pListEntry;
REFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM);
NdisReleaseSpinLock(&pCtl->Lock);
InitEnumContext(&Enum);
while (pListEntry = EnumListEntry(&pCtl->CallList, &Enum, &pCtl->pAdapter->Lock))
{
pCall = CONTAINING_RECORD(pListEntry,
CALL_SESSION,
ListEntry);
if (IS_CALL(pCall))
{
CallEventDisconnect(pCall);
}
}
EnumComplete(&Enum, &pCtl->pAdapter->Lock);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM); // pair above
NdisAcquireSpinLock(&pCtl->Lock);
goto ccDone;
}
if (!IsListEmpty(&pCtl->MessageList))
{
goto ccDone;
}
NdisMCancelTimer(&pCtl->Echo.Timer, &TimeoutStopped);
if (pCtl->Reference.Count!=2 )
{
DEBUGMSG(DBG_TUNNEL, (DTEXT("CtlpCleanup %d references held, not cleaning up.\n"),
pCtl->Reference.Count));
goto ccDone;
}
NdisMCancelTimer(&pCtl->WaitTimeout, &TimeoutStopped);
NdisMCancelTimer(&pCtl->StopTimeout, &TimeoutStopped);
if (pCtl->hCtdi)
{
WPLOG(LL_I, LM_TUNNEL, ("TCP disconnecting %!IPADDR!",
pCtl->Remote.Address.Address[0].Address[0].in_addr));
CtdiDisconnect(pCtl->hCtdi, FALSE);
CtdiClose(pCtl->hCtdi);
pCtl->hCtdi = NULL;
}
if (pCtl->hCtdiEndpoint)
{
CtdiClose(pCtl->hCtdiEndpoint);
pCtl->hCtdiEndpoint = NULL;
}
CleanupComplete = TRUE;
ccDone:
if (!CleanupComplete)
{
pCtl->Cleanup = FALSE;
}
NdisReleaseSpinLock(&pCtl->Lock);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_CLEANUP); // For this work item.
if (CleanupComplete)
{
//CtdiDeleteHostRoute(&pCtl->Remote.Address);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_INITIAL); // Should be last one
}
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlpCleanup\n")));
}
VOID
CtlCleanup(
PCONTROL_TUNNEL pCtl,
BOOLEAN Locked
)
{
DEBUGMSG(DBG_FUNC|DBG_TUNNEL, (DTEXT("+CtlCleanup %08x\n"), pCtl));
if (!Locked)
{
NdisAcquireSpinLock(&pCtl->Lock);
}
if (!pCtl->Cleanup)
{
REFERENCE_OBJECT_EX(pCtl, CTL_REF_CLEANUP);
pCtl->Cleanup = TRUE;
if (ScheduleWorkItem(CtlpCleanup, pCtl, NULL, 0)!=NDIS_STATUS_SUCCESS)
{
pCtl->Cleanup = FALSE;
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_CLEANUP);
}
}
if (!Locked)
{
NdisReleaseSpinLock(&pCtl->Lock);
}
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlCleanup\n")));
}
VOID
CtlpQueryConnInfoCallback(
IN PVOID pContext,
IN PVOID pData,
IN NDIS_STATUS Result
)
{
PCONTROL_TUNNEL pCtl = pContext;
PTDI_CONNECTION_INFO pInfo = pData;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlpQueryConnInfoCallback Result=%x\n"), Result));
ASSERT(IS_CTL(pCtl));
if (Result==NDIS_STATUS_SUCCESS && IS_CTL(pCtl) && pCtl->State!=STATE_CTL_CLEANUP)
{
pCtl->Speed = (pInfo->Throughput.QuadPart==-1) ? 0 : pInfo->Throughput.LowPart;
DBG_D(DBG_INIT, pCtl->Speed);
}
MyMemFree(pInfo, sizeof(*pInfo));
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_QUERYCONNINFO);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlpQueryConnInfoCallback\n")));
}
VOID
CtlpQueryAddrInfoCallback(
IN PVOID pContext,
IN PVOID pData,
IN NDIS_STATUS Result
)
{
PCONTROL_TUNNEL pCtl = pContext;
PTDI_ADDRESS_INFO pAddrInfo = pData;
PTA_IP_ADDRESS pIp = (PTA_IP_ADDRESS)&pAddrInfo->Address;
ASSERT(IS_CTL(pCtl));
if (Result==NDIS_STATUS_SUCCESS && IS_CTL(pCtl) && pCtl->State!=STATE_CTL_CLEANUP)
{
pCtl->LocalAddress = pIp->Address[0].Address[0].in_addr;
}
MyMemFree(pAddrInfo, sizeof(*pAddrInfo));
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_QUERYADDRINFO);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlpQueryAddrInfoCallback\n")));
}
STATIC VOID
CtlpEngine(
IN PCONTROL_TUNNEL pCtl,
IN PUCHAR pNewData,
IN ULONG Length
)
{
UNALIGNED PPTP_HEADER *pHeader;
ULONG NeededLength;
ULONG MessageType;
BOOLEAN TimerStopped;
BOOLEAN PacketValid;
BOOLEAN Shutdown = FALSE;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlpEngine\n")));
// Check to see if we have a full packet yet.
ASSERT(IS_CTL(pCtl));
while (Length)
{
if (pCtl->BytesInPartialBuffer)
{
while (Length && pCtl->BytesInPartialBuffer < sizeof(PPTP_HEADER))
{
pCtl->PartialPacketBuffer[pCtl->BytesInPartialBuffer++] = *pNewData++;
Length--;
}
if (pCtl->BytesInPartialBuffer < sizeof(PPTP_HEADER))
{
return;
}
pHeader = (UNALIGNED PPTP_HEADER*)pCtl->PartialPacketBuffer;
}
else if (Length >= sizeof(PPTP_HEADER))
{
pHeader = (UNALIGNED PPTP_HEADER*)pNewData;
}
else
{
// Too little data. Copy and exit.
NdisMoveMemory(pCtl->PartialPacketBuffer, pNewData, Length);
pCtl->BytesInPartialBuffer = Length;
return;
}
// We have the header. Validate it.
PacketValid = FALSE;
NeededLength = htons(pHeader->Length);
MessageType = htons(pHeader->MessageType);
if (NeededLength <= MAX_CONTROL_PACKET_LENGTH &&
pHeader->Cookie == htonl(PPTP_MAGIC_COOKIE) &&
pHeader->PacketType == htons(PPTP_CONTROL_MESSAGE) &&
MessageType >= CONTROL_START_REQUEST &&
MessageType < NUM_MESSAGE_TYPES)
{
// ToDo: will probably need some v2 code here for the first packet
if (NeededLength == pCtl->PptpMessageLength[MessageType])
{
PacketValid = TRUE;
}
}
if (PacketValid)
{
if (pCtl->BytesInPartialBuffer+Length < NeededLength)
{
// We don't have the whole packet yet. Copy and exit.
NdisMoveMemory(&pCtl->PartialPacketBuffer[pCtl->BytesInPartialBuffer],
pNewData,
Length);
pCtl->BytesInPartialBuffer += Length;
return;
}
if (pCtl->BytesInPartialBuffer)
{
// Make cetain the entire packet is in our PartialPacketBuffer and process it there.
NeededLength -= pCtl->BytesInPartialBuffer;
NdisMoveMemory(&pCtl->PartialPacketBuffer[pCtl->BytesInPartialBuffer],
pNewData,
NeededLength);
// We now have one complete packet in the PartialPacketBuffer
pNewData += NeededLength;
Length -= NeededLength;
pHeader = (UNALIGNED PPTP_HEADER *)pCtl->PartialPacketBuffer;
// We've got the whole packet, and we're about to consume it
// Clear the var so next time through we start from the buffer
// beginning
pCtl->BytesInPartialBuffer = 0;
}
else
{
// The whole packet is in the new buffer. Process it there.
pHeader = (UNALIGNED PPTP_HEADER *)pNewData;
pNewData += NeededLength;
Length -= NeededLength;
}
switch (ntohs(pHeader->MessageType))
{
case CONTROL_START_REQUEST:
{
UNALIGNED PPTP_CONTROL_START_PACKET *pPacket =
(UNALIGNED PPTP_CONTROL_START_PACKET*)pHeader;
UNALIGNED PPTP_CONTROL_START_PACKET *pReply;
NDIS_STATUS Status;
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CONTROL_START_REQUEST\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CONTROL_START_REQUEST <- %!IPADDR!, pCtl %p, state %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State));
switch (pCtl->State)
{
case STATE_CTL_WAIT_REQUEST:
{
pReply = CtlAllocPacket(pCtl, CONTROL_START_REPLY);
if (pReply)
{
PTDI_CONNECTION_INFO pInfo;
PTDI_ADDRESS_INFO pAddrInfo;
NdisMCancelTimer(&pCtl->WaitTimeout, &TimerStopped);
// Take the pertinent data.
pCtl->Remote.Version = htons(pPacket->Version);
pCtl->Remote.Framing = htonl(pPacket->FramingCapabilities);
pCtl->Remote.Bearer = htonl(pPacket->BearerCapabilities);
NdisMoveMemory(pCtl->Remote.HostName, pPacket->HostName, MAX_HOSTNAME_LENGTH);
pCtl->Remote.HostName[MAX_HOSTNAME_LENGTH-1] = '\0';
NdisMoveMemory(pCtl->Remote.Vendor, pPacket->Vendor, MAX_VENDOR_LENGTH);
pCtl->Remote.Vendor[MAX_VENDOR_LENGTH-1] = '\0';
pReply->Version = ntohs(PPTP_PROTOCOL_VERSION_1_00);
pReply->FramingCapabilities = htonl(FRAMING_SYNC);
pReply->BearerCapabilities = htonl(BEARER_ANALOG|BEARER_DIGITAL);
pReply->MaxChannels = 0;
pReply->FirmwareRevision = htons(PPTP_FIRMWARE_REVISION);
// NdisMoveMemory(pReply->HostName, PptpHostName, MAX_HOSTNAME_LENGTH);
NdisMoveMemory(pReply->Vendor, PPTP_VENDOR, min(sizeof(PPTP_VENDOR), MAX_VENDOR_LENGTH));
if (pCtl->Remote.Version==PPTP_PROTOCOL_VERSION_1_00)
{
pReply->ResultCode = RESULT_CONTROL_START_SUCCESS;
CtlSetState(pCtl, STATE_CTL_ESTABLISHED, 0, UNLOCKED);
}
else
{
WPLOG(LL_A, LM_TUNNEL, ("Bad version %d", pCtl->Remote.Version));
pReply->ResultCode = RESULT_CONTROL_START_VERSION_NOT_SUPPORTED;
Shutdown = TRUE;
}
WPLOG(LL_M, LM_TUNNEL, ("SEND CONTROL_START_REPLY (SUCCESS) -> %!IPADDR!, pCtl %p",
pCtl->Remote.Address.Address[0].Address[0].in_addr, pCtl));
Status = CtlSend(pCtl, pReply);
if(Status != NDIS_STATUS_PENDING && Status != NDIS_STATUS_SUCCESS)
{
Shutdown = TRUE;
}
if(!Shutdown)
{
pInfo = MyMemAlloc(sizeof(*pInfo), TAG_CTL_CONNINFO);
if (pInfo)
{
REFERENCE_OBJECT_EX(pCtl, CTL_REF_QUERYCONNINFO);
// Get the interface speed
Status = CtdiQueryInformation(pCtl->hCtdi,
TDI_QUERY_CONNECTION_INFO,
pInfo,
sizeof(*pInfo),
CtlpQueryConnInfoCallback,
pCtl);
ASSERT(Status == NDIS_STATUS_PENDING);
}
else
{
// Not so harmful, the deault speed is reported if it fails
WPLOG(LL_A, LM_Res, ("Failed to alloc CONNECTION_INFO"));
}
// Query the local address
pAddrInfo = MyMemAlloc(sizeof(*pAddrInfo)+TDI_ADDRESS_LENGTH_IP, TAG_CTL_CONNINFO);
if (pAddrInfo)
{
REFERENCE_OBJECT_EX(pCtl, CTL_REF_QUERYADDRINFO);
// Get the interface speed
Status = CtdiQueryInformation(pCtl->hCtdi,
TDI_QUERY_ADDRESS_INFO,
pAddrInfo,
sizeof(*pAddrInfo)+TDI_ADDRESS_LENGTH_IP,
CtlpQueryAddrInfoCallback,
pCtl);
ASSERT(Status == NDIS_STATUS_PENDING);
}
else
{
WPLOG(LL_A, LM_Res, ("Failed to alloc ADDRESS_INFO"));
}
}
}
else
{
// Alloc failed. Do nothing, and timeout will cause cleanup.
}
break;
}
case STATE_CTL_CLEANUP:
DEBUGMSG(DBG_WARN, (DTEXT("Shutting down tunnel, ignore packet\n")));
WPLOG(LL_A, LM_TUNNEL, ("Shutting down tunnel, ignore packet"));
break;
default:
DEBUGMSG(DBG_WARN, (DTEXT("Bad state, shutdown tunnel\n")));
WPLOG(LL_A, LM_TUNNEL, ("Bad state, shutdown tunnel"));
Shutdown = TRUE;
break;
}
break;
}
case CONTROL_START_REPLY:
{
UNALIGNED PPTP_CONTROL_REPLY_PACKET *pPacket =
(UNALIGNED PPTP_CONTROL_REPLY_PACKET*)pHeader;
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CONTROL_START_REPLY\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CONTROL_START_REPLY <- %!IPADDR!, pCtl %p, state %d, RC %d, EC %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State, pPacket->ResultCode, pPacket->ErrorCode));
switch (pCtl->State)
{
case STATE_CTL_WAIT_REPLY:
{
ENUM_CONTEXT Enum;
PCALL_SESSION pCall;
PLIST_ENTRY pListEntry;
NdisMCancelTimer(&pCtl->WaitTimeout, &TimerStopped);
// Take the pertinent data.
pCtl->Remote.Version = htons(pPacket->Version);
pCtl->Remote.Framing = htonl(pPacket->FramingCapabilities);
pCtl->Remote.Bearer = htonl(pPacket->BearerCapabilities);
NdisMoveMemory(pCtl->Remote.HostName, pPacket->HostName, MAX_HOSTNAME_LENGTH);
pCtl->Remote.HostName[MAX_HOSTNAME_LENGTH-1] = '\0';
NdisMoveMemory(pCtl->Remote.Vendor, pPacket->Vendor, MAX_VENDOR_LENGTH);
pCtl->Remote.Vendor[MAX_VENDOR_LENGTH-1] = '\0';
if (pCtl->Remote.Version==PPTP_PROTOCOL_VERSION_1_00 &&
pPacket->ResultCode == RESULT_CONTROL_START_SUCCESS &&
pPacket->ErrorCode == 0)
{
CtlSetState(pCtl, STATE_CTL_ESTABLISHED, 0, UNLOCKED);
// Notify all calls on this session.
REFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM);
InitEnumContext(&Enum);
while (pListEntry = EnumListEntry(&pCtl->CallList, &Enum, &pCtl->pAdapter->Lock))
{
pCall = CONTAINING_RECORD(pListEntry,
CALL_SESSION,
ListEntry);
if (IS_CALL(pCall))
{
CallEventOutboundTunnelEstablished(pCall,
NDIS_STATUS_SUCCESS);
}
}
EnumComplete(&Enum, &pCtl->pAdapter->Lock);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM); // pair above
}
else
{
Shutdown = TRUE;
}
break;
}
default:
break;
}
break;
}
case CONTROL_ECHO_REQUEST:
{
UNALIGNED PPTP_CONTROL_ECHO_REQUEST_PACKET *pPacket =
(UNALIGNED PPTP_CONTROL_ECHO_REQUEST_PACKET*)pHeader;
UNALIGNED PPTP_CONTROL_ECHO_REPLY_PACKET *pReply;
DEBUGMSG(DBG_TUNNEL, (DTEXT("ECHO_REQUEST RECEIVED\n")));
WPLOG(LL_V, LM_CMsg, ("RECV Echo-Request <- %!IPADDR!",
pCtl->Remote.Address.Address[0].Address[0].in_addr));
pCtl->Echo.Needed = FALSE;
if (pCtl->Remote.Version==PPTP_PROTOCOL_VERSION_1_00)
{
pReply = CtlAllocPacket(pCtl, CONTROL_ECHO_REPLY);
if (pReply)
{
pReply->Identifier = pPacket->Identifier;
pReply->ResultCode = RESULT_CONTROL_ECHO_SUCCESS;
// ToDo: why would we send a ResultCode==failure?
CtlSend(pCtl, pReply); // ToDo: return value?
}
}
break;
}
case CALL_OUT_REQUEST:
{
UNALIGNED PPTP_CALL_OUT_REQUEST_PACKET *pPacket =
(UNALIGNED PPTP_CALL_OUT_REQUEST_PACKET*)pHeader;
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CALL_OUT_REQUEST\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CALL_OUT_REQUEST <- %!IPADDR!, pCtl %p, state %d, Peer's Cid %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State, htons(pPacket->CallId)));
switch (pCtl->State)
{
case STATE_CTL_ESTABLISHED:
{
DBG_X(DBG_TUNNEL, htons(pPacket->CallId));
DBG_X(DBG_TUNNEL, htons(pPacket->SerialNumber));
DBG_D(DBG_TUNNEL, htonl(pPacket->MinimumBPS));
DBG_D(DBG_TUNNEL, htonl(pPacket->MaximumBPS));
DBG_X(DBG_TUNNEL, htonl(pPacket->BearerType));
DBG_X(DBG_TUNNEL, htonl(pPacket->FramingType));
DBG_X(DBG_TUNNEL, htons(pPacket->RecvWindowSize));
DBG_X(DBG_TUNNEL, htons(pPacket->ProcessingDelay));
DBG_X(DBG_TUNNEL, htons(pPacket->PhoneNumberLength));
DBG_S(DBG_TUNNEL, pPacket->PhoneNumber);
DBG_S(DBG_TUNNEL, pPacket->Subaddress);
// It's possible we had just closed our last call & were
// waiting for a CONTROL_STOP_REQUEST from the other side.
NdisMCancelTimer(&pCtl->StopTimeout, &TimerStopped);
CallEventCallOutRequest(pCtl->pAdapter,
pCtl,
pPacket);
break;
}
default:
// Bogus, but not enough to kill us. Ignore it.
break;
}
break;
}
case CALL_OUT_REPLY:
{
UNALIGNED PPTP_CALL_OUT_REPLY_PACKET *pPacket =
(UNALIGNED PPTP_CALL_OUT_REPLY_PACKET*)pHeader;
ULONG PktCallId = htons(pPacket->PeerCallId);
ULONG CallId = CallIdToDeviceId(PktCallId);
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CALL_OUT_REPLY\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CALL_OUT_REPLY, pCtl %p, state %d, Cid %d Pkt-Cid %d, Peer's Cid %d, RC %d, EC %d",
pCtl, pCtl->State, CallId, PktCallId, htons(pPacket->CallId),
pPacket->ResultCode, pPacket->ErrorCode));
if (pCtl->State==STATE_CTL_ESTABLISHED)
{
PCALL_SESSION pCall = CallGetCall(pCtl->pAdapter, CallId);
if (pCall)
{
DBG_X(DBG_TUNNEL, htons(pPacket->PeerCallId));
DBG_X(DBG_TUNNEL, pPacket->ResultCode);
DBG_X(DBG_TUNNEL, pPacket->ErrorCode);
DBG_X(DBG_TUNNEL, htons(pPacket->CauseCode));
DBG_D(DBG_TUNNEL, htonl(pPacket->ConnectSpeed));
DBG_D(DBG_TUNNEL, htons(pPacket->RecvWindowSize));
DBG_X(DBG_TUNNEL, htonl(pPacket->PhysicalChannelId));
CallEventCallOutReply(pCall, pPacket);
}
else
{
DEBUGMSG(DBG_WARN, (DTEXT("Bogus PeerCallId %d\n"), PktCallId));
WPLOG(LL_A, LM_TUNNEL, ("Bogus PeerCallId %d", PktCallId));
}
}
else
{
// Bogus, but not enough to kill us. Ignore it.
WPLOG(LL_A, LM_TUNNEL, ("Tunnel state is not ESTABLISHED!"));
}
break;
}
case CALL_CLEAR_REQUEST:
{
UNALIGNED PPTP_CALL_CLEAR_REQUEST_PACKET *pPacket =
(UNALIGNED PPTP_CALL_CLEAR_REQUEST_PACKET*)pHeader;
ULONG CallId = htons(pPacket->CallId);
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CALL_CLEAR_REQUEST\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CALL_CLEAR_REQUEST <- %!IPADDR!, pCtl %p, state %d Peer's Cid %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State, CallId));
if (pCtl->State==STATE_CTL_ESTABLISHED)
{
ENUM_CONTEXT Enum;
PCALL_SESSION pCall;
PLIST_ENTRY pListEntry;
// The peer has given us his own call ID, which does little
// good for fast lookup of the call. Find the associated call.
REFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM);
InitEnumContext(&Enum);
do
{
NdisAcquireSpinLock(&pCtl->pAdapter->Lock);
pListEntry = EnumListEntry(&pCtl->CallList, &Enum, NULL);
if(pListEntry != NULL)
{
pCall = CONTAINING_RECORD(pListEntry,
CALL_SESSION,
ListEntry);
if (IS_CALL(pCall) && pCall->pCtl==pCtl && pCall->Remote.CallId==CallId)
{
REFERENCE_OBJECT(pCall);
NdisReleaseSpinLock(&pCtl->pAdapter->Lock);
CallEventCallClearRequest(pCall, pPacket, pCtl);
DEREFERENCE_OBJECT(pCall);
break;
}
}
NdisReleaseSpinLock(&pCtl->pAdapter->Lock);
} while(pListEntry != NULL);
EnumComplete(&Enum, &pCtl->pAdapter->Lock);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM); // pair above
}
break;
}
case CALL_DISCONNECT_NOTIFY:
{
UNALIGNED PPTP_CALL_DISCONNECT_NOTIFY_PACKET *pPacket =
(UNALIGNED PPTP_CALL_DISCONNECT_NOTIFY_PACKET*)pHeader;
ULONG CallId = htons(pPacket->CallId);
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CALL_DISCONNECT_NOTIFY\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CALL_DISCONNECT_NOTIFY <- %!IPADDR!, pCtl %p, state %d, Peer's Cid %d, RC %d, EC %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State, CallId, pPacket->ResultCode, pPacket->ErrorCode));
if (pCtl->State==STATE_CTL_ESTABLISHED)
{
ENUM_CONTEXT Enum;
PCALL_SESSION pCall;
PLIST_ENTRY pListEntry;
// The peer has given us his own call ID, which does little
// good for fast lookup of the call. Find the associated call.
REFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM);
InitEnumContext(&Enum);
while (pListEntry = EnumListEntry(&pCtl->CallList, &Enum, &pCtl->pAdapter->Lock))
{
pCall = CONTAINING_RECORD(pListEntry,
CALL_SESSION,
ListEntry);
if (IS_CALL(pCall) && pCall->pCtl==pCtl && pCall->Remote.CallId==CallId)
{
DBG_X(DBG_TUNNEL, htons(pPacket->CallId));
DBG_X(DBG_TUNNEL, pPacket->ResultCode);
DBG_X(DBG_TUNNEL, pPacket->ErrorCode);
DBG_X(DBG_TUNNEL, htons(pPacket->CauseCode));
CallEventCallDisconnectNotify(pCall, pPacket);
break;
}
}
EnumComplete(&Enum, &pCtl->pAdapter->Lock);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM); // pair above
}
break;
}
case CONTROL_STOP_REQUEST:
{
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CONTROL_STOP_REQUEST\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CONTROL_STOP_REQUEST <- %!IPADDR!, pCtl %p, state %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State));
switch (pCtl->State)
{
case STATE_CTL_WAIT_STOP:
{
NdisMCancelTimer(&pCtl->StopTimeout, &TimerStopped);
// No break
}
case STATE_CTL_WAIT_REPLY:
case STATE_CTL_ESTABLISHED:
{
UNALIGNED PPTP_CONTROL_STOP_PACKET *pReply;
pReply = CtlAllocPacket(pCtl, CONTROL_STOP_REPLY);
if (pReply)
{
pReply->ResultCode = RESULT_CONTROL_STOP_SUCCESS;
WPLOG(LL_M, LM_TUNNEL, ("SEND CONTROL_STOP_REPLY (SUCCESS) -> %!IPADDR!",
pCtl->Remote.Address.Address[0].Address[0].in_addr));
CtlSend(pCtl, pReply);
}
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, UNLOCKED);
CtlCleanup(pCtl, UNLOCKED);
break;
}
default:
break;
}
break;
}
case CONTROL_STOP_REPLY:
{
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CONTROL_STOP_REPLY\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CONTROL_STOP_REPLY <- %!IPADDR!, pCtl %p, state %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State));
if (pCtl->State==STATE_CTL_WAIT_STOP)
{
NdisMCancelTimer(&pCtl->StopTimeout, &TimerStopped);
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, UNLOCKED);
CtlCleanup(pCtl, UNLOCKED);
}
break;
}
case SET_LINK_INFO:
{
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV SET_LINK_INFO\n")));
WPLOG(LL_M, LM_CMsg, ("RECV SET_LINK_INFO <- %!IPADDR!, pCtl %p",
pCtl->Remote.Address.Address[0].Address[0].in_addr, pCtl));
break;
}
case CALL_IN_REQUEST:
{
UNALIGNED PPTP_CALL_IN_REQUEST_PACKET *pPacket =
(UNALIGNED PPTP_CALL_IN_REQUEST_PACKET*)pHeader;
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CALL_IN_REQUEST\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CALL_IN_REQUEST <- %!IPADDR!, pCtl %p, state %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State));
if (pCtl->State==STATE_CTL_ESTABLISHED)
{
DBG_X(DBG_TUNNEL, htons(pPacket->CallId));
DBG_X(DBG_TUNNEL, htons(pPacket->SerialNumber));
DBG_X(DBG_TUNNEL, htonl(pPacket->BearerType));
DBG_X(DBG_TUNNEL, htonl(pPacket->PhysicalChannelId));
DBG_X(DBG_TUNNEL, htons(pPacket->DialedNumberLength));
DBG_X(DBG_TUNNEL, htons(pPacket->DialingNumberLength));
DBG_S(DBG_TUNNEL, pPacket->DialedNumber);
DBG_S(DBG_TUNNEL, pPacket->DialingNumber);
DBG_S(DBG_TUNNEL, pPacket->Subaddress);
// It's possible we had just closed our last call & were
// waiting for a CONTROL_STOP_REQUEST from the other side.
NdisMCancelTimer(&pCtl->StopTimeout, &TimerStopped);
CallEventCallInRequest(pCtl->pAdapter,
pCtl,
pPacket);
}
break;
}
case CALL_IN_REPLY:
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CALL_IN_REPLY\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CALL_IN_REPLY <- %!IPADDR!, pCtl %p, state %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State));
// We should never get one of these.
break;
case CALL_IN_CONNECTED:
{
UNALIGNED PPTP_CALL_IN_CONNECT_PACKET *pPacket =
(UNALIGNED PPTP_CALL_IN_CONNECT_PACKET*)pHeader;
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV CALL_IN_CONNECTED\n")));
WPLOG(LL_M, LM_CMsg, ("RECV CALL_IN_CONNECTED <- %!IPADDR!, pCtl %p, state %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->State));
if (pCtl->State==STATE_CTL_ESTABLISHED)
{
ULONG CallId = htons(pPacket->PeerCallId);
PCALL_SESSION pCall;
DBG_X(DBG_TUNNEL, htons(pPacket->PeerCallId));
DBG_X(DBG_TUNNEL, htonl(pPacket->ConnectSpeed));
DBG_X(DBG_TUNNEL, htons(pPacket->RecvWindowSize));
DBG_X(DBG_TUNNEL, htons(pPacket->ProcessingDelay));
DBG_X(DBG_TUNNEL, htonl(pPacket->FramingType));
pCall = CallGetCall(pCtl->pAdapter, CallIdToDeviceId(CallId));
if (pCall)
{
CallEventCallInConnect(pCall, pPacket);
}
}
break;
}
case CONTROL_ECHO_REPLY:
DEBUGMSG(DBG_TUNNEL, (DTEXT("CONTROL_ECHO_REPLY RECEIVED\n")));
WPLOG(LL_V, LM_CMsg, ("RECV CONTROL_ECHO_REPLY <- %!IPADDR!",
pCtl->Remote.Address.Address[0].Address[0].in_addr));
break;
case WAN_ERROR_NOTIFY:
DEBUGMSG(DBG_TUNNEL, (DTEXT("RECV WAN_ERROR_NOTIFY\n")));
WPLOG(LL_A, LM_CMsg, ("RECV WAN_ERROR_NOTIFY <- %!IPADDR!",
pCtl->Remote.Address.Address[0].Address[0].in_addr));
break;
default:
DEBUGMSG(DBG_TUNNEL|DBG_WARN, (DTEXT("UNKNOWN RECEIVED\n")));
WPLOG(LL_A, LM_CMsg, ("RECV unknown message %d <- %!IPADDR!",
ntohs(pHeader->MessageType),
pCtl->Remote.Address.Address[0].Address[0].in_addr));
break;
}
}
else // !PacketValid
{
Shutdown = TRUE;
WPLOG(LL_A, LM_TUNNEL, ("RECV Invalid packet <- %!IPADDR!" \
" NeededLength %d, Cookie 0x%0x, P-Type %d, M-Type %d",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
NeededLength, pHeader->Cookie, pHeader->PacketType, MessageType));
}
if (Shutdown)
{
DEBUGMSG(DBG_TUNNEL|DBG_ERROR, (DTEXT("Tunnel problem, shutting it down.\n")));
WPLOG(LL_A, LM_TUNNEL, ("Tunnel problem, shutting it down."));
//CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, UNLOCKED);
//CtlCleanup(pCtl, UNLOCKED);
break;
}
}
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlpEngine\n")));
}
NDIS_STATUS
CtlConnectQueryCallback(
IN PVOID pContext,
IN PTRANSPORT_ADDRESS pAddress,
IN HANDLE hNewCtdi,
OUT PVOID *pNewContext
)
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PPPTP_ADAPTER pAdapter = pContext;
PTA_IP_ADDRESS pIp = (PTA_IP_ADDRESS)pAddress;
PCONTROL_TUNNEL pCtl;
BOOLEAN Accepting = TRUE;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlConnectQueryCallback\n")));
DEBUGMSG(DBG_TUNNEL, (DTEXT("Request to connect from %d.%d.%d.%d\n"),
IPADDR(pIp->Address[0].Address[0].in_addr)));
WPLOG(LL_M, LM_TUNNEL, ("New TCP connection from %!IPADDR!, pCtdi %p",
pIp->Address[0].Address[0].in_addr, hNewCtdi));
if(PptpAuthenticateIncomingCalls)
{
ULONG i;
BOOLEAN bMatch = FALSE;
for (i=0; i<g_ulAcceptClientAddresses; i++)
{
if ((pIp->Address[0].Address[0].in_addr & g_AcceptClientList[i].Mask)==
(g_AcceptClientList[i].Address & g_AcceptClientList[i].Mask))
{
bMatch = TRUE;
break;
}
}
if (!bMatch)
{
DEBUGMSG(DBG_TUNNEL|DBG_WARN, (DTEXT("No match found for IP %d.%d.%d.%d. Refused.\n"),
IPADDR(pIp->Address[0].Address[0].in_addr)));
WPLOG(LL_A, LM_TUNNEL, ("IP not authenticated %!IPADDR!",
pIp->Address[0].Address[0].in_addr));
Accepting = FALSE;
Status = NDIS_STATUS_FAILURE;
}
}
if (Accepting)
{
pCtl = CtlAlloc(pAdapter);
if (!pCtl)
{
Status = NDIS_STATUS_RESOURCES;
}
else
{
NdisAcquireSpinLock(&pAdapter->Lock);
CtlSetState(pCtl, STATE_CTL_WAIT_REQUEST, 0, LOCKED);
pCtl->Inbound = TRUE;
NdisReleaseSpinLock(&pAdapter->Lock);
NdisAcquireSpinLock(&pCtl->Lock);
pCtl->hCtdi = hNewCtdi;
pCtl->Remote.Address = *pIp;
NdisMSetTimer(&pCtl->WaitTimeout, PptpMessageTimeout*1000);
if (PptpEchoTimeout)
{
NdisMSetPeriodicTimer(&pCtl->Echo.Timer, PptpEchoTimeout*1000);
pCtl->Echo.Needed = TRUE;
}
NdisReleaseSpinLock(&pCtl->Lock);
*pNewContext = pCtl;
}
}
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtlConnectQueryCallback %08x\n"), Status));
return Status;
}
NDIS_STATUS
CtlConnectCompleteCallback(
IN PVOID pContext,
IN HANDLE hNewCtdi,
IN NDIS_STATUS ConnectStatus
)
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PCONTROL_TUNNEL pCtl = pContext;
PPTP_CONTROL_START_PACKET *pPacket = NULL;
PTDI_CONNECTION_INFO pInfo;
PTDI_ADDRESS_INFO pAddrInfo;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlConnectCompleteCallback\n")));
if (IS_CTL(pCtl))
{
if (pCtl->State!=STATE_CTL_DIALING)
{
DEBUGMSG(DBG_TUNNEL|DBG_ERR(Status), (DTEXT("Ctl in wrong state after connect %d\n"), pCtl->State));
WPLOG(LL_A, LM_CMsg, ("pCtl %p in wrong state after connect %d",
pCtl, pCtl->State));
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, UNLOCKED);
CtlCleanup(pCtl, UNLOCKED);
Status = NDIS_STATUS_FAILURE;
}
else if (ConnectStatus==NDIS_STATUS_SUCCESS)
{
pPacket = CtlAllocPacket(pCtl, CONTROL_START_REQUEST);
CtlSetState(pCtl, STATE_CTL_WAIT_REPLY, 0, UNLOCKED);
NdisAcquireSpinLock(&pCtl->Lock);
pCtl->hCtdi = hNewCtdi;
NdisMSetTimer(&pCtl->WaitTimeout, PptpMessageTimeout*1000);
if (PptpEchoTimeout)
{
NdisMSetPeriodicTimer(&pCtl->Echo.Timer, PptpEchoTimeout*1000);
pCtl->Echo.Needed = TRUE;
}
NdisReleaseSpinLock(&pCtl->Lock);
WPLOG(LL_M, LM_CMsg, ("TCP connection to %!IPADDR! is UP, starting tunnel pCtl %p",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl));
if (pPacket)
{
pPacket->Version = ntohs(PPTP_PROTOCOL_VERSION_1_00); // ToDo: do v2
pPacket->FramingCapabilities = ntohl(FRAMING_ASYNC);
pPacket->BearerCapabilities = ntohl(BEARER_ANALOG);
pPacket->MaxChannels = 0;
pPacket->FirmwareRevision = htons(PPTP_FIRMWARE_REVISION);
// NdisMoveMemory(pPacket->HostName, PptpHostName, MAX_HOSTNAME_LENGTH);
NdisMoveMemory(pPacket->Vendor, PPTP_VENDOR, min(sizeof(PPTP_VENDOR), MAX_VENDOR_LENGTH));
WPLOG(LL_M, LM_TUNNEL, ("SEND CONTROL_START_REQUEST -> %!IPADDR!",
pCtl->Remote.Address.Address[0].Address[0].in_addr));
CtlSend(pCtl, pPacket); // ToDo: return value?
}
else
{
// Allocation failure will be covered by timeout
}
pInfo = MyMemAlloc(sizeof(*pInfo), TAG_CTL_CONNINFO);
if (pInfo)
{
REFERENCE_OBJECT_EX(pCtl, CTL_REF_QUERYCONNINFO);
Status = CtdiQueryInformation(pCtl->hCtdi,
TDI_QUERY_CONNECTION_INFO,
pInfo,
sizeof(*pInfo),
CtlpQueryConnInfoCallback,
pCtl);
ASSERT(NT_SUCCESS(Status));
Status = NDIS_STATUS_SUCCESS;
}
else
{
WPLOG(LL_A, LM_Res, ("Failed to alloc CTL_CONNINFO"));
}
// Query the local address
pAddrInfo = MyMemAlloc(sizeof(*pAddrInfo)+TDI_ADDRESS_LENGTH_IP, TAG_CTL_CONNINFO);
if (pAddrInfo)
{
REFERENCE_OBJECT_EX(pCtl, CTL_REF_QUERYADDRINFO);
Status = CtdiQueryInformation(pCtl->hCtdi,
TDI_QUERY_ADDRESS_INFO,
pAddrInfo,
sizeof(*pAddrInfo)+TDI_ADDRESS_LENGTH_IP,
CtlpQueryAddrInfoCallback,
pCtl);
ASSERT(NT_SUCCESS(Status));
Status = NDIS_STATUS_SUCCESS;
}
else
{
WPLOG(LL_A, LM_Res, ("Failed to alloc CTL_CONNINFO"));
}
}
else
{
ENUM_CONTEXT Enum;
PCALL_SESSION pCall;
PLIST_ENTRY pListEntry;
WPLOG(LL_A, LM_CMsg, ("TCP Connection to %!IPADDR! failed. pCtl %p NDIS Error 0x%x",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, ConnectStatus));
REFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM);
InitEnumContext(&Enum);
while (pListEntry = EnumListEntry(&pCtl->CallList, &Enum, &pCtl->pAdapter->Lock))
{
pCall = CONTAINING_RECORD(pListEntry,
CALL_SESSION,
ListEntry);
if (IS_CALL(pCall))
{
CallEventConnectFailure(pCall, ConnectStatus);
}
}
EnumComplete(&Enum, &pCtl->pAdapter->Lock);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_ENUM); // pair above
Status = NDIS_STATUS_FAILURE;
}
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_CONNECTCALLBACK); // pair at call to CtdiConnect
}
else
{
Status = NDIS_STATUS_FAILURE;
WPLOG(LL_A, LM_CMsg, ("Invalid pCtl %p", pCtl));
}
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtlConnectCompleteCallback %08x\n"), Status));
return Status;
}
NDIS_STATUS
CtlDisconnectCallback(
IN PVOID pContext,
IN BOOLEAN Abortive
)
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PCONTROL_TUNNEL pCtl = pContext;
BOOLEAN Cleanup = TRUE;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlDisconnectCallback\n")));
WPLOG(LL_M, LM_TUNNEL, ("TCP %!IPADDR! is DOWN, pCtl %p pCtdi %p",
pCtl->Remote.Address.Address[0].Address[0].in_addr,
pCtl, pCtl->hCtdi));
NdisAcquireSpinLock(&pCtl->pAdapter->Lock);
if (pCtl->State!=STATE_CTL_CLEANUP)
{
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, LOCKED);
Cleanup = TRUE;
}
NdisReleaseSpinLock(&pCtl->pAdapter->Lock);
if (Cleanup)
{
CtlCleanup(pCtl, UNLOCKED);
}
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtlDisconnectCallback %08x\n"), Status));
return Status;
}
NDIS_STATUS
CtlReceiveCallback(
IN PVOID pContext,
IN PUCHAR pBuffer,
IN ULONG ulLength
)
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PCONTROL_TUNNEL pCtl = pContext;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlReceiveCallback\n")));
// We must copy or consume the data before leaving this function.
ASSERT(IS_CTL(pCtl));
CtlpEngine(pCtl, pBuffer, ulLength);
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtlReceiveCallback %08x\n"), Status));
return Status;
}
NDIS_STATUS
CtlConnectCall(
IN PPPTP_ADAPTER pAdapter,
IN PCALL_SESSION pCall,
IN PTA_IP_ADDRESS pTargetAddress
)
{
TA_IP_ADDRESS Local;
PCONTROL_TUNNEL pCtl = NULL;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ENUM_CONTEXT Enum;
PLIST_ENTRY pListEntry;
BOOLEAN SignalEstablished = FALSE;
BOOLEAN Connected = FALSE;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlConnectCall\n")));
DEBUGMSG(DBG_CALL, (DTEXT("New Dial request: Call:%p Addr:%08x\n"),
pCall, pTargetAddress->Address[0].Address[0].in_addr));
WPLOG(LL_M, LM_CALL, ("Dial: %!IPADDR! pCall %p",
pTargetAddress->Address[0].Address[0].in_addr, pCall));
InitEnumContext(&Enum);
NdisAcquireSpinLock(&pAdapter->Lock);
while (!Connected && (pListEntry = EnumListEntry(&pAdapter->ControlTunnelList, &Enum, NULL)))
{
pCtl = CONTAINING_RECORD(pListEntry, CONTROL_TUNNEL, ListEntry);
if((pCtl->State>=STATE_CTL_DIALING && pCtl->State<=STATE_CTL_ESTABLISHED) &&
pTargetAddress->Address[0].Address[0].in_addr==pCtl->Remote.Address.Address[0].Address[0].in_addr)
{
DEBUGMSG(DBG_CALL, (DTEXT("Existing tunnel found for call %08x\n"), pCall));
WPLOG(LL_M, LM_CALL, ("Existing tunnel pCtl %p found for call %p", pCtl, pCall));
REFERENCE_OBJECT_EX(pCtl, CTL_REF_CTLCONNECT);
NdisReleaseSpinLock(&pAdapter->Lock);
Connected = CallConnectToCtl(pCall, pCtl, FALSE);
if (Connected)
{
// keep the reference for now
}
else
{
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_CTLCONNECT);
}
NdisAcquireSpinLock(&pAdapter->Lock);
if (Connected && pCtl->State==STATE_CTL_ESTABLISHED)
{
SignalEstablished = TRUE;
}
}
}
EnumComplete(&Enum, NULL);
NdisReleaseSpinLock(&pAdapter->Lock);
if (Connected)
{
if (SignalEstablished)
{
CallEventOutboundTunnelEstablished(pCall,
NDIS_STATUS_SUCCESS);
}
// We found an existing tunnel, for which we have a reference. Drop it.
if(pCtl)
{
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_CTLCONNECT);
}
}
else
{
HANDLE hCtdiEndpoint;
pCtl = CtlAlloc(pAdapter);
if (!pCtl)
{
Status = NDIS_STATUS_RESOURCES;
goto cmcDone;
}
WPLOG(LL_M, LM_CALL, ("New tunnel pCtl %p created for call %p", pCtl, pCall));
NdisAcquireSpinLock(&pAdapter->Lock);
CtlSetState(pCtl, STATE_CTL_DIALING, 0, LOCKED);
pCtl->Inbound = pCall->Inbound;
NdisReleaseSpinLock(&pAdapter->Lock);
Connected = CallConnectToCtl(pCall, pCtl, FALSE);
if (!Connected)
{
Status = NDIS_STATUS_FAILURE;
}
else
{
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;
Status = CtdiCreateEndpoint(&hCtdiEndpoint,
AF_INET,
SOCK_STREAM,
(PTRANSPORT_ADDRESS)&Local,
0);
if (Status!=NDIS_STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, (DTEXT("CtdiCreateEndpoint (STREAM) failed %08x\n"), Status));
WPLOG(LL_A, LM_TUNNEL, ("CtdiCreateEndpoint (STREAM) failed %08x", Status));
goto cmcDone;
}
NdisAcquireSpinLock(&pCtl->Lock);
pCtl->hCtdiEndpoint = hCtdiEndpoint;
pCtl->Remote.Address = *pTargetAddress;
REFERENCE_OBJECT_EX(pCtl, CTL_REF_CONNECTCALLBACK); // Pair in CtlConnectCompleteCallback
NdisReleaseSpinLock(&pCtl->Lock);
WPLOG(LL_M, LM_TUNNEL, ("Attempting to set a TCP connection pCtl %p, pCtdi %p",
pCtl, hCtdiEndpoint));
Status = CtdiConnect(pCtl->hCtdiEndpoint,
(PTRANSPORT_ADDRESS)pTargetAddress,
CtlConnectCompleteCallback,
CtlReceiveCallback,
CtlDisconnectCallback,
pCtl);
}
}
cmcDone:
if ( (Status!=NDIS_STATUS_SUCCESS && Status!=NDIS_STATUS_PENDING))
{
if (IS_CTL(pCtl))
{
WPLOG(LL_A, LM_TUNNEL, ("TCP Connection to %!IPADDR! failed immediately NDIS Error 0x%x",
pCtl->Remote.Address.Address[0].Address[0].in_addr, Status));
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, UNLOCKED);
CtlCleanup(pCtl, UNLOCKED);
}
}
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtlConnectCall %08x\n"), Status));
return Status;
}
NDIS_STATUS
CtlDisconnectCall(
IN PCALL_SESSION pCall
)
{
PPPTP_ADAPTER pAdapter = pCall->pAdapter;
PCONTROL_TUNNEL pCtl = pCall->pCtl;
BOOLEAN Inbound = pCall->Inbound;
BOOLEAN CloseTunnelNow = FALSE;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlDisconnectCall\n")));
ASSERT(IS_CTL(pCtl));
DEBUGMSG(DBG_CALL, (DTEXT("Call:%08x disconnected\n"), pCall));
REFERENCE_OBJECT_EX(pCtl, CTL_REF_DISCONNECT);
CallDisconnectFromCtl(pCall, pCtl);
NdisAcquireSpinLock(&pAdapter->Lock);
if (IsListEmpty(&pCtl->CallList))
{
if (pCtl->State==STATE_CTL_ESTABLISHED)
{
if (!pCtl->Inbound)
{
CtlSetState(pCtl, STATE_CTL_WAIT_STOP, 0, LOCKED);
CloseTunnelNow = TRUE;
}
else
{
NdisMSetTimer(&pCtl->StopTimeout, PptpMessageTimeout*1000);
}
}
else
{
// Tunnel is already gone.
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, LOCKED);
NdisReleaseSpinLock(&pAdapter->Lock);
CtlCleanup(pCtl, UNLOCKED);
NdisAcquireSpinLock(&pAdapter->Lock);
}
}
NdisReleaseSpinLock(&pAdapter->Lock);
if (CloseTunnelNow)
{
PPPTP_CONTROL_STOP_PACKET pPacket;
pPacket = CtlAllocPacket(pCtl, CONTROL_STOP_REQUEST);
if (!pPacket)
{
Status = NDIS_STATUS_RESOURCES;
// Don't attempt to shutdown gracefully. Just close everything.
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, UNLOCKED);
CtlCleanup(pCtl, UNLOCKED);
}
else
{
pPacket->Reason = CONTROL_STOP_GENERAL;
WPLOG(LL_M, LM_TUNNEL, ("SEND CONTROL_STOP_REQUEST (STOP_GENERAL) -> %!IPADDR!",
pCtl->Remote.Address.Address[0].Address[0].in_addr));
CtlSend(pCtl, pPacket);
NdisAcquireSpinLock(&pCtl->Lock);
NdisMSetTimer(&pCtl->StopTimeout, PptpMessageTimeout*1000);
NdisReleaseSpinLock(&pCtl->Lock);
}
}
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_DISCONNECT); // Pair above
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtlDisconnectCall, %08x\n"), Status));
return Status;
}
NDIS_STATUS
CtlListen(
IN PPPTP_ADAPTER pAdapter
)
{
NDIS_STATUS Status;
TA_IP_ADDRESS Ip;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlListen\n")));
if (pAdapter->hCtdiListen)
{
// Already listening. Bail with success.
Status = NDIS_STATUS_SUCCESS;
goto clDone;
}
NdisZeroMemory(&Ip, sizeof(Ip));
Ip.TAAddressCount = 1;
Ip.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
Ip.Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
Ip.Address[0].Address[0].sin_port = htons(PptpControlPort);
Ip.Address[0].Address[0].in_addr = 0;
Status = CtdiCreateEndpoint(&pAdapter->hCtdiListen,
AF_INET,
SOCK_STREAM,
(PTRANSPORT_ADDRESS)&Ip,
0);
if (Status!=NDIS_STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, (DTEXT("CtdiCreateEndpoint (LISTEN) failed %08x\n"), Status));
WPLOG(LL_A, LM_TUNNEL, ("CtdiCreateEndpoint (LISTEN) failed %08x", Status));
goto clDone;
}
Status = CtdiListen(pAdapter->hCtdiListen,
PptpListensPending,
CtlConnectQueryCallback,
CtlReceiveCallback,
CtlDisconnectCallback,
pAdapter);
if (Status!=NDIS_STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, (DTEXT("CtdiListen failed %08x\n"), Status));
WPLOG(LL_A, LM_TUNNEL, ("CtdiListen failed %08x", Status));
goto clDone;
}
clDone:
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtlListen %08x\n"), Status));
return Status;
}
STATIC VOID
CtlpSendMessageComplete(
IN PVOID pContext,
IN PVOID pDatagramContext,
IN PUCHAR pBuffer,
IN NDIS_STATUS Result
)
{
PCONTROL_TUNNEL pCtl = pContext;
UNREFERENCED_PARAMETER(pDatagramContext);
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlpSendMessageComplete\n")));
ASSERT(IS_CTL(pCtl));
CtlFreePacket(pCtl, pBuffer);
if (Result!=NDIS_STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, (DTEXT("Failed to send control message %08x\n"), Result));
WPLOG(LL_A, LM_TUNNEL, ("Failed to send control message %08x", Result));
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, UNLOCKED);
CtlCleanup(pCtl, UNLOCKED);
}
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlpSendMessageComplete\n")));
}
NDIS_STATUS
CtlSend(
IN PCONTROL_TUNNEL pCtl,
IN PVOID pPacketBuffer
)
{
ULONG PacketLength = htons(((UNALIGNED PPTP_HEADER *)pPacketBuffer)->Length);
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlSend %08x\n"), pPacketBuffer));
ASSERT(IS_CTL(pCtl));
DEBUGMSG(DBG_TUNNEL, (DTEXT("SENDING %s\n"),
ControlMsgToString( htons(((UNALIGNED PPTP_HEADER *)pPacketBuffer)->MessageType))));
Status = CtdiSend(pCtl->hCtdi,
CtlpSendMessageComplete,
pCtl,
pPacketBuffer,
PacketLength);
if (Status==NDIS_STATUS_PENDING)
{
Status = NDIS_STATUS_SUCCESS;
}
if (Status!=NDIS_STATUS_SUCCESS)
{
gCounters.ulCtlSendFail++;
WPLOG(LL_A, LM_TUNNEL, ("Failed with status %x", Status));
}
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-CtlSend %08x\n"), Status));
return Status;
}
CONTROL_STATE
CtlSetState(
IN PCONTROL_TUNNEL pCtl,
IN CONTROL_STATE State,
IN ULONG_PTR StateParam,
IN BOOLEAN Locked
)
{
CONTROL_STATE PreviousState;
ASSERT(IS_CTL(pCtl));
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlSetState %p state %d --> %d\n"), pCtl, pCtl->State, State));
WPLOG(LL_M, LM_TUNNEL, ("pCtl %p state %s --> %s",
pCtl, szCtlState(pCtl->State), szCtlState(State)));
if (!Locked)
{
NdisAcquireSpinLock(&pCtl->pAdapter->Lock);
}
PreviousState = pCtl->State;
pCtl->State = State;
if (!Locked)
{
NdisReleaseSpinLock(&pCtl->pAdapter->Lock);
}
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlSetState\n")));
return PreviousState;
}
// StanA: lift some functions from L2TP to convert an IP address to text
VOID
ReversePsz(
IN OUT CHAR* psz )
// Reverse the order of the characters in 'psz' in place.
//
{
CHAR* pchLeft;
CHAR* pchRight;
pchLeft = psz;
pchRight = psz + strlen( psz ) - 1;
while (pchLeft < pchRight)
{
CHAR ch;
ch = *pchLeft;
*pchLeft++ = *pchRight;
*pchRight-- = ch;
}
}
VOID
ultoa(
IN ULONG ul,
OUT CHAR* pszBuf )
// Convert 'ul' to null-terminated string form in caller's 'pszBuf'. It's
// caller job to make sure 'pszBuf' is long enough to hold the returned
// string.
//
{
CHAR* pch;
pch = pszBuf;
do
{
*pch++ = (CHAR )((ul % 10) + '0');
ul /= 10;
}
while (ul);
*pch = '\0';
ReversePsz( pszBuf );
}
PWCHAR
StringToIpAddressW(
IN PWCHAR pszIpAddress,
IN OUT PTA_IP_ADDRESS pAddress,
OUT PBOOLEAN pValidAddress
)
// Convert an address of the form #.#.#.#[:#][ \0] to a binary ip address
// [ and optional port ]
// Return a pointer to the end of the address. If the string is determined
// to not contain an IP address, return the passed-in pszIpAddress unchanged
// ToDo: IPv6
{
PWCHAR pStartString = pszIpAddress;
ULONG Octet;
ULONG NumOctets;
ULONG IpAddress = IPADDR_ZERO;
ULONG Port = PptpControlPort;
*pValidAddress = FALSE;
// Find the first digit.
while (*pszIpAddress && (*pszIpAddress<L'0' || *pszIpAddress>L'9'))
{
pszIpAddress++;
}
if (!*pszIpAddress)
{
return pStartString;
}
for (NumOctets = 0; NumOctets<4 && *pszIpAddress; NumOctets++)
{
Octet = 0;
while (*pszIpAddress && *pszIpAddress>=L'0' && *pszIpAddress<=L'9')
{
Octet = Octet * 10 + *pszIpAddress - L'0';
if (Octet>0xff)
{
return pStartString;
}
pszIpAddress++;
}
if (NumOctets < 3)
{
if (*pszIpAddress!='.' || *(++pszIpAddress) < L'0' || *pszIpAddress > L'9')
{
return pStartString;
}
}
IpAddress = (IpAddress << 8) + Octet;
}
if (*pszIpAddress==':')
{
// They've also specified the port. Parse it.
while (*pszIpAddress && *pszIpAddress>=L'0' && *pszIpAddress<=L'9')
{
Port = Port * 10 + *pszIpAddress - L'0';
if (Port>0xffff)
{
return pStartString;
}
pszIpAddress++;
}
}
if(IpAddress != IPADDR_ZERO && IpAddress != IPADDR_BROADCAST && !IPADDR_IS_MULTICAST(IpAddress))
{
pAddress->TAAddressCount = 1;
pAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
pAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
pAddress->Address[0].Address[0].sin_port = htons((USHORT)Port);
pAddress->Address[0].Address[0].in_addr = htonl(IpAddress);
*pValidAddress = TRUE;
}
return pszIpAddress;
}
PUCHAR
StringToIpAddress(
IN PUCHAR pszIpAddress,
IN OUT PTA_IP_ADDRESS pAddress,
OUT PBOOLEAN pValidAddress
)
// Convert an address of the form #.#.#.#[:#][ \0] to a binary ip address
// [ and optional port ]
// Return a pointer to the end of the address. If the string is determined
// to not contain an IP address, return the passed-in pszIpAddress unchanged
// ToDo: IPv6
{
PUCHAR pStartString = pszIpAddress;
ULONG Octet;
ULONG NumOctets;
ULONG IpAddress = IPADDR_ZERO;
ULONG Port = PptpControlPort;
*pValidAddress = FALSE;
// Find the first digit.
while (*pszIpAddress && (*pszIpAddress<'0' || *pszIpAddress>'9'))
{
pszIpAddress++;
}
if (!*pszIpAddress)
{
return pStartString;
}
for (NumOctets = 0; NumOctets<4 && *pszIpAddress; NumOctets++)
{
Octet = 0;
while (*pszIpAddress && *pszIpAddress>='0' && *pszIpAddress<='9')
{
Octet = Octet * 10 + *pszIpAddress - '0';
if (Octet>0xff)
{
return pStartString;
}
pszIpAddress++;
}
if (NumOctets < 3)
{
if (*pszIpAddress!='.' || *(++pszIpAddress) < '0' || *pszIpAddress > '9')
{
return pStartString;
}
}
IpAddress = (IpAddress << 8) + Octet;
}
if (*pszIpAddress==':')
{
// They've also specified the port. Parse it.
while (*pszIpAddress && *pszIpAddress>='0' && *pszIpAddress<='9')
{
Port = Port * 10 + *pszIpAddress - '0';
if (Port>0xffff)
{
return pStartString;
}
pszIpAddress++;
}
}
// Validate the IP address
// IpAddress in host byte order
if(IpAddress != IPADDR_ZERO && IpAddress != IPADDR_BROADCAST && !IPADDR_IS_MULTICAST(IpAddress))
{
pAddress->TAAddressCount = 1;
pAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
pAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
pAddress->Address[0].Address[0].sin_port = htons((USHORT)Port);
pAddress->Address[0].Address[0].in_addr = htonl(IpAddress);
*pValidAddress = TRUE;
}
return pszIpAddress;
}
VOID
IpAddressToString(
IN ULONG ulIpAddress,
OUT CHAR* pszIpAddress )
// Converts network byte-ordered IP addresss 'ulIpAddress' to a string in
// the a.b.c.d form and returns same in caller's 'pszIpAddress' buffer.
// The buffer should be at least 16 characters long.
//
// ToDo: IPv6
{
CHAR szBuf[ 3 + 1 ];
ULONG ulA = (ulIpAddress & 0xFF000000) >> 24;
ULONG ulB = (ulIpAddress & 0x00FF0000) >> 16;
ULONG ulC = (ulIpAddress & 0x0000FF00) >> 8;
ULONG ulD = (ulIpAddress & 0x000000FF);
ultoa( ulA, szBuf );
strcpy( pszIpAddress, szBuf );
strcat( pszIpAddress, "." );
ultoa( ulB, szBuf );
strcat( pszIpAddress, szBuf );
strcat( pszIpAddress, "." );
ultoa( ulC, szBuf );
strcat( pszIpAddress, szBuf );
strcat( pszIpAddress, "." );
ultoa( ulD, szBuf );
strcat( pszIpAddress, szBuf );
}
VOID
CtlpEchoTimeout(
IN PVOID SystemSpecific1,
IN PVOID pContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
)
{
PCONTROL_TUNNEL pCtl = pContext;
PPTP_CONTROL_ECHO_REQUEST_PACKET *pPacket;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlpEchoTimeout\n")));
NdisAcquireSpinLock(&pCtl->Lock);
// Don't take the adapter lock because we're only reading the State
if (pCtl->State!=STATE_CTL_CLEANUP)
{
BOOLEAN DoEcho = pCtl->Echo.Needed;
LONG Identifier = ++(pCtl->Echo.Identifier);
pCtl->Echo.Needed = TRUE;
NdisReleaseSpinLock(&pCtl->Lock);
if (DoEcho)
{
pPacket = CtlAllocPacket(pCtl, CONTROL_ECHO_REQUEST);
if (pPacket)
{
pPacket->Identifier = Identifier;
// ToDo: deal with V2 stuff
CtlSend(pCtl, pPacket);
}
}
}
else
{
NdisReleaseSpinLock(&pCtl->Lock);
}
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlpEchoTimeout\n")));
}
VOID
CtlpDeathTimeout(
IN PVOID SystemSpecific1,
IN PVOID pContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
)
{
PCONTROL_TUNNEL pCtl = pContext;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlpDeathTimeout\n")));
WPLOG(LL_A, LM_TUNNEL, ("Fatal timeout on tunnel %!IPADDR!",
pCtl->Remote.Address.Address[0].Address[0].in_addr));
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, UNLOCKED);
CtlCleanup(pCtl, UNLOCKED);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlpDeathTimeout\n")));
}
VOID
CtlpCleanupLooseEnds(
PPPTP_ADAPTER pAdapter
)
{
ENUM_CONTEXT Enum;
PLIST_ENTRY pListEntry;
PCONTROL_TUNNEL pCtl;
DEBUGMSG(DBG_FUNC, (DTEXT("+CtlpCleanupLooseEnds\n")));
NdisAcquireSpinLock(&pAdapter->Lock);
InitEnumContext(&Enum);
while (pListEntry = EnumListEntry(&pAdapter->ControlTunnelList, &Enum, NULL))
{
pCtl = CONTAINING_RECORD(pListEntry, CONTROL_TUNNEL, ListEntry);
if (pCtl->State==STATE_CTL_CLEANUP)
{
// REFERENCE the Ctl so it doesn't go away before we call CtlCleanup
REFERENCE_OBJECT_EX(pCtl, CTL_REF_CLEANUPLOOSEENDS);
NdisReleaseSpinLock(&pAdapter->Lock);
CtlCleanup(pCtl, UNLOCKED);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_CLEANUPLOOSEENDS); // Pair above
NdisAcquireSpinLock(&pAdapter->Lock);
}
}
EnumComplete(&Enum, NULL);
NdisReleaseSpinLock(&pAdapter->Lock);
DEBUGMSG(DBG_FUNC, (DTEXT("-CtlpCleanupLooseEnds\n")));
}
NDIS_STATUS
PptpInitialize(
PPPTP_ADAPTER pAdapter
)
{
NDIS_STATUS Status;
TA_IP_ADDRESS Local;
HANDLE hCtdi;
DEBUGMSG(DBG_FUNC, (DTEXT("+PptpInitialize\n")));
if(!PptpClientSide)
{
Status = RngInit();
if (Status!=STATUS_SUCCESS)
{
WPLOG(LL_A, LM_Res, ("RngInit failed %08x", Status));
goto piDone;
}
}
Status = CtdiInitialize(CTDI_FLAG_ENABLE_ROUTING|CTDI_FLAG_NETWORK_HEADER);
if (Status!=STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, (DTEXT("CtdiInitialize failed %08x\n"), Status));
WPLOG(LL_A, LM_TUNNEL, ("CtdiInitialize failed %08x", Status));
goto piDone;
}
if (!pAdapter->hCtdiDg)
{
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 = PptpProtocolNumber;
Local.Address[0].Address[0].in_addr = 0;
Status = CtdiCreateEndpoint(&hCtdi,
AF_INET,
SOCK_RAW, //ToDo: RAWIP?
(PTRANSPORT_ADDRESS)&Local,
sizeof(DGRAM_CONTEXT));
if (Status!=NDIS_STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, (DTEXT("CtdiCreateEndpoint (RAW) failed %08x\n"), Status));
WPLOG(LL_A, LM_TUNNEL, ("CtdiCreateEndpoint (RAW) failed %08x", Status));
goto piDone;
}
CtdiEnableIpHdrIncl(hCtdi);
NdisAcquireSpinLock(&pAdapter->Lock);
pAdapter->hCtdiDg = hCtdi;
NdisReleaseSpinLock(&pAdapter->Lock);
Status = CtdiSetEventHandler(pAdapter->hCtdiDg,
TDI_EVENT_RECEIVE_DATAGRAM,
CallReceiveDatagramCallback,
pAdapter);
if (Status!=NDIS_STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, (DTEXT("CtdiSetEventHandler failed %08x\n"), Status));
WPLOG(LL_A, LM_TUNNEL, ("CtdiSetEventHandler failed %08x", Status));
goto piDone;
}
}
piDone:
if (Status==STATUS_SUCCESS)
{
PptpInitialized = TRUE;
}
DEBUGMSG(DBG_FUNC|DBG_ERR(Status), (DTEXT("-PptpInitialize %08x\n"), Status));
return Status;
}
CHAR *pControlMessageStrings[] =
{
"INVALID CONTROL MESSAGE NUMBER", // 0
"CONTROL_START_REQUEST", // 1
"CONTROL_START_REPLY", // 2
"CONTROL_STOP_REQUEST", // 3
"CONTROL_STOP_REPLY", // 4
"CONTROL_ECHO_REQUEST", // 5
"CONTROL_ECHO_REPLY", // 6
"CALL_OUT_REQUEST", // 7
"CALL_OUT_REPLY", // 8
"CALL_IN_REQUEST", // 9
"CALL_IN_REPLY", // 10
"CALL_IN_CONNECTED", // 11
"CALL_CLEAR_REQUEST", // 12
"CALL_DISCONNECT_NOTIFY", // 13
"WAN_ERROR_NOTIFY", // 14
"SET_LINK_INFO", // 15
};
CHAR *ControlMsgToString( ULONG Message )
{
if( Message >= NUM_MESSAGE_TYPES ){
return pControlMessageStrings[0];
}else{
return pControlMessageStrings[Message];
}
}
VOID
CtlpCleanupCtls(
PPPTP_ADAPTER pAdapter
)
{
ENUM_CONTEXT Enum;
PLIST_ENTRY pListEntry;
PCONTROL_TUNNEL pCtl;
NdisAcquireSpinLock(&pAdapter->Lock);
InitEnumContext(&Enum);
while (pListEntry = EnumListEntry(&pAdapter->ControlTunnelList, &Enum, NULL))
{
pCtl = CONTAINING_RECORD(pListEntry, CONTROL_TUNNEL, ListEntry);
// REFERENCE the Ctl so it doesn't go away before we call CtlCleanup
REFERENCE_OBJECT_EX(pCtl, CTL_REF_CLEANUPCTLS);
NdisReleaseSpinLock(&pAdapter->Lock);
CtlSetState(pCtl, STATE_CTL_CLEANUP, 0, UNLOCKED);
CtlCleanup(pCtl, UNLOCKED);
DEREFERENCE_OBJECT_EX(pCtl, CTL_REF_CLEANUPCTLS); // Pair above
NdisAcquireSpinLock(&pAdapter->Lock);
}
EnumComplete(&Enum, NULL);
NdisReleaseSpinLock(&pAdapter->Lock);
}