rebase dns.c against upstream
authorMike Blumenkrantz <michael.blumenkrantz@gmail.com>
Tue, 22 Jan 2013 08:01:13 +0000 (08:01 +0000)
committerMike Blumenkrantz <michael.blumenkrantz@gmail.com>
Tue, 22 Jan 2013 08:01:13 +0000 (08:01 +0000)
SVN revision: 83058

ChangeLog
NEWS
src/lib/ecore_con/dns.c
src/lib/ecore_con/dns.h

index 55704c3..10da6bb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,8 @@
-2013-01-07  Sung W. Park (sung_)
+2013-01-22  Mike Blumenkrantz
+
+        * rebase dns.c against upstream
+
+2013-01-22  Sung W. Park (sung_)
 
        * Applied on-demand memory allocation policy for Evas GL Surface
         for direct rendering optimization.  For direct rendering in EvasGL,
diff --git a/NEWS b/NEWS
index aae56af..e81a27e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -80,6 +80,7 @@ Improvements:
     * Upgrade liblinebreak to latest version of libunibreak.
     * edje entry: cursor position and location will be passed when it's really changed in case of mouse down, move, and up event.
     * edje entry: remove ecore_imf_context_reset in mouse up event because it's useless.
+    * rebase dns.c against upstream
 
 Fixes:
     * Fix PPC (big endian) image codec bug.
index fad42e9..15d9bb0 100644 (file)
  * USE OR OTHER DEALINGS IN THE SOFTWARE.
  * ==========================================================================
  */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#if !defined(__FreeBSD__)
+#if !defined(__FreeBSD__) && !defined(__sun)
 #ifndef _XOPEN_SOURCE
 #define _XOPEN_SOURCE  600
 #endif
 #endif
 
 #include <stddef.h>            /* offsetof() */
+#ifdef _WIN32
+#define uint32_t unsigned int
+#else
 #include <stdint.h>            /* uint32_t */
+#endif
 #include <stdlib.h>            /* malloc(3) realloc(3) free(3) rand(3) random(3) arc4random(3) */
 #include <stdio.h>             /* FILE fopen(3) fclose(3) getc(3) rewind(3) */
 
@@ -52,9 +52,9 @@
 
 #include <ctype.h>             /* isspace(3) isdigit(3) */
 
-#include <time.h>              /* time_t time(2) */
+#include <time.h>              /* time_t time(2) difftime(3) */
 
-#include <signal.h>            /* sig_atomic_t */
+#include <signal.h>            /* SIGPIPE sigemptyset(3) sigaddset(3) sigpending(2) sigprocmask(2) pthread_sigmask(3) sigtimedwait(2) */
 
 #include <errno.h>             /* errno EINVAL ENOENT */
 
@@ -62,6 +62,9 @@
 #include <assert.h>            /* assert(3) */
 
 #if _WIN32
+#ifndef FD_SETSIZE
+#define FD_SETSIZE 256
+#endif
 #include <winsock2.h>
 #include <ws2tcpip.h>
 #else
@@ -75,7 +78,7 @@
 
 #include <fcntl.h>             /* F_SETFD F_GETFL F_SETFL O_NONBLOCK fcntl(2) */
 
-#include <unistd.h>            /* gethostname(3) close(2) */
+#include <unistd.h>            /* _POSIX_THREADS gethostname(3) close(2) */
 
 #include <poll.h>              /* POLLIN POLLOUT */
 
 
 
 /*
+ * C O M P I L E R  A N N O T A T I O N S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if __GNUC__
+#define DNS_NOTUSED __attribute__((unused))
+#else
+#define DNS_NOTUSED
+#endif
+
+#if __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+
+/*
  * S T A N D A R D  M A C R O S
  *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 
 /*
+ * M I S C E L L A N E O U S  C O M P A T
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if _WIN32 || _WIN64
+#define PRIuZ "Iu"
+#else
+#define PRIuZ "zu"
+#endif
+
+#ifndef DNS_THREAD_SAFE
+#if (defined _REENTRANT || defined _THREAD_SAFE) && _POSIX_THREADS > 0
+#define DNS_THREAD_SAFE 1
+#else
+#define DNS_THREAD_SAFE 0
+#endif
+#endif
+
+
+/*
  * D E B U G  M A C R O S
  *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -224,6 +267,8 @@ const char *dns_strerror(int error) {
                return "Invalid section specified";
        case DNS_EUNKNOWN:
                return "Unknown DNS error";
+       case DNS_EADDRESS:
+               return "Invalid textual address form";
        default:
                return strerror(error);
        } /* switch() */
@@ -235,6 +280,11 @@ const char *dns_strerror(int error) {
  *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
+DNS_NOTUSED static void dns_atomic_fence(void) {
+       return;
+} /* dns_atomic_fence() */
+
+
 static unsigned dns_atomic_inc(dns_atomic_t *i) {
        return (*i)++;
 } /* dns_atomic_inc() */
@@ -245,6 +295,20 @@ static unsigned dns_atomic_dec(dns_atomic_t *i) {
 } /* dns_atomic_dec() */
 
 
+static unsigned dns_atomic_load(dns_atomic_t *i) {
+       return *i;
+} /* dns_atomic_load() */
+
+
+DNS_NOTUSED static unsigned dns_atomic_store(dns_atomic_t *i, unsigned n) {
+       unsigned o;
+
+       o       = dns_atomic_load(i);
+       *i      = n;
+       return o;
+} /* dns_atomic_store() */
+
+
 /*
  * C R Y P T O  R O U T I N E S
  *
@@ -429,6 +493,26 @@ static unsigned dns_k_permutor_E(struct dns_k_permutor *p, unsigned n) {
        return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0);
 } /* dns_k_permutor_E() */
 
+
+DNS_NOTUSED static unsigned dns_k_permutor_D(struct dns_k_permutor *p, unsigned n) {
+       unsigned l[2], r[2];
+       unsigned i;
+
+       i               = p->rounds - 1;
+       l[i % 2]        = p->mask & (n >> p->shift);
+       r[i % 2]        = p->mask & (n >> 0);
+
+       do {
+               i--;
+
+               r[i % 2]        = l[(i + 1) % 2];
+               l[i % 2]        = r[(i + 1) % 2] ^ dns_k_permutor_F(p, i, l[(i + 1) % 2]);
+       } while (i > 0);
+
+       return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0);
+} /* dns_k_permutor_D() */
+
+
 static unsigned dns_k_permutor_step(struct dns_k_permutor *p) {
        unsigned n;
 
@@ -508,36 +592,30 @@ static unsigned short dns_k_shuffle16(unsigned short n, unsigned s) {
  *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
-/*
- * Monotonic Time
- *
- */
-static time_t dns_now(void) {
-       /* XXX: Assumes sizeof (time_t) <= sizeof (sig_atomic_t) */
-       static volatile sig_atomic_t last, tick;
-       volatile sig_atomic_t tmp_last, tmp_tick;
-       time_t now;
+#define DNS_MAXINTERVAL 300
 
-       time(&now);
+struct dns_clock {
+       time_t sample, elapsed;
+}; /* struct dns_clock */
 
-       tmp_last        = last;
+static void dns_begin(struct dns_clock *clk) {
+       clk->sample = time(0);
+       clk->elapsed = 0;
+} /* dns_begin() */
 
-       if (now > tmp_last) {
-               tmp_tick        = tick;
-               tmp_tick        += now - tmp_last;
-               tick            = tmp_tick;
-       }
+static time_t dns_elapsed(struct dns_clock *clk) {
+       time_t curtime;
 
-       last    = now;
+       if ((time_t)-1 == time(&curtime))
+               return clk->elapsed;
 
-       return tick;
-} /* dns_now() */
+       if (curtime > clk->sample)
+               clk->elapsed += (time_t)MIN(difftime(curtime, clk->sample), DNS_MAXINTERVAL);
 
-static time_t dns_elapsed(time_t from) {
-       time_t now      = dns_now();
+       clk->sample = curtime;
 
-       return (now > from)? now - from : 0;
-} /* dns_elpased() */
+       return clk->elapsed;
+} /* dns_elapsed() */
 
 
 static size_t dns_af_len(int af) {
@@ -552,6 +630,8 @@ static size_t dns_af_len(int af) {
        return table[af];
 } /* dns_af_len() */
 
+#define dns_sa_family(sa)      (((struct sockaddr *)(sa))->sa_family)
+
 #define dns_sa_len(sa)         dns_af_len(dns_sa_family(sa))
 
 
@@ -605,7 +685,7 @@ static int dns_inet_pton(int af, const void *src, void *dst) {
        }
 } /* dns_inet_pton() */
 
-const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) {
+static const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) {
        union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u;
 
        /* NOTE: WSAAddressToString will print .sin_port unless zeroed. */
@@ -630,9 +710,29 @@ const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim)
 
        return dst;
 } /* dns_inet_ntop() */
+#else
+#define dns_inet_pton(...)     inet_pton(__VA_ARGS__)
+#define dns_inet_ntop(...)     inet_ntop(__VA_ARGS__)
 #endif
 
 
