|
|
/*++
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 ); }
|