--- embedaddon/dnsmasq/src/dnsmasq.c 2013/07/29 19:37:40 1.1.1.1 +++ embedaddon/dnsmasq/src/dnsmasq.c 2014/06/15 16:31:38 1.1.1.2 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2014 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 @@ -50,8 +50,13 @@ int main (int argc, char **argv) #if defined(HAVE_LINUX_NETWORK) cap_user_header_t hdr = NULL; cap_user_data_t data = NULL; + char *bound_device = NULL; + int did_bind = 0; #endif +#if defined(HAVE_DHCP) || defined(HAVE_DHCP6) struct dhcp_context *context; + struct dhcp_relay *relay; +#endif #ifdef LOCALEDIR setlocale(LC_ALL, ""); @@ -76,16 +81,29 @@ int main (int argc, char **argv) umask(022); /* known umask, create leases and pid files as 0644 */ read_opts(argc, argv, compile_opts); - + if (daemon->edns_pktsz < PACKETSZ) daemon->edns_pktsz = PACKETSZ; +#ifdef HAVE_DNSSEC + /* Enforce min packet big enough for DNSSEC */ + if (option_bool(OPT_DNSSEC_VALID) && daemon->edns_pktsz < EDNS_PKTSZ) + daemon->edns_pktsz = EDNS_PKTSZ; +#endif + daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? daemon->edns_pktsz : DNSMASQ_PACKETSZ; daemon->packet = safe_malloc(daemon->packet_buff_sz); - + daemon->addrbuff = safe_malloc(ADDRSTRLEN); + +#ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID)) + { + daemon->keyname = safe_malloc(MAXDNAME); + daemon->workspacename = safe_malloc(MAXDNAME); + } +#endif - #ifdef HAVE_DHCP if (!daemon->lease_file) { @@ -126,6 +144,19 @@ int main (int argc, char **argv) } #endif + if (option_bool(OPT_DNSSEC_VALID)) + { +#ifdef HAVE_DNSSEC + if (!daemon->ds) + die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF); + + if (daemon->cachesize < CACHESIZ) + die(_("Cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF); +#else + die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF); +#endif + } + #ifndef HAVE_TFTP if (option_bool(OPT_TFTP)) die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF); @@ -166,50 +197,47 @@ int main (int argc, char **argv) daemon->soa_sn = now; #endif -#ifdef HAVE_DHCP - if (daemon->dhcp || daemon->dhcp6) - { +#ifdef HAVE_DHCP6 + if (daemon->dhcp6) + { + daemon->doing_ra = option_bool(OPT_RA); -# ifdef HAVE_DHCP6 - if (daemon->dhcp6) + for (context = daemon->dhcp6; context; context = context->next) { - daemon->doing_ra = option_bool(OPT_RA); - - for (context = daemon->dhcp6; context; context = context->next) - { - if (context->flags & CONTEXT_DHCP) - daemon->doing_dhcp6 = 1; - if (context->flags & CONTEXT_RA) - daemon->doing_ra = 1; -#ifndef HAVE_LINUX_NETWORK - if (context->flags & CONTEXT_TEMPLATE) - die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF); + if (context->flags & CONTEXT_DHCP) + daemon->doing_dhcp6 = 1; + if (context->flags & CONTEXT_RA) + daemon->doing_ra = 1; +#if !defined(HAVE_LINUX_NETWORK) && !defined(HAVE_BSD_NETWORK) + if (context->flags & CONTEXT_TEMPLATE) + die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF); #endif - } } -# endif - - /* Note that order matters here, we must call lease_init before - creating any file descriptors which shouldn't be leaked - to the lease-script init process. We need to call common_init - before lease_init to allocate buffers it uses.*/ + } +#endif + +#ifdef HAVE_DHCP + /* Note that order matters here, we must call lease_init before + creating any file descriptors which shouldn't be leaked + to the lease-script init process. We need to call common_init + before lease_init to allocate buffers it uses.*/ + if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6) + { + dhcp_common_init(); if (daemon->dhcp || daemon->doing_dhcp6) - { - dhcp_common_init(); - lease_init(now); - } - - if (daemon->dhcp) - dhcp_init(); - + lease_init(now); + } + + if (daemon->dhcp || daemon->relay4) + dhcp_init(); + # ifdef HAVE_DHCP6 - if (daemon->doing_ra) - ra_init(now); - - if (daemon->doing_dhcp6) - dhcp6_init(); + if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6) + ra_init(now); + + if (daemon->doing_dhcp6 || daemon->relay6) + dhcp6_init(); # endif - } #endif @@ -218,14 +246,16 @@ int main (int argc, char **argv) ipset_init(); #endif -#ifdef HAVE_LINUX_NETWORK +#if defined(HAVE_LINUX_NETWORK) netlink_init(); - - if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND)) - die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF); +#elif defined(HAVE_BSD_NETWORK) + route_init(); #endif - if (!enumerate_interfaces()) + if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND)) + die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF); + + if (!enumerate_interfaces(1) || !enumerate_interfaces(0)) die(_("failed to find list of interfaces: %s"), NULL, EC_MISC); if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)) @@ -239,17 +269,29 @@ int main (int argc, char **argv) #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP) /* after enumerate_interfaces() */ + bound_device = whichdevice(); + if (daemon->dhcp) { - bindtodevice(daemon->dhcpfd); - if (daemon->enable_pxe) - bindtodevice(daemon->pxefd); + if (!daemon->relay4 && bound_device) + { + bindtodevice(bound_device, daemon->dhcpfd); + did_bind = 1; + } + if (daemon->enable_pxe && bound_device) + { + bindtodevice(bound_device, daemon->pxefd); + did_bind = 1; + } } #endif #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6) - if (daemon->dhcp6) - bindtodevice(daemon->dhcp6fd); + if (daemon->doing_dhcp6 && !daemon->relay6 && bound_device) + { + bindtodevice(bound_device, daemon->dhcp6fd); + did_bind = 1; + } #endif } else @@ -257,12 +299,20 @@ int main (int argc, char **argv) #ifdef HAVE_DHCP6 /* after enumerate_interfaces() */ - if (daemon->doing_dhcp6 || daemon->doing_ra) + if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra) join_multicast(1); + + /* After netlink_init() and before create_helper() */ + lease_make_duid(now); #endif if (daemon->port != 0) - cache_init(); + { + cache_init(); +#ifdef HAVE_DNSSEC + blockdata_init(); +#endif + } if (option_bool(OPT_DBUS)) #ifdef HAVE_DBUS @@ -347,7 +397,7 @@ int main (int argc, char **argv) piperead = pipefd[0]; pipewrite = pipefd[1]; /* prime the pipe to load stuff first time. */ - send_event(pipewrite, EVENT_RELOAD, 0, NULL); + send_event(pipewrite, EVENT_INIT, 0, NULL); err_pipe[1] = -1; @@ -612,12 +662,29 @@ int main (int argc, char **argv) } #endif + if (option_bool(OPT_LOCAL_SERVICE)) + my_syslog(LOG_INFO, _("DNS service limited to local subnets")); + +#ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID)) + { + my_syslog(LOG_INFO, _("DNSSEC validation enabled")); + if (option_bool(OPT_DNSSEC_TIME)) + my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload")); + } +#endif + if (log_err != 0) my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"), daemon->log_file, strerror(log_err)); - + if (bind_fallback) my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations")); + + if (option_bool(OPT_NOWILD)) + warn_bound_listeners(); + + warn_int_names(); if (!option_bool(OPT_NOWILD)) for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) @@ -641,10 +708,16 @@ int main (int argc, char **argv) for (context = daemon->dhcp; context; context = context->next) log_context(AF_INET, context); + for (relay = daemon->relay4; relay; relay = relay->next) + log_relay(AF_INET, relay); + # ifdef HAVE_DHCP6 for (context = daemon->dhcp6; context; context = context->next) log_context(AF_INET6, context); + for (relay = daemon->relay6; relay; relay = relay->next) + log_relay(AF_INET6, relay); + if (daemon->doing_dhcp6 || daemon->doing_ra) dhcp_construct_contexts(now); @@ -652,6 +725,11 @@ int main (int argc, char **argv) my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled")); # endif +# ifdef HAVE_LINUX_NETWORK + if (did_bind) + my_syslog(MS_DHCP | LOG_INFO, _("DHCP, sockets bound exclusively to interface %s"), bound_device); +# endif + /* after dhcp_contruct_contexts */ if (daemon->dhcp || daemon->doing_dhcp6) lease_find_interfaces(now); @@ -749,7 +827,7 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP - if (daemon->dhcp) + if (daemon->dhcp || daemon->relay4) { FD_SET(daemon->dhcpfd, &rset); bump_maxfd(daemon->dhcpfd, &maxfd); @@ -762,7 +840,7 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP6 - if (daemon->doing_dhcp6) + if (daemon->doing_dhcp6 || daemon->relay6) { FD_SET(daemon->dhcp6fd, &rset); bump_maxfd(daemon->dhcp6fd, &maxfd); @@ -775,11 +853,14 @@ int main (int argc, char **argv) } #endif -#ifdef HAVE_LINUX_NETWORK +#if defined(HAVE_LINUX_NETWORK) FD_SET(daemon->netlinkfd, &rset); bump_maxfd(daemon->netlinkfd, &maxfd); +#elif defined(HAVE_BSD_NETWORK) + FD_SET(daemon->routefd, &rset); + bump_maxfd(daemon->routefd, &maxfd); #endif - + FD_SET(piperead, &rset); bump_maxfd(piperead, &maxfd); @@ -820,19 +901,26 @@ int main (int argc, char **argv) now = dnsmasq_time(); check_log_writer(&wset); - + + /* prime. */ + enumerate_interfaces(1); + /* Check the interfaces to see if any have exited DAD state and if so, bind the address. */ if (is_dad_listeners()) { - enumerate_interfaces(); + enumerate_interfaces(0); /* NB, is_dad_listeners() == 1 --> we're binding interfaces */ create_bound_listeners(0); + warn_bound_listeners(); } -#ifdef HAVE_LINUX_NETWORK +#if defined(HAVE_LINUX_NETWORK) if (FD_ISSET(daemon->netlinkfd, &rset)) netlink_multicast(now); +#elif defined(HAVE_BSD_NETWORK) + if (FD_ISSET(daemon->routefd, &rset)) + route_sock(now); #endif /* Check for changes to resolv files once per second max. */ @@ -871,7 +959,7 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP - if (daemon->dhcp) + if (daemon->dhcp || daemon->relay4) { if (FD_ISSET(daemon->dhcpfd, &rset)) dhcp_packet(now, 0); @@ -880,7 +968,7 @@ int main (int argc, char **argv) } #ifdef HAVE_DHCP6 - if (daemon->doing_dhcp6 && FD_ISSET(daemon->dhcp6fd, &rset)) + if ((daemon->doing_dhcp6 || daemon->relay6) && FD_ISSET(daemon->dhcp6fd, &rset)) dhcp6_packet(now); if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset)) @@ -1036,7 +1124,7 @@ static void async_event(int pipe, time_t now) { pid_t p; struct event_desc ev; - int i; + int i, check = 0; char *msg; /* NOTE: the memory used to return msg is leaked: use msgs in events only @@ -1046,12 +1134,36 @@ static void async_event(int pipe, time_t now) switch (ev.event) { case EVENT_RELOAD: +#ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID) && option_bool(OPT_DNSSEC_TIME)) + { + my_syslog(LOG_INFO, _("now checking DNSSEC signature timestamps")); + reset_option_bool(OPT_DNSSEC_TIME); + } +#endif + /* fall through */ + + case EVENT_INIT: clear_cache_and_reload(now); - if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL)) + + if (daemon->port != 0) { - reload_servers(daemon->resolv_files->name); - check_servers(); + if (daemon->resolv_files && option_bool(OPT_NO_POLL)) + { + reload_servers(daemon->resolv_files->name); + check = 1; + } + + if (daemon->servers_file) + { + read_servers_file(); + check = 1; + } + + if (check) + check_servers(); } + #ifdef HAVE_DHCP rerun_scripts(); #endif @@ -1223,6 +1335,8 @@ void poll_resolv(int force, int do_reload, time_t now) void clear_cache_and_reload(time_t now) { + (void)now; + if (daemon->port != 0) cache_reload(); @@ -1265,7 +1379,7 @@ static int set_dns_listeners(time_t now, fd_set *set, /* will we be able to get memory? */ if (daemon->port != 0) - get_new_frec(now, &wait); + get_new_frec(now, &wait, 0); for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) { @@ -1352,63 +1466,71 @@ static void check_dns_listeners(fd_set *set, time_t no if (confd == -1) continue; - + if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1) { close(confd); continue; } + + /* Make sure that the interface list is up-to-date. + + We do this here as we may need the results below, and + the DNS code needs them for --interface-name stuff. - if (option_bool(OPT_NOWILD)) - iface = listener->iface; /* May be NULL */ - else - { - int if_index; - char intr_name[IF_NAMESIZE]; + Multiple calls to enumerate_interfaces() per select loop are + inhibited, so calls to it in the child process (which doesn't select()) + have no effect. This avoids two processes reading from the same + netlink fd and screwing the pooch entirely. + */ - /* In full wildcard mode, need to refresh interface list. - This happens automagically in CLEVERBIND */ - if (!option_bool(OPT_CLEVERBIND)) - enumerate_interfaces(); - - /* if we can find the arrival interface, check it's one that's allowed */ - if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 && - indextoname(listener->tcpfd, if_index, intr_name)) - { - struct all_addr addr; - addr.addr.addr4 = tcp_addr.in.sin_addr; + enumerate_interfaces(0); + + if (option_bool(OPT_NOWILD)) + iface = listener->iface; /* May be NULL */ + else + { + int if_index; + char intr_name[IF_NAMESIZE]; + + /* if we can find the arrival interface, check it's one that's allowed */ + if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 && + indextoname(listener->tcpfd, if_index, intr_name)) + { + struct all_addr addr; + addr.addr.addr4 = tcp_addr.in.sin_addr; #ifdef HAVE_IPV6 - if (tcp_addr.sa.sa_family == AF_INET6) - addr.addr.addr6 = tcp_addr.in6.sin6_addr; + if (tcp_addr.sa.sa_family == AF_INET6) + addr.addr.addr6 = tcp_addr.in6.sin6_addr; #endif - - for (iface = daemon->interfaces; iface; iface = iface->next) - if (iface->index == if_index) - break; - - if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name)) - client_ok = 0; - } - - if (option_bool(OPT_CLEVERBIND)) - iface = listener->iface; /* May be NULL */ - else - { - /* Check for allowed interfaces when binding the wildcard address: - we do this by looking for an interface with the same address as - the local address of the TCP connection, then looking to see if that's - an allowed interface. As a side effect, we get the netmask of the - interface too, for localisation. */ - - for (iface = daemon->interfaces; iface; iface = iface->next) - if (sockaddr_isequal(&iface->addr, &tcp_addr)) - break; - - if (!iface) - client_ok = 0; - } - } - + + for (iface = daemon->interfaces; iface; iface = iface->next) + if (iface->index == if_index) + break; + + if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name)) + client_ok = 0; + } + + if (option_bool(OPT_CLEVERBIND)) + iface = listener->iface; /* May be NULL */ + else + { + /* Check for allowed interfaces when binding the wildcard address: + we do this by looking for an interface with the same address as + the local address of the TCP connection, then looking to see if that's + an allowed interface. As a side effect, we get the netmask of the + interface too, for localisation. */ + + for (iface = daemon->interfaces; iface; iface = iface->next) + if (sockaddr_isequal(&iface->addr, &tcp_addr)) + break; + + if (!iface) + client_ok = 0; + } + } + if (!client_ok) { shutdown(confd, SHUT_RDWR);