summaryrefslogtreecommitdiff
path: root/sbin/dhclient/options.c
diff options
context:
space:
mode:
authorKenneth R Westerback <krw@cvs.openbsd.org>2015-10-26 16:32:34 +0000
committerKenneth R Westerback <krw@cvs.openbsd.org>2015-10-26 16:32:34 +0000
commit17a200456dbcb6b279efef3e2be92bc24ebd0a77 (patch)
tree8e2e054e9f781480c6a3304ca3b4eb066b258f1d /sbin/dhclient/options.c
parent86f40f24a92f411db61b7b115133b3c0809661c7 (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/dhclient/options.c')
-rw-r--r--sbin/dhclient/options.c126
1 files changed, 125 insertions, 1 deletions
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.
*/