]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Similar to NSD PR#113, implement that interface names can be used,
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 27 Aug 2020 12:53:33 +0000 (14:53 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 27 Aug 2020 12:53:33 +0000 (14:53 +0200)
  eg. something like interface: eth0 is resolved at server start and
  uses the IP addresses for that named interface.

config.h.in
configure
configure.ac
daemon/daemon.c
doc/Changelog
doc/unbound.conf.5.in
services/listen_dnsport.c
services/listen_dnsport.h
testcode/fake_event.c
util/config_file.c
util/config_file.h

index 65c25851785b89682105ce5939d0b8bb34ed0397..1054ba17668f94a3e06795b11af8ff44eb817a09 100644 (file)
 /* Define to 1 if you have the `getentropy' function. */
 #undef HAVE_GETENTROPY
 
+/* Define to 1 if you have the `getifaddrs' function. */
+#undef HAVE_GETIFADDRS
+
 /* Define to 1 if you have the <getopt.h> header file. */
 #undef HAVE_GETOPT_H
 
 /* If we have htobe64 */
 #undef HAVE_HTOBE64
 
+/* Define to 1 if you have the <ifaddrs.h> header file. */
+#undef HAVE_IFADDRS_H
+
 /* Define to 1 if you have the `inet_aton' function. */
 #undef HAVE_INET_ATON
 
 /* Define to 1 if you have the <nettle/eddsa.h> header file. */
 #undef HAVE_NETTLE_EDDSA_H
 
+/* Define to 1 if you have the <net/if.h> header file. */
+#undef HAVE_NET_IF_H
+
 /* Use libnss for crypto */
 #undef HAVE_NSS
 
index 4df378a8a044a8e9a8e1c68e5b8d9f318bcaf77b..e46d7f59e7eff07e2c7f17bdd6fa5650bb5f808a 100755 (executable)
--- a/configure
+++ b/configure
@@ -14726,7 +14726,7 @@ $as_echo "no" >&6; }
 fi
 
 # Checks for header files.
-for ac_header in stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h
+for ac_header in stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h ifaddrs.h net/if.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
@@ -20224,7 +20224,7 @@ if test "$ac_res" != no; then :
 
 fi
 
-for ac_func in tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4
+for ac_func in tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4 getifaddrs
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
index aa38a56d2805001cc2791f21f5a7e04aa6ae8cac..4bb4e94636fc291546df35eaedcf323059bb4e4c 100644 (file)
@@ -399,7 +399,7 @@ ACX_LIBTOOL_C_ONLY
 PKG_PROG_PKG_CONFIG
 
 # Checks for header files.
-AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h],,, [AC_INCLUDES_DEFAULT])
+AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h ifaddrs.h net/if.h],,, [AC_INCLUDES_DEFAULT])
 
 # Check for Apple header. This uncovers TARGET_OS_IPHONE, TARGET_OS_TV or TARGET_OS_WATCH
 AC_CHECK_HEADERS([TargetConditionals.h])
