ipv4 quadonly, make test program compile
[adns] / src / addrfam.c
index 30ee96b..99e2fab 100644 (file)
@@ -30,6 +30,7 @@
 #include <unistd.h>
 #include <inttypes.h>
 #include <stddef.h>
+#include <stdbool.h>
 
 #include <sys/types.h>
 #include <netdb.h>
@@ -207,74 +208,102 @@ const void *adns__sockaddr_to_inaddr(const struct sockaddr *sa)
 
 #define ADDRFAM_DEBUG
 #ifdef ADDRFAM_DEBUG
+static void af_debug_func(const char *fmt, ...) {
+  int esave= errno;
+  va_list al;
+  va_start(al,fmt);
+  vfprintf(stderr,fmt,al);
+  va_end(al);
+  errno= esave;
+}
 # define af_debug(fmt,...) \
-  (fprintf(stderr, "%s: " fmt "\n", __func__, __VA_ARGS__))
+  (af_debug_func("%s: " fmt "\n", __func__, __VA_ARGS__))
 #else
 # define af_debug(fmt,...) ((void)("" fmt "", __VA_ARGS__))
 #endif
 
-int adns_text2addr(const char *addr, uint16_t port, struct sockaddr *sa,
-                  socklen_t *salen /* set if OK or ENOSPC */) {
+static bool addrtext_our_errno(int e) {
+  return
+    e==EAFNOSUPPORT ||
+    e==EINVAL ||
+    e==ENOSPC ||
+    e==ENOSYS;
+}
+
+static bool addrtext_scope_use_ifname(const struct sockaddr *sa) {
+  const struct in6_addr *in6= &CSIN6(sa)->sin6_addr;
+  return
+    IN6_IS_ADDR_LINKLOCAL(in6) ||
+    IN6_IS_ADDR_MC_LINKLOCAL(in6);
+}
+
+int adns_text2addr(const char *text, uint16_t port, adns_queryflags flags,
+                  struct sockaddr *sa, socklen_t *salen_io) {
   int af;
   char copybuf[INET6_ADDRSTRLEN];
-  const char *parse=addr;
+  const char *parse=text;
   const char *scopestr=0;
   socklen_t needlen;
   void *dst;
   uint16_t *portp;
 
 #define INVAL(how) do{                         \
-  af_debug("invalid: %s: `%s'", how, addr);    \
+  af_debug("invalid: %s: `%s'", how, text);    \
   return EINVAL;                               \
 }while(0)
 
-  port= htons(port);
-
-  if (!strchr(addr, ':')) { /* INET */
-
 #define AFCORE(INETx,SINx,sinx)                        \
     af= AF_##INETx;                            \
     dst = &SINx(sa)->sinx##_addr;              \
     portp = &SINx(sa)->sinx##_port;            \
     needlen= sizeof(*SINx(sa));
 
+  if (!strchr(text, ':')) { /* INET */
+
     AFCORE(INET,SIN,sin);
 
   } else { /* INET6 */
 
     AFCORE(INET6,SIN6,sin6);
 
-    const char *percent= strchr(addr, '%');
+    const char *percent= strchr(text, '%');
     if (percent) {
-      ptrdiff_t lhslen = percent - addr;
+      ptrdiff_t lhslen = percent - text;
       if (lhslen >= INET6_ADDRSTRLEN) INVAL("scoped addr lhs too long");
-      memcpy(copybuf, addr, lhslen);
+      memcpy(copybuf, text, lhslen);
       copybuf[lhslen]= 0;
 
       parse= copybuf;
       scopestr= percent+1;
 
-      af_debug("will parse scoped address `%s' %% `%s'", parse, scopestr);
+      af_debug("will parse scoped addr `%s' %% `%s'", parse, scopestr);
     }
 
+  }
+
 #undef AFCORE
 
-  }
+  if (scopestr && (flags & adns_qf_addrlit_scope_forbid))
+    INVAL("scoped addr but _scope_forbid");
 
-  if (*salen < needlen) {
-    *salen = needlen;
+  if (*salen_io < needlen) {
+    *salen_io = needlen;
     return ENOSPC;
   }
-  *salen = needlen;
 
   memset(sa, 0, needlen);
+
+  sa->sa_family= af;
   *portp = htons(port);
 
-  int r= inet_pton(af,parse,dst);
-  if (!r) INVAL("inet_pton rejected");
-  if (r<0) {
-    af_debug("inet_pton failed on `%s'", parse);
-    return errno;
+  if (af == AF_INET && !(flags & adns_qf_addrlit_ipv4_quadonly)) {
+    /* we have to use inet_aton to deal with non-dotted-quad literals */
+    int r= inet_aton(parse,&SIN(sa)->sin_addr);
+    if (!r) INVAL("inet_aton rejected");
+  } else {
+    int r= inet_pton(af,parse,dst);
+    if (!r) INVAL("inet_pton rejected");
+    assert(r>0);
   }
 
   if (scopestr) {
@@ -287,11 +316,11 @@ int adns_text2addr(const char *addr, uint16_t port, struct sockaddr *sa,
       if (scope > ~(uint32_t)0)
        INVAL("numeric scope id too large for uint32_t");
     } else { /* !!*ep */
-      const struct in6_addr *in6= &SIN6(sa)->sin6_addr;
-      if (!IN6_IS_ADDR_LINKLOCAL(in6) &&
-         !IN6_IS_ADDR_MC_LINKLOCAL(in6)) {
+      if (flags & adns_qf_addrlit_scope_numeric)
+       INVAL("non-numeric scope but _scope_numeric");
+      if (!addrtext_scope_use_ifname(sa)) {
        af_debug("cannot convert non-numeric scope"
-                " in non-link-local addr `%s'", addr);
+                " in non-link-local addr `%s'", text);
        return ENOSYS;
       }
       errno= 0;
@@ -313,8 +342,7 @@ int adns_text2addr(const char *addr, uint16_t port, struct sockaddr *sa,
                 strerror(errno));
        if (errno==0) {
          return ENXIO;
-       } else if (errno==EAFNOSUPPORT || errno==EINVAL ||
-                  errno==ENOSPC || errno==ENOSYS) {
+       } else if (addrtext_our_errno(errno)) {
          /* we use these for other purposes, urgh. */
          perror("adns: adns_text2addr: if_nametoindex"
                 " failed with unexpected error");
@@ -325,27 +353,27 @@ int adns_text2addr(const char *addr, uint16_t port, struct sockaddr *sa,
       } else { /* ix>0 */
        if (scope > ~(uint32_t)0) {
          fprintf(stderr,"adns: adns_text2addr: if_nametoindex"
-                 " returned an interface index >2^32 which will not fit"
+                 " returned an interface index >=2^32 which will not fit"
                  " in sockaddr_in6.sin6_scope_id");
          return EIO;
        }
       }
     } /* else; !!*ep */
 
-    SIN6(sa)->sin6_scope_id= htonl(scope);
+    SIN6(sa)->sin6_scope_id= scope;
   } /* if (scopestr) */
 
+  *salen_io = needlen;
   return 0;
 }
 
-int adns_addr2text(const struct sockaddr *sa,
-                  char *addr_buffer, int *addr_buflen,
-                  int *port_r) {
+int adns_addr2text(const struct sockaddr *sa, adns_queryflags flags,
+                  char *buffer, int *buflen_io, int *port_r) {
   const void *src;
   int port;
 
-  if (*addr_buflen < ADNS_ADDR2TEXT_BUFLEN) {
-    *addr_buflen = ADNS_ADDR2TEXT_BUFLEN;
+  if (*buflen_io < ADNS_ADDR2TEXT_BUFLEN) {
+    *buflen_io = ADNS_ADDR2TEXT_BUFLEN;
     return ENOSPC;
   }
 
@@ -356,19 +384,46 @@ int adns_addr2text(const struct sockaddr *sa,
     default: return EAFNOSUPPORT;
   }
 
-  const char *ok= inet_ntop(sa->sa_family, src, addr_buffer, *addr_buflen);
+  const char *ok= inet_ntop(sa->sa_family, src, buffer, *buflen_io);
   assert(ok);
 
   if (sa->sa_family == AF_INET6) {
     uint32_t scope = CSIN6(sa)->sin6_scope_id;
     if (scope) {
-      scope = ntohl(scope);
-      int scopeoffset = strlen(addr_buffer);
-      int remain = *addr_buflen - scopeoffset;
-      int r = snprintf(addr_buffer + scopeoffset, remain,
-                      "%%%"PRIu32"", scope);
-      assert(r < *addr_buflen - scopeoffset);
-      af_debug("printed scoped address `%s'", addr_buffer);
+      if (flags & adns_qf_addrlit_scope_forbid)
+       return EINVAL;
+      int scopeoffset = strlen(buffer);
+      int remain = *buflen_io - scopeoffset;
+      char *scopeptr =  buffer + scopeoffset;
+      assert(remain >= IF_NAMESIZE+1/*%*/);
+      *scopeptr++= '%'; remain--;
+      bool parsedname = 0;
+      af_debug("will print scoped addr %s %% %"PRIu32"", buffer, scope);
+      if (scope <= UINT_MAX /* so we can pass it to if_indextoname */
+         && !(flags & adns_qf_addrlit_scope_numeric)
+         && addrtext_scope_use_ifname(sa)) {
+       parsedname = if_indextoname(scope, scopeptr);
+       if (!parsedname) {
+         af_debug("if_indextoname rejected scope (errno=%s)",
+                  strerror(errno));
+         if (errno==ENXIO) {
+           /* fair enough, show it as a number then */
+         } else if (addrtext_our_errno(errno)) {
+           /* we use these for other purposes, urgh. */
+           perror("adns: adns_addr2text: if_indextoname"
+                  " failed with unexpected error");
+           return EIO;
+         } else {
+           return errno;
+         }
+       }
+      }
+      if (!parsedname) {
+       int r = snprintf(scopeptr, remain,
+                        "%"PRIu32"", scope);
+       assert(r < *buflen_io - scopeoffset);
+      }
+      af_debug("printed scoped addr `%s'", buffer);
     }
   }