[patch] update unbound forwards with dhclient nameservers

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

[patch] update unbound forwards with dhclient nameservers

Gregor Best
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 *);

Reply | Threaded
Open this post in threaded view
|

Re: [patch] update unbound forwards with dhclient nameservers

Florian Obser-2
On Sun, Jul 19, 2015 at 01:08:46PM +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.
>

Oh god, yes please. I always wanted to write this diff myself ;)
More comments in the diff below.

> 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 }

Use update-unbound (minus, not underscrore) for the keyword; also
update the man page.


>  };
>  
>  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;

I think you should rejiggle this loop. I don't think you will have the
case where you have two seperators in a row because the sender in
dhclient sanatizes this. However, if you do end up with two seperators
suddenly you have a NULL arg in the middle and you'll lose the rest of
the args.


> + 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;

If you rejiggle the loop above this can be a break.

> + 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;

I see that dhclient does the :1 in other structs. It's not
particularly sensible here, just use an int.

>   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 *);
>

--
I'm not entirely sure you are real.

Reply | Threaded
Open this post in threaded view
|

Re: [patch] update unbound forwards with dhclient nameservers

Stuart Henderson-6
In reply to this post by Gregor Best
On 2015/07/19 13:08, 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'm doing this without patches, using a script run by entr (from packages)
to watch the lease file (written with -L /etc/dhclient.lease.<ifname>,
i.e. "!dhclient -L /etc/dhclient.lease.iwn0 iwn0" in /etc/hostname.iwn0).

The code doing -L in dhclient takes care to keep the same inode for this
file specifically to support doing this.

$ cat dhcp-watcher
#!/bin/sh
gw=$(route -n get -inet 0.0.0.0 | awk '/interface/ {print $2}')
dns=$(awk '/domain-name-servers/ {gsub("[;,]", " ", $3); print $3;}' /etc/dhclient.lease.$gw)
unbound-control forward_add . $dns > /dev/null
echo "default now on $gw: $(unbound-control list_forwards)" | logger -t dhcp-watcher

$ cat dhcp-watcher.run
#!/bin/sh
/etc/dhcp-watcher
echo /etc/dhclient.lease.* | tr ' ' '\n' | /usr/local/bin/entr /etc/dhcp-watcher

Reply | Threaded
Open this post in threaded view
|

Re: [patch] update unbound forwards with dhclient nameservers

phessler
In reply to this post by Gregor Best
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:

       `

Reply | Threaded
Open this post in threaded view
|

Re: [patch] update unbound forwards with dhclient nameservers

Gregor Best-2
In reply to this post by Florian Obser-2
On Sun, Jul 19, 2015 at 03:31:33PM +0000, Florian Obser wrote:
> [...]
> Oh god, yes please. I always wanted to write this diff myself ;)
> More comments in the diff below.
> [...]

Great to read. I've attached an updated patch.

--
        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 19 Jul 2015 17:31:13 -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 19 Jul 2015 17:48:11 -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,89 @@ 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, **srv;
+ int rslt, i = 0;
+ 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";
+ srv = &args[2];
+ while (i < MAXNS) {
+ p = strsep(&ns, " ");
+ if (p == NULL)
+ break;
+ if (*p == '\0')
+ continue;
+ rslt = asprintf(&srv[i++], "%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 (!srv[i])
+ break;
+ free(srv[i]);
+ }
 }
 
 /*
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 19 Jul 2015 17:31:34 -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 19 Jul 2015 17:32:27 -0000
@@ -146,6 +146,7 @@ struct client_config {
  time_t backoff_cutoff;
  enum { IGNORE, ACCEPT, PREFER }
  bootp_policy;
+ int update_unbound;
  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 *);

Reply | Threaded
Open this post in threaded view
|

Re: [patch] update unbound forwards with dhclient nameservers

Stuart Henderson-6
On 2015/07/19 19:53, Gregor Best wrote:
> On Sun, Jul 19, 2015 at 03:31:33PM +0000, Florian Obser wrote:
> > [...]
> > Oh god, yes please. I always wanted to write this diff myself ;)
> > More comments in the diff below.
> > [...]
>
> Great to read. I've attached an updated patch.

I'm uncertain about whether dhclient should do this at all, it seems
to be the opposite of the direction dhclient has been going in recently,
but if it is added, it probably wants to be disabled for the ramdisk.

Reply | Threaded
Open this post in threaded view
|

Re: [patch] update unbound forwards with dhclient nameservers

Gregor Best-2
On Sun, Jul 19, 2015 at 07:03:59PM +0100, Stuart Henderson wrote:
> [...]
> I'm uncertain about whether dhclient should do this at all, it seems
> to be the opposite of the direction dhclient has been going in
> recently,
> [...]

I've had a similar intuition at first, but it's one less thing to run
versus the "monitor lease file" approach and it only calls one static
external command with relatively fixed parameters instead of what the
removed `script' option did. In the end, it's a matter of personal
taste, I think.

> [...]
> but if it is added, it probably wants to be disabled for the ramdisk.
> [...]