@@ -1552,7 +1552,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([
   AC_MSG_RESULT(no))
 
 AC_SEARCH_LIBS([setusercontext], [util])
-AC_CHECK_FUNCS([tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4])
+AC_CHECK_FUNCS([tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4 getifaddrs])
 AC_CHECK_FUNCS([setresuid],,[AC_CHECK_FUNCS([setreuid])])
 AC_CHECK_FUNCS([setresgid],,[AC_CHECK_FUNCS([setregid])])
 
index 34f7bfda1a5fe1ede913255b04d7890f14c12846..f480c94e61f6d5f3505a31c09dbb833b87dfc74d 100644 (file)
@@ -308,6 +308,8 @@ daemon_open_shared_ports(struct daemon* daemon)
 {
        log_assert(daemon);
        if(daemon->cfg->port != daemon->listening_port) {
+               char** resif = NULL;
+               int num_resif = 0;
                size_t i;
                struct listen_port* p0;
                daemon->reuseport = 0;
@@ -318,15 +320,18 @@ daemon_open_shared_ports(struct daemon* daemon)
                        free(daemon->ports);
                        daemon->ports = NULL;
                }
+               if(!resolve_interface_names(daemon->cfg, &resif, &num_resif))
+                       return 0;
                /* see if we want to reuseport */
 #ifdef SO_REUSEPORT
                if(daemon->cfg->so_reuseport && daemon->cfg->num_threads > 0)
                        daemon->reuseport = 1;
 #endif
                /* try to use reuseport */
-               p0 = listening_ports_open(daemon->cfg, &daemon->reuseport);
+               p0 = listening_ports_open(daemon->cfg, resif, num_resif, &daemon->reuseport);
                if(!p0) {
                        listening_ports_free(p0);
+                       config_del_strarray(resif, num_resif);
                        return 0;
                }
                if(daemon->reuseport) {
@@ -340,6 +345,7 @@ daemon_open_shared_ports(struct daemon* daemon)
                if(!(daemon->ports = (struct listen_port**)calloc(
                        daemon->num_ports, sizeof(*daemon->ports)))) {
                        listening_ports_free(p0);
+                       config_del_strarray(resif, num_resif);
                        return 0;
                }
                daemon->ports[0] = p0;
@@ -348,16 +354,19 @@ daemon_open_shared_ports(struct daemon* daemon)
                        for(i=1; i<daemon->num_ports; i++) {
                                if(!(daemon->ports[i]=
                                        listening_ports_open(daemon->cfg,
+                                               resif, num_resif,
                                                &daemon->reuseport))
                                        || !daemon->reuseport ) {
                                        for(i=0; i<daemon->num_ports; i++)
                                                listening_ports_free(daemon->ports[i]);
                                        free(daemon->ports);
                                        daemon->ports = NULL;
+                                       config_del_strarray(resif, num_resif);
                                        return 0;
                                }
                        }
                }
+               config_del_strarray(resif, num_resif);
                daemon->listening_port = daemon->cfg->port;
        }
        if(!daemon->cfg->remote_control_enable && daemon->rc_port) {
index 0351df5bc86d6dcb424e46f5392654ad90cdccda..11e81088ff3d248f9ebb0f0d0922f506dce8b029 100644 (file)
@@ -1,3 +1,8 @@
+27 August 2020: Wouter
+       - Similar to NSD PR#113, implement that interface names can be used,
+         eg. something like interface: eth0 is resolved at server start and
+         uses the IP addresses for that named interface.
+
 26 August 2020: George
        - Update documentation in python example code.
 
index 951094ed1ab3f1b4d15e44b9460c29da9e91ea0f..fa4850aaddb8f863a4740013169471c6c6876ac9 100644 (file)
@@ -122,7 +122,8 @@ The port number, default 53, on which the server responds to queries.
 Interface to use to connect to the network. This interface is listened to
 for queries from clients, and answers to clients are given from it.
 Can be given multiple times to work on several interfaces. If none are
-given the default is to listen to localhost.
+given the default is to listen to localhost.  If an interface name is used
+instead of an ip address, the list of ip addresses on that interface are used.
 The interfaces are not changed on a reload (kill \-HUP) but only on restart.
 A port number can be specified with @port (without spaces between
 interface and port number), if not specified the default port (from
index cc56d3fd31670f9bbbd5da75060700069b0252ef..63d9f0398e7b4854499bd2f1d82f1e711f735739 100644 (file)
 #include <systemd/sd-daemon.h>
 #endif
 
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
 /** number of queued TCP connections for listen() */
 #define TCP_BACKLOG 256 
 
@@ -1439,8 +1446,163 @@ listen_delete(struct listen_dnsport* front)
        }
 }
 
+#ifdef HAVE_GETIFADDRS
+static int
+resolve_ifa_name(struct ifaddrs *ifas, const char *search_ifa, char ***ip_addresses, int *ip_addresses_size)
+{
+       struct ifaddrs *ifa;
+       int last_ip_addresses_size = *ip_addresses_size;
+
+       for(ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
+               sa_family_t family;
+               const char* atsign;
+#ifdef INET6      /* |   address ip    | % |  ifa name  | @ |  port  | nul */
+               char addr_buf[INET6_ADDRSTRLEN + 1 + IF_NAMESIZE + 1 + 16 + 1];
+#else
+               char addr_buf[INET_ADDRSTRLEN + 1 + 16 + 1];
+#endif
+
+               if((atsign=strrchr(search_ifa, '@')) != NULL) {
+                       if(strlen(ifa->ifa_name) != (size_t)(atsign-search_ifa)
+                          || strncmp(ifa->ifa_name, search_ifa,
+                          atsign-search_ifa) != 0)
+                               continue;
+               } else {
+                       if(strcmp(ifa->ifa_name, search_ifa) != 0)
+                               continue;
+                       atsign = "";
+               }
+
+               if(ifa->ifa_addr == NULL)
+                       continue;
+
+               family = ifa->ifa_addr->sa_family;
+               if(family == AF_INET) {
+                       char a4[INET_ADDRSTRLEN + 1];
+                       struct sockaddr_in *in4 = (struct sockaddr_in *)
+                               ifa->ifa_addr;
+                       if(!inet_ntop(family, &in4->sin_addr, a4, sizeof(a4))) {
+                               log_err("inet_ntop failed");
+                               return 0;
+                       }
+                       snprintf(addr_buf, sizeof(addr_buf), "%s%s",
+                               a4, atsign);
+               }
+#ifdef INET6
+               else if(family == AF_INET6) {
+                       struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)
+                               ifa->ifa_addr;
+                       char a6[INET6_ADDRSTRLEN + 1];
+                       char if_index_name[IF_NAMESIZE + 1];
+                       if_index_name[0] = 0;
+                       if(!inet_ntop(family, &in6->sin6_addr, a6, sizeof(a6))) {
+                               log_err("inet_ntop failed");
+                               return 0;
+                       }
+                       if_indextoname(in6->sin6_scope_id,
+                               (char *)if_index_name);
+                       if (strlen(if_index_name) != 0) {
+                               snprintf(addr_buf, sizeof(addr_buf),
+                                       "%s%%%s%s", a6, if_index_name, atsign);
+                       } else {
+                               snprintf(addr_buf, sizeof(addr_buf), "%s%s",
+                                       a6, atsign);
+                       }
+               }
+#endif
+               else {
+                       continue;
+               }
+               verbose(4, "interface %s has address %s", search_ifa, addr_buf);
+
+               *ip_addresses = realloc(*ip_addresses, sizeof(char *) * (*ip_addresses_size + 1));
+               if(!*ip_addresses) {
+                       log_err("realloc failed: out of memory");
+                       return 0;
+               }
+               (*ip_addresses)[*ip_addresses_size] = strdup(addr_buf);
+               if(!(*ip_addresses)[*ip_addresses_size]) {
+                       log_err("strdup failed: out of memory");
+                       return 0;
+               }
+               (*ip_addresses_size)++;
+       }
+
+       if (*ip_addresses_size == last_ip_addresses_size) {
+               *ip_addresses = realloc(*ip_addresses, sizeof(char *) * (*ip_addresses_size + 1));
+               if(!*ip_addresses) {
+                       log_err("realloc failed: out of memory");
+                       return 0;
+               }
+               (*ip_addresses)[*ip_addresses_size] = strdup(search_ifa);
+               if(!(*ip_addresses)[*ip_addresses_size]) {
+                       log_err("strdup failed: out of memory");
+                       return 0;
+               }
+               (*ip_addresses_size)++;
+       }
+       return 1;
+}
+#endif /* HAVE_GETIFADDRS */
+
+int resolve_interface_names(struct config_file* cfg, char*** resif,
+       int* num_resif)
+{
+#ifdef HAVE_GETIFADDRS
+       int i;
+       struct ifaddrs *addrs;
+       if(cfg->num_ifs == 0) {
+               *resif = NULL;
+               *num_resif = 0;
+               return 1;
+       }
+       if(getifaddrs(&addrs) == -1) {
+               log_err("failed to list interfaces: getifaddrs: %s",
+                       strerror(errno));
+               freeifaddrs(addrs);
+               return 0;
+       }
+       for(i=0; i<cfg->num_ifs; i++) {
+               if(!resolve_ifa_name(addrs, cfg->ifs[i], resif, num_resif)) {
+                       freeifaddrs(addrs);
+                       config_del_strarray(*resif, *num_resif);
+                       *resif = NULL;
+                       *num_resif = 0;
+                       return 0;
+               }
+       }
+       freeifaddrs(addrs);
+       return 1;
+#else
+       int i;
+       if(cfg->num_ifs == 0) {
+               *resif = NULL;
+               *num_resif = 0;
+               return 1;
+       }
+       *num_resif = cfg->num_ifs;
+       *resif = calloc(*num_resif, sizeof(**resif));
+       if(!*resif) {
+               log_err("out of memory");
+               return 0;
+       }
+       for(i=0; i<*num_resif; i++) {
+               (*resif)[i] = strdup(cfg->ifs[i]);
+               if(!((*resif)[i])) {
+                       log_err("out of memory");
+                       config_del_strarray(*resif, *num_resif);
+                       *resif = NULL;
+                       *num_resif = 0;
+                       return 0;
+               }
+       }
+       return 1;
+#endif /* HAVE_GETIFADDRS */
+}
+
 struct listen_port* 
