From a0baa43f48769d623bc7dfa2f8b6f04f844467c1 Mon Sep 17 00:00:00 2001 From: Jeroen Koekkoek Date: Thu, 24 Mar 2022 16:26:12 +0100 Subject: [PATCH] Allow user to specify socket type per ip-addres Listening on both UDP and TCP is likely unwanted behavior when support for XDP lands. Allow the user to configure listening on UDP, TCP or both per ip-address option in preparation. Specifying no socket at all ensures both UDP and TCP are opened, which is the current default behavior, thereby remaining backwards compatible configuration wise. --- configlexer.lex | 55 +- configparser.y | 24 + ipc.c | 4 +- nsd-checkconf.c | 4 + nsd.c | 692 +++++++++++------- nsd.conf.5.in | 5 +- nsd.conf.sample.in | 8 + nsd.h | 60 +- options.h | 2 + server.c | 236 +++--- tpkg/checkconf.tdir/checkconf.check | 75 ++ tpkg/checkconf.tdir/checkconf.nsd11.conf | 9 + .../checkconf.nsd11.invalid.conf | 4 + .../checkconf.tdir/checkconf.nsd11.tcp2x.conf | 4 + .../checkconf.tdir/checkconf.nsd11.udp2x.conf | 4 + tpkg/socket_types.tdir/socket_types.conf | 19 + tpkg/socket_types.tdir/socket_types.dsc | 16 + tpkg/socket_types.tdir/socket_types.post | 14 + tpkg/socket_types.tdir/socket_types.pre | 32 + tpkg/socket_types.tdir/socket_types.test | 50 ++ tpkg/socket_types.tdir/socket_types.zone | 7 + 21 files changed, 881 insertions(+), 443 deletions(-) create mode 100644 tpkg/checkconf.tdir/checkconf.nsd11.conf create mode 100644 tpkg/checkconf.tdir/checkconf.nsd11.invalid.conf create mode 100644 tpkg/checkconf.tdir/checkconf.nsd11.tcp2x.conf create mode 100644 tpkg/checkconf.tdir/checkconf.nsd11.udp2x.conf create mode 100644 tpkg/socket_types.tdir/socket_types.conf create mode 100644 tpkg/socket_types.tdir/socket_types.dsc create mode 100644 tpkg/socket_types.tdir/socket_types.post create mode 100644 tpkg/socket_types.tdir/socket_types.pre create mode 100644 tpkg/socket_types.tdir/socket_types.test create mode 100644 tpkg/socket_types.tdir/socket_types.zone diff --git a/configlexer.lex b/configlexer.lex index a16883406..e44f8fd2f 100644 --- a/configlexer.lex +++ b/configlexer.lex @@ -178,6 +178,7 @@ COMMENT \# COLON \: ANY [^\"\n\r\\]|\\. +%s ip_address %x quotedstring include include_quoted %% @@ -185,7 +186,6 @@ ANY [^\"\n\r\\]|\\. {SPACE}*{COMMENT}.* { LEXOUT(("comment(%s) ", yytext)); /* ignore */ } server{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER;} name{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NAME;} -ip-address{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;} interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;} ip-transparent{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_TRANSPARENT;} ip-freebind{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_FREEBIND;} @@ -299,24 +299,42 @@ cookie-secret{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_SECRET;} cookie-secret-file{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_SECRET_FILE;} xfrd-tcp-max{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_TCP_MAX;} xfrd-tcp-pipeline{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_TCP_PIPELINE;} -{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} -servers={UNQUOTEDLETTER}* { +ip-address{COLON} { + BEGIN(ip_address); + LEXOUT(("v(%s) ", yytext)); + return VAR_IP_ADDRESS; +} +tcp { + LEXOUT(("v(%s) ", yytext)); + return VAR_TCP; +} +udp { + LEXOUT(("v(%s) ", yytext)); + return VAR_UDP; +} +servers={UNQUOTEDLETTER}* { yyless(yyleng - (yyleng - 8)); LEXOUT(("v(%s) ", yytext)); return VAR_SERVERS; } -bindtodevice={UNQUOTEDLETTER}* { +bindtodevice={UNQUOTEDLETTER}* { yyless(yyleng - (yyleng - 13)); LEXOUT(("v(%s) ", yytext)); return VAR_BINDTODEVICE; } -setfib={UNQUOTEDLETTER}* { +setfib={UNQUOTEDLETTER}* { yyless(yyleng - (yyleng - 7)); LEXOUT(("v(%s) ", yytext)); return VAR_SETFIB; } +{NEWLINE} { + BEGIN(INITIAL); + LEXOUT(("NL\n")); + cfg_parser->line++; +} + cpu-affinity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CPU_AFFINITY; } xfrd-cpu-affinity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_CPU_AFFINITY; } server-[1-9][0-9]*-cpu-affinity{COLON} { @@ -333,24 +351,24 @@ server-[1-9][0-9]*-cpu-affinity{COLON} { /* Quoted strings. Strip leading and ending quotes */ \" { BEGIN(quotedstring); LEXOUT(("QS ")); } <> { - c_error("EOF inside quoted string"); - BEGIN(INITIAL); + c_error("EOF inside quoted string"); + BEGIN(INITIAL); } {ANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); } \n { cfg_parser->line++; yymore(); } \" { - LEXOUT(("QE ")); - BEGIN(INITIAL); - yytext[yyleng - 1] = '\0'; + LEXOUT(("QE ")); + BEGIN(INITIAL); + yytext[yyleng - 1] = '\0'; c_lval.str = region_strdup(cfg_parser->opt->region, yytext); - return STRING; + return STRING; } /* include: directive */ include{COLON} { LEXOUT(("v(%s) ", yytext)); BEGIN(include); } <> { - c_error("EOF inside include directive"); - BEGIN(INITIAL); + c_error("EOF inside include directive"); + BEGIN(INITIAL); } {SPACE}* { LEXOUT(("ISP ")); /* ignore */ } {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} @@ -361,8 +379,8 @@ include{COLON} { LEXOUT(("v(%s) ", yytext)); BEGIN(include); } BEGIN(INITIAL); } <> { - c_error("EOF inside quoted string"); - BEGIN(INITIAL); + c_error("EOF inside quoted string"); + BEGIN(INITIAL); } {ANY}* { LEXOUT(("ISTR(%s) ", yytext)); yymore(); } {NEWLINE} { cfg_parser->line++; yymore(); } @@ -382,7 +400,10 @@ include{COLON} { LEXOUT(("v(%s) ", yytext)); BEGIN(include); } } } -{UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", yytext)); - c_lval.str = region_strdup(cfg_parser->opt->region, yytext); return STRING; } +{UNQUOTEDLETTER}* { + LEXOUT(("unquotedstr(%s) ", yytext)); + c_lval.str = region_strdup(cfg_parser->opt->region, yytext); + return STRING; +} %% diff --git a/configparser.y b/configparser.y index fb8be0d9c..7f663a6cb 100644 --- a/configparser.y +++ b/configparser.y @@ -164,6 +164,7 @@ static int parse_range(const char *str, long long *low, long long *high); %token VAR_ALLOW_QUERY %token VAR_AXFR %token VAR_UDP +%token VAR_TCP %token VAR_NOTIFY_RETRY %token VAR_ALLOW_NOTIFY %token VAR_REQUEST_XFR @@ -226,6 +227,7 @@ server_option: cfg_parser->ip = $2; } + socket_types socket_options { cfg_parser->ip = NULL; @@ -494,6 +496,26 @@ server_option: } ; +socket_types: + | udp_and_tcp + { cfg_parser->ip->udp = 1; + cfg_parser->ip->tcp = 1; + } + | VAR_UDP + { cfg_parser->ip->tcp = 0; + cfg_parser->ip->udp = 1; + } + | VAR_TCP + { cfg_parser->ip->tcp = 1; + cfg_parser->ip->udp = 0; + } + ; + +udp_and_tcp: + VAR_UDP VAR_TCP + | VAR_TCP VAR_UDP + ; + socket_options: | socket_options socket_option ; @@ -971,6 +993,8 @@ ip_address: cfg_parser->opt->region, sizeof(*ip)); ip->address = region_strdup(cfg_parser->opt->region, $1); ip->fib = -1; + ip->udp = 2; + ip->tcp = 2; $$ = ip; } ; diff --git a/ipc.c b/ipc.c index 87478f7e1..bb80f8d7b 100644 --- a/ipc.c +++ b/ipc.c @@ -86,8 +86,8 @@ child_handle_parent_command(int fd, short event, void* arg) break; case NSD_QUIT_CHILD: /* close our listening sockets and ack */ - server_close_all_sockets(data->nsd->udp, data->nsd->ifs); - server_close_all_sockets(data->nsd->tcp, data->nsd->ifs); + server_close_all_sockets(&data->nsd->udp); + server_close_all_sockets(&data->nsd->tcp); /* mode == NSD_QUIT_CHILD */ if(write(fd, &mode, sizeof(mode)) == -1) { VERBOSITY(3, (LOG_INFO, "quit child write: %s", diff --git a/nsd-checkconf.c b/nsd-checkconf.c index b9c947288..c9c277767 100644 --- a/nsd-checkconf.c +++ b/nsd-checkconf.c @@ -607,6 +607,10 @@ config_test_print_server(nsd_options_type* opt) for(ip = opt->ip_addresses; ip; ip=ip->next) { printf("\tip-address: %s", ip->address); + if(ip->udp == 1) + printf(" udp"); + if(ip->tcp == 1) + printf(" tcp"); if(ip->servers) { const char *sep; struct range_option *n; diff --git a/nsd.c b/nsd.c index cfc41abe9..08ef4b655 100644 --- a/nsd.c +++ b/nsd.c @@ -63,6 +63,9 @@ static char hostname[MAXHOSTNAMELEN]; extern config_parser_state_type* cfg_parser; static void version(void) ATTR_NORETURN; +static const char *default_tcp_port = TCP_PORT; +static const char *default_udp_port = UDP_PORT; + /* * Print the help text. * @@ -151,145 +154,400 @@ version(void) } static void -copyaddrinfo(struct nsd_addrinfo *dest, struct addrinfo *src) +figure_port( + unsigned short *port, + const struct ip_address_option *ip, + const char *default_port) { - dest->ai_flags = src->ai_flags; - dest->ai_family = src->ai_family; - dest->ai_socktype = src->ai_socktype; - dest->ai_addrlen = src->ai_addrlen; - memcpy(&dest->ai_addr, src->ai_addr, src->ai_addrlen); + int i = 0; + const char *at; + + assert(default_port); + if (ip && ip->address && (at = strrchr(ip->address, '@'))) { + i = atoi(++at); + if (!i || i > 65535) + error("invalid port number in ip-address %s", ip->address); + } else { + const char *a = nsd.options->port ? nsd.options->port : default_port; + i = atoi(a); + if (!i || i > 65535) + error("invalid port number %s", a); + } + assert(i > 0 && i <= 65535); + *port = (unsigned short)i; } +#ifdef INET6 +# define ADDRSTRLEN INET6_ADDRSTRLEN +#else +# define ADDRSTRLEN INET_ADDRSTRLEN +#endif + static void -setup_socket( - struct nsd_socket *sock, const char *node, const char *port, - struct addrinfo *hints) +figure_sockaddr( + struct sockaddr_storage *sockaddr, + const struct ip_address_option *ip, + const struct addrinfo *hints) { - int ret; - char *host; - char host_buf[sizeof("65535") + INET6_ADDRSTRLEN + 1 /* '\0' */]; - const char *service; - struct addrinfo *addr = NULL; - - sock->fib = -1; - if(node) { - char *sep; - - if (strlcpy(host_buf, node, sizeof(host_buf)) >= sizeof(host_buf)) { - error("cannot parse address '%s': %s", node, - strerror(ENAMETOOLONG)); + int eai; + char addr[ADDRSTRLEN], service[sizeof("65535")]; + const char *node = NULL; + unsigned short port = 0; + struct addrinfo *ai = NULL; + + assert(!ip || ip->address); + + addr[0] = '\0'; + service[0] = '\0'; + + figure_port(&port, ip, hints->ai_socktype == SOCK_DGRAM ? default_udp_port : default_tcp_port); + snprintf(service, sizeof(service), "%u", port); + + if (ip) { + const char *at = strrchr(ip->address, '@'); + int addrlen = 0; + if (at) + addrlen = at - ip->address; + else + addrlen = strlen(ip->address); + snprintf(addr, sizeof(addr), "%.*s", addrlen, ip->address); + node = addr; + } + + if ((eai = getaddrinfo(node, service, hints, &ai)) != 0) + error("cannot parse address %s: getaddrinfo: %s", + node ? node : "(null)", gai_strerror(eai)); + + memmove(sockaddr, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); +} + +static int compare_sockaddr( + const struct sockaddr *sa1, const struct sockaddr *sa2, int port) +{ + int cmp; + + if (!sa1 || !sa2) + return (sa1 ? +1 : (sa2 ? -1 : 0)); + switch (sa1->sa_family) { +#ifdef INET6 + case AF_INET6: + { + const struct sockaddr_in6 *s1, *s2; + + if (sa2->sa_family != AF_INET6) + return +1; + s1 = (const struct sockaddr_in6 *)sa1; + s2 = (const struct sockaddr_in6 *)sa2; + if ((cmp = memcmp(&s1->sin6_addr, &s2->sin6_addr, sizeof(s1->sin6_addr)))) + return cmp; + if (!port) + break; + if (s1->sin6_port < s2->sin6_port) + return -1; + if (s1->sin6_port > s2->sin6_port) + return +1; + break; } +#endif + default: + { + const struct sockaddr_in *s1, *s2; - host = host_buf; - sep = strchr(host_buf, '@'); - if(sep != NULL) { - *sep = '\0'; - service = sep + 1; - } else { - service = port; + assert(sa1->sa_family == AF_INET); + if (sa2->sa_family != AF_INET) + return -1; + s1 = (const struct sockaddr_in *)sa1; + s2 = (const struct sockaddr_in *)sa2; + if ((cmp = memcmp(&s1->sin_addr, &s2->sin_addr, sizeof(s1->sin_addr)))) + return cmp; + if (!port) + break; + if (s1->sin_port < s2->sin_port) + return -1; + if (s1->sin_port > s2->sin_port) + return +1; + break; } - } else { - host = NULL; - service = port; - } + } - if((ret = getaddrinfo(host, service, hints, &addr)) == 0) { - copyaddrinfo(&sock->addr, addr); - freeaddrinfo(addr); - } else { - error("cannot parse address '%s': getaddrinfo: %s %s", - host ? host : "(null)", - gai_strerror(ret), - ret==EAI_SYSTEM ? strerror(errno) : ""); + return 0; +} + +#ifdef HAVE_GETIFADDRS +static void +setup_inet_socket_device( + struct nsd_socket *socket, + const struct ip_address_option *ip, + const struct ifaddrs *ifaddrs) +{ + char *colon; + + assert(socket); + assert(ip); + assert(ifaddrs); + +#ifdef INET6 + assert(socket->type == AF_INET || socket->type == AF_INET6); +#else + assert(socket->type == AF_INET); +#endif + assert(socket->type == socket->address.inet.ss_family); + + for(; ifaddrs; ifaddrs = ifaddrs->ifa_next) { + if (!ifaddrs->ifa_addr) + continue; + if ((ifaddrs->ifa_flags & IFF_UP) == 0 || + (ifaddrs->ifa_flags & IFF_LOOPBACK) != 0 || + (ifaddrs->ifa_flags & IFF_RUNNING) == 0) + continue; + if (compare_sockaddr(ifaddrs->ifa_addr, (struct sockaddr *)&socket->address.inet, 0) == 0) + break; } + + if (!ifaddrs) + error("cannot find device for ip-address %s", ip->address); + + strlcpy(socket->device, ifaddrs->ifa_name, sizeof(socket->device)); + if ((colon = strrchr(socket->device, '@'))) + *colon = '\0'; } +#endif /* HAVE_GETIFADDRS */ static void -figure_socket_servers( - struct nsd_socket *sock, struct ip_address_option *ip) +setup_socket_servers( + struct nsd_socket *socket, + const struct ip_address_option *ip) { - int i; - struct range_option *server; + int server; + struct range_option *range; - sock->servers = xalloc_zero(nsd_bitset_size(nsd.child_count)); - region_add_cleanup(nsd.region, free, sock->servers); - nsd_bitset_init(sock->servers, nsd.child_count); + socket->servers = xalloc_zero(nsd_bitset_size(nsd.child_count)); + region_add_cleanup(nsd.region, free, socket->servers); + nsd_bitset_init(socket->servers, nsd.child_count); - if(!ip || !ip->servers) { - /* every server must listen on this socket */ - for(i = 0; i < (int)nsd.child_count; i++) { - nsd_bitset_set(sock->servers, i); - } + /* all servers must listen on this socket */ + if (!ip || !ip->servers) { + for (server = 0; server < (int)nsd.child_count; server++) + nsd_bitset_set(socket->servers, (size_t)server); return; } /* only specific servers must listen on this socket */ - for(server = ip->servers; server; server = server->next) { - if(server->first == server->last) { - if(server->first <= 0) { - error("server %d specified for ip-address %s " - "is invalid; server ranges are 1-based", - server->first, ip->address); - } else if(server->last > (int)nsd.child_count) { - error("server %d specified for ip-address %s " - "exceeds number of servers configured " - "in server-count", - server->first, ip->address); + for (range = ip->servers; range; range = range->next) { + if (range->first == range->last) { + if (range->first <= 0) { + error("server %d specified for %s is invalid; " + "server ranges are 1-based", + range->first, ip->address); + } else if (range->first > (int)nsd.child_count) { + error("server %d specified for %s is invalid; " + "%d exceeds number of servers configured in server-count", + range->first, ip->address, range->first); } } else { - /* parse_range must ensure range itself is valid */ - assert(server->first < server->last); - if(server->first <= 0) { - error("server range %d-%d specified for " - "ip-address %s is invalid; server " - "ranges are 1-based", - server->first, server->last, ip->address); - } else if(server->last > (int)nsd.child_count) { - error("server range %d-%d specified for " - "ip-address %s exceeds number of servers " - "configured in server-count", - server->first, server->last, ip->address); + /* parse_range ensures range is valid */ + assert(range->first < range->last); + if (range->first <= 0) { + error("server range %d-%d specified for %s is invalid; " + "server ranges are 1-based", + range->first, range->last, ip->address); + } else if (range->last > (int)nsd.child_count) { + error("server range %d-%d specified for %s is invalid; " + "%d exceeds number of server configured in server-count", + range->first, range->last, ip->address, range->last); } } - for(i = server->first - 1; i < server->last; i++) { - nsd_bitset_set(sock->servers, i); - } - } + + for (server = range->first - 1; server < range->last; server++) + nsd_bitset_set(socket->servers, (size_t)server); + } } static void -figure_default_sockets( - struct nsd_socket **udp, struct nsd_socket **tcp, size_t *ifs, - const char *udp_port, const char *tcp_port, - const struct addrinfo *hints) +setup_inet_socket( + const struct ip_address_option *ip, + const struct ifaddrs *ifaddrs, + int type, + const struct sockaddr *sockaddr) +{ + struct nsd_socket_set *set; + struct nsd_socket *sockets, *socket; + size_t addrlen; + + assert(type == SOCK_DGRAM || type == SOCK_STREAM); + set = type == SOCK_DGRAM ? &nsd.udp : &nsd.tcp; + assert(!set->count || set->sockets); + + sockets = xrealloc(set->sockets, (set->count+1) * sizeof(*sockets)); + assert(sockets); + region_remove_cleanup(nsd.region, free, set->sockets); + set->count++; + set->sockets = sockets; + region_add_cleanup(nsd.region, free, set->sockets); + + socket = &set->sockets[set->count - 1]; + memset(socket, 0, sizeof(*socket)); + socket->socket = -1; + socket->family = sockaddr->sa_family; + socket->type = type; +#ifdef INET6 + assert(sockaddr->sa_family == AF_INET || + sockaddr->sa_family == AF_INET6); + if (sockaddr->sa_family == AF_INET6) + addrlen = sizeof(struct sockaddr_in6); + else +#else + assert(sockaddr->sa_family == AF_INET); +#endif + addrlen = sizeof(struct sockaddr_in); + memmove(&socket->address.inet, sockaddr, addrlen); + + setup_socket_servers(socket, ip); + + if (ip && ip->dev) { + socket->flags = NSD_BIND_DEVICE; + setup_inet_socket_device(socket, ip, ifaddrs); + } +} + +static int +setup_if_sockets( + const struct ip_address_option *ip, + const struct ifaddrs *ifaddrs) { - size_t i = 0, n = 1; - struct addrinfo ai[2] = { *hints, *hints }; + int count = 0, exists = 0; + char name[IFNAMSIZ]; + size_t namelen = 0; + unsigned short udp_port = 0, tcp_port = 0; + + /* not an interface if length exceeds size restrictions */ + if (!namelen || namelen >= IFNAMSIZ) + return 0; + strlcpy(name, ip->address, namelen); + figure_port(&udp_port, ip, default_udp_port); + figure_port(&tcp_port, ip, default_tcp_port); - assert(udp != NULL); - assert(tcp != NULL); - assert(ifs != NULL); + for (const struct ifaddrs *ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { + struct sockaddr_storage addr; + unsigned short *port = NULL; - ai[0].ai_socktype = SOCK_DGRAM; - ai[1].ai_socktype = SOCK_STREAM; + if (strcmp(ifa->ifa_name, name) != 0) + continue; + /* require at least one socket is created if interface exists */ + exists = 1; + /* skip interfaces that have no address associated */ + if (!ifa->ifa_addr) + continue; + switch (ifa->ifa_addr->sa_family) { + case AF_INET: + if (!nsd.options->do_ip4) + continue; + port = &((struct sockaddr_in *)&addr)->sin_port; + memmove(&addr, ifa->ifa_addr, sizeof(struct sockaddr_in)); + break; #ifdef INET6 -#ifdef IPV6_V6ONLY - if (hints->ai_family == AF_UNSPEC) { - ai[0].ai_family = AF_INET6; - ai[1].ai_family = AF_INET6; - n++; + case AF_INET6: + if (!nsd.options->do_ip6) + continue; + port = &((struct sockaddr_in6 *)&addr)->sin6_port; + memmove(&addr, ifa->ifa_addr, sizeof(struct sockaddr_in6)); + break; +#endif + default: + continue; + } + + assert(port); + if (ip->udp) { + *port = udp_port; + setup_inet_socket(ip, ifaddrs, SOCK_DGRAM, (struct sockaddr *)&addr); + count++; + } + + if (ip->tcp) { + *port = tcp_port; + setup_inet_socket(ip, ifaddrs, SOCK_STREAM, (struct sockaddr *)&addr); + count++; + } + } + + if (exists && !count) { + log_msg(LOG_WARNING, "warning: no sockets created for interface %s", + ip->address); } -#endif /* IPV6_V6ONLY */ -#endif /* INET6 */ - *udp = xalloc_zero((n + 1) * sizeof(struct nsd_socket)); - *tcp = xalloc_zero((n + 1) * sizeof(struct nsd_socket)); - region_add_cleanup(nsd.region, free, *udp); - region_add_cleanup(nsd.region, free, *tcp); + return count; +} + +static int +setup_ip_sockets( + const struct ip_address_option *ip, + const struct ifaddrs *ifaddrs) +{ + int count = 0; + struct addrinfo hints; + struct sockaddr_storage addr; + memset(&hints, 0, sizeof(hints)); #ifdef INET6 - if(hints->ai_family == AF_UNSPEC) { + hints.ai_family = AF_UNSPEC; + if (nsd.options->do_ip4 && !nsd.options->do_ip6) + hints.ai_family = AF_INET; + if (nsd.options->do_ip6 && !nsd.options->do_ip4) + hints.ai_family = AF_INET6; +#else + hints.ai_family = AF_INET; +#endif + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + + if (ip->udp) { + hints.ai_socktype = SOCK_DGRAM; + figure_sockaddr(&addr, ip, &hints); + setup_inet_socket(ip, ifaddrs, SOCK_DGRAM, (struct sockaddr *)&addr); + count++; + } + + if (ip->tcp) { + hints.ai_socktype = SOCK_STREAM; + figure_sockaddr(&addr, ip, &hints); + setup_inet_socket(ip, ifaddrs, SOCK_STREAM, (struct sockaddr *)&addr); + count++; + } + + assert(count); + return count; +} + +static int +setup_default_sockets(void) +{ + struct addrinfo hints[2]; + struct sockaddr_storage addr; + const char *udp_port = UDP_PORT, *tcp_port = TCP_PORT; + + assert(!nsd.udp.sockets && nsd.udp.count == 0); + assert(!nsd.tcp.sockets && nsd.tcp.count == 0); + + memset(&hints, 0, sizeof(hints)); +#ifdef INET6 + hints[0].ai_family = AF_UNSPEC; + if (nsd.options->do_ip4 && !nsd.options->do_ip6) + hints[0].ai_family = AF_INET; + if (nsd.options->do_ip6 && !nsd.options->do_ip4) + hints[0].ai_family = AF_INET6; +#else + hints[0].ai_family = AF_INET; +#endif + hints[0].ai_flags = AI_PASSIVE | AI_NUMERICHOST; + + memmove(&hints[1], &hints[0], sizeof(hints[0])); + hints[0].ai_socktype = SOCK_DGRAM; + hints[1].ai_socktype = SOCK_STREAM; + +#ifdef INET6 + if (hints[0].ai_family == AF_UNSPEC) { /* * With IPv6 we'd like to open two separate sockets, * one for IPv4 and one for IPv6, both listening to @@ -303,166 +561,72 @@ figure_default_sockets( * automatically mapped to our IPv6 socket. */ #ifdef IPV6_V6ONLY - int r; + int eai; struct addrinfo *addrs[2] = { NULL, NULL }; - if((r = getaddrinfo(NULL, udp_port, &ai[0], &addrs[0])) == 0 && - (r = getaddrinfo(NULL, tcp_port, &ai[1], &addrs[1])) == 0) + hints[0].ai_family = AF_INET6; + hints[1].ai_family = AF_INET6; + + if ((eai = getaddrinfo(NULL, udp_port, &hints[0], &addrs[0])) == 0 && + (eai = getaddrinfo(NULL, tcp_port, &hints[1], &addrs[1])) == 0) { - (*udp)[i].flags |= NSD_SOCKET_IS_OPTIONAL; - (*udp)[i].fib = -1; - copyaddrinfo(&(*udp)[i].addr, addrs[0]); - figure_socket_servers(&(*udp)[i], NULL); - (*tcp)[i].flags |= NSD_SOCKET_IS_OPTIONAL; - (*tcp)[i].fib = -1; - copyaddrinfo(&(*tcp)[i].addr, addrs[1]); - figure_socket_servers(&(*tcp)[i], NULL); - i++; + setup_inet_socket(NULL, NULL, SOCK_DGRAM, addrs[0]->ai_addr); + nsd.udp.sockets[nsd.udp.count - 1].flags |= NSD_OPTIONAL_SOCKET; + setup_inet_socket(NULL, NULL, SOCK_STREAM, addrs[1]->ai_addr); + nsd.tcp.sockets[nsd.tcp.count - 1].flags |= NSD_OPTIONAL_SOCKET; } else { log_msg(LOG_WARNING, "No IPv6, fallback to IPv4. getaddrinfo: %s", - r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r)); + eai == EAI_SYSTEM ? strerror(errno) : gai_strerror(eai)); } - if(addrs[0]) + if (addrs[0]) freeaddrinfo(addrs[0]); - if(addrs[1]) + if (addrs[1]) freeaddrinfo(addrs[1]); - ai[0].ai_family = AF_INET; - ai[1].ai_family = AF_INET; + hints[0].ai_family = AF_INET; + hints[1].ai_family = AF_INET; #endif /* IPV6_V6ONLY */ } #endif /* INET6 */ - *ifs = i + 1; - setup_socket(&(*udp)[i], NULL, udp_port, &ai[0]); - figure_socket_servers(&(*udp)[i], NULL); - setup_socket(&(*tcp)[i], NULL, tcp_port, &ai[1]); - figure_socket_servers(&(*tcp)[i], NULL); + figure_sockaddr(&addr, NULL, &hints[0]); + setup_inet_socket(NULL, NULL, SOCK_DGRAM, (struct sockaddr *)&addr); + figure_sockaddr(&addr, NULL, &hints[1]); + setup_inet_socket(NULL, NULL, SOCK_STREAM, (struct sockaddr *)&addr); + return nsd.udp.count + nsd.tcp.count; } -#ifdef HAVE_GETIFADDRS static int -find_device( - struct nsd_socket *sock, - const struct ifaddrs *ifa) +setup_sockets(void) { - for(; ifa != NULL; ifa = ifa->ifa_next) { - if((ifa->ifa_addr == NULL) || - (ifa->ifa_addr->sa_family != sock->addr.ai_family) || - ((ifa->ifa_flags & IFF_UP) == 0 || - (ifa->ifa_flags & IFF_LOOPBACK) != 0 || - (ifa->ifa_flags & IFF_RUNNING) == 0)) - { - continue; - } - -#ifdef INET6 - if(ifa->ifa_addr->sa_family == AF_INET6) { - struct sockaddr_in6 *sa1, *sa2; - size_t sz = sizeof(struct in6_addr); - sa1 = (struct sockaddr_in6 *)ifa->ifa_addr; - sa2 = (struct sockaddr_in6 *)&sock->addr.ai_addr; - if(memcmp(&sa1->sin6_addr, &sa2->sin6_addr, sz) == 0) { - break; - } - } else -#endif - if(ifa->ifa_addr->sa_family == AF_INET) { - struct sockaddr_in *sa1, *sa2; - sa1 = (struct sockaddr_in *)ifa->ifa_addr; - sa2 = (struct sockaddr_in *)&sock->addr.ai_addr; - if(sa1->sin_addr.s_addr == sa2->sin_addr.s_addr) { - break; - } - } - } - - if(ifa != NULL) { - size_t len = strlcpy(sock->device, ifa->ifa_name, sizeof(sock->device)); - if(len < sizeof(sock->device)) { - char *colon = strchr(sock->device, ':'); - if(colon != NULL) - *colon = '\0'; - return 1; - } - } - - return 0; -} -#endif /* HAVE_GETIFADDRS */ - -static void -figure_sockets( - struct nsd_socket **udp, struct nsd_socket **tcp, size_t *ifs, - struct ip_address_option *ips, - const char *udp_port, const char *tcp_port, - const struct addrinfo *hints) -{ - size_t i = 0; - struct addrinfo ai = *hints; struct ip_address_option *ip; #ifdef HAVE_GETIFADDRS - struct ifaddrs *ifa = NULL; + struct ifaddrs *ifaddrs = NULL; #endif int bind_device = 0; - if(!ips) { - figure_default_sockets( - udp, tcp, ifs, udp_port, tcp_port, hints); - return; - } + if (!nsd.options->ip_addresses) + return setup_default_sockets(); - *ifs = 0; - for(ip = ips; ip; ip = ip->next) { - (*ifs)++; + for(ip = nsd.options->ip_addresses; ip; ip = ip->next) bind_device |= (ip->dev != 0); - } #ifdef HAVE_GETIFADDRS - if(bind_device && getifaddrs(&ifa) == -1) { + if(bind_device && getifaddrs(&ifaddrs) == -1) error("getifaddrs failed: %s", strerror(errno)); - } #endif - *udp = xalloc_zero((*ifs + 1) * sizeof(struct nsd_socket)); - *tcp = xalloc_zero((*ifs + 1) * sizeof(struct nsd_socket)); - region_add_cleanup(nsd.region, free, *udp); - region_add_cleanup(nsd.region, free, *tcp); - - ai.ai_flags |= AI_NUMERICHOST; - for(ip = ips, i = 0; ip; ip = ip->next, i++) { - ai.ai_socktype = SOCK_DGRAM; - setup_socket(&(*udp)[i], ip->address, udp_port, &ai); - figure_socket_servers(&(*udp)[i], ip); - ai.ai_socktype = SOCK_STREAM; - setup_socket(&(*tcp)[i], ip->address, tcp_port, &ai); - figure_socket_servers(&(*tcp)[i], ip); - if(ip->fib != -1) { - (*udp)[i].fib = ip->fib; - (*tcp)[i].fib = ip->fib; - } -#ifdef HAVE_GETIFADDRS - if(ip->dev != 0) { - (*udp)[i].flags |= NSD_BIND_DEVICE; - (*tcp)[i].flags |= NSD_BIND_DEVICE; - if(ifa != NULL && (find_device(&(*udp)[i], ifa) == 0 || - find_device(&(*tcp)[i], ifa) == 0)) - { - error("cannot find device for ip-address %s", - ip->address); - } - } -#endif + for(ip = nsd.options->ip_addresses; ip; ip = ip->next) { + if (!setup_if_sockets(ip, ifaddrs)) + setup_ip_sockets(ip, ifaddrs); } - assert(i == *ifs); - #ifdef HAVE_GETIFADDRS - if(ifa != NULL) { - freeifaddrs(ifa); - } + freeifaddrs(ifaddrs); #endif + + return nsd.udp.count + nsd.tcp.count; } /* print server affinity for given socket. "*" if socket has no affinity with @@ -472,7 +636,7 @@ figure_sockets( printed if socket has affinity with servers number one and three, but not server number two. */ static ssize_t -print_socket_servers(struct nsd_socket *sock, char *buf, size_t bufsz) +print_socket_servers(const struct nsd_socket *sock, char *buf, size_t bufsz) { int i, x, y, z, n = (int)(sock->servers->size); char *sep = ""; @@ -528,23 +692,16 @@ print_socket_servers(struct nsd_socket *sock, char *buf, size_t bufsz) } static void -print_sockets( - struct nsd_socket *udp, struct nsd_socket *tcp, size_t ifs) +print_sockets(void) { char sockbuf[INET6_ADDRSTRLEN + 6 + 1]; char *serverbuf; - size_t i, serverbufsz, servercnt; + size_t serverbufsz, servercnt; const char *fmt = "listen on ip-address %s (%s) with server(s): %s"; struct nsd_bitset *servers; + struct nsd_socket_set *sets[2] = { &nsd.udp, &nsd.tcp }; - if(ifs == 0) { - return; - } - - assert(udp != NULL); - assert(tcp != NULL); - - servercnt = udp[0].servers->size; + servercnt = nsd.child_count; serverbufsz = (((servercnt / 10) * servercnt) + servercnt) + 1; serverbuf = xalloc(serverbufsz); @@ -552,23 +709,22 @@ print_sockets( servers = xalloc(nsd_bitset_size(servercnt)); nsd_bitset_init(servers, (size_t)servercnt); - for(i = 0; i < ifs; i++) { - assert(udp[i].servers->size == servercnt); - addrport2str((void*)&udp[i].addr.ai_addr, sockbuf, sizeof(sockbuf)); - print_socket_servers(&udp[i], serverbuf, serverbufsz); - nsd_bitset_or(servers, servers, udp[i].servers); - VERBOSITY(3, (LOG_NOTICE, fmt, sockbuf, "udp", serverbuf)); - assert(tcp[i].servers->size == servercnt); - addrport2str((void*)&tcp[i].addr.ai_addr, sockbuf, sizeof(sockbuf)); - print_socket_servers(&tcp[i], serverbuf, serverbufsz); - nsd_bitset_or(servers, servers, tcp[i].servers); - VERBOSITY(3, (LOG_NOTICE, fmt, sockbuf, "tcp", serverbuf)); + for (size_t i = 0; i < (sizeof(sets)/sizeof(*sets)); i++) { + const struct nsd_socket_set *set = sets[i]; + for (size_t j = 0; j < set->count; j++) { + const struct nsd_socket *socket = &set->sockets[j]; + const char *socktype = socket->type == SOCK_DGRAM ? "udp" : "tcp"; + assert(socket->servers->size == servercnt); + addrport2str((void*)&socket->address.inet, sockbuf, sizeof(sockbuf)); + print_socket_servers(socket, serverbuf, serverbufsz); + nsd_bitset_or(servers, servers, socket->servers); + VERBOSITY(3, (LOG_NOTICE, fmt, sockbuf, socktype, serverbuf)); + } } - /* warn user of unused servers */ - for(i = 0; i < servercnt; i++) { - if(!nsd_bitset_isset(servers, i)) { + for (size_t i = 0; i < servercnt; i++) { + if (!nsd_bitset_isset(servers, i)) { log_msg(LOG_WARNING, "server %zu will not listen on " "any specified ip-address", i+1); } @@ -882,8 +1038,6 @@ main(int argc, char *argv[]) struct ip_address_option *ip; struct addrinfo hints; - const char *udp_port = 0; - const char *tcp_port = 0; const char *configfile = CONFIGFILE; @@ -1016,8 +1170,8 @@ main(int argc, char *argv[]) if (atoi(optarg) == 0) { error("port argument must be numeric."); } - tcp_port = optarg; - udp_port = optarg; + default_tcp_port = optarg; + default_udp_port = optarg; break; case 's': #ifdef BIND8_STATS @@ -1143,16 +1297,6 @@ main(int argc, char *argv[]) nsd.tls_ctx = NULL; #endif - if(udp_port == 0) - { - if(nsd.options->port != 0) { - udp_port = nsd.options->port; - tcp_port = nsd.options->port; - } else { - udp_port = UDP_PORT; - tcp_port = TCP_PORT; - } - } #ifdef BIND8_STATS if(nsd.st.period == 0) { nsd.st.period = nsd.options->statistics; @@ -1338,9 +1482,7 @@ main(int argc, char *argv[]) nsd.this_child = NULL; - resolve_interface_names(nsd.options); - figure_sockets(&nsd.udp, &nsd.tcp, &nsd.ifs, - nsd.options->ip_addresses, udp_port, tcp_port, &hints); + setup_sockets(); /* Parse the username into uid and gid */ nsd.gid = getgid(); @@ -1453,7 +1595,7 @@ main(int argc, char *argv[]) } #endif - print_sockets(nsd.udp, nsd.tcp, nsd.ifs); + print_sockets(); /* Setup the signal handling... */ action.sa_handler = sig_handler; @@ -1521,8 +1663,8 @@ main(int argc, char *argv[]) break; default: /* Parent is done */ - server_close_all_sockets(nsd.udp, nsd.ifs); - server_close_all_sockets(nsd.tcp, nsd.ifs); + server_close_all_sockets(&nsd.udp); + server_close_all_sockets(&nsd.tcp); exit(0); } diff --git a/nsd.conf.5.in b/nsd.conf.5.in index 6162c9d14..98581906b 100644 --- a/nsd.conf.5.in +++ b/nsd.conf.5.in @@ -142,9 +142,10 @@ clause. There may only be one .B server: clause. .TP -.B ip\-address:\fR [@port] [servers] [bindtodevice] [setfib] +.B ip\-address:\fR [@port] [tcp|udp] [servers] [bindtodevice] [setfib] NSD will bind to the listed ip\-address. Can be given multiple times -to bind multiple ip\-addresses. Optionally, a port number can be given. +to bind multiple ip\-addresses. Optionally, a port number and protocol type +can be provided. If none are given NSD listens to the wildcard interface. Same as commandline option .BR \-a. .IP diff --git a/nsd.conf.sample.in b/nsd.conf.sample.in index 0e006bafb..49da305e2 100644 --- a/nsd.conf.sample.in +++ b/nsd.conf.sample.in @@ -40,6 +40,14 @@ server: # ip-address: 1.2.3.4@5678 # ip-address: 12fe::8ef0 # + # IP addresses can be configured for a particular protocol if desired + # (default is to accept UDP and TCP). Specify what protocol types to + # to accept by specifying the protocol with the IP address. + # + # ip-address: 1.2.3.4 tcp + # ip-address: 1.2.3.4@5678 udp + # ip-address: 12fe::8ef0 tcp udp + # # IP addresses can be configured per-server to avoid waking up more # than one server when a packet comes in (thundering herd problem) or # to partition sockets across servers to improve select/poll diff --git a/nsd.h b/nsd.h index 177d5d5cf..c9aee0684 100644 --- a/nsd.h +++ b/nsd.h @@ -119,26 +119,46 @@ typedef unsigned long stc_type; #define ZTATUP2(nsd, zone, stc, i) /* Nothing */ #endif /* USE_ZONE_STATS */ -#define NSD_SOCKET_IS_OPTIONAL (1<<0) +#define NSD_OPTIONAL_SOCKET (1<<0) #define NSD_BIND_DEVICE (1<<1) -struct nsd_addrinfo -{ - int ai_flags; - int ai_family; - int ai_socktype; - socklen_t ai_addrlen; - struct sockaddr_storage ai_addr; -}; - struct nsd_socket { - struct nsd_addrinfo addr; - int s; - int flags; - struct nsd_bitset *servers; + int socket; + int family; /* address family, i.e. AF_INET, AF_INET6 or AF_XDP */ + int type; /* socket type, i.e. SOCK_DGRAM, SOCK_STREAM or SOCK_RAW */ + int flags; /* flags to instruct server, not interface flags */ char device[IFNAMSIZ]; int fib; + struct nsd_bitset *servers; + union { + struct sockaddr_storage inet; +#if 0 + struct { + unsigned int index; + unsigned int queue; + // xdp sockets bind to a netdev/queue combination as opposed to an ip + // address. if a network interface is specified, there is no need to + // distinguish between ip addresses. if an ip address is specified, + // filters apply. + // + // address to filter on are communicated to the ebpf program via a + // separate map (or not) based on the configuration + struct { + size_t count; + struct sockaddr_storage *addresses; + } filter; + } xdp; +#endif + } address; +}; + +struct nsd_socket_set +{ + /* number of interfaces */ + size_t count; + /* array increased for udp and tcp if so_reuseport is in use */ + struct nsd_socket *sockets; }; struct nsd_child @@ -256,16 +276,14 @@ struct nsd cpuset_t* xfrd_cpuset; #endif - /* number of interfaces */ - size_t ifs; /* non0 if so_reuseport is in use, if so, tcp, udp array increased */ int reuseport; - /* TCP specific configuration (array size ifs) */ - struct nsd_socket* tcp; + /* TCP specific configuration */ + struct nsd_socket_set tcp; - /* UDP specific configuration (array size ifs) */ - struct nsd_socket* udp; + /* UDP specific configuration */ + struct nsd_socket_set udp; edns_data_type edns_ipv4; #if defined(INET6) @@ -361,7 +379,7 @@ int server_prepare(struct nsd *nsd); void server_main(struct nsd *nsd); void server_child(struct nsd *nsd); void server_shutdown(struct nsd *nsd) ATTR_NORETURN; -void server_close_all_sockets(struct nsd_socket sockets[], size_t n); +void server_close_all_sockets(struct nsd_socket_set *set); const char* nsd_event_vs(void); const char* nsd_event_method(void); struct event_base* nsd_child_event_base(void); diff --git a/options.h b/options.h index 54357c863..95f4c5848 100644 --- a/options.h +++ b/options.h @@ -195,6 +195,8 @@ struct ip_address_option { struct range_option* servers; int dev; int fib; + int udp; + int tcp; }; struct cpu_option { diff --git a/server.c b/server.c index 284f370cd..b44441fe4 100644 --- a/server.c +++ b/server.c @@ -721,9 +721,8 @@ set_cloexec(struct nsd_socket *sock) { assert(sock != NULL); - if(fcntl(sock->s, F_SETFD, FD_CLOEXEC) == -1) { - const char *socktype = - sock->addr.ai_family == SOCK_DGRAM ? "udp" : "tcp"; + if(fcntl(sock->socket, F_SETFD, FD_CLOEXEC) == -1) { + const char *socktype = sock->type == SOCK_DGRAM ? "udp" : "tcp"; log_msg(LOG_ERR, "fcntl(..., O_CLOEXEC) failed for %s: %s", socktype, strerror(errno)); return -1; @@ -751,7 +750,7 @@ set_reuseport(struct nsd_socket *sock) static const char optname[] = "SO_REUSEPORT"; #endif /* SO_REUSEPORT_LB */ - if (0 == setsockopt(sock->s, SOL_SOCKET, opt, &on, sizeof(on))) { + if (0 == setsockopt(sock->socket, SOL_SOCKET, opt, &on, sizeof(on))) { return 1; } else if(verbosity >= 3 || errno != ENOPROTOOPT) { log_msg(LOG_ERR, "setsockopt(..., %s, ...) failed: %s", @@ -770,7 +769,7 @@ set_reuseaddr(struct nsd_socket *sock) { #ifdef SO_REUSEADDR int on = 1; - if(setsockopt(sock->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == 0) { + if(setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == 0) { return 1; } log_msg(LOG_ERR, "setsockopt(..., SO_REUSEADDR, ...) failed: %s", @@ -786,7 +785,7 @@ set_rcvbuf(struct nsd_socket *sock, int rcv) #ifdef SO_RCVBUF #ifdef SO_RCVBUFFORCE if(0 == setsockopt( - sock->s, SOL_SOCKET, SO_RCVBUFFORCE, &rcv, sizeof(rcv))) + sock->socket, SOL_SOCKET, SO_RCVBUFFORCE, &rcv, sizeof(rcv))) { return 1; } @@ -798,7 +797,7 @@ set_rcvbuf(struct nsd_socket *sock, int rcv) return -1; #else /* !SO_RCVBUFFORCE */ if (0 == setsockopt( - sock->s, SOL_SOCKET, SO_RCVBUF, &rcv, sizeof(rcv))) + sock->socket, SOL_SOCKET, SO_RCVBUF, &rcv, sizeof(rcv))) { return 1; } @@ -820,7 +819,7 @@ set_sndbuf(struct nsd_socket *sock, int snd) #ifdef SO_SNDBUF #ifdef SO_SNDBUFFORCE if(0 == setsockopt( - sock->s, SOL_SOCKET, SO_SNDBUFFORCE, &snd, sizeof(snd))) + sock->socket, SOL_SOCKET, SO_SNDBUFFORCE, &snd, sizeof(snd))) { return 1; } @@ -832,7 +831,7 @@ set_sndbuf(struct nsd_socket *sock, int snd) return -1; #else /* !SO_SNDBUFFORCE */ if(0 == setsockopt( - sock->s, SOL_SOCKET, SO_SNDBUF, &snd, sizeof(snd))) + sock->socket, SOL_SOCKET, SO_SNDBUF, &snd, sizeof(snd))) { return 1; } @@ -851,10 +850,9 @@ set_sndbuf(struct nsd_socket *sock, int snd) static int set_nonblock(struct nsd_socket *sock) { - const char *socktype = - sock->addr.ai_socktype == SOCK_DGRAM ? "udp" : "tcp"; + const char *socktype = sock->type == SOCK_DGRAM ? "udp" : "tcp"; - if(fcntl(sock->s, F_SETFL, O_NONBLOCK) == -1) { + if(fcntl(sock->socket, F_SETFL, O_NONBLOCK) == -1) { log_msg(LOG_ERR, "fctnl(..., O_NONBLOCK) failed for %s: %s", socktype, strerror(errno)); return -1; @@ -869,11 +867,10 @@ set_ipv6_v6only(struct nsd_socket *sock) { #ifdef IPV6_V6ONLY int on = 1; - const char *socktype = - sock->addr.ai_socktype == SOCK_DGRAM ? "udp" : "tcp"; + const char *socktype = sock->type == SOCK_DGRAM ? "udp" : "tcp"; if(0 == setsockopt( - sock->s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) + sock->socket, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) { return 1; } @@ -912,7 +909,7 @@ set_ipv6_use_min_mtu(struct nsd_socket *sock) static const char optname[] = "IPV6_MTU"; #endif if(0 == setsockopt( - sock->s, IPPROTO_IPV6, opt, &optval, sizeof(optval))) + sock->socket, IPPROTO_IPV6, opt, &optval, sizeof(optval))) { return 1; } @@ -946,7 +943,7 @@ set_ipv4_no_pmtu_disc(struct nsd_socket *sock) */ optval = IP_PMTUDISC_OMIT; if(0 == setsockopt( - sock->s, IPPROTO_IP, opt, &optval, sizeof(optval))) + sock->socket, IPPROTO_IP, opt, &optval, sizeof(optval))) { return 1; } @@ -958,7 +955,7 @@ set_ipv4_no_pmtu_disc(struct nsd_socket *sock) /* Use IP_PMTUDISC_DONT if IP_PMTUDISC_OMIT failed / undefined. */ optval = IP_PMTUDISC_DONT; if(0 == setsockopt( - sock->s, IPPROTO_IP, opt, &optval, sizeof(optval))) + sock->socket, IPPROTO_IP, opt, &optval, sizeof(optval))) { return 1; } @@ -970,7 +967,7 @@ set_ipv4_no_pmtu_disc(struct nsd_socket *sock) #elif defined(IP_DONTFRAG) int off = 0; if (0 == setsockopt( - sock->s, IPPROTO_IP, IP_DONTFRAG, &off, sizeof(off))) + sock->socket, IPPROTO_IP, IP_DONTFRAG, &off, sizeof(off))) { return 1; } @@ -990,9 +987,8 @@ set_ip_freebind(struct nsd_socket *sock) { #ifdef IP_FREEBIND int on = 1; - const char *socktype = - sock->addr.ai_socktype == SOCK_DGRAM ? "udp" : "tcp"; - if(setsockopt(sock->s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) == 0) + const char *socktype = sock->type == SOCK_DGRAM ? "udp" : "tcp"; + if(setsockopt(sock->socket, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) == 0) { return 1; } @@ -1051,12 +1047,11 @@ set_ip_transparent(struct nsd_socket *sock) # endif int on = 1; - const char *socktype = - sock->addr.ai_socktype == SOCK_DGRAM ? "udp" : "tcp"; - const int is_ip6 = (sock->addr.ai_family == AF_INET6); + const char *socktype = sock->type == SOCK_DGRAM ? "udp" : "tcp"; + const int is_ip6 = (sock->family == AF_INET6); if(0 == setsockopt( - sock->s, + sock->socket, is_ip6 ? NSD_SOCKET_OPTION_TRANSPARENT_OPTLEVEL6 : NSD_SOCKET_OPTION_TRANSPARENT_OPTLEVEL, is_ip6 ? NSD_SOCKET_OPTION_TRANSPARENT6 : NSD_SOCKET_OPTION_TRANSPARENT, &on, sizeof(on))) @@ -1076,7 +1071,7 @@ static int set_tcp_maxseg(struct nsd_socket *sock, int mss) { #if defined(IPPROTO_TCP) && defined(TCP_MAXSEG) - if(setsockopt(sock->s, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss)) == 0) { + if(setsockopt(sock->socket, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss)) == 0) { return 1; } log_msg(LOG_ERR, "setsockopt(..., TCP_MAXSEG, ...) failed for tcp: %s", @@ -1108,7 +1103,7 @@ set_tcp_fastopen(struct nsd_socket *sock) qlen = 5; #endif if (0 == setsockopt( - sock->s, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen))) + sock->socket, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen))) { return 1; } @@ -1136,7 +1131,7 @@ static int set_bindtodevice(struct nsd_socket *sock) { #if defined(SO_BINDTODEVICE) - if(setsockopt(sock->s, SOL_SOCKET, SO_BINDTODEVICE, + if(setsockopt(sock->socket, SOL_SOCKET, SO_BINDTODEVICE, sock->device, strlen(sock->device)) == -1) { log_msg(LOG_ERR, "setsockopt(..., %s, %s, ...) failed: %s", @@ -1155,7 +1150,7 @@ static int set_setfib(struct nsd_socket *sock) { #if defined(SO_SETFIB) - if(setsockopt(sock->s, SOL_SOCKET, SO_SETFIB, + if(setsockopt(sock->socket, SOL_SOCKET, SO_SETFIB, (const void *)&sock->fib, sizeof(sock->fib)) == -1) { log_msg(LOG_ERR, "setsockopt(..., %s, %d, ...) failed: %s", @@ -1174,13 +1169,12 @@ static int open_udp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works) { int rcv = 1*1024*1024, snd = 1*1024*1024; + socklen_t addrlen = 0; - if(-1 == (sock->s = socket( - sock->addr.ai_family, sock->addr.ai_socktype, 0))) - { + if(-1 == (sock->socket = socket(sock->family, sock->type, 0))) { #ifdef INET6 - if((sock->flags & NSD_SOCKET_IS_OPTIONAL) && - (sock->addr.ai_family == AF_INET6) && + if((sock->flags & NSD_OPTIONAL_SOCKET) && + (sock->family == AF_INET6) && (errno == EAFNOSUPPORT)) { log_msg(LOG_WARNING, "fallback to UDP4, no IPv6: " @@ -1207,13 +1201,16 @@ open_udp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works) if(set_sndbuf(sock, snd) == -1) return -1; #ifdef INET6 - if(sock->addr.ai_family == AF_INET6) { + if(sock->family == AF_INET6) { + addrlen = sizeof(struct sockaddr_in6); if(set_ipv6_v6only(sock) == -1 || set_ipv6_use_min_mtu(sock) == -1) return -1; } else #endif /* INET6 */ - if(sock->addr.ai_family == AF_INET) { + { + addrlen = sizeof(struct sockaddr_in); + assert(sock->family == AF_INET); if(set_ipv4_no_pmtu_disc(sock) == -1) return -1; } @@ -1233,9 +1230,9 @@ open_udp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works) if(sock->fib != -1 && set_setfib(sock) == -1) return -1; - if(bind(sock->s, (struct sockaddr *)&sock->addr.ai_addr, sock->addr.ai_addrlen) == -1) { + if(bind(sock->socket, (struct sockaddr *)&sock->address.inet, addrlen) == -1) { char buf[256]; - addrport2str((void*)&sock->addr.ai_addr, buf, sizeof(buf)); + addrport2str((void*)&sock->address.inet, buf, sizeof(buf)); log_msg(LOG_ERR, "can't bind udp socket %s: %s", buf, strerror(errno)); return -1; @@ -1247,18 +1244,17 @@ open_udp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works) static int open_tcp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works) { + socklen_t addrlen = 0; #ifdef USE_TCP_FASTOPEN report_tcp_fastopen_config(); #endif (void)reuseport_works; - if(-1 == (sock->s = socket( - sock->addr.ai_family, sock->addr.ai_socktype, 0))) - { + if(-1 == (sock->socket = socket(sock->family, sock->type, 0))) { #ifdef INET6 - if((sock->flags & NSD_SOCKET_IS_OPTIONAL) && - (sock->addr.ai_family == AF_INET6) && + if((sock->flags & NSD_OPTIONAL_SOCKET) && + (sock->family == AF_INET6) && (errno == EAFNOSUPPORT)) { log_msg(LOG_WARNING, "fallback to TCP4, no IPv6: " @@ -1278,12 +1274,17 @@ open_tcp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works) (void)set_reuseaddr(sock); #ifdef INET6 - if(sock->addr.ai_family == AF_INET6) { + if(sock->family == AF_INET6) { + addrlen = sizeof(struct sockaddr_in6); if (set_ipv6_v6only(sock) == -1 || set_ipv6_use_min_mtu(sock) == -1) return -1; - } + } else #endif + { + assert(sock->family == AF_INET); + addrlen = sizeof(struct sockaddr_in); + } if(nsd->tcp_mss > 0) set_tcp_maxseg(sock, nsd->tcp_mss); @@ -1299,9 +1300,9 @@ open_tcp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works) if(sock->fib != -1 && set_setfib(sock) == -1) return -1; - if(bind(sock->s, (struct sockaddr *)&sock->addr.ai_addr, sock->addr.ai_addrlen) == -1) { + if(bind(sock->socket, (struct sockaddr *)&sock->address.inet, addrlen) == -1) { char buf[256]; - addrport2str((void*)&sock->addr.ai_addr, buf, sizeof(buf)); + addrport2str((void*)&sock->address.inet, buf, sizeof(buf)); log_msg(LOG_ERR, "can't bind tcp socket %s: %s", buf, strerror(errno)); return -1; @@ -1311,7 +1312,7 @@ open_tcp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works) (void)set_tcp_fastopen(sock); #endif - if(listen(sock->s, TCP_BACKLOG) == -1) { + if(listen(sock->socket, TCP_BACKLOG) == -1) { log_msg(LOG_ERR, "can't listen: %s", strerror(errno)); return -1; } @@ -1328,51 +1329,42 @@ server_init(struct nsd *nsd) size_t i; int reuseport = 1; /* Determine if REUSEPORT works. */ - /* open server interface ports */ - for(i = 0; i < nsd->ifs; i++) { - if(open_udp_socket(nsd, &nsd->udp[i], &reuseport) == -1 || - open_tcp_socket(nsd, &nsd->tcp[i], &reuseport) == -1) - { + /* open server udp interface ports */ + for (i = 0; i < nsd->udp.count; i++) { + if (open_udp_socket(nsd, &nsd->udp.sockets[i], &reuseport) == -1) return -1; - } } - if(nsd->reuseport && reuseport) { - size_t ifs = nsd->ifs * nsd->reuseport; + /* open server tcp interface ports */ + for (i = 0; i < nsd->tcp.count; i++) { + if (open_tcp_socket(nsd, &nsd->tcp.sockets[i], &reuseport) == -1) + return -1; + } + + if (nsd->reuseport && reuseport) { + size_t ifs = nsd->udp.count * nsd->reuseport; - /* increase the size of the interface arrays, there are going + /* increase the size of the udp interface array, there are going * to be separate interface file descriptors for every server * instance */ - region_remove_cleanup(nsd->region, free, nsd->udp); - region_remove_cleanup(nsd->region, free, nsd->tcp); - - nsd->udp = xrealloc(nsd->udp, ifs * sizeof(*nsd->udp)); - nsd->tcp = xrealloc(nsd->tcp, ifs * sizeof(*nsd->tcp)); - region_add_cleanup(nsd->region, free, nsd->udp); - region_add_cleanup(nsd->region, free, nsd->tcp); - if(ifs > nsd->ifs) { - memset(&nsd->udp[nsd->ifs], 0, - (ifs-nsd->ifs)*sizeof(*nsd->udp)); - memset(&nsd->tcp[nsd->ifs], 0, - (ifs-nsd->ifs)*sizeof(*nsd->tcp)); + region_remove_cleanup(nsd->region, free, nsd->udp.sockets); + + nsd->udp.sockets = xrealloc(nsd->udp.sockets, ifs * sizeof(*nsd->udp.sockets)); + region_add_cleanup(nsd->region, free, nsd->udp.sockets); + + if (ifs > nsd->udp.count) { + memset(&nsd->udp.sockets[nsd->udp.count], 0, + (ifs - nsd->udp.count) * sizeof(*nsd->udp.sockets)); } - for(i = nsd->ifs; i < ifs; i++) { - nsd->udp[i] = nsd->udp[i%nsd->ifs]; - nsd->udp[i].s = -1; - if(open_udp_socket(nsd, &nsd->udp[i], &reuseport) == -1) { + for (i = nsd->udp.count; i < ifs; i++) { + nsd->udp.sockets[i] = nsd->udp.sockets[i%nsd->udp.count]; + nsd->udp.sockets[i].socket = -1; + if (open_udp_socket(nsd, &nsd->udp.sockets[i], &reuseport) == -1) return -1; - } - /* Turn off REUSEPORT for TCP by copying the socket - * file descriptor. - * This means we should not close TCP used by - * other servers in reuseport enabled mode, in - * server_child(). - */ - nsd->tcp[i] = nsd->tcp[i%nsd->ifs]; } - nsd->ifs = ifs; + nsd->udp.count = ifs; } else { nsd->reuseport = 0; } @@ -1469,20 +1461,20 @@ server_start_children(struct nsd *nsd, region_type* region, netio_type* netio, static void server_close_socket(struct nsd_socket *sock) { - if(sock->s != -1) { - close(sock->s); - sock->s = -1; + if(sock->socket != -1) { + close(sock->socket); + sock->socket = -1; } } void -server_close_all_sockets(struct nsd_socket sockets[], size_t n) +server_close_all_sockets(struct nsd_socket_set *set) { size_t i; /* Close all the sockets... */ - for (i = 0; i < n; ++i) { - server_close_socket(&sockets[i]); + for (i = 0; i < set->count; ++i) { + server_close_socket(&set->sockets[i]); } } @@ -1495,8 +1487,8 @@ server_shutdown(struct nsd *nsd) { size_t i; - server_close_all_sockets(nsd->udp, nsd->ifs); - server_close_all_sockets(nsd->tcp, nsd->ifs); + server_close_all_sockets(&nsd->udp); + server_close_all_sockets(&nsd->tcp); /* CHILD: close command channel to parent */ if(nsd->this_child && nsd->this_child->parent_fd != -1) { @@ -1689,8 +1681,8 @@ server_send_soa_xfrd(struct nsd* nsd, int shortsoa) if(nsd->signal_hint_shutdown) { shutdown: log_msg(LOG_WARNING, "signal received, shutting down..."); - server_close_all_sockets(nsd->udp, nsd->ifs); - server_close_all_sockets(nsd->tcp, nsd->ifs); + server_close_all_sockets(&nsd->udp); + server_close_all_sockets(&nsd->tcp); #ifdef HAVE_SSL daemon_remote_close(nsd->rc); #endif @@ -2737,8 +2729,8 @@ server_main(struct nsd *nsd) log_msg(LOG_WARNING, "signal received, shutting down..."); /* close opened ports to avoid race with restart of nsd */ - server_close_all_sockets(nsd->udp, nsd->ifs); - server_close_all_sockets(nsd->tcp, nsd->ifs); + server_close_all_sockets(&nsd->udp); + server_close_all_sockets(&nsd->tcp); #ifdef HAVE_SSL daemon_remote_close(nsd->rc); #endif @@ -2904,7 +2896,7 @@ add_udp_handler( data->socket = sock; memset(handler, 0, sizeof(*handler)); - event_set(handler, sock->s, EV_PERSIST|EV_READ, handle_udp, data); + event_set(handler, sock->socket, EV_PERSIST|EV_READ, handle_udp, data); if(event_base_set(nsd->event_base, handler) != 0) log_msg(LOG_ERR, "nsd udp: event_base_set failed"); if(event_add(handler, NULL) != 0) @@ -2925,12 +2917,12 @@ add_tcp_handler( #ifdef HAVE_SSL if (nsd->tls_ctx && nsd->options->tls_port && - using_tls_port((struct sockaddr *)&sock->addr.ai_addr, nsd->options->tls_port)) + using_tls_port((struct sockaddr *)&sock->address.inet, nsd->options->tls_port)) { data->tls_accept = 1; if(verbosity >= 2) { char buf[48]; - addrport2str((void*)(struct sockaddr_storage*)&sock->addr.ai_addr, buf, sizeof(buf)); + addrport2str((void*)(struct sockaddr_storage*)&sock->address.inet, buf, sizeof(buf)); VERBOSITY(4, (LOG_NOTICE, "setup TCP for TLS service on interface %s", buf)); } } else { @@ -2939,7 +2931,7 @@ add_tcp_handler( #endif memset(handler, 0, sizeof(*handler)); - event_set(handler, sock->s, EV_PERSIST|EV_READ, handle_tcp_accept, data); + event_set(handler, sock->socket, EV_PERSIST|EV_READ, handle_tcp_accept, data); if(event_base_set(nsd->event_base, handler) != 0) log_msg(LOG_ERR, "nsd tcp: event_base_set failed"); if(event_add(handler, NULL) != 0) @@ -2982,10 +2974,10 @@ server_child(struct nsd *nsd) #endif if (!(nsd->server_kind & NSD_SERVER_TCP)) { - server_close_all_sockets(nsd->tcp, nsd->ifs); + server_close_all_sockets(&nsd->tcp); } if (!(nsd->server_kind & NSD_SERVER_UDP)) { - server_close_all_sockets(nsd->udp, nsd->ifs); + server_close_all_sockets(&nsd->udp); } if (nsd->this_child->parent_fd != -1) { @@ -3008,15 +3000,15 @@ server_child(struct nsd *nsd) } if(nsd->reuseport) { - numifs = nsd->ifs / nsd->reuseport; + numifs = nsd->udp.count / nsd->reuseport; from = numifs * nsd->this_child->child_num; - if(from+numifs > nsd->ifs) { /* should not happen */ + if(from+numifs > nsd->udp.count) { /* should not happen */ from = 0; - numifs = nsd->ifs; + numifs = nsd->udp.count; } } else { from = 0; - numifs = nsd->ifs; + numifs = nsd->udp.count; } if (nsd->server_kind & NSD_SERVER_UDP) { @@ -3035,19 +3027,19 @@ server_child(struct nsd *nsd) msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen; } - for (i = 0; i < nsd->ifs; i++) { + for (i = 0; i < nsd->udp.count; i++) { int listen; struct udp_handler_data *data; - listen = nsd_bitset_isset(nsd->udp[i].servers, child); + listen = nsd_bitset_isset(nsd->udp.sockets[i].servers, child); if(i >= from && i < (from + numifs) && listen) { data = region_alloc_zero( nsd->server_region, sizeof(*data)); - add_udp_handler(nsd, &nsd->udp[i], data); + add_udp_handler(nsd, &nsd->udp.sockets[i], data); } else { /* close sockets intended for other servers */ - server_close_socket(&nsd->udp[i]); + server_close_socket(&nsd->udp.sockets[i]); } } } @@ -3063,27 +3055,19 @@ server_child(struct nsd *nsd) tcp_accept_handlers = region_alloc_array(server_region, numifs, sizeof(*tcp_accept_handlers)); - for (i = 0; i < nsd->ifs; i++) { + for (i = 0; i < nsd->tcp.count; i++) { int listen; struct tcp_accept_handler_data *data; - listen = nsd_bitset_isset(nsd->tcp[i].servers, child); + listen = nsd_bitset_isset(nsd->tcp.sockets[i].servers, child); - if(i >= from && i < (from + numifs) && listen) { - data = &tcp_accept_handlers[i-from]; + if (listen) { + data = &tcp_accept_handlers[i]; memset(data, 0, sizeof(*data)); - add_tcp_handler(nsd, &nsd->tcp[i], data); + add_tcp_handler(nsd, &nsd->tcp.sockets[i], data); } else { - /* close sockets intended for other servers */ - /* - * uncomment this once tcp servers are no - * longer copied in the tcp fd copy line - * in server_init(). - server_close_socket(&nsd->tcp[i]); - */ - /* close sockets not meant for this server*/ - if(!listen) - server_close_socket(&nsd->tcp[i]); + /* close sockets not meant for this server */ + server_close_socket(&nsd->tcp.sockets[i]); } } } else { @@ -3423,9 +3407,9 @@ handle_udp(int fd, short event, void* arg) /* Account... */ #ifdef BIND8_STATS - if (data->socket->addr.ai_family == AF_INET) { + if (data->socket->family == AF_INET) { STATUP(data->nsd, qudp); - } else if (data->socket->addr.ai_family == AF_INET6) { + } else if (data->socket->family == AF_INET6) { STATUP(data->nsd, qudp6); } #endif diff --git a/tpkg/checkconf.tdir/checkconf.check b/tpkg/checkconf.tdir/checkconf.check index eb40201f6..55e6cb75b 100644 --- a/tpkg/checkconf.tdir/checkconf.check +++ b/tpkg/checkconf.tdir/checkconf.check @@ -516,3 +516,78 @@ checkconf.nsd09.conf:9: error: expected yes or no read checkconf.nsd09.conf failed: 1 errors in configuration file checkconf.nsd10.conf:10: error: expected a number read checkconf.nsd10.conf failed: 1 errors in configuration file +# Read file checkconf.nsd11.conf: 0 patterns, 0 fixed-zones, 0 keys, 0 tls-auth. +# Config settings. +server: + debug-mode: no + ip-transparent: no + ip-freebind: no + reuseport: no + do-ip4: yes + do-ip6: yes + send-buffer-size: 0 + receive-buffer-size: 0 + hide-version: no + hide-identity: no + drop-updates: no + tcp-reject-overflow: no + database: "/var/db/nsd/nsd.db" + #identity: + #version: + #nsid: + #logfile: + log-only-syslog: no + server-count: 1 + tcp-count: 100 + tcp-query-count: 0 + tcp-timeout: 120 + tcp-mss: 0 + outgoing-tcp-mss: 0 + xfrd-tcp-max: 128 + xfrd-tcp-pipeline: 128 + ipv4-edns-size: 1232 + ipv6-edns-size: 1232 + pidfile: "/var/run/nsd.pid" + port: "53" + statistics: 0 + #chroot: + username: "nsd" + zonesdir: "/etc/nsd" + xfrdfile: "/var/db/nsd/xfrd.state" + zonelistfile: "/var/db/nsd/zone.list" + xfrdir: "/tmp" + xfrd-reload-timeout: 1 + log-time-ascii: yes + round-robin: no + minimal-responses: no + confine-to-zone: no + refuse-any: no + verbosity: 0 + ip-address: 127.0.0.1@53053 + ip-address: 127.0.0.1@53153 udp + ip-address: 127.0.0.1@53253 tcp + ip-address: 127.0.0.1@53453 udp tcp + ip-address: 127.0.0.1@53553 udp tcp + zonefiles-check: yes + zonefiles-write: 0 + #tls-service-key: + #tls-service-pem: + #tls-service-ocsp: + tls-port: "853" + #tls-cert-bundle: + answer-cookie: no + cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + +remote-control: + control-enable: no + control-port: 8952 + server-key-file: "/etc/nsd/nsd_server.key" + server-cert-file: "/etc/nsd/nsd_server.pem" + control-key-file: "/etc/nsd/nsd_control.key" + control-cert-file: "/etc/nsd/nsd_control.pem" +checkconf.nsd11.invalid.conf:4: at 'foo': error: syntax error +read checkconf.nsd11.invalid.conf failed: 1 errors in configuration file +checkconf.nsd11.tcp2x.conf:4: at 'tcp': error: syntax error +read checkconf.nsd11.tcp2x.conf failed: 1 errors in configuration file +checkconf.nsd11.udp2x.conf:4: at 'udp': error: syntax error +read checkconf.nsd11.udp2x.conf failed: 1 errors in configuration file diff --git a/tpkg/checkconf.tdir/checkconf.nsd11.conf b/tpkg/checkconf.tdir/checkconf.nsd11.conf new file mode 100644 index 000000000..8ec5a6e10 --- /dev/null +++ b/tpkg/checkconf.tdir/checkconf.nsd11.conf @@ -0,0 +1,9 @@ +# Configuration file to ensure proper handling of socket type per ip-address. +# Accepted values are upd, tcp, and a combination of the two. The socket type +# may also be omitted, in which case both udp and tcp sockets are opened. +server: + ip-address: 127.0.0.1@53053 + ip-address: 127.0.0.1@53153 udp + ip-address: 127.0.0.1@53253 tcp + ip-address: 127.0.0.1@53453 udp tcp + ip-address: 127.0.0.1@53553 tcp udp diff --git a/tpkg/checkconf.tdir/checkconf.nsd11.invalid.conf b/tpkg/checkconf.tdir/checkconf.nsd11.invalid.conf new file mode 100644 index 000000000..450709d99 --- /dev/null +++ b/tpkg/checkconf.tdir/checkconf.nsd11.invalid.conf @@ -0,0 +1,4 @@ +# Configuration file to ensure proper handling of socket type per ip-address. +# Specifying the same socket type twice is prohibited. +server: + ip-address: 127.0.0.1@53053 foo bar diff --git a/tpkg/checkconf.tdir/checkconf.nsd11.tcp2x.conf b/tpkg/checkconf.tdir/checkconf.nsd11.tcp2x.conf new file mode 100644 index 000000000..783a8fd02 --- /dev/null +++ b/tpkg/checkconf.tdir/checkconf.nsd11.tcp2x.conf @@ -0,0 +1,4 @@ +# Configuration file to ensure proper handling of socket type per ip-address. +# Specifying the same socket type twice is prohibited. +server: + ip-address: 127.0.0.1@53053 tcp tcp diff --git a/tpkg/checkconf.tdir/checkconf.nsd11.udp2x.conf b/tpkg/checkconf.tdir/checkconf.nsd11.udp2x.conf new file mode 100644 index 000000000..1acb11207 --- /dev/null +++ b/tpkg/checkconf.tdir/checkconf.nsd11.udp2x.conf @@ -0,0 +1,4 @@ +# Configuration file to ensure proper handling of socket type per ip-address. +# Specifying the same socket type twice is prohibited. +server: + ip-address: 127.0.0.1@53053 udp udp diff --git a/tpkg/socket_types.tdir/socket_types.conf b/tpkg/socket_types.tdir/socket_types.conf new file mode 100644 index 000000000..26aa21599 --- /dev/null +++ b/tpkg/socket_types.tdir/socket_types.conf @@ -0,0 +1,19 @@ +server: + ip-address: 127.0.0.1@NSD_PORT + ip-address: 127.0.0.1@NSD_TCP_PORT tcp + ip-address: 127.0.0.1@NSD_UDP_PORT udp + ip-address: 127.0.0.1@NSD_TCP_UDP_PORT tcp udp + logfile: "nsd.log" + difffile: ixfr.db + xfrdfile: xfrd.state + zonesdir: "" + username: "" + chroot: "" + pidfile: NSD_PID + database: "" + verbosity: 5 + zonelistfile: "zone.list" + +zone: + name: example.com. + zonefile: socket_types.zone diff --git a/tpkg/socket_types.tdir/socket_types.dsc b/tpkg/socket_types.tdir/socket_types.dsc new file mode 100644 index 000000000..24dcd30d6 --- /dev/null +++ b/tpkg/socket_types.tdir/socket_types.dsc @@ -0,0 +1,16 @@ +BaseName: socket_types +Version: 1.0 +Description: Test server only opens specific socket types if specified +CreationDate: Thu Mar 24 12:43:57 PM CET 2022 +Maintainer: Jeroen Koekkoek +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: socket_types.pre +Post: socket_types.post +Test: socket_types.test +AuxFiles: socket_types.conf +Passed: +Failure: diff --git a/tpkg/socket_types.tdir/socket_types.post b/tpkg/socket_types.tdir/socket_types.post new file mode 100644 index 000000000..bb94033f0 --- /dev/null +++ b/tpkg/socket_types.tdir/socket_types.post @@ -0,0 +1,14 @@ +# #-- socket_types.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +# do your teardown here +if ! test -e $NSD_PID ; then + exit 0 +fi + +# kill NSD +kill_pid `cat $NSD_PID` diff --git a/tpkg/socket_types.tdir/socket_types.pre b/tpkg/socket_types.tdir/socket_types.pre new file mode 100644 index 000000000..225d224aa --- /dev/null +++ b/tpkg/socket_types.tdir/socket_types.pre @@ -0,0 +1,32 @@ +# #-- socket_types.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +get_random_port 4 +NSD_PORT=$RND_PORT +NSD_TCP_PORT=$(($RND_PORT + 1)) +NSD_UDP_PORT=$(($RND_PORT + 2)) +NSD_TCP_UDP_PORT=$(($RND_PORT + 3)) +NSD_PID="nsd.pid" + +echo port: $NSD_PORT +echo tcp port: $NSD_TCP_PORT +echo udp port: $NSD_UDP_PORT +echo tcp+udp port: $NSD_TCP_UDP_PORT + +# share the vars +echo "export NSD_PORT=$NSD_PORT" >> .tpkg.var.test +echo "export NSD_TCP_PORT=$NSD_TCP_PORT" >> .tpkg.var.test +echo "export NSD_UDP_PORT=$NSD_UDP_PORT" >> .tpkg.var.test +echo "export NSD_TCP_UDP_PORT=$NSD_TCP_UDP_PORT" >> .tpkg.var.test +echo "export NSD_PID=$NSD_PID" >> .tpkg.var.test + +sed -e "s/NSD_PORT/$NSD_PORT/" \ + -e "s/NSD_TCP_PORT/$NSD_TCP_PORT/" \ + -e "s/NSD_UDP_PORT/$NSD_UDP_PORT/" \ + -e "s/NSD_TCP_UDP_PORT/$NSD_TCP_UDP_PORT/" \ + -e "s/NSD_PID/$NSD_PID/" \ + < socket_types.conf > nsd.conf diff --git a/tpkg/socket_types.tdir/socket_types.test b/tpkg/socket_types.tdir/socket_types.test new file mode 100644 index 000000000..65fdeb965 --- /dev/null +++ b/tpkg/socket_types.tdir/socket_types.test @@ -0,0 +1,50 @@ +# #-- socket_types.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +# start NSD +PRE="../.." +TPKG_NSD="$PRE/nsd" + +$TPKG_NSD -c nsd.conf -p $NSD_PORT -V 5 +wait_nsd_up nsd.log +wait_logfile nsd.log "nsd started" + +cat nsd.log + +# ensure server is listening on tcp sockets +for PORT in $NSD_PORT $NSD_TCP_PORT $NSD_TCP_UDP_PORT; do + if dig @127.0.0.1 -p $PORT +tcp +timeout=1 ns1.example.com; then + echo "server is correctly accepting TCP queries on 127.0.0.1@$PORT" + else + echo "server is incorrectly not accepting TCP queries on 127.0.0.1@$PORT" + exit 1 + fi +done + +# ensure server is listening on udp sockets +for PORT in $NSD_PORT $NSD_UDP_PORT $NSD_TCP_UDP_PORT; do + if dig @127.0.0.1 -p $PORT +notcp +timeout=1 ns1.example.com; then + echo "server is correctly accepting UDP queries on 127.0.0.1@$PORT" + else + echo "server is incorrectly not accepting UDP queries on 127.0.0.1@$PORT" + exit 1 + fi +done + +if dig @127.0.0.1 -p $NSD_UDP_PORT +tcp +timeout=1 ns1.example.com; then + echo "server is incorrectly accepting TCP queries on 127.0.0.1@$NSD_UDP_PORT" + exit 1 +else + echo "server is correctly not accepting TCP queries on 127.0.0.1@$NSD_UDP_PORT" +fi + +if dig @127.0.0.1 -p $NSD_TCP_PORT +notcp +timeout=1 ns1.example.com; then + echo "server is incorrectly accepting UDP queries on 127.0.0.1@$NSD_TCP_PORT" + exit 1 +else + echo "server is correctly not accepting UDP queries on 127.0.0.1@$NSD_TCP_PORT" +fi diff --git a/tpkg/socket_types.tdir/socket_types.zone b/tpkg/socket_types.tdir/socket_types.zone new file mode 100644 index 000000000..b563a2a52 --- /dev/null +++ b/tpkg/socket_types.tdir/socket_types.zone @@ -0,0 +1,7 @@ +$ORIGIN example.com. +$TTL 86400 +@ IN SOA ns1.example.com. hostmaster.example.com. (2022032401 21600 3600 604800 86400) + NS ns1.example.com. + NS ns2.example.com. +ns1 A 127.0.0.1 +ns2 A 127.0.0.2