import of dnsmasq-2.48.tar.gz
diff --git a/src/rfc2131.c b/src/rfc2131.c
index c9320ee..35047ab 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -16,6 +16,8 @@
 
 #include "dnsmasq.h"
 
+#ifdef HAVE_DHCP
+
 #define BOOTREQUEST              1
 #define BOOTREPLY                2
 #define DHCP_COOKIE              0x63825363
@@ -49,6 +51,8 @@
 #define OPTION_USER_CLASS        77
 #define OPTION_CLIENT_FQDN       81
 #define OPTION_AGENT_ID          82
+#define OPTION_ARCH              93
+#define OPTION_PXE_UUID          97
 #define OPTION_SUBNET_SELECT     118
 #define OPTION_END               255
 
@@ -56,7 +60,13 @@
 #define SUBOPT_REMOTE_ID         2
 #define SUBOPT_SUBNET_SELECT     5     /* RFC 3527 */
 #define SUBOPT_SUBSCR_ID         6     /* RFC 3393 */
-#define SUBOPT_SERVER_OR        11     /* RFC 5107 */
+#define SUBOPT_SERVER_OR         11    /* RFC 5107 */
+
+#define SUBOPT_PXE_BOOT_ITEM     71    /* PXE standard */
+#define SUBOPT_PXE_DISCOVERY     6
+#define SUBOPT_PXE_SERVERS       8
+#define SUBOPT_PXE_MENU          9
+#define SUBOPT_PXE_MENU_PROMPT   10
 
 #define DHCPDISCOVER             1
 #define DHCPOFFER                2
@@ -78,14 +88,15 @@
 static void option_put_string(struct dhcp_packet *mess, unsigned char *end, 
 			      int opt, char *string, int null_term);
 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, void *addr, 
-		       unsigned char *ext_mac, int mac_len, char *interface, char *string);
+static struct in_addr option_addr_arr(unsigned char *opt, int offset);
+static unsigned int option_uint(unsigned char *opt, int i, int size);
+static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
+		       int mac_len, char *interface, char *string, u32 xid);
 static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
 static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid);
-static void clear_packet(struct dhcp_packet *mess, unsigned char *end, unsigned char *agent_id);
-static void restore_agent_id(unsigned char *agent_id, struct dhcp_packet *mess, unsigned char *real_end);
+static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
+			       unsigned char *agent_id, unsigned char *real_end);
+static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
 static void do_options(struct dhcp_context *context,
 		       struct dhcp_packet *mess,
 		       unsigned char *real_end, 
@@ -95,12 +106,18 @@
 		       struct dhcp_netid *netid,
 		       struct in_addr subnet_addr,
 		       unsigned char fqdn_flags,
-		       int null_term,
-		       unsigned char *agent_id);
+		       int null_term, int pxearch,
+		       unsigned char *uuid);
+
 
 static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); 
-static void do_encap_opts(int encap, struct dhcp_packet *mess, unsigned char *end, int null_term);
-	  
+static void do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
+static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
+static int prune_vendor_opts(struct dhcp_netid *netid);
+static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid);
+struct dhcp_boot *find_boot(struct dhcp_netid *netid);
+
+  
 size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
 		  size_t sz, time_t now, int unicast_dest, int *is_inform)
 {
@@ -109,9 +126,10 @@
   struct dhcp_vendor *vendor;
   struct dhcp_mac *mac;
   struct dhcp_netid_list *id_list;
-  int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0;
+  int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
   struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
   unsigned char *end = (unsigned char *)(mess + 1); 
+  unsigned char *real_end = (unsigned char *)(mess + 1); 
   char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
   int hostname_auth = 0, borken_opt = 0;
   unsigned char *req_options = NULL;
@@ -123,11 +141,12 @@
   unsigned short fuzz = 0;
   unsigned int mess_type = 0;
   unsigned char fqdn_flags = 0;
-  unsigned char *agent_id = NULL;
+  unsigned char *agent_id = NULL, *uuid = NULL;
   unsigned char *emac = NULL;
   int emac_len = 0;
   struct dhcp_netid known_id, iface_id;
   struct dhcp_opt *o;
+  unsigned char pxe_uuid[17];
 
   subnet_addr.s_addr = override.s_addr = 0;
 
@@ -145,7 +164,7 @@
   /* check for DHCP rather than BOOTP */
   if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
     {
-      mess_type = option_uint(opt, 1);
+      mess_type = option_uint(opt, 0, 1);
 
       /* only insist on a cookie for DHCP. */
       if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
@@ -156,7 +175,7 @@
 	 sent includes the IP and UDP headers, hence the magic "-28" */
       if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
 	{
-	  size_t size = (size_t)option_uint(opt, 2) - 28;
+	  size_t size = (size_t)option_uint(opt, 0, 2) - 28;
 	  
 	  if (size > DHCP_PACKET_MAX)
 	    size = DHCP_PACKET_MAX;
@@ -166,7 +185,7 @@
 	  if (expand_buf(&daemon->dhcp_packet, size))
 	    {
 	      mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
-	      end = ((unsigned char *)mess) + size;
+	      real_end = end = ((unsigned char *)mess) + size;
 	    }
 	}
 
@@ -187,7 +206,8 @@
 	  unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
 	  if (last_opt && last_opt < end - total)
 	    {
-	      agent_id = end - total;
+	      end -= total;
+	      agent_id = end;
 	      memcpy(agent_id, opt, total);
 	    }
 
@@ -313,7 +333,7 @@
   
   if (!context)
     {
-      my_syslog(LOG_WARNING, _("no address range available for DHCP request %s %s"), 
+      my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"), 
 		subnet_addr.s_addr ? _("with subnet selector") : _("via"),
 		subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
       return 0;
@@ -325,14 +345,15 @@
   if (daemon->options & OPT_LOG_OPTS)
     {
       struct dhcp_context *context_tmp;
-      my_syslog(LOG_INFO, _("DHCP packet: transaction-id is %u"), mess->xid);
       for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
 	{
 	  strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
-	  if (context_tmp->flags & CONTEXT_STATIC)
-	    my_syslog(LOG_INFO, _("Available DHCP subnet: %s/%s"), daemon->namebuff, inet_ntoa(context_tmp->netmask));
+	  if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
+	    my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP subnet: %s/%s"),
+		      ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
 	  else
-	    my_syslog(LOG_INFO, _("Available DHCP range: %s -- %s"), daemon->namebuff, inet_ntoa(context_tmp->end));
+	    my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP range: %s -- %s"), 
+		      ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
 	}
     }
 
@@ -356,7 +377,7 @@
       struct in_addr *logaddr = NULL;
 
       /* must have a MAC addr for bootp */
-      if (mess->htype == 0 || mess->hlen == 0)
+      if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
 	return 0;
       
       if (have_config(config, CONFIG_DISABLE))
@@ -464,15 +485,15 @@
 				now); 
 	      lease_set_interface(lease, int_index);
 	      
-	      clear_packet(mess, end, NULL);
+	      clear_packet(mess, end);
 	      do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), 
-			 domain, netid, subnet_addr, 0, 0, NULL);
+			 domain, netid, subnet_addr, 0, 0, 0, NULL);
 	    }
 	}
       
