--- embedaddon/dnsmasq/src/network.c 2014/06/15 16:31:38 1.1.1.2 +++ embedaddon/dnsmasq/src/network.c 2023/09/27 11:02:07 1.1.1.5 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2022 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 @@ -16,10 +16,6 @@ #include "dnsmasq.h" -#ifndef IN6_IS_ADDR_ULA -#define IN6_IS_ADDR_ULA(a) ((((__const uint32_t *) (a))[0] & htonl (0xfe00000)) == htonl (0xfc000000)) -#endif - #ifdef HAVE_LINUX_NETWORK int indextoname(int fd, int index, char *name) @@ -33,9 +29,9 @@ 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; + return 1; } @@ -86,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; } } @@ -113,19 +109,14 @@ 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; - /* Note: have to check all and not bail out early, so that we set the - "used" flags. - - May be called with family == AF_LOCALto check interface by name only. */ + /* Note: have to check all and not bail out early, so that we set the "used" flags. + May be called with family == AF_LOCAL to check interface by name only. */ - if (auth) - *auth = 0; - if (daemon->if_names || daemon->if_addrs) { ret = 0; @@ -139,14 +130,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 } } @@ -155,44 +144,46 @@ int iface_check(int family, struct all_addr *addr, cha if (tmp->name && wildcard_match(tmp->name, name)) ret = 0; + if (auth) + { + *auth = 0; - for (tmp = daemon->authinterface; tmp; tmp = tmp->next) - if (tmp->name) - { - if (strcmp(tmp->name, name) == 0 && - (tmp->addr.sa.sa_family == 0 || tmp->addr.sa.sa_family == family)) + for (tmp = daemon->authinterface; tmp; tmp = tmp->next) + if (tmp->name) + { + if (strcmp(tmp->name, name) == 0 && + (tmp->addr.sa.sa_family == 0 || tmp->addr.sa.sa_family == family)) + break; + } + else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET && + tmp->addr.in.sin_addr.s_addr == addr->addr4.s_addr) 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) - 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)) - break; -#endif - - if (tmp && auth) - { - *auth = 1; - ret = 1; + else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 && + IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6)) + break; + + if (tmp) + { + *auth = 1; + ret = 1; + } } return ret; } -/* 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) { @@ -201,14 +192,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; @@ -218,7 +206,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; @@ -228,7 +216,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; @@ -240,14 +228,16 @@ struct iface_param { }; static int iface_allowed(struct iface_param *param, int if_index, char *label, - union mysockaddr *addr, struct in_addr netmask, int prefixlen, int dad) + union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags) { struct irec *iface; - int mtu = 0, loopback; + struct cond_domain *cond; + int loopback; struct ifreq ifr; 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 @@ -263,11 +253,10 @@ static int iface_allowed(struct iface_param *param, in if (loopback) dhcp_ok = 0; - if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1) - mtu = ifr.ifr_mtu; - 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)) @@ -290,22 +279,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; @@ -333,12 +318,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) @@ -354,57 +338,161 @@ 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 /* Update addresses from interface_names. These are a set independent of the set we're listening on. */ for (int_name = daemon->int_names; int_name; int_name = int_name->next) - if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0 && - (addr->sa.sa_family == int_name->family || int_name->family == 0)) + if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0) { - if (param->spare) + struct addrlist *lp; + + al = NULL; + + if (addr->sa.sa_family == AF_INET && (int_name->flags & (IN4 | INP4))) { - al = param->spare; - param->spare = al->next; + struct in_addr newaddr = addr->in.sin_addr; + + if (int_name->flags & INP4) + { + if (netmask.s_addr == 0xffffffff) + continue; + + newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) | + (int_name->proto4.s_addr & ~netmask.s_addr); + } + + /* check for duplicates. */ + for (lp = int_name->addr; lp; lp = lp->next) + if (lp->flags == 0 && lp->addr.addr4.s_addr == newaddr.s_addr) + break; + + if (!lp) + { + if (param->spare) + { + al = param->spare; + param->spare = al->next; + } + else + al = whine_malloc(sizeof(struct addrlist)); + + if (al) + { + al->flags = 0; + al->addr.addr4 = newaddr; + } + } } - else - al = whine_malloc(sizeof(struct addrlist)); + + if (addr->sa.sa_family == AF_INET6 && (int_name->flags & (IN6 | INP6))) + { + struct in6_addr newaddr = addr->in6.sin6_addr; + + if (int_name->flags & INP6) + { + int i; + + /* No sense in doing /128. */ + if (prefixlen == 128) + continue; + + for (i = 0; i < 16; i++) + { + int bits = ((i+1)*8) - prefixlen; + + if (bits >= 8) + newaddr.s6_addr[i] = int_name->proto6.s6_addr[i]; + else if (bits >= 0) + { + unsigned char mask = 0xff << bits; + newaddr.s6_addr[i] = + (addr->in6.sin6_addr.s6_addr[i] & mask) | + (int_name->proto6.s6_addr[i] & ~mask); + } + } + } + + /* check for duplicates. */ + for (lp = int_name->addr; lp; lp = lp->next) + if ((lp->flags & ADDRLIST_IPV6) && + IN6_ARE_ADDR_EQUAL(&lp->addr.addr6, &newaddr)) + break; + + if (!lp) + { + if (param->spare) + { + al = param->spare; + param->spare = al->next; + } + else + al = whine_malloc(sizeof(struct addrlist)); + + if (al) + { + al->flags = ADDRLIST_IPV6; + al->addr.addr6 = newaddr; + + /* 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; + } + } + } if (al) { al->next = int_name->addr; int_name->addr = al; - - if (addr->sa.sa_family == AF_INET) - { - al->addr.addr.addr4 = addr->in.sin_addr; - al->flags = 0; - } -#ifdef HAVE_IPV6 - else - { - al->addr.addr.addr6 = addr->in6.sin6_addr; - al->flags = ADDRLIST_IPV6; - } -#endif } } } - + + /* Update addresses for domain=, */ + for (cond = daemon->cond_domain; cond; cond = cond->next) + if (cond->interface && strncmp(label, cond->interface, IF_NAMESIZE) == 0) + { + struct addrlist *al; + + if (param->spare) + { + al = param->spare; + param->spare = al->next; + } + else + al = whine_malloc(sizeof(struct addrlist)); + + if (addr->sa.sa_family == AF_INET) + { + al->addr.addr4 = addr->in.sin_addr; + al->flags = 0; + } + else + { + al->addr.addr6 = addr->in6.sin6_addr; + al->flags = ADDRLIST_IPV6; + } + + al->prefixlen = prefixlen; + al->next = cond->al; + cond->al = al; + } + /* 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 = dad; + iface->dad = !!(iface_flags & IFACE_TENTATIVE); iface->found = 1; /* for garbage collection */ + iface->netmask = netmask; return 1; } @@ -432,14 +520,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. */ @@ -472,16 +558,22 @@ static int iface_allowed(struct iface_param *param, in /* add to list */ if ((iface = whine_malloc(sizeof(struct irec)))) { + int mtu = 0; + + if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1) + mtu = ifr.ifr_mtu; + iface->addr = *addr; iface->netmask = netmask; iface->tftp_ok = tftp_ok; iface->dhcp_ok = dhcp_ok; iface->dns_auth = auth_dns; iface->mtu = mtu; - iface->dad = dad; + iface->dad = !!(iface_flags & IFACE_TENTATIVE); 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 +589,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) @@ -523,22 +614,22 @@ static int iface_allowed_v6(struct in6_addr *local, in else addr.in6.sin6_scope_id = 0; - return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, !!(flags & IFACE_TENTATIVE)); + 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) { union mysockaddr addr; int prefix, bit; + + (void)broadcast; /* warning */ memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SOCKADDR_SA_LEN addr.in.sin_len = sizeof(addr.in); #endif addr.in.sin_family = AF_INET; - addr.in.sin_addr = broadcast; /* warning */ addr.in.sin_addr = local; addr.in.sin_port = htons(daemon->port); @@ -547,20 +638,97 @@ 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|MS_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; - static int done = 0, active = 0; + static int done = 0; struct iface_param param; int errsave, ret = 1; struct addrlist *addr, *tmp; struct interface_name *intname; + struct cond_domain *cond; struct irec *iface; #ifdef HAVE_AUTH struct auth_zone *zone; #endif - + struct server *serv; + /* Do this max once per select cycle - also inhibits netlink socket use in TCP child processes. */ @@ -570,17 +738,33 @@ int enumerate_interfaces(int reset) return 1; } - if (done || active) + if (done) return 1; done = 1; - /* protect against recusive calls from iface_enumerate(); */ - active = 1; - if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return 0; - + + /* iface indexes can change when interfaces are created/destroyed. + We use them in the main forwarding control path, when the path + to a server is specified by an interface, so cache them. + Update the cache here. */ + for (serv = daemon->servers; serv; serv = serv->next) + if (serv->interface[0] != 0) + { +#ifdef HAVE_LINUX_NETWORK + struct ifreq ifr; + + safe_strncpy(ifr.ifr_name, serv->interface, IF_NAMESIZE); + if (ioctl(param.fd, SIOCGIFINDEX, &ifr) != -1) + serv->ifindex = ifr.ifr_ifindex; +#else + serv->ifindex = if_nametoindex(serv->interface); +#endif + } + +again: /* Mark interfaces for garbage collection */ for (iface = daemon->interfaces; iface; iface = iface->next) iface->found = 0; @@ -598,6 +782,19 @@ int enumerate_interfaces(int reset) intname->addr = NULL; } + /* remove addresses stored against cond-domains. */ + for (cond = daemon->cond_domain; cond; cond = cond->next) + { + for (addr = cond->al; addr; addr = tmp) + { + tmp = addr->next; + addr->next = spare; + spare = addr; + } + + cond->al = NULL; + } + /* Remove list of addresses of local interfaces */ for (addr = daemon->interface_addrs; addr; addr = tmp) { @@ -631,12 +828,15 @@ 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); + if (ret < 0) + goto again; + else if (ret) + { + ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4); + if (ret < 0) + goto again; + } errsave = errno; close(param.fd); @@ -646,10 +846,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) { @@ -657,29 +858,19 @@ 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; - active = 0; return ret; } @@ -703,7 +894,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 */ @@ -713,7 +904,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); @@ -722,7 +913,7 @@ static int make_sock(union mysockaddr *addr, int type, if (fd != -1) close (fd); - errno = errsav; + errno = errsave; if (dienow) { @@ -740,17 +931,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) @@ -767,15 +961,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; @@ -802,43 +993,47 @@ 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 int opt = 1; struct cmsghdr *cmptr; struct msghdr msg; + socklen_t len; - /* use mshdr do that the CMSDG_* macros are available */ + /* use mshdr so that the CMSDG_* macros are available */ msg.msg_control = daemon->packet; - msg.msg_controllen = daemon->packet_buff_sz; - + msg.msg_controllen = len = daemon->packet_buff_sz; + /* we overwrote the buffer... */ - daemon->srv_save = NULL; - + daemon->srv_save = NULL; + if (af == AF_INET) { if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 && - getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1) - for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) - if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) - { - union { - unsigned char *c; - struct in_pktinfo *p; - } p; - - p.c = CMSG_DATA(cmptr); - if_index = p.p->ipi_ifindex; - } + getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, &len) != -1) + { + msg.msg_controllen = len; + for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) + if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) + { + union { + unsigned char *c; + struct in_pktinfo *p; + } p; + + p.c = CMSG_DATA(cmptr); + if_index = p.p->ipi_ifindex; + } + } } -#ifdef HAVE_IPV6 else { /* Only the RFC-2292 API has the ability to find the interface for TCP connections, @@ -854,9 +1049,10 @@ int tcp_interface(int fd, int af) #endif if (set_ipv6pktinfo(fd) && - getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1) + getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, &len) != -1) { - for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) + msg.msg_controllen = len; + for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) { union { @@ -869,7 +1065,6 @@ int tcp_interface(int fd, int af) } } } -#endif /* IPV6 */ #endif /* Linux */ return if_index; @@ -899,7 +1094,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; @@ -907,7 +1101,6 @@ static struct listener *create_listeners(union mysocka tftpfd = make_sock(addr, SOCK_DGRAM, dienow); addr->in6.sin6_port = save; } -# endif } #endif @@ -915,10 +1108,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; } @@ -940,11 +1134,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); @@ -954,25 +1147,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|MS_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 @@ -992,6 +1212,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|MS_DEBUG, _("listening on %s port %d"), daemon->addrbuff, port); + } } } @@ -1033,6 +1259,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; @@ -1081,23 +1316,30 @@ void join_multicast(int dienow) if ((daemon->doing_dhcp6 || daemon->relay6) && setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) - err = 1; + err = errno; inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr); if (daemon->doing_dhcp6 && setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) - err = 1; + err = errno; inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr); if (daemon->doing_ra && setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) - err = 1; + err = errno; if (err) { char *s = _("interface %s failed to join DHCPv6 multicast group: %s"); + errno = err; + +#ifdef HAVE_LINUX_NETWORK + if (errno == ENOMEM) + my_syslog(LOG_ERR, _("try increasing /proc/sys/net/core/optmem_max")); +#endif + if (dienow) die(s, iface->name, EC_BADNET); else @@ -1108,82 +1350,87 @@ void join_multicast(int dienow) } #endif -/* return a UDP socket bound to a random port, have to cope with straying into - occupied port nos and reserved ones. */ -int random_sock(int family) +int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp) { - int fd; + union mysockaddr addr_copy = *addr; + unsigned short port; + int tries = 1; + unsigned short ports_avail = 1; - if ((fd = socket(family, SOCK_DGRAM, 0)) != -1) - { - union mysockaddr addr; - unsigned int ports_avail = 65536u - (unsigned short)daemon->min_port; - int tries = ports_avail < 30 ? 3 * ports_avail : 100; + if (addr_copy.sa.sa_family == AF_INET) + port = addr_copy.in.sin_port; + else + port = addr_copy.in6.sin6_port; - memset(&addr, 0, sizeof(addr)); - addr.sa.sa_family = family; - - /* don't loop forever if all ports in use. */ - - if (fix_fd(fd)) - while(tries--) - { - unsigned short port = rand16(); - - if (daemon->min_port != 0) - port = htons(daemon->min_port + (port % ((unsigned short)ports_avail))); - - if (family == AF_INET) - { - addr.in.sin_addr.s_addr = INADDR_ANY; - addr.in.sin_port = port; -#ifdef HAVE_SOCKADDR_SA_LEN - addr.in.sin_len = sizeof(struct sockaddr_in); -#endif - } -#ifdef HAVE_IPV6 - else - { - addr.in6.sin6_addr = in6addr_any; - addr.in6.sin6_port = port; -#ifdef HAVE_SOCKADDR_SA_LEN - addr.in6.sin6_len = sizeof(struct sockaddr_in6); -#endif - } -#endif - - if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0) - return fd; - - if (errno != EADDRINUSE && errno != EACCES) - break; - } - - close(fd); + /* cannot set source _port_ for TCP connections. */ + if (is_tcp) + port = 0; + else if (port == 0 && daemon->max_port != 0) + { + /* Bind a random port within the range given by min-port and max-port if either + or both are set. Otherwise use the OS's random ephemeral port allocation by + leaving port == 0 and tries == 1 */ + ports_avail = daemon->max_port - daemon->min_port + 1; + tries = (ports_avail < SMALL_PORT_RANGE) ? ports_avail : 100; + port = htons(daemon->min_port + (rand16() % ports_avail)); } - - return -1; -} + while (1) + { + /* elide bind() call if it's to port 0, address 0 */ + if (addr_copy.sa.sa_family == AF_INET) + { + if (port == 0 && addr_copy.in.sin_addr.s_addr == 0) + break; + addr_copy.in.sin_port = port; + } + else + { + if (port == 0 && IN6_IS_ADDR_UNSPECIFIED(&addr_copy.in6.sin6_addr)) + break; + addr_copy.in6.sin6_port = port; + } + + if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1) + break; + + if (errno != EADDRINUSE && errno != EACCES) + return 0; -int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp) -{ - union mysockaddr addr_copy = *addr; + if (--tries == 0) + return 0; - /* cannot set source _port_ for TCP connections. */ - if (is_tcp) + /* For small ranges, do a systematic search, not a random one. */ + if (ports_avail < SMALL_PORT_RANGE) + { + unsigned short hport = ntohs(port); + if (hport++ == daemon->max_port) + hport = daemon->min_port; + port = htons(hport); + } + else + port = htons(daemon->min_port + (rand16() % ports_avail)); + } + + if (!is_tcp && ifindex > 0) { +#if defined(IP_UNICAST_IF) if (addr_copy.sa.sa_family == AF_INET) - addr_copy.in.sin_port = 0; -#ifdef HAVE_IPV6 - else - addr_copy.in6.sin6_port = 0; + { + 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 } - - if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) == -1) - return 0; - + + (void)intname; /* suppress potential unused warning */ #if defined(SO_BINDTODEVICE) if (intname[0] != 0 && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE) == -1) @@ -1193,33 +1440,32 @@ int local_bind(int fd, union mysockaddr *addr, char *i return 1; } -static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname) +static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsigned int ifindex) { struct serverfd *sfd; 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) + the INADDR_ANY/port0 socket have sfd set to NULL, this is + anything without an explictly set source port. */ + if (!daemon->osport) { errno = 0; if (addr->sa.sa_family == AF_INET && - addr->in.sin_addr.s_addr == INADDR_ANY && 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 } - + /* may have a suitable one already */ for (sfd = daemon->sfds; sfd; sfd = sfd->next ) - if (sockaddr_isequal(&sfd->source_addr, addr) && + if (ifindex == sfd->ifindex && + sockaddr_isequal(&sfd->source_addr, addr) && strcmp(intname, sfd->interface) == 0) return sfd; @@ -1233,20 +1479,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; } @@ -1255,6 +1505,7 @@ static struct serverfd *allocate_sfd(union mysockaddr void pre_allocate_sfds(void) { struct server *srv; + struct serverfd *sfd; if (daemon->query_port != 0) { @@ -1266,8 +1517,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, "", 0))) + sfd->preallocated = 1; + memset(&addr, 0, sizeof(addr)); addr.in6.sin6_family = AF_INET6; addr.in6.sin6_addr = in6addr_any; @@ -1275,17 +1527,16 @@ 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, "", 0))) + sfd->preallocated = 1; } for (srv = daemon->servers; srv; srv = srv->next) - if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) && - !allocate_sfd(&srv->source_addr, srv->interface) && + if (!allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) && 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, " "); @@ -1296,187 +1547,170 @@ void pre_allocate_sfds(void) } } -void mark_servers(int flag) +void check_servers(int no_loop_check) { + struct irec *iface; struct server *serv; - - /* mark everything with argument flag */ - for (serv = daemon->servers; serv; serv = serv->next) - if (serv->flags & flag) - serv->flags |= SERV_MARK; -} - -void cleanup_servers(void) -{ - struct server *serv, *tmp, **up; - - /* unlink and free anything still marked. */ - for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) - { - tmp = serv->next; - if (serv->flags & SERV_MARK) - { - server_gone(serv); - *up = serv->next; - if (serv->domain) - free(serv->domain); - free(serv); - } - else - up = &serv->next; - } -} - -void add_update_server(int flags, - union mysockaddr *addr, - union mysockaddr *source_addr, - const char *interface, - const char *domain) -{ - struct server *serv, *next = NULL; - char *domain_str = NULL; + struct serverfd *sfd, *tmp, **up; + int port = 0, count; + int locals = 0; - /* See if there is a suitable candidate, and unmark */ - for (serv = daemon->servers; serv; serv = serv->next) - if (serv->flags & SERV_MARK) - { - if (domain) - { - if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain)) - continue; - } - else - { - if (serv->flags & SERV_HAS_DOMAIN) - continue; - } - - break; - } +#ifdef HAVE_LOOP + if (!no_loop_check) + loop_send_probes(); +#endif - if (serv) - { - domain_str = serv->domain; - next = serv->next; - } - else if ((serv = whine_malloc(sizeof (struct server)))) - { - /* Not found, create a new one. */ - if (domain && !(domain_str = whine_malloc(strlen(domain)+1))) - { - free(serv); - serv = NULL; - } - else - { - struct server *s; - /* Add to the end of the chain, for order */ - if (!daemon->servers) - daemon->servers = serv; - else - { - for (s = daemon->servers; s->next; s = s->next); - s->next = serv; - } - if (domain) - strcpy(domain_str, domain); - } - } + /* clear all marks. */ + mark_servers(0); - if (serv) - { - memset(serv, 0, sizeof(struct server)); - serv->flags = flags; - serv->domain = domain_str; - serv->next = next; - serv->queries = serv->failed_queries = 0; - - if (domain) - serv->flags |= SERV_HAS_DOMAIN; - - if (interface) - strcpy(serv->interface, interface); - if (addr) - serv->addr = *addr; - if (source_addr) - serv->source_addr = *source_addr; - } -} - -void check_servers(void) -{ - struct irec *iface; - struct server *serv; - int port = 0; - - /* interface may be new since startup */ + /* interface may be new since startup */ if (!option_bool(OPT_NOWILD)) enumerate_interfaces(0); - - for (serv = daemon->servers; serv; serv = serv->next) - { - if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND))) - { - port = prettyprint_addr(&serv->addr, daemon->namebuff); - /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */ - if (serv->addr.sa.sa_family == AF_INET && - serv->addr.in.sin_addr.s_addr == 0) - { - serv->flags |= SERV_MARK; - continue; - } + /* don't garbage collect pre-allocated sfds. */ + for (sfd = daemon->sfds; sfd; sfd = sfd->next) + sfd->used = sfd->preallocated; - for (iface = daemon->interfaces; iface; iface = iface->next) - if (sockaddr_isequal(&serv->addr, &iface->addr)) - break; - if (iface) - { - my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff); - serv->flags |= SERV_MARK; - continue; - } + for (count = 0, serv = daemon->servers; serv; serv = serv->next) + { + /* Init edns_pktsz for newly created server records. */ + if (serv->edns_pktsz == 0) + serv->edns_pktsz = daemon->edns_pktsz; + +#ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID)) + { + if (!(serv->flags & SERV_FOR_NODOTS)) + serv->flags |= SERV_DO_DNSSEC; - /* Do we need a socket set? */ - if (!serv->sfd && - !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) && - errno != 0) + /* Disable DNSSEC validation when using server=/domain/.... servers + unless there's a configured trust anchor. */ + if (strlen(serv->domain) != 0) { - my_syslog(LOG_WARNING, - _("ignoring nameserver %s - cannot make/bind socket: %s"), - daemon->namebuff, strerror(errno)); - serv->flags |= SERV_MARK; - continue; + struct ds_config *ds; + char *domain = serv->domain; + + /* .example.com is valid */ + while (*domain == '.') + domain++; + + for (ds = daemon->ds; ds; ds = ds->next) + if (ds->name[0] != 0 && hostname_isequal(domain, ds->name)) + break; + + if (!ds) + serv->flags &= ~SERV_DO_DNSSEC; } } +#endif - if (!(serv->flags & SERV_NO_REBIND)) + port = prettyprint_addr(&serv->addr, daemon->namebuff); + + /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */ + if (serv->addr.sa.sa_family == AF_INET && + serv->addr.in.sin_addr.s_addr == 0) { - if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV)) - { - char *s1, *s2; - if (!(serv->flags & SERV_HAS_DOMAIN)) - s1 = _("unqualified"), s2 = _("names"); - else if (strlen(serv->domain) == 0) - s1 = _("default"), s2 = ""; - else - s1 = _("domain"), s2 = serv->domain; - - if (serv->flags & SERV_NO_ADDR) - my_syslog(LOG_INFO, _("using local addresses only 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 if (!(serv->flags & SERV_LITERAL_ADDRESS)) - my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2); - } - else if (serv->interface[0] != 0) - my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface); + serv->flags |= SERV_MARK; + continue; + } + + for (iface = daemon->interfaces; iface; iface = iface->next) + if (sockaddr_isequal(&serv->addr, &iface->addr)) + break; + if (iface) + { + my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff); + serv->flags |= SERV_MARK; + continue; + } + + /* Do we need a socket set? */ + if (!serv->sfd && + !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) && + errno != 0) + { + my_syslog(LOG_WARNING, + _("ignoring nameserver %s - cannot make/bind socket: %s"), + daemon->namebuff, strerror(errno)); + serv->flags |= SERV_MARK; + continue; + } + + if (serv->sfd) + serv->sfd->used = 1; + + if (count == SERVERS_LOGGED) + my_syslog(LOG_INFO, _("more servers are defined but not logged")); + + if (++count > SERVERS_LOGGED) + continue; + + if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS)) + { + char *s1, *s2, *s3 = "", *s4 = ""; + +#ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC)) + s3 = _("(no DNSSEC)"); +#endif + if (serv->flags & SERV_FOR_NODOTS) + s1 = _("unqualified"), s2 = _("names"); + else if (strlen(serv->domain) == 0) + s1 = _("default"), s2 = ""; else - my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); + s1 = _("domain"), s2 = serv->domain, s4 = (serv->flags & SERV_WILDCARD) ? "*" : ""; + + my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s%s %s"), daemon->namebuff, port, s1, s4, s2, s3); } +#ifdef HAVE_LOOP + else if (serv->flags & SERV_LOOP) + my_syslog(LOG_INFO, _("NOT using nameserver %s#%d - query loop detected"), daemon->namebuff, port); +#endif + else if (serv->interface[0] != 0) + my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface); + else + my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); + } + + for (count = 0, serv = daemon->local_domains; serv; serv = serv->next) + { + if (++count > SERVERS_LOGGED) + continue; + + if ((serv->flags & SERV_LITERAL_ADDRESS) && + !(serv->flags & (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)) && + strlen(serv->domain)) + { + count--; + if (++locals <= LOCALS_LOGGED) + my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain); + } + else if (serv->flags & SERV_USE_RESOLV) + my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain); + } + + 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); - cleanup_servers(); + /* 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(); /* remove servers we just deleted. */ + build_server_array(); } /* Return zero if no servers found, in that case we keep polling. @@ -1511,7 +1745,7 @@ int reload_servers(char *fname) memset(&addr, 0, sizeof(addr)); memset(&source_addr, 0, sizeof(source_addr)); - if ((addr.in.sin_addr.s_addr = inet_addr(token)) != (in_addr_t) -1) + if (inet_pton(AF_INET, token, &addr.in.sin_addr) > 0) { #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in); @@ -1521,7 +1755,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; @@ -1549,12 +1782,8 @@ int reload_servers(char *fname) else continue; } -#else /* IPV6 */ - else - continue; -#endif - add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL); + add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL, NULL); gotone = 1; } @@ -1564,10 +1793,11 @@ int reload_servers(char *fname) return gotone; } -#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_BSD_NETWORK) /* Called when addresses are added or deleted from an interface */ void newaddress(time_t now) { + struct dhcp_relay *relay; + (void)now; if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) || @@ -1576,6 +1806,12 @@ void newaddress(time_t now) if (option_bool(OPT_CLEVERBIND)) create_bound_listeners(0); + +#ifdef HAVE_DHCP + /* clear cache of subnet->relay index */ + for (relay = daemon->relay4; relay; relay = relay->next) + relay->iface_index = 0; +#endif #ifdef HAVE_DHCP6 if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra) @@ -1586,11 +1822,8 @@ void newaddress(time_t now) if (daemon->doing_dhcp6) lease_find_interfaces(now); -#endif -} + for (relay = daemon->relay6; relay; relay = relay->next) + relay->iface_index = 0; #endif - - - - +}