- 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.
This commit is contained in:
W.C.A. Wijngaards 2020-08-27 14:53:33 +02:00
parent a912786ca9
commit f6a527c25a
11 changed files with 225 additions and 16 deletions

View file

@ -272,6 +272,9 @@
/* Define to 1 if you have the `getentropy' function. */ /* Define to 1 if you have the `getentropy' function. */
#undef HAVE_GETENTROPY #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. */ /* Define to 1 if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H #undef HAVE_GETOPT_H
@ -302,6 +305,9 @@
/* If we have htobe64 */ /* If we have htobe64 */
#undef 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. */ /* Define to 1 if you have the `inet_aton' function. */
#undef HAVE_INET_ATON #undef HAVE_INET_ATON
@ -371,6 +377,9 @@
/* Define to 1 if you have the <nettle/eddsa.h> header file. */ /* Define to 1 if you have the <nettle/eddsa.h> header file. */
#undef HAVE_NETTLE_EDDSA_H #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 */ /* Use libnss for crypto */
#undef HAVE_NSS #undef HAVE_NSS

4
configure vendored
View file

@ -14726,7 +14726,7 @@ $as_echo "no" >&6; }
fi fi
# Checks for header files. # 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 : do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` 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 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 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 : do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"

View file

@ -399,7 +399,7 @@ ACX_LIBTOOL_C_ONLY
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
# Checks for header files. # 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 # Check for Apple header. This uncovers TARGET_OS_IPHONE, TARGET_OS_TV or TARGET_OS_WATCH
AC_CHECK_HEADERS([TargetConditionals.h]) AC_CHECK_HEADERS([TargetConditionals.h])
@ -1552,7 +1552,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([
AC_MSG_RESULT(no)) AC_MSG_RESULT(no))
AC_SEARCH_LIBS([setusercontext], [util]) 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([setresuid],,[AC_CHECK_FUNCS([setreuid])])
AC_CHECK_FUNCS([setresgid],,[AC_CHECK_FUNCS([setregid])]) AC_CHECK_FUNCS([setresgid],,[AC_CHECK_FUNCS([setregid])])

View file

