windows-nt/Source/XPSP1/NT/net/rras/ip/nathlp/dhcp/dhcpmsg.cpp
2020-09-26 16:20:57 +08:00

1933 lines
49 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1998, Microsoft Corporation
Module Name:
dhcpmsg.c
Abstract:
This module contains declarations related to the DHCP allocator's
message-processing.
Author:
Abolade Gbadegesin (aboladeg) 6-Mar-1998
Revision History:
Raghu Gatta (rgatta) 15-Dec-2000
+ Changed manner in which the option DHCP_TAG_DOMAIN_NAME is
added in DhcpBuildReplyMessage().
+ Inform DNS component via DnsUpdate() in DhcpProcessRequestMessage().
Raghu Gatta (rgatta) 20-Apr-2001
+ IP/1394 support changes
--*/
#include "precomp.h"
#pragma hdrstop
//
// EXTERNAL DECLARATIONS
//
extern PIP_DNS_PROXY_GLOBAL_INFO DnsGlobalInfo;
extern PWCHAR DnsICSDomainSuffix;
extern CRITICAL_SECTION DnsGlobalInfoLock;
//
// FORWARD DECLARATIONS
//
VOID
DhcpAppendOptionToMessage(
DHCP_OPTION UNALIGNED** Optionp,
UCHAR Tag,
UCHAR Length,
UCHAR Option[]
);
VOID
DhcpBuildReplyMessage(
PDHCP_INTERFACE Interfacep,
PNH_BUFFER Bufferp,
DHCP_OPTION UNALIGNED** Option,
UCHAR MessageType,
BOOLEAN DynamicDns,
DHCP_OPTION UNALIGNED* OptionArray[]
);
ULONG
DhcpExtractOptionsFromMessage(
PDHCP_HEADER Headerp,
ULONG MessageSize,
DHCP_OPTION UNALIGNED* OptionArray[]
);
VOID
DnsUpdate(
CHAR *pszName,
ULONG len,
ULONG ulAddress
);
VOID
DhcpAppendOptionToMessage(
DHCP_OPTION UNALIGNED** Optionp,
UCHAR Tag,
UCHAR Length,
UCHAR Option[]
)
/*++
Routine Description:
This routine is invoked to append an option to a DHCP message.
Arguments:
Optionp - on input, the point at which to append the option;
on output, the point at which to append the next option.
Tag - the option tag
Length - the option length
Option - the option's data
Return Value:
none.
--*/
{
PROFILE("DhcpAppendOptionToMessage");
(*Optionp)->Tag = Tag;
if (!Length) {
*Optionp = (DHCP_OPTION UNALIGNED *)((PUCHAR)*Optionp + 1);
} else {
(*Optionp)->Length = Length;
CopyMemory((*Optionp)->Option, Option, Length);
*Optionp = (DHCP_OPTION UNALIGNED *)((PUCHAR)*Optionp + Length + 2);
}
} // DhcpAppendOptionToMessage
VOID
DhcpBuildReplyMessage(
PDHCP_INTERFACE Interfacep,
PNH_BUFFER Bufferp,
DHCP_OPTION UNALIGNED** Option,
UCHAR MessageType,
BOOLEAN DynamicDns,
DHCP_OPTION UNALIGNED* OptionArray[]
)
/*++
Routine Description:
This routine is called to construct the options portion
of a reply message.
Arguments:
Interfacep - the interface on which the reply will be sent
Bufferp - the buffer containing the reply
Option - the start of the options portion on input;
on output, the end of the message
MessageType - the type of message to be sent
DynamicDns - indicates whether to include the 'dynamic-dns' option.
OptionArray - options extracted from message
Return Value:
none.
Environment:
Invoked with 'Interfacep' referenced by the caller.
--*/
{
ULONG Address;
ULONG SubnetMask;
ULONG i;
//
// Obtain the address and mask for the endpoint
//
Address = NhQueryAddressSocket(Bufferp->Socket);
SubnetMask = PtrToUlong(Bufferp->Context2);
if (MessageType == DHCP_MESSAGE_BOOTP) {
((PDHCP_HEADER)Bufferp->Buffer)->BootstrapServerAddress = Address;
} else {
//
// Always begin with the 'message-type' option.
//
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_MESSAGE_TYPE,
1,
&MessageType
);
//
// Provide our address as the server-identifier
//
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_SERVER_IDENTIFIER,
4,
(PUCHAR)&Address
);
}
if (MessageType != DHCP_MESSAGE_NAK) {
PCHAR DomainName;
ULONG dnSize;
ULONG LeaseTime;
UCHAR NbtNodeType = DHCP_NBT_NODE_TYPE_M;
ULONG RebindingTime;
ULONG RenewalTime;
EnterCriticalSection(&DhcpGlobalInfoLock);
LeaseTime = DhcpGlobalInfo->LeaseTime * 60;
LeaveCriticalSection(&DhcpGlobalInfoLock);
RebindingTime = (LeaseTime * 3) / 4;
RenewalTime = LeaseTime / 2;
if (RenewalTime > DHCP_MAXIMUM_RENEWAL_TIME) {
RenewalTime = DHCP_MAXIMUM_RENEWAL_TIME;
}
LeaseTime = htonl(LeaseTime);
RebindingTime = htonl(RebindingTime);
RenewalTime = htonl(RenewalTime);
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_SUBNET_MASK,
4,
(PUCHAR)&SubnetMask
);
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_ROUTER,
4,
(PUCHAR)&Address
);
////
//// RFC 2132 9.14 : server treats client identifier as an opaque object
//// append the client identifier if present in received message
////
//if (OptionArray[DhcpOptionClientIdentifier])
//{
// DhcpAppendOptionToMessage(
// Option,
// DHCP_TAG_CLIENT_IDENTIFIER,
// OptionArray[DhcpOptionClientIdentifier]->Length,
// (PUCHAR)OptionArray[DhcpOptionClientIdentifier]->Option
// );
//}
if (MessageType != DHCP_MESSAGE_BOOTP) {
//specify the DNS server in the message if DNS proxy is enabled
//or DNS server is running on local host
if (NhIsDnsProxyEnabled() || !NoLocalDns) {
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_DNS_SERVER,
4,
(PUCHAR)&Address
);
}
if (NhIsWinsProxyEnabled()) {
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_WINS_SERVER,
4,
(PUCHAR)&Address
);
}
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_RENEWAL_TIME,
4,
(PUCHAR)&RenewalTime
);
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_REBINDING_TIME,
4,
(PUCHAR)&RebindingTime
);
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_LEASE_TIME,
4,
(PUCHAR)&LeaseTime
);
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_NBT_NODE_TYPE,
1,
&NbtNodeType
);
if (DynamicDns) {
UCHAR DynamicDns[3] = { 0x03, 0, 0 };
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_DYNAMIC_DNS,
sizeof(DynamicDns),
DynamicDns
);
}
//if (NhpStopDnsEvent && DnsICSDomainSuffix)
if (DnsGlobalInfo && DnsICSDomainSuffix)
{
EnterCriticalSection(&DnsGlobalInfoLock);
dnSize = wcstombs(NULL, DnsICSDomainSuffix, 0);
DomainName = reinterpret_cast<PCHAR>(NH_ALLOCATE(dnSize + 1));
if (DomainName)
{
wcstombs(DomainName, DnsICSDomainSuffix, (dnSize + 1));
}
LeaveCriticalSection(&DnsGlobalInfoLock);
}
else
//
// at this point we have no DNS enabled
// so we default to old behaviour
//
{
DomainName = NhQuerySharedConnectionDomainName();
}
if (DomainName)
{
//
// We include the terminating nul in the domain name
// even though the RFC says we should not, because
// the DHCP server does so.
//
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_DOMAIN_NAME,
(UCHAR)(lstrlenA(DomainName) + 1),
(PUCHAR)DomainName
);
NH_FREE(DomainName);
}
}
}
DhcpAppendOptionToMessage(
Option,
DHCP_TAG_END,
0,
NULL
);
} // DhcpBuildReplyMessage
ULONG
DhcpExtractOptionsFromMessage(
PDHCP_HEADER Headerp,
ULONG MessageSize,
DHCP_OPTION UNALIGNED* OptionArray[]
)
/*++
Routine Description:
This routine is invoked to parse the options contained in a DHCP message.
Pointers to each option are stored in the given option array.
Arguments:
Headerp - the header of the DHCP message to be parsed
MessageSize - the size of the message to be parsed
OptionArray - receives the parsed options
Return Value:
ULONG - Win32 status code.
--*/
{
DHCP_OPTION UNALIGNED* Index;
DHCP_OPTION UNALIGNED* End;
PROFILE("DhcpExtractOptionsFromMessage");
//
// Initialize the option array to be empty
//
ZeroMemory(OptionArray, DhcpOptionCount * sizeof(PDHCP_OPTION));
//
// Check that the message is large enough to hold options
//
if (MessageSize < sizeof(DHCP_HEADER)) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpExtractOptionsFromMessage: message size %d too small",
MessageSize
);
NhWarningLog(
IP_AUTO_DHCP_LOG_MESSAGE_TOO_SMALL,
0,
""
);
return ERROR_INVALID_DATA;
}
//
// Ensure that the magic cookie is present; if not, there are no options.
//
if (MessageSize < (sizeof(DHCP_HEADER) + sizeof(DHCP_FOOTER)) ||
*(ULONG UNALIGNED*)Headerp->Footer[0].Cookie != DHCP_MAGIC_COOKIE) {
return NO_ERROR;
}
//
// Parse the message's options, if any
//
End = (PDHCP_OPTION)((PUCHAR)Headerp + MessageSize);
Index = (PDHCP_OPTION)&Headerp->Footer[1];
while (Index < End && Index->Tag != DHCP_TAG_END) {
if ((DHCP_TAG_PAD != Index->Tag) &&
(End < (PDHCP_OPTION)(Index->Option + Index->Length))) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpExtractOptionsFromMessage: option truncated at %d bytes",
MessageSize
);
NhWarningLog(
IP_AUTO_DHCP_LOG_INVALID_FORMAT,
0,
""
);
return ERROR_INVALID_DATA;
}
switch (Index->Tag) {
case DHCP_TAG_PAD:
NhTrace(TRACE_FLAG_DHCP, "Pad");
break;
case DHCP_TAG_CLIENT_IDENTIFIER:
NhTrace(TRACE_FLAG_DHCP, "ClientIdentifier");
OptionArray[DhcpOptionClientIdentifier] = Index; break;
case DHCP_TAG_MESSAGE_TYPE:
NhTrace(TRACE_FLAG_DHCP, "MessageType");
if (Index->Length < 1) { break; }
OptionArray[DhcpOptionMessageType] = Index; break;
case DHCP_TAG_REQUESTED_ADDRESS:
NhTrace(TRACE_FLAG_DHCP, "RequestedAddress");
if (Index->Length < 4) { break; }
OptionArray[DhcpOptionRequestedAddress] = Index; break;
case DHCP_TAG_PARAMETER_REQUEST_LIST:
NhTrace(TRACE_FLAG_DHCP, "ParameterRequestList");
if (Index->Length < 1) { break; }
OptionArray[DhcpOptionParameterRequestList] = Index; break;
case DHCP_TAG_ERROR_MESSAGE:
NhTrace(TRACE_FLAG_DHCP, "ErrorMessage");
if (Index->Length < 1) { break; }
OptionArray[DhcpOptionErrorMessage] = Index; break;
case DHCP_TAG_DYNAMIC_DNS:
NhTrace(TRACE_FLAG_DHCP, "DynamicDns");
if (Index->Length < 1) { break; }
OptionArray[DhcpOptionDynamicDns] = Index; break;
case DHCP_TAG_HOST_NAME:
NhTrace(TRACE_FLAG_DHCP, "HostName");
if (Index->Length < 1) { break; }
OptionArray[DhcpOptionHostName] = Index; break;
}
if (DHCP_TAG_PAD != Index->Tag) {
Index = (PDHCP_OPTION)(Index->Option + Index->Length);
}
else {
Index = (PDHCP_OPTION)((PUCHAR)Index + 1);
}
}
if (Index->Tag != DHCP_TAG_END) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpExtractOptionsFromMessage: message truncated to %d bytes",
MessageSize
);
NhWarningLog(
IP_AUTO_DHCP_LOG_INVALID_FORMAT,
0,
""
);
return ERROR_INVALID_DATA;
}
return NO_ERROR;
} // DhcpExtractOptionsFromMessage
VOID
DhcpProcessBootpMessage(
PDHCP_INTERFACE Interfacep,
PNH_BUFFER Bufferp,
DHCP_OPTION UNALIGNED* OptionArray[]
)
/*++
Routine Description:
This routine is called to process a received BOOTP message.
Arguments:
Interfacep - the interface on which the message was received
Bufferp - the buffer containing the message
OptionArray - options extracted from the message
Return Value:
none.
Environment:
Invoked with 'Interfacep' referenced by the caller.
--*/
{
ULONG AssignedAddress;
ULONG Error;
UCHAR ExistingAddress[MAX_HARDWARE_ADDRESS_LENGTH];
ULONG ExistingAddressLength;
PDHCP_HEADER Headerp;
ULONG MessageLength;
PDHCP_HEADER Offerp;
DHCP_OPTION UNALIGNED* Option;
ULONG ReplyAddress;
USHORT ReplyPort;
PNH_BUFFER Replyp;
ULONG ScopeNetwork;
ULONG ScopeMask;
BOOLEAN bIsLocal = FALSE;
PROFILE("DhcpProcessBootpMessage");
ZeroMemory(ExistingAddress, sizeof(ExistingAddress));
Headerp = (PDHCP_HEADER)Bufferp->Buffer;
if (!Headerp->ClientAddress) {
AssignedAddress = 0;
} else {
//
// Validate the address requested by the client
//
AssignedAddress = Headerp->ClientAddress;
EnterCriticalSection(&DhcpGlobalInfoLock);
ScopeNetwork = DhcpGlobalInfo->ScopeNetwork;
ScopeMask = DhcpGlobalInfo->ScopeMask;
LeaveCriticalSection(&DhcpGlobalInfoLock);
if ((AssignedAddress & ~ScopeMask) == 0 ||
(AssignedAddress & ~ScopeMask) == ~ScopeMask ||
(AssignedAddress & ScopeMask) != (ScopeNetwork & ScopeMask)) {
//
// The client is on the wrong subnet, or has an all-zeros
// or all-ones address on the subnet.
// Select a different address for the client.
//
AssignedAddress = 0;
} else if (!DhcpIsUniqueAddress(
AssignedAddress,
&bIsLocal,
ExistingAddress,
&ExistingAddressLength
) &&
(bIsLocal ||
((Headerp->HardwareAddressType != 7 && // due to WinXP Bridge bug + WinME Client bug
Headerp->HardwareAddressLength) && // if address length is zero we wont compare
(ExistingAddressLength < Headerp->HardwareAddressLength ||
memcmp(
ExistingAddress,
Headerp->HardwareAddress,
Headerp->HardwareAddressLength
))))) {
//
// Someone has the requested address, and it's not the requestor.
//
AssignedAddress = 0;
} else if (DhcpIsReservedAddress(AssignedAddress, NULL, 0)) {
//
// The address is reserved for someone else.
//
AssignedAddress = 0;
}
}
if (!AssignedAddress &&
!(AssignedAddress =
DhcpAcquireUniqueAddress(
NULL,
0,
Headerp->HardwareAddress,
Headerp->HardwareAddressLength
))) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessBootpMessage: address-allocation failed"
);
return;
}
//
// Acquire a buffer for the reply we will send back
//
Replyp = NhAcquireBuffer();
if (!Replyp) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessBootpMessage: buffer-allocation failed"
);
NhErrorLog(
IP_AUTO_DHCP_LOG_ALLOCATION_FAILED,
0,
"%d",
sizeof(NH_BUFFER)
);
return;
}
//
// Pick up fields from the original buffer;
// the routines setting up the reply will attempt to read these,
// so they are set to the values from the original buffer.
//
Replyp->Socket = Bufferp->Socket;
Replyp->ReadAddress = Bufferp->ReadAddress;
Replyp->WriteAddress = Bufferp->WriteAddress;
Replyp->Context = Bufferp->Context;
Replyp->Context2 = Bufferp->Context2;
Offerp = (PDHCP_HEADER)Replyp->Buffer;
//
// Copy the original header
//
*Offerp = *Headerp;
//
// Set up the offer-header fields
//
Offerp->Operation = BOOTP_OPERATION_REPLY;
Offerp->AssignedAddress = AssignedAddress;
Offerp->ServerHostName[0] = 0;
Offerp->BootFile[0] = 0;
Offerp->SecondsSinceBoot = 0;
*(ULONG UNALIGNED *)Offerp->Footer[0].Cookie = DHCP_MAGIC_COOKIE;
//
// Fill in options
//
Option = (PDHCP_OPTION)&Offerp->Footer[1];
DhcpBuildReplyMessage(
Interfacep,
Replyp,
&Option,
DHCP_MESSAGE_BOOTP,
FALSE,
OptionArray
);
//
// Send the offer to the BOOTP client
//
EnterCriticalSection(&DhcpInterfaceLock);
if (!DHCP_REFERENCE_INTERFACE(Interfacep)) {
LeaveCriticalSection(&DhcpInterfaceLock);
NhReleaseBuffer(Replyp);
} else {
LeaveCriticalSection(&DhcpInterfaceLock);
if (Headerp->RelayAgentAddress) {
ReplyAddress = Headerp->RelayAgentAddress;
ReplyPort = DHCP_PORT_SERVER;
} else {
ReplyAddress = INADDR_BROADCAST;
ReplyPort = DHCP_PORT_CLIENT;
}
MessageLength = (ULONG)((PUCHAR)Option - Replyp->Buffer);
if (MessageLength < sizeof(DHCP_HEADER) + BOOTP_VENDOR_LENGTH) {
MessageLength = sizeof(DHCP_HEADER) + BOOTP_VENDOR_LENGTH;
}
Error =
NhWriteDatagramSocket(
&DhcpComponentReference,
Bufferp->Socket,
ReplyAddress,
ReplyPort,
Replyp,
MessageLength,
DhcpWriteCompletionRoutine,
Interfacep,
Bufferp->Context2
);
if (!Error) {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.BootpOffersSent)
);
} else {
DHCP_DEREFERENCE_INTERFACE(Interfacep);
NhReleaseBuffer(Replyp);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessBootpMessage: error %d sending reply",
Error
);
NhErrorLog(
IP_AUTO_DHCP_LOG_REPLY_FAILED,
Error,
"%I",
NhQueryAddressSocket(Bufferp->Socket)
);
}
}
} // DhcpProcessBootpMessage
VOID
DhcpProcessDiscoverMessage(
PDHCP_INTERFACE Interfacep,
PNH_BUFFER Bufferp,
DHCP_OPTION UNALIGNED* OptionArray[]
)
/*++
Routine Description:
This routine is called to process a received DHCPDISCOVER message.
Arguments:
Interfacep - the interface on which the discover was received
Bufferp - the buffer containing the message
OptionArray - options extracted from the message
Return Value:
none.
Environment:
Invoked with 'Interfacep' referenced by the caller.
--*/
{
ULONG AssignedAddress;
ULONG Error;
UCHAR ExistingAddress[MAX_HARDWARE_ADDRESS_LENGTH];
ULONG ExistingAddressLength;
PDHCP_HEADER Headerp;
ULONG MessageLength;
PDHCP_HEADER Offerp;
DHCP_OPTION UNALIGNED* Option;
ULONG ReplyAddress;
USHORT ReplyPort;
PNH_BUFFER Replyp;
ULONG ScopeNetwork;
ULONG ScopeMask;
BOOLEAN bIsLocal = FALSE;
PROFILE("DhcpProcessDiscoverMessage");
ZeroMemory(ExistingAddress, sizeof(ExistingAddress));
Headerp = (PDHCP_HEADER)Bufferp->Buffer;
//
// See if the client is renewing or requesting
//
if (!OptionArray[DhcpOptionRequestedAddress]) {
AssignedAddress = 0;
} else {
//
// Validate the address requested by the client
//
AssignedAddress =
*(ULONG UNALIGNED*)OptionArray[DhcpOptionRequestedAddress]->Option;
EnterCriticalSection(&DhcpGlobalInfoLock);
ScopeNetwork = DhcpGlobalInfo->ScopeNetwork;
ScopeMask = DhcpGlobalInfo->ScopeMask;
LeaveCriticalSection(&DhcpGlobalInfoLock);
if ((AssignedAddress & ~ScopeMask) == 0 ||
(AssignedAddress & ~ScopeMask) == ~ScopeMask ||
(AssignedAddress & ScopeMask) != (ScopeNetwork & ScopeMask)) {
//
// The client is on the wrong subnet, or has an all-zeroes
// or all-ones address on the subnet.
// Select a different address for the client.
//
AssignedAddress = 0;
} else if (!DhcpIsUniqueAddress(
AssignedAddress,
&bIsLocal,
ExistingAddress,
&ExistingAddressLength
) &&
(bIsLocal ||
((Headerp->HardwareAddressType != 7 && // due to WinXP Bridge bug + WinME Client bug
Headerp->HardwareAddressLength) && // if address length is zero we wont compare
(ExistingAddressLength < Headerp->HardwareAddressLength ||
memcmp(
ExistingAddress,
Headerp->HardwareAddress,
Headerp->HardwareAddressLength
))))) {
//
// Someone has the requested address, and it's not the requestor.
//
AssignedAddress = 0;
} else if (OptionArray[DhcpOptionHostName]) {
if (DhcpIsReservedAddress(
AssignedAddress,
reinterpret_cast<PCHAR>(
OptionArray[DhcpOptionHostName]->Option
),
OptionArray[DhcpOptionHostName]->Length
)) {
//
// The address is reserved for someone else,
// or the client has a different address reserved.
//
AssignedAddress = 0;
}
} else if (DhcpIsReservedAddress(AssignedAddress, NULL, 0)) {
//
// The address is reserved for someone else.
//
AssignedAddress = 0;
}
}
//
// Generate an address for the client if necessary
//
if (!AssignedAddress) {
if (!OptionArray[DhcpOptionHostName]) {
AssignedAddress =
DhcpAcquireUniqueAddress(
NULL,
0,
Headerp->HardwareAddress,
Headerp->HardwareAddressLength
);
} else {
AssignedAddress =
DhcpAcquireUniqueAddress(
reinterpret_cast<PCHAR>(
OptionArray[DhcpOptionHostName]->Option
),
OptionArray[DhcpOptionHostName]->Length,
Headerp->HardwareAddress,
Headerp->HardwareAddressLength
);
}
if (!AssignedAddress) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessDiscoverMessage: address-allocation failed"
);
return;
}
}
//
// Acquire a buffer for the offer we will send back
//
Replyp = NhAcquireBuffer();
if (!Replyp) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessDiscoverMessage: buffer-allocation failed"
);
NhErrorLog(
IP_AUTO_DHCP_LOG_ALLOCATION_FAILED,
0,
"%d",
sizeof(NH_BUFFER)
);
return;
}
//
// Pick up fields from the original message
// the routines setting up the reply will attempt to read these,
// so they are set to the values from the original buffer.
//
Replyp->Socket = Bufferp->Socket;
Replyp->ReadAddress = Bufferp->ReadAddress;
Replyp->WriteAddress = Bufferp->WriteAddress;
Replyp->Context = Bufferp->Context;
Replyp->Context2 = Bufferp->Context2;
Offerp = (PDHCP_HEADER)Replyp->Buffer;
//
// Copy the original discover header
//
*Offerp = *Headerp;
//
// IP/1394 support (RFC 2855)
//
if ((IP1394_HTYPE == Offerp->HardwareAddressType) &&
(0 == Offerp->HardwareAddressLength))
{
//
// MUST set client hardware address to zero
//
ZeroMemory(Offerp->HardwareAddress, sizeof(Offerp->HardwareAddress));
}
//
// Set up the offer-header fieldds
//
Offerp->Operation = BOOTP_OPERATION_REPLY;
Offerp->AssignedAddress = AssignedAddress;
Offerp->ServerHostName[0] = 0;
Offerp->BootFile[0] = 0;
Offerp->SecondsSinceBoot = 0;
*(ULONG UNALIGNED *)Offerp->Footer[0].Cookie = DHCP_MAGIC_COOKIE;
//
// Fill in options
//
Option = (PDHCP_OPTION)&Offerp->Footer[1];
DhcpBuildReplyMessage(
Interfacep,
Replyp,
&Option,
DHCP_MESSAGE_OFFER,
(BOOLEAN)(OptionArray[DhcpOptionDynamicDns] ? TRUE : FALSE),
OptionArray
);
//
// Send the offer to the client
//
EnterCriticalSection(&DhcpInterfaceLock);
if (!DHCP_REFERENCE_INTERFACE(Interfacep)) {
LeaveCriticalSection(&DhcpInterfaceLock);
NhReleaseBuffer(Replyp);
} else {
LeaveCriticalSection(&DhcpInterfaceLock);
if (Headerp->RelayAgentAddress) {
ReplyAddress = Headerp->RelayAgentAddress;
ReplyPort = DHCP_PORT_SERVER;
} else {
ReplyAddress = INADDR_BROADCAST;
ReplyPort = DHCP_PORT_CLIENT;
}
MessageLength = (ULONG)((PUCHAR)Option - Replyp->Buffer);
if (MessageLength < sizeof(DHCP_HEADER) + BOOTP_VENDOR_LENGTH) {
MessageLength = sizeof(DHCP_HEADER) + BOOTP_VENDOR_LENGTH;
}
Error =
NhWriteDatagramSocket(
&DhcpComponentReference,
Bufferp->Socket,
ReplyAddress,
ReplyPort,
Replyp,
MessageLength,
DhcpWriteCompletionRoutine,
Interfacep,
Bufferp->Context2
);
if (!Error) {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.OffersSent)
);
} else {
DHCP_DEREFERENCE_INTERFACE(Interfacep);
NhReleaseBuffer(Replyp);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessDiscoverMessage: error %d sending reply",
Error
);
NhErrorLog(
IP_AUTO_DHCP_LOG_REPLY_FAILED,
Error,
"%I",
NhQueryAddressSocket(Bufferp->Socket)
);
}
}
} // DhcpProcessDiscoverMessage
VOID
DhcpProcessInformMessage(
PDHCP_INTERFACE Interfacep,
PNH_BUFFER Bufferp,
DHCP_OPTION UNALIGNED* OptionArray[]
)
/*++
Routine Description:
This routine is called to process a received DHCPINFORM message.
Arguments:
Interfacep - the interface on which the inform was received
Bufferp - the buffer containing the message
OptionArray - options extracted from the message
Return Value:
none.
Environment:
Invoked with 'Interfacep' referenced by the caller.
--*/
{
PDHCP_HEADER Ackp;
ULONG Error;
PDHCP_HEADER Headerp;
ULONG MessageLength;
DHCP_OPTION UNALIGNED* Option;
ULONG ReplyAddress;
USHORT ReplyPort;
PNH_BUFFER Replyp;
PROFILE("DhcpProcessInformMessage");
Headerp = (PDHCP_HEADER)Bufferp->Buffer;
//
// Acquire a buffer for the ack we will send back
//
Replyp = NhAcquireBuffer();
if (!Replyp) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessInformMessage: buffer-allocation failed"
);
NhErrorLog(
IP_AUTO_DHCP_LOG_ALLOCATION_FAILED,
0,
"%d",
sizeof(NH_BUFFER)
);
return;
}
//
// Pick up fields from the original message
// the routines setting up the reply will attempt to read these,
// so they are set to the values from the original buffer.
//
Replyp->Socket = Bufferp->Socket;
Replyp->ReadAddress = Bufferp->ReadAddress;
Replyp->WriteAddress = Bufferp->WriteAddress;
Replyp->Context = Bufferp->Context;
Replyp->Context2 = Bufferp->Context2;
Ackp = (PDHCP_HEADER)Replyp->Buffer;
//
// Copy the original header
//
*Ackp = *Headerp;
//
// IP/1394 support (RFC 2855)
//
if ((IP1394_HTYPE == Ackp->HardwareAddressType) &&
(0 == Ackp->HardwareAddressLength))
{
//
// MUST set client hardware address to zero
//
ZeroMemory(Ackp->HardwareAddress, sizeof(Ackp->HardwareAddress));
}
//
// Set up the ack-header fieldds
//
Ackp->Operation = BOOTP_OPERATION_REPLY;
Ackp->AssignedAddress = 0;
Ackp->ServerHostName[0] = 0;
Ackp->BootFile[0] = 0;
Ackp->SecondsSinceBoot = 0;
*(ULONG UNALIGNED *)Ackp->Footer[0].Cookie = DHCP_MAGIC_COOKIE;
//
// Fill in options
//
Option = (PDHCP_OPTION)&Ackp->Footer[1];
DhcpBuildReplyMessage(
Interfacep,
Replyp,
&Option,
DHCP_MESSAGE_ACK,
(BOOLEAN)(OptionArray[DhcpOptionDynamicDns] ? TRUE : FALSE),
OptionArray
);
//
// Send the offer to the client
//
EnterCriticalSection(&DhcpInterfaceLock);
if (!DHCP_REFERENCE_INTERFACE(Interfacep)) {
LeaveCriticalSection(&DhcpInterfaceLock);
NhReleaseBuffer(Replyp);
} else {
LeaveCriticalSection(&DhcpInterfaceLock);
if (Headerp->RelayAgentAddress) {
ReplyAddress = Headerp->RelayAgentAddress;
ReplyPort = DHCP_PORT_SERVER;
} else {
ReplyAddress = INADDR_BROADCAST;
ReplyPort = DHCP_PORT_CLIENT;
}
MessageLength = (ULONG)((PUCHAR)Option - Replyp->Buffer);
if (MessageLength < sizeof(DHCP_HEADER) + BOOTP_VENDOR_LENGTH) {
MessageLength = sizeof(DHCP_HEADER) + BOOTP_VENDOR_LENGTH;
}
Error =
NhWriteDatagramSocket(
&DhcpComponentReference,
Bufferp->Socket,
ReplyAddress,
ReplyPort,
Replyp,
MessageLength,
DhcpWriteCompletionRoutine,
Interfacep,
Bufferp->Context2
);
if (!Error) {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.AcksSent)
);
} else {
DHCP_DEREFERENCE_INTERFACE(Interfacep);
NhReleaseBuffer(Replyp);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessInformMessage: error %d sending reply",
Error
);
NhErrorLog(
IP_AUTO_DHCP_LOG_REPLY_FAILED,
Error,
"%I",
NhQueryAddressSocket(Bufferp->Socket)
);
}
}
} // DhcpProcessInformMessage
VOID
DhcpProcessMessage(
PDHCP_INTERFACE Interfacep,
PNH_BUFFER Bufferp
)
/*++
Routine Description:
This routine is invoked to process a DHCP client message.
Arguments:
Interfacep - the interface on which the request was received
Bufferp - the buffer containing the message received
Return Value:
none.
Environment:
Invoked internally with 'Interfacep' referenced by the caller.
--*/
{
ULONG Error;
PDHCP_HEADER Headerp;
UCHAR MessageType;
PDHCP_OPTION OptionArray[DhcpOptionCount];
PROFILE("DhcpProcessMessage");
Headerp = (PDHCP_HEADER)Bufferp->Buffer;
#if DBG
NhDump(
TRACE_FLAG_DHCP,
Bufferp->Buffer,
Bufferp->BytesTransferred,
1
);
#endif
//
// Extract pointers to each option in the message
//
Error =
DhcpExtractOptionsFromMessage(
Headerp,
Bufferp->BytesTransferred,
OptionArray
);
if (Error) {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.MessagesIgnored)
);
}
else
//
// Look for the message-type;
// This distinguishes BOOTP from DHCP clients.
//
if (!OptionArray[DhcpOptionMessageType]) {
DhcpProcessBootpMessage(
Interfacep,
Bufferp,
OptionArray
);
} else if (Headerp->HardwareAddressLength <=
sizeof(Headerp->HardwareAddress) &&
DhcpIsLocalHardwareAddress(
Headerp->HardwareAddress, Headerp->HardwareAddressLength)) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessMessage: ignoring message, from self"
);
} else switch(MessageType = OptionArray[DhcpOptionMessageType]->Option[0]) {
case DHCP_MESSAGE_DISCOVER: {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.DiscoversReceived)
);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessMessage: received DISCOVER message"
);
DhcpProcessDiscoverMessage(
Interfacep,
Bufferp,
OptionArray
);
break;
}
case DHCP_MESSAGE_REQUEST: {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.RequestsReceived)
);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessMessage: received REQUEST message"
);
DhcpProcessRequestMessage(
Interfacep,
Bufferp,
OptionArray
);
break;
}
case DHCP_MESSAGE_INFORM: {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.InformsReceived)
);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessMessage: received INFORM message"
);
DhcpProcessInformMessage(
Interfacep,
Bufferp,
OptionArray
);
break;
}
case DHCP_MESSAGE_DECLINE: {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.DeclinesReceived)
);
// log message
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessMessage: received DECLINE message"
);
break;
}
case DHCP_MESSAGE_RELEASE: {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.ReleasesReceived)
);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessMessage: received RELEASE message"
);
break;
}
default: {
InterlockedIncrement(
reinterpret_cast<LPLONG>(&DhcpStatistics.MessagesIgnored)
);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessMessage: message type %d invalid",
MessageType
);
NhWarningLog(
IP_AUTO_DHCP_LOG_INVALID_DHCP_MESSAGE_TYPE,
0,
"%d",
MessageType
);
break;
}
}
//
// Post the buffer for another read
//
EnterCriticalSection(&DhcpInterfaceLock);
if (!DHCP_REFERENCE_INTERFACE(Interfacep)) {
LeaveCriticalSection(&DhcpInterfaceLock);
NhReleaseBuffer(Bufferp);
} else {
LeaveCriticalSection(&DhcpInterfaceLock);
Error =
NhReadDatagramSocket(
&DhcpComponentReference,
Bufferp->Socket,
Bufferp,
DhcpReadCompletionRoutine,
Bufferp->Context,
Bufferp->Context2
);
if (Error) {
ACQUIRE_LOCK(Interfacep);
DhcpDeferReadInterface(Interfacep, Bufferp->Socket);
RELEASE_LOCK(Interfacep);
DHCP_DEREFERENCE_INTERFACE(Interfacep);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessMessage: error %d reposting read",
Error
);
NhWarningLog(
IP_AUTO_DHCP_LOG_RECEIVE_FAILED,
Error,
"%I",
NhQueryAddressSocket(Bufferp->Socket)
);
NhReleaseBuffer(Bufferp);
}
}
} // DhcpProcessMessage
VOID
DhcpProcessRequestMessage(
PDHCP_INTERFACE Interfacep,
PNH_BUFFER Bufferp,
DHCP_OPTION UNALIGNED* OptionArray[]
)
/*++
Routine Description:
This routine is called to process a request message.
Arguments:
Interfacep - the interface on which the request was received
Bufferp - the buffer containing the message received
OptionArray - options extracted from the message
Return Value:
none.
Environment:
Invoked internally with 'Interfacep' referenced by the caller.
--*/
{
ULONG AssignedAddress = 0;
ULONG Error;
UCHAR ExistingAddress[MAX_HARDWARE_ADDRESS_LENGTH];
ULONG ExistingAddressLength;
PDHCP_HEADER Headerp;
ULONG MessageLength;
PDHCP_HEADER Offerp;
DHCP_OPTION UNALIGNED* Option;
ULONG ReplyAddress;
USHORT ReplyPort;
PNH_BUFFER Replyp;
UCHAR ReplyType = DHCP_MESSAGE_ACK;
ULONG ScopeNetwork;
ULONG ScopeMask;
BOOLEAN bIsLocal = FALSE;
PROFILE("DhcpProcessRequestMessage");
ZeroMemory(ExistingAddress, sizeof(ExistingAddress));
Headerp = (PDHCP_HEADER)Bufferp->Buffer;
//
// Validate the address requested by the client
//
if (!Headerp->ClientAddress && !OptionArray[DhcpOptionRequestedAddress]) {
//
// The client left out the address being requested
//
ReplyType = DHCP_MESSAGE_NAK;
} else {
//
// Try to see if the address is in use.
//
AssignedAddress =
Headerp->ClientAddress
? Headerp->ClientAddress
: *(ULONG UNALIGNED*)
OptionArray[DhcpOptionRequestedAddress]->Option;
EnterCriticalSection(&DhcpGlobalInfoLock);
ScopeNetwork = DhcpGlobalInfo->ScopeNetwork;
ScopeMask = DhcpGlobalInfo->ScopeMask;
LeaveCriticalSection(&DhcpGlobalInfoLock);
if ((AssignedAddress & ~ScopeMask) == 0 ||
(AssignedAddress & ~ScopeMask) == ~ScopeMask ||
(AssignedAddress & ScopeMask) != (ScopeNetwork & ScopeMask)) {
//
// The client is on the wrong subnet, or has an all-ones
// or all-zeroes address.
//
ReplyType = DHCP_MESSAGE_NAK;
} else if (!DhcpIsUniqueAddress(
AssignedAddress,
&bIsLocal,
ExistingAddress,
&ExistingAddressLength
) &&
(bIsLocal ||
((Headerp->HardwareAddressType != 7 && // due to WinXP Bridge bug + WinME Client bug
Headerp->HardwareAddressLength) && // if address length is zero we wont compare
(ExistingAddressLength < Headerp->HardwareAddressLength ||
memcmp(
ExistingAddress,
Headerp->HardwareAddress,
Headerp->HardwareAddressLength
))))) {
//
// Someone has the requested address, and it's not the requestor.
//
ReplyType = DHCP_MESSAGE_NAK;
} else if (OptionArray[DhcpOptionHostName]) {
if (DhcpIsReservedAddress(
AssignedAddress,
reinterpret_cast<PCHAR>(
OptionArray[DhcpOptionHostName]->Option
),
OptionArray[DhcpOptionHostName]->Length
)) {
//
// The address is reserved for someone else,
// or the client has a different address reserved.
//
ReplyType = DHCP_MESSAGE_NAK;
}
} else if (DhcpIsReservedAddress(AssignedAddress, NULL, 0)) {
//
// The address is reserved for someone else.
//
ReplyType = DHCP_MESSAGE_NAK;
}
}
//
// Acquire a buffer for the reply we will send back
//
Replyp = NhAcquireBuffer();
if (!Replyp) {
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessRequestMessage: buffer-allocation failed"
);
NhErrorLog(
IP_AUTO_DHCP_LOG_ALLOCATION_FAILED,
0,
"%d",
sizeof(NH_BUFFER)
);
return;
}
//
// Pick up fields to be used in the reply-buffer
// the routines setting up the reply will attempt to read these,
// so they are set to the values from the original buffer.
//
Replyp->Socket = Bufferp->Socket;
Replyp->ReadAddress = Bufferp->ReadAddress;
Replyp->WriteAddress = Bufferp->WriteAddress;
Replyp->Context = Bufferp->Context;
Replyp->Context2 = Bufferp->Context2;
Offerp = (PDHCP_HEADER)Replyp->Buffer;
//
// Copy the original discover header
//
*Offerp = *Headerp;
//
// IP/1394 support (RFC 2855)
//
if ((IP1394_HTYPE == Offerp->HardwareAddressType) &&
(0 == Offerp->HardwareAddressLength))
{
//
// MUST set client hardware address to zero
//
ZeroMemory(Offerp->HardwareAddress, sizeof(Offerp->HardwareAddress));
}
//
// Set up the offer-header fieldds
//
Offerp->Operation = BOOTP_OPERATION_REPLY;
Offerp->AssignedAddress = AssignedAddress;
Offerp->ServerHostName[0] = 0;
Offerp->BootFile[0] = 0;
Offerp->SecondsSinceBoot = 0;
*(ULONG UNALIGNED *)Offerp->Footer[0].Cookie = DHCP_MAGIC_COOKIE;
//
// Fill in options
//
Option = (PDHCP_OPTION)&Offerp->Footer[1];
DhcpBuildReplyMessage(
Interfacep,
Replyp,
&Option,
ReplyType,
(BOOLEAN)(OptionArray[DhcpOptionDynamicDns] ? TRUE : FALSE),
OptionArray
);
//
// NEW LOGIC HERE => tied to DNS
//
if (DHCP_MESSAGE_ACK == ReplyType)
{
//
// We perform the equivalent of Dynamic DNS here
// by informing the DNS component that this client exists
//
if (OptionArray[DhcpOptionHostName])
{
//
// check if DNS component is active
//
if (REFERENCE_DNS())
{
DnsUpdate(
reinterpret_cast<PCHAR>(OptionArray[DhcpOptionHostName]->Option),
(ULONG) OptionArray[DhcpOptionHostName]->Length,
AssignedAddress
);
DEREFERENCE_DNS();
}
}
}
//
// Send the reply to the client
//
EnterCriticalSection(&DhcpInterfaceLock);
if (!DHCP_REFERENCE_INTERFACE(Interfacep)) {
LeaveCriticalSection(&DhcpInterfaceLock);
NhReleaseBuffer(Replyp);
} else {
LeaveCriticalSection(&DhcpInterfaceLock);
if (Headerp->RelayAgentAddress) {
ReplyAddress = Headerp->RelayAgentAddress;
ReplyPort = DHCP_PORT_SERVER;
} else {
ReplyAddress = INADDR_BROADCAST;
ReplyPort = DHCP_PORT_CLIENT;
}
MessageLength = (ULONG)((PUCHAR)Option - Replyp->Buffer);
if (MessageLength < sizeof(DHCP_HEADER) + BOOTP_VENDOR_LENGTH) {
MessageLength = sizeof(DHCP_HEADER) + BOOTP_VENDOR_LENGTH;
}
Error =
NhWriteDatagramSocket(
&DhcpComponentReference,
Bufferp->Socket,
ReplyAddress,
ReplyPort,
Replyp,
MessageLength,
DhcpWriteCompletionRoutine,
Interfacep,
Bufferp->Context2
);
if (!Error) {
InterlockedIncrement(
(ReplyType == DHCP_MESSAGE_ACK)
? reinterpret_cast<LPLONG>(&DhcpStatistics.AcksSent)
: reinterpret_cast<LPLONG>(&DhcpStatistics.NaksSent)
);
} else {
DHCP_DEREFERENCE_INTERFACE(Interfacep);
NhReleaseBuffer(Replyp);
NhTrace(
TRACE_FLAG_DHCP,
"DhcpProcessRequestMessage: error %d sending reply",
Error
);
NhErrorLog(
IP_AUTO_DHCP_LOG_REPLY_FAILED,
Error,
"%I",
NhQueryAddressSocket(Bufferp->Socket)
);
}
}
} // DhcpProcessRequestMessage
ULONG
DhcpWriteClientRequestMessage(
PDHCP_INTERFACE Interfacep,
PDHCP_BINDING Binding
)
/*++
Routine Description:
This routine is invoked to check for the existence of a DHCP server
on the given interface and address. It generates a BOOTP request
on a socket bound to the DHCP client port.
Arguments:
Interfacep - the interface on which the client request is to be sent
Binding - the binding on which the request is to be sent
Return Value:
ULONG - status code.
Environment:
Invoked with 'Interfacep' locked and with a reference made to 'Interfacep'
for the send which occurs here.
If the routine fails, it is the caller's responsibility to release
the reference.
--*/
{
PNH_BUFFER Bufferp;
ULONG Error;
PDHCP_HEADER Headerp;
SOCKET Socket;
PROFILE("DhcpWriteClientRequestMessage");
//
// Create a socket using the given address
//
Error =
NhCreateDatagramSocket(
Binding->Address,
DHCP_PORT_CLIENT,
&Binding->ClientSocket
);
if (Error) {
NhTrace(
TRACE_FLAG_IF,
"DhcpWriteClientRequestMessage: error %d creating socket for %s",
Error,
INET_NTOA(Binding->Address)
);
NhWarningLog(
IP_AUTO_DHCP_LOG_DETECTION_UNAVAILABLE,
Error,
"%I",
Binding->Address
);
return Error;
}
//
// Allocate a buffer for the BOOTP request
//
Bufferp = NhAcquireBuffer();
if (!Bufferp) {
NhDeleteDatagramSocket(Binding->ClientSocket);
Binding->ClientSocket = INVALID_SOCKET;
NhTrace(
TRACE_FLAG_IF,
"DhcpWriteClientRequestMessage: error allocating buffer for %s",
INET_NTOA(Binding->Address)
);
NhErrorLog(
IP_AUTO_DHCP_LOG_ALLOCATION_FAILED,
0,
"%d",
sizeof(NH_BUFFER)
);
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Initialize the BOOTP request
//
Headerp = (PDHCP_HEADER)Bufferp->Buffer;
ZeroMemory(Headerp, sizeof(*Headerp));
Headerp->Operation = BOOTP_OPERATION_REQUEST;
Headerp->HardwareAddressType = 1;
Headerp->HardwareAddressLength = 6;
Headerp->TransactionId = DHCP_DETECTION_TRANSACTION_ID;
Headerp->SecondsSinceBoot = 10;
Headerp->Flags |= BOOTP_FLAG_BROADCAST;
Headerp->ClientAddress = Binding->Address;
Headerp->HardwareAddress[1] = 0xab;
*(PULONG)(Headerp->Footer[0].Cookie) = DHCP_MAGIC_COOKIE;
*(PUCHAR)(Headerp->Footer + 1) = DHCP_TAG_END;
//
// Send the BOOTP request on the socket
//
Error =
NhWriteDatagramSocket(
&DhcpComponentReference,
Binding->ClientSocket,
INADDR_BROADCAST,
DHCP_PORT_SERVER,
Bufferp,
sizeof(DHCP_HEADER) + BOOTP_VENDOR_LENGTH,
DhcpWriteClientRequestCompletionRoutine,
(PVOID)Interfacep,
UlongToPtr(Binding->Address)
);
if (Error) {
NhReleaseBuffer(Bufferp);
NhDeleteDatagramSocket(Binding->ClientSocket);
Binding->ClientSocket = INVALID_SOCKET;
NhTrace(
TRACE_FLAG_IF,
"DhcpWriteClientRequestMessage: error %d writing request for %s",
Error,
INET_NTOA(Binding->Address)
);
NhWarningLog(
IP_AUTO_DHCP_LOG_DETECTION_UNAVAILABLE,
Error,
"%I",
Binding->Address
);
return Error;
}
return NO_ERROR;
} // DhcpWriteClientRequestMessage