]> git.decadent.org.uk Git - odhcp6c.git/blobdiff - src/dhcpv6.c
Fix parsing empty IA_NA, IA_PD and invalid IA_ADDR options
[odhcp6c.git] / src / dhcpv6.c
index 7c6da00c3d3e6ef723e0f23a13f58847e0f606d6..ca41db6b48e88dd90d05b73eb270f4d585fc5c55 100644 (file)
@@ -179,11 +179,9 @@ int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout)
 #ifdef EXT_CER_ID
                        htons(DHCPV6_OPT_CER_ID),
 #endif
-#ifdef EXT_S46
                        htons(DHCPV6_OPT_S46_CONT_MAPE),
                        htons(DHCPV6_OPT_S46_CONT_MAPT),
                        htons(DHCPV6_OPT_S46_CONT_LW),
-#endif
                };
                odhcp6c_add_state(STATE_ORO, oro, sizeof(oro));
        }
@@ -366,7 +364,7 @@ static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs)
        struct dhcpv6_ia_hdr hdr_ia_na = {
                htons(DHCPV6_OPT_IA_NA),
                htons(sizeof(hdr_ia_na) - 4),
-               1, 0, 0
+               htonl(1), 0, 0
        };
 
        struct dhcpv6_ia_addr pa[ia_na_entries];
@@ -553,10 +551,12 @@ int dhcpv6_request(enum dhcpv6_msg type)
                                round_start = odhcp6c_get_milli_time()) {
                        uint8_t buf[1536], cmsg_buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
                        struct iovec iov = {buf, sizeof(buf)};
-                       struct msghdr msg = {NULL, 0, &iov, 1,
+                       struct sockaddr_in6 addr;
+                       struct msghdr msg = {&addr, sizeof(addr), &iov, 1,
                                        cmsg_buf, sizeof(cmsg_buf), 0};
                        struct in6_pktinfo *pktinfo = NULL;
 
+
                        // Check for pending signal
                        if (odhcp6c_signal_process())
                                return -1;
@@ -601,7 +601,7 @@ int dhcpv6_request(enum dhcpv6_msg type)
                                        "%llums", (unsigned long long)elapsed);
 
                        if (retx->handler_reply)
-                               len = retx->handler_reply(type, rc, opt, opt_end);
+                               len = retx->handler_reply(type, rc, opt, opt_end, &addr);
 
                        if (len > 0 && round_end - round_start > 1000)
                                round_end = 1000 + round_start;
@@ -693,7 +693,7 @@ static bool dhcpv6_response_is_valid(const void *buf, ssize_t len,
                        rcmsg = odata[0];
                } else if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)) {
                        ia_present = true;
-                       if (olen < sizeof(struct dhcpv6_ia_hdr))
+                       if (olen < -4 + sizeof(struct dhcpv6_ia_hdr))
                                options_valid = false;
                }
                else if ((otype == DHCPV6_OPT_IA_ADDR) || (otype == DHCPV6_OPT_IA_PREFIX) ||
@@ -731,7 +731,7 @@ int dhcpv6_poll_reconfigure(void)
 
 
 static int dhcpv6_handle_reconfigure(_unused enum dhcpv6_msg orig, const int rc,
-               const void *opt, const void *end)
+               const void *opt, const void *end, _unused const struct sockaddr_in6 *from)
 {
        uint16_t otype, olen;
        uint8_t *odata, msg = DHCPV6_MSG_RENEW;
@@ -741,14 +741,14 @@ static int dhcpv6_handle_reconfigure(_unused enum dhcpv6_msg orig, const int rc,
                                odata[0] == DHCPV6_MSG_INFO_REQ))
                        msg = odata[0];
 
-       dhcpv6_handle_reply(DHCPV6_MSG_UNKNOWN, rc, NULL, NULL);
+       dhcpv6_handle_reply(DHCPV6_MSG_UNKNOWN, rc, NULL, NULL, NULL);
        return msg;
 }
 
 
 // Collect all advertised servers
 static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