-      log_packet(NULL, logaddr, mess->chaddr, mess->hlen, iface_name, message);
+      log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
       
-      return message ? 0 : dhcp_packet_size(mess, netid);
+      return message ? 0 : dhcp_packet_size(mess, netid, agent_id, real_end);
     }
       
   if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
@@ -533,6 +554,9 @@
 	client_hostname = daemon->dhcp_buff;
     }
 
+  if (client_hostname && daemon->options & OPT_LOG_OPTS)
+    my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
+  
   if (have_config(config, CONFIG_NAME))
     {
       hostname = config->hostname;
@@ -669,9 +693,9 @@
   if (daemon->options & OPT_LOG_OPTS)
     {
       if (sanitise(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->namebuff))
-	my_syslog(LOG_INFO, _("Vendor class: %s"), daemon->namebuff);
+	my_syslog(MS_DHCP | LOG_INFO, _("%u Vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
       if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
-	my_syslog(LOG_INFO, _("User class: %s"), daemon->namebuff);
+	my_syslog(MS_DHCP | LOG_INFO, _("%u User class: %s"), ntohl(mess->xid), daemon->namebuff);
     }
 
   /* if all the netids in the ignore list are present, ignore this client */
@@ -683,6 +707,116 @@
   if (have_config(config, CONFIG_NOCLID))
     clid = NULL;
           
+  /* Check if client is PXE client. */
+  if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) && 
+      strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
+    {
+      if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
+	{
+	  memcpy(pxe_uuid, option_ptr(opt, 0), 17);
+	  uuid = pxe_uuid;
+	}
+
+      /* Check if this is really a PXE bootserver request, and handle specially if so. */
+      if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
+	  (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
+	  (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
+	{
+	  struct pxe_service *service;
+	  int type = option_uint(opt, 0, 2);
+	  int layer = option_uint(opt, 2, 2);
+	  unsigned char save71[4];
+	  struct dhcp_opt opt71;
+
+	  if (layer & 0x8000)
+	    {
+	      my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
+	      return 0;
+	    }
+
+	  memcpy(save71, option_ptr(opt, 0), 4);
+	  
+	  for (service = daemon->pxe_services; service; service = service->next)
+	    if (service->type == type)
+	      break;
+	  
+	  if (!service || !service->basename)
+	    return 0;
+	  
+	  clear_packet(mess, end);
+	  
+	  mess->yiaddr = mess->ciaddr;
+	  mess->ciaddr.s_addr = 0;
+	  if (service->server.s_addr != 0)
+	    mess->siaddr = service->server; 
+	  else
+	    mess->siaddr = context->local; 
+	  
+	  snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
+	  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
+	  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
+	  pxe_misc(mess, end, uuid);
+	  
+	  prune_vendor_opts(netid);
+	  opt71.val = save71;
+	  opt71.opt = SUBOPT_PXE_BOOT_ITEM;
+	  opt71.len = 4;
+	  opt71.flags = DHOPT_VENDOR_MATCH;
+	  opt71.netid = NULL;
+	  opt71.next = daemon->dhcp_opts;
+	  do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
+	  
+	  log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
+	  return dhcp_packet_size(mess, netid, agent_id, real_end);	  
+	}
+      
+      if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
+	{
+	  pxearch = option_uint(opt, 0, 2);
+
+	  /* proxy DHCP here. The DHCPREQUEST stuff is for gPXE */
+	  if ((mess_type == DHCPDISCOVER || mess_type == DHCPREQUEST) && 
+	      (context->flags & CONTEXT_PROXY))
+	    {
+	      struct dhcp_boot *boot = find_boot(netid);
+
+	      mess->yiaddr.s_addr = 0;
+	      if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
+		{
+		  mess->ciaddr.s_addr = 0;
+		  mess->flags |= htons(0x8000); /* broadcast */
+		}
+
+	      clear_packet(mess, end);
+	      
+	      /* Provide the bootfile here, for gPXE, and in case we have no menu items
+		 and set discovery_control = 8 */
+	      if (boot)
+		{
+		  if (boot->next_server.s_addr)
+		    mess->siaddr = boot->next_server;
+		  
+		  if (boot->file)
+		    strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
+		}
+
+	      option_put(mess, end, OPTION_MESSAGE_TYPE, 1, 
+			 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
+	      option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
+	      pxe_misc(mess, end, uuid);
+	      prune_vendor_opts(netid);
+	      do_encap_opts(pxe_opts(pxearch, netid), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
+	      
+	      log_packet("PXE", NULL, emac, emac_len, iface_name, "proxy", mess->xid);
+	      return dhcp_packet_size(mess, netid, agent_id, real_end);	  
+	    }
+	}
+    }
+
+  /* if we're just a proxy server, go no further */
+  if (context->flags & CONTEXT_PROXY)
+    return 0;
+  
   if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
     {
       req_options = (unsigned char *)daemon->dhcp_buff2;
@@ -703,7 +837,7 @@
       if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
 	return 0;
       
-      log_packet("DECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff);
+      log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
       
       if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
 	lease_prune(lease, now);
@@ -712,7 +846,7 @@
 	  config->addr.s_addr == option_addr(opt).s_addr)
 	{
 	  prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
-	  my_syslog(LOG_WARNING, _("disabling DHCP static address %s for %s"), 
+	  my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), 
 		    inet_ntoa(config->addr), daemon->dhcp_buff);
 	  config->flags |= CONFIG_DECLINED;
 	  config->decline_time = now;
@@ -735,7 +869,7 @@
       else
 	message = _("unknown lease");
 
-      log_packet("RELEASE", &mess->ciaddr, emac, emac_len, iface_name, message);
+      log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
 	
       return 0;
       
@@ -765,7 +899,7 @@
 		  int len;
 		  unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
 						       ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
-		  my_syslog(LOG_WARNING, _("not using configured address %s because it is leased to %s"),
+		  my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
 			    addrs, print_mac(daemon->namebuff, mac, len));
 		}
 	      else
@@ -775,10 +909,10 @@
 		    if (context->router.s_addr == config->addr.s_addr)
 		      break;
 		  if (tmp)
-		    my_syslog(LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
+		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
 		  else if (have_config(config, CONFIG_DECLINED) &&
 			   difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
-		    my_syslog(LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
+		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
 		  else
 		    conf = config->addr;
 		}
@@ -786,7 +920,9 @@
 	  
 	  if (conf.s_addr)
 	    mess->yiaddr = conf;
-	  else if (lease && address_available(context, lease->addr, netid))
+	  else if (lease && 
+		   address_available(context, lease->addr, netid) && 
+		   !config_find_by_address(daemon->dhcp_conf, lease->addr))
 	    mess->yiaddr = lease->addr;
 	  else if (opt && address_available(context, addr, netid) && !lease_find_by_addr(addr) && 
 		   !config_find_by_address(daemon->dhcp_conf, addr))
@@ -797,12 +933,12 @@
 	    message = _("no address available");      
 	}
       
-      log_packet("DISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message); 
+      log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid); 
 
       if (message || !(context = narrow_context(context, mess->yiaddr, netid)))
 	return 0;
 
-      log_packet("OFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL);
+      log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
 
       if (context->netid.net)
 	{
@@ -811,7 +947,7 @@
 	}
        
       time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
-      clear_packet(mess, end, agent_id);
+      clear_packet(mess, end);
       option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
       option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
       option_put(mess, end, OPTION_LEASE_TIME, 4, time);
@@ -822,9 +958,9 @@
 	  option_put(mess, end, OPTION_T2, 4, (time*7)/8);
 	}
       do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), 
-		 domain, netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
+		 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
       
-      return dhcp_packet_size(mess, netid);
+      return dhcp_packet_size(mess, netid, agent_id, real_end);
       
     case DHCPREQUEST:
       if (ignore || have_config(config, CONFIG_DISABLE))
@@ -905,7 +1041,7 @@
 	  mess->yiaddr = mess->ciaddr;
 	}
       
-      log_packet("REQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL);
+      log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
  
       if (!message)
 	{
@@ -951,7 +1087,7 @@
 		 a lease from one of it's MACs to give the address to another. */
 	      if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
 		{
-		  my_syslog(LOG_INFO, _("abandoning lease to %s of %s"),
+		  my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
 			    print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len), 
 			    inet_ntoa(ltmp->addr));
 		  lease = ltmp;
@@ -977,15 +1113,13 @@
 
       if (message)
 	{
-	  log_packet("NAK", &mess->yiaddr, emac, emac_len, iface_name, message);
+	  log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
 	  
 	  mess->yiaddr.s_addr = 0;
-	  clear_packet(mess, end, agent_id);
+	  clear_packet(mess, end);
 	  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
 	  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
 	  option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
-	  /* DHCPNAK gets agent-id too */
-	  restore_agent_id(agent_id, mess, end);
 	  /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on 
 	     a distant subnet which unicast a REQ to us won't work. */
 	  if (!unicast_dest || mess->giaddr.s_addr != 0 || 
@@ -1065,9 +1199,9 @@
 	  else
 	    override = lease->override;
 
-	  log_packet("ACK", &mess->yiaddr, emac, emac_len, iface_name, hostname);  
+	  log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);  
 	  
-	  clear_packet(mess, end, agent_id);
+	  clear_packet(mess, end);
 	  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
 	  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
 	  option_put(mess, end, OPTION_LEASE_TIME, 4, time);
@@ -1079,16 +1213,16 @@
 	      option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
 	    }
 	  do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), 
-		     domain, netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
+		     domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
 	}
 
-      return dhcp_packet_size(mess, netid); 
+      return dhcp_packet_size(mess, netid, agent_id, real_end); 
       
     case DHCPINFORM:
       if (ignore || have_config(config, CONFIG_DISABLE))
 	message = _("ignored");
       
-      log_packet("INFORM", &mess->ciaddr, emac, emac_len, iface_name, message);
+      log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
      
       if (message || mess->ciaddr.s_addr == 0)
 	return 0;
@@ -1106,7 +1240,7 @@
       if (!hostname)
 	hostname = host_from_dns(mess->ciaddr);
 
-      log_packet("ACK", &mess->ciaddr, emac, emac_len, iface_name, hostname);
+      log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
       
       if (context && context->netid.net)
 	{
@@ -1122,7 +1256,7 @@
 	    override = lease->override;
 	}
 
-      clear_packet(mess, end, agent_id);
+      clear_packet(mess, end);
       option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
       option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
       
@@ -1137,10 +1271,10 @@
 	}
 
       do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
-		 domain, netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
+		 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
       
       *is_inform = 1; /* handle reply differently */
-      return dhcp_packet_size(mess, netid); 
+      return dhcp_packet_size(mess, netid, agent_id, real_end); 
     }
   
   return 0;
@@ -1187,7 +1321,7 @@
   
   if (opt)
     { 
-      unsigned int req_time = option_uint(opt, 4);
+      unsigned int req_time = option_uint(opt, 0, 4);
       if (req_time < 120 )
 	req_time = 120; /* sanity */
       if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
@@ -1231,36 +1365,63 @@
 }
 
 static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
-		       int mac_len, char *interface, char *string)
+		       int mac_len, char *interface, char *string, u32 xid)
 {
   struct in_addr a;
-
+ 
   /* addr may be misaligned */
   if (addr)
     memcpy(&a, addr, sizeof(a));
   
-  my_syslog(LOG_INFO, "%s%s(%s) %s%s%s %s",
-	    type ? "DHCP" : "BOOTP",
-	    type ? type : "",
-	    interface, 
-	    addr ? inet_ntoa(a) : "",
-	    addr ? " " : "",
-	    print_mac(daemon->namebuff, ext_mac, mac_len),
-	    string ? string : "");
+  print_mac(daemon->namebuff, ext_mac, mac_len);
+  
+  if(daemon->options & OPT_LOG_OPTS)
+     my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
+	       ntohl(xid), 
+	       type,
+	       interface, 
+	       addr ? inet_ntoa(a) : "",
+	       addr ? " " : "",
+	       daemon->namebuff,
+	       string ? string : "");
+  else
+    my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
+	      type,
+	      interface, 
+	      addr ? inet_ntoa(a) : "",
+	      addr ? " " : "",
+	      daemon->namebuff,
+	      string ? string : "");
 }
 
