diff options
author | Kenneth R Westerback <krw@cvs.openbsd.org> | 2015-10-26 16:32:34 +0000 |
---|---|---|
committer | Kenneth R Westerback <krw@cvs.openbsd.org> | 2015-10-26 16:32:34 +0000 |
commit | 17a200456dbcb6b279efef3e2be92bc24ebd0a77 (patch) | |
tree | 8e2e054e9f781480c6a3304ca3b4eb066b258f1d /sbin | |
parent | 86f40f24a92f411db61b7b115133b3c0809661c7 (diff) |
Give dhclient(8) the ability to use option 119, a.k.a. "Domain
Search" if supplied by the server.
Requested by a few. Original diff from Ray Lai via tech@.
Tested & ok claudio@
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/dhclient/clparse.c | 4 | ||||
-rw-r--r-- | sbin/dhclient/dhclient.c | 57 | ||||
-rw-r--r-- | sbin/dhclient/dhcp.h | 7 | ||||
-rw-r--r-- | sbin/dhclient/dhcpd.h | 4 | ||||
-rw-r--r-- | sbin/dhclient/options.c | 126 | ||||
-rw-r--r-- | sbin/dhclient/tables.c | 4 |
6 files changed, 183 insertions, 19 deletions
diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c index ae33a75970f..99a895d926b 100644 --- a/sbin/dhclient/clparse.c +++ b/sbin/dhclient/clparse.c @@ -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. */ @@ -89,6 +89,8 @@ read_client_conf(void) 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 [config->requested_option_count++] = DHO_HOST_NAME; diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c index b324e9d81db..8c97f1c859e 100644 --- a/sbin/dhclient/dhclient.c +++ b/sbin/dhclient/dhclient.c @@ -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) diff --git a/sbin/dhclient/dhcp.h b/sbin/dhclient/dhcp.h index 731f8a97054..c0dc07c1e99 100644 --- a/sbin/dhclient/dhcp.h +++ b/sbin/dhclient/dhcp.h @@ -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. */ @@ -50,6 +50,10 @@ #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 diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h index a23f3f15e42..f2868cbb164 100644 --- a/sbin/dhclient/dhcpd.h +++ b/sbin/dhclient/dhcpd.h @@ -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 */ diff --git a/sbin/dhclient/options.c b/sbin/dhclient/options.c index aee0c27566a..389d99feaa7 100644 --- a/sbin/dhclient/options.c +++ b/sbin/dhclient/options.c @@ -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. */ diff --git a/sbin/dhclient/tables.c b/sbin/dhclient/tables.c index 84abdb8fda5..ff6c4dfd847 100644 --- a/sbin/dhclient/tables.c +++ b/sbin/dhclient/tables.c @@ -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" }, |