chiark / gitweb /
lib/addr.c, etc.: Return plain addresses from `netaddress_resolve'.
[disorder] / lib / addr.c
index ecbab22e7c48ea9d4f66e8658c334d1aef3d8c97..0ba88f18a26206f476224a4722fc0140b8c60788 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2004, 2007, 2008, 2013 Richard Kettlewell
  *
  * 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
 
 #include "common.h"
 
+#include <stddef.h>
+
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/un.h>
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#if HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+#if HAVE_WS2TCPIP_H
+# include <Ws2tcpip.h>
+#endif
 
 #include "log.h"
 #include "printf.h"
@@ -58,28 +71,32 @@ struct addrinfo *get_address(const struct stringlist *a,
   struct addrinfo *res;
   char *name;
   int rc;
+  char errbuf[1024];
 
   switch(a->n) {  
   case 1:
     byte_xasprintf(&name, "host * service %s", a->s[0]);
     if((rc = getaddrinfo(0, a->s[0], pref, &res))) {
-      error(0, "getaddrinfo %s: %s", a->s[0], gai_strerror(rc));
+      disorder_error(0, "getaddrinfo %s: %s", a->s[0],
+                     format_error(ec_getaddrinfo, rc, errbuf, sizeof errbuf));
       return 0;
     }
     break;
   case 2:
     byte_xasprintf(&name, "host %s service %s", a->s[0], a->s[1]);
     if((rc = getaddrinfo(a->s[0], a->s[1], pref, &res))) {
-      error(0, "getaddrinfo %s %s: %s", a->s[0], a->s[1], gai_strerror(rc));
+      disorder_error(0, "getaddrinfo %s %s: %s",
+                     a->s[0], a->s[1],
+                     format_error(ec_getaddrinfo, rc, errbuf, sizeof errbuf));
       return 0;
     }
     break;
   default:
-    error(0, "invalid network address specification (n=%d)", a->n);
+    disorder_error(0, "invalid network address specification (n=%d)", a->n);
     return 0;
   }
   if(!res || (pref && res->ai_socktype != pref->ai_socktype)) {
-    error(0, "getaddrinfo didn't give us a suitable socket address");
+    disorder_error(0, "getaddrinfo didn't give us a suitable socket address");
     if(res)
       freeaddrinfo(res);
     return 0;
@@ -126,7 +143,7 @@ int sockaddrcmp(const struct sockaddr *a,
     return memcmp(&in6a->sin6_addr, &in6b->sin6_addr,
                  sizeof (struct in6_addr));
   default:
-    fatal(0, "unsupported protocol family %d", a->sa_family);
+    disorder_fatal(0, "unsupported protocol family %d", a->sa_family);
   }
 }
 
@@ -158,12 +175,12 @@ static inline char *format_sockaddr4(const struct sockaddr_in *sin4) {
 
   if(sin4->sin_port)
     byte_xasprintf(&r, "%s port %u",
-                  inet_ntop(sin4->sin_family, &sin4->sin_addr,
+                  inet_ntop(sin4->sin_family, (void *)&sin4->sin_addr,
                             buffer, sizeof buffer),
                   ntohs(sin4->sin_port));
   else
     byte_xasprintf(&r, "%s",
-                  inet_ntop(sin4->sin_family, &sin4->sin_addr,
+                  inet_ntop(sin4->sin_family, (void *)&sin4->sin_addr,
                             buffer, sizeof buffer));
   return r;
 }
@@ -174,20 +191,22 @@ static inline char *format_sockaddr6(const struct sockaddr_in6 *sin6) {
 
   if(sin6->sin6_port)
     byte_xasprintf(&r, "%s port %u",
-                  inet_ntop(sin6->sin6_family, &sin6->sin6_addr,
+                  inet_ntop(sin6->sin6_family, (void *)&sin6->sin6_addr,
                             buffer, sizeof buffer),
                   ntohs(sin6->sin6_port));
   else
     byte_xasprintf(&r, "%s",
-                  inet_ntop(sin6->sin6_family, &sin6->sin6_addr,
+                  inet_ntop(sin6->sin6_family, (void *)&sin6->sin6_addr,
                             buffer, sizeof buffer));
   return r;
 }
 
+#if HAVE_SYS_UN_H
 /** @brief Format a UNIX socket address */
 static inline char *format_sockaddrun(const struct sockaddr_un *sun) {
   return xstrdup(sun->sun_path);
 }
+#endif
     
 /** @brief Construct a text description a sockaddr
  * @param sa Socket address
@@ -199,8 +218,10 @@ char *format_sockaddr(const struct sockaddr *sa) {
     return format_sockaddr4((const struct sockaddr_in *)sa);
   case AF_INET6:
     return format_sockaddr6((const struct sockaddr_in6 *)sa);
+#if HAVE_SYS_UN_H
   case AF_UNIX:
     return format_sockaddrun((const struct sockaddr_un *)sa);
+#endif
   default:
     return 0;
   }
@@ -216,6 +237,8 @@ int netaddress_parse(struct netaddress *na,
                     int nvec,
                     char **vec) {
   const char *port;
+  long p;
+  int e;
 
   na->af = AF_UNSPEC;
   if(nvec > 0 && vec[0][0] == '-') {
@@ -266,8 +289,7 @@ int netaddress_parse(struct netaddress *na,
     }
     if(port[strspn(port, "0123456789")])
       return -1;
-    long p;
-    int e = xstrtol(&p, port, NULL, 10);
+    e = xstrtol(&p, port, NULL, 10);
 
     if(e)
       return -1;
@@ -304,7 +326,7 @@ void netaddress_format(const struct netaddress *na,
   if(na->port != -1) {
     char buffer[64];
 
-    snprintf(buffer, sizeof buffer, "%d", na->port);
+    byte_snprintf(buffer, sizeof buffer, "%d", na->port);
     vector_append(v, xstrdup(buffer));
   }
   vector_terminate(v);
@@ -317,29 +339,76 @@ void netaddress_format(const struct netaddress *na,
 /** @brief Resolve a network address
  * @param na Address structure
  * @param passive True if passive (bindable) address is desired
- * @param protocol Protocol number desired (e.g. @c IPPROTO_TCP)
- * @return List of suitable addresses or NULL
+ * @param type Socket type (e.g., @c SOCK_STREAM, @c SOCK_DGRAM)
+ * @param[out] raddr_out Vector of address/length pairs
+ * @param[out] nraddr_out Length of output vector
+ * @return Zero on success, -1 on error
+ *
+ * Call netaddress_free_resolved() to free the resolved addresses.
  */
-struct addrinfo *netaddress_resolve(const struct netaddress *na,
-                                   int passive,
-                                   int protocol) {
-  struct addrinfo *res, hints[1];
+int netaddress_resolve(const struct netaddress *na, int passive, int type,
+                      struct resolved **raddr_out, size_t *nraddr_out) {
+  struct resolved *raddr;
+  size_t i, nraddr;
+  struct addrinfo *res, *ai, hints[1];
+  struct sockaddr_un *sun;
   char service[64];
   int rc;
+  char errbuf[1024];
 
-  memset(hints, 0, sizeof hints);
-  hints->ai_family = na->af;
-  hints->ai_protocol = protocol;
-  hints->ai_flags = passive ? AI_PASSIVE : 0;
-  snprintf(service, sizeof service, "%d", na->port);
-  rc = getaddrinfo(na->address, service, hints, &res);
-  if(rc) {
-    error(0, "getaddrinfo %s %d: %s",
-         na->address ? na->address : "*",
-         na->port, gai_strerror(rc));
-    return NULL;
+#if HAVE_SYS_UN_H
+  if (na->af == AF_UNIX) {
+    /* `getaddrinfo' won't work, so we make our own one */
+    raddr = xmalloc(sizeof(*raddr));
+    raddr->len = offsetof(struct sockaddr_un, sun_path) +
+      strlen(na->address) + 1;
+    sun = xmalloc_noptr(raddr->len);
+    sun->sun_family = AF_UNIX;
+    strcpy(sun->sun_path, na->address);
+    raddr->sa = (struct sockaddr *)sun;
+    nraddr = 1;
+  } else
+#endif
+  {
+    /* get the system to do the heavy lifting */
+    memset(hints, 0, sizeof hints);
+    hints->ai_family = na->af;
+    hints->ai_socktype = type;
+    hints->ai_flags = passive ? AI_PASSIVE : 0;
+    byte_snprintf(service, sizeof service, "%d", na->port);
+    rc = getaddrinfo(na->address, service, hints, &res);
+    if(rc) {
+      disorder_error(0, "getaddrinfo %s %d: %s",
+                    na->address ? na->address : "*",
+                    na->port,
+                    format_error(ec_getaddrinfo, rc, errbuf, sizeof errbuf));
+      return -1;
+    }
+    /* copy the addresses into an output vector */
+    for(ai = res, nraddr = 0; ai; ai = ai->ai_next, nraddr++);
+    raddr = xmalloc(nraddr*sizeof(*raddr));
+    for(ai = res, i = 0; ai; ai = ai->ai_next, i++) {
+      raddr[i].sa = xmalloc_noptr(ai->ai_addrlen);
+      raddr[i].len = ai->ai_addrlen;
+      memcpy(raddr[i].sa, ai->ai_addr, ai->ai_addrlen);
+    }
+    freeaddrinfo(res);
   }
-  return res;
+  *raddr_out = raddr;
+  *nraddr_out = nraddr;
+  return 0;
+}
+
+/** @brief Free a vector of resolved addresses */
+void netaddress_free_resolved(struct resolved *raddr, size_t nraddr)
+{
+  size_t i;
+
+  if (!raddr)
+    return;
+  for(i = 0; i < nraddr; i++)
+    xfree(raddr[i].sa);
+  xfree(raddr);
 }
 
 /*