import of dnsmasq-2.8.tar.gz
diff --git a/CHANGELOG b/CHANGELOG
index 27edc33..90104ed 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -962,3 +962,44 @@
 	     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 @@
 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=<mx name>
 Return an MX record named <mx name> pointing to the host specified in the --mx-target switch
@@ -358,12 +363,24 @@
 .TP
 .B \-U, --dhcp-vendorclass=<network-id>,<vendor-class>
 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=<network-id>,<user-class>
+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=<filename>,[<servername>[,<server address>]]
 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 @@
 # 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 *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_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 @@
   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 @@
 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 @@
 		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 @@
   
   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 @@
 		  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 @@
   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 @@
   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 @@
   {"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 @@
 "-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=<id>,<class>   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 @@
 		    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 @@
 			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 @@
 	      
 	    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 @@
 		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 @@
 		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 @@
 			       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 @@
 		    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 @@
 		if ((new->opt = atoi(optarg)) == 0)
 		  {
 		    option = '?';
+		    if (new->netid)
+		      free(new->netid);
 		    free(new);
 		    break;
 		  }
@@ -960,7 +984,7 @@
 		      {
 			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 @@
 	      }
 
 	    case 'U':
+	    case 'j':
 	      {
 		char *comma;
 		
@@ -1073,10 +1098,11 @@
 		  {
 		    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 @@
 				     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 @@
 {
   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 @@
   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 @@
 		  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 @@
   
   def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
   
-  netid = context->netid;
-  if (have_config(config, CONFIG_NETID))
-    netid = config->netid;
-  else if ((opt = option_find(mess, sz, OPTION_VENDOR_ID)))
+  if (context->netid.net)
     {
-      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;
+      context->netid.next = netid;
+      netid = &context->netid;
     }
   
+  if (have_config(config, CONFIG_NETID))
+    {
+      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 @@
 	}
       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 @@
 	  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 @@
       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 @@
 	}
       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 @@
       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 @@
   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 @@
   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 @@
 				     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 @@
   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 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))