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