From 4124b03b8c8f4942358abaf3153bf55bbddd539c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 1 Oct 2022 15:22:23 +0900 Subject: [PATCH 1/9] sd-dhcp6-client: do not use implicit cast from integer to boolean --- src/libsystemd-network/sd-dhcp6-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 606e48ce6f..b774772229 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -779,7 +779,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { if (r < 0) return r; - assert(client->duid_len); + assert(client->duid_len > 0); r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID, client->duid_len, &client->duid); if (r < 0) From 93ed1c0eb9b9434ffc292f09f533dc0decad6318 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 1 Oct 2022 15:27:12 +0900 Subject: [PATCH 2/9] sd-dhcp6-client: use assert() in non-public functions --- src/libsystemd-network/dhcp6-option.c | 30 +++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 5e91e86f53..0d26aef234 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -212,9 +212,9 @@ bool dhcp6_option_can_request(uint16_t option) { } static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { - assert_return(buf, -EINVAL); - assert_return(*buf, -EINVAL); - assert_return(buflen, -EINVAL); + assert(buf); + assert(*buf); + assert(buflen); if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) return -ENOBUFS; @@ -228,11 +228,16 @@ static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, si return 0; } -int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, - size_t optlen, const void *optval) { +int dhcp6_option_append( + uint8_t **buf, + size_t *buflen, + uint16_t code, + size_t optlen, + const void *optval) { + int r; - assert_return(optval || optlen == 0, -EINVAL); + assert(optval || optlen == 0); r = option_append_hdr(buf, buflen, code, optlen); if (r < 0) @@ -335,10 +340,10 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { uint16_t len; int r; - assert_return(buf, -EINVAL); - assert_return(*buf, -EINVAL); - assert_return(buflen, -EINVAL); - assert_return(ia, -EINVAL); + assert(buf); + assert(*buf); + assert(buflen); + assert(ia); /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */ @@ -393,7 +398,10 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { uint8_t buffer[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX]; int r; - assert_return(buf && *buf && buflen && fqdn, -EINVAL); + assert(buf); + assert(*buf); + assert(buflen); + assert(fqdn); buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */ From 4ec5b5c7785b21d958118cb741c98654bcb9e25a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 1 Oct 2022 15:31:50 +0900 Subject: [PATCH 3/9] sd-dhcp6-client: make dhcp6_option_append_fqdn() or friends handle zero length value gracefully --- src/libsystemd-network/dhcp6-option.c | 13 +++++++--- src/libsystemd-network/dhcp6-option.h | 2 +- src/libsystemd-network/sd-dhcp6-client.c | 32 +++++++++--------------- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 0d26aef234..4fd75fbf3b 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -256,7 +256,6 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet assert(buf); assert(*buf); assert(buflen); - assert(vendor_options); ORDERED_SET_FOREACH(options, vendor_options) { _cleanup_free_ uint8_t *p = NULL; @@ -401,7 +400,9 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { assert(buf); assert(*buf); assert(buflen); - assert(fqdn); + + if (isempty(fqdn)) + return 0; buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */ @@ -431,7 +432,9 @@ int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const * assert(buf); assert(*buf); assert(buflen); - assert(!strv_isempty(user_class)); + + if (strv_isempty(user_class)) + return 0; STRV_FOREACH(s, user_class) { size_t len = strlen(*s); @@ -463,7 +466,9 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const assert(buf); assert(*buf); assert(buflen); - assert(!strv_isempty(vendor_class)); + + if (strv_isempty(vendor_class)) + return 0; enterprise_identifier = htobe32(SYSTEMD_PEN); diff --git a/src/libsystemd-network/dhcp6-option.h b/src/libsystemd-network/dhcp6-option.h index 80aba7f37f..d044222124 100644 --- a/src/libsystemd-network/dhcp6-option.h +++ b/src/libsystemd-network/dhcp6-option.h @@ -77,7 +77,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); -int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class); +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class); int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options); int dhcp6_option_parse( diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index b774772229..2704e1ca5a 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -602,29 +602,21 @@ static int client_append_common_options_in_managed_mode( return r; } - if (client->fqdn) { - r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn); - if (r < 0) - return r; - } + r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn); + if (r < 0) + return r; - if (client->user_class) { - r = dhcp6_option_append_user_class(opt, optlen, client->user_class); - if (r < 0) - return r; - } + r = dhcp6_option_append_user_class(opt, optlen, client->user_class); + if (r < 0) + return r; - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class); - if (r < 0) - return r; - } + r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class); + if (r < 0) + return r; - if (!ordered_set_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options); - if (r < 0) - return r; - } + r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options); + if (r < 0) + return r; return 0; } From 6f8c3de6b7ff74f567b015f0e75c32b06d9d5d41 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 1 Oct 2022 15:42:41 +0900 Subject: [PATCH 4/9] sd-dhcp6-client: simplify dhcp6_option_append_ia() --- src/libsystemd-network/dhcp6-option.c | 47 +++++---------------------- 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 4fd75fbf3b..de54c36a71 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -282,7 +282,6 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) { struct iaaddr a; - int r; assert(buf); assert(*buf); @@ -294,19 +293,11 @@ static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct .address = address->address, }; - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); - if (r < 0) - return r; - - *buf = mempcpy(*buf, &a, sizeof(struct iaaddr)); - *buflen -= sizeof(struct iaaddr); - - return offsetof(DHCP6Option, data) + sizeof(struct iaaddr); + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr), &a); } static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) { struct iapdprefix p; - int r; assert(buf); assert(*buf); @@ -322,21 +313,13 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct i .address = prefix->address, }; - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); - if (r < 0) - return r; - - *buf = mempcpy(*buf, &p, sizeof(struct iapdprefix)); - *buflen -= sizeof(struct iapdprefix); - - return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix), &p); } int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { struct ia_header header; - size_t ia_buflen; - uint8_t *ia_hdr; - uint16_t len; + uint8_t data[512], *p; + size_t len, size; int r; assert(buf); @@ -366,31 +349,19 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { assert_not_reached(); } - if (*buflen < offsetof(DHCP6Option, data) + len) - return -ENOBUFS; - - ia_hdr = *buf; - ia_buflen = *buflen; - - /* The header will be written at the end of this function. */ - *buf += offsetof(DHCP6Option, data); - *buflen -= offsetof(DHCP6Option, data); - - *buf = mempcpy(*buf, &header, len); - *buflen -= len; + p = mempcpy(data, &header, len); + size = sizeof(data) - len; LIST_FOREACH(addresses, addr, ia->addresses) { if (ia->type == SD_DHCP6_OPTION_IA_PD) - r = option_append_pd_prefix(buf, buflen, &addr->iapdprefix); + r = option_append_pd_prefix(&p, &size, &addr->iapdprefix); else - r = option_append_ia_address(buf, buflen, &addr->iaaddr); + r = option_append_ia_address(&p, &size, &addr->iaaddr); if (r < 0) return r; - - len += r; } - return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len); + return dhcp6_option_append(buf, buflen, ia->type, p - data, data); } int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { From 451daa394e6d256c33599794559454e765243b4e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 1 Oct 2022 15:44:02 +0900 Subject: [PATCH 5/9] sd-dhcp6-client: slightly shorten dhcp6_option_append_fqdn() --- src/libsystemd-network/dhcp6-option.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index de54c36a71..f257c56681 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -378,7 +378,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */ /* Store domain name after flags field */ - r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1, false); + r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1, false); if (r <= 0) return r; @@ -391,9 +391,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { if (dns_name_is_single_label(fqdn)) r--; - r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer); - - return r; + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer); } int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) { From 60cbf2ea8228e3d2b7dd06fe6ad25dbf09339fef Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 1 Oct 2022 15:48:13 +0900 Subject: [PATCH 6/9] sd-dhcp6-client: use GREEDY_REALLOC() And merge 'total' and 'offset' -> 'n' --- src/libsystemd-network/dhcp6-option.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index f257c56681..f0a6b53a42 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -396,7 +396,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) { _cleanup_free_ uint8_t *p = NULL; - size_t total = 0, offset = 0; + size_t n = 0; assert(buf); assert(*buf); @@ -407,24 +407,19 @@ int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const * STRV_FOREACH(s, user_class) { size_t len = strlen(*s); - uint8_t *q; - if (len > 0xffff || len == 0) + if (len > UINT16_MAX || len == 0) return -EINVAL; - q = realloc(p, total + len + 2); - if (!q) + + if (!GREEDY_REALLOC(p, n + len + 2)) return -ENOMEM; - p = q; - - unaligned_write_be16(&p[offset], len); - memcpy(&p[offset + 2], *s, len); - - offset += 2 + len; - total += 2 + len; + unaligned_write_be16(p + n, len); + memcpy(p + n + 2, *s, len); + n += len + 2; } - return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p); + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, n, p); } int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class) { From cb9c303d70a30853898a2833634949ad35e9fa41 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 1 Oct 2022 15:53:33 +0900 Subject: [PATCH 7/9] sd-dhcp6-client: use GREEDY_REALLOC() in dhcp6_option_append_vendor_class() --- src/libsystemd-network/dhcp6-option.c | 29 +++++++++------------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index f0a6b53a42..d258c9aafa 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -424,8 +424,7 @@ int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const * int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class) { _cleanup_free_ uint8_t *p = NULL; - uint32_t enterprise_identifier; - size_t total, offset; + size_t n = 0; assert(buf); assert(*buf); @@ -434,36 +433,28 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const if (strv_isempty(vendor_class)) return 0; - enterprise_identifier = htobe32(SYSTEMD_PEN); - - p = memdup(&enterprise_identifier, sizeof(enterprise_identifier)); - if (!p) + if (!GREEDY_REALLOC(p, sizeof(be32_t))) return -ENOMEM; - total = sizeof(enterprise_identifier); - offset = total; + /* Enterprise Identifier */ + unaligned_write_be32(p, SYSTEMD_PEN); + n += sizeof(be32_t); STRV_FOREACH(s, vendor_class) { size_t len = strlen(*s); - uint8_t *q; if (len > UINT16_MAX || len == 0) return -EINVAL; - q = realloc(p, total + len + 2); - if (!q) + if (!GREEDY_REALLOC(p, n + len + 2)) return -ENOMEM; - p = q; - - unaligned_write_be16(&p[offset], len); - memcpy(&p[offset + 2], *s, len); - - offset += 2 + len; - total += 2 + len; + unaligned_write_be16(p + n, len); + memcpy(p + n + 2, *s, len); + n += len + 2; } - return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p); + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, n, p); } int dhcp6_option_parse( From 045422384093b97d21ae6296bd651555d9eeebea Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 2 Oct 2022 22:51:31 +0900 Subject: [PATCH 8/9] sd-dhcp6-client: rename buf -> p No functional changes, just preparation for the next commit. --- src/libsystemd-network/sd-dhcp6-client.c | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 2704e1ca5a..e27a5c4fe1 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -641,7 +641,7 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) } static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *optlen) { - _cleanup_free_ be16_t *buf = NULL; + _cleanup_free_ be16_t *p = NULL; be16_t *req_opts; size_t n; @@ -652,29 +652,29 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *opt switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: n = client->n_req_opts; - buf = new(be16_t, n + 2); - if (!buf) + p = new(be16_t, n + 2); + if (!p) return -ENOMEM; - memcpy_safe(buf, client->req_opts, n * sizeof(be16_t)); - buf[n++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME); /* RFC 8415 section 21.23 */ - buf[n++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT); /* RFC 8415 section 21.25 */ + memcpy_safe(p, client->req_opts, n * sizeof(be16_t)); + p[n++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME); /* RFC 8415 section 21.23 */ + p[n++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT); /* RFC 8415 section 21.25 */ - typesafe_qsort(buf, n, be16_compare_func); - req_opts = buf; + typesafe_qsort(p, n, be16_compare_func); + req_opts = p; break; case DHCP6_STATE_SOLICITATION: n = client->n_req_opts; - buf = new(be16_t, n + 1); - if (!buf) + p = new(be16_t, n + 1); + if (!p) return -ENOMEM; - memcpy_safe(buf, client->req_opts, n * sizeof(be16_t)); - buf[n++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT); /* RFC 8415 section 21.24 */ + memcpy_safe(p, client->req_opts, n * sizeof(be16_t)); + p[n++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT); /* RFC 8415 section 21.24 */ - typesafe_qsort(buf, n, be16_compare_func); - req_opts = buf; + typesafe_qsort(p, n, be16_compare_func); + req_opts = p; break; default: From 9d2d346aaeda53c3dc0c4b3077ec2e39fdfa61c4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 30 Sep 2022 03:37:02 +0900 Subject: [PATCH 9/9] sd-dhcp6-client: allow to build large packet Previously, the maximum packet size was hardcoded with 512 + size of the DHCP header. This makes the packet size increased when necessary. See option_append_hdr(). Previously, all functions which append DHCP options took the head of the unused area of the packet, and the size of the free area. However, with this change, the buffer for the whole packet may be reallocated, hence now they take the head of the packet and the offset to the free area. Fixes #24851. --- src/libsystemd-network/dhcp6-option.c | 82 ++++++++++++---------- src/libsystemd-network/dhcp6-option.h | 12 ++-- src/libsystemd-network/sd-dhcp6-client.c | 69 +++++++++--------- src/libsystemd-network/test-dhcp6-client.c | 26 +++---- 4 files changed, 91 insertions(+), 98 deletions(-) diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index d258c9aafa..0b8393ca2c 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -211,26 +211,30 @@ bool dhcp6_option_can_request(uint16_t option) { } } -static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { +static int option_append_hdr(uint8_t **buf, size_t *offset, uint16_t optcode, size_t optlen) { assert(buf); assert(*buf); - assert(buflen); + assert(offset); - if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) + if (optlen > 0xffff) return -ENOBUFS; - unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode); - unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen); + if (optlen + offsetof(DHCP6Option, data) > SIZE_MAX - *offset) + return -ENOBUFS; - *buf += offsetof(DHCP6Option, data); - *buflen -= offsetof(DHCP6Option, data); + if (!GREEDY_REALLOC(*buf, *offset + optlen + offsetof(DHCP6Option, data))) + return -ENOMEM; + unaligned_write_be16(*buf + *offset + offsetof(DHCP6Option, code), optcode); + unaligned_write_be16(*buf + *offset + offsetof(DHCP6Option, len), optlen); + + *offset += offsetof(DHCP6Option, data); return 0; } int dhcp6_option_append( uint8_t **buf, - size_t *buflen, + size_t *offset, uint16_t code, size_t optlen, const void *optval) { @@ -239,23 +243,23 @@ int dhcp6_option_append( assert(optval || optlen == 0); - r = option_append_hdr(buf, buflen, code, optlen); + r = option_append_hdr(buf, offset, code, optlen); if (r < 0) return r; - *buf = mempcpy_safe(*buf, optval, optlen); - *buflen -= optlen; + memcpy_safe(*buf + *offset, optval, optlen); + *offset += optlen; return 0; } -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) { +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *offset, OrderedSet *vendor_options) { sd_dhcp6_option *options; int r; assert(buf); assert(*buf); - assert(buflen); + assert(offset); ORDERED_SET_FOREACH(options, vendor_options) { _cleanup_free_ uint8_t *p = NULL; @@ -272,7 +276,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet unaligned_write_be16(p + 6, options->length); memcpy(p + 8, options->data, options->length); - r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p); + r = dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_VENDOR_OPTS, total, p); if (r < 0) return r; } @@ -280,12 +284,12 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet return 0; } -static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) { +static int option_append_ia_address(uint8_t **buf, size_t *offset, const struct iaaddr *address) { struct iaaddr a; assert(buf); assert(*buf); - assert(buflen); + assert(offset); assert(address); /* Do not append T1 and T2. */ @@ -293,15 +297,15 @@ static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct .address = address->address, }; - return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr), &a); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr), &a); } -static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) { +static int option_append_pd_prefix(uint8_t **buf, size_t *offset, const struct iapdprefix *prefix) { struct iapdprefix p; assert(buf); assert(*buf); - assert(buflen); + assert(offset); assert(prefix); if (prefix->prefixlen == 0) @@ -313,18 +317,18 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct i .address = prefix->address, }; - return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix), &p); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix), &p); } -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { +int dhcp6_option_append_ia(uint8_t **buf, size_t *offset, const DHCP6IA *ia) { + _cleanup_free_ uint8_t *data = NULL; struct ia_header header; - uint8_t data[512], *p; - size_t len, size; + size_t len; int r; assert(buf); assert(*buf); - assert(buflen); + assert(offset); assert(ia); /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */ @@ -349,28 +353,30 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { assert_not_reached(); } - p = mempcpy(data, &header, len); - size = sizeof(data) - len; + if (!GREEDY_REALLOC(data, len)) + return -ENOMEM; + + memcpy(data, &header, len); LIST_FOREACH(addresses, addr, ia->addresses) { if (ia->type == SD_DHCP6_OPTION_IA_PD) - r = option_append_pd_prefix(&p, &size, &addr->iapdprefix); + r = option_append_pd_prefix(&data, &len, &addr->iapdprefix); else - r = option_append_ia_address(&p, &size, &addr->iaaddr); + r = option_append_ia_address(&data, &len, &addr->iaaddr); if (r < 0) return r; } - return dhcp6_option_append(buf, buflen, ia->type, p - data, data); + return dhcp6_option_append(buf, offset, ia->type, len, data); } -int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { +int dhcp6_option_append_fqdn(uint8_t **buf, size_t *offset, const char *fqdn) { uint8_t buffer[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX]; int r; assert(buf); assert(*buf); - assert(buflen); + assert(offset); if (isempty(fqdn)) return 0; @@ -391,16 +397,16 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { if (dns_name_is_single_label(fqdn)) r--; - return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer); } -int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) { +int dhcp6_option_append_user_class(uint8_t **buf, size_t *offset, char * const *user_class) { _cleanup_free_ uint8_t *p = NULL; size_t n = 0; assert(buf); assert(*buf); - assert(buflen); + assert(offset); if (strv_isempty(user_class)) return 0; @@ -419,16 +425,16 @@ int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const * n += len + 2; } - return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, n, p); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_USER_CLASS, n, p); } -int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class) { +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *offset, char * const *vendor_class) { _cleanup_free_ uint8_t *p = NULL; size_t n = 0; assert(buf); assert(*buf); - assert(buflen); + assert(offset); if (strv_isempty(vendor_class)) return 0; @@ -454,7 +460,7 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const n += len + 2; } - return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, n, p); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_VENDOR_CLASS, n, p); } int dhcp6_option_parse( diff --git a/src/libsystemd-network/dhcp6-option.h b/src/libsystemd-network/dhcp6-option.h index d044222124..36841dd270 100644 --- a/src/libsystemd-network/dhcp6-option.h +++ b/src/libsystemd-network/dhcp6-option.h @@ -72,13 +72,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); bool dhcp6_option_can_request(uint16_t option); -int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, +int dhcp6_option_append(uint8_t **buf, size_t *offset, uint16_t code, size_t optlen, const void *optval); -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); -int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); -int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); -int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class); -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options); +int dhcp6_option_append_ia(uint8_t **buf, size_t *offset, const DHCP6IA *ia); +int dhcp6_option_append_fqdn(uint8_t **buf, size_t *offset, const char *fqdn); +int dhcp6_option_append_user_class(uint8_t **buf, size_t *offset, char * const *user_class); +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *offset, char * const *vendor_class); +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *offset, OrderedSet *vendor_options); int dhcp6_option_parse( const uint8_t *buf, diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index e27a5c4fe1..375f984940 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -574,8 +574,8 @@ static void client_stop(sd_dhcp6_client *client, int error) { static int client_append_common_options_in_managed_mode( sd_dhcp6_client *client, - uint8_t **opt, - size_t *optlen, + uint8_t **buf, + size_t *offset, const DHCP6IA *ia_na, const DHCP6IA *ia_pd) { @@ -587,34 +587,35 @@ static int client_append_common_options_in_managed_mode( DHCP6_STATE_REQUEST, DHCP6_STATE_RENEW, DHCP6_STATE_REBIND)); - assert(opt); - assert(optlen); + assert(buf); + assert(*buf); + assert(offset); if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) { - r = dhcp6_option_append_ia(opt, optlen, ia_na); + r = dhcp6_option_append_ia(buf, offset, ia_na); if (r < 0) return r; } if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) { - r = dhcp6_option_append_ia(opt, optlen, ia_pd); + r = dhcp6_option_append_ia(buf, offset, ia_pd); if (r < 0) return r; } - r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn); + r = dhcp6_option_append_fqdn(buf, offset, client->fqdn); if (r < 0) return r; - r = dhcp6_option_append_user_class(opt, optlen, client->user_class); + r = dhcp6_option_append_user_class(buf, offset, client->user_class); if (r < 0) return r; - r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class); + r = dhcp6_option_append_vendor_class(buf, offset, client->vendor_class); if (r < 0) return r; - r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options); + r = dhcp6_option_append_vendor_option(buf, offset, client->vendor_options); if (r < 0) return r; @@ -640,14 +641,15 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) } } -static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *optlen) { +static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) { _cleanup_free_ be16_t *p = NULL; be16_t *req_opts; size_t n; assert(client); - assert(opt); - assert(optlen); + assert(buf); + assert(*buf); + assert(offset); switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: @@ -685,18 +687,18 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *opt if (n == 0) return 0; - return dhcp6_option_append(opt, optlen, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts); } int dhcp6_client_send_message(sd_dhcp6_client *client) { - _cleanup_free_ DHCP6Message *message = NULL; + _cleanup_free_ uint8_t *buf = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; struct sd_dhcp6_option *j; - size_t len, optlen = 512; - uint8_t *opt; usec_t elapsed_usec, time_now; be16_t elapsed_time; + DHCP6Message *message; + size_t offset; int r; assert(client); @@ -706,16 +708,13 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { if (r < 0) return r; - len = sizeof(DHCP6Message) + optlen; - - message = malloc0(len); - if (!message) + if (!GREEDY_REALLOC0(buf, offsetof(DHCP6Message, options))) return -ENOMEM; - opt = (uint8_t *)(message + 1); - + message = (DHCP6Message*) buf; message->transaction_id = client->transaction_id; message->type = client_message_type_from_state(client); + offset = offsetof(DHCP6Message, options); switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: @@ -723,12 +722,12 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { case DHCP6_STATE_SOLICITATION: if (client->rapid_commit) { - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); if (r < 0) return r; } - r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + r = client_append_common_options_in_managed_mode(client, &buf, &offset, &client->ia_na, &client->ia_pd); if (r < 0) return r; @@ -737,7 +736,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); if (r < 0) @@ -748,7 +747,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { assert(client->lease); - r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + r = client_append_common_options_in_managed_mode(client, &buf, &offset, client->lease->ia_na, client->lease->ia_pd); if (r < 0) return r; @@ -761,24 +760,24 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { } if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_MUD_URL_V6, + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), client->mudurl); if (r < 0) return r; } - r = client_append_oro(client, &opt, &optlen); + r = client_append_oro(client, &buf, &offset); if (r < 0) return r; assert(client->duid_len > 0); - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID, + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID, client->duid_len, &client->duid); if (r < 0) return r; ORDERED_HASHMAP_FOREACH(j, client->extra_options) { - r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); + r = dhcp6_option_append(&buf, &offset, j->option, j->length, j->data); if (r < 0) return r; } @@ -788,18 +787,16 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { * been trying to complete a DHCP message exchange. */ elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX); elapsed_time = htobe16(elapsed_usec); - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time); + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time); if (r < 0) return r; - r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message, - len - optlen); + r = dhcp6_network_send_udp_socket(client->fd, &all_servers, buf, offset); if (r < 0) return r; log_dhcp6_client(client, "Sent %s", - dhcp6_message_type_to_string(message->type)); - + dhcp6_message_type_to_string(client_message_type_from_state(client))); return 0; } diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index e5c09f9664..c3c89514f8 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -179,7 +179,7 @@ TEST(parse_domain) { } TEST(option) { - uint8_t packet[] = { + static const uint8_t packet[] = { 'F', 'O', 'O', 'H', 'O', 'G', 'E', 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07, 'A', 'B', 'C', 'D', 'E', 'F', 'G', @@ -187,7 +187,7 @@ TEST(option) { '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'A', 'R', }; - uint8_t result[] = { + static const uint8_t result[] = { 'F', 'O', 'O', 'H', 'O', 'G', 'E', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -195,10 +195,9 @@ TEST(option) { 'B', 'A', 'R', }; _cleanup_free_ uint8_t *buf = NULL; - size_t offset, pos, optlen, outlen = sizeof(result); + size_t offset, pos, optlen; const uint8_t *optval; uint16_t optcode; - uint8_t *out; assert_se(sizeof(packet) == sizeof(result)); @@ -227,15 +226,10 @@ TEST(option) { assert_se(optlen == 7); assert_se(optval == packet + 11); + free(buf); + assert_se(buf = memdup(result, sizeof(result))); pos = 7; - outlen -= 7; - out = &result[pos]; - - assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0); - - pos += 4 + optlen; - assert_se(out == &result[pos]); - assert_se(*out == 0x00); + assert_se(dhcp6_option_append(&buf, &pos, optcode, optlen, optval) >= 0); assert_se(dhcp6_option_parse(packet, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0); @@ -243,13 +237,9 @@ TEST(option) { assert_se(optlen == 9); assert_se(optval == packet + 22); - assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0); + assert_se(dhcp6_option_append(&buf, &pos, optcode, optlen, optval) >= 0); - pos += 4 + optlen; - assert_se(out == &result[pos]); - assert_se(*out == 'B'); - - assert_se(memcmp(packet, result, sizeof(packet)) == 0); + assert_se(memcmp(packet, buf, sizeof(packet)) == 0); } TEST(option_status) {