]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd-network/sd-dhcp-lease.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-lease.c
index ef50ed17a15c6d4bae185c83d60bb6b099a5625a..ec5ecaeabc1d916a9781fe81b93d2b0ff6f50e64 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -231,6 +232,21 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
         return (int) lease->static_route_size;
 }
 
+int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
+        unsigned r;
+
+        assert_return(lease, -EINVAL);
+        assert_return(domains, -EINVAL);
+
+        r = strv_length(lease->search_domains);
+        if (r > 0) {
+                *domains = lease->search_domains;
+                return (int) r;
+        }
+
+        return -ENODATA;
+}
+
 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);
@@ -282,9 +298,8 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
         free(lease->static_route);
         free(lease->client_id);
         free(lease->vendor_specific);
-        free(lease);
-
-        return NULL;
+        strv_free(lease->search_domains);
+        return mfree(lease);
 }
 
 static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
@@ -385,6 +400,23 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
         return 0;
 }
 
+static void filter_bogus_addresses(struct in_addr *addresses, size_t *n) {
+        size_t i, j;
+
+        /* Silently filter DNS/NTP servers supplied to us that do not make outside of the local scope. */
+
+        for (i = 0, j = 0; i < *n; i ++) {
+
+                if (in4_addr_is_null(addresses+i) ||
+                    in4_addr_is_localhost(addresses+i))
+                        continue;
+
+                addresses[j++] = addresses[i];
+        }
+
+        *n = j;
+}
+
 static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
         assert(option);
         assert(ret);
@@ -406,6 +438,8 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add
                 if (!addresses)
                         return -ENOMEM;
 
+                filter_bogus_addresses(addresses, &n_addresses);
+
                 free(*ret);
                 *ret = addresses;
                 *n_ret = n_addresses;
@@ -438,7 +472,7 @@ static int lease_parse_routes(
                 struct sd_dhcp_route *route = *routes + *routes_size;
                 int r;
 
-                r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
+                r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
                 if (r < 0) {
                         log_debug("Failed to determine destination prefix length from class based IP, ignoring");
                         continue;
@@ -577,6 +611,11 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                 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_DEFAULT_MIN_SIZE) {
+                        log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_DEFAULT_MIN_SIZE);
+                        lease->mtu = DHCP_DEFAULT_MIN_SIZE;
+                }
+
                 break;
 
         case SD_DHCP_OPTION_DOMAIN_NAME:
@@ -588,6 +627,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
 
                 break;
 
+        case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST:
+                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) {
@@ -679,6 +724,96 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
         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_return(option && len > 0, -ENODATA);
+
+        while (pos < len) {
+                _cleanup_free_ char *name = NULL;
+                size_t n = 0, allocated = 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, allocated, 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 ((c & 0xc0) == 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, allocated, 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;
+        }
+
+        *domains = names;
+        names = NULL;
+
+        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 *cur, *option;
 
@@ -734,6 +869,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         const char *string;
         uint16_t mtu;
         _cleanup_free_ sd_dhcp_route **routes = NULL;
+        char **search_domains = NULL;
         uint32_t t1, t2, lifetime;
         int r;
 
@@ -791,22 +927,29 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
 
         r = sd_dhcp_lease_get_dns(lease, &addresses);
         if (r > 0) {
-                fputs("DNS=", f);
+                fputs_unlocked("DNS=", f);
                 serialize_in_addrs(f, addresses, r);
-                fputs("\n", f);
+                fputs_unlocked("\n", f);
         }
 
         r = sd_dhcp_lease_get_ntp(lease, &addresses);
         if (r > 0) {
-                fputs("NTP=", f);
+                fputs_unlocked("NTP=", f);
                 serialize_in_addrs(f, addresses, r);
-                fputs("\n", f);
+                fputs_unlocked("\n", f);
         }
 
         r = sd_dhcp_lease_get_domainname(lease, &string);
         if (r >= 0)
                 fprintf(f, "DOMAINNAME=%s\n", string);
 
+        r = sd_dhcp_lease_get_search_domains(lease, &search_domains);
+        if (r > 0) {
+                fputs_unlocked("DOMAIN_SEARCH_LIST=", f);
+                fputstrv(f, search_domains, NULL, NULL);
+                fputs_unlocked("\n", f);
+        }
+
         r = sd_dhcp_lease_get_hostname(lease, &string);
         if (r >= 0)
                 fprintf(f, "HOSTNAME=%s\n", string);
@@ -888,6 +1031,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                 *ntp = NULL,
                 *mtu = NULL,
                 *routes = NULL,
+                *domains = NULL,
                 *client_id_hex = NULL,
                 *vendor_specific_hex = NULL,
                 *lifetime = NULL,
@@ -916,6 +1060,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                            "MTU", &mtu,
                            "DOMAINNAME", &lease->domainname,
                            "HOSTNAME", &lease->hostname,
+                           "DOMAIN_SEARCH_LIST", &domains,
                            "ROOT_PATH", &lease->root_path,
                            "ROUTES", &routes,
                            "CLIENTID", &client_id_hex,
@@ -1021,6 +1166,18 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                         log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu);
         }
 
+        if (domains) {
+                _cleanup_strv_free_ char **a = NULL;
+                a = strv_split(domains, " ");
+                if (!a)
+                        return -ENOMEM;
+
+                if (!strv_isempty(a)) {
+                        lease->search_domains = a;
+                        a = NULL;
+                }
+        }
+
         if (routes) {
                 r = deserialize_dhcp_routes(
                                 &lease->static_route,
@@ -1097,7 +1254,7 @@ int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
         address.s_addr = lease->address;
 
         /* fall back to the default subnet masks based on address class */
-        r = in_addr_default_subnet_mask(&address, &mask);
+        r = in4_addr_default_subnet_mask(&address, &mask);
         if (r < 0)
                 return r;