Solved

c programming with Bind 8 resolver library

Posted on 2003-11-16
1
1,744 Views
Last Modified: 2012-05-04
Hi folks,
    I'm writing a c program. This program sends a specific type DNS query to some domain name servers and receives responses from them. The query type could be A(for IP address), CX(for mail exchanger), NS(for authoritative name server) or ANY(for all kinds of querys).  It takes two files for parameters: the first one is the list of name servers to be inquired, the second is the list of inquired zones.
    Because I'm not familiar with resolver library, I cannot figure out what's wrong with my program. For example, when I set the query type to be NS, the returned results are different from the ones when I use nslookup utility. I wonder if you nice people can take a look at my incomplete code and help me figure out what's the problem. Many thanks.

/****************************************************************
 * check_dns — Retrieve the related record from each name server*
 *   for a given domain and print out them.        *
 *                                                              *
 * usage: check_dns serverfile zonefile                         *
 *                                                              *  
 ****************************************************************

/* Various header files */
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <resolv.h>

/* Error variables */
extern int h_errno;  /* for resolver errors */
extern int errno;    /* general system errors */

/* Our own routines; code included later in this chapter */
void queryNameServers();   /* grab SOA records from servers */
void returnCodeError();    /* report response packet errors */
void readInFile();         /* read strings line by line from a file */
char *getIpAddressFromLong();     /* restore IP address from a long integer*/

/* Maximum number of name servers we will check */
#define MAX_NS     20
#define MAX_ZONE   50

/* Define the query type constant */
#define TYPE_A      1
#define TYPE_NS     2
#define TYPE_SOA    3
#define TYPE_MX     4
#define TYPE_ANY    5

              
main(argc, argv)
     int argc;
     char *argv[];
{
  char *nsList[MAX_NS];      /* list of name servers */
  int nsNum = 0;             /* number of name servers in list */
  char *zoneList[MAX_ZONE];  /* list of zones */
  int zoneNum = 0;            /* number of zones */
  FILE *serverfile, *zonefile;
  int i;

  /* sanity check: one (and only one) argument? */
  if(argc != 3) {
    (void) fprintf(stderr, "usage: %s serverfile  zonefile\n", argv[0]);
    exit(1);
  }

  if ((zonefile = fopen(argv[2], "r")) == NULL) {
    (void) fprintf(stderr, "error when opening file %s\n", argv[2]);
    exit(1);
  }

  readInFile(argv[1], nsList, &nsNum);
  readInFile(argv[2], zoneList, &zoneNum);

  (void) res_init();
  /*
   * Find the name servers for the domain.
   * The name servers are written into nsList.
   */
  //  findNameServers(argv[1], nsList, &nsNum);
  /*
   * Query each name server for the domain's SOA record.
   * The name servers are read from nsList.
   */

  for(i=0; i<zoneNum; i++) {
    queryNameServers(zoneList[i], nsList, nsNum, TYPE_MX);
    sleep(1);
  }

  exit(0);
}

/****************************************************************
 *  readInFile - read strings line by line from a file pointed  *
 *  by pFile and store them in list. Update the recNum to       *
 *  be the number of records in this file                       *
 ***************************************************************/
void
readInFile(fileName, list, recNum)
     char fileName[];
     char *list[];
     int *recNum;
{
  FILE *pFile;
  char ch;
  int i;

  if ((pFile = fopen(fileName, "r")) == NULL) {
    (void) fprintf(stderr, "error when opening file %s\n", fileName);
    exit(1);
  }  

  list[*recNum] = (char *)malloc(MAXDNAME);
  i = 0;
   
  /* read in a new line */
  while( (ch = fgetc(pFile)) != EOF) {
    if(ch == ' ' || ch == '\t')
      continue;
    else if(ch == '\n') {
      (*recNum)++;
      list[*recNum] = (char *)malloc(MAXDNAME);
      i = 0;
    }
    else if(i<MAXDNAME) {
      list[*recNum][i] = ch;
      i++;
    }
  }   /* end of inner while loop */

  fclose(pFile);
}


/****************************************************************
 * queryNameServers — Query each of the name servers in nsList  *
 *     for the SOA record of the given domain. Report any       *
 *     errors encountered.  (e.g., a name server not running or *
 *     the response not being an authoritative response.)  If   *
 *     there are no errors, print out the serial number for the *
 *     domain.                                                  *
 ****************************************************************/
