From 52d4abf2f965574c5d135e0b43d027e553a31fa0 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 21 Mar 2012 21:39:48 +0000 Subject: [PATCH 1/3] Make --listen-address work for all 127.0.0.0/8 addresses. --- CHANGELOG | 11 ++++++++++- src/dnsmasq.c | 35 +++++++++++++++++------------------ src/dnsmasq.h | 2 +- src/forward.c | 2 +- src/network.c | 20 ++++++++++++++++++++ src/tftp.c | 40 +++++++++++++++++++++++++++------------- 6 files changed, 76 insertions(+), 34 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a08d25b..6c1c07b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -40,7 +40,16 @@ version 2.61 Set the environment variable DNSMASQ_LOG_DHCP when running the script id --log-dhcp is in effect, so that script can - taylor their logging verbosity. Suggestion from Malte Forkel. + taylor their logging verbosity. Suggestion from Malte + Forkel. + + Arrange that addresses specified with --listen-address + work even if there is no interface carrying the + address. This is chiefly useful for IPv4 loopback + addresses, where any address in 127.0.0.0/8 is a valid + loopback address, but normally only 127.0.0.1 appears on + the lo interface. Thanks to Mathieu Trudel-Lapierre for + the idea and initial patch. version 2.60 diff --git a/src/dnsmasq.c b/src/dnsmasq.c index efe63d1..8663aa3 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -209,13 +209,6 @@ int main (int argc, char **argv) for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) if (if_tmp->name && !if_tmp->used) die(_("unknown interface %s"), if_tmp->name, EC_BADNET); - - for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next) - if (!if_tmp->used) - { - prettyprint_addr(&if_tmp->addr, daemon->namebuff); - die(_("no interface with address %s"), daemon->namebuff, EC_BADNET); - } } else create_wildcard_listeners(); @@ -1284,18 +1277,19 @@ static void check_dns_listeners(fd_set *set, time_t now) int confd; struct irec *iface = NULL; pid_t p; + union mysockaddr tcp_addr; + socklen_t tcp_len = sizeof(union mysockaddr); + + while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR); - while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR); - - if (confd == -1) + if (confd == -1 || + getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1) continue; if (option_bool(OPT_NOWILD)) - iface = listener->iface; + iface = listener->iface; /* May be NULL */ else { - union mysockaddr tcp_addr; - socklen_t tcp_len = sizeof(union mysockaddr); /* 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 @@ -1303,14 +1297,13 @@ static void check_dns_listeners(fd_set *set, time_t now) interface too, for localisation. */ /* interface may be new since startup */ - if (enumerate_interfaces() && - getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1) + if (enumerate_interfaces()) for (iface = daemon->interfaces; iface; iface = iface->next) if (sockaddr_isequal(&iface->addr, &tcp_addr)) break; } - if (!iface) + if (!iface && !option_bool(OPT_NOWILD)) { shutdown(confd, SHUT_RDWR); close(confd); @@ -1336,7 +1329,13 @@ static void check_dns_listeners(fd_set *set, time_t now) unsigned char *buff; struct server *s; int flags; - + struct in_addr netmask; + + if (iface) + netmask = iface->netmask; + else + netmask.s_addr = 0; + #ifndef NO_FORK /* Arrange for SIGALARM after CHILD_LIFETIME seconds to terminate the process. */ @@ -1354,7 +1353,7 @@ static void check_dns_listeners(fd_set *set, time_t now) if ((flags = fcntl(confd, F_GETFL, 0)) != -1) fcntl(confd, F_SETFL, flags & ~O_NONBLOCK); - buff = tcp_request(confd, now, &iface->addr, iface->netmask); + buff = tcp_request(confd, now, &tcp_addr, netmask); shutdown(confd, SHUT_RDWR); close(confd); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index f2a6f36..0a3e66f 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -395,7 +395,7 @@ struct irec { struct listener { int fd, tcpfd, tftpfd, family; - struct irec *iface; /* only valid for non-wildcard */ + struct irec *iface; /* only sometimes valid for non-wildcard */ struct listener *next; }; diff --git a/src/forward.c b/src/forward.c index e3bf7d7..f999dc6 100644 --- a/src/forward.c +++ b/src/forward.c @@ -660,7 +660,7 @@ void receive_query(struct listener *listen, time_t now) /* packet buffer overwritten */ daemon->srv_save = NULL; - if (listen->family == AF_INET && option_bool(OPT_NOWILD)) + if (listen->iface && listen->family == AF_INET && option_bool(OPT_NOWILD)) { dst_addr_4 = listen->iface->addr.in.sin_addr; netmask = listen->iface->netmask; diff --git a/src/network.c b/src/network.c index 32bd517..f5dcf97 100644 --- a/src/network.c +++ b/src/network.c @@ -511,6 +511,7 @@ void create_bound_listeners(int dienow) { struct listener *new; struct irec *iface; + struct iname *if_tmp; for (iface = daemon->interfaces; iface; iface = iface->next) if (!iface->done && !iface->dad && @@ -521,6 +522,25 @@ void create_bound_listeners(int dienow) daemon->listeners = new; iface->done = 1; } + + /* Check for --listen-address options that haven't been used because there's + no interface with a matching address. These may be valid: eg it's possible + to listen on 127.0.1.1 even if the loopback interface is 127.0.0.1 + + If the address isn't valid the bind() will fail and we'll die(). + + The resulting listeners have the ->iface field NULL, and this has to be + handled by the DNS and TFTP code. It disables --localise-queries processing + (no netmask) and some MTU login the tftp code. */ + + for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next) + if (!if_tmp->used && + (new = create_listeners(&if_tmp->addr, 1, dienow))) + { + new->iface = NULL; + new->next = daemon->listeners; + daemon->listeners = new; + } } int is_dad_listeners(void) diff --git a/src/tftp.c b/src/tftp.c index 3ef6545..f6c0e21 100644 --- a/src/tftp.c +++ b/src/tftp.c @@ -57,7 +57,7 @@ void tftp_request(struct listener *listen, time_t now) int mtuflag = IP_PMTUDISC_DONT; #endif char namebuff[IF_NAMESIZE]; - char *name; + char *name = NULL; char *prefix = daemon->tftp_prefix; struct tftp_prefix *pref; struct interface_list *ir; @@ -95,9 +95,20 @@ void tftp_request(struct listener *listen, time_t now) if (option_bool(OPT_NOWILD)) { - addr = listen->iface->addr; - mtu = listen->iface->mtu; - name = listen->iface->name; + if (listen->iface) + { + addr = listen->iface->addr; + mtu = listen->iface->mtu; + name = listen->iface->name; + } + else + { + /* we're listening on an address that doesn't appear on an interface, + ask the kernel what the socket is bound to */ + socklen_t tcp_len = sizeof(union mysockaddr); + if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1) + return; + } } else { @@ -211,15 +222,18 @@ void tftp_request(struct listener *listen, time_t now) mtu = ifr.ifr_mtu; } - /* check for per-interface prefix */ - for (pref = daemon->if_prefix; pref; pref = pref->next) - if (strcmp(pref->interface, name) == 0) - prefix = pref->prefix; - - /* wierd TFTP interfaces disable special options. */ - for (ir = daemon->tftp_interfaces; ir; ir = ir->next) - if (strcmp(ir->interface, name) == 0) - special = 1; + if (name) + { + /* check for per-interface prefix */ + for (pref = daemon->if_prefix; pref; pref = pref->next) + if (strcmp(pref->interface, name) == 0) + prefix = pref->prefix; + + /* wierd TFTP interfaces disable special options. */ + for (ir = daemon->tftp_interfaces; ir; ir = ir->next) + if (strcmp(ir->interface, name) == 0) + special = 1; + } if (listen->family == AF_INET) { From 7d2b5c958309013e889299f66bc62be7e33583b2 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Fri, 23 Mar 2012 10:00:02 +0000 Subject: [PATCH 2/3] Fix crash in DHCPINFORM without valid --dhcp-range. --- CHANGELOG | 4 ++++ src/rfc2131.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 6c1c07b..5431b49 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -51,6 +51,10 @@ version 2.61 the lo interface. Thanks to Mathieu Trudel-Lapierre for the idea and initial patch. + Fix crash, introduced in 2.60, when a DHCPINFORM is + received from a network which has no valid dhcp-range. + Thanks to Stephane Glondu for the bug report. + version 2.60 Fix compilation problem in Mac OS X Lion. Thanks to Olaf diff --git a/src/rfc2131.c b/src/rfc2131.c index 83dc4c1..d5581bc 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -2095,7 +2095,8 @@ static void do_options(struct dhcp_context *context, struct dhcp_netid_list *id_list; /* filter options based on tags, those we want get DHOPT_TAGOK bit set */ - context->netid.next = NULL; + if (context) + context->netid.next = NULL; tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts); /* logging */ From 442560beb4b983f0de07ec8be5c3ed8fb5bff0de Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Fri, 23 Mar 2012 10:01:13 +0000 Subject: [PATCH 3/3] Debian changelog for preivious fix. --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 71e2b8d..022f51a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +dnsmasq (2.60-2) unstable; urgency=high + + * Fix DHCPv4 segfault. (closes: #665008) + + -- Simon Kelley Fri, 23 Mar 2012 09:37:23 +0000 + dnsmasq (2.61-1) unstable; urgency=low * New upstream.