Solved

c programming with Bind 8 resolver library

Posted on 2003-11-16
1
1,742 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

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

An Outlet in Cocoa is a persistent reference to a GUI control; it connects a property (a variable) to a control.  For example, it is common to create an Outlet for the text field GUI control and change the text that appears in this field via that Ou…
This is a short and sweet, but (hopefully) to the point article. There seems to be some fundamental misunderstanding about the function prototype for the "main" function in C and C++, more specifically what type this function should return. I see so…
The goal of this video is to provide viewers with basic examples to understand opening and writing to files in the C programming language.
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use for-loops in the C programming language.

708 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

21 Experts available now in Live!

Get 1:1 Help Now