API: Reject unknown flags
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Fri, 24 Oct 2014 20:36:36 +0000 (21:36 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 26 Oct 2014 11:42:43 +0000 (11:42 +0000)
Check for unknown flags in adns_initflags, adns_queryflags, and
adns_rrtype.

This will allow us to extend the API in the future but still retain
backward-ABI-safety: new applications which pass a new flag but are
run against old adns will get an error (typically, ENOSYS).

In each case we reserve a few of the spare bits for `harmless'
extensions: flags which we decide, at the time of introduction, that
it is harmless for old implementations to ignore.

We simply provide explicit hex values for the flags.  This is easier
than listing all of the actually-defined flags, and probably no less
reliable.  Failing to add a new flag to the check as well as to the
enum will produce an obvious error (unless the `harmless' flag is used
- and those are at the top of the space to reduce that probability).

In the case of adns_rrtype, all of the external functions quickly pass
the type to adns__findtype, so we can do the check there.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
17 files changed:
regress/case-unknown-flags-harmless.err [new file with mode: 0644]
regress/case-unknown-flags-harmless.out [new file with mode: 0644]
regress/case-unknown-flags-harmless.sys [new file with mode: 0644]
regress/case-unknown-flags-init.err [new file with mode: 0644]
regress/case-unknown-flags-init.out [new file with mode: 0644]
regress/case-unknown-flags-init.sys [new file with mode: 0644]
regress/case-unknown-flags-query.err [new file with mode: 0644]
regress/case-unknown-flags-query.out [new file with mode: 0644]
regress/case-unknown-flags-query.sys [new file with mode: 0644]
regress/case-unknown-flags-type.err [new file with mode: 0644]
regress/case-unknown-flags-type.out [new file with mode: 0644]
regress/case-unknown-flags-type.sys [new file with mode: 0644]
src/addrfam.c
src/adns.h
src/query.c
src/setup.c
src/types.c

diff --git a/regress/case-unknown-flags-harmless.err b/regress/case-unknown-flags-harmless.err
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/regress/case-unknown-flags-harmless.out b/regress/case-unknown-flags-harmless.out
new file mode 100644 (file)
index 0000000..08a05a8
--- /dev/null
@@ -0,0 +1,5 @@
+adns debug: using nameserver 172.18.45.6
+chiark.greenend.org.uk flags 1074266112 type 1 A(-) submitted
+chiark.greenend.org.uk flags 1074266112 type A(-): OK; nrrs=1; cname=$; owner=$; ttl=3600
+ 212.13.197.229
+rc=0
diff --git a/regress/case-unknown-flags-harmless.sys b/regress/case-unknown-flags-harmless.sys
new file mode 100644 (file)
index 0000000..e06fc00
--- /dev/null
@@ -0,0 +1,34 @@
+./adnstest default -0x4000
+:0x60000000|1 0x40080000/chiark.greenend.org.uk
+ start 1414182720.022422
+ socket domain=AF_INET type=SOCK_DGRAM
+ socket=6
+ +0.000183
+ fcntl fd=6 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000097
+ fcntl fd=6 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
+ +0.000076
+ sendto fd=6 addr=172.18.45.6:53
+     311f0100 00010000 00000000 06636869 61726b08 67726565 6e656e64 036f7267
+     02756b00 00010001.
+ sendto=40
+ +0.000272
+ select max=7 rfds=[6] wfds=[] efds=[] to=1.999728
+ select=1 rfds=[6] wfds=[] efds=[]
+ +0.102141
+ recvfrom fd=6 buflen=512
+ recvfrom=OK addr=172.18.45.6:53
+     311f8580 00010001 00020002 06636869 61726b08 67726565 6e656e64 036f7267
+     02756b00 00010001 c00c0001 00010000 0e100004 d40dc5e5 c0130002 00010001
+     51800011 036e7331 0a72656c 61746976 697479c0 13c01300 02000100 01518000
+     06036e73 30c048c0 61000100 01000151 800004ac 122d06c0 44000100 01000151
+     800004ac 122d0b.
+ +0.000252
+ recvfrom fd=6 buflen=512
+ recvfrom=EAGAIN
+ +0.000070
+ close fd=6
+ close=OK
+ +0.000138
diff --git a/regress/case-unknown-flags-init.err b/regress/case-unknown-flags-init.err
new file mode 100644 (file)
index 0000000..8d1d1d8
--- /dev/null
@@ -0,0 +1 @@
+adns failure: init: errno=ENOSYS
diff --git a/regress/case-unknown-flags-init.out b/regress/case-unknown-flags-init.out
new file mode 100644 (file)
index 0000000..daa767e
--- /dev/null
@@ -0,0 +1 @@
+rc=2
diff --git a/regress/case-unknown-flags-init.sys b/regress/case-unknown-flags-init.sys
new file mode 100644 (file)
index 0000000..e177c78
--- /dev/null
@@ -0,0 +1,2 @@
+./adnstest default -0x6000
+:0x60000000|1 0x40080000/chiark.greenend.org.uk
diff --git a/regress/case-unknown-flags-query.err b/regress/case-unknown-flags-query.err
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/regress/case-unknown-flags-query.out b/regress/case-unknown-flags-query.out
new file mode 100644 (file)
index 0000000..ac8e541
--- /dev/null
@@ -0,0 +1,3 @@
+adns debug: using nameserver 172.18.45.6
+chiark.greenend.org.uk flags 1075314688 type 1 not implemented
+rc=0
diff --git a/regress/case-unknown-flags-query.sys b/regress/case-unknown-flags-query.sys
new file mode 100644 (file)
index 0000000..ce849e8
--- /dev/null
@@ -0,0 +1,15 @@
+./adnstest default -0x4000
+:0x60000000|1 0x40180000/chiark.greenend.org.uk
+ start 1414182790.489799
+ socket domain=AF_INET type=SOCK_DGRAM
+ socket=6
+ +0.000123
+ fcntl fd=6 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000047
+ fcntl fd=6 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
+ +0.000039
+ close fd=6
+ close=OK
+ +0.000149
diff --git a/regress/case-unknown-flags-type.err b/regress/case-unknown-flags-type.err
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/regress/case-unknown-flags-type.out b/regress/case-unknown-flags-type.out
new file mode 100644 (file)
index 0000000..a12353a
--- /dev/null
@@ -0,0 +1,3 @@
+adns debug: using nameserver 172.18.45.6
+chiark.greenend.org.uk flags 1074266112 type 1 not implemented
+rc=0
diff --git a/regress/case-unknown-flags-type.sys b/regress/case-unknown-flags-type.sys
new file mode 100644 (file)
index 0000000..41705b8
--- /dev/null
@@ -0,0 +1,15 @@
+./adnstest default -0x4000
+:0x70000000|1 0x40080000/chiark.greenend.org.uk
+ start 1414182770.758670
+ socket domain=AF_INET type=SOCK_DGRAM
+ socket=6
+ +0.000142
+ fcntl fd=6 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000080
+ fcntl fd=6 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
+ +0.000060
+ close fd=6
+ close=OK
+ +0.000170
index 528036c..5067048 100644 (file)
@@ -256,9 +256,18 @@ static bool addrtext_scope_use_ifname(const struct sockaddr *sa) {
     IN6_IS_ADDR_MC_LINKLOCAL(in6);
 }
 