-static void log_options(unsigned char *start)
+static void log_options(unsigned char *start, u32 xid)
 {
   while (*start != OPTION_END)
     {
-      char *text = option_string(start[0]);
-      unsigned char trunc = start[1] < 13 ? start[1] : 13;
-      my_syslog(LOG_INFO, "sent size:%3d option:%3d%s%s%s%s%s", 
-		start[1], start[0],
+      int is_ip, is_name, i;
+      char *text = option_string(start[0], &is_ip, &is_name);
+      unsigned char trunc = option_len(start);
+      
+      if (is_ip)
+	for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ) 
+	  {
+	    if (i != 0)
+	      strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
+	    strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff));
+	  }
+      else if (!is_name || !sanitise(start, daemon->namebuff))
+	{
+	  if (trunc > 13)
+	    trunc = 13;
+	  print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
+	}
+      
+      my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s", 
+		ntohl(xid), option_len(start), start[0],
 		text ? ":" : "", text ? text : "",
-		start[1] == 0 ? "" : "  ",
-		start[1] == 0 ? "" : print_mac(daemon->namebuff, &start[2], trunc),
-		trunc == start[1] ? "" : "...");
+		trunc == 0 ? "" : "  ",
+		trunc == 0 ? "" : daemon->namebuff,
+		trunc == option_len(start) ? "" : "...");
       start += start[1] + 2;
     }
 }
