--- embedaddon/dnsmasq/src/network.c 2016/11/02 09:57:01 1.1.1.3 +++ embedaddon/dnsmasq/src/network.c 2021/03/17 00:56:46 1.1.1.4 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ int indextoname(int fd, int index, char *name) if (ioctl(fd, SIOCGIFNAME, &ifr) == -1) return 0; - strncpy(name, ifr.ifr_name, IF_NAMESIZE); + safe_strncpy(name, ifr.ifr_name, IF_NAMESIZE); return 1; } @@ -82,12 +82,12 @@ int indextoname(int fd, int index, char *name) for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++) { struct lifreq lifr; - strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE); + safe_strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE); if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0) return 0; if (lifr.lifr_index == index) { - strncpy(name, lifr.lifr_name, IF_NAMESIZE); + safe_strncpy(name, lifr.lifr_name, IF_NAMESIZE); return 1; } } @@ -109,7 +109,7 @@ int indextoname(int fd, int index, char *name) #endif -int iface_check(int family, struct all_addr *addr, char *name, int *auth) +int iface_check(int family, union all_addr *addr, char *name, int *auth) { struct iname *tmp; int ret = 1, match_addr = 0; @@ -135,14 +135,12 @@ int iface_check(int family, struct all_addr *addr, cha if (tmp->addr.sa.sa_family == family) { if (family == AF_INET && - tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr) + tmp->addr.in.sin_addr.s_addr == addr->addr4.s_addr) ret = match_addr = tmp->used = 1; -#ifdef HAVE_IPV6 else if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, - &addr->addr.addr6)) + &addr->addr6)) ret = match_addr = tmp->used = 1; -#endif } } @@ -160,13 +158,11 @@ int iface_check(int family, struct all_addr *addr, cha break; } else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET && - tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr) + tmp->addr.in.sin_addr.s_addr == addr->addr4.s_addr) break; -#ifdef HAVE_IPV6 else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 && - IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr.addr6)) + IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6)) break; -#endif if (tmp && auth) { @@ -178,17 +174,17 @@ int iface_check(int family, struct all_addr *addr, cha } -/* Fix for problem that the kernel sometimes reports the loopback inerface as the +/* Fix for problem that the kernel sometimes reports the loopback interface as the arrival interface when a packet originates locally, even when sent to address of an interface other than the loopback. Accept packet if it arrived via a loopback interface, even when we're not accepting packets that way, as long as the destination address is one we're believing. Interface list must be up-to-date before calling. */ -int loopback_exception(int fd, int family, struct all_addr *addr, char *name) +int loopback_exception(int fd, int family, union all_addr *addr, char *name) { struct ifreq ifr; struct irec *iface; - strncpy(ifr.ifr_name, name, IF_NAMESIZE); + safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE); if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 && ifr.ifr_flags & IFF_LOOPBACK) { @@ -197,14 +193,11 @@ int loopback_exception(int fd, int family, struct all_ { if (family == AF_INET) { - if (iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr) + if (iface->addr.in.sin_addr.s_addr == addr->addr4.s_addr) return 1; } -#ifdef HAVE_IPV6 - else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr.addr6)) + else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr6)) return 1; -#endif - } } return 0; @@ -214,7 +207,7 @@ int loopback_exception(int fd, int family, struct all_ on the relevant address, but the name of the arrival interface, derived from the index won't match the config. Check that we found an interface address for the arrival interface: daemon->interfaces must be up-to-date. */ -int label_exception(int index, int family, struct all_addr *addr) +int label_exception(int index, int family, union all_addr *addr) { struct irec *iface; @@ -224,7 +217,7 @@ int label_exception(int index, int family, struct all_ for (iface = daemon->interfaces; iface; iface = iface->next) if (iface->index == index && iface->addr.sa.sa_family == AF_INET && - iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr) + iface->addr.in.sin_addr.s_addr == addr->addr4.s_addr) return 1; return 0; @@ -244,6 +237,7 @@ static int iface_allowed(struct iface_param *param, in int tftp_ok = !!option_bool(OPT_TFTP); int dhcp_ok = 1; int auth_dns = 0; + int is_label = 0; #if defined(HAVE_DHCP) || defined(HAVE_TFTP) struct iname *tmp; #endif @@ -264,6 +258,8 @@ static int iface_allowed(struct iface_param *param, in if (!label) label = ifr.ifr_name; + else + is_label = strcmp(label, ifr.ifr_name); /* maintain a list of all addresses on all interfaces for --local-service option */ if (option_bool(OPT_LOCAL_SERVICE)) @@ -286,22 +282,18 @@ static int iface_allowed(struct iface_param *param, in if (addr->sa.sa_family == AF_INET) { - al->addr.addr.addr4 = addr->in.sin_addr; + al->addr.addr4 = addr->in.sin_addr; al->flags = 0; } -#ifdef HAVE_IPV6 else { - al->addr.addr.addr6 = addr->in6.sin6_addr; + al->addr.addr6 = addr->in6.sin6_addr; al->flags = ADDRLIST_IPV6; } -#endif } } -#ifdef HAVE_IPV6 if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr)) -#endif { struct interface_name *int_name; struct addrlist *al; @@ -329,12 +321,11 @@ static int iface_allowed(struct iface_param *param, in al->next = zone->subnet; zone->subnet = al; al->prefixlen = prefixlen; - al->addr.addr.addr4 = addr->in.sin_addr; + al->addr.addr4 = addr->in.sin_addr; al->flags = 0; } } -#ifdef HAVE_IPV6 if (addr->sa.sa_family == AF_INET6 && (name->flags & AUTH6)) { if (param->spare) @@ -350,12 +341,10 @@ static int iface_allowed(struct iface_param *param, in al->next = zone->subnet; zone->subnet = al; al->prefixlen = prefixlen; - al->addr.addr.addr6 = addr->in6.sin6_addr; + al->addr.addr6 = addr->in6.sin6_addr; al->flags = ADDRLIST_IPV6; } } -#endif - } #endif @@ -380,20 +369,18 @@ static int iface_allowed(struct iface_param *param, in if (addr->sa.sa_family == AF_INET) { - al->addr.addr.addr4 = addr->in.sin_addr; + al->addr.addr4 = addr->in.sin_addr; al->flags = 0; } -#ifdef HAVE_IPV6 else { - al->addr.addr.addr6 = addr->in6.sin6_addr; + al->addr.addr6 = addr->in6.sin6_addr; al->flags = ADDRLIST_IPV6; /* Privacy addresses and addresses still undergoing DAD and deprecated addresses don't appear in forward queries, but will in reverse ones. */ if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE))) al->flags |= ADDRLIST_REVONLY; } -#endif } } } @@ -401,10 +388,11 @@ static int iface_allowed(struct iface_param *param, in /* check whether the interface IP has been added already we call this routine multiple times. */ for (iface = daemon->interfaces; iface; iface = iface->next) - if (sockaddr_isequal(&iface->addr, addr)) + if (sockaddr_isequal(&iface->addr, addr) && iface->index == if_index) { iface->dad = !!(iface_flags & IFACE_TENTATIVE); iface->found = 1; /* for garbage collection */ + iface->netmask = netmask; return 1; } @@ -432,14 +420,12 @@ static int iface_allowed(struct iface_param *param, in } if (addr->sa.sa_family == AF_INET && - !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, label, &auth_dns)) + !iface_check(AF_INET, (union all_addr *)&addr->in.sin_addr, label, &auth_dns)) return 1; -#ifdef HAVE_IPV6 if (addr->sa.sa_family == AF_INET6 && - !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, label, &auth_dns)) + !iface_check(AF_INET6, (union all_addr *)&addr->in6.sin6_addr, label, &auth_dns)) return 1; -#endif #ifdef HAVE_DHCP /* No DHCP where we're doing auth DNS. */ @@ -482,6 +468,7 @@ static int iface_allowed(struct iface_param *param, in iface->found = 1; iface->done = iface->multicast_done = iface->warned = 0; iface->index = if_index; + iface->label = is_label; if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1))) { strcpy(iface->name, ifr.ifr_name); @@ -497,7 +484,6 @@ static int iface_allowed(struct iface_param *param, in return 0; } -#ifdef HAVE_IPV6 static int iface_allowed_v6(struct in6_addr *local, int prefix, int scope, int if_index, int flags, int preferred, int valid, void *vparam) @@ -525,7 +511,6 @@ static int iface_allowed_v6(struct in6_addr *local, in return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, flags); } -#endif static int iface_allowed_v4(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam) @@ -548,7 +533,82 @@ static int iface_allowed_v4(struct in_addr local, int return iface_allowed((struct iface_param *)vparam, if_index, label, &addr, netmask, prefix, 0); } - + +/* + * Clean old interfaces no longer found. + */ +static void clean_interfaces() +{ + struct irec *iface; + struct irec **up = &daemon->interfaces; + + for (iface = *up; iface; iface = *up) + { + if (!iface->found && !iface->done) + { + *up = iface->next; + free(iface->name); + free(iface); + } + else + { + up = &iface->next; + } + } +} + +/** Release listener if no other interface needs it. + * + * @return 1 if released, 0 if still required + */ +static int release_listener(struct listener *l) +{ + if (l->used > 1) + { + struct irec *iface; + for (iface = daemon->interfaces; iface; iface = iface->next) + if (iface->done && sockaddr_isequal(&l->addr, &iface->addr)) + { + if (iface->found) + { + /* update listener to point to active interface instead */ + if (!l->iface->found) + l->iface = iface; + } + else + { + l->used--; + iface->done = 0; + } + } + + /* Someone is still using this listener, skip its deletion */ + if (l->used > 0) + return 0; + } + + if (l->iface->done) + { + int port; + + port = prettyprint_addr(&l->iface->addr, daemon->addrbuff); + my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s port %d"), + l->iface->name, l->iface->index, daemon->addrbuff, port); + /* In case it ever returns */ + l->iface->done = 0; + } + + if (l->fd != -1) + close(l->fd); + if (l->tcpfd != -1) + close(l->tcpfd); + if (l->tftpfd != -1) + close(l->tftpfd); + + free(l); + return 1; +} + int enumerate_interfaces(int reset) { static struct addrlist *spare = NULL; @@ -629,9 +689,7 @@ int enumerate_interfaces(int reset) param.spare = spare; -#ifdef HAVE_IPV6 ret = iface_enumerate(AF_INET6, ¶m, iface_allowed_v6); -#endif if (ret) ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4); @@ -644,10 +702,11 @@ int enumerate_interfaces(int reset) /* Garbage-collect listeners listening on addresses that no longer exist. Does nothing when not binding interfaces or for listeners on localhost, since the ->iface field is NULL. Note that this needs the protections - against re-entrancy, hence it's here. It also means there's a possibility, + against reentrancy, hence it's here. It also means there's a possibility, in OPT_CLEVERBIND mode, that at listener will just disappear after a call to enumerate_interfaces, this is checked OK on all calls. */ struct listener *l, *tmp, **up; + int freed = 0; for (up = &daemon->listeners, l = daemon->listeners; l; l = tmp) { @@ -655,25 +714,17 @@ int enumerate_interfaces(int reset) if (!l->iface || l->iface->found) up = &l->next; - else + else if (release_listener(l)) { - *up = l->next; - - /* In case it ever returns */ - l->iface->done = 0; - - if (l->fd != -1) - close(l->fd); - if (l->tcpfd != -1) - close(l->tcpfd); - if (l->tftpfd != -1) - close(l->tftpfd); - - free(l); + *up = tmp; + freed = 1; } } + + if (freed) + clean_interfaces(); } - + errno = errsave; spare = param.spare; @@ -699,7 +750,7 @@ static int make_sock(union mysockaddr *addr, int type, if ((fd = socket(family, type, 0)) == -1) { - int port, errsav; + int port, errsave; char *s; /* No error if the kernel just doesn't support this IP flavour */ @@ -709,7 +760,7 @@ static int make_sock(union mysockaddr *addr, int type, return -1; err: - errsav = errno; + errsave = errno; port = prettyprint_addr(addr, daemon->addrbuff); if (!option_bool(OPT_NOWILD) && !option_bool(OPT_CLEVERBIND)) sprintf(daemon->addrbuff, "port %d", port); @@ -718,7 +769,7 @@ static int make_sock(union mysockaddr *addr, int type, if (fd != -1) close (fd); - errno = errsav; + errno = errsave; if (dienow) { @@ -736,17 +787,20 @@ static int make_sock(union mysockaddr *addr, int type, if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd)) goto err; -#ifdef HAVE_IPV6 if (family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1) goto err; -#endif if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) == -1) goto err; if (type == SOCK_STREAM) { - if (listen(fd, 5) == -1) +#ifdef TCP_FASTOPEN + int qlen = 5; + setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)); +#endif + + if (listen(fd, TCP_BACKLOG) == -1) goto err; } else if (family == AF_INET) @@ -763,15 +817,12 @@ static int make_sock(union mysockaddr *addr, int type, #endif } } -#ifdef HAVE_IPV6 else if (!set_ipv6pktinfo(fd)) goto err; -#endif return fd; } -#ifdef HAVE_IPV6 int set_ipv6pktinfo(int fd) { int opt = 1; @@ -798,12 +849,13 @@ int set_ipv6pktinfo(int fd) return 0; } -#endif /* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */ int tcp_interface(int fd, int af) { + (void)fd; /* suppress potential unused warning */ + (void)af; /* suppress potential unused warning */ int if_index = 0; #ifdef HAVE_LINUX_NETWORK @@ -838,7 +890,6 @@ int tcp_interface(int fd, int af) } } } -#ifdef HAVE_IPV6 else { /* Only the RFC-2292 API has the ability to find the interface for TCP connections, @@ -870,7 +921,6 @@ int tcp_interface(int fd, int af) } } } -#endif /* IPV6 */ #endif /* Linux */ return if_index; @@ -900,7 +950,6 @@ static struct listener *create_listeners(union mysocka tftpfd = make_sock(addr, SOCK_DGRAM, dienow); addr->in.sin_port = save; } -# ifdef HAVE_IPV6 else { short save = addr->in6.sin6_port; @@ -908,7 +957,6 @@ static struct listener *create_listeners(union mysocka tftpfd = make_sock(addr, SOCK_DGRAM, dienow); addr->in6.sin6_port = save; } -# endif } #endif @@ -916,10 +964,11 @@ static struct listener *create_listeners(union mysocka { l = safe_malloc(sizeof(struct listener)); l->next = NULL; - l->family = addr->sa.sa_family; l->fd = fd; l->tcpfd = tcpfd; - l->tftpfd = tftpfd; + l->tftpfd = tftpfd; + l->addr = *addr; + l->used = 1; l->iface = NULL; } @@ -941,11 +990,10 @@ void create_wildcard_listeners(void) l = create_listeners(&addr, !!option_bool(OPT_TFTP), 1); -#ifdef HAVE_IPV6 memset(&addr, 0, sizeof(addr)); -# ifdef HAVE_SOCKADDR_SA_LEN +#ifdef HAVE_SOCKADDR_SA_LEN addr.in6.sin6_len = sizeof(addr.in6); -# endif +#endif addr.in6.sin6_family = AF_INET6; addr.in6.sin6_addr = in6addr_any; addr.in6.sin6_port = htons(daemon->port); @@ -955,25 +1003,52 @@ void create_wildcard_listeners(void) l->next = l6; else l = l6; -#endif daemon->listeners = l; } +static struct listener *find_listener(union mysockaddr *addr) +{ + struct listener *l; + for (l = daemon->listeners; l; l = l->next) + if (sockaddr_isequal(&l->addr, addr)) + return l; + return NULL; +} + void create_bound_listeners(int dienow) { struct listener *new; struct irec *iface; struct iname *if_tmp; + struct listener *existing; for (iface = daemon->interfaces; iface; iface = iface->next) - if (!iface->done && !iface->dad && iface->found && - (new = create_listeners(&iface->addr, iface->tftp_ok, dienow))) + if (!iface->done && !iface->dad && iface->found) { - new->iface = iface; - new->next = daemon->listeners; - daemon->listeners = new; - iface->done = 1; + existing = find_listener(&iface->addr); + if (existing) + { + iface->done = 1; + existing->used++; /* increase usage counter */ + } + else if ((new = create_listeners(&iface->addr, iface->tftp_ok, dienow))) + { + new->iface = iface; + new->next = daemon->listeners; + daemon->listeners = new; + iface->done = 1; + + /* Don't log the initial set of listen addresses created + at startup, since this is happening before the logging + system is initialised and the sign-on printed. */ + if (!dienow) + { + int port = prettyprint_addr(&iface->addr, daemon->addrbuff); + my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s port %d"), + iface->name, iface->index, daemon->addrbuff, port); + } + } } /* Check for --listen-address options that haven't been used because there's @@ -993,6 +1068,12 @@ void create_bound_listeners(int dienow) { new->next = daemon->listeners; daemon->listeners = new; + + if (!dienow) + { + int port = prettyprint_addr(&if_tmp->addr, daemon->addrbuff); + my_syslog(LOG_DEBUG, _("listening on %s port %d"), daemon->addrbuff, port); + } } } @@ -1034,6 +1115,15 @@ void warn_bound_listeners(void) my_syslog(LOG_WARNING, _("LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)")); } +void warn_wild_labels(void) +{ + struct irec *iface; + + for (iface = daemon->interfaces; iface; iface = iface->next) + if (iface->found && iface->name && iface->label) + my_syslog(LOG_WARNING, _("warning: using interface %s instead"), iface->name); +} + void warn_int_names(void) { struct interface_name *intname; @@ -1136,11 +1226,8 @@ int random_sock(int family) if (fix_fd(fd)) while(tries--) { - unsigned short port = rand16(); + unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); - if (daemon->min_port != 0 || daemon->max_port != MAX_PORT) - port = htons(daemon->min_port + (port % ((unsigned short)ports_avail))); - if (family == AF_INET) { addr.in.sin_addr.s_addr = INADDR_ANY; @@ -1149,7 +1236,6 @@ int random_sock(int family) addr.in.sin_len = sizeof(struct sockaddr_in); #endif } -#ifdef HAVE_IPV6 else { addr.in6.sin6_addr = in6addr_any; @@ -1158,7 +1244,6 @@ int random_sock(int family) addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif } -#endif if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0) return fd; @@ -1174,24 +1259,70 @@ int random_sock(int family) } -int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp) +int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp) { union mysockaddr addr_copy = *addr; + unsigned short port; + int tries = 1, done = 0; + unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1; + + if (addr_copy.sa.sa_family == AF_INET) + port = addr_copy.in.sin_port; + else + port = addr_copy.in6.sin6_port; /* cannot set source _port_ for TCP connections. */ if (is_tcp) + port = 0; + + /* Bind a random port within the range given by min-port and max-port */ + if (port == 0) { + tries = ports_avail < 30 ? 3 * ports_avail : 100; + port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); + } + + while (tries--) + { if (addr_copy.sa.sa_family == AF_INET) - addr_copy.in.sin_port = 0; -#ifdef HAVE_IPV6 + addr_copy.in.sin_port = port; else - addr_copy.in6.sin6_port = 0; -#endif + addr_copy.in6.sin6_port = port; + + if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1) + { + done = 1; + break; + } + + if (errno != EADDRINUSE && errno != EACCES) + return 0; + + port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); } - - if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) == -1) + + if (!done) return 0; - + + if (!is_tcp && ifindex > 0) + { +#if defined(IP_UNICAST_IF) + if (addr_copy.sa.sa_family == AF_INET) + { + uint32_t ifindex_opt = htonl(ifindex); + return setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0; + } +#endif +#if defined (IPV6_UNICAST_IF) + if (addr_copy.sa.sa_family == AF_INET6) + { + uint32_t ifindex_opt = htonl(ifindex); + return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0; + } +#endif + } + + (void)intname; /* suppress potential unused warning */ #if defined(SO_BINDTODEVICE) if (intname[0] != 0 && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE) == -1) @@ -1204,8 +1335,10 @@ int local_bind(int fd, union mysockaddr *addr, char *i static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname) { struct serverfd *sfd; + unsigned int ifindex = 0; int errsave; - + int opt = 1; + /* when using random ports, servers which would otherwise use the INADDR_ANY/port0 socket have sfd set to NULL */ if (!daemon->osport && intname[0] == 0) @@ -1217,18 +1350,20 @@ static struct serverfd *allocate_sfd(union mysockaddr addr->in.sin_port == htons(0)) return NULL; -#ifdef HAVE_IPV6 if (addr->sa.sa_family == AF_INET6 && memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 && addr->in6.sin6_port == htons(0)) return NULL; -#endif } + + if (intname && strlen(intname) != 0) + ifindex = if_nametoindex(intname); /* index == 0 when not binding to an interface */ /* may have a suitable one already */ for (sfd = daemon->sfds; sfd; sfd = sfd->next ) if (sockaddr_isequal(&sfd->source_addr, addr) && - strcmp(intname, sfd->interface) == 0) + strcmp(intname, sfd->interface) == 0 && + ifindex == sfd->ifindex) return sfd; /* need to make a new one. */ @@ -1241,20 +1376,24 @@ static struct serverfd *allocate_sfd(union mysockaddr free(sfd); return NULL; } - - if (!local_bind(sfd->fd, addr, intname, 0) || !fix_fd(sfd->fd)) + + if ((addr->sa.sa_family == AF_INET6 && setsockopt(sfd->fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1) || + !local_bind(sfd->fd, addr, intname, ifindex, 0) || !fix_fd(sfd->fd)) { - errsave = errno; /* save error from bind. */ + errsave = errno; /* save error from bind/setsockopt. */ close(sfd->fd); free(sfd); errno = errsave; return NULL; } - - strcpy(sfd->interface, intname); + + safe_strncpy(sfd->interface, intname, sizeof(sfd->interface)); sfd->source_addr = *addr; sfd->next = daemon->sfds; + sfd->ifindex = ifindex; + sfd->preallocated = 0; daemon->sfds = sfd; + return sfd; } @@ -1263,6 +1402,7 @@ static struct serverfd *allocate_sfd(union mysockaddr void pre_allocate_sfds(void) { struct server *srv; + struct serverfd *sfd; if (daemon->query_port != 0) { @@ -1274,8 +1414,9 @@ void pre_allocate_sfds(void) #ifdef HAVE_SOCKADDR_SA_LEN addr.in.sin_len = sizeof(struct sockaddr_in); #endif - allocate_sfd(&addr, ""); -#ifdef HAVE_IPV6 + if ((sfd = allocate_sfd(&addr, ""))) + sfd->preallocated = 1; + memset(&addr, 0, sizeof(addr)); addr.in6.sin6_family = AF_INET6; addr.in6.sin6_addr = in6addr_any; @@ -1283,8 +1424,8 @@ void pre_allocate_sfds(void) #ifdef HAVE_SOCKADDR_SA_LEN addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif - allocate_sfd(&addr, ""); -#endif + if ((sfd = allocate_sfd(&addr, ""))) + sfd->preallocated = 1; } for (srv = daemon->servers; srv; srv = srv->next) @@ -1293,7 +1434,7 @@ void pre_allocate_sfds(void) errno != 0 && option_bool(OPT_NOWILD)) { - prettyprint_addr(&srv->source_addr, daemon->namebuff); + (void)prettyprint_addr(&srv->source_addr, daemon->namebuff); if (srv->interface[0] != 0) { strcat(daemon->namebuff, " "); @@ -1417,7 +1558,7 @@ void add_update_server(int flags, serv->flags |= SERV_HAS_DOMAIN; if (interface) - strcpy(serv->interface, interface); + safe_strncpy(serv->interface, interface, sizeof(serv->interface)); if (addr) serv->addr = *addr; if (source_addr) @@ -1429,19 +1570,18 @@ void check_servers(void) { struct irec *iface; struct server *serv; + struct serverfd *sfd, *tmp, **up; int port = 0, count; + int locals = 0; /* interface may be new since startup */ if (!option_bool(OPT_NOWILD)) enumerate_interfaces(0); - -#ifdef HAVE_DNSSEC - /* Disable DNSSEC validation when using server=/domain/.... servers - unless there's a configured trust anchor. */ - for (serv = daemon->servers; serv; serv = serv->next) - serv->flags |= SERV_DO_DNSSEC; -#endif + /* don't garbage collect pre-allocated sfds. */ + for (sfd = daemon->sfds; sfd; sfd = sfd->next) + sfd->used = sfd->preallocated; + for (count = 0, serv = daemon->servers; serv; serv = serv->next) { if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND))) @@ -1453,6 +1593,11 @@ void check_servers(void) #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { + if (!(serv->flags & SERV_FOR_NODOTS)) + serv->flags |= SERV_DO_DNSSEC; + + /* Disable DNSSEC validation when using server=/domain/.... servers + unless there's a configured trust anchor. */ if (serv->flags & SERV_HAS_DOMAIN) { struct ds_config *ds; @@ -1469,8 +1614,6 @@ void check_servers(void) if (!ds) serv->flags &= ~SERV_DO_DNSSEC; } - else if (serv->flags & SERV_FOR_NODOTS) - serv->flags &= ~SERV_DO_DNSSEC; } #endif @@ -1505,6 +1648,9 @@ void check_servers(void) serv->flags |= SERV_MARK; continue; } + + if (serv->sfd) + serv->sfd->used = 1; } if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS)) @@ -1527,7 +1673,11 @@ void check_servers(void) s1 = _("domain"), s2 = serv->domain; if (serv->flags & SERV_NO_ADDR) - my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2); + { + count--; + if (++locals <= LOCALS_LOGGED) + my_syslog(LOG_INFO, _("using only locally-known addresses for %s %s"), s1, s2); + } else if (serv->flags & SERV_USE_RESOLV) my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2); else @@ -1544,9 +1694,25 @@ void check_servers(void) } } + if (locals > LOCALS_LOGGED) + my_syslog(LOG_INFO, _("using %d more local addresses"), locals - LOCALS_LOGGED); if (count - 1 > SERVERS_LOGGED) my_syslog(LOG_INFO, _("using %d more nameservers"), count - SERVERS_LOGGED - 1); + /* Remove unused sfds */ + for (sfd = daemon->sfds, up = &daemon->sfds; sfd; sfd = tmp) + { + tmp = sfd->next; + if (!sfd->used) + { + *up = sfd->next; + close(sfd->fd); + free(sfd); + } + else + up = &sfd->next; + } + cleanup_servers(); } @@ -1592,7 +1758,6 @@ int reload_servers(char *fname) source_addr.in.sin_addr.s_addr = INADDR_ANY; source_addr.in.sin_port = htons(daemon->query_port); } -#ifdef HAVE_IPV6 else { int scope_index = 0; @@ -1620,10 +1785,6 @@ int reload_servers(char *fname) else continue; } -#else /* IPV6 */ - else - continue; -#endif add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL); gotone = 1;