diff options
author | 2019-04-02 02:59:43 +0000 | |
---|---|---|
committer | 2019-04-02 02:59:43 +0000 | |
commit | f1e54185fbd1f7e608e5f5532ac018acf887fd4e (patch) | |
tree | 6faec35f034478d10bb2753dfc33422a3d75ccba | |
parent | Use consistant idiom for checking return value of (diff) | |
download | wireguard-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.c | 68 | ||||
-rw-r--r-- | sbin/dhclient/dhclient.c | 79 | ||||
-rw-r--r-- | sbin/dhclient/dhcpd.h | 4 | ||||
-rw-r--r-- | sbin/dhclient/options.c | 167 |
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; } |