void
queryNameServers(domain, nsList, nsNum, type)
     char *domain;
     char *nsList[];
     int nsNum;
     short type;
{
  union {
    HEADER hdr;               /* defined in resolv.h */
    u_char buf[NS_PACKETSZ];  /* defined in arpa/nameser.h */
  } query, response;            /* query and response buffers */
  int responseLen, queryLen;    /* buffer lengths */
  u_char     *cp;        /* character pointer to parse DNS packet */
  struct in_addr saveNsAddr [MAXNS]; /* addrs saved from _res */
  int nsCount;          /* count of addresses saved from _res */
  struct hostent *host;  /* structure for looking up ns addr */
  int i, k;                /* counter variable */
  ns_msg handle;  /* handle for response packet */
  ns_rr rr;       /* expanded resource record */
  int qtype;      /* query type */
  int quesNum, ansNum, authNum;      
                 /* questions, answers number and authorative name servers number */

  /*
   * Save the _res name server list since
   * we will need to restore it later.
   */

  nsCount = _res.nscount;

  for(i = 0;i < nsCount; i++)
    saveNsAddr[i] = _res.nsaddr_list[i].sin_addr;
  /*
   * Turn off the search algorithm and turn off appending
   * the default domain before we call gethostbyname(); the
   * name server names will be fully qualified.
   */
  _res.options &= ~(RES_DNSRCH | RES_DEFNAMES);
  /*
   * Query each name server for a record.
   */
  for(nsNum-- ; nsNum >= 0; nsNum--) {
    /*
     * First, we have to get the IP address of every server.
     * So far, all we have are names. We use gethostbyname
     * to get the addresses, rather than anything fancy.
     * But first, we have to restore certain values in _res
     * because _res affects gethostbyname (). (We altered
     * _res in the previous iteration through the loop.)
     *
     * We can't just call res_init () again to restore
     * these values since some of the _res fields are
     * initialized when the variable is declared, not when
     * res_init () is called.
     */
    _res.options |= RES_RECURSE; /* recursion on (default) */
    _res.options |= RES_USEVC;
    _res.retry = 4;               /* 4 retries (default) */
    _res.nscount = nsCount;       /* original name servers */
    for (i = 0; i < nsCount; i++)
      _res.nsaddr_list[i].sin_addr = saveNsAddr[i];
    /* Look up the name server's address */
    host = gethostbyname (nsList[nsNum]);
    if (host == NULL) {
      (void) fprintf (stderr, "There is no address for %s\n",
                  nsList [nsNum]);
      continue; /* nsNum for-loop */
    }

    /*
     * Now get ready for the real fun. host contains IP
     * addresses for the name server we're testing.
     * Store the first address for host in the _res
     * structure. Soon, we'll look up the specified record. . .
     */
    (void) memcpy ((void *)&_res.nsaddr_list[0].sin_addr,
               (void *)host->h_addr_list[0], (size_t)host->h_length);
    _res.nscount = 1;
    /*
     * Turn off recursion. We don't want the name server
     * querying another server for the SOA record; this name
     * server ought to be authoritative for this data.
     */
    _res.options &= ~RES_RECURSE;
    /*
     * Reduce the number of retries. We may be checking
     * several name servers, so we don't want to wait too
     * long for any one server. With two retries and only
     * one address to query, we'll wait at most 15 seconds.
     */
    _res.retry = 2;
    /*
     * We want to see the response code in the next
     * response, so we must make the query packet and
     * send it ourselves instead of having res_query()
     * do it for us. If res_query() returned -1, there
     * might not be a response to look at.
     
     *
     * There is no need to check for res_mkquery()
     * returning -1. If the compression was going to
     * fail, it would have failed when we called
     * res_query() earlier with this domain name.
     */

    switch(type) {
    case TYPE_A:
      qtype = ns_t_a;
      break;
    case TYPE_NS:
      qtype = ns_t_ns;
      break;
    case TYPE_SOA:
      qtype = ns_t_soa;
      break;
    case TYPE_MX:
      qtype = ns_t_mx;
      break;
    case TYPE_ANY:
      qtype = ns_t_any;
      break;
    }

    queryLen = res_mkquery(
                     ns_o_query,     /* regular query */
                     domain,         /* the domain to look up */
                     ns_c_in,        /* Internet type */
                     qtype,          /* Look up the specified type record */
                     (u_char *)NULL, /* always NULL */
                     0, /* length of NULL */
                     (u_char *)NULL, /* always NULL */
                     (u_char *)&query, /* buffer for the query */
                     sizeof(query)); /* size of the buffer */

    /*
     * Send the query packet. If there is no name server
     * running on the target host, res_send() returns -1
     * and errno is ECONNREFUSED. First, clear out errno.
     */
    errno = 0;
    if ((responseLen = res_send ((u_char *)&query,/* the query */
                         queryLen, /* true length*/
                         (u_char *)&response,/*buffer */
                         sizeof(response))) /*buf size*/
      < 0) { /* error */
      if (errno == ECONNREFUSED) { /* no server on the host */
      (void) fprintf (stderr,
                    "There is no name server running on %s\n",
                  nsList [nsNum]);
      } else {                   /* anything else: no response */
      (void) fprintf (stderr,
                  "There was no response from %s\n",
                  nsList [nsNum]);
      }
      continue; /* nsNum for-loop */
    }


    /*
     * Initialize a handle to this response. The handle will
     * be used later to extract information from the response.
     */
    if (ns_initparse(response.buf, responseLen, &handle) < 0) {
      fprintf (stderr, "ns_initparse: %s\n", strerror(errno));
      return;
    }
    /*
     * If the response reports an error, issue a message
     * and proceed to the next server in the list.
     */
    if (ns_msg_getflag (handle, ns_f_rcode) != ns_r_noerror) {
     
      returnCodeError(ns_msg_getflag(handle, ns_f_rcode),
                  nsList[nsNum]);
      continue; /* nsNum for-loop */
    }

    quesNum = ns_msg_count(handle, ns_s_qd);
    ansNum = ns_msg_count(handle, ns_s_an);
    authNum = ns_msg_count(handle, ns_s_ns);

    switch(type) {

    case TYPE_A:            
      printf("%s:\t\t%s\t\t", nsList[i], domain);

      for(k=0; k<ansNum; k++) {
      if (ns_parserr (&handle, ns_s_an, k, &rr)) {
        if (errno != ENODEV) {
          fprintf (stderr, "ns_parserr: %s\n",
                 strerror (errno));
        }
      } /* end if (ns_parserr...) */
      
      cp = (u_char *)ns_rr_rdata (rr);  /* current resource record */
      
      if(ns_rr_type(rr) == ns_t_a)
        printf("%s\t%d\t", getIpAddressFromLong(ns_get32(cp)), ns_rr_ttl(rr));

      } /* end for(k=...) */
      printf("\n");
      break;

    case TYPE_NS:
      printf("%s:\t\t%s\t\t", nsList[i], domain);

      for(k=0; k<authNum; k++) {
      if (ns_parserr (&handle, ns_s_ns, k, &rr)) {
        if (errno != ENODEV) {
          fprintf (stderr, "ns_parserr: %s\n",
                 strerror (errno));
        }
      } /* end if (ns_parserr...) */
      
      printf("Place 0000.\n");

      cp = (u_char *)ns_rr_rdata (rr);  /* current resource record */

      printf("Place 1111.\n");

      if(ns_rr_type(rr) == ns_t_ns) {
        char *tmpStr = (char *)malloc(MAXDNAME);

        ns_name_uncompress(
                       ns_msg_base(handle), /* Start of the packet   */
                       ns_msg_end(handle),  /* End of the packet     */
                       ns_rr_rdata(rr),     /* Position in the packet*/
                       tmpStr,      /* Result                */
                       MAXDNAME);  
        printf("%s\t%d\t", tmpStr, ns_rr_ttl(rr));
      }
      }

      /* nonauthorative answers */
      for(k=0; k<authNum; k++) {
      if (ns_parserr (&handle, ns_s_ns, k, &rr)) {
        if (errno != ENODEV) {
          fprintf (stderr, "ns_parserr: %s\n",
                 strerror (errno));
        }
      } /* end if (ns_parserr...) */

      cp = (u_char *)ns_rr_rdata (rr);  /* current resource record */

      if(ns_rr_type(rr) != ns_t_mx) {
        char *tmpStr = (char *)malloc(MAXDNAME);
        
        ns_name_uncompress(
                       ns_msg_base(handle), /* Start of the packet   */
                       ns_msg_end(handle),  /* End of the packet     */
                       ns_rr_rdata(rr),     /* Position in the packet*/
                       tmpStr,      /* Result                */
                       MAXDNAME);
        printf("%s\t%d\t", tmpStr, ns_rr_ttl(rr));
      }
      }

      printf("\n");
      break;
     
    case TYPE_SOA:
      break;
    case TYPE_MX:
      printf("%s:\t\t%s\t\t", nsList[i], domain);

      /* authorative answers */
      for(k=0; k<ansNum; k++) {
      if (ns_parserr (&handle, ns_s_an, k, &rr)) {
        if (errno != ENODEV) {
          fprintf (stderr, "ns_parserr: %s\n",
                 strerror (errno));
        }
      } /* end if (ns_parserr...) */

      cp = (u_char *)ns_rr_rdata (rr);  /* current resource record */

      if(ns_rr_type(rr) != ns_t_mx) {
        char *tmpStr = (char *)malloc(MAXDNAME);
        
        ns_name_uncompress(
                       ns_msg_base(handle), /* Start of the packet   */
                       ns_msg_end(handle),  /* End of the packet     */
                       ns_rr_rdata(rr),     /* Position in the packet*/
                       tmpStr,      /* Result                */
                       MAXDNAME);
        printf("%s\t%d\t", tmpStr, ns_rr_ttl(rr));
      }
      }

      /* nonauthorative answers */
      for(k=0; k<authNum; k++) {
      if (ns_parserr (&handle, ns_s_ns, k, &rr)) {
        if (errno != ENODEV) {
          fprintf (stderr, "ns_parserr: %s\n",
                 strerror (errno));
        }
      } /* end if (ns_parserr...) */

      cp = (u_char *)ns_rr_rdata (rr);  /* current resource record */

      if(ns_rr_type(rr) != ns_t_mx) {
        char *tmpStr = (char *)malloc(MAXDNAME);
        
        ns_name_uncompress(
                       ns_msg_base(handle), /* Start of the packet   */
                       ns_msg_end(handle),  /* End of the packet     */
                       ns_rr_rdata(rr),     /* Position in the packet*/
                       tmpStr,      /* Result                */
                       MAXDNAME);
        printf("%s\t%d\t", tmpStr, ns_rr_ttl(rr));
      }
      }
      printf("\n");

      break;

    case TYPE_ANY:
      break;
      }
   
    /*
     * Expand the answer section record number 0 into rr.
     */
    /*
    if (ns_parserr (&handle, ns_s_an, 0, &rr)) {
      if (errno != ENODEV) {
      fprintf (stderr, "ns_parserr: %s\n",
             strerror (errno));
      }
    }

    cp = (u_char *)ns_rr_rdata (rr);
    printf("place 2.\n");
    printf("%s\t", getIpAddressFromLong(ns_get32(cp)));
    printf("place 3.\n");
    */

    /* restore the saved name save address */
    for (k = 0; k < nsCount; k++)
      _res.nsaddr_list[k].sin_addr = saveNsAddr[k];

    _res.nscount = nsCount;
   
  } /* end of nsNum for-loop */
}