@@ -1315,23 +1476,28 @@
   return NULL;
 }
 
-static struct in_addr option_addr(unsigned char *opt)
+static struct in_addr option_addr_arr(unsigned char *opt, int offset)
 {
   /* this worries about unaligned data in the option. */
   /* struct in_addr is network byte order */
   struct in_addr ret;
 
-  memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
+  memcpy(&ret, option_ptr(opt, offset), INADDRSZ);
 
   return ret;
 }
 
-static unsigned int option_uint(unsigned char *opt, int size)
+static struct in_addr option_addr(unsigned char *opt)
+{
+  return option_addr_arr(opt, 0);
+}
+
+static unsigned int option_uint(unsigned char *opt, int offset, int size)
 {
   /* this worries about unaligned data and byte order */
   unsigned int ret = 0;
   int i;
-  unsigned char *p = option_ptr(opt, 0);
+  unsigned char *p = option_ptr(opt, offset);
   
   for (i = 0; i < size; i++)
     ret = (ret << 8) | *p++;
@@ -1360,7 +1526,8 @@
   return NULL;
 }
 
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid)
+static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
+			       unsigned char *agent_id, unsigned char *real_end)
 {
   unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
   unsigned char *overload;
@@ -1368,6 +1535,15 @@
   struct dhcp_netid_list *id_list;
   struct dhcp_netid *n;
 
+  /* move agent_id back down to the end of the packet */
+  if (agent_id)
+    {
+      unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
+      memmove(p, agent_id, real_end - agent_id);
+      p += real_end - agent_id;
+      memset(p, 0, real_end - p); /* in case of overlap */
+    }
+  
   /* We do logging too */
   if (netid && (daemon->options & OPT_LOG_OPTS))
     {
@@ -1388,30 +1564,40 @@
 	    }
 	}
       p[MAXDNAME - 1] = 0;
