1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /*
   7  * Copyright (c) 1985, 1988 Regents of the University of California.
   8  * All rights reserved.
   9  *
  10  * Redistribution and use in source and binary forms are permitted
  11  * provided that this notice is preserved and that due credit is given
  12  * to the University of California at Berkeley. The name of the University
  13  * may not be used to endorse or promote products derived from this
  14  * software without specific prior written permission. This software
  15  * is provided ``as is'' without express or implied warranty.
  16  *
  17  */
  18 
  19 #include <sys/param.h>
  20 #include <sys/socket.h>
  21 #include <netinet/in.h>
  22 #include <ctype.h>
  23 #include <netdb.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 <syslog.h>
  30 
  31 /*
  32  * When the name service switch calls libresolv, it doesn't want fallback
  33  * to /etc/hosts, so we provide a method to turn it off.
  34  */
  35 static int      no_hosts_fallback = 0;
  36 
  37 void
  38 __res_set_no_hosts_fallback(void) {
  39         no_hosts_fallback = 1;
  40 }
  41 
  42 static int
  43 __res_no_hosts_fallback(void) {
  44         return(no_hosts_fallback);
  45 }
  46 
  47 static char *h_addr_ptrs[MAXADDRS + 1];
  48 
  49 static struct hostent host;
  50 static char *host_aliases[MAXALIASES];
  51 static char hostbuf[BUFSIZ+1];
  52 static struct in_addr host_addr;
  53 static char HOSTDB[] = "/etc/hosts";
  54 static FILE *hostf = NULL;
  55 static char hostaddr[MAXADDRS];
  56 static char *host_addrs[2];
  57 static int stayopen = 0;
  58 static char *any();
  59 
  60 #if PACKETSZ > 1024
  61 #define MAXPACKET       PACKETSZ
  62 #else
  63 #define MAXPACKET       1024
  64 #endif
  65 
  66 typedef union {
  67         HEADER hdr;
  68         u_char buf[MAXPACKET];
  69 } querybuf;
  70 
  71 static union {
  72         long al;
  73         char ac;
  74 } align;
  75 
  76 
  77 int h_errno;
  78 
  79 static struct hostent *
  80 getanswer(answer, anslen, iquery)
  81         querybuf *answer;
  82         int anslen;
  83         int iquery;
  84 {
  85         register HEADER *hp;
  86         register u_char *cp;
  87         register int n;
  88         u_char *eom;
  89         char *bp, **ap;
  90         int type, class, buflen, ancount, qdcount;
  91         int haveanswer, getclass = C_ANY;
  92         char **hap;
  93 
  94         eom = answer->buf + anslen;
  95         /*
  96          * find first satisfactory answer
  97          */
  98         hp = &answer->hdr;
  99         ancount = ntohs(hp->ancount);
 100         qdcount = ntohs(hp->qdcount);
 101         bp = hostbuf;
 102         buflen = sizeof (hostbuf);
 103         cp = answer->buf + sizeof (HEADER);
 104         if (qdcount) {
 105                 if (iquery) {
 106                         if ((n = dn_expand((char *)answer->buf, eom,
 107                                                 cp, bp, buflen)) < 0) {
 108                                 h_errno = NO_RECOVERY;
 109                                 return ((struct hostent *) NULL);
 110                         }
 111                         cp += n + QFIXEDSZ;
 112                         host.h_name = bp;
 113                         n = strlen(bp) + 1;
 114                         bp += n;
 115                         buflen -= n;
 116                 } else
 117                         cp += dn_skipname(cp, eom) + QFIXEDSZ;
 118                 while (--qdcount > 0)
 119                         cp += dn_skipname(cp, eom) + QFIXEDSZ;
 120         } else if (iquery) {
 121                 if (hp->aa)
 122                         h_errno = HOST_NOT_FOUND;
 123                 else
 124                         h_errno = TRY_AGAIN;
 125                 return ((struct hostent *) NULL);
 126         }
 127         ap = host_aliases;
 128         host.h_aliases = host_aliases;
 129         hap = h_addr_ptrs;
 130 #if BSD >= 43 || defined(h_addr)     /* new-style hostent structure */
 131         host.h_addr_list = h_addr_ptrs;
 132 #endif
 133         haveanswer = 0;
 134         while (--ancount >= 0 && cp < eom && haveanswer < MAXADDRS) {
 135                 if ((n = dn_expand((char *)answer->buf, eom,
 136                                                 cp, bp, buflen)) < 0)
 137                         break;
 138                 cp += n;
 139                 type = _getshort(cp);
 140                 cp += sizeof (u_short);
 141                 class = _getshort(cp);
 142                 cp += sizeof (u_short) + sizeof (u_long);
 143                 n = _getshort(cp);
 144                 cp += sizeof (u_short);
 145                 if (type == T_CNAME) {
 146                         cp += n;
 147                         if (ap >= &host_aliases[MAXALIASES-1])
 148                                 continue;
 149                         *ap++ = bp;
 150                         n = strlen(bp) + 1;
 151                         bp += n;
 152                         buflen -= n;
 153                         continue;
 154                 }
 155                 if (iquery && type == T_PTR) {
 156                         if ((n = dn_expand((char *)answer->buf, eom,
 157                                         cp, bp, buflen)) < 0) {
 158                                 cp += n;
 159                                 continue;
 160                         }
 161                         cp += n;
 162                         host.h_name = bp;
 163                         return (&host);
 164                 }
 165                 if (iquery || type != T_A) {
 166 #ifdef DEBUG
 167                         if (_res.options & RES_DEBUG)
 168                                 printf("unexpected answer type %d, size %d\n",
 169                                         type, n);
 170 #endif
 171                         cp += n;
 172                         continue;
 173                 }
 174                 if (haveanswer) {
 175                         if (n != host.h_length) {
 176                                 cp += n;
 177                                 continue;
 178                         }
 179                         if (class != getclass) {
 180                                 cp += n;
 181                                 continue;
 182                         }
 183                 } else {
 184                         host.h_length = n;
 185                         getclass = class;
 186                         host.h_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC;
 187                         if (!iquery) {
 188                                 host.h_name = bp;
 189                                 bp += strlen(bp) + 1;
 190                         }
 191                 }
 192 
 193                 bp += sizeof (align) - ((u_long)bp % sizeof (align));
 194 
 195                 if (bp + n >= &hostbuf[sizeof (hostbuf)]) {
 196 #ifdef DEBUG
 197                         if (_res.options & RES_DEBUG)
 198                                 printf("size (%d) too big\n", n);
 199 #endif
 200                         break;
 201                 }
 202 #ifdef SYSV
 203                 memcpy((void *)(*hap++ = bp), (void *)cp, n);
 204 #else
 205                 bcopy(cp, *hap++ = bp, n);
 206 #endif
 207                 bp += n;
 208                 cp += n;
 209                 haveanswer++;
 210         }
 211         if (haveanswer) {
 212                 *ap = NULL;
 213 #if BSD >= 43 || defined(h_addr)     /* new-style hostent structure */
 214                 *hap = NULL;
 215 #else
 216                 host.h_addr = h_addr_ptrs[0];
 217 #endif
 218                 return (&host);
 219         } else {
 220                 h_errno = TRY_AGAIN;
 221                 return ((struct hostent *) NULL);
 222         }
 223 }
 224 
 225 static struct hostent *_gethtbyname();
 226 
 227 struct hostent *
 228 res_gethostbyname(name)
 229         char *name;
 230 {
 231         querybuf buf;
 232         register char *cp;
 233         int n;
 234         struct hostent *hp, *gethostdomain();
 235 
 236         /*
 237          * disallow names consisting only of digits/dots, unless
 238          * they end in a dot.
 239          */
 240         if (isdigit(name[0]))
 241                 for (cp = name; /*EMPTY*/; ++cp) {
 242                         if (!*cp) {
 243                                 if (*--cp == '.')
 244                                         break;
 245                                 h_errno = HOST_NOT_FOUND;
 246                                 return ((struct hostent *) NULL);
 247                         }
 248                         if (!isdigit(*cp) && *cp != '.')
 249                                 break;
 250                 }
 251 
 252         if ((n = res_search(name, C_IN, T_A, buf.buf, sizeof (buf))) < 0) {
 253 #ifdef DEBUG
 254                 if (_res.options & RES_DEBUG)
 255                         printf("res_search failed\n");
 256 #endif
 257                 if (errno == ECONNREFUSED)
 258                         return (_gethtbyname(name));
 259                 else
 260                         return ((struct hostent *) NULL);
 261         }
 262         return (getanswer(&buf, n, 0));
 263 }
 264 
 265 static struct hostent *_gethtbyaddr();
 266 
 267 static struct hostent *
 268 _getrhbyaddr(addr, len, type)
 269         char *addr;
 270         int len, type;
 271 {
 272         int n;
 273         querybuf buf;
 274         register struct hostent *hp;
 275         char qbuf[MAXDNAME];
 276 
 277         if (type != AF_INET)
 278                 return ((struct hostent *) NULL);
 279         (void) sprintf(qbuf, "%d.%d.%d.%d.in-addr.arpa",
 280                 ((unsigned)addr[3] & 0xff),
 281                 ((unsigned)addr[2] & 0xff),
 282                 ((unsigned)addr[1] & 0xff),
 283                 ((unsigned)addr[0] & 0xff));
 284         n = res_query(qbuf, C_IN, T_PTR, (char *)&buf, sizeof (buf));
 285         if (n < 0) {
 286 #ifdef DEBUG
 287                 if (_res.options & RES_DEBUG)
 288                         printf("res_query failed\n");
 289 #endif
 290                 if (errno == ECONNREFUSED)
 291                         return (_gethtbyaddr(addr, len, type));
 292                 return ((struct hostent *) NULL);
 293         }
 294         hp = getanswer(&buf, n, 1);
 295         if (hp == NULL)
 296                 return ((struct hostent *) NULL);
 297         hp->h_addrtype = type;
 298         hp->h_length = len;
 299         h_addr_ptrs[0] = (char *)&host_addr;
 300         h_addr_ptrs[1] = (char *)0;
 301         host_addr = *(struct in_addr *)addr;
 302         return (hp);
 303 }
 304 
 305 /*
 306  * First we get what the PTR record says, but do an extra call
 307  * to gethostbyname() to make sure that someone is not trying to
 308  * spoof us.  Hopefully this is not done that often, so good
 309  * performance is not really an issue.
 310  */
 311 struct hostent *
 312 res_gethostbyaddr(addr, len, type)
 313         char    *addr;
 314         int     len;
 315         int     type;
 316 {
 317         char            **a, hbuf[MAXHOSTNAMELEN];
 318         struct hostent  *hp, *hp2;
 319 
 320         if ((hp = _getrhbyaddr(addr, len, type)) == (struct hostent *)NULL)
 321                 return ((struct hostent *)NULL);
 322 
 323         /* hang on to what we got as an answer */
 324         (void) strcpy(hbuf, hp->h_name);
 325 
 326         /* check to make sure by doing a forward query */
 327         if ((hp2 = res_gethostbyname(hbuf)) != (struct hostent *)NULL)
 328                 for (a = hp2->h_addr_list; *a; a++)
 329 #ifdef SYSV
 330                         if (memcmp(*a, addr, hp2->h_length) == 0)
 331 #else
 332                         if (bcmp(*a, addr, hp2->h_length) == 0)
 333 #endif
 334                                 return (hp2);
 335 
 336         /*
 337          * we've been spoofed, make sure to log it.
 338          * XXX - syslog needs a security priority level.
 339          */
 340         syslog(LOG_NOTICE, "gethostbyaddr: %s != %s", hbuf,
 341                 inet_ntoa(*(struct in_addr *)addr));
 342         return ((struct hostent *)NULL);
 343 }
 344 
 345 static void
 346 _sethtent(int f)
 347 {
 348         if (__res_no_hosts_fallback()) return;
 349 
 350         if (hostf == NULL)
 351                 hostf = fopen(HOSTDB, "r");
 352         else
 353                 rewind(hostf);
 354         stayopen |= f;
 355 }
 356 
 357 static void
 358 _endhtent(void)
 359 {
 360         if (__res_no_hosts_fallback()) return;
 361 
 362         if (hostf && !stayopen) {
 363                 (void) fclose(hostf);
 364                 hostf = NULL;
 365         }
 366 }
 367 
 368 static struct hostent *
 369 _gethtent()
 370 {
 371         char *p;
 372         register char *cp, **q;
 373 
 374         if (__res_no_hosts_fallback()) return(NULL);
 375 
 376         if (hostf == NULL && (hostf = fopen(HOSTDB, "r")) == NULL)
 377                 return (NULL);
 378 again:
 379         if ((p = fgets(hostbuf, BUFSIZ, hostf)) == NULL)
 380                 return (NULL);
 381         if (*p == '#')
 382                 goto again;
 383         cp = any(p, "#\n");
 384         if (cp == NULL)
 385                 goto again;
 386         *cp = '\0';
 387         cp = any(p, " \t");
 388         if (cp == NULL)
 389                 goto again;
 390         *cp++ = '\0';
 391         /* THIS STUFF IS INTERNET SPECIFIC */
 392 #if BSD >= 43 || defined(h_addr)     /* new-style hostent structure */
 393         host.h_addr_list = host_addrs;
 394 #endif
 395         host.h_addr = hostaddr;
 396         *((u_long *)host.h_addr) = inet_addr(p);
 397         host.h_length = sizeof (u_long);
 398         host.h_addrtype = AF_INET;
 399         while (*cp == ' ' || *cp == '\t')
 400                 cp++;
 401         host.h_name = cp;
 402         q = host.h_aliases = host_aliases;
 403         cp = any(cp, " \t");
 404         if (cp != NULL)
 405                 *cp++ = '\0';
 406         while (cp && *cp) {
 407                 if (*cp == ' ' || *cp == '\t') {
 408                         cp++;
 409                         continue;
 410                 }
 411                 if (q < &host_aliases[MAXALIASES - 1])
 412                         *q++ = cp;
 413                 cp = any(cp, " \t");
 414                 if (cp != NULL)
 415                         *cp++ = '\0';
 416         }
 417         *q = NULL;
 418         return (&host);
 419 }
 420 
 421 static char *
 422 any(cp, match)
 423         register char *cp;
 424         char *match;
 425 {
 426         register char *mp, c;
 427 
 428         while (c = *cp) {
 429                 for (mp = match; *mp; mp++)
 430                         if (*mp == c)
 431                                 return (cp);
 432                 cp++;
 433         }
 434         return ((char *)0);
 435 }
 436 
 437 static struct hostent *
 438 _gethtbyname(name)
 439         char *name;
 440 {
 441         register struct hostent *p;
 442         register char **cp;
 443 
 444         _sethtent(0);
 445         while (p = _gethtent()) {
 446                 if (strcasecmp(p->h_name, name) == 0)
 447                         break;
 448                 for (cp = p->h_aliases; *cp != 0; cp++)
 449                         if (strcasecmp(*cp, name) == 0)
 450                                 goto found;
 451         }
 452 found:
 453         _endhtent();
 454         return (p);
 455 }
 456 
 457 static struct hostent *
 458 _gethtbyaddr(addr, len, type)
 459         char *addr;
 460         int len, type;
 461 {
 462         register struct hostent *p;
 463 
 464         _sethtent(0);
 465         while (p = _gethtent())
 466 #ifdef SYSV
 467                 if (p->h_addrtype == type && !memcmp(p->h_addr, addr, len))
 468 #else
 469                 if (p->h_addrtype == type && !bcmp(p->h_addr, addr, len))
 470 #endif
 471                         break;
 472         _endhtent();
 473         return (p);
 474 }