home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / net / bind-contrib.tar.gz / bind-contrib.tar / contrib / nutshell / ch13.check_soa.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-25  |  20.8 KB  |  559 lines

  1. /****************************************************************
  2.  * check_soa -- Retrieve the SOA record from each name server   *
  3.  *     for a given domain and print out the serial number.      *
  4.  *                                                              *
  5.  * usage: check_soa domain                                      *
  6.  *                                                              *
  7.  * The following errors are reported:                           *
  8.  *     o There is no address for a server.                      *
  9.  *     o There is no server running on this host.               *
  10.  *     o There was no response from a server.                   *
  11.  *     o The server is not authoritative for the domain.        *
  12.  *     o The response had an error response code.               *
  13.  *     o The response had more than one answer.                 *
  14.  *     o The response answer did not contain an SOA record.     *
  15.  *     o The expansion of a compressed domain name failed.      *
  16.  ****************************************************************/
  17.  
  18. /* Various header files */
  19. #include <sys/types.h>
  20. #include <sys/socket.h>
  21. #include <netinet/in.h>
  22. #include <netdb.h>
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <errno.h>
  26. #include <arpa/inet.h>
  27. #include <arpa/nameser.h>
  28. #include <resolv.h>
  29. #include <string.h>
  30.  
  31. /* Error variables */
  32. extern int h_errno;  /* for resolver errors */
  33. extern int errno;    /* general system errors */
  34.  
  35. #ifndef __BIT_TYPES_DEFINED__
  36. /* typedef from BIND source file compat/include/sys/bitypes.h */
  37. typedef unsigned int            u_int32_t;
  38. #endif  /* __BIT_TYPES_DEFINED__ */
  39.  
  40. /* Our own routines; code included later in this chapter */
  41. void nsError();            /* report resolver errors */
  42. void findNameServers();    /* find a domain's name servers */
  43. void queryNameServers();   /* grab SOA records from servers */
  44. void returnCodeError();    /* report response packet errors */
  45. int  skipToData();         /* skip to the resource record data */
  46. int  skipName();           /* skip a compressed name */
  47.  
  48. /* Maximum number of name servers we will check */
  49. #define NSLIMIT 20
  50.  
  51. main(argc, argv)
  52. int argc;
  53. char *argv[];
  54. {
  55.     char *nsList[NSLIMIT]; /* list of name servers */
  56.     int  nsNum = 0;        /* number of name servers in list */
  57.  
  58.     /* sanity check: one (and only one) argument? */
  59.     if(argc != 2){
  60.         (void) fprintf(stderr, "usage: %s domain\n", argv[0]);
  61.         exit(1);
  62.     }
  63.  
  64.     (void) res_init();
  65.  
  66.     /* 
  67.      * Find the name servers for the domain.
  68.      * The name servers are written into nsList.
  69.      */
  70.     findNameServers(argv[1], nsList, &nsNum);
  71.  
  72.     /* 
  73.      * Query each name server for the domain's SOA record.
  74.      * The name servers are read from nsList.
  75.      */
  76.     queryNameServers(argv[1], nsList, nsNum);
  77.  
  78.     exit(0);
  79. }
  80.  
  81. /****************************************************************
  82.  * findNameServers -- find all of the name servers for the      *
  83.  *     given domain and store their names in nsList.  nsNum is  *
  84.  *     the number of servers in the nsList array.               *
  85.  ****************************************************************/
  86. void
  87. findNameServers(domain, nsList, nsNum)
  88. char *domain;
  89. char *nsList[];
  90. int  *nsNum;
  91. {
  92.     union {
  93.         HEADER hdr;           /* defined in resolv.h */
  94.         u_char buf[PACKETSZ]; /* defined in arpa/nameser.h */
  95.     } response;               /* response buffers */
  96.     int responseLen;          /* buffer length */
  97.  
  98.     u_char     *cp;       /* character pointer to parse DNS packet */
  99.     u_char     *endOfMsg; /* need to know the end of the message */
  100.     u_short    class;     /* classes defined in arpa/nameser.h */
  101.     u_short    type;      /* types defined in arpa/nameser.h */
  102.     u_int32_t  ttl;       /* resource record time to live */
  103.     u_short    dlen;      /* size of resource record data */
  104.  
  105.     int i, count, dup; /* misc variables */
  106.  
  107.     /* 
  108.      * Look up the NS records for the given domain name.
  109.      * We expect the domain to be a fully qualified name, so
  110.      * we use res_query().  If we wanted the resolver search 
  111.      * algorithm, we would have used res_search() instead.
  112.      */
  113.     if((responseLen = 
  114.            res_query(domain,      /* the domain we care about   */
  115.                      C_IN,        /* Internet class records     */
  116.                      T_NS,        /* Look up name server records*/
  117.                      (u_char *)&response,      /*response buffer*/
  118.                      sizeof(response)))        /*buffer size    */
  119.                                         < 0){  /*If negative    */
  120.         nsError(h_errno, domain); /* report the error           */
  121.         exit(1);                  /* and quit                   */
  122.     }
  123.  
  124.     /*
  125.      * Keep track of the end of the message so we don't 
  126.      * pass it while parsing the response.  responseLen is 
  127.      * the value returned by res_query.
  128.      */
  129.     endOfMsg = response.buf + responseLen;
  130.  
  131.     /*
  132.      * Set a pointer to the start of the question section, 
  133.      * which begins immediately AFTER the header.
  134.      */
  135.     cp = response.buf + sizeof(HEADER);
  136.  
  137.     /*
  138.      * Skip over the whole question section.  The question 
  139.      * section is comprised of a name, a type, and a class.  
  140.      * QFIXEDSZ (defined in arpa/nameser.h) is the size of 
  141.      * the type and class portions, which is fixed.  Therefore, 
  142.      * we can skip the question section by skipping the 
  143.      * name (at the beginning) and then advancing QFIXEDSZ.
  144.      * After this calculation, cp points to the start of the 
  145.      * answer section, which is a list of NS records.
  146.      */
  147.     cp += skipName(cp, endOfMsg) + QFIXEDSZ;
  148.  
  149.     /*
  150.      * Create a list of name servers from the response.
  151.      * NS records may be in the answer section and/or in the
  152.      * authority section depending on the DNS implementation.  
  153.      * Walk through both.  The name server addresses may be in
  154.      * the additional records section, but we will ignore them
  155.      * since it is much easier to call gethostbyname() later
  156.      * than to parse and store the addresses here.
  157.      */
  158.     count = ntohs(response.hdr.ancount) + 
  159.             ntohs(response.hdr.nscount);
  160.     while (    (--count >= 0)        /* still more records     */
  161.             && (cp < endOfMsg)       /* still inside the packet*/
  162.             && (*nsNum < NSLIMIT)) { /* still under our limit  */
  163.  
  164.         /* Skip to the data portion of the resource record */
  165.         cp += skipToData(cp, &type, &class, &ttl, &dlen, endOfMsg);
  166.  
  167.         if (type == T_NS) {
  168.  
  169.             /*
  170.              * Allocate storage for the name.  Like any good 
  171.              * programmer should, we test malloc's return value, 
  172.              * and quit if it fails.
  173.              */
  174.             nsList[*nsNum] = (char *) malloc (MAXDNAME);
  175.             if(nsList[*nsNum] == NULL){
  176.                 (void) fprintf(stderr, "malloc failed\n");
  177.                 exit(1);
  178.             }
  179.  
  180.             /* Expand the name server's name */
  181.             if (dn_expand(response.buf, /* Start of the packet   */
  182.                           endOfMsg,     /* End of the packet     */
  183.                           cp,           /* Position in the packet*/
  184.                           (u_char *)nsList[*nsNum], /* Result    */
  185.                           MAXDNAME)     /* size of nsList buffer */
  186.                                     < 0) { /* Negative: error    */
  187.                 (void) fprintf(stderr, "dn_expand failed\n");
  188.                 exit(1);
  189.             }
  190.  
  191.             /*
  192.              * Check the name we've just unpacked and add it to 
  193.              * the list of servers if it is not a duplicate.
  194.              * If it is a duplicate, just ignore it.
  195.              */
  196.             for(i = 0, dup=0; (i < *nsNum) && !dup; i++)
  197.                 dup = !strcasecmp(nsList[i], nsList[*nsNum]);
  198.             if(dup) 
  199.                 free(nsList[*nsNum]);
  200.             else
  201.                 (*nsNum)++;
  202.         }
  203.  
  204.         /* Advance the pointer over the resource record data */
  205.         cp += dlen;
  206.  
  207.     } /* end of while */
  208. }
  209.  
  210. /****************************************************************
  211.  * queryNameServers -- Query each of the name servers in nsList *
  212.  *     for the SOA record of the given domain.  Report any      *
  213.  *     errors encountered.  (e.g., a name server not running or *
  214.  *     the response not being an authoritative response.)  If   *
  215.  *     there are no errors, print out the serial number for the *
  216.  *     domain.                                                  *
  217.  ****************************************************************/
  218. void
  219. queryNameServers(domain, nsList, nsNum)
  220. char *domain;
  221. char *nsList[];
  222. int nsNum;
  223. {
  224.     union {
  225.         HEADER hdr;            /* defined in resolv.h */
  226.         u_char buf[PACKETSZ];  /* defined in arpa/nameser.h */
  227.     } query, response;         /* query and response buffers */
  228.     int responseLen, queryLen; /* buffer lengths */
  229.  
  230.     u_char     *cp;       /* character pointer to parse DNS packet */
  231.     u_char     *endOfMsg; /* need to know the end of the message */
  232.     u_short    class;     /* classes defined in arpa/nameser.h */
  233.     u_short    type;      /* types defined in arpa/nameser.h */
  234.     u_int32_t  ttl;       /* resource record time to live */
  235.     u_short    dlen;      /* size of resource record data */
  236.     u_int32_t  serial;    /* SOA serial number */
  237.  
  238.     struct in_addr saveNsAddr[MAXNS];  /* addrs saved from _res */
  239.     int nsCount;          /* count of addresses saved from _res */
  240.     struct hostent *host; /* structure for looking up ns addr */
  241.     int i;                /* counter variable */
  242.  
  243.     /*
  244.      * Save the _res name server list since 
  245.      * we will need to restore it later.
  246.      */
  247.     nsCount = _res.nscount;
  248.     for(i = 0; i < nsCount; i++)
  249.       saveNsAddr[i] = _res.nsaddr_list[i].sin_addr;
  250.  
  251.     /*
  252.      * Turn off the search algorithm and turn off appending 
  253.      * the default domain before we call gethostbyname(); the 
  254.      * name server names will be fully qualified.
  255.      */
  256.     _res.options &= ~(RES_DNSRCH | RES_DEFNAMES);
  257.  
  258.     /*
  259.      * Query each name server for an SOA record.
  260.      */
  261.     for(nsNum-- ; nsNum >= 0; nsNum--){
  262.  
  263.         /* 
  264.          * First, we have to get the IP address of every server.
  265.          * So far, all we have are names.  We use gethostbyname
  266.          * to get the addresses, rather than anything fancy.
  267.          * But first, we have to restore certain values in _res 
  268.          * because _res affects gethostbyname().  (We altered
  269.          * _res in the previous iteration through the loop.)
  270.          *
  271.          * We can't just call res_init() again to restore
  272.          * these values since some of the _res fields are 
  273.          * initialized when the variable is declared, not when 
  274.          * res_init() is called.
  275.          */
  276.         _res.options |= RES_RECURSE;  /* recursion on (default) */
  277.         _res.retry = 4;               /* 4 retries (default)    */
  278.         _res.nscount = nsCount;       /* original name servers  */
  279.         for(i = 0; i < nsCount; i++)
  280.             _res.nsaddr_list[i].sin_addr = saveNsAddr[i];
  281.  
  282.         /* Look up the name server's address */
  283.         host = gethostbyname(nsList[nsNum]);
  284.         if (host == NULL) {
  285.             (void) fprintf(stderr,"There is no address for %s\n",
  286.                                               nsList[nsNum]);
  287.             continue; /* nsNum for-loop */
  288.         }
  289.  
  290.         /*
  291.          * Now get ready for the real fun.  host contains IP 
  292.          * addresses for the name server we're testing.
  293.          * Store the first address for host in the _res 
  294.          * structure.  Soon, we'll look up the SOA record...
  295.          */
  296.         (void) memcpy((void *)&_res.nsaddr_list[0].sin_addr,
  297.            (void *)host->h_addr_list[0], (size_t)host->h_length);
  298.         _res.nscount = 1;
  299.  
  300.         /*
  301.          * Turn off recursion.  We don't want the name server
  302.          * querying another server for the SOA record; this name 
  303.          * server ought to be authoritative for this data.
  304.          */
  305.         _res.options &= ~RES_RECURSE;
  306.  
  307.         /*
  308.          * Reduce the number of retries.  We may be checking
  309.          * several name servers, so we don't want to wait too
  310.          * long for any one server.  With two retries and only
  311.          * one address to query, we'll wait at most 15 seconds.
  312.          */
  313.         _res.retry = 2;
  314.  
  315.         /*
  316.          * We want to see the response code in the next
  317.          * response, so we must make the query packet and 
  318.          * send it ourselves instead of having res_query()
  319.          * do it for us.  If res_query() returned -1, there
  320.          * might not be a response to look at.  
  321.          *
  322.          * There is no need to check for res_mkquery() 
  323.          * returning -1.  If the compression was going to 
  324.          * fail, it would have failed when we called 
  325.          * res_query() earlier with this domain name.
  326.          */
  327.         queryLen = res_mkquery(
  328.                      QUERY,           /* regular query         */
  329.                      domain,          /* the domain to look up */
  330.                      C_IN,            /* Internet type         */
  331.                      T_SOA,           /* Look up an SOA record */
  332.                      (char *)NULL,    /* always NULL           */
  333.                      0,               /* length of NULL        */
  334.                      (struct rrec *)NULL, /* always NULL       */
  335.                      (char *)&query,  /* buffer for the query  */
  336.                      sizeof(query));  /* size of the buffer    */
  337.  
  338.         /*
  339.          * Send the query packet.  If there is no name server
  340.          * running on the target host, res_send() returns -1
  341.          * and errno is ECONNREFUSED.  First, clear out errno.
  342.          */
  343.         errno = 0;
  344.         if((responseLen = res_send((char *)&query, /* the query  */
  345.                                     queryLen,      /* true length*/
  346.                                     (char *)&response, /*buffer  */
  347.                                     sizeof(response))) /*buf size*/
  348.                                         < 0){          /* error  */
  349.             if(errno == ECONNREFUSED) { /* no server on the host */
  350.                 (void) fprintf(stderr, 
  351.                     "There is no name server running on %s\n",
  352.                     nsList[nsNum]);
  353.             } else {                    /* anything else: no response */
  354.                 (void) fprintf(stderr, 
  355.                     "There was no response from %s\n", 
  356.                     nsList[nsNum]);
  357.             }
  358.             continue; /* nsNum for-loop */
  359.         }
  360.  
  361.         /*
  362.          * Set up the pointers to parse the response.
  363.          * We set up two pointers: one to the end of the message 
  364.          * (so we can test for overruns) and one to the question 
  365.          * section (which we'll move as we parse the response).
  366.          */
  367.         endOfMsg = response.buf + responseLen;
  368.         cp = response.buf + sizeof(HEADER);             
  369.  
  370.         /*
  371.          * If the response reports an error, issue a message
  372.          * and proceed to the next server in the list.
  373.          */
  374.         if(response.hdr.rcode != NOERROR){
  375.             returnCodeError((int)response.hdr.rcode, 
  376.                                                   nsList[nsNum]);
  377.             continue; /* nsNum for-loop */
  378.         }
  379.  
  380.         /*
  381.          * Did we receive an authoritative response?  Check the 
  382.          * authoritative answer bit.  If the server isn't
  383.          * authoritative, report it, and go on to the next server.
  384.          */
  385.         if(!response.hdr.aa){
  386.             (void) fprintf(stderr, 
  387.                 "%s is not authoritative for %s\n",
  388.                 nsList[nsNum], domain);
  389.             continue; /* nsNum for-loop */
  390.         }
  391.  
  392.         /* 
  393.          * The response should only contain one answer; if more,
  394.          * report the error, and proceed to the next server.
  395.          */
  396.         if(ntohs(response.hdr.ancount) != 1){
  397.             (void) fprintf(stderr, 
  398.                 "%s: expected 1 answer, got %d\n",
  399.                 nsList[nsNum], ntohs(response.hdr.ancount));
  400.             continue; /* nsNum for-loop */
  401.         }
  402.  
  403.         /* 
  404.          * Skip the question section (we know what we asked, 
  405.          * don't we?).  cp now points to the answer section.
  406.          */
  407.         cp += skipName(cp, endOfMsg) + QFIXEDSZ;
  408.  
  409.         /* 
  410.          * cp is now pointing at a resource record in the answer 
  411.          * section.  Skip to the data portion of this record;
  412.          * in the process, extract the type, class, etc. 
  413.          */
  414.         cp += skipToData(cp, &type, &class, &ttl, &dlen, endOfMsg);
  415.  
  416.         /* 
  417.          * We asked for an SOA record; if we got something else,
  418.          * report the error and proceed to the next server.
  419.          */
  420.         if (type != T_SOA) {
  421.             (void) fprintf(stderr, 
  422.                 "%s: expected answer type %d, got %d\n",
  423.                 nsList[nsNum], T_SOA, type);
  424.             continue; /* nsNum for-loop */
  425.         }
  426.  
  427.         /* 
  428.          * Skip the SOA origin and mail address, which we don't
  429.          * care about.  Both are standard "compressed names."
  430.          */
  431.         cp += skipName(cp, endOfMsg);
  432.         cp += skipName(cp, endOfMsg);
  433.  
  434.         /* cp now points to the serial number; print it. */
  435.         GETLONG(serial, cp);
  436.         (void) printf("%s has serial number %d\n", 
  437.             nsList[nsNum], serial);
  438.  
  439.     } /* end of nsNum for-loop */
  440. }
  441.  
  442. /****************************************************************
  443.  * skipName -- This routine skips over a domain name.  If the   *
  444.  *     domain name expansion fails, it reports an error and     *
  445.  *     exits.  dn_skipname() is probably not on your manual     *
  446.  *     page; it is similar to dn_expand() except that it just   *
  447.  *     skips over the name.  dn_skipname() is in res_comp.c if  *
  448.  *     you need to find it.                                     *
  449.  ****************************************************************/
  450. int
  451. skipName(cp, endOfMsg)
  452. u_char *cp;
  453. u_char *endOfMsg;
  454. {
  455.     int n;
  456.  
  457.     if((n = dn_skipname(cp, endOfMsg)) < 0){
  458.         (void) fprintf(stderr, "dn_skipname failed\n");
  459.         exit(1);
  460.     }
  461.     return(n);
  462. }
  463.  
  464. /****************************************************************
  465.  * skipToData -- This routine advances the cp pointer to the    *
  466.  *     start of the resource record data portion.  On the way,  *
  467.  *     it fills in the type, class, ttl, and data length        *
  468.  ****************************************************************/
  469. int
  470. skipToData(cp, type, class, ttl, dlen, endOfMsg)
  471. u_char     *cp;
  472. u_short    *type;
  473. u_short    *class;
  474. u_int32_t  *ttl;
  475. u_short    *dlen;
  476. u_char     *endOfMsg;
  477. {
  478.     u_char *tmp_cp = cp;  /* temporary version of cp */
  479.  
  480.     /* Skip the domain name; it matches the name we looked up */
  481.     tmp_cp += skipName(tmp_cp, endOfMsg);
  482.  
  483.     /*
  484.      * Grab the type, class, and ttl.  GETSHORT and GETLONG
  485.      * are macros defined in arpa/nameser.h.
  486.      */
  487.     GETSHORT(*type, tmp_cp);
  488.     GETSHORT(*class, tmp_cp);
  489.     GETLONG(*ttl, tmp_cp);
  490.     GETSHORT(*dlen, tmp_cp);
  491.  
  492.     return(tmp_cp - cp);
  493. }
  494.  
  495. /****************************************************************
  496.  * nsError -- Print an error message from h_errno for a failure *
  497.  *     looking up NS records.  res_query() converts the DNS     *
  498.  *     packet return code to a smaller list of errors and       *
  499.  *     places the error value in h_errno.  There is a routine   *
  500.  *     called herror() for printing out strings from h_errno    *
  501.  *     like perror() does for errno.  Unfortunately, the        *
  502.  *     herror() messages assume you are looking up address      *
  503.  *     records for hosts.  In this program, we are looking up   *
  504.  *     NS records for domains, so we need our own list of error *
  505.  *     strings.                                                 *
  506.  ****************************************************************/
  507. void
  508. nsError(error, domain)
  509. int error;
  510. char *domain;
  511. {
  512.     switch(error){
  513.         case HOST_NOT_FOUND:
  514.           (void) fprintf(stderr, "Unknown domain: %s\n", domain);
  515.           break;
  516.         case NO_DATA:
  517.           (void) fprintf(stderr, "No NS records for %s\n", domain); 
  518.           break;
  519.         case TRY_AGAIN:
  520.           (void) fprintf(stderr, "No response for NS query\n");
  521.           break;
  522.         default:
  523.           (void) fprintf(stderr, "Unexpected error\n");
  524.           break;
  525.     }
  526. }
  527.  
  528. /****************************************************************
  529.  * returnCodeError -- print out an error message from a DNS     *
  530.  *     response return code.                                    *
  531.  ****************************************************************/
  532. void
  533. returnCodeError(rcode, nameserver)
  534. int rcode;
  535. char *nameserver;
  536. {
  537.     (void) fprintf(stderr, "%s: ", nameserver);
  538.     switch(rcode){
  539.         case FORMERR:
  540.           (void) fprintf(stderr, "FORMERR response\n");
  541.           break;
  542.         case SERVFAIL:
  543.           (void) fprintf(stderr, "SERVFAIL response\n");
  544.           break;
  545.         case NXDOMAIN:
  546.           (void) fprintf(stderr, "NXDOMAIN response\n");
  547.           break;
  548.         case NOTIMP:
  549.           (void) fprintf(stderr, "NOTIMP response\n");
  550.           break;
  551.         case REFUSED:
  552.           (void) fprintf(stderr, "REFUSED response\n");
  553.           break;
  554.         default:
  555.           (void) fprintf(stderr, "unexpected return code\n");
  556.           break;
  557.     }
  558. }
  559.