-      my_syslog(LOG_INFO, _("tags: %s"), p);
+      my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), p);
     } 
    
   /* add END options to the regions. */
-  if ((overload = find_overload(mess)))
+  overload = find_overload(mess);
+  
+  if (overload && (option_uint(overload, 0, 1) & 1))
     {
-      if (option_uint(overload, 1) & 1)
-	{
-	  *dhcp_skip_opts(mess->file) = OPTION_END;
-	  if (daemon->options & OPT_LOG_OPTS)
-	    log_options(mess->file);
-	}
-      if (option_uint(overload, 1) & 2)
-	{
-	  *dhcp_skip_opts(mess->sname) = OPTION_END;
-	  if (daemon->options & OPT_LOG_OPTS)
-	    log_options(mess->sname);
-	}
+      *dhcp_skip_opts(mess->file) = OPTION_END;
+      if (daemon->options & OPT_LOG_OPTS)
+	log_options(mess->file, mess->xid);
     }
+  else if ((daemon->options & OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
+    my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
+  
+  if (overload && (option_uint(overload, 0, 1) & 2))
+    {
+      *dhcp_skip_opts(mess->sname) = OPTION_END;
+      if (daemon->options & OPT_LOG_OPTS)
+	log_options(mess->sname, mess->xid);
+    }
+  else if ((daemon->options & OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
+    my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
+
 
   *p++ = OPTION_END;
   
   if (daemon->options & OPT_LOG_OPTS)
-    log_options(&mess->options[0] + sizeof(u32));
+    {
+      if (mess->siaddr.s_addr != 0)
+	my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
+      
+      log_options(&mess->options[0] + sizeof(u32), mess->xid);
+    } 
   
   for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
     if (match_netid(id_list->list, netid, 0))
@@ -1475,7 +1661,7 @@
 	}
       
       if (!p)
-	my_syslog(LOG_WARNING, _("cannot send DHCP/BOOTP option %d: no space left in packet"), opt);
+	my_syslog(MS_DHCP | LOG_WARNING, _("cannot send DHCP/BOOTP option %d: no space left in packet"), opt);
     }
  
   if (p)
@@ -1539,7 +1725,7 @@
     }  
   return len;
 }
- 
+
 static int in_list(unsigned char *list, int opt)
 {
   int i;
@@ -1560,7 +1746,7 @@
   struct dhcp_opt *tmp;  
   for (tmp = opts; tmp; tmp = tmp->next)
     if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)))
-      if (match_netid(tmp->netid, netid, 1) || match_netid(tmp->netid, netid, 0))
+      if (match_netid(tmp->netid, netid, netid ? 0 : 1))
 	return tmp;
 	      
   return netid ? option_find2(NULL, opts, opt) : NULL;
@@ -1572,7 +1758,7 @@
 {
   for (; dopt; dopt = dopt->next)
     {
-      dopt->flags &= ~(DHOPT_ENCAP_MATCH | DHOPT_ENCAP_DONE);
+      dopt->flags &= ~DHOPT_VENDOR_MATCH;
       if (opt && (dopt->flags & DHOPT_VENDOR))
 	{
 	  int i, len = 0;
@@ -1581,22 +1767,23 @@
 	  for (i = 0; i <= (option_len(opt) - len); i++)
 	    if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
 	      {
-		dopt->flags |= DHOPT_ENCAP_MATCH;
+		dopt->flags |= DHOPT_VENDOR_MATCH;
 		break;
 	      }
 	}
     }
 }
 
