diff options
-rw-r--r-- | CHANGELOG | 18 | ||||
-rw-r--r-- | FAQ | 19 | ||||
-rw-r--r-- | dnsmasq-rh.spec | 2 | ||||
-rw-r--r-- | dnsmasq-suse.spec | 2 | ||||
-rw-r--r-- | src/config.h | 2 | ||||
-rw-r--r-- | src/dhcp.c | 38 | ||||
-rw-r--r-- | src/dnsmasq.h | 2 | ||||
-rw-r--r-- | src/network.c | 4 | ||||
-rw-r--r-- | src/rfc2131.c | 100 |
9 files changed, 121 insertions, 66 deletions
@@ -1307,8 +1307,22 @@ version 2.18 Enable long command line options on FreeBSD when the C library supports them. - - +version 2.19 + Tweaked the Linux-only interface discovery code to cope + with interface-indexes larger than 8 bits in + /proc/net/if_inet6. This only affects Linux, obviously. + Thanks to Richard Atterer for the bug report. + + Check for under-length option fields in DHCP packets, a + zero length client-id, in particluar, could seriously + confuse dnsmasq 'till now. Thanks to Will Murname for help + with that. + + If a DHCP-allocated address has an associated name in + /etc/hosts, and the client does not provide a hostname + parameter and there is no hostname in a matching dhcp-host + option, send the /etc/hosts name as the hostname in + the DHCP lease. Thanks to Will Murname for the suggestion. @@ -115,7 +115,7 @@ A: Resolver code sometime does strange things when given names without --expand-hosts and --domain-suffix options. Q: Can I get dnsmasq to save the contents of its cache to disk when - I shut my machine down and re-load when it starts again. + I shut my machine down and re-load when it starts again? A: No, that facility is not provided. Very few names in the DNS have their time-to-live set for longer than a few hours so most of the @@ -299,7 +299,22 @@ A: Because when a Gentoo box shuts down, it releases its lease with dnsmasq ignores it until is times out and restarts the process. To fix this, set the dhcp-authoritative flag in dnsmasq. - +Q: My laptop has two network interfaces, a wired one and a wireless + one. I never use both interfaces at the same time, and I'd like the + same IP and configuration to be used irrespcetive of which + interface is in use. How can I do that. + +A: By default, the identity of a machine is determined by using the + MAC address, which is associated with interface hardware. Once an + IP is bound to the MAC address of one interface, it cannot be + associated with another MAC address until after the DHCP lease + expires. The solution to this is to use a client-id as the machine + identity rather than the MAC address. If you arrange for the same + client-id to sent when either interface is in use, the DHCP server + will recognise the same machine, and use the same address. The + method for setting the client-id varies with DHCP client software, + dhcpcd uses the "-I" flag. Windows uses a registry setting, + see http://www.jsiinc.com/SUBF/TIP2800/rh2845.htm diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec index 61ddb17..87f3d59 100644 --- a/dnsmasq-rh.spec +++ b/dnsmasq-rh.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.18 +Version: 2.19 Release: 1 Copyright: GPL Group: System Environment/Daemons diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec index 9dcb709..8fec85a 100644 --- a/dnsmasq-suse.spec +++ b/dnsmasq-suse.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.18 +Version: 2.19 Release: 1 Copyright: GPL Group: Productivity/Networking/DNS/Servers diff --git a/src/config.h b/src/config.h index 0426447..03747a5 100644 --- a/src/config.h +++ b/src/config.h @@ -12,7 +12,7 @@ /* Author's email: simon@thekelleys.org.uk */ -#define VERSION "2.18" +#define VERSION "2.19" #define FTABSIZ 150 /* max number of outstanding requests */ #define MAX_PROCS 20 /* max no children for TCP requests */ @@ -656,3 +656,41 @@ void dhcp_update_configs(struct dhcp_config *configs) } } +/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts + for this address. If it has a domain part, that must match the set domain and + it gets stripped. */ +char *host_from_dns(struct daemon *daemon, struct in_addr addr) +{ + struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4); + char *hostname = NULL; + + if (lookup && (lookup->flags & F_HOSTS)) + { + hostname = daemon->dhcp_buff; + hostname[256] = 0; + strncpy(hostname, cache_get_name(lookup), 256); + hostname = strip_hostname(daemon, hostname); + } + + return hostname; +} + +char *strip_hostname(struct daemon *daemon, char *hostname) +{ + char *dot = strchr(hostname, '.'); + if (dot) + { + if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix)) + { + syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname); + hostname = NULL; + } + else + { + *dot = 0; /* truncate */ + if (strlen(hostname) == 0) + hostname = NULL; /* nothing left */ + } + } + return hostname; +} diff --git a/src/dnsmasq.h b/src/dnsmasq.h index dd61c41..7f163a2 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -478,6 +478,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs, void dhcp_update_configs(struct dhcp_config *configs); void dhcp_read_ethers(struct daemon *daemon); struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr); +char *strip_hostname(struct daemon *daemon, char *hostname); +char *host_from_dns(struct daemon *daemon, struct in_addr addr); /* lease.c */ void lease_update_file(int force, time_t now); diff --git a/src/network.c b/src/network.c index 7815ae6..5250371 100644 --- a/src/network.c +++ b/src/network.c @@ -178,7 +178,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon) unsigned int plen, scope, flags, if_idx; char devname[20], addrstring[32]; - while (fscanf(f, "%32s %02x %02x %02x %02x %20s\n", + while (fscanf(f, "%32s %x %x %x %x %20s\n", addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF) { int i; @@ -199,9 +199,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon) 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); } diff --git a/src/rfc2131.c b/src/rfc2131.c index 925aa82..0c33324 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -59,13 +59,13 @@ static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dh static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string); static void bootp_option_put(struct dhcp_packet *mess, struct dhcp_boot *boot_opts, struct dhcp_netid *netids); -static int option_len(unsigned char *opt); +static unsigned int option_len(unsigned char *opt); static void *option_ptr(unsigned char *opt); static struct in_addr option_addr(unsigned char *opt); static unsigned int option_uint(unsigned char *opt, int size); static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string); static int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool); -static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type); +static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, unsigned int minsize); static unsigned char *do_req_options(struct dhcp_context *context, unsigned char *p, unsigned char *end, unsigned char *req_options, @@ -126,7 +126,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam return 0; /* check for DHCP rather than BOOTP */ - if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE))) + if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1))) { mess_type = option_uint(opt, 1); @@ -136,18 +136,18 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam /* Some buggy clients set ciaddr when they shouldn't, so clear that here since it can affect the context-determination code. */ - if ((option_find(mess, sz, OPTION_REQUESTED_IP) || mess_type == DHCPDISCOVER)) + if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER)) mess->ciaddr.s_addr = 0; /* Check for RFC3011 subnet selector */ - if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT))) + if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ))) subnet_addr = option_addr(opt); /* If there is no client identifier option, use the hardware address */ - if ((opt = option_find(mess, sz, OPTION_CLIENT_ID))) + if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1))) { - clid = option_ptr(opt); clid_len = option_len(opt); + clid = option_ptr(opt); } else clid = mess->chaddr; @@ -253,9 +253,9 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam if (have_config(config, CONFIG_NAME)) hostname = config->hostname; - else if ((opt = option_find(mess, sz, OPTION_HOSTNAME))) + else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1))) { - int len = option_len(opt); + unsigned int len = option_len(opt); hostname = daemon->dhcp_buff; memcpy(hostname, option_ptr(opt), len); /* May not be zero terminated */ @@ -263,33 +263,14 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam /* ensure there are no strange chars in there */ if (!canonicalise(hostname)) hostname = NULL; - else + else if ((hostname = strip_hostname(daemon, hostname)) && !config) { - char *dot = strchr(hostname, '.'); - if (dot) - { - if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix)) - { - syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname); - hostname = NULL; - } - else - { - *dot = 0; /* truncate */ - if (strlen(hostname) == 0) - hostname = NULL; /* nothing left */ - } - } - /* Search again now we have a hostname. Only accept configs without CLID and HWADDR here, (they won't match) to avoid impersonation by name. */ - if (!config) - { - struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, hostname); - if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR)) - config = new; - } + struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, hostname); + if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR)) + config = new; } } @@ -307,10 +288,10 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam assume that the option is a single string and non RFC3004 compliant and just do the substring match. dhclient provides these broken options. */ - if ((opt = option_find(mess, sz, OPTION_USER_CLASS))) + if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1))) { unsigned char *ucp = option_ptr(opt); - int tmp, j; + unsigned int tmp, j; for (j = 0; j < option_len(opt); j += ucp[j] + 1); if (j == option_len(opt)) for (j = 0; j < option_len(opt); j = tmp) @@ -321,9 +302,9 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam } for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) - if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS))) + if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS, 1))) { - int i; + unsigned int i; for (i = 0; i <= (option_len(opt) - vendor->len); i++) if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0) { @@ -348,23 +329,22 @@ 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); - if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS))) + if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0))) { - int len = option_len(opt); req_options = daemon->dhcp_buff2; - memcpy(req_options, option_ptr(opt), len); - req_options[len] = OPTION_END; + memcpy(req_options, option_ptr(opt), option_len(opt)); + req_options[option_len(opt)] = OPTION_END; } switch (mess_type) { case DHCPDECLINE: - if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) || + if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) || (iface_addr.s_addr != option_addr(opt).s_addr)) return 0; /* sanitise any message. Paranoid? Moi? */ - if ((opt = option_find(mess, sz, OPTION_MESSAGE))) + if ((opt = option_find(mess, sz, OPTION_MESSAGE, 1))) { char *p = option_ptr(opt), *q = daemon->dhcp_buff; int i; @@ -379,7 +359,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam message = daemon->dhcp_buff; } - if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP))) + if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) return 0; log_packet("DECLINE", option_ptr(opt), mess->chaddr, iface_name, message); @@ -401,7 +381,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam return 0; case DHCPRELEASE: - if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) || + if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) || (iface_addr.s_addr != option_addr(opt).s_addr)) return 0; @@ -415,7 +395,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam return 0; case DHCPDISCOVER: - if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP))) + if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) addr = option_addr(opt); if (ignore || have_config(config, CONFIG_DISABLE)) message = "ignored"; @@ -442,7 +422,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam } time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; - if ((opt = option_find(mess, sz, OPTION_LEASE_TIME))) + if ((opt = option_find(mess, sz, OPTION_LEASE_TIME, 4))) { unsigned int req_time = option_uint(opt, 4); if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time)) @@ -472,12 +452,12 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam case DHCPREQUEST: if (ignore || have_config(config, CONFIG_DISABLE)) return 0; - if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP))) + if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) { /* SELECTING or INIT_REBOOT */ mess->yiaddr = option_addr(opt); - if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER))) + if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ))) { /* SELECTING */ if (iface_addr.s_addr != option_addr(opt).s_addr) @@ -569,7 +549,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam } time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; - if ((opt = option_find(mess, sz, OPTION_LEASE_TIME))) + if ((opt = option_find(mess, sz, OPTION_LEASE_TIME, 4))) { unsigned int req_time = option_uint(opt, 4); if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time)) @@ -577,6 +557,8 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam } lease_set_hwaddr(lease, mess->chaddr); + if (!hostname) + hostname = host_from_dns(daemon, mess->yiaddr); if (hostname) lease_set_hostname(lease, hostname, daemon->domain_suffix); lease_set_expires(lease, time == 0xffffffff ? 0 : now + (time_t)time); @@ -620,6 +602,8 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam 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)); + if (!hostname) + hostname = host_from_dns(daemon, mess->yiaddr); p = do_req_options(context, p, end, req_options, daemon, hostname, iface_addr, netid, subnet_addr); p = option_end(p, end, mess); @@ -644,7 +628,7 @@ static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, string ? string : ""); } -static int option_len(unsigned char *opt) +static unsigned int option_len(unsigned char *opt) { return opt[1]; } @@ -756,13 +740,13 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt while (*p != OPTION_END) { - if (end && (p >= end)) + if (p >= end) return 0; /* malformed packet */ else if (*p == OPTION_PAD) p++; else if (*p == OPTION_OVERLOAD) { - if (end && (p >= end - 3)) + if (p >= end - 3) return 0; /* malformed packet */ if (overload) *overload = *(p+2); @@ -771,10 +755,10 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt else { int opt_len;; - if (end && (p >= end - 2)) + if (p >= end - 2) return 0; /* malformed packet */ opt_len = option_len(p); - if (end && (p >= end - (2 + opt_len))) + if (p >= end - (2 + opt_len)) return 0; /* malformed packet */ if (*p == opt) return p; @@ -785,7 +769,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt return NULL; } -static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type) +static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, unsigned int minsize) { int overload = 0; unsigned char *ret; @@ -798,7 +782,11 @@ static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_ty if (!ret && (overload & 2)) ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload); - + + /* Check the option field is big enough */ + if (ret && (option_len(ret) < minsize)) + ret = NULL; + return ret; } |