Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 4 Dec 2011 14:44:32 +0000 (UTC)
From:      Jean-Sebastien Pedron <dumbbell@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r228259 - in head: sbin/dhclient tools/regression/sbin tools/regression/sbin/dhclient
Message-ID:  <201112041444.pB4EiWjW091072@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: dumbbell
Date: Sun Dec  4 14:44:31 2011
New Revision: 228259
URL: http://svn.freebsd.org/changeset/base/228259

Log:
  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)

Added:
  head/tools/regression/sbin/dhclient/
  head/tools/regression/sbin/dhclient/Makefile   (contents, props changed)
  head/tools/regression/sbin/dhclient/fake.c   (contents, props changed)
  head/tools/regression/sbin/dhclient/option-domain-search.c   (contents, props changed)
Modified:
  head/sbin/dhclient/clparse.c
  head/sbin/dhclient/dhclient-script
  head/sbin/dhclient/dhclient.c
  head/sbin/dhclient/dhcp-options.5
  head/sbin/dhclient/dhcp.h
  head/sbin/dhclient/options.c
  head/sbin/dhclient/tables.c
  head/tools/regression/sbin/Makefile

Modified: head/sbin/dhclient/clparse.c
==============================================================================
--- head/sbin/dhclient/clparse.c	Sun Dec  4 12:10:24 2011	(r228258)
+++ head/sbin/dhclient/clparse.c	Sun Dec  4 14:44:31 2011	(r228259)
@@ -100,6 +100,8 @@ read_client_conf(void)
 	    DHO_DOMAIN_NAME_SERVERS;
 	top_level_config.requested_options
 	    [top_level_config.requested_option_count++] = DHO_HOST_NAME;
