Give dhclient(8) the ability to use option 119, a.k.a. "Domain
authorkrw <krw@openbsd.org>
Mon, 26 Oct 2015 16:32:33 +0000 (16:32 +0000)
committerkrw <krw@openbsd.org>
Mon, 26 Oct 2015 16:32:33 +0000 (16:32 +0000)
Search" if supplied by the server.

Requested by a few. Original diff from Ray Lai via tech@.

Tested & ok claudio@

sbin/dhclient/clparse.c
sbin/dhclient/dhclient.c
sbin/dhclient/dhcp.h
sbin/dhclient/dhcpd.h
sbin/dhclient/options.c
sbin/dhclient/tables.c

index ae33a75..99a895d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: clparse.c,v 1.92 2015/05/18 17:51:21 krw Exp $        */
+/*     $OpenBSD: clparse.c,v 1.93 2015/10/26 16:32:33 krw Exp $        */
 
 /* Parser for dhclient config and lease files. */
 
@@ -88,6 +88,8 @@ read_client_conf(void)
            [config->requested_option_count++] = DHO_ROUTERS;
        config->requested_options
            [config->requested_option_count++] = DHO_DOMAIN_NAME;
+       config->requested_options
+           [config->requested_option_count++] = DHO_DOMAIN_SEARCH;
        config->requested_options
            [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
        config->requested_options
index b324e9d..8c97f1c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dhclient.c,v 1.364 2015/09/08 17:19:20 krw Exp $      */
+/*     $OpenBSD: dhclient.c,v 1.365 2015/10/26 16:32:33 krw Exp $      */
 
 /*
  * Copyright 2004 Henning Brauer <henning@openbsd.org>
@@ -99,7 +99,7 @@ int            res_hnok_list(const char *dn);
 void            fork_privchld(int, int);
 void            get_ifname(char *);
 char           *resolv_conf_contents(struct option_data  *,
-                    struct option_data *);
+                    struct option_data *, struct option_data *);
 void            write_resolv_conf(u_int8_t *, size_t);
 void            write_option_db(u_int8_t *, size_t);
 
@@ -905,7 +905,8 @@ bind_lease(void)
        }
 
        client->new->resolv_conf = resolv_conf_contents(
-           &options[DHO_DOMAIN_NAME], &options[DHO_DOMAIN_NAME_SERVERS]);
+           &options[DHO_DOMAIN_NAME], &options[DHO_DOMAIN_NAME_SERVERS],
+           &options[DHO_DOMAIN_SEARCH]);
 
        /* Replace the old active lease with the new one. */
        client->active = client->new;
@@ -1097,8 +1098,8 @@ struct client_lease *
 packet_to_lease(struct in_addr client_addr, struct option_data *options)
 {
        struct client_lease *lease;
-       char *pretty;
-       int i;
+       char *pretty, *buf;
+       int i, sz;
 
        lease = calloc(1, sizeof(struct client_lease));
        if (!lease) {
@@ -1120,6 +1121,21 @@ packet_to_lease(struct in_addr client_addr, struct option_data *options)
                if (strlen(pretty) == 0)
                        continue;
                switch (i) {
+               case DHO_DOMAIN_SEARCH:
+                       /* Must decode the option into text to check names. */
+                       buf = calloc(1, DHCP_DOMAIN_SEARCH_LEN);
+                       if (buf == NULL)
+                               error("No memory to decode domain search");
+                       sz = pretty_print_domain_search(buf,
+                           DHCP_DOMAIN_SEARCH_LEN,
+                           options[i].data, options[i].len);
+                       if (strlen(buf) == 0)
+                               continue;
+                       if (sz == -1 || !res_hnok_list(buf))
+                               warning("Bogus data for option %s",
+                                   dhcp_options[i].name);
+                       free(buf);
+                       break;
                case DHO_DOMAIN_NAME:
                        /*
                         * Allow deviant but historically blessed
@@ -1917,8 +1933,9 @@ res_hnok(const char *name)
 }
 
 /*
- * resolv_conf(5) says a max of 6 domains and total length of 1024 bytes are
- * acceptable for the 'search' statement.
+ * resolv_conf(5) says a max of DHCP_DOMAIN_SEARCH_CNT domains and total
+ * length of DHCP_DOMAIN_SEARCH_LEN bytes are acceptable for the 'search'
+ * statement.
  */
 int
 res_hnok_list(const char *names)
@@ -1926,7 +1943,7 @@ res_hnok_list(const char *names)
        char *dupnames, *hn, *inputstring;
        int count;
 
-       if (strlen(names) >= 1024)
+       if (strlen(names) >= DHCP_DOMAIN_SEARCH_LEN)
                return (0);
 
        dupnames = inputstring = strdup(names);
@@ -1940,7 +1957,7 @@ res_hnok_list(const char *names)
                if (res_hnok(hn) == 0)
                        break;
                count++;
-               if (count > 6)
+               if (count > DHCP_DOMAIN_SEARCH_CNT)
                        break;
        }
 
@@ -2095,15 +2112,29 @@ get_ifname(char *arg)
  */
 char *
 resolv_conf_contents(struct option_data  *domainname,
-    struct option_data *nameservers)
+    struct option_data *nameservers, struct option_data *domainsearch)
 {
-       char *dn, *ns, *nss[MAXNS], *contents, *courtesy, *p;
+       char *dn, *ns, *nss[MAXNS], *contents, *courtesy, *p, *buf;
        size_t len;
-       int i, rslt;
+       int i, rslt, sz;
 
        memset(nss, 0, sizeof(nss));
 
-       if (domainname->len) {
+       if (domainsearch->len) {
+               buf = calloc(1, DHCP_DOMAIN_SEARCH_LEN);
+               if (buf == NULL)
+                       error("No memory to decode domain search");
+               sz = pretty_print_domain_search(buf, DHCP_DOMAIN_SEARCH_LEN,
+                   domainsearch->data, domainsearch->len);
+               if (sz == -1)
+                       dn = strdup("");
+               else {
+                       rslt = asprintf(&dn, "search %s\n", buf);
+                       if (rslt == -1)
+                               dn = NULL;
+               }
+               free(buf);
+       } else if (domainname->len) {
                rslt = asprintf(&dn, "search %s\n",
                    pretty_print_option(DHO_DOMAIN_NAME, domainname, 0));
                if (rslt == -1)
index 731f8a9..c0dc07c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dhcp.h,v 1.17 2014/01/21 03:07:50 krw Exp $   */
+/*     $OpenBSD: dhcp.h,v 1.18 2015/10/26 16:32:33 krw Exp $   */
 
 /* Protocol structures. */
 
 #define DHCP_MTU_MAX           1500
 #define DHCP_OPTION_LEN                (DHCP_MTU_MAX - DHCP_FIXED_LEN)
 
+/* Respect historical limits on 'search' line in resolv.conf(5) */
+#define DHCP_DOMAIN_SEARCH_LEN 1024
+#define DHCP_DOMAIN_SEARCH_CNT 6
+
 #define BOOTP_MIN_LEN          300
 
 struct dhcp_packet {
@@ -171,6 +175,7 @@ struct dhcp_packet {
 #define DHO_NDS_SERVERS                        85
 #define DHO_NDS_TREE_NAME              86
 #define DHO_NDS_CONTEXT                        87
+#define        DHO_DOMAIN_SEARCH               119
 #define DHO_CLASSLESS_STATIC_ROUTES    121
 #define DHO_TFTP_CONFIG_FILE           144
 #define DHO_VOIP_CONFIGURATION_SERVER  150
index a23f3f1..f2868cb 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dhcpd.h,v 1.151 2015/08/19 17:52:10 krw Exp $ */
+/*     $OpenBSD: dhcpd.h,v 1.152 2015/10/26 16:32:33 krw Exp $ */
 
 /*
  * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
@@ -217,6 +217,8 @@ char *pretty_print_option(unsigned int, struct option_data *, int);
 int pretty_print_string(unsigned char *, size_t, unsigned char *, size_t, int);
 int pretty_print_classless_routes(unsigned char *, size_t, unsigned char *,
     size_t);
+int pretty_print_domain_search(unsigned char *, size_t, unsigned char *,
+    size_t);
 void do_packet(unsigned int, struct in_addr, struct ether_addr *);
 
 /* errwarn.c */
index aee0c27..389d99f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: options.c,v 1.73 2014/10/27 17:01:28 krw Exp $        */
+/*     $OpenBSD: options.c,v 1.74 2015/10/26 16:32:33 krw Exp $        */
 
 /* DHCP options parsing and reassembly. */
 
@@ -45,6 +45,7 @@
 #include <vis.h>
 
 int parse_option_buffer(struct option_data *, unsigned char *, int);
+int expand_search_domain_name(unsigned char *, size_t, int *, unsigned char *);
 
 /*
  * Parse options out of the specified buffer, storing addresses of
@@ -289,6 +290,129 @@ pretty_print_classless_routes(unsigned char *dst, size_t dstlen,
        return (total);
 }
 
+int
+expand_search_domain_name(unsigned char *src, size_t srclen, int *offset,
+    unsigned char *domain_search)
+{
+       int domain_name_len, i, label_len, pointer, pointed_len;
+       char *cursor;
+
+       cursor = domain_search + strlen(domain_search);
+       domain_name_len = 0;
+
+       i = *offset;
+       while (i <= srclen) {
+               label_len = src[i];
+               if (label_len == 0) {
+                       /*
+                        * A zero-length label marks the end of this
+                        * domain name.
+                        */
+                       *offset = i + 1;
+                       return (domain_name_len);
+               } else if (label_len & 0xC0) {
+                       /* This is a pointer to another list of labels. */
+                       if (i + 1 >= srclen) {
+                               /* The pointer is truncated. */
+                               warning("Truncated pointer in DHCP Domain "
+                                   "Search option.");
+                               return (-1);
+                       }
+
+                       pointer = ((label_len & ~(0xC0)) << 8) + src[i + 1];
+                       if (pointer >= *offset) {
+                               /*
+                                * The pointer must indicates a prior
+                                * occurance.
+                                */
+                               warning("Invalid forward pointer in DHCP "
+                                   "Domain Search option compression.");
+                               return (-1);
+                       }
+
+                       pointed_len = expand_search_domain_name(src, srclen,
+                           &pointer, domain_search);
+                       domain_name_len += pointed_len;
+
+                       *offset = i + 2;
+                       return (domain_name_len);
+               }
+               if (i + label_len + 1 > srclen) {
+                       warning("Truncated label in DHCP Domain Search "
+                           "option.");
+                       return (-1);
+               }
+               /*
+                * Update the domain name length with the length of the
+                * current label, plus a trailing dot ('.').
+                */
+               domain_name_len += label_len + 1;
+
+               if (strlen(domain_search) + domain_name_len >=
+                   DHCP_DOMAIN_SEARCH_LEN) {
+                       warning("Domain search list too long.");
+                       return (-1);
+               }
+
+               /* Copy the label found. */
+               memcpy(cursor, src + i + 1, label_len);
+               cursor[label_len] = '.';
+
+               /* Move cursor. */
+               i += label_len + 1;
+               cursor += label_len + 1;
+       }
+
+       warning("Truncated DHCP Domain Search option.");
+
+       return (-1);
+}
+
+/*
+ * Must special case DHO_DOMAIN_SEARCH because it is encoded as described
+ * in RFC 1035 section 4.1.4.
+ */
+int
+pretty_print_domain_search(unsigned char *dst, size_t dstlen,
+    unsigned char *src, size_t srclen)
+{
+       int offset, len, expanded_len, domains;
+       unsigned char *domain_search, *cursor;
+
+       domain_search = calloc(1, DHCP_DOMAIN_SEARCH_LEN);
+       if (domain_search == NULL)
+               error("Can't allocate storage for expanded domain-search\n");
+
+       /* Compute expanded length. */
+       expanded_len = len = 0;
+       domains = 0;
+       offset = 0;
+       while (offset < srclen) {
+               cursor = domain_search + strlen(domain_search);
+               if (domain_search[0]) {
+                       *cursor = ' ';
+                       expanded_len++;
+               }
+               len = expand_search_domain_name(src, srclen, &offset,
+                   domain_search);
+               if (len == -1) {
+                       free(domain_search);
+                       return (-1);
+               }
+               domains++;
+               expanded_len += len;
+               if (domains > DHCP_DOMAIN_SEARCH_CNT) {
+                       free(domain_search);
+                       return (-1);
+               }
+       }
+
+       strlcat(dst, domain_search, dstlen);
+       free(domain_search);
+
+       return (0);
+}
+
 /*
  * Format the specified option so that a human can easily read it.
  */
index 84abdb8..ff6c4df 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: tables.c,v 1.18 2014/01/21 03:07:50 krw Exp $ */
+/*     $OpenBSD: tables.c,v 1.19 2015/10/26 16:32:33 krw Exp $ */
 
 /* Tables of information. */
 
@@ -179,7 +179,7 @@ const struct option dhcp_options[256] = {
        /* 116 */ { "option-116", "X" },
        /* 117 */ { "option-117", "X" },
        /* 118 */ { "option-118", "X" },
-       /* 119 */ { "option-119", "X" },
+       /* 119 */ { "domain-search", "X" },
        /* 120 */ { "option-120", "X" },
        /* 121 */ { "classless-static-routes", "CIA" },
        /* 122 */ { "option-122", "X" },