@ -308,6 +308,8 @@ daemon_open_shared_ports(struct daemon* daemon)
{ {
log_assert(daemon); log_assert(daemon);
if(daemon->cfg->port != daemon->listening_port) { if(daemon->cfg->port != daemon->listening_port) {
char** resif = NULL;
int num_resif = 0;
size_t i; size_t i;
struct listen_port* p0; struct listen_port* p0;
daemon->reuseport = 0; daemon->reuseport = 0;
@ -318,15 +320,18 @@ daemon_open_shared_ports(struct daemon* daemon)
free(daemon->ports); free(daemon->ports);
daemon->ports = NULL; daemon->ports = NULL;
} }
if(!resolve_interface_names(daemon->cfg, &resif, &num_resif))
return 0;
/* see if we want to reuseport */ /* see if we want to reuseport */
#ifdef SO_REUSEPORT #ifdef SO_REUSEPORT
if(daemon->cfg->so_reuseport && daemon->cfg->num_threads > 0) if(daemon->cfg->so_reuseport && daemon->cfg->num_threads > 0)
daemon->reuseport = 1; daemon->reuseport = 1;
#endif #endif
/* try to use reuseport */ /* 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) { if(!p0) {
listening_ports_free(p0); listening_ports_free(p0);
config_del_strarray(resif, num_resif);
return 0; return 0;
} }
if(daemon->reuseport) { if(daemon->reuseport) {
@ -340,6 +345,7 @@ daemon_open_shared_ports(struct daemon* daemon)
if(!(daemon->ports = (struct listen_port**)calloc( if(!(daemon->ports = (struct listen_port**)calloc(
daemon->num_ports, sizeof(*daemon->ports)))) { daemon->num_ports, sizeof(*daemon->ports)))) {
listening_ports_free(p0); listening_ports_free(p0);
config_del_strarray(resif, num_resif);
return 0; return 0;
} }
daemon->ports[0] = p0; daemon->ports[0] = p0;
@ -348,16 +354,19 @@ daemon_open_shared_ports(struct daemon* daemon)
for(i=1; i<daemon->num_ports; i++) { for(i=1; i<daemon->num_ports; i++) {
if(!(daemon->ports[i]= if(!(daemon->ports[i]=
listening_ports_open(daemon->cfg, listening_ports_open(daemon->cfg,
resif, num_resif,
&daemon->reuseport)) &daemon->reuseport))
|| !daemon->reuseport ) { || !daemon->reuseport ) {
for(i=0; i<daemon->num_ports; i++) for(i=0; i<daemon->num_ports; i++)
listening_ports_free(daemon->ports[i]); listening_ports_free(daemon->ports[i]);
free(daemon->ports); free(daemon->ports);
daemon->ports = NULL; daemon->ports = NULL;
config_del_strarray(resif, num_resif);
return 0; return 0;
} }
} }
} }
config_del_strarray(resif, num_resif);
daemon->listening_port = daemon->cfg->port; daemon->listening_port = daemon->cfg->port;
} }
if(!daemon->cfg->remote_control_enable && daemon->rc_port) { if(!daemon->cfg->remote_control_enable && daemon->rc_port) {

View 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 26 August 2020: George
- Update documentation in python example code. - Update documentation in python example code.

View 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 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. 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 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. 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 A port number can be specified with @port (without spaces between
interface and port number), if not specified the default port (from interface and port number), if not specified the default port (from

View file

@ -70,6 +70,13 @@
#include <systemd/sd-daemon.h> #include <systemd/sd-daemon.h>
#endif #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() */ /** number of queued TCP connections for listen() */
#define TCP_BACKLOG 256 #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* 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 listen_port* list = NULL;
struct addrinfo hints; struct addrinfo hints;
@ -1459,7 +1621,7 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
/* no name lookups on our listening ports */ /* no name lookups on our listening ports */
if(cfg->num_ifs > 0) if(num_ifs > 0)
hints.ai_flags |= AI_NUMERICHOST; hints.ai_flags |= AI_NUMERICHOST;
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
#ifndef INET6 #ifndef INET6
@ -1469,7 +1631,7 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
return NULL; return NULL;
} }
/* create ip4 and ip6 ports so that return addresses are nice. */ /* 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) { if(do_ip6) {
hints.ai_family = AF_INET6; hints.ai_family = AF_INET6;
if(!ports_create_if(do_auto?"::0":"::1", if(!ports_create_if(do_auto?"::0":"::1",
@ -1498,12 +1660,12 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
return NULL; return NULL;
} }
} }
} else for(i = 0; i<cfg->num_ifs; i++) { } else for(i = 0; i<num_ifs; i++) {
if(str_is_ip6(cfg->ifs[i])) { if(str_is_ip6(ifs[i])) {
if(!do_ip6) if(!do_ip6)
continue; continue;
hints.ai_family = AF_INET6; 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, do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf, cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port, cfg->ssl_port, cfg->tls_additional_port,
@ -1517,7 +1679,7 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
if(!do_ip4) if(!do_ip4)
continue; continue;
hints.ai_family = AF_INET; 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, do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf, cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port, cfg->ssl_port, cfg->tls_additional_port,

View file

@ -117,19 +117,32 @@ struct listen_port {
* interfaces for IP4 and/or IP6, for UDP and/or TCP. * interfaces for IP4 and/or IP6, for UDP and/or TCP.
* On the given port number. It creates the sockets. * On the given port number. It creates the sockets.
* @param cfg: settings on what ports to open. * @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, * @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 * set to false on exit if reuseport failed to apply (because of no
* kernel support). * kernel support).
* @return: linked list of ports or NULL on error. * @return: linked list of ports or NULL on error.
*/ */
struct listen_port* listening_ports_open(struct config_file* cfg, 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. * Close and delete the (list of) listening ports.
*/ */
void listening_ports_free(struct listen_port* list); 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. * Create commpoints with for this thread for the shared ports.
* @param base: the comm_base that provides event functionality. * @param base: the comm_base that provides event functionality.

View 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"); 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), 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)) int* ATTR_UNUSED(reuseport))
{ {
return calloc(1, 1); return calloc(1, 1);

View file

@ -1387,8 +1387,8 @@ config_delviews(struct config_view* p)
p = np; p = np;
} }
} }
/** delete string array */
static void void
config_del_strarray(char** array, int num) config_del_strarray(char** array, int num)
{ {
int i; int i;

View file

@ -969,6 +969,9 @@ void config_deldblstrlist(struct config_str2list* list);
*/ */
void config_deltrplstrlist(struct config_str3list* list); void config_deltrplstrlist(struct config_str3list* list);
/** delete string array */
void config_del_strarray(char** array, int num);
/** delete stringbytelist */ /** delete stringbytelist */
void config_del_strbytelist(struct config_strbytelist* list); void config_del_strbytelist(struct config_strbytelist* list);