+	top_level_config.requested_options
+	    [top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH;
 
 	if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
 		do {

Modified: head/sbin/dhclient/dhclient-script
==============================================================================
--- head/sbin/dhclient/dhclient-script	Sun Dec  4 12:10:24 2011	(r228258)
+++ head/sbin/dhclient/dhclient-script	Sun Dec  4 14:44:31 2011	(r228259)
@@ -201,7 +201,9 @@ add_new_resolv_conf() {
 	local tmpres=/var/run/resolv.conf.${interface}
 	rm -f $tmpres
 
-	if [ -n "$new_domain_name" ]; then
+	if [ -n "$new_domain_search" ]; then
+		echo "search $new_domain_search" >>$tmpres
+	elif [ -n "$new_domain_name" ]; then
 		echo "search $new_domain_name" >>$tmpres
 	fi
 

Modified: head/sbin/dhclient/dhclient.c
==============================================================================
--- head/sbin/dhclient/dhclient.c	Sun Dec  4 12:10:24 2011	(r228258)
+++ head/sbin/dhclient/dhclient.c	Sun Dec  4 14:44:31 2011	(r228259)
@@ -2401,6 +2401,7 @@ check_option(struct client_lease *l, int
 		}
 		return (1);
 	case DHO_DOMAIN_NAME:
+	case DHO_DOMAIN_SEARCH:
 		if (!res_hnok(sbuf)) {
 			if (!check_search(sbuf)) {
 				warning("Bogus domain search list %d: %s (%s)",

Modified: head/sbin/dhclient/dhcp-options.5
==============================================================================
--- head/sbin/dhclient/dhcp-options.5	Sun Dec  4 12:10:24 2011	(r228258)
+++ head/sbin/dhclient/dhcp-options.5	Sun Dec  4 14:44:31 2011	(r228259)
@@ -265,6 +265,10 @@ character set.
 .It Ic option domain-name Ar string ;
 This option specifies the domain name that the client should use when
 resolving hostnames via the Domain Name System.
+.It Ic option domain-search Ar string ;
+This option specifies a list of domain names that the client should use
+when resolving hostnames via the Domain Name System. This option is
+defined in RFC 3397.
 .It Ic option swap-server Ar ip-address ;
 This specifies the IP address of the client's swap server.
 .It Ic option root-path Ar string ;

Modified: head/sbin/dhclient/dhcp.h
==============================================================================
--- head/sbin/dhclient/dhcp.h	Sun Dec  4 12:10:24 2011	(r228258)
+++ head/sbin/dhclient/dhcp.h	Sun Dec  4 14:44:31 2011	(r228259)
@@ -169,6 +169,7 @@ struct dhcp_packet {
 #define	DHO_STREETTALK_SERVER		75
 #define	DHO_STREETTALK_DA_SERVER	76
 #define DHO_DHCP_USER_CLASS_ID		77
+#define	DHO_DOMAIN_SEARCH		119
 #define DHO_CLASSLESS_ROUTES		121
 #define DHO_END				255
 

Modified: head/sbin/dhclient/options.c
==============================================================================
--- head/sbin/dhclient/options.c	Sun Dec  4 12:10:24 2011	(r228258)
+++ head/sbin/dhclient/options.c	Sun Dec  4 14:44:31 2011	(r228259)
@@ -55,6 +55,10 @@ void	parse_options(struct packet *);
 void	parse_option_buffer(struct packet *, unsigned char *, int);
 int	store_options(unsigned char *, int, struct tree_cache **,
 	    unsigned char *, int, int, int, int);
+void	expand_domain_search(struct packet *packet);
+int	find_search_domain_name_len(struct option_data *option, int *offset);
+void	expand_search_domain_name(struct option_data *option, int *offset,
+	    unsigned char **domain_search);
 
 
 /*
@@ -94,6 +98,11 @@ parse_options(struct packet *packet)
 			    (unsigned char *)packet->raw->sname,
 			    sizeof(packet->raw->sname));
 	}
+
+	/* Expand DHCP Domain Search option. */
+	if (packet->options_valid) {
+		expand_domain_search(packet);
+	}
 }
 
 /*
@@ -194,6 +203,163 @@ parse_option_buffer(struct packet *packe
 }
 
 /*
+ * 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 packet *packet)
+{
+	int offset, expanded_len;
+	struct option_data *option;
+	unsigned char *domain_search, *cursor;
+
+	if (packet->options[DHO_DOMAIN_SEARCH].data == NULL)
+		return;
+
+	option = &packet->options[DHO_DOMAIN_SEARCH];
+
+	/* Compute final expanded length. */
+	expanded_len = 0;
+	offset = 0;
+	while (offset < option->len) {
+		/* We add 1 for the space between domain names. */
+		expanded_len +=
+		    find_search_domain_name_len(option, &offset) + 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. */
+				error("Truncated pointer in DHCP Domain "
+				    "Search option.");
+			}
+
+			pointer = ((label_len & ~(0xC0)) << 8) +
+			    option->data[i + 1];
+			if (pointer >= *offset) {
+				/*
+				 * The pointer must indicates a prior
+				 * occurance.
+				 */
+				error("Invalid forward pointer in DHCP Domain "
+				    "Search option compression.");
+			}
+
+			pointed_len = find_search_domain_name_len(option,
+			    &pointer);
+			domain_name_len += pointed_len;
+
+			*offset = i + 2;
+			return (domain_name_len);
+		}
+
+		if (i + label_len >= option->len) {
+			error("Truncated label in DHCP Domain Search option.");
+		}
+
+		/*
+		 * 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;
+	}
+
+	error("Truncated DHCP Domain Search option.");
+
+	return (0);
+}
+
+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;
+	}
+}
+
+/*
  * cons options into a big buffer, and then split them out into the
  * three separate buffers if needed.  This allows us to cons up a set of
  * vendor options using the same routine.

Modified: head/sbin/dhclient/tables.c
==============================================================================
--- head/sbin/dhclient/tables.c	Sun Dec  4 12:10:24 2011	(r228258)
+++ head/sbin/dhclient/tables.c	Sun Dec  4 14:44:31 2011	(r228259)
@@ -184,7 +184,7 @@ struct option dhcp_options[256] = {
 	{ "option-116", "X",				&dhcp_universe, 116 },
 	{ "option-117", "X",				&dhcp_universe, 117 },
 	{ "option-118", "X",				&dhcp_universe, 118 },
-	{ "option-119", "X",				&dhcp_universe, 119 },
+	{ "domain-search", "t",				&dhcp_universe, 119 },
 	{ "option-120", "X",				&dhcp_universe, 120 },
 	{ "classless-routes", "BA",			&dhcp_universe, 121 },
 	{ "option-122", "X",				&dhcp_universe, 122 },
@@ -400,12 +400,13 @@ unsigned char dhcp_option_default_priori
 	DHO_IRC_SERVER,
 	DHO_STREETTALK_SERVER,
 	DHO_STREETTALK_DA_SERVER,
+	DHO_DOMAIN_SEARCH,
 
 	/* Presently-undefined options... */
 	62, 63, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
 	92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
 	106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
-	118, 119, 120, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+	118,      120, 122, 123, 124, 125, 126, 127, 128, 129, 130,
 	131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
 	143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
 	155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,

Modified: head/tools/regression/sbin/Makefile
==============================================================================
--- head/tools/regression/sbin/Makefile	Sun Dec  4 12:10:24 2011	(r228258)
+++ head/tools/regression/sbin/Makefile	Sun Dec  4 14:44:31 2011	(r228259)
@@ -1,5 +1,5 @@
 # $FreeBSD$
 
-SUBDIR=	growfs
+SUBDIR=	dhclient growfs
 
 .include <bsd.subdir.mk>

Added: head/tools/regression/sbin/dhclient/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tools/regression/sbin/dhclient/Makefile	Sun Dec  4 14:44:31 2011	(r228259)
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.PATH:	${.CURDIR}/../../../../sbin/dhclient
+
+SRCS=	alloc.c convert.c hash.c options.c tables.c			\
+	fake.c								\
+	option-domain-search.c
+
+CFLAGS+= -I${.CURDIR}/../../../../sbin/dhclient
+LDADD=	-lutil
+
+PROG=	option-domain-search
+
+WARNS?=	2
+
+.include <bsd.prog.mk>

Added: head/tools/regression/sbin/dhclient/fake.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tools/regression/sbin/dhclient/fake.c	Sun Dec  4 14:44:31 2011	(r228259)
@@ -0,0 +1,60 @@
+/* $FreeBSD$ */
+
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "dhcpd.h"
+
+extern jmp_buf env;
+
+void
+error(char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+
+	longjmp(env, 1);
+}
+
+int
+warning(char *fmt, ...)
+{
+	int ret;
+	va_list ap;
+
+	va_start(ap, fmt);
+	ret = vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+
+	return ret;
+}
+
+int
+note(char *fmt, ...)
+{
+	int ret;
+	va_list ap;
+
+	va_start(ap, fmt);
+	ret = vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+
+	return ret;
+}
+
+void
+bootp(struct packet *packet)
+{
+}
+
+void
+dhcp(struct packet *packet)
+{
+}

Added: head/tools/regression/sbin/dhclient/option-domain-search.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tools/regression/sbin/dhclient/option-domain-search.c	Sun Dec  4 14:44:31 2011	(r228259)
@@ -0,0 +1,328 @@
+/* $FreeBSD$ */
+
+#include <setjmp.h>
+#include <stdlib.h>
+
+#include "dhcpd.h"
+
+jmp_buf env;
+
+void	expand_domain_search(struct packet *packet);
+
+void
+no_option_present()
+{
+	int ret;
+	struct option_data option;
+	struct packet p;
+
+	option.data = NULL;
+	option.len  = 0;
+	p.options[DHO_DOMAIN_SEARCH] = option;
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (p.options[DHO_DOMAIN_SEARCH].len != 0 ||
+	    p.options[DHO_DOMAIN_SEARCH].data != NULL)
+		abort();
+}
+
+void
+one_domain_valid()
+{
+	int ret;
+	struct packet p;
+	struct option_data *option;
+
+	char *data     = "\007example\003org\0";
+	char *expected = "example.org.";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 13;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (option->len != strlen(expected) ||
+	    strcmp(option->data, expected) != 0)
+		abort();
+
+	free(option->data);
+}
+
+void
+one_domain_truncated1()
+{
+	int ret;
+	struct option_data *option;
+	struct packet p;
+
+	char *data = "\007example\003org";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 12;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (ret != 1)
+		abort();
+
+	free(option->data);
+}
+
+void
+one_domain_truncated2()
+{
+	int ret;
+	struct option_data *option;
+	struct packet p;
+
+	char *data = "\007ex";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 3;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (ret != 1)
+		abort();
+
+	free(option->data);
+}
+
+void
+two_domains_valid()
+{
+	int ret;
+	struct packet p;
+	struct option_data *option;
+
+	char *data     = "\007example\003org\0\007example\003com\0";
+	char *expected = "example.org. example.com.";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 26;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (option->len != strlen(expected) ||
+	    strcmp(option->data, expected) != 0)
+		abort();
+
+	free(option->data);
+}
+
+void
+two_domains_truncated1()
+{
+	int ret;
+	struct option_data *option;
+	struct packet p;
+
+	char *data = "\007example\003org\0\007example\003com";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 25;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (ret != 1)
+		abort();
+
+	free(option->data);
+}
+
+void
+two_domains_truncated2()
+{
+	int ret;
+	struct option_data *option;
+	struct packet p;
+
+	char *data = "\007example\003org\0\007ex";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 16;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (ret != 1)
+		abort();
+
+	free(option->data);
+}
+
+void
+two_domains_compressed()
+{
+	int ret;
+	struct packet p;
+	struct option_data *option;
+
+	char *data     = "\007example\003org\0\006foobar\xc0\x08";
+	char *expected = "example.org. foobar.org.";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 22;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (option->len != strlen(expected) ||
+	    strcmp(option->data, expected) != 0)
+		abort();
+
+	free(option->data);
+}
+
+void
+two_domains_infloop()
+{
+	int ret;
+	struct packet p;
+	struct option_data *option;
+
+	char *data = "\007example\003org\0\006foobar\xc0\x0d";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 22;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (ret != 1)
+		abort();
+
+	free(option->data);
+}
+
+void
+two_domains_forwardptr()
+{
+	int ret;
+	struct packet p;
+	struct option_data *option;
+
+	char *data = "\007example\003org\xc0\x0d\006foobar\0";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 22;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (ret != 1)
+		abort();
+
+	free(option->data);
+}
+
+void
+two_domains_truncatedptr()
+{
+	int ret;
+	struct packet p;
+	struct option_data *option;
+
+	char *data = "\007example\003org\0\006foobar\xc0";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 21;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (ret != 1)
+		abort();
+
+	free(option->data);
+}
+
+void
+multiple_domains_valid()
+{
+	int ret;
+	struct packet p;
+	struct option_data *option;
+
+	char *data =
+	    "\007example\003org\0\002cl\006foobar\003com\0\002fr\xc0\x10";
+
+	char *expected = "example.org. cl.foobar.com. fr.foobar.com.";
+
+	option = &p.options[DHO_DOMAIN_SEARCH];
+	option->len  = 33;
+	option->data = malloc(option->len);
+	memcpy(option->data, data, option->len);
+
+	ret = setjmp(env);
+	if (ret == 0)
+		expand_domain_search(&p);
+
+	if (option->len != strlen(expected) ||
+	    strcmp(option->data, expected) != 0)
+		abort();
+
+	free(option->data);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+	no_option_present();
+
+	one_domain_valid();
+	one_domain_truncated1();
+	one_domain_truncated2();
+
+	two_domains_valid();
+	two_domains_truncated1();
+	two_domains_truncated2();
+
+	two_domains_compressed();
+	two_domains_infloop();
+	two_domains_forwardptr();
+	two_domains_truncatedptr();
+
+	multiple_domains_valid();
+
+	return (0);
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201112041444.pB4EiWjW091072>