I really like this idea, modulo the comments that Florian made.
On 2015 Jul 19 (Sun) at 13:08:46 +0200 (+0200), Gregor Best wrote: :Hello, : :the following is a patch that adds an option called `update_unbound' to :dhclient.conf. With this option enabled, dhclient will call : : unbound-control forwards <ns1> <ns2> <ns3> : :instead of rewriting /etc/resolv.conf. : :My usage scenario is that I'm running unbound on my laptop as a local :resolver. /etc/resolv.conf is configured to only use 127.0.0.1 as the :nameserver. : :I use this because I have a few VPN connections that have custom DNS :namespaces, which I manage with Unbound's forward zones. Since :sometimes I am in networks that have a split horizon DNS (e.g. :University, hackerspace, etc...), I can't use a statically configured :fallback forward zone for all my requests. Manually calling using :unbound-control to change the forward DNS became too annoying, so I :wrote this patch. : :Does the patch make sense in OpenBSD or should I keep it as a local :change? : :-- : Gregor Best :-- : :Index: clparse.c :=================================================================== :RCS file: /mnt/media/cvs/src/sbin/dhclient/clparse.c,v :retrieving revision 1.92 :diff -u -p -r1.92 clparse.c :--- clparse.c 18 May 2015 17:51:21 -0000 1.92 :+++ clparse.c 9 Jul 2015 15:00:05 -0000 :@@ -75,6 +75,7 @@ read_client_conf(void) : config->backoff_cutoff = 15; : config->initial_interval = 3; : config->bootp_policy = ACCEPT; :+ config->update_unbound = 0; : config->requested_options : [config->requested_option_count++] = DHO_SUBNET_MASK; : config->requested_options :@@ -269,6 +270,10 @@ parse_client_statement(FILE *cfile) : case TOK_NEXT_SERVER: : if (parse_ip_addr(cfile, &config->next_server)) : parse_semi(cfile); :+ break; :+ case TOK_UPDATE_UNBOUND: :+ config->update_unbound = 1; :+ parse_semi(cfile); : break; : default: : parse_warn("expecting a statement."); :Index: conflex.c :=================================================================== :RCS file: /mnt/media/cvs/src/sbin/dhclient/conflex.c,v :retrieving revision 1.32 :diff -u -p -r1.32 conflex.c :--- conflex.c 18 May 2015 17:51:21 -0000 1.32 :+++ conflex.c 9 Jul 2015 14:54:56 -0000 :@@ -341,7 +341,8 @@ static const struct keywords { : { "send", TOK_SEND }, : { "server-name", TOK_SERVER_NAME }, : { "supersede", TOK_SUPERSEDE }, :- { "timeout", TOK_TIMEOUT } :+ { "timeout", TOK_TIMEOUT }, :+ { "update_unbound", TOK_UPDATE_UNBOUND } : }; : : int kw_cmp(const void *k, const void *e); :Index: dhclient.c :=================================================================== :RCS file: /mnt/media/cvs/src/sbin/dhclient/dhclient.c,v :retrieving revision 1.361 :diff -u -p -r1.361 dhclient.c :--- dhclient.c 18 May 2015 14:59:42 -0000 1.361 :+++ dhclient.c 9 Jul 2015 15:52:41 -0000 :@@ -97,6 +97,7 @@ int res_hnok(const char *dn); : : void fork_privchld(int, int); : void get_ifname(char *); :+void update_unbound_forwards(struct option_data *); : char *resolv_conf_contents(struct option_data *, : struct option_data *); : void write_resolv_conf(u_int8_t *, size_t); :@@ -903,8 +904,12 @@ bind_lease(void) : goto newlease; : } : :- client->new->resolv_conf = resolv_conf_contents( :- &options[DHO_DOMAIN_NAME], &options[DHO_DOMAIN_NAME_SERVERS]); :+ if (config->update_unbound) { :+ update_unbound_forwards(&options[DHO_DOMAIN_NAME_SERVERS]); :+ } else { :+ client->new->resolv_conf = resolv_conf_contents( :+ &options[DHO_DOMAIN_NAME], &options[DHO_DOMAIN_NAME_SERVERS]); :+ } : : /* Replace the old active lease with the new one. */ : client->active = client->new; :@@ -2042,6 +2047,88 @@ get_ifname(char *arg) : close(s); : } else if (strlcpy(ifi->name, arg, IFNAMSIZ) >= IFNAMSIZ) : error("Interface name too long"); :+} :+ :+/* :+ * Update unbound forwarder list :+ */ :+void :+update_unbound_forwards(struct option_data *nameservers) :+{ :+ char *ns; :+ int rslt; :+ :+ if (!nameservers->len) { :+ return; :+ } :+ :+ ns = pretty_print_option(DHO_DOMAIN_NAME_SERVERS, nameservers, 0); :+ :+ rslt = imsg_compose(unpriv_ibuf, IMSG_UPDATE_UNBOUND_FORWARDS, :+ 0, 0, -1, ns, strlen(ns) + 1); :+ :+ if (rslt == -1) { :+ warning("update_unbound_forwards: imsg_compose: %s", :+ strerror(errno)); :+ } :+ :+ flush_unpriv_ibuf("update_unbound_forwards"); :+} :+ :+void :+priv_update_unbound_forwards(struct imsg *imsg) :+{ :+ char *args[MAXNS + 3]; /* `unbound-control', `forward', final NULL */ :+ char *ns, *p; :+ int i, rslt; :+ size_t sz; :+ pid_t child; :+ :+ if (imsg->hdr.len < IMSG_HEADER_SIZE) { :+ warning("short IMSG_UPDATE_UNBOUND_FORWARDS"); :+ return; :+ } :+ :+ ns = imsg->data; :+ sz = imsg->hdr.len - IMSG_HEADER_SIZE; :+ ns[sz] = '\0'; /* Make sure we're terminated properly */ :+ :+ /* Construct unbound-control arguments */ :+ memset(args, 0, sizeof(args)); :+ args[0] = "unbound-control"; :+ args[1] = "forward"; :+ for (i = 0; i < MAXNS; i++) { :+ p = strsep(&ns, " "); :+ if (p == NULL) :+ break; :+ if (*p == '\0') :+ continue; :+ rslt = asprintf(&args[i + 2], "%s", p); :+ if (rslt == -1) :+ error("no memory for nameserver"); :+ } :+ :+ switch ((child = fork())) { :+ case -1: :+ error("cannot fork"); :+ break; :+ case 0: :+ fclose(stdout); /* Prevent noise from unbound-control */ :+ execvp("unbound-control", args); :+ error("execve failed: %s", strerror(errno)); :+ break; :+ default: :+ if (waitpid(child, NULL, 0) < 0) { :+ error("waitpid: %s", strerror(errno)); :+ } :+ break; :+ } :+ :+ for (i = 0; i < MAXNS; i++) { :+ if (!args[i + 2]) :+ continue; :+ free(args[i + 2]); :+ } : } : : /* :Index: dhclient.conf.5 :=================================================================== :RCS file: /mnt/media/cvs/src/sbin/dhclient/dhclient.conf.5,v :retrieving revision 1.31 :diff -u -p -r1.31 dhclient.conf.5 :--- dhclient.conf.5 11 Nov 2013 15:39:20 -0000 1.31 :+++ dhclient.conf.5 9 Jul 2015 16:24:32 -0000 :@@ -406,6 +406,16 @@ specified name. : Interfaces for which there is no interface declaration will use the : parameters declared outside of any interface declaration, : or the default settings. :+.It Ic update_unbound ; :+The :+.Ic update_unbound :+statement causes dhclient to update the unbound forward DNS list with received :+name servers instead of updating /etc/resolv.conf. :+This is essentially the same as calling :+.Pp :+.D1 unbound-control forward <ns1> <ns2> <ns3> :+.Pp :+after acquiring a lease that contains name server information. : .El : .Sh EXAMPLES : The following configuration file is used on a laptop :Index: dhcpd.h :=================================================================== :RCS file: /mnt/media/cvs/src/sbin/dhclient/dhcpd.h,v :retrieving revision 1.150 :diff -u -p -r1.150 dhcpd.h :--- dhcpd.h 18 May 2015 14:59:42 -0000 1.150 :+++ dhcpd.h 9 Jul 2015 14:51:11 -0000 :@@ -146,6 +146,7 @@ struct client_config { : time_t backoff_cutoff; : enum { IGNORE, ACCEPT, PREFER } : bootp_policy; :+ int update_unbound: 1; : TAILQ_HEAD(, reject_elem) reject_list; : char *resolv_tail; : char *filename; :Index: dhctoken.h :=================================================================== :RCS file: /mnt/media/cvs/src/sbin/dhclient/dhctoken.h,v :retrieving revision 1.9 :diff -u -p -r1.9 dhctoken.h :--- dhctoken.h 5 Dec 2013 22:31:35 -0000 1.9 :+++ dhctoken.h 9 Jul 2015 14:55:15 -0000 :@@ -78,6 +78,7 @@ : #define TOK_REJECT 292 : #define TOK_LINK_TIMEOUT 294 : #define TOK_IGNORE 295 :+#define TOK_UPDATE_UNBOUND 296 : : #define is_identifier(x) ((x) >= TOK_FIRST_TOKEN && \ : (x) != TOK_STRING && \ :Index: privsep.c :=================================================================== :RCS file: /mnt/media/cvs/src/sbin/dhclient/privsep.c,v :retrieving revision 1.39 :diff -u -p -r1.39 privsep.c :--- privsep.c 7 Feb 2015 10:08:06 -0000 1.39 :+++ privsep.c 9 Jul 2015 15:20:34 -0000 :@@ -93,6 +93,9 @@ dispatch_imsg(struct imsgbuf *ibuf) : case IMSG_WRITE_OPTION_DB: : priv_write_option_db(&imsg); : break; :+ case IMSG_UPDATE_UNBOUND_FORWARDS: :+ priv_update_unbound_forwards(&imsg); :+ break; : : default: : warning("received unknown message, code %u", :Index: privsep.h :=================================================================== :RCS file: /mnt/media/cvs/src/sbin/dhclient/privsep.h,v :retrieving revision 1.28 :diff -u -p -r1.28 privsep.h :--- privsep.h 10 Feb 2015 04:20:26 -0000 1.28 :+++ privsep.h 9 Jul 2015 15:21:18 -0000 :@@ -19,6 +19,7 @@ : #include <arpa/inet.h> : : #include <imsg.h> :+#include <resolv.h> : : enum imsg_code { : IMSG_NONE, :@@ -29,7 +30,8 @@ enum imsg_code { : IMSG_SET_INTERFACE_MTU, : IMSG_HUP, : IMSG_WRITE_RESOLV_CONF, :- IMSG_WRITE_OPTION_DB :+ IMSG_WRITE_OPTION_DB, :+ IMSG_UPDATE_UNBOUND_FORWARDS : }; : : struct imsg_delete_address { :@@ -72,3 +74,4 @@ void priv_set_interface_mtu(struct imsg_ : void priv_write_resolv_conf(struct imsg *); : void priv_write_option_db(struct imsg *); : void priv_write_file(char *, int, mode_t, uid_t, gid_t, u_int8_t *, size_t); :+void priv_update_unbound_forwards(struct imsg *); : -- QUOTE OF THE DAY: `