-               const void *opt, const void *end)
+               const void *opt, const void *end, _unused const struct sockaddr_in6 *from)
 {
        uint16_t olen, otype;
        uint8_t *odata, pref = 0;
@@ -761,7 +761,7 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
        dhcpv6_for_each_option(opt, end, otype, olen, odata) {
                if (orig == DHCPV6_MSG_SOLICIT &&
                                (otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA) &&
-                               olen > sizeof(struct dhcpv6_ia_hdr)) {
+                               olen > -4 + sizeof(struct dhcpv6_ia_hdr)) {
                        struct dhcpv6_ia_hdr *ia_hdr = (void*)(&odata[-4]);
                        dhcpv6_parse_ia(ia_hdr, odata + olen + sizeof(*ia_hdr));
                }
@@ -800,8 +800,8 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
                        struct dhcpv6_ia_hdr *h = (struct dhcpv6_ia_hdr*)&odata[-4];
                        uint8_t *oend = odata + olen, *d;
                        dhcpv6_for_each_option(&h[1], oend, otype, olen, d) {
-                               if (otype == DHCPV6_OPT_IA_PREFIX && (olen + 4) >=
-                                               (uint16_t)sizeof(struct dhcpv6_ia_prefix)) {
+                               if (otype == DHCPV6_OPT_IA_PREFIX &&
+                                               olen >= -4 + sizeof(struct dhcpv6_ia_prefix)) {
                                        struct dhcpv6_ia_prefix *p = (struct dhcpv6_ia_prefix*)&d[-4];
                                        have_pd = p->prefix;
                                }
@@ -810,7 +810,8 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
                        struct dhcpv6_ia_hdr *h = (struct dhcpv6_ia_hdr*)&odata[-4];
                        uint8_t *oend = odata + olen, *d;
                        dhcpv6_for_each_option(&h[1], oend, otype, olen, d)
-                               if (otype == DHCPV6_OPT_IA_ADDR)
+                               if (otype == DHCPV6_OPT_IA_ADDR &&
+                                               olen >= -4 + sizeof(struct dhcpv6_ia_addr))
                                        have_na = true;
                }
        }
@@ -856,18 +857,18 @@ static int dhcpv6_commit_advert(void)
 
 
 static int dhcpv6_handle_rebind_reply(enum dhcpv6_msg orig, const int rc,
-               const void *opt, const void *end)
+               const void *opt, const void *end, const struct sockaddr_in6 *from)
 {
-       dhcpv6_handle_advert(orig, rc, opt, end);
+       dhcpv6_handle_advert(orig, rc, opt, end, from);
        if (dhcpv6_commit_advert() < 0)
                return -1;
 
-       return dhcpv6_handle_reply(orig, rc, opt, end);
+       return dhcpv6_handle_reply(orig, rc, opt, end, from);
 }
 
 
 static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
-               const void *opt, const void *end)
+               const void *opt, const void *end, const struct sockaddr_in6 *from)
 {
        uint8_t *odata;
        uint16_t otype, olen;
@@ -922,16 +923,19 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                odhcp6c_clear_state(STATE_S46_MAPT);
                odhcp6c_clear_state(STATE_S46_MAPE);
                odhcp6c_clear_state(STATE_S46_LW);
+               odhcp6c_clear_state(STATE_PASSTHRU);
        }
 
        // Parse and find all matching IAs
        dhcpv6_for_each_option(opt, end, otype, olen, odata) {
+               bool passthru = true;
+
                if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)
