From 59353a6b5643cf71f1af21a68d734da68ec95bd1 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sun, 21 Nov 2004 19:34:28 +0000 Subject: [PATCH] import of dnsmasq-2.18.tar.gz --- CHANGELOG | 22 ++++++- dnsmasq-rh.spec | 2 +- dnsmasq-suse.spec | 2 +- src/cache.c | 2 + src/config.h | 17 +++-- src/dhcp.c | 30 +++++++-- src/dnsmasq.c | 2 + src/dnsmasq.h | 9 +-- src/network.c | 165 ++++++++++++++++++++++++---------------------- src/option.c | 16 +++-- src/rfc2131.c | 99 ++++++++++++++++------------ 11 files changed, 225 insertions(+), 141 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2519ee9..6c8ccaa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1288,9 +1288,29 @@ version 2.17 clients. Credit to Cedric Duval for spotting this. Fix rare crash associated with long DNS names and CNAME - records. Thanks to Holger_Hoffstatte and especially Steve + records. Thanks to Holger Hoffstatte and especially Steve Grecni for help chasing that one down. +version 2.18 + Reworked the Linux interface discovery code (again) to + cope with interfaces which have only IPv6 addresses and + interfaces with more than one IPv6 address. Thanks to + Martin Pels for help with that. + + Fix problems which occured when more than one dhcp-range + was specified in the same subnet: sometimes parameters + (lease time, network-id tag) from the wrong one would be + used. Thanks to Rory Campbell-Lange for the bug report. + + Reset cache statistics when clearing the cache. + + Enable long command line options on FreeBSD when the + C library supports them. + + + + + diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec index 0cc9ba8..61ddb17 100644 --- a/dnsmasq-rh.spec +++ b/dnsmasq-rh.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.17 +Version: 2.18 Release: 1 Copyright: GPL Group: System Environment/Daemons diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec index b253a5b..9dcb709 100644 --- a/dnsmasq-suse.spec +++ b/dnsmasq-suse.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.17 +Version: 2.18 Release: 1 Copyright: GPL Group: Productivity/Networking/DNS/Servers diff --git a/src/cache.c b/src/cache.c index a49016a..c50dbe7 100644 --- a/src/cache.c +++ b/src/cache.c @@ -602,6 +602,8 @@ void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *a struct crec *cache, **up, *tmp; int i; + cache_inserted = cache_live_freed = 0; + for (i=0; istatic_only && addr >= start && addr <= end) - return 1; + return context; } - return 0; + return NULL; } - + +struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr) +{ + /* We start of with a set of possible contexts, all on the current subnet. + These are chained on ->current. + Here we have an address, and return the actual context correponding to that + address. Note that none may fit, if the address came a dhcp-host and is outside + any dhcp-range. In that case we return a static range is possible, or failing that, + any context on the subnet. (If there's more than one, this is a dodgy configuration: + maybe there should be a warning.) */ + + struct dhcp_context *tmp = address_available(context, taddr); + + if (tmp) + return tmp; + + for (tmp = context; tmp; tmp = tmp->current) + if (tmp->static_only) + return tmp; + + return context; +} + struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr) { struct dhcp_config *config; diff --git a/src/dnsmasq.c b/src/dnsmasq.c index cf28a42..7333bde 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -549,11 +549,13 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now) if (!match || (num_kids >= MAX_PROCS)) close(confd); +#ifndef NO_FORK else if (!(daemon->options & OPT_DEBUG) && fork()) { num_kids++; close(confd); } +#endif else { char *buff; diff --git a/src/dnsmasq.h b/src/dnsmasq.h index e96e139..dd61c41 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -29,6 +29,9 @@ #include #include +/* and this. */ +#include + #include "config.h" #include @@ -50,9 +53,6 @@ #include #include #include -#ifdef HAVE_GETOPT_LONG -# include -#endif #include #include #include @@ -467,7 +467,8 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port); void dhcp_init(struct daemon *daemon); void dhcp_packet(struct daemon *daemon, time_t now); -int address_available(struct dhcp_context *context, struct in_addr addr); +struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr); +struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr); int address_allocate(struct dhcp_context *context, struct daemon *daemon, struct in_addr *addrp, unsigned char *hwaddr); struct dhcp_config *find_config(struct dhcp_config *configs, diff --git a/src/network.c b/src/network.c index 71b7342..7815ae6 100644 --- a/src/network.c +++ b/src/network.c @@ -14,11 +14,33 @@ #include "dnsmasq.h" -static struct irec *add_iface(struct daemon *daemon, struct irec *list, char *name, union mysockaddr *addr) +static struct irec *add_iface(struct daemon *daemon, struct irec *list, + char *name, int is_loopback, union mysockaddr *addr) { struct irec *iface; struct iname *tmp; + /* If we are restricting the set of interfaces to use, make + sure that loopback interfaces are in that set. */ + if (daemon->if_names && is_loopback) + { + struct iname *lo; + for (lo = daemon->if_names; lo; lo = lo->next) + if (lo->name && strcmp(lo->name, name) == 0) + { + lo->isloop = 1; + break; + } + if (!lo) + { + lo = safe_malloc(sizeof(struct iname)); + lo->name = safe_string_alloc(name); + lo->isloop = lo->used = 1; + lo->next = daemon->if_names; + daemon->if_names = lo; + } + } + /* check blacklist */ if (daemon->if_except) for (tmp = daemon->if_except; tmp; tmp = tmp->next) @@ -67,6 +89,10 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list, char *na struct irec *enumerate_interfaces(struct daemon *daemon) { +#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6) + FILE *f; +#endif + union mysockaddr addr; struct irec *iface = NULL; char *buf, *ptr; struct ifreq *ifr = NULL; @@ -74,7 +100,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon) int lastlen = 0; int len = 20 * sizeof(struct ifreq); int fd = socket(PF_INET, SOCK_DGRAM, 0); - + if (fd == -1) die ("cannot create socket to enumerate interfaces: %s", NULL); @@ -99,9 +125,8 @@ struct irec *enumerate_interfaces(struct daemon *daemon) free(buf); } - for (ptr = buf; ptr < buf + len; ) + for (ptr = buf; ptr < buf + ifc.ifc_len; ) { - union mysockaddr addr; #ifdef HAVE_SOCKADDR_SA_LEN /* subsequent entries may not be aligned, so copy into an aligned buffer to avoid nasty complaints about @@ -141,76 +166,47 @@ struct irec *enumerate_interfaces(struct daemon *daemon) if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0) die("ioctl error getting interface flags: %m", NULL); - /* If we are restricting the set of interfaces to use, make - sure that loopback interfaces are in that set. */ - if (daemon->if_names && (ifr->ifr_flags & IFF_LOOPBACK)) - { - struct iname *lo; - for (lo = daemon->if_names; lo; lo = lo->next) - if (lo->name && strcmp(lo->name, ifr->ifr_name) == 0) - { - lo->isloop = 1; - break; - } - if (!lo) - { - lo = safe_malloc(sizeof(struct iname)); - lo->name = safe_string_alloc(ifr->ifr_name); - lo->isloop = lo->used = 1; - lo->next = daemon->if_names; - daemon->if_names = lo; - } - } - - iface = add_iface(daemon, iface, ifr->ifr_name, &addr); - -#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6) - /* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */ - /* This code snarfed from net-tools 1.60 and certainly linux specific, though - it shouldn't break on other Unices, and their SIOGIFCONF might work. */ - { - FILE *f = fopen(IP6INTERFACES, "r"); - int found = 0; - union mysockaddr addr6; - - if (f) - { - unsigned int plen, scope, flags, if_idx; - char devname[20], addrstring[32]; - - while (fscanf(f, "%32s %02x %02x %02x %02x %20s\n", - addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF) - { - if (strcmp(devname, ifr->ifr_name) == 0) - { - int i; - unsigned char *addr6p = (unsigned char *) &addr6.in6.sin6_addr; - memset(&addr6, 0, sizeof(addr6)); - addr6.sa.sa_family = AF_INET6; - for (i=0; i<16; i++) - { - unsigned int byte; - sscanf(addrstring+i+i, "%02x", &byte); - addr6p[i] = byte; - } - addr6.in6.sin6_port = htons(daemon->port); - addr6.in6.sin6_flowinfo = htonl(0); - addr6.in6.sin6_scope_id = htonl(scope); - - found = 1; - break; - } - } - - fclose(f); - } - - if (found) - iface = add_iface(daemon, iface, ifr->ifr_name, &addr6); - } -#endif /* LINUX */ + iface = add_iface(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr); } - + +#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6) + /* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */ + /* This code snarfed from net-tools 1.60 and certainly linux specific, though + it shouldn't break on other Unices, and their SIOGIFCONF might work. */ + if ((f = fopen(IP6INTERFACES, "r"))) + { + unsigned int plen, scope, flags, if_idx; + char devname[20], addrstring[32]; + + while (fscanf(f, "%32s %02x %02x %02x %02x %20s\n", + addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF) + { + int i; + struct ifreq sifr; + unsigned char *addr6p = (unsigned char *) &addr.in6.sin6_addr; + memset(&addr, 0, sizeof(addr)); + addr.sa.sa_family = AF_INET6; + for (i=0; i<16; i++) + { + unsigned int byte; + sscanf(addrstring+i+i, "%02x", &byte); + addr6p[i] = byte; + } + addr.in6.sin6_port = htons(daemon->port); + addr.in6.sin6_flowinfo = htonl(0); + addr.in6.sin6_scope_id = htonl(scope); + + strncpy(sifr.ifr_name, devname, IF_NAMESIZE); + if (ioctl(fd, SIOCGIFFLAGS, &sifr) < 0) + die("ioctl error getting interface flags: %m", NULL); + + iface = add_iface(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr); + + } + fclose(f); + } +#endif /* LINUX */ + if (buf) free(buf); #ifdef HAVE_SOCKADDR_SA_LEN @@ -361,7 +357,6 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port) struct listener *new = safe_malloc(sizeof(struct listener)); new->family = iface->addr.sa.sa_family; new->next = listeners; - listeners = new; if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 || (new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 || setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || @@ -383,9 +378,25 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port) #endif if (bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1 || - bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1 || - listen(new->tcpfd, 5) == -1) - die("failed to bind listening socket: %s", NULL); + bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1) + { +#ifdef HAVE_IPV6 + if (iface->addr.sa.sa_family == AF_INET6 && errno == ENODEV) + { + close(new->tcpfd); + close(new->fd); + free(new); + } + else +#endif + die("failed to bind listening socket: %s", NULL); + } + else + { + listeners = new; + if (listen(new->tcpfd, 5) == -1) + die("failed to listen on socket: %s", NULL); + } } return listeners; diff --git a/src/option.c b/src/option.c index 5c46f0d..1fc2b8d 100644 --- a/src/option.c +++ b/src/option.c @@ -478,8 +478,10 @@ struct daemon *read_opts (int argc, char **argv) } case 'a': - { + do { struct iname *new = safe_malloc(sizeof(struct iname)); + if ((comma = strchr(optarg, ','))) + *comma++ = 0; new->next = daemon->if_addrs; #ifdef HAVE_IPV6 if (inet_pton(AF_INET, optarg, &new->addr.in.sin_addr)) @@ -510,14 +512,14 @@ struct daemon *read_opts (int argc, char **argv) { option = '?'; /* error */ free(new); - new = NULL; + break; } - if (new) - daemon->if_addrs = new; - break; - } - + daemon->if_addrs = new; + optarg = comma; + } while (optarg); + break; + case 'S': case 'A': { diff --git a/src/rfc2131.c b/src/rfc2131.c index 0f9f30a..925aa82 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -94,7 +94,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam char *hostname = NULL; char *req_options = NULL; char *message = NULL; - unsigned int renewal_time, expires_time, def_time; + unsigned int time; struct dhcp_config *config; struct dhcp_netid *netid = NULL; struct in_addr addr, subnet_addr; @@ -171,13 +171,6 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam { context_tmp->current = context; context = context_tmp; - - /* start to build netid chain */ - if (context_tmp->netid.net) - { - context_tmp->netid.next = netid; - netid = &context_tmp->netid; - } } if (!context) @@ -205,6 +198,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam mess->yiaddr = config->addr; if (lease_find_by_addr(config->addr)) message = "address in use"; + context = narrow_context(context, config->addr); } else message = "no address configured"; @@ -217,6 +211,12 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam if (have_config(config, CONFIG_NAME)) hostname = config->hostname; + if (context->netid.net) + { + context->netid.next = netid; + netid = &context->netid; + } + if (have_config(config, CONFIG_NETID)) { config->netid.next = netid; @@ -348,27 +348,6 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam /* do we have a lease in store? */ lease = lease_find_by_client(clid, clid_len); - def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; - - if ((opt = option_find(mess, sz, OPTION_LEASE_TIME))) - { - unsigned int req_time = option_uint(opt, 4); - - if (def_time == 0xffffffff || - (req_time != 0xffffffff && req_time < def_time)) - expires_time = renewal_time = req_time; - else - expires_time = renewal_time = def_time; - } - else - { - renewal_time = def_time; - if (lease) - expires_time = (unsigned int)difftime(lease->expires, now); - else - expires_time = def_time; - } - if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS))) { int len = option_len(opt); @@ -455,16 +434,33 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam if (message) return 0; + context = narrow_context(context, mess->yiaddr); + if (context->netid.net) + { + context->netid.next = netid; + netid = &context->netid; + } + + time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; + if ((opt = option_find(mess, sz, OPTION_LEASE_TIME))) + { + unsigned int req_time = option_uint(opt, 4); + if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time)) + time = req_time; + } + else if (lease && lease->expires != 0) + time = (unsigned int)difftime(lease->expires, now); + mess->siaddr = iface_addr; bootp_option_put(mess, daemon->boot_config, netid); p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER); p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr)); - p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time); + p = option_put(p, end, OPTION_LEASE_TIME, 4, time); /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */ - if (expires_time != 0xffffffff) + if (time != 0xffffffff) { - p = option_put(p, end, OPTION_T1, 4, (expires_time/2)); - p = option_put(p, end, OPTION_T2, 4, ((expires_time * 7)/8)); + p = option_put(p, end, OPTION_T1, 4, (time/2)); + p = option_put(p, end, OPTION_T2, 4, (time*7)/8); } p = do_req_options(context, p, end, req_options, daemon, NULL, iface_addr, netid, subnet_addr); @@ -513,9 +509,6 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam /* desynchronise renewals */ fuzz = rand16(); - while (fuzz > (renewal_time/16)) - fuzz = fuzz/2; - mess->yiaddr = mess->ciaddr; } @@ -568,20 +561,37 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam { log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname); + context = narrow_context(context, mess->yiaddr); + if (context->netid.net) + { + context->netid.next = netid; + netid = &context->netid; + } + + time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; + if ((opt = option_find(mess, sz, OPTION_LEASE_TIME))) + { + unsigned int req_time = option_uint(opt, 4); + if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time)) + time = req_time; + } + lease_set_hwaddr(lease, mess->chaddr); if (hostname) lease_set_hostname(lease, hostname, daemon->domain_suffix); - lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time); + lease_set_expires(lease, time == 0xffffffff ? 0 : now + (time_t)time); mess->siaddr = iface_addr; bootp_option_put(mess, daemon->boot_config, netid); p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr)); - p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time); - if (renewal_time != 0xffffffff) + p = option_put(p, end, OPTION_LEASE_TIME, 4, time); + if (time != 0xffffffff) { - p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz); - p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz); + while (fuzz > (time/16)) + fuzz = fuzz/2; + p = option_put(p, end, OPTION_T1, 4, (time/2) - fuzz); + p = option_put(p, end, OPTION_T2, 4, ((time * 7)/8) - fuzz); } p = do_req_options(context, p, end, req_options, daemon, hostname, iface_addr, netid, subnet_addr); @@ -599,6 +609,13 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam if (message || mess->ciaddr.s_addr == 0) return 0; + context = narrow_context(context, mess->ciaddr); + if (context->netid.net) + { + context->netid.next = netid; + netid = &context->netid; + } + mess->siaddr = iface_addr; bootp_option_put(mess, daemon->boot_config, netid); p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);