| /* |
| * Copyright 2015 Gary Mills |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* |
| * Copyright (c) 1985, 1988 Regents of the University of California. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms are permitted |
| * provided that this notice is preserved and that due credit is given |
| * to the University of California at Berkeley. The name of the University |
| * may not be used to endorse or promote products derived from this |
| * software without specific prior written permission. This software |
| * is provided ``as is'' without express or implied warranty. |
| * |
| */ |
| |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <ctype.h> |
| #include <netdb.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <arpa/inet.h> |
| #include <arpa/nameser.h> |
| #include <resolv.h> |
| #include <syslog.h> |
| #include "crossl.h" |
| |
| /* |
| * When the name service switch calls libresolv, it doesn't want fallback |
| * to /etc/hosts, so we provide a method to turn it off. |
| */ |
| static int no_hosts_fallback = 0; |
| |
| void |
| __res_set_no_hosts_fallback(void) { |
| no_hosts_fallback = 1; |
| } |
| |
| static int |
| __res_no_hosts_fallback(void) { |
| return(no_hosts_fallback); |
| } |
| |
| static char *h_addr_ptrs[MAXADDRS + 1]; |
| |
| static struct hostent host; |
| static char *host_aliases[MAXALIASES]; |
| static char hostbuf[BUFSIZ+1]; |
| static struct in_addr host_addr; |
| static char HOSTDB[] = "/etc/hosts"; |
| static FILE *hostf = NULL; |
| static char hostaddr[MAXADDRS]; |
| static char *host_addrs[2]; |
| static int stayopen = 0; |
| static char *any(); |
| |
| #if PACKETSZ > 1024 |
| #define MAXPACKET PACKETSZ |
| #else |
| #define MAXPACKET 1024 |
| #endif |
| |
| typedef union { |
| HEADER hdr; |
| u_char buf[MAXPACKET]; |
| } querybuf; |
| |
| static union { |
| long al; |
| char ac; |
| } align; |
| |
| |
| int h_errno; |
| |
| static struct hostent * |
| getanswer(answer, anslen, iquery) |
| querybuf *answer; |
| int anslen; |
| int iquery; |
| { |
| register HEADER *hp; |
| register u_char *cp; |
| register int n; |
| u_char *eom; |
| char *bp, **ap; |
| int type, class, buflen, ancount, qdcount; |
| int haveanswer, getclass = C_ANY; |
| char **hap; |
| |
| eom = answer->buf + anslen; |
| /* |
| * find first satisfactory answer |
| */ |
| hp = &answer->hdr; |
| ancount = ntohs(hp->ancount); |
| qdcount = ntohs(hp->qdcount); |
| bp = hostbuf; |
| buflen = sizeof (hostbuf); |
| cp = answer->buf + sizeof (HEADER); |
| if (qdcount) { |
| if (iquery) { |
| if ((n = dn_expand(answer->buf, eom, |
| cp, (u_char *)bp, buflen)) < 0) { |
| h_errno = NO_RECOVERY; |
| return ((struct hostent *) NULL); |
| } |
| cp += n + QFIXEDSZ; |
| host.h_name = bp; |
| n = strlen(bp) + 1; |
| bp += n; |
| buflen -= n; |
| } else |
| cp += dn_skipname(cp, eom) + QFIXEDSZ; |
| while (--qdcount > 0) |
| cp += dn_skipname(cp, eom) + QFIXEDSZ; |
| } else if (iquery) { |
| if (hp->aa) |
| h_errno = HOST_NOT_FOUND; |
| else |
| h_errno = TRY_AGAIN; |
| return ((struct hostent *) NULL); |
| } |
| ap = host_aliases; |
| host.h_aliases = host_aliases; |
| hap = h_addr_ptrs; |
| #if BSD >= 43 || defined(h_addr) /* new-style hostent structure */ |
| host.h_addr_list = h_addr_ptrs; |
| #endif |
| haveanswer = 0; |
| while (--ancount >= 0 && cp < eom && haveanswer < MAXADDRS) { |
| if ((n = dn_expand(answer->buf, eom, |
| cp, (u_char *)bp, buflen)) < 0) |
| break; |
| cp += n; |
| type = _getshort(cp); |
| cp += sizeof (u_short); |
| class = _getshort(cp); |
| cp += sizeof (u_short) + sizeof (u_long); |
| n = _getshort(cp); |
| cp += sizeof (u_short); |
| if (type == T_CNAME) { |
| cp += n; |
| if (ap >= &host_aliases[MAXALIASES-1]) |
| continue; |
| *ap++ = bp; |
| n = strlen(bp) + 1; |
| bp += n; |
| buflen -= n; |
| continue; |
| } |
| if (iquery && type == T_PTR) { |
| if ((n = dn_expand(answer->buf, eom, |
| cp, (u_char *)bp, buflen)) < 0) { |
| cp += n; |
| continue; |
| } |
| cp += n; |
| host.h_name = bp; |
| return (&host); |
| } |
| if (iquery || type != T_A) { |
| #ifdef DEBUG |
| if (_res.options & RES_DEBUG) |
| printf("unexpected answer type %d, size %d\n", |
| type, n); |
| #endif |
| cp += n; |
| continue; |
| } |
| if (haveanswer) { |
| if (n != host.h_length) { |
| cp += n; |
| continue; |
| } |
| if (class != getclass) { |
| cp += n; |
| continue; |
| } |
| } else { |
| host.h_length = n; |
| getclass = class; |
| host.h_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC; |
| if (!iquery) { |
| host.h_name = bp; |
| bp += strlen(bp) + 1; |
| } |
| } |
| |
| bp += sizeof (align) - ((u_long)bp % sizeof (align)); |
| |
| if (bp + n >= &hostbuf[sizeof (hostbuf)]) { |
| #ifdef DEBUG |
| if (_res.options & RES_DEBUG) |
| printf("size (%d) too big\n", n); |
| #endif |
| break; |
| } |
| #ifdef SYSV |
| memcpy((void *)(*hap++ = bp), (void *)cp, n); |
| #else |
| bcopy(cp, *hap++ = bp, n); |
| #endif |
| bp += n; |
| cp += n; |
| haveanswer++; |
| } |
| if (haveanswer) { |
| *ap = NULL; |
| #if BSD >= 43 || defined(h_addr) /* new-style hostent structure */ |
| *hap = NULL; |
| #else |
| host.h_addr = h_addr_ptrs[0]; |
| #endif |
| return (&host); |
| } else { |
| h_errno = TRY_AGAIN; |
| return ((struct hostent *) NULL); |
| } |
| } |
| |
| static struct hostent *_gethtbyname(); |
| |
| struct hostent * |
| res_gethostbyname(name) |
| char *name; |
| { |
| querybuf buf; |
| register char *cp; |
| int n; |
| |
| /* |
| * disallow names consisting only of digits/dots, unless |
| * they end in a dot. |
| */ |
| if (isdigit(name[0])) |
| for (cp = name; /*EMPTY*/; ++cp) { |
| if (!*cp) { |
| if (*--cp == '.') |
| break; |
| h_errno = HOST_NOT_FOUND; |
| return ((struct hostent *) NULL); |
| } |
| if (!isdigit(*cp) && *cp != '.') |
| break; |
| } |
| |
| if ((n = res_search(name, C_IN, T_A, buf.buf, sizeof (buf))) < 0) { |
| #ifdef DEBUG |
| if (_res.options & RES_DEBUG) |
| printf("res_search failed\n"); |
| #endif |
| if (errno == ECONNREFUSED) |
| return (_gethtbyname(name)); |
| else |
| return ((struct hostent *) NULL); |
| } |
| return (getanswer(&buf, n, 0)); |
| } |
| |
| static struct hostent *_gethtbyaddr(); |
| |
| static struct hostent * |
| _getrhbyaddr(addr, len, type) |
| char *addr; |
| int len, type; |
| { |
| int n; |
| querybuf buf; |
| register struct hostent *hp; |
| char qbuf[MAXDNAME]; |
| |
| if (type != AF_INET) |
| return ((struct hostent *) NULL); |
| (void) sprintf(qbuf, "%d.%d.%d.%d.in-addr.arpa", |
| ((unsigned)addr[3] & 0xff), |
| ((unsigned)addr[2] & 0xff), |
| ((unsigned)addr[1] & 0xff), |
| ((unsigned)addr[0] & 0xff)); |
| n = res_query(qbuf, C_IN, T_PTR, (u_char *)&buf, sizeof (buf)); |
| if (n < 0) { |
| #ifdef DEBUG |
| if (_res.options & RES_DEBUG) |
| printf("res_query failed\n"); |
| #endif |
| if (errno == ECONNREFUSED) |
| return (_gethtbyaddr(addr, len, type)); |
| return ((struct hostent *) NULL); |
| } |
| hp = getanswer(&buf, n, 1); |
| if (hp == NULL) |
| return ((struct hostent *) NULL); |
| hp->h_addrtype = type; |
| hp->h_length = len; |
| h_addr_ptrs[0] = (char *)&host_addr; |
| h_addr_ptrs[1] = (char *)0; |
| host_addr = *(struct in_addr *)addr; |
| return (hp); |
| } |
| |
| /* |
| * First we get what the PTR record says, but do an extra call |
| * to gethostbyname() to make sure that someone is not trying to |
| * spoof us. Hopefully this is not done that often, so good |
| * performance is not really an issue. |
| */ |
| struct hostent * |
| res_gethostbyaddr(addr, len, type) |
| char *addr; |
| int len; |
| int type; |
| { |
| char **a, hbuf[MAXHOSTNAMELEN]; |
| struct hostent *hp, *hp2; |
| |
| if ((hp = _getrhbyaddr(addr, len, type)) == (struct hostent *)NULL) |
| return ((struct hostent *)NULL); |
| |
| /* hang on to what we got as an answer */ |
| (void) strcpy(hbuf, hp->h_name); |
| |
| /* check to make sure by doing a forward query */ |
| if ((hp2 = res_gethostbyname(hbuf)) != (struct hostent *)NULL) |
| for (a = hp2->h_addr_list; *a; a++) |
| #ifdef SYSV |
| if (memcmp(*a, addr, hp2->h_length) == 0) |
| #else |
| if (bcmp(*a, addr, hp2->h_length) == 0) |
| #endif |
| return (hp2); |
| |
| /* |
| * we've been spoofed, make sure to log it. |
| * XXX - syslog needs a security priority level. |
| */ |
| syslog(LOG_NOTICE, "gethostbyaddr: %s != %s", hbuf, |
| inet_ntoa(*(struct in_addr *)addr)); |
| return ((struct hostent *)NULL); |
| } |
| |
| static void |
| _sethtent(int f) |
| { |
| if (__res_no_hosts_fallback()) return; |
| |
| if (hostf == NULL) |
| hostf = fopen(HOSTDB, "r"); |
| else |
| rewind(hostf); |
| stayopen |= f; |
| } |
| |
| static void |
| _endhtent(void) |
| { |
| if (__res_no_hosts_fallback()) return; |
| |
| if (hostf && !stayopen) { |
| (void) fclose(hostf); |
| hostf = NULL; |
| } |
| } |
| |
| static struct hostent * |
| _gethtent() |
| { |
| char *p; |
| register char *cp, **q; |
| |
| if (__res_no_hosts_fallback()) return(NULL); |
| |
| if (hostf == NULL && (hostf = fopen(HOSTDB, "r")) == NULL) |
| return (NULL); |
| again: |
| if ((p = fgets(hostbuf, BUFSIZ, hostf)) == NULL) |
| return (NULL); |
| if (*p == '#') |
| goto again; |
| cp = any(p, "#\n"); |
| if (cp == NULL) |
| goto again; |
| *cp = '\0'; |
| cp = any(p, " \t"); |
| if (cp == NULL) |
| goto again; |
| *cp++ = '\0'; |
| /* THIS STUFF IS INTERNET SPECIFIC */ |
| #if BSD >= 43 || defined(h_addr) /* new-style hostent structure */ |
| host.h_addr_list = host_addrs; |
| #endif |
| host.h_addr = hostaddr; |
| *((u_long *)host.h_addr) = inet_addr(p); |
| host.h_length = sizeof (u_long); |
| host.h_addrtype = AF_INET; |
| while (*cp == ' ' || *cp == '\t') |
| cp++; |
| host.h_name = cp; |
| q = host.h_aliases = host_aliases; |
| cp = any(cp, " \t"); |
| if (cp != NULL) |
| *cp++ = '\0'; |
| while (cp && *cp) { |
| if (*cp == ' ' || *cp == '\t') { |
| cp++; |
| continue; |
| } |
| if (q < &host_aliases[MAXALIASES - 1]) |
| *q++ = cp; |
| cp = any(cp, " \t"); |
| if (cp != NULL) |
| *cp++ = '\0'; |
| } |
| *q = NULL; |
| return (&host); |
| } |
| |
| static char * |
| any(cp, match) |
| register char *cp; |
| char *match; |
| { |
| register char *mp, c; |
| |
| while (c = *cp) { |
| for (mp = match; *mp; mp++) |
| if (*mp == c) |
| return (cp); |
| cp++; |
| } |
| return ((char *)0); |
| } |
| |
| static struct hostent * |
| _gethtbyname(name) |
| char *name; |
| { |
| register struct hostent *p; |
| register char **cp; |
| |
| _sethtent(0); |
| while (p = _gethtent()) { |
| if (strcasecmp(p->h_name, name) == 0) |
| break; |
| for (cp = p->h_aliases; *cp != 0; cp++) |
| if (strcasecmp(*cp, name) == 0) |
| goto found; |
| } |
| found: |
| _endhtent(); |
| return (p); |
| } |
| |
| static struct hostent * |
| _gethtbyaddr(addr, len, type) |
| char *addr; |
| int len, type; |
| { |
| register struct hostent *p; |
| |
| _sethtent(0); |
| while (p = _gethtent()) |
| #ifdef SYSV |
| if (p->h_addrtype == type && !memcmp(p->h_addr, addr, len)) |
| #else |
| if (p->h_addrtype == type && !bcmp(p->h_addr, addr, len)) |
| #endif |
| break; |
| _endhtent(); |
| return (p); |
| } |