-                               && olen > sizeof(struct dhcpv6_ia_hdr)) {
+                               && olen > -4 + sizeof(struct dhcpv6_ia_hdr)) {
                        struct dhcpv6_ia_hdr *ia_hdr = (void*)(&odata[-4]);
 
                        // Test ID
-                       if (ia_hdr->iaid != 1 && otype == DHCPV6_OPT_IA_NA)
+                       if (ia_hdr->iaid != htonl(1) && otype == DHCPV6_OPT_IA_NA)
                                continue;
 
                        uint16_t code = DHCPV6_Success;
@@ -963,18 +967,21 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                                continue;
 
                        dhcpv6_parse_ia(ia_hdr, odata + olen + sizeof(*ia_hdr));
+                       passthru = false;
                } else if (otype == DHCPV6_OPT_STATUS && olen >= 2) {
                        uint8_t *mdata = (olen > 2) ? &odata[2] : NULL;
                        uint16_t mlen = (olen > 2) ? olen - 2 : 0;
                        uint16_t code = ((int)odata[0]) << 8 | ((int)odata[1]);
 
                        dhcpv6_handle_status_code(orig, code, mdata, mlen, &ret);
+                       passthru = false;
                }
                else if (otype == DHCPV6_OPT_DNS_SERVERS) {
                        if (olen % 16 == 0)
                                odhcp6c_add_state(STATE_DNS, odata, olen);
                } else if (otype == DHCPV6_OPT_DNS_DOMAIN) {
                        odhcp6c_add_state(STATE_SEARCH, odata, olen);
+                       passthru = false;
                } else if (otype == DHCPV6_OPT_SNTP_SERVERS) {
                        if (olen % 16 == 0)
                                odhcp6c_add_state(STATE_SNTP_IP, odata, olen);
@@ -999,27 +1006,33 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                        odhcp6c_add_state(STATE_SIP_FQDN, odata, olen);
                } else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4) {
                        refresh = ntohl(*((uint32_t*)odata));
-               } else if (otype == DHCPV6_OPT_AUTH && olen == -4 +
-                               sizeof(struct dhcpv6_auth_reconfigure)) {
-                       struct dhcpv6_auth_reconfigure *r = (void*)&odata[-4];
-                       if (r->protocol == 3 && r->algorithm == 1 &&
-                                       r->reconf_type == 1)
-                               memcpy(reconf_key, r->key, sizeof(r->key));
+                       passthru = false;
+               } else if (otype == DHCPV6_OPT_AUTH) {
+                       if (olen == -4 + sizeof(struct dhcpv6_auth_reconfigure)) {
+                               struct dhcpv6_auth_reconfigure *r = (void*)&odata[-4];
+                               if (r->protocol == 3 && r->algorithm == 1 &&
+                                               r->reconf_type == 1)
+                                       memcpy(reconf_key, r->key, sizeof(r->key));
+                       }
+                       passthru = false;
                } else if (otype == DHCPV6_OPT_AFTR_NAME && olen > 3) {
                        size_t cur_len;
                        odhcp6c_get_state(STATE_AFTR_NAME, &cur_len);
                        if (cur_len == 0)
                                odhcp6c_add_state(STATE_AFTR_NAME, odata, olen);
+                       passthru = false;
                } else if (otype == DHCPV6_OPT_SOL_MAX_RT && olen == 4) {
                        uint32_t sol_max_rt = ntohl(*((uint32_t *)odata));
                        if (sol_max_rt >= DHCPV6_SOL_MAX_RT_MIN &&
                                        sol_max_rt <= DHCPV6_SOL_MAX_RT_MAX)
                                dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = sol_max_rt;
+                       passthru = false;
                } else if (otype == DHCPV6_OPT_INF_MAX_RT && olen == 4) {
                        uint32_t inf_max_rt = ntohl(*((uint32_t *)odata));
                        if (inf_max_rt >= DHCPV6_INF_MAX_RT_MIN &&
                                        inf_max_rt <= DHCPV6_INF_MAX_RT_MAX)
                                dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = inf_max_rt;
+                       passthru = false;
 #ifdef EXT_CER_ID
                } else if (otype == DHCPV6_OPT_CER_ID && olen == -4 +
                                sizeof(struct dhcpv6_cer_id)) {
@@ -1027,23 +1040,34 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                        struct in6_addr any = IN6ADDR_ANY_INIT;
                        if (memcmp(&cer_id->addr, &any, sizeof(any)))
                                odhcp6c_add_state(STATE_CER, &cer_id->addr, sizeof(any));
+                       passthru = false;
 #endif
-#ifdef EXT_S46
                } else if (otype == DHCPV6_OPT_S46_CONT_MAPT) {
                        odhcp6c_add_state(STATE_S46_MAPT, odata, olen);
+                       passthru = false;
                } else if (otype == DHCPV6_OPT_S46_CONT_MAPE) {
                        size_t mape_len;
                        odhcp6c_get_state(STATE_S46_MAPE, &mape_len);
                        if (mape_len == 0)
                                odhcp6c_add_state(STATE_S46_MAPE, odata, olen);
+                       passthru = false;
                } else if (otype == DHCPV6_OPT_S46_CONT_LW) {
                        odhcp6c_add_state(STATE_S46_LW, odata, olen);
-#endif
-               } else if (otype != DHCPV6_OPT_CLIENTID &&
-                               otype != DHCPV6_OPT_SERVERID) {
-                       odhcp6c_add_state(STATE_CUSTOM_OPTS,
-                                       &odata[-4], olen + 4);
+                       passthru = false;
+               } else if (otype == DHCPV6_OPT_CLIENTID ||
+                               otype == DHCPV6_OPT_SERVERID ||
+                               otype == DHCPV6_OPT_IA_TA ||
+                               otype == DHCPV6_OPT_PREF ||
+                               otype == DHCPV6_OPT_UNICAST ||
+                               otype == DHCPV6_OPT_FQDN ||
+                               otype == DHCPV6_OPT_RECONF_ACCEPT) {
+                       passthru = false;
+               } else {
+                       odhcp6c_add_state(STATE_CUSTOM_OPTS, &odata[-4], olen + 4);
                }
+
+               if (passthru)
+                       odhcp6c_add_state(STATE_PASSTHRU, &odata[-4], olen + 4);
        }
 
        if (orig != DHCPV6_MSG_INFO_REQ) {
@@ -1069,6 +1093,11 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                        default :
                                break;
                        }
+
+                       if (orig == DHCPV6_MSG_REBIND || orig == DHCPV6_MSG_REQUEST) {
+                               odhcp6c_clear_state(STATE_SERVER_ADDR);
+                               odhcp6c_add_state(STATE_SERVER_ADDR, &from->sin6_addr, 16);
+                       }
                }
        }
        else if (ret > 0) {