/**HEADER********************************************************************
* 
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2008 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: resolver.c$
* $Version : 3.5.20.0$
* $Date    : Feb-16-2010$
*
* Comments:
*
*   This file contains the Domain Name System resolver
*   functions for RTCS.
*
*END************************************************************************/

#include <rtcs.h>
#include "rtcs_prv.h"

#if RTCSCFG_ENABLE_DNS | RTCSCFG_ENABLE_LWDNS

#include <ctype.h>
#include <string.h>
#include "dns.h"

#endif

#if RTCSCFG_ENABLE_DNS

/*

GCC compiler did not have ctype in libc.
Used these definitions to resolve the problem:

#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-'A'+'a' : (c))
#define isalpha(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
#define isdigit(c) ( (c) >= '0' && (c) <= '9' )
#define isalnum(c) (isalpha(c) || isdigit(c))

*/


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_get_query_id()
* Returned Value  : uint_16.
* Comments        : The function returns a unique value that
*                   is used by the resolver for queries and keeps track
*                   of the last query id used.
*
*END*-----------------------------------------------------------------*/

static uint_16 DNS_get_query_id
   (
   void
   )

{ /* Body */

   static uint_16 current_query_id = 0;

   /* The masking is required for TI machines to operate correctly */
   if ( current_query_id == 0 ) {
      current_query_id = RTCS_rand() & DNS_UINT_16_MASK;
      if ( current_query_id == 0 ) {
         current_query_id++;
      } /* Endif */
   } else  {
      current_query_id++;
      current_query_id &= DNS_UINT_16_MASK;
      if ( current_query_id == 0 ) {
         current_query_id++;
      } /* Endif */
   } /* Endif */
   return( current_query_id );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_update_TTL()
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/


static void DNS_update_TTL
   (
   DNS_INTERNAL_QUERY_STRUCT _PTR_ query_ptr
   )

{ /* Body */

   TIME_STRUCT       time;
   int_32            difference;

   _time_get( &time );

   difference = time.SECONDS - query_ptr->LAST_TTL_CHECK;

   if ( query_ptr->TTL <= difference ) {
      query_ptr->TTL = 0;
   } else {
      query_ptr->TTL -= difference;
   } /* Endif */

   query_ptr->LAST_TTL_CHECK = time.SECONDS;

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_free_internal_query()
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/

static void DNS_free_internal_query
   (
   DNS_INTERNAL_QUERY_STRUCT _PTR_  query
   )

{ /* Body */

   DNS_CNAME_STRUCT _PTR_    free_cname_ptr;
   DNS_CNAME_STRUCT _PTR_    next_cname_ptr;

   if ( query->QNAME_PTR != NULL ) {
      _mem_free( query->QNAME_PTR );
   } /* Endif */

   free_cname_ptr = query->CNAME;
   while ( free_cname_ptr != NULL ) {
      next_cname_ptr = free_cname_ptr->NEXT;
      _mem_free( free_cname_ptr->CNAME_PTR );
      RTCS_part_free( free_cname_ptr );
      free_cname_ptr = next_cname_ptr;
   } /* Endwhile */

   RTCS_part_free( query );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_Send_UDP_Query()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_Send_UDP_Query
   (
   DNS_CONTROL_STRUCT         _PTR_  control_ptr,
   DNS_INTERNAL_QUERY_STRUCT _PTR_  query_ptr,
   boolean                          cname_retry
   )

{ /* Body */

   DNS_MESSAGE_HEADER_STRUCT _PTR_   message_head_ptr;
   DNS_MESSAGE_TAIL_STRUCT   _PTR_   message_tail_ptr;
   DNS_CNAME_STRUCT          _PTR_   cname_ptr;
   sockaddr_in                       raddr;
   int_32                            error;
   uint_32                           buffer_size;
   uint_32                           name_size;
   uchar                     _PTR_   name_ptr;
   uchar                     _PTR_   temp_ptr;

   /* Remote address, Name Servers use port 53 */
   raddr.sin_family      = AF_INET;
   raddr.sin_port        = DNS_NAMESERVER_PORT;
   raddr.sin_addr.s_addr = query_ptr->SERVER_IPADDR;

   /* set up buffer for sending query.   */
   message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT _PTR_)control_ptr->RES_BUFFER;

   htons(message_head_ptr->ID, query_ptr->QUERY_ID);
   htons(message_head_ptr->CONTROL, query_ptr->CONTROL);
   htons(message_head_ptr->QDCOUNT, DNS_SINGLE_QUERY);
   htons(message_head_ptr->ANCOUNT, 0);
   htons(message_head_ptr->NSCOUNT, 0);
   htons(message_head_ptr->ARCOUNT, 0);

   name_ptr  = control_ptr->RES_BUFFER + sizeof(DNS_MESSAGE_HEADER_STRUCT);
   if ( cname_retry == TRUE ) {
      cname_ptr = query_ptr->CNAME;
      while ( cname_ptr->NEXT != NULL ) {
         cname_ptr = cname_ptr->NEXT;
      } /* Endwhile */
      name_size = strlen((char _PTR_)cname_ptr->CNAME_PTR );
      temp_ptr  = cname_ptr->CNAME_PTR;
   } else {
      name_size = strlen((char _PTR_)query_ptr->QNAME_PTR );
      temp_ptr  = query_ptr->QNAME_PTR;
   } /* Endif */

   /* To include the terminating NULL char */
   name_size++;
   buffer_size = sizeof(DNS_MESSAGE_HEADER_STRUCT) +  name_size
                 + sizeof(DNS_MESSAGE_TAIL_STRUCT);

   while( name_size != 0 ) {
     htonc(name_ptr, *temp_ptr);
     name_ptr++;
     temp_ptr++;
     name_size--;
   } /* Endwhile */

   message_tail_ptr = (DNS_MESSAGE_TAIL_STRUCT _PTR_)name_ptr;
   htons(message_tail_ptr->QTYPE, query_ptr->QTYPE);
   htons(message_tail_ptr->QCLASS, query_ptr->QCLASS);


   error = sendto(control_ptr->RES_SOCKET, control_ptr->RES_BUFFER, buffer_size, 0,
                  &raddr, sizeof(raddr));
   if (error == RTCS_ERROR) {
      RTCS_log_error(ERROR_DNS, RTCSERR_DNS_UNABLE_TO_SEND_QUERY, 1, 0, 0);
      error = RTCSERR_DNS_UNABLE_TO_SEND_QUERY;
   } else {
      error = DNS_OK;
   } /* Endif */

   return( error );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_return_UDP_response_to_query()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_return_UDP_response_to_query
   (
   DNS_INTERNAL_QUERY_STRUCT _PTR_ current_query_ptr,
   uchar _PTR_                     buffer_ptr,
   uint_32                         buffer_size,
   uint_32                         query_sock
   )

{ /* Body */

   int_32        error;

   error = sendto(query_sock, buffer_ptr, buffer_size, 0,
                  &current_query_ptr->CLIENT_SIN,
                  sizeof(current_query_ptr->CLIENT_SIN) );
   if (error == RTCS_ERROR) {
      RTCS_log_error(ERROR_DNS, RTCSERR_DNS_UNABLE_TO_SEND_QUERY, 2, 0, 0);

      DNS_free_internal_query( current_query_ptr );
      return( RTCSERR_DNS_UNABLE_TO_SEND_QUERY );
   } /* Endif */

   DNS_free_internal_query( current_query_ptr );
   return( DNS_OK );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_free_resource_record()
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/


static void DNS_free_resource_record
   (
   DNS_RR_HEADER_STRUCT _PTR_ list_ptr
   )

{ /* Body */

   DNS_ADDR_RECORD_STRUCT _PTR_ addr_rr_ptr;
   DNS_NAME_RECORD_STRUCT _PTR_ name_rr_ptr;

   switch ( list_ptr->TYPE ) {
      case DNS_A:
         addr_rr_ptr = (DNS_ADDR_RECORD_STRUCT _PTR_)((void _PTR_)list_ptr);
         RTCS_part_free( addr_rr_ptr );
         break;

      case DNS_PTR:
      case DNS_CNAME:
      case DNS_NS:
         name_rr_ptr = (DNS_NAME_RECORD_STRUCT _PTR_)((void _PTR_)list_ptr);
         if ( name_rr_ptr->ANS_NAME_PTR ) {
            _mem_free( name_rr_ptr->ANS_NAME_PTR );
         }/* Endif */
         RTCS_part_free( name_rr_ptr );         
         break;

      default:
         /*
         ** If we reach here, there could be a potential memory leak.
         ** Depending upon the Resource Record type, it may not all 
         ** be freed.
         */
         RTCS_part_free( list_ptr );
   } /* Endswitch */

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_free_slist_records()
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/


static void DNS_free_slist_records
   (
   DNS_SLIST_STRUCT _PTR_ list_ptr
   )

{ /* Body */

/* NOT IMPLEMENTED */
} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_get_best_name_server_for_query()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

/* Start CR 2424
   NOTE: This only works if DNS entries are added correctly into the linked
   list, with the last entry's NEXT == NULL.  This should be done using the
   API for adding DNS entries (CR 2257):
        {
          DNS_SLIST_STRUCT tmp_slist_entry;
          DNS_init_slist_entry(&tmp_slist_entry);
     	  return( DNS_add_slist_entry(&tmp_slist_entry));
        }
*/
#define PRIMARY_NAME_SERVER_TIMEOUT 300 // 5 minutes

static uint_32 DNS_get_best_name_server_for_query
   (
   DNS_INTERNAL_QUERY_STRUCT _PTR_ query_ptr,
   DNS_CONTROL_STRUCT         _PTR_ control_ptr
   )

{ /* Body */

   static DNS_SLIST_STRUCT _PTR_ best_ptr = NULL;
   TIME_STRUCT                   time;
   static uint_32                last_time;
   
   // Start with the Primary DNS Server
   if (best_ptr == NULL) {
      best_ptr = control_ptr->LOCAL_SLIST_PTR;
      query_ptr->SERVER_IPADDR = best_ptr->IPADDR;
      return( DNS_OK );
   }

   // Only change our notion of best server if this query has exhausted
   // its QUERY_RETRIES on our current best server.  If the query expired
   // on some other server, we cannot use that info to change our current
   // notion of best server, since our current best server may be fine.
   if (query_ptr->SERVER_IPADDR == best_ptr->IPADDR) {
      _time_get( &time );
   
      // Go back to the Primary every so often to see if it has returned.
      if (best_ptr != control_ptr->LOCAL_SLIST_PTR) {
         if ((time.SECONDS - last_time) >= PRIMARY_NAME_SERVER_TIMEOUT) {
            best_ptr = control_ptr->LOCAL_SLIST_PTR;
            query_ptr->SERVER_IPADDR = best_ptr->IPADDR;
            return( DNS_OK );
         }
      }
   
      // If the query has exhausted its retries, change our notion of best.
      // If that takes us back to the Primary, we can ignore the timeout.
      if (query_ptr->QUERY_RETRIES <= 0) {
         if (best_ptr->NEXT) {
            best_ptr = best_ptr->NEXT;
            last_time = time.SECONDS;
         }
         else {
            best_ptr = control_ptr->LOCAL_SLIST_PTR;
         }
      }
   }
   query_ptr->SERVER_IPADDR = best_ptr->IPADDR;
   return( DNS_OK );

} /* Endbody */
// End CR 2424


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_get_next_expired_query()
* Returned Value  : DNS_QUERY_ID_STRUCT _PTR_
* Comments        :
*
*END*-----------------------------------------------------------------*/


static  DNS_INTERNAL_QUERY_STRUCT _PTR_ DNS_get_next_expired_query
   (
   DNS_INTERNAL_QUERY_STRUCT _PTR_ _PTR_ query_head_ptr
   )

{ /* Body */

   DNS_INTERNAL_QUERY_STRUCT _PTR_ expired_query_ptr;


   if ( *query_head_ptr == NULL ) {
      return( NULL );
   } /* Endif */


   expired_query_ptr = *query_head_ptr;

   DNS_update_TTL( expired_query_ptr );

   if ( expired_query_ptr->TTL == 0 ) {
      *query_head_ptr = expired_query_ptr->NEXT;
      return( expired_query_ptr );
   } /* Endif */

   return( NULL );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_add_query_to_list()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_add_query_to_list
   (
   DNS_INTERNAL_QUERY_STRUCT _PTR_ _PTR_ query_head_ptr,
   DNS_INTERNAL_QUERY_STRUCT _PTR_       new_query_ptr
   )

{ /* Body */

   DNS_INTERNAL_QUERY_STRUCT _PTR_ previous_ptr = NULL;
   DNS_INTERNAL_QUERY_STRUCT _PTR_ temp_ptr;

   /*
   ** set the new_query_ptr->NEXT to be NULL, in case this is a query
   ** retry, the NEXT field may not be NULL. Just to be safe.
   */
   new_query_ptr->NEXT = NULL;

   /* first check to see if list is empty */
   if ( *query_head_ptr == NULL) {
      *query_head_ptr = new_query_ptr;
      return( DNS_OK );
   } /* Endif */

   temp_ptr  = *query_head_ptr;

   while ( temp_ptr != NULL ) {
      DNS_update_TTL( temp_ptr );

      if ( new_query_ptr->TTL < temp_ptr->TTL  ) {
         if ( previous_ptr ) {
            previous_ptr->NEXT  = new_query_ptr;
            new_query_ptr->NEXT = temp_ptr;
            return( DNS_OK );
         } else {
            new_query_ptr->NEXT = temp_ptr;
            *query_head_ptr     = new_query_ptr;
            return( DNS_OK );
         } /* Endif */
      } /* Endif */
      previous_ptr = temp_ptr;
      temp_ptr = temp_ptr->NEXT;
   } /* Endwhile */

   previous_ptr->NEXT = new_query_ptr;
   return( DNS_OK );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_send_no_query_error_response()
* Returned Value  : uint_32
* Comments        : This function is only used if DNS is unable to allocate
*                   or fill a DNS_INTERNAL_QUERY_STRUCT
*
*END*-----------------------------------------------------------------*/

static void DNS_send_no_query_error_response
   (
   DNS_CONTROL_STRUCT _PTR_ control_ptr,
   uint_16                 error_code,
   sockaddr_in       _PTR_ client_sin_ptr
   )

{ /* Body */

   DNS_MESSAGE_HEADER_STRUCT _PTR_  message_head_ptr;
   int_32                           error;
   uint_16                          pack_variable;

   message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT _PTR_)control_ptr->RES_BUFFER;
   pack_variable = ntohs(message_head_ptr->CONTROL);
   htons(message_head_ptr->CONTROL,
         (pack_variable | DNS_RESPONSE | error_code));
   htons(message_head_ptr->ANCOUNT, DNS_NO_ANSWER);
   htons(message_head_ptr->ARCOUNT, 0);
   htons(message_head_ptr->NSCOUNT, 0);

   error = sendto(control_ptr->RES_SOCKET, control_ptr->RES_BUFFER,
                           control_ptr->RES_BUFFER_SIZE, 0,
                           client_sin_ptr, sizeof(*client_sin_ptr));
   if (error == RTCS_ERROR) {
      RTCS_log_error(ERROR_DNS, RTCSERR_DNS_UNABLE_TO_SEND_QUERY, 3, 0, 0);
   } /* Endif */

} /* Endbody */

/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_fill_generic_response_buffer()
* Returned Value  : uint-32
* Comments        : This function is used to fill the response buffer
*                   with information common to all queries
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_fill_generic_response_buffer
   (
   uchar                     _PTR_ buffer,
   DNS_INTERNAL_QUERY_STRUCT _PTR_ query_ptr,
   uint_16                         error_code
   )

{ /* Body */

   DNS_MESSAGE_HEADER_STRUCT _PTR_   message_head_ptr;
   DNS_MESSAGE_TAIL_STRUCT   _PTR_   message_tail_ptr;
   uchar                     _PTR_   name_ptr;
   uchar                     _PTR_   temp_ptr;
   uint_32                           name_size;
   uint_32                           buffer_size;

   message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT _PTR_)buffer;

   name_size = strlen((char _PTR_)query_ptr->QNAME_PTR );
   /* To include the terminating NULL char */
   name_size++;

   buffer_size =   sizeof(DNS_MESSAGE_HEADER_STRUCT) + name_size
                 + sizeof(DNS_MESSAGE_TAIL_STRUCT);

   temp_ptr = buffer + sizeof(DNS_MESSAGE_HEADER_STRUCT)
                     + name_size;

   message_tail_ptr = (DNS_MESSAGE_TAIL_STRUCT _PTR_)temp_ptr;

   name_ptr  = buffer + sizeof(DNS_MESSAGE_HEADER_STRUCT);
   temp_ptr  = query_ptr->QNAME_PTR;

   htons(message_head_ptr->ID, query_ptr->QUERY_ID);
   htons(message_head_ptr->CONTROL,
        (query_ptr->CONTROL | error_code | DNS_RESPONSE));
   htons(message_head_ptr->ANCOUNT, DNS_NO_ANSWER);
   htons(message_head_ptr->QDCOUNT, DNS_SINGLE_QUERY);
   htons(message_head_ptr->NSCOUNT, DNS_NO_ANSWER);
   htons(message_head_ptr->ARCOUNT, DNS_NO_ANSWER);

   while ( name_size ) {
      htonc(name_ptr, *temp_ptr);
      name_ptr++;
      temp_ptr++;
      name_size--;
   } /* Endwhile */

   htons(message_tail_ptr->QTYPE, query_ptr->QTYPE);
   htons(message_tail_ptr->QCLASS, query_ptr->QCLASS);

   return( buffer_size );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_send_error_response()
* Returned Value  : void
* Comments        : This function is used if a DNS_INTERNAL_QUERY_STRUCT
*                   exists
*
*END*-----------------------------------------------------------------*/

static void DNS_send_error_response
   (
   uchar                     _PTR_ buffer,
   DNS_INTERNAL_QUERY_STRUCT _PTR_ query_ptr,
   uint_16                         error_code,
   uint_32                         query_sock,
   int_32                          actual_error
   )

{ /* Body */

   int_32                            error;
   uint_32                           buffer_size;

   if ( actual_error != DNS_OK ) {
      RTCS_log_error(ERROR_DNS, actual_error, 0, 0, 0);
   } /* Endif */

   buffer_size = DNS_fill_generic_response_buffer( buffer, query_ptr,
                                                   error_code );
   /* Send the response buffer to the client */
   error = sendto(query_sock, buffer, buffer_size, 0,
                  &query_ptr->CLIENT_SIN,
                  sizeof(query_ptr->CLIENT_SIN));
   if (error == RTCS_ERROR) {
      RTCS_log_error(ERROR_DNS, RTCSERR_DNS_UNABLE_TO_SEND_QUERY, 4, 0, 0);
   } /* Endif */

   DNS_free_internal_query( query_ptr );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_check_outstanding_queries()
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/


static void DNS_check_outstanding_queries
   (
   DNS_CONTROL_STRUCT _PTR_ control_ptr
   )

{ /* Body */

   DNS_INTERNAL_QUERY_STRUCT _PTR_   current_ptr;
   int_32                            error;

   current_ptr = DNS_get_next_expired_query( &control_ptr->RES_QUERY_HEAD_PTR );

      while ( current_ptr ) {
         /*
         ** cycle through all queries and retry with different server, or
         ** return "name server error" response to client
         */
         // Start CR 2424
         current_ptr->QUERY_RETRIES--;
         error = DNS_get_best_name_server_for_query( current_ptr, control_ptr );
         // End CR 2424
         
         if ( current_ptr->QUERY_RETRIES <= 0 ) {
            DNS_send_error_response( control_ptr->RES_BUFFER, current_ptr,
                                     DNS_QUERY_REFUSED,
                                     control_ptr->RES_SOCKET, DNS_OK );
            current_ptr = DNS_get_next_expired_query(
                             &control_ptr->RES_QUERY_HEAD_PTR );
            continue;
         } /* Endif */

         if ( error != DNS_OK ) {
            DNS_send_error_response( control_ptr->RES_BUFFER, current_ptr,
                                     DNS_SERVER_FAILURE, control_ptr->RES_SOCKET,
                                     error );
            current_ptr = DNS_get_next_expired_query(
                             &control_ptr->RES_QUERY_HEAD_PTR );
            continue;
         } /* Endif */

         current_ptr->TTL      = DNS_QUERY_RETRY_TIME;
         current_ptr->QUERY_ID = DNS_get_query_id();

         error = DNS_Send_UDP_Query( control_ptr, current_ptr, FALSE );
         if ( error == RTCS_ERROR ) {
            DNS_send_error_response( control_ptr->RES_BUFFER, current_ptr,
                                     DNS_QUERY_REFUSED, control_ptr->RES_SOCKET,
                                     error );
            current_ptr = DNS_get_next_expired_query(
                             &control_ptr->RES_QUERY_HEAD_PTR );
            continue;
         } /* Endif */

         DNS_add_query_to_list( &control_ptr->RES_QUERY_HEAD_PTR, current_ptr );
         current_ptr = DNS_get_next_expired_query(
                          &control_ptr->RES_QUERY_HEAD_PTR );
      } /* Endwhile */

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_update_cache()
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/

static void DNS_update_cache
   (
   DNS_CONTROL_STRUCT _PTR_ control_ptr
   )

{ /* Body */
   DNS_RR_LIST_STRUCT _PTR_ _PTR_ local_cache_ptr = &control_ptr->CACHE_PTR;
   DNS_RR_LIST_STRUCT       _PTR_ previous_cache_ptr = NULL;
   DNS_RR_LIST_STRUCT       _PTR_ next_cache_ptr;
   DNS_RR_LIST_STRUCT       _PTR_ free_cache_ptr;
   DNS_RR_HEADER_STRUCT     _PTR_ previous_rr_head_ptr;
   DNS_RR_HEADER_STRUCT     _PTR_ next_rr_head_ptr;
   DNS_RR_HEADER_STRUCT     _PTR_ free_rr_head_ptr;
   TIME_STRUCT                    time;
   uint_32                        difference;

   _time_get( &time);

   next_cache_ptr = *local_cache_ptr;

   while ( next_cache_ptr != NULL ) {
      next_rr_head_ptr = next_cache_ptr->RR_HEAD_PTR;
      previous_rr_head_ptr = NULL;
      while ( next_rr_head_ptr != NULL ) {
         difference = time.SECONDS - next_rr_head_ptr->LAST_TTL_CHECK;
         if ( next_rr_head_ptr->TTL <= difference ) {
            if ( previous_rr_head_ptr ) {
               previous_rr_head_ptr->NEXT = next_rr_head_ptr->NEXT;
               free_rr_head_ptr           = next_rr_head_ptr;
               next_rr_head_ptr           = next_rr_head_ptr->NEXT;
            } else {
               next_cache_ptr->RR_HEAD_PTR = next_rr_head_ptr->NEXT;
               free_rr_head_ptr            = next_rr_head_ptr;
               next_rr_head_ptr            = next_rr_head_ptr->NEXT;
            } /* Endif */

            DNS_free_resource_record( free_rr_head_ptr );
         } else {
            next_rr_head_ptr->LAST_TTL_CHECK = time.SECONDS;
            next_rr_head_ptr->TTL            =  next_rr_head_ptr->TTL
                                                - difference;
            previous_rr_head_ptr = next_rr_head_ptr;
            next_rr_head_ptr     = next_rr_head_ptr->NEXT;
         } /* Endif */
      } /* Endwhile */

      if ( next_cache_ptr->RR_HEAD_PTR == NULL ) {
         if ( previous_cache_ptr ) {
            previous_cache_ptr->NEXT = next_cache_ptr->NEXT;
            free_cache_ptr = next_cache_ptr;
            next_cache_ptr = next_cache_ptr->NEXT;
         } else {
            *local_cache_ptr = next_cache_ptr->NEXT;
            free_cache_ptr = next_cache_ptr;
            next_cache_ptr = next_cache_ptr->NEXT;
         } /* Endif */

         if (control_ptr->NUM_NAMES_CACHED > 0) {
            control_ptr->NUM_NAMES_CACHED--;
         }/* Endif */
         _mem_free( free_cache_ptr->NAME_PTR );
         RTCS_part_free( free_cache_ptr );
      } else {
         previous_cache_ptr = next_cache_ptr;
         next_cache_ptr     = next_cache_ptr->NEXT;
      } /* Endif */
   } /* Endwhile */
} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_check_packet_for_name()
* Returned Value  : uint_32
* Comments        : Crude, but it will do for now.
*                   We should check every lable for compression, not
*                   just whole names -- could reduce packet size.
*                   (See DNS_assign_answer_middle_ptr() for a more
*                    elegant solution idea)
*                   However, this doesn't violate RFC 1035.
*
*END*-----------------------------------------------------------------*/

static uint_16 DNS_check_packet_for_name
   (
   uchar _PTR_ buffer_ptr,
   uint_32     buffer_size,
   uchar _PTR_ name_ptr
   )
{ /* Body */
   DNS_MESSAGE_HEADER_STRUCT _PTR_ message_head =
      (DNS_MESSAGE_HEADER_STRUCT _PTR_)buffer_ptr;
   uchar _PTR_ temp_ptr = buffer_ptr + sizeof(DNS_MESSAGE_HEADER_STRUCT);
   DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_ response_ptr;
   uint_16 i, num_queries = ntohs(message_head->QDCOUNT);

   /* First, need to check the original queries. */
   for (i = 0; i < num_queries; i++) {
      if (strcmp((char _PTR_)temp_ptr, (char _PTR_)name_ptr) == 0) {
         return((uint_16)(temp_ptr - buffer_ptr));
      } /* Endif */
      if(ntohc(temp_ptr) & DNS_COMPRESSED_NAME_MASK) {
         temp_ptr += sizeof(uint_16);
      } else {
         temp_ptr += strlen((char _PTR_)temp_ptr) + 1;
      } /* Endif */
      temp_ptr += sizeof(DNS_MESSAGE_TAIL_STRUCT);
   } /* Endfor */

   /* Now check the answer sections. */
   while (temp_ptr < buffer_ptr + buffer_size) {
      if (strcmp((char _PTR_)temp_ptr, (char _PTR_)name_ptr) == 0) {
         return((uint_16)(temp_ptr - buffer_ptr));
      } /* Endif */
      if(ntohc(temp_ptr) & DNS_COMPRESSED_NAME_MASK) {
         temp_ptr += sizeof(uint_16);
      } else {
         temp_ptr += strlen((char _PTR_)temp_ptr) + 1;
      } /* Endif */
      response_ptr = (DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_)temp_ptr;
      temp_ptr += sizeof(DNS_RESPONSE_RR_MIDDLE_STRUCT);
      /* Checking the answer section for name as well. */
      if ((ntohs(response_ptr->TYPE) == DNS_PTR ||
           ntohs(response_ptr->TYPE) == DNS_CNAME ||
           ntohs(response_ptr->TYPE) == DNS_NS) &&
          (strcmp((char _PTR_)temp_ptr, (char _PTR_)name_ptr) == 0))
      {
          return((uint_16)(temp_ptr - buffer_ptr));
      } /* Endif */
      temp_ptr += ntohs(response_ptr->RDLENGTH);
   } /* Endwhile */
   return(0);
} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_fill_answer_section_of_buffer()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_fill_answer_section_of_buffer
   (
   DNS_CONTROL_STRUCT   _PTR_ control_ptr,
   DNS_RR_HEADER_STRUCT _PTR_ rr_ptr,
   uchar                _PTR_ buffer_ptr,
   int_32               _PTR_ buffer_size_ptr
   )

{ /* Body */

   DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_ response_ptr;
   uchar                         _PTR_ answer_ptr;
   uchar                         _PTR_ temp_ptr;
   uint_32                             new_buffer_size = *buffer_size_ptr;
   uint_32                             name_size;
   uint_16                             pack_variable, name_location;

   answer_ptr = buffer_ptr + *buffer_size_ptr;

   name_location = DNS_check_packet_for_name(buffer_ptr, *buffer_size_ptr,
      rr_ptr->DOMAIN_NAME_PTR);
   if(name_location) {
      /* Name is already in packet, use offset */
      pack_variable = (DNS_COMPRESSED_NAME_MASK << 8) | name_location;
      htons(answer_ptr, pack_variable);
      new_buffer_size += sizeof( uint_16 );
      answer_ptr      += sizeof( uint_16 );
   } else {
      /* Name doesn't exist in packet yet */
      temp_ptr  = rr_ptr->DOMAIN_NAME_PTR;
      name_size = strlen((char _PTR_)rr_ptr->DOMAIN_NAME_PTR );
      /* Need to include the last '\0' character as well */
      name_size++;
      new_buffer_size += name_size;
      while( name_size != 0 ) {
         htonc(answer_ptr, *temp_ptr);
         answer_ptr++;
         temp_ptr++;
         name_size--;
      } /* Endwhile */
   } /* Endif */

   response_ptr = (DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_)answer_ptr;

   htons(response_ptr->TYPE, rr_ptr->TYPE);
   htons(response_ptr->CLASS, rr_ptr->CLASS);
   htonl(response_ptr->TTL, rr_ptr->TTL);

   new_buffer_size += sizeof( DNS_RESPONSE_RR_MIDDLE_STRUCT );
   answer_ptr      += sizeof( DNS_RESPONSE_RR_MIDDLE_STRUCT );

   switch ( rr_ptr->TYPE ) {
      case DNS_A:
         htons(response_ptr->RDLENGTH, sizeof( _ip_address ));
         htonl(answer_ptr,
            ((DNS_ADDR_RECORD_STRUCT _PTR_)((void _PTR_)rr_ptr))->IPADDR);
         new_buffer_size += sizeof( _ip_address );
         break;

      case DNS_PTR:
      case DNS_CNAME:
      case DNS_NS:
         name_location = DNS_check_packet_for_name(buffer_ptr, *buffer_size_ptr,
            ((DNS_NAME_RECORD_STRUCT _PTR_)((void _PTR_)rr_ptr))->ANS_NAME_PTR);
         if(name_location) {
            /* Name is already in packet, use offset */
            pack_variable = (DNS_COMPRESSED_NAME_MASK << 8) | name_location;
            htons(answer_ptr, pack_variable);
            new_buffer_size += sizeof( uint_16 );
            answer_ptr      += sizeof( uint_16 );
         } else {
            temp_ptr = ((DNS_NAME_RECORD_STRUCT _PTR_)
               ((void _PTR_)rr_ptr))->ANS_NAME_PTR;
            name_size = strlen((char _PTR_)temp_ptr);
            name_size++; /* need the '\0' char */
            htons(response_ptr->RDLENGTH, name_size);
            new_buffer_size += name_size;
            while ( name_size != 0 ) {
               htonc(answer_ptr, *temp_ptr);
               answer_ptr++;
               temp_ptr++;
               name_size--;
            } /* Endwhile */
         } /* Endif */
         break;

      default:
      /*
      ** Unknown rr type, set new_buffer_size to 0, so cached data
      ** will not be used.
      */
      new_buffer_size = 0;
   } /* Endswitch */

   if (new_buffer_size) {
      *buffer_size_ptr = new_buffer_size;
   } /* Endif */

   return( new_buffer_size );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_fill_response_buffer_with_rr_data()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_fill_response_buffer_with_rr_data
   (
   DNS_CONTROL_STRUCT   _PTR_ control_ptr,
   DNS_RR_HEADER_STRUCT _PTR_ record_ptr,
   boolean                    all_data,
   boolean                    resolver_query
   )

{ /* Body */

   DNS_MESSAGE_HEADER_STRUCT _PTR_ message_head_ptr;
   DNS_RR_HEADER_STRUCT      _PTR_ temp_rr_ptr;
   int_32                    _PTR_ buffer_size_ptr;
   uchar                     _PTR_ buffer_ptr;
   uint_32                         buffer_size;
   uint_16                         num_answers = 0;
   uint_16                         pack_variable;

   temp_rr_ptr = record_ptr;

   if ( resolver_query == TRUE ) {
      message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT_PTR)control_ptr->RES_BUFFER;
      buffer_size_ptr  = &control_ptr->RES_BUFFER_SIZE;
   } else {
      message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT_PTR)control_ptr->NS_BUFFER;
      buffer_size_ptr  = &control_ptr->NS_BUFFER_SIZE;
   } /* Endif */
   buffer_ptr = (uchar _PTR_)message_head_ptr;

   pack_variable    = ntohs( message_head_ptr->CONTROL );

   if ( all_data == TRUE ) {
      while ( temp_rr_ptr != NULL ) {
         num_answers++;
         buffer_size = DNS_fill_answer_section_of_buffer( control_ptr, temp_rr_ptr,
            buffer_ptr, buffer_size_ptr );
         temp_rr_ptr = temp_rr_ptr->NEXT;
      } /* Endwhile */
   } else {
      num_answers++;
      buffer_size = DNS_fill_answer_section_of_buffer( control_ptr, temp_rr_ptr, buffer_ptr,
         buffer_size_ptr );
   } /* Endif */

   /* insert return information here */
   htons(message_head_ptr->ANCOUNT, num_answers);
   pack_variable |= DNS_RESPONSE;
   htons(message_head_ptr->CONTROL, pack_variable);

   return( buffer_size );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_rr_canonical_data_exists()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_rr_canonical_data_exists
   (
   DNS_RECURSIVE_CACHE_STRUCT_PTR cache_ptr
   )
{ /* Body */
   uint_32                    buffer_size = 0;
   DNS_RR_LIST_STRUCT   _PTR_ new_list_ptr = cache_ptr->CACHE_HEAD;
   DNS_RR_HEADER_STRUCT _PTR_ record_ptr;

   if (++cache_ptr->NUM_TRIES >= DNS_MAX_NUM_CNAME_RETRIES ) {
      return 0;
   } /* Endif */

   while ( new_list_ptr != NULL ) {
      if( strcmp((char _PTR_)new_list_ptr->NAME_PTR,
                 (char _PTR_)cache_ptr->NAME_PTR) == 0 ) {
         /* The names match, see if the type of data requested exists */
         record_ptr = new_list_ptr->RR_HEAD_PTR;
         /* Search through the records for the data requested. */
         while ( record_ptr != NULL ) {
            if (( cache_ptr->NEW_QUERY_PTR->QTYPE == record_ptr->TYPE ) &&
                ( cache_ptr->NEW_QUERY_PTR->QCLASS == record_ptr->CLASS )) {
               /* We have a hit on data & class, fill buffer with info. */
               buffer_size =
                  DNS_fill_answer_section_of_buffer(cache_ptr->CONTROL_PTR,
                  record_ptr, cache_ptr->BUFFER_PTR,
                  cache_ptr->BUFFER_SIZE_PTR);
               break;
            } else if ( cache_ptr->NEW_QUERY_PTR->QCLASS == record_ptr->CLASS &&
                        record_ptr->TYPE == DNS_CNAME )
            {
               /*
               ** We have an alias, see if the data type exists for the
               ** Canonical Name.
               */
               cache_ptr->NAME_PTR  = ((DNS_NAME_RECORD_STRUCT _PTR_)
                  ((void _PTR_)record_ptr))->ANS_NAME_PTR;
               buffer_size = DNS_rr_canonical_data_exists(cache_ptr);
               if (buffer_size) {
                  /* We've had a hit, put alias hit in the buffer as well.*/
                  buffer_size =
                     DNS_fill_answer_section_of_buffer(cache_ptr->CONTROL_PTR,
                     record_ptr, cache_ptr->BUFFER_PTR,
                     cache_ptr->BUFFER_SIZE_PTR);
               } /* Endif */
               break;
            } else {
               record_ptr = record_ptr->NEXT;
            } /* Endif */
         } /* Endwhile */
         break;
      } else {
         new_list_ptr = new_list_ptr->NEXT;
      } /* Endif */
   } /* Endwhile */

   return(buffer_size);
} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_assign_answer_middle_ptr()
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/

static DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_ DNS_assign_answer_middle_ptr
   (
   uchar _PTR_ answer_head_ptr,
   uint_16     total_answers,
   uint_16     current_answer_num
   )

{ /* Body */

   DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_ rr_middle_ptr = NULL;
   uchar                         _PTR_ temp_ptr;
   uint_32                             name_size = 0;

   if ( total_answers - 1 > current_answer_num ) {
      temp_ptr = answer_head_ptr;

      while( (ntohc(temp_ptr) != '\0') &&
             name_size < DNS_MAX_CHARS_IN_NAME &&
             !(ntohc(temp_ptr) & DNS_COMPRESSED_NAME_MASK)) {
            name_size += ntohc(temp_ptr);
            temp_ptr = temp_ptr + ntohc(temp_ptr) + 1;
      } /* Endwhile */

      if ( ntohc(temp_ptr) & DNS_COMPRESSED_NAME_MASK ) {
         temp_ptr+= 2;
      } else {
         temp_ptr++;
      }/* Endif */

      rr_middle_ptr = (DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_)temp_ptr;
   } /* Endif */

   return( rr_middle_ptr );

} /* Endbody */

/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_assign_answer_pointers()
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/

static void DNS_assign_answer_pointers
   (
   uchar                               _PTR_ buffer,
   uchar                         _PTR_ _PTR_ answer_ptr,
   DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_ _PTR_ answer_middle_ptr
   )

{ /* Body */

   uchar _PTR_ temp_ptr;
   uint_32     name_size;

   temp_ptr = buffer + sizeof(DNS_MESSAGE_HEADER_STRUCT);
   name_size = strlen((char _PTR_)temp_ptr);
   name_size++;

   *answer_ptr = temp_ptr + name_size + sizeof(DNS_MESSAGE_TAIL_STRUCT);
   temp_ptr    = *answer_ptr;

   /* Passing this function two hard coded values to force the assignment */
   *answer_middle_ptr = DNS_assign_answer_middle_ptr( temp_ptr, 3, 0 );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_rr_data_exists()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/


static uint_32 DNS_rr_data_exists
   (
   DNS_INTERNAL_QUERY_STRUCT _PTR_ new_query_ptr,
   DNS_CONTROL_STRUCT        _PTR_ control_ptr,
   uchar                     _PTR_ buffer_ptr,
   int_32                    _PTR_ buffer_size_ptr,
   uint_32                         query_type
   )

{ /* Body */

   DNS_RR_LIST_STRUCT   _PTR_ new_list_ptr, _PTR_ cache_head;
   DNS_RR_LIST_STRUCT   _PTR_ previous_ptr = NULL;
   DNS_RR_HEADER_STRUCT _PTR_ record_ptr;
   DNS_RECURSIVE_CACHE_STRUCT cache;
   DNS_MESSAGE_HEADER_STRUCT _PTR_   message_head_ptr;
   uint_16                    pack_variable;
   uint_32                    ans_count, i, buffer_size = 0;
   int_32                     cmp_result;
   DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_ answer_middle_ptr;
   uchar                _PTR_ answer_head_ptr;
   boolean                    resolver_query;

   if ( query_type == DNS_NS_CACHE_DATA ) {
      new_list_ptr = control_ptr->CACHE_PTR;
      resolver_query = FALSE;
   } else if ( query_type == DNS_NS_AUTH_DATA ) {
      new_list_ptr = control_ptr->NS_ZONE_AUTH_LIST;
      resolver_query = FALSE;
   } else if (query_type == DNS_RES_CACHE_DATA ) {
      new_list_ptr = control_ptr->CACHE_PTR;
      resolver_query = TRUE;
   } /* Endif */
   cache_head = new_list_ptr;

   while ( new_list_ptr != NULL ) {
      cmp_result = strcmp((char _PTR_)new_list_ptr->NAME_PTR,
         (char _PTR_)new_query_ptr->QNAME_PTR );
      if ( cmp_result == 0 ) {
         /* The names match, see if the type of data requested exists */
         record_ptr = new_list_ptr->RR_HEAD_PTR;
         if ( new_query_ptr->QTYPE == DNS_ALL ) {
            buffer_size = DNS_fill_response_buffer_with_rr_data(
                                                            control_ptr,
                                                            record_ptr,
                                                            TRUE,
                                                            resolver_query );
         } else {
            /*
            ** We have a hit on the name, but we only want one specific record,
            ** search through the records for this name for the data requested.
            */
            while ( record_ptr != NULL ) {
               if (( new_query_ptr->QTYPE == record_ptr->TYPE ) &&
                   ( new_query_ptr->QCLASS == record_ptr->CLASS )) {
                  /* We have a hit on data & class, fill buffer with info. */
                  buffer_size = DNS_fill_response_buffer_with_rr_data(
                                                            control_ptr,
                                                            record_ptr,
                                                            FALSE,
                                                            resolver_query );
                  break;
               } else if ( new_query_ptr->QCLASS == record_ptr->CLASS &&
                           record_ptr->TYPE == DNS_CNAME )
               {
                  /*
                  ** We have an alias, see if the data type exists for the
                  ** Canonical Name.
                  */
                  ans_count = ntohs(((DNS_MESSAGE_HEADER_STRUCT _PTR_)buffer_ptr)->ANCOUNT );
                  if(ans_count != 0 ) {
                     /*
                     ** We are checking a response, not a new query, see if the
                     ** answer exists in the packet already.
                     */
                     DNS_assign_answer_pointers(buffer_ptr, &answer_head_ptr,
                        &answer_middle_ptr);
                     for (i = 0; (i <= ans_count) && answer_middle_ptr; i++) {
                         if ((new_query_ptr->QTYPE == ntohs(answer_middle_ptr->TYPE)) &&
                              (new_query_ptr->QCLASS == ntohs(answer_middle_ptr->CLASS)))
                         {
                            buffer_size = *buffer_size_ptr;
                            break;
                         } /* Endif */
                         answer_head_ptr = ((uchar _PTR_)(answer_middle_ptr + 1)) +
                            ntohs(answer_middle_ptr->RDLENGTH);
                         answer_middle_ptr = DNS_assign_answer_middle_ptr(
                            answer_head_ptr, ans_count, i);
                     } /* Endfor */
                     break;
                  } else {
                     cache.NEW_QUERY_PTR = new_query_ptr;
                     cache.CONTROL_PTR = control_ptr;
                     cache.CACHE_HEAD  = cache_head;
                     cache.NAME_PTR    = record_ptr->DOMAIN_NAME_PTR;
                     cache.BUFFER_PTR  = buffer_ptr;
                     cache.BUFFER_SIZE_PTR = buffer_size_ptr;
                     cache.NUM_TRIES       = 0;
                     buffer_size = DNS_rr_canonical_data_exists(&cache);
                     if (buffer_size) {
                        /* insert return information here */
                        message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT _PTR_)buffer_ptr;
                        htons(message_head_ptr->ANCOUNT, cache.NUM_TRIES);
                        pack_variable = ntohs(message_head_ptr->CONTROL);
                        pack_variable |= DNS_RESPONSE;
                        htons(message_head_ptr->CONTROL, pack_variable);
                     } /* Endif */
                     break;
                  } /* Endif */
               } else {
                  record_ptr = record_ptr->NEXT;
               } /* Endif */
            } /* Endwhile */
         } /* Endif */

         break;
      } else {
         previous_ptr = new_list_ptr;
         new_list_ptr = new_list_ptr->NEXT;
      } /* Endif */
   } /* Endwhile */

   if ( previous_ptr != NULL && new_list_ptr != NULL &&
        (query_type == DNS_NS_CACHE_DATA ||
         query_type == DNS_RES_CACHE_DATA)  ) {
      /*
      ** The most recently accessed data in the cache should always be at
      ** the head of the list.  If the new_list_ptr is NULL, cached
      ** data for the name doesn't exist in the list.
      */
      previous_ptr->NEXT    = new_list_ptr->NEXT;
      new_list_ptr->NEXT    = control_ptr->CACHE_PTR;
      control_ptr->CACHE_PTR = new_list_ptr;
   } /* Endif */

   return( buffer_size );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_is_valid_IP_addr()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_is_valid_IP_addr
   (
   uchar _PTR_       buffer_ptr,
   uchar _PTR_       domain_name,
   uchar _PTR_ _PTR_ local_addr_buffer
   )

{ /* Body */
   uchar _PTR_ check_ptr;
   uchar _PTR_ temp_ptr;
   uchar _PTR_ free_buffer;
   uchar       appended_name[] = "in-addr.arpa";
   uint_16     new_location;
   uint_16     label_size;
   int_32      byte_num;
   uint_32     digit_count;
   uint_32     total_size = 0;
   uint_32     i;
   uint_32     j;

   temp_ptr = (uchar _PTR_)RTCS_mem_alloc_zero( DNS_MAX_CHARS_IN_NAME );
   if ( temp_ptr == NULL ) {
      RTCS_log_error(ERROR_DNS, DNS_SERVER_FAILURE, 1, 0, 0);
      return( DNS_SERVER_FAILURE );
   } /* Endif */

   free_buffer = temp_ptr;

   /* first check to see if name is written in correct IP address format */

   check_ptr = domain_name;

   for ( i = 0; i < 4; i++) {
      label_size = ntohc(check_ptr);

      if ( label_size & DNS_COMPRESSED_NAME_MASK ) {
         new_location = ntohs(check_ptr) & DNS_COMPRESSED_LOCATION_MASK;
         check_ptr    = buffer_ptr + new_location;
         label_size   = ntohc(check_ptr);
      } /* Endif */

      /*
      ** Some IP addresses using "in-addr.arpa" do not always have four
      ** labels, if this is the case, break out of the loop.
      */
      if ( label_size == 7 ) {
         break;
      } /* Endif */

      *temp_ptr++ = label_size;
      check_ptr++;
      total_size++;
      digit_count = 0;

     if (( label_size < 1 ) || ( label_size > 3 )) {
         _mem_free( free_buffer );
         return( RTCSERR_DNS_INVALID_IP_ADDR );
      } /* Endif */

      for ( j = 0; j < label_size; j++ ) {
         if ( digit_count == 0 ) {
              byte_num = 0;
         } /* Endif */
         digit_count++;

         if ( isdigit(ntohc(check_ptr)) ) {
            /* number is in range */
            byte_num    = byte_num + ntohc(check_ptr) - '0';
            byte_num    = byte_num * 10;
            *temp_ptr++ = ntohc(check_ptr);
            check_ptr++;
            total_size++;
         } else {
            /* if the characters are not decimal digits it's invalid */
            _mem_free( free_buffer );
            return( RTCSERR_DNS_INVALID_IP_ADDR );
         } /* Endif */
      } /* Endfor */

      byte_num = byte_num / 10; /* shift back */

      if ( (byte_num < 0 ) || (byte_num > 255) ) {
         /* if the number does fall within this range it's invalid */
            _mem_free( free_buffer );
            return( RTCSERR_DNS_INVALID_IP_ADDR );
         } /* Endif */
   } /* Endfor */

   if ( ntohc(check_ptr) != 7 ) {
      if ( ntohc(check_ptr) & DNS_COMPRESSED_NAME_MASK ) {
         new_location = ntohs(check_ptr) & DNS_COMPRESSED_LOCATION_MASK;
         check_ptr     = buffer_ptr + new_location;
         if ( ntohc(check_ptr) != 7 ) {
            _mem_free( free_buffer );
            return( RTCSERR_DNS_INVALID_IP_ADDR );
         } /* Endif */

      }else {
         _mem_free( free_buffer );
         return( RTCSERR_DNS_INVALID_IP_ADDR  );
      } /* Endif */
   } /* Endif */

   *temp_ptr++ = ntohc(check_ptr);
   check_ptr++;

   /* should be "in-addr" */
   for ( i = 0; i < 7;  i++) {
      if ( tolower(ntohc(check_ptr)) != appended_name[i] ) {
         _mem_free( free_buffer );
         return( RTCSERR_DNS_INVALID_IP_ADDR  );
      } /* Endif */
      *temp_ptr++ = appended_name[i];
      check_ptr++;
   } /* Endfor */

   if ( ntohc(check_ptr) != 4 ) {
      if ( ntohc(check_ptr) & DNS_COMPRESSED_NAME_MASK ) {
         new_location = ntohs(check_ptr) & DNS_COMPRESSED_LOCATION_MASK;
         check_ptr     = buffer_ptr + new_location;
         if ( ntohc(check_ptr) != 4 ) {
            _mem_free( free_buffer );
            return( RTCSERR_DNS_INVALID_IP_ADDR );
         } /* Endif */

      }else {
         _mem_free( free_buffer );
         return( RTCSERR_DNS_INVALID_IP_ADDR  );
      } /* Endif */
   } /* Endif */

   *temp_ptr++ = ntohc(check_ptr);
   check_ptr++;
   i++;

   /* should be "arpa" */
   for ( ; i < 12;  i++) {
      if ( tolower(ntohc(check_ptr)) != appended_name[i] ) {
         _mem_free( free_buffer );
         return( RTCSERR_DNS_INVALID_IP_ADDR  );
      } /* Endif */
      *temp_ptr++ = appended_name[i];
      check_ptr++;
   } /* Endfor */

   /*
   ** If we got this far, it's a legal IP addr, get a buffer,
   ** fill it with the address.
   */

   /*
   ** Total size is the size of the IP address, 14 is the size of
   ** "in-addr.arpa" including all label length chars and the terminating
   ** '\0' character.
   */
   total_size += sizeof(appended_name) + 2;
   *local_addr_buffer = (uchar _PTR_)RTCS_mem_alloc_zero( total_size );
   if ( *local_addr_buffer == NULL ) {
      // START CR 2178
      _mem_free( free_buffer );
      // END CR 2178
      RTCS_log_error(ERROR_DNS, DNS_SERVER_FAILURE, 2, 0, 0);
      return( DNS_SERVER_FAILURE );
   } /* Endif */

   _mem_copy(free_buffer, *local_addr_buffer, total_size);
   _mem_free( free_buffer );

   return( DNS_OK );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_is_valid_domain_name()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/


uint_32 DNS_is_valid_domain_name
   (
   uchar _PTR_         buffer_ptr,
   uchar _PTR_         domain_name,
   uchar _PTR_  _PTR_  local_name_buffer
   )

{ /* Body */
   uchar _PTR_  fill_ptr;
   uchar _PTR_  free_ptr;
   uchar _PTR_  check_name;
   uint_16      new_location;
   uint_32      total_length = 0;
   uint_32      label_size;
   uint_32      error;
   uint_32      i;

   error = DNS_is_valid_IP_addr(buffer_ptr, domain_name, local_name_buffer);
   if ( error != DNS_OK ) {
       /* If the request is for the ROOT Domain Name */
      if ( ntohc(domain_name) == '\0' ) {
         *local_name_buffer = (uchar _PTR_)RTCS_mem_alloc_zero( 1 );
         if ( *local_name_buffer == NULL ) {
            RTCS_log_error(ERROR_DNS, DNS_SERVER_FAILURE, 3, 0, 0);
            return( DNS_SERVER_FAILURE );
         } /* Endif */

         (*local_name_buffer)[0] = '\0';
         return( DNS_OK );
      } /* Endif */

      fill_ptr = (uchar _PTR_)RTCS_mem_alloc_zero( DNS_MAX_CHARS_IN_NAME );
      if ( fill_ptr == NULL ) {
         RTCS_log_error(ERROR_DNS, DNS_SERVER_FAILURE, 4, 0, 0);
         return( DNS_SERVER_FAILURE );
      } /* Endif */

      free_ptr = fill_ptr;
      check_name = domain_name;
      while ( ntohc(check_name) != '\0' ) {
         if ( ntohc(check_name) & DNS_COMPRESSED_NAME_MASK ) {
              new_location = ntohs(check_name) & DNS_COMPRESSED_LOCATION_MASK;
              check_name   = buffer_ptr + new_location;
         } /* Endif */

         label_size  = ntohc(check_name);
         *fill_ptr++ = label_size;
         check_name++;
         total_length++;

         /*
         ** RFC 1035 says labels must start with a letter, but in practice
         ** some aren't
         */
         if ( isalnum(ntohc(check_name)) ) {
            *fill_ptr++ = ntohc(check_name);
            check_name++;
            total_length++;
         } else {
            _mem_free( free_ptr );
            return( RTCSERR_DNS_INVALID_NAME );
         } /* Endif */


         for ( i = 1; i < label_size; i++ ) {
            /* Domain Name characters can only be letters, hyphens, or numbers */
            if ((isalnum(ntohc(check_name)) || ntohc(check_name) == '-' ) &&
                total_length < DNS_MAX_CHARS_IN_NAME ) {
               *fill_ptr++ = ntohc(check_name);
               check_name++;
               total_length++;
            } else {
               _mem_free( free_ptr );
               return( RTCSERR_DNS_INVALID_NAME );
            } /* Endif */
         } /* Endfor */
      } /* Endwhile */

      /* This is to append the terminating '\0' character to the string */
      *fill_ptr = '\0';
      total_length++;

      *local_name_buffer = (uchar _PTR_)RTCS_mem_alloc_zero( total_length );
      if ( *local_name_buffer == NULL ) {
          /* START CR 2178 */
            _mem_free( free_ptr );
          /* END CR 2178 */
            RTCS_log_error(ERROR_DNS, DNS_SERVER_FAILURE, 5, 0, 0);
            return( DNS_SERVER_FAILURE );
      } /* Endif */

      _mem_copy(free_ptr, *local_name_buffer, total_length);
      _mem_free( free_ptr );
      error = DNS_OK;
   } /* Endif */

   return( error );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_handle_new_query()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

 static uint_32 DNS_handle_new_query
   (
   DNS_CONTROL_STRUCT _PTR_ control_ptr,
   sockaddr_in        _PTR_ client_sin_ptr
   )

{ /* Body */

   DNS_INTERNAL_QUERY_STRUCT _PTR_  new_query_ptr;
   DNS_MESSAGE_HEADER_STRUCT _PTR_  message_head_ptr;
   DNS_MESSAGE_TAIL_STRUCT   _PTR_  message_tail_ptr;
   uchar                     _PTR_  qname_ptr;
   uchar                     _PTR_  temp_ptr;
   TIME_STRUCT                      time;
   uint_32                          buffer_size;
   uint_32                          local_name_size;
/* Start CR 2171 */   
   int_32                           error=DNS_OK;
/* End CR 2171 */   

   _time_get( &time );

   message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT _PTR_)control_ptr->RES_BUFFER;
   qname_ptr = control_ptr->RES_BUFFER + sizeof( DNS_MESSAGE_HEADER_STRUCT );


   new_query_ptr = (DNS_INTERNAL_QUERY_STRUCT _PTR_)
      RTCS_part_alloc_zero(control_ptr->INT_QUERY_PARTID);
   if ( new_query_ptr == NULL ) {
      RTCS_log_error( ERROR_DNS, RTCSERR_DNS_UNABLE_TO_ALLOCATE_MEMORY, 0, 0, 0);
      DNS_send_no_query_error_response( control_ptr, DNS_SERVER_FAILURE,
                                        client_sin_ptr );
      return( DNS_SERVER_FAILURE );
   } /* Endif */

   temp_ptr = qname_ptr;

   local_name_size = 0;
   while( (ntohc(temp_ptr) != '\0') &&
          local_name_size < DNS_MAX_CHARS_IN_NAME ) {
      local_name_size = local_name_size + ntohc(temp_ptr) + 1;
      temp_ptr  = temp_ptr  + ntohc(temp_ptr) + 1;
   } /* Endwhile */

   /* to include the last NULL (or compressed pointer) char */
   temp_ptr++;
   message_tail_ptr = (DNS_MESSAGE_TAIL_STRUCT _PTR_)temp_ptr;

   switch ( ntohs(message_tail_ptr->QTYPE) ) {

      case DNS_A:
         error = DNS_is_valid_domain_name( control_ptr->RES_BUFFER, qname_ptr,
                                           &new_query_ptr->QNAME_PTR );
         break;

      case DNS_PTR:
         error = DNS_is_valid_IP_addr( control_ptr->RES_BUFFER, qname_ptr,
                                       &new_query_ptr->QNAME_PTR );
         break;

      default:
         // Start CR 2171
         DNS_free_internal_query( new_query_ptr );
         DNS_send_no_query_error_response(control_ptr, DNS_QUERY_NOT_IMPLEMENTED,
                                          client_sin_ptr );
         return DNS_OK;
         // End CR 2171
   } /* Endswitch */

   if ( error == RTCSERR_DNS_INVALID_NAME || error == RTCSERR_DNS_INVALID_IP_ADDR ) {
      DNS_free_internal_query( new_query_ptr );
      DNS_send_no_query_error_response( control_ptr, DNS_NAME_ERROR,
                                        client_sin_ptr );
      return( DNS_OK );
   } /* Endif */

   if ( error == DNS_SERVER_FAILURE ) {
      DNS_free_internal_query( new_query_ptr );
      DNS_send_no_query_error_response( control_ptr, DNS_SERVER_FAILURE,
                                        client_sin_ptr );
      return( DNS_OK );
   } /* Endif */

   new_query_ptr->QTYPE          = ntohs(message_tail_ptr->QTYPE);
   new_query_ptr->QCLASS         = ntohs(message_tail_ptr->QCLASS);
   new_query_ptr->CONTROL        = ntohs(message_head_ptr->CONTROL)
                                   | DNS_RECURSION_DESIRED;

   new_query_ptr->CLIENT_SIN.sin_family      = client_sin_ptr->sin_family;
   new_query_ptr->CLIENT_SIN.sin_port        = client_sin_ptr->sin_port;
   new_query_ptr->CLIENT_SIN.sin_addr.s_addr =
                                            client_sin_ptr->sin_addr.s_addr;
   /*
   ** Check to see if the information is in the local cache. If it is,
   ** return it to the client, otherwise, make the query to a name server
   */
   buffer_size = DNS_rr_data_exists( new_query_ptr, control_ptr,
      control_ptr->RES_BUFFER, &control_ptr->RES_BUFFER_SIZE,
      DNS_RES_CACHE_DATA );

   if ( buffer_size != 0 ) {
      error = DNS_return_UDP_response_to_query( new_query_ptr,
                                                control_ptr->RES_BUFFER,
                                                buffer_size,
                                                control_ptr->RES_SOCKET );
      return( DNS_OK );
   } /* Endif */

   new_query_ptr->TTL            = DNS_QUERY_RETRY_TIME;
   new_query_ptr->QUERY_ID       = DNS_get_query_id();
   new_query_ptr->QUERY_RETRIES  = DNS_MAX_NUMBER_OF_QUERY_TRIES;
   new_query_ptr->LAST_TTL_CHECK = time.SECONDS;
   new_query_ptr->NEXT           = NULL;
   new_query_ptr->SERVER_IPADDR  = 0;

   error = DNS_get_best_name_server_for_query( new_query_ptr,
                                               control_ptr );
   if ( error != DNS_OK ) {
      DNS_send_error_response( control_ptr->RES_BUFFER, new_query_ptr,
                               DNS_SERVER_FAILURE, control_ptr->RES_SOCKET,
                               error );
      return( RTCSERR_DNS_UNABLE_TO_SEND_QUERY );
   } /* Endif */

   error = DNS_Send_UDP_Query( control_ptr, new_query_ptr, FALSE );
   if ( error != DNS_OK ) {
      DNS_send_error_response( control_ptr->RES_BUFFER, new_query_ptr,
                               DNS_SERVER_FAILURE, control_ptr->RES_SOCKET,
                               error );
	   return( RTCSERR_DNS_UNABLE_TO_SEND_QUERY );
   } /* Endif */

   error = DNS_add_query_to_list( &control_ptr->RES_QUERY_HEAD_PTR,
                                  new_query_ptr );
   if ( error != DNS_OK ) {
      DNS_send_error_response( control_ptr->RES_BUFFER, new_query_ptr,
                               DNS_SERVER_FAILURE, control_ptr->RES_SOCKET,
                               error );
   } /* Endif */

   return( error );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_retrieve_query()
* Returned Value  : DNS_INTERNAL_QUERY_STRUCT_PTR
* Comments        :
*
*END*-----------------------------------------------------------------*/


static DNS_INTERNAL_QUERY_STRUCT _PTR_ DNS_retrieve_query
   (
   uint_16                               id,
   DNS_INTERNAL_QUERY_STRUCT _PTR_ _PTR_ query_head_ptr
   )

{ /* Body */

   DNS_INTERNAL_QUERY_STRUCT _PTR_ previous_ptr = NULL;
   DNS_INTERNAL_QUERY_STRUCT _PTR_ next_query_ptr;

   next_query_ptr  = *query_head_ptr;

   while ( next_query_ptr != NULL ) {
      if ( next_query_ptr->QUERY_ID == id ) {
         if ( previous_ptr != NULL ) {
            previous_ptr->NEXT  = next_query_ptr->NEXT;
            break;
         } else {
            *query_head_ptr = next_query_ptr->NEXT;
            break;
         } /* Endif */
      } /* Endif */
      previous_ptr = next_query_ptr;
      next_query_ptr = next_query_ptr->NEXT;
   } /* Endwhile */

   return( next_query_ptr );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_remove_last_entry_from_cache
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/
static void DNS_remove_last_entry_from_cache
   (
   DNS_CONTROL_STRUCT _PTR_ control_ptr
   )
{ /* Body */
   DNS_RR_LIST_STRUCT _PTR_  _PTR_ rr_list_ptr = &control_ptr->CACHE_PTR;
   DNS_RR_HEADER_STRUCT      _PTR_ rr_ptr;
   DNS_RR_HEADER_STRUCT      _PTR_ free_ptr;

   
   if (*rr_list_ptr) {
      while ((*rr_list_ptr)->NEXT) {
         /* Run the list, we have to delete the last entry. */
         rr_list_ptr = &(*rr_list_ptr)->NEXT;
      } /* Endwhile */
   
      /* Delete the entry */
      if ( control_ptr->NUM_NAMES_CACHED > 0 ) {
         control_ptr->NUM_NAMES_CACHED--;
      }/* Endif */
      
      rr_ptr = (*rr_list_ptr)->RR_HEAD_PTR;
      while (rr_ptr) {
         free_ptr = rr_ptr;
         rr_ptr = rr_ptr->NEXT;
         DNS_free_resource_record(free_ptr);
      } /* Endwhile */
      
      _mem_free((*rr_list_ptr)->NAME_PTR);
      RTCS_part_free(*rr_list_ptr);
      *rr_list_ptr = NULL;
   }/* Endif */
} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_add_to_cache()
* Returned Value  : void
* Comments        :
*
*END*-----------------------------------------------------------------*/

static void DNS_add_to_cache
   (
   DNS_CONTROL_STRUCT _PTR_ control_ptr,
   uint_32                 module
   )

{ /* Body */

   DNS_MESSAGE_HEADER_STRUCT     _PTR_ message_head_ptr;
   DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_ answer_middle_ptr;
   DNS_RR_HEADER_STRUCT          _PTR_ rr_head_ptr;
   DNS_RR_HEADER_STRUCT          _PTR_ temp_cache_ptr;
   DNS_RR_HEADER_STRUCT          _PTR_ previous_cache_ptr = NULL;
   DNS_RR_LIST_STRUCT            _PTR_ rr_list_ptr;
   DNS_ADDR_RECORD_STRUCT        _PTR_ addr_rr_ptr;
   DNS_NAME_RECORD_STRUCT        _PTR_ name_rr_ptr;
   TIME_STRUCT                         time;
   uchar                         _PTR_ buffer_ptr = NULL;
   uchar                         _PTR_ answer_head_ptr;
   uchar                         _PTR_ temp_ptr;
   uint_16                             num_answers;
   uint_16                             control;
   uint_16                             answer_length;
   uint_32                             i;
   uint_32                             error;
   int_32                              cmp_val;
   int_32                              ttl;

   if ( module == DNS_NS_CACHE_DATA ) {
      buffer_ptr = control_ptr->NS_BUFFER;
   } /* Endif */

   if ( module == DNS_RES_CACHE_DATA ) {
      buffer_ptr = control_ptr->RES_BUFFER;
   } /* Endif */

   if ( buffer_ptr != NULL ) {
      DNS_assign_answer_pointers(buffer_ptr, &answer_head_ptr,
                                 &answer_middle_ptr );
      message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT _PTR_)buffer_ptr;
      num_answers = ntohs(message_head_ptr->ANCOUNT);

      control = ntohs(message_head_ptr->CONTROL);
      if ( (control & DNS_TRUNCATION) == 0 ) {
         /* If the truncation bit is not set, cache information */
         for ( i = 0; (i < num_answers) && answer_middle_ptr; i++ ) {
            ttl           = ntohl(answer_middle_ptr->TTL);
            answer_length = ntohs(answer_middle_ptr->RDLENGTH);
            temp_ptr      = (uchar _PTR_)answer_middle_ptr;
            temp_ptr      += sizeof(DNS_RESPONSE_RR_MIDDLE_STRUCT);

            if ( (ttl == 0) || (ttl > DNS_MAX_ALLOWED_TTL)) {

               /* bypass this answer, set the pointers for the next one */
               temp_ptr = temp_ptr + answer_length;
               answer_head_ptr = temp_ptr;
               answer_middle_ptr = DNS_assign_answer_middle_ptr(answer_head_ptr,
                                                                num_answers, i);
               continue;
            } /* Endif */

            if ((control & DNS_NAME_ERROR) != 0 ) {
               if ((control & DNS_AUTHORITATIVE_NAME_ERROR) !=
                   DNS_AUTHORITATIVE_NAME_ERROR ) {
                  /*
                  ** Don't cache this answer if the name error isn't
                  ** authoritative.
                  */
                  break;
               } /* Endif */
            } /* Endif */

            switch ( ntohs(answer_middle_ptr->TYPE) ) {
               case DNS_A:
                  addr_rr_ptr = (DNS_ADDR_RECORD_STRUCT _PTR_)
                     RTCS_part_alloc_zero(control_ptr->ADDR_RR_PARTID);
                  if (!addr_rr_ptr) {
                    RTCS_log_error(ERROR_DNS, DNS_SERVER_FAILURE, 5, 0, 0);
                    return;
                  }/* Endif */
                  addr_rr_ptr->IPADDR = ntohl((uchar_ptr)temp_ptr);
                  rr_head_ptr = (DNS_RR_HEADER_STRUCT _PTR_)
                     ((void _PTR_)addr_rr_ptr);
                  break;

               case DNS_PTR:
               case DNS_CNAME:
               case DNS_NS:
                  name_rr_ptr = (DNS_NAME_RECORD_STRUCT _PTR_)
                     RTCS_part_alloc_zero(control_ptr->NAME_RR_PARTID);
                  if (!name_rr_ptr) {
                    RTCS_log_error(ERROR_DNS, DNS_SERVER_FAILURE, 5, 0, 0);
                    return;
                  }/* Endif */
                  error = DNS_is_valid_domain_name(buffer_ptr, temp_ptr,
                                                &name_rr_ptr->ANS_NAME_PTR );
                  if ( error != DNS_OK ) {
                     RTCS_part_free(name_rr_ptr);
                     /* set pointers for next answer */
                     temp_ptr = temp_ptr + answer_length;
                     answer_head_ptr = temp_ptr;
                     answer_middle_ptr = DNS_assign_answer_middle_ptr(
                                                              answer_head_ptr,
                                                              num_answers, i );
                     continue;
                  } /* Endif */
                  rr_head_ptr = (DNS_RR_HEADER_STRUCT _PTR_)
                     ((void _PTR_)name_rr_ptr);
                  break;

               default:
                  /* Unknown type, don't cache */
                  /* set pointers for next answer */
                  temp_ptr = temp_ptr + answer_length;
                  answer_head_ptr = temp_ptr;
                  answer_middle_ptr = DNS_assign_answer_middle_ptr(
                                                              answer_head_ptr,
                                                              num_answers, i );
                  continue;
            } /* Endswitch */

            if ( (control & DNS_AUTHORITATIVE_NAME_ERROR) !=
                 DNS_AUTHORITATIVE_NAME_ERROR ) {
               rr_head_ptr->TTL = ttl;
            } else {
               rr_head_ptr->TTL = DNS_NAME_ERROR_TTL;
            } /* Endif */

            rr_head_ptr->CLASS = ntohs(answer_middle_ptr->CLASS);
            rr_head_ptr->TYPE  = ntohs(answer_middle_ptr->TYPE);
            rr_head_ptr->NEXT  = NULL;

            /*
            ** May have to check name format here. Answers may truncate
            ** differently when same answer name is used more than once.
            */
            error = DNS_is_valid_domain_name(buffer_ptr, answer_head_ptr,
                                             &rr_head_ptr->DOMAIN_NAME_PTR );
            if ( error != DNS_OK ) {
               DNS_free_resource_record( rr_head_ptr );
               /* set pointers for next answer */
               temp_ptr = temp_ptr + answer_length;
               answer_head_ptr = temp_ptr;
               answer_middle_ptr = DNS_assign_answer_middle_ptr(
                                                              answer_head_ptr,
                                                              num_answers, i );
               continue;
            } /* Endif */

            _time_get( &time );
            rr_head_ptr->LAST_TTL_CHECK = time.SECONDS;
            /*
            ** need exclusive access, both the Resolver and Name Server can
            ** modify the cache
            */
            RTCS_mutex_lock(&control_ptr->CACHE_MUTEX);
            rr_list_ptr = control_ptr->CACHE_PTR;

            while ( rr_list_ptr != NULL ) {
               cmp_val = strcmp((char _PTR_)rr_head_ptr->DOMAIN_NAME_PTR,
                                (char _PTR_)rr_list_ptr->NAME_PTR );
               if ( cmp_val == 0 ) {
                  /* The two are equal, cached data for this name exists. */
                  break;
               } /* Endif */
               rr_list_ptr = rr_list_ptr->NEXT;
            } /* Endwhile */

            if ( rr_list_ptr == NULL ) {
               /* We're either at the head or the tail of the list */
               rr_list_ptr = (DNS_RR_LIST_STRUCT _PTR_)
                  RTCS_part_alloc_zero(control_ptr->RR_LIST_PARTID);
               if ( rr_list_ptr == NULL ) {
                  RTCS_log_error(ERROR_DNS, RTCSERR_DNS_UNABLE_TO_ALLOCATE_MEMORY, 0, 0, 0);
                  _mem_free( rr_head_ptr->DOMAIN_NAME_PTR );
                  DNS_free_resource_record( rr_head_ptr );
                  /* set pointers for next answer */
                  temp_ptr = temp_ptr + answer_length;
                  answer_head_ptr = temp_ptr;
                  answer_middle_ptr = DNS_assign_answer_middle_ptr(
                                                              answer_head_ptr,
                                                              num_answers, i );
                  continue;
               } /* Endif */

               rr_list_ptr->NAME_PTR    = rr_head_ptr->DOMAIN_NAME_PTR;
               rr_list_ptr->RR_HEAD_PTR = rr_head_ptr;
               rr_list_ptr->NEXT        = control_ptr->CACHE_PTR;
               control_ptr->CACHE_PTR    = rr_list_ptr;
               control_ptr->NUM_NAMES_CACHED++;
            } else {
               /*
               ** Cached data using the same name exists, search the
               ** existing sub-list under the name for the same record type
               */
               temp_cache_ptr = rr_list_ptr->RR_HEAD_PTR;
               while ( temp_cache_ptr != NULL ) {
                  if ( temp_cache_ptr->TYPE == rr_head_ptr->TYPE ) {
                  /* cached data of this type exists, replace with new stuff */
                     break;
                  } /* Endif */
                  previous_cache_ptr = temp_cache_ptr;
                  temp_cache_ptr     = temp_cache_ptr->NEXT;
               } /* Endwhile */

               if ( temp_cache_ptr == NULL ) {
                  /* data of this type doesn't exist. Insert new stuff */
                  rr_head_ptr->NEXT        = rr_list_ptr->RR_HEAD_PTR;
               } else {
                  /* This data exists previously, replace it */
                  rr_head_ptr->NEXT = rr_list_ptr->RR_HEAD_PTR->NEXT;

                  if ( previous_cache_ptr == NULL ) {
                     /* We're at the head of the list */
                     DNS_free_resource_record( rr_list_ptr->RR_HEAD_PTR );
                  } else {
                     previous_cache_ptr->NEXT = temp_cache_ptr->NEXT;
                     DNS_free_resource_record( temp_cache_ptr );
                  } /* Endif */
               } /* Endif */
               rr_list_ptr->RR_HEAD_PTR = rr_head_ptr;
               /* 
               ** We only keep one reference to the name for the resource
               ** records for any given name.  Since a node was pre-existing,
               ** just have this record refer to it's string, and free the
               ** one we allocated.
               */
               _mem_free(rr_head_ptr->DOMAIN_NAME_PTR);
               rr_head_ptr->DOMAIN_NAME_PTR = rr_list_ptr->NAME_PTR;
            } /* Endif */
            RTCS_mutex_unlock(&control_ptr->CACHE_MUTEX);
            temp_ptr = temp_ptr + answer_length;
            answer_head_ptr = temp_ptr;
            answer_middle_ptr = DNS_assign_answer_middle_ptr( answer_head_ptr,
                                                              num_answers, i );
         } /* Endfor */

         while(DNS_CACHE_NAME_LIMIT && 
            (control_ptr->NUM_NAMES_CACHED > DNS_CACHE_NAME_LIMIT) && 
            /* This is here as a paranoid check to break out of loop. */
            control_ptr->CACHE_PTR) 
         {
            /* 
            ** We're over the limit, delete the last entry in the cache until
            ** we're at the limit. 
            */
            DNS_remove_last_entry_from_cache(control_ptr);
         }/* Endwhile */
      } /* Endif */
   } /* Endif */
} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_retry_query_with_CNAME()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_retry_query_with_CNAME
   (
   DNS_CONTROL_STRUCT         _PTR_ control_ptr,
   DNS_INTERNAL_QUERY_STRUCT _PTR_ query_ptr,
   DNS_RESPONSE_RR_MIDDLE_STRUCT  _PTR_ answer_ptr
   )

{ /* Body */
   DNS_CNAME_STRUCT  _PTR_ _PTR_  previous_ptr;
   DNS_CNAME_STRUCT  _PTR_  temp_ptr;
   DNS_CNAME_STRUCT  _PTR_  new_cname_ptr;
   uchar             _PTR_  cname_ptr;
   int_32                   error;
   uint_32                  cname_retries = 0;
   int_32                   cmp_result;

   cname_ptr = (uchar _PTR_)answer_ptr;
   cname_ptr += sizeof( DNS_RESPONSE_RR_MIDDLE_STRUCT );

   new_cname_ptr = (DNS_CNAME_STRUCT _PTR_)
      RTCS_part_alloc_zero(control_ptr->CNAME_PARTID);
   if ( !new_cname_ptr ) {
      RTCS_log_error(ERROR_DNS, DNS_SERVER_FAILURE, 5, 0, 0);
      return( DNS_SERVER_FAILURE );
   }/* Endif */
   error = DNS_is_valid_domain_name( control_ptr->RES_BUFFER, cname_ptr,
                           (uchar _PTR_ _PTR_)&new_cname_ptr->CNAME_PTR );
   if ( error != DNS_OK ) {
      DNS_send_error_response( control_ptr->RES_BUFFER,
                               query_ptr, DNS_NAME_ERROR,
                               control_ptr->RES_SOCKET, DNS_OK );
   } /* Endif */

   previous_ptr = &query_ptr->CNAME;

   temp_ptr = *previous_ptr;

   while ( temp_ptr != NULL ) {
      cmp_result = strcmp((char _PTR_)new_cname_ptr->CNAME_PTR,
         (char _PTR_)temp_ptr->CNAME_PTR );
      if ( cmp_result == 0 ) {
         /* the names are equal, we have a CNAME loop, return error */
         _mem_free(new_cname_ptr->CNAME_PTR);
         RTCS_part_free(new_cname_ptr);
         DNS_send_error_response( control_ptr->RES_BUFFER,
                                  query_ptr, DNS_NAME_ERROR,
                                  control_ptr->RES_SOCKET, DNS_OK );
         error = DNS_NAME_ERROR;
         break;
      } /* Endif */
      cname_retries++;
      if ( cname_retries == DNS_MAX_NUM_CNAME_RETRIES ) {
         /* BIND only allows for 8 CNAME query retries */
         _mem_free(new_cname_ptr->CNAME_PTR);
         RTCS_part_free(new_cname_ptr);
         error = DNS_return_UDP_response_to_query( query_ptr,
                                                   control_ptr->RES_BUFFER,
                                                   control_ptr->RES_BUFFER_SIZE,
                                                   control_ptr->RES_SOCKET );
         error = DNS_NAME_ERROR;
         break;
      } /* Endif */
      previous_ptr = &(*previous_ptr)->NEXT;
      temp_ptr     = *previous_ptr;
   } /* Endwhile */

   if ( error == DNS_OK ) {
      *previous_ptr  = new_cname_ptr;
      new_cname_ptr->NEXT = NULL;
      query_ptr->QUERY_ID  = DNS_get_query_id();

      error = DNS_Send_UDP_Query( control_ptr, query_ptr, TRUE );
      if ( error == RTCS_ERROR ) {
         DNS_send_error_response( control_ptr->RES_BUFFER, query_ptr,
                                  DNS_SERVER_FAILURE, control_ptr->RES_SOCKET,
                                  error );
      } else {

      DNS_add_query_to_list( &control_ptr->RES_QUERY_HEAD_PTR, query_ptr );

      } /* Endif */
   } /* Endif */

   return( error );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_search_response_for_referral()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_search_response_for_referral
   (
   DNS_CONTROL_STRUCT         _PTR_ control_ptr,
   DNS_INTERNAL_QUERY_STRUCT _PTR_ query_ptr
   )

{ /* Body */

   /* NOT IMPLEMENTED */

   /* SPR P122-0258-01
   ** This is here to get the resolver to return quickly when a name
   ** that doesn't exist is queried for, since we only support queries
   ** to a single DNS Server for now. When multiple DNS Server support
   ** is added, remove this function call and allow for proper resend
   ** behaviour.
   */

   DNS_send_error_response( control_ptr->RES_BUFFER,
                            query_ptr, DNS_QUERY_NOT_IMPLEMENTED,
                            control_ptr->RES_SOCKET, DNS_OK );
   /* END SPR P122-0258-01 */

   return( DNS_OK );

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_handle_internal_query()
* Returned Value  : DNS_INTERNAL_QUERY_STRUCT_PTR
* Comments        : This function is for the Name Server or Resolver to
*                   make queries for NS records to be used to make queries
*                   for authoritative data if the original query fails.
*
*END*-----------------------------------------------------------------*/

static uint_32 DNS_handle_internal_query
   (
   DNS_CONTROL_STRUCT         _PTR_ control_ptr,
   DNS_INTERNAL_QUERY_STRUCT _PTR_ query_ptr
   )

{ /* Body */
   return( DNS_OK );
} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_Send_TCP_Query()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/



 static uint_32 DNS_Send_TCP_Query
    (
    DNS_CONTROL_STRUCT         _PTR_ control_ptr,
    DNS_INTERNAL_QUERY_STRUCT _PTR_ query_ptr
    )

{ /* Body */

   /* NOT IMPLEMENTED */
   DNS_send_error_response( control_ptr->RES_BUFFER,
                            query_ptr, DNS_QUERY_NOT_IMPLEMENTED,
                            control_ptr->RES_SOCKET, DNS_OK );

   return( DNS_OK );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_handle_UDP_response()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

 static uint_32 DNS_handle_UDP_response
   (
   DNS_CONTROL_STRUCT   _PTR_ control_ptr
   )

{ /* Body */

   DNS_MESSAGE_HEADER_STRUCT     _PTR_  message_head_ptr;
   DNS_MESSAGE_TAIL_STRUCT       _PTR_  message_tail_ptr;
   DNS_INTERNAL_QUERY_STRUCT     _PTR_  query_ptr;
   DNS_RESPONSE_RR_MIDDLE_STRUCT _PTR_  answer_middle_ptr;
   uchar                         _PTR_  answer_ptr;
   TIME_STRUCT                          time;
   uint_16                              answer_count;
   uint_16                              control;
   uint_16                              answer_type;
   int_32                               error = DNS_OK;

   _time_get( &time );
   message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT _PTR_)control_ptr->RES_BUFFER;
   answer_count     = ntohs( message_head_ptr->ANCOUNT );
   control          = ntohs( message_head_ptr->CONTROL );
   query_ptr        = DNS_retrieve_query( ntohs(message_head_ptr->ID),
                                           &control_ptr->RES_QUERY_HEAD_PTR );
   if ( query_ptr != NULL ) {
      /* Query still exists, respond accordingly */

 /* Start CR 1965 */     
         if ( query_ptr->INTERNAL_QUERY == TRUE ) {
            /* Name Server query was made by resolver, not for a client */
            error = DNS_handle_internal_query( control_ptr, query_ptr );
            if ( error != DNS_OK ) {
               DNS_send_error_response( control_ptr->RES_BUFFER,
                                        query_ptr, DNS_SERVER_FAILURE,
                                        control_ptr->RES_SOCKET, error );
            } /* Endif */
         } else {
            if ( answer_count != 0 ) {
               /* An answer was received, respond accordingly */
               /*
               ** Cache the response, so we don't have to request whatever was
               ** given to us in the future.
               */
               DNS_add_to_cache( control_ptr, DNS_RES_CACHE_DATA );
               DNS_assign_answer_pointers( control_ptr->RES_BUFFER, &answer_ptr,
                                           &answer_middle_ptr );
               if ( answer_middle_ptr ) {
                  message_tail_ptr = (DNS_MESSAGE_TAIL_STRUCT _PTR_)(answer_ptr
                     - sizeof(DNS_MESSAGE_TAIL_STRUCT));
                  answer_type = ntohs(answer_middle_ptr->TYPE);

                  if ( answer_type == ntohs(message_tail_ptr->QTYPE) ) {
                     /* Answer is what we wanted as a response, return to client */
                     error = DNS_return_UDP_response_to_query( query_ptr,
                        control_ptr->RES_BUFFER, control_ptr->RES_BUFFER_SIZE,
                        control_ptr->RES_SOCKET );
                  } else {
                     /*
                     ** An answer exists, but it's not what we want.  Check all
                     ** the answers to see if what we are looking for is in the
                     ** buffer at all.  If it is, return the buffer to the client,
                     ** if not, use the information received to make another query
                     */
                     if ( answer_type == DNS_CNAME ) {
                        if ( query_ptr->QUERY_RETRIES-- <= 0 ) {
                           DNS_send_error_response( control_ptr->RES_BUFFER,
                              query_ptr, DNS_QUERY_REFUSED,
                              control_ptr->RES_SOCKET, DNS_OK );
                        } else {
                           /*
                           ** If we get here, an answer exists, but doesn't
                           ** match query type, or there is more than one
                           ** answer, since we've cached the response, check
                           ** the cache again to see if the answer was in the
                           ** packet we received.  If it isn't there, retry the
                           ** query using the cannonical name reference.
                           */
                           if (DNS_rr_data_exists( query_ptr, control_ptr,
                               control_ptr->RES_BUFFER,
                               &control_ptr->RES_BUFFER_SIZE, DNS_RES_CACHE_DATA))
                           {
                              error = DNS_return_UDP_response_to_query(
                                 query_ptr, control_ptr->RES_BUFFER,
                                 control_ptr->RES_BUFFER_SIZE,
                                 control_ptr->RES_SOCKET );
                           } else {
                              error = DNS_retry_query_with_CNAME( control_ptr,
                                 query_ptr, answer_middle_ptr);
                           } /* Endif */
                        } /* Endif */
                     } /* Endif */
                  } /* Endif */
               } /* Endif */
            } else {
               /*
               ** No answer exists in the response.  Check Name Server records
               ** for referrals to retry query.  Also check additional records.
               ** and RCODE for clues to retry the query.
               */
               error = DNS_search_response_for_referral( control_ptr,
                  query_ptr );
                  
               if ( control & DNS_TRUNCATION ) {
                  /* If the message was truncated, retry the query using TCP */
                  error = DNS_Send_TCP_Query( control_ptr, query_ptr );
            } /* Endif */
         } /* Endif */
      } /* Endif */
/* End CR 1965 */
/* Start CR 1119 */
#if 0
   } else {
      /*
      ** Query doesn't exist anymore.  Cache if answer is authoritative,
      ** otherwise, do nothing
      */
      if ( ((control & DNS_RCODE_MASK) == DNS_NO_ERROR ||
            (control & DNS_RCODE_MASK) == DNS_NAME_ERROR) &&
            (control & DNS_AUTHORITATIVE_ANSWER) ) {
         DNS_add_to_cache( control_ptr, DNS_RES_CACHE_DATA );
      } /* Endif */
#endif
/* End CR 1119 */   
   } /* Endif */

   return( error );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_refresh_root_servers_task()
* Returned Value  :
* Comments        :
*
*END*-----------------------------------------------------------------*/

static void DNS_refresh_root_servers_task
   (
   uint_32 temp_ptr
   )
{ /* Body */

  /*NOT IMPLEMENTED */
} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_refresh_root_servers()
* Returned Value  :
* Comments        :
*
*END*-----------------------------------------------------------------*/

 static uint_32 DNS_refresh_root_servers
   (
   DNS_CONTROL_STRUCT _PTR_ control_ptr
   )
{ /* Body */

  /* NOT IMPLEMENTED */
  return( DNS_OK );

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : DNS_Resolver_task()
* Returned Value  :
* Comments        :
*
*END*-----------------------------------------------------------------*/

void DNS_Resolver_task
   (
   pointer new_control_ptr,
   pointer creator
   )
{ /* Body */

   DNS_CONTROL_STRUCT         _PTR_  control_ptr;
   DNS_MESSAGE_HEADER_STRUCT _PTR_  message_head_ptr;
   DNS_SLIST_STRUCT          _PTR_  first_root_ptr;
   sockaddr_in                      addr;
   TIME_STRUCT                      time;
   uint_32                          difference;
   uint_16                          rlen;
   int_32                           error;

   control_ptr = (DNS_CONTROL_STRUCT _PTR_)new_control_ptr;

   /*
   ** Root Server List updating is not yet implemented,
   ** remove "control_ptr->UPDATING = TRUE;" when it is coded.
   */
   control_ptr->UPDATING = TRUE;

   _mem_zero((uchar _PTR_) &addr, sizeof(addr));

   addr.sin_family      = AF_INET;
   addr.sin_port        = DNS_RESOLVER_PORT;
   addr.sin_addr.s_addr = INADDR_ANY;

   error = RTCS_attachsock( control_ptr->RES_SOCKET );
   if ( error == (int_32)RTCS_SOCKET_ERROR ) {
      RTCS_task_exit(creator, RTCSERR_DNS_INITIALIZATION_FAILURE);
   } /* Endif */

   error = bind(control_ptr->RES_SOCKET, &addr, sizeof(sockaddr_in));
   if ( error != RTCS_OK ) {
      RTCS_task_exit(creator, RTCSERR_DNS_INITIALIZATION_FAILURE);
   } /* Endif */
   RTCS_task_resume_creator(creator, RTCS_OK);

   while ( TRUE ) {

      _time_get ( &time );

      /*
      ** Check the TTL expiry time for root name servers, if expired
      ** call refresh function to update them
      */
      first_root_ptr = (DNS_SLIST_STRUCT _PTR_)control_ptr->ROOT_SLIST_PTR;
      difference = time.SECONDS - first_root_ptr->LAST_TTL_CHECK;
      first_root_ptr->LAST_TTL_CHECK = time.SECONDS;

      RTCS_mutex_lock(&control_ptr->CACHE_MUTEX);
      if (first_root_ptr->TTL <= difference
          && ( control_ptr->UPDATING == FALSE )) {
         RTCS_mutex_unlock(&control_ptr->CACHE_MUTEX);
         error = DNS_refresh_root_servers( control_ptr );
         if ( error != DNS_OK ) {
            RTCS_log_error(ERROR_DNS, RTCSERR_DNS_UNABLE_TO_UPDATE_ROOT_SERVERS, 0, 0, 0);
         } /* Endif */
      } else {
         RTCS_mutex_unlock(&control_ptr->CACHE_MUTEX);
         first_root_ptr->TTL = first_root_ptr->TTL - difference;
      } /* Endif */

      DNS_check_outstanding_queries( control_ptr );

      if ( control_ptr->NS_BUFFER == NULL ) {
         /*
         ** If the Name Server task is running, it has the responsibility
         ** to update the cache(using the mutex!), if it isn't, let the 
         ** resolver do it.
         */
         /* 
         ** Don't need the mutex here, since the DNS Server doesn't exist on 
         ** the target, the resolver will be the only task accessing it.
         */
         DNS_update_cache(control_ptr);
      } /* Endif */

      /*
      ** now wait for queries or responses, bypass rest of while loop
      ** if none are received
      */

      error = (uint_32)RTCS_selectset( &control_ptr->RES_SOCKET, 1,
                                       DNS_RESOLVER_RETRY_TIME );

      if (( error == (uint_32)NULL ) || ( error == RTCS_ERROR )) {
         continue;
      } /* Endif */


      /* Received a message, respond accordingly */
      rlen = sizeof(addr);
      control_ptr->RES_BUFFER_SIZE = recvfrom(control_ptr->RES_SOCKET,
                                             control_ptr->RES_BUFFER,
                                             DNS_MAX_UDP_MESSAGE_SIZE,
                                             0, &addr, &rlen);
      if (control_ptr->RES_BUFFER_SIZE == RTCS_ERROR) {
         RTCS_log_error(ERROR_DNS, RTCSERR_DNS_SOCKET_RECV_ERROR, 0, 0, 0);
         continue;
      } /* Endif */

/* Start CR 1119 */
      if (control_ptr->RES_BUFFER_SIZE < sizeof(DNS_MESSAGE_HEADER_STRUCT)) {
         RTCS_log_error(ERROR_DNS, RTCSERR_DNS_SHORT_MESSAGE_ERROR, 0, 0, 0);
         continue;
      } /* Endif */
/* End CR 1119 */

      message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT_PTR)control_ptr->RES_BUFFER;

      if ( ntohs(message_head_ptr->CONTROL) & DNS_RESPONSE ) {
         /* It's a response to a query, parse and return it to client */
         error = DNS_handle_UDP_response( control_ptr );
         if ( error != DNS_OK ) {
            RTCS_log_error( ERROR_DNS, error, 0, 0, 0);
         } /* Endif */
      } else {
         if (addr.sin_addr.s_addr == INADDR_LOOPBACK) {
            /* It's a new query */
            error = DNS_handle_new_query( control_ptr, &addr );
         } else {
            error = ERROR_DNS;
         } /* Endif */
         if ( error != DNS_OK ) {
            RTCS_log_error( ERROR_DNS, error, 0, 0, 0);
         } /* Endif */
      } /* Endif */
   } /* Endwhile */

} /* Endbody */

#endif



#if RTCSCFG_ENABLE_LWDNS

_ip_address LWDNS_server_ipaddr = 0;

/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : LWDNS_Send_Query()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/


static uint_32 LWDNS_Send_Query
   (
   uint_32     sock,
   char_ptr    name_to_resolve,
   char_ptr    send_buffer,
   uint_32_ptr id_ptr
   )
{ 
   DNS_MESSAGE_HEADER_STRUCT_PTR    message_head_ptr;
   DNS_MESSAGE_TAIL_STRUCT_PTR      message_tail_ptr;
   DNS_CNAME_STRUCT_PTR             cname_ptr;
   sockaddr_in                      raddr;
   int_32                           error;
   uint_32                          i,buffer_size;
   uint_32                          name_size;
   char_ptr                         name_ptr,len_ptr;
   uint_16                          id;
   uchar                            len;


   name_size = strlen((char _PTR_)name_to_resolve ) ; //To include the terminating NULL char

   /* set up buffer for sending query.   */
   message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT _PTR_)send_buffer;
   name_ptr  = &send_buffer[sizeof(DNS_MESSAGE_HEADER_STRUCT)];
   id = RTCS_rand(); // TASK_NUMBER_FROM_TASKID(_task_get_id()) ;
   if (id_ptr) {
      *id_ptr = id;
   }
   htons(message_head_ptr->ID, id );
   htons(message_head_ptr->CONTROL, DNS_STANDARD_QUERY|DNS_RECURSION_DESIRED);
   htons(message_head_ptr->QDCOUNT, DNS_SINGLE_QUERY);
   htons(message_head_ptr->ANCOUNT, 0);
   htons(message_head_ptr->NSCOUNT, 0);
   htons(message_head_ptr->ARCOUNT, 0);

   len_ptr=name_ptr;
   len=0;
   if (*name_ptr != '.') 
      name_ptr++;

   for (i=0;i<name_size;i++) {
      if (name_to_resolve[i] == '.') {
         *len_ptr = len;
         len = 0;
         len_ptr = name_ptr;
      } else { 
         htonc(name_ptr, name_to_resolve[i]);
         len++;
      }
     name_ptr++;
   } 
   *len_ptr = len;
    *name_ptr++ = 0;

   message_tail_ptr = (DNS_MESSAGE_TAIL_STRUCT _PTR_) name_ptr;
   name_ptr += sizeof(DNS_MESSAGE_TAIL_STRUCT);
   
   htons(message_tail_ptr->QTYPE, DNS_A);
   htons(message_tail_ptr->QCLASS, DNS_IN);


   error = send(sock, send_buffer, (name_ptr-send_buffer), 0);

   if (error == RTCS_ERROR) {
      error = RTCS_geterror(sock);
   } else {
      error = DNS_OK;
   } 

   return( error );
} 

static boolean domains_equal(char_ptr req_domain, char_ptr res_domain) 
{
 // this needs to be fixed.
   uint_32  req_index=0,res_index=0,i;
   uchar    len, c;
   
   len = res_domain[res_index++];
   do { 
      if (len > 127) {
         return FALSE;
      }
      for (i=0;i<len;i++) {
         if (req_domain[req_index++] != tolower(res_domain[res_index++])) {
            return FALSE;
         }
      }
      len = res_domain[res_index++];
      c = req_domain[req_index++];
   } while (len && (c == '.'));
   return (len ==0) && (c==0);
}

static boolean domain_name_extract(uint_32 start, char_ptr rx_buffer,char_ptr ipname,uint_32 ipnamesize) 
{
   uint_32 s_index=start, d_index=0,i;
   uchar   b;

   do {
      b = rx_buffer[s_index++];
      if (b == DNS_COMPRESSED_NAME_MASK) {
          s_index = rx_buffer[s_index];
      } else {
         for (i=0;i<b;i++) {
            if (d_index >= ipnamesize-1) break;
            ipname[d_index++] = rx_buffer[s_index++];
         }   
         if (d_index >= ipnamesize-1) break;
         ipname[d_index++] = '.';
      }
   } while (rx_buffer[s_index]);
   
   if (d_index) {
      ipname[--d_index] = 0;
   } else {
      ipname[0] = 0;
   }
   
   return TRUE;
}


static uint_32 domain_name_fragment_length(uchar_ptr name) 
{
   uint_32 i=0;
   
   while ( (name[i] != 0) && (name[i] != DNS_COMPRESSED_NAME_MASK)) { 
      i++;
   }
   return (name[i]?i+2:i+1);
}


/*FUNCTION*-------------------------------------------------------------
*
* Function Name   : LWDNS_Get_Response()
* Returned Value  : uint_32
* Comments        :
*
*END*-----------------------------------------------------------------*/

 static boolean LWDNS_Get_Response
   (
   uint_32           sock,
   char_ptr          name_to_resolve,
   char_ptr          rx_buffer,
   uint_32           req_id,
   _ip_address _PTR_ ipaddr_ptr, 
   char_ptr          ipname, 
   uint_32           ipnamesize
   )
{ 
   DNS_MESSAGE_HEADER_STRUCT_PTR       message_head_ptr;
   DNS_MESSAGE_TAIL_STRUCT_PTR         message_tail_ptr;
   DNS_RESPONSE_RR_MIDDLE_STRUCT_PTR   answer_middle_ptr;
   uchar_ptr                           answer_ptr;
   uint_16                             answer_count,query_count;
   uint_16                             control, rdlength;
   uint_16                             answer_type;
   _ip_address                         ip_addr = INADDR_ANY;
   sockaddr_in                         addr;
   uint_16                             rlen,id;
   int_32                              response_size;
   char_ptr                           temp_ptr;
   _ip_address *                       ip_answer_ptr;
   uint_32                             i,offset;



   rlen = sizeof(addr);
   response_size = recv(sock,rx_buffer, DNS_MAX_UDP_MESSAGE_SIZE, 0);
   if ((response_size != RTCS_ERROR)   && (response_size >= sizeof(DNS_MESSAGE_HEADER_STRUCT))) {
      message_head_ptr = (DNS_MESSAGE_HEADER_STRUCT_PTR) rx_buffer;
      id           = ntohs( message_head_ptr->ID );
      query_count  = ntohs( message_head_ptr->QDCOUNT );
      answer_count = ntohs( message_head_ptr->ANCOUNT );
      control      = ntohs( message_head_ptr->CONTROL );
      
      if ( (id == req_id ) && (query_count==1) && answer_count) {
      
         temp_ptr = &rx_buffer[sizeof(DNS_MESSAGE_HEADER_STRUCT)];
         
         
         if (domains_equal(name_to_resolve, temp_ptr)) {
            temp_ptr +=strlen(temp_ptr) + 1 + sizeof(DNS_MESSAGE_TAIL_STRUCT);
         
            for (i=0;i<answer_count;i++) {
               answer_ptr =  (uchar_ptr)temp_ptr;

               offset = domain_name_fragment_length(answer_ptr);
             
               answer_middle_ptr = (DNS_RESPONSE_RR_MIDDLE_STRUCT_PTR) &answer_ptr[offset];
               answer_type = ntohs( answer_middle_ptr->TYPE );
               rdlength = ntohs( answer_middle_ptr->RDLENGTH );
               if ( (answer_type == DNS_A ) && (rdlength == 4)) {
                  if (ipname != NULL) {  
                     domain_name_extract(temp_ptr-rx_buffer,rx_buffer,ipname,ipnamesize);
                  }
                  ip_answer_ptr = (_ip_address *)  &answer_ptr[offset  + sizeof(DNS_RESPONSE_RR_MIDDLE_STRUCT)];
                  if (ipaddr_ptr != NULL) {
                     *ipaddr_ptr = ntohl (ip_answer_ptr);
                  }
                  return TRUE;
               }
               
               
               temp_ptr += offset + sizeof(DNS_RESPONSE_RR_MIDDLE_STRUCT) + rdlength;
            }
         }
      }
   }
   
   return FALSE;
}



boolean LWDNS_resolve( 
   char_ptr          name_to_resolve, 
   _ip_address _PTR_ ipaddr_ptr, 
   char_ptr          ipname, 
   uint_32           ipnamesize
   )
{
   boolean           success=FALSE;
   sockaddr_in       laddr,raddr;
   uint_32           sock=0;
   char_ptr          buf=NULL;
   uint_32           result,id;
   int_32            error;
   
   *ipaddr_ptr = 0;
   if (LWDNS_server_ipaddr) {
      buf= RTCS_mem_alloc(DNS_MAX_UDP_MESSAGE_SIZE);
      if (buf) {
         _mem_set_type(buf,MEM_TYPE_DNS_UDP_MESSAGE);
         _mem_zero((uchar _PTR_) &laddr, sizeof(laddr));
         laddr.sin_family      = AF_INET;
         laddr.sin_port        = 0; // DNS_RESOLVER_PORT;
         laddr.sin_addr.s_addr = INADDR_ANY;

         _mem_zero((uchar _PTR_) &raddr, sizeof(raddr));
         /* Remote address, Name Servers use port 53 */
         raddr.sin_family      = AF_INET;
         raddr.sin_port        = DNS_NAMESERVER_PORT;
         raddr.sin_addr.s_addr = LWDNS_server_ipaddr;

#if RTCSCFG_ENABLE_UDP
         /* Bind to UDP port */
         sock = socket(PF_INET, SOCK_DGRAM, 0);
#else
         sock = socket(PF_INET, SOCK_STREAM, 0);
#endif
         if (sock) {
            error = bind(sock, &laddr, sizeof(laddr));
            if ( error == RTCS_OK ) {
               error = connect(sock, &raddr, sizeof(raddr));
               if (error == RTCS_OK ) {
                  error = LWDNS_Send_Query(sock,  name_to_resolve, buf, &id);
                  if (error == RTCS_OK ) {
                     result = (uint_32)RTCS_selectset( &sock, 1, 3000 );

                     if (result==sock) {
                        success = LWDNS_Get_Response(sock, name_to_resolve, buf, id, ipaddr_ptr, ipname, ipnamesize);
                     } 
                  }
               }
            }
            shutdown(sock,0);
         }
         _mem_free(buf);
      }
   }
   
   return success;
} 

#endif
/* EOF */
