📄 option.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
//
// PPP Option Negotiation Implementation
//
// Include Files
#include "windows.h"
#include "types.h"
#include "layerfsm.h"
#include "debug.h"
#include "raserror.h"
#define MALLOC(size) LocalAlloc(LPTR, (size))
#define FREE(ptr) LocalFree(ptr)
void
OptionContextInitialize(
POptionContext pContext,
PSTR szProtocolName,
OPTION_RESET_PEER_OPT_CB CbResetPeerOptions,
PVOID CbContext)
//
// Initialize an option negotiation context structure.
//
{
pContext->szProtocolName = szProtocolName;
pContext->pOptionList = NULL;
pContext->CbResetPeerOptions = CbResetPeerOptions;
pContext->CbContext = CbContext;
pContext->dwMaxFailure = DEFAULT_MAX_FAILURE;
pContext->dwConsecutiveFailureCount = 0;
}
void
OptionContextCleanup(
POptionContext pContext)
//
// Release all the resources allocated for an option negotiation context
//
{
POptionInfo pInfo,
pNext;
if (pContext)
{
for (pInfo = pContext->pOptionList; pInfo; pInfo = pNext)
{
pNext = pInfo->next;
FREE(pInfo);
}
}
}
DWORD
OptionInfoAdd(
POptionContext pContext,
POptionDescriptor pDescriptor,
OptionRequireLevel orlLocal,
OptionRequireLevel orlPeer
)
//
// Add an option to the list of options known by the context.
//
{
POptionInfo pInfo, *ppInfo;
DWORD dwResult = NO_ERROR;
pInfo = (POptionInfo)MALLOC(sizeof(OptionInfo));
if (pInfo == NULL)
{
dwResult = ERROR_OUTOFMEMORY;
}
else
{
pInfo->pDescriptor = pDescriptor;
pInfo->orlLocal = orlLocal;
pInfo->orlPeer = orlPeer;
pInfo->onsLocal = ONS_Initial;
// Add the new node to the end of the Option List
for (ppInfo = &pContext->pOptionList; *ppInfo; ppInfo = &(*ppInfo)->next)
;
pInfo->next = NULL;
*ppInfo = pInfo;
}
return dwResult;
}
POptionInfo
OptionInfoFind(
POptionContext pContext,
BYTE type)
//
// Find the option info for the specified type.
//
// Return NULL if type not found.
//
{
POptionInfo pInfo;
for (pInfo = pContext->pOptionList;
pInfo;
pInfo = pInfo->next)
{
if (pInfo->pDescriptor->type == type)
break;
}
return pInfo;
}
DWORD
OptionSetORL(
POptionContext pContext,
BYTE type,
OptionRequireLevel orlLocal,
OptionRequireLevel orlPeer
)
//
// Set the ORLs for the specified option
//
{
POptionInfo pInfo;
DWORD dwResult = NO_ERROR;
pInfo = OptionInfoFind(pContext, type);
if (pInfo)
{
pInfo->orlLocal = orlLocal;
pInfo->orlPeer = orlPeer;
}
else
{
dwResult = ERROR_INVALID_PARAMETER;
}
return dwResult;
}
DWORD
OptionResponseAdd(
IN OUT PBYTE pResponse,
IN DWORD cbResponse,
IN BYTE code, // ACK, NAK, REJ
IN BYTE optionType, // protocol specific option type identifier
IN PBYTE pOptData, OPTIONAL
IN DWORD cbOptData)
//
// Add an option to a response packet being built.
//
// If the response packet is already a NAK or REJ, then don't add
// any ACKs to it.
// If the response packet is already a REJ, the only add more REJs
// to it.
//
{
BYTE codeCurrent = pResponse[0];
DWORD dwResult = NO_ERROR;
DWORD offset;
DWORD lenOpt;
ASSERT(cbOptData < 254);
if (code < codeCurrent)
{
// Don't add the option to the response, since
// we are already doing a REJ or a NAK of other options.
}
else
{
if (code > codeCurrent)
{
// Reset the packet to the new code
pResponse[0] = code;
pResponse[2] = 0;
pResponse[3] = 4;
}
// Add the option to the list of options in the response
offset = (pResponse[2] << 8) | pResponse[3];
lenOpt = 2 + cbOptData;
if (offset + lenOpt < cbResponse)
{
pResponse[offset] = optionType;
pResponse[offset + 1] = (BYTE)lenOpt;
memcpy(pResponse + offset + 2, pOptData, cbOptData);
offset += lenOpt;
pResponse[2] = (BYTE)(offset >> 8);
pResponse[3] = (BYTE)offset;
}
else
{
dwResult = ERROR_INSUFFICIENT_BUFFER;
}
}
return dwResult;
}
DWORD
OptionsWalk(
IN PBYTE pOptions,
IN DWORD cbOptions,
IN OUT POptionContext pContext, OPTIONAL
IN DWORD (*pOptionCb)(POptionContext pContext, POptionHeader pOption) OPTIONAL)
//
// Walk through the options contained within a frame, calling the
// specified function on each option.
// If the options are invalid in any way, return ERROR_PPP_INVALID_PACKET.
//
{
POptionHeader pOption;
DWORD dwResult = NO_ERROR;
BOOL frameOptionSizesOk = TRUE;
for (pOption = (POptionHeader)pOptions;
cbOptions;
pOption = (POptionHeader)((PBYTE)pOption + pOption->Len))
{
if ((cbOptions < 2)
|| (pOption->Len < 2)
|| (pOption->Len > cbOptions ))
{
// Invalid frame, either:
// 1. Insufficient size for the option header (Type + Len)
// 2. option length is too small (less than option header size)
// 3. option length is too large (greater than remaining bytes in frame)
dwResult = ERROR_PPP_INVALID_PACKET;
break;
}
if (pOptionCb)
{
dwResult = pOptionCb(pContext, pOption);
if (dwResult != NO_ERROR)
break;
}
cbOptions -= pOption->Len;
}
return dwResult;
}
#ifdef DEBUG
void
OptionDebugAppendOptionList(
IN POptionContext pContext,
IN PSTR pBuffer,
IN PBYTE pOptions,
IN DWORD cbOptions)
//
// Append to pBuffer an option list in ASCII readable text.
//
{
POptionHeader pOption;
BOOL frameOptionSizesOk = TRUE;
POptionInfo pInfo;
pBuffer += strlen(pBuffer);
for (pOption = (POptionHeader)pOptions;
cbOptions;
pOption = (POptionHeader)((PBYTE)pOption + pOption->Len))
{
if ((cbOptions < 2)
|| (pOption->Len < 2)
|| (pOption->Len > cbOptions ))
{
pBuffer += sprintf(pBuffer, "ERROR - BAD OPTION Len=%u cbOptions=%u ", pOption->Len, cbOptions);
break;
}
pInfo = OptionInfoFind(pContext, pOption->Type);
if (pInfo)
{
pBuffer += sprintf(pBuffer, " %hs", pInfo->pDescriptor->szName);
}
else
{
pBuffer += sprintf(pBuffer, " [%u]", pOption->Type);
}
if (pOption->Len > OPTION_HEADER_LENGTH)
{
BYTE cbOptData = pOption->Len - OPTION_HEADER_LENGTH;
PBYTE pOptData = (PBYTE)pOption + OPTION_HEADER_LENGTH;
*(pBuffer++) = '=';
if (pInfo && pInfo->pDescriptor->OptionToStrCb)
{
pInfo->pDescriptor->OptionToStrCb(pContext->CbContext, pInfo, pOptData, cbOptData, &pBuffer);
}
else
{
// Just do hex dump of option data
while (cbOptData--)
{
pBuffer += sprintf(pBuffer, "%02X", *pOptData++);
}
}
}
cbOptions -= pOption->Len;
}
}
#endif
DWORD
OptionRequest(
IN OUT POptionContext pContext,
IN POptionHeader pOption)
//
// The peer is requesting a value for the option via a configure-request.
//
{
DWORD dwResult = NO_ERROR;
POptionInfo pInfo;
BYTE type = pOption->Type;
BYTE len = pOption->Len;
BYTE code;
PBYTE pOptData = (PBYTE)pOption + OPTION_HEADER_LENGTH;
DWORD cbOptData = len - OPTION_HEADER_LENGTH;
//
// Any length errors should have been caught already.
//
ASSERT(len >= OPTION_HEADER_LENGTH);
pInfo = OptionInfoFind(pContext, type);
if (pInfo == NULL || pInfo->orlPeer == ORL_Unsupported)
{
//
// We don't support this option, inform the peer via a reject.
//
code = PPP_CONFIGURE_REJ;
}
else if (pInfo->pDescriptor->fixedLength != OPTION_VARIABLE_LENGTH
&& pInfo->pDescriptor->fixedLength != cbOptData)
{
//
// Option length is invalid. Pretty hopeless situation if the peer is unable to
// set the option length to the value we expect.
//
DEBUGMSG(ZONE_WARN, (TEXT("PPP: WARNING - Protocol %hs Option %hs length is %u, expected %u\n"),
pContext->szProtocolName, pInfo->pDescriptor->szName, len, pInfo->pDescriptor->fixedLength));
code = PPP_CONFIGURE_REJ;
}
else
{
pInfo->bRequested = TRUE;
pInfo->pDescriptor->RequestOptionCb(pContext->CbContext, pInfo, &code, &pOptData, &cbOptData);
if (code == PPP_CONFIGURE_NAK
&& pContext->dwConsecutiveFailureCount >= pContext->dwMaxFailure)
{
DEBUGMSG(ZONE_WARN, (TEXT("PPP: WARNING - Protocol %hs Failing to converge, REJecting Option %hs instead of NAKing\n"),
pContext->szProtocolName, pInfo->pDescriptor->szName));
code = PPP_CONFIGURE_REJ;
pOptData = (PBYTE)pOption + OPTION_HEADER_LENGTH;
cbOptData = len - OPTION_HEADER_LENGTH;
}
}
//
// Update the response
//
dwResult = OptionResponseAdd(pContext->pResponse, pContext->cbResponse, code, type, pOptData, cbOptData);
return dwResult;
}
DWORD
OptionAcked(
IN OUT POptionContext pContext,
IN POptionHeader pOption)
//
// The value for the option has been accepted from the negotiations,
// and we can now make it go into effect.
//
{
DWORD dwResult = NO_ERROR;
POptionInfo pInfo;
BYTE type = pOption->Type;
BYTE len = pOption->Len;
//
// Any length errors should have been caught already.
//
ASSERT(len >= OPTION_HEADER_LENGTH);
pInfo = OptionInfoFind(pContext, type);
//
// All the options in an ACK message should be ones that we sent
// in a config-request. And we should only be sending options
// that we support in a config-request. Hence info should always
// be found for an option here.
//
ASSERT(pInfo && pInfo->onsLocal == ONS_Requested);
if (pInfo)
{
ASSERT(pInfo->pDescriptor->fixedLength == OPTION_VARIABLE_LENGTH || pInfo->pDescriptor->fixedLength == len - OPTION_HEADER_LENGTH);
pInfo->onsLocal = ONS_Acked;
if (pInfo->pDescriptor->AckOptionCb)
dwResult = pInfo->pDescriptor->AckOptionCb(pContext->CbContext, pInfo, (PBYTE)pOption + OPTION_HEADER_LENGTH, len - OPTION_HEADER_LENGTH);
}
return dwResult;
}
DWORD
OptionNak(
IN OUT POptionContext pContext,
IN POptionHeader pOption)
//
// The peer is suggesting a value for the option.
//
{
DWORD dwResult = NO_ERROR;
POptionInfo pInfo;
BYTE type = pOption->Type;
BYTE len = pOption->Len;
PBYTE pOptData = (PBYTE)pOption + OPTION_HEADER_LENGTH;
DWORD cbOptData = len - OPTION_HEADER_LENGTH;
//
// Any length errors should have been caught already.
//
ASSERT(len >= OPTION_HEADER_LENGTH);
pInfo = OptionInfoFind(pContext, type);
//
// A NAK can to either suggest an alternative value for an option
// that was present in the configure request, or can be requesting
// the negotiation of an option that was not present in the configure-request.
// In the latter case, we may not support that option at all, so
// pInfo will be NULL and we just ignore the peer on that option.
//
if (pInfo && pInfo->orlLocal != ORL_Unsupported)
{
if (pInfo->pDescriptor->fixedLength != OPTION_VARIABLE_LENGTH
&& pInfo->pDescriptor->fixedLength != cbOptData)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -