From a222641cb06189d287bf2e00a3f1f26019b80ac7 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 13 May 2004 20:27:08 +0100 Subject: [PATCH] import of dnsmasq-2.8.tar.gz --- CHANGELOG | 41 ++++++++++++++ dnsmasq-rh.spec | 2 +- dnsmasq-suse.spec | 2 +- dnsmasq.8 | 23 +++++++- dnsmasq.conf.example | 8 +++ src/config.h | 2 +- src/dnsmasq.h | 16 ++++-- src/forward.c | 2 +- src/lease.c | 6 +- src/option.c | 78 +++++++++++++++++--------- src/rfc2131.c | 129 ++++++++++++++++++++++++++++++++----------- src/util.c | 26 +++++++-- 12 files changed, 260 insertions(+), 75 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 27edc33..90104ed 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -962,3 +962,44 @@ release 2.7 ancient IBM LANMAN DHCP clients. Thanks to Jim Louvau for help with this. +release 2.8 + Pad DHCP packets to a minimum size of 300 bytes. This + fixes interoperability problems with the Linux in-kernel + DHCP/BOOTP client. Thanks to Richard Musil for + diagnosing this and supplying a patch. + + Fixed option-parsing bug and potential memory leak. Patch + from Richard Musil. + + Improved vendor class configuration and added user class + configuration. Specifically: (1) options are matched on + the netids from dhcp-range, dhcp-host, vendor class and + user class(es). Multiple net-ids are allowed and options + are searched on them all. (2) matches agains vendor class + and user class are now on a substring, if the given + string is a substring of the vendor/user class, then a + match occurs. Thanks again to Richard Musil for prompting + this. + + Make "#" match any domain on --address and --server + flags. --address=/#/1.2.3.4 will return 1.2.3.4 for _any_ + domain not otherwise matched. Of course + --server=/#/1.2.3.4 is exactly equivalent to + --server=1.2.3.4. Special request from Josh Howlett. + + Fixed a nasty bug which would cause dnsmasq to lose track + of leases for hosts which had a --dhcp-host flag without + a name specification. The mechanism for this was that + the hostname could get erroneously set as a zero-length + string and then written to the leases file as a + mal-formed line. Restarting dnsmasq would then lose the lease. + Alex Hermann's work helped chase down this problem. + + Add checks against DHCP clients which return zero-length + hostnames. This avoids the potential lease-loss problems + reffered to above. Also, if a client sends a hostname when + it creates a lease but subsequently sends no or a + zero-length hostname whilst renewing, continue to use the + existing hostname, don't wipe it out. + + Tweaked option parsing to flag some parameter errors. diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec index 59982ae..2ac533e 100644 --- a/dnsmasq-rh.spec +++ b/dnsmasq-rh.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.7 +Version: 2.8 Release: 1 Copyright: GPL Group: System Environment/Daemons diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec index 9268a46..8803a10 100644 --- a/dnsmasq-suse.spec +++ b/dnsmasq-suse.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.7 +Version: 2.8 Release: 1 Copyright: GPL Group: Productivity/Networking/DNS/Servers diff --git a/dnsmasq.8 b/dnsmasq.8 index 92d0dd1..5c7be0d 100644 --- a/dnsmasq.8 +++ b/dnsmasq.8 @@ -223,7 +223,12 @@ with the specified IP address which may be IPv4 or IPv6. To give both IPv4 and IPv6 addresses for a domain, use repeated -A flags. Note that /etc/hosts and DHCP leases override this for individual names. A common use of this is to redirect the entire doubleclick.net -domain to some friendly local web server to avoid banner ads. +domain to some friendly local web server to avoid banner ads. The +domain specification works in the same was as for --server, with the +additional facility that /#/ matches any domain. Thus +--address=/#/1.2.3.4 will always return 1.2.3.4 for any query not +answered from /etc/hosts or DHCP and not sent to an upstream +nameserver by a more specific --server directive. .TP .B \-m, --mx-host= Return an MX record named pointing to the host specified in the --mx-target switch @@ -358,12 +363,24 @@ of this flag. .TP .B \-U, --dhcp-vendorclass=, Map from a vendor-class string to a network id. Most DHCP clients provide a -"vendor class" which represents, in some sense, the type of host. This options +"vendor class" which represents, in some sense, the type of host. This option maps vendor classes to network ids, so that DHCP options may be selectively delivered to different classes of hosts. For example .B dhcp-vendorclass=printers,Hewlett-Packard JetDirect will allow options to be set only for HP printers like so: -.B --dhcp-option=printers,3,192.168.4.4 +.B --dhcp-option=printers,3,192.168.4.4 +The vendor-class string is +substring matched against the vendor-class supplied by the client, to +allow fuzzy matching. +.TP +.B \-j, --dhcp-userclass=, +Map from a user-class string to a network id (with substring +matching, like vendor classes). Most DHCP clients provide a +"user class" which is configurable. This option +maps user classes to network ids, so that DHCP options may be selectively delivered +to different classes of hosts. It is possible, for instance to use +this to set a different printer server for hosts in the class +"accounts" than for hosts in the class "engineering". .TP .B \-M, --dhcp-boot=,[[,]] Set BOOTP options to be returned by the DHCP server. These are needed diff --git a/dnsmasq.conf.example b/dnsmasq.conf.example index f4d5969..2bd8b51 100644 --- a/dnsmasq.conf.example +++ b/dnsmasq.conf.example @@ -171,6 +171,14 @@ filterwin2k # the machine with ethernet address 11:22:33:44:55:66 #dhcp-host=11:22:33:44:55:66,net:red +# Send extra options which are tagged as "red" to any machine whose +# DHCP vendorclass string includes the substring "Linux" +#dhcp-vendorclass=red,Linux + +# Send extra options which are tagged as "red" to any machine one +# of whose DHCP userclass strings includes the substring "accounts" +#dhcp-userclass=red,accounts + # If this line is uncommented, dnsmasq will read /etc/ethers and act # on the ethernet-address/IP pairs found there just as if they had # been given as --dhcp-host options. Useful if you keep diff --git a/src/config.h b/src/config.h index b6bf746..c7a435c 100644 --- a/src/config.h +++ b/src/config.h @@ -12,7 +12,7 @@ /* Author's email: simon@thekelleys.org.uk */ -#define VERSION "2.7" +#define VERSION "2.8" #define FTABSIZ 150 /* max number of outstanding requests */ #define TIMEOUT 20 /* drop queries after TIMEOUT seconds */ diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 0021d4e..2ffb64e 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -237,12 +237,18 @@ struct dhcp_lease { struct dhcp_lease *next; }; +struct dhcp_netid { + char *net; + struct dhcp_netid *next; +}; + struct dhcp_config { unsigned int flags; int clid_len; /* length of client identifier */ unsigned char *clid; /* clientid */ unsigned char hwaddr[ETHER_ADDR_LEN]; - char *hostname, *netid; + char *hostname; + struct dhcp_netid netid; struct in_addr addr; unsigned int lease_time; struct dhcp_config *next; @@ -265,8 +271,9 @@ struct dhcp_opt { }; struct dhcp_vendor { - int len; - char *data, *net; + int len, is_vendor, used; + char *data; + struct dhcp_netid netid; struct dhcp_vendor *next; }; @@ -274,7 +281,7 @@ struct dhcp_context { unsigned int lease_time; struct in_addr netmask, broadcast; struct in_addr start, end; /* range of available addresses */ - char *netid; + struct dhcp_netid netid; struct dhcp_context *next; }; @@ -340,6 +347,7 @@ int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name, unsigned short rand16(void); int legal_char(char c); int canonicalise(char *s); +int atoi_check(char *a, int *res); void die(char *message, char *arg1); void complain(char *message, char *arg1); void *safe_malloc(int size); diff --git a/src/forward.c b/src/forward.c index 1f4ad0f..fb4568c 100644 --- a/src/forward.c +++ b/src/forward.c @@ -177,7 +177,7 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr, unsigned int domainlen = strlen(serv->domain); if (namelen >= domainlen && hostname_isequal(dnamebuff + namelen - domainlen, serv->domain) && - domainlen > matchlen) + domainlen >= matchlen) { if (serv->flags & SERV_LITERAL_ADDRESS) { /* flags gets set if server is in fact an answer */ diff --git a/src/lease.c b/src/lease.c index 450851f..59afe5f 100644 --- a/src/lease.c +++ b/src/lease.c @@ -109,7 +109,7 @@ void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain) for (lease = leases; lease; lease = lease->next) if ((config = find_config(dhcp_configs, NULL, lease->clid, lease->clid_len, lease->hwaddr, NULL)) && - (config->hostname)) + (config->flags & CONFIG_NAME)) lease_set_hostname(lease, config->hostname, domain); } @@ -145,7 +145,7 @@ void lease_update_file(int force, time_t now) expires, lease->hwaddr[0], lease->hwaddr[1], lease->hwaddr[2], lease->hwaddr[3], lease->hwaddr[4], lease->hwaddr[5], inet_ntoa(lease->addr), - lease->hostname ? lease->hostname : "*"); + lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*"); if (lease->clid_len) { @@ -311,7 +311,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix) struct dhcp_lease *lease_tmp; char *new_name = NULL, *new_fqdn = NULL; - if (lease->hostname && name && strcmp(lease->hostname, name) == 0) + if (lease->hostname && name && hostname_isequal(lease->hostname, name)) return; if (!name && !lease->hostname) diff --git a/src/option.c b/src/option.c index b1fef2f..077c384 100644 --- a/src/option.c +++ b/src/option.c @@ -21,7 +21,7 @@ struct myoption { int val; }; -#define OPTSTRING "ZDNLERzowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:" +#define OPTSTRING "ZDNLERzowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:" static struct myoption opts[] = { {"version", 0, 0, 'v'}, @@ -71,6 +71,7 @@ static struct myoption opts[] = { {"read-ethers", 0, 0, 'Z' }, {"alias", 1, 0, 'V' }, {"dhcp-vendorclass", 1, 0, 'U'}, + {"dhcp-userclass", 1, 0, 'j'}, {0, 0, 0, 0} }; @@ -121,6 +122,7 @@ static char *usage = "-H, --addn-hosts=path Specify a hosts file to be read in addition to " HOSTSFILE ".\n" "-i, --interface=interface Specify interface(s) to listen on.\n" "-I, --except-interface=int Specify interface(s) NOT to listen on.\n" +"-j, --dhcp-userclass=, Map DHCP user class to option set.\n" "-l, --dhcp-leasefile=path Specify where to store DHCP leases (defaults to " LEASEFILE ").\n" "-L, --localmx Return MX records for local hosts.\n" "-m, --mx-host=host_name Specify the MX name to reply to.\n" @@ -474,11 +476,15 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso optarg++; while ((end = strchr(optarg, '/'))) { - char *domain; + char *domain = NULL; *end = 0; - if (!canonicalise(optarg)) + /* # matches everything and becomes a zero length domain string */ + if (strcmp(optarg, "#") == 0) + domain = ""; + else if (!canonicalise(optarg)) option = '?'; - domain = safe_string_alloc(optarg); /* NULL if strlen is zero */ + else + domain = safe_string_alloc(optarg); /* NULL if strlen is zero */ serv = safe_malloc(sizeof(struct server)); serv->next = newlist; newlist = serv; @@ -527,14 +533,16 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso if ((portno = strchr(source+1, '#'))) { *portno = 0; - source_port = atoi(portno+1); + if (!atoi_check(portno+1, &source_port)) + option = '?'; } } if ((portno = strchr(optarg, '#'))) /* is there a port no. */ { *portno = 0; - serv_port = atoi(portno+1); + if (!atoi_check(portno+1, &serv_port)) + option = '?'; } #ifdef HAVE_IPV6 @@ -614,32 +622,46 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso case 'c': { - int size = atoi(optarg); - /* zero is OK, and means no caching. */ - - if (size < 0) - size = 0; - else if (size > 10000) - size = 10000; - - *cachesize = size; + int size; + if (!atoi_check(optarg, &size)) + option = '?'; + else + { + /* zero is OK, and means no caching. */ + + if (size < 0) + size = 0; + else if (size > 10000) + size = 10000; + + *cachesize = size; + } break; } case 'p': - *port = atoi(optarg); + if (!atoi_check(optarg, port)) + option = '?'; break; case 'Q': - *query_port = atoi(optarg); + if (!atoi_check(optarg, query_port)) + option = '?'; break; case 'T': - *local_ttl = (unsigned long)atoi(optarg); - break; + { + int ttl; + if (!atoi_check(optarg, &ttl)) + option = '?'; + else + *local_ttl = (unsigned long)ttl; + break; + } case 'X': - *dhcp_max = atoi(optarg); + if (!atoi_check(optarg, dhcp_max)) + option = '?'; break; case 'F': @@ -652,7 +674,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso new->lease_time = DEFLEASE; new->netmask.s_addr = 0; new->broadcast.s_addr = 0; - new->netid = NULL; + new->netid.net = NULL; for (cp = optarg; *cp; cp++) @@ -662,7 +684,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso if (*cp != ',' && (comma = strchr(optarg, ','))) { *comma = 0; - new->netid = safe_string_alloc(optarg); + new->netid.net = safe_string_alloc(optarg); a[0] = comma + 1; } else @@ -807,7 +829,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso arg[3] == ':') { new->flags |= CONFIG_NETID; - new->netid = safe_string_alloc(arg+4); + new->netid.net = safe_string_alloc(arg+4); } else if (sscanf(a[j], "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5) == 6) @@ -888,7 +910,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso if (new->flags & CONFIG_CLID) free(new->clid); if (new->flags & CONFIG_NETID) - free(new->netid); + free(new->netid.net); free(new); } else @@ -931,6 +953,8 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso if ((new->opt = atoi(optarg)) == 0) { option = '?'; + if (new->netid) + free(new->netid); free(new); break; } @@ -960,7 +984,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso { is_dec = is_addr = 0; if (!((*cp >='A' && *cp <= 'F') || - (*cp >='a' && *cp <= 'F'))) + (*cp >='a' && *cp <= 'f'))) is_hex = 0; } @@ -1064,6 +1088,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso } case 'U': + case 'j': { char *comma; @@ -1073,10 +1098,11 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso { struct dhcp_vendor *new = safe_malloc(sizeof(struct dhcp_vendor)); *comma = 0; - new->net = safe_string_alloc(optarg); + new->netid.net = safe_string_alloc(optarg); new->len = strlen(comma+1); new->data = safe_malloc(new->len); memcpy(new->data, comma+1, new->len); + new->is_vendor = (option == 'U'); new->next = *dhcp_vendors; *dhcp_vendors = new; } diff --git a/src/rfc2131.c b/src/rfc2131.c index dcba4b3..3ceef7d 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -18,6 +18,10 @@ #define BOOTREPLY 2 #define DHCP_COOKIE 0x63825363 +/* The Linux in-kernel DHCP client silently ignores any packet + smaller than this. Sigh........... */ +#define MIN_PACKETSZ 300 + #define OPTION_PAD 0 #define OPTION_NETMASK 1 #define OPTION_ROUTER 3 @@ -37,6 +41,7 @@ #define OPTION_T2 59 #define OPTION_VENDOR_ID 60 #define OPTION_CLIENT_ID 61 +#define OPTION_USER_CLASS 77 #define OPTION_END 255 #define DHCPDISCOVER 1 @@ -49,6 +54,7 @@ #define DHCPINFORM 8 static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val); +static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start); 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, char *filename, char *sname); static int option_len(unsigned char *opt); @@ -64,7 +70,7 @@ static unsigned char *do_req_options(struct dhcp_context *context, char *domainname, char *hostname, struct in_addr router, struct in_addr iface_addr, - int iface_mtu, char *netid); + int iface_mtu, struct dhcp_netid *netid); static int have_config(struct dhcp_config *config, unsigned int mask) { @@ -84,6 +90,7 @@ int dhcp_reply(struct dhcp_context *context, { unsigned char *opt, *clid; struct dhcp_lease *lease; + struct dhcp_vendor *vendor; int clid_len; struct dhcp_packet *mess = &rawpacket->data; unsigned char *p = mess->options; @@ -94,7 +101,7 @@ int dhcp_reply(struct dhcp_context *context, char *message = NULL; unsigned int renewal_time, expires_time, def_time; struct dhcp_config *config; - char *netid; + struct dhcp_netid *netid = NULL; struct in_addr addr; unsigned short fuzz = 0; @@ -168,7 +175,11 @@ int dhcp_reply(struct dhcp_context *context, hostname = NULL; } else - *dot = 0; /* truncate */ + { + *dot = 0; /* truncate */ + if (strlen(hostname) == 0) + hostname = NULL; /* nothing left */ + } } /* search again now we have a hostname */ config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname); @@ -177,18 +188,58 @@ int dhcp_reply(struct dhcp_context *context, def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; - netid = context->netid; + if (context->netid.net) + { + context->netid.next = netid; + netid = &context->netid; + } + if (have_config(config, CONFIG_NETID)) - netid = config->netid; - else if ((opt = option_find(mess, sz, OPTION_VENDOR_ID))) { - struct dhcp_vendor *vendor; - for (vendor = vendors; vendor; vendor = vendor->next) - if (vendor->len == option_len(opt) && - memcmp(vendor->data, option_ptr(opt), vendor->len) == 0) - netid = vendor->net; + config->netid.next = netid; + netid = &config->netid; } + + /* Theres a chance that carefully chosen data could match the same + vendor/user option twice and make a loop in the netid chain. */ + for (vendor = vendors; vendor; vendor = vendor->next) + vendor->used = 0; + + if ((opt = option_find(mess, sz, OPTION_VENDOR_ID))) + for (vendor = vendors; vendor; vendor = vendor->next) + if (vendor->is_vendor && !vendor->used) + { + int i; + for (i = 0; i <= (option_len(opt) - vendor->len); i++) + if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0) + { + vendor->used = 1; + vendor->netid.next = netid; + netid = &vendor->netid; + break; + } + } + if ((opt = option_find(mess, sz, OPTION_USER_CLASS))) + { + unsigned char *ucp = option_ptr(opt); + int j; + for (j = 0; j < option_len(opt); j += ucp[j] + 1) + for (vendor = vendors; vendor; vendor = vendor->next) + if (!vendor->is_vendor && !vendor->used) + { + int i; + for (i = 0; i <= (ucp[j] - vendor->len); i++) + if (memcmp(vendor->data, &ucp[j+i+1], vendor->len) == 0) + { + vendor->used = 1; + vendor->netid.next = netid; + netid = &vendor->netid; + break; + } + } + } + /* Can have setting to ignore the client ID for a particular MAC address or hostname */ if (have_config(config, CONFIG_NOCLID)) { @@ -316,7 +367,7 @@ int dhcp_reply(struct dhcp_context *context, } p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix, NULL, router, iface_addr, iface_mtu, netid); - p = option_put(p, end, OPTION_END, 0, 0); + p = option_end(p, end, mess); log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL); return p - (unsigned char *)mess; @@ -385,7 +436,7 @@ int dhcp_reply(struct dhcp_context *context, bootp_option_put(mess, NULL, NULL); p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK); p = option_put_string(p, end, OPTION_MESSAGE, message); - p = option_put(p, end, OPTION_END, 0, 0); + p = option_end(p, end, mess); mess->flags |= htons(0x8000); /* broadcast */ return p - (unsigned char *)mess; } @@ -393,7 +444,8 @@ int dhcp_reply(struct dhcp_context *context, log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname); lease_set_hwaddr(lease, mess->chaddr); - lease_set_hostname(lease, hostname, domain_suffix); + if (hostname) + lease_set_hostname(lease, hostname, domain_suffix); lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time); bootp_option_put(mess, dhcp_file, dhcp_sname); @@ -408,7 +460,7 @@ int dhcp_reply(struct dhcp_context *context, } p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix, hostname, router, iface_addr, iface_mtu, netid); - p = option_put(p, end, OPTION_END, 0, 0); + p = option_end(p, end, mess); return p - (unsigned char *)mess; case DHCPINFORM: @@ -424,7 +476,7 @@ int dhcp_reply(struct dhcp_context *context, p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr)); p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix, hostname, router, iface_addr, iface_mtu, netid); - p = option_put(p, end, OPTION_END, 0, 0); + p = option_end(p, end, mess); log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname); return p - (unsigned char *)mess; @@ -494,20 +546,26 @@ static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int i; /* always keep one octet space for the END option. */ - if ((opt == OPTION_END) || (p + len + 3 < end)) + if (p + len + 3 < end) { *(p++) = opt; - if (opt != OPTION_END) - { - *(p++) = len; - - for (i = 0; i < len; i++) - *(p++) = val >> (8 * (len - (i + 1))); - } + *(p++) = len; + + for (i = 0; i < len; i++) + *(p++) = val >> (8 * (len - (i + 1))); } return p; } +static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start) +{ + *(p++) = OPTION_END; + while ((p < end) && (p - ((unsigned char *)start) < MIN_PACKETSZ)) + *p++ = 0; + + return p; +} + static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string) { if (p + strlen(string) + 3 < end) @@ -587,16 +645,25 @@ static int in_list(unsigned char *list, int opt) return 0; } -static struct dhcp_opt *option_find2(char *netid, struct dhcp_opt *opts, int opt) +static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt) { struct dhcp_opt *tmp; + struct dhcp_netid *tmp1; for (tmp = opts; tmp; tmp = tmp->next) - if (tmp->opt == opt && - ((!netid && !tmp->netid) || - (netid && tmp->netid && strcmp(tmp->netid, netid) == 0))) - return tmp; - + if (tmp->opt == opt) + { + if (netid) + { + if (tmp->netid) + for (tmp1 = netid; tmp1; tmp1 = tmp1->next) + if (strcmp(tmp->netid, tmp1->net) == 0) + return tmp; + } + else if (!tmp->netid) + return tmp; + } + return netid ? option_find2(NULL, opts, opt) : NULL; } @@ -607,7 +674,7 @@ static unsigned char *do_req_options(struct dhcp_context *context, char *domainname, char *hostname, struct in_addr router, struct in_addr iface_addr, - int iface_mtu, char *netid) + int iface_mtu, struct dhcp_netid *netid) { struct dhcp_opt *opt; diff --git a/src/util.c b/src/util.c index a6b5bfe..a9e8377 100644 --- a/src/util.c +++ b/src/util.c @@ -11,7 +11,7 @@ */ -/* Code in this file contributed by Rob Funk. */ +/* Some code in this file contributed by Rob Funk. */ #include "dnsmasq.h" @@ -85,6 +85,18 @@ unsigned short rand16(void) return( (unsigned short) (rand() >> 15) ); } +int atoi_check(char *a, int *res) +{ + char *p; + + for (p = a; *p; p++) + if (*p < '0' || *p > '9') + return 0; + + *res = atoi(a); + return 1; +} + int legal_char(char c) { /* check for legal char a-z A-Z 0-9 - @@ -100,12 +112,18 @@ int legal_char(char c) int canonicalise(char *s) { - /* check for legal chars ans remove trailing . */ + /* check for legal chars and remove trailing . + also fail empty string. */ int l = strlen(s); char c; - if (l>0 && s[l-1] == '.') - s[l-1] = 0; + if (l == 0) return 0; + + if (s[l-1] == '.') + { + if (l == 1) return 0; + s[l-1] = 0; + } while ((c = *s++)) if (c != '.' && !legal_char(c)) -- 2.47.2