>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" },