char *
getIpAddressFromLong(longAddr)
     unsigned long longAddr;
{
  struct sockaddr_in inp;
  char *ipStr;

  memcpy(&inp.sin_addr, &longAddr, sizeof(longAddr));
  ipStr = inet_ntoa(inp.sin_addr);

  return ipStr;
}


/****************************************************************
 * returnCodeError — print out an error message from a DNS      *
 *     response return code.                                    *
 ****************************************************************/
void
returnCodeError (rcode, nameserver)
     ns_rcode rcode;
     char *nameserver;
{
  (void) fprintf (stderr, "%s: ", nameserver);
  switch (rcode) {
  case ns_r_formerr:
    (void) fprintf (stderr, "FORMERR response\n");
    break;
  case ns_r_servfail:
    (void) fprintf (stderr, "SERVFAIL response\n");
    break;
  case ns_r_nxdomain:
    (void) fprintf (stderr, "NXDOMAIN response\n");
    break;
  case ns_r_notimpl:
    (void) fprintf (stderr, "NOTIMP response\n");
    break;
  case ns_r_refused:
    (void) fprintf (stderr, "REFUSED response\n");
    break;
  default:
    (void) fprintf (stderr, "unexpected return code\n");
    break;
  }
}       
      
0
Comment
Question by:summer_soccer
1 Comment
 
LVL 16

Accepted Solution

by:
_nn_ earned 100 total points
ID: 9765139
I suspect you're making possibly wrong assumptions about the way nslookup works. If I recall correctly, nslookup will do recursive queries by default. Your code disables them at some point... Can you please provide a detailed example (nameservers and hosts) where the outputs (apparently) do not match ?
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Summary: This tutorial covers some basics of pointer, pointer arithmetic and function pointer. What is a pointer: A pointer is a variable which holds an address. This address might be address of another variable/address of devices/address of fu…
Windows programmers of the C/C++ variety, how many of you realise that since Window 9x Microsoft has been lying to you about what constitutes Unicode (http://en.wikipedia.org/wiki/Unicode)? They will have you believe that Unicode requires you to use…
The goal of this video is to provide viewers with basic examples to understand opening and reading files in the C programming language.
The goal of this video is to provide viewers with basic examples to understand and use conditional statements in the C programming language.

943 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now