/* resolv.c: DNS Resolver * * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>, * The Silver Hammer Group, Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 5-Oct-2000 W. Greathouse wgreathouse@smva.com * Fix memory leak and memory corruption. * -- Every name resolution resulted in * a new parse of resolv.conf and new * copy of nameservers allocated by * strdup. * -- Every name resolution resulted in * a new read of resolv.conf without * resetting index from prior read... * resulting in exceeding array bounds. * * Limit nameservers read from resolv.conf * * Add "search" domains from resolv.conf * * Some systems will return a security * signature along with query answer for * dynamic DNS entries. * -- skip/ignore this answer * * Include arpa/nameser.h for defines. * * General cleanup * * 20-Jun-2001 Michal Moskal <malekith@pld.org.pl> * partial IPv6 support (i.e. gethostbyname2() and resolve_address2() * functions added), IPv6 nameservers are also supported. * * 6-Oct-2001 Jari Korva <jari.korva@iki.fi> * more IPv6 support (IPv6 support for gethostbyaddr(); * address family parameter and improved IPv6 support for get_hosts_byname * and read_etc_hosts; getnameinfo() port from glibc; defined * defined ip6addr_any and in6addr_loopback) * * 2-Feb-2002 Erik Andersen <andersee@debian.org> * Added gethostent(), sethostent(), and endhostent() * */ #define __FORCE_GLIBC #include <features.h> #include <string.h> #include <stdio.h> #include <signal.h> #include <errno.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <resolv.h> #include <netdb.h> #include <ctype.h> #include <arpa/nameser.h> #include <sys/utsname.h> #include <sys/un.h> #define MAX_RECURSE 5 #define REPLY_TIMEOUT 10 #define MAX_RETRIES 15 #define MAX_SERVERS 3 #define MAX_SEARCH 4 #undef DEBUG /*#define DEBUG*/ #ifdef DEBUG #define DPRINTF(X,args...) fprintf(stderr, X, ##args) #else #define DPRINTF(X,args...) #endif /* DEBUG */ struct resolv_header { int id; int qr,opcode,aa,tc,rd,ra,rcode; int qdcount; int ancount; int nscount; int arcount; }; struct resolv_question { char * dotted; int qtype; int qclass; }; struct resolv_answer { char * dotted; int atype; int aclass; int ttl; int rdlength; unsigned char * rdata; int rdoffset; }; enum etc_hosts_action { GET_HOSTS_BYNAME = 0, GETHOSTENT, GET_HOSTS_BYADDR, }; extern int nameservers; extern char * nameserver[MAX_SERVERS]; extern int searchdomains; extern char * searchdomain[MAX_SEARCH]; extern struct hostent * get_hosts_byname(const char * name, int type); extern struct hostent * get_hosts_byaddr(const char * addr, int len, int type); extern void __open_etc_hosts(FILE **fp); extern struct hostent * read_etc_hosts(FILE *fp, const char * name, int type, enum etc_hosts_action action); extern int resolve_address(const char * address, int nscount, char ** nsip, struct in_addr * in); extern int resolve_mailbox(const char * address, int nscount, char ** nsip, struct in_addr * in); extern int dns_lookup(const char * name, int type, int nscount, char ** nsip, unsigned char ** outpacket, struct resolv_answer * a); int encode_dotted(const char * dotted, unsigned char * dest, int maxlen); int decode_dotted(const unsigned char * message, int offset, char * dest, int maxlen); int length_dotted(const unsigned char * message, int offset); int encode_header(struct resolv_header * h, unsigned char * dest, int maxlen); int decode_header(unsigned char * data, struct resolv_header * h); int encode_question(struct resolv_question * q, unsigned char * dest, int maxlen); int decode_question(unsigned char * message, int offset, struct resolv_question * q); int encode_answer(struct resolv_answer * a, unsigned char * dest, int maxlen); int decode_answer(unsigned char * message, int offset, struct resolv_answer * a); int length_question(unsigned char * message, int offset); extern int open_nameservers(void); #ifdef L_encodeh int encode_header(struct resolv_header *h, unsigned char *dest, int maxlen) { if (maxlen < HFIXEDSZ) return -1; dest[0] = (h->id & 0xff00) >> 8; dest[1] = (h->id & 0x00ff) >> 0; dest[2] = (h->qr ? 0x80 : 0) | ((h->opcode & 0x0f) << 3) | (h->aa ? 0x04 : 0) | (h->tc ? 0x02 : 0) | (h->rd ? 0x01 : 0); dest[3] = (h->ra ? 0x80 : 0) | (h->rcode & 0x0f); dest[4] = (h->qdcount & 0xff00) >> 8; dest[5] = (h->qdcount & 0x00ff) >> 0; dest[6] = (h->ancount & 0xff00) >> 8; dest[7] = (h->ancount & 0x00ff) >> 0; dest[8] = (h->nscount & 0xff00) >> 8; dest[9] = (h->nscount & 0x00ff) >> 0; dest[10] = (h->arcount & 0xff00) >> 8; dest[11] = (h->arcount & 0x00ff) >> 0; return HFIXEDSZ; } #endif #ifdef L_decodeh int decode_header(unsigned char *data, struct resolv_header *h) { h->id = (data[0] << 8) | data[1]; h->qr = (data[2] & 0x80) ? 1 : 0; h->opcode = (data[2] >> 3) & 0x0f; h->aa = (data[2] & 0x04) ? 1 : 0; h->tc = (data[2] & 0x02) ? 1 : 0; h->rd = (data[2] & 0x01) ? 1 : 0; h->ra = (data[3] & 0x80) ? 1 : 0; h->rcode = data[3] & 0x0f; h->qdcount = (data[4] << 8) | data[5]; h->ancount = (data[6] << 8) | data[7]; h->nscount = (data[8] << 8) | data[9]; h->arcount = (data[10] << 8) | data[11]; return HFIXEDSZ; } #endif #ifdef L_encoded /* Encode a dotted string into nameserver transport-level encoding. This routine is fairly dumb, and doesn't attempt to compress the data */ int encode_dotted(const char *dotted, unsigned char *dest, int maxlen) { int used = 0; while (dotted && *dotted) { char *c = strchr(dotted, '.'); int l = c ? c - dotted : strlen(dotted); if (l >= (maxlen - used - 1)) return -1; dest[used++] = l; memcpy(dest + used, dotted, l); used += l; if (c) dotted = c + 1; else break; } if (maxlen < 1) return -1; dest[used++] = 0; return used; } #endif #ifdef L_decoded /* Decode a dotted string from nameserver transport-level encoding. This routine understands compressed data. */ int decode_dotted(const unsigned char *data, int offset, char *dest, int maxlen) { int l; int measure = 1; int total = 0; int used = 0; if (!data) return -1; while ((l=data[offset++])) { if (measure) total++; if ((l & 0xc0) == (0xc0)) { if (measure) total++; /* compressed item, redirect */ offset = ((l & 0x3f) << 8) | data[offset]; measure = 0; continue; } if ((used + l + 1) >= maxlen) return -1; memcpy(dest + used, data + offset, l); offset += l; used += l; if (measure) total += l; if (data[offset] != 0) dest[used++] = '.'; else dest[used++] = '\0'; } DPRINTF("Total decode len = %d\n", total); return total; } #endif #ifdef L_lengthd int length_dotted(const unsigned char *data, int offset) { int orig_offset = offset; int l; if (!data) return -1; while ((l = data[offset++])) { if ((l & 0xc0) == (0xc0)) { offset++; break; } offset += l; } return offset - orig_offset; } #endif #ifdef L_encodeq int encode_question(struct resolv_question *q, unsigned char *dest, int maxlen) { int i; i = encode_dotted(q->dotted, dest, maxlen); if (i < 0) return i; dest += i; maxlen -= i; if (maxlen < 4) return -1; dest[0] = (q->qtype & 0xff00) >> 8; dest[1] = (q->qtype & 0x00ff) >> 0; dest[2] = (q->qclass & 0xff00) >> 8; dest[3] = (q->qclass & 0x00ff) >> 0; return i + 4; } #endif #ifdef L_decodeq int decode_question(unsigned char *message, int offset, struct resolv_question *q) { char temp[256]; int i; i = decode_dotted(message, offset, temp, sizeof(temp)); if (i < 0) return i; offset += i; q->dotted = strdup(temp); q->qtype = (message[offset + 0] << 8) | message[offset + 1]; q->qclass = (message[offset + 2] << 8) | message[offset + 3]; return i + 4; } #endif #ifdef L_lengthq int length_question(unsigned char *message, int offset) { int i; i = length_dotted(message, offset); if (i < 0) return i; return i + 4; } #endif #ifdef L_encodea int encode_answer(struct resolv_answer *a, unsigned char *dest, int maxlen) { int i; i = encode_dotted(a->dotted, dest, maxlen); if (i < 0) return i; dest += i; maxlen -= i; if (maxlen < (RRFIXEDSZ+a->rdlength)) return -1; *dest++ = (a->atype & 0xff00) >> 8; *dest++ = (a->atype & 0x00ff) >> 0; *dest++ = (a->aclass & 0xff00) >> 8; *dest++ = (a->aclass & 0x00ff) >> 0; *dest++ = (a->ttl & 0xff000000) >> 24; *dest++ = (a->ttl & 0x00ff0000) >> 16; *dest++ = (a->ttl & 0x0000ff00) >> 8; *dest++ = (a->ttl & 0x000000ff) >> 0; *dest++ = (a->rdlength & 0xff00) >> 8; *dest++ = (a->rdlength & 0x00ff) >> 0; memcpy(dest, a->rdata, a->rdlength); return i + RRFIXEDSZ + a->rdlength; } #endif #ifdef L_decodea int decode_answer(unsigned char *message, int offset, struct resolv_answer *a) { char temp[256]; int i; i = decode_dotted(message, offset, temp, sizeof(temp)); if (i < 0) return i; message += offset + i; a->dotted = strdup(temp); a->atype = (message[0] << 8) | message[1]; message += 2; a->aclass = (message[0] << 8) | message[1]; message += 2; a->ttl = (message[0] << 24) | (message[1] << 16) | (message[2] << 8) | (message[3] << 0); message += 4; a->rdlength = (message[0] << 8) | message[1]; message += 2; a->rdata = message; a->rdoffset = offset + i + RRFIXEDSZ; DPRINTF("i=%d,rdlength=%d\n", i, a->rdlength); return i + RRFIXEDSZ + a->rdlength; } #endif #ifdef L_encodep int encode_packet(struct resolv_header *h, struct resolv_question **q, struct resolv_answer **an, struct resolv_answer **ns, struct resolv_answer **ar, unsigned char *dest, int maxlen) { int i, total = 0; int j; i = encode_header(h, dest, maxlen); if (i < 0) return i; dest += i; maxlen -= i; total += i; for (j = 0; j < h->qdcount; j++) { i = encode_question(q[j], dest, maxlen); if (i < 0) return i; dest += i; maxlen -= i; total += i; } for (j = 0; j < h->ancount; j++) { i = encode_answer(an[j], dest, maxlen); if (i < 0) return i; dest += i; maxlen -= i; total += i; } for (j = 0; j < h->nscount; j++) { i = encode_answer(ns[j], dest, maxlen); if (i < 0) return i; dest += i; maxlen -= i; total += i; } for (j = 0; j < h->arcount; j++) { i = encode_answer(ar[j], dest, maxlen); if (i < 0) return i; dest += i; maxlen -= i; total += i; } return total; } #endif #ifdef L_decodep int decode_packet(unsigned char *data, struct resolv_header *h) { return decode_header(data, h); } #endif #ifdef L_formquery int form_query(int id, const char *name, int type, unsigned char *packet, int maxlen) { struct resolv_header h; struct resolv_question q; int i, j; memset(&h, 0, sizeof(h)); h.id = id; h.qdcount = 1; q.dotted = (char *) name; q.qtype = type; q.qclass = C_IN; /* CLASS_IN */ i = encode_header(&h, packet, maxlen); if (i < 0) return i; j = encode_question(&q, packet + i, maxlen - i); if (j < 0) return j; return i + j; } #endif #ifdef L_dnslookup int dns_caught_signal = 0; void dns_catch_signal(int signo) { dns_caught_signal = 1; } int dns_lookup(const char *name, int type, int nscount, char **nsip, unsigned char **outpacket, struct resolv_answer *a) { static int id = 1; int i, j, len, fd, pos; static int ns = 0; struct sockaddr_in sa; #ifdef __UCLIBC_HAS_IPV6__ struct sockaddr_in6 sa6; #endif /* __UCLIBC_HAS_IPV6__ */ int oldalarm; __sighandler_t oldhandler; struct resolv_header h; struct resolv_question q; int retries = 0; unsigned char * packet = malloc(PACKETSZ); char * lookup = malloc(MAXDNAME); int variant = 0; #ifdef __UCLIBC_HAS_IPV6__ int v6; #endif /* __UCLIBC_HAS_IPV6__ */ fd = -1; if (!packet || !lookup || !nscount) goto fail; DPRINTF("Looking up type %d answer for '%s'\n", type, name); ns %= nscount; while (retries++ < MAX_RETRIES) { #ifdef __UCLIBC_HAS_IPV6__ v6 = (inet_pton(AF_INET6, nsip[ns], &sa6.sin6_addr) > 0); #endif /* __UCLIBC_HAS_IPV6__ */ if (fd != -1) close(fd); #ifndef __UCLIBC_HAS_IPV6__ fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); #else /* __UCLIBC_HAS_IPV6__ */ fd = socket(v6 ? AF_INET6 : AF_INET, SOCK_DGRAM, IPPROTO_UDP); #endif /* __UCLIBC_HAS_IPV6__ */ if (fd == -1) goto fail; memset(packet, 0, PACKETSZ); memset(&h, 0, sizeof(h)); h.id = ++id; h.qdcount = 1; h.rd = 1; DPRINTF("encoding header\n", h.rd); i = encode_header(&h, packet, PACKETSZ); if (i < 0) goto fail; strncpy(lookup,name,MAXDNAME); if (variant < searchdomains && strchr(lookup, '.') == NULL) { strncat(lookup,".", MAXDNAME); strncat(lookup,searchdomain[variant], MAXDNAME); } DPRINTF("lookup name: %s\n", lookup); q.dotted = (char *)lookup; q.qtype = type; q.qclass = C_IN; /* CLASS_IN */ j = encode_question(&q, packet+i, PACKETSZ-i); if (j < 0) goto fail; len = i + j; DPRINTF("On try %d, sending query to port %d of machine %s\n", retries, NAMESERVER_PORT, nsip[ns]); #ifndef __UCLIBC_HAS_IPV6__ sa.sin_family = AF_INET; sa.sin_port = htons(NAMESERVER_PORT); sa.sin_addr.s_addr = inet_addr(nsip[ns]); #else /* __UCLIBC_HAS_IPV6__ */ if (v6) { sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(NAMESERVER_PORT); /* sa6.sin6_addr is already here */ } else { sa.sin_family = AF_INET; sa.sin_port = htons(NAMESERVER_PORT); sa.sin_addr.s_addr = inet_addr(nsip[ns]); } #endif /* __UCLIBC_HAS_IPV6__ */ #ifndef __UCLIBC_HAS_IPV6__ if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { #else /* __UCLIBC_HAS_IPV6__ */ if (connect(fd, (struct sockaddr *) (v6 ? &sa6 : &sa), v6 ? sizeof(sa6) : sizeof(sa)) == -1) { #endif /* __UCLIBC_HAS_IPV6__ */ if (errno == ENETUNREACH) { /* routing error, presume not transient */ goto tryall; } else /* retry */ continue; } DPRINTF("Transmitting packet of length %d, id=%d, qr=%d\n", len, h.id, h.qr); send(fd, packet, len, 0); dns_caught_signal = 0; oldalarm = alarm(REPLY_TIMEOUT); oldhandler = signal(SIGALRM, dns_catch_signal); i = recv(fd, packet, PACKETSZ, 0); alarm(0); signal(SIGALRM, oldhandler); alarm(oldalarm); DPRINTF("Timeout=%d, len=%d\n", dns_caught_signal, i); if (dns_caught_signal) /* timed out, so retry send and receive, to next nameserver on queue */ goto again; if (i < HFIXEDSZ) /* too short ! */ goto again; decode_header(packet, &h); DPRINTF("id = %d, qr = %d\n", h.id, h.qr); if ((h.id != id) || (!h.qr)) /* unsolicited */ goto again; DPRINTF("Got response %s\n", "(i think)!"); DPRINTF("qrcount=%d,ancount=%d,nscount=%d,arcount=%d\n", h.qdcount, h.ancount, h.nscount, h.arcount); DPRINTF("opcode=%d,aa=%d,tc=%d,rd=%d,ra=%d,rcode=%d\n", h.opcode, h.aa, h.tc, h.rd, h.ra, h.rcode); if ((h.rcode) || (h.ancount < 1)) { /* negative result, not present */ goto again; } pos = HFIXEDSZ; for (j = 0; j < h.qdcount; j++) { DPRINTF("Skipping question %d at %d\n", j, pos); i = length_question(packet, pos); DPRINTF("Length of question %d is %d\n", j, i); if (i < 0) goto again; pos += i; } DPRINTF("Decoding answer at pos %d\n", pos); for (j=0;j<h.ancount;j++) { i = decode_answer(packet, pos, a); if (i<0) { DPRINTF("failed decode %d\n", i); goto again; } /* For all but T_SIG, accept first answer */ if (a->atype != T_SIG) break; DPRINTF("skipping T_SIG %d\n", i); free(a->dotted); pos += i; } DPRINTF("Answer name = |%s|\n", a->dotted); DPRINTF("Answer type = |%d|\n", a->atype); close(fd); if (outpacket) *outpacket = packet; else free(packet); free(lookup); return (0); /* success! */ tryall: /* if there are other nameservers, give them a go, otherwise return with error */ variant = 0; if (retries >= nscount*(searchdomains+1)) goto fail; again: /* if there are searchdomains, try them or fallback as passed */ if (variant < searchdomains) { /* next search */ variant++; } else { /* next server, first search */ ns = (ns + 1) % nscount; variant = 0; } } fail: if (fd != -1) close(fd); if (lookup) free(lookup); if (packet) free(packet); return -1; } #endif #ifdef L_resolveaddress int resolve_address(const char *address, int nscount, char **nsip, struct in_addr *in) { unsigned char *packet; struct resolv_answer a; char temp[256]; int i; int nest = 0; if (!address || !in) return -1; strncpy(temp, address, sizeof(temp)); for (;;) { i = dns_lookup(temp, T_A, nscount, nsip, &packet, &a); if (i < 0) return -1; free(a.dotted); if (a.atype == T_CNAME) { /* CNAME */ DPRINTF("Got a CNAME in resolve_address()\n"); i = decode_dotted(packet, a.rdoffset, temp, sizeof(temp)); free(packet); if (i < 0) return -1; if (++nest > MAX_RECURSE) return -1; continue; } else if (a.atype == T_A) { /* ADDRESS */ free(packet); break; } else { free(packet); return -1; } } if (in) memcpy(in, a.rdata, INADDRSZ); /* IPv4 T_A */ return 0; } #endif #ifdef L_resolvemailbox int resolve_mailbox(const char *address, int nscount, char **nsip, struct in_addr *in) { struct resolv_answer a; unsigned char *packet; char temp[256]; int nest = 0; int i; if (!address || !in) return -1; /* look up mail exchange */ i = dns_lookup(address, T_MX, nscount, nsip, &packet, &a); strncpy(temp, address, sizeof(temp)); if (i >= 0) { i = decode_dotted(packet, a.rdoffset+2, temp, sizeof(temp)); free(packet); } for (;;) { i = dns_lookup(temp, T_A, nscount, nsip, &packet, &a); if (i < 0) return -1; free(a.dotted); if (a.atype == T_CNAME) { /* CNAME */ DPRINTF("Got a CNAME in resolve_mailbox()\n"); i = decode_dotted(packet, a.rdoffset, temp, sizeof(temp)); free(packet); if (i < 0) return i; if (++nest > MAX_RECURSE) return -1; continue; } else if (a.atype == T_A) { /* ADDRESS */ free(packet); break; } else { free(packet); return -1; } } if (in) memcpy(in, a.rdata, INADDRSZ); /* IPv4 */ return 0; } #endif #ifdef L_opennameservers int nameservers; char * nameserver[MAX_SERVERS]; int searchdomains; char * searchdomain[MAX_SEARCH]; /* * we currently read formats not quite the same as that on normal * unix systems, we can have a list of nameservers after the keyword. */ int open_nameservers() { FILE *fp; int i; #define RESOLV_ARGS 5 char szBuffer[128], *p, *argv[RESOLV_ARGS]; int argc; if (nameservers > 0) return 0; if ((fp = fopen("/etc/resolv.conf", "r")) || (fp = fopen("/etc/config/resolv.conf", "r"))) { while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) { for (p = szBuffer; *p && isspace(*p); p++) /* skip white space */; if (*p == '\0' || *p == '\n' || *p == '#') /* skip comments etc */ continue; argc = 0; while (*p && argc < RESOLV_ARGS) { argv[argc++] = p; while (*p && !isspace(*p) && *p != '\n') p++; while (*p && (isspace(*p) || *p == '\n')) /* remove spaces */ *p++ = '\0'; } if (strcmp(argv[0], "nameserver") == 0) { for (i = 1; i < argc && nameservers < MAX_SERVERS; i++) { nameserver[nameservers++] = strdup(argv[i]); DPRINTF("adding nameserver %s\n", argv[i]); } } /* domain and search are mutually exclusive, the last one wins */ if (strcmp(argv[0],"domain")==0 || strcmp(argv[0],"search")==0) { while (searchdomains > 0) { free(searchdomain[--searchdomains]); searchdomain[searchdomains] = NULL; } for (i=1; i < argc && searchdomains < MAX_SEARCH; i++) { searchdomain[searchdomains++] = strdup(argv[i]); DPRINTF("adding search %s\n", argv[i]); } } } fclose(fp); } else { DPRINTF("failed to open %s\n", "resolv.conf"); } DPRINTF("nameservers = %d\n", nameservers); return 0; } #endif #ifdef L_closenameservers void close_nameservers(void) { while (nameservers > 0) { free(nameserver[--nameservers]); nameserver[nameservers] = NULL; } while (searchdomains > 0) { free(searchdomain[--searchdomains]); searchdomain[searchdomains] = NULL; } } #endif #ifdef L_resolvename const char *resolve_name(const char *name, int mailbox) { struct in_addr in; int i; /* shortcut: is it a valid IP address to begin with? */ if (inet_aton(name, &in)) return name; open_nameservers(); DPRINTF("looking up '%s', mailbox=%d, nameservers=%d\n", name, mailbox, nameservers); if (mailbox) i = resolve_mailbox(name, nameservers, nameserver, &in); else i = resolve_address(name, nameservers, nameserver, &in); if (i < 0) return 0; DPRINTF("success = '%s'\n", inet_ntoa(in)); return inet_ntoa(in); } #endif #ifdef L_gethostbyname struct hostent *gethostbyname(const char *name) { static struct hostent h; static char namebuf[256]; static struct in_addr in; static struct in_addr *addr_list[2]; struct hostent *hp; unsigned char *packet; struct resolv_answer a; int i; int nest = 0; open_nameservers(); if (!name) return 0; if ((hp = get_hosts_byname(name, AF_INET))) /* do /etc/hosts first */ return(hp); memset(&h, 0, sizeof(h)); addr_list[0] = ∈ addr_list[1] = 0; strncpy(namebuf, name, sizeof(namebuf)); /* First check if this is already an address */ if (inet_aton(name, &in)) { h.h_name = namebuf; h.h_addrtype = AF_INET; h.h_length = sizeof(in); h.h_addr_list = (char **) addr_list; return &h; } for (;;) { i = dns_lookup(namebuf, 1, nameservers, nameserver, &packet, &a); if (i < 0) return 0; strncpy(namebuf, a.dotted, sizeof(namebuf)); free(a.dotted); if (a.atype == T_CNAME) { /* CNAME */ DPRINTF("Got a CNAME in gethostbyname()\n"); i = decode_dotted(packet, a.rdoffset, namebuf, sizeof(namebuf)); free(packet); if (i < 0) return 0; if (++nest > MAX_RECURSE) return 0; continue; } else if (a.atype == T_A) { /* ADDRESS */ memcpy(&in, a.rdata, sizeof(in)); h.h_name = namebuf; h.h_addrtype = AF_INET; h.h_length = sizeof(in); h.h_addr_list = (char **) addr_list; free(packet); break; } else { free(packet); return 0; } } return &h; } #endif #ifdef L_gethostbyname2 #ifdef __UCLIBC_HAS_IPV6__ /* TBD: Not the right place for defining these, I guess */ const struct in6_addr in6addr_any = { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; const struct in6_addr in6addr_loopback = { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }; #endif /* __UCLIBC_HAS_IPV6__ */ struct hostent *gethostbyname2(const char *name, int family) { #ifndef __UCLIBC_HAS_IPV6__ return family == AF_INET ? gethostbyname(name) : (struct hostent*)0; #else /* __UCLIBC_HAS_IPV6__ */ static struct hostent h; static char namebuf[256]; static struct in6_addr in; static struct in6_addr *addr_list[2]; struct hostent *hp; unsigned char *packet; struct resolv_answer a; int i; int nest = 0; if (family == AF_INET) return gethostbyname(name); if (family != AF_INET6) return NULL; open_nameservers(); if (!name) return 0; if ((hp = get_hosts_byname(name, family))) /* do /etc/hosts first */ return(hp); memset(&h, 0, sizeof(h)); addr_list[0] = ∈ addr_list[1] = 0; strncpy(namebuf, name, sizeof(namebuf)); /* First check if this is already an address */ if (inet_pton(AF_INET6, name, &in)) { h.h_name = namebuf; h.h_addrtype = AF_INET6; h.h_length = sizeof(in); h.h_addr_list = (char **) addr_list; return &h; } for (;;) { i = dns_lookup(namebuf, T_AAAA, nameservers, nameserver, &packet, &a); if (i < 0) return 0; strncpy(namebuf, a.dotted, sizeof(namebuf)); free(a.dotted); if (a.atype == T_CNAME) { /* CNAME */ DPRINTF("Got a CNAME in gethostbyname()\n"); i = decode_dotted(packet, a.rdoffset, namebuf, sizeof(namebuf)); free(packet); if (i < 0) return 0; if (++nest > MAX_RECURSE) return 0; continue; } else if (a.atype == T_AAAA) { /* ADDRESS */ memcpy(&in, a.rdata, sizeof(in)); h.h_name = namebuf; h.h_addrtype = AF_INET6; h.h_length = sizeof(in); h.h_addr_list = (char **) addr_list; free(packet); break; } else { free(packet); return 0; } } return &h; #endif /* __UCLIBC_HAS_IPV6__ */ } #endif #ifdef L_getnetbyname struct netent * getnetbyname(const char * name) { return NULL; } #endif #ifdef L_res_init int res_init(void) { return(0); } #endif #ifdef L_res_query #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif int res_query(const char *dname, int class, int type, unsigned char *answer, int anslen) { unsigned char * packet = 0; struct resolv_answer a; int i; open_nameservers(); if (!dname || class != 1 /* CLASS_IN */) return(-1); memset((char *) &a, '\0', sizeof(a)); i = dns_lookup(dname, type, nameservers, nameserver, &packet, &a); if (i < 0) return(-1); free(a.dotted); if (a.atype == type) { /* CNAME*/ if (anslen && answer) memcpy(answer, a.rdata, MIN(anslen, a.rdlength)); if (packet) free(packet); return(MIN(anslen, a.rdlength)); } if (packet) free(packet); return 0; } #endif #ifdef L_gethostbyaddr struct hostent *gethostbyaddr (const void *addr, socklen_t len, int type) { static struct hostent h; static char namebuf[256]; static struct in_addr in; static struct in_addr *addr_list[2]; #ifdef __UCLIBC_HAS_IPV6__ char *qp; static struct in6_addr in6; static struct in6_addr *addr_list6[2]; #endif /* __UCLIBC_HAS_IPV6__ */ struct hostent *hp; unsigned char *packet; struct resolv_answer a; int i; int nest = 0; if (!addr) return 0; switch (type) { case AF_INET: if (len != sizeof(struct in_addr)) return 0; break; #ifdef __UCLIBC_HAS_IPV6__ case AF_INET6: if (len != sizeof(struct in6_addr)) return 0; break; #endif /* __UCLIBC_HAS_IPV6__ */ default: return 0; } if ((hp = get_hosts_byaddr(addr, len, type))) /* do /etc/hosts first */ return(hp); open_nameservers(); memset(&h, 0, sizeof(h)); if(type == AF_INET) { unsigned char *tmp_addr = (unsigned char *)addr; memcpy(&in.s_addr, addr, len); addr_list[0] = ∈ sprintf(namebuf, "%u.%u.%u.%u.in-addr.arpa", tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); #ifdef __UCLIBC_HAS_IPV6__ } else { memcpy(&in6.s6_addr, addr, len); addr_list6[0] = &in6; qp = namebuf; for (i = len - 1; i >= 0; i--) { qp += sprintf(qp, "%x.%x.", in6.s6_addr[i] & 0xf, (in6.s6_addr[i] >> 4) & 0xf); } strcpy(qp, "ip6.int"); #endif /* __UCLIBC_HAS_IPV6__ */ } addr_list[1] = 0; for (;;) { i = dns_lookup(namebuf, T_PTR, nameservers, nameserver, &packet, &a); if (i < 0) return 0; strncpy(namebuf, a.dotted, sizeof(namebuf)); free(a.dotted); if (a.atype == T_CNAME) { /* CNAME */ DPRINTF("Got a CNAME in gethostbyaddr()\n"); i = decode_dotted(packet, a.rdoffset, namebuf, sizeof(namebuf)); free(packet); if (i < 0) return 0; if (++nest > MAX_RECURSE) return 0; continue; } else if (a.atype == T_PTR) { /* ADDRESS */ i = decode_dotted(packet, a.rdoffset, namebuf, sizeof(namebuf)); free(packet); h.h_name = namebuf; h.h_addrtype = type; if(type == AF_INET) { h.h_length = sizeof(in); #ifdef __UCLIBC_HAS_IPV6__ } else { h.h_length = sizeof(in6); #endif /* __UCLIBC_HAS_IPV6__ */ } h.h_addr_list = (char **) addr_list; break; } else { free(packet); return 0; } } return &h; } #endif #ifdef L_read_etc_hosts void __open_etc_hosts(FILE **fp) { if ((*fp = fopen("/etc/hosts", "r")) == NULL) { *fp = fopen("/etc/config/hosts", "r"); } return; } struct hostent * read_etc_hosts(FILE * fp, const char * name, int type, enum etc_hosts_action action) { static struct hostent h; static struct in_addr in; static struct in_addr *addr_list[2]; #ifdef __UCLIBC_HAS_IPV6__ static struct in6_addr in6; static struct in6_addr *addr_list6[2]; #endif /* __UCLIBC_HAS_IPV6__ */ static char line[80]; char *cp; #define MAX_ALIAS 5 char *alias[MAX_ALIAS]; int aliases, i; if (action!=GETHOSTENT) { __open_etc_hosts(&fp); if (fp == NULL) { return((struct hostent *)NULL); } } while (fgets(line, sizeof(line), fp)) { if ((cp = strchr(line, '#'))) *cp = '\0'; aliases = 0; cp = line; while (*cp) { while (*cp && isspace(*cp)) *cp++ = '\0'; if (!*cp) continue; if (aliases < MAX_ALIAS) alias[aliases++] = cp; while (*cp && !isspace(*cp)) cp++; } if (aliases < 2) continue; /* syntax error really */ if (action==GETHOSTENT) { /* Return whatever the next entry happens to be. */ break; } else if (action==GET_HOSTS_BYADDR) { if (strcmp(name, alias[0]) != 0) continue; } else { /* GET_HOSTS_BYNAME */ for (i = 1; i < aliases; i++) if (strcasecmp(name, alias[i]) == 0) break; if (i >= aliases) continue; } if (type == AF_INET && inet_pton(AF_INET, alias[0], &in) > 0) { addr_list[0] = ∈ addr_list[1] = 0; h.h_name = alias[1]; h.h_addrtype = AF_INET; h.h_length = sizeof(in); h.h_addr_list = (char**) addr_list; #ifdef __UCLIBC_HAS_IPV6__ } else if (type == AF_INET6 && inet_pton(AF_INET6, alias[0], &in6) > 0) { addr_list6[0] = &in6; addr_list6[1] = 0; h.h_name = alias[1]; h.h_addrtype = AF_INET6; h.h_length = sizeof(in6); h.h_addr_list = (char**) addr_list6; #endif /* __UCLIBC_HAS_IPV6__ */ } else { break; /* bad ip address */ } if (action!=GETHOSTENT) { fclose(fp); } return(&h); } if (action!=GETHOSTENT) { fclose(fp); } return((struct hostent *) NULL); } #endif #ifdef L_endhostent extern int __stay_open; extern FILE * __gethostent_fp; void endhostent (void) { __stay_open = 0; if (__gethostent_fp) { fclose(__gethostent_fp); } } #endif #ifdef L_sethostent extern int __stay_open; void sethostent (int stay_open) { __stay_open = stay_open; } #endif #ifdef L_gethostent int __stay_open; FILE * __gethostent_fp; struct hostent *gethostent (void) { struct hostent *host; if (__gethostent_fp == NULL) { __open_etc_hosts(&__gethostent_fp); if (__gethostent_fp == NULL) { return((struct hostent *)NULL); } } host = read_etc_hosts(__gethostent_fp, NULL, AF_INET, GETHOSTENT); if (__stay_open==0) { fclose(__gethostent_fp); } return(host); } #endif #ifdef L_get_hosts_byname struct hostent * get_hosts_byname(const char * name, int type) { return(read_etc_hosts(NULL, name, type, GET_HOSTS_BYNAME)); } #endif #ifdef L_get_hosts_byaddr struct hostent * get_hosts_byaddr(const char * addr, int len, int type) { #ifndef __UCLIBC_HAS_IPV6__ char ipaddr[INET_ADDRSTRLEN]; #else char ipaddr[INET6_ADDRSTRLEN]; #endif /* __UCLIBC_HAS_IPV6__ */ switch (type) { case AF_INET: if (len != sizeof(struct in_addr)) return 0; break; #ifdef __UCLIBC_HAS_IPV6__ case AF_INET6: if (len != sizeof(struct in6_addr)) return 0; break; #endif /* __UCLIBC_HAS_IPV6__ */ default: return 0; } inet_ntop(type, addr, ipaddr, sizeof(ipaddr)); return(read_etc_hosts(NULL, ipaddr, type, GET_HOSTS_BYADDR)); } #endif #ifdef L_getnameinfo #ifndef min # define min(x,y) (((x) > (y)) ? (y) : (x)) #endif /* min */ int getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, unsigned int flags) { int serrno = errno; int ok = 0; struct hostent *h = NULL; char domain[256]; if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM)) return EAI_BADFLAGS; if (sa == NULL || addrlen < sizeof (sa_family_t)) return EAI_FAMILY; switch (sa->sa_family) { case AF_LOCAL: break; case AF_INET: if (addrlen < sizeof (struct sockaddr_in)) return EAI_FAMILY; break; #ifdef __UCLIBC_HAS_IPV6__ case AF_INET6: if (addrlen < sizeof (struct sockaddr_in6)) return EAI_FAMILY; break; #endif /* __UCLIBC_HAS_IPV6__ */ default: return EAI_FAMILY; } if (host != NULL && hostlen > 0) switch (sa->sa_family) { case AF_INET: #ifdef __UCLIBC_HAS_IPV6__ case AF_INET6: #endif /* __UCLIBC_HAS_IPV6__ */ if (!(flags & NI_NUMERICHOST)) { #ifdef __UCLIBC_HAS_IPV6__ if (sa->sa_family == AF_INET6) h = gethostbyaddr ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr), sizeof(struct in6_addr), AF_INET6); else #endif /* __UCLIBC_HAS_IPV6__ */ h = gethostbyaddr ((const void *) &(((const struct sockaddr_in *)sa)->sin_addr), sizeof(struct in_addr), AF_INET); if (h) { char *c; if ((flags & NI_NOFQDN) && (getdomainname (domain, sizeof(domain)) == 0) && (c = strstr (h->h_name, domain)) && (c != h->h_name) && (*(--c) == '.')) { strncpy (host, h->h_name, min(hostlen, (size_t) (c - h->h_name))); host[min(hostlen - 1, (size_t) (c - h->h_name))] = '\0'; ok = 1; } else { strncpy (host, h->h_name, hostlen); ok = 1; } } } if (!ok) { if (flags & NI_NAMEREQD) { errno = serrno; return EAI_NONAME; } else { const char *c; #ifdef __UCLIBC_HAS_IPV6__ if (sa->sa_family == AF_INET6) { const struct sockaddr_in6 *sin6p; sin6p = (const struct sockaddr_in6 *) sa; c = inet_ntop (AF_INET6, (const void *) &sin6p->sin6_addr, host, hostlen); #if 0 /* Does scope id need to be supported? */ uint32_t scopeid; scopeid = sin6p->sin6_scope_id; if (scopeid != 0) { /* Buffer is >= IFNAMSIZ+1. */ char scopebuf[IFNAMSIZ + 1]; char *scopeptr; int ni_numericscope = 0; size_t real_hostlen = __strnlen (host, hostlen); size_t scopelen = 0; scopebuf[0] = SCOPE_DELIMITER; scopebuf[1] = '\0'; scopeptr = &scopebuf[1]; if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr)) { if (if_indextoname (scopeid, scopeptr) == NULL) ++ni_numericscope; else scopelen = strlen (scopebuf); } else { ++ni_numericscope; } if (ni_numericscope) scopelen = 1 + snprintf (scopeptr, (scopebuf + sizeof scopebuf - scopeptr), "%u", scopeid); if (real_hostlen + scopelen + 1 > hostlen) return EAI_SYSTEM; memcpy (host + real_hostlen, scopebuf, scopelen + 1); } #endif } else #endif /* __UCLIBC_HAS_IPV6__ */ c = inet_ntop (AF_INET, (const void *) &(((const struct sockaddr_in *) sa)->sin_addr), host, hostlen); if (c == NULL) { errno = serrno; return EAI_SYSTEM; } } ok = 1; } break; case AF_LOCAL: if (!(flags & NI_NUMERICHOST)) { struct utsname utsname; if (!uname (&utsname)) { strncpy (host, utsname.nodename, hostlen); break; }; }; if (flags & NI_NAMEREQD) { errno = serrno; return EAI_NONAME; } strncpy (host, "localhost", hostlen); break; default: return EAI_FAMILY; } if (serv && (servlen > 0)) { switch (sa->sa_family) { case AF_INET: #ifdef __UCLIBC_HAS_IPV6__ case AF_INET6: #endif /* __UCLIBC_HAS_IPV6__ */ if (!(flags & NI_NUMERICSERV)) { struct servent *s; s = getservbyport (((const struct sockaddr_in *) sa)->sin_port, ((flags & NI_DGRAM) ? "udp" : "tcp")); if (s) { strncpy (serv, s->s_name, servlen); break; } } snprintf (serv, servlen, "%d", ntohs (((const struct sockaddr_in *) sa)->sin_port)); break; case AF_LOCAL: strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen); break; } } if (host && (hostlen > 0)) host[hostlen-1] = 0; if (serv && (servlen > 0)) serv[servlen-1] = 0; errno = serrno; return 0; } #endif