1728 lines
60 KiB
C++
1728 lines
60 KiB
C++
/*++
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
protocol.cxx
|
|
|
|
Abstract:
|
|
This module contains the server to client protocol for DHCP.
|
|
|
|
Author:
|
|
Manny Weiser (mannyw) 21-Oct-1992
|
|
|
|
Environment:
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
Madan Appiah (madana) 21-Oct-1993
|
|
|
|
Arthur Bierer (arthurbi) 15-July-1998
|
|
hacked up to use with Wininet's auto-proxy detection code
|
|
|
|
--*/
|
|
|
|
#include <wininetp.h>
|
|
#include "aproxp.h"
|
|
|
|
#include "apdetect.h"
|
|
|
|
#ifndef VXD
|
|
// ping routines.. ICMP
|
|
#include <ipexport.h>
|
|
//#include <icmpif.h>
|
|
#include <icmpapi.h>
|
|
#endif
|
|
|
|
#ifdef NEWNT
|
|
extern BOOL DhcpGlobalIsService;
|
|
#endif // NEWNT
|
|
|
|
DWORD // Time in seconds
|
|
DhcpCalculateWaitTime( // how much time to wait
|
|
IN DWORD RoundNum, // which round is this
|
|
OUT DWORD *WaitMilliSecs // if needed the # in milli seconds
|
|
);
|
|
|
|
|
|
POPTION
|
|
FormatDhcpInform(
|
|
PDHCP_CONTEXT DhcpContext
|
|
);
|
|
|
|
DWORD
|
|
SendDhcpInform(
|
|
PDHCP_CONTEXT DhcpContext,
|
|
PDWORD TransactionId
|
|
);
|
|
|
|
DWORD // status
|
|
SendInformAndGetReplies( // send an inform packet and collect replies
|
|
IN PDHCP_CONTEXT DhcpContext, // the context to send out of
|
|
IN DWORD nInformsToSend,// how many informs to send?
|
|
IN DWORD MaxAcksToWait, // how many acks to wait for
|
|
OUT DHCP_EXPECTED_OPTIONS *pExpectedOptions // list of things parsed out of request
|
|
);
|
|
|
|
VOID
|
|
DhcpExtractFullOrLiteOptions( // Extract some important options alone or ALL
|
|
IN PDHCP_CONTEXT DhcpContext,
|
|
IN LPBYTE OptStart, // start of the options stuff
|
|
IN DWORD MessageSize, // # of bytes of options
|
|
IN BOOL LiteOnly, // next struc is EXPECTED_OPTIONS and not FULL_OPTIONS
|
|
OUT LPVOID DhcpOptions, // this is where the options would be stored
|
|
IN OUT PLIST_ENTRY RecdOptions, // if !LiteOnly this gets filled with all incoming options
|
|
IN OUT DWORD *LeaseExpiry, // if !LiteOnly input expiry time, else output expiry time
|
|
IN LPBYTE ClassName, // if !LiteOnly this is used to add to the option above
|
|
IN DWORD ClassLen // if !LiteOnly this gives the # of bytes of classname
|
|
);
|
|
|
|
DWORD
|
|
SendDhcpMessage(
|
|
PDHCP_CONTEXT DhcpContext,
|
|
DWORD MessageLength,
|
|
PDWORD TransactionId
|
|
);
|
|
|
|
DWORD
|
|
OpenDhcpSocket(
|
|
PDHCP_CONTEXT DhcpContext
|
|
);
|
|
|
|
DWORD
|
|
GetSpecifiedDhcpMessage(
|
|
PDHCP_CONTEXT DhcpContext,
|
|
PDWORD BufferLength,
|
|
DWORD TransactionId,
|
|
DWORD TimeToWait
|
|
);
|
|
|
|
DWORD
|
|
CloseDhcpSocket(
|
|
PDHCP_CONTEXT DhcpContext
|
|
);
|
|
|
|
|
|
// functions
|
|
|
|
|
|
|
|
DWORD // Time in seconds
|
|
DhcpCalculateWaitTime( // how much time to wait
|
|
IN DWORD RoundNum, // which round is this
|
|
OUT DWORD *WaitMilliSecs // if needed the # in milli seconds
|
|
) {
|
|
DWORD MilliSecs;
|
|
//DWORD WaitTimes[4] = { 4000, 8000, 16000, 32000 };
|
|
DWORD WaitTimes[4] = { 2000, 4000, 8000, 16000 };
|
|
|
|
if( WaitMilliSecs ) *WaitMilliSecs = 0;
|
|
if( RoundNum >= sizeof(WaitTimes)/sizeof(WaitTimes[0]) )
|
|
return 0;
|
|
|
|
MilliSecs = WaitTimes[RoundNum] - 1000 + ((rand()*((DWORD) 2000))/RAND_MAX);
|
|
if( WaitMilliSecs ) *WaitMilliSecs = MilliSecs;
|
|
|
|
return (MilliSecs + 501)/1000;
|
|
}
|
|
|
|
|
|
VOID _inline
|
|
ConcatOption(
|
|
IN OUT LPBYTE *Buf, // input buffer to re-alloc
|
|
IN OUT ULONG *BufSize, // input buffer size
|
|
IN BYTE UNALIGNED *Data, // data to append
|
|
IN ULONG DataSize // how many bytes to add?
|
|
)
|
|
{
|
|
LPBYTE NewBuf;
|
|
ULONG NewSize;
|
|
|
|
NewSize = (*BufSize) + DataSize;
|
|
NewBuf = (LPBYTE) DhcpAllocateMemory(NewSize);
|
|
if( NULL == NewBuf ) { // could not alloc memory?
|
|
return; // can't do much
|
|
}
|
|
|
|
memcpy(NewBuf, *Buf, *BufSize); // copy existing part
|
|
memcpy(NewBuf + *BufSize, Data, DataSize); // copy new stuff
|
|
|
|
if( NULL != *Buf ) DhcpFreeMemory(*Buf); // if we alloc'ed mem, free it now
|
|
*Buf = NewBuf;
|
|
*BufSize = NewSize; // fill in new values..
|
|
}
|
|
|
|
VOID
|
|
DhcpExtractFullOrLiteOptions( // Extract some important options alone or ALL
|
|
IN PDHCP_CONTEXT DhcpContext, // input context
|
|
IN LPBYTE OptStart, // start of the options stuff
|
|
IN DWORD MessageSize, // # of bytes of options
|
|
IN BOOL LiteOnly, // next struc is EXPECTED_OPTIONS and not FULL_OPTIONS
|
|
OUT LPVOID DhcpOptions, // this is where the options would be stored
|
|
IN OUT PLIST_ENTRY RecdOptions, // if !LiteOnly this gets filled with all incoming options
|
|
IN OUT DWORD *LeaseExpiry, // if !LiteOnly input expiry time, else output expiry time
|
|
IN LPBYTE ClassName, // if !LiteOnly this is used to add to the option above
|
|
IN DWORD ClassLen // if !LiteOnly this gives the # of bytes of classname
|
|
) {
|
|
BYTE UNALIGNED* ThisOpt;
|
|
BYTE UNALIGNED* NextOpt;
|
|
BYTE UNALIGNED* EndOpt;
|
|
BYTE UNALIGNED* MagicCookie;
|
|
DWORD Error;
|
|
DWORD Size, ThisSize, UClassSize = 0;
|
|
LPBYTE UClass= NULL; // concatenation of all OPTION_USER_CLASS options
|
|
PDHCP_EXPECTED_OPTIONS ExpOptions;
|
|
PDHCP_FULL_OPTIONS FullOptions;
|
|
BYTE ReqdCookie[] = {
|
|
(BYTE)DHCP_MAGIC_COOKIE_BYTE1,
|
|
(BYTE)DHCP_MAGIC_COOKIE_BYTE2,
|
|
(BYTE)DHCP_MAGIC_COOKIE_BYTE3,
|
|
(BYTE)DHCP_MAGIC_COOKIE_BYTE4
|
|
};
|
|
|
|
|
|
EndOpt = OptStart + MessageSize; // all options should be < EndOpt;
|
|
ExpOptions = (PDHCP_EXPECTED_OPTIONS)DhcpOptions;
|
|
FullOptions = (PDHCP_FULL_OPTIONS)DhcpOptions;
|
|
RtlZeroMemory((LPBYTE)DhcpOptions, LiteOnly?sizeof(*ExpOptions):sizeof(*FullOptions));
|
|
// if(!LiteOnly) InitializeListHead(RecdOptions); -- clear off this list for getting ALL options
|
|
// dont clear off options... just accumulate over..
|
|
|
|
MagicCookie = OptStart;
|
|
if( 0 == MessageSize ) goto DropPkt; // nothing to do in this case
|
|
if( 0 != memcmp(MagicCookie, ReqdCookie, sizeof(ReqdCookie)) )
|
|
goto DropPkt; // oops, cant handle this packet
|
|
|
|
NextOpt = &MagicCookie[sizeof(ReqdCookie)];
|
|
while( NextOpt < EndOpt && OPTION_END != *NextOpt ) {
|
|
if( OPTION_PAD == *NextOpt ) { // handle pads right away
|
|
NextOpt++;
|
|
continue;
|
|
}
|
|
|
|
ThisOpt = NextOpt; // take a good look at this option
|
|
if( NextOpt + 2 > EndOpt ) { // goes over boundary?
|
|
break;
|
|
}
|
|
|
|
NextOpt += 2 + (unsigned)ThisOpt[1]; // Option[1] holds the size of this option
|
|
Size = ThisOpt[1];
|
|
|
|
if( NextOpt > EndOpt ) { // illegal option that goes over boundary!
|
|
break; // ignore the error, but dont take this option
|
|
}
|
|
|
|
if(!LiteOnly) do { // look for any OPTION_MSFT_CONTINUED ..
|
|
if( NextOpt >= EndOpt ) break; // no more options
|
|
if( OPTION_MSFT_CONTINUED != NextOpt[0] ) break;
|
|
if( NextOpt + 1 + NextOpt[1] > EndOpt ) {
|
|
NextOpt = NULL; // do this so that we know to quit at the end..
|
|
break;
|
|
}
|
|
|
|
NextOpt++; // skip opt code
|
|
ThisSize = NextOpt[0]; // # of bytes to shift back..
|
|
memcpy(ThisOpt+2+Size, NextOpt+1,ThisSize);
|
|
NextOpt += ThisSize+1;
|
|
Size += ThisSize;
|
|
} while(1); // keep stringing up any "continued" options..
|
|
|
|
if( NULL == NextOpt ) { // err parsing OPTION_MSFT_CONTINUED ..
|
|
break;
|
|
}
|
|
|
|
if( LiteOnly ) { // handle the small subnet of options
|
|
switch( ThisOpt[0] ) { // ThisOpt[0] is OptionId, ThisOpt[1] is size
|
|
case OPTION_MESSAGE_TYPE:
|
|
if( ThisOpt[1] != 1 ) goto DropPkt;
|
|
ExpOptions->MessageType = &ThisOpt[2];
|
|
continue;
|
|
case OPTION_SUBNET_MASK:
|
|
if( ThisOpt[1] != sizeof(DWORD) ) goto DropPkt;
|
|
ExpOptions->SubnetMask = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
continue;
|
|
case OPTION_LEASE_TIME:
|
|
if( ThisOpt[1] != sizeof(DWORD) ) goto DropPkt;
|
|
ExpOptions->LeaseTime = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
continue;
|
|
case OPTION_SERVER_IDENTIFIER:
|
|
if( ThisOpt[1] != sizeof(DWORD) ) goto DropPkt;
|
|
ExpOptions->ServerIdentifier = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
continue;
|
|
case OPTION_DOMAIN_NAME:
|
|
if( ThisOpt[1] == 0 ) goto DropPkt;
|
|
ExpOptions->DomainName = (BYTE UNALIGNED *)&ThisOpt[2];
|
|
ExpOptions->DomainNameSize = ThisOpt[1];
|
|
break;
|
|
case OPTION_WPAD_URL:
|
|
if( ThisOpt[1] == 0 ) goto DropPkt;
|
|
ExpOptions->WpadUrl = (BYTE UNALIGNED *)&ThisOpt[2];
|
|
ExpOptions->WpadUrlSize = ThisOpt[1];
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
} else { // Handle the full set of options
|
|
switch( ThisOpt[0] ) {
|
|
case OPTION_MESSAGE_TYPE:
|
|
if( Size != 1 ) goto DropPkt;
|
|
FullOptions->MessageType = &ThisOpt[2];
|
|
break;
|
|
case OPTION_SUBNET_MASK:
|
|
if( Size != sizeof(DWORD) ) goto DropPkt;
|
|
FullOptions->SubnetMask = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
break;
|
|
case OPTION_LEASE_TIME:
|
|
if( Size != sizeof(DWORD) ) goto DropPkt;
|
|
FullOptions->LeaseTime = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
break;
|
|
case OPTION_SERVER_IDENTIFIER:
|
|
if( Size != sizeof(DWORD) ) goto DropPkt;
|
|
FullOptions->ServerIdentifier = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
break;
|
|
case OPTION_RENEWAL_TIME: // T1Time
|
|
if( Size != sizeof(DWORD) ) goto DropPkt;
|
|
FullOptions->T1Time = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
break;
|
|
case OPTION_REBIND_TIME: // T2Time
|
|
if( Size != sizeof(DWORD) ) goto DropPkt;
|
|
FullOptions->T2Time = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
break;
|
|
case OPTION_ROUTER_ADDRESS:
|
|
if( Size < sizeof(DWORD) || (Size % sizeof(DWORD) ) )
|
|
goto DropPkt; // There can be many router addresses
|
|
FullOptions->GatewayAddresses = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
FullOptions->nGateways = Size / sizeof(DWORD);
|
|
break;
|
|
case OPTION_STATIC_ROUTES:
|
|
if( Size < 2*sizeof(DWORD) || (Size % (2*sizeof(DWORD))) )
|
|
goto DropPkt; // the static routes come in pairs
|
|
FullOptions->StaticRouteAddresses = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
FullOptions->nStaticRoutes = Size/(2*sizeof(DWORD));
|
|
break;
|
|
case OPTION_DYNDNS_BOTH:
|
|
if( Size < 3 ) goto DropPkt;
|
|
FullOptions->DnsFlags = (BYTE UNALIGNED *)&ThisOpt[2];
|
|
FullOptions->DnsRcode1 = (BYTE UNALIGNED *)&ThisOpt[3];
|
|
FullOptions->DnsRcode2 = (BYTE UNALIGNED *)&ThisOpt[3];
|
|
break;
|
|
case OPTION_DOMAIN_NAME:
|
|
if( Size == 0 ) goto DropPkt;
|
|
FullOptions->DomainName = (BYTE UNALIGNED *)&ThisOpt[2];
|
|
FullOptions->DomainNameSize = Size;
|
|
break;
|
|
case OPTION_WPAD_URL:
|
|
if( Size == 0 ) goto DropPkt;
|
|
FullOptions->WpadUrl = (BYTE UNALIGNED *)&ThisOpt[2];
|
|
FullOptions->WpadUrlSize = Size;
|
|
break;
|
|
case OPTION_DOMAIN_NAME_SERVERS:
|
|
if( Size < sizeof(DWORD) || (Size % sizeof(DWORD) ))
|
|
goto DropPkt;
|
|
FullOptions->DnsServerList = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
|
|
FullOptions->nDnsServers = Size / sizeof(DWORD);
|
|
break;
|
|
case OPTION_MESSAGE:
|
|
if( Size == 0 ) break; // ignore zero sized packets
|
|
FullOptions->ServerMessage = &ThisOpt[2];
|
|
FullOptions->ServerMessageLength = ThisOpt[1];
|
|
break;
|
|
case OPTION_MCAST_LEASE_START:
|
|
if ( Size != sizeof(DATE_TIME) ) goto DropPkt;
|
|
FullOptions->MCastLeaseStartTime = (DWORD UNALIGNED *)&ThisOpt[2];
|
|
break;
|
|
case OPTION_MCAST_TTL:
|
|
if ( Size != 1 ) goto DropPkt;
|
|
FullOptions->MCastTTL = (BYTE UNALIGNED *)&ThisOpt[2];
|
|
break;
|
|
case OPTION_USER_CLASS:
|
|
if( Size <= 6) goto DropPkt;
|
|
ConcatOption(&UClass, &UClassSize, &ThisOpt[2], Size);
|
|
continue; // don't add this option yet...
|
|
|
|
default:
|
|
// unknowm message, nothing to do.. especially dont log this
|
|
break;
|
|
}
|
|
|
|
} // if LiteOnly then else
|
|
} // while NextOpt < EndOpt
|
|
|
|
if( LiteOnly && LeaseExpiry ) { // If asked to calculate lease expiration time..
|
|
DWORD LeaseTime;
|
|
time_t TimeNow, ExpirationTime;
|
|
|
|
// BBUGBUGBUG [arthurbi] broken intensionlly, dead code.
|
|
//if( ExpOptions->LeaseTime ) LeaseTime = _I_ntohl(*ExpOptions->LeaseTime);
|
|
if( ExpOptions->LeaseTime ) LeaseTime = 0;
|
|
else LeaseTime = DHCP_MINIMUM_LEASE;
|
|
ExpirationTime = (TimeNow = time(NULL)) + (time_t)LeaseTime;
|
|
if( ExpirationTime < TimeNow ) {
|
|
ExpirationTime = INFINIT_TIME;
|
|
}
|
|
|
|
*LeaseExpiry = (DWORD)ExpirationTime ;
|
|
}
|
|
|
|
if( !LiteOnly && NULL != UClass ) { // we have a user class list to pass on..
|
|
DhcpAssert(UClassSize != 0 ); // we better have something here..
|
|
DhcpFreeMemory(UClass); UClass = NULL;
|
|
}
|
|
|
|
return;
|
|
|
|
DropPkt:
|
|
RtlZeroMemory(DhcpOptions, LiteOnly?sizeof(ExpOptions):sizeof(FullOptions));
|
|
if( LiteOnly && LeaseExpiry ) *LeaseExpiry = (DWORD) time(NULL) + DHCP_MINIMUM_LEASE;
|
|
//if(!LiteOnly) DhcpFreeAllOptions(RecdOptions);// ok undo the options that we just added
|
|
if(!LiteOnly && NULL != UClass ) DhcpFreeMemory(UClass);
|
|
}
|
|
|
|
POPTION // ptr to add additional options
|
|
FormatDhcpInform( // format the packet for an INFORM
|
|
IN PDHCP_CONTEXT DhcpContext // format for this context
|
|
) {
|
|
LPOPTION option;
|
|
LPBYTE OptionEnd;
|
|
BYTE value;
|
|
PDHCP_MESSAGE dhcpMessage;
|
|
|
|
dhcpMessage = DhcpContext->MessageBuffer;
|
|
RtlZeroMemory( dhcpMessage, DHCP_SEND_MESSAGE_SIZE );
|
|
|
|
|
|
// BUGBUG [arthurbi] -
|
|
// For RAS client, use broadcast bit, otherwise the router will try
|
|
// to send as unicast to made-up RAS client hardware address, which
|
|
// will not work. So will this work without it?
|
|
|
|
|
|
|
|
// Transaction ID is filled in during send
|
|
|
|
|
|
dhcpMessage->Operation = BOOT_REQUEST;
|
|
dhcpMessage->HardwareAddressType = DhcpContext->HardwareAddressType;
|
|
dhcpMessage->SecondsSinceBoot = (WORD) DhcpContext->SecondsSinceBoot;
|
|
memcpy(dhcpMessage->HardwareAddress,DhcpContext->HardwareAddress,DhcpContext->HardwareAddressLength);
|
|
dhcpMessage->HardwareAddressLength = (BYTE)DhcpContext->HardwareAddressLength;
|
|
dhcpMessage->ClientIpAddress = DhcpContext->IpAddress;
|
|
//dhcpMessage->Reserved = 0;
|
|
//dhcpMessage->Reserved = _I_htons(DHCP_BROADCAST);
|
|
//if ( IS_MDHCP_CTX(DhcpContext ) ) MDHCP_MESSAGE( dhcpMessage );
|
|
|
|
option = &dhcpMessage->Option;
|
|
OptionEnd = (LPBYTE)dhcpMessage + DHCP_SEND_MESSAGE_SIZE;
|
|
|
|
|
|
// always add magic cookie first
|
|
|
|
|
|
option = (LPOPTION) DhcpAppendMagicCookie( (LPBYTE) option, OptionEnd );
|
|
|
|
value = DHCP_INFORM_MESSAGE;
|
|
option = DhcpAppendOption(
|
|
option,
|
|
OPTION_MESSAGE_TYPE,
|
|
&value,
|
|
1,
|
|
OptionEnd
|
|
);
|
|
|
|
|
|
// BUGBUG [arthurbi], shouldn't we uncomment this?
|
|
|
|
|
|
// un comment later on
|
|
/*option = DhcpAppendClassIdOption(
|
|
DhcpContext,
|
|
(LPBYTE)option,
|
|
OptionEnd
|
|
);*/
|
|
|
|
return( option );
|
|
}
|
|
|
|
|
|
DWORD // status
|
|
SendDhcpInform( // send an inform packet after filling required options
|
|
IN PDHCP_CONTEXT DhcpContext, // sned out for this context
|
|
IN OUT DWORD *pdwXid // use this Xid (if zero fill something and return it)
|
|
) {
|
|
DWORD size;
|
|
DWORD Error;
|
|
POPTION option;
|
|
LPBYTE OptionEnd;
|
|
BYTE SentOpt[OPTION_END+1];
|
|
BYTE SentVOpt[OPTION_END+1];
|
|
BYTE VendorOpt[OPTION_END+1];
|
|
DWORD VendorOptSize;
|
|
|
|
RtlZeroMemory(SentOpt, sizeof(SentOpt)); // initialize boolean arrays
|
|
RtlZeroMemory(SentVOpt, sizeof(SentVOpt)); // so that no option is presumed sent
|
|
VendorOptSize = 0; // encapsulated vendor option is empty
|
|
option = FormatDhcpInform( DhcpContext ); // core format
|
|
|
|
OptionEnd = (LPBYTE)(DhcpContext->MessageBuffer) + DHCP_SEND_MESSAGE_SIZE;
|
|
|
|
if( DhcpContext->ClientIdentifier.fSpecified) // client id specified in registy
|
|
option = DhcpAppendClientIDOption( // ==> use this client id as option
|
|
option,
|
|
DhcpContext->ClientIdentifier.bType,
|
|
DhcpContext->ClientIdentifier.pbID,
|
|
(BYTE)DhcpContext->ClientIdentifier.cbID,
|
|
OptionEnd
|
|
);
|
|
else // client id was not specified
|
|
option = DhcpAppendClientIDOption( // ==> use hw addr as client id
|
|
option,
|
|
DhcpContext->HardwareAddressType,
|
|
DhcpContext->HardwareAddress,
|
|
(BYTE)DhcpContext->HardwareAddressLength,
|
|
OptionEnd
|
|
);
|
|
|
|
{ // add hostname and comment options
|
|
char szHostName[255];
|
|
|
|
if ( _I_gethostname(szHostName, ARRAY_ELEMENTS(szHostName)) != SOCKET_ERROR )
|
|
{
|
|
option = DhcpAppendOption(option, OPTION_HOST_NAME, (LPBYTE)szHostName, (BYTE)((strlen(szHostName) + 1) * sizeof(CHAR)), OptionEnd);
|
|
}
|
|
}
|
|
|
|
if( NULL != DhcpGlobalClientClassInfo ) { // if we have any info on client class..
|
|
option = DhcpAppendOption(
|
|
option,
|
|
OPTION_CLIENT_CLASS_INFO,
|
|
(LPBYTE)DhcpGlobalClientClassInfo,
|
|
strlen(DhcpGlobalClientClassInfo),
|
|
OptionEnd
|
|
);
|
|
}
|
|
|
|
SentOpt[OPTION_MESSAGE_TYPE] = TRUE; // these must have been added by now
|
|
if(DhcpContext->ClassIdLength) SentOpt[OPTION_USER_CLASS] = TRUE;
|
|
SentOpt[OPTION_CLIENT_CLASS_INFO] = TRUE;
|
|
SentOpt[OPTION_CLIENT_ID] = TRUE;
|
|
SentOpt[OPTION_REQUESTED_ADDRESS] = TRUE;
|
|
SentOpt[OPTION_HOST_NAME] = TRUE;
|
|
|
|
option = DhcpAppendSendOptions( // append all other options we need to send
|
|
DhcpContext, // for this context
|
|
&DhcpContext->SendOptionsList, // this is the list of options to send out
|
|
DhcpContext->ClassId, // which class.
|
|
DhcpContext->ClassIdLength, // how many bytes are there in the class id
|
|
(LPBYTE)option, // start of the buffer to add the options
|
|
(LPBYTE)OptionEnd, // end of the buffer up to which we can add options
|
|
SentOpt, // this is the boolean array that marks what opt were sent
|
|
SentVOpt, // this is for vendor spec options
|
|
VendorOpt, // this would contain some vendor specific options
|
|
&VendorOptSize // the # of bytes of vendor options added to VendorOpt param
|
|
);
|
|
|
|
if( !SentOpt[OPTION_VENDOR_SPEC_INFO] && VendorOptSize && VendorOptSize <= OPTION_END )
|
|
option = DhcpAppendOption( // add vendor specific options if we havent already sent it
|
|
option,
|
|
OPTION_VENDOR_SPEC_INFO,
|
|
VendorOpt,
|
|
(BYTE)VendorOptSize,
|
|
OptionEnd
|
|
);
|
|
|
|
option = DhcpAppendOption( option, OPTION_END, NULL, 0, OptionEnd );
|
|
size = (DWORD)((PBYTE)option - (PBYTE)DhcpContext->MessageBuffer);
|
|
|
|
return SendDhcpMessage( // finally send the message and return
|
|
DhcpContext,
|
|
size,
|
|
pdwXid
|
|
);
|
|
}
|
|
|
|
DWORD
|
|
InitializeDhcpSocket(
|
|
SOCKET *Socket,
|
|
DHCP_IP_ADDRESS IpAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes and binds a socket to the specified IP address.
|
|
|
|
Arguments:
|
|
|
|
Socket - Returns a pointer to the initialized socket.
|
|
|
|
IpAddress - The IP address to bind the socket to. It is legitimate
|
|
to bind a socket to 0.0.0.0 if the card has no current IP address.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
DWORD error;
|
|
DWORD closeError;
|
|
DWORD value;
|
|
struct sockaddr_in socketName;
|
|
DWORD i;
|
|
SOCKET sock;
|
|
|
|
|
|
// Sockets initialization
|
|
|
|
|
|
sock = _I_socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
|
|
|
|
if ( sock == INVALID_SOCKET ) {
|
|
error = _I_WSAGetLastError();
|
|
DhcpPrint(("socket failed, error = %ld\n", error ));
|
|
return( error );
|
|
}
|
|
|
|
|
|
// Make the socket share-able
|
|
|
|
|
|
value = 1;
|
|
|
|
error = _I_setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char FAR *)&value, sizeof(value) );
|
|
if ( error != 0 ) {
|
|
error = _I_WSAGetLastError();
|
|
DhcpPrint(("setsockopt failed, err = %ld\n", error ));
|
|
|
|
closeError = _I_closesocket( sock );
|
|
if ( closeError != 0 ) {
|
|
DhcpPrint(("closesocket failed, err = %d\n", closeError ));
|
|
}
|
|
return( error );
|
|
}
|
|
|
|
error = _I_setsockopt( sock, SOL_SOCKET, SO_BROADCAST, (char FAR *)&value, sizeof(value) );
|
|
if ( error != 0 ) {
|
|
error = _I_WSAGetLastError();
|
|
DhcpPrint(("setsockopt failed, err = %ld\n", error ));
|
|
|
|
closeError = _I_closesocket( sock );
|
|
if ( closeError != 0 ) {
|
|
DhcpPrint(("closesocket failed, err = %d\n", closeError ));
|
|
}
|
|
return( error );
|
|
}
|
|
|
|
|
|
// If the IpAddress is zero, set the special socket option to make
|
|
// stack work with zero address.
|
|
|
|
|
|
if( IpAddress == 0 ) {
|
|
value = 1234;
|
|
error = _I_setsockopt( sock, SOL_SOCKET, 0x8000, (char FAR *)&value, sizeof(value) );
|
|
if ( error != 0 ) {
|
|
error = _I_WSAGetLastError();
|
|
DhcpPrint(("setsockopt failed, err = %ld\n", error ));
|
|
|
|
closeError = _I_closesocket( sock );
|
|
if ( closeError != 0 ) {
|
|
DhcpPrint(("closesocket failed, err = %d\n", closeError ));
|
|
}
|
|
return( error );
|
|
}
|
|
}
|
|
|
|
socketName.sin_family = PF_INET;
|
|
socketName.sin_port = _I_htons( DHCP_CLIENT_PORT );
|
|
socketName.sin_addr.s_addr = IpAddress;
|
|
|
|
for ( i = 0; i < 8 ; i++ ) {
|
|
socketName.sin_zero[i] = 0;
|
|
}
|
|
|
|
|
|
// Bind this socket to the DHCP server port
|
|
|
|
|
|
error = _I_bind(
|
|
sock,
|
|
(struct sockaddr FAR *)&socketName,
|
|
sizeof( socketName )
|
|
);
|
|
|
|
if ( error != 0 ) {
|
|
error = _I_WSAGetLastError();
|
|
DhcpPrint(("bind failed (address 0x%lx), err = %ld\n", IpAddress, error ));
|
|
closeError = _I_closesocket( sock );
|
|
if ( closeError != 0 ) {
|
|
DhcpPrint(("closesocket failed, err = %d\n", closeError ));
|
|
}
|
|
return( error );
|
|
}
|
|
|
|
*Socket = sock;
|
|
return( NO_ERROR );
|
|
}
|
|
|
|
|
|
DWORD // status
|
|
SendInformAndGetReplies( // send an inform packet and collect replies
|
|
IN PDHCP_CONTEXT DhcpContext, // the context to send out of
|
|
IN DWORD nInformsToSend,// how many informs to send?
|
|
IN DWORD MaxAcksToWait, // how many acks to wait for
|
|
OUT DHCP_EXPECTED_OPTIONS *pExpectedOptions // list of things parsed out of request
|
|
) {
|
|
time_t StartTime;
|
|
time_t TimeNow;
|
|
DWORD TimeToWait;
|
|
DWORD Error;
|
|
DWORD Xid;
|
|
DWORD MessageSize;
|
|
DWORD RoundNum;
|
|
DWORD MessageCount;
|
|
DWORD LeaseExpirationTime;
|
|
DHCP_FULL_OPTIONS FullOptions;
|
|
|
|
DhcpPrint(("SendInformAndGetReplies entered\n"));
|
|
|
|
if((Error = OpenDhcpSocket(DhcpContext)) != ERROR_SUCCESS) {
|
|
DhcpPrint(("Could not open socket for this interface! (%ld)\n", Error));
|
|
return Error;
|
|
}
|
|
|
|
Xid = 0; // Will be generated by first SendDhcpPacket
|
|
MessageCount = 0; // total # of messages we have got
|
|
|
|
DhcpContext->SecondsSinceBoot = 0; // start at zero..
|
|
for( RoundNum = 0; RoundNum < nInformsToSend; RoundNum ++ ) {
|
|
Error = SendDhcpInform(DhcpContext, &Xid);
|
|
if( ERROR_SUCCESS != Error ) {
|
|
DhcpPrint(("SendDhcpInform: %ld\n", Error));
|
|
goto Cleanup;
|
|
} else {
|
|
DhcpPrint(("Sent DhcpInform\n"));
|
|
}
|
|
|
|
TimeToWait = DhcpCalculateWaitTime(RoundNum, NULL);
|
|
DhcpContext->SecondsSinceBoot += TimeToWait; // do this so that next time thru it can go thru relays..
|
|
StartTime = time(NULL);
|
|
while ( TRUE ) { // wiat for the specified wait time
|
|
MessageSize = DHCP_MESSAGE_SIZE;
|
|
|
|
DhcpPrint(("Waiting for ACK[Xid=%x]: %ld seconds\n",Xid, TimeToWait));
|
|
Error = GetSpecifiedDhcpMessage( // try to receive an ACK
|
|
DhcpContext,
|
|
&MessageSize,
|
|
Xid,
|
|
(DWORD)TimeToWait
|
|
);
|
|
if ( Error == ERROR_SEM_TIMEOUT ) break;
|
|
if( Error != ERROR_SUCCESS ) {
|
|
DhcpPrint(("GetSpecifiedDhcpMessage: %ld\n", Error));
|
|
goto Cleanup;
|
|
}
|
|
|
|
DhcpExtractFullOrLiteOptions( // Need to see if this is an ACK
|
|
DhcpContext,
|
|
(LPBYTE)&DhcpContext->MessageBuffer->Option,
|
|
MessageSize - DHCP_MESSAGE_FIXED_PART_SIZE,
|
|
TRUE, // do lite extract only
|
|
pExpectedOptions, // check for only expected options
|
|
NULL, // unused
|
|
&LeaseExpirationTime,
|
|
NULL, // unused
|
|
0 // unused
|
|
);
|
|
|
|
if( NULL == pExpectedOptions->MessageType ) {
|
|
DhcpPrint(("Received no message type!\n"));
|
|
} else if( DHCP_ACK_MESSAGE != *(pExpectedOptions->MessageType) ) {
|
|
DhcpPrint(("Received unexpected message type: %ld\n", *(pExpectedOptions->MessageType)));
|
|
} else if( NULL == pExpectedOptions->ServerIdentifier ) {
|
|
DhcpPrint(("Received no server identifier, dropping inform ACK\n"));
|
|
} else {
|
|
MessageCount ++;
|
|
DhcpPrint(("Received %ld ACKS so far\n", MessageCount));
|
|
DhcpExtractFullOrLiteOptions( // do FULL options..
|
|
DhcpContext,
|
|
(LPBYTE)&DhcpContext->MessageBuffer->Option,
|
|
MessageSize - DHCP_MESSAGE_FIXED_PART_SIZE,
|
|
FALSE,
|
|
&FullOptions,
|
|
&(DhcpContext->RecdOptionsList),
|
|
&LeaseExpirationTime,
|
|
DhcpContext->ClassId,
|
|
DhcpContext->ClassIdLength
|
|
);
|
|
if( MessageCount >= MaxAcksToWait ) goto Cleanup;
|
|
} // if( it is an ACK and ServerId present )
|
|
|
|
TimeNow = time(NULL); // Reset the time values to reflect new time
|
|
if( TimeToWait < (DWORD) (TimeNow - StartTime) ) {
|
|
break; // no more time left to wait..
|
|
}
|
|
TimeToWait -= (DWORD)(TimeNow - StartTime); // recalculate time now
|
|
StartTime = TimeNow; // reset start time also
|
|
} // end of while ( TimeToWait > 0)
|
|
} // for (RoundNum = 0; RoundNum < nInformsToSend ; RoundNum ++ )
|
|
|
|
Cleanup:
|
|
CloseDhcpSocket(DhcpContext);
|
|
if( MessageCount ) Error = ERROR_SUCCESS;
|
|
DhcpPrint(("SendInformAndGetReplies: got %d ACKS (returning %ld)\n", MessageCount,Error));
|
|
return Error;
|
|
}
|
|
|
|
|
|
// This function gets the options from the server using DHCP_INFORM message.
|
|
// It picks the first ACK and then processes it.
|
|
// It ignores any errors caused by TIME_OUTS as that only means there is no
|
|
// server, or the server does not have this functionality. No point giving up
|
|
// because of that.
|
|
|
|
BOOL // win32 status
|
|
DhcpDoInform( // send an inform packet if necessary
|
|
IN CAdapterInterface * pAdapterInterface,
|
|
IN BOOL fBroadcast, // Do we broadcast this inform, or unicast to server?
|
|
OUT LPSTR lpszAutoProxyUrl,
|
|
IN DWORD dwAutoProxyUrlLength
|
|
) {
|
|
DHCP_CONTEXT StackDhcpContext; // input context to do inform on
|
|
PDHCP_CONTEXT DhcpContext = &StackDhcpContext;
|
|
DWORD Error;
|
|
DWORD LocalError;
|
|
BOOL WasPlumbedBefore;
|
|
time_t OldT2Time;
|
|
DHCP_EXPECTED_OPTIONS ExpectedOptions;
|
|
|
|
*lpszAutoProxyUrl = '\0';
|
|
|
|
if ( ! pAdapterInterface->IsDhcp() ) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (! pAdapterInterface->CopyAdapterInfoToDhcpContext(DhcpContext) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
// mdhcp uses INADDR_ANY so it does not have to have an ipaddress.
|
|
if( 0 == DhcpContext->IpAddress && !IS_MDHCP_CTX( DhcpContext) ) {
|
|
DhcpPrint(("Cannot do DhcpInform on an adapter without ip address!\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
// Open the socket ahead... so that things work. Tricky, else does not work!!!
|
|
if((Error = OpenDhcpSocket(DhcpContext)) != ERROR_SUCCESS ) {
|
|
DhcpPrint(("Could not open socket (%ld)\n", Error));
|
|
return FALSE;
|
|
}
|
|
|
|
// If you always need to broadcast this message, the KLUDGE is to
|
|
// set pContext->T2Time = 0; and pContext->fFlags &= ~DHCP_CONTEXT_FLAGS_PLUMBED
|
|
// and that should do the trick! Safe to change the struct as it was cloned.
|
|
OldT2Time = DhcpContext->T2Time;
|
|
WasPlumbedBefore = IS_ADDRESS_PLUMBED(DhcpContext);
|
|
if(fBroadcast) {
|
|
DhcpContext->T2Time = 0; // !!!! KLUDGE.. look at SendDhcpMessage to understand this ..
|
|
ADDRESS_UNPLUMBED(DhcpContext);
|
|
CONNECTION_BROADCAST(DhcpContext);
|
|
} else {
|
|
DhcpContext->T2Time = (-1);
|
|
}
|
|
|
|
memset((void *) &ExpectedOptions, 0, sizeof(DHCP_EXPECTED_OPTIONS));
|
|
|
|
Error = SendInformAndGetReplies( // get replies on this
|
|
DhcpContext, // context to send on
|
|
2, // send atmost 2 informs
|
|
1, // wait for as many as 4 packets..
|
|
&ExpectedOptions
|
|
);
|
|
DhcpContext->LastInformSent = time(NULL); // record when the last inform was sent
|
|
DhcpContext->T2Time = OldT2Time;
|
|
if( WasPlumbedBefore ) ADDRESS_PLUMBED(DhcpContext);
|
|
|
|
LocalError = CloseDhcpSocket(DhcpContext);
|
|
DhcpAssert(ERROR_SUCCESS == LocalError);
|
|
|
|
if( ERROR_SUCCESS != Error ) {
|
|
DhcpPrint(("DhcpDoInform:return [0x%lx]\n", Error));
|
|
}
|
|
else
|
|
{
|
|
|
|
// Did we actually get a response with an URL that can be used ?
|
|
|
|
|
|
if ( ExpectedOptions.WpadUrl &&
|
|
ExpectedOptions.WpadUrlSize > 0 &&
|
|
dwAutoProxyUrlLength > ExpectedOptions.WpadUrlSize )
|
|
{
|
|
memcpy(lpszAutoProxyUrl, ExpectedOptions.WpadUrl, ExpectedOptions.WpadUrlSize );
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
SendDhcpMessage(
|
|
PDHCP_CONTEXT DhcpContext,
|
|
DWORD MessageLength,
|
|
PDWORD TransactionId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sends a UDP message to the DHCP server specified
|
|
in the DhcpContext.
|
|
|
|
Arguments:
|
|
|
|
DhcpContext - A pointer to a DHCP context block.
|
|
|
|
MessageLength - The length of the message to send.
|
|
|
|
TransactionID - The transaction ID for this message. If 0, the
|
|
function generates a random ID, and returns it.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
DWORD error;
|
|
int i;
|
|
struct sockaddr_in socketName;
|
|
time_t TimeNow;
|
|
BOOL LockedInterface = FALSE;
|
|
|
|
if ( *TransactionId == 0 ) {
|
|
*TransactionId = (rand() << 16) + rand();
|
|
}
|
|
|
|
DhcpContext->MessageBuffer->TransactionID = *TransactionId;
|
|
|
|
|
|
// Initialize the outgoing address.
|
|
|
|
|
|
socketName.sin_family = PF_INET;
|
|
socketName.sin_port = _I_htons( DHCP_SERVR_PORT );
|
|
|
|
if ( IS_MDHCP_CTX(DhcpContext) ) {
|
|
socketName.sin_addr.s_addr = DhcpContext->DhcpServerAddress;
|
|
if ( CLASSD_NET_ADDR( DhcpContext->DhcpServerAddress ) ) {
|
|
int TTL = 16;
|
|
|
|
// Set TTL
|
|
// MBUG: we need to read this from the registry.
|
|
|
|
if (_I_setsockopt(
|
|
DhcpContext->Socket,
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_TTL,
|
|
(char *)&TTL,
|
|
sizeof((int)TTL)) == SOCKET_ERROR){
|
|
|
|
error = _I_WSAGetLastError();
|
|
DhcpPrint(("could not set MCast TTL %ld\n",error ));
|
|
return error;
|
|
}
|
|
|
|
}
|
|
} else if( IS_ADDRESS_PLUMBED(DhcpContext) &&
|
|
!IS_MEDIA_RECONNECTED(DhcpContext) && // media reconnect - braodcast
|
|
!IS_POWER_RESUMED(DhcpContext) ) { // power resumed - broadcast
|
|
|
|
|
|
// If we are past T2, use the broadcast address; otherwise,
|
|
// direct this to the server.
|
|
|
|
|
|
TimeNow = time( NULL );
|
|
|
|
// BUGBUG why did we broadcast here before ?
|
|
if ( TimeNow > DhcpContext->T2Time && IS_CONNECTION_BROADCAST(DhcpContext)) {
|
|
socketName.sin_addr.s_addr = (DHCP_IP_ADDRESS)(INADDR_BROADCAST);
|
|
} else {
|
|
socketName.sin_addr.s_addr = DhcpContext->DhcpServerAddress;
|
|
}
|
|
}
|
|
else {
|
|
socketName.sin_addr.s_addr = (DHCP_IP_ADDRESS)(INADDR_BROADCAST);
|
|
INET_ASSERT(FALSE);
|
|
}
|
|
|
|
for ( i = 0; i < 8 ; i++ ) {
|
|
socketName.sin_zero[i] = 0;
|
|
}
|
|
|
|
if( socketName.sin_addr.s_addr ==
|
|
(DHCP_IP_ADDRESS)(INADDR_BROADCAST) ) {
|
|
|
|
DWORD Error = ERROR_SUCCESS;
|
|
DWORD InterfaceId;
|
|
|
|
|
|
// BUGBUG TODO [arthurbi] This code below is needed for
|
|
// Broadcasts to work. We need to make some fancy driver
|
|
// calls to work...
|
|
|
|
|
|
|
|
// if we broadcast a message, inform IP stack - the adapter we
|
|
// like to send this broadcast on, otherwise it will pick up the
|
|
// first uninitialized adapter.
|
|
|
|
|
|
// InterfaceId = DhcpContext->IpInterfaceContext;
|
|
|
|
// if( !IPSetInterface( InterfaceId ) ) {
|
|
// // DhcpAssert( FALSE );
|
|
// Error = ERROR_GEN_FAILURE;
|
|
// }
|
|
|
|
// InterfaceId = ((PLOCAL_CONTEXT_INFO)
|
|
// DhcpContext->LocalInformation)->IpInterfaceContext;
|
|
|
|
// LOCK_INTERFACE();
|
|
// LockedInterface = TRUE;
|
|
// Error = IPSetInterface( InterfaceId );
|
|
// DhcpAssert( Error == ERROR_SUCCESS );
|
|
|
|
if( ERROR_SUCCESS != Error ) {
|
|
DhcpPrint(("IPSetInterface failed with %lx error\n", Error));
|
|
UNLOCK_INTERFACE();
|
|
return Error;
|
|
}
|
|
}
|
|
|
|
|
|
// send minimum DHCP_MIN_SEND_RECV_PK_SIZE (300) bytes, otherwise
|
|
// bootp relay agents don't like the packet.
|
|
|
|
|
|
MessageLength = (MessageLength > DHCP_MIN_SEND_RECV_PK_SIZE) ?
|
|
MessageLength : DHCP_MIN_SEND_RECV_PK_SIZE;
|
|
error = _I_sendto(
|
|
DhcpContext->Socket,
|
|
(PCHAR)DhcpContext->MessageBuffer,
|
|
MessageLength,
|
|
0,
|
|
(struct sockaddr *)&socketName,
|
|
sizeof( struct sockaddr )
|
|
);
|
|
|
|
#ifndef VXD
|
|
if( LockedInterface ) { UNLOCK_INTERFACE(); }
|
|
#endif VXD
|
|
|
|
if ( error == SOCKET_ERROR ) {
|
|
error = _I_WSAGetLastError();
|
|
DhcpPrint(("Send failed, error = %ld\n", error ));
|
|
} else {
|
|
IF_DEBUG( PROTOCOL ) {
|
|
DhcpPrint(("Sent message to %s: \n", _I_inet_ntoa( socketName.sin_addr )));
|
|
}
|
|
|
|
DhcpDumpMessage( DEBUG_PROTOCOL_DUMP, DhcpContext->MessageBuffer );
|
|
error = NO_ERROR;
|
|
}
|
|
|
|
return( error );
|
|
}
|
|
|
|
DWORD
|
|
OpenDhcpSocket(
|
|
PDHCP_CONTEXT DhcpContext
|
|
)
|
|
{
|
|
|
|
DWORD Error;
|
|
PLOCAL_CONTEXT_INFO localInfo;
|
|
|
|
if ( DhcpContext->Socket != INVALID_SOCKET ) {
|
|
return ( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
// create a socket for the dhcp protocol. it's important to bind the
|
|
// socket to the correct ip address. There are currently three cases:
|
|
|
|
// 1. If the interface has been autoconfigured, it already has an address,
|
|
// say, IP1. If the client receives a unicast offer from a dhcp server
|
|
// the offer will be addressed to IP2, which is the client's new dhcp
|
|
// address. If we bind the dhcp socket to IP1, the client won't be able
|
|
// to receive unicast responses. So, we bind the socket to 0.0.0.0.
|
|
// This will allow the socket to receive a unicast datagram addressed to
|
|
// any address.
|
|
|
|
// 2. If the interface in not plumbed (i.e. doesn't have an address) bind
|
|
// the socket to 0.0.0.0
|
|
|
|
// 3. If the interface has been plumbed has in *not* autoconfigured, then
|
|
// bind to the current address.
|
|
|
|
|
|
Error = InitializeDhcpSocket(
|
|
&DhcpContext->Socket,
|
|
DhcpContext->IpAddress
|
|
);
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
DhcpContext->Socket = INVALID_SOCKET;
|
|
DhcpPrint((" Socket Open failed, %ld\n", Error ));
|
|
}
|
|
|
|
return(Error);
|
|
}
|
|
|
|
DWORD
|
|
CloseDhcpSocket(
|
|
PDHCP_CONTEXT DhcpContext
|
|
)
|
|
{
|
|
|
|
DWORD Error = ERROR_SUCCESS;
|
|
|
|
if( DhcpContext->Socket != INVALID_SOCKET ) {
|
|
|
|
BOOL Bool;
|
|
|
|
Error = _I_closesocket( DhcpContext->Socket );
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
DhcpPrint((" Socket close failed, %ld\n", Error ));
|
|
}
|
|
|
|
DhcpContext->Socket = INVALID_SOCKET;
|
|
|
|
|
|
// Reset the IP stack to send DHCP broadcast to first
|
|
// uninitialized stack.
|
|
|
|
|
|
//Bool = IPResetInterface();
|
|
//DhcpAssert( Bool == TRUE );
|
|
}
|
|
|
|
return( Error );
|
|
}
|
|
|
|
|
|
typedef struct /* anonymous */ { // structure to hold waiting recvfroms
|
|
LIST_ENTRY RecvList; // other elements in this list
|
|
PDHCP_CONTEXT Ctxt; // which context is this wait for?
|
|
DWORD InBufLen; // what was the buffer size to recv in?
|
|
PDWORD BufLen; // how many bytes did we recvd?
|
|
DWORD Xid; // what xid is this wait for?
|
|
time_t ExpTime; // wait until what time?
|
|
HANDLE WaitEvent; // event for waiting on..
|
|
BOOL Recd; // was a packet received..?
|
|
} RECV_CTXT, *PRECV_CTXT; // ctxt used to recv on..
|
|
|
|
VOID
|
|
InsertInPriorityList( // insert in priority list according to Secs
|
|
IN OUT PRECV_CTXT Ctxt, // Secs field changed to hold offset
|
|
IN PLIST_ENTRY List,
|
|
OUT PBOOL First // adding in first location?
|
|
)
|
|
{
|
|
PRECV_CTXT ThisCtxt;
|
|
PLIST_ENTRY InitList; // "List" param at function entry
|
|
|
|
|
|
if( IsListEmpty(List) ) { // no element in list? add this and quit
|
|
*First = TRUE; // adding at head
|
|
} else {
|
|
*First = FALSE; // adding at tail..
|
|
}
|
|
|
|
InsertTailList( List, &Ctxt->RecvList); // insert element..
|
|
//LeaveCriticalSection( &DhcpGlobalRecvFromCritSect );
|
|
}
|
|
|
|
DWORD
|
|
TryReceive( // try to recv pkt on 0.0.0.0 socket
|
|
IN SOCKET Socket, // socket to recv on
|
|
IN LPBYTE Buffer, // buffer to fill
|
|
OUT PDWORD BufLen, // # of bytes filled in buffer
|
|
OUT PDWORD Xid, // Xid of recd pkt
|
|
IN DWORD Secs // # of secs to spend waiting?
|
|
)
|
|
{
|
|
DWORD Error;
|
|
struct timeval timeout;
|
|
fd_set SockSet;
|
|
struct sockaddr SockName;
|
|
int SockNameSize;
|
|
|
|
FD_ZERO(&SockSet);
|
|
FD_SET(Socket,&SockSet);
|
|
|
|
SockNameSize = sizeof( SockName );
|
|
|
|
timeout.tv_sec = Secs;
|
|
timeout.tv_usec = 0;
|
|
|
|
DhcpPrint(("Select: waiting for: %ld seconds\n", Secs));
|
|
Error = _I_select( 0, &SockSet, NULL, NULL, &timeout );
|
|
if( ERROR_SUCCESS == Error ) { // timed out..
|
|
DhcpPrint(("Recv timed out..\n"));
|
|
return ERROR_SEM_TIMEOUT;
|
|
}
|
|
|
|
Error = _I_recvfrom(Socket,(char *)Buffer,*BufLen, 0, &SockName, &SockNameSize);
|
|
if( SOCKET_ERROR == Error ) {
|
|
Error = _I_WSAGetLastError();
|
|
DhcpPrint(("Recv failed 0x%lx\n",Error));
|
|
} else {
|
|
*BufLen = Error;
|
|
Error = ERROR_SUCCESS;
|
|
*Xid = ((PDHCP_MESSAGE)Buffer)->TransactionID;
|
|
DhcpPrint(("Recd msg XID: 0x%lx [Mdhcp? %s]\n", *Xid,
|
|
IS_MDHCP_MESSAGE(((PDHCP_MESSAGE)Buffer))?"yes":"no" ));
|
|
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
VOID
|
|
DispatchPkt( // find out any takers for Xid
|
|
IN OUT PRECV_CTXT Ctxt, // ctxt that has buffer and buflen
|
|
IN DWORD Xid // recd Xid
|
|
)
|
|
{
|
|
do { // not a loop, just for ease of use
|
|
LPBYTE Tmp;
|
|
PLIST_ENTRY Entry;
|
|
PRECV_CTXT ThisCtxt;
|
|
|
|
Entry = DhcpGlobalRecvFromList.Flink;
|
|
while(Entry != &DhcpGlobalRecvFromList ) {
|
|
ThisCtxt = CONTAINING_RECORD(Entry, RECV_CTXT, RecvList);
|
|
Entry = Entry->Flink;
|
|
|
|
if(Xid != ThisCtxt->Xid ) continue; // mismatch.. nothing more todo
|
|
|
|
// now check for same type of message and ctxt...
|
|
if( (unsigned)IS_MDHCP_MESSAGE((Ctxt->Ctxt->MessageBuffer))
|
|
!=
|
|
IS_MDHCP_CTX( (ThisCtxt->Ctxt) )
|
|
) {
|
|
|
|
// The contexts dont match.. give up
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
// check for same hardware address..
|
|
|
|
|
|
if( ThisCtxt->Ctxt->HardwareAddressLength != Ctxt->Ctxt->MessageBuffer->HardwareAddressLength ) {
|
|
continue;
|
|
}
|
|
|
|
if( 0 != memcmp(ThisCtxt->Ctxt->HardwareAddress,
|
|
Ctxt->Ctxt->MessageBuffer->HardwareAddress,
|
|
ThisCtxt->Ctxt->HardwareAddressLength
|
|
) ) {
|
|
continue;
|
|
}
|
|
|
|
// matched.. switch buffers to give this guy this due..
|
|
|
|
DhcpDumpMessage(DEBUG_PROTOCOL_DUMP, (PDHCP_MESSAGE)(Ctxt->Ctxt->MessageBuffer) );
|
|
|
|
*(ThisCtxt->BufLen) = *(Ctxt->BufLen);
|
|
Tmp = (LPBYTE)(Ctxt->Ctxt)->MessageBuffer;
|
|
(Ctxt->Ctxt)->MessageBuffer = (ThisCtxt->Ctxt)->MessageBuffer;
|
|
(ThisCtxt->Ctxt)->MessageBuffer = (PDHCP_MESSAGE)Tmp;
|
|
|
|
RemoveEntryList(&ThisCtxt->RecvList);
|
|
InitializeListHead(&ThisCtxt->RecvList);
|
|
DhcpAssert(FALSE == ThisCtxt->Recd);
|
|
ThisCtxt->Recd = TRUE;
|
|
if( 0 == SetEvent(ThisCtxt->WaitEvent) ) {
|
|
DhcpAssert(FALSE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
} while (FALSE);
|
|
//LeaveCriticalSection(&DhcpGlobalRecvFromCritSect);
|
|
}
|
|
|
|
DWORD
|
|
ProcessRecvFromSocket( // wait using select and process incoming pkts
|
|
IN OUT PRECV_CTXT Ctxt // ctxt to use
|
|
)
|
|
{
|
|
time_t TimeNow;
|
|
SOCKET Socket;
|
|
LPBYTE Buffer;
|
|
DWORD Xid;
|
|
DWORD Error;
|
|
PLIST_ENTRY Entry;
|
|
|
|
Socket = (Ctxt->Ctxt)->Socket;
|
|
TimeNow = time(NULL);
|
|
|
|
Error = ERROR_SEM_TIMEOUT;
|
|
while(TimeNow <= Ctxt->ExpTime ) { // while required to wait
|
|
Buffer = (LPBYTE)((Ctxt->Ctxt)->MessageBuffer);
|
|
*(Ctxt->BufLen) = Ctxt->InBufLen;
|
|
Error = TryReceive(Socket, Buffer, Ctxt->BufLen, &Xid, (DWORD)(Ctxt->ExpTime - TimeNow));
|
|
if( ERROR_SUCCESS != Error ) { // did not recv?
|
|
if( WSAECONNRESET != Error ) break; // ignore possibly spurious conn-resets..
|
|
else { TimeNow = time(NULL); continue; }
|
|
}
|
|
|
|
if( Xid == Ctxt->Xid ) break; // this was destined for this ctxt only..
|
|
|
|
DispatchPkt(Ctxt, Xid);
|
|
TimeNow = time(NULL);
|
|
}
|
|
|
|
if( TimeNow > Ctxt->ExpTime ) { // we timed out.
|
|
Error = ERROR_SEM_TIMEOUT;
|
|
}
|
|
|
|
// now done.. so we must remove this ctxt from the list and signal first guy
|
|
//EnterCriticalSection(&DhcpGlobalRecvFromCritSect);
|
|
RemoveEntryList(&Ctxt->RecvList);
|
|
CloseHandle(Ctxt->WaitEvent);
|
|
if( !IsListEmpty(&DhcpGlobalRecvFromList)) { // ok got an elt.. signal this.
|
|
Entry = DhcpGlobalRecvFromList.Flink;
|
|
Ctxt = CONTAINING_RECORD(Entry, RECV_CTXT, RecvList);
|
|
if( 0 == SetEvent(Ctxt->WaitEvent) ) {
|
|
DhcpAssert(FALSE);
|
|
}
|
|
}
|
|
//LeaveCriticalSection(&DhcpGlobalRecvFromCritSect);
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
// get dhcp message with requested transaction id, but also make sure only one
|
|
// socket is used at any given time (one socket bound to 0.0.0.0), and also
|
|
// re-distribute message for some other thread if that is also required..
|
|
|
|
DWORD
|
|
GetSpecifiedDhcpMessageEx(
|
|
IN OUT PDHCP_CONTEXT DhcpContext, // which context to recv for
|
|
OUT PDWORD BufferLength, // how big a buffer was read?
|
|
IN DWORD Xid, // which xid to look for?
|
|
IN DWORD TimeToWait // how many seconds to sleep?
|
|
)
|
|
{
|
|
RECV_CTXT Ctxt; // element in list for this call to getspe..
|
|
BOOL First; // is this the first element in list?
|
|
DWORD Result;
|
|
|
|
Ctxt.Ctxt = DhcpContext; // fill in the context
|
|
Ctxt.InBufLen = *BufferLength;
|
|
Ctxt.BufLen = BufferLength;
|
|
Ctxt.Xid = Xid;
|
|
Ctxt.ExpTime = time(NULL) + TimeToWait;
|
|
Ctxt.WaitEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
Ctxt.Recd = FALSE;
|
|
if( NULL == Ctxt.WaitEvent ) {
|
|
DhcpAssert(NULL != Ctxt.WaitEvent);
|
|
return GetLastError();
|
|
}
|
|
|
|
First = FALSE;
|
|
InsertInPriorityList(&Ctxt, &DhcpGlobalRecvFromList, &First);
|
|
|
|
if( First ) { // this *is* the first call to GetSpec..
|
|
Result = ProcessRecvFromSocket(&Ctxt);
|
|
} else { // we wait for other calls to go thru..
|
|
Result = WaitForSingleObject(Ctxt.WaitEvent, TimeToWait * 1000);
|
|
//EnterCriticalSection(&DhcpGlobalRecvFromCritSect);
|
|
if( Ctxt.Recd || WAIT_FAILED == Result || WAIT_TIMEOUT == Result ) {
|
|
if( WAIT_FAILED == Result ) Result = GetLastError();
|
|
else if (WAIT_TIMEOUT == Result ) Result = ERROR_SEM_TIMEOUT;
|
|
else Result = ERROR_SUCCESS;
|
|
|
|
RemoveEntryList(&Ctxt.RecvList); // remove it from list
|
|
//LeaveCriticalSection(&DhcpGlobalRecvFromCritSect);
|
|
CloseHandle(Ctxt.WaitEvent);
|
|
return Result;
|
|
} else {
|
|
DhcpAssert(WAIT_OBJECT_0 == Result && Ctxt.Recd == FALSE );
|
|
// have not received a packet but have been woken up? must be first in line now..
|
|
//LeaveCriticalSection(&DhcpGlobalRecvFromCritSect);
|
|
Result = ProcessRecvFromSocket(&Ctxt);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
#define RATIO 1
|
|
DWORD
|
|
GetSpecifiedDhcpMessage(
|
|
PDHCP_CONTEXT DhcpContext,
|
|
PDWORD BufferLength,
|
|
DWORD TransactionId,
|
|
DWORD TimeToWait
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function waits TimeToWait seconds to receives the specified
|
|
DHCP response.
|
|
|
|
Arguments:
|
|
|
|
DhcpContext - A pointer to a DHCP context block.
|
|
|
|
BufferLength - Returns the size of the input buffer.
|
|
|
|
TransactionID - A filter. Wait for a message with this TID.
|
|
|
|
TimeToWait - Time, in milli seconds, to wait for the message.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation. If the specified message has been
|
|
been returned, the status is ERROR_TIMEOUT.
|
|
|
|
--*/
|
|
{
|
|
struct sockaddr socketName;
|
|
int socketNameSize = sizeof( socketName );
|
|
struct timeval timeout;
|
|
time_t startTime, now;
|
|
DWORD error;
|
|
DWORD actualTimeToWait;
|
|
SOCKET clientSocket;
|
|
fd_set readSocketSet;
|
|
|
|
if( !IS_ADDRESS_PLUMBED(DhcpContext) ) {
|
|
|
|
// For RAS server Lease API this call won't happen as we don't have to do this nonsense
|
|
|
|
error = GetSpecifiedDhcpMessageEx(
|
|
DhcpContext,
|
|
BufferLength,
|
|
TransactionId,
|
|
TimeToWait
|
|
);
|
|
if( ERROR_SUCCESS == error ) {
|
|
// received a message frm the dhcp server..
|
|
SERVER_REACHED(DhcpContext);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
startTime = time( NULL );
|
|
actualTimeToWait = TimeToWait;
|
|
|
|
|
|
// Setup the file descriptor set for select.
|
|
|
|
|
|
clientSocket = DhcpContext->Socket;
|
|
|
|
FD_ZERO( &readSocketSet );
|
|
FD_SET( clientSocket, &readSocketSet );
|
|
|
|
while ( 1 ) {
|
|
|
|
timeout.tv_sec = actualTimeToWait / RATIO;
|
|
timeout.tv_usec = actualTimeToWait % RATIO;
|
|
DhcpPrint(("Select: waiting for: %ld seconds\n", actualTimeToWait));
|
|
error = _I_select( 0, &readSocketSet, NULL, NULL, &timeout );
|
|
|
|
if ( error == 0 ) {
|
|
|
|
|
|
// Timeout before read data is available.
|
|
|
|
|
|
DhcpPrint(("Recv timed out\n", 0 ));
|
|
error = ERROR_SEM_TIMEOUT;
|
|
break;
|
|
}
|
|
|
|
error = _I_recvfrom(
|
|
clientSocket,
|
|
(PCHAR)DhcpContext->MessageBuffer,
|
|
*BufferLength,
|
|
0,
|
|
&socketName,
|
|
&socketNameSize
|
|
);
|
|
|
|
if ( error == SOCKET_ERROR ) {
|
|
error = _I_WSAGetLastError();
|
|
DhcpPrint(("Recv failed, error = %ld\n", error ));
|
|
|
|
if( WSAECONNRESET != error ) break;
|
|
|
|
|
|
// ignore connreset -- this could be caused by someone sending random ICMP port unreachable.
|
|
|
|
|
|
} else if (DhcpContext->MessageBuffer->TransactionID == TransactionId ) {
|
|
|
|
DhcpPrint(( "Received Message, XID = %lx, MDhcp = %d.\n",
|
|
TransactionId,
|
|
IS_MDHCP_MESSAGE( DhcpContext->MessageBuffer) ));
|
|
|
|
if (((unsigned)IS_MDHCP_MESSAGE( DhcpContext->MessageBuffer) == IS_MDHCP_CTX( DhcpContext))) {
|
|
DhcpDumpMessage(DEBUG_PROTOCOL_DUMP, DhcpContext->MessageBuffer );
|
|
|
|
*BufferLength = error;
|
|
error = NO_ERROR;
|
|
|
|
if( DhcpContext->MessageBuffer->HardwareAddressLength == DhcpContext->HardwareAddressLength
|
|
&& 0 == memcmp(DhcpContext->MessageBuffer->HardwareAddress,
|
|
DhcpContext->HardwareAddress,
|
|
DhcpContext->HardwareAddressLength
|
|
)) {
|
|
|
|
|
|
// Transction IDs match, same type (MDHCP/DHCP), Hardware addresses match!
|
|
|
|
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
DhcpPrint(( "Received a buffer with unknown XID = %lx\n",
|
|
DhcpContext->MessageBuffer->TransactionID ));
|
|
}
|
|
|
|
|
|
// We received a message, but not the one we're interested in.
|
|
// Reset the timeout to reflect elapsed time, and wait for
|
|
// another message.
|
|
|
|
now = time( NULL );
|
|
actualTimeToWait = (DWORD)(TimeToWait - RATIO * (now - startTime));
|
|
if ( (LONG)actualTimeToWait < 0 ) {
|
|
error = ERROR_SEM_TIMEOUT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ERROR_SEM_TIMEOUT != error )
|
|
{
|
|
|
|
// a message was received from a DHCP server. disable IP autoconfiguration.
|
|
|
|
|
|
SERVER_REACHED(DhcpContext);
|
|
}
|
|
|
|
return( error );
|
|
}
|
|
|
|
|
|
DWORD
|
|
QueryWellKnownDnsName(
|
|
IN OUT LPSTR lpszAutoProxyUrl,
|
|
IN DWORD dwAutoProxyUrlLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function walks a list of standard DNS names trying to find
|
|
an entry for "wpad.some-domain-here.org" If it does, it constructs
|
|
an URL that is suitable for use in auto-proxy.
|
|
|
|
Arguments:
|
|
|
|
lpszAutoProxyUrl - Url used to return a successful auto-proxy discover
|
|
|
|
dwAutoProxyUrlLength - length of buffer passed in above
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - if we found a URL/DNS name
|
|
|
|
ERROR_NOT_FOUND - on error
|
|
|
|
revised: joshco 7-oct-1998
|
|
if we dont get a valid domain back, be sure and try
|
|
the netbios name ("wpad") no trailing dot.
|
|
|
|
revised: joshco 7-oct-1998
|
|
use the define PROXY_AUTO_DETECT_PATH instead
|
|
of hardcoding "wpad.dat"
|
|
|
|
--*/
|
|
|
|
{
|
|
#define WORK_BUFFER_SIZE 356
|
|
|
|
DEBUG_ENTER((DBG_SOCKETS,
|
|
Dword,
|
|
"QueryWellKnownDnsName",
|
|
"%x, %u",
|
|
lpszAutoProxyUrl,
|
|
dwAutoProxyUrlLength
|
|
));
|
|
|
|
char szHostDomain[WORK_BUFFER_SIZE];
|
|
char * pszTemp = szHostDomain ;
|
|
char *pszDot1 = NULL;
|
|
char *pszDot2 = NULL;
|
|
DWORD error = ERROR_NOT_FOUND;
|
|
DWORD dwMinDomain = 2; // By default, assume domain is of the form: .domain-name.org
|
|
|
|
lstrcpy(szHostDomain, "wpad.");
|
|
pszTemp += (sizeof("wpad.") - 1);
|
|
|
|
if ( SockGetSingleValue(CONFIG_DOMAIN,
|
|
(LPBYTE)pszTemp,
|
|
WORK_BUFFER_SIZE - sizeof("wpad.")
|
|
) != ERROR_SUCCESS )
|
|
{
|
|
lstrcpy(szHostDomain, "wpad.");
|
|
pszTemp = szHostDomain ;
|
|
pszTemp += (sizeof("wpad.") - 1);
|
|
}
|
|
|
|
if ( (GetProxyDetectType() & PROXY_AUTO_DETECT_TYPE_NO_DOMAIN ) ||
|
|
*pszTemp == '\0' )
|
|
{
|
|
// if the debug setting for no domain (netbios) or
|
|
// we didnt get back a valid domain, then just do the
|
|
// netbios name.
|
|
// XXBUG sockgetsinglevalue returns true even if there is no domain
|
|
|
|
INET_ASSERT(*(pszTemp - 1 ) == '.');
|
|
|
|
*(pszTemp - 1) = '\0';
|
|
}
|
|
|
|
// Now determine which form the domain name follows:
|
|
// domain-name.org
|
|
// domain-name.co.uk
|
|
pszDot1 = &szHostDomain[lstrlen(szHostDomain)-1];
|
|
|
|
while (pszDot1 >= szHostDomain && *pszDot1 != '.')
|
|
pszDot1--;
|
|
|
|
// Only check .?? endings
|
|
if (pszDot1 >= szHostDomain && (pszDot1 + 3 == &szHostDomain[lstrlen(szHostDomain)]) )
|
|
{
|
|
pszDot2 = pszDot1 - 1;
|
|
|
|
while (pszDot2 >= szHostDomain && *pszDot2 != '.')
|
|
pszDot2--;
|
|
|
|
if (pszDot2 >= szHostDomain && pszDot2 + 3 >= pszDot1)
|
|
{
|
|
// Domain ended in something of the form: .co.uk
|
|
// This requires at least 3 pieces then to be considered a domain
|
|
dwMinDomain = 3;
|
|
}
|
|
else if ((pszDot2 + 4) == pszDot1)
|
|
{
|
|
// Check domain endings of the form ending in .com.uk
|
|
// These special 3-letter pieces also need 3 dots to be classified
|
|
// as a domain. Unfortunately, we can't leverage the equivalent
|
|
// code used by cookies because there, the strings are reversed.
|
|
static const char *s_pachSpecialDomains[] = {"COM", "EDU", "NET", "ORG", "GOV", "MIL", "INT" };
|
|
|
|
for (int i=0; i < ARRAY_ELEMENTS(s_pachSpecialDomains); i++)
|
|
{
|
|
if (StrCmpNIC(pszDot2+1, s_pachSpecialDomains[i], 3) == 0)
|
|
{
|
|
dwMinDomain = 3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
PHOSTENT lpHostent = _I_gethostbyname(szHostDomain);
|
|
|
|
if ( lpHostent != NULL )
|
|
{
|
|
// Found a host, extract the IP address and form an URL to use.
|
|
|
|
char *pszAddressStr;
|
|
LPBYTE * addressList;
|
|
struct in_addr sin_addr;
|
|
DWORD dwIPAddressSize;
|
|
|
|
addressList = (LPBYTE *)lpHostent->h_addr_list;
|
|
*(LPDWORD)&sin_addr = *(LPDWORD)addressList[0] ;
|
|
|
|
pszAddressStr = _I_inet_ntoa (sin_addr);
|
|
|
|
INET_ASSERT(pszAddressStr);
|
|
|
|
dwIPAddressSize = lstrlen(pszAddressStr);
|
|
|
|
if ( dwAutoProxyUrlLength < (dwIPAddressSize +
|
|
sizeof("http://") + sizeof(PROXY_AUTO_DETECT_PATH) ) )
|
|
{
|
|
error = ERROR_INSUFFICIENT_BUFFER;
|
|
goto quit;
|
|
}
|
|
|
|
wsprintf(lpszAutoProxyUrl, "http://%s/%s", pszAddressStr, PROXY_AUTO_DETECT_PATH );
|
|
error = ERROR_SUCCESS;
|
|
goto quit;
|
|
}
|
|
else
|
|
{
|
|
// Did not find anything yet, reduce the domain level,
|
|
// and if we're in the root domain stop and return error
|
|
|
|
DWORD dwPeriodCnt = 0, dwNewEndLength = 0;
|
|
LPSTR lpszPeriod1 = NULL, lpszPeriod2 = NULL;
|
|
|
|
for (pszTemp = szHostDomain; *pszTemp; pszTemp++ )
|
|
{
|
|
if ( *pszTemp == '.' ) {
|
|
dwPeriodCnt ++;
|
|
if ( lpszPeriod1 == NULL ) {
|
|
lpszPeriod1 = pszTemp;
|
|
}
|
|
else if ( lpszPeriod2 == NULL ) {
|
|
lpszPeriod2 = pszTemp;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( dwPeriodCnt <= dwMinDomain)
|
|
{
|
|
error = ERROR_NOT_FOUND;
|
|
goto quit;
|
|
}
|
|
|
|
dwNewEndLength = lstrlen(lpszPeriod2);
|
|
MoveMemory(lpszPeriod1, lpszPeriod2, dwNewEndLength);
|
|
*(lpszPeriod1 + dwNewEndLength) = '\0';
|
|
}
|
|
}
|
|
|
|
quit:
|
|
DEBUG_LEAVE(error);
|
|
return error;
|
|
}
|
|
|
|
// End of file
|