home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / n / bind / bind-4.001 / bind-4~ / bind-4.9.3-BETA9 / named / db_glue.c < prev    next >
C/C++ Source or Header  |  1994-07-22  |  15KB  |  711 lines

  1. #if !defined(lint) && !defined(SABER)
  2. static char sccsid[] = "@(#)db_glue.c    4.4 (Berkeley) 6/1/90";
  3. static char rcsid[] = "$Id: db_glue.c,v 4.9.1.12 1994/07/22 08:42:39 vixie Exp $";
  4. #endif /* not lint */
  5.  
  6. /*
  7.  * ++Copyright++ 1986, 1988
  8.  * -
  9.  * Copyright (c) 1986, 1988
  10.  *    The Regents of the University of California.  All rights reserved.
  11.  * 
  12.  * Redistribution and use in source and binary forms, with or without
  13.  * modification, are permitted provided that the following conditions
  14.  * are met:
  15.  * 1. Redistributions of source code must retain the above copyright
  16.  *    notice, this list of conditions and the following disclaimer.
  17.  * 2. Redistributions in binary form must reproduce the above copyright
  18.  *    notice, this list of conditions and the following disclaimer in the
  19.  *    documentation and/or other materials provided with the distribution.
  20.  * 3. All advertising materials mentioning features or use of this software
  21.  *    must display the following acknowledgement:
  22.  *     This product includes software developed by the University of
  23.  *     California, Berkeley and its contributors.
  24.  * 4. Neither the name of the University nor the names of its contributors
  25.  *    may be used to endorse or promote products derived from this software
  26.  *    without specific prior written permission.
  27.  * 
  28.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  29.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  30.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  31.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  32.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  34.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  37.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  38.  * SUCH DAMAGE.
  39.  * -
  40.  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
  41.  * 
  42.  * Permission to use, copy, modify, and distribute this software for any
  43.  * purpose with or without fee is hereby granted, provided that the above
  44.  * copyright notice and this permission notice appear in all copies, and that
  45.  * the name of Digital Equipment Corporation not be used in advertising or
  46.  * publicity pertaining to distribution of the document or software without
  47.  * specific, written prior permission.
  48.  * 
  49.  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
  50.  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
  51.  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
  52.  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  53.  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  54.  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  55.  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  56.  * SOFTWARE.
  57.  * -
  58.  * --Copyright--
  59.  */
  60.  
  61. #include <sys/types.h>
  62. #include <sys/uio.h>
  63. #include <sys/param.h>
  64. #include <sys/stat.h>
  65. #include <netinet/in.h>
  66. #include <arpa/nameser.h>
  67. #include <stdio.h>
  68. #include <syslog.h>
  69. #include <ctype.h>
  70. #include <netdb.h>
  71. #include <resolv.h>
  72. #include <errno.h>
  73.  
  74. #include "named.h"
  75.  
  76. struct valuelist {
  77.     struct valuelist *next, *prev;
  78.     char    *name;
  79.     char    *proto;
  80.     int    port;
  81. } *servicelist, *protolist;
  82.  
  83. #if defined(ultrix)
  84. /* ultrix 4.0 has some icky packaging details.  work around them here.
  85.  * since this module is linked into named and named-xfer, we end up
  86.  * forcing both to drag in our own res_send rather than ultrix's hesiod
  87.  * version of that.
  88.  */
  89. static const int (*unused_junk)__P((const u_char *, int, u_char *, int)) =
  90.     res_send;
  91. ;
  92. #endif
  93.  
  94. void
  95. buildservicelist()
  96. {
  97.     struct servent *sp;
  98.     struct valuelist *slp;
  99.  
  100. #ifdef MAYBE_HESIOD
  101.     setservent(0);
  102. #else
  103.     setservent(1);
  104. #endif
  105.     while (sp = getservent()) {
  106.         slp = (struct valuelist *)malloc(sizeof(struct valuelist));
  107.         slp->name = savestr(sp->s_name);
  108.         slp->proto = savestr(sp->s_proto);
  109.         slp->port = ntohs((u_int16_t)sp->s_port);
  110.         slp->next = servicelist;
  111.         slp->prev = NULL;
  112.         if (servicelist)
  113.             servicelist->prev = slp;
  114.         servicelist = slp;
  115.     }
  116.     endservent();
  117. }
  118.  
  119. void
  120. buildprotolist()
  121. {
  122.     struct protoent *pp;
  123.     struct valuelist *slp;
  124.  
  125. #ifdef MAYBE_HESIOD
  126.     setprotoent(0);
  127. #else
  128.     setprotoent(1);
  129. #endif
  130.     while (pp = getprotoent()) {
  131.         slp = (struct valuelist *)malloc(sizeof(struct valuelist));
  132.         slp->name = savestr(pp->p_name);
  133.         slp->port = pp->p_proto;
  134.         slp->next = protolist;
  135.         slp->prev = NULL;
  136.         if (protolist)
  137.             protolist->prev = slp;
  138.         protolist = slp;
  139.     }
  140.     endprotoent();
  141. }
  142.  
  143. static int
  144. findservice(s, list)
  145.     register char *s;
  146.     register struct valuelist **list;
  147. {
  148.     register struct valuelist *lp = *list;
  149.     int n;
  150.  
  151.     for (; lp != NULL; lp = lp->next)
  152.         if (strcasecmp(lp->name, s) == 0) {
  153.             if (lp != *list) {
  154.                 lp->prev->next = lp->next;
  155.                 if (lp->next)
  156.                     lp->next->prev = lp->prev;
  157.                 (*list)->prev = lp;
  158.                 lp->next = *list;
  159.                 *list = lp;
  160.             }
  161.             return (lp->port);
  162.         }
  163.     if (sscanf(s, "%d", &n) != 1 || n <= 0)
  164.         n = -1;
  165.     return (n);
  166. }
  167.  
  168. /*
  169.  * Convert service name or (ascii) number to int.
  170.  */
  171. int
  172. servicenumber(p)
  173.     char *p;
  174. {
  175.     return (findservice(p, &servicelist));
  176. }
  177.  
  178. /*
  179.  * Convert protocol name or (ascii) number to int.
  180.  */
  181. int
  182. protocolnumber(p)
  183.     char *p;
  184. {
  185.     return (findservice(p, &protolist));
  186. }
  187.  
  188. #if defined(__STDC__) || defined(__GNUC__)
  189. static struct servent *
  190. cgetservbyport(u_int16_t port,
  191.            char *proto)
  192. #else
  193. static struct servent *
  194. cgetservbyport(port, proto)
  195.     u_int16_t port;
  196.     char *proto;
  197. #endif
  198. {
  199.     register struct valuelist **list = &servicelist;
  200.     register struct valuelist *lp = *list;
  201.     static struct servent serv;
  202.  
  203.     port = htons(port);
  204.     for (; lp != NULL; lp = lp->next) {
  205.         if (port != (u_int16_t)lp->port)
  206.             continue;
  207.         if (strcasecmp(lp->proto, proto) == 0) {
  208.             if (lp != *list) {
  209.                 lp->prev->next = lp->next;
  210.                 if (lp->next)
  211.                     lp->next->prev = lp->prev;
  212.                 (*list)->prev = lp;
  213.                 lp->next = *list;
  214.                 *list = lp;
  215.             }
  216.             serv.s_name = lp->name;
  217.             serv.s_port = htons((u_int16_t)lp->port);
  218.             serv.s_proto = lp->proto;
  219.             return (&serv);
  220.         }
  221.     }
  222.     return (0);
  223. }
  224.  
  225. static struct protoent *
  226. cgetprotobynumber(proto)
  227.     register int proto;
  228. {
  229.     register struct valuelist **list = &protolist;
  230.     register struct valuelist *lp = *list;
  231.     static struct protoent prot;
  232.  
  233.     for (; lp != NULL; lp = lp->next)
  234.         if (lp->port == proto) {
  235.             if (lp != *list) {
  236.                 lp->prev->next = lp->next;
  237.                 if (lp->next)
  238.                     lp->next->prev = lp->prev;
  239.                 (*list)->prev = lp;
  240.                 lp->next = *list;
  241.                 *list = lp;
  242.             }
  243.             prot.p_name = lp->name;
  244.             prot.p_proto = lp->port;
  245.             return (&prot);
  246.         }
  247.     return (0);
  248. }
  249.  
  250. char *
  251. protocolname(num)
  252.     int num;
  253. {
  254.     static    char number[8];
  255.     struct protoent *pp;
  256.  
  257.     pp = cgetprotobynumber(num);
  258.     if(pp == 0)  {
  259.         (void) sprintf(number, "%d", num);
  260.         return (number);
  261.     }
  262.     return (pp->p_name);
  263. }
  264.  
  265. #if defined(__STDC__) || defined(__GNUC__)
  266. char *
  267. servicename(u_int16_t port, char *proto)
  268. #else
  269. char *
  270. servicename(port, proto)
  271.     u_int16_t port;
  272.     char *proto;
  273. #endif
  274. {
  275.     static    char number[8];
  276.     struct servent *ss;
  277.  
  278.     ss = cgetservbyport(htons(port), proto);
  279.     if (ss == 0)  {
  280.         (void) sprintf(number, "%d", port);
  281.         return (number);
  282.     }
  283.     return (ss->s_name);
  284. }
  285.  
  286. int
  287. db_getclev(origin)
  288.     char *origin;
  289. {
  290.     int lev = 0;
  291.     dprintf(1, (ddt, "db_getclev of \"%s\"", origin));
  292.     if (origin && *origin)
  293.         lev++;
  294.     while (origin && (origin = strchr(origin, '.'))) {
  295.         origin++;
  296.         lev++;
  297.     }
  298.     dprintf(1, (ddt, " = %d\n", lev));
  299.     return (lev);
  300. }
  301.  
  302. void
  303. gettime(ttp)
  304.     struct timeval *ttp;
  305. {
  306.     if (gettimeofday(ttp, NULL) < 0)
  307.         syslog(LOG_ERR, "gettimeofday: %m");
  308.     return;
  309. }
  310.  
  311. #if !defined(BSD)
  312. int
  313. getdtablesize()
  314. {
  315. #if defined(USE_POSIX)
  316.     int j = (int) sysconf(_SC_OPEN_MAX);
  317.  
  318.     if (j >= 0)
  319.         return (j);
  320. #endif /* POSIX */
  321.     return (FD_SETSIZE);
  322. }
  323. #endif /* BSD */
  324.  
  325. int
  326. my_close(fd)
  327.     int fd;
  328. {
  329.     int s = close(fd);
  330.  
  331.     if (s < 0) {
  332.         syslog(LOG_ERR, "close(%d) failed: %m", fd);
  333.         dprintf(3, (ddt, "close(%d) failed: %s\n",
  334.                 fd, strerror(errno)));
  335.     } else {
  336.         dprintf(3, (ddt, "close(%d) succeeded\n", fd));
  337.     }
  338.     return (s);
  339. }
  340.  
  341. #ifdef GEN_AXFR
  342. /*
  343.  * Map class names to number
  344.  */
  345. struct map {
  346.     char    *token;
  347.     int    val;
  348. };
  349.  
  350. static struct map map_class[] = {
  351.     "in",    C_IN,
  352.     "chaos", C_CHAOS,
  353.     "hs",    C_HS,
  354.     NULL,    0,
  355. };
  356.  
  357. int
  358. get_class(class)
  359.     char *class;
  360. {
  361.     struct map *mp;
  362.  
  363.     if (isdigit(*class))
  364.         return (atoi(class));
  365.     for (mp = map_class; mp->token != NULL; mp++)
  366.         if (strcasecmp(class, mp->token) == 0)
  367.             return (mp->val);
  368.     return (C_IN);
  369. }
  370. #endif
  371.  
  372. int
  373. my_fclose(fp)
  374.     FILE *fp;
  375. {
  376.     int fd = fileno(fp),
  377.         s = fclose(fp);
  378.  
  379.     if (s < 0) {
  380.         syslog(LOG_ERR, "fclose(%d) failed: %m", fd);
  381.         dprintf(3, (ddt, "fclose(%d) failed: %s\n",
  382.                 fd, strerror(errno)));
  383.     } else {
  384.         dprintf(3, (ddt, "fclose(%d) succeeded\n", fd));
  385.     }
  386.     return (s);
  387. }
  388.  
  389. /*
  390.  * Make a copy of a string and return a pointer to it.
  391.  */
  392. char *
  393. savestr(str)
  394.     char *str;
  395. {
  396.     char *cp;
  397.  
  398.     cp = (char *)malloc(strlen(str) + 1);
  399.     if (cp == NULL) {
  400.         syslog(LOG_ERR, "savestr: %m");
  401.         exit(1);
  402.     }
  403.     (void) strcpy(cp, str);
  404.     return (cp);
  405. }
  406.  
  407. int
  408. writemsg(rfd, msg, msglen)
  409.     int rfd;
  410.     u_char *msg;
  411.     int msglen;
  412. {
  413.     struct iovec iov[2];
  414.     u_char len[INT16SZ];
  415.  
  416.     __putshort(msglen, len);
  417.     iov[0].iov_base = (char *)len;
  418.     iov[0].iov_len = INT16SZ;
  419.     iov[1].iov_base = (char *)msg;
  420.     iov[1].iov_len = msglen;
  421.     if (writev(rfd, iov, 2) != INT16SZ + msglen) {
  422.         dprintf(1, (ddt, "write failed %d\n", errno));
  423.         return (-1);
  424.     }
  425.     return (0);
  426. }
  427.  
  428. /* rm_datum(dp, np, pdp)
  429.  *    remove datum 'dp' from name 'np'.  pdp is previous data pointer.
  430.  * return value:
  431.  *    "next" field from removed datum, suitable for relinking
  432.  */
  433. struct databuf *
  434. rm_datum(dp, np, pdp)
  435.     register struct databuf *dp;
  436.     register struct namebuf *np;
  437.     register struct databuf *pdp;
  438. {
  439.     register struct databuf *ndp = dp->d_next;
  440.  
  441.     dprintf(3, (ddt, "rm_datum(%x, %x, %x) -> %x\n",
  442.             dp, np->n_data, pdp, ndp));
  443. #ifdef INVQ
  444.     rminv(dp);
  445. #endif
  446.     if (pdp == NULL)
  447.         np->n_data = ndp;
  448.     else
  449.         pdp->d_next = ndp;
  450. #ifdef    DATUMREFCNT
  451.     if (--(dp->d_rcnt)) {
  452.         switch(dp->d_type) {
  453.         case T_NS:
  454.             dprintf(1, (ddt, "rm_datum: %s rcnt = %d\n",
  455.                 dp->d_data, dp->d_rcnt));
  456.             break;
  457.         case T_A:
  458.             dprintf(1, (ddt, "rm_datum: %08.8X rcnt = %d\n",
  459.                 *(int32_t*)(dp->d_data), dp->d_rcnt));
  460.             break;
  461.         default:
  462.             dprintf(1, (ddt, "rm_datum: rcnt = %d\n", dp->d_rcnt));
  463.         }
  464.     } else
  465. #endif
  466.     free((char *)dp);
  467.     return (ndp);
  468. }
  469.  
  470. /* rm_name(np, he, pnp)
  471.  *    remove name 'np' from parent 'pp'.  pnp is previous name pointer.
  472.  * return value:
  473.  *    "next" field from removed name, suitable for relinking
  474.  */
  475. struct namebuf *
  476. rm_name(np, pp, pnp)
  477.     struct namebuf *np, **pp, *pnp;
  478. {
  479.     struct namebuf *nnp = np->n_next;
  480.     char *msg;
  481.  
  482.     /* verify */
  483.     if ( (np->n_data && (msg = "data"))
  484.       || (np->n_hash && (msg = "hash"))
  485.         ) {
  486.         dprintf(1, (ddt,
  487.                 "rm_name(%x(%s)): non-nil %s pointer\n",
  488.                 np, np->n_dname?np->n_dname:"Nil", msg));
  489.         syslog(LOG_ERR,
  490.             "rm_name(%x(%s)): non-nil %s pointer\n",
  491.             np, np->n_dname?np->n_dname:"Nil", msg);
  492.         abort();
  493.     }
  494.  
  495.     /* unlink */
  496.     if (pnp) {
  497.         pnp->n_next = nnp;
  498.     } else {
  499.         *pp = nnp;
  500.     }
  501.  
  502.     /* deallocate */
  503.     free(np->n_dname);
  504.     free((char*) np);
  505.  
  506.     /* done */
  507.     return (nnp);
  508. }
  509.  
  510. /*
  511.  * Get the domain name of 'np' and put in 'buf'.  Bounds checking is done.
  512.  */
  513. void
  514. getname(np, buf, buflen)
  515.     struct namebuf *np;
  516.     char *buf;
  517.     int buflen;
  518. {
  519.     register char *cp;
  520.     register int i;
  521.  
  522.     cp = buf;
  523.     while (np != NULL) {
  524.         if ((i = strlen(np->n_dname))+1 >= buflen) {
  525.             *cp = '\0';
  526.             syslog(LOG_ERR, "domain name too long: %s...\n", buf);
  527.             strcpy(buf, "Name_Too_Long");
  528.             return;
  529.         }
  530.         if (cp != buf)
  531.             *cp++ = '.';
  532.         (void) strcpy(cp, np->n_dname);
  533.         cp += i;
  534.         buflen -= (i+1);
  535.         np = np->n_parent;
  536.     }
  537.     *cp = '\0';
  538. }
  539.  
  540. #ifdef INVQ
  541. /*
  542.  * Add data 'dp' to inverse query tables for name 'np'.
  543.  */
  544. void
  545. addinv(np, dp)
  546.     struct namebuf *np;
  547.     struct databuf *dp;
  548. {
  549.     register struct invbuf *ip;
  550.     register int hval, i;
  551.  
  552.     switch (dp->d_type) {
  553.     case T_A:
  554.     case T_UID:
  555.     case T_GID:
  556.         break;
  557.  
  558.     default:
  559.         return;
  560.     }
  561.  
  562.     hval = dhash(dp->d_data, dp->d_size);
  563.     for (ip = invtab[hval]; ip != NULL; ip = ip->i_next)
  564.         for (i = 0; i < INVBLKSZ; i++)
  565.             if (ip->i_dname[i] == NULL) {
  566.                 ip->i_dname[i] = np;
  567.                 return;
  568.             }
  569.     ip = saveinv();
  570.     ip->i_next = invtab[hval];
  571.     invtab[hval] = ip;
  572.     ip->i_dname[0] = np;
  573. }
  574.  
  575. /*
  576.  * Remove data 'odp' from inverse query table.
  577.  */
  578. void
  579. rminv(odp)
  580.     struct databuf *odp;
  581. {
  582.     register struct invbuf *ip;
  583.     register struct databuf *dp;
  584.     struct namebuf *np;
  585.     register int i;
  586.  
  587.     for (ip = invtab[dhash(odp->d_data, odp->d_size)]; ip != NULL;
  588.         ip = ip->i_next) {
  589.         for (i = 0; i < INVBLKSZ; i++) {
  590.             if ((np = ip->i_dname[i]) == NULL)
  591.                 break;
  592.             for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
  593.                 if (dp != odp)
  594.                     continue;
  595.                 while (i < INVBLKSZ-1) {
  596.                     ip->i_dname[i] = ip->i_dname[i+1];
  597.                     i++;
  598.                 }
  599.                 ip->i_dname[i] = NULL;
  600.                 return;
  601.             }
  602.         }
  603.     }
  604. }
  605.  
  606. /*
  607.  * Allocate an inverse query buffer.
  608.  */
  609. struct invbuf *
  610. saveinv()
  611. {
  612.     register struct invbuf *ip;
  613.  
  614.     ip = (struct invbuf *) malloc(sizeof(struct invbuf));
  615.     if (ip == NULL) {
  616.         syslog(LOG_ERR, "saveinv: %m");
  617.         exit(1);
  618.     }
  619.     ip->i_next = NULL;
  620.     bzero((char *)ip->i_dname, sizeof(ip->i_dname));
  621.     return (ip);
  622. }
  623. #endif /*INVQ*/
  624.  
  625. /*
  626.  * Compute hash value from data.
  627.  */
  628. int
  629. dhash(dp, dlen)
  630.     u_char *dp;
  631.     int dlen;
  632. {
  633.     register u_char *cp;
  634.     register unsigned hval;
  635.     register int n;
  636.  
  637.     n = dlen;
  638.     if (n > 8)
  639.         n = 8;
  640.     hval = 0;
  641.     for (cp = dp; --n >= 0; ) {
  642.         hval <<= 1;
  643.         hval += *cp++;
  644.     }
  645.     return (hval % INVHASHSZ);
  646. }
  647.  
  648. /*
  649. ** SAMEDOMAIN -- Check whether a name belongs to a domain
  650. ** ------------------------------------------------------
  651. **
  652. **    Returns:
  653. **        TRUE if the given name lies in the domain.
  654. **        FALSE otherwise.
  655. **
  656. **    Trailing dots are first removed from name and domain.
  657. **    Always compare complete subdomains, not only whether the
  658. **    domain name is the trailing string of the given name.
  659. **
  660. **    "host.foobar.top" lies in "foobar.top" and in "top" and in ""
  661. **    but NOT in "bar.top"
  662. **
  663. **    this implementation of samedomain() is thanks to Bob Heiney.
  664. */
  665.  
  666. int
  667. samedomain(a, b)
  668.     const char *a, *b;
  669. {
  670.     size_t la, lb;
  671.     const char *cp;
  672.  
  673.     la = strlen(a);
  674.     lb = strlen(b);
  675.  
  676.     /* don't count trailing dots, if any. */
  677.     if (la && a[la-1]=='.')
  678.         la--;
  679.     if (lb && b[lb-1]=='.')
  680.         lb--;
  681.  
  682.     /* lb==0 means b is the root domain, so a must be in b. */
  683.     if (lb == 0)
  684.         return (1);
  685.  
  686.     /* b longer than a means a can't be in b. */
  687.     if (lb > la)
  688.         return (0);
  689.  
  690.     /* We use strncasecmp because we might be trying to
  691.      * ignore trailing dots. */
  692.     if (lb == la)
  693.         return (strncasecmp(a, b, lb) == 0);
  694.  
  695.     /* Ok, we know la > lb. */
  696.  
  697.     /* Point at the character before the last 'lb' characters of a. */
  698.     cp = a + (la - lb - 1);
  699.  
  700.     /* If it isn't '.', can't be a match (this lets us avoid
  701.      * having "foobar.com" match "bar.com"). */
  702.     if (*cp != '.')
  703.         return (0);
  704.  
  705.     cp++;
  706.  
  707.     /* We use strncasecmp because we might be trying to
  708.      * ignore trailing dots. */
  709.     return (strncasecmp(cp, b, lb)==0);
  710. }
  711.