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