Index: /trunk/src/VBox/Devices/Network/slirp/bootp.c
===================================================================
--- /trunk/src/VBox/Devices/Network/slirp/bootp.c	(revision 64298)
+++ /trunk/src/VBox/Devices/Network/slirp/bootp.c	(revision 64299)
@@ -49,5 +49,5 @@
     uint32_t xid;
     bool allocated;
-    uint8_t macaddr[6];
+    uint8_t macaddr[ETH_ALEN];
     struct in_addr addr;
     int number;
@@ -63,23 +63,49 @@
 static void bootp_reply(PNATState pData, struct mbuf *m0, int offReply, uint16_t flags);
 
-static uint8_t *dhcp_find_option(uint8_t *vend, uint8_t tag)
-{
-    uint8_t *q = vend;
-    uint8_t len;
-    /** @todo magic validation */
-    q += 4; /*magic*/
-    while(*q != RFC1533_END)
-    {
-        if (*q == RFC1533_PAD)
-        {
-            q++;
+
+static uint8_t *dhcp_find_option(uint8_t *vendor, size_t vlen, uint8_t tag, ssize_t checklen)
+{
+    uint8_t *q = vendor;
+    size_t len = vlen;
+
+    q += sizeof(rfc1533_cookie);
+    len -= sizeof(rfc1533_cookie);
+
+    while (len > 0)
+    {
+        uint8_t *optptr = q;
+        uint8_t opt;
+        uint8_t optlen;
+
+        opt = *q++;
+        --len;
+
+        if (opt == RFC1533_END)
+            break;
+
+        if (opt == RFC1533_PAD)
             continue;
-        }
-        if (*q == tag)
-            return q;
-        q++;
-        len = *q;
-        q += 1 + len;
-    }
+
+        if (len == 0)
+            break;              /* no option length byte */
+
+        optlen = *q++;
+        --len;
+
+        if (len < optlen)
+            break;              /* option value truncated */
+
+        if (opt == tag)
+        {
+            if (checklen > 0 && optlen != checklen)
+                break;          /* wrong option size */
+
+            return optptr;
+        }
+
+        q += optlen;
+        len -= optlen;
+    }
+
     return NULL;
 }
@@ -399,5 +425,5 @@
 };
 
-static int dhcp_decode_request(PNATState pData, struct bootp_t *bp, struct mbuf *m)
+static int dhcp_decode_request(PNATState pData, struct bootp_t *bp, size_t vlen, struct mbuf *m)
 {
     BOOTPClient *bc = NULL;
@@ -410,6 +436,8 @@
 
     /* need to understand which type of request we get */
-    req_ip = dhcp_find_option(&bp->bp_vend[0], RFC2132_REQ_ADDR);
-    server_ip = dhcp_find_option(&bp->bp_vend[0], RFC2132_SRV_ID);
+    req_ip = dhcp_find_option(bp->bp_vend, vlen,
+                              RFC2132_REQ_ADDR, sizeof(struct in_addr));
+    server_ip = dhcp_find_option(bp->bp_vend, vlen,
+                                 RFC2132_SRV_ID, sizeof(struct in_addr));
 
     bc = find_addr(pData, &daddr, bp->bp_hwaddr);
@@ -498,6 +526,5 @@
                }
 
-               Assert((bp->bp_hlen == ETH_ALEN));
-               memcpy(bc->macaddr, bp->bp_hwaddr, bp->bp_hlen);
+               memcpy(bc->macaddr, bp->bp_hwaddr, ETH_ALEN);
                bc->addr.s_addr = bp->bp_ciaddr.s_addr;
             }
@@ -544,6 +571,6 @@
                 }
             }
-            Assert((bp->bp_hlen == ETH_ALEN));
-            memcpy(bc->macaddr, bp->bp_hwaddr, bp->bp_hlen);
+
+            memcpy(bc->macaddr, bp->bp_hwaddr, ETH_ALEN);
             bc->addr.s_addr = ui32;
             break;
@@ -584,5 +611,5 @@
                 return -1;
             }
-            memcpy(bc->macaddr, bp->bp_hwaddr, 6);
+            memcpy(bc->macaddr, bp->bp_hwaddr, ETH_ALEN);
         }
 
@@ -679,5 +706,5 @@
  *
  */
