diff --git a/src/dhcp.c b/src/dhcp.c index 11c69bc1..e2d48244 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -808,7 +808,6 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type) const struct dhcp_lease *lease = &state->lease; char hbuf[HOSTNAME_MAX_LEN + 1]; const char *hostname; - const struct vivco *vivco; int mtu; #ifdef AUTH uint8_t *auth, auth_len; @@ -1137,35 +1136,35 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type) p += ifo->mudurl[0] + 1; } +#ifndef SMALL if (ifo->vivco_len && !has_option_mask(ifo->nomask, DHO_VIVCO)) { - AREA_CHECK(sizeof(ul)); - *p++ = DHO_VIVCO; - lp = p++; - *lp = sizeof(ul); - ul = htonl(ifo->vivco_en); - memcpy(p, &ul, sizeof(ul)); - p += sizeof(ul); - for (i = 0, vivco = ifo->vivco; - i < ifo->vivco_len; - i++, vivco++) - { - AREA_FIT(vivco->len); - if (vivco->len + 2 + *lp > 255) { - logerrx("%s: VIVCO option too big", - ifp->name); - free(bootp); - return -1; - } - *p++ = (uint8_t)vivco->len; - memcpy(p, vivco->data, vivco->len); - p += vivco->len; + struct vivco *vivco = ifo->vivco; + size_t vlen = ifo->vivco_len; + struct rfc3396_ctx rctx = { + .code = DHO_VIVCO, + .buf = &p, + .buflen = AREA_LEFT, + }; + + for (; vlen > 0; vivco++, vlen--) { + ul = htonl(vivco->en); + if (rfc3396_write(&rctx, &ul, sizeof(ul)) == -1) + goto toobig; + lp = rfc3396_zero(&rctx); + if (lp == NULL) + goto toobig; + if (rfc3396_write_byte(&rctx, + (uint8_t)vivco->len) == -1) + goto toobig; + if (rfc3396_write(&rctx, + vivco->data, vivco->len) == -1) + goto toobig; *lp = (uint8_t)(*lp + vivco->len + 1); } } - -#ifndef SMALL + if (ifo->vsio_len && !has_option_mask(ifo->nomask, DHO_VIVSO)) { diff --git a/src/dhcp6.c b/src/dhcp6.c index ca076e4e..ea0bff53 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -276,29 +276,28 @@ dhcp6_makeuser(void *data, const struct interface *ifp) return sizeof(o) + olen; } +#ifndef SMALL +/* DHCPv6 Option 16 (Vendor Class Option) */ static size_t dhcp6_makevendor(void *data, const struct interface *ifp) { const struct if_options *ifo; - size_t len, vlen, i; + size_t len = 0, optlen, vlen, i; uint8_t *p; const struct vivco *vivco; struct dhcp6_option o; ifo = ifp->options; - len = sizeof(uint32_t); /* IANA PEN */ - if (ifo->vivco_en) { - vlen = 0; + if (ifo->vivco_len > 0) { for (i = 0, vivco = ifo->vivco; i < ifo->vivco_len; i++, vivco++) - vlen += sizeof(uint16_t) + vivco->len; - len += vlen; + len += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) + vivco->len; } else if (ifo->vendorclassid[0] != '\0') { /* dhcpcd owns DHCPCD_IANA_PEN. * If you need your own string, get your own IANA PEN. */ vlen = strlen(ifp->ctx->vendor); - len += sizeof(uint16_t) + vlen; + len += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) + vlen; } else return 0; @@ -312,19 +311,19 @@ dhcp6_makevendor(void *data, const struct interface *ifp) uint16_t hvlen; p = data; - o.code = htons(D6_OPTION_VENDOR_CLASS); - o.len = htons((uint16_t)len); - memcpy(p, &o, sizeof(o)); - p += sizeof(o); - pen = htonl(ifo->vivco_en ? ifo->vivco_en : DHCPCD_IANA_PEN); - memcpy(p, &pen, sizeof(pen)); - p += sizeof(pen); - if (ifo->vivco_en) { + if (ifo->vivco_len > 0) { for (i = 0, vivco = ifo->vivco; i < ifo->vivco_len; - i++, vivco++) - { + i++, vivco++) { + optlen = sizeof(uint32_t) + sizeof(uint16_t) + vivco->len; + o.code = htons(D6_OPTION_VENDOR_CLASS); + o.len = htons((uint16_t)optlen); + memcpy(p, &o, sizeof(o)); + p += sizeof(o); + pen = htonl(vivco->en); + memcpy(p, &pen, sizeof(pen)); + p += sizeof(pen); hvlen = htons((uint16_t)vivco->len); memcpy(p, &hvlen, sizeof(hvlen)); p += sizeof(hvlen); @@ -332,17 +331,22 @@ dhcp6_makevendor(void *data, const struct interface *ifp) p += vivco->len; } } else if (ifo->vendorclassid[0] != '\0') { + o.code = htons(D6_OPTION_VENDOR_CLASS); + o.len = htons((uint16_t)len); + memcpy(p, &o, sizeof(o)); + p += sizeof(o); + pen = htonl(DHCPCD_IANA_PEN); + memcpy(p, &pen, sizeof(pen)); + p += sizeof(pen); hvlen = htons((uint16_t)vlen); memcpy(p, &hvlen, sizeof(hvlen)); p += sizeof(hvlen); memcpy(p, ifp->ctx->vendor, vlen); } } - - return sizeof(o) + len; + return len; } -#ifndef SMALL /* DHCPv6 Option 17 (Vendor-Specific Information Option) */ static size_t dhcp6_makevendoropts(void *data, const struct interface *ifp) @@ -875,10 +879,10 @@ dhcp6_makemessage(struct interface *ifp) if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS)) len += dhcp6_makeuser(NULL, ifp); - if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS)) - len += dhcp6_makevendor(NULL, ifp); #ifndef SMALL + if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS)) + len += dhcp6_makevendor(NULL, ifp); if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS)) len += dhcp6_makevendoropts(NULL, ifp); #endif @@ -1199,10 +1203,10 @@ dhcp6_makemessage(struct interface *ifp) if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS)) p += dhcp6_makeuser(p, ifp); - if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS)) - p += dhcp6_makevendor(p, ifp); #ifndef SMALL + if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS)) + p += dhcp6_makevendor(p, ifp); if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS)) p += dhcp6_makevendoropts(p, ifp); #endif diff --git a/src/if-options.c b/src/if-options.c index c50f65a1..54ae590b 100644 --- a/src/if-options.c +++ b/src/if-options.c @@ -656,6 +656,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, struct dhcp_opt **dop, *ndop; size_t *dop_len, dl, odl; struct vivco *vivco; + const struct vivco *vivco_endp = ifo->vivco + ifo->vivco_len; struct group *grp; #ifdef AUTH struct token *token; @@ -2111,6 +2112,10 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, break; case O_VENDCLASS: ARG_REQUIRED; +#ifdef SMALL + logwarnx("%s: vendor options not compiled in", ifname); + return -1; +#else fp = strwhite(arg); if (fp) *fp++ = '\0'; @@ -2119,6 +2124,12 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, logerrx("invalid code: %s", arg); return -1; } + for (vivco = ifo->vivco; vivco != vivco_endp; vivco++) { + if (vivco->en == (uint32_t)u) { + logerrx("vendor class option for enterprise number %u already defined", vivco->en); + return -1; + } + } fp = strskipwhite(fp); if (fp) { s = parse_string(NULL, 0, fp); @@ -2149,11 +2160,12 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, return -1; } ifo->vivco = vivco; - ifo->vivco_en = (uint32_t)u; vivco = &ifo->vivco[ifo->vivco_len++]; + vivco->en = (uint32_t)u; vivco->len = dl; vivco->data = (uint8_t *)np; break; +#endif case O_AUTHPROTOCOL: ARG_REQUIRED; #ifdef AUTH @@ -2994,12 +3006,12 @@ free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo) opt++, ifo->dhcp6_override_len--) free_dhcp_opt_embenc(opt); free(ifo->dhcp6_override); +#ifndef SMALL for (vo = ifo->vivco; ifo->vivco_len > 0; vo++, ifo->vivco_len--) free(vo->data); free(ifo->vivco); -#ifndef SMALL for (vsio = ifo->vsio; ifo->vsio_len > 0; vsio++, ifo->vsio_len--) diff --git a/src/if-options.h b/src/if-options.h index f3b9c17a..c892e75d 100644 --- a/src/if-options.h +++ b/src/if-options.h @@ -61,7 +61,6 @@ #define USERCLASS_MAX_LEN 255 #define VENDOR_MAX_LEN 255 #define MUDURL_MAX_LEN 255 -#define ENTERPRISE_NUMS_MAX_LEN 255 #define DHCPCD_ARP (1ULL << 0) #define DHCPCD_RELEASE (1ULL << 1) @@ -220,12 +219,12 @@ struct if_ia { #endif }; +#ifndef SMALL struct vivco { + uint32_t en; size_t len; uint8_t *data; }; - -#ifndef SMALL struct vsio_so { uint16_t opt; uint16_t len; @@ -303,13 +302,12 @@ struct if_options { size_t nd_override_len; struct dhcp_opt *dhcp6_override; size_t dhcp6_override_len; - uint32_t vivco_en; - struct vivco *vivco; - size_t vivco_len; struct dhcp_opt *vivso_override; size_t vivso_override_len; #ifndef SMALL + size_t vivco_len; + struct vivco *vivco; size_t vsio_len; struct vsio *vsio; size_t vsio6_len;