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 via email to