/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: queue.c Abstract: Domain Name System (DNS) Server Queue functionality specific to Dynamic DNS registration. Author: Ram Viswanathan (ramv) March 27 1997 Revision History: --*/ #include "local.h" extern DWORD g_MainQueueCount; // // Queue allocations in dnslib heap // #define QUEUE_ALLOC_HEAP(Size) Dns_Alloc(Size) #define QUEUE_ALLOC_HEAP_ZERO(Size) Dns_AllocZero(Size) #define QUEUE_FREE_HEAP(pMem) Dns_Free(pMem) // // helper functions // PQELEMENT DequeueNoCrit( PDYNDNSQUEUE pQueue ); DWORD AddToTimedOutQueueNoCrit( PQELEMENT pNewElement, PDYNDNSQUEUE pRetryQueue, DWORD dwRetryTime ); DWORD HostAddrCmp( REGISTER_HOST_ENTRY HostAddr1, REGISTER_HOST_ENTRY HostAddr2 ) { // // DCR: Ram's HostAddrCmp will need update for IPv6 // // returns 0 if the two hostaddresses are the same. Else we simply // return (DWORD)-1 // if ( (HostAddr1.dwOptions == HostAddr2.dwOptions) && (HostAddr1.Addr.ipAddr == HostAddr2.Addr.ipAddr)) { return(0); } else { return( (DWORD)-1); } return ( (DWORD) -1); } VOID DeleteListEntry( PDYNDNSQUEUE pQueue, PQELEMENT* ppQElement ); DWORD InitializeQueues( PDYNDNSQUEUE * ppQueue, PDYNDNSQUEUE * ppTimedOutQueue ) /* InitializeQueue() This function initializes the queue system. This is invoked for the first time when you create the main queue and timed out queue Allocates appropriate memory etc */ { DWORD dwRetval = ERROR_SUCCESS; *ppQueue = (PDYNDNSQUEUE) QUEUE_ALLOC_HEAP_ZERO( sizeof(DYNDNSQUEUE) ); if (!*ppQueue){ dwRetval = GetLastError(); goto Exit; } *ppTimedOutQueue = (PDYNDNSQUEUE) QUEUE_ALLOC_HEAP_ZERO( sizeof(DYNDNSQUEUE) ); if (!*ppTimedOutQueue){ dwRetval = GetLastError(); goto Exit; } (*ppQueue)->pHead = NULL; (*ppQueue)->pTail = NULL; (*ppTimedOutQueue)->pHead = NULL; (*ppTimedOutQueue)->pTail = NULL; Exit: if (dwRetval) { if ( *ppQueue ) QUEUE_FREE_HEAP( *ppQueue ); if ( *ppTimedOutQueue ) QUEUE_FREE_HEAP( *ppTimedOutQueue ); } return(dwRetval); } DWORD FreeQueue( PDYNDNSQUEUE pQueue ) /* FreeQueue() Frees the queue object. If there exist any entries in the queue, we just blow them away */ { PQELEMENT pQElement; DWORD dwRetval = ERROR_SUCCESS; EnterCriticalSection(&g_QueueCS); if (!pQueue){ dwRetval = ERROR_SUCCESS; goto Exit; } while (pQueue->pHead){ pQElement = DequeueNoCrit(pQueue); DNS_ASSERT(pQElement); if ( pQElement->pszName ) QUEUE_FREE_HEAP( pQElement->pszName ); if ( pQElement->DnsServerList ) QUEUE_FREE_HEAP( pQElement->DnsServerList ); QUEUE_FREE_HEAP( pQElement ); pQElement = NULL; } QUEUE_FREE_HEAP( pQueue ); Exit: LeaveCriticalSection(&g_QueueCS); return(ERROR_SUCCESS); } DWORD Enqueue( PQELEMENT pNewElement, PDYNDNSQUEUE pQueue, PDYNDNSQUEUE pTimedOutQueue ) /* Enqueue() Adds new element to queue Arguments: Return Value: is 0 if Success. and (DWORD)-1 if failure. */ { // add to tail of queue PQELEMENT pIterator = NULL; DWORD dwRetval = 0; DWORD dwRetryTime = 0; pNewElement->pFLink = NULL; pNewElement->pBLink = NULL; pNewElement->dwRetryTime = 0; // unnecessary for this queue pNewElement->dwRetryCount = 0; EnterCriticalSection(&g_QueueCS); if (!pQueue || !pTimedOutQueue){ dwRetval = (DWORD)-1; goto Exit; } dwRetryTime = ProcessQDependencies(pTimedOutQueue, pNewElement); if (dwRetryTime){ // // we have dependents in timed out queue. Add to timed out queue // insert this element at the appropriate position // AddToTimedOutQueueNoCrit(pNewElement, pTimedOutQueue, dwRetryTime+1); } else { ProcessQDependencies(pQueue, pNewElement); if ( pQueue->pTail ) { DNS_ASSERT(!pQueue->pTail->pBLink); pQueue->pTail->pBLink = pNewElement; pNewElement->pFLink = pQueue->pTail; pNewElement->pBLink = NULL; pQueue->pTail = pNewElement; } else { // // no tail element means no head element either // pQueue->pTail = pNewElement; pQueue->pHead = pNewElement; pNewElement->pBLink = NULL; pNewElement->pFLink = NULL; } g_MainQueueCount++; } Exit: LeaveCriticalSection(&g_QueueCS); return (dwRetval); } PQELEMENT DequeueNoCrit( PDYNDNSQUEUE pQueue ) /* DequeueNoCrit() Removes an element from a queue. No Critical Section Used by freequeue and by Dequeue Arguments: Return Value: is the element at head of queue if Success. and NULL if failure. */ { PQELEMENT pQueuePtr = NULL; PQELEMENT pRet = NULL; if (!pQueue || !pQueue->pHead){ goto Exit; } pRet = pQueue->pHead; pQueuePtr= pRet->pBLink; if (pQueuePtr){ pQueuePtr->pFLink = NULL; pQueue->pHead = pQueuePtr; } else { // // no more elements in the Queue // pQueue->pHead = pQueue->pTail = NULL; } pRet->pFLink = NULL; pRet->pBLink = NULL; Exit: return (pRet); } PQELEMENT Dequeue( PDYNDNSQUEUE pQueue ) /* Dequeue() Removes an element from a queue. Arguments: Return Value: is the element at head of queue if Success. and NULL if failure. */ { PQELEMENT pQElement = NULL; EnterCriticalSection(&g_QueueCS); pQElement = DequeueNoCrit(pQueue); LeaveCriticalSection(&g_QueueCS); return (pQElement); } DWORD AddToTimedOutQueueNoCrit( PQELEMENT pNewElement, PDYNDNSQUEUE pRetryQueue, DWORD dwRetryTime ) /* AddToTimedOutQueueNoCrit() Adds new element to timedout queue. Now the new element is added in a list of elements sorted according to decreasing order of Retry Times. An insertion sort type of algorithm is used. Arguments: Return Value: is 0 if Success. and (DWORD)-1 if failure. */ { DWORD dwRetval = ERROR_SUCCESS; PQELEMENT pTraverse = NULL; DWORD dwVal = 0; // // parameter validation // if(!pNewElement || !pRetryQueue){ dwRetval = (DWORD)-1; goto Exit; } // retry again in dwRetryTime pNewElement->dwRetryTime = dwRetryTime; pNewElement->dwRetryCount++; // // check to see if there are any dependencies // dwVal = ProcessQDependencies ( pRetryQueue, pNewElement ); // // ignore return values because we are inserting in the new queue // at a position determined by dwRetryTime // if (!pRetryQueue->pTail){ // // the queue has no elements // no tail element means no head element either // pRetryQueue->pTail = pNewElement; pRetryQueue->pHead = pNewElement; dwRetval = 0; goto Exit; } // // elements must be added in decreasing order of timeouts. // go in and scan the list from the head. // pTraverse = pRetryQueue->pHead; while ( pTraverse !=NULL && pTraverse->dwRetryTime <= pNewElement->dwRetryTime){ pTraverse = pTraverse->pBLink; } if (pTraverse == NULL){ // Now adding to the tail of the list pNewElement->pFLink = pRetryQueue->pTail; pNewElement->pBLink = NULL; pRetryQueue->pTail->pBLink = pNewElement; pRetryQueue->pTail = pNewElement; } else { // // insert in place // pNewElement->pBLink = pTraverse; pNewElement->pFLink = pTraverse->pFLink; if (pTraverse->pFLink){ pTraverse->pFLink->pBLink = pNewElement; } pTraverse->pFLink = pNewElement; } Exit: return (dwRetval); } DWORD AddToTimedOutQueue( PQELEMENT pNewElement, PDYNDNSQUEUE pRetryQueue, DWORD dwRetryTime ) { DWORD dwRetval = ERROR_SUCCESS; EnterCriticalSection(&g_QueueCS); dwRetval = AddToTimedOutQueueNoCrit( pNewElement, pRetryQueue, dwRetryTime ); LeaveCriticalSection(&g_QueueCS); return (dwRetval); } DWORD GetEarliestRetryTime( PDYNDNSQUEUE pRetryQueue ) /* GetEarliestRetryTime() Checks to see if there is any element at the head of the queue and gets the retry time for this element Arguments: Return Value: is retrytime if success and DWORD(-1) if there is no element or other failure */ { DWORD dwRetryTime ; EnterCriticalSection(&g_QueueCS); dwRetryTime = pRetryQueue && pRetryQueue->pHead ? (pRetryQueue->pHead->dwRetryTime): (DWORD)-1; LeaveCriticalSection(&g_QueueCS); return dwRetryTime; } /* VOID ProcessMainQDependencies( PDYNDNSQUEUE pQueue, PQELEMENT pQElement ) { // // when you are adding an element to a main queue, you // just care about the case where all elements aren't // FORWARD_ONLY // BOOL fDelThisTime = FALSE; PQELEMENT pIterator = pQueue->pTail; while (pIterator!= NULL){ fDelThisTime = FALSE; if (!HostAddrCmp(pIterator->HostAddr, pQElement->HostAddr)){ // // ip addresses matched // if ((pIterator->dwOperation & DYNDNS_ADD_ENTRY) && (pQElement->dwOperation & DYNDNS_DELETE_ENTRY)) { if ( pIterator->pszName && pQElement->pszName && !wcsicmp_ThatWorks( pIterator->pszName, pQElement->pszName ) ) { // // blow away earlier entry entirely // DeleteListEntry(pQueue, &pIterator); fDelThisTime = TRUE; } // // if names are not the same do nothing. // Issue: Will we hit this code at all? Put // soft ASSERTS in this. // } else if ((pIterator->dwOperation & DYNDNS_DELETE_ENTRY) && (pQElement->dwOperation & DYNDNS_ADD_ENTRY)) { if ( pIterator->pszName && pQElement->pszName && !wcsicmp_ThatWorks( pIterator->pszName, pQElement->pszName ) ) { // // blow away earlier entry entirely // DeleteListEntry(pQueue, &pIterator); fDelThisTime = TRUE; } else { // // replace iterator element with just the forward // delete if (!pIterator->fDoForward) { // // there is no forward that is requested // blow away this entry // DeleteListEntry(pQueue, &pIterator); fDelThisTime = TRUE; } else { // // if you want to do a forward. Then just do // the forward. Ignore reverses // pIterator ->fDoForwardOnly = TRUE; } } } else if ((pIterator->dwOperation & DYNDNS_ADD_ENTRY) && (pQElement->dwOperation & DYNDNS_ADD_ENTRY)) { // replace the old entry with a forward delete. // this is an error. Need to replace earlier add // forward with an explicit Delete // if ( pIterator->pszName && pQElement->pszName && !wcsicmp_ThatWorks( pIterator->pszName, pQElement->pszName ) ) { DeleteListEntry(pQueue, &pIterator); fDelThisTime = TRUE; } else { // // Log entries into this area. This should // be a soft assert if you are here // Names dont match, so you need to replace earlier // add with a delete forward only // if (!pIterator->fDoForward) { // // there is no forward add requested // blow away this entry // DeleteListEntry(pQueue, &pIterator); fDelThisTime = TRUE; } else { // // if you want to *explicitly* delete old // forward and then add the new forward/reverse. // pIterator ->fDoForwardOnly = TRUE; pIterator ->dwOperation &= ~(DYNDNS_ADD_ENTRY) & DYNDNS_DELETE_ENTRY; } } } else if ((pIterator->dwOperation & DYNDNS_DELETE_ENTRY) && (pQElement->dwOperation & DYNDNS_DELETE_ENTRY)) { // // if both are deletes. // if ( pIterator->pszName && pQElement->pszName && !wcsicmp_ThatWorks( pIterator->pszName, pQElement->pszName ) ) { // // blow away earlier entry. An optimization // DeleteListEntry(pQueue, &pIterator); fDelThisTime = TRUE; } // // if names dont match, do nothing. (To paraphrase, // the DNS Server needs to do both!! // } } if (pIterator && !fDelThisTime) { // pIterator may have changed because of blowing away an entry pIterator = pIterator->pFLink; } } } */ DWORD ProcessQDependencies( PDYNDNSQUEUE pTimedOutQueue, PQELEMENT pQElement ) /* This function returns the retry time of the last element that you needed to blow out, 0 if no element needed to be removed */ { PQELEMENT pIterator = pTimedOutQueue->pTail; DWORD dwRetryTime = 0; BOOL fDelThisTime = FALSE; while (pIterator) { fDelThisTime = FALSE; if (!pIterator->fDoForwardOnly && !pQElement->fDoForwardOnly){ // // both elements are not forward only, check on ip addresses // if (!HostAddrCmp(pIterator->HostAddr, pQElement->HostAddr)){ // // ip addresses matched // if ((pIterator->dwOperation & DYNDNS_ADD_ENTRY) && (pQElement->dwOperation & DYNDNS_DELETE_ENTRY)) { if ( pIterator->pszName && pQElement->pszName && !wcsicmp_ThatWorks( pIterator->pszName, pQElement->pszName ) ) { // // blow away earlier entry entirely // dwRetryTime = pIterator -> dwRetryTime; DeleteListEntry(pTimedOutQueue, &pIterator); fDelThisTime = TRUE; } // // if names are not the same do nothing. // // Issue: Will we hit this code at all? Put // soft ASSERTS in this. // }else if ((pIterator->dwOperation & DYNDNS_DELETE_ENTRY) && (pQElement->dwOperation & DYNDNS_ADD_ENTRY)) { if ( pIterator->pszName && pQElement->pszName && !wcsicmp_ThatWorks( pIterator->pszName, pQElement->pszName ) ) { // // blow away earlier entry entirely // dwRetryTime = pIterator -> dwRetryTime; DeleteListEntry(pTimedOutQueue, &pIterator); fDelThisTime = TRUE; } else { // replace iterator element with just the forward dwRetryTime = pIterator -> dwRetryTime; pIterator -> fDoForwardOnly = TRUE; } }else if ((pIterator->dwOperation & DYNDNS_ADD_ENTRY) && (pQElement->dwOperation & DYNDNS_ADD_ENTRY)) { // replace the old entry with a forward delete. // this is an error. Need to replace earlier add // forward with an explicit Delete // if ( pIterator->pszName && pQElement->pszName && !wcsicmp_ThatWorks( pIterator->pszName, pQElement->pszName ) ) { DeleteListEntry(pTimedOutQueue, &pIterator); fDelThisTime = TRUE; } else { // // Log entries into this area. This should // be a soft assert if you are here // Names dont match, so you need to replace earlier // add with a delete forward only // if (!pIterator->fDoForward) { // // there is no forward add requested // blow away this entry // DeleteListEntry(pTimedOutQueue, &pIterator); fDelThisTime = TRUE; } else { // // if you want to *explicitly* delete old // forward and then add the new forward/reverse. // pIterator ->fDoForwardOnly = TRUE; pIterator ->dwOperation &= ~(DYNDNS_ADD_ENTRY) & DYNDNS_DELETE_ENTRY; } } } else if ((pIterator->dwOperation & DYNDNS_DELETE_ENTRY) && (pQElement->dwOperation & DYNDNS_DELETE_ENTRY)) { // // if both are deletes. // if ( pIterator->pszName && pQElement->pszName && !wcsicmp_ThatWorks( pIterator->pszName, pQElement->pszName ) ) { // // blow away earlier entry. An optimization // DeleteListEntry(pTimedOutQueue, &pIterator); fDelThisTime = TRUE; } // // if names dont match, do nothing. (To paraphrase, // the DNS Server needs to do both!! // } } } else if (pIterator->fDoForwardOnly) { if ( pIterator->pszName && pQElement->pszName && !wcsicmp_ThatWorks( pIterator->pszName, pQElement->pszName ) ) { if ((pIterator->dwOperation & DYNDNS_ADD_ENTRY) && (pQElement->dwOperation & DYNDNS_ADD_ENTRY)) { if (!HostAddrCmp(pIterator->HostAddr, pQElement->HostAddr)) { // // optimization blow away earlier entry // DeleteListEntry(pTimedOutQueue, &pIterator); fDelThisTime = TRUE; } // // if names dont match, do nothing // } else if ((pIterator->dwOperation & DYNDNS_ADD_ENTRY) && (pQElement->dwOperation & DYNDNS_DELETE_ENTRY)) { if (!HostAddrCmp(pIterator->HostAddr, pQElement->HostAddr)){ // // blow away earlier entry // dwRetryTime = pIterator -> dwRetryTime; DeleteListEntry(pTimedOutQueue, &pIterator); fDelThisTime = TRUE; } // // if addresses dont match, do nothing // } else if ((pIterator->dwOperation & DYNDNS_DELETE_ENTRY) && (pQElement->dwOperation & DYNDNS_ADD_ENTRY)) { if (!HostAddrCmp(pIterator->HostAddr, pQElement->HostAddr)){ // // blow away earlier entry // dwRetryTime = pIterator -> dwRetryTime; DeleteListEntry(pTimedOutQueue, &pIterator); fDelThisTime = TRUE; } // // if addresses dont match, then dont do anything // } else { // both are deletes // do nothing here. i.e. DNS Server does both } } } else if (!pIterator->fDoForwardOnly && pQElement->fDoForwardOnly) { // // new element is forward only // // // if both elements are forwards, we cannot whack anything // out in any case, do nothing // } if (!fDelThisTime && pIterator){ pIterator = pIterator ->pFLink; } } return (dwRetryTime); } VOID DeleteListEntry( PDYNDNSQUEUE pQueue, PQELEMENT* ppIterator ) { PQELEMENT pPrev, pNext; PQELEMENT pIterator = *ppIterator; DHCP_CALLBACK_FN pfnDhcpCallBack = NULL; PVOID pvData = NULL; pPrev = pIterator ->pBLink; pNext = pIterator ->pFLink; if (pPrev) { pPrev->pFLink = pNext; } if (pNext) { pNext ->pBLink = pPrev; } if (pIterator == pQueue ->pHead) { pQueue->pHead = pIterator ->pBLink; } if (pIterator == pQueue ->pTail) { pQueue->pTail = pIterator ->pFLink; } *ppIterator = pIterator ->pFLink; pfnDhcpCallBack = pIterator->pfnDhcpCallBack; pvData = pIterator->pvData; // blow away entry if ( pIterator -> pszName ) QUEUE_FREE_HEAP( pIterator->pszName ); if ( pIterator -> DnsServerList ) QUEUE_FREE_HEAP( pIterator->DnsServerList ); if ( pfnDhcpCallBack ) (*pfnDhcpCallBack)(DNSDHCP_SUPERCEDED, pvData); QUEUE_FREE_HEAP( pIterator ); }