Subject: IPv6, resolv.conf, RFC2133, etc.

From: Ian Jackson <[email-masked]>
Date: Sun, 21 May 2000 18:43:57 +0100 (BST)

I'm working on an alternative resolver library, which is currently in
beta (www.chiark.greenend.org.uk/~ian/adns/).  I want to add IPv6
support, but it's not entirely clear to me what the appropriate
behaviour is in all cases.

This mailing list probably isn't quite the right place for my query,
but comp.protocols.dns.bind doesn't seem much better, so I hope the WG
will forgive the intrusion.  If not then please feel free to tell me
where else to go.  Thanks.

I have basically two sets of issues: one is what the syntax of
resolv.conf should be (and what extensions in it I should understand);
the other is what the resolver behaviour should be in various
circumstances.

My resolver can parse an additional configuration file, so if it's
absolutely necessary the sysadmin can configure adns and libresolv
differently, but this involves the admin writing two config files and
keeping them in step, so it seems like a bad idea to rely on it.

The `easy' part is querying a nameserver via IPv6: if I can parse the
resolv.conf to have an IPv6 address in it then I can just open IPv6
UDP and TCP sockets and use them.  However, it's not clear to me what
syntax I should accept in resolv.conf for this.  Should I simply
accept the form `nameserver <IPv6 address>' ?  Supposing IPv6-capable
resolvers should use an IPv6-addressable nameserver, but there is an
IPv4-addressable nameserver for non-IPv6-capable resolvers, will the
sysadmin be able to write something appropriate in the resolv.conf ?
What will libresolv do ?  If I support IPv6 transport, am I required
to support the various DNS extensions (which I don't currently) ?

The next most easy thing is querying for AAAA records when the caller
explicitly asks for them.  The only question there is what order to
sort them in.  Can I expect the `sortlist' directive to contain both
IPv4 and IPv6 addresses, or should I look for a separate `sortlist6'
directive or some such ?

The hardest thing is when the caller just wants `an address' and I
have to decide whether to query for A and/or AAAA, and whether to
return IPv4 results as IPv6-mapped.  RFC2133 says what libresolv does
(or is supposed to do), so I should do something vaguely similar.

I'll describe what my current plan is, and you people can tell me
whether that's a good idea:

By default, unless the programmer specifies IPv6 support when they
instantiate adns (or start a query), address lookups will only look
for A records and will return them as AF_INET.

If the programmer passes the IPv6 flag at init time (or for each
query) they are advertising that they're using the IPv6 socket API in
RFC2133.  adns will then do lookups for AAAA records first, and
failing that for A records.  If there are no AAAAs but only As, it
will return the As as AF_INET6.  Additionally, this flag makes queries
for A records return them IPv6-mapped too.

Optionally, the programmer can choose on a per-query basis never to
have IPv4 addresses returned IPv6-mapped; this will mean that A
queries always return AF_INET addresses, and that general address
queries may return either AF_INET6 or AF_INET addresses (but never
both).

There has to be some mechanism for the sysadmin to say whether
applications which are _compiled_ with IPv6 support should do AAAA
lookups and try to use IPv6.  Otherwise either applications need to be
recompiled according to whether a host has IPv6 connectivity, or they
will try to make IPv6 connections when there isn't any and need
explicit fallback code (which is tedious, and it's error prone to have
every application author implement this).

Therefore I propose to have adns never do AAAA queries for general
address lookups unless there's a particular option set in resolv.conf,
eg `options ipv6capable'.  NB that this is _not_ the same as the
RES_USE_INET6 option (`options inet6') which is contemplated in
RFC2133; I think that the behaviour of that option is so pathological
that it can't be used safely.

It might be useful to have a system-wide option to do A lookups _as
well as_ AAAA lookups, for hosts which are known to have `weird'
connectivity.  This option shouldn't be set by default, because it
effectively requires an extra check for IPv4 addresses for every
transaction for ever more.

The end result is that I expect a program which just wants to connect
to a service would do a lookup for addresses for a name (or a lookup
for SRV records, which returns a list of names with a list of
addresses for each).

They then simply call socket(), connect() on each one until they find
one that works.  If the application is not IPv6 capable then it will
only see IPv4 addresses.  If the application is it sets the IPv6 adns
initialisation option and it will then see IPv6 addresses if the
sysadmin has configured `options ipv6capable' and IPv6-mapped IPv4
addresses otherwise.

Comments ?

The API I'm thinking of can be seen in the file adns.h on the branch
branch-2000-05-07-ipv6 in my cvsweb, which is at
 http://www.chiark.greenend.org.uk/ucgi/~ijackson/cvsweb/adns/src/adns.h
I've included below the diff from the base of that branch.

Thanks for any input.

Ian.

Index: src/adns.h
===================================================================
RCS file: /usr/src/CVS/adns/src/adns.h,v
retrieving revision 1.77
retrieving revision 1.77.2.1
diff -u -r1.77 -r1.77.2.1
--- src/adns.h	2000/03/20 03:24:25	1.77
+++ src/adns.h	2000/05/07 13:50:08	1.77.2.1
@@ -51,7 +51,7 @@
  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
  *
- *  $Id: adns.h,v 1.77 2000/03/20 03:24:25 ian Exp $
+ *  $Id: adns.h,v 1.77.2.1 2000/05/07 13:50:08 ian Exp $
  */
 
 #ifndef ADNS_H_INCLUDED
@@ -68,6 +68,10 @@
 #include <sys/time.h>
 #include <unistd.h>
 
+#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;
@@ -84,6 +88,7 @@
   adns_if_nosigpipe=    0x0040, /* applic has SIGPIPE set to SIG_IGN, do not protect */
   adns_if_checkc_entex= 0x0100, /* do consistency checks on entry/exit to adns funcs */
   adns_if_checkc_freq=  0x0300  /* do consistency checks very frequently (slow!) */
+  adns_if_ip6=          0x1000  /* make default be adns_qf_ip6 */
 } adns_initflags;
 
 typedef enum {
@@ -96,9 +101,44 @@
   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 */
+  adns_qf_ip6=             0x00001000, /* return addrs as AF_INET6 in all cases */
+  adns_qf_ip6mixed=        0x00002000, /* return mixture of AF_INET and AF_INET6 */
+  adns_qf_ip4=             0x00003000, /* never return AF_INET6, even with _if_ip6 */
   adns__qf_internalmask=   0x0ff00000
 } adns_queryflags;
 
+/* IPv6 support:
+ *  adns_if_ip6 has the effect of changing the default from
+ *  adns_qf_ip4 to adns_qf_ip6.
+ *
+ * query type   _qf__ip4             _qf_ip6mixed           _qf_ip6
+ *
+ * _r_addr      A? => AF_INET        AAAA? => AF_INET6      AAAA? => AF_INET6
+ *              else fail            else A? => AF_INET     else A? => AF_INET6
+ *
+ * _r_a         A => AF_INET         A => AF_INET           A => AF_INET6
+ *
+ * _r_aaaa      AAAA => AF_INET6     AAAA => AF_INET6       AAAA => AF_INET6
+ *
+ * Ie,
+ *  _ip4:      only A lookups will be done, and everything is
+ *             returned as AF_INET addresses;
+ *  _ip6mixed: will look for AAAA records first, and if there
+ *             are any, return them as AF_INET6, otherwise like _ip4.
+ *  _ip6:      like _ip6mixed, but will return even IPv4 addresses in
+ *             IPv6-mapped form inside AF_INET6, for all query types.
+ *
+ * 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 and _r_addr without checking explicitly whether the host
+ * has IPv6 connectivity.
+ *
+ * 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 and perhaps produce extra data */
@@ -129,6 +169,11 @@
   adns_r_rp_raw=            17,
   adns_r_rp=                    adns_r_rp_raw|adns__qtf_mail822,
 
+  adns_r_aaaa=              28,
+
+  adns_r_srv_raw=           33,
+  adns_r_srv=                   adns_r_srv_raw|adns__qtf_deref,
+  
   adns_r_addr=                  adns_r_a|adns__qtf_deref
   
 } adns_rrtype;
@@ -251,6 +296,7 @@
   union {
     struct sockaddr sa;
     struct sockaddr_in inet;
+    struct sockaddr_in6 inet6;
   } addr;
 } adns_rr_addr;
 
@@ -290,6 +336,20 @@
 } adns_rr_soa;
 
 typedef struct {
+  unsigned short priority;
+  unsigned short weight;
+  unsigned short port;
+  adns_rr_hostaddr ha;
+} adns_rr_srv;
+
+typedef struct {
+  unsigned short priority;
+  unsigned short weight;
+  unsigned short port;
+  char *target;
+} adns_rr_srvraw;
+
+typedef struct {
   adns_status status;
   char *cname; /* always NULL if query was for CNAME records */
   char *owner; /* only set if requested in query flags, and may be 0 on error anyway */
@@ -303,12 +363,15 @@
     adns_rr_intstr *(*manyistr);      /* txt (list of strings ends with i=-1, str=0) */
     adns_rr_addr *addr;               /* addr */
     struct in_addr *inaddr;           /* a */
+    struct in6_addr *inaddr6;         /* aaaa */
     adns_rr_hostaddr *hostaddr;       /* ns */
     adns_rr_intstrpair *intstrpair;   /* hinfo */
     adns_rr_strpair *strpair;         /* rp, rp_raw */
     adns_rr_inthostaddr *inthostaddr; /* mx */
     adns_rr_intstr *intstr;           /* mx_raw */
     adns_rr_soa *soa;                 /* soa, soa_raw */
+    adns_rr_srv *srv;                 /* srv */
+    adns_rr_srvraw *srvraw;           /* srv_raw */
   } rrs;
 } adns_answer;
 
@@ -424,6 +487,13 @@
  *   Changes the consistency checking frequency; this overrides the
  *   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
@@ -508,7 +578,32 @@
 			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 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).  In the `fallback'
+ * case, will invent an SRV record with 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,

--------------------------------------------------------------------
IETF IPng Working Group Mailing List
IPng Home Page:                      http://playground.sun.com/ipng
FTP archive:                      ftp://playground.sun.com/pub/ipng
Direct all administrative requests to [email-masked]
--------------------------------------------------------------------

---

From: [email-masked]
Date: Sun, 28 May 2000 22:53:45 +0900

>the other is what the resolver behaviour should be in various

	about resolv.conf syntax: the easiest way is to have "nameserver
	<ipv6 address>".  BIND4/8 resolver code (res_init.c) simply ignore
	unparsable lines, as well as unparsable address portion in
	"nameserver foo" line.  so this should be safe to put
	"nameserver <v6addr>" line into resolv.conf, even if old binary may
	look at the file.

	about behavior: there are various APIs visible to the userland
	programmers.  I think you may want to honor API definition in RFCs
	and X/Open documents, and behave as necessary.  for example:
	- gethostbyname() - IPv4 only -> A query
	- gethostbyname2(AF_INET) - if af == AF_INET, IPv4 only -> A query
	- gethostbyname2(AF_INET6) - see RFC2133
	- getipnodebyname(AF_INET) - see RFC2553, IPv4 only -> A query
	- getipnodebyname(AF_INET6) - see RFC2553
	(not sure if the answer is relevant for your library suite)

itojun