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