/*++ Copyright (c) 1994 Microsoft Corporation Module Name: mdhccapi.c Abstract: This file contains the client side APIs for the Madcap. Author: Munil Shah (munils) 02-Sept-97 Environment: User Mode - Win32 Revision History: --*/ #include "precomp.h" #include "dhcpglobal.h" #include #include #define MADCAP_DATA_ALLOCATE #include "mdhcpcli.h" #include // // constants // #define Madcap_ADAPTER_NAME L"Madcap Adapter" #define MadcapMiscPrint( Msg ) DhcpPrint(( DEBUG_MISC, ( Msg ) )) static LONG Initialized = 0; WSADATA MadcapGlobalWsaData; DWORD MadcapInitGlobalData( VOID ) /*++ Routine Description: This routine initializes data required for Multicast APIs to work correctly. This has to be called exactly once (and this is ensured by calling it in DLL init in dhcp.c ) Return Value: This function returns a Win32 status. --*/ { DWORD Error; LOCK_MSCOPE_LIST(); do { if( Initialized > 0 ) { Initialized ++; Error = NO_ERROR; break; } gMadcapScopeList = NULL; gMScopeQueryInProgress = FALSE; gMScopeQueryEvent = CreateEvent( NULL, // no security. TRUE, // manual reset. FALSE, // initial state is not-signaled. NULL ); // no name. if( gMScopeQueryEvent == NULL ) { Error = GetLastError(); break; } Error = WSAStartup( 0x0101, &MadcapGlobalWsaData ); if( ERROR_SUCCESS != Error ) { CloseHandle(gMScopeQueryEvent); gMScopeQueryEvent = NULL; break; } Initialized ++; Error = NO_ERROR; } while ( 0 ); UNLOCK_MSCOPE_LIST(); return Error; } VOID MadcapCleanupGlobalData( VOID ) /*++ Routine Description: This routine cleans up any resources allocated in MadcapInitGlobalData. This can be called even if the InitData routine fails.. Return Value: Nothing --*/ { LOCK_MSCOPE_LIST(); do { DhcpAssert(Initialized >= 0); if( Initialized <= 0 ) break; Initialized --; if( 0 != Initialized ) break; gMadcapScopeList = NULL; gMScopeQueryInProgress = FALSE; if( NULL != gMScopeQueryEvent ) { CloseHandle(gMScopeQueryEvent); gMScopeQueryEvent = NULL; } WSACleanup(); } while ( 0 ); UNLOCK_MSCOPE_LIST(); } BOOL ShouldRequeryMScopeList() /*++ Routine Description: This routine checks if the multicast scope list can be queried or not. * If there is already a query in progress, then this routine waits for that to complete and then returns FALSE. * If there is no query in progress, it returns TRUE. Arguments: Return Value: The status of the operation. --*/ { LOCK_MSCOPE_LIST(); if ( gMScopeQueryInProgress ) { DWORD result; DhcpPrint((DEBUG_API, "MScopeQuery is in progress - waiting\n")); // make sure the event is not in signalled state from before. ResetEvent( gMScopeQueryEvent ); UNLOCK_MSCOPE_LIST(); switch( result = WaitForSingleObject( gMScopeQueryEvent, INFINITE ) ) { case WAIT_OBJECT_0: // it's signled now, no need to requery the list, just take the result from previous query. DhcpPrint((DEBUG_API, "MScopeQuery event signalled\n")); return FALSE; case WAIT_ABANDONED: DhcpPrint((DEBUG_ERRORS, "WaitForSingleObject - thread died before the wait completed\n")); return TRUE; case WAIT_FAILED: DhcpPrint((DEBUG_ERRORS, "WaitForSingleObject failed - %lx\n",GetLastError())); DhcpAssert(FALSE); default: DhcpPrint((DEBUG_ERRORS, "WaitForSingleObject returned unknown value - %lx\n", result)); DhcpAssert(FALSE); return TRUE; } } else { gMScopeQueryInProgress = TRUE; UNLOCK_MSCOPE_LIST(); return TRUE; } } DWORD InitializeMadcapSocket( 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. Return Value: The status of the operation. --*/ { DWORD error; DWORD closeError; DWORD value; struct sockaddr_in socketName; DWORD i; SOCKET sock; // // Sockets initialization // sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( sock == INVALID_SOCKET ) { error = WSAGetLastError(); DhcpPrint(( DEBUG_ERRORS, "socket failed, error = %ld\n", error )); return( error ); } // // Make the socket share-able // value = 1; error = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char FAR *)&value, sizeof(value) ); if ( error != 0 ) { error = WSAGetLastError(); DhcpPrint((DEBUG_ERRORS, "setsockopt failed, err = %ld\n", error )); closeError = closesocket( sock ); if ( closeError != 0 ) { DhcpPrint((DEBUG_ERRORS, "closesocket failed, err = %d\n", closeError )); } return( error ); } socketName.sin_family = PF_INET; socketName.sin_port = 0; // let the winsock pick a port for us. 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 = bind( sock, (struct sockaddr FAR *)&socketName, sizeof( socketName ) ); if ( error != 0 ) { error = WSAGetLastError(); DhcpPrint((DEBUG_ERRORS, "bind failed, err = %ld\n", error )); closeError = closesocket( sock ); if ( closeError != 0 ) { DhcpPrint((DEBUG_ERRORS, "closesocket failed, err = %d\n", closeError )); } return( error ); } // set the multicast IF to be the one on which we are doing Madcap if (INADDR_ANY != IpAddress) { value = IpAddress; DhcpPrint((DEBUG_ERRORS, "setsockopt: IP_MULTICAST_IF, if = %lx\n", IpAddress )); error = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (char FAR *)&value, sizeof(value) ); if ( error != 0 ) { error = WSAGetLastError(); DhcpPrint((DEBUG_ERRORS, "setsockopt failed, err = %ld\n", error )); closeError = closesocket( sock ); if ( closeError != 0 ) { DhcpPrint((DEBUG_ERRORS, "closesocket failed, err = %d\n", closeError )); } return( error ); } } *Socket = sock; return( NO_ERROR ); } DWORD ReInitializeMadcapSocket( SOCKET *Socket, DHCP_IP_ADDRESS IpAddress ) /*++ Routine Description: This function closes and reinitializes the socket to specified IP address. Arguments: Socket - Returns a pointer to the initialized socket. IpAddress - The IP address to bind the socket to. Return Value: The status of the operation. --*/ { DWORD Error; if (*Socket != INVALID_SOCKET) { Error = closesocket( *Socket ); if ( Error != 0 ) { DhcpPrint((DEBUG_ERRORS, "closesocket failed, err = %d\n", Error )); return Error; } } return InitializeMadcapSocket( Socket, IpAddress ); } DWORD OpenMadcapSocket( PDHCP_CONTEXT DhcpContext ) { DWORD Error; PLOCAL_CONTEXT_INFO localInfo; struct sockaddr_in socketName; int sockAddrLen; localInfo = DhcpContext->LocalInformation; if ( INVALID_SOCKET == localInfo->Socket ) { Error = InitializeMadcapSocket(&localInfo->Socket, DhcpContext->IpAddress); if( Error != ERROR_SUCCESS ) { localInfo->Socket = INVALID_SOCKET; DhcpPrint(( DEBUG_ERRORS, " Socket Open failed, %ld\n", Error )); return Error; } } // find out which port we are bound to. sockAddrLen = sizeof(struct sockaddr_in); Error = getsockname( localInfo->Socket , (struct sockaddr FAR *)&socketName, &sockAddrLen ); if ( Error != 0 ) { DWORD closeError; Error = WSAGetLastError(); DhcpPrint((DEBUG_ERRORS, "bind failed, err = %ld\n", Error )); closeError = closesocket( localInfo->Socket ); if ( closeError != 0 ) { DhcpPrint((DEBUG_ERRORS, "closesocket failed, err = %d\n", closeError )); } return( Error ); } return(Error); } DWORD CreateMadcapContext( IN OUT PDHCP_CONTEXT *ppContext, IN LPMCAST_CLIENT_UID pRequestID, IN DHCP_IP_ADDRESS IpAddress ) /*++ Routine Description: This routine creates a dummy context for doing Madcap operation on it. Arguments: ppContext - pointer to where context pointer is to be stored. pRequestID - The client id to be used in the context. IpAddress - The ipaddress the context is initialized with. Return Value: The status of the operation. --*/ { DWORD Error; PDHCP_CONTEXT DhcpContext = NULL; ULONG DhcpContextSize; PLOCAL_CONTEXT_INFO LocalInfo = NULL; LPVOID Ptr; LPDHCP_LEASE_INFO LocalLeaseInfo = NULL; time_t LeaseObtained; DWORD T1, T2, Lease; DWORD AdapterNameLen; // // prepare dhcp context structure. // DhcpContextSize = ROUND_UP_COUNT(sizeof(DHCP_CONTEXT), ALIGN_WORST) + ROUND_UP_COUNT(pRequestID->ClientUIDLength, ALIGN_WORST) + ROUND_UP_COUNT(sizeof(LOCAL_CONTEXT_INFO), ALIGN_WORST) + ROUND_UP_COUNT(DHCP_RECV_MESSAGE_SIZE, ALIGN_WORST); Ptr = DhcpAllocateMemory( DhcpContextSize ); if ( Ptr == NULL ) { return( ERROR_NOT_ENOUGH_MEMORY ); } RtlZeroMemory( Ptr, DhcpContextSize ); // // make sure the pointers are aligned. // DhcpContext = Ptr; Ptr = ROUND_UP_POINTER( (LPBYTE)Ptr + sizeof(DHCP_CONTEXT), ALIGN_WORST); DhcpContext->ClientIdentifier.pbID = Ptr; Ptr = ROUND_UP_POINTER( (LPBYTE)Ptr + pRequestID->ClientUIDLength, ALIGN_WORST); DhcpContext->LocalInformation = Ptr; LocalInfo = Ptr; Ptr = ROUND_UP_POINTER( (LPBYTE)Ptr + sizeof(LOCAL_CONTEXT_INFO), ALIGN_WORST); DhcpContext->MadcapMessageBuffer = Ptr; // // initialize fields. // DhcpContext->ClientIdentifier.fSpecified = TRUE; DhcpContext->ClientIdentifier.bType = HARDWARE_TYPE_NONE; DhcpContext->ClientIdentifier.cbID = pRequestID->ClientUIDLength; RtlCopyMemory( DhcpContext->ClientIdentifier.pbID, pRequestID->ClientUID, pRequestID->ClientUIDLength ); DhcpContext->IpAddress = IpAddress; DhcpContext->SubnetMask = DhcpDefaultSubnetMask(0); DhcpContext->DhcpServerAddress = MADCAP_SERVER_IP_ADDRESS; DhcpContext->DesiredIpAddress = 0; SET_MDHCP_STATE(DhcpContext); InitializeListHead(&DhcpContext->RenewalListEntry); InitializeListHead(&DhcpContext->SendOptionsList); InitializeListHead(&DhcpContext->RecdOptionsList); InitializeListHead(&DhcpContext->FbOptionsList); InitializeListHead(&DhcpContext->NicListEntry); DhcpContext->DontPingGatewayFlag = TRUE; // // copy local info. // // // unused portion of the local info. // LocalInfo->IpInterfaceContext = 0xFFFFFFFF; LocalInfo->IpInterfaceInstance = 0xFFFFFFFF; LocalInfo->AdapterName = NULL; LocalInfo->NetBTDeviceName= NULL; LocalInfo->RegistryKey= NULL; LocalInfo->Socket = INVALID_SOCKET; LocalInfo->DefaultGatewaysSet = FALSE; // // used portion of the local info. // LocalInfo->Socket = INVALID_SOCKET; // // open socket now. receive any. // Error = InitializeMadcapSocket(&LocalInfo->Socket,DhcpContext->IpAddress); if( Error != ERROR_SUCCESS ) { DhcpFreeMemory( DhcpContext ); return Error; } else { *ppContext = DhcpContext; return Error; } } DWORD SendMadcapMessage( 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->MadcapMessageBuffer->TransactionID = *TransactionId; // // Initialize the outgoing address. // socketName.sin_family = PF_INET; socketName.sin_port = htons( MADCAP_SERVER_PORT); 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 (setsockopt( ((PLOCAL_CONTEXT_INFO)DhcpContext->LocalInformation)->Socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&TTL, sizeof((int)TTL)) == SOCKET_ERROR){ error = WSAGetLastError(); DhcpPrint((DEBUG_ERRORS,"could not set MCast TTL %ld\n",error )); return error; } } for ( i = 0; i < 8 ; i++ ) { socketName.sin_zero[i] = 0; } error = sendto( ((PLOCAL_CONTEXT_INFO) DhcpContext->LocalInformation)->Socket, (PCHAR)DhcpContext->MadcapMessageBuffer, MessageLength, 0, (struct sockaddr *)&socketName, sizeof( struct sockaddr ) ); if ( error == SOCKET_ERROR ) { error = WSAGetLastError(); DhcpPrint(( DEBUG_ERRORS, "Send failed, error = %ld\n", error )); } else { IF_DEBUG( PROTOCOL ) { DhcpPrint(( DEBUG_PROTOCOL, "Sent message to %s: \n", inet_ntoa( socketName.sin_addr ))); } MadcapDumpMessage( DEBUG_PROTOCOL_DUMP, DhcpContext->MadcapMessageBuffer, DHCP_MESSAGE_SIZE ); error = NO_ERROR; } return( error ); } WIDE_OPTION UNALIGNED * // ptr to add additional options FormatMadcapCommonMessage( // format the packet for an INFORM IN PDHCP_CONTEXT DhcpContext, // format for this context IN BYTE MessageType ) { DWORD size; DWORD Error; WIDE_OPTION UNALIGNED *option; LPBYTE OptionEnd; PMADCAP_MESSAGE dhcpMessage; dhcpMessage = DhcpContext->MadcapMessageBuffer; RtlZeroMemory( dhcpMessage, DHCP_SEND_MESSAGE_SIZE ); dhcpMessage->Version = MADCAP_VERSION; dhcpMessage->MessageType = MessageType; dhcpMessage->AddressFamily = htons(MADCAP_ADDR_FAMILY_V4); option = &dhcpMessage->Option; OptionEnd = (LPBYTE)dhcpMessage + DHCP_SEND_MESSAGE_SIZE; option = AppendWideOption( // ==> use this client id as option option, MADCAP_OPTION_LEASE_ID, DhcpContext->ClientIdentifier.pbID, (WORD)DhcpContext->ClientIdentifier.cbID, OptionEnd ); return( option ); } DWORD // status SendMadcapInform( // 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; WIDE_OPTION UNALIGNED * option; LPBYTE OptionEnd; WORD OptVal[] = { // for now we just need this one option. htons(MADCAP_OPTION_MCAST_SCOPE_LIST) // multicast scope list. }; option = FormatMadcapCommonMessage(DhcpContext, MADCAP_INFORM_MESSAGE); OptionEnd = (LPBYTE)(DhcpContext->MadcapMessageBuffer) + DHCP_SEND_MESSAGE_SIZE; option = AppendWideOption( option, MADCAP_OPTION_REQUEST_LIST, OptVal, sizeof (OptVal), OptionEnd ); option = AppendWideOption( option, MADCAP_OPTION_END, NULL, 0, OptionEnd ); size = (DWORD)((PBYTE)option - (PBYTE)DhcpContext->MadcapMessageBuffer); return SendMadcapMessage( // finally send the message and return DhcpContext, size, pdwXid ); } DWORD // status SendMadcapDiscover( // send an inform packet after filling required options IN PDHCP_CONTEXT DhcpContext, // sned out for this context IN PIPNG_ADDRESS pScopeID, IN PMCAST_LEASE_REQUEST pAddrRequest, IN OUT DWORD *pdwXid // use this Xid (if zero fill something and return it) ) { DWORD size; WIDE_OPTION UNALIGNED * option; LPBYTE OptionEnd; option = FormatMadcapCommonMessage(DhcpContext, MADCAP_DISCOVER_MESSAGE); OptionEnd = (LPBYTE)(DhcpContext->MadcapMessageBuffer) + DHCP_SEND_MESSAGE_SIZE; DhcpAssert(pScopeID); option = AppendWideOption( option, MADCAP_OPTION_MCAST_SCOPE, (LPBYTE)&pScopeID->IpAddrV4, sizeof (pScopeID->IpAddrV4), OptionEnd ); if (pAddrRequest->LeaseDuration) { DWORD Lease = htonl(pAddrRequest->LeaseDuration); option = AppendWideOption( option, MADCAP_OPTION_LEASE_TIME, (LPBYTE)&Lease, sizeof (Lease), OptionEnd ); } if( pAddrRequest->MinLeaseDuration ) { DWORD MinLease = htonl(pAddrRequest->MinLeaseDuration); option = AppendWideOption( option, MADCAP_OPTION_MIN_LEASE_TIME, (LPBYTE)&MinLease, sizeof(MinLease), OptionEnd ); } if( pAddrRequest->MaxLeaseStartTime ) { DWORD TimeNow = htonl((DWORD)time(NULL)); DWORD StartTime = htonl(pAddrRequest->MaxLeaseStartTime); option = AppendWideOption( option, MADCAP_OPTION_MAX_START_TIME, (LPBYTE)&StartTime, sizeof (StartTime), OptionEnd ); if( !(pAddrRequest->LeaseStartTime) ) { // // if lease start time specified, then current time // option will be added at a later point // option = AppendWideOption( option, MADCAP_OPTION_TIME, (LPBYTE)&TimeNow, sizeof (TimeNow), OptionEnd ); } } if (pAddrRequest->LeaseStartTime) { DWORD TimeNow = htonl((DWORD)time(NULL)); DWORD StartTime = htonl(pAddrRequest->LeaseStartTime); option = AppendWideOption( option, MADCAP_OPTION_START_TIME, (LPBYTE)&StartTime, sizeof (StartTime), OptionEnd ); option = AppendWideOption( option, MADCAP_OPTION_TIME, (LPBYTE)&TimeNow, sizeof (TimeNow), OptionEnd ); } option = AppendWideOption( option, MADCAP_OPTION_END, NULL, 0, OptionEnd ); size = (DWORD)((PBYTE)option - (PBYTE)DhcpContext->MadcapMessageBuffer); return SendMadcapMessage( // finally send the message and return DhcpContext, size, pdwXid ); } DWORD // status SendMadcapRequest( // IN PDHCP_CONTEXT DhcpContext, // sned out for this context IN PIPNG_ADDRESS pScopeID, IN PMCAST_LEASE_REQUEST pAddrRequest, IN DWORD SelectedServer, // is there a prefernce for a server? IN OUT DWORD *pdwXid // use this Xid (if zero fill something and return it) ) { DWORD size; WIDE_OPTION UNALIGNED * option; LPBYTE OptionEnd; BYTE ServerId[6]; WORD AddrFamily = htons(MADCAP_ADDR_FAMILY_V4); option = FormatMadcapCommonMessage(DhcpContext, MADCAP_REQUEST_MESSAGE); OptionEnd = (LPBYTE)(DhcpContext->MadcapMessageBuffer) + DHCP_SEND_MESSAGE_SIZE; option = AppendMadcapAddressList( option, (DWORD UNALIGNED *)pAddrRequest->pAddrBuf, pAddrRequest->AddrCount, OptionEnd ); option = AppendWideOption( option, MADCAP_OPTION_MCAST_SCOPE, (LPBYTE)&pScopeID->IpAddrV4, sizeof (pScopeID->IpAddrV4), OptionEnd ); if (pAddrRequest->LeaseDuration) { DWORD TimeNow = (DWORD)time(NULL); DWORD Lease = htonl(pAddrRequest->LeaseDuration); option = AppendWideOption( option, MADCAP_OPTION_LEASE_TIME, (LPBYTE)&Lease, sizeof (Lease), OptionEnd ); } if( pAddrRequest->MinLeaseDuration ) { DWORD MinLease = htonl(pAddrRequest->MinLeaseDuration); option = AppendWideOption( option, MADCAP_OPTION_MIN_LEASE_TIME, (LPBYTE)&MinLease, sizeof(MinLease), OptionEnd ); } if( pAddrRequest->MaxLeaseStartTime ) { DWORD TimeNow = htonl((DWORD)time(NULL)); DWORD StartTime = htonl(pAddrRequest->MaxLeaseStartTime); option = AppendWideOption( option, MADCAP_OPTION_MAX_START_TIME, (LPBYTE)&StartTime, sizeof (StartTime), OptionEnd ); if( !(pAddrRequest->LeaseStartTime) ) { // // if lease start time specified, then current time // option will be added at a later point // option = AppendWideOption( option, MADCAP_OPTION_TIME, (LPBYTE)&TimeNow, sizeof (TimeNow), OptionEnd ); } } if (pAddrRequest->LeaseStartTime) { DWORD TimeNow = htonl((DWORD)time(NULL)); DWORD StartTime = htonl(pAddrRequest->LeaseStartTime); option = AppendWideOption( option, MADCAP_OPTION_START_TIME, (LPBYTE)&StartTime, sizeof (StartTime), OptionEnd ); option = AppendWideOption( option, MADCAP_OPTION_TIME, (LPBYTE)&TimeNow, sizeof (TimeNow), OptionEnd ); } memcpy(ServerId, &AddrFamily, 2); memcpy(ServerId + 2, &SelectedServer, 4); option = AppendWideOption( option, // append this option to talk to that server alone MADCAP_OPTION_SERVER_ID, (LPBYTE)&ServerId, sizeof( ServerId ), OptionEnd ); option = AppendWideOption( option, MADCAP_OPTION_END, NULL, 0, OptionEnd ); size = (DWORD)((PBYTE)option - (PBYTE)DhcpContext->MadcapMessageBuffer); return SendMadcapMessage( // finally send the message and return DhcpContext, size, pdwXid ); } DWORD // status SendMadcapRenew( // send an inform packet after filling required options IN PDHCP_CONTEXT DhcpContext, // sned out for this context IN PMCAST_LEASE_REQUEST pAddrRequest, IN OUT DWORD *pdwXid // use this Xid (if zero fill something and return it) ) { DWORD size; WIDE_OPTION UNALIGNED * option; LPBYTE OptionEnd; option = FormatMadcapCommonMessage(DhcpContext, MADCAP_RENEW_MESSAGE); OptionEnd = (LPBYTE)(DhcpContext->MadcapMessageBuffer) + DHCP_SEND_MESSAGE_SIZE; if (pAddrRequest->LeaseDuration) { DWORD Lease = htonl(pAddrRequest->LeaseDuration); option = AppendWideOption( option, MADCAP_OPTION_LEASE_TIME, (LPBYTE)&Lease, sizeof (Lease), OptionEnd ); } if( pAddrRequest->MinLeaseDuration ) { DWORD MinLease = htonl(pAddrRequest->MinLeaseDuration); option = AppendWideOption( option, MADCAP_OPTION_MIN_LEASE_TIME, (LPBYTE)&MinLease, sizeof(MinLease), OptionEnd ); } if( pAddrRequest->MaxLeaseStartTime ) { DWORD TimeNow = htonl((DWORD)time(NULL)); DWORD StartTime = htonl(pAddrRequest->MaxLeaseStartTime); option = AppendWideOption( option, MADCAP_OPTION_MAX_START_TIME, (LPBYTE)&StartTime, sizeof (StartTime), OptionEnd ); if( !(pAddrRequest->LeaseStartTime) ) { // // if lease start time specified, then current time // option will be added at a later point // option = AppendWideOption( option, MADCAP_OPTION_TIME, (LPBYTE)&TimeNow, sizeof (TimeNow), OptionEnd ); } } if (pAddrRequest->LeaseStartTime) { DWORD TimeNow = htonl((DWORD)time(NULL)); DWORD StartTime = htonl(pAddrRequest->LeaseStartTime); option = AppendWideOption( option, MADCAP_OPTION_START_TIME, (LPBYTE)&StartTime, sizeof (StartTime), OptionEnd ); option = AppendWideOption( option, MADCAP_OPTION_TIME, (LPBYTE)&TimeNow, sizeof (TimeNow), OptionEnd ); } option = AppendWideOption( option, MADCAP_OPTION_END, NULL, 0, OptionEnd ); size = (DWORD)((PBYTE)option - (PBYTE)DhcpContext->MadcapMessageBuffer); return SendMadcapMessage( // finally send the message and return DhcpContext, size, pdwXid ); } DWORD // status SendMadcapRelease( // 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; WIDE_OPTION UNALIGNED * option; LPBYTE OptionEnd; option = FormatMadcapCommonMessage(DhcpContext, MADCAP_RELEASE_MESSAGE); OptionEnd = (LPBYTE)(DhcpContext->MadcapMessageBuffer) + DHCP_SEND_MESSAGE_SIZE; option = AppendWideOption( option, MADCAP_OPTION_END, NULL, 0, OptionEnd ); size = (DWORD)((PBYTE)option - (PBYTE)DhcpContext->MadcapMessageBuffer); return SendMadcapMessage( // finally send the message and return DhcpContext, size, pdwXid ); } #define RATIO 1 DWORD GetSpecifiedMadcapMessage( 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; time_t actualTimeToWait; SOCKET clientSocket; fd_set readSocketSet; PMADCAP_MESSAGE MadcapMessage; startTime = time( NULL ); actualTimeToWait = TimeToWait; // // Setup the file descriptor set for select. // clientSocket = ((PLOCAL_CONTEXT_INFO)DhcpContext->LocalInformation)->Socket; MadcapMessage = DhcpContext->MadcapMessageBuffer; FD_ZERO( &readSocketSet ); FD_SET( clientSocket, &readSocketSet ); while ( 1 ) { timeout.tv_sec = (long)(actualTimeToWait / RATIO); timeout.tv_usec = (long)(actualTimeToWait % RATIO); DhcpPrint((DEBUG_TRACE, "Select: waiting for: %ld seconds\n", actualTimeToWait)); error = select( 0, &readSocketSet, NULL, NULL, &timeout ); if ( error == 0 ) { // // Timeout before read data is available. // DhcpPrint(( DEBUG_ERRORS, "Recv timed out\n", 0 )); error = ERROR_TIMEOUT; break; } error = recvfrom( clientSocket, (PCHAR)MadcapMessage, *BufferLength, 0, &socketName, &socketNameSize ); if ( error == SOCKET_ERROR ) { error = WSAGetLastError(); DhcpPrint(( DEBUG_ERRORS, "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 (error <= MADCAP_MESSAGE_FIXED_PART_SIZE) { DhcpPrint(( DEBUG_PROTOCOL, "Received a too short madcap message, length = %lx\n", error )); } else if (MadcapMessage->TransactionID == TransactionId ) { DhcpPrint(( DEBUG_PROTOCOL, "Received Message, XID = %lx\n", TransactionId)); // just sanity check the remaining fields if ( MADCAP_VERSION == MadcapMessage->Version && MADCAP_ADDR_FAMILY_V4 == ntohs(MadcapMessage->AddressFamily)) { MadcapDumpMessage( DEBUG_PROTOCOL_DUMP, MadcapMessage, DHCP_RECV_MESSAGE_SIZE ); *BufferLength = error; error = NO_ERROR; break; } } else { DhcpPrint(( DEBUG_PROTOCOL, "Received a buffer with unknown XID = %lx\n", MadcapMessage->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 = TimeToWait - RATIO * (now - startTime); if ( (LONG)actualTimeToWait < 0 ) { error = ERROR_TIMEOUT; break; } } return( error ); } //-------------------------------------------------------------------------------- // This function decides if multicast offer is to be accepted or not. //-------------------------------------------------------------------------------- BOOL AcceptMadcapMsg( IN DWORD MessageType, // message type to which this response came IN PDHCP_CONTEXT DhcpContext, // The context of the adapter.. IN PMADCAP_OPTIONS MadcapOptions, // rcvd options. IN DHCP_IP_ADDRESS SelectedServer, // the server which we care about. OUT DWORD *Error // additional fatal error. ) { PMADCAP_MESSAGE MadcapMessage; *Error = ERROR_SUCCESS; MadcapMessage = DhcpContext->MadcapMessageBuffer; if ( !MadcapOptions->ServerIdentifier ){ DhcpPrint((DEBUG_ERRORS, "Received no server identifier, dropping response\n")); return FALSE; } if ( !MadcapOptions->ClientGuid ){ DhcpPrint((DEBUG_ERRORS, "Received no client identifier, dropping response\n")); return FALSE; } if (DhcpContext->ClientIdentifier.cbID != MadcapOptions->ClientGuidLength || 0 != memcmp(DhcpContext->ClientIdentifier.pbID, MadcapOptions->ClientGuid, MadcapOptions->ClientGuidLength) ) { return FALSE; } if (MadcapOptions->MCastLeaseStartTime && !MadcapOptions->Time) { DhcpPrint((DEBUG_ERRORS, "Received start time but no current time\n")); return FALSE; } switch( MessageType ) { case MADCAP_INFORM_MESSAGE: if (MADCAP_ACK_MESSAGE != MadcapMessage->MessageType) { return FALSE; } break; case MADCAP_DISCOVER_MESSAGE: if (MADCAP_OFFER_MESSAGE != MadcapMessage->MessageType) { return FALSE; } if (!MadcapOptions->AddrRangeList) { return FALSE; } if (!MadcapOptions->LeaseTime) { return FALSE; } if (!MadcapOptions->McastScope) { return FALSE; } break; case MADCAP_RENEW_MESSAGE: case MADCAP_REQUEST_MESSAGE: if (MADCAP_NACK_MESSAGE == MadcapMessage->MessageType && SelectedServer == *MadcapOptions->ServerIdentifier) { DhcpPrint((DEBUG_ERRORS, "Received NACK\n")); *Error = ERROR_ACCESS_DENIED; return FALSE; } if (MADCAP_ACK_MESSAGE != MadcapMessage->MessageType) { return FALSE; } if (SelectedServer && SelectedServer != *MadcapOptions->ServerIdentifier) { return FALSE; } if (!MadcapOptions->LeaseTime) { return FALSE; } if (!MadcapOptions->AddrRangeList) { return FALSE; } if (!MadcapOptions->McastScope) { return FALSE; } break; case MADCAP_RELEASE_MESSAGE: if (MADCAP_ACK_MESSAGE != MadcapMessage->MessageType) { return FALSE; } break; default: DhcpAssert( FALSE ); DhcpPrint(( DEBUG_PROTOCOL, "Received Unknown Message.\n")); return FALSE; } // Is this really necessary? if (MadcapOptions->Error) { return FALSE; } return TRUE; // accept this message. } VOID MadcapExtractOptions( // 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 OUT PMADCAP_OPTIONS MadcapOptions, // this is where the options would be stored IN OUT PLIST_ENTRY RecdOptions, // if !LiteOnly this gets filled with all incoming options IN DWORD ServerId // if !LiteOnly this specifies the server which gave this ) { WIDE_OPTION UNALIGNED* NextOpt; BYTE UNALIGNED* EndOpt; WORD Size; DWORD OptionType; DWORD Error; WORD AddrFamily; EndOpt = OptStart + MessageSize; // all options should be < EndOpt; RtlZeroMemory((LPBYTE)MadcapOptions, sizeof(*MadcapOptions)); if( 0 == MessageSize ) goto DropPkt; // nothing to do in this case NextOpt = (WIDE_OPTION UNALIGNED*)OptStart; while( NextOpt->OptionValue <= EndOpt && MADCAP_OPTION_END != (OptionType = ntohs(NextOpt->OptionType)) ) { Size = ntohs(NextOpt->OptionLength); if ((NextOpt->OptionValue + Size) > EndOpt) { goto DropPkt; } switch( OptionType ) { case MADCAP_OPTION_LEASE_TIME: if( Size != sizeof(DWORD) ) goto DropPkt; MadcapOptions->LeaseTime = (DWORD UNALIGNED *)NextOpt->OptionValue; break; case MADCAP_OPTION_SERVER_ID: if (Size != 6) goto DropPkt; AddrFamily = ntohs(*(WORD UNALIGNED *)NextOpt->OptionValue); if ( MADCAP_ADDR_FAMILY_V4 != AddrFamily ) goto DropPkt; MadcapOptions->ServerIdentifier = (DHCP_IP_ADDRESS UNALIGNED *)(NextOpt->OptionValue+2); break; case MADCAP_OPTION_LEASE_ID: if( 0 == Size ) goto DropPkt; MadcapOptions->ClientGuidLength = Size; MadcapOptions->ClientGuid = NextOpt->OptionValue; break; case MADCAP_OPTION_MCAST_SCOPE: if( Size != sizeof(DWORD) ) goto DropPkt; MadcapOptions->McastScope = (DWORD UNALIGNED *)NextOpt->OptionValue; break; case MADCAP_OPTION_START_TIME: if ( Size != sizeof(DATE_TIME) ) goto DropPkt; MadcapOptions->MCastLeaseStartTime = (DWORD UNALIGNED *)NextOpt->OptionValue; break; case MADCAP_OPTION_ADDR_LIST: if( Size % 6 ) goto DropPkt; MadcapOptions->AddrRangeList = NextOpt->OptionValue; MadcapOptions->AddrRangeListSize = Size; break; case MADCAP_OPTION_TIME: if( Size != sizeof(DWORD) ) goto DropPkt; MadcapOptions->Time = (DWORD UNALIGNED *)NextOpt->OptionValue; break; case MADCAP_OPTION_FEATURE_LIST: break; case MADCAP_OPTION_RETRY_TIME: if( Size != sizeof(DWORD) ) goto DropPkt; MadcapOptions->RetryTime = (DWORD UNALIGNED *)NextOpt->OptionValue; break; case MADCAP_OPTION_ERROR: if( Size != sizeof(DWORD) ) goto DropPkt; MadcapOptions->Error = (DWORD UNALIGNED *)NextOpt->OptionValue; break; default: // unknowm message, nothing to do.. especially dont log this break; } if (RecdOptions) { DhcpAssert(ServerId); Error = MadcapAddIncomingOption( // Now add this option to the list RecdOptions, OptionType, ServerId, NextOpt->OptionValue, Size, (DWORD)INFINIT_TIME ); if (ERROR_SUCCESS != Error) { goto DropPkt; } } NextOpt = (WIDE_OPTION UNALIGNED*)(NextOpt->OptionValue + Size); } // while NextOpt < EndOpt return; DropPkt: RtlZeroMemory(MadcapOptions, sizeof(MadcapOptions)); if(RecdOptions) DhcpFreeAllOptions(RecdOptions);// ok undo the options that we just added } DWORD MadcapDoInform( IN PDHCP_CONTEXT DhcpContext ) /*++ Routine Description: This routine does the inform part by sending inform messages and collecting responses etc. on given context. In case of no-response, no error is returned as a timeout is not considered an error. Arguments: DhcpContext -- context to dhcp struct fBroadcast -- should the inform be broadcast or unicast? Return Values: Win32 errors --*/ { time_t StartTime; time_t TimeNow; time_t TimeToWait; DWORD Error; DWORD Xid; DWORD MessageSize; DWORD RoundNum; DWORD MessageCount; DWORD LeaseExpirationTime; MADCAP_OPTIONS MadcapOptions; BOOL GotAck; #define MIN_ACKS_FOR_INFORM MADCAP_QUERY_SCOPE_LIST_RETRIES DWORD MadcapServers[MIN_ACKS_FOR_INFORM]; DhcpPrint((DEBUG_PROTOCOL, "MadcapDoInform entered\n")); Xid = 0; // Will be generated by first SendDhcpPacket MessageCount = 0; // total # of messages we have got TimeToWait = MADCAP_QUERY_SCOPE_LIST_TIME * 1000; TimeToWait += ((rand() * ((DWORD) 1000))/RAND_MAX); TimeToWait /= 1000; for( RoundNum = 0; RoundNum <= MADCAP_QUERY_SCOPE_LIST_RETRIES; RoundNum ++ ) { if( RoundNum != MADCAP_QUERY_SCOPE_LIST_RETRIES ) { Error = SendMadcapInform(DhcpContext, &Xid); if( ERROR_SUCCESS != Error ) { DhcpPrint((DEBUG_ERRORS, "SendMadcapInform: %ld\n", Error)); goto Cleanup; } else { DhcpPrint((DEBUG_PROTOCOL, "Sent DhcpInform\n")); } } StartTime = time(NULL); while ( TRUE ) { // wiat for the specified wait time MessageSize = DHCP_RECV_MESSAGE_SIZE; DhcpPrint((DEBUG_TRACE, "Waiting for ACK[Xid=%x]: %ld seconds\n",Xid, TimeToWait)); Error = GetSpecifiedMadcapMessage( // try to receive an ACK DhcpContext, &MessageSize, Xid, (DWORD)TimeToWait ); if ( Error == ERROR_TIMEOUT ) break; if( Error != ERROR_SUCCESS ) { DhcpPrint((DEBUG_ERRORS, "GetSpecifiedMadcapMessage: %ld\n", Error)); goto Cleanup; } MadcapExtractOptions( // Need to see if this is an ACK DhcpContext, (LPBYTE)&DhcpContext->MadcapMessageBuffer->Option, MessageSize - MADCAP_MESSAGE_FIXED_PART_SIZE, &MadcapOptions, // check for only expected options NULL, // unused 0 // unused ); GotAck = AcceptMadcapMsg( // check up and see if we find this offer kosher MADCAP_INFORM_MESSAGE, DhcpContext, &MadcapOptions, 0, &Error ); if (GotAck) { ULONG i; for( i = 0; i < MessageCount ; i ++ ) { if( MadcapServers[i] == *MadcapOptions.ServerIdentifier ) { break; } } if( i == MessageCount && MessageCount < MIN_ACKS_FOR_INFORM ) { MessageCount ++; MadcapServers[i] = *MadcapOptions.ServerIdentifier; } DhcpPrint((DEBUG_TRACE, "Received %ld ACKS so far\n", MessageCount)); MadcapExtractOptions( // do FULL options.. DhcpContext, (LPBYTE)&DhcpContext->MadcapMessageBuffer->Option, MessageSize - MADCAP_MESSAGE_FIXED_PART_SIZE, &MadcapOptions, &(DhcpContext->RecdOptionsList), *MadcapOptions.ServerIdentifier ); } TimeNow = time(NULL); // Reset the time values to reflect new time if( TimeToWait < (TimeNow - StartTime) ) { break; // no more time left to wait.. } TimeToWait -= (TimeNow - StartTime); // recalculate time now StartTime = TimeNow; // reset start time also } // end of while ( TimeToWait > 0) if( MessageCount >= MIN_ACKS_FOR_INFORM ) goto Cleanup; if( RoundNum != 0 && MessageCount != 0 ) goto Cleanup; TimeToWait = MADCAP_QUERY_SCOPE_LIST_TIME ; } // for (RoundNum = 0; RoundNum < nInformsToSend ; RoundNum ++ ) Cleanup: CloseDhcpSocket(DhcpContext); if( MessageCount ) Error = ERROR_SUCCESS; DhcpPrint((DEBUG_PROTOCOL, "MadcapDoInform: got %d ACKS (returning %ld)\n", MessageCount,Error)); return Error; } DWORD CopyMScopeList( IN OUT PMCAST_SCOPE_ENTRY pScopeList, IN OUT PDWORD pScopeLen, OUT PDWORD pScopeCount ) /*++ Routine Description: This routine obtains the multicast scope list from the Madcap server. It sends DHCPINFORM to Madcap multicast address and collects all the responses. Arguments: Return Value: The status of the operation. --*/ { PMCAST_SCOPE_ENTRY pScopeSource; DWORD i; LOCK_MSCOPE_LIST(); if ( *pScopeLen >= gMadcapScopeList->ScopeLen ) { RtlCopyMemory( pScopeList, gMadcapScopeList->pScopeBuf, gMadcapScopeList->ScopeLen ); *pScopeLen = gMadcapScopeList->ScopeLen; *pScopeCount = gMadcapScopeList->ScopeCount; // remember the start pointer because we need to remap all the buffers into client space. pScopeSource = gMadcapScopeList->pScopeBuf; UNLOCK_MSCOPE_LIST(); // now remap UNICODE_STRING scope desc to client address space. for (i=0;i<*pScopeCount;i++) { pScopeList[i].ScopeDesc.Buffer = (USHORT *) ((PBYTE)pScopeList + ((PBYTE)pScopeList[i].ScopeDesc.Buffer - (PBYTE)pScopeSource)); } return ERROR_SUCCESS; } else { UNLOCK_MSCOPE_LIST(); return ERROR_INSUFFICIENT_BUFFER; } } DWORD StoreMScopeList( IN PDHCP_CONTEXT pContext, IN BOOL NewList ) /*++ Routine Description: This routine stores the scope list it retrieved from the inform requests into the global scope list.. the scope option is of the following form. --------------------------------- | code (2 byte) | length (2byte)| --------------------------------- | count ( 4 bytes ) | --------------------------------- | Scope list --------------------------------- where scope list is of the following form -------------------------------------------------------------------------- | scope ID(4 byte) | Last Addr(4/16) |TTL(1) | Count (1) | Description...| -------------------------------------------------------------------------- where scope description is of the following form Language Tag -------------------------------------------------------------- | Flags(1) | Tag Length(1) | Tag...| Name Length(1) | Name...| -------------------------------------------------------------- Arguments: pContext - pointer to the context to be used during inform NewList - TRUE if a new list is to be created o/w prepend the current list. Return Value: The status of the operation. --*/ { PBYTE pOptBuf; PBYTE pOptBufEnd; PLIST_ENTRY pOptionList; PDHCP_OPTION pScopeOption, pFirstOption, pPrevOption; DWORD TotalNewScopeDescLen; DWORD TotalNewScopeCount; DWORD TotalNewScopeListMem; PMCAST_SCOPE_LIST pScopeList; PMCAST_SCOPE_ENTRY pNextScope; LPWSTR pNextUnicodeBuf; DWORD TotalCurrScopeListMem; DWORD TotalCurrScopeCount; DWORD Error; DWORD IpAddrLen; BOOL WellFormed; // MBUG - make sure we collect options from all the servers when // we do dhcpinform. // initialize variables. TotalNewScopeCount = TotalCurrScopeCount = 0; TotalNewScopeDescLen = 0; pScopeList = NULL; Error = ERROR_SUCCESS; LOCK_MSCOPE_LIST(); if (FALSE == NewList) { TotalCurrScopeListMem = gMadcapScopeList->ScopeLen; TotalCurrScopeCount = gMadcapScopeList->ScopeCount; DhcpPrint(( DEBUG_API, "StoreMScopeList: appending to CurrScopeLen %ld, ScopeCount %ld\n", gMadcapScopeList->ScopeLen, gMadcapScopeList->ScopeCount )); } // First calculate the space required for the scope list. // pFirstOption is used to track that we traverse the list only once pOptionList = &pContext->RecdOptionsList; pFirstOption = NULL; WellFormed = TRUE; while ( ( pScopeOption = DhcpFindOption( pOptionList, MADCAP_OPTION_MCAST_SCOPE_LIST, FALSE, NULL, 0, 0 //dont care about serverid )) && ( pScopeOption != pFirstOption ) ) { DWORD ScopeCount; DWORD i; // point to the next entry in the list. pOptionList = &pScopeOption->OptionList; // set the pFirstOption if it is not set already. if ( !pFirstOption ) { pFirstOption = pScopeOption; IpAddrLen = (pScopeOption->OptionVer.Proto == PROTO_MADCAP_V6 ? 16 : 4); } // if the last option was not well formatted from the list // then remove it from the list. if (!WellFormed) { DhcpDelOption(pPrevOption); //we may need to reset first option pointer. if (pPrevOption == pFirstOption) { pFirstOption = pScopeOption; } } else { WellFormed = FALSE; // set it back to false for this iteration. } // save the prev option pointer pPrevOption = pScopeOption; pOptBuf = pScopeOption->Data; pOptBufEnd = pScopeOption->Data + pScopeOption->DataLen; ScopeCount = 0; // Read the scope count if ( pOptBuf < pOptBufEnd ) { ScopeCount = *pOptBuf; pOptBuf ++; } else continue; for ( i=0;iScopeCount = 0; // we will fill this up as we go. pScopeList->ScopeLen = TotalNewScopeListMem - sizeof(MCAST_SCOPE_LIST) + sizeof(MCAST_SCOPE_ENTRY); // set the first scope pointer. pNextScope = pScopeList->pScopeBuf; // unicode strings starts after all the fixed sized scope structures. pNextUnicodeBuf = (LPWSTR)((PBYTE)pScopeList + ROUND_UP_COUNT( sizeof(MCAST_SCOPE_LIST) + // scope list struct sizeof(MCAST_SCOPE_ENTRY) * (TotalNewScopeCount -1), ALIGN_WORST)); // scope buffers. DhcpPrint(( DEBUG_API, "ScopeList %lx TotalNewScopeListMem %d, ScopeDescBuff %lx\n", pScopeList, TotalNewScopeListMem,pNextUnicodeBuf)); // now repeat the loop and fill up the scopelist. pOptionList = &pContext->RecdOptionsList; pFirstOption = NULL; while ( ( pScopeOption = DhcpFindOption( pOptionList, MADCAP_OPTION_MCAST_SCOPE_LIST, FALSE, NULL, 0, 0 //dont care about serverid )) && ( pScopeOption != pFirstOption ) ) { DWORD ScopeCount; DWORD i; DHCP_IP_ADDRESS ServerIpAddr; // point to the next entry in the list. pOptionList = &pScopeOption->OptionList; // set the pFirstOption if it is not set already. if ( !pFirstOption ) { pFirstOption = pScopeOption; } pOptBuf = pScopeOption->Data; DhcpPrint(( DEBUG_API, "MScopeOption - Data %lx\n", pOptBuf )); pOptBufEnd = pScopeOption->Data + pScopeOption->DataLen; // store ipaddr ServerIpAddr = pScopeOption->ServerId; DhcpPrint(( DEBUG_API, "MScopeOption - ServerIpAddr %lx\n", ServerIpAddr )); // read the scope count. ScopeCount = *pOptBuf; pOptBuf++; DhcpPrint(( DEBUG_API, "MScopeOption - ScopeCount %ld\n", ScopeCount )); for ( i=0;iScopeDesc, pNextUnicodeBuf ); pNextScope->ScopeDesc.MaximumLength = MaximumLength; pNextUnicodeBuf = (LPWSTR)((PBYTE)pNextUnicodeBuf + MaximumLength); DhcpAssert((PBYTE)pNextUnicodeBuf <= ((PBYTE)pScopeList + TotalNewScopeListMem)); } else { // set the unicode descriptor string to NULL; pNextScope->ScopeDesc.Length = pNextScope->ScopeDesc.MaximumLength = 0; pNextScope->ScopeDesc.Buffer = NULL; } // everything looks good, now fill up the NextScope pNextScope->ScopeCtx.ScopeID = ScopeID; pNextScope->ScopeCtx.ServerID.IpAddrV4 = ServerIpAddr; pNextScope->ScopeCtx.Interface.IpAddrV4 = pContext->IpAddress; pNextScope->LastAddr = LastAddr; pNextScope->TTL = TTL; pNextScope++; pScopeList->ScopeCount++; } } DhcpAssert( pScopeList->ScopeCount == (TotalNewScopeCount - TotalCurrScopeCount) ); // now append the previous scope list if exist. if (FALSE == NewList) { DWORD CurrScopeCount; PMCAST_SCOPE_ENTRY CurrScopeNextPtr; CurrScopeCount = gMadcapScopeList->ScopeCount; CurrScopeNextPtr = gMadcapScopeList->pScopeBuf; while(CurrScopeCount--) { *pNextScope = *CurrScopeNextPtr; // now copy the unicode strings also. RtlCopyMemory( pNextUnicodeBuf, CurrScopeNextPtr->ScopeDesc.Buffer, CurrScopeNextPtr->ScopeDesc.MaximumLength); pNextScope->ScopeDesc.Buffer = pNextUnicodeBuf ; pNextUnicodeBuf = (LPWSTR)((PBYTE)pNextUnicodeBuf + CurrScopeNextPtr->ScopeDesc.MaximumLength); pNextScope++; CurrScopeNextPtr++; } pScopeList->ScopeCount += gMadcapScopeList->ScopeCount; DhcpAssert( pScopeList->ScopeCount == TotalNewScopeCount); } // Finally copy this buffer to our global pointer. // first free the existing list. if (gMadcapScopeList) DhcpFreeMemory( gMadcapScopeList ); gMadcapScopeList = pScopeList; Cleanup: UNLOCK_MSCOPE_LIST(); return Error; } DWORD ObtainMScopeList( ) /*++ Routine Description: This routine obtains the multicast scope list from the Madcap server. It sends DHCPINFORM to Madcap multicast address and collects all the responses. Arguments: Return Value: The status of the operation. --*/ { MCAST_CLIENT_UID RequestID; BYTE IDBuf[MCAST_CLIENT_ID_LEN]; PDHCP_CONTEXT pContext; DWORD Error; PMIB_IPADDRTABLE pIpAddrTable; PLOCAL_CONTEXT_INFO localInfo; DWORD i; BOOL NewList; pContext = NULL; Error = ERROR_SUCCESS; pIpAddrTable = NULL; if ( !ShouldRequeryMScopeList() ) { return ERROR_SUCCESS; } else { RequestID.ClientUID = IDBuf; RequestID.ClientUIDLength = MCAST_CLIENT_ID_LEN; Error = GenMadcapClientUID( RequestID.ClientUID, &RequestID.ClientUIDLength ); if ( ERROR_SUCCESS != Error) goto Exit; Error = CreateMadcapContext(&pContext, &RequestID, INADDR_ANY ); if ( ERROR_SUCCESS != Error ) goto Exit; APICTXT_ENABLED(pContext); // mark the context as being created by the API localInfo = pContext->LocalInformation; // now get primary ipaddresses on each adapter. Error = GetIpPrimaryAddresses(&pIpAddrTable); if ( ERROR_SUCCESS != Error ) { goto Exit; } DhcpPrint((DEBUG_API, "ObtainMScopeList: ipaddress table has %d addrs\n", pIpAddrTable->dwNumEntries)); NewList = TRUE; Error = ERROR_TIMEOUT; for (i = 0; i < pIpAddrTable->dwNumEntries; i++) { DWORD LocalError; PMIB_IPADDRROW pAddrRow; pAddrRow = &pIpAddrTable->table[i]; // if primary bit set this is a primary address. if (0 == (pAddrRow->wType & MIB_IPADDR_PRIMARY) || 0 == pAddrRow->dwAddr || htonl(INADDR_LOOPBACK) == pAddrRow->dwAddr) { continue; } DhcpPrint((DEBUG_API, "ObtainMScopeList: DoInform on %s interface\n", DhcpIpAddressToDottedString(ntohl(pAddrRow->dwAddr)) )); LocalError = ReInitializeMadcapSocket(&localInfo->Socket, pAddrRow->dwAddr); if (ERROR_SUCCESS != LocalError) { continue; } pContext->IpAddress = pAddrRow->dwAddr; // now do the inform and get scope list. LocalError = MadcapDoInform(pContext); if ( ERROR_SUCCESS == LocalError ) { // now copy the scope list. LocalError = StoreMScopeList(pContext, NewList); if (ERROR_SUCCESS == LocalError ) { NewList = FALSE; Error = ERROR_SUCCESS; } } LOCK_OPTIONS_LIST(); DhcpDestroyOptionsList(&pContext->SendOptionsList, &DhcpGlobalClassesList); DhcpDestroyOptionsList(&pContext->RecdOptionsList, &DhcpGlobalClassesList); UNLOCK_OPTIONS_LIST(); } Exit: // signal the thread could be waiting on this. LOCK_MSCOPE_LIST(); gMScopeQueryInProgress = FALSE; UNLOCK_MSCOPE_LIST(); SetEvent( gMScopeQueryEvent ); if ( pContext ) { DhcpDestroyContext( pContext ); } if (pIpAddrTable) { DhcpFreeMemory( pIpAddrTable ); } return Error; } } DWORD GenMadcapClientUID( OUT PBYTE pRequestID, IN OUT PDWORD pRequestIDLen ) /*++ Routine Description: This routine generates a client UID. Arguments: pRequestID - pointer where client UID is to be stored. pRequestIDLen - pointer where the length of request id is stored. Return Value: --*/ { PULONG UID; RPC_STATUS Status; GUID RequestGuid; DhcpAssert( pRequestID && pRequestIDLen ); if (*pRequestIDLen < MCAST_CLIENT_ID_LEN) { DhcpPrint((DEBUG_ERRORS,"GenMadcapId - IDLen too small, %ld\n", *pRequestIDLen )); return ERROR_INVALID_PARAMETER; } Status = UuidCreate( &RequestGuid ); if (Status != RPC_S_OK) { Status = ERROR_LUIDS_EXHAUSTED; } *pRequestID++ = 0; // first octet is type and for guid the type is 0 *((GUID UNALIGNED *)pRequestID) = RequestGuid; return Status; } DWORD ObtainMadcapAddress( IN PDHCP_CONTEXT DhcpContext, IN PIPNG_ADDRESS pScopeID, IN PMCAST_LEASE_REQUEST pAddrRequest, IN OUT PMCAST_LEASE_RESPONSE pAddrResponse ) /*++ Routine Description: This routine attempts to obtains a new lease from a DHCP server. Arguments: DhcpContext - Points to a DHCP context block for the NIC to initialize. MadcapOptions - Returns DHCP options returned by the DHCP server. Return Value: --*/ { MADCAP_OPTIONS MadcapOptions; DATE_TIME HostOrderLeaseTime; DWORD Error; time_t StartTime; time_t InitialStartTime; time_t TimeNow; time_t TimeToWait; DWORD Xid; DWORD RoundNum; DWORD MessageSize; DWORD SelectedServer; DWORD SelectedAddress; DWORD LeaseExpiryTime; BOOL GotOffer; PMCAST_LEASE_REQUEST pRenewRequest; Xid = 0; // generate xid on first send. keep it same throughout SelectedServer = (DWORD)-1; SelectedAddress = (DWORD)-1; GotOffer = FALSE; InitialStartTime = time(NULL); Error = ERROR_SEM_TIMEOUT; // Make private copy of the request so that we don't modify original request. pRenewRequest = DhcpAllocateMemory( sizeof(*pAddrRequest) + sizeof(DWORD)*(pAddrRequest->AddrCount)); if (NULL == pRenewRequest) { return ERROR_NOT_ENOUGH_MEMORY; } memcpy(pRenewRequest,pAddrRequest,sizeof(*pAddrRequest) ); pRenewRequest->pAddrBuf = (PBYTE)pRenewRequest + sizeof(*pRenewRequest); if (pAddrRequest->pAddrBuf) { memcpy(pRenewRequest->pAddrBuf, pAddrRequest->pAddrBuf, sizeof(DWORD)*(pAddrRequest->AddrCount)); } for (RoundNum = 0; RoundNum < MADCAP_MAX_RETRIES; RoundNum++ ) { Error = SendMadcapDiscover( // send a discover packet DhcpContext, pScopeID, pAddrRequest, &Xid ); if ( Error != ERROR_SUCCESS ) { // can't really fail here DhcpPrint((DEBUG_ERRORS, "Send Dhcp Discover failed, %ld.\n", Error)); return Error ; } DhcpPrint((DEBUG_PROTOCOL, "Sent DhcpDiscover Message.\n")); TimeToWait = DhcpCalculateWaitTime(RoundNum, NULL); StartTime = time(NULL); while ( TimeToWait > 0 ) { // wait for specified time MessageSize = DHCP_RECV_MESSAGE_SIZE; DhcpPrint((DEBUG_TRACE, "Waiting for Offer: %ld seconds\n", TimeToWait)); Error = GetSpecifiedMadcapMessage( // try to receive an offer DhcpContext, &MessageSize, Xid, (DWORD)TimeToWait ); if ( Error == ERROR_TIMEOUT ) { // get out and try another discover DhcpPrint(( DEBUG_PROTOCOL, "Dhcp offer receive Timeout.\n" )); break; } if ( ERROR_SUCCESS != Error ) { // unexpected error DhcpPrint(( DEBUG_PROTOCOL, "Dhcp Offer receive failed, %ld.\n", Error )); return Error ; } MadcapExtractOptions( // now extract basic information DhcpContext, (LPBYTE)&DhcpContext->MadcapMessageBuffer->Option, MessageSize - MADCAP_MESSAGE_FIXED_PART_SIZE, &MadcapOptions, NULL, 0 ); GotOffer = AcceptMadcapMsg( // check up and see if we find this offer kosher MADCAP_DISCOVER_MESSAGE, DhcpContext, &MadcapOptions, 0, &Error ); DhcpAssert(ERROR_SUCCESS == Error); Error = ExpandMadcapAddressList( MadcapOptions.AddrRangeList, MadcapOptions.AddrRangeListSize, (DWORD UNALIGNED *)pRenewRequest->pAddrBuf, &pRenewRequest->AddrCount ); if (ERROR_SUCCESS != Error) { GotOffer = FALSE; } if (GotOffer) { break; } TimeNow = time( NULL ); // calc the remaining wait time for this round TimeToWait -= ((TimeNow - StartTime)); StartTime = TimeNow; } // while (TimeToWait > 0 ) if(GotOffer) { // if we got an offer, everything should be fine DhcpAssert(ERROR_SUCCESS == Error); break; } } // for n tries... send discover. if(!GotOffer ) { // did not get any valid offers DhcpPrint((DEBUG_ERRORS, "ObtainMadcapAddress timed out\n")); Error = ERROR_TIMEOUT ; goto Cleanup; } DhcpPrint((DEBUG_PROTOCOL, "Successfully received a DhcpOffer (%s) ", inet_ntoa(*(struct in_addr *)pRenewRequest->pAddrBuf) )); DhcpPrint((DEBUG_PROTOCOL, "from %s.\n", inet_ntoa(*(struct in_addr*)MadcapOptions.ServerIdentifier) )); SelectedServer = *MadcapOptions.ServerIdentifier; Error = RenewMadcapAddress( DhcpContext, pScopeID, pRenewRequest, pAddrResponse, SelectedServer ); Cleanup: if (pRenewRequest) { DhcpFreeMemory(pRenewRequest); } return Error; } DWORD RenewMadcapAddress( IN PDHCP_CONTEXT DhcpContext, IN PIPNG_ADDRESS pScopeID, IN PMCAST_LEASE_REQUEST pAddrRequest, IN OUT PMCAST_LEASE_RESPONSE pAddrResponse, IN DHCP_IP_ADDRESS SelectedServer ) /*++ Routine Description: This routine is called for two different purposes. 1. To request an address for which we got offer. 2. To renew an address. Arguments: DhcpContext - Points to a DHCP context block for the NIC to initialize. pScopeID - ScopeId from which the address is to be renewed. for renewals this is passed as null. pAddrRequest - The lease info structure describing the request. pAddrResponse - The lease info structure which receives the response data. SelectedServer - If we are sending REQUEST message then this describes the server from which we accepted the offer originally. Return Value: The status of the operation. --*/ { MADCAP_OPTIONS MadcapOptions; DWORD Error; DWORD Xid; DWORD RoundNum; size_t TimeToWait; DWORD MessageSize; DWORD LeaseTime; DWORD LeaseExpiryTime; time_t InitialStartTime; time_t StartTime; time_t TimeNow; BOOL GotAck; DATE_TIME HostOrderLeaseTime; BOOL Renew; Xid = 0; // new Xid will be generated first time InitialStartTime = time(NULL); GotAck = FALSE; Error = ERROR_TIMEOUT; Renew = (0 == SelectedServer); for ( RoundNum = 0; RoundNum < MADCAP_MAX_RETRIES; RoundNum ++ ) { if (Renew) { Error = SendMadcapRenew( DhcpContext, pAddrRequest, &Xid ); } else { Error = SendMadcapRequest( // send a request DhcpContext, pScopeID, pAddrRequest, SelectedServer, // &Xid ); } if ( Error != ERROR_SUCCESS ) { // dont expect send to fail DhcpPrint(( DEBUG_ERRORS,"Send request failed, %ld.\n", Error)); return Error ; } TimeToWait = DhcpCalculateWaitTime(RoundNum, NULL); StartTime = time(NULL); while ( TimeToWait > 0 ) { // try to recv message for this full period MessageSize = DHCP_RECV_MESSAGE_SIZE; Error = GetSpecifiedMadcapMessage( // expect to recv an ACK DhcpContext, &MessageSize, Xid, TimeToWait ); if ( Error == ERROR_TIMEOUT ) { // No response, so resend DHCP REQUEST. DhcpPrint(( DEBUG_PROTOCOL, "Dhcp ACK receive Timeout.\n" )); break; } if ( ERROR_SUCCESS != Error ) { // unexpected error DhcpPrint(( DEBUG_PROTOCOL, "Dhcp ACK receive failed, %ld.\n", Error )); return Error ; } MadcapExtractOptions( // now extract basic information DhcpContext, (LPBYTE)&DhcpContext->MadcapMessageBuffer->Option, MessageSize - MADCAP_MESSAGE_FIXED_PART_SIZE, &MadcapOptions, NULL, 0 ); GotAck = AcceptMadcapMsg( // check up and see if we find this offer kosher Renew ? MADCAP_RENEW_MESSAGE : MADCAP_REQUEST_MESSAGE, DhcpContext, &MadcapOptions, SelectedServer, &Error ); if (ERROR_SUCCESS != Error) { return Error; } // check that the ack came from the same server as the selected server. if ( SelectedServer && SelectedServer != *MadcapOptions.ServerIdentifier ) { GotAck = FALSE; } Error = ExpandMadcapAddressList( MadcapOptions.AddrRangeList, MadcapOptions.AddrRangeListSize, (DWORD UNALIGNED *)pAddrResponse->pAddrBuf, &pAddrResponse->AddrCount ); if (ERROR_SUCCESS != Error) { GotAck = FALSE; } if ( GotAck ) { break; } TimeNow = time( NULL ); TimeToWait -= (TimeNow - StartTime); StartTime = TimeNow; } // while time to wait if(TRUE == GotAck) { // if we got an ack, everything must be good DhcpAssert(ERROR_SUCCESS == Error); // cannot have any errors break; } DhcpContext->SecondsSinceBoot = (DWORD)(InitialStartTime - TimeNow); } // for RoundNum < MAX_RETRIES if(!GotAck) { DhcpPrint((DEBUG_ERRORS, "RenewMadcapAddress timed out\n")); return ERROR_TIMEOUT; } if (0 == SelectedServer ) SelectedServer = *MadcapOptions.ServerIdentifier; if( MadcapOptions.LeaseTime ) LeaseTime = ntohl(*MadcapOptions.LeaseTime); else LeaseTime = 0; pAddrResponse->ServerAddress.IpAddrV4 = SelectedServer; time( &TimeNow ); pAddrResponse->LeaseStartTime = (LONG)TimeNow; pAddrResponse->LeaseEndTime = (LONG)(TimeNow+LeaseTime); DhcpPrint((DEBUG_PROTOCOL, "Accepted ACK (%s) ", inet_ntoa(*(struct in_addr *)pAddrResponse->pAddrBuf) )); DhcpPrint((DEBUG_PROTOCOL, "from %s.\n", inet_ntoa(*(struct in_addr *)&SelectedServer))); DhcpPrint((DEBUG_PROTOCOL, "Lease is %ld secs.\n", LeaseTime)); return ERROR_SUCCESS; } DWORD ReleaseMadcapAddress( PDHCP_CONTEXT DhcpContext ) /*++ Routine Description: This routine to releases a lease for an IP address. Since the packet we send is not responded to, we assume that the release works. Arguments: DhcpContext - Points to a DHCP context block for the NIC to initialize. Return Value: None. --*/ { DWORD Xid; MADCAP_OPTIONS MadcapOptions; DWORD Error; time_t StartTime; time_t InitialStartTime; time_t TimeNow; time_t TimeToWait; DWORD RoundNum; DWORD MessageSize; BOOL GotAck; Xid = 0; // new Xid will be generated first time GotAck = FALSE; InitialStartTime = time(NULL); Error = ERROR_TIMEOUT; for (RoundNum = 0; RoundNum < MADCAP_MAX_RETRIES; RoundNum++ ) { Error = SendMadcapRelease( // send a discover packet DhcpContext, &Xid ); if ( Error != ERROR_SUCCESS ) { // can't really fail here DhcpPrint((DEBUG_ERRORS, "Send Dhcp Release failed, %ld.\n", Error)); return Error ; } DhcpPrint((DEBUG_PROTOCOL, "Sent DhcpRelease Message.\n")); TimeToWait = DhcpCalculateWaitTime(RoundNum, NULL); StartTime = time(NULL); while ( TimeToWait > 0 ) { // wait for specified time MessageSize = DHCP_RECV_MESSAGE_SIZE; DhcpPrint((DEBUG_TRACE, "Waiting for Ack: %ld seconds\n", TimeToWait)); Error = GetSpecifiedMadcapMessage( // try to receive an offer DhcpContext, &MessageSize, Xid, (DWORD)TimeToWait ); if ( Error == ERROR_TIMEOUT ) { // get out and try another discover DhcpPrint(( DEBUG_PROTOCOL, "Dhcp Ack receive Timeout.\n" )); break; } if ( ERROR_SUCCESS != Error ) { // unexpected error DhcpPrint(( DEBUG_PROTOCOL, "Dhcp Ack receive failed, %ld.\n", Error )); return Error ; } MadcapExtractOptions( // now extract basic information DhcpContext, (LPBYTE)&DhcpContext->MadcapMessageBuffer->Option, MessageSize - MADCAP_MESSAGE_FIXED_PART_SIZE, &MadcapOptions, NULL, 0 ); GotAck = AcceptMadcapMsg( // check up and see if we find this offer kosher MADCAP_RELEASE_MESSAGE, DhcpContext, &MadcapOptions, DhcpContext->DhcpServerAddress, &Error ); DhcpAssert(ERROR_SUCCESS == Error); if (GotAck) { break; } TimeNow = time( NULL ); // calc the remaining wait time for this round TimeToWait -= ((TimeNow - StartTime)); StartTime = TimeNow; } // while (TimeToWait > 0 ) if(GotAck) { // if we got an offer, everything should be fine DhcpAssert(ERROR_SUCCESS == Error); break; } } // for n tries... send discover. if(!GotAck ) { // did not get any valid offers DhcpPrint((DEBUG_ERRORS, "MadcapReleaseAddress timed out\n")); Error = ERROR_TIMEOUT ; } else { DhcpPrint((DEBUG_PROTOCOL, "Successfully released the address\n" )); Error = ERROR_SUCCESS; } return Error; }