-listening_ports_open(struct config_file* cfg, int* reuseport)
+listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
+       int* reuseport)
 {
        struct listen_port* list = NULL;
        struct addrinfo hints;
@@ -1459,7 +1621,7 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
        memset(&hints, 0, sizeof(hints));
        hints.ai_flags = AI_PASSIVE;
        /* no name lookups on our listening ports */
-       if(cfg->num_ifs > 0)
+       if(num_ifs > 0)
                hints.ai_flags |= AI_NUMERICHOST;
        hints.ai_family = AF_UNSPEC;
 #ifndef INET6
@@ -1469,7 +1631,7 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
                return NULL;
        }
        /* create ip4 and ip6 ports so that return addresses are nice. */
-       if(do_auto || cfg->num_ifs == 0) {
+       if(do_auto || num_ifs == 0) {
                if(do_ip6) {
                        hints.ai_family = AF_INET6;
                        if(!ports_create_if(do_auto?"::0":"::1", 
@@ -1498,12 +1660,12 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
                                return NULL;
                        }
                }
-       } else for(i = 0; i<cfg->num_ifs; i++) {
-               if(str_is_ip6(cfg->ifs[i])) {
+       } else for(i = 0; i<num_ifs; i++) {
+               if(str_is_ip6(ifs[i])) {
                        if(!do_ip6)
                                continue;
                        hints.ai_family = AF_INET6;
-                       if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, 
+                       if(!ports_create_if(ifs[i], 0, cfg->do_udp,
                                do_tcp, &hints, portbuf, &list, 
                                cfg->so_rcvbuf, cfg->so_sndbuf,
                                cfg->ssl_port, cfg->tls_additional_port,
@@ -1517,7 +1679,7 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
                        if(!do_ip4)
                                continue;
                        hints.ai_family = AF_INET;
-                       if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, 
+                       if(!ports_create_if(ifs[i], 0, cfg->do_udp,
                                do_tcp, &hints, portbuf, &list, 
                                cfg->so_rcvbuf, cfg->so_sndbuf,
                                cfg->ssl_port, cfg->tls_additional_port,
index ddd1b63a4d95895045f8437acf16371a68e7a44a..680ec84bba4fb9379df12291135d9a0b64f74e74 100644 (file)
@@ -117,19 +117,32 @@ struct listen_port {
  * interfaces for IP4 and/or IP6, for UDP and/or TCP.
  * On the given port number. It creates the sockets.
  * @param cfg: settings on what ports to open.
+ * @param ifs: interfaces to open, array of IP addresses, ip[@port].
+ * @param num_ifs: length of ifs.
  * @param reuseport: set to true if you want reuseport, or NULL to not have it,
  *   set to false on exit if reuseport failed to apply (because of no
  *   kernel support).
  * @return: linked list of ports or NULL on error.
  */
 struct listen_port* listening_ports_open(struct config_file* cfg,
-       int* reuseport);
+       char** ifs, int num_ifs, int* reuseport);
 
 /**
  * Close and delete the (list of) listening ports.
  */
 void listening_ports_free(struct listen_port* list);
 
+/**
+ * Resolve interface names in config and store result IP addresses
+ * @param cfg: config
+ * @param resif: string array (malloced array of malloced strings) with
+ *     result.  NULL if cfg has none.
+ * @param num_resif: length of resif.  Zero if cfg has zero num_ifs.
+ * @return 0 on failure.
+ */
+int resolve_interface_names(struct config_file* cfg, char*** resif,
+       int* num_resif);
+
 /**
  * Create commpoints with for this thread for the shared ports.
  * @param base: the comm_base that provides event functionality.
index 59df27f5703261f1444318ac9100157e8a96e266..2ffb46c3aab27afaeebd198199493cf07bb83941 100644 (file)
@@ -1300,7 +1300,14 @@ void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
        log_info("double delete of pending serviced query");
 }
 
+int resolve_interface_names(struct config_file* ATTR_UNUSED(cfg),
+       char*** ATTR_UNUSED(resif), int* ATTR_UNUSED(num_resif))
+{
+       return 1;
+}
+
 struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg),
+       char** ATTR_UNUSED(ifs), int ATTR_UNUSED(num_ifs),
        int* ATTR_UNUSED(reuseport))
 {
        return calloc(1, 1);
index e1c372b0efc5c01c7f6a6fcaf581140392b1d5ba..5f79baa7ec3ba811e7001a7134f45450f7d584f2 100644 (file)
@@ -1387,8 +1387,8 @@ config_delviews(struct config_view* p)
                p = np;
        }
 }
-/** delete string array */
-static void
+
+void
 config_del_strarray(char** array, int num)
 {
        int i;
index cdae04c23569dd982eca145cb688803e7f898515..948bd1d0de3e94797a06c9427575857d0af54ed9 100644 (file)
@@ -969,6 +969,9 @@ void config_deldblstrlist(struct config_str2list* list);
  */
 void config_deltrplstrlist(struct config_str3list* list);
 
+/** delete string array */
+void config_del_strarray(char** array, int num);
+
 /** delete stringbytelist */
 void config_del_strbytelist(struct config_strbytelist* list);