X-Git-Url: https://git.decadent.org.uk/gitweb/?p=odhcp6c.git;a=blobdiff_plain;f=src%2Fdhcpv6.c;h=f91f2cf16b8c8ff6fe881310433b98b68e2d8780;hp=a3e0a18e50ec1644b07110e371ac52bd6283ce27;hb=ca0693fe09d59a95192c4ad86492f05dfadbbf10;hpb=4a169b02b003fe50b2bccdf6b8c78a4de245669f diff --git a/src/dhcpv6.c b/src/dhcpv6.c index a3e0a18..f91f2cf 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -109,6 +110,14 @@ static uint8_t reconf_key[16]; static unsigned int client_options = 0; +static uint32_t ntohl_unaligned(const uint8_t *data) +{ + uint32_t buf; + + memcpy(&buf, data, sizeof(buf)); + return ntohl(buf); +} + int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout) { client_options = options; @@ -576,7 +585,9 @@ int dhcpv6_request(enum dhcpv6_msg type) // Receive rounds for (; len < 0 && (round_start < round_end); round_start = odhcp6c_get_milli_time()) { - uint8_t buf[1536], cmsg_buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + uint8_t buf[1536]; + uint8_t cmsg_buf[CMSG_SPACE(sizeof(struct in6_pktinfo))] + __aligned(__alignof__(struct cmsghdr)); struct iovec iov = {buf, sizeof(buf)}; struct sockaddr_in6 addr; struct msghdr msg = {.msg_name = &addr, .msg_namelen = sizeof(addr), @@ -691,7 +702,8 @@ static bool dhcpv6_response_is_valid(const void *buf, ssize_t len, continue; md5_ctx_t md5; - uint8_t serverhash[16], secretbytes[64], hash[16]; + uint8_t serverhash[16], secretbytes[64]; + uint32_t hash[4]; memcpy(serverhash, r->key, sizeof(serverhash)); memset(r->key, 0, sizeof(r->key)); @@ -797,34 +809,23 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc, if (otype == DHCPV6_OPT_SERVERID && olen <= 130) { memcpy(cand.duid, odata, olen); cand.duid_len = olen; - } else if (otype == DHCPV6_OPT_STATUS && olen >= 2) { - int error = ((int)odata[0] << 8 | (int)odata[1]); - - switch (error) { - case DHCPV6_NoPrefixAvail: - // Status code on global level - cand.preference -= 2000; - break; - - default : - break; - } } else if (otype == DHCPV6_OPT_PREF && olen >= 1 && cand.preference >= 0) { cand.preference = pref = odata[0]; } else if (otype == DHCPV6_OPT_RECONF_ACCEPT) { cand.wants_reconfigure = true; } else if (otype == DHCPV6_OPT_SOL_MAX_RT && olen == 4) { - uint32_t sol_max_rt = ntohl(*((uint32_t *)odata)); + uint32_t sol_max_rt = ntohl_unaligned(odata); if (sol_max_rt >= DHCPV6_SOL_MAX_RT_MIN && sol_max_rt <= DHCPV6_SOL_MAX_RT_MAX) cand.sol_max_rt = sol_max_rt; } else if (otype == DHCPV6_OPT_INF_MAX_RT && olen == 4) { - uint32_t inf_max_rt = ntohl(*((uint32_t *)odata)); + uint32_t inf_max_rt = ntohl_unaligned(odata); if (inf_max_rt >= DHCPV6_INF_MAX_RT_MIN && inf_max_rt <= DHCPV6_INF_MAX_RT_MAX) cand.inf_max_rt = inf_max_rt; - } else if (otype == DHCPV6_OPT_IA_PD && request_prefix) { + } else if (otype == DHCPV6_OPT_IA_PD && request_prefix && + olen >= -4 + sizeof(struct dhcpv6_ia_hdr)) { 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) { @@ -834,7 +835,8 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc, have_pd = p->prefix; } } - } else if (otype == DHCPV6_OPT_IA_NA) { + } else if (otype == DHCPV6_OPT_IA_NA && + olen >= -4 + sizeof(struct dhcpv6_ia_hdr)) { 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) @@ -1036,7 +1038,7 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc, } else if (otype == DHCPV6_OPT_SIP_SERVER_D) { odhcp6c_add_state(STATE_SIP_FQDN, odata, olen); } else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4) { - refresh = ntohl(*((uint32_t*)odata)); + refresh = ntohl_unaligned(odata); passthru = false; } else if (otype == DHCPV6_OPT_AUTH) { if (olen == -4 + sizeof(struct dhcpv6_auth_reconfigure)) { @@ -1053,13 +1055,13 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc, 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)); + uint32_t sol_max_rt = ntohl_unaligned(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)); + uint32_t inf_max_rt = ntohl_unaligned(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; @@ -1197,7 +1199,7 @@ static int dhcpv6_parse_ia(void *opt, void *end) if (elen > 64) elen = 64; - if (elen <= 32 || elen <= entry.length) { + if (entry.length < 32 || elen <= entry.length) { ok = false; continue; } @@ -1302,16 +1304,22 @@ static int dhcpv6_calc_refresh_timers(void) static void dhcpv6_log_status_code(const uint16_t code, const char *scope, - const void *status_msg, const int len) + const void *status_msg, int len) { - uint8_t buf[len + 3]; + const char *src = status_msg; + char buf[len + 3]; + char *dst = buf; - memset(buf, 0, sizeof(buf)); if (len) { - buf[0] = '('; - memcpy(&buf[1], status_msg, len); - buf[len + 1] = ')'; + *dst++ = '('; + while (len--) { + *dst = isprint((unsigned char)*src) ? *src : '?'; + src++; + dst++; + } + *dst++ = ')'; } + *dst = 0; syslog(LOG_WARNING, "Server returned %s status %i %s", scope, code, buf); @@ -1374,6 +1382,7 @@ static void dhcpv6_handle_ia_status_code(const enum dhcpv6_msg orig, } } +// Note this always takes ownership of cand->ia_na and cand->ia_pd static void dhcpv6_add_server_cand(const struct dhcpv6_server_cand *cand) { size_t cand_len, i; @@ -1396,7 +1405,10 @@ static void dhcpv6_add_server_cand(const struct dhcpv6_server_cand *cand) break; } - odhcp6c_insert_state(STATE_SERVER_CAND, i * sizeof(*c), cand, sizeof(*cand)); + if (odhcp6c_insert_state(STATE_SERVER_CAND, i * sizeof(*c), cand, sizeof(*cand))) { + free(cand->ia_na); + free(cand->ia_pd); + } } static void dhcpv6_clear_all_server_cand(void)