summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkrw <krw@openbsd.org>2019-04-02 02:59:43 +0000
committerkrw <krw@openbsd.org>2019-04-02 02:59:43 +0000
commitf1e54185fbd1f7e608e5f5532ac018acf887fd4e (patch)
tree6faec35f034478d10bb2753dfc33422a3d75ccba
parentUse consistant idiom for checking return value of (diff)
downloadwireguard-openbsd-f1e54185fbd1f7e608e5f5532ac018acf887fd4e.tar.xz
wireguard-openbsd-f1e54185fbd1f7e608e5f5532ac018acf887fd4e.zip
Add human readable parsing/display of RFC1035 data in domain-search
option. Replace handrolled dn_expand() with the system version. Existing hex octet versions still accepted. New format is option domain-search "my.domain.org", "fw.my.domain.org"; It is now possible to append and prepend domains to the list provided by the server. Documention update to dhcp-options(5) in the pipeline! Inspired by dhcpd(8) domain-search diff from William Ahern. Code peered at by florian@ and kn@.
-rw-r--r--sbin/dhclient/clparse.c68
-rw-r--r--sbin/dhclient/dhclient.c79
-rw-r--r--sbin/dhclient/dhcpd.h4
-rw-r--r--sbin/dhclient/options.c167
4 files changed, 186 insertions, 132 deletions
diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c
index 6998056e8fb..f934a171de5 100644
--- a/sbin/dhclient/clparse.c
+++ b/sbin/dhclient/clparse.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clparse.c,v 1.184 2019/03/20 20:10:00 krw Exp $ */
+/* $OpenBSD: clparse.c,v 1.185 2019/04/02 02:59:43 krw Exp $ */
/* Parser for dhclient config and lease files. */
@@ -69,6 +69,7 @@
void parse_conf_decl(FILE *, char *);
int parse_hex_octets(FILE *, unsigned int *, uint8_t **);
+int parse_domain_list(FILE *, int *, char **);
int parse_option_list(FILE *, int *, uint8_t *);
int parse_interface(FILE *, char *);
int parse_lease(FILE *, char *, struct client_lease **);
@@ -471,6 +472,53 @@ parse_hex_octets(FILE *cfile, unsigned int *len, uint8_t **buf)
return 0;
}
+int
+parse_domain_list(FILE *cfile, int *len, char **dp)
+{
+ uint8_t buf[DHCP_DOMAIN_SEARCH_LEN];
+ char *domain;
+ int count, token;
+
+ memset(buf, 0, sizeof(buf));
+ count = 0;
+
+ do {
+ if (parse_string(cfile, NULL, &domain) == 0)
+ return 0;
+
+ count++;
+ if (count > DHCP_DOMAIN_SEARCH_CNT) {
+ parse_warn("more than 6 search domains");
+ break;
+ }
+
+ if (count > 1)
+ strlcat(buf, " ", sizeof(buf));
+ if (strlcat(buf, domain, sizeof(buf)) >= sizeof(buf)) {
+ parse_warn("domain list too long");
+ break;
+ }
+
+ token = peek_token(NULL, cfile);
+ if (token == ';') {
+ *dp = strdup(buf);
+ if (*dp == NULL)
+ fatal("domain name list");
+ *len = strlen(buf) + 1;
+ memcpy(*dp, buf, *len);
+ return 1;
+ }
+ token = next_token(NULL, cfile);
+ if (token != ',')
+ parse_warn("';' or ',' expected");
+ } while (token == ',');
+
+ if (token != ';')
+ skip_to_semi(cfile);
+
+ return 0;
+}
+
/*
* option-list :==
* <nil>
@@ -824,6 +872,24 @@ parse_option(FILE *cfile, int *code, struct option_data *options)
len = 1 + (cidr[0] + 7) / 8;
dp = cidr;
break;
+ case 'D':
+ if (peek_token(NULL, cfile) == TOK_STRING) {
+ if (parse_domain_list(cfile, &len,
+ (char **)&dp) == 0)
+ return 0;
+ } else if (parse_hex_octets(cfile, &len, &dp)
+ == 0) {
+ return 0;
+ } else {
+ val = rfc1035_as_string(dp, len);
+ free(dp);
+ dp = strdup(val);
+ if (dp == NULL)
+ fatal("RFC1035 hex octets");
+ len = strlen(dp) + 1;
+ }
+ freedp = 1;
+ break;
default:
log_warnx("%s: bad format %c in "
"parse_option_param", log_procname, *fmt);
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
index e4fc18d214d..b13bb985032 100644
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhclient.c,v 1.630 2019/03/22 16:45:48 krw Exp $ */
+/* $OpenBSD: dhclient.c,v 1.631 2019/04/02 02:59:43 krw Exp $ */
/*
* Copyright 2004 Henning Brauer <henning@openbsd.org>
@@ -1127,17 +1127,20 @@ packet_to_lease(struct interface_info *ifi, struct option_data *options)
if (options[i].len == 0)
continue;
name = code_to_name(i);
- pretty = pretty_print_option(i, &options[i], 0);
+ if (i == DHO_DOMAIN_SEARCH) {
+ /* Replace RFC 1035 data with a string. */
+ pretty = rfc1035_as_string(options[i].data, options[i].len);
+ free(options[i].data);
+ options[i].data = strdup(pretty);
+ if (options[i].data == NULL)
+ fatal("RFC1035 string");
+ options[i].len = strlen(lease->options[i].data) + 1;
+ } else
+ pretty = pretty_print_option(i, &options[i], 0);
if (strlen(pretty) == 0)
continue;
switch (i) {
case DHO_DOMAIN_SEARCH:
- if (strlen(pretty) == 0 || res_hnok_list(pretty) == 0) {
- log_debug("%s: invalid host name in %s",
- log_procname, name);
- continue;
- }
- break;
case DHO_DOMAIN_NAME:
/*
* Allow deviant but historically blessed
@@ -1865,7 +1868,6 @@ lease_as_proposal(struct client_lease *lease)
{
struct proposal *proposal;
struct option_data *opt;
- char *pretty;
proposal = calloc(1, sizeof(*proposal));
if (proposal == NULL)
@@ -1921,11 +1923,9 @@ lease_as_proposal(struct client_lease *lease)
if (lease->options[DHO_DOMAIN_SEARCH].len != 0) {
opt = &lease->options[DHO_DOMAIN_SEARCH];
- pretty = pretty_print_domain_search(opt->data, opt->len);
- if (pretty != NULL && strlen(pretty) > 0 && strlen(pretty) <
- sizeof(proposal->rtsearch)) {
- proposal->rtsearch_len = strlen(pretty);
- memcpy(proposal->rtsearch, pretty,
+ if (opt->len < sizeof(proposal->rtsearch)) {
+ proposal->rtsearch_len = strlen(opt->data);
+ memcpy(proposal->rtsearch, opt->data,
proposal->rtsearch_len);
proposal->addrs |= RTA_SEARCH;
} else
@@ -2134,6 +2134,57 @@ res_hnok_list(const char *names)
return count > 0 && count < 7 && hn == NULL;
}
+/*
+ * Decode a byte string encoding a list of domain names as specified in RFC 1035
+ * section 4.1.4.
+ *
+ * The result is a string consisting of a blank separated list of domain names.
+ *
+ e.g. 3:65:6e:67:5:61:70:70:6c:65:3:63:6f:6d:0:9:6d:61:72:6b:65:74:69:6e:67:c0:04
+ *
+ * 3 |'e'|'n'|'g'| 5 |'a'|'p'|'p'|'l'|
+ * 'e'| 3 |'c'|'o'|'m'| 0 | 9 |'m'|'a'|
+ * 'r'|'k'|'e'|'t'|'i'|'n'|'g'|xC0|x04|
+ *
+ * will be translated to
+ *
+ * "eng.apple.com. marketing.apple.com."
+ */
+char *
+rfc1035_as_string(unsigned char *src, size_t srclen)
+{
+ static char search[DHCP_DOMAIN_SEARCH_LEN];
+ unsigned char name[DHCP_DOMAIN_SEARCH_LEN];
+ unsigned char *endsrc, *cp;
+ int len, domains;
+
+ memset(search, 0, sizeof(search));
+
+ /* Compute expanded length. */
+ domains = 0;
+ cp = src;
+ endsrc = src + srclen;
+
+ while (cp < endsrc && domains < DHCP_DOMAIN_SEARCH_CNT) {
+ len = dn_expand(src, endsrc, cp, name, sizeof(name));
+ if (len == -1)
+ goto bad;
+ cp += len;
+ if (domains > 0)
+ strlcat(search, " ", sizeof(search));
+ strlcat(search, name, sizeof(search));
+ if (strlcat(search, ".", sizeof(search)) >= sizeof(search))
+ goto bad;
+ domains++;
+ }
+
+ return search;
+
+bad:
+ memset(search, 0, sizeof(search));
+ return search;
+}
+
void
fork_privchld(struct interface_info *ifi, int fd, int fd2)
{
diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h
index a765409d961..980777a629d 100644
--- a/sbin/dhclient/dhcpd.h
+++ b/sbin/dhclient/dhcpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcpd.h,v 1.276 2019/03/22 16:45:48 krw Exp $ */
+/* $OpenBSD: dhcpd.h,v 1.277 2019/04/02 02:59:43 krw Exp $ */
/*
* Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
@@ -160,7 +160,6 @@ struct option_data *unpack_options(struct dhcp_packet *);
char *pretty_print_option(unsigned int, struct option_data *,
int);
char *pretty_print_string(unsigned char *, size_t, int);
-char *pretty_print_domain_search(unsigned char *, size_t);
char *code_to_name(int);
char *code_to_format(int);
int code_to_action(int, int);
@@ -224,6 +223,7 @@ void bootreply(struct interface_info *, struct option_data *,
void free_client_lease(struct client_lease *);
void routefd_handler(struct interface_info *, int);
void state_preboot(struct interface_info *);
+char *rfc1035_as_string(unsigned char *, size_t);
/* packet.c */
void assemble_eh_header(struct ether_addr, struct ether_header *);
diff --git a/sbin/dhclient/options.c b/sbin/dhclient/options.c
index 38beeb18834..833a0aebaad 100644
--- a/sbin/dhclient/options.c
+++ b/sbin/dhclient/options.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: options.c,v 1.117 2019/03/22 16:45:48 krw Exp $ */
+/* $OpenBSD: options.c,v 1.118 2019/04/02 02:59:43 krw Exp $ */
/* DHCP options parsing and reassembly. */
@@ -51,6 +51,7 @@
#include <netinet/if_ether.h>
#include <ctype.h>
+#include <resolv.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -64,7 +65,8 @@
int parse_option_buffer(struct option_data *, unsigned char *, int);
void pretty_print_classless_routes(unsigned char *, size_t, unsigned char *,
size_t);
-int expand_search_domain_name(unsigned char *, size_t, int *, unsigned char *);
+void pretty_print_domain_list(unsigned char *, size_t, unsigned char *,
+ size_t);
/*
* DHCP Option names, formats and codes, from RFC1533.
@@ -82,6 +84,7 @@ int expand_search_domain_name(unsigned char *, size_t, int *, unsigned char *);
* A - array of whatever precedes (e.g., IA means array of IP addresses)
* C - CIDR description
* X - hex octets
+ * D - domain name list, comma separated list of domain names.
*/
static const struct {
@@ -207,7 +210,7 @@ static const struct {
/* 116 */ { NULL, NULL },
/* 117 */ { NULL, NULL },
/* 118 */ { NULL, NULL },
- /* 119 */ { "domain-search", "X" },
+ /* 119 */ { "domain-search", "D" },
/* 120 */ { NULL, NULL },
/* 121 */ { "classless-static-routes", "CIA" },
/* 122 */ { NULL, NULL },
@@ -410,10 +413,7 @@ code_to_action(int code, int action)
char *fmt;
fmt = code_to_format(code);
- if (fmt == NULL || strpbrk(fmt, "At") != NULL)
- return action;
-
- if (fmt[0] == 'X' && code != DHO_DOMAIN_SEARCH)
+ if (fmt == NULL || strpbrk(fmt, "ADtX") != NULL)
return action;
/*
@@ -652,122 +652,56 @@ bad:
memset(buf, 0, buflen);
}
-int
-expand_search_domain_name(unsigned char *src, size_t srclen, int *offset,
- unsigned char *domain_search)
+/*
+ * Print string containing blank separated list of domain names
+ * as a comma separated list of double-quote delimited strings.
+ *
+ * e.g. "eng.apple.com. marketing.apple.com."
+ *
+ * will be translated to
+ *
+ * "eng.apple.com.", "marketing.apple.com."
+ */
+void
+pretty_print_domain_list(unsigned char *src, size_t srclen,
+ unsigned char *buf, size_t buflen)
{
- char *cursor;
- unsigned int i;
- int domain_name_len, label_len, pointer, pointed_len;
-
- 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) != 0) {
- /* This is a pointer to another list of labels. */
- if (i + 1 >= srclen) {
- /* The pointer is truncated. */
- log_warnx("%s: truncated pointer in DHCP "
- "Domain Search option", log_procname);
- return -1;
- }
+ char *dupnames, *hn, *inputstring;
+ int count;
- pointer = ((label_len & ~(0xC0)) << 8) + src[i + 1];
- if (pointer >= *offset) {
- /*
- * The pointer must indicates a prior
- * occurance.
- */
- log_warnx("%s: invalid forward pointer in DHCP "
- "Domain Search option compression",
- log_procname);
- return -1;
- }
-
- pointed_len = expand_search_domain_name(src, srclen,
- &pointer, domain_search);
- domain_name_len += pointed_len;
+ memset(buf, 0, buflen);
- *offset = i + 2;
- return domain_name_len;
- }
- if (i + label_len + 1 > srclen) {
- log_warnx("%s: truncated label in DHCP Domain Search "
- "option", log_procname);
- 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 (srclen >= DHCP_DOMAIN_SEARCH_LEN || strlen(src) == 0 ||
+ strlen(src) > DHCP_DOMAIN_SEARCH_LEN)
+ return;
- if (strlen(domain_search) + domain_name_len >=
- DHCP_DOMAIN_SEARCH_LEN) {
- log_warnx("%s: domain search list too long",
- log_procname);
- return -1;
- }
+ dupnames = inputstring = strdup(src);
+ if (inputstring == NULL)
+ fatal("domain name list");
- /* Copy the label found. */
- memcpy(cursor, src + i + 1, label_len);
- cursor[label_len] = '.';
-
- /* Move cursor. */
- i += label_len + 1;
- cursor += label_len + 1;
+ count = 0;
+ while ((hn = strsep(&inputstring, " \t")) != NULL) {
+ if (strlen(hn) == 0)
+ continue;
+ if (res_hnok(hn) == 0)
+ goto bad;
+ if (count > 0)
+ strlcat(buf, ", ", buflen);
+ strlcat(buf, "\"", buflen);
+ strlcat(buf, hn, buflen);
+ if (strlcat(buf, "\"", buflen) >= buflen)
+ goto bad;
+ count++;
+ if (count > DHCP_DOMAIN_SEARCH_CNT)
+ goto bad;
}
- log_warnx("%s: truncated DHCP Domain Search option", log_procname);
-
- return -1;
-}
-
-/*
- * Must special case DHO_DOMAIN_SEARCH because it is encoded as described
- * in RFC 1035 section 4.1.4.
- */
-char *
-pretty_print_domain_search(unsigned char *src, size_t srclen)
-{
- static char domain_search[DHCP_DOMAIN_SEARCH_LEN];
- unsigned char *cursor;
- unsigned int offset;
- int len, expanded_len, domains;
-
- memset(domain_search, 0, sizeof(domain_search));
-
- /* Compute expanded length. */
- expanded_len = len = 0;
- domains = 0;
- offset = 0;
- while (offset < srclen) {
- cursor = domain_search + strlen(domain_search);
- if (domain_search[0] != '\0') {
- *cursor = ' ';
- expanded_len++;
- }
- len = expand_search_domain_name(src, srclen, &offset,
- domain_search);
- if (len == -1)
- return NULL;
- domains++;
- expanded_len += len;
- if (domains > DHCP_DOMAIN_SEARCH_CNT)
- return NULL;
- }
+ free(dupnames);
+ return;
- return domain_search;
+bad:
+ free(dupnames);
+ memset(buf, 0, buflen);
}
/*
@@ -812,6 +746,9 @@ pretty_print_option(unsigned int code, struct option_data *option,
case DHO_CLASSLESS_MS_STATIC_ROUTES:
pretty_print_classless_routes(dp, len, optbuf, sizeof(optbuf));
goto done;
+ case DHO_DOMAIN_SEARCH:
+ pretty_print_domain_list(dp, len, optbuf, sizeof(optbuf));
+ goto done;
default:
break;
}