+static dns_error_t dns_pton(int af, const void *src, void *dst) {
+       switch (dns_inet_pton(af, src, dst)) {
+       case 1:
+               return 0;
+       case -1:
+               return dns_soerr();
+       default:
+               return DNS_EADDRESS;
+       }
+} /* dns_pton() */
+
+
+static dns_error_t dns_ntop(int af, const void *src, void *dst, unsigned long lim) {
+       return (dns_inet_ntop(af, src, dst, lim))? 0 : dns_soerr();
+} /* dns_ntop() */
+
+
 size_t dns_strlcpy(char *dst, const char *src, size_t lim) {
        char *d         = dst;
        char *e         = &dst[lim];
@@ -678,7 +778,7 @@ size_t dns_strlcat(char *dst, const char *src, size_t lim) {
 } /* dns_strlcat() */
 
 
-#if defined(_WIN32) || defined(__SUNPRO_C)
+#if _WIN32
 
 static char *dns_strsep(char **sp, const char *delim) {
        char *p;
@@ -714,7 +814,7 @@ static int dns_poll(int fd, short events, int timeout) {
        if (!events)
                return 0;
 
-       assert(fd >= 0 && fd < FD_SETSIZE);
+       assert(fd >= 0 && (unsigned)fd < FD_SETSIZE);
 
        FD_ZERO(&rset);
        FD_ZERO(&wset);
@@ -731,6 +831,71 @@ static int dns_poll(int fd, short events, int timeout) {
 } /* dns_poll() */
 
 
+#if !_WIN32
+DNS_NOTUSED static int dns_sigmask(int how, const sigset_t *set, sigset_t *oset) {
+#if DNS_THREAD_SAFE
+       return pthread_sigmask(how, set, oset);
+#else
+       return (0 == sigprocmask(how, set, oset))? 0 : errno;
+#endif
+} /* dns_sigmask() */
+#endif
+
+
+static long dns_send(int fd, const void *src, size_t lim, int flags) {
+#if _WIN32 || !defined SIGPIPE || defined SO_NOSIGPIPE
+       return send(fd, src, lim, flags);
+#elif defined MSG_NOSIGNAL
+       return send(fd, src, lim, flags|MSG_NOSIGNAL);
+#elif _POSIX_REALTIME_SIGNALS > 0 /* require sigtimedwait */
+       /*
+        * SIGPIPE handling similar to the approach described in
+        * http://krokisplace.blogspot.com/2010/02/suppressing-sigpipe-in-library.html
+        */
+       sigset_t pending, blocked, piped;
+       long count;
+       int saved, error;
+
+       sigemptyset(&pending);
+       sigpending(&pending);
+
+       if (!sigismember(&pending, SIGPIPE)) {
+               sigemptyset(&piped);
+               sigaddset(&piped, SIGPIPE);
+               sigemptyset(&blocked);
+
+               if ((error = dns_sigmask(SIG_BLOCK, &piped, &blocked)))
+                       goto error;
+       }
+
+       count = send(fd, src, lim, flags);
+
+       if (!sigismember(&pending, SIGPIPE)) {
+               saved = errno;
+
+               if (count == -1 && errno == EPIPE) {
+                       while (-1 == sigtimedwait(&piped, NULL, &(struct timespec){ 0, 0 }) && errno == EINTR)
+                               ;;
+               }
+
+               if ((error = dns_sigmask(SIG_SETMASK, &blocked, NULL)))
+                       goto error;
+
+               errno = saved;
+       }
+
+       return count;
+error:
+       errno = error;
+
+       return -1;
+#else
+#error "unable to suppress SIGPIPE"
+       return send(fd, src, lim, flags);
+#endif
+} /* dns_send() */
+
+
 /*
  * P A C K E T  R O U T I N E S
  *
@@ -949,7 +1114,6 @@ void dns_p_dictadd(struct dns_packet *P, unsigned short dn) {
 int dns_p_push(struct dns_packet *P, enum dns_section section, const void *dn, size_t dnlen, enum dns_type type, enum dns_class class, unsigned ttl, const void *any) {
        size_t end = P->end;
        int error;
-       unsigned short count;
 
        if ((error = dns_d_push(P, dn, dnlen)))
                goto error;
@@ -986,8 +1150,7 @@ update:
                if (!P->qd.base && (error = dns_p_study(P)))
                        goto error;
 
-               count = ntohs(dns_header(P)->qdcount) + 1;
-               dns_header(P)->qdcount = htons(count);
+               dns_header(P)->qdcount = htons(ntohs(dns_header(P)->qdcount) + 1);
 
                P->qd.end  = P->end;
                P->an.base = P->end;
@@ -1005,8 +1168,7 @@ update:
                if (!P->an.base && (error = dns_p_study(P)))
                        goto error;
 
-               count = ntohs(dns_header(P)->ancount) + 1;
-               dns_header(P)->ancount = htons(count);
+               dns_header(P)->ancount = htons(ntohs(dns_header(P)->ancount) + 1);
 
                P->an.end  = P->end;
                P->ns.base = P->end;
@@ -1022,8 +1184,7 @@ update:
                if (!P->ns.base && (error = dns_p_study(P)))
                        goto error;
 
-               count = ntohs(dns_header(P)->nscount) + 1;
-               dns_header(P)->nscount = htons(count);
+               dns_header(P)->nscount = htons(ntohs(dns_header(P)->nscount) + 1);
 
                P->ns.end  = P->end;
                P->ar.base = P->end;
@@ -1034,8 +1195,7 @@ update:
                if (!P->ar.base && (error = dns_p_study(P)))
                        goto error;
 
-               count = ntohs(dns_header(P)->arcount) + 1;
-               dns_header(P)->arcount = htons(count);
+               dns_header(P)->arcount = htons(ntohs(dns_header(P)->arcount) + 1);
 
                P->ar.end = P->end;
 
@@ -1327,7 +1487,7 @@ size_t dns_d_cleave(void *dst, size_t lim, const void *src, size_t len) {
 } /* dns_d_cleave() */
 
 
-size_t dns_d_comp(void *dst_, size_t lim, const void *src_, size_t len, struct dns_packet *P, int *error EINA_UNUSED) {
+size_t dns_d_comp(void *dst_, size_t lim, const void *src_, size_t len, struct dns_packet *P, int *error) {
        struct { unsigned char *b; size_t p, x; } dst, src;
        unsigned char ch        = '.';
 
@@ -1702,7 +1862,7 @@ unsigned short dns_rr_skip(unsigned short src, struct dns_packet *P) {
 
 static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P) {
        enum dns_section section;
-       unsigned count, ind;
+       unsigned count, index;
        unsigned short rp;
 
        if (src >= P->qd.base && src < P->qd.end)
@@ -1716,13 +1876,13 @@ static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P)
 
        /* NOTE: Possibly bad memoization. Try it the hard-way. */
 
-       for (rp = 12, ind = 0; rp < src && rp < P->end; ind++)
+       for (rp = 12, index = 0; rp < src && rp < P->end; index++)
                rp = dns_rr_skip(rp, P);
 
        section = DNS_S_QD;
        count   = dns_p_count(P, section);
 
-       while (ind >= count && section <= DNS_S_AR) {
+       while (index >= count && section <= DNS_S_AR) {
                section <<= 1;
                count += dns_p_count(P, section);
        }
@@ -1857,7 +2017,7 @@ static unsigned short dns_rr_i_start(struct dns_rr_i *i, struct dns_packet *P) {
        else
                rp = 12;
 
-       for (rp = 12; rp < P->end; rp = dns_rr_skip(rp, P)) {
+       for (; rp < P->end; rp = dns_rr_skip(rp, P)) {
                if ((error = dns_rr_parse(&rr, rp, P)))
                        continue;
 
@@ -1948,12 +2108,12 @@ lower:
 } /* dns_rr_i_skip() */
 
 
-int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i EINA_UNUSED, struct dns_packet *P EINA_UNUSED) {
+int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) {
        return (int)a->dn.p - (int)b->dn.p;
 } /* dns_rr_i_packet() */
 
 
-int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i EINA_UNUSED, struct dns_packet *P) {
+int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) {
        int cmp;
 
        if ((cmp = a->section - b->section))
@@ -1966,7 +2126,7 @@ int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i EINA_U
 } /* dns_rr_i_order() */
 
 
-int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P EINA_UNUSED) {
+int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) {
        int cmp;
 
        while (!i->state.regs[0])
@@ -1979,7 +2139,7 @@ int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, str
 } /* dns_rr_i_shuffle() */
 
 
-struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P EINA_UNUSED) {
+struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P) {
        static const struct dns_rr_i i_initializer;
 
        i->state        = i_initializer.state;
@@ -2731,6 +2891,70 @@ size_t dns_srv_cname(void *dst, size_t lim, struct dns_srv *srv) {
 } /* dns_srv_cname() */
 
 
+unsigned int dns_opt_ttl(const struct dns_opt *opt) {
+       unsigned int ttl = 0;
+
+       ttl |= (0xffU & opt->rcode) << 24U;
+       ttl |= (0xffU & opt->version) << 16U;
+
+       return ttl;
+} /* dns_opt_ttl() */
+
+
+unsigned short dns_opt_class(const struct dns_opt *opt) {
+       return opt->maxsize;
+} /* dns_opt_class() */
+
+
+struct dns_opt *dns_opt_init(struct dns_opt *opt, size_t size) {
+       assert(size >= offsetof(struct dns_opt, data));
+
+       opt->size = size - offsetof(struct dns_opt, data);
+       opt->len  = 0;
+
+       opt->rcode   = 0;
+       opt->version = 0;
+       opt->maxsize = 512;
+
+       return opt;
+} /* dns_opt_init() */
+
+
+int dns_opt_parse(struct dns_opt *opt, struct dns_rr *rr, struct dns_packet *P) {
+       opt->len = 0;
+
+       return 0;
+} /* dns_opt_parse() */
+
+
+int dns_opt_push(struct dns_packet *P, struct dns_opt *opt) {
+       return 0;
+} /* dns_opt_push() */
+
+
+int dns_opt_cmp(const struct dns_opt *a, const struct dns_opt *b) {
+       return 0;
+} /* dns_opt_cmp() */
+
+
+size_t dns_opt_print(void *dst, size_t lim, struct dns_opt *opt) {
+       size_t p = 0, src;
+
+       p += dns__printchar(dst, lim, p, '"');
+
+       for (src = 0; src < opt->len; src++) {
+               p += dns__printchar(dst, lim, p, '\\');
+               p += dns__print10(dst, lim, p, opt->data[src], 3);
+       }
+
+       p += dns__printchar(dst, lim, p, '"');
+
+       dns__printnul(dst, lim, p);
+
+       return p;
+} /* dns_opt_print() */
+
+
 int dns_ptr_parse(struct dns_ptr *ptr, struct dns_rr *rr, struct dns_packet *P) {
        return dns_ns_parse((struct dns_ns *)ptr, rr, P);
 } /* dns_ptr_parse() */
@@ -2827,7 +3051,7 @@ int dns_sshfp_push(struct dns_packet *P, struct dns_sshfp *fp) {
 int dns_sshfp_cmp(const struct dns_sshfp *a, const struct dns_sshfp *b) {
        int cmp;
 
-       if ((cmp = a->algo - b->algo) || (cmp - a->type - b->type))
+       if ((cmp = a->algo - b->algo) || (cmp = a->type - b->type))
                return cmp;
 
        switch (a->type) {
@@ -2953,7 +3177,7 @@ int dns_txt_push(struct dns_packet *P, struct dns_txt *txt) {
 } /* dns_txt_push() */
 
 
-int dns_txt_cmp(const struct dns_txt *a EINA_UNUSED, const struct dns_txt *b EINA_UNUSED) {
+int dns_txt_cmp(const struct dns_txt *a, const struct dns_txt *b) {
        return -1;
 } /* dns_txt_cmp() */
 
@@ -3013,6 +3237,7 @@ static const struct {
        { DNS_T_CNAME,  "CNAME",  &dns_cname_parse,  &dns_cname_push,  &dns_cname_cmp,  &dns_cname_print,  &dns_cname_cname },
        { DNS_T_SOA,    "SOA",    &dns_soa_parse,    &dns_soa_push,    &dns_soa_cmp,    &dns_soa_print,    0                },
        { DNS_T_SRV,    "SRV",    &dns_srv_parse,    &dns_srv_push,    &dns_srv_cmp,    &dns_srv_print,    &dns_srv_cname   },
+       { DNS_T_OPT,    "OPT",    &dns_opt_parse,    &dns_opt_push,    &dns_opt_cmp,    &dns_opt_print,    0                },
        { DNS_T_PTR,    "PTR",    &dns_ptr_parse,    &dns_ptr_push,    &dns_ptr_cmp,    &dns_ptr_print,    &dns_ptr_cname   },
        { DNS_T_TXT,    "TXT",    &dns_txt_parse,    &dns_txt_push,    &dns_txt_cmp,    &dns_txt_print,    0                },
        { DNS_T_SPF,    "SPF",    &dns_txt_parse,    &dns_txt_push,    &dns_txt_cmp,    &dns_txt_print,    0                },
@@ -3529,6 +3754,11 @@ struct dns_resolv_conf *dns_resconf_local(int *error_) {
        if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf")))
                goto error;
 
+       if ((error = dns_nssconf_loadpath(resconf, "/etc/nsswitch.conf"))) {
+               if (error != ENOENT)
+                       goto error;
+       }
+
        return resconf;
 error:
        *error_ = error;
@@ -3539,25 +3769,13 @@ error:
 } /* dns_resconf_local() */
 
 
-struct dns_resolv_conf *dns_resconf_root(int *error_) {
+struct dns_resolv_conf *dns_resconf_root(int *error) {
        struct dns_resolv_conf *resconf;
-       int error;
-
-       if (!(resconf = dns_resconf_open(&error)))
-               goto error;
-
-       if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf")))
-               goto error;
 
-       resconf->options.recurse        = 1;
+       if ((resconf = dns_resconf_local(error)))
+               resconf->options.recurse = 1;
 
        return resconf;
-error:
-       *error_ = error;
-
-       dns_resconf_close(resconf);
-
-       return 0;
 } /* dns_resconf_root() */
 
 
@@ -3636,7 +3854,7 @@ static enum dns_resconf_keyword dns_resconf_keyword(const char *word) {
 static int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) {
        struct { char buf[128], *p; } addr = { "", addr.buf };
        unsigned short port = 0;
-       int ch, af = AF_INET;
+       int ch, af = AF_INET, error;
 
        while ((ch = *src++)) {
                switch (ch) {
@@ -3668,12 +3886,8 @@ static int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) {
        } /* while() */
 inet:
 
-       switch (dns_inet_pton(af, addr.buf, dns_sa_addr(af, ss))) {
-       case -1:
-               return errno;
-       case 0:
-               return EINVAL;
-       } /* switch() */
+       if ((error = dns_pton(af, addr.buf, dns_sa_addr(af, ss))))
+               return error;
 
        port = (!port)? 53 : port;
        *dns_sa_port(af, ss) = htons(port);
@@ -3875,11 +4089,518 @@ int dns_resconf_loadpath(struct dns_resolv_conf *resconf, const char *path) {
 } /* dns_resconf_loadpath() */
 
 
+struct dns_anyconf {
+       char *token[16];
+       unsigned count;
+       char buffer[1024], *tp, *cp;
+}; /* struct dns_anyconf */
+
+
+static void dns_anyconf_reset(struct dns_anyconf *cf) {
+       cf->count = 0;
+       cf->tp = cf->cp = cf->buffer;
+} /* dns_anyconf_reset() */
+
+
+static int dns_anyconf_push(struct dns_anyconf *cf) {
+       if (!(cf->cp < endof(cf->buffer) && cf->count < lengthof(cf->token)))
+               return ENOMEM;
+
+       *cf->cp++ = '\0';
+       cf->token[cf->count++] = cf->tp;
+       cf->tp = cf->cp;
+
+       return 0;
+} /* dns_anyconf_push() */
+
+
+static void dns_anyconf_pop(struct dns_anyconf *cf) {
+       if (cf->count > 0) {
+               --cf->count;
+               cf->tp = cf->cp = cf->token[cf->count];
+               cf->token[cf->count] = 0;
+       }
+} /* dns_anyconf_pop() */
+
+
+static int dns_anyconf_addc(struct dns_anyconf *cf, int ch) {
+       if (!(cf->cp < endof(cf->buffer)))
+               return ENOMEM;
+
+       *cf->cp++ = ch;
+
+       return 0;
+} /* dns_anyconf_addc() */
+
+
+static _Bool dns_anyconf_match(const char *pat, int mc) {
+       _Bool match;
+       int pc;
+
+       if (*pat == '^') {
+               match = 0;
+               ++pat;
+       } else {
+               match = 1;
+       }
+
+       while ((pc = *(const unsigned char *)pat++)) {
+               switch (pc) {
+               case '%':
+                       if (!(pc = *(const unsigned char *)pat++))
+                               return !match;
+
+                       switch (pc) {
+                       case 'a':
+                               if (isalpha(mc))
+                                       return match;
+                               break;
+                       case 'd':
+                               if (isdigit(mc))
+                                       return match;
+                               break;
+                       case 'w':
+                               if (isalnum(mc))
+                                       return match;
+                               break;
+                       case 's':
+                               if (isspace(mc))
+                                       return match;
+                               break;
+                       default:
+                               if (mc == pc)
+                                       return match;
+                               break;
+                       } /* switch() */
+
+                       break;
+               default:
+                       if (mc == pc)
+                               return match;
+                       break;
+               } /* switch() */
+       } /* while() */
+
+       return !match;
+} /* dns_anyconf_match() */
+
+
+static int dns_anyconf_peek(FILE *fp) {
+       int ch;
+       ch = getc(fp);
+       ungetc(ch, fp);
+       return ch;
+} /* dns_anyconf_peek() */
+
+
+static size_t dns_anyconf_skip(const char *pat, FILE *fp) {
+       size_t count = 0;
+       int ch;
+
+       while (EOF != (ch = getc(fp))) {
+               if (dns_anyconf_match(pat, ch)) {
+                       count++;
+                       continue;
+               }
+
+               ungetc(ch, fp);
+
+               break;
+       }
+
+       return count;
+} /* dns_anyconf_skip() */
+
+
+static size_t dns_anyconf_scan(struct dns_anyconf *cf, const char *pat, FILE *fp, int *error) {
+       size_t len;
+       int ch;
+
+       while (EOF != (ch = getc(fp))) {
+               if (dns_anyconf_match(pat, ch)) {
+                       if ((*error = dns_anyconf_addc(cf, ch)))
+                               return 0;
+
+                       continue;
+               } else {
+                       ungetc(ch, fp);
+
+                       break;
+               }
+       }
+
+       if ((len = cf->cp - cf->tp)) {
+               if ((*error = dns_anyconf_push(cf)))
+                       return 0;
+
+               return len;
+       } else {
+               *error = 0;
+
+               return 0; 
+       }
+} /* dns_anyconf_scan() */
+
+
+DNS_NOTUSED static void dns_anyconf_dump(struct dns_anyconf *cf, FILE *fp) {
+       unsigned i;
+
+       fprintf(fp, "tokens:");
+
+       for (i = 0; i < cf->count; i++) {
+               fprintf(fp, " %s", cf->token[i]);
+       }
+
+       fputc('\n', fp);
+} /* dns_anyconf_dump() */
+
+
+enum dns_nssconf_keyword {
+       DNS_NSSCONF_INVALID = 0,
+       DNS_NSSCONF_HOSTS   = 1,
+       DNS_NSSCONF_SUCCESS,
+       DNS_NSSCONF_NOTFOUND,
+       DNS_NSSCONF_UNAVAIL,
+       DNS_NSSCONF_TRYAGAIN,
+       DNS_NSSCONF_CONTINUE,
+       DNS_NSSCONF_RETURN,
+       DNS_NSSCONF_FILES,
+       DNS_NSSCONF_DNS,
+       DNS_NSSCONF_MDNS,
+
+       DNS_NSSCONF_LAST,
+}; /* enum dns_nssconf_keyword */
+
+static enum dns_nssconf_keyword dns_nssconf_keyword(const char *word) {
+       static const char *list[] = {
+               [DNS_NSSCONF_HOSTS]    = "hosts",
+               [DNS_NSSCONF_SUCCESS]  = "success",
+               [DNS_NSSCONF_NOTFOUND] = "notfound",
+               [DNS_NSSCONF_UNAVAIL]  = "unavail",
+               [DNS_NSSCONF_TRYAGAIN] = "tryagain",
+               [DNS_NSSCONF_CONTINUE] = "continue",
+               [DNS_NSSCONF_RETURN]   = "return",
+               [DNS_NSSCONF_FILES]    = "files",
+               [DNS_NSSCONF_DNS]      = "dns",
+               [DNS_NSSCONF_MDNS]     = "mdns",
+       };
+       unsigned i;
+
+       for (i = 1; i < lengthof(list); i++) {
+               if (list[i] && 0 == strcasecmp(list[i], word))
+                       return i;
+       }
+
+       return DNS_NSSCONF_INVALID;
+} /* dns_nssconf_keyword() */
+
+
+static enum dns_nssconf_keyword dns_nssconf_c2k(int ch) {
+       static const char map[] = {
+               ['S'] = DNS_NSSCONF_SUCCESS,
+               ['N'] = DNS_NSSCONF_NOTFOUND,
+               ['U'] = DNS_NSSCONF_UNAVAIL,
+               ['T'] = DNS_NSSCONF_TRYAGAIN,
+               ['C'] = DNS_NSSCONF_CONTINUE,
+               ['R'] = DNS_NSSCONF_RETURN,
+               ['f'] = DNS_NSSCONF_FILES,
+               ['F'] = DNS_NSSCONF_FILES,
+               ['d'] = DNS_NSSCONF_DNS,
+               ['D'] = DNS_NSSCONF_DNS,
+               ['b'] = DNS_NSSCONF_DNS,
+               ['B'] = DNS_NSSCONF_DNS,
+               ['m'] = DNS_NSSCONF_MDNS,
+               ['M'] = DNS_NSSCONF_MDNS,
+       };
+
+       return (ch >= 0 && ch < (int)lengthof(map))? map[ch] : DNS_NSSCONF_INVALID;
+} /* dns_nssconf_c2k() */
+
+
+DNS_PRAGMA_PUSH
+DNS_PRAGMA_QUIET
+
+static int dns_nssconf_k2c(int k) {
+       static const char map[DNS_NSSCONF_LAST] = {
+               [DNS_NSSCONF_SUCCESS]  = 'S',
+               [DNS_NSSCONF_NOTFOUND] = 'N',
+               [DNS_NSSCONF_UNAVAIL]  = 'U',
+               [DNS_NSSCONF_TRYAGAIN] = 'T',
+               [DNS_NSSCONF_CONTINUE] = 'C',
+               [DNS_NSSCONF_RETURN]   = 'R',
+               [DNS_NSSCONF_FILES]    = 'f',
+               [DNS_NSSCONF_DNS]      = 'b',
+               [DNS_NSSCONF_MDNS]     = 'm',
+       };
+
+       return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : '?') : '?';
+} /* dns_nssconf_k2c() */
+
+static const char *dns_nssconf_k2s(int k) {
+       static const char *const map[DNS_NSSCONF_LAST] = {
+               [DNS_NSSCONF_SUCCESS]  = "SUCCESS",
+               [DNS_NSSCONF_NOTFOUND] = "NOTFOUND",
+               [DNS_NSSCONF_UNAVAIL]  = "UNAVAIL",
+               [DNS_NSSCONF_TRYAGAIN] = "TRYAGAIN",
+               [DNS_NSSCONF_CONTINUE] = "continue",
+               [DNS_NSSCONF_RETURN]   = "return",
+               [DNS_NSSCONF_FILES]    = "files",
+               [DNS_NSSCONF_DNS]      = "dns",
+               [DNS_NSSCONF_MDNS]     = "mdns",
+       };
+
+       return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : "") : "";
+} /* dns_nssconf_k2s() */
+
+DNS_PRAGMA_POP
+
+
+int dns_nssconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) {
+       enum dns_nssconf_keyword source, status, action;
+       char lookup[sizeof resconf->lookup] = "", *lp;
+       struct dns_anyconf cf;
+       size_t i;
+       int error;
+
+       while (!feof(fp) && !ferror(fp)) {
+               dns_anyconf_reset(&cf);
+
+               dns_anyconf_skip("%s", fp);
+
+               if (!dns_anyconf_scan(&cf, "%w_", fp, &error))
+                       goto nextent;
+
+               if (DNS_NSSCONF_HOSTS != dns_nssconf_keyword(cf.token[0]))
+                       goto nextent;
+
+               dns_anyconf_pop(&cf);
+
+               if (!dns_anyconf_skip(": \t", fp))
+                       goto nextent;
+
+               *(lp = lookup) = '\0';
+
+               while (dns_anyconf_scan(&cf, "%w_", fp, &error)) {
+                       dns_anyconf_skip(" \t", fp);
+
+                       if ('[' == dns_anyconf_peek(fp)) {
+                               dns_anyconf_skip("[ \t", fp);
+
+                               while (dns_anyconf_scan(&cf, "%w_", fp, &error)) {
+                                       dns_anyconf_skip("= \t", fp);
+                                       if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) {
+                                               dns_anyconf_pop(&cf); /* discard status */
+                                               dns_anyconf_skip("^#;]\n", fp); /* skip to end of criteria */
+                                               break;
+                                       }
+                                       dns_anyconf_skip(" \t", fp);
+                               }
+
+                               dns_anyconf_skip("] \t", fp);
+                       }
+
+                       if (endof(lookup) - lp < cf.count + 1) /* +1 for '\0' */ 
+                               goto nextsrc;
+
+                       source = dns_nssconf_keyword(cf.token[0]);
+
+                       switch (source) {
+                       case DNS_NSSCONF_DNS:
+                       case DNS_NSSCONF_MDNS:
+                       case DNS_NSSCONF_FILES:
+                               *lp++ = dns_nssconf_k2c(source);
+                               break;
+                       default:
+                               goto nextsrc;
+                       }
+
+                       for (i = 1; i + 1 < cf.count; i += 2) {
+                               status = dns_nssconf_keyword(cf.token[i]);
+                               action = dns_nssconf_keyword(cf.token[i + 1]);
+
+                               switch (status) {
+                               case DNS_NSSCONF_SUCCESS:
+                               case DNS_NSSCONF_NOTFOUND:
+                               case DNS_NSSCONF_UNAVAIL:
+                               case DNS_NSSCONF_TRYAGAIN:
+                                       *lp++ = dns_nssconf_k2c(status);
+                                       break;
+                               default:
+                                       continue;
+                               }
+
+                               switch (action) {
+                               case DNS_NSSCONF_CONTINUE:
+                               case DNS_NSSCONF_RETURN:
+                                       break;
+                               default:
+                                       action = (status == DNS_NSSCONF_SUCCESS)
+                                              ? DNS_NSSCONF_RETURN
+                                              : DNS_NSSCONF_CONTINUE;
+                                       break;
+                               }
+                               
+                               *lp++ = dns_nssconf_k2c(action);
+                       }
+nextsrc:
+                       *lp = '\0';
+                       dns_anyconf_reset(&cf);
+               }
+nextent:
+               dns_anyconf_skip("^\n", fp);
+       }
+
+       if (*lookup)
+               strncpy(resconf->lookup, lookup, sizeof resconf->lookup);
+
+       return 0;
+} /* dns_nssconf_loadfile() */
+
+
+int dns_nssconf_loadpath(struct dns_resolv_conf *resconf, const char *path) {
+       FILE *fp;
+       int error;
+
+       if (!(fp = fopen(path, "r")))
+               return dns_syerr();
+
+       error = dns_nssconf_loadfile(resconf, fp);
+
+       fclose(fp);
+
+       return error;
+} /* dns_nssconf_loadpath() */
+
+
+struct dns_nssconf_source {
+       enum dns_nssconf_keyword source, success, notfound, unavail, tryagain;
+}; /* struct dns_nssconf_source */
+
+typedef unsigned dns_nssconf_i;
+
+static inline int dns_nssconf_peek(const struct dns_resolv_conf *resconf, dns_nssconf_i state) {
+       return (state < lengthof(resconf->lookup) && resconf->lookup[state])? resconf->lookup[state] : 0;
+} /* dns_nssconf_peek() */
+
+static _Bool dns_nssconf_next(struct dns_nssconf_source *src, const struct dns_resolv_conf *resconf, dns_nssconf_i *state) {
+       int source, status, action;
+
+       src->source = DNS_NSSCONF_INVALID;
+       src->success = DNS_NSSCONF_RETURN;
+       src->notfound = DNS_NSSCONF_CONTINUE;
+       src->unavail = DNS_NSSCONF_CONTINUE;
+       src->tryagain = DNS_NSSCONF_CONTINUE;
+
+       while ((source = dns_nssconf_peek(resconf, *state))) {
+               source = dns_nssconf_c2k(source);
+               ++*state;
+
+               switch (source) {
+               case DNS_NSSCONF_FILES:
+               case DNS_NSSCONF_DNS:
+               case DNS_NSSCONF_MDNS:
+                       src->source = source;
+                       break;
+               default:
+                       continue;
+               }
+
+               while ((status = dns_nssconf_peek(resconf, *state)) && (action = dns_nssconf_peek(resconf, *state + 1))) {
+                       status = dns_nssconf_c2k(status);
+                       action = dns_nssconf_c2k(action);
+
+                       switch (action) {
+                       case DNS_NSSCONF_RETURN:
+                       case DNS_NSSCONF_CONTINUE:
+                               break;
+                       default:
+                               goto done;
+                       }
+
+                       switch (status) {
+                       case DNS_NSSCONF_SUCCESS:
+                               src->success = action;
+                               break;
+                       case DNS_NSSCONF_NOTFOUND:
+                               src->notfound = action;
+                               break;
+                       case DNS_NSSCONF_UNAVAIL:
+                               src->unavail = action;
+                               break;
+                       case DNS_NSSCONF_TRYAGAIN:
+                               src->tryagain = action;
+                               break;
+                       default:
+                               goto done;
+                       }
+
+                       *state += 2;
+               }
+
+               break;
+       }
+done:
+       return src->source != DNS_NSSCONF_INVALID;
+} /* dns_nssconf_next() */
+
+
+static int dns_nssconf_dump_status(int status, int action, unsigned *count, FILE *fp) {
+       switch (status) {
+       case DNS_NSSCONF_SUCCESS:
+               if (action == DNS_NSSCONF_RETURN)
+                       return 0;
+               break;
+       default:
+               if (action == DNS_NSSCONF_CONTINUE)
+                       return 0;
+               break;
+       }
+
+       fputc(' ', fp);
+
+       if (!*count)
+               fputc('[', fp);
+
+       fprintf(fp, "%s=%s", dns_nssconf_k2s(status), dns_nssconf_k2s(action));
+
+       ++*count;
+
+       return 0;
+} /* dns_nssconf_dump_status() */
+
+
+int dns_nssconf_dump(struct dns_resolv_conf *resconf, FILE *fp) {
+       struct dns_nssconf_source src;
+       dns_nssconf_i i = 0;
+
+       fputs("hosts:", fp);
+
+       while (dns_nssconf_next(&src, resconf, &i)) {
+               unsigned n = 0;
+
+               fprintf(fp, " %s", dns_nssconf_k2s(src.source));
+
+               dns_nssconf_dump_status(DNS_NSSCONF_SUCCESS, src.success, &n, fp);
+               dns_nssconf_dump_status(DNS_NSSCONF_NOTFOUND, src.notfound, &n, fp);
+               dns_nssconf_dump_status(DNS_NSSCONF_UNAVAIL, src.unavail, &n, fp);
+               dns_nssconf_dump_status(DNS_NSSCONF_TRYAGAIN, src.tryagain, &n, fp);
+
+               if (n)
+                       fputc(']', fp);
+       }
+
+       fputc('\n', fp);
+
+       return 0;
+} /* dns_nssconf_dump() */
+
+
 int dns_resconf_setiface(struct dns_resolv_conf *resconf, const char *addr, unsigned short port) {
-       int af  = (strchr(addr, ':'))? AF_INET6 : AF_INET;
+       int af = (strchr(addr, ':'))? AF_INET6 : AF_INET;
+       int error;
 
-       if (1 != dns_inet_pton(af, addr, dns_sa_addr(af, &resconf->iface)))
-               return dns_soerr();
+       if ((error = dns_pton(af, addr, dns_sa_addr(af, &resconf->iface))))
+               return error;
 
        *dns_sa_port(af, &resconf->iface)       = htons(port);
        resconf->iface.ss_family                = af;
@@ -3977,6 +4698,9 @@ int dns_resconf_dump(struct dns_resolv_conf *resconf, FILE *fp) {
        fputc('\n', fp);
 
 
+       fputs("; ", fp);
+       dns_nssconf_dump(resconf, fp);
+
        fprintf(fp, "lookup");
 
        for (i = 0; i < lengthof(resconf->lookup) && resconf->lookup[i]; i++) {
@@ -4004,6 +4728,17 @@ int dns_resconf_dump(struct dns_resolv_conf *resconf, FILE *fp) {
        if (resconf->options.smart)
                fprintf(fp, " smart");
 
+       switch (resconf->options.tcp) {
+       case DNS_RESCONF_TCP_ENABLE:
+               break;
+       case DNS_RESCONF_TCP_ONLY:
+               fprintf(fp, " tcp");
+               break;
+       case DNS_RESCONF_TCP_DISABLE:
+               fprintf(fp, " tcp:disable");
+               break;
+       }
+
        fputc('\n', fp);
 
 
@@ -4045,7 +4780,7 @@ struct dns_hints {
 }; /* struct dns_hints */
 
 
-struct dns_hints *dns_hints_open(struct dns_resolv_conf *resconf EINA_UNUSED, int *error) {
+struct dns_hints *dns_hints_open(struct dns_resolv_conf *resconf, int *error) {
        static const struct dns_hints H_initializer;
        struct dns_hints *H;
 
@@ -4163,8 +4898,8 @@ struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) {
        for (i = 0; i < lengthof(root_hints); i++) {
                af      = root_hints[i].af;
 
-               if (1 != dns_inet_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss)))
-                       goto soerr;
+               if ((error = dns_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss))))
+                       goto error;
 
                *dns_sa_port(af, &ss)   = htons(53);
                ss.ss_family            = af;
@@ -4174,10 +4909,6 @@ struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) {
        }
 
        return hints;
-soerr:
-       error   = dns_soerr();
-
-       goto error;
 error:
        *error_ = error;
 
@@ -4421,15 +5152,16 @@ int dns_hints_dump(struct dns_hints *hints, FILE *fp) {
        struct dns_hints_soa *soa;
        char addr[INET6_ADDRSTRLEN];
        unsigned i;
-       int af;
+       int af, error;
 
        for (soa = hints->head; soa; soa = soa->next) {
                fprintf(fp, "ZONE \"%s\"\n", soa->zone);
 
                for (i = 0; i < soa->count; i++) {
-                       af      = soa->addrs[i].ss.ss_family;
-                       if (!dns_inet_ntop(af, dns_sa_addr(af, &soa->addrs[i].ss), addr, sizeof addr))
-                               return dns_soerr();
+                       af = soa->addrs[i].ss.ss_family;
+
+                       if ((error = dns_ntop(af, dns_sa_addr(af, &soa->addrs[i].ss), addr, sizeof addr)))
+                               return error;
 
                        fprintf(fp, "\t(%d) [%s]:%hu\n", (int)soa->addrs[i].priority, addr, ntohs(*dns_sa_port(af, &soa->addrs[i].ss)));
                }
@@ -4444,47 +5176,47 @@ int dns_hints_dump(struct dns_hints *hints, FILE *fp) {
  *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
-static dns_atomic_t dns_cache_acquire(struct dns_cache *cache EINA_UNUSED) {
+static dns_atomic_t dns_cache_acquire(struct dns_cache *cache) {
        return 0;
 } /* dns_cache_acquire() */
 
 
-static dns_atomic_t dns_cache_release(struct dns_cache *cache EINA_UNUSED) {
+static dns_atomic_t dns_cache_release(struct dns_cache *cache) {
        return 0;
 } /* dns_cache_release() */
 
 
-static struct dns_packet *dns_cache_query(struct dns_packet *query EINA_UNUSED, struct dns_cache *cache EINA_UNUSED, int *error EINA_UNUSED) {
+static struct dns_packet *dns_cache_query(struct dns_packet *query, struct dns_cache *cache, int *error) {
        return 0;
 } /* dns_cache_submit() */
 
 
-static int dns_cache_submit(struct dns_packet *query EINA_UNUSED, struct dns_cache *cache EINA_UNUSED) {
+static int dns_cache_submit(struct dns_packet *query, struct dns_cache *cache) {
        return 0;
 } /* dns_cache_submit() */
 
 
-static int dns_cache_check(struct dns_cache *cache EINA_UNUSED) {
+static int dns_cache_check(struct dns_cache *cache) {
        return 0;
 } /* dns_cache_check() */
 
 
-static struct dns_packet *dns_cache_fetch(struct dns_cache *cache EINA_UNUSED, int *error EINA_UNUSED) {
+static struct dns_packet *dns_cache_fetch(struct dns_cache *cache, int *error) {
        return 0;
 } /* dns_cache_fetch() */
 
 
-static int dns_cache_pollfd(struct dns_cache *cache EINA_UNUSED) {
+static int dns_cache_pollfd(struct dns_cache *cache) {
        return -1;
 } /* dns_cache_pollfd() */
 
 
-static short dns_cache_events(struct dns_cache *cache EINA_UNUSED) {
+static short dns_cache_events(struct dns_cache *cache) {
        return 0;
 } /* dns_cache_events() */
 
 
-static void dns_cache_clear(struct dns_cache *cache EINA_UNUSED) {
+static void dns_cache_clear(struct dns_cache *cache) {
        return;
 } /* dns_cache_clear() */
 
@@ -4562,6 +5294,13 @@ static int dns_socket(struct sockaddr *local, int type, int *error_) {
                goto soerr;
 #endif
 
+#if defined(SO_NOSIGPIPE)
+       if (type == SOCK_DGRAM) {
+               if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, sizeof (int)))
+                       goto soerr;
+       }
+#endif
+
        if (local->sa_family != AF_INET && local->sa_family != AF_INET6)
                return fd;
 
@@ -4592,10 +5331,12 @@ soerr:
        error   = dns_soerr();
 
        goto error;
+#if defined(F_SETFD) || defined(O_NONBLOCK)
 syerr:
        error   = dns_syerr();
 
        goto error;
+#endif
 error:
        *error_ = error;
 
@@ -4650,7 +5391,7 @@ struct dns_socket {
        struct dns_packet *query;
        size_t qout;
 
-       time_t began;
+       struct dns_clock elapsed;
 
        struct dns_packet *answer;
        size_t alen, apos;
@@ -4832,7 +5573,8 @@ int dns_so_submit(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *
 
        so->query       = Q;
        so->qout        = 0;
-       so->began       = dns_now();
+
+       dns_begin(&so->elapsed);
 
        if (dns_header(so->query)->qid == 0)
                dns_header(so->query)->qid      = dns_so_mkqid(so);
@@ -4882,6 +5624,11 @@ static int dns_so_verify(struct dns_socket *so, struct dns_packet *P) {
 } /* dns_so_verify() */
 
 
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warray-bounds"
+#endif
+
 static int dns_so_tcp_send(struct dns_socket *so) {
        unsigned char *qsrc;
        size_t qend;
@@ -4894,7 +5641,7 @@ static int dns_so_tcp_send(struct dns_socket *so) {
        qend = so->query->end + 2;
 
        while (so->qout < qend) {
-               if (0 > (n = send(so->tcp, (void *)&qsrc[so->qout], qend - so->qout, 0)))
+               if (0 > (n = dns_send(so->tcp, (void *)&qsrc[so->qout], qend - so->qout, 0)))
                        return dns_soerr();
 
                so->qout += n;
@@ -4944,6 +5691,10 @@ static int dns_so_tcp_recv(struct dns_socket *so) {
        return 0;
 } /* dns_so_tcp_recv() */
 
+#if __clang__
+#pragma clang diagnostic pop
+#endif
+
 
 int dns_so_check(struct dns_socket *so) {
        int error;
@@ -5098,7 +5849,7 @@ error:
 
 
 time_t dns_so_elapsed(struct dns_socket *so) {
-       return dns_elapsed(so->began);
+       return dns_elapsed(&so->elapsed);
 } /* dns_so_elapsed() */
 
 
@@ -5229,7 +5980,7 @@ struct dns_resolver {
        enum dns_type qtype;
        enum dns_class qclass;
 
-       time_t began;
+       struct dns_clock elapsed;
 
        dns_resconf_i_t search;
 
@@ -5264,7 +6015,7 @@ static int dns_res_tcp2type(int tcp) {
        }
 } /* dns_res_tcp2type() */
 
-struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_hosts *hosts, struct dns_hints *hints, struct dns_cache *cache, const struct dns_options *opts, int *error_) {
+struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_hosts *hosts, struct dns_hints *hints, struct dns_cache *cache, const struct dns_options *opts, int *_error) {
        static const struct dns_resolver R_initializer
                = { .refcount = 1, };
        struct dns_resolver *R  = 0;
@@ -5290,7 +6041,7 @@ struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_ho
         * dns_resconf_local() by default would create undesirable surpises.
         */
        if (!resconf || !hosts || !hints)
-               goto error;
+               goto _error;
 
        if (!(R = malloc(sizeof *R)))
                goto syerr;
@@ -5310,8 +6061,8 @@ struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_ho
 syerr:
        error   = dns_syerr();
 error:
-       *error_ = error;
-
+       *_error = error;
+_error:
        dns_res_close(R);
 
        dns_resconf_close(resconf);
@@ -5350,7 +6101,7 @@ epilog:
 } /* dns_res_stub() */
 
 
-static void dns_res_reset_frame(struct dns_resolver *R EINA_UNUSED, struct dns_res_frame *frame) {
+static void dns_res_reset_frame(struct dns_resolver *R, struct dns_res_frame *frame) {
        free(frame->query);
        free(frame->answer);
        free(frame->hints);
@@ -5618,7 +6369,7 @@ exec:
 
                F->state++;
        case DNS_R_SWITCH:
-               while (F->which < (int)sizeof R->resconf->lookup) {
+               while (F->which < (int)sizeof R->resconf->lookup && R->resconf->lookup[F->which]) {
                        switch (R->resconf->lookup[F->which++]) {
                        case 'b': case 'B':
                                goto(R->sp, DNS_R_BIND);
@@ -5755,6 +6506,8 @@ exec:
 
                F->state++;
        case DNS_R_HINTS:
+               free(F->hints);
+
                if (!(F->hints = dns_hints_query(R->hints, F->query, &error)))
                        goto error;
 
@@ -5893,7 +6646,7 @@ exec:
                }
 
                if (!R->resconf->options.recurse)
-                       goto(R->sp, DNS_R_SWITCH);
+                       goto(R->sp, DNS_R_SEARCH);
 
                dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) {
                        free(F->hints);
@@ -6083,9 +6836,11 @@ error:
 void dns_res_clear(struct dns_resolver *R) {
        switch (R->stack[R->sp].state) {
        case DNS_R_CHECK:
-               return R->cache->clear(R->cache);
+               R->cache->clear(R->cache);
+               break;
        default:
-               return dns_so_clear(&R->so);
+               dns_so_clear(&R->so);
+               break;
        }
 } /* dns_res_clear() */
 
@@ -6120,7 +6875,7 @@ int dns_res_pollfd(struct dns_resolver *R) {
 
 
 time_t dns_res_elapsed(struct dns_resolver *R) {
-       return dns_elapsed(R->began);
+       return dns_elapsed(&R->elapsed);
 } /* dns_res_elapsed() */
 
 
@@ -6138,7 +6893,7 @@ int dns_res_submit(struct dns_resolver *R, const char *qname, enum dns_type qtyp
        R->qtype        = qtype;
        R->qclass       = qclass;
 
-       R->began        = dns_now();
+       dns_begin(&R->elapsed);
 
        return 0;
 } /* dns_res_submit() */
@@ -6534,7 +7289,7 @@ time_t dns_ai_elapsed(struct dns_addrinfo *ai) {
 
 
 void dns_ai_clear(struct dns_addrinfo *ai) {
-       return dns_res_clear(ai->res);
+       dns_res_clear(ai->res);
 } /* dns_ai_clear() */
 
 
@@ -6815,6 +7570,7 @@ enum dns_rcode dns_ircode(const char *name) {
 } /* dns_ircode() */
 
 
+\f
 /*
  * C O M M A N D - L I N E / R E G R E S S I O N  R O U T I N E S
  *
@@ -6840,17 +7596,7 @@ struct {
        struct {
                const char *path[8];
                unsigned count;
-       } resconf;
-
-       struct {
-               const char *path[8];
-               unsigned count;
-       } hosts;
-
-       struct {
-               const char *path[8];
-               unsigned count;
-       } cache;
+       } resconf, nssconf, hosts, cache;
 
        const char *qname;
        enum dns_type qtype;
@@ -6923,7 +7669,7 @@ static void *grow(unsigned char *p, size_t size) {
        void *tmp;
 
        if (!(tmp = realloc(p, size)))
-               panic("realloc(%zu): %s", size, dns_strerror(errno));
+               panic("realloc(%"PRIuZ"): %s", size, dns_strerror(errno));
 
        return tmp;
 } /* grow() */
@@ -6931,7 +7677,7 @@ static void *grow(unsigned char *p, size_t size) {
 
 static size_t add(size_t a, size_t b) {
        if (~a < b)
-               panic("%zu + %zu: integer overflow", a, b);
+               panic("%"PRIuZ" + %"PRIuZ": integer overflow", a, b);
 
        return a + b;
 } /* add() */
@@ -6989,6 +7735,27 @@ static struct dns_resolv_conf *resconf(void) {
                        panic("%s: %s", path, dns_strerror(error));
        }
 
+       for (i = 0; i < MAIN.nssconf.count; i++) {
+               path    = MAIN.nssconf.path[i];
+
+               if (0 == strcmp(path, "-"))
+                       error   = dns_nssconf_loadfile(resconf, stdin);
+               else
+                       error   = dns_nssconf_loadpath(resconf, path);
+
+               if (error)
+                       panic("%s: %s", path, dns_strerror(error));
+       }
+
+       if (!MAIN.nssconf.count) {
+               path = "/etc/nsswitch.conf";
+
+               if (!(error = dns_nssconf_loadpath(resconf, path)))
+                       MAIN.nssconf.path[MAIN.nssconf.count++] = path;
+               else if (error != ENOENT)
+                       panic("%s: %s", path, dns_strerror(error));
+       }
+
        return resconf;
 } /* resconf() */
 
@@ -7086,7 +7853,7 @@ static int parse_packet(int argc, char *argv[]) {
        P->end  = fread(P->data, 1, P->size, stdin);
 
        fputs(";; [HEADER]\n", stdout);
-       fprintf(stdout, ";;     qr : %s(%d)\n", (dns_header(P)->qr)? "QUERY" : "RESPONSE", dns_header(P)->qr);
+       fprintf(stdout, ";;     qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr);
        fprintf(stdout, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode);
        fprintf(stdout, ";;     aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa);
        fprintf(stdout, ";;     tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc);
@@ -7118,9 +7885,8 @@ static int parse_packet(int argc, char *argv[]) {
        struct dns_rr rrset[32];
        struct dns_rr_i *rri    = dns_rr_i_new(Q, .name = dns_d_new("ns8.yahoo.com", DNS_D_ANCHOR), .sort = MAIN.sort);
        unsigned rrcount        = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error);
-       unsigned i;
 
-       for (i = 0; i < rrcount; i++) {
+       for (unsigned i = 0; i < rrcount; i++) {
                rr      = rrset[i];
 #endif
                if (section != rr.section)
@@ -7133,10 +7899,10 @@ static int parse_packet(int argc, char *argv[]) {
        }
 
        if (MAIN.verbose > 1) {
-               fprintf(stderr, "orig:%zu\n", P->end);
+               fprintf(stderr, "orig:%"PRIuZ"\n", P->end);
                hexdump(P->data, P->end, stdout);
 
-               fprintf(stderr, "copy:%zu\n", Q->end);
+               fprintf(stderr, "copy:%"PRIuZ"\n", Q->end);
                hexdump(Q->data, Q->end, stdout);
        }
 
@@ -7175,7 +7941,7 @@ static int expand_domain(int argc, char *argv[]) {
        len = slurp(&src, 0, stdin, "-");
 
        if (!(pkt = dns_p_make(len, &error)))
-               panic("malloc(%zu): %s", len, dns_strerror(error));
+               panic("malloc(%"PRIuZ"): %s", len, dns_strerror(error));
 
        memcpy(pkt->data, src, len);
        pkt->end = len;
@@ -7205,13 +7971,16 @@ static int expand_domain(int argc, char *argv[]) {
 static int show_resconf(int argc, char *argv[]) {
        unsigned i;
 
-       resconf();      /* load it */
+       resconf(); /* load it */
 
        fputs("; SOURCES\n", stdout);
 
        for (i = 0; i < MAIN.resconf.count; i++)
                fprintf(stdout, ";   %s\n", MAIN.resconf.path[i]);
 
+       for (i = 0; i < MAIN.nssconf.count; i++)
+               fprintf(stdout, ";   %s\n", MAIN.nssconf.path[i]);
+
        fputs(";\n", stdout);
 
        dns_resconf_dump(resconf(), stdout);
@@ -7220,6 +7989,27 @@ static int show_resconf(int argc, char *argv[]) {
 } /* show_resconf() */
 
 
+static int show_nssconf(int argc, char *argv[]) {
+       unsigned i;
+
+       resconf();
+
+       fputs("# SOURCES\n", stdout);
+
+       for (i = 0; i < MAIN.resconf.count; i++)
+               fprintf(stdout, "#   %s\n", MAIN.resconf.path[i]);
+
+       for (i = 0; i < MAIN.nssconf.count; i++)
+               fprintf(stdout, "#   %s\n", MAIN.nssconf.path[i]);
+
+       fputs("#\n", stdout);
+
+       dns_nssconf_dump(resconf(), stdout);
+
+       return 0;
+} /* show_nssconf() */
+
+
 static int show_hosts(int argc, char *argv[]) {
        unsigned i;
 
@@ -7256,7 +8046,7 @@ static int query_hosts(int argc, char *argv[]) {
                union { struct in_addr a; struct in6_addr a6; } addr;
                int af  = (strchr(MAIN.qname, ':'))? AF_INET6 : AF_INET;
 
-               if (1 != dns_inet_pton(af, MAIN.qname, &addr))
+               if ((error = dns_pton(af, MAIN.qname, &addr)))
                        panic("%s: %s", MAIN.qname, dns_strerror(error));
 
                qlen    = dns_ptr_qname(qname, sizeof qname, af, &addr);
@@ -7366,8 +8156,8 @@ static int send_query(int argc, char *argv[]) {
        if (argc > 1) {
                ss.ss_family    = (strchr(argv[1], ':'))? AF_INET6 : AF_INET;
                
-               if (1 != dns_inet_pton(ss.ss_family, argv[1], dns_sa_addr(ss.ss_family, &ss)))
-                       panic("%s: invalid host address", argv[1]);
+               if ((error = dns_pton(ss.ss_family, argv[1], dns_sa_addr(ss.ss_family, &ss))))
+                       panic("%s: %s", argv[1], dns_strerror(error));
 
                *dns_sa_port(ss.ss_family, &ss) = htons(53);
        } else
@@ -7399,7 +8189,7 @@ static int send_query(int argc, char *argv[]) {
                panic("dns_so_open: %s", dns_strerror(error));
 
        while (!(A = dns_so_query(so, Q, (struct sockaddr *)&ss, &error))) {
-               if (error != EAGAIN)
+               if (error != DNS_EAGAIN)
                        panic("dns_so_query: %s (%d)", dns_strerror(error), error);
                if (dns_so_elapsed(so) > 10)
                        panic("query timed-out");
@@ -7492,7 +8282,7 @@ static int resolve_query(int argc, char *argv[]) {
                panic("%s: %s", MAIN.qname, dns_strerror(error));
 
        while ((error = dns_res_check(R))) {
-               if (error != EAGAIN)
+               if (error != DNS_EAGAIN)
                        panic("dns_res_check: %s (%d)", dns_strerror(error), error);
                if (dns_res_elapsed(R) > 30)
                        panic("query timed-out");
@@ -7506,11 +8296,11 @@ static int resolve_query(int argc, char *argv[]) {
 
        st = dns_res_stat(R);
        putchar('\n');
-       printf(";; queries:  %zu\n", st->queries);
-       printf(";; udp sent: %zu in %zu bytes\n", st->udp.sent.count, st->udp.sent.bytes);
-       printf(";; udp rcvd: %zu in %zu bytes\n", st->udp.rcvd.count, st->udp.rcvd.bytes);
-       printf(";; tcp sent: %zu in %zu bytes\n", st->tcp.sent.count, st->tcp.sent.bytes);
-       printf(";; tcp rcvd: %zu in %zu bytes\n", st->tcp.rcvd.count, st->tcp.rcvd.bytes);
+       printf(";; queries:  %"PRIuZ"\n", st->queries);
+       printf(";; udp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.sent.count, st->udp.sent.bytes);
+       printf(";; udp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.rcvd.count, st->udp.rcvd.bytes);
+       printf(";; tcp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.sent.count, st->tcp.sent.bytes);
+       printf(";; tcp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.rcvd.count, st->tcp.rcvd.bytes);
 
        dns_res_close(R);
 
@@ -7552,7 +8342,7 @@ static int resolve_addrinfo(int argc, char *argv[]) {
                        break;
                case ENOENT:
                        break;
-               case EAGAIN:
+               case DNS_EAGAIN:
                        if (dns_ai_elapsed(ai) > 30)
                                panic("query timed-out");
 
@@ -7696,7 +8486,7 @@ static int sizes(int argc, char *argv[]) {
                SIZE(struct dns_sshfp, struct dns_txt, union dns_any),
                SIZE(struct dns_resolv_conf, struct dns_hosts, struct dns_hints, struct dns_hints_i),
                SIZE(struct dns_options, struct dns_socket, struct dns_resolver, struct dns_addrinfo),
-               SIZE(struct dns_cache),
+               SIZE(struct dns_cache), SIZE(size_t), SIZE(void *), SIZE(long)
        };
        unsigned i, max;
 
@@ -7704,7 +8494,7 @@ static int sizes(int argc, char *argv[]) {
                max = MAX(max, strlen(type[i].name));
 
        for (i = 0; i < lengthof(type); i++)
-               printf("%*s : %zu\n", max, type[i].name, type[i].size);
+               printf("%*s : %"PRIuZ"\n", max, type[i].name, type[i].size);
 
        return 0;
 } /* sizes() */
@@ -7716,6 +8506,7 @@ static const struct { const char *cmd; int (*run)(); const char *help; } cmds[]
        { "expand-domain",      &expand_domain,         "expand domain at offset NN in packet from stdin" },
        { "show-resconf",       &show_resconf,          "show resolv.conf data" },
        { "show-hosts",         &show_hosts,            "show hosts data" },
+       { "show-nssconf",       &show_nssconf,          "show nsswitch.conf data" },
        { "query-hosts",        &query_hosts,           "query A, AAAA or PTR in hosts data" },
        { "search-list",        &search_list,           "generate query search list from domain" },
        { "permute-set",        &permute_set,           "generate random permutation -> (0 .. N or N .. M)" },
@@ -7745,6 +8536,7 @@ static void print_usage(const char *progname, FILE *fp) {
        static const char *usage        = 
                " [OPTIONS] COMMAND [ARGS]\n"
                "  -c PATH   Path to resolv.conf\n"
+               "  -n PATH   Path to nsswitch.conf\n"
                "  -l PATH   Path to local hosts\n"
                "  -z PATH   Path to zone cache\n"
                "  -q QNAME  Query name\n"
@@ -7794,7 +8586,7 @@ int main(int argc, char **argv) {
        unsigned i;
        int ch;
 
-       while (-1 != (ch = getopt(argc, argv, "q:t:c:l:z:s:vVh"))) {
+       while (-1 != (ch = getopt(argc, argv, "q:t:c:n:l:z:s:vVh"))) {
                switch (ch) {
                case 'c':
                        assert(MAIN.resconf.count < lengthof(MAIN.resconf.path));
@@ -7802,6 +8594,12 @@ int main(int argc, char **argv) {
                        MAIN.resconf.path[MAIN.resconf.count++] = optarg;
 
                        break;
+               case 'n':
+                       assert(MAIN.nssconf.count < lengthof(MAIN.nssconf.path));
+
+                       MAIN.nssconf.path[MAIN.nssconf.count++] = optarg;
+
+                       break;
                case 'l':
                        assert(MAIN.hosts.count < lengthof(MAIN.hosts.path));
 
@@ -7881,3 +8679,14 @@ int main(int argc, char **argv) {
 
 
 #endif /* DNS_MAIN */
+
+
+/*
+ * pop file-scoped compiler annotations
+ */
+#if __clang__
+#pragma clang diagnostic pop
+#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
+#pragma GCC diagnostic pop
+#endif
+
index 07f7f16..3504dd2 100644 (file)
@@ -1,7 +1,7 @@
 /* ==========================================================================
  * dns.h - Recursive, Reentrant DNS Resolver.
  * --------------------------------------------------------------------------
- * Copyright (c) 2009, 2010  William Ahern
+ * Copyright (c) 2009, 2010, 2012  William Ahern
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the
@@ -37,6 +37,7 @@
 #include <winsock2.h>
 #include <ws2tcpip.h>
 #else
+#include <sys/param.h>         /* BYTE_ORDER BIG_ENDIAN _BIG_ENDIAN */
 #include <sys/types.h>         /* socklen_t */
 #include <sys/socket.h>                /* struct socket */
 
@@ -47,8 +48,6 @@
 #include <netdb.h>             /* struct addrinfo */
 #endif
 
-#include <Eina.h>              /* EINA_UNUSED */
-
 
 /*
  * V E R S I O N
@@ -66,9 +65,9 @@
 
 #define DNS_VENDOR "william@25thandClement.com"
 
-#define DNS_V_REL  0x20110117
-#define DNS_V_ABI  0x20100709
-#define DNS_V_API  0x20100709
+#define DNS_V_REL  0x20121023
+#define DNS_V_ABI  0x20120806
+#define DNS_V_API  0x20120806
 
 
 const char *dns_vendor(void);
@@ -81,22 +80,81 @@ int dns_v_api(void);
 /*
  * E R R O R S
  *
+ * Errors and exceptions are always returned through an int. This should
+ * hopefully make integration easier in the majority of circumstances, and
+ * also cut down on useless compiler warnings.
+ *
+ * System and library errors are returned together. POSIX guarantees that
+ * all system errors are positive integers. Library errors are always
+ * negative integers in the range DNS_EBASE to DNS_ELAST, with the high bits
+ * set to the three magic ASCII characters "dns".
+ *
+ * dns_strerror() returns static English string descriptions of all known
+ * errors, and punts the remainder to strerror(3).
+ *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
+#define DNS_EBASE -(('d' << 24) | ('n' << 16) | ('s' << 8) | 64)
+
+#define dns_error_t int /* for documentation only */
+
 enum dns_errno {
-       DNS_ENOBUFS     = -(('d' << 24) | ('n' << 16) | ('s' << 8) | 64),
+       DNS_ENOBUFS = DNS_EBASE,
        DNS_EILLEGAL,
        DNS_EORDER,
        DNS_ESECTION,
        DNS_EUNKNOWN,
+       DNS_EADDRESS,
+       DNS_ELAST,
 }; /* dns_errno */
 
-const char *dns_strerror(int);
+const char *dns_strerror(dns_error_t);
 
 extern int dns_debug;
 
 
 /*
+ * C O M P I L E R  A N N O T A T I O N S
+ *
+ * GCC with -Wextra, and clang by default, complain about overrides in
+ * initializer lists. Overriding previous member initializers is well
+ * defined behavior in C. dns.c relies on this behavior to define default,
+ * overrideable member values when instantiating configuration objects.
+ *
+ * dns_quietinit() guards a compound literal expression with pragmas to
+ * silence these shrill warnings. This alleviates the burden of requiring
+ * third-party projects to adjust their compiler flags.
+ *
+ * NOTE: If you take the address of the compound literal, take the address
+ * of the transformed expression, otherwise the compound literal lifetime is
+ * tied to the scope of the GCC statement expression.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if defined __clang__
+#define DNS_PRAGMA_PUSH _Pragma("clang diagnostic push")
+#define DNS_PRAGMA_QUIET _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"")
+#define DNS_PRAGMA_POP _Pragma("clang diagnostic pop")
+
+#define dns_quietinit(...) \
+       DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__ DNS_PRAGMA_POP
+#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
+#define DNS_PRAGMA_PUSH _Pragma("GCC diagnostic push")
+#define DNS_PRAGMA_QUIET _Pragma("GCC diagnostic ignored \"-Woverride-init\"")
+#define DNS_PRAGMA_POP _Pragma("GCC diagnostic pop")
+
+/* GCC parses the _Pragma operator less elegantly than clang. */
+#define dns_quietinit(...) \
+       ({ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__; DNS_PRAGMA_POP })
+#else
+#define DNS_PRAGMA_PUSH
+#define DNS_PRAGMA_QUIET
+#define DNS_PRAGMA_POP
+#define dns_quietinit(...) __VA_ARGS__
+#endif
+
+
+/*
  * E V E N T S  I N T E R F A C E S
  *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -168,6 +226,7 @@ enum dns_type {
        DNS_T_TXT       = 16,
        DNS_T_AAAA      = 28,
        DNS_T_SRV       = 33,
+       DNS_T_OPT       = 41,
        DNS_T_SSHFP     = 44,
        DNS_T_SPF       = 99,
 
@@ -261,7 +320,7 @@ extern unsigned (*dns_random)(void);
 struct dns_header {
                unsigned qid:16;
 
-#if BYTE_ORDER == BIG_ENDIAN
+#if (defined BYTE_ORDER && BYTE_ORDER == BIG_ENDIAN) || (defined __sun && defined _BIG_ENDIAN)
                unsigned qr:1;
                unsigned opcode:4;
                unsigned aa:1;
@@ -422,7 +481,8 @@ int dns_rr_cmp(struct dns_rr *, struct dns_packet *, struct dns_rr *, struct dns
 size_t dns_rr_print(void *, size_t, struct dns_rr *, struct dns_packet *, int *);
 
 
-#define dns_rr_i_new(P, ...)           dns_rr_i_init(&(struct dns_rr_i){ .data = 0, __VA_ARGS__ }, (P))
+#define dns_rr_i_new(P, ...) \
+       dns_rr_i_init(&dns_quietinit((struct dns_rr_i){ 0, __VA_ARGS__ }), (P))
 
 struct dns_rr_i {
        enum dns_section section;
@@ -618,6 +678,38 @@ size_t dns_srv_cname(void *, size_t, struct dns_srv *);
 
 
 /*
+ * OPT  R E S O U R C E  R E C O R D
+ */
+
+#define DNS_OPT_MINDATA 512
+
+#define DNS_OPT_BADVERS 16
+
+struct dns_opt {
+       size_t size, len;
+
+       unsigned char rcode, version;
+       unsigned short maxsize;
+
+       unsigned char data[DNS_OPT_MINDATA];
+}; /* struct dns_opt */
+
+unsigned int dns_opt_ttl(const struct dns_opt *);
+
+unsigned short dns_opt_class(const struct dns_opt *);
+
+struct dns_opt *dns_opt_init(struct dns_opt *, size_t);
+
+int dns_opt_parse(struct dns_opt *, struct dns_rr *, struct dns_packet *);
+
+int dns_opt_push(struct dns_packet *, struct dns_opt *);
+
+int dns_opt_cmp(const struct dns_opt *, const struct dns_opt *);
+
+size_t dns_opt_print(void *, size_t, struct dns_opt *);
+
+
+/*
  * SSHFP  R E S O U R C E  R E C O R D
  */
 
@@ -682,6 +774,7 @@ union dns_any {
        struct dns_soa soa;
        struct dns_ptr ptr;
        struct dns_srv srv;
+       struct dns_opt opt;
        struct dns_sshfp sshfp;
        struct dns_txt txt, spf, rdata;
 }; /* union dns_any */
@@ -742,7 +835,7 @@ struct dns_resolv_conf {
        char search[4][DNS_D_MAXNAME + 1];
 
        /* (f)ile, (b)ind, (c)ache */
-       char lookup[3];
+       char lookup[4 * (1 + (4 * 2))];
 
        struct {
                _Bool edns0;
@@ -791,8 +884,14 @@ int dns_resconf_loadfile(struct dns_resolv_conf *, FILE *);
 
 int dns_resconf_loadpath(struct dns_resolv_conf *, const char *);
 
+int dns_nssconf_loadfile(struct dns_resolv_conf *, FILE *);
+
+int dns_nssconf_loadpath(struct dns_resolv_conf *, const char *);
+
 int dns_resconf_dump(struct dns_resolv_conf *, FILE *);
 
+int dns_nssconf_dump(struct dns_resolv_conf *, FILE *);
+
 int dns_resconf_setiface(struct dns_resolv_conf *, const char *, unsigned short);
 
 typedef unsigned long dns_resconf_i_t;
@@ -885,7 +984,7 @@ void dns_cache_close(struct dns_cache *);
 #define DNS_OPTS_INITIALIZER  { DNS_OPTS_INITIALIZER_ }
 #define DNS_OPTS_INIT(...)    { DNS_OPTS_INITIALIZER_, __VA_ARGS__ }
 
-#define dns_opts(...) (&(struct dns_options)DNS_OPTS_INIT(__VA_ARGS__))
+#define dns_opts(...) (&dns_quietinit((struct dns_options)DNS_OPTS_INIT(__VA_ARGS__)))
 
 struct dns_options {
        /*
@@ -1028,15 +1127,7 @@ int dns_ai_poll(struct dns_addrinfo *, int);
 
 const struct dns_stat *dns_ai_stat(struct dns_addrinfo *);
 
-void *dns_sa_addr(int af, void *sa);
-unsigned short *dns_sa_port(int af, void *sa);
-#if _WIN32
-const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim);
-#else
-#define dns_inet_pton(...)     inet_pton(__VA_ARGS__)
-#define dns_inet_ntop(...)     inet_ntop(__VA_ARGS__)
-#endif
-#define dns_sa_family(sa)      (((struct sockaddr *)(sa))->sa_family)
+
 /*
  * U T I L I T Y  I N T E R F A C E S
  *
@@ -1073,4 +1164,15 @@ size_t dns_strlcat(char *, const char *, size_t);
 #define DNS_PP_D11 10
 #define DNS_PP_DEC(N) DNS_PP_XPASTE(DNS_PP_D, N)
 
+
+void *dns_sa_addr(int af, void *sa);
+unsigned short *dns_sa_port(int af, void *sa);
+#if _WIN32
+const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim);
+#else
+#define dns_inet_pton(...)     inet_pton(__VA_ARGS__)
+#define dns_inet_ntop(...)     inet_ntop(__VA_ARGS__)
+#endif
+#define dns_sa_family(sa)      (((struct sockaddr *)(sa))->sa_family)
+
 #endif /* DNS_H */