/*++ 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(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(&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( 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( 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(&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(&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(&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(&DhcpStatistics.MessagesIgnored) ); } else if (DhcpIsLocalHardwareAddress( Headerp->HardwareAddress, Headerp->HardwareAddressLength)) { NhTrace( TRACE_FLAG_DHCP, "DhcpProcessMessage: ignoring message, from self" ); InterlockedIncrement( reinterpret_cast(&DhcpStatistics.MessagesIgnored) ); } else switch(MessageType = OptionArray[DhcpOptionMessageType]->Option[0]) { case DHCP_MESSAGE_DISCOVER: { InterlockedIncrement( reinterpret_cast(&DhcpStatistics.DiscoversReceived) ); NhTrace( TRACE_FLAG_DHCP, "DhcpProcessMessage: received DISCOVER message" ); DhcpProcessDiscoverMessage( Interfacep, Bufferp, OptionArray ); break; } case DHCP_MESSAGE_REQUEST: { InterlockedIncrement( reinterpret_cast(&DhcpStatistics.RequestsReceived) ); NhTrace( TRACE_FLAG_DHCP, "DhcpProcessMessage: received REQUEST message" ); DhcpProcessRequestMessage( Interfacep, Bufferp, OptionArray ); break; } case DHCP_MESSAGE_INFORM: { InterlockedIncrement( reinterpret_cast(&DhcpStatistics.InformsReceived) ); NhTrace( TRACE_FLAG_DHCP, "DhcpProcessMessage: received INFORM message" ); DhcpProcessInformMessage( Interfacep, Bufferp, OptionArray ); break; } case DHCP_MESSAGE_DECLINE: { InterlockedIncrement( reinterpret_cast(&DhcpStatistics.DeclinesReceived) ); // log message NhTrace( TRACE_FLAG_DHCP, "DhcpProcessMessage: received DECLINE message" ); break; } case DHCP_MESSAGE_RELEASE: { InterlockedIncrement( reinterpret_cast(&DhcpStatistics.ReleasesReceived) ); NhTrace( TRACE_FLAG_DHCP, "DhcpProcessMessage: received RELEASE message" ); break; } default: { InterlockedIncrement( reinterpret_cast(&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( 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(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(&DhcpStatistics.AcksSent) : reinterpret_cast(&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