+static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
+ assert(option);
+ assert(ret);
+
+ if (len == 4) {
+ *ret = unaligned_read_be32((be32_t*) option);
+
+ if (*ret < min)
+ *ret = min;
+ }
+}
+
+static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
+ lease_parse_u32(option, len, (uint32_t *)ret, 0);
+}
+
+static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
+ assert(option);
+ assert(ret);
+
+ if (len == 2) {
+ *ret = unaligned_read_be16((be16_t*) option);
+
+ if (*ret < min)
+ *ret = min;
+ }
+}
+
+static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
+ assert(option);
+ assert(ret);
+
+ if (len == 4)
+ memcpy(ret, option, 4);
+}
+
+static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
+ assert(option);
+ assert(ret);
+
+ if (len == 1)
+ *ret = !!(*option);
+}
+
+static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
+ assert(option);
+ assert(ret);
+
+ if (len == 1) {
+ *ret = *option;
+
+ if (*ret < min)
+ *ret = min;
+ }
+}
+
+static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
+ assert(option);
+ assert(ret);
+
+ if (len >= 1) {
+ char *string;
+
+ string = strndup((const char *)option, len);
+ if (!string)
+ return -errno;
+
+ free(*ret);
+ *ret = string;
+ }
+
+ return 0;
+}
+
+static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) {
+ assert(option);
+ assert(ret);
+ assert(ret_size);
+
+ if (len && !(len % (4 * mult))) {
+ size_t size;
+ struct in_addr *addresses;
+
+ size = len / 4;
+
+ addresses = newdup(struct in_addr, option, size);
+ if (!addresses)
+ return -ENOMEM;
+
+ free(*ret);
+ *ret = addresses;
+ *ret_size = size;
+ }
+
+ return 0;
+}
+
+static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
+ return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
+}
+
+static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
+ return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
+}
+
+static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
+ size_t *routes_size, size_t *routes_allocated) {
+
+ struct in_addr addr;
+
+ assert(option);
+ assert(routes);
+ assert(routes_size);
+ assert(routes_allocated);
+
+ if (!len)
+ return 0;
+
+ if (len % 8 != 0)
+ return -EINVAL;
+
+ if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
+ return -ENOMEM;
+
+ while (len >= 8) {
+ struct sd_dhcp_route *route = *routes + *routes_size;
+ int r;
+
+ r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
+ if (r < 0) {
+ log_error("Failed to determine destination prefix length from class based IP, ignoring");
+ continue;
+ }
+
+ lease_parse_be32(option, 4, &addr.s_addr);
+ route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
+ option += 4;
+
+ lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+ option += 4;
+
+ len -= 8;
+ (*routes_size)++;
+ }
+
+ return 0;
+}
+
+/* parses RFC3442 Classless Static Route Option */
+static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
+ size_t *routes_size, size_t *routes_allocated) {
+
+ assert(option);
+ assert(routes);
+ assert(routes_size);
+ assert(routes_allocated);
+
+ /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
+
+ while (len > 0) {
+ uint8_t dst_octets;
+ struct sd_dhcp_route *route;
+
+ if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
+ return -ENOMEM;
+
+ route = *routes + *routes_size;
+
+ dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
+ route->dst_prefixlen = *option;
+ option++;
+ len--;
+
+ /* can't have more than 4 octets in IPv4 */
+ if (dst_octets > 4 || len < dst_octets)
+ return -EINVAL;
+
+ route->dst_addr.s_addr = 0;
+ memcpy(&route->dst_addr.s_addr, option, dst_octets);
+ option += dst_octets;
+ len -= dst_octets;
+
+ if (len < 4)
+ return -EINVAL;
+
+ lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+ option += 4;
+ len -= 4;
+
+ (*routes_size)++;
+ }
+
+ return 0;
+}
+