From patchwork Fri Feb 15 20:16:42 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Stefan_Fr=C3=B6berg?= X-Patchwork-Id: 220844 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from whitealder.osuosl.org (whitealder.osuosl.org [140.211.166.138]) by ozlabs.org (Postfix) with ESMTP id EFDB62C007A for ; Sat, 16 Feb 2013 07:25:33 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 1CCFB8CDCD; Fri, 15 Feb 2013 20:25:30 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id BWXJDs7ehrz5; Fri, 15 Feb 2013 20:24:27 +0000 (UTC) Received: from ash.osuosl.org (ash.osuosl.org [140.211.166.34]) by whitealder.osuosl.org (Postfix) with ESMTP id 11EFF8BD20; Fri, 15 Feb 2013 20:24:26 +0000 (UTC) X-Original-To: buildroot@lists.busybox.net Delivered-To: buildroot@osuosl.org Received: from whitealder.osuosl.org (whitealder.osuosl.org [140.211.166.138]) by ash.osuosl.org (Postfix) with ESMTP id B06278F75B for ; Fri, 15 Feb 2013 20:24:31 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 2ED778681E for ; Fri, 15 Feb 2013 20:24:24 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 1aNrYIk7RBKg for ; Fri, 15 Feb 2013 20:24:05 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mail.petroprogram.com (mail.petroprogram.com [194.89.34.74]) by whitealder.osuosl.org (Postfix) with ESMTPS id 6B28882C34 for ; Fri, 15 Feb 2013 20:24:04 +0000 (UTC) Received: from localhost.fi (unknown [194.89.34.74]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) (Authenticated sender: stefan.froberg@petroprogram.com) by mail.petroprogram.com (Postfix) with ESMTPSA id C0C8342A8F; Fri, 15 Feb 2013 22:15:26 +0200 (EET) From: =?UTF-8?q?Stefan=20Fr=C3=B6berg?= To: buildroot@busybox.net Date: Fri, 15 Feb 2013 22:16:42 +0200 Message-Id: <1360959402-24775-1-git-send-email-stefan.froberg@petroprogram.com> X-Mailer: git-send-email 1.7.7.6 MIME-Version: 1.0 Subject: [Buildroot] [PATCH] new package: adns X-BeenThere: buildroot@busybox.net X-Mailman-Version: 2.1.14 Precedence: list List-Id: Discussion and development of buildroot List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: buildroot-bounces@busybox.net Sender: buildroot-bounces@busybox.net GNU adns Advanced, easy to use, asynchronous-capable DNS client library and utilities. adns is a resolver library for C (and C++) programs, and a collection of useful DNS resolver utilities. Signed-off-by: Stefan Fröberg --- package/Config.in | 1 + package/adns/Config.in | 7 + package/adns/adns-1.4-cnamechain.patch | 1275 +++++++++++++++ package/adns/adns-1.4-destdir.patch | 54 + package/adns/adns-1.4-ipv6.patch | 2725 ++++++++++++++++++++++++++++++++ package/adns/adns-1.4-rh514838.patch | 26 + package/adns/adns.mk | 15 + 7 files changed, 4103 insertions(+), 0 deletions(-) create mode 100644 package/adns/Config.in create mode 100644 package/adns/adns-1.4-cnamechain.patch create mode 100644 package/adns/adns-1.4-destdir.patch create mode 100644 package/adns/adns-1.4-ipv6.patch create mode 100644 package/adns/adns-1.4-rh514838.patch create mode 100644 package/adns/adns.mk diff --git a/package/Config.in b/package/Config.in index 8588951..ec282b9 100644 --- a/package/Config.in +++ b/package/Config.in @@ -612,6 +612,7 @@ source "package/sound-theme-freedesktop/Config.in" endmenu menu "Networking applications" +source "package/adns/Config.in" source "package/argus/Config.in" source "package/arptables/Config.in" source "package/avahi/Config.in" diff --git a/package/adns/Config.in b/package/adns/Config.in new file mode 100644 index 0000000..1971381 --- /dev/null +++ b/package/adns/Config.in @@ -0,0 +1,7 @@ +config BR2_PACKAGE_ADNS + bool "adns" + help + Advanced, easy to use, asynchronous-capable DNS client + library and utilities. + + http://www.chiark.greenend.org.uk/~ian/adns diff --git a/package/adns/adns-1.4-cnamechain.patch b/package/adns/adns-1.4-cnamechain.patch new file mode 100644 index 0000000..1eadf54 --- /dev/null +++ b/package/adns/adns-1.4-cnamechain.patch @@ -0,0 +1,1275 @@ +[ADNS] Re: CNAME chains +Brad Spencer spencer at infointeractive.com +Mon, 28 Aug 2006 14:43:00 -0300 + +Previous message: CNAME chains +Next message: CNAME chains option +Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] +--pf9I7BMVVzbSWLtt +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline + +On Fri, Aug 25, 2006 at 11:36:04AM -0700, William Ahern wrote: +> On Fri, Aug 25, 2006 at 09:39:01AM +0100, peter burden wrote: +> > Hello, +> > Is there any way to make ADNS follow CNAME chains ? +> > +> > I have set the adns_qf_cname_loose query flag and it seems OK for a +> > single +> > CNAME - e.g. (output from 'dig') + +I posted a small patch back in 2003 that made changes to adns so that +it would follow CNAME chains. See + +http://www.chiark.greenend.org.uk/pipermail/adns-discuss/2003/001072.html + +The patch included in that post is against an old adns version, so I +have attached my latest version of the patch to this message. (I have +not tested that the attached patch applied cleanly to the current adns +source, but it may be slightly more in sync with the current version.) + +> CNAME chains are technically not allowed. Such chains are violations of the +> specifications. Also, I believe MX host lookups returning CNAMEs (i.e. MX +> yahoo.com -> A mail.yahoo.com -> CNAME foo) is also illegal. + +I have also been told that CNAME chains are illegal, but I can not +find any actual text that says that a resolver should fail when it +encounters them. In fact, RFC 1034 Section 3.6.2 says: + + Domain names in RRs which point at another name should always point at + the primary name and not the alias. This avoids extra indirections in + accessing information. For example, the address to name RR for the + above host should be: + + 52.0.0.10.IN-ADDR.ARPA IN PTR C.ISI.EDU + + rather than pointing at USC-ISIC.ARPA. + +The above implies that CNAME chains are illegal, IMO. But then, the +next sentence is: + + Of course, by the robustness principle, domain software should not + fail when presented with CNAME chains or loops; CNAME chains + should be followed and CNAME loops signalled as an error. + +This advice, coupled with the fact that CNAME chains exist in the +wild, triggered me to create the patch in the first place. My patch +doesn't detect loops, but instead simply won't follow chains longer +than a certain (hard-coded) size. + +Hope this helps! + +-- +------------------------------------------------------------------ +Brad Spencer - spencer@infointeractive.com - "It's quite nice..." +Systems Architect | InfoInterActive Corp. | A Canadian AOL Company + +--pf9I7BMVVzbSWLtt +Content-Type: text/plain; charset=us-ascii +Content-Disposition: attachment; filename="cname_chains.diff" + +diff -Naur adns-1.4.org/src/internal.h adns-1.4/src/internal.h +--- adns-1.4.org/src/internal.h 2013-01-02 19:55:38.935194976 +0200 ++++ adns-1.4/src/internal.h 2013-01-02 19:55:47.129330386 +0200 +@@ -231,6 +231,9 @@ + int cname_dglen, cname_begin; + /* If non-0, has been allocated using . */ + ++ int cname_alias_hops_left; ++ /* The number of cname alias hops we will allow */ ++ + vbuf search_vb; + int search_origlen, search_pos, search_doneabs; + /* Used by the searching algorithm. The query domain in textual form +diff -Naur adns-1.4.org/src/internal.h.orig adns-1.4/src/internal.h.orig +--- adns-1.4.org/src/internal.h.orig 1970-01-01 02:00:00.000000000 +0200 ++++ adns-1.4/src/internal.h.orig 2006-04-08 17:36:57.000000000 +0300 +@@ -0,0 +1,760 @@ ++/* ++ * internal.h ++ * - declarations of private objects with external linkage (adns__*) ++ * - definitons of internal macros ++ * - comments regarding library data structures ++ */ ++/* ++ * This file is part of adns, which is ++ * Copyright (C) 1997-2000,2003,2006 Ian Jackson ++ * Copyright (C) 1999-2000,2003,2006 Tony Finch ++ * Copyright (C) 1991 Massachusetts Institute of Technology ++ * (See the file INSTALL for full details.) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#ifndef ADNS_INTERNAL_H_INCLUDED ++#define ADNS_INTERNAL_H_INCLUDED ++ ++#include "config.h" ++typedef unsigned char byte; ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "adns.h" ++#include "dlist.h" ++ ++#ifdef ADNS_REGRESS_TEST ++# include "hredirect.h" ++#endif ++ ++/* Configuration and constants */ ++ ++#define MAXSERVERS 5 ++#define MAXSORTLIST 15 ++#define UDPMAXRETRIES 15 ++#define UDPRETRYMS 2000 ++#define TCPWAITMS 30000 ++#define TCPCONNMS 14000 ++#define TCPIDLEMS 30000 ++#define MAXTTLBELIEVE (7*86400) /* any TTL > 7 days is capped */ ++ ++#define DNS_PORT 53 ++#define DNS_MAXUDP 512 ++#define DNS_MAXLABEL 63 ++#define DNS_MAXDOMAIN 255 ++#define DNS_HDRSIZE 12 ++#define DNS_IDOFFSET 0 ++#define DNS_CLASS_IN 1 ++ ++#define DNS_INADDR_ARPA "in-addr", "arpa" ++ ++#define MAX_POLLFDS ADNS_POLLFDS_RECOMMENDED ++ ++typedef enum { ++ cc_user, ++ cc_entex, ++ cc_freq ++} consistency_checks; ++ ++typedef enum { ++ rcode_noerror, ++ rcode_formaterror, ++ rcode_servfail, ++ rcode_nxdomain, ++ rcode_notimp, ++ rcode_refused ++} dns_rcode; ++ ++/* Shared data structures */ ++ ++typedef union { ++ adns_status status; ++ char *cp; ++ adns_rrtype type; ++ int i; ++ struct in_addr ia; ++ unsigned long ul; ++} rr_align; ++ ++typedef struct { ++ int used, avail; ++ byte *buf; ++} vbuf; ++ ++typedef struct { ++ adns_state ads; ++ adns_query qu; ++ int serv; ++ const byte *dgram; ++ int dglen, nsstart, nscount, arcount; ++ struct timeval now; ++} parseinfo; ++ ++typedef struct typeinfo { ++ adns_rrtype typekey; ++ const char *rrtname; ++ const char *fmtname; ++ int rrsz; ++ ++ void (*makefinal)(adns_query qu, void *data); ++ /* Change memory management of *data. ++ * Previously, used alloc_interim, now use alloc_final. ++ */ ++ ++ adns_status (*convstring)(vbuf *vb, const void *data); ++ /* Converts the RR data to a string representation in vbuf. ++ * vbuf will be appended to (it must have been initialised), ++ * and will not be null-terminated by convstring. ++ */ ++ ++ adns_status (*parse)(const parseinfo *pai, int cbyte, ++ int max, void *store_r); ++ /* Parse one RR, in dgram of length dglen, starting at cbyte and ++ * extending until at most max. ++ * ++ * The RR should be stored at *store_r, of length qu->typei->rrsz. ++ * ++ * If there is an overrun which might indicate truncation, it should set ++ * *rdstart to -1; otherwise it may set it to anything else positive. ++ * ++ * nsstart is the offset of the authority section. ++ */ ++ ++ int (*diff_needswap)(adns_state ads,const void *datap_a,const void *datap_b); ++ /* Returns !0 if RR a should be strictly after RR b in the sort order, ++ * 0 otherwise. Must not fail. ++ */ ++ ++ adns_status (*qdparselabel)(adns_state ads, ++ const char **p_io, const char *pe, int labelnum, ++ char label_r[DNS_MAXDOMAIN], int *ll_io, ++ adns_queryflags flags, ++ const struct typeinfo *typei); ++ /* Parses one label from the query domain string. On entry, *p_io ++ * points to the next character to parse and *ll_io is the size of ++ * the buffer. pe points just after the end of the query domain ++ * string. On successful return, label_r[] and *ll_io are filled in ++ * and *p_io points to *pe or just after the label-ending `.'. */ ++ ++ void (*postsort)(adns_state ads, void *array, int nrrs, ++ const struct typeinfo *typei); ++ /* Called immediately after the RRs have been sorted, and may rearrange ++ * them. (This is really for the benefit of SRV's bizarre weighting ++ * stuff.) May be 0 to mean nothing needs to be done. ++ */ ++} typeinfo; ++ ++adns_status adns__qdpl_normal(adns_state ads, ++ const char **p_io, const char *pe, int labelnum, ++ char label_r[], int *ll_io, ++ adns_queryflags flags, ++ const typeinfo *typei); ++ /* implemented in transmit.c, used by types.c as default ++ * and as part of implementation for some fancier types */ ++ ++typedef struct allocnode { ++ struct allocnode *next, *back; ++} allocnode; ++ ++union maxalign { ++ byte d[1]; ++ struct in_addr ia; ++ long l; ++ void *p; ++ void (*fp)(void); ++ union maxalign *up; ++} data; ++ ++typedef struct { ++ void *ext; ++ void (*callback)(adns_query parent, adns_query child); ++ union { ++ adns_rr_addr ptr_parent_addr; ++ adns_rr_hostaddr *hostaddr; ++ } info; ++} qcontext; ++ ++struct adns__query { ++ adns_state ads; ++ enum { query_tosend, query_tcpw, query_childw, query_done } state; ++ adns_query back, next, parent; ++ struct { adns_query head, tail; } children; ++ struct { adns_query back, next; } siblings; ++ struct { allocnode *head, *tail; } allocations; ++ int interim_allocd, preserved_allocd; ++ void *final_allocspace; ++ ++ const typeinfo *typei; ++ byte *query_dgram; ++ int query_dglen; ++ ++ vbuf vb; ++ /* General-purpose messing-about buffer. ++ * Wherever a `big' interface is crossed, this may be corrupted/changed ++ * unless otherwise specified. ++ */ ++ ++ adns_answer *answer; ++ /* This is allocated when a query is submitted, to avoid being unable ++ * to relate errors to queries if we run out of memory. During ++ * query processing status, rrs is 0. cname is set if ++ * we found a cname (this corresponds to cname_dgram in the query ++ * structure). type is set from the word go. nrrs and rrs ++ * are set together, when we find how many rrs there are. ++ * owner is set during querying unless we're doing searchlist, ++ * in which case it is set only when we find an answer. ++ */ ++ ++ byte *cname_dgram; ++ int cname_dglen, cname_begin; ++ /* If non-0, has been allocated using . */ ++ ++ vbuf search_vb; ++ int search_origlen, search_pos, search_doneabs; ++ /* Used by the searching algorithm. The query domain in textual form ++ * is copied into the vbuf, and _origlen set to its length. Then ++ * we walk the searchlist, if we want to. _pos says where we are ++ * (next entry to try), and _doneabs says whether we've done the ++ * absolute query yet (0=not yet, 1=done, -1=must do straight away, ++ * but not done yet). If flags doesn't have adns_qf_search then ++ * the vbuf is initialised but empty and everything else is zero. ++ */ ++ ++ int id, flags, retries; ++ int udpnextserver; ++ unsigned long udpsent; /* bitmap indexed by server */ ++ struct timeval timeout; ++ time_t expires; /* Earliest expiry time of any record we used. */ ++ ++ qcontext ctx; ++ ++ /* Possible states: ++ * ++ * state Queue child id nextudpserver udpsent tcpfailed ++ * ++ * tosend NONE null >=0 0 zero zero ++ * tosend udpw null >=0 any nonzero zero ++ * tosend NONE null >=0 any nonzero zero ++ * ++ * tcpw tcpw null >=0 irrelevant any any ++ * ++ * child childw set >=0 irrelevant irrelevant irrelevant ++ * child NONE null >=0 irrelevant irrelevant irrelevant ++ * done output null -1 irrelevant irrelevant irrelevant ++ * ++ * Queries are only not on a queue when they are actually being processed. ++ * Queries in state tcpw/tcpw have been sent (or are in the to-send buffer) ++ * iff the tcp connection is in state server_ok. ++ * ++ * +------------------------+ ++ * START -----> | tosend/NONE | ++ * +------------------------+ ++ * / |\ \ ++ * too big for UDP / UDP timeout \ \ send via UDP ++ * send via TCP / more retries \ \ ++ * when conn'd / desired \ \ ++ * | | | ++ * v | v ++ * +-----------+ +-------------+ ++ * | tcpw/tcpw | ________ | tosend/udpw | ++ * +-----------+ \ +-------------+ ++ * | | | UDP timeout | | ++ * | | | no more | | ++ * | | | retries | | ++ * \ | TCP died | desired | | ++ * \ \ no more | | | ++ * \ \ servers | TCP / | ++ * \ \ to try | timeout / | ++ * got \ \ v |_ | got ++ * reply \ _| +------------------+ / reply ++ * \ | done/output FAIL | / ++ * \ +------------------+ / ++ * \ / ++ * _| |_ ++ * (..... got reply ....) ++ * / \ ++ * need child query/ies / \ no child query ++ * / \ ++ * |_ _| ++ * +---------------+ +----------------+ ++ * | childw/childw | ----------------> | done/output OK | ++ * +---------------+ children done +----------------+ ++ */ ++}; ++ ++struct query_queue { adns_query head, tail; }; ++ ++struct adns__state { ++ adns_initflags iflags; ++ adns_logcallbackfn *logfn; ++ void *logfndata; ++ int configerrno; ++ struct query_queue udpw, tcpw, childw, output; ++ adns_query forallnext; ++ int nextid, udpsocket, tcpsocket; ++ vbuf tcpsend, tcprecv; ++ int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip; ++ enum adns__tcpstate { ++ server_disconnected, server_connecting, ++ server_ok, server_broken ++ } tcpstate; ++ struct timeval tcptimeout; ++ /* This will have tv_sec==0 if it is not valid. It will always be ++ * valid if tcpstate _connecting. When _ok, it will be nonzero if ++ * we are idle (ie, tcpw queue is empty), in which case it is the ++ * absolute time when we will close the connection. ++ */ ++ struct sigaction stdsigpipe; ++ sigset_t stdsigmask; ++ struct pollfd pollfds_buf[MAX_POLLFDS]; ++ struct server { ++ struct in_addr addr; ++ } servers[MAXSERVERS]; ++ struct sortlist { ++ struct in_addr base, mask; ++ } sortlist[MAXSORTLIST]; ++ char **searchlist; ++ unsigned short rand48xsubi[3]; ++}; ++ ++/* From setup.c: */ ++ ++int adns__setnonblock(adns_state ads, int fd); /* => errno value */ ++ ++/* From general.c: */ ++ ++void adns__vlprintf(adns_state ads, const char *fmt, va_list al); ++void adns__lprintf(adns_state ads, const char *fmt, ++ ...) PRINTFFORMAT(2,3); ++ ++void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent, ++ int serv, adns_query qu, const char *fmt, va_list al); ++ ++void adns__debug(adns_state ads, int serv, adns_query qu, ++ const char *fmt, ...) PRINTFFORMAT(4,5); ++void adns__warn(adns_state ads, int serv, adns_query qu, ++ const char *fmt, ...) PRINTFFORMAT(4,5); ++void adns__diag(adns_state ads, int serv, adns_query qu, ++ const char *fmt, ...) PRINTFFORMAT(4,5); ++ ++int adns__vbuf_ensure(vbuf *vb, int want); ++int adns__vbuf_appendstr(vbuf *vb, const char *data); /* doesn't include nul */ ++int adns__vbuf_append(vbuf *vb, const byte *data, int len); ++/* 1=>success, 0=>realloc failed */ ++void adns__vbuf_appendq(vbuf *vb, const byte *data, int len); ++void adns__vbuf_init(vbuf *vb); ++void adns__vbuf_free(vbuf *vb); ++ ++const char *adns__diag_domain(adns_state ads, int serv, adns_query qu, ++ vbuf *vb, ++ const byte *dgram, int dglen, int cbyte); ++/* Unpicks a domain in a datagram and returns a string suitable for ++ * printing it as. Never fails - if an error occurs, it will ++ * return some kind of string describing the error. ++ * ++ * serv may be -1 and qu may be 0. vb must have been initialised, ++ * and will be left in an arbitrary consistent state. ++ * ++ * Returns either vb->buf, or a pointer to a string literal. Do not modify ++ * vb before using the return value. ++ */ ++ ++void adns__isort(void *array, int nobjs, int sz, void *tempbuf, ++ int (*needswap)(void *context, const void *a, const void *b), ++ void *context); ++/* Does an insertion sort of array which must contain nobjs objects ++ * each sz bytes long. tempbuf must point to a buffer at least ++ * sz bytes long. needswap should return !0 if a>b (strictly, ie ++ * wrong order) 0 if a<=b (ie, order is fine). ++ */ ++ ++void adns__sigpipe_protect(adns_state); ++void adns__sigpipe_unprotect(adns_state); ++/* If SIGPIPE protection is not disabled, will block all signals except ++ * SIGPIPE, and set SIGPIPE's disposition to SIG_IGN. (And then restore.) ++ * Each call to _protect must be followed by a call to _unprotect before ++ * any significant amount of code gets to run, since the old signal mask ++ * is stored in the adns structure. ++ */ ++ ++/* From transmit.c: */ ++ ++adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r, ++ const char *owner, int ol, ++ const typeinfo *typei, adns_rrtype type, ++ adns_queryflags flags); ++/* Assembles a query packet in vb. A new id is allocated and returned. ++ */ ++ ++adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r, ++ const byte *qd_dgram, int qd_dglen, ++ int qd_begin, ++ adns_rrtype type, adns_queryflags flags); ++/* Same as adns__mkquery, but takes the owner domain from an existing datagram. ++ * That domain must be correct and untruncated. ++ */ ++ ++void adns__querysend_tcp(adns_query qu, struct timeval now); ++/* Query must be in state tcpw/tcpw; it will be sent if possible and ++ * no further processing can be done on it for now. The connection ++ * might be broken, but no reconnect will be attempted. ++ */ ++ ++void adns__query_send(adns_query qu, struct timeval now); ++/* Query must be in state tosend/NONE; it will be moved to a new state, ++ * and no further processing can be done on it for now. ++ * (Resulting state is one of udp/timew, tcpwait/timew (if server not ++ * connected), tcpsent/timew, child/childw or done/output.) ++ * __query_send may decide to use either UDP or TCP depending whether ++ * _qf_usevc is set (or has become set) and whether the query is too ++ * large. ++ */ ++ ++/* From query.c: */ ++ ++adns_status adns__internal_submit(adns_state ads, adns_query *query_r, ++ const typeinfo *typei, vbuf *qumsg_vb, ++ int id, ++ adns_queryflags flags, struct timeval now, ++ const qcontext *ctx); ++/* Submits a query (for internal use, called during external submits). ++ * ++ * The new query is returned in *query_r, or we return adns_s_nomemory. ++ * ++ * The query datagram should already have been assembled in qumsg_vb; ++ * the memory for it is _taken over_ by this routine whether it ++ * succeeds or fails (if it succeeds, the vbuf is reused for qu->vb). ++ * ++ * *ctx is copied byte-for-byte into the query. ++ * ++ * When the child query is done, ctx->callback will be called. The ++ * child will already have been taken off both the global list of ++ * queries in ads and the list of children in the parent. The child ++ * will be freed when the callback returns. The parent will have been ++ * taken off the global childw queue. ++ * ++ * The callback should either call adns__query_done, if it is ++ * complete, or adns__query_fail, if an error has occurred, in which ++ * case the other children (if any) will be cancelled. If the parent ++ * has more unfinished children (or has just submitted more) then the ++ * callback may choose to wait for them - it must then put the parent ++ * back on the childw queue. ++ */ ++ ++void adns__search_next(adns_state ads, adns_query qu, struct timeval now); ++/* Walks down the searchlist for a query with adns_qf_search. ++ * The query should have just had a negative response, or not had ++ * any queries sent yet, and should not be on any queue. ++ * The query_dgram if any will be freed and forgotten and a new ++ * one constructed from the search_* members of the query. ++ * ++ * Cannot fail (in case of error, calls adns__query_fail). ++ */ ++ ++void *adns__alloc_interim(adns_query qu, size_t sz); ++void *adns__alloc_preserved(adns_query qu, size_t sz); ++/* Allocates some memory, and records which query it came from ++ * and how much there was. ++ * ++ * If an error occurs in the query, all the memory from _interim is ++ * simply freed. If the query succeeds, one large buffer will be made ++ * which is big enough for all these allocations, and then ++ * adns__alloc_final will get memory from this buffer. ++ * ++ * _alloc_interim can fail (and return 0). ++ * The caller must ensure that the query is failed. ++ * ++ * The memory from _preserved is is kept and transferred into the ++ * larger buffer - unless we run out of memory, in which case it too ++ * is freed. When you use _preserved you have to add code to the ++ * x_nomem error exit case in adns__makefinal_query to clear out the ++ * pointers you made to those allocations, because that's when they're ++ * thrown away; you should also make a note in the declaration of ++ * those pointer variables, to note that they are _preserved rather ++ * than _interim. If they're in the answer, note it here: ++ * answer->cname and answer->owner are _preserved. ++ */ ++ ++void adns__transfer_interim(adns_query from, adns_query to, ++ void *block, size_t sz); ++/* Transfers an interim allocation from one query to another, so that ++ * the `to' query will have room for the data when we get to makefinal ++ * and so that the free will happen when the `to' query is freed ++ * rather than the `from' query. ++ * ++ * It is legal to call adns__transfer_interim with a null pointer; this ++ * has no effect. ++ * ++ * _transfer_interim also ensures that the expiry time of the `to' query ++ * is no later than that of the `from' query, so that child queries' ++ * TTLs get inherited by their parents. ++ */ ++ ++void *adns__alloc_mine(adns_query qu, size_t sz); ++/* Like _interim, but does not record the length for later ++ * copying into the answer. This just ensures that the memory ++ * will be freed when we're done with the query. ++ */ ++ ++void *adns__alloc_final(adns_query qu, size_t sz); ++/* Cannot fail, and cannot return 0. ++ */ ++ ++void adns__makefinal_block(adns_query qu, void **blpp, size_t sz); ++void adns__makefinal_str(adns_query qu, char **strp); ++ ++void adns__reset_preserved(adns_query qu); ++/* Resets all of the memory management stuff etc. to take account of ++ * only the _preserved stuff from _alloc_preserved. Used when we find ++ * an error somewhere and want to just report the error (with perhaps ++ * CNAME, owner, etc. info), and also when we're halfway through RRs ++ * in a datagram and discover that we need to retry the query. ++ */ ++ ++void adns__query_done(adns_query qu); ++void adns__query_fail(adns_query qu, adns_status stat); ++ ++/* From reply.c: */ ++ ++void adns__procdgram(adns_state ads, const byte *dgram, int len, ++ int serv, int viatcp, struct timeval now); ++/* This function is allowed to cause new datagrams to be constructed ++ * and sent, or even new queries to be started. However, ++ * query-sending functions are not allowed to call any general event ++ * loop functions in case they accidentally call this. ++ * ++ * Ie, receiving functions may call sending functions. ++ * Sending functions may NOT call receiving functions. ++ */ ++ ++/* From types.c: */ ++ ++const typeinfo *adns__findtype(adns_rrtype type); ++ ++/* From parse.c: */ ++ ++typedef struct { ++ adns_state ads; ++ adns_query qu; ++ int serv; ++ const byte *dgram; ++ int dglen, max, cbyte, namelen; ++ int *dmend_r; ++} findlabel_state; ++ ++void adns__findlabel_start(findlabel_state *fls, adns_state ads, ++ int serv, adns_query qu, ++ const byte *dgram, int dglen, int max, ++ int dmbegin, int *dmend_rlater); ++/* Finds labels in a domain in a datagram. ++ * ++ * Call this routine first. ++ * dmend_rlater may be null. ads (and of course fls) may not be. ++ * serv may be -1, qu may be null - they are for error reporting. ++ */ ++ ++adns_status adns__findlabel_next(findlabel_state *fls, ++ int *lablen_r, int *labstart_r); ++/* Then, call this one repeatedly. ++ * ++ * It will return adns_s_ok if all is well, and tell you the length ++ * and start of successive labels. labstart_r may be null, but ++ * lablen_r must not be. ++ * ++ * After the last label, it will return with *lablen_r zero. ++ * Do not then call it again; instead, just throw away the findlabel_state. ++ * ++ * *dmend_rlater will have been set to point to the next part of ++ * the datagram after the label (or after the uncompressed part, ++ * if compression was used). *namelen_rlater will have been set ++ * to the length of the domain name (total length of labels plus ++ * 1 for each intervening dot). ++ * ++ * If the datagram appears to be truncated, *lablen_r will be -1. ++ * *dmend_rlater, *labstart_r and *namelen_r may contain garbage. ++ * Do not call _next again. ++ * ++ * There may also be errors, in which case *dmend_rlater, ++ * *namelen_rlater, *lablen_r and *labstart_r may contain garbage. ++ * Do not then call findlabel_next again. ++ */ ++ ++typedef enum { ++ pdf_quoteok= 0x001 ++} parsedomain_flags; ++ ++adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu, ++ vbuf *vb, parsedomain_flags flags, ++ const byte *dgram, int dglen, int *cbyte_io, ++ int max); ++/* vb must already have been initialised; it will be reset if necessary. ++ * If there is truncation, vb->used will be set to 0; otherwise ++ * (if there is no error) vb will be null-terminated. ++ * If there is an error vb and *cbyte_io may be left indeterminate. ++ * ++ * serv may be -1 and qu may be 0 - they are used for error reporting only. ++ */ ++ ++adns_status adns__parse_domain_more(findlabel_state *fls, adns_state ads, ++ adns_query qu, vbuf *vb, ++ parsedomain_flags flags, ++ const byte *dgram); ++/* Like adns__parse_domain, but you pass it a pre-initialised findlabel_state, ++ * for continuing an existing domain or some such of some kind. Also, unlike ++ * _parse_domain, the domain data will be appended to vb, rather than replacing ++ * the existing contents. ++ */ ++ ++adns_status adns__findrr(adns_query qu, int serv, ++ const byte *dgram, int dglen, int *cbyte_io, ++ int *type_r, int *class_r, unsigned long *ttl_r, ++ int *rdlen_r, int *rdstart_r, ++ int *ownermatchedquery_r); ++/* Finds the extent and some of the contents of an RR in a datagram ++ * and does some checks. The datagram is *dgram, length dglen, and ++ * the RR starts at *cbyte_io (which is updated afterwards to point ++ * to the end of the RR). ++ * ++ * The type, class, TTL and RRdata length and start are returned iff ++ * the corresponding pointer variables are not null. type_r, class_r ++ * and ttl_r may not be null. The TTL will be capped. ++ * ++ * If ownermatchedquery_r != 0 then the owner domain of this ++ * RR will be compared with that in the query (or, if the query ++ * has gone to a CNAME lookup, with the canonical name). ++ * In this case, *ownermatchedquery_r will be set to 0 or 1. ++ * The query datagram (or CNAME datagram) MUST be valid and not truncated. ++ * ++ * If there is truncation then *type_r will be set to -1 and ++ * *cbyte_io, *class_r, *rdlen_r, *rdstart_r and *eo_matched_r will be ++ * undefined. ++ * ++ * qu must obviously be non-null. ++ * ++ * If an error is returned then *type_r will be undefined too. ++ */ ++ ++adns_status adns__findrr_anychk(adns_query qu, int serv, ++ const byte *dgram, int dglen, int *cbyte_io, ++ int *type_r, int *class_r, ++ unsigned long *ttl_r, ++ int *rdlen_r, int *rdstart_r, ++ const byte *eo_dgram, int eo_dglen, ++ int eo_cbyte, int *eo_matched_r); ++/* Like adns__findrr_checked, except that the datagram and ++ * owner to compare with can be specified explicitly. ++ * ++ * If the caller thinks they know what the owner of the RR ought to ++ * be they can pass in details in eo_*: this is another (or perhaps ++ * the same datagram), and a pointer to where the putative owner ++ * starts in that datagram. In this case *eo_matched_r will be set ++ * to 1 if the datagram matched or 0 if it did not. Either ++ * both eo_dgram and eo_matched_r must both be non-null, or they ++ * must both be null (in which case eo_dglen and eo_cbyte will be ignored). ++ * The eo datagram and contained owner domain MUST be valid and ++ * untruncated. ++ */ ++ ++void adns__update_expires(adns_query qu, unsigned long ttl, ++ struct timeval now); ++/* Updates the `expires' field in the query, so that it doesn't exceed ++ * now + ttl. ++ */ ++ ++int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len); ++ ++/* From event.c: */ ++ ++void adns__tcp_broken(adns_state ads, const char *what, const char *why); ++/* what and why may be both 0, or both non-0. */ ++ ++void adns__tcp_tryconnect(adns_state ads, struct timeval now); ++ ++void adns__autosys(adns_state ads, struct timeval now); ++/* Make all the system calls we want to if the application wants us to. ++ * Must not be called from within adns internal processing functions, ++ * lest we end up in recursive descent ! ++ */ ++ ++void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, ++ struct timeval *tv_buf); ++ ++int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]); ++void adns__fdevents(adns_state ads, ++ const struct pollfd *pollfds, int npollfds, ++ int maxfd, const fd_set *readfds, ++ const fd_set *writefds, const fd_set *exceptfds, ++ struct timeval now, int *r_r); ++int adns__internal_check(adns_state ads, ++ adns_query *query_io, ++ adns_answer **answer, ++ void **context_r); ++ ++void adns__timeouts(adns_state ads, int act, ++ struct timeval **tv_io, struct timeval *tvbuf, ++ struct timeval now); ++/* If act is !0, then this will also deal with the TCP connection ++ * if previous events broke it or require it to be connected. ++ */ ++ ++/* From check.c: */ ++ ++void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc); ++ ++/* Useful static inline functions: */ ++ ++static inline int ctype_whitespace(int c) { ++ return c==' ' || c=='\n' || c=='\t'; ++} ++static inline int ctype_digit(int c) { return c>='0' && c<='9'; } ++static inline int ctype_alpha(int c) { ++ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); ++} ++static inline int ctype_822special(int c) { ++ return strchr("()<>@,;:\\\".[]",c) != 0; ++} ++static inline int ctype_domainunquoted(int c) { ++ return ctype_alpha(c) || ctype_digit(c) || (strchr("-_/+",c) != 0); ++} ++ ++static inline int errno_resources(int e) { return e==ENOMEM || e==ENOBUFS; } ++ ++/* Useful macros */ ++ ++#define MEM_ROUND(sz) \ ++ (( ((sz)+sizeof(union maxalign)-1) / sizeof(union maxalign) ) \ ++ * sizeof(union maxalign) ) ++ ++#define GETIL_B(cb) (((dgram)[(cb)++]) & 0x0ff) ++#define GET_B(cb,tv) ((tv)= GETIL_B((cb))) ++#define GET_W(cb,tv) ((tv)=0,(tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv)) ++#define GET_L(cb,tv) ( (tv)=0, \ ++ (tv)|=(GETIL_B((cb))<<24), \ ++ (tv)|=(GETIL_B((cb))<<16), \ ++ (tv)|=(GETIL_B((cb))<<8), \ ++ (tv)|=GETIL_B(cb), \ ++ (tv) ) ++ ++#endif +diff -Naur adns-1.4.org/src/query.c adns-1.4/src/query.c +--- adns-1.4.org/src/query.c 2013-01-02 19:55:38.931192468 +0200 ++++ adns-1.4/src/query.c 2013-01-02 19:55:47.131331642 +0200 +@@ -63,6 +63,8 @@ + + qu->cname_dgram= 0; + qu->cname_dglen= qu->cname_begin= 0; ++ /* Allow CNAME chains up to some sane limit */ ++ qu->cname_alias_hops_left = 10; + + adns__vbuf_init(&qu->search_vb); + qu->search_origlen= qu->search_pos= qu->search_doneabs= 0; +diff -Naur adns-1.4.org/src/reply.c adns-1.4/src/reply.c +--- adns-1.4.org/src/reply.c 2013-01-02 19:55:38.936195603 +0200 ++++ adns-1.4/src/reply.c 2013-01-02 19:55:47.134333524 +0200 +@@ -190,12 +190,13 @@ + if (qu->flags & adns_qf_cname_forbid) { + adns__query_fail(qu,adns_s_prohibitedcname); + return; +- } else if (qu->cname_dgram) { /* Ignore second and subsequent CNAME(s) */ ++ } else if (qu->cname_dgram && --(qu->cname_alias_hops_left) <= 0) { /* Don't follow "too long" CNAME chains */ + adns__debug(ads,serv,qu,"allegedly canonical name %s" +- " is actually alias for %s", qu->answer->cname, ++ " is actually alias for %s and aliases too deep", ++ qu->answer->cname, + adns__diag_domain(ads,serv,qu, &qu->vb, + dgram,dglen,rdstart)); +- adns__query_fail(qu,adns_s_prohibitedcname); ++ adns__query_fail(qu,adns_s_norecurse); + return; + } else if (wantedrrs) { /* Ignore CNAME(s) after RR(s). */ + adns__debug(ads,serv,qu,"ignoring CNAME (to %s) coexisting with RR", +diff -Naur adns-1.4.org/src/reply.c.orig adns-1.4/src/reply.c.orig +--- adns-1.4.org/src/reply.c.orig 1970-01-01 02:00:00.000000000 +0200 ++++ adns-1.4/src/reply.c.orig 2006-04-08 17:36:57.000000000 +0300 +@@ -0,0 +1,390 @@ ++/* ++ * reply.c ++ * - main handling and parsing routine for received datagrams ++ */ ++/* ++ * This file is part of adns, which is ++ * Copyright (C) 1997-2000,2003,2006 Ian Jackson ++ * Copyright (C) 1999-2000,2003,2006 Tony Finch ++ * Copyright (C) 1991 Massachusetts Institute of Technology ++ * (See the file INSTALL for full details.) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#include ++ ++#include "internal.h" ++ ++void adns__procdgram(adns_state ads, const byte *dgram, int dglen, ++ int serv, int viatcp, struct timeval now) { ++ int cbyte, rrstart, wantedrrs, rri, foundsoa, foundns, cname_here; ++ int id, f1, f2, qdcount, ancount, nscount, arcount; ++ int flg_ra, flg_rd, flg_tc, flg_qr, opcode; ++ int rrtype, rrclass, rdlength, rdstart; ++ int anstart, nsstart, arstart; ++ int ownermatched, l, nrrs; ++ unsigned long ttl, soattl; ++ const typeinfo *typei; ++ adns_query qu, nqu; ++ dns_rcode rcode; ++ adns_status st; ++ vbuf tempvb; ++ byte *newquery, *rrsdata; ++ parseinfo pai; ++ ++ if (dglen>3; ++ flg_tc= f1&0x02; ++ flg_rd= f1&0x01; ++ flg_ra= f2&0x80; ++ rcode= (f2&0x0f); ++ ++ cname_here= 0; ++ ++ if (!flg_qr) { ++ adns__diag(ads,serv,0,"server sent us a query, not a response"); ++ return; ++ } ++ if (opcode) { ++ adns__diag(ads,serv,0,"server sent us unknown opcode" ++ " %d (wanted 0=QUERY)",opcode); ++ return; ++ } ++ ++ qu= 0; ++ /* See if we can find the relevant query, or leave qu=0 otherwise ... */ ++ ++ if (qdcount == 1) { ++ for (qu= viatcp ? ads->tcpw.head : ads->udpw.head; qu; qu= nqu) { ++ nqu= qu->next; ++ if (qu->id != id) continue; ++ if (dglen < qu->query_dglen) continue; ++ if (memcmp(qu->query_dgram+DNS_HDRSIZE, ++ dgram+DNS_HDRSIZE, ++ qu->query_dglen-DNS_HDRSIZE)) ++ continue; ++ if (viatcp) { ++ assert(qu->state == query_tcpw); ++ } else { ++ assert(qu->state == query_tosend); ++ if (!(qu->udpsent & (1<tcpw,qu); ++ else LIST_UNLINK(ads->udpw,qu); ++ } ++ } ++ ++ /* If we're going to ignore the packet, we return as soon as we have ++ * failed the query (if any) and printed the warning message (if ++ * any). ++ */ ++ switch (rcode) { ++ case rcode_noerror: ++ case rcode_nxdomain: ++ break; ++ case rcode_formaterror: ++ adns__warn(ads,serv,qu,"server cannot understand our query" ++ " (Format Error)"); ++ if (qu) adns__query_fail(qu,adns_s_rcodeformaterror); ++ return; ++ case rcode_servfail: ++ if (qu) adns__query_fail(qu,adns_s_rcodeservfail); ++ else adns__debug(ads,serv,qu,"server failure on unidentifiable query"); ++ return; ++ case rcode_notimp: ++ adns__warn(ads,serv,qu,"server claims not to implement our query"); ++ if (qu) adns__query_fail(qu,adns_s_rcodenotimplemented); ++ return; ++ case rcode_refused: ++ adns__debug(ads,serv,qu,"server refused our query"); ++ if (qu) adns__query_fail(qu,adns_s_rcoderefused); ++ return; ++ default: ++ adns__warn(ads,serv,qu,"server gave unknown response code %d",rcode); ++ if (qu) adns__query_fail(qu,adns_s_rcodeunknown); ++ return; ++ } ++ ++ if (!qu) { ++ if (!qdcount) { ++ adns__diag(ads,serv,0,"server sent reply without quoting our question"); ++ } else if (qdcount>1) { ++ adns__diag(ads,serv,0,"server claimed to answer %d" ++ " questions with one message", qdcount); ++ } else if (ads->iflags & adns_if_debug) { ++ adns__vbuf_init(&tempvb); ++ adns__debug(ads,serv,0,"reply not found, id %02x, query owner %s", ++ id, adns__diag_domain(ads,serv,0,&tempvb, ++ dgram,dglen,DNS_HDRSIZE)); ++ adns__vbuf_free(&tempvb); ++ } ++ return; ++ } ++ ++ /* We're definitely going to do something with this packet and this ++ * query now. */ ++ ++ anstart= qu->query_dglen; ++ arstart= -1; ++ ++ /* Now, take a look at the answer section, and see if it is complete. ++ * If it has any CNAMEs we stuff them in the answer. ++ */ ++ wantedrrs= 0; ++ cbyte= anstart; ++ for (rri= 0; rriiflags & adns_if_debug) { ++ adns__debug(ads,serv,qu,"ignoring RR with an unexpected owner %s", ++ adns__diag_domain(ads,serv,qu, &qu->vb, ++ dgram,dglen,rrstart)); ++ } ++ continue; ++ } ++ if (rrtype == adns_r_cname && ++ (qu->answer->type & adns_rrt_typemask) != adns_r_cname) { ++ if (qu->flags & adns_qf_cname_forbid) { ++ adns__query_fail(qu,adns_s_prohibitedcname); ++ return; ++ } else if (qu->cname_dgram) { /* Ignore second and subsequent CNAME(s) */ ++ adns__debug(ads,serv,qu,"allegedly canonical name %s" ++ " is actually alias for %s", qu->answer->cname, ++ adns__diag_domain(ads,serv,qu, &qu->vb, ++ dgram,dglen,rdstart)); ++ adns__query_fail(qu,adns_s_prohibitedcname); ++ return; ++ } else if (wantedrrs) { /* Ignore CNAME(s) after RR(s). */ ++ adns__debug(ads,serv,qu,"ignoring CNAME (to %s) coexisting with RR", ++ adns__diag_domain(ads,serv,qu, &qu->vb, ++ dgram,dglen,rdstart)); ++ } else { ++ qu->cname_begin= rdstart; ++ qu->cname_dglen= dglen; ++ st= adns__parse_domain(ads,serv,qu, &qu->vb, ++ qu->flags & adns_qf_quotefail_cname ++ ? 0 : pdf_quoteok, ++ dgram,dglen, &rdstart,rdstart+rdlength); ++ if (!qu->vb.used) goto x_truncated; ++ if (st) { adns__query_fail(qu,st); return; } ++ l= strlen(qu->vb.buf)+1; ++ qu->answer->cname= adns__alloc_preserved(qu,l); ++ if (!qu->answer->cname) { ++ adns__query_fail(qu,adns_s_nomemory); ++ return; ++ } ++ ++ qu->cname_dgram= adns__alloc_mine(qu,dglen); ++ memcpy(qu->cname_dgram,dgram,dglen); ++ ++ memcpy(qu->answer->cname,qu->vb.buf,l); ++ cname_here= 1; ++ adns__update_expires(qu,ttl,now); ++ /* If we find the answer section truncated after this point we restart ++ * the query at the CNAME; if beforehand then we obviously have to use ++ * TCP. If there is no truncation we can use the whole answer if ++ * it contains the relevant info. ++ */ ++ } ++ } else if (rrtype == (qu->answer->type & adns_rrt_typemask)) { ++ wantedrrs++; ++ } else { ++ adns__debug(ads,serv,qu,"ignoring answer RR" ++ " with irrelevant type %d",rrtype); ++ } ++ } ++ ++ /* We defer handling truncated responses here, in case there was a CNAME ++ * which we could use. ++ */ ++ if (flg_tc) goto x_truncated; ++ ++ nsstart= cbyte; ++ ++ if (!wantedrrs) { ++ /* Oops, NODATA or NXDOMAIN or perhaps a referral ++ * (which would be a problem) */ ++ ++ /* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records ++ * in authority section */ ++ foundsoa= 0; soattl= 0; foundns= 0; ++ for (rri= 0; rriflags & adns_qf_search && !qu->cname_dgram) { ++ adns__search_next(ads,qu,now); ++ } else { ++ adns__query_fail(qu,adns_s_nxdomain); ++ } ++ return; ++ } ++ ++ if (foundsoa || !foundns) { ++ /* Aha ! A NODATA response, good. */ ++ adns__update_expires(qu,soattl,now); ++ adns__query_fail(qu,adns_s_nodata); ++ return; ++ } ++ ++ /* Now what ? No relevant answers, no SOA, and at least some NS's. ++ * Looks like a referral. Just one last chance ... if we came across ++ * a CNAME in this datagram then we should probably do our own CNAME ++ * lookup now in the hope that we won't get a referral again. ++ */ ++ if (cname_here) goto x_restartquery; ++ ++ /* Bloody hell, I thought we asked for recursion ? */ ++ if (!flg_ra) { ++ adns__diag(ads,serv,qu,"server is not willing" ++ " to do recursive lookups for us"); ++ adns__query_fail(qu,adns_s_norecurse); ++ } else { ++ if (!flg_rd) ++ adns__diag(ads,serv,qu,"server thinks" ++ " we didn't ask for recursive lookup"); ++ else ++ adns__debug(ads,serv,qu,"server claims to do recursion," ++ " but gave us a referral"); ++ adns__query_fail(qu,adns_s_invalidresponse); ++ } ++ return; ++ } ++ ++ /* Now, we have some RRs which we wanted. */ ++ ++ qu->answer->rrs.untyped= adns__alloc_interim(qu,qu->typei->rrsz*wantedrrs); ++ if (!qu->answer->rrs.untyped) { ++ adns__query_fail(qu,adns_s_nomemory); ++ return; ++ } ++ ++ typei= qu->typei; ++ cbyte= anstart; ++ rrsdata= qu->answer->rrs.bytes; ++ ++ pai.ads= qu->ads; ++ pai.qu= qu; ++ pai.serv= serv; ++ pai.dgram= dgram; ++ pai.dglen= dglen; ++ pai.nsstart= nsstart; ++ pai.nscount= nscount; ++ pai.arcount= arcount; ++ pai.now= now; ++ ++ for (rri=0, nrrs=0; rrianswer->type & adns_rrt_typemask) || ++ !ownermatched) ++ continue; ++ adns__update_expires(qu,ttl,now); ++ st= typei->parse(&pai, rdstart,rdstart+rdlength, rrsdata+nrrs*typei->rrsz); ++ if (st) { adns__query_fail(qu,st); return; } ++ if (rdstart==-1) goto x_truncated; ++ nrrs++; ++ } ++ assert(nrrs==wantedrrs); ++ qu->answer->nrrs= nrrs; ++ ++ /* This may have generated some child queries ... */ ++ if (qu->children.head) { ++ qu->state= query_childw; ++ LIST_LINK_TAIL(ads->childw,qu); ++ return; ++ } ++ adns__query_done(qu); ++ return; ++ ++ x_truncated: ++ ++ if (!flg_tc) { ++ adns__diag(ads,serv,qu,"server sent datagram which points outside itself"); ++ adns__query_fail(qu,adns_s_invalidresponse); ++ return; ++ } ++ qu->flags |= adns_qf_usevc; ++ ++ x_restartquery: ++ if (qu->cname_dgram) { ++ st= adns__mkquery_frdgram(qu->ads,&qu->vb,&qu->id, ++ qu->cname_dgram,qu->cname_dglen,qu->cname_begin, ++ qu->answer->type, qu->flags); ++ if (st) { adns__query_fail(qu,st); return; } ++ ++ newquery= realloc(qu->query_dgram,qu->vb.used); ++ if (!newquery) { adns__query_fail(qu,adns_s_nomemory); return; } ++ ++ qu->query_dgram= newquery; ++ qu->query_dglen= qu->vb.used; ++ memcpy(newquery,qu->vb.buf,qu->vb.used); ++ } ++ ++ if (qu->state == query_tcpw) qu->state= query_tosend; ++ qu->retries= 0; ++ adns__reset_preserved(qu); ++ adns__query_send(qu,now); ++} + +--pf9I7BMVVzbSWLtt-- diff --git a/package/adns/adns-1.4-destdir.patch b/package/adns/adns-1.4-destdir.patch new file mode 100644 index 0000000..3270ed4 --- /dev/null +++ b/package/adns/adns-1.4-destdir.patch @@ -0,0 +1,54 @@ +Fix installation directory +Upstream-Status: Pending + +diff -Naur adns-1.4.org/client/Makefile.in adns-1.4/client/Makefile.in +--- adns-1.4.org/client/Makefile.in 2013-01-02 20:43:31.157268223 +0200 ++++ adns-1.4/client/Makefile.in 2013-01-02 20:53:11.831761446 +0200 +@@ -58,9 +58,9 @@ + all: $(TARGETS) + + install: $(TARG_INSTALL) +- mkdir -p $(bindir) ++ $(INSTALL_PROGRAM) -d $(DESTDIR)/$(bindir) + set -xe; for f in $(TARG_INSTALL); \ +- do $(INSTALL_PROGRAM) $$f $(bindir)/$$f; done ++ do $(INSTALL_PROGRAM) $$f $(DESTDIR)/$(bindir)/$$f; done + + uninstall: + for f in $(TARGETS); do rm -f $(bindir)/$$f; done +diff -Naur adns-1.4.org/dynamic/Makefile.in adns-1.4/dynamic/Makefile.in +--- adns-1.4.org/dynamic/Makefile.in 2013-01-02 20:43:31.164272612 +0200 ++++ adns-1.4/dynamic/Makefile.in 2013-01-02 20:54:42.702711906 +0200 +@@ -30,10 +30,10 @@ + ALLOBJS= $(addsuffix _p.o, $(basename $(LIBOBJS))) + + install: +- mkdir -p $(libdir) +- $(INSTALL_PROGRAM) $(SHLIBFILE) $(libdir)/$(SHLIBFILE) +- ln -sf $(SHLIBFILE) $(libdir)/$(SHLIBSONAME) +- ln -sf $(SHLIBSONAME) $(libdir)/$(SHLIBFORLINK) ++ $(INSTALL_PROGRAM) -d $(DESTDIR)/$(libdir) ++ $(INSTALL_PROGRAM) $(SHLIBFILE) $(DESTDIR)/$(libdir)/$(SHLIBFILE) ++ ln -sf $(SHLIBFILE) $(DESTDIR)/$(libdir)/$(SHLIBSONAME) ++ ln -sf $(SHLIBSONAME) $(DESTDIR)/$(libdir)/$(SHLIBFORLINK) + + uninstall: + rm -f $(libdir)/$(SHLIBFILE) $(libdir)/$(SHLIBSONAME) +diff -Naur adns-1.4.org/src/Makefile.in adns-1.4/src/Makefile.in +--- adns-1.4.org/src/Makefile.in 2013-01-02 20:43:31.162271358 +0200 ++++ adns-1.4/src/Makefile.in 2013-01-02 20:56:31.669003041 +0200 +@@ -28,10 +28,11 @@ + include adns.make + + install: +- mkdir -p $(libdir) $(includedir) ++ $(INSTALL_PROGRAM) -d $(DESTDIR)/$(libdir) ++ $(INSTALL_PROGRAM) -d $(DESTDIR)/$(includedir) + set -xe; for f in $(TARGETS); \ +- do $(INSTALL_DATA) $$f $(libdir)/$$f; done +- $(INSTALL_DATA) $(srcdir)/../src/adns.h $(includedir)/adns.h ++ do $(INSTALL_DATA) $$f $(DESTDIR)/$(libdir)/$$f; done ++ $(INSTALL_DATA) $(srcdir)/../src/adns.h $(DESTDIR)/$(includedir)/adns.h + + uninstall: + for f in $(TARGETS); do rm -f $(libdir)/$$f; done diff --git a/package/adns/adns-1.4-ipv6.patch b/package/adns/adns-1.4-ipv6.patch new file mode 100644 index 0000000..8d02257 --- /dev/null +++ b/package/adns/adns-1.4-ipv6.patch @@ -0,0 +1,2725 @@ +Add IPv6 support +Upstream-Status: Pending + +diff --git a/Makefile b/Makefile +index 5ba3e19..a87bc87 100644 +--- a/Makefile ++++ b/Makefile +@@ -56,9 +56,9 @@ dist_tmp=dist_tmp/adns-$(DISTVERSION) + dist: distprep + rm -rf dist_tmp* + mkdir dist_tmp $(dist_tmp) +- find \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \ ++ find . \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \ + sed -e 's#.*#mkdir -p $(dist_tmp)/&#' | sh +- find \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \ ++ find . \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \ + sed -e 's#.*#ln & $(dist_tmp)/&#' | sh + $(MAKE) -C dist_tmp/adns-$(DISTVERSION) distclean + cd dist_tmp && tar cf ../$(dist_tmp).tar `basename $(dist_tmp)` +diff --git a/Makefile.in b/Makefile.in +index 6e2e449..0babf0e 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -56,9 +56,9 @@ dist_tmp=dist_tmp/adns-$(DISTVERSION) + dist: distprep + rm -rf dist_tmp* + mkdir dist_tmp $(dist_tmp) +- find \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \ ++ find . \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \ + sed -e 's#.*#mkdir -p $(dist_tmp)/&#' | sh +- find \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \ ++ find . \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \ + sed -e 's#.*#ln & $(dist_tmp)/&#' | sh + $(MAKE) -C dist_tmp/adns-$(DISTVERSION) distclean + cd dist_tmp && tar cf ../$(dist_tmp).tar `basename $(dist_tmp)` +diff --git a/client/adh-main.c b/client/adh-main.c +index b6f3bd4..f2032ec 100644 +--- a/client/adh-main.c ++++ b/client/adh-main.c +@@ -91,6 +91,7 @@ void of_type(const struct optioninfo *oi, const char *arg, const char *arg2) { + { adns_r_rp, "rp" }, + { adns_r_srv, "srv" }, + { adns_r_addr, "addr" }, ++ { adns_r_srv, "srv" }, + + /* types with only one version */ + { adns_r_cname, "cname" }, +@@ -99,6 +100,7 @@ void of_type(const struct optioninfo *oi, const char *arg, const char *arg2) { + + /* raw versions */ + { adns_r_a, "a" }, ++ { adns_r_aaaa, "aaaa" }, + { adns_r_ns_raw, "ns-" }, + { adns_r_soa_raw, "soa-" }, + { adns_r_ptr_raw, "ptr-" }, +diff --git a/client/adh-opts.c b/client/adh-opts.c +index 08310e0..7b17c89 100644 +--- a/client/adh-opts.c ++++ b/client/adh-opts.c +@@ -32,6 +32,8 @@ int ov_verbose= 0; + adns_rrtype ov_type= adns_r_none; + int ov_search=0, ov_qc_query=0, ov_qc_anshost=0, ov_qc_cname=1; + int ov_tcp=0, ov_cname=0, ov_format=fmt_default; ++int ov_ipflags=0; ++int ov_ip6mapped=0; + char *ov_id= 0; + struct perqueryflags_remember ov_pqfr = { 1,1,1, tm_none }; + +@@ -114,6 +116,16 @@ static const struct optioninfo perquery_options[]= { + { ot_value, "CNAME ok for query domain, but not in RRs (default)", + "Cs", "cname-ok", &ov_cname, 0 }, + ++ { ot_desconly, "per-query IPv6 mode:" }, ++ { ot_value, "Ask only for IPv6 addresses", ++ "I6", "ip6-only", &ov_ipflags, adns_qf_ip6 }, ++ { ot_value, "Ask only for IPv4 addresses", ++ "I4", "ip4-only", &ov_ipflags, adns_qf_ip4 }, ++ { ot_value, "Ask for both IPv4 and IPv6 addresses (default)", ++ "IX", "ipv6-mixed", &ov_ipflags, adns_qf_ip4|adns_qf_ip6 }, ++ { ot_value, "Ask for both IPv4 and IPv6 addresses, using IPv4-mapped IPv6 addresses", ++ "IM", "ipv6-mapped", &ov_ip6mapped, adns_qf_ip6mapped }, ++ + { ot_desconly, "asynchronous/pipe mode options:" }, + { ot_funcarg, "Set , default is decimal sequence starting 0", + 0, "asynch-id", 0,0, &of_asynch_id, "id" }, +diff --git a/client/adh-query.c b/client/adh-query.c +index 125bb33..2186004 100644 +--- a/client/adh-query.c ++++ b/client/adh-query.c +@@ -92,24 +92,37 @@ static void prep_query(struct query_node **qun_r, int *quflags_r) { + (ov_qc_query ? adns_qf_quoteok_query : 0) | + (ov_qc_anshost ? adns_qf_quoteok_anshost : 0) | + (ov_qc_cname ? 0 : adns_qf_quoteok_cname) | ++ ov_ipflags | ov_ip6mapped | + ov_cname, + + *qun_r= qun; + } + ++static int a2addr(adns_rr_addr *rr, const char *addr) { ++ char *p; ++ if (strchr(addr, ':')) { ++ memset(&rr->addr.inet6, 0, sizeof(rr->addr.inet6)); ++ rr->addr.sa.sa_family = AF_INET6; ++ p = (char *) &rr->addr.inet6.sin6_addr; ++ } ++ else { ++ memset(&rr->addr.inet, 0, sizeof(rr->addr.inet)); ++ rr->addr.sa.sa_family = AF_INET; ++ p = (char *) &rr->addr.inet.sin_addr; ++ } ++ return inet_pton(rr->addr.sa.sa_family, addr, p) > 0; ++} ++ + void of_ptr(const struct optioninfo *oi, const char *arg, const char *arg2) { + struct query_node *qun; + int quflags, r; +- struct sockaddr_in sa; +- +- memset(&sa,0,sizeof(sa)); +- sa.sin_family= AF_INET; +- if (!inet_aton(arg,&sa.sin_addr)) usageerr("invalid IP address %s",arg); ++ adns_rr_addr rr; + ++ if (!a2addr(&rr, arg)) usageerr("invalid IP address %s",arg); + prep_query(&qun,&quflags); + qun->owner= xstrsave(arg); + r= adns_submit_reverse(ads, +- (struct sockaddr*)&sa, ++ &rr.addr.sa, + ov_type == adns_r_none ? adns_r_ptr : ov_type, + quflags, + qun, +@@ -122,17 +135,14 @@ void of_ptr(const struct optioninfo *oi, const char *arg, const char *arg2) { + void of_reverse(const struct optioninfo *oi, const char *arg, const char *arg2) { + struct query_node *qun; + int quflags, r; +- struct sockaddr_in sa; +- +- memset(&sa,0,sizeof(sa)); +- sa.sin_family= AF_INET; +- if (!inet_aton(arg,&sa.sin_addr)) usageerr("invalid IP address %s",arg); ++ adns_rr_addr rr; + ++ if (!a2addr(&rr, arg)) usageerr("invalid IP address %s",arg); + prep_query(&qun,&quflags); + qun->owner= xmalloc(strlen(arg) + strlen(arg2) + 2); + sprintf(qun->owner, "%s %s", arg,arg2); + r= adns_submit_reverse_any(ads, +- (struct sockaddr*)&sa, arg2, ++ &rr.addr.sa, arg2, + ov_type == adns_r_none ? adns_r_txt : ov_type, + quflags, + qun, +diff --git a/client/adnshost.h b/client/adnshost.h +index fcc96a3..7e2341a 100644 +--- a/client/adnshost.h ++++ b/client/adnshost.h +@@ -81,6 +81,8 @@ extern int ov_verbose; + extern adns_rrtype ov_type; + extern int ov_search, ov_qc_query, ov_qc_anshost, ov_qc_cname; + extern int ov_tcp, ov_cname, ov_format; ++extern int ov_ipflags; ++extern int ov_ip6mapped; + extern char *ov_id; + extern struct perqueryflags_remember ov_pqfr; + +diff --git a/client/adnstest.c b/client/adnstest.c +index 550cf27..ae70285 100644 +--- a/client/adnstest.c ++++ b/client/adnstest.c +@@ -119,13 +119,16 @@ static const adns_rrtype defaulttypes[]= { + adns_r_ptr_raw, + adns_r_hinfo, + adns_r_mx_raw, ++ adns_r_srv_raw, + adns_r_txt, + adns_r_rp_raw, ++ adns_r_aaaa, /* Does the order matter? */ + + adns_r_addr, + adns_r_ns, + adns_r_ptr, + adns_r_mx, ++ adns_r_srv, + + adns_r_soa, + adns_r_rp, +diff --git a/regress/case-connfail.sys b/regress/case-connfail.sys +index b62923b..2064368 100644 +--- a/regress/case-connfail.sys ++++ b/regress/case-connfail.sys +@@ -3,76 +3,88 @@ + start 1056289303.784817 + socket type=SOCK_DGRAM + socket=6 ++ +0.000066 ++ socket type=SOCK_DGRAM ++ socket=7 + +0.000031 + fcntl fd=6 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000010 + fcntl fd=6 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK ++ +0.000264 ++ fcntl fd=7 cmd=F_GETFL ++ fcntl=~O_NONBLOCK&... ++ +0.000087 ++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl=OK + +0.000007 + socket type=SOCK_STREAM +- socket=7 ++ socket=8 + +0.000059 +- fcntl fd=7 cmd=F_GETFL ++ fcntl fd=8 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000007 +- fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl fd=8 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK + +0.000006 +- connect fd=7 addr=172.18.45.36:53 ++ connect fd=8 addr=172.18.45.36:53 + connect=ENOTSOCK + +0.000013 +- close fd=7 ++ close fd=8 + close=OK + +0.000031 + socket type=SOCK_STREAM +- socket=7 ++ socket=8 + +0.000035 +- fcntl fd=7 cmd=F_GETFL ++ fcntl fd=8 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000006 +- fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl fd=8 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK + +0.000007 +- connect fd=7 addr=172.18.45.6:53 ++ connect fd=8 addr=172.18.45.6:53 + connect=ENOTSOCK + +0.000008 +- close fd=7 ++ close fd=8 + close=OK + +0.000013 +- select max=7 rfds=[6] wfds=[] efds=[] to=0.000000 ++ select max=8 rfds=[6,7] wfds=[] efds=[] to=0.000000 + select=0 rfds=[] wfds=[] efds=[] + +0.000036 + socket type=SOCK_STREAM +- socket=7 ++ socket=8 + +0.000036 +- fcntl fd=7 cmd=F_GETFL ++ fcntl fd=8 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000007 +- fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl fd=8 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK + +0.000006 +- connect fd=7 addr=172.18.45.36:53 ++ connect fd=8 addr=172.18.45.36:53 + connect=ENOTSOCK + +0.000008 +- close fd=7 ++ close fd=8 + close=OK + +0.000013 + socket type=SOCK_STREAM +- socket=7 ++ socket=8 + +0.000036 +- fcntl fd=7 cmd=F_GETFL ++ fcntl fd=8 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000007 +- fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl fd=8 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK + +0.000006 +- connect fd=7 addr=172.18.45.6:53 ++ connect fd=8 addr=172.18.45.6:53 + connect=ENOTSOCK + +0.000008 +- close fd=7 ++ close fd=8 + close=OK + +0.000012 + close fd=6 + close=OK ++ +0.000067 ++ close fd=7 ++ close=OK + +0.000023 +diff --git a/regress/case-flags10.sys b/regress/case-flags10.sys +index fe0b341..99f1f8b 100644 +--- a/regress/case-flags10.sys ++++ b/regress/case-flags10.sys +@@ -3,13 +3,25 @@ adnstest default + start 929580072.670441 + socket type=SOCK_DGRAM + socket=4 ++ +0.000066 ++ socket type=SOCK_DGRAM ++ socket=7 + +0.000191 + fcntl fd=4 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000084 + fcntl fd=4 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK ++ +0.000264 ++ fcntl fd=7 cmd=F_GETFL ++ fcntl=~O_NONBLOCK&... ++ +0.000087 ++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl=OK + +0.000061 + close fd=4 + close=OK ++ +0.000067 ++ close fd=7 ++ close=OK + +0.000001 +diff --git a/regress/case-longdom1.sys b/regress/case-longdom1.sys +index a54e14d..6920322 100644 +--- a/regress/case-longdom1.sys ++++ b/regress/case-longdom1.sys +@@ -3,13 +3,25 @@ adnstest default + start 951955690.505811 + socket type=SOCK_DGRAM + socket=4 ++ +0.000066 ++ socket type=SOCK_DGRAM ++ socket=7 + +0.000126 + fcntl fd=4 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000058 + fcntl fd=4 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK ++ +0.000264 ++ fcntl fd=7 cmd=F_GETFL ++ fcntl=~O_NONBLOCK&... ++ +0.000087 ++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl=OK + +0.000035 + close fd=4 + close=OK ++ +0.000067 ++ close fd=7 ++ close=OK + +0.000269 +diff --git a/regress/case-longdomsrch0.sys b/regress/case-longdomsrch0.sys +index 298bec8..94be025 100644 +--- a/regress/case-longdomsrch0.sys ++++ b/regress/case-longdomsrch0.sys +@@ -3,13 +3,25 @@ adnstest ndots100 + start 951956073.321566 + socket type=SOCK_DGRAM + socket=4 ++ +0.000066 ++ socket type=SOCK_DGRAM ++ socket=7 + +0.000131 + fcntl fd=4 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000056 + fcntl fd=4 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK ++ +0.000264 ++ fcntl fd=7 cmd=F_GETFL ++ fcntl=~O_NONBLOCK&... ++ +0.000087 ++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl=OK + +0.000034 + close fd=4 + close=OK ++ +0.000067 ++ close fd=7 ++ close=OK + +0.000340 +diff --git a/regress/case-longlab1.sys b/regress/case-longlab1.sys +index 5b0e46a..d832c9d 100644 +--- a/regress/case-longlab1.sys ++++ b/regress/case-longlab1.sys +@@ -3,13 +3,25 @@ adnstest default + start 951955261.286712 + socket type=SOCK_DGRAM + socket=4 ++ +0.000066 ++ socket type=SOCK_DGRAM ++ socket=7 + +0.000128 + fcntl fd=4 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000053 + fcntl fd=4 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK ++ +0.000264 ++ fcntl fd=7 cmd=F_GETFL ++ fcntl=~O_NONBLOCK&... ++ +0.000087 ++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl=OK + +0.000033 + close fd=4 + close=OK ++ +0.000067 ++ close fd=7 ++ close=OK + +0.000238 +diff --git a/regress/case-tcpmultipart.sys b/regress/case-tcpmultipart.sys +index d26ded2..00e2488 100644 +--- a/regress/case-tcpmultipart.sys ++++ b/regress/case-tcpmultipart.sys +@@ -3,12 +3,21 @@ adnstest tunnel + start 938365454.994875 + socket type=SOCK_DGRAM + socket=4 ++ +0.000066 ++ socket type=SOCK_DGRAM ++ socket=7 + +0.000164 + fcntl fd=4 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000055 + fcntl fd=4 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK ++ +0.000264 ++ fcntl fd=7 cmd=F_GETFL ++ fcntl=~O_NONBLOCK&... ++ +0.000087 ++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl=OK + +0.000043 + socket type=SOCK_STREAM + socket=5 +@@ -22,7 +31,7 @@ adnstest tunnel + connect fd=5 addr=172.31.80.9:53 + connect=EINPROGRESS + +0.000414 +- select max=6 rfds=[4] wfds=[5] efds=[] to=13.998324 ++ select max=8 rfds=[4,7] wfds=[5] efds=[] to=13.998324 + select=1 rfds=[] wfds=[5] efds=[] + +1.-647444 + read fd=5 buflen=1 +@@ -43,7 +52,7 @@ adnstest tunnel + 2d616464 72046172 70610000 0c0001. + write=47 + +0.000273 +- select max=6 rfds=[4,5] wfds=[] efds=[5] to=29.644233 ++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=29.644233 + select=1 rfds=[5] wfds=[] efds=[] + +0.538651 + read fd=5 buflen=2 +@@ -66,7 +75,7 @@ adnstest tunnel + read fd=5 buflen=297 + read=EAGAIN + +0.000476 +- select max=6 rfds=[4,5] wfds=[] efds=[5] to=29.105246 ++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=29.105246 + select=1 rfds=[5] wfds=[] efds=[] + +1.-401146 + read fd=5 buflen=297 +@@ -109,7 +118,7 @@ adnstest tunnel + read fd=5 buflen=2572 + read=EAGAIN + +0.000101 +- select max=6 rfds=[4,5] wfds=[] efds=[5] to=28.502804 ++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=28.502804 + select=1 rfds=[5] wfds=[] efds=[] + +0.336462 + read fd=5 buflen=2572 +@@ -148,7 +157,7 @@ adnstest tunnel + read fd=5 buflen=1624 + read=EAGAIN + +0.000124 +- select max=6 rfds=[4,5] wfds=[] efds=[5] to=28.162903 ++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=28.162903 + select=1 rfds=[5] wfds=[] efds=[] + +1.-683589 + read fd=5 buflen=1624 +@@ -187,7 +196,7 @@ adnstest tunnel + read fd=5 buflen=676 + read=EAGAIN + +0.000114 +- select max=6 rfds=[4,5] wfds=[] efds=[5] to=27.843177 ++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=27.843177 + select=1 rfds=[5] wfds=[] efds=[] + +0.376863 + read fd=5 buflen=676 +@@ -230,7 +239,7 @@ adnstest tunnel + read fd=5 buflen=3248 + read=EAGAIN + +0.000066 +- select max=6 rfds=[4,5] wfds=[] efds=[5] to=27.454446 ++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=27.454446 + select=1 rfds=[5] wfds=[] efds=[] + +0.316770 + read fd=5 buflen=3248 +@@ -242,6 +251,9 @@ adnstest tunnel + +0.000429 + close fd=4 + close=OK ++ +0.000067 ++ close fd=7 ++ close=OK + +0.000375 + close fd=5 + close=OK +diff --git a/regress/case-timeout.sys b/regress/case-timeout.sys +index f810c3b..756becc 100644 +--- a/regress/case-timeout.sys ++++ b/regress/case-timeout.sys +@@ -3,19 +3,28 @@ adnstest noserver + start 912889153.349504 + socket type=SOCK_DGRAM + socket=4 ++ +0.000066 ++ socket type=SOCK_DGRAM ++ socket=7 + +0.000193 + fcntl fd=4 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000088 + fcntl fd=4 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK ++ +0.000264 ++ fcntl fd=7 cmd=F_GETFL ++ fcntl=~O_NONBLOCK&... ++ +0.000087 ++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl=OK + +0.000072 + sendto fd=4 addr=172.18.45.36:53 + 311f0100 00010000 00000000 06636869 61726b08 67726565 6e656e64 036f7267 + 02756b00 00010001. + sendto=40 + +0.000617 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999383 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999383 + select=0 rfds=[] wfds=[] efds=[] + +2.008683 + sendto fd=4 addr=172.18.45.36:53 +@@ -23,7 +32,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000406 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999594 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999594 + select=0 rfds=[] wfds=[] efds=[] + +2.009544 + sendto fd=4 addr=172.18.45.36:53 +@@ -31,7 +40,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000428 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999572 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999572 + select=0 rfds=[] wfds=[] efds=[] + +2.009567 + sendto fd=4 addr=172.18.45.36:53 +@@ -39,7 +48,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000449 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999551 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999551 + select=0 rfds=[] wfds=[] efds=[] + +2.009551 + sendto fd=4 addr=172.18.45.36:53 +@@ -47,7 +56,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000381 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999619 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999619 + select=0 rfds=[] wfds=[] efds=[] + +2.009614 + sendto fd=4 addr=172.18.45.36:53 +@@ -55,7 +64,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000383 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999617 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999617 + select=0 rfds=[] wfds=[] efds=[] + +2.009622 + sendto fd=4 addr=172.18.45.36:53 +@@ -63,7 +72,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000387 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999613 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999613 + select=0 rfds=[] wfds=[] efds=[] + +2.009603 + sendto fd=4 addr=172.18.45.36:53 +@@ -71,7 +80,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000404 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999596 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999596 + select=0 rfds=[] wfds=[] efds=[] + +2.009607 + sendto fd=4 addr=172.18.45.36:53 +@@ -79,7 +88,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000468 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999532 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999532 + select=0 rfds=[] wfds=[] efds=[] + +2.009526 + sendto fd=4 addr=172.18.45.36:53 +@@ -87,7 +96,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000431 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999569 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999569 + select=0 rfds=[] wfds=[] efds=[] + +2.009564 + sendto fd=4 addr=172.18.45.36:53 +@@ -95,7 +104,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000429 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999571 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999571 + select=0 rfds=[] wfds=[] efds=[] + +2.009586 + sendto fd=4 addr=172.18.45.36:53 +@@ -103,7 +112,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000479 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999521 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999521 + select=0 rfds=[] wfds=[] efds=[] + +2.009511 + sendto fd=4 addr=172.18.45.36:53 +@@ -111,7 +120,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000430 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999570 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999570 + select=0 rfds=[] wfds=[] efds=[] + +2.009571 + sendto fd=4 addr=172.18.45.36:53 +@@ -119,7 +128,7 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000440 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999560 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999560 + select=0 rfds=[] wfds=[] efds=[] + +2.009564 + sendto fd=4 addr=172.18.45.36:53 +@@ -127,9 +136,12 @@ adnstest noserver + 02756b00 00010001. + sendto=40 + +0.000439 +- select max=5 rfds=[4] wfds=[] efds=[] to=1.999561 ++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999561 + select=0 rfds=[] wfds=[] efds=[] + +2.009554 + close fd=4 + close=OK ++ +0.000067 ++ close fd=7 ++ close=OK + +0.000267 +diff --git a/regress/case-unknownq.sys b/regress/case-unknownq.sys +index 736210d..052c028 100644 +--- a/regress/case-unknownq.sys ++++ b/regress/case-unknownq.sys +@@ -3,13 +3,25 @@ adnstest default + start 933811310.565828 + socket type=SOCK_DGRAM + socket=4 ++ +0.000066 ++ socket type=SOCK_DGRAM ++ socket=7 + +0.000264 + fcntl fd=4 cmd=F_GETFL + fcntl=~O_NONBLOCK&... + +0.000087 + fcntl fd=4 cmd=F_SETFL O_NONBLOCK|... + fcntl=OK ++ +0.000264 ++ fcntl fd=7 cmd=F_GETFL ++ fcntl=~O_NONBLOCK&... ++ +0.000087 ++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|... ++ fcntl=OK + +0.000067 + close fd=4 + close=OK ++ +0.000067 ++ close fd=7 ++ close=OK + +0.000307 +diff --git a/regress/hcommon.c b/regress/hcommon.c +index 0324e58..60cd7cc 100644 +--- a/regress/hcommon.c ++++ b/regress/hcommon.c +@@ -144,10 +144,18 @@ void Qwrite( int fd , const void *buf , size_t len ) { + Q_vb(); + } + void Tvbaddr(const struct sockaddr *addr, int len) { +- const struct sockaddr_in *ai= (const struct sockaddr_in*)addr; +- assert(len==sizeof(struct sockaddr_in)); +- assert(ai->sin_family==AF_INET); +- Tvbf("%s:%u",inet_ntoa(ai->sin_addr),htons(ai->sin_port)); ++ if(addr->sa_family==AF_INET) { ++ const struct sockaddr_in *ai= (const struct sockaddr_in*)addr; ++ assert(len==sizeof(struct sockaddr_in)); ++ assert(ai->sin_family==AF_INET); ++ Tvbf("%s:%u",inet_ntoa(ai->sin_addr),htons(ai->sin_port)); ++ } else { ++ char buf[INET6_ADDRSTRLEN]; ++ const struct sockaddr_in6 *ai6= (const struct sockaddr_in6*)addr; ++ assert(len==sizeof(struct sockaddr_in6)); ++ assert(ai6->sin6_family==AF_INET6); ++ Tvbf("%s:%u",inet_ntop(AF_INET6, &(ai6->sin6_addr), buf, sizeof(buf)),htons(ai6->sin6_port)); ++ } + } + void Tvbbytes(const void *buf, int len) { + const byte *bp; +diff --git a/regress/hcommon.c.m4 b/regress/hcommon.c.m4 +index 0f205fe..5de19d4 100644 +--- a/regress/hcommon.c.m4 ++++ b/regress/hcommon.c.m4 +@@ -129,11 +129,18 @@ m4_define(`hm_specsyscall', `') + m4_include(`hsyscalls.i4') + + void Tvbaddr(const struct sockaddr *addr, int len) { +- const struct sockaddr_in *ai= (const struct sockaddr_in*)addr; +- +- assert(len==sizeof(struct sockaddr_in)); +- assert(ai->sin_family==AF_INET); +- Tvbf("%s:%u",inet_ntoa(ai->sin_addr),htons(ai->sin_port)); ++ if(addr->sa_family==AF_INET) { ++ const struct sockaddr_in *ai= (const struct sockaddr_in*)addr; ++ assert(len==sizeof(struct sockaddr_in)); ++ assert(ai->sin_family==AF_INET); ++ Tvbf("%s:%u",inet_ntoa(ai->sin_addr),htons(ai->sin_port)); ++ } else { ++ char buf[INET6_ADDRSTRLEN]; ++ const struct sockaddr_in6 *ai6= (const struct sockaddr_in6*)addr; ++ assert(len==sizeof(struct sockaddr_in6)); ++ assert(ai6->sin6_family==AF_INET6); ++ Tvbf("%s:%u",inet_ntop(AF_INET6, &(ai6->sin6_addr), buf, sizeof(buf)),htons(ai6->sin6_port)); ++ } + } + + void Tvbbytes(const void *buf, int len) { +diff --git a/regress/hplayback.c b/regress/hplayback.c +index 594f7e6..e0e2246 100644 +--- a/regress/hplayback.c ++++ b/regress/hplayback.c +@@ -153,22 +153,42 @@ static void Ppollfds(struct pollfd *fds, int nfds) { + } + #endif + static void Paddr(struct sockaddr *addr, int *lenr) { ++ struct sockaddr_in6 *sa6= (struct sockaddr_in6*)addr; + struct sockaddr_in *sa= (struct sockaddr_in*)addr; + char *p, *ep; + long ul; + assert(*lenr >= sizeof(*sa)); +- p= strchr(vb2.buf+vb2.used,':'); +- if (!p) Psyntax("no port on address"); +- *p++= 0; + memset(sa,0,sizeof(*sa)); +- sa->sin_family= AF_INET; +- if (!inet_aton(vb2.buf+vb2.used,&sa->sin_addr)) Psyntax("invalid address"); +- ul= strtoul(p,&ep,10); +- if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)"); +- if (ul >= 65536) Psyntax("port too large"); +- sa->sin_port= htons(ul); +- *lenr= sizeof(*sa); +- vb2.used= ep - (char*)vb2.buf; ++ if (!inet_aton(vb2.buf+vb2.used,&sa->sin_addr)){ ++ p= strchr(vb2.buf+vb2.used,':'); ++ if (!p) Psyntax("no port on address"); ++ *p++= 0; ++ sa->sin_family= AF_INET; ++ ul= strtoul(p,&ep,10); ++ if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)"); ++ if (ul >= 65536) Psyntax("port too large"); ++ sa->sin_port= htons(ul); ++ *lenr= sizeof(*sa); ++ vb2.used= ep - (char*)vb2.buf; ++ return; ++ } else { ++ assert(*lenr >= sizeof(*sa6)); ++ memset(sa6,0,sizeof(*sa6)); ++ if (!inet_pton(AF_INET6, vb2.buf+vb2.used, &sa6->sin6_addr)) { ++ p= strrchr(vb2.buf+vb2.used,':'); ++ if (!p) Psyntax("no port on address"); ++ *p++= 0; ++ sa6->sin6_family= AF_INET6; ++ ul= strtoul(p,&ep,10); ++ if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)"); ++ if (ul >= 65536) Psyntax("port too large"); ++ sa6->sin6_port= htons(ul); ++ *lenr= sizeof(*sa6); ++ vb2.used= ep - (char*)vb2.buf; ++ return; ++ } ++ } ++ Psyntax("invalid address"); + } + static int Pbytes(byte *buf, int maxlen) { + static const char hexdigits[]= "0123456789abcdef"; +@@ -283,7 +303,6 @@ int Hpoll( struct pollfd *fds , int nfds , int timeout ) { + int Hsocket( int domain , int type , int protocol ) { + int r, amtread; + char *ep; +- Tmust("socket","domain",domain==AF_INET); + Tmust("socket","type",type==SOCK_STREAM || type==SOCK_DGRAM); + Qsocket( type ); + if (!adns__vbuf_ensure(&vb2,1000)) Tnomem(); +diff --git a/regress/hplayback.c.m4 b/regress/hplayback.c.m4 +index 868aa52..d023b7c 100644 +--- a/regress/hplayback.c.m4 ++++ b/regress/hplayback.c.m4 +@@ -210,24 +210,42 @@ static void Ppollfds(struct pollfd *fds, int nfds) { + #endif + + static void Paddr(struct sockaddr *addr, int *lenr) { ++ struct sockaddr_in6 *sa6= (struct sockaddr_in6*)addr; + struct sockaddr_in *sa= (struct sockaddr_in*)addr; + char *p, *ep; + long ul; +- + assert(*lenr >= sizeof(*sa)); +- p= strchr(vb2.buf+vb2.used,':'); +- if (!p) Psyntax("no port on address"); +- *p++= 0; + memset(sa,0,sizeof(*sa)); +- sa->sin_family= AF_INET; +- if (!inet_aton(vb2.buf+vb2.used,&sa->sin_addr)) Psyntax("invalid address"); +- ul= strtoul(p,&ep,10); +- if (*ep && *ep != hm_squote hm_squote) Psyntax("invalid port (bad syntax)"); +- if (ul >= 65536) Psyntax("port too large"); +- sa->sin_port= htons(ul); +- *lenr= sizeof(*sa); +- +- vb2.used= ep - (char*)vb2.buf; ++ if (!inet_aton(vb2.buf+vb2.used,&sa->sin_addr)){ ++ p= strchr(vb2.buf+vb2.used,':'); ++ if (!p) Psyntax("no port on address"); ++ *p++= 0; ++ sa->sin_family= AF_INET; ++ ul= strtoul(p,&ep,10); ++ if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)"); ++ if (ul >= 65536) Psyntax("port too large"); ++ sa->sin_port= htons(ul); ++ *lenr= sizeof(*sa); ++ vb2.used= ep - (char*)vb2.buf; ++ return; ++ } else { ++ assert(*lenr >= sizeof(*sa6)); ++ memset(sa6,0,sizeof(*sa6)); ++ if (!inet_pton(AF_INET6, vb2.buf+vb2.used, &sa6->sin6_addr)) { ++ p= strrchr(vb2.buf+vb2.used,':'); ++ if (!p) Psyntax("no port on address"); ++ *p++= 0; ++ sa6->sin6_family= AF_INET6; ++ ul= strtoul(p,&ep,10); ++ if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)"); ++ if (ul >= 65536) Psyntax("port too large"); ++ sa6->sin6_port= htons(ul); ++ *lenr= sizeof(*sa6); ++ vb2.used= ep - (char*)vb2.buf; ++ return; ++ } ++ } ++ Psyntax("invalid address"); + } + + static int Pbytes(byte *buf, int maxlen) { +diff --git a/regress/hrecord.c b/regress/hrecord.c +index 88e24a4..67dff6f 100644 +--- a/regress/hrecord.c ++++ b/regress/hrecord.c +@@ -81,7 +81,6 @@ int Hpoll( struct pollfd *fds , int nfds , int timeout ) { + #endif + int Hsocket( int domain , int type , int protocol ) { + int r, e; +- Tmust("socket","domain",domain==AF_INET); + Tmust("socket","type",type==SOCK_STREAM || type==SOCK_DGRAM); + Qsocket( type ); + r= socket( domain , type , protocol ); +diff --git a/regress/hsyscalls.i4 b/regress/hsyscalls.i4 +index ad90104..b5dfc56 100644 +--- a/regress/hsyscalls.i4 ++++ b/regress/hsyscalls.i4 +@@ -70,7 +70,7 @@ hm_syscall( + + hm_syscall( + socket, `hm_rv_fd', ` +- hm_arg_must(int,domain,AF_INET) hm_na ++ hm_arg_ign(int,domain) hm_na + hm_arg_socktype(type) hm_na + hm_arg_ign(int,protocol) hm_na + ') +diff --git a/src/adns.h b/src/adns.h +index 34f9f49..aad05fd 100644 +--- a/src/adns.h ++++ b/src/adns.h +@@ -71,6 +71,10 @@ + extern "C" { /* I really dislike this - iwj. */ + #endif + ++#ifndef AF_INET6 ++#include "adns-in6fake.h" ++#endif ++ + /* All struct in_addr anywhere in adns are in NETWORK byte order. */ + + typedef struct adns__state *adns_state; +@@ -87,7 +91,10 @@ typedef enum { /* In general, or together the desired flags: */ + adns_if_eintr= 0x0020,/* allow _wait and _synchronous to return EINTR */ + adns_if_nosigpipe= 0x0040,/* applic has SIGPIPE ignored, do not protect */ + adns_if_checkc_entex=0x0100,/* consistency checks on entry/exit to adns fns */ +- adns_if_checkc_freq= 0x0300 /* consistency checks very frequently (slow!) */ ++ adns_if_checkc_freq= 0x0300,/* consistency checks very frequently (slow!) */ ++ adns_if_ip4only= 0x1000,/* make default be adns_qf_ip4 */ ++ adns_if_ip6only= 0x2000,/* make default be adns_qf_ip6 */ ++ adns_if_ip6mapped= 0x4000,/* make default be adns_qf_ip4|adns_qf_ip6|adns_qf_ip6mapped */ + } adns_initflags; + + typedef enum { /* In general, or together the desired flags: */ +@@ -101,9 +108,54 @@ typedef enum { /* In general, or together the desired flags: */ + adns_qf_quotefail_cname=0x00000080,/* refuse if quote-req chars in CNAME we go via */ + adns_qf_cname_loose= 0x00000100,/* allow refs to CNAMEs - without, get _s_cname */ + adns_qf_cname_forbid= 0x00000200,/* don't follow CNAMEs, instead give _s_cname */ ++ ++ /* Affects addr queries and additional section processing */ ++ adns_qf_ip4= 0x00001000, /* Ask for A records */ ++ adns_qf_ip6= 0x00002000, /* Ask for AAAA records */ ++ adns_qf_ip6mapped= 0x00004000, /* Return any IPv4 addresses as IPv6 mapped addresses */ ++ ++ adns__qf_ip_mask= 0x00003000, + adns__qf_internalmask= 0x0ff00000 + } adns_queryflags; + ++/* IPv6 support: ++ * ++ * The _qf_ip4 and _qf_ip6 says which kinds of address records (A and ++ * AAAA) we should ask for. _qf_ip6mapped says how we return ipv6 ++ * addresses to the caller. Four modes of operation, corresponding to ++ * the _if_ip* flags: ++ * ++ * Record type: A AAAA ++ * flags: ++ * ++ * Default => AF_INET => AF_INET6 ++ * ++ * _if_ip4only => AF_INET not used ++ * ++ * _if_ip6only not used => AF_INET6 ++ * ++ * _if_ipv6mapped => AF_INET6 => AF_INET6 ++ * ++ * _if_ip4only => AF_INET6 not used ++ * | _if_ipv6mapped ++ * ++ * Furthermore, there are configuration options which can prevent the ++ * use of either AAAA or A records for _r_addr; so it is safe to use ++ * _qf_ip6_mapped and _r_addr without checking explicitly whether the host ++ * has IPv6 connectivity. ++ * ++ * The corresponding _qf_ip* flags are constructed from the _if_ip* ++ * flags and the query flags submitted to functions like adns_submit. ++ * If none of _qf_ip4 and _qf_ip6 are set explicitly in the query ++ * flags, the default behaviour is used. If the flags are set, the ++ * default configuration is overridden. ++ * ++ * Applications which do not support IPv4 should set none of these ++ * flags. Applications which have been `naively' converted to use ++ * AF_INET6 throughout should set adns_if_ip6. Applications which ++ * know what they are doing should know which flags to set :-). ++ */ ++ + typedef enum { + adns_rrt_typemask= 0x0ffff, + adns__qtf_deref= 0x10000,/* dereference domains; perhaps get extra data */ +@@ -127,6 +179,8 @@ typedef enum { + * + * Don't forget adns_qf_quoteok if that's what you want. */ + ++ adns__qtf_special= 0x80000,/* no simple correspondence to a single rr type */ ++ + adns_r_none= 0, + + adns_r_a= 1, +@@ -151,6 +205,7 @@ typedef enum { + + adns_r_rp_raw= 17, + adns_r_rp= adns_r_rp_raw|adns__qtf_mail822, ++ adns_r_aaaa= 28, /* RFC 1886 */ + + /* For SRV records, query domain without _qf_quoteok_query must look + * as expected from SRV RFC with hostname-like Name. _With_ +@@ -158,7 +213,8 @@ typedef enum { + adns_r_srv_raw= 33, + adns_r_srv= adns_r_srv_raw|adns__qtf_deref, + +- adns_r_addr= adns_r_a|adns__qtf_deref ++ /* FIXME: Maybe add adns__qtf_deref too? */ ++ adns_r_addr= 1 | adns__qtf_special, + + } adns_rrtype; + +@@ -284,9 +340,13 @@ typedef enum { + + typedef struct { + int len; ++#if 0 ++ int order; /* Cache index on sortlist? */ ++#endif + union { + struct sockaddr sa; + struct sockaddr_in inet; ++ struct sockaddr_in6 inet6; + } addr; + } adns_rr_addr; + +@@ -355,6 +415,7 @@ typedef struct { + adns_rr_intstr *(*manyistr); /* txt (list strs ends with i=-1, str=0)*/ + adns_rr_addr *addr; /* addr */ + struct in_addr *inaddr; /* a */ ++ struct in6_addr *in6addr; /* aaaa */ + adns_rr_hostaddr *hostaddr; /* ns */ + adns_rr_intstrpair *intstrpair; /* hinfo */ + adns_rr_strpair *strpair; /* rp, rp_raw */ +@@ -506,6 +567,13 @@ int adns_init_logfn(adns_state *newstate_r, adns_initflags flags, + * setting of adns_if_check_entex, adns_if_check_freq, or neither, + * in the flags passed to adns_init. + * ++ * in6only ++ * in4only ++ * Return only IPv6, respectively only IPv4 addresses, in ++ * _rr_addr's. This may result in an adns_s_nodata error, if the ++ * application only supports, or the remote host only has, the wrong ++ * kind of address. ++ * + * There are a number of environment variables which can modify the + * behaviour of adns. They take effect only if adns_init is used, and + * the caller of adns_init can disable them using adns_if_noenv. In +@@ -589,7 +657,33 @@ int adns_submit_reverse(adns_state ads, + void *context, + adns_query *query_r); + /* type must be _r_ptr or _r_ptr_raw. _qf_search is ignored. +- * addr->sa_family must be AF_INET or you get ENOSYS. ++ * addr->sa_family must be AF_INET or AF_INET6 or you get ENOSYS. ++ */ ++ ++int adns_getaddrinfo(adns_state ads, ++ const char *name, /* Eg, "www.example.coom" */ ++ const char *service, /* Eg, "http" */ ++ const char *protocol, /* Eg, "tcp" */ ++ unsigned short defaultport, /* Eg, 80 */ ++ adns_queryflags flags, ++ adns_answer **answer_r, int *invented_r); ++/* Does an SRV lookup (RFC2052). If this fails, tries an AAAA or A ++ * lookup instead, and if found uses getservbyname to find the port ++ * number (or failing that, uses defaultport. The defaultport is in ++ * hot byte order). In the `fallback' case, will invent an SRV record ++ * which have priority and weight == 0 and set *invented_r to 1; if ++ * real SRV records were found, will set *invented_r to 0. invented_r ++ * may be null but answer_r may not be. If _getaddrinfo returns ++ * nonzero, *answer_r and/or *invented_r may or may not have been ++ * overwritten and should not be used. ++ * ++ * NB, like adns_synchronous, can fail either by returning an errno ++ * value, or by returning an adns_answer with ->nrrs==0 and ++ * ->status!=0. ++ * ++ * You have to write two loops when using the returned value, an outer ++ * one to loop over the returned SRV's, and an inner one to loop over ++ * the addresses for each one. + */ + + int adns_submit_reverse_any(adns_state ads, +@@ -602,7 +696,7 @@ int adns_submit_reverse_any(adns_state ads, + /* For RBL-style reverse `zone's; look up + * . + * Any type is allowed. _qf_search is ignored. +- * addr->sa_family must be AF_INET or you get ENOSYS. ++ * addr->sa_family must be AF_INET or AF_INET6 or you get ENOSYS. + */ + + void adns_finish(adns_state ads); +@@ -830,7 +924,7 @@ int adns_beforepoll(adns_state ads, struct pollfd *fds, + * In any case this call won't block. + */ + +-#define ADNS_POLLFDS_RECOMMENDED 2 ++#define ADNS_POLLFDS_RECOMMENDED 3 + /* If you allocate an fds buf with at least RECOMMENDED entries then + * you are unlikely to need to enlarge it. You are recommended to do + * so if it's convenient. However, you must be prepared for adns to +diff --git a/src/check.c b/src/check.c +index 41cdde5..704a15e 100644 +--- a/src/check.c ++++ b/src/check.c +@@ -4,6 +4,7 @@ + */ + /* + * This file is part of adns, which is ++ * Copyright (C) 2009 Luca Bruno + * Copyright (C) 1997-2000,2003,2006 Ian Jackson + * Copyright (C) 1999-2000,2003,2006 Tony Finch + * Copyright (C) 1991 Massachusetts Institute of Technology +@@ -24,6 +25,8 @@ + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + ++#include ++ + #include "internal.h" + + void adns_checkconsistency(adns_state ads, adns_query qu) { +@@ -77,11 +80,11 @@ static void checkc_notcpbuf(adns_state ads) { + static void checkc_global(adns_state ads) { + int i; + +- assert(ads->udpsocket >= 0); +- ++ assert((ads->udpsocket >= 0) || (ads->udpsocket6 >= 0)); ++#if 0 + for (i=0; insortlist; i++) + assert(!(ads->sortlist[i].base.s_addr & ~ads->sortlist[i].mask.s_addr)); +- ++#endif + assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers); + + switch (ads->tcpstate) { +diff --git a/src/event.c b/src/event.c +index ad5861e..3fb95a1 100644 +--- a/src/event.c ++++ b/src/event.c +@@ -6,6 +6,7 @@ + */ + /* + * This file is part of adns, which is ++ * Copyright (C) 2009 Luca Bruno + * Copyright (C) 1997-2000,2003,2006 Ian Jackson + * Copyright (C) 1999-2000,2003,2006 Tony Finch + * Copyright (C) 1991 Massachusetts Institute of Technology +@@ -100,6 +101,7 @@ static void tcp_broken_events(adns_state ads) { + void adns__tcp_tryconnect(adns_state ads, struct timeval now) { + int r, fd, tries; + struct sockaddr_in addr; ++ struct sockaddr_in6 addr6; + struct protoent *proto; + + for (tries=0; triesnservers; tries++) { +@@ -123,7 +125,7 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) { + adns__diag(ads,-1,0,"unable to find protocol no. for TCP !"); + return; + } +- fd= socket(AF_INET,SOCK_STREAM,proto->p_proto); ++ fd= socket(ads->servers[ads->tcpserver].sin_family,SOCK_STREAM,proto->p_proto); + if (fd<0) { + adns__diag(ads,-1,0,"cannot create TCP socket: %s",strerror(errno)); + return; +@@ -135,11 +137,19 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) { + close(fd); + return; + } +- memset(&addr,0,sizeof(addr)); +- addr.sin_family= AF_INET; +- addr.sin_port= htons(DNS_PORT); +- addr.sin_addr= ads->servers[ads->tcpserver].addr; +- r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr)); ++ if(ads->servers[ads->tcpserver].sin_family==AF_INET) { ++ memset(&addr,0,sizeof(addr)); ++ addr.sin_family= AF_INET; ++ addr.sin_port= htons(DNS_PORT); ++ addr.sin_addr= ads->servers[ads->tcpserver].addr; ++ r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr)); ++ } else { ++ memset(&addr6,0,sizeof(addr6)); ++ addr6.sin6_family= AF_INET6; ++ addr6.sin6_port= htons(DNS_PORT); ++ addr6.sin6_addr= ads->servers[ads->tcpserver].addr6; ++ r= connect(fd,(const struct sockaddr*)&addr6,sizeof(addr6)); ++ } + ads->tcpsocket= fd; + ads->tcpstate= server_connecting; + if (r==0) { tcp_connected(ads,now); return; } +@@ -311,34 +321,41 @@ void adns_processtimeouts(adns_state ads, const struct timeval *now) { + int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) { + /* Returns the number of entries filled in. Always zeroes revents. */ + +- assert(MAX_POLLFDS==2); +- +- pollfds_buf[0].fd= ads->udpsocket; +- pollfds_buf[0].events= POLLIN; +- pollfds_buf[0].revents= 0; ++ assert(MAX_POLLFDS==3); ++ if (ads->udpsocket >= 0) { ++ pollfds_buf[0].fd= ads->udpsocket; ++ pollfds_buf[0].events= POLLIN; ++ pollfds_buf[0].revents= 0; ++ } ++ if(ads->udpsocket6 >= 0) { ++ pollfds_buf[1].fd= ads->udpsocket6; ++ pollfds_buf[1].events= POLLIN; ++ pollfds_buf[1].revents= 0; ++ } + + switch (ads->tcpstate) { + case server_disconnected: + case server_broken: +- return 1; ++ return 2; + case server_connecting: +- pollfds_buf[1].events= POLLOUT; ++ pollfds_buf[2].events= POLLOUT; + break; + case server_ok: +- pollfds_buf[1].events= ++ pollfds_buf[2].events= + ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI; + break; + default: + abort(); + } +- pollfds_buf[1].fd= ads->tcpsocket; +- return 2; ++ pollfds_buf[2].fd= ads->tcpsocket; ++ return 3; + } + + int adns_processreadable(adns_state ads, int fd, const struct timeval *now) { + int want, dgramlen, r, udpaddrlen, serv, old_skip; + byte udpbuf[DNS_MAXUDP]; + struct sockaddr_in udpaddr; ++ struct sockaddr_in6 udpaddr6; + + adns__consistency(ads,0,cc_entex); + +@@ -431,6 +448,48 @@ int adns_processreadable(adns_state ads, int fd, const struct timeval *now) { + adns__procdgram(ads,udpbuf,r,serv,0,*now); + } + } ++ else if (fd == ads->udpsocket6) { ++ for (;;) { ++ udpaddrlen= sizeof(udpaddr6); ++ r= recvfrom(ads->udpsocket6,udpbuf,sizeof(udpbuf),0, ++ (struct sockaddr*)&udpaddr6,&udpaddrlen); ++ if (r<0) { ++ if (errno == EAGAIN || errno == EWOULDBLOCK) { r= 0; goto xit; } ++ if (errno == EINTR) continue; ++ if (errno_resources(errno)) { r= errno; goto xit; } ++ adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno)); ++ r= 0; goto xit; ++ } ++ if (udpaddrlen != sizeof(udpaddr6)) { ++ adns__diag(ads,-1,0,"datagram received with wrong address length %d" ++ " (expected %lu)", udpaddrlen, ++ (unsigned long)sizeof(udpaddr6)); ++ continue; ++ } ++ if (udpaddr6.sin6_family != AF_INET6) { ++ adns__diag(ads,-1,0,"datagram received with wrong protocol family" ++ " %u (expected %u)",udpaddr6.sin6_family,AF_INET6); ++ continue; ++ } ++ if (ntohs(udpaddr6.sin6_port) != DNS_PORT) { ++ adns__diag(ads,-1,0,"datagram received from wrong port" ++ " %u (expected %u)", ntohs(udpaddr6.sin6_port),DNS_PORT); ++ continue; ++ } ++ for (serv= 0; ++ serv < ads->nservers && ++ (memcmp(&(ads->servers[serv].addr6.s6_addr), &(udpaddr6.sin6_addr.s6_addr), sizeof(struct in6_addr))); ++ serv++); ++ if (serv >= ads->nservers) { ++ char buf_dst[INET6_ADDRSTRLEN]; ++ adns__warn(ads,-1,0,"datagram received from unknown nameserver %s", ++ inet_ntop(AF_INET6, &(udpaddr6.sin6_addr), buf_dst, INET6_ADDRSTRLEN*sizeof(char))); ++ continue; ++ } ++ adns__procdgram(ads,udpbuf,r,serv,0,*now); ++ } ++ } ++ + r= 0; + xit: + adns__consistency(ads,0,cc_entex); +diff --git a/src/internal.h b/src/internal.h +index 58cd15d..e4e56c9 100644 +--- a/src/internal.h ++++ b/src/internal.h +@@ -129,6 +129,16 @@ typedef struct typeinfo { + * and will not be null-terminated by convstring. + */ + ++ void (*submithook)(adns_query qu, ++ /* FIXME: Do we need to pass flags? Isn't qu->flags enough? */ ++ adns_queryflags flags, ++ struct timeval now); ++ /* If NULL, submitting a query means to format it and send it over ++ * the wire. If non-NULL, the labels are written to qu->vb, and then ++ * this function is called. It's the hook's responsibility to submit ++ * the query, or submit some other queries and put the original on ++ * the child queue. */ ++ + adns_status (*parse)(const parseinfo *pai, int cbyte, + int max, void *store_r); + /* Parse one RR, in dgram of length dglen, starting at cbyte and +@@ -176,6 +186,8 @@ adns_status adns__qdpl_normal(adns_state ads, + + typedef struct allocnode { + struct allocnode *next, *back; ++ size_t size; ++ /* Needed for realloc */ + } allocnode; + + union maxalign { +@@ -191,11 +203,16 @@ typedef struct { + void *ext; + void (*callback)(adns_query parent, adns_query child); + union { +- adns_rr_addr ptr_parent_addr; + adns_rr_hostaddr *hostaddr; + } info; + } qcontext; + ++typedef struct { ++ union { ++ adns_rr_addr ptr_addr; ++ } info; ++} qextra; ++ + struct adns__query { + adns_state ads; + enum { query_tosend, query_tcpw, query_childw, query_done } state; +@@ -242,13 +259,19 @@ struct adns__query { + * the vbuf is initialised but empty and everything else is zero. + */ + +- int id, flags, retries; ++ int id; ++ /* -2 at allocation, -1 when done, >= 0 while the query is pending. */ ++ ++ int flags, retries; + int udpnextserver; + unsigned long udpsent; /* bitmap indexed by server */ + struct timeval timeout; + time_t expires; /* Earliest expiry time of any record we used. */ + + qcontext ctx; ++ /* Information related to the parent of the query */ ++ qextra extra; ++ /* Extra information about this query. */ + + /* Possible states: + * +@@ -270,34 +293,34 @@ struct adns__query { + * + * +------------------------+ + * START -----> | tosend/NONE | +- * +------------------------+ +- * / |\ \ +- * too big for UDP / UDP timeout \ \ send via UDP +- * send via TCP / more retries \ \ +- * when conn'd / desired \ \ +- * | | | +- * v | v +- * +-----------+ +-------------+ +- * | tcpw/tcpw | ________ | tosend/udpw | +- * +-----------+ \ +-------------+ +- * | | | UDP timeout | | +- * | | | no more | | +- * | | | retries | | +- * \ | TCP died | desired | | +- * \ \ no more | | | +- * \ \ servers | TCP / | +- * \ \ to try | timeout / | +- * got \ \ v |_ | got +- * reply \ _| +------------------+ / reply +- * \ | done/output FAIL | / +- * \ +------------------+ / +- * \ / +- * _| |_ +- * (..... got reply ....) +- * / \ ++ * _____+------------------------+ ++ * consists of __----- / |\ \ ++ * child- / / UDP timeout \ \ send via UDP ++ * queries / too big for UDP/ more retries \ \ ++ * only / send via TCP / desired \ \ ++ * / when conn'd / | | ++ * / |_ | v ++ * | +-----------+ +-------------+ ++ * | | tcpw/tcpw | ________ | tosend/udpw | ++ * | +-----------+ \ +-------------+ ++ * | | | | UDP timeout | | ++ * | | | | no more | | ++ * | | | | retries | | ++ * | \ | TCP died | desired | | ++ * | \ \ no more | | | ++ * | \ \ servers | TCP / | ++ * | \ \ to try | timeout / | ++ * | got \ \ v |_ | got ++ * | reply \ _| +------------------+ / reply ++ * \ \ | done/output FAIL | / ++ * \ \ +------------------+ / ++ * \ \ / ++ * \ _| |_ ++ * \ (..... got reply ....) ++ * \ / \ + * need child query/ies / \ no child query +- * / \ +- * |_ _| ++ * \ / \ ++ * _| |_ _| + * +---------------+ +----------------+ + * | childw/childw | ----------------> | done/output OK | + * +---------------+ children done +----------------+ +@@ -313,7 +336,7 @@ struct adns__state { + int configerrno; + struct query_queue udpw, tcpw, childw, output; + adns_query forallnext; +- int nextid, udpsocket, tcpsocket; ++ int nextid, udpsocket, udpsocket6, tcpsocket; + vbuf tcpsend, tcprecv; + int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip; + enum adns__tcpstate { +@@ -330,10 +353,17 @@ struct adns__state { + sigset_t stdsigmask; + struct pollfd pollfds_buf[MAX_POLLFDS]; + struct server { ++ sa_family_t sin_family; + struct in_addr addr; ++ struct in6_addr addr6; + } servers[MAXSERVERS]; + struct sortlist { +- struct in_addr base, mask; ++ sa_family_t family; ++ unsigned prefix; ++ union { ++ struct in_addr inet; ++ struct in6_addr inet6; ++ } base; + } sortlist[MAXSORTLIST]; + char **searchlist; + unsigned short rand48xsubi[3]; +@@ -401,6 +431,15 @@ void adns__sigpipe_unprotect(adns_state); + + /* From transmit.c: */ + ++adns_status adns__mkquery_labels(adns_state ads, vbuf *vb, ++ const char *owner, int ol, ++ const typeinfo *typei, adns_queryflags flags); ++/* Assembles the owner part of a query packet in vb. */ ++ ++adns_status adns__mkquery_labels_frdgram(adns_state ads, vbuf *vb, ++ const byte *qd_dgram, int qd_dglen, ++ int qd_begin); ++ + adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r, + const char *owner, int ol, + const typeinfo *typei, adns_rrtype type, +@@ -408,6 +447,11 @@ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r, + /* Assembles a query packet in vb. A new id is allocated and returned. + */ + ++adns_status adns__mkquery_frlabels(adns_state ads, vbuf *vb, int *id_r, ++ char *l, int llen, ++ adns_rrtype type, adns_queryflags flags); ++/* Same as adns__mkquery, but with the labels preformatted. */ ++ + adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r, + const byte *qd_dgram, int qd_dglen, + int qd_begin, +@@ -447,6 +491,9 @@ adns_status adns__internal_submit(adns_state ads, adns_query *query_r, + * the memory for it is _taken over_ by this routine whether it + * succeeds or fails (if it succeeds, the vbuf is reused for qu->vb). + * ++ * For query types with a submithook (i.e. adns_r_addr), ++ * vbuf should contain just the label, not a complete query. ++ * + * *ctx is copied byte-for-byte into the query. + * + * When the child query is done, ctx->callback will be called. The +@@ -474,6 +521,7 @@ void adns__search_next(adns_state ads, adns_query qu, struct timeval now); + */ + + void *adns__alloc_interim(adns_query qu, size_t sz); ++void *adns__realloc_interim(adns_query qu, void *p, size_t sz); + void *adns__alloc_preserved(adns_query qu, size_t sz); + /* Allocates some memory, and records which query it came from + * and how much there was. +diff --git a/src/query.c b/src/query.c +index d09702e..2894e4d 100644 +--- a/src/query.c ++++ b/src/query.c +@@ -36,6 +36,10 @@ + + #include "internal.h" + ++#if DMALLOC ++# include ++#endif ++ + static adns_query query_alloc(adns_state ads, + const typeinfo *typei, adns_rrtype type, + adns_queryflags flags, struct timeval now) { +@@ -76,6 +80,7 @@ static adns_query query_alloc(adns_state ads, + qu->expires= now.tv_sec + MAXTTLBELIEVE; + + memset(&qu->ctx,0,sizeof(qu->ctx)); ++ memset(&qu->extra,0,sizeof(qu->extra)); + + qu->answer->status= adns_s_ok; + qu->answer->cname= qu->answer->owner= 0; +@@ -88,6 +93,20 @@ static adns_query query_alloc(adns_state ads, + return qu; + } + ++static adns_queryflags default_ip6_flags(adns_state ads) ++{ ++ adns_queryflags flags = 0; ++ ++ if (!(ads->iflags & adns_if_ip4only)) ++ flags |= adns_qf_ip4; ++ if (!(ads->iflags & adns_if_ip6only)) ++ flags |= adns_qf_ip6; ++ if (ads->iflags & adns_if_ip6mapped) ++ flags |= adns_qf_ip6mapped; ++ ++ return flags; ++} ++ + static void query_submit(adns_state ads, adns_query qu, + const typeinfo *typei, vbuf *qumsg_vb, int id, + adns_queryflags flags, struct timeval now) { +@@ -108,6 +127,7 @@ static void query_submit(adns_state ads, adns_query qu, + adns__query_send(qu,now); + } + ++/* FIXME: Take a adns_rrtype type artument? */ + adns_status adns__internal_submit(adns_state ads, adns_query *query_r, + const typeinfo *typei, vbuf *qumsg_vb, + int id, +@@ -115,12 +135,26 @@ adns_status adns__internal_submit(adns_state ads, adns_query *query_r, + const qcontext *ctx) { + adns_query qu; + ++ if (!(flags & adns__qf_ip_mask)) ++ flags |= default_ip6_flags(ads); ++ + qu= query_alloc(ads,typei,typei->typekey,flags,now); + if (!qu) { adns__vbuf_free(qumsg_vb); return adns_s_nomemory; } + *query_r= qu; + + memcpy(&qu->ctx,ctx,sizeof(qu->ctx)); +- query_submit(ads,qu, typei,qumsg_vb,id,flags,now); ++ ++ if (typei->submithook) { ++ qu->vb = *qumsg_vb; ++ adns__vbuf_init(qumsg_vb); ++ ++ typei->submithook(qu, flags, now); ++ if (qu->children.head) { ++ qu->state= query_childw; ++ LIST_LINK_TAIL(ads->childw,qu); ++ } ++ } ++ else query_submit(ads,qu, typei,qumsg_vb,id,flags,now); + + return adns_s_ok; + } +@@ -133,21 +167,32 @@ static void query_simple(adns_state ads, adns_query qu, + int id; + adns_status stat; + +- stat= adns__mkquery(ads,&qu->vb,&id, owner,ol, +- typei,qu->answer->type, flags); +- if (stat) { +- if (stat == adns_s_querydomaintoolong && (flags & adns_qf_search)) { +- adns__search_next(ads,qu,now); +- return; +- } else { +- adns__query_fail(qu,stat); +- return; ++ if (typei->submithook) { ++ stat= adns__mkquery_labels(ads, &qu->vb, owner, ol, typei, flags); ++ if (stat) goto fail; ++ ++ typei->submithook(qu, flags, now); ++ if (qu->children.head) { ++ qu->state= query_childw; ++ LIST_LINK_TAIL(ads->childw,qu); + } ++ return; + } ++ else { ++ stat= adns__mkquery(ads,&qu->vb,&id, owner,ol, ++ typei,qu->answer->type,flags); ++ if (stat) goto fail; + + vb_new= qu->vb; + adns__vbuf_init(&qu->vb); + query_submit(ads,qu, typei,&vb_new,id, flags,now); ++ return; ++ } ++ fail: ++ if (stat == adns_s_querydomaintoolong && (flags & adns_qf_search)) ++ adns__search_next(ads,qu,now); ++ else ++ adns__query_fail(qu,stat); + } + + void adns__search_next(adns_state ads, adns_query qu, struct timeval now) { +@@ -222,6 +267,9 @@ int adns_submit(adns_state ads, + + adns__consistency(ads,0,cc_entex); + ++ if (!(flags & adns__qf_ip_mask)) ++ flags |= default_ip6_flags(ads); ++ + typei= adns__findtype(type); + if (!typei) return ENOSYS; + +@@ -288,13 +336,13 @@ int adns_submit_reverse_any(adns_state ads, + + flags &= ~adns_qf_search; + +- if (addr->sa_family != AF_INET) return ENOSYS; +- iaddr= (const unsigned char*) +- &(((const struct sockaddr_in*)addr) -> sin_addr); +- ++ switch (addr->sa_family) { ++ default: return ENOSYS; ++ case AF_INET: ++ iaddr= (const unsigned char*) &((const struct sockaddr_in*)addr)->sin_addr; + lreq= strlen(zone) + 4*4 + 1; + if (lreq > sizeof(shortbuf)) { +- buf= malloc(strlen(zone) + 4*4 + 1); ++ buf= malloc(lreq); + if (!buf) return errno; + buf_free= buf; + } else { +@@ -302,7 +350,32 @@ int adns_submit_reverse_any(adns_state ads, + buf_free= 0; + } + sprintf(buf, "%d.%d.%d.%d.%s", iaddr[3], iaddr[2], iaddr[1], iaddr[0], zone); +- ++ break; ++ case AF_INET6: ++ iaddr= (const unsigned char*) &((const struct sockaddr_in6*)addr)->sin6_addr; ++ lreq = strlen(zone) + 2*32 + 1; ++ if (lreq > sizeof(shortbuf)) { ++ buf= malloc(lreq); ++ if (!buf) return errno; ++ buf_free= buf; ++ } ++ else { ++ buf= shortbuf; ++ buf_free= 0; ++ } ++ strcpy(buf + 2*32, zone); ++ { ++ int i; ++ const unsigned char *p; ++ static const unsigned char hex[16] = "0123456789abcdef"; ++ for (i = 0, p = iaddr + 15; i < 2*32; p--) { ++ buf[i++] = hex[*p & 0xf]; ++ buf[i++] = '.'; ++ buf[i++] = hex[*p / 0x10]; ++ buf[i++] = '.'; ++ } ++ } ++ } + r= adns_submit(ads,buf,type,flags,context,query_r); + free(buf_free); + return r; +@@ -314,9 +387,34 @@ int adns_submit_reverse(adns_state ads, + adns_queryflags flags, + void *context, + adns_query *query_r) { ++ int r; ++ /* Address record used for forward lookup and consistency check */ ++ adns_rr_addr rr; ++ const char *zone; ++ + if (type != adns_r_ptr && type != adns_r_ptr_raw) return EINVAL; +- return adns_submit_reverse_any(ads,addr,"in-addr.arpa", ++ memset(&rr, 0, sizeof(rr)); ++ rr.addr.sa.sa_family = addr->sa_family; ++ ++ switch (addr->sa_family) { ++ default: return ENOSYS; ++ case AF_INET: ++ zone = "in-addr.arpa"; ++ rr.len = sizeof(rr.addr.inet); ++ rr.addr.inet.sin_addr = ((const struct sockaddr_in *)addr)->sin_addr; ++ break; ++ case AF_INET6: ++ zone = "ip6.arpa"; ++ rr.len = sizeof(rr.addr.inet6); ++ rr.addr.inet6.sin6_addr = ((const struct sockaddr_in6 *)addr)->sin6_addr; ++ break; ++ } ++ ++ r= adns_submit_reverse_any(ads,addr,zone, + type,flags,context,query_r); ++ if (r) return r; ++ (*query_r)->extra.info.ptr_addr = rr; ++ return 0; + } + + int adns_synchronous(adns_state ads, +@@ -344,9 +442,36 @@ static void *alloc_common(adns_query qu, size_t sz) { + an= malloc(MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz)); + if (!an) return 0; + LIST_LINK_TAIL(qu->allocations,an); ++ an->size = sz; + return (byte*)an + MEM_ROUND(sizeof(*an)); + } + ++void *adns__realloc_interim(adns_query qu, void *p, size_t sz) { ++ allocnode *an; ++ allocnode *nan; ++ ++ sz = MEM_ROUND(sz); ++ assert(sz); /* Freeing via realloc not supported */ ++ assert(!qu->final_allocspace); ++ ++ an = (allocnode *) ((byte *) p - MEM_ROUND(sizeof(*an))); ++ assert(an->size <= qu->interim_allocd); ++ ++ nan = realloc(an, MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz)); ++ if (!nan) return 0; ++ ++ qu->interim_allocd -= nan->size; ++ qu->interim_allocd += sz; ++ nan->size = sz; ++ ++ if (nan->next) nan->next->back = nan; ++ else qu->allocations.tail = nan; ++ if (nan->back) nan->back->next = nan; ++ else qu->allocations.head = nan; ++ ++ return (byte*)nan + MEM_ROUND(sizeof(*nan)); ++} ++ + void *adns__alloc_interim(adns_query qu, size_t sz) { + void *rv; + +diff --git a/src/setup.c b/src/setup.c +index 44c3cee..07b1f13 100644 +--- a/src/setup.c ++++ b/src/setup.c +@@ -5,6 +5,7 @@ + */ + /* + * This file is part of adns, which is ++ * Copyright (C) 2009 Luca Bruno + * Copyright (C) 1997-2000,2003,2006 Ian Jackson + * Copyright (C) 1999-2000,2003,2006 Tony Finch + * Copyright (C) 1991 Massachusetts Institute of Technology +@@ -41,12 +42,12 @@ + + static void readconfig(adns_state ads, const char *filename, int warnmissing); + +-static void addserver(adns_state ads, struct in_addr addr) { ++static void addserverv4(adns_state ads, struct in_addr addr) { + int i; + struct server *ss; + + for (i=0; inservers; i++) { +- if (ads->servers[i].addr.s_addr == addr.s_addr) { ++ if ((ads->servers[i].sin_family == AF_INET) && (ads->servers[i].addr.s_addr == addr.s_addr)) { + adns__debug(ads,-1,0,"duplicate nameserver %s ignored",inet_ntoa(addr)); + return; + } +@@ -58,10 +59,35 @@ static void addserver(adns_state ads, struct in_addr addr) { + } + + ss= ads->servers+ads->nservers; ++ ss->sin_family= AF_INET; + ss->addr= addr; + ads->nservers++; + } + ++static void addserverv6(adns_state ads, struct in6_addr addr) { ++ int i; ++ struct server *ss; ++ char buf[INET6_ADDRSTRLEN]; ++ ++ for (i=0; inservers; i++) { ++ if ((ads->servers[i].sin_family == AF_INET6) && !(memcmp(&(ads->servers[i].addr6.s6_addr), &(addr.s6_addr), sizeof(struct in6_addr)))) { ++ adns__debug(ads,-1,0,"duplicate nameserver %s ignored", inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN*sizeof(char))); ++ return; ++ } ++ } ++ ++ if (ads->nservers>=MAXSERVERS) { ++ adns__diag(ads,-1,0,"too many nameservers, ignoring %s", inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN*sizeof(char))); ++ return; ++ } ++ ++ ss= ads->servers+ads->nservers; ++ ss->sin_family= AF_INET6; ++ ss->addr6= addr; ++ ads->nservers++; ++} ++ ++ + static void freesearchlist(adns_state ads) { + if (ads->nsearchlist) free(*ads->searchlist); + free(ads->searchlist); +@@ -105,16 +131,28 @@ static int nextword(const char **bufp_io, const char **word_r, int *l_r) { + + static void ccf_nameserver(adns_state ads, const char *fn, + int lno, const char *buf) { +- struct in_addr ia; +- +- if (!inet_aton(buf,&ia)) { +- configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf); +- return; ++ struct in_addr ia4; ++ struct in6_addr ia6; ++ char ns_name[INET6_ADDRSTRLEN]; ++ ++ if (!inet_aton(buf,&ia4)) { ++ if (!inet_pton(AF_INET6, buf,&ia6)) { ++ configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf); ++ return; ++ } ++ else { ++ adns__debug(ads,-1,0,"using nameserver %s", inet_ntop(AF_INET6, &ia6, ns_name, INET6_ADDRSTRLEN*sizeof(char))); ++ addserverv6(ads,ia6); ++ ++ } ++ } ++ else { ++ adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia4)); ++ addserverv4(ads,ia4); + } +- adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia)); +- addserver(ads,ia); + } + ++ + static void ccf_search(adns_state ads, const char *fn, + int lno, const char *buf) { + const char *bufp, *word; +@@ -150,6 +188,7 @@ static void ccf_search(adns_state ads, const char *fn, + + static void ccf_sortlist(adns_state ads, const char *fn, + int lno, const char *buf) { ++ /* FIXME: Handle IPv6 addresses */ + const char *word; + char tbuf[200], *slash, *ep; + struct in_addr base, mask; +@@ -191,6 +230,21 @@ static void ccf_sortlist(adns_state ads, const char *fn, + " overlaps address `%s'",slash,tbuf); + continue; + } ++ { ++ /* Convert bitmask to prefix length */ ++ unsigned long bits; ++ ++ for(bits=ntohl(mask.s_addr), initial = 0; ++ bits & 0x80000000UL; ++ bits <<= 1) ++ initial++; ++ ++ if (bits & 0xffffffff) { ++ configparseerr(ads,fn,lno, ++ "mask `%s' in sortlist is non-continuous",slash); ++ continue; ++ } ++ } + } else { + initial= strtoul(slash,&ep,10); + if (*ep || initial>32) { +@@ -202,11 +256,11 @@ static void ccf_sortlist(adns_state ads, const char *fn, + } else { + baselocal= ntohl(base.s_addr); + if (!baselocal & 0x080000000UL) /* class A */ +- mask.s_addr= htonl(0x0ff000000UL); ++ initial = 8; + else if ((baselocal & 0x0c0000000UL) == 0x080000000UL) +- mask.s_addr= htonl(0x0ffff0000UL); /* class B */ ++ initial= 16; /* class B */ + else if ((baselocal & 0x0f0000000UL) == 0x0e0000000UL) +- mask.s_addr= htonl(0x0ff000000UL); /* class C */ ++ initial= 24; /* class C */ + else { + configparseerr(ads,fn,lno, "network address `%s'" + " in sortlist is not in classed ranges," +@@ -215,8 +269,10 @@ static void ccf_sortlist(adns_state ads, const char *fn, + } + } + +- ads->sortlist[ads->nsortlist].base= base; +- ads->sortlist[ads->nsortlist].mask= mask; ++ ads->sortlist[ads->nsortlist].family= AF_INET; ++ ads->sortlist[ads->nsortlist].base.inet= base; ++ ads->sortlist[ads->nsortlist].prefix= initial; ++ + ads->nsortlist++; + } + } +@@ -522,7 +578,7 @@ static int init_begin(adns_state *ads_r, adns_initflags flags, + LIST_INIT(ads->output); + ads->forallnext= 0; + ads->nextid= 0x311f; +- ads->udpsocket= ads->tcpsocket= -1; ++ ads->udpsocket= ads->udpsocket6= ads->tcpsocket= -1; + adns__vbuf_init(&ads->tcpsend); + adns__vbuf_init(&ads->tcprecv); + ads->tcprecv_skip= 0; +@@ -550,16 +606,22 @@ static int init_finish(adns_state ads) { + if (ads->logfn && ads->iflags & adns_if_debug) + adns__lprintf(ads,"adns: no nameservers, using localhost\n"); + ia.s_addr= htonl(INADDR_LOOPBACK); +- addserver(ads,ia); ++ addserverv4(ads,ia); + } + + proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; } + ads->udpsocket= socket(AF_INET,SOCK_DGRAM,proto->p_proto); + if (ads->udpsocket<0) { r= errno; goto x_free; } + ++ ads->udpsocket6= socket(AF_INET6,SOCK_DGRAM,proto->p_proto); ++ if (ads->udpsocket6<0) { r= errno; goto x_free6; } ++ + r= adns__setnonblock(ads,ads->udpsocket); + if (r) { r= errno; goto x_closeudp; } + ++ r= adns__setnonblock(ads,ads->udpsocket6); ++ if (r) { r= errno; goto x_closeudp6; } ++ + return 0; + + x_closeudp: +@@ -567,6 +629,12 @@ static int init_finish(adns_state ads) { + x_free: + free(ads); + return r; ++ ++ x_closeudp6: ++ close(ads->udpsocket6); ++ x_free6: ++ free(ads); ++ return r; + } + + static void init_abort(adns_state ads) { +@@ -678,7 +746,10 @@ void adns_finish(adns_state ads) { + else if (ads->output.head) adns_cancel(ads->output.head); + else break; + } +- close(ads->udpsocket); ++ if (ads->udpsocket >= 0) ++ close(ads->udpsocket); ++ if (ads->udpsocket6 >= 0) ++ close(ads->udpsocket6); + if (ads->tcpsocket >= 0) close(ads->tcpsocket); + adns__vbuf_free(&ads->tcpsend); + adns__vbuf_free(&ads->tcprecv); +diff --git a/src/transmit.c b/src/transmit.c +index 7afb90f..fb733fc 100644 +--- a/src/transmit.c ++++ b/src/transmit.c +@@ -5,6 +5,7 @@ + */ + /* + * This file is part of adns, which is ++ * Copyright (C) 2009 Luca Bruno + * Copyright (C) 1997-2000,2003,2006 Ian Jackson + * Copyright (C) 1999-2000,2003,2006 Tony Finch + * Copyright (C) 1991 Massachusetts Institute of Technology +@@ -62,6 +63,8 @@ static adns_status mkquery_header(adns_state ads, vbuf *vb, + return adns_s_ok; + } + ++/* FIXME: Return value is always adns_s_ok, and never used. But I ++ * don't understand why we can assert that we have space in the vbuf. */ + static adns_status mkquery_footer(vbuf *vb, adns_rrtype type) { + byte *rqp; + +@@ -118,17 +121,15 @@ adns_status adns__qdpl_normal(adns_state ads, + return adns_s_ok; + } + +-adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r, ++adns_status adns__mkquery_labels(adns_state ads, vbuf *vb, + const char *owner, int ol, +- const typeinfo *typei, adns_rrtype type, +- adns_queryflags flags) { ++ const typeinfo *typei, adns_queryflags flags) { + int labelnum, ll, nbytes; +- byte label[255]; +- byte *rqp; ++ byte label[255], *rqp; + const char *p, *pe; + adns_status st; + +- st= mkquery_header(ads,vb,id_r,ol+2); if (st) return st; ++ if (!adns__vbuf_ensure(vb,ol+2)) return adns_s_nomemory; + + MKQUERY_START(vb); + +@@ -149,22 +150,31 @@ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r, + MKQUERY_ADDB(0); + + MKQUERY_STOP(vb); ++ return adns_s_ok; ++} ++ ++adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r, ++ const char *owner, int ol, ++ const typeinfo *typei, adns_rrtype type, ++ adns_queryflags flags) { ++ adns_status st; + ++ st= mkquery_header(ads,vb,id_r,ol+2); if (st) return st; ++ st= adns__mkquery_labels(ads, vb, owner, ol, typei, flags); if (st) return st; + st= mkquery_footer(vb,type); + + return adns_s_ok; + } + +-adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r, ++adns_status adns__mkquery_labels_frdgram(adns_state ads, vbuf *vb, + const byte *qd_dgram, int qd_dglen, +- int qd_begin, +- adns_rrtype type, adns_queryflags flags) { ++ int qd_begin) { ++ adns_status st; + byte *rqp; + findlabel_state fls; + int lablen, labstart; +- adns_status st; + +- st= mkquery_header(ads,vb,id_r,qd_dglen); if (st) return st; ++ if (!adns__vbuf_ensure(vb,qd_dglen)) return adns_s_nomemory; + + MKQUERY_START(vb); + +@@ -181,6 +191,30 @@ adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r, + + MKQUERY_STOP(vb); + ++ return adns_s_ok; ++} ++ ++adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r, ++ const byte *qd_dgram, int qd_dglen, ++ int qd_begin, ++ adns_rrtype type, adns_queryflags flags) { ++ adns_status st; ++ ++ st= mkquery_header(ads,vb,id_r,qd_dglen); if (st) return st; ++ st= adns__mkquery_labels_frdgram(ads, vb, qd_dgram, qd_dglen, qd_begin); ++ if (st) return st; ++ st= mkquery_footer(vb,type); ++ ++ return adns_s_ok; ++} ++ ++adns_status adns__mkquery_frlabels(adns_state ads, vbuf *vb, int *id_r, ++ char *l, int llen, ++ adns_rrtype type, adns_queryflags flags) { ++ adns_status st; ++ ++ st= mkquery_header(ads,vb,id_r,llen); if (st) return st; ++ if (!adns__vbuf_append(vb, l, llen)) return adns_s_nomemory; + st= mkquery_footer(vb,type); + + return adns_s_ok; +@@ -251,6 +285,7 @@ static void query_usetcp(adns_query qu, struct timeval now) { + + void adns__query_send(adns_query qu, struct timeval now) { + struct sockaddr_in servaddr; ++ struct sockaddr_in6 servaddr6; + int serv, r; + adns_state ads; + +@@ -266,15 +301,25 @@ void adns__query_send(adns_query qu, struct timeval now) { + } + + serv= qu->udpnextserver; +- memset(&servaddr,0,sizeof(servaddr)); +- + ads= qu->ads; +- servaddr.sin_family= AF_INET; +- servaddr.sin_addr= ads->servers[serv].addr; +- servaddr.sin_port= htons(DNS_PORT); ++ ++ if(ads->servers[serv].sin_family == AF_INET) { ++ memset(&servaddr,0,sizeof(servaddr)); ++ servaddr.sin_family= ads->servers[serv].sin_family; ++ servaddr.sin_addr= ads->servers[serv].addr; ++ servaddr.sin_port= htons(DNS_PORT); ++ r= sendto(ads->udpsocket,qu->query_dgram,qu->query_dglen,0, ++ (const struct sockaddr*)&servaddr,sizeof(servaddr)); ++ } else { ++ memset(&servaddr6,0,sizeof(servaddr6)); ++ servaddr6.sin6_family= ads->servers[serv].sin_family; ++ servaddr6.sin6_addr= ads->servers[serv].addr6; ++ servaddr6.sin6_port= htons(DNS_PORT); ++ r= sendto(ads->udpsocket6,qu->query_dgram,qu->query_dglen,0, ++ (const struct sockaddr*)&servaddr6,sizeof(servaddr6)); ++ } ++ + +- r= sendto(ads->udpsocket,qu->query_dgram,qu->query_dglen,0, +- (const struct sockaddr*)&servaddr,sizeof(servaddr)); + if (r<0 && errno == EMSGSIZE) { + qu->retries= 0; + query_usetcp(qu,now); +diff --git a/src/types.c b/src/types.c +index 36ff879..712ae69 100644 +--- a/src/types.c ++++ b/src/types.c +@@ -48,12 +48,15 @@ + * _manyistr (mf,cs) + * _txt (pa) + * _inaddr (pa,dip,di) +- * _addr (pa,di,csp,cs) ++ * _in6addr (pa,cs) ++ * _addr (sh,di,csp,cs) + * _domain (pap) + * _host_raw (pa) + * _hostaddr (pap,pa,dip,di,mfp,mf,csp,cs +pap_findaddrs) + * _mx_raw (pa,di) + * _mx (pa,di) ++ * _srv_raw (pa,di,mf,cs) ++ * _srv (pa,di,mf,cs) + * _inthostaddr (mf,cs) + * _ptr (pa) + * _strpair (mf,cs) +@@ -251,14 +254,20 @@ static adns_status pa_inaddr(const parseinfo *pai, int cbyte, + return adns_s_ok; + } + +-static int search_sortlist(adns_state ads, struct in_addr ad) { ++static int search_sortlist_in(adns_state ads, struct in_addr ad) { + const struct sortlist *slp; + int i; + + for (i=0, slp=ads->sortlist; +- insortlist && +- !((ad.s_addr & slp->mask.s_addr) == slp->base.s_addr); +- i++, slp++); ++ insortlist; ++ i++, slp++) { ++ if (slp->family == AF_INET) { ++ struct in_addr mask; ++ mask.s_addr = htonl(-1 << slp->prefix); ++ if ( (ad.s_addr & mask.s_addr ) == slp->base.inet.s_addr) ++ break; ++ } ++ } + return i; + } + +@@ -267,8 +276,8 @@ static int dip_inaddr(adns_state ads, struct in_addr a, struct in_addr b) { + + if (!ads->nsortlist) return 0; + +- ai= search_sortlist(ads,a); +- bi= search_sortlist(ads,b); ++ ai= search_sortlist_in(ads,a); ++ bi= search_sortlist_in(ads,b); + return bidgram + cbyte, 16); ++ return adns_s_ok; ++} ++ ++static int search_sortlist_in6(adns_state ads, const struct in6_addr *ad) { ++ const struct sortlist *slp; ++ int i; ++ ++ for (i=0, slp=ads->sortlist; ++ insortlist; ++ i++, slp++) { ++ if (slp->family == AF_INET6) { ++ int pb = slp->prefix / 8; ++ int mask = 0xff & (-1 << (slp->prefix % 8)); ++ if (memcmp(ad->s6_addr, slp->base.inet6.s6_addr, pb) == 0 ++ && (!mask ++ || (ad->s6_addr[pb] & mask) == slp->base.inet6.s6_addr[pb])) ++ break; ++ } ++ } ++ return i; ++} ++ ++static int dip_in6addr(adns_state ads, ++ const struct in6_addr *a, const struct in6_addr *b) { ++ int ai, bi; ++ ++ if (!ads->nsortlist) return 0; ++ ++ ai= search_sortlist_in6(ads,a); ++ bi= search_sortlist_in6(ads,b); ++ return bisin6_family = AF_INET6; ++ sa->sin6_addr.s6_addr16[5] = 0xffff; ++ sa->sin6_addr.s6_addr32[3] = in->s_addr; ++} ++ ++static void icb_addr(adns_query parent, adns_query child) { ++ adns_answer *cans= child->answer; ++ adns_answer *pans= parent->answer; ++ adns_state ads= parent->ads; ++ adns_rr_addr *addr; ++ ++ int i; ++ ++ if (parent->expires > child->expires) parent->expires = child->expires; ++ ++ if (cans->status == adns_s_nxdomain) { ++ adns__query_fail(parent,cans->status); ++ return; ++ } ++ if (cans->status == adns_s_nodata && parent->children.head) { ++ /* We may get records from the remaining queries */ ++ LIST_LINK_TAIL(ads->childw,parent); ++ return; ++ } ++ if (cans->status) { ++ if (pans->nrrs) ++ adns__query_done(parent); ++ else ++ adns__query_fail(parent,cans->status); ++ return; ++ } ++ ++ assert(cans->nrrs); ++ ++ /* Copy CNAME. CNAME must be consistent for both queries. */ ++ if (cans->cname && pans->cname) { ++ if (strcmp(cans->cname, pans->cname)) { ++ adns__query_fail(parent, adns_s_inconsistent); ++ return; ++ } ++ } ++ else if (pans->cname) { ++ adns__query_fail(parent, adns_s_inconsistent); ++ return; ++ } ++ else if (cans->cname) { ++ size_t len; ++ if (pans->nrrs) { ++ adns__query_fail(parent, adns_s_inconsistent); ++ return; ++ } ++ len = strlen(cans->cname) + 1; ++ pans->cname = adns__alloc_preserved(parent, len); ++ if (!pans->cname) { ++ adns__query_fail(parent, adns_s_nomemory); ++ return; ++ } ++ memcpy(pans->cname, cans->cname, len); ++ } ++ if (pans->nrrs) ++ { ++ void *p = adns__realloc_interim(parent,pans->rrs.untyped, ++ sizeof(adns_rr_addr) * (cans->nrrs + pans->nrrs)); ++ if (!p) { ++ adns__query_fail(parent, adns_s_nomemory); ++ return; ++ } ++ pans->rrs.untyped = p; ++ addr = pans->rrs.addr + pans->nrrs; ++ pans->nrrs += cans->nrrs; ++ } ++ else { ++ pans->rrs.untyped ++ = adns__alloc_interim(parent,sizeof(adns_rr_addr) * cans->nrrs); ++ if (!pans->rrs.untyped) { ++ adns__query_fail(parent,adns_s_nomemory); ++ return; ++ } ++ pans->nrrs = cans->nrrs; ++ addr = pans->rrs.addr; ++ } ++ ++ switch (cans->type) { ++ default: abort(); ++ case adns_r_a: ++ if (parent->flags & adns_qf_ip6mapped) ++ for (i = 0; inrrs; i++) { ++ addr[i].len = sizeof(struct sockaddr_in6); ++ mk_mapped_ipv6(&addr[i].addr.inet6, &cans->rrs.inaddr[i]); ++ } ++ else ++ for (i = 0; inrrs; i++) { ++ addr[i].len = sizeof(struct sockaddr_in); ++ memset(&addr[i].addr.inet, 0, sizeof(addr[i].addr.inet)); ++ addr[i].addr.inet.sin_family = AF_INET; ++ addr[i].addr.inet.sin_addr = cans->rrs.inaddr[i]; ++ } ++ break; ++ case adns_r_aaaa: ++ for (i = 0; inrrs; i++) { ++ addr[i].len = sizeof(struct sockaddr_in6); ++ memset(&addr[i].addr.inet6, 0, sizeof(addr[i].addr.inet6)); ++ addr[i].addr.inet6.sin6_family = AF_INET6; ++ addr[i].addr.inet6.sin6_addr = cans->rrs.in6addr[i]; ++ } ++ break; ++ } ++ ++ if (!parent->children.head) { ++ adns__query_done(parent); ++ return; ++ } else { ++ LIST_LINK_TAIL(ads->childw,parent); ++ return; ++ } ++} ++ ++static void sh_addr(adns_query qu, ++ adns_queryflags flags, struct timeval now) ++{ ++ adns_status st; ++ int id; ++ qcontext ctx; ++ adns_query nqu; ++ vbuf vb; ++ ++ assert(flags & adns__qf_ip_mask); ++ ++ /* Must have a non-negative id, or else adns__internal_check will ++ * think that we are on the output queue. */ ++ qu->id = 0; ++ ++ ctx.ext= 0; ++ ctx.callback= icb_addr; ++ /* What to store in ctx.info? */ ++ ++ adns__vbuf_init(&vb); ++ ++ if (flags & adns_qf_ip4) { /* A query */ ++ st= adns__mkquery_frlabels(qu->ads, &vb, &id, ++ qu->vb.buf, qu->vb.used, adns_r_a, flags); ++ if (st) { adns__query_fail(qu, st); return; } ++ ++ st= adns__internal_submit(qu->ads, &nqu, adns__findtype(adns_r_a), ++ &vb, id, flags, now, &ctx); ++ if (st) { adns__query_fail(qu, st); return; } ++ ++ nqu->parent = qu; ++ LIST_LINK_TAIL_PART(qu->children,nqu,siblings.); ++ } ++ ++ if (flags & adns_qf_ip6) { /* AAAA query */ ++ st= adns__mkquery_frlabels(qu->ads, &vb, &id, ++ qu->vb.buf, qu->vb.used, adns_r_aaaa, flags); ++ if (st) { adns__query_fail(qu, st); return; } ++ ++ st= adns__internal_submit(qu->ads, &nqu, adns__findtype(adns_r_aaaa), ++ &vb, id, flags, now, &ctx); ++ if (st) { adns__query_fail(qu, st); return; } ++ ++ nqu->parent = qu; ++ LIST_LINK_TAIL_PART(qu->children,nqu,siblings.); ++ } ++ assert(qu->children.head); ++} ++ ++static adns_status pap_addr(const parseinfo *pai, adns_rrtype type, int cbyte, ++ int max, adns_rr_addr *rr) { ++ + const byte *dgram= pai->dgram; ++ adns_queryflags flags = pai->qu->flags; ++ ++ switch (type) ++ { ++ default: abort(); ++ case adns_r_a: ++ assert(flags & adns_qf_ip4); + + if (max-cbyte != 4) return adns_s_invaliddata; +- storeto->len= sizeof(storeto->addr.inet); +- memset(&storeto->addr,0,sizeof(storeto->addr.inet)); +- storeto->addr.inet.sin_family= AF_INET; +- memcpy(&storeto->addr.inet.sin_addr,dgram+cbyte,4); ++ ++ if (flags & adns_qf_ip6mapped) { ++ rr->len = sizeof(struct sockaddr_in6); ++ mk_mapped_ipv6(&rr->addr.inet6, (const struct in_addr *) (dgram+cbyte)); ++ } ++ else { ++ rr->len= sizeof(rr->addr.inet); ++ memset(&rr->addr.inet,0,sizeof(rr->addr.inet)); ++ rr->addr.inet.sin_family= AF_INET; ++ memcpy(&rr->addr.inet.sin_addr,dgram+cbyte,4); ++ } ++ break; ++ case adns_r_aaaa: ++ assert(flags & adns_qf_ip6); ++ ++ if (max-cbyte != 16) return adns_s_invaliddata; ++ ++ rr->len= sizeof(rr->addr.inet6); ++ memset(&rr->addr,0,sizeof(rr->addr.inet6)); ++ rr->addr.inet6.sin6_family= AF_INET6; ++ memcpy(&rr->addr.inet6.sin6_addr,dgram+cbyte,16); ++ ++ break; ++ } ++ + return adns_s_ok; + } + ++static int search_sortlist_addr(adns_state ads, const adns_rr_addr *ad) { ++ switch(ad->addr.sa.sa_family) { ++ default: abort(); ++ case AF_INET: return search_sortlist_in(ads, ad->addr.inet.sin_addr); ++ case AF_INET6: return search_sortlist_in6(ads, &ad->addr.inet6.sin6_addr); ++ } ++} ++ ++static int dip_addr(adns_state ads, ++ const adns_rr_addr *a, const adns_rr_addr *b) { ++ int ai, bi; ++ ai = search_sortlist_addr(ads, a); ++ bi = search_sortlist_addr(ads, b); ++ return biaddr.sa.sa_family == AF_INET); +- return dip_inaddr(ads, ap->addr.inet.sin_addr, bp->addr.inet.sin_addr); ++ return dip_addr(ads, ap, bp); + } + + static int div_addr(void *context, const void *datap_a, const void *datap_b) { +@@ -320,7 +599,7 @@ static int div_addr(void *context, const void *datap_a, const void *datap_b) { + + static adns_status csp_addr(vbuf *vb, const adns_rr_addr *rrp) { + const char *ia; +- char buf[30]; ++ char buf[INET6_ADDRSTRLEN]; + + switch (rrp->addr.inet.sin_family) { + case AF_INET: +@@ -328,6 +607,12 @@ static adns_status csp_addr(vbuf *vb, const adns_rr_addr *rrp) { + ia= inet_ntoa(rrp->addr.inet.sin_addr); assert(ia); + CSP_ADDSTR(ia); + break; ++ case AF_INET6: ++ CSP_ADDSTR("INET6 "); ++ ia= inet_ntop(AF_INET6, &rrp->addr.inet6.sin6_addr, ++ buf, sizeof(buf)); assert(ia); ++ CSP_ADDSTR(ia); ++ break; + default: + sprintf(buf,"AF=%u",rrp->addr.sa.sa_family); + CSP_ADDSTR(buf); +@@ -424,17 +709,22 @@ static adns_status pap_findaddrs(const parseinfo *pai, adns_rr_hostaddr *ha, + &type, &class, &ttl, &rdlen, &rdstart, + pai->dgram, pai->dglen, dmstart, &ownermatched); + if (st) return st; +- if (!ownermatched || class != DNS_CLASS_IN || type != adns_r_a) { ++ if (!ownermatched || class != DNS_CLASS_IN) { + if (naddrs>0) break; else continue; + } ++ if (! ((type == adns_r_a && (pai->qu->flags & adns_qf_ip4)) ++ || (type == adns_r_aaaa && (pai->qu->flags & adns_qf_ip6)))) { ++ if (naddrs>0) break; else continue; ++ } ++ + if (naddrs == -1) { + naddrs= 0; + } + if (!adns__vbuf_ensure(&pai->qu->vb, (naddrs+1)*sizeof(adns_rr_addr))) + R_NOMEM; + adns__update_expires(pai->qu,ttl,pai->now); +- st= pa_addr(pai, rdstart,rdstart+rdlen, +- pai->qu->vb.buf + naddrs*sizeof(adns_rr_addr)); ++ st= pap_addr(pai, type, rdstart,rdstart+rdlen, ++ (adns_rr_addr *) pai->qu->vb.buf + naddrs); + if (st) return st; + naddrs++; + } +@@ -476,7 +766,6 @@ static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io, + adns_status st; + int dmstart, cbyte; + qcontext ctx; +- int id; + adns_query nqu; + adns_queryflags nflags; + +@@ -500,9 +789,8 @@ static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io, + if (st) return st; + if (rrp->naddrs != -1) return adns_s_ok; + +- st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id, +- pai->dgram, pai->dglen, dmstart, +- adns_r_addr, adns_qf_quoteok_query); ++ st= adns__mkquery_labels_frdgram(pai->ads, &pai->qu->vb, ++ pai->dgram, pai->dglen, dmstart); + if (st) return st; + + ctx.ext= 0; +@@ -513,7 +801,7 @@ static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io, + if (!(pai->qu->flags & adns_qf_cname_loose)) nflags |= adns_qf_cname_forbid; + + st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr), +- &pai->qu->vb, id, nflags, pai->now, &ctx); ++ &pai->qu->vb, 0, nflags, pai->now, &ctx); + if (st) return st; + + nqu->parent= pai->qu; +@@ -539,11 +827,7 @@ static int dip_hostaddr(adns_state ads, + if (ap->astatus != bp->astatus) return ap->astatus; + if (ap->astatus) return 0; + +- assert(ap->addrs[0].addr.sa.sa_family == AF_INET); +- assert(bp->addrs[0].addr.sa.sa_family == AF_INET); +- return dip_inaddr(ads, +- ap->addrs[0].addr.inet.sin_addr, +- bp->addrs[0].addr.inet.sin_addr); ++ return dip_addr(ads, &ap->addrs[0], &bp->addrs[0]); + } + + static int di_hostaddr(adns_state ads, +@@ -717,7 +1001,7 @@ static void icb_ptr(adns_query parent, adns_query child) { + return; + } + +- queried= &parent->ctx.info.ptr_parent_addr; ++ queried= &parent->extra.info.ptr_addr; + for (i=0, found=cans->rrs.addr; inrrs; i++, found++) { + if (queried->len == found->len && + !memcmp(&queried->addr,&found->addr,queried->len)) { +@@ -734,18 +1018,12 @@ static void icb_ptr(adns_query parent, adns_query child) { + adns__query_fail(parent,adns_s_inconsistent); + } + ++/* FIXME: Completely different in adns-1.4. */ + static adns_status pa_ptr(const parseinfo *pai, int dmstart, + int max, void *datap) { +- static const char *const (expectdomain[])= { DNS_INADDR_ARPA }; +- + char **rrp= datap; + adns_status st; +- adns_rr_addr *ap; +- findlabel_state fls; +- char *ep; +- byte ipv[4]; +- char labbuf[4]; +- int cbyte, i, lablen, labstart, l, id; ++ int cbyte; + adns_query nqu; + qcontext ctx; + +@@ -755,48 +1033,20 @@ static adns_status pa_ptr(const parseinfo *pai, int dmstart, + if (st) return st; + if (cbyte != max) return adns_s_invaliddata; + +- ap= &pai->qu->ctx.info.ptr_parent_addr; +- if (!ap->len) { +- adns__findlabel_start(&fls, pai->ads, -1, pai->qu, +- pai->qu->query_dgram, pai->qu->query_dglen, +- pai->qu->query_dglen, DNS_HDRSIZE, 0); +- for (i=0; i<4; i++) { +- st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st); +- if (lablen<=0 || lablen>3) return adns_s_querydomainwrong; +- memcpy(labbuf, pai->qu->query_dgram + labstart, lablen); +- labbuf[lablen]= 0; +- ipv[3-i]= strtoul(labbuf,&ep,10); +- if (*ep) return adns_s_querydomainwrong; +- if (lablen>1 && pai->qu->query_dgram[labstart]=='0') +- return adns_s_querydomainwrong; +- } +- for (i=0; iqu->query_dgram + labstart, expectdomain[i], l)) +- return adns_s_querydomainwrong; +- } +- st= adns__findlabel_next(&fls,&lablen,0); assert(!st); +- if (lablen) return adns_s_querydomainwrong; +- +- ap->len= sizeof(struct sockaddr_in); +- memset(&ap->addr,0,sizeof(ap->addr.inet)); +- ap->addr.inet.sin_family= AF_INET; +- ap->addr.inet.sin_addr.s_addr= +- htonl((ipv[0]<<24) | (ipv[1]<<16) | (ipv[2]<<8) | (ipv[3])); +- } ++ /* Should be initialized by adns_submit_reverse. If it's not, we ++ * can't do any consistency checking. */ ++ if (!pai->qu->extra.info.ptr_addr.len) return adns_s_ok; + +- st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id, +- pai->dgram, pai->dglen, dmstart, +- adns_r_addr, adns_qf_quoteok_query); ++ pai->qu->vb.used = 0; ++ st= adns__mkquery_labels_frdgram(pai->ads, &pai->qu->vb, ++ pai->dgram, pai->dglen, dmstart); + if (st) return st; + + ctx.ext= 0; + ctx.callback= icb_ptr; + memset(&ctx.info,0,sizeof(ctx.info)); + st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr), +- &pai->qu->vb, id, ++ &pai->qu->vb, 0, + adns_qf_quoteok_query, pai->now, &ctx); + if (st) return st; + +@@ -1250,13 +1500,16 @@ static void mf_flat(adns_query qu, void *data) { } + + #define DEEP_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \ + { adns_r_##code, rrt,fmt,TYPESZ_M(memb), mf_##memb, \ +- printer,parser,comparer, adns__qdpl_normal,0 } ++ printer,0,parser,comparer, adns__qdpl_normal,0 } + #define FLAT_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \ + { adns_r_##code, rrt,fmt,TYPESZ_M(memb), mf_flat, \ +- printer,parser,comparer, adns__qdpl_normal,0 } ++ printer,0,parser,comparer, adns__qdpl_normal,0 } + #define XTRA_TYPE(code,rrt,fmt,memb,parser,comparer,printer,qdpl,postsort) \ + { adns_r_##code, rrt,fmt,TYPESZ_M(memb), mf_##memb, \ +- printer,parser,comparer,qdpl,postsort } ++ printer,0,parser,comparer,qdpl,postsort } ++#define SPECIAL_TYPE(code,rrt,fmt,memb,submit,comparer,printer) \ ++ { adns_r_##code, rrt,fmt,TYPESZ_M(memb), mf_flat, \ ++ printer,submit,0,comparer, adns__qdpl_normal,0 } + + static const typeinfo typeinfos[] = { + /* Must be in ascending order of rrtype ! */ +@@ -1271,10 +1524,11 @@ DEEP_TYPE(hinfo, "HINFO", 0, intstrpair,pa_hinfo, 0, cs_hinfo ), + DEEP_TYPE(mx_raw, "MX", "raw",intstr, pa_mx_raw, di_mx_raw,cs_inthost ), + DEEP_TYPE(txt, "TXT", 0, manyistr,pa_txt, 0, cs_txt ), + DEEP_TYPE(rp_raw, "RP", "raw",strpair, pa_rp, 0, cs_rp ), ++FLAT_TYPE(aaaa, "AAAA", 0, in6addr, pa_in6addr, di_in6addr, cs_in6addr ), + XTRA_TYPE(srv_raw,"SRV", "raw",srvraw , pa_srvraw, di_srv, cs_srvraw, + qdpl_srv, postsort_srv), + +-FLAT_TYPE(addr, "A", "addr", addr, pa_addr, di_addr, cs_addr ), ++/* adns__qtf_deref set */ + DEEP_TYPE(ns, "NS", "+addr",hostaddr,pa_hostaddr,di_hostaddr,cs_hostaddr ), + DEEP_TYPE(ptr, "PTR","checked",str, pa_ptr, 0, cs_domain ), + DEEP_TYPE(mx, "MX", "+addr",inthostaddr,pa_mx, di_mx, cs_inthostaddr), +@@ -1283,6 +1537,9 @@ XTRA_TYPE(srv, "SRV","+addr",srvha, pa_srvha, di_srv, cs_srvha, + + DEEP_TYPE(soa, "SOA","822", soa, pa_soa, 0, cs_soa ), + DEEP_TYPE(rp, "RP", "822", strpair, pa_rp, 0, cs_rp ), ++ ++/* adns__qtf_special set */ ++SPECIAL_TYPE(addr,"", "addr",addr,sh_addr, di_addr, cs_addr ), + }; + + static const typeinfo typeinfo_unknown= diff --git a/package/adns/adns-1.4-rh514838.patch b/package/adns/adns-1.4-rh514838.patch new file mode 100644 index 0000000..d0da303 --- /dev/null +++ b/package/adns/adns-1.4-rh514838.patch @@ -0,0 +1,26 @@ +Fixes Bug 514838 + +Upstream-Status: Pending +URL: https://bugzilla.redhat.com/show_bug.cgi?id=514838 + +diff -up adns-1.4/src/general.c.rh514838 adns-1.4/src/general.c +--- adns-1.4/src/general.c.rh514838 2006-04-08 16:36:57.000000000 +0200 ++++ adns-1.4/src/general.c 2009-08-06 13:55:06.752562767 +0200 +@@ -267,6 +267,8 @@ static const struct sinfo { + SINFO( nodata, "No such data" ) + }; + ++static const char *unknown_error_str = "unknown error code"; ++ + static int si_compar(const void *key, const void *elem) { + const adns_status *st= key; + const struct sinfo *si= elem; +@@ -283,7 +285,7 @@ const char *adns_strerror(adns_status st + const struct sinfo *si; + + si= findsinfo(st); +- return si->string; ++ return (si == NULL) ? unknown_error_str : si->string; + } + + const char *adns_errabbrev(adns_status st) { diff --git a/package/adns/adns.mk b/package/adns/adns.mk new file mode 100644 index 0000000..be4aefd --- /dev/null +++ b/package/adns/adns.mk @@ -0,0 +1,15 @@ +############################################################# +# +# adns +# +############################################################# + +ADNS_VERSION = 1.4 +ADNS_SOURCE = adns-$(ADNS_VERSION).tar.gz +ADNS_SITE = http://www.chiark.greenend.org.uk/~ian/adns/ftp/ +ADNS_AUTORECONF = YES +ADNS_INSTALL_STAGING = YES +ADNS_CONF_OPT += --enable-dynamic=elf + +$(eval $(autotools-package)) +