Add requested options to dhcp-script environment.

Allows fingerprinting of DHCP clients in the external
dhcp-script.

Change-Id: I4ed3966249701a311e4d52b71c8b16c0bfab4735
diff --git a/src/dhcp-protocol.h b/src/dhcp-protocol.h
index 4c09614..6d4110f 100644
--- a/src/dhcp-protocol.h
+++ b/src/dhcp-protocol.h
@@ -85,6 +85,7 @@
 #define BRDBAND_FORUM_IANA       3561 /* Broadband forum IANA enterprise */
 
 #define DHCP_CHADDR_MAX 16
+#define DHCP_OPT_MAX    312
 
 struct dhcp_packet {
   u8 op, htype, hlen, hops;
@@ -92,5 +93,5 @@
   u16 secs, flags;
   struct in_addr ciaddr, yiaddr, siaddr, giaddr;
   u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
-  u8 options[312];
+  u8 options[DHCP_OPT_MAX];
 };
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index d841fdc..418f815 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -674,6 +674,7 @@
   } *slaac_address;
   int vendorclass_count;
 #endif
+  unsigned char req_options[DHCP_OPT_MAX];
   struct dhcp_lease *next;
 };
 
diff --git a/src/helper.c b/src/helper.c
index 4be53c3..636ab58 100644
--- a/src/helper.c
+++ b/src/helper.c
@@ -72,6 +72,7 @@
 #endif
   unsigned char hwaddr[DHCP_CHADDR_MAX];
   char interface[IF_NAMESIZE];
+  unsigned char req_options[DHCP_OPT_MAX];
 };
 
 static struct script_data *buf = NULL;
@@ -499,6 +500,23 @@
 	  
 	  my_setenv("DNSMASQ_DOMAIN", domain, &err);
 	  
+	  {
+	    unsigned char *p;
+	    char strbuf[4096] = {0};
+	    for (p = data.req_options; *p != OPTION_END; p++)
+	      {
+	        char d[16];
+	        if (p != data.req_options) strcat(strbuf, ",");
+	        snprintf(d, sizeof(d), "%u", *p);
+	        if ((strlen(strbuf) + strlen(d) + 3) > sizeof(strbuf)) {
+	          break;
+	        }
+	        strcat(strbuf, d);
+	      }
+
+	    my_setenv("DNSMASQ_REQUESTED_OPTIONS", strbuf, &err);
+	  }
+
 	  end = extradata + data.ed_len;
 	  buf = extradata;
 	  
@@ -698,6 +716,7 @@
   memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
   if (!indextoname(fd, lease->last_interface, buf->interface))
     buf->interface[0] = 0;
+  memcpy(buf->req_options, lease->req_options, sizeof(buf->req_options));
   
 #ifdef HAVE_BROKEN_RTC 
   buf->length = lease->length;
diff --git a/src/lease.c b/src/lease.c
index 5d56b1b..41f8a51 100644
--- a/src/lease.c
+++ b/src/lease.c
@@ -729,6 +729,7 @@
   lease->length = 0xffffffff; /* illegal value */
 #endif
   lease->hwaddr_len = 256; /* illegal value */
+  lease->req_options[0] = OPTION_END;
   lease->next = leases;
   leases = lease;
   
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 5c90408..3c0a165 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -1261,6 +1261,16 @@
 		  if (mess->giaddr.s_addr)
 		    lease->giaddr = mess->giaddr;
 		  
+		  if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
+		    {
+		      int len = sizeof(lease->req_options);
+		      if (option_len(opt) < len) {
+		        len = option_len(opt);
+		      }
+		      memcpy(lease->req_options, option_ptr(opt, 0), len);
+		      lease->req_options[len] = OPTION_END;
+		    }
+
 		  free(lease->extradata);
 		  lease->extradata = NULL;
 		  lease->extradata_size = lease->extradata_len = 0;
@@ -1308,6 +1318,7 @@
 			ucp++, len--;
 		      lease_add_extradata(lease, ucp, len, 0);
 		    }
+		  
 		}
 #endif
 	    }