X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-network%2Fdhcp-option.c;h=b6110c5f166cc53b2a650e30e68f6012b6089aa7;hp=7bf8812676d214644e85c5500e832dd9288e7d35;hb=709d6710d047566c71f03e579a02c3d99fe15a3e;hpb=20b958bf157dfb2f521b191ef7158035bcaa3003 diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index 7bf881267..b6110c5f1 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -26,16 +26,20 @@ #include "dhcp-internal.h" -int dhcp_option_append(uint8_t options[], size_t size, size_t *offset, - uint8_t code, size_t optlen, const void *optval) { +static int option_append(uint8_t options[], size_t size, size_t *offset, + uint8_t code, size_t optlen, const void *optval) { assert(options); assert(offset); + if (code != DHCP_OPTION_END) + /* always make sure there is space for an END option */ + size --; + switch (code) { case DHCP_OPTION_PAD: case DHCP_OPTION_END: - if (size - *offset < 1) + if (size < *offset + 1) return -ENOBUFS; options[*offset] = code; @@ -43,14 +47,17 @@ int dhcp_option_append(uint8_t options[], size_t size, size_t *offset, break; default: - if (size - *offset < optlen + 2) + if (size < *offset + optlen + 2) return -ENOBUFS; - assert(optval); - options[*offset] = code; options[*offset + 1] = optlen; - memcpy(&options[*offset + 2], optval, optlen); + + if (optlen) { + assert(optval); + + memcpy(&options[*offset + 2], optval, optlen); + } *offset += optlen + 2; @@ -60,86 +67,157 @@ int dhcp_option_append(uint8_t options[], size_t size, size_t *offset, return 0; } -static int parse_options(const uint8_t *buf, size_t buflen, uint8_t *overload, +int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, + uint8_t overload, + uint8_t code, size_t optlen, const void *optval) { + size_t file_offset = 0, sname_offset =0; + bool file, sname; + int r; + + assert(message); + assert(offset); + + file = overload & DHCP_OVERLOAD_FILE; + sname = overload & DHCP_OVERLOAD_SNAME; + + if (*offset < size) { + /* still space in the options array */ + r = option_append(message->options, size, offset, code, optlen, optval); + if (r >= 0) + return 0; + else if (r == -ENOBUFS && (file || sname)) { + /* did not fit, but we have more buffers to try + close the options array and move the offset to its end */ + r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + *offset = size; + } else + return r; + } + + if (overload & DHCP_OVERLOAD_FILE) { + file_offset = *offset - size; + + if (file_offset < sizeof(message->file)) { + /* still space in the 'file' array */ + r = option_append(message->file, sizeof(message->file), &file_offset, code, optlen, optval); + if (r >= 0) { + *offset = size + file_offset; + return 0; + } else if (r == -ENOBUFS && sname) { + /* did not fit, but we have more buffers to try + close the file array and move the offset to its end */ + r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + *offset = size + sizeof(message->file); + } else + return r; + } + } + + if (overload & DHCP_OVERLOAD_SNAME) { + sname_offset = *offset - size - (file ? sizeof(message->file) : 0); + + if (sname_offset < sizeof(message->sname)) { + /* still space in the 'sname' array */ + r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval); + if (r >= 0) { + *offset = size + (file ? sizeof(message->file) : 0) + sname_offset; + return 0; + } else { + /* no space, or other error, give up */ + return r; + } + } + } + + return -ENOBUFS; +} + +static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload, uint8_t *message_type, dhcp_option_cb_t cb, - void *user_data) -{ - const uint8_t *code = buf; - const uint8_t *len; + void *user_data) { + uint8_t code, len; + size_t offset = 0; - while (buflen > 0) { - switch (*code) { + while (offset < buflen) { + switch (options[offset]) { case DHCP_OPTION_PAD: - buflen -= 1; - code++; + offset++; + break; case DHCP_OPTION_END: return 0; case DHCP_OPTION_MESSAGE_TYPE: - if (buflen < 3) + if (buflen < offset + 3) return -ENOBUFS; - buflen -= 3; - len = code + 1; - if (*len != 1) + len = options[++offset]; + if (len != 1) return -EINVAL; if (message_type) - *message_type = *(len + 1); + *message_type = options[++offset]; + else + offset++; - code += 3; + offset++; break; case DHCP_OPTION_OVERLOAD: - if (buflen < 3) + if (buflen < offset + 3) return -ENOBUFS; - buflen -= 3; - len = code + 1; - if (*len != 1) + len = options[++offset]; + if (len != 1) return -EINVAL; if (overload) - *overload = *(len + 1); + *overload = options[++offset]; + else + offset++; - code += 3; + offset++; break; default: - if (buflen < 3) + if (buflen < offset + 3) return -ENOBUFS; - len = code + 1; + code = options[offset]; + len = options[++offset]; - if (buflen < (size_t)*len + 2) + if (buflen < ++offset + len) return -EINVAL; - buflen -= *len + 2; if (cb) - cb(*code, *len, len + 1, user_data); + cb(code, len, &options[offset], user_data); - code += *len + 2; + offset += len; break; } } - if (buflen) + if (offset < buflen) return -EINVAL; return 0; } int dhcp_option_parse(DHCPMessage *message, size_t len, - dhcp_option_cb_t cb, void *user_data) -{ + dhcp_option_cb_t cb, void *user_data) { uint8_t overload = 0; uint8_t message_type = 0; - int res; + int r; if (!message) return -EINVAL; @@ -149,23 +227,23 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, len -= sizeof(DHCPMessage); - res = parse_options(message->options, len, &overload, &message_type, - cb, user_data); - if (res < 0) - return res; + r = parse_options(message->options, len, &overload, &message_type, + cb, user_data); + if (r < 0) + return r; if (overload & DHCP_OVERLOAD_FILE) { - res = parse_options(message->file, sizeof(message->file), + r = parse_options(message->file, sizeof(message->file), NULL, &message_type, cb, user_data); - if (res < 0) - return res; + if (r < 0) + return r; } if (overload & DHCP_OVERLOAD_SNAME) { - res = parse_options(message->sname, sizeof(message->sname), + r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, cb, user_data); - if (res < 0) - return res; + if (r < 0) + return r; } if (message_type)