systemd's dhcp client doesn't accept the hack of putting multiple,
space-separated search domains in the domain-name option. The following
patch parses option domain-search as a list of host names and uses
dn_comp(3) from libc to compress the list for the on-wire option value.

Example dhcpd.conf usage:

  option domain-search openbsd.org, example.com, a.example.com;

Index: confpars.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/confpars.c,v
retrieving revision 1.33
diff -u -p -r1.33 confpars.c
--- confpars.c  24 Apr 2017 14:58:36 -0000      1.33
+++ confpars.c  27 Feb 2019 07:23:41 -0000
@@ -43,7 +43,9 @@
 
 #include <net/if.h>
 
+#include <limits.h>
 #include <netdb.h>
+#include <resolv.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -1207,6 +1209,12 @@ parse_option_param(FILE *cfile, struct g
                                        tree = tree_concat(tree, tree_const(
                                            buf, (cprefix + 7) / 8));
                                break;
+                       case 'Z':
+                               t = parse_domain_and_comp(cfile, uniform);
+                               if (!t)
+                                       return;
+                               tree = tree_concat(tree, t);
+                               break;
                        default:
                                log_warnx("Bad format %c in "
                                    "parse_option_param.", *fmt);
@@ -1467,4 +1475,82 @@ parse_address_range(FILE *cfile, struct 
 
        /* Create the new address range. */
        new_address_range(low, high, subnet, dynamic);
+}
+
+static void push_domain_list(char ***domains, size_t *count, char *domain)
+{
+       *domains = reallocarray(*domains, *count + 1, sizeof **domains);
+       if (!*domains)
+               fatalx("Can't allocate domain list");
+
+       (*domains)[*count] = domain;
+       ++*count;
+}
+
+static void
+free_domain_list(char **domains, size_t count)
+{
+       for (size_t i = 0; i < count; i++)
+               free(domains[i]);
+       free(domains);
+}
+
+struct tree *
+parse_domain_and_comp(FILE *cfile, int uniform)
+{
+       char **domains = NULL;
+       size_t count = 0;
+       unsigned char *buf = NULL;
+       size_t bufsiz = 0, bufn = 0;
+       unsigned char **bufptrs = NULL;
+       struct tree *rv = NULL;
+       int token;
+
+       do {
+               char *domain;
+
+               domain = parse_host_name(cfile);
+               if (!domain)
+                       goto error;
+               push_domain_list(&domains, &count, domain);
+               /*
+                * openbsd.org normally compresses to [7]openbsd[3]org[0]. 
+                * +2 to string length provides space for leading and
+                * trailing (root) prefix lengths not already accounted for
+                * by dots, and also provides sufficient space for pointer
+                * compression.
+                */
+               bufsiz = bufsiz + 2 + strlen(domain);
+               token = peek_token(NULL, cfile);
+               if (token == ',')
+                       token = next_token(NULL, cfile);
+       } while (uniform && token == ',');
+
+       buf = malloc(bufsiz);
+       if (!buf)
+               fatalx("Can't allocate compressed domain buffer");
+       bufptrs = calloc(count + 1, sizeof *bufptrs);
+       if (!bufptrs)
+               fatalx("Can't allocate compressed pointer list");
+       bufptrs[0] = buf;
+       
+       /* dn_comp takes an int for the output buffer size */
+       if (!(bufsiz <= INT_MAX))
+               fatalx("Size of compressed domain buffer too large");
+       for (size_t i = 0; i < count; i++) {
+               int n;
+
+               /* see bufsiz <= INT_MAX assertion, above */
+               n = dn_comp(domains[i], &buf[bufn], bufsiz - bufn, bufptrs, 
&bufptrs[count + 1]);
+               if (n == -1)
+                       fatalx("Can't compress domain");
+               bufn += (size_t)n;
+       }
+
+       rv = tree_const(buf, bufn);
+error:
+       free_domain_list(domains, count);
+       free(buf);
+       free(bufptrs);
+       return rv;
 }
Index: dhcp.h
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/dhcp.h,v
retrieving revision 1.10
diff -u -p -r1.10 dhcp.h
--- dhcp.h      21 Jan 2014 03:07:51 -0000      1.10
+++ dhcp.h      27 Feb 2019 07:23:41 -0000
@@ -171,6 +171,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
Index: dhcpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/dhcpd.h,v
retrieving revision 1.66
diff -u -p -r1.66 dhcpd.h
--- dhcpd.h     4 Aug 2017 02:01:46 -0000       1.66
+++ dhcpd.h     27 Feb 2019 07:23:41 -0000
@@ -371,6 +371,7 @@ time_t                       parse_date(FILE *);
 unsigned char          *parse_numeric_aggregate(FILE *, unsigned char *,
                            int *, int, int, int);
 void                    convert_num(unsigned char *, char *, int, int);
+struct tree            *parse_domain_and_comp(FILE *, int);
 
 /* tree.c */
 pair                    cons(caddr_t, pair);
Index: tables.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcpd/tables.c,v
retrieving revision 1.13
diff -u -p -r1.13 tables.c
--- tables.c    13 Feb 2017 19:13:14 -0000      1.13
+++ tables.c    27 Feb 2019 07:23:41 -0000
@@ -193,7 +193,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", "ZA",                        &dhcp_universe, 119 },
        { "option-120", "X",                            &dhcp_universe, 120 },
        { "classless-static-routes", "CIA",             &dhcp_universe, 121 },
        { "option-122", "X",                            &dhcp_universe, 122 },
@@ -367,6 +367,7 @@ unsigned char dhcp_option_default_priori
        DHO_BOOT_SIZE,
        DHO_MERIT_DUMP,
        DHO_DOMAIN_NAME,
+       DHO_DOMAIN_SEARCH,
        DHO_SWAP_SERVER,
        DHO_ROOT_PATH,
        DHO_EXTENSIONS_PATH,
@@ -412,7 +413,7 @@ unsigned char dhcp_option_default_priori
         80,  81,       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,
+       110, 111, 112, 113, 114, 115, 116, 117, 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,

Reply via email to