-static void do_encap_opts(int encap, struct dhcp_packet *mess, unsigned char *end, int null_term)
+static void do_encap_opts(struct dhcp_opt *opt, int encap, int flag,  
+			  struct dhcp_packet *mess, unsigned char *end, int null_term)
 {
   int len, enc_len;
-  struct dhcp_opt *opt, *start;
+  struct dhcp_opt *start;
   unsigned char *p;
     
   /* find size in advance */
-  for (enc_len = 0, start = opt = daemon->dhcp_opts; opt; opt = opt->next)
-    if (opt->flags & DHOPT_ENCAP_MATCH)
+  for (enc_len = 0, start = opt; opt; opt = opt->next)
+    if (opt->flags & flag)
       {
 	int new = do_opt(opt, NULL, NULL, null_term) + 2;
 	if (enc_len + new <= 255)
@@ -1605,7 +1792,7 @@
 	  {
 	    p = free_space(mess, end, encap, enc_len);
 	    for (; start && start != opt; start = start->next)
-	      if (p && (start->flags & DHOPT_ENCAP_MATCH))
+	      if (p && (start->flags & flag))
 		{
 		  len = do_opt(start, p + 2, NULL, null_term);
 		  *(p++) = start->opt;
@@ -1621,7 +1808,7 @@
       (p = free_space(mess, end, encap, enc_len + 1)))
     {
       for (; start; start = start->next)
-	if (start->flags & DHOPT_ENCAP_MATCH)
+	if (start->flags & flag)
 	  {
 	    len = do_opt(start, p + 2, NULL, null_term);
 	    *(p++) = start->opt;
@@ -1632,72 +1819,169 @@
     }
 }
 
-static void clear_packet(struct dhcp_packet *mess, unsigned char *end, unsigned char *agent_id)
+static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
 {
-  /* don't clear agent_id */
-  if (agent_id)
-    end = agent_id;
+  unsigned char *p;
 
+  option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
+  if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
+    memcpy(p, uuid, 17);
+}
+
+static int prune_vendor_opts(struct dhcp_netid *netid)
+{
+  int force = 0;
+  struct dhcp_opt *opt;
+
+  /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
+  for (opt = daemon->dhcp_opts; opt; opt = opt->next)
+    if (opt->flags & DHOPT_VENDOR_MATCH)
+      {
+	if (!match_netid(opt->netid, netid, 1))
+	  opt->flags &= ~DHOPT_VENDOR_MATCH;
+	else if (opt->flags & DHOPT_FORCE)
+	  force = 1;
+      }
+  return force;
+}
+
+static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid)
+{
+#define NUM_OPTS 4  
+
+  unsigned  char *p, *q;
+  struct pxe_service *service;
+  static struct dhcp_opt *o, *ret;
+  int i, j = NUM_OPTS - 1;
+  
+  /* We pass back references to these, hence they are declared static */
+  static unsigned char discovery_control;
+  static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' }; 
+  static struct dhcp_opt *fake_opts = NULL;
+  
+  /* We are found by broadcast, so disable multicast. It gets switched on again
+     if we point to other servers and don't give a unicast address. Note that
+     we don't provide our own address for services we are the boot server for because unicast 
+     discovery is to port 4011 and we don't listen there. If you are using proxy DHCP 
+     and DHCP relays, the relay will need to forward to the proxy too. */
+  discovery_control = 2;
+  
+  ret = daemon->dhcp_opts;
+  
+  if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
+    return ret;
+
+  for (i = 0; i < NUM_OPTS; i++)
+    {
+      fake_opts[i].flags = DHOPT_VENDOR_MATCH;
+      fake_opts[i].netid = NULL;
+      fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
+    }
+  
+  /* create the data for the PXE_MENU and PXE_SERVERS options. */
+  p = (unsigned char *)daemon->dhcp_buff;
+  q = (unsigned char *)daemon->dhcp_buff2;
+
+  for (i = 0, service = daemon->pxe_services; service; service = service->next)
+    if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
+      {
+	size_t len = strlen(service->menu);
+	/* opt 43 max size is 255. encapsulated option has type and length
+	   bytes, so its max size is 253. */
+	if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
+	  {
+	    *(p++) = service->type >> 8;
+	    *(p++) = service->type;
+	    *(p++) = len;
+	    memcpy(p, service->menu, len);
+	    p += len;
+	    i++;
+	  }
+	else
+	  {
+	  toobig:
+	    my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
+	    return daemon->dhcp_opts;
+	  }
+	
+	if (!service->basename)
+	  {
+	    if (service->server.s_addr != 0)
+	      {
+		if (q - (unsigned char *)daemon->dhcp_buff2 + 3 + INADDRSZ >= 253)
+		  goto toobig;
+		
+		/* Boot service with known address - give it */
+		*(q++) = service->type >> 8;
+		*(q++) = service->type;
+		*(q++) = 1;
+		/* dest misaligned */
+		memcpy(q, &service->server.s_addr, INADDRSZ);
+		q += INADDRSZ;
+	      }
+	    else if (service->type != 0)
+	      /* We're not supplying a server, so let the client multicast.
+		 type zero is "local boot" so no need for M/C on that. */
+	      discovery_control = 0;
+	  }	  
+      }
+
+  /* if no prompt, wait forever if there's a choice */
+  fake_prompt[0] = (i > 1) ? 255 : 0;
+  
+  if (i == 0)
+    discovery_control = 8; /* no menu - just use use mess->filename */
+  else
+    {
+      ret = &fake_opts[j--];
+      ret->len = p - (unsigned char *)daemon->dhcp_buff;
+      ret->val = (unsigned char *)daemon->dhcp_buff;
+      ret->opt = SUBOPT_PXE_MENU;
+
+      if (q - (unsigned char *)daemon->dhcp_buff2 != 0)
+	{
+	  ret = &fake_opts[j--]; 
+	  ret->len = q - (unsigned char *)daemon->dhcp_buff2;
+	  ret->val = (unsigned char *)daemon->dhcp_buff2;
+	  ret->opt = SUBOPT_PXE_SERVERS;
+	}
+    }
+
+  for (o = daemon->dhcp_opts; o; o = o->next)
+    if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
+      break;
+  
+  if (!o)
+    {
+      ret = &fake_opts[j--]; 
+      ret->len = sizeof(fake_prompt);
+      ret->val = fake_prompt;
+      ret->opt = SUBOPT_PXE_MENU_PROMPT;
+    }
+  
+  if (discovery_control != 0)
+    {
+      ret = &fake_opts[j--]; 
+      ret->len = 1;
+      ret->opt = SUBOPT_PXE_DISCOVERY;
+      ret->val= &discovery_control;
+    }
+
+  return ret;
+}
+  
+static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
+{
   memset(mess->sname, 0, sizeof(mess->sname));
   memset(mess->file, 0, sizeof(mess->file));
   memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
   mess->siaddr.s_addr = 0;
 }
 
-static void restore_agent_id(unsigned char *agent_id, struct dhcp_packet *mess, unsigned char *real_end)
-{ 
-  if (agent_id)
-    {
-      unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
-      memmove(p, agent_id, real_end - agent_id);
-      p += real_end - agent_id;
-      memset(p, 0, real_end - p); /* in case of overlap */
-    }
-}
-
-static void do_options(struct dhcp_context *context,
-		       struct dhcp_packet *mess,
-		       unsigned char *real_end, 
-		       unsigned char *req_options,
-		       char *hostname, 
-		       char *domain, char *config_domain,
-		       struct dhcp_netid *netid,
-		       struct in_addr subnet_addr,
-		       unsigned char fqdn_flags,
-		       int null_term,
-		       unsigned char *agent_id)
+struct dhcp_boot *find_boot(struct dhcp_netid *netid)
 {
-  struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
   struct dhcp_boot *boot;
-  unsigned char *p, *end = agent_id ? agent_id : real_end;
-  int i, len, force_encap = 0;
-  unsigned char f0 = 0, s0 = 0;
-  int done_file = 0, done_server = 0;
 
-  if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
-    my_syslog(LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
-  
-  /* logging */
-  if ((daemon->options & OPT_LOG_OPTS) && req_options)
-    {
-      char *q = daemon->namebuff;
-      for (i = 0; req_options[i] != OPTION_END; i++)
-	{
-	  char *s = option_string(req_options[i]);
-	  q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
-			"%d%s%s%s", 
-			req_options[i],
-			s ? ":" : "",
-			s ? s : "", 
-			req_options[i+1] == OPTION_END ? "" : ", ");
-	  if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
-	    {
-	      q = daemon->namebuff;
-	      my_syslog(LOG_INFO, _("requested options: %s"), daemon->namebuff);
-	    }
-	}
-    }
-      
   /* decide which dhcp-boot option we're using */
   for (boot = daemon->boot_config; boot; boot = boot->next)
     if (match_netid(boot->netid, netid, 0))
@@ -1707,7 +1991,53 @@
     for (boot = daemon->boot_config; boot; boot = boot->next)
       if (match_netid(boot->netid, netid, 1))
 	break;
+
+  return boot;
+}
+
+static void do_options(struct dhcp_context *context,
+		       struct dhcp_packet *mess,
+		       unsigned char *end, 
+		       unsigned char *req_options,
+		       char *hostname, 
+		       char *domain, char *config_domain,
+		       struct dhcp_netid *netid,
+		       struct in_addr subnet_addr,
+		       unsigned char fqdn_flags,
+		       int null_term, int pxe_arch,
+		       unsigned char *uuid)
+{
+  struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
+  struct dhcp_boot *boot;
+  unsigned char *p;
+  int i, len, force_encap = 0;
+  unsigned char f0 = 0, s0 = 0;
+  int done_file = 0, done_server = 0;
+
+  if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
+    my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
   
+  /* logging */
+  if ((daemon->options & OPT_LOG_OPTS) && req_options)
+    {
+      char *q = daemon->namebuff;
+      for (i = 0; req_options[i] != OPTION_END; i++)
+	{
+	  char *s = option_string(req_options[i], NULL, NULL);
+	  q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
+			"%d%s%s%s", 
+			req_options[i],
+			s ? ":" : "",
+			s ? s : "", 
+			req_options[i+1] == OPTION_END ? "" : ", ");
+	  if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
+	    {
+	      q = daemon->namebuff;
+	      my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
+	    }
+	}
+    }
+      
   if (context)
     mess->siaddr = context->local;
   
@@ -1716,8 +2046,8 @@
      and very old DHCP clients won't have this, we also 
      provide an manual option to disable it.
      Some PXE ROMs have bugs (surprise!) and need zero-terminated 
-     names, so we always send those. */
-  if (boot)
+     names, so we always send those.  */
+  if ((boot = find_boot(netid)))
     {
       if (boot->sname)
 	{	  
@@ -1741,21 +2071,18 @@
       
       if (boot->next_server.s_addr)
 	mess->siaddr = boot->next_server;
-	
-      if (daemon->options & OPT_LOG_OPTS)
-	my_syslog(LOG_INFO, _("next server: %s"), inet_ntoa(mess->siaddr));
     }
   else
     /* Use the values of the relevant options if no dhcp-boot given and
-       they're no explicitly asked for as options. */
+       they're not explicitly asked for as options. */
     {
-      if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
+      if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && mess->file[0] == 0 &&
 	  (opt = option_find2(netid, config_opts, OPTION_FILENAME)))
 	{
 	  strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
 	  done_file = 1;
 	}
-
+      
       if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
 	  (opt = option_find2(netid, config_opts, OPTION_SNAME)))
 	{
@@ -1763,16 +2090,7 @@
 	  done_server = 1;
 	}
     }
-  
-  if (daemon->options & OPT_LOG_OPTS)
-    { 
-      if (strlen((char *)mess->file) != 0)
-	my_syslog(LOG_INFO, _("bootfile name: %s"), (char *)mess->file);
-      
-      if (strlen((char *)mess->sname) != 0)
-	my_syslog(LOG_INFO, _("server name: %s"), (char *)mess->sname);
-    } 
-      
+        
   /* We don't want to do option-overload for BOOTP, so make the file and sname
      fields look like they are in use, even when they aren't. This gets restored
      at the end of this function. */
@@ -1908,8 +2226,12 @@
 	   optno == OPTION_DOMAINNAME ||
 	   optno == OPTION_HOSTNAME))
 	continue;
