#include "dhcp-client-internal.h"
#include "dhcp-lease-internal.h"
#include "dhcp-option.h"
-#include "dhcp-route.h"
-#include "dns-def.h"
-#include "dns-domain.h"
+#include "dhcp-route.h" /* IWYU pragma: keep */
#include "dns-resolver-internal.h"
-#include "hostname-util.h"
#include "in-addr-util.h"
#include "ip-util.h"
-#include "network-common.h"
#include "set.h"
-#include "sort-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
-#include "unaligned.h"
void dhcp_lease_set_timestamp(sd_dhcp_lease *lease, const triple_timestamp *timestamp) {
assert(lease);
int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr);
}
-int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) {
- return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr);
-}
-int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) {
- return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr);
-}
-int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) {
- return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr);
-}
int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
assert_return(lease, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
- assert_return(lease, -EINVAL);
- assert_return(hostname, -EINVAL);
-
- /* FQDN option (81) always takes precedence. */
-
- if (lease->fqdn)
- *hostname = lease->fqdn;
- else if (lease->hostname)
- *hostname = lease->hostname;
- else
- return -ENODATA;
-
- return 0;
-}
-
-int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
+int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **ret) {
assert_return(lease, -EINVAL);
- assert_return(root_path, -EINVAL);
- if (!lease->root_path)
+ if (!lease->hostname)
return -ENODATA;
- *root_path = lease->root_path;
+ if (ret)
+ *ret = lease->hostname;
return 0;
}
return 0;
}
-int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
- assert_return(lease, -EINVAL);
- assert_return(addr, -EINVAL);
-
- if (lease->next_server == 0)
- return -ENODATA;
-
- addr->s_addr = lease->next_server;
- return 0;
-}
-
/*
* The returned routes array must be freed by the caller.
* Route objects have the same lifetime of the lease and must not be freed.
return lease && lease->sixrd_n_br_addresses > 0;
}
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
- assert_return(lease, -EINVAL);
- assert_return(data, -EINVAL);
- assert_return(data_len, -EINVAL);
-
- if (lease->vendor_specific_len <= 0)
- return -ENODATA;
-
- *data = lease->vendor_specific;
- *data_len = lease->vendor_specific_len;
- return 0;
-}
-
static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
- struct sd_dhcp_raw_option *option;
-
assert(lease);
sd_dhcp_message_unref(lease->message);
- while ((option = LIST_POP(options, lease->private_options))) {
- free(option->data);
- free(option);
- }
-
- free(lease->root_path);
free(lease->router);
free(lease->timezone);
free(lease->hostname);
- free(lease->fqdn);
free(lease->domainname);
free(lease->captive_portal);
dns_resolver_free_array(lease->dnr, lease->n_dnr);
free(lease->static_routes);
free(lease->classless_routes);
- free(lease->vendor_specific);
strv_free(lease->search_domains);
free(lease->sixrd_br_addresses);
return mfree(lease);
DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_lease, sd_dhcp_lease, dhcp_lease_free);
-static int lease_parse_be32_seconds(const uint8_t *option, size_t len, bool max_as_infinity, usec_t *ret) {
- assert(option);
- assert(ret);
-
- if (len != 4)
- return -EINVAL;
-
- *ret = unaligned_be32_sec_to_usec(option, max_as_infinity);
- return 0;
-}
-
-static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
- assert(option);
- assert(ret);
-
- if (len != 2)
- return -EINVAL;
-
- *ret = unaligned_read_be16((be16_t*) option);
- if (*ret < min)
- *ret = min;
-
- return 0;
-}
-
-static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
- assert(option);
- assert(ret);
-
- if (len != 4)
- return -EINVAL;
-
- memcpy(ret, option, 4);
- return 0;
-}
-
-static int lease_parse_domain(const uint8_t *option, size_t len, char **domain) {
- _cleanup_free_ char *name = NULL, *normalized = NULL;
- int r;
-
- assert(option);
- assert(domain);
-
- r = dhcp_option_parse_string(option, len, &name);
- if (r < 0)
- return r;
- if (!name) {
- *domain = mfree(*domain);
- return 0;
- }
-
- r = dns_name_normalize(name, 0, &normalized);
- if (r < 0)
- return r;
-
- if (is_localhost(normalized))
- return -EINVAL;
-
- if (dns_name_is_root(normalized))
- return -EINVAL;
-
- return free_and_replace(*domain, normalized);
-}
-
-static int lease_parse_fqdn(const uint8_t *option, size_t len, char **fqdn) {
- _cleanup_free_ char *name = NULL, *normalized = NULL;
- int r;
-
- assert(option);
- assert(fqdn);
-
- /* RFC 4702 Section 2
- *
- * Byte 0: Flags (S: server should perform A RR updates, O: override existing A RR,
- * E: encoding (0=ASCII, 1=Wire format), N: no server updates)
- * Byte 1: RCODE1 (ignored on receipt)
- * Byte 2: RCODE2 (ignored on receipt)
- * Bytes 3+: Domain Name */
-
- if (len <= 3)
- return -EBADMSG;
-
- size_t data_len = len - 3;
- const uint8_t *data = option + 3;
-
- /* In practice, many servers send DNS wire format regardless of the E flag, so ignore and try wire
- * format first, then fall back to ASCII if that fails. */
- r = dns_name_from_wire_format(&data, &data_len, &name);
- if (r < 0) {
- if (FLAGS_SET(option[0], DHCP_FQDN_FLAG_E))
- return -EBADMSG;
-
- /* Wire format failed, try ASCII format */
- r = dhcp_option_parse_string(option + 3, len - 3, &name);
- if (r < 0)
- return r;
- }
-
- if (!name) {
- *fqdn = mfree(*fqdn);
- return 0;
- }
-
- r = dns_name_normalize(name, 0, &normalized);
- if (r < 0)
- return r;
-
- if (is_localhost(normalized))
- return -EINVAL;
-
- if (dns_name_is_root(normalized))
- return -EINVAL;
-
- return free_and_replace(*fqdn, normalized);
-}
-
-static int lease_parse_captive_portal(const uint8_t *option, size_t len, char **uri) {
- _cleanup_free_ char *s = NULL;
- int r;
-
- assert(option);
- assert(uri);
-
- r = dhcp_option_parse_string(option, len, &s);
- if (r < 0)
- return r;
- if (s && !in_charset(s, URI_VALID))
- return -EINVAL;
-
- return free_and_replace(*uri, s);
-}
-
-static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **addresses, size_t *n_addresses) {
- assert(option || len == 0);
- assert(addresses);
- assert(n_addresses);
-
- if (len <= 0) {
- *n_addresses = 0;
- *addresses = mfree(*addresses);
- return 0;
- }
-
- if (len % 4 != 0)
- return -EINVAL;
-
- size_t n = len / 4;
- struct in_addr *a = newdup(struct in_addr, option, n);
- if (!a)
- return -ENOMEM;
-
- *n_addresses = n;
- return free_and_replace(*addresses, a);
-}
-
-static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_addr **sips, size_t *n_sips) {
- assert(option || len == 0);
- assert(sips);
- assert(n_sips);
-
- if (len <= 0)
- return -EINVAL;
-
- /* The SIP record is like the other, regular server records, but prefixed with a single "encoding"
- * byte that is either 0 or 1. We only support it to be 1 for now. Let's drop it and parse it like
- * the other fields */
-
- if (option[0] != 1) { /* We only support IP address encoding for now */
- *sips = mfree(*sips);
- *n_sips = 0;
- return 0;
- }
-
- return lease_parse_in_addrs(option + 1, len - 1, sips, n_sips);
-}
-
-static int lease_parse_dns_name(const uint8_t *optval, size_t optlen, char **ret) {
- _cleanup_free_ char *name = NULL;
- int r;
-
- assert(optval);
- assert(ret);
-
- r = dns_name_from_wire_format(&optval, &optlen, &name);
- if (r < 0)
- return r;
- if (r == 0 || optlen != 0)
- return -EBADMSG;
-
- *ret = TAKE_PTR(name);
- return r;
-}
-
-static int lease_parse_dnr(const uint8_t *option, size_t len, sd_dns_resolver **dnr, size_t *n_dnr) {
- int r;
- sd_dns_resolver *res_list = NULL;
- size_t n_resolvers = 0;
- CLEANUP_ARRAY(res_list, n_resolvers, dns_resolver_free_array);
-
- assert(option || len == 0);
- assert(dnr);
- assert(n_dnr);
-
- _cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {};
-
- size_t offset = 0;
- while (offset < len) {
- /* Instance Data length */
- if (offset + 2 > len)
- return -EBADMSG;
- size_t ilen = unaligned_read_be16(option + offset);
- if (offset + ilen + 2 > len)
- return -EBADMSG;
- offset += 2;
- size_t iend = offset + ilen;
-
- /* priority */
- if (offset + 2 > len)
- return -EBADMSG;
- res.priority = unaligned_read_be16(option + offset);
- offset += 2;
-
- /* Authenticated Domain Name */
- if (offset + 1 > len)
- return -EBADMSG;
- ilen = option[offset++];
- if (offset + ilen > iend)
- return -EBADMSG;
-
- r = lease_parse_dns_name(option + offset, ilen, &res.auth_name);
- if (r < 0)
- return r;
- r = dns_name_is_valid_ldh(res.auth_name);
- if (r < 0)
- return r;
- if (!r)
- return -EBADMSG;
- if (dns_name_is_root(res.auth_name))
- return -EBADMSG;
- offset += ilen;
-
- /* RFC9463 § 3.1.6: In ADN-only mode, server omits everything after the ADN.
- * We don't support these, but they are not invalid. */
- if (offset == iend) {
- log_debug("Received ADN-only DNRv4 option, ignoring.");
- sd_dns_resolver_done(&res);
- continue;
- }
-
- /* IPv4 addrs */
- if (offset + 1 > len)
- return -EBADMSG;
- ilen = option[offset++];
- if (offset + ilen > iend)
- return -EBADMSG;
-
- size_t n_addrs;
- _cleanup_free_ struct in_addr *addrs = NULL;
- r = lease_parse_in_addrs(option + offset, ilen, &addrs, &n_addrs);
- if (r < 0)
- return r;
- offset += ilen;
-
- /* RFC9463 § 3.1.8: option MUST include at least one valid IP addr */
- if (!n_addrs)
- return -EBADMSG;
-
- res.addrs = new(union in_addr_union, n_addrs);
- if (!res.addrs)
- return -ENOMEM;
- for (size_t i = 0; i < n_addrs; i++) {
- union in_addr_union addr = {.in = addrs[i]};
- /* RFC9463 § 5.2 client MUST discard multicast and host loopback addresses */
- if (in_addr_is_multicast(AF_INET, &addr) ||
- in_addr_is_localhost(AF_INET, &addr))
- return -EBADMSG;
- res.addrs[i] = addr;
- }
- res.n_addrs = n_addrs;
- res.family = AF_INET;
-
- /* service params */
- r = dnr_parse_svc_params(option + offset, iend-offset, &res);
- if (r < 0)
- return r;
- if (r == 0) {
- /* We can't use this record, but it was not invalid. */
- log_debug("Received DNRv4 option with unsupported SvcParams, ignoring.");
- sd_dns_resolver_done(&res);
- continue;
- }
- offset = iend;
-
- /* Append the latest resolver */
- if (!GREEDY_REALLOC0(res_list, n_resolvers+1))
- return -ENOMEM;
-
- res_list[n_resolvers++] = TAKE_STRUCT(res);
- }
-
- typesafe_qsort(res_list, n_resolvers, dns_resolver_prio_compare);
-
- dns_resolver_free_array(*dnr, *n_dnr);
- *dnr = TAKE_PTR(res_list);
- *n_dnr = n_resolvers;
-
- return n_resolvers;
-}
-
-static int lease_parse_static_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
- int r;
-
- assert(lease);
- assert(option || len <= 0);
-
- if (len % 8 != 0)
- return -EINVAL;
-
- while (len >= 8) {
- struct in_addr dst, gw;
- uint8_t prefixlen;
-
- assert_se(lease_parse_be32(option, 4, &dst.s_addr) >= 0);
- option += 4;
-
- assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0);
- option += 4;
-
- len -= 8;
-
- r = in4_addr_default_prefixlen(&dst, &prefixlen);
- if (r < 0) {
- log_debug("sd-dhcp-lease: cannot determine class of received static route, ignoring.");
- continue;
- }
-
- (void) in4_addr_mask(&dst, prefixlen);
-
- if (!GREEDY_REALLOC(lease->static_routes, lease->n_static_routes + 1))
- return -ENOMEM;
-
- lease->static_routes[lease->n_static_routes++] = (struct sd_dhcp_route) {
- .dst_addr = dst,
- .gw_addr = gw,
- .dst_prefixlen = prefixlen,
- };
- }
-
- return 0;
-}
-
-/* parses RFC3442 Classless Static Route Option */
-static int lease_parse_classless_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
- assert(lease);
- assert(option || len <= 0);
-
- /* option format: (subnet-mask-width significant-subnet-octets gateway-ip) */
-
- while (len > 0) {
- uint8_t prefixlen, dst_octets;
- struct in_addr dst = {}, gw;
-
- prefixlen = *option;
- option++;
- len--;
-
- dst_octets = DIV_ROUND_UP(prefixlen, 8);
-
- /* can't have more than 4 octets in IPv4 */
- if (dst_octets > 4 || len < dst_octets)
- return -EINVAL;
-
- memcpy(&dst, option, dst_octets);
- option += dst_octets;
- len -= dst_octets;
-
- if (len < 4)
- return -EINVAL;
-
- assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0);
- option += 4;
- len -= 4;
-
- if (!GREEDY_REALLOC(lease->classless_routes, lease->n_classless_routes + 1))
- return -ENOMEM;
-
- lease->classless_routes[lease->n_classless_routes++] = (struct sd_dhcp_route) {
- .dst_addr = dst,
- .gw_addr = gw,
- .dst_prefixlen = prefixlen,
- };
- }
-
- return 0;
-}
-
-static int lease_parse_6rd(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
- uint8_t ipv4masklen, prefixlen;
- struct in6_addr prefix;
- _cleanup_free_ struct in_addr *br_addresses = NULL;
- size_t n_br_addresses;
-
- assert(lease);
- assert(option);
-
- /* See RFC 5969 Section 7.1.1 */
-
- if (lease->sixrd_n_br_addresses > 0)
- /* Multiple 6rd option?? */
- return -EINVAL;
-
- /* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */
- if (len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) ||
- (len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0)
- return -EINVAL;
-
- /* IPv4MaskLen: The number of high-order bits that are identical across all CE IPv4 addresses
- * within a given 6rd domain. This may be any value between 0 and 32. Any value
- * greater than 32 is invalid. */
- ipv4masklen = option[0];
- if (ipv4masklen > 32)
- return -EINVAL;
-
- /* 6rdPrefixLen: The IPv6 prefix length of the SP's 6rd IPv6 prefix in number of bits. For the
- * purpose of bounds checking by DHCP option processing, the sum of
- * (32 - IPv4MaskLen) + 6rdPrefixLen MUST be less than or equal to 128. */
- prefixlen = option[1];
- if (32 - ipv4masklen + prefixlen > 128)
- return -EINVAL;
-
- /* 6rdPrefix: The service provider's 6rd IPv6 prefix represented as a 16-octet IPv6 address.
- * The bits in the prefix after the 6rdPrefixlen number of bits are reserved and
- * MUST be initialized to zero by the sender and ignored by the receiver. */
- memcpy(&prefix, option + 2, sizeof(struct in6_addr));
- (void) in6_addr_mask(&prefix, prefixlen);
-
- /* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relays for a given 6rd domain. */
- n_br_addresses = (len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr);
- br_addresses = newdup(struct in_addr, option + 2 + sizeof(struct in6_addr), n_br_addresses);
- if (!br_addresses)
- return -ENOMEM;
-
- lease->sixrd_ipv4masklen = ipv4masklen;
- lease->sixrd_prefixlen = prefixlen;
- lease->sixrd_prefix = prefix;
- lease->sixrd_br_addresses = TAKE_PTR(br_addresses);
- lease->sixrd_n_br_addresses = n_br_addresses;
-
- return 0;
-}
-
-int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
- sd_dhcp_lease *lease = ASSERT_PTR(userdata);
- int r;
-
- switch (code) {
-
- case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
- r = lease_parse_be32_seconds(option, len, /* max_as_infinity= */ true, &lease->lifetime);
- if (r < 0)
- log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
-
- break;
-
- case SD_DHCP_OPTION_SERVER_IDENTIFIER:
- r = lease_parse_be32(option, len, &lease->server_address);
- if (r < 0)
- log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
-
- break;
-
- case SD_DHCP_OPTION_SUBNET_MASK:
- r = lease_parse_be32(option, len, &lease->subnet_mask);
- if (r < 0)
- log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_BROADCAST:
- r = lease_parse_be32(option, len, &lease->broadcast);
- if (r < 0)
- log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_ROUTER:
- r = lease_parse_in_addrs(option, len, &lease->router, &lease->router_size);
- if (r < 0)
- log_debug_errno(r, "Failed to parse router addresses, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_RAPID_COMMIT:
- if (len > 0)
- log_debug("Invalid DHCP Rapid Commit option, ignoring.");
- lease->rapid_commit = true;
- break;
-
- case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
- r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size);
- if (r < 0)
- log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_NTP_SERVER:
- r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_NTP].addr, &lease->servers[SD_DHCP_LEASE_NTP].size);
- if (r < 0)
- log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_SIP_SERVER:
- r = lease_parse_sip_server(option, len, &lease->servers[SD_DHCP_LEASE_SIP].addr, &lease->servers[SD_DHCP_LEASE_SIP].size);
- if (r < 0)
- log_debug_errno(r, "Failed to parse SIP server, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_POP3_SERVER:
- r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_POP3].addr, &lease->servers[SD_DHCP_LEASE_POP3].size);
- if (r < 0)
- log_debug_errno(r, "Failed to parse POP3 server, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_SMTP_SERVER:
- r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_SMTP].addr, &lease->servers[SD_DHCP_LEASE_SMTP].size);
- if (r < 0)
- log_debug_errno(r, "Failed to parse SMTP server, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_LPR_SERVER:
- r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_LPR].addr, &lease->servers[SD_DHCP_LEASE_LPR].size);
- if (r < 0)
- log_debug_errno(r, "Failed to parse LPR server, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL:
- r = lease_parse_captive_portal(option, len, &lease->captive_portal);
- if (r < 0)
- log_debug_errno(r, "Failed to parse captive portal, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_STATIC_ROUTE:
- r = lease_parse_static_routes(lease, option, len);
- if (r < 0)
- log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_MTU_INTERFACE:
- r = lease_parse_u16(option, len, &lease->mtu, 68);
- if (r < 0)
- log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
- if (lease->mtu < DHCP_MIN_PACKET_SIZE) {
- log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_MIN_PACKET_SIZE);
- lease->mtu = DHCP_MIN_PACKET_SIZE;
- }
-
- break;
-
- case SD_DHCP_OPTION_DOMAIN_NAME:
- r = lease_parse_domain(option, len, &lease->domainname);
- if (r < 0) {
- log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
- return 0;
- }
-
- break;
-
- case SD_DHCP_OPTION_DOMAIN_SEARCH:
- r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains);
- if (r < 0)
- log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_HOST_NAME:
- r = lease_parse_domain(option, len, &lease->hostname);
- if (r < 0) {
- log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
- return 0;
- }
-
- break;
-
- case SD_DHCP_OPTION_FQDN:
- r = lease_parse_fqdn(option, len, &lease->fqdn);
- if (r < 0) {
- log_debug_errno(r, "Failed to parse FQDN, ignoring: %m");
- return 0;
- }
-
- break;
-
- case SD_DHCP_OPTION_ROOT_PATH: {
- _cleanup_free_ char *p = NULL;
-
- r = dhcp_option_parse_string(option, len, &p);
- if (r < 0)
- log_debug_errno(r, "Failed to parse root path, ignoring: %m");
-
- free_and_replace(lease->root_path, p);
- break;
- }
- case SD_DHCP_OPTION_RENEWAL_TIME:
- r = lease_parse_be32_seconds(option, len, /* max_as_infinity= */ true, &lease->t1);
- if (r < 0)
- log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_REBINDING_TIME:
- r = lease_parse_be32_seconds(option, len, /* max_as_infinity= */ true, &lease->t2);
- if (r < 0)
- log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
- r = lease_parse_classless_routes(lease, option, len);
- if (r < 0)
- log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_TZDB_TIMEZONE: {
- _cleanup_free_ char *tz = NULL;
-
- r = dhcp_option_parse_string(option, len, &tz);
- if (r < 0) {
- log_debug_errno(r, "Failed to parse timezone option, ignoring: %m");
- return 0;
- }
-
- if (!timezone_is_valid(tz, LOG_DEBUG)) {
- log_debug("Timezone is not valid, ignoring.");
- return 0;
- }
-
- free_and_replace(lease->timezone, tz);
-
- break;
- }
-
- case SD_DHCP_OPTION_V4_DNR:
- r = lease_parse_dnr(option, len, &lease->dnr, &lease->n_dnr);
- if (r < 0) {
- log_debug_errno(r, "Failed to parse network-designated resolvers, ignoring: %m");
- return 0;
- }
-
- break;
-
- case SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION:
-
- if (len <= 0)
- lease->vendor_specific = mfree(lease->vendor_specific);
- else {
- void *p;
-
- p = memdup(option, len);
- if (!p)
- return -ENOMEM;
-
- free_and_replace(lease->vendor_specific, p);
- }
-
- lease->vendor_specific_len = len;
- break;
-
- case SD_DHCP_OPTION_6RD:
- r = lease_parse_6rd(lease, option, len);
- if (r < 0)
- log_debug_errno(r, "Failed to parse 6rd option, ignoring: %m");
- break;
-
- case SD_DHCP_OPTION_IPV6_ONLY_PREFERRED:
- r = lease_parse_be32_seconds(option, len, /* max_as_infinity= */ false, &lease->ipv6_only_preferred_usec);
- if (r < 0)
- log_debug_errno(r, "Failed to parse IPv6 only preferred option, ignoring: %m");
-
- else if (lease->ipv6_only_preferred_usec < MIN_V6ONLY_WAIT_USEC &&
- !network_test_mode_enabled())
- lease->ipv6_only_preferred_usec = MIN_V6ONLY_WAIT_USEC;
- break;
-
- case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
- r = dhcp_lease_insert_private_option(lease, code, option, len);
- if (r < 0)
- return r;
-
- break;
-
- default:
- log_debug("Ignoring DHCP option %"PRIu8" while parsing.", code);
- }
-
- return 0;
-}
-
-/* Parses compressed domain names. */
-int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains) {
- _cleanup_strv_free_ char **names = NULL;
- size_t pos = 0, cnt = 0;
- int r;
-
- assert(domains);
- assert(option || len == 0);
-
- if (len == 0)
- return -EBADMSG;
-
- while (pos < len) {
- _cleanup_free_ char *name = NULL;
- size_t n = 0;
- size_t jump_barrier = pos, next_chunk = 0;
- bool first = true;
-
- for (;;) {
- uint8_t c;
- c = option[pos++];
-
- if (c == 0) {
- /* End of name */
- break;
- } else if (c <= 63) {
- const char *label;
-
- /* Literal label */
- label = (const char*) (option + pos);
- pos += c;
- if (pos >= len)
- return -EBADMSG;
-
- if (!GREEDY_REALLOC(name, n + !first + DNS_LABEL_ESCAPED_MAX))
- return -ENOMEM;
-
- if (first)
- first = false;
- else
- name[n++] = '.';
-
- r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX);
- if (r < 0)
- return r;
-
- n += r;
- } else if (FLAGS_SET(c, 0xc0)) {
- /* Pointer */
-
- uint8_t d;
- uint16_t ptr;
-
- if (pos >= len)
- return -EBADMSG;
-
- d = option[pos++];
- ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
-
- /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
- if (ptr >= jump_barrier)
- return -EBADMSG;
- jump_barrier = ptr;
-
- /* Save current location so we don't end up re-parsing what's parsed so far. */
- if (next_chunk == 0)
- next_chunk = pos;
-
- pos = ptr;
- } else
- return -EBADMSG;
- }
-
- if (!GREEDY_REALLOC(name, n + 1))
- return -ENOMEM;
- name[n] = 0;
-
- r = strv_extend(&names, name);
- if (r < 0)
- return r;
-
- cnt++;
-
- if (next_chunk != 0)
- pos = next_chunk;
- }
-
- strv_free_and_replace(*domains, names);
-
- return cnt;
-}
-
-int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
- struct sd_dhcp_raw_option *option, *before = NULL;
-
- assert(lease);
-
- LIST_FOREACH(options, cur, lease->private_options) {
- if (tag < cur->tag) {
- before = cur;
- break;
- }
- if (tag == cur->tag) {
- log_debug("Ignoring duplicate option, tagged %i.", tag);
- return 0;
- }
- }
-
- option = new(struct sd_dhcp_raw_option, 1);
- if (!option)
- return -ENOMEM;
-
- option->tag = tag;
- option->length = len;
- option->data = memdup(data, len);
- if (!option->data) {
- free(option);
- return -ENOMEM;
- }
-
- LIST_INSERT_BEFORE(options, lease->private_options, before, option);
- return 0;
-}
-
int dhcp_lease_new(sd_dhcp_lease **ret) {
sd_dhcp_lease *lease;
return 0;
}
-int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
- struct in_addr address, mask;
- int r;
-
- assert(lease);
-
- if (lease->subnet_mask != INADDR_ANY)
- return 0;
-
- if (lease->address == 0)
- return -ENODATA;
-
- address.s_addr = lease->address;
-
- /* fall back to the default subnet masks based on address class */
- r = in4_addr_default_subnet_mask(&address, &mask);
- if (r < 0)
- return r;
-
- lease->subnet_mask = mask.s_addr;
-
- return 0;
-}
-
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id **ret) {
- assert_return(lease, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (!sd_dhcp_client_id_is_set(&lease->client_id))
- return -ENODATA;
-
- *ret = &lease->client_id;
-
- return 0;
-}
-
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id *client_id) {
- assert_return(lease, -EINVAL);
-
- if (!sd_dhcp_client_id_is_set(client_id))
- return sd_dhcp_client_id_clear(&lease->client_id);
-
- lease->client_id = *client_id;
-
- return 0;
-}
-
int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);