I've attached an updated patch, in case this is still interesting to
someone. Do I need to add anything other than `#ifdef SMALL' in the
appropriate places?

--
        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 19 Jul 2015 18:44:12 -0000
@@ -75,6 +75,9 @@ read_client_conf(void)
  config->backoff_cutoff = 15;
  config->initial_interval = 3;
  config->bootp_policy = ACCEPT;
+#ifndef SMALL
+ config->update_unbound = 0;
+#endif
  config->requested_options
     [config->requested_option_count++] = DHO_SUBNET_MASK;
  config->requested_options
@@ -270,6 +273,12 @@ parse_client_statement(FILE *cfile)
  if (parse_ip_addr(cfile, &config->next_server))
  parse_semi(cfile);
  break;
+#ifndef SMALL
+ case TOK_UPDATE_UNBOUND:
+ config->update_unbound = 1;
+ parse_semi(cfile);
+ break;
+#endif
  default:
  parse_warn("expecting a statement.");
  if (token != ';')
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 19 Jul 2015 18:48:00 -0000
@@ -341,7 +341,10 @@ static const struct keywords {
  { "send", TOK_SEND },
  { "server-name", TOK_SERVER_NAME },
  { "supersede", TOK_SUPERSEDE },
- { "timeout", TOK_TIMEOUT }
+ { "timeout", TOK_TIMEOUT },
+#ifndef SMALL
+ { "update-unbound", TOK_UPDATE_UNBOUND }
+#endif
 };
 
 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 19 Jul 2015 18:45:28 -0000
@@ -97,6 +97,9 @@ int res_hnok(const char *dn);
 
 void fork_privchld(int, int);
 void get_ifname(char *);
+#ifndef SMALL
+void update_unbound_forwards(struct option_data *);
+#endif
 char *resolv_conf_contents(struct option_data  *,
      struct option_data *);
 void write_resolv_conf(u_int8_t *, size_t);
@@ -903,8 +906,15 @@ bind_lease(void)
  goto newlease;
  }
 
- client->new->resolv_conf = resolv_conf_contents(
-    &options[DHO_DOMAIN_NAME], &options[DHO_DOMAIN_NAME_SERVERS]);
+#ifndef SMALL
+ if (config->update_unbound) {
+ update_unbound_forwards(&options[DHO_DOMAIN_NAME_SERVERS]);
+ } else
+#endif
+ {
+ 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;
@@ -2043,6 +2053,91 @@ get_ifname(char *arg)
  } else if (strlcpy(ifi->name, arg, IFNAMSIZ) >= IFNAMSIZ)
  error("Interface name too long");
 }
+
+#ifndef SMALL
+/*
+ * 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, **srv;
+ int rslt, i = 0;
+ 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";
+ srv = &args[2];
+ while (i < MAXNS) {
+ p = strsep(&ns, " ");
+ if (p == NULL)
+ break;
+ if (*p == '\0')
+ continue;
+ rslt = asprintf(&srv[i++], "%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 (!srv[i])
+ break;
+ free(srv[i]);
+ }
+}
+#endif /* SMALL */
 
 /*
  * Update resolv.conf.
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 19 Jul 2015 17:31:34 -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 19 Jul 2015 18:45:57 -0000
@@ -146,6 +146,9 @@ struct client_config {
  time_t backoff_cutoff;
  enum { IGNORE, ACCEPT, PREFER }
  bootp_policy;
+#ifndef SMALL
+ int update_unbound;
+#endif
  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 19 Jul 2015 18:43:36 -0000
@@ -78,6 +78,9 @@
 #define TOK_REJECT 292
 #define TOK_LINK_TIMEOUT 294
 #define TOK_IGNORE 295
+#ifndef SMALL
+#define TOK_UPDATE_UNBOUND 296
+#endif /* SMALL */
 
 #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 19 Jul 2015 18:46:06 -0000
@@ -93,6 +93,11 @@ dispatch_imsg(struct imsgbuf *ibuf)
  case IMSG_WRITE_OPTION_DB:
  priv_write_option_db(&imsg);
  break;
+#ifndef SMALL
+ case IMSG_UPDATE_UNBOUND_FORWARDS:
+ priv_update_unbound_forwards(&imsg);
+ break;
+#endif
 
  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 19 Jul 2015 18:46:55 -0000
@@ -29,7 +29,10 @@ enum imsg_code {
  IMSG_SET_INTERFACE_MTU,
  IMSG_HUP,
  IMSG_WRITE_RESOLV_CONF,
- IMSG_WRITE_OPTION_DB
+ IMSG_WRITE_OPTION_DB,
+#ifndef SMALL
+ IMSG_UPDATE_UNBOUND_FORWARDS
+#endif
 };
 
 struct imsg_delete_address {
@@ -72,3 +75,6 @@ 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);
+#ifndef SMALL
+void priv_update_unbound_forwards(struct imsg *);
+#endif

Reply | Threaded
Open this post in threaded view
|

Re: [patch] update unbound forwards with dhclient nameservers

Florian Obser-2
On Sun, Jul 19, 2015 at 08:53:04PM +0200, Gregor Best wrote:

> On Sun, Jul 19, 2015 at 07:03:59PM +0100, Stuart Henderson wrote:
> > [...]
> > I'm uncertain about whether dhclient should do this at all, it seems
> > to be the opposite of the direction dhclient has been going in
> > recently,
> > [...]
>
> I've had a similar intuition at first, but it's one less thing to run
> versus the "monitor lease file" approach and it only calls one static
> external command with relatively fixed parameters instead of what the
> removed `script' option did. In the end, it's a matter of personal
> taste, I think.

Indeed, I prodded krw@ to have a look / final say.

If it's inaproriate for dhclient we might re-consider this for the
magical "manage-network-daemon" if/when it shows up.

As far as I'm concerned having unbound run on localhost is a sensible
setup. Btw. my use case is for stupid networks that require that you
use their resolvers.


--
I'm not entirely sure you are real.