+
+      /* vendor-class comes from elsewhere for PXE */
+      if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
+	continue;
       
-      /* always force null-term for filename ans servername - buggy PXE again. */
+      /* always force null-term for filename and servername - buggy PXE again. */
       len = do_opt(opt, NULL, context, 
 		   (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
 
@@ -1925,24 +2247,14 @@
 	}  
     }
 
-  /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
-  for (opt = config_opts; opt; opt = opt->next)
-    if (opt->flags & DHOPT_ENCAP_MATCH)
-      {
-	if (!match_netid(opt->netid, netid, 1) && !match_netid(opt->netid, netid, 0))
-	  opt->flags &= ~DHOPT_ENCAP_MATCH;
-	else if (opt->flags & DHOPT_FORCE)
-	  force_encap = 1;
-      }
-  
-  if (force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT))
-    do_encap_opts(OPTION_VENDOR_CLASS_OPT, mess, end, null_term);
-  
   /* Now send options to be encapsulated in arbitrary options, 
      eg dhcp-option=encap:172,17,.......
      The may be more that one "outer" to do, so group
      all the options which match each outer in turn. */
   for (opt = config_opts; opt; opt = opt->next)
+    opt->flags &= ~DHOPT_ENCAP_DONE;
+  
+  for (opt = config_opts; opt; opt = opt->next)
     if ((opt->flags & (DHOPT_ENCAPSULATE | DHOPT_ENCAP_DONE)) ==  DHOPT_ENCAPSULATE)
       {
 	struct dhcp_opt *o;
@@ -1954,7 +2266,7 @@
 	    if ((o->flags & DHOPT_ENCAPSULATE) && opt->u.encap == o->u.encap)
 	      {
 		o->flags |= DHOPT_ENCAP_DONE;
-		if ((match_netid(o->netid, netid, 1) || match_netid(o->netid, netid, 0)) &&
+		if (match_netid(o->netid, netid, 1) &&
 		    (o->flags & DHOPT_FORCE || in_list(req_options, o->u.encap)))
 		  {
 		    o->flags |= DHOPT_ENCAP_MATCH;
@@ -1964,13 +2276,24 @@
 	  }
 	
 	if (found)
-	  do_encap_opts(opt->u.encap, mess, end, null_term);
+	  do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
       }
 
-  /* move agent_id back down to the end of the packet */
-  restore_agent_id(agent_id, mess, real_end);
+  /* Must precede pxe_opts, since it overwrites req_options */
+  force_encap = prune_vendor_opts(netid);
+  if (in_list(req_options, OPTION_VENDOR_CLASS_OPT))
+    force_encap = 1;
 
-  /* restore BOOTP anti-overload hack */
+  if (pxe_arch != -1)
+    {
+      pxe_misc(mess, end, uuid);
+      config_opts = pxe_opts(pxe_arch, netid);
+    }
+
+  if (force_encap)
+    do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term);
+  
+   /* restore BOOTP anti-overload hack */
   if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
     {
       mess->file[0] = f0;
@@ -1978,3 +2301,11 @@
     }
 }
 
+#endif
+  
+
+  
+  
+
+
+