+static int textaddr_check_qf(adns_queryflags flags) {
+  if (flags & ~(adns_queryflags)(adns_qf_addrlit_scope_forbid|
+                                adns_qf_addrlit_scope_numeric|
+                                adns_qf_addrlit_ipv4_quadonly|
+                                0x40000000))
+    return ENOSYS;
+  return 0;
+}
+
 int adns_text2addr(const char *text, uint16_t port, adns_queryflags flags,
                   struct sockaddr *sa, socklen_t *salen_io) {
-  int af;
+  int r, af;
   char copybuf[INET6_ADDRSTRLEN];
   const char *parse=text;
   const char *scopestr=0;
@@ -266,6 +275,8 @@ int adns_text2addr(const char *text, uint16_t port, adns_queryflags flags,
   void *dst;
   uint16_t *portp;
 
+  r= textaddr_check_qf(flags);  if (r) return r;
+
 #define INVAL(how) do{                         \
   af_debug("invalid: %s: `%s'", how, text);    \
   return EINVAL;                               \
@@ -389,7 +400,9 @@ int adns_text2addr(const char *text, uint16_t port, adns_queryflags flags,
 int adns_addr2text(const struct sockaddr *sa, adns_queryflags flags,
                   char *buffer, int *buflen_io, int *port_r) {
   const void *src;
-  int port;
+  int r, port;
+
+  r= textaddr_check_qf(flags);  if (r) return r;
 
   if (*buflen_io < ADNS_ADDR2TEXT_BUFLEN) {
     *buflen_io = ADNS_ADDR2TEXT_BUFLEN;
index 896d866..4cabc4a 100644 (file)
@@ -709,8 +709,7 @@ int adns_addr2text(const struct sockaddr *sa, adns_queryflags flags,
    * port is always in host byte order and is simply copied to and
    * from the appropriate sockaddr field (byteswapped as necessary).
    *
-   * The only flags supported are adns_qf_addrlit_...; others are
-   * ignored.
+   * The only flags supported are adns_qf_addrlit_...
    *
    * Error return values are:
    *
@@ -733,6 +732,8 @@ int adns_addr2text(const struct sockaddr *sa, adns_queryflags flags,
    *
    *  EAFNOSUPPORT   sa->sa_family is not supported (addr2text only).
    *
+   *  ENOSYS    Unsupported flags set.
+   *
    * Only if neither adns_qf_addrlit_scope_forbid nor
    * adns_qf_addrlit_scope_numeric are set:
    *
index 1f51b69..ff56fc3 100644 (file)
@@ -275,6 +275,12 @@ int adns_submit(adns_state ads,
 
   adns__consistency(ads,0,cc_entex);
 
+  if (flags & ~(adns_queryflags)0x4009ffff)
+    /* 0x40080000 are reserved for `harmless' future expansion
+     * 0x00000020 used to be adns_qf_quoteok_cname, now the default;
+     * see also addrfam.c:textaddr_check_qf */
+    return ENOSYS;
+
   typei= adns__findtype(type);
   if (!typei) return ENOSYS;
 
index 12d2241..fd20c43 100644 (file)
@@ -567,6 +567,10 @@ static int init_begin(adns_state *ads_r, adns_initflags flags,
   adns_state ads;
   pid_t pid;
   
+  if (flags & ~(adns_initflags)(0x4fff))
+    /* 0x4000 is reserved for `harmless' future expansion */
+    return ENOSYS;
+
   ads= malloc(sizeof(*ads)); if (!ads) return errno;
 
   ads->iflags= flags;
index 06d0d4c..d65e155 100644 (file)
@@ -1693,6 +1693,10 @@ DEEP_TYPE(unknown,0, "unknown",byteblock,opaque,  0,     opaque            );
 const typeinfo *adns__findtype(adns_rrtype type) {
   const typeinfo *begin, *end, *mid;
 
+  if (type & ~(adns_rrtype)0x63ffffff)
+    /* 0x60000000 is reserved for `harmless' future expansion */
+    return 0;
+
   if (type & adns_r_unknown) return &typeinfo_unknown;
   type &= adns_rrt_reprmask;