-static void dhcp_decode(PNATState pData, struct bootp_t *bp, const uint8_t *buf, int size)
+static void dhcp_decode(PNATState pData, struct bootp_t *bp, size_t vlen)
 {
     const uint8_t *pu8RawDhcpObject;
@@ -688,16 +715,13 @@
     struct mbuf *m = NULL;
 
-    pu8RawDhcpObject = buf;
-    if (size < 5)
+    if (memcmp(bp->bp_vend, rfc1533_cookie, sizeof(rfc1533_cookie)) != 0)
         return;
 
-    if (memcmp(pu8RawDhcpObject, rfc1533_cookie, 4) != 0)
+    pu8RawDhcpObject = dhcp_find_option(bp->bp_vend, vlen, RFC2132_MSG_TYPE, 1);
+    if (pu8RawDhcpObject == NULL)
         return;
-
-    /* note: pu8RawDhcpObject doesn't point to parameter buf */
-    pu8RawDhcpObject = dhcp_find_option(bp->bp_vend, RFC2132_MSG_TYPE);
-    Assert(pu8RawDhcpObject);
-    if (!pu8RawDhcpObject)
+    if (pu8RawDhcpObject[1] != 1) /* option length */
         return;
+
     /**
      * We're going update dns list at least once per DHCP transaction (!not on every operation
@@ -720,9 +744,10 @@
             || pData->fUseHostResolver))
     {
-        uint8_t i = 2; /* i = 0 - tag, i == 1 - length */
-        parameter_list = dhcp_find_option(&bp->bp_vend[0], RFC2132_PARAM_LIST);
-        for (;parameter_list && i < parameter_list[1]; ++i)
-        {
-            if (parameter_list[i] == RFC1533_DNS)
+        uint8_t i;
+
+        parameter_list = dhcp_find_option(bp->bp_vend, vlen, RFC2132_PARAM_LIST, -1);
+        for (i = 0; parameter_list && i < parameter_list[1]; ++i)
+        {
+            if (parameter_list[2 + i] == RFC1533_DNS)
             {
                 /* XXX: How differs it from host Suspend/Resume? */
@@ -754,5 +779,5 @@
 
         case DHCPREQUEST:
-            rc = dhcp_decode_request(pData, bp, m);
+            rc = dhcp_decode_request(pData, bp, vlen, m);
             if (rc > 0)
                 goto reply;
@@ -765,13 +790,12 @@
 
         case DHCPDECLINE:
-            /* note: pu8RawDhcpObject doesn't point to DHCP header, now it's expected it points
-             * to Dhcp Option RFC2132_REQ_ADDR
-             */
-            pu8RawDhcpObject = dhcp_find_option(&bp->bp_vend[0], RFC2132_REQ_ADDR);
-            if (!pu8RawDhcpObject)
+            pu8RawDhcpObject = dhcp_find_option(bp->bp_vend, vlen,
+                                                RFC2132_REQ_ADDR, sizeof(struct in_addr));
+            if (pu8RawDhcpObject == NULL)
             {
                 Log(("NAT: RFC2132_REQ_ADDR not found\n"));
                 break;
             }
+
             req_ip.s_addr = *(uint32_t *)(pu8RawDhcpObject + 2);
             rc = bootp_cache_lookup_ether_by_ip(pData, req_ip.s_addr, NULL);
@@ -843,7 +867,24 @@
 {
     struct bootp_t *bp = mtod(m, struct bootp_t *);
-
-    if (bp->bp_op == BOOTP_REQUEST)
-        dhcp_decode(pData, bp, bp->bp_vend, DHCP_OPT_LEN);
+    u_int mlen = m_length(m, NULL);
+    size_t vlen;
+
+    if (mlen < RT_OFFSETOF(struct bootp_t, bp_vend) + BOOTP_VENDOR_LEN)
+        return;
+
+    if (bp->bp_op != BOOTP_REQUEST)
+        return;
+
+    if (bp->bp_htype != RTNET_ARP_ETHER)
+        return;
+
+    if (bp->bp_hlen != ETH_ALEN)
+        return;
+
+    if (bp->bp_hops != 0)
+        return;
+
+    vlen = mlen - RT_OFFSETOF(struct bootp_t, bp_vend);
+    dhcp_decode(pData, bp, vlen);
 }
 
