|
|
/*++
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)) {
NhTrace( TRACE_FLAG_DHCP, "DhcpProcessMessage: ignoring message since HWAddrLength " "is too long" );
InterlockedIncrement( reinterpret_cast<LPLONG>(&DhcpStatistics.MessagesIgnored) );
} else if (DhcpIsLocalHardwareAddress( Headerp->HardwareAddress, Headerp->HardwareAddressLength)) {
NhTrace( TRACE_FLAG_DHCP, "DhcpProcessMessage: ignoring message, from self" );
InterlockedIncrement( reinterpret_cast<LPLONG>(&DhcpStatistics.MessagesIgnored) );
} 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
|