>From FreeBSD:

> Support domain-search in dhclient(8)
> The "domain-search" option (option 119) allows a DHCP server to publish
> a list of implicit domain suffixes used during name lookup. This option
> is described in RFC 3397.
> For instance, if the domain-search option says:
>     ".example.org .example.com"
> and one wants to resolve "foobar", the resolver will try:
>     1. "foobar.example.org"
>     2. "foobar.example.com"
> The file /etc/resolv.conf is updated with a "search" directive if the
> DHCP server provides "domain-search".
> A regression test suite is included in this patch under
> tools/regression/sbin/dhclient.
> PR: bin/151940
> Sponsored by Yakaz (http://www.yakaz.com)

I haven't been able to make the regress tests work yet, so they are
not included.

Ray


diff -up /root/dhclient.head/clparse.c ./clparse.c
--- /root/dhclient.head/clparse.c       Wed Sep  2 11:11:54 2015
+++ ./clparse.c Wed Sep  2 10:22:58 2015
@@ -92,6 +92,8 @@ read_client_conf(void)
            [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
        config->requested_options
            [config->requested_option_count++] = DHO_HOST_NAME;
+       config->requested_options
+           [config->requested_option_count++] = DHO_DOMAIN_SEARCH;
 
        if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
                do {
diff -up /root/dhclient.head/dhclient.c ./dhclient.c
--- /root/dhclient.head/dhclient.c      Wed Sep  2 11:11:54 2015
+++ ./dhclient.c        Wed Sep  2 13:41:32 2015
@@ -98,8 +98,8 @@ 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 *);
+char           *resolv_conf_contents(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_SEARCH],
+           &options[DHO_DOMAIN_NAME_SERVERS]);
 
        /* Replace the old active lease with the new one. */
        client->active = client->new;
@@ -1121,6 +1122,7 @@ packet_to_lease(struct in_addr client_addr, struct opt
                        continue;
                switch (i) {
                case DHO_DOMAIN_NAME:
+               case DHO_DOMAIN_SEARCH:
                        /*
                         * Allow deviant but historically blessed
                         * practice of supplying multiple domain names
@@ -2094,7 +2096,7 @@ get_ifname(char *arg)
  * Update resolv.conf.
  */
 char *
-resolv_conf_contents(struct option_data  *domainname,
+resolv_conf_contents(struct option_data *domainname, struct option_data 
*domainsearch,
     struct option_data *nameservers)
 {
        char *dn, *ns, *nss[MAXNS], *contents, *courtesy, *p;
@@ -2103,7 +2105,12 @@ resolv_conf_contents(struct option_data  *domainname,
 
        memset(nss, 0, sizeof(nss));
 
-       if (domainname->len) {
+       if (domainsearch->len) {
+               rslt = asprintf(&dn, "search %s\n",
+                   pretty_print_option(DHO_DOMAIN_SEARCH, domainsearch, 0));
+               if (rslt == -1)
+                       dn = NULL;
+       } else if (domainname->len) {
                rslt = asprintf(&dn, "search %s\n",
                    pretty_print_option(DHO_DOMAIN_NAME, domainname, 0));
                if (rslt == -1)
diff -up /root/dhclient.head/dhcp.h ./dhcp.h
--- /root/dhclient.head/dhcp.h  Wed Sep  2 11:11:54 2015
+++ ./dhcp.h    Wed Sep  2 10:26:39 2015
@@ -171,6 +171,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
diff -up /root/dhclient.head/options.c ./options.c
--- /root/dhclient.head/options.c       Wed Sep  2 11:11:54 2015
+++ ./options.c Wed Sep  2 10:25:58 2015
@@ -45,6 +45,10 @@
 #include <vis.h>
 
 int parse_option_buffer(struct option_data *, unsigned char *, int);
+void expand_domain_search(struct option_data *);
+int find_search_domain_name_len(struct option_data *, int *);
+void expand_search_domain_name(struct option_data *, int *,
+    unsigned char **);
 
 /*
  * Parse options out of the specified buffer, storing addresses of
@@ -570,6 +574,11 @@ do_packet(unsigned int from_port, struct in_addr from,
                }
        }
 
+       /* Expand DHCP Domain Search option. */
+       if (options_valid) {
+               expand_domain_search(options);
+       }
+
        type = "<unknown>";
        handler = NULL;
 
@@ -616,4 +625,169 @@ do_packet(unsigned int from_port, struct in_addr from,
 
        for (i = 0; i < 256; i++)
                free(options[i].data);
+}
+
+/*
+ * Expand DHCP Domain Search option. The value of this option is
+ * encoded like DNS' list of labels. See:
+ *   RFC 3397
+ *   RFC 1035
+ */
+void
+expand_domain_search(struct option_data *options)
+{
+       int offset, expanded_len, next_domain_len;
+       struct option_data *option;
+       unsigned char *domain_search, *cursor;
+
+       if (options[DHO_DOMAIN_SEARCH].data == NULL)
+               return;
+
+       option = &options[DHO_DOMAIN_SEARCH];
+
+       /* Compute final expanded length. */
+       expanded_len = 0;
+       offset = 0;
+       while (offset < option->len) {
+               next_domain_len = find_search_domain_name_len(option, &offset);
+               if (next_domain_len < 0)
+                       /* The Domain Search option value is invalid. */
+                       return;
+
+               /* We add 1 for the space between domain names. */
+               expanded_len += next_domain_len + 1;
+       }
+       if (expanded_len > 0)
+               /* Remove 1 for the superfluous trailing space. */
+               --expanded_len;
+
+       domain_search = malloc(expanded_len + 1);
+       if (domain_search == NULL)
+               error("Can't allocate storage for expanded domain-search\n");
+
+       offset = 0;
+       cursor = domain_search;
+       while (offset < option->len) {
+               expand_search_domain_name(option, &offset, &cursor);
+               cursor[0] = ' ';
+               cursor++;
+       }
+       domain_search[expanded_len] = '\0';
+
+       free(option->data);
+       option->len = expanded_len;
+       option->data = domain_search;
+}
+
+int
+find_search_domain_name_len(struct option_data *option, int *offset)
+{
+       int domain_name_len, i, label_len, pointer, pointed_len;
+
+       domain_name_len = 0;
+
+       i = *offset;
+       while (i <= option->len) {
+               label_len = option->data[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 >= option->len) {
+                               /* The pointer is truncated. */
+                               warning("Truncated pointer in DHCP Domain "
+                                   "Search option.");
+                               return (-1);
+                       }
+
+                       pointer = ((label_len & ~(0xC0)) << 8) +
+                           option->data[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 = find_search_domain_name_len(option,
+                           &pointer);
+                       domain_name_len += pointed_len;
+
+                       *offset = i + 2;
+                       return (domain_name_len);
+               }
+
+               /* offset + label + next len */
+               if (i + label_len + 1 > option->len) {
+                       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;
+
+               /* Move cursor. */
+               i += label_len + 1;
+       }
+
+       warning("Truncated DHCP Domain Search option.");
+
+       return (-1);
+}
+
+void
+expand_search_domain_name(struct option_data *option, int *offset,
+    unsigned char **domain_search)
+{
+       int i, label_len, pointer;
+       unsigned char *cursor;
+
+       /*
+        * This is the same loop than the function above
+        * (find_search_domain_name_len). Therefore, we remove checks,
+        * they're already done. Here, we just make the copy.
+        */
+       i = *offset;
+       cursor = *domain_search;
+       while (i <= option->len) {
+               label_len = option->data[i];
+               if (label_len == 0) {
+                       /*
+                        * A zero-length label marks the end of this
+                        * domain name.
+                        */
+                       *offset = i + 1;
+                       *domain_search = cursor;
+                       return;
+               } else if (label_len & 0xC0) {
+                       /* This is a pointer to another list of labels. */
+                       pointer = ((label_len & ~(0xC0)) << 8) +
+                           option->data[i + 1];
+
+                       expand_search_domain_name(option, &pointer, &cursor);
+
+                       *offset = i + 2;
+                       *domain_search = cursor;
+                       return;
+               }
+
+               /* Copy the label found. */
+               memcpy(cursor, option->data + i + 1, label_len);
+               cursor[label_len] = '.';
+
+               /* Move cursor. */
+               i += label_len + 1;
+               cursor += label_len + 1;
+       }
 }
diff -up /root/dhclient.head/tables.c ./tables.c
--- /root/dhclient.head/tables.c        Wed Sep  2 11:11:54 2015
+++ ./tables.c  Wed Sep  2 10:26:18 2015
@@ -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" },


Reply via email to