On Thu, Nov 28, 2019 at 03:26:34PM +0100, Otto Moerbeek wrote:

> Hi,
> 
> In many offices, split horizon DNS is used. This means that if you are
> in the office you are supposed to use a specific resolver that will
> hand out different results than when asking for the same name on the
> rest of the internet.
> 
> Until now unwind could not really handle that, e.g. in recursing mode,
> it would produce the view as from outside of the office. 
> 
> With this diff, it becomes possible to force using a specific resolver
> when resolving names in specific domains.
> 
> For example, with this unwind.conf:
> 
> # Office forwarder
> forwarder 1.2.3.4 
> force forwarder {
>       myoffice.com
>       dmz.colocation.com
> }
> 
> This will make unwind always use the mentioned forwarder for anything
> under office.com or dmz.colocation.com. If the forwarder is dead,
> regular resolving is done for these names and www.office.com will
> likely return the external address.
> 
> Often split-horizon DNS breaks DNSSEC for these specific domains. If
> that is the case, you can use
> 
> force acceptbogus forwarder { 
>       ... 
> }
> 
> please test this,
> 
>       -Otto
> 
> OAIndex: frontend.c

Dont know hwre that OA is comming from.  But it confuses patch, making
it skip first part of the diff. Proper diff below:

        -Otto

Index: frontend.c
===================================================================
RCS file: /cvs/src/sbin/unwind/frontend.c,v
retrieving revision 1.40
diff -u -p -r1.40 frontend.c
--- frontend.c  27 Nov 2019 17:09:12 -0000      1.40
+++ frontend.c  28 Nov 2019 14:24:17 -0000
@@ -336,6 +336,7 @@ frontend_dispatch_main(int fd, short eve
                case IMSG_RECONF_BLOCKLIST_FILE:
                case IMSG_RECONF_FORWARDER:
                case IMSG_RECONF_DOT_FORWARDER:
+               case IMSG_RECONF_FORCE:
                        imsg_receive_config(&imsg, &nconf);
                        break;
                case IMSG_RECONF_END:
Index: parse.y
===================================================================
RCS file: /cvs/src/sbin/unwind/parse.y,v
retrieving revision 1.20
diff -u -p -r1.20 parse.y
--- parse.y     28 Nov 2019 10:02:44 -0000      1.20
+++ parse.y     28 Nov 2019 14:24:17 -0000
@@ -90,8 +90,9 @@ struct sockaddr_storage       *host_ip(const c
 
 typedef struct {
        union {
-               int64_t          number;
-               char            *string;
+               int64_t                          number;
+               char                            *string;
+               struct force_tree                force;
        } v;
        int lineno;
 } YYSTYPE;
@@ -101,12 +102,13 @@ typedef struct {
 %token INCLUDE ERROR
 %token FORWARDER DOT PORT 
 %token AUTHENTICATION NAME PREFERENCE RECURSOR DHCP STUB
-%token BLOCK LIST LOG
+%token BLOCK LIST LOG FORCE ACCEPTBOGUS
 
 %token <v.string>      STRING
 %token <v.number>      NUMBER
-%type  <v.number>      yesno port dot prefopt log
+%type  <v.number>      yesno port dot prefopt log acceptbogus
 %type  <v.string>      string authname
+%type  <v.force>       force_list
 
 %%
 
@@ -117,6 +119,7 @@ grammar             : /* empty */
                | grammar uw_pref '\n'
                | grammar uw_forwarder '\n'
                | grammar block_list '\n'
+               | grammar force '\n'
                | grammar error '\n'            { file->errors++; }
                ;
 
@@ -311,6 +314,63 @@ dot        :       DOT                             { $$ = 
DOT; }
 log    :       LOG                             { $$ = 1; }
        |       /* empty */                     { $$ = 0; }
        ;
+
+force  :       FORCE acceptbogus prefopt '{' force_list optnl '}' {
+                       struct force_tree_entry *n, *nxt;
+                       int error = 0;
+
+                       for (n = RB_MIN(force_tree, &$5); n != NULL;
+                           n = nxt) {
+                               nxt = RB_NEXT(force_tree, &conf->force, n);
+                               n->acceptbogus = $2;
+                               n->type = $3;
+                               RB_REMOVE(force_tree, &$5, n);
+                               if (RB_INSERT(force_tree, &conf->force,
+                                   n)) {
+                                       yyerror("%s already in an force "
+                                           "list", n->domain);
+                                       error = 1;
+                               }
+                       }
+                       if (error)
+                               YYERROR;
+               }
+       ;
+
+acceptbogus:   ACCEPTBOGUS { $$ = 1; }
+       |       /* empty */ { $$ = 0; }
+       ;
+
+force_list:    force_list optnl STRING {
+                       struct force_tree_entry *e;
+                       size_t                           len;
+
+                       len = strlen($3);
+                       e = malloc(sizeof(*e));
+                       if (e == NULL)
+                               err(1, NULL);
+                       if (strlcpy(e->domain, $3, sizeof(e->domain)) >=
+                           sizeof(e->domain)) {
+                               yyerror("force %s too long", $3);
+                               free($3);
+                               YYERROR;
+                       }
+                       free($3);
+                       if (len == 0 || e->domain[len-1] != '.') {
+                               if (strlcat(e->domain, ".",
+                                   sizeof((e->domain))) >=
+                                   sizeof((e->domain))) {
+                                       yyerror("force %s too long", $3);
+                                       YYERROR;
+                               }
+                       }
+                       RB_INSERT(force_tree, &$$, e);
+               }
+       |       /* empty */ {
+                       RB_INIT(&$$);
+               }
+       ;
+
 %%
 
 struct keywords {
@@ -346,10 +406,12 @@ lookup(char *s)
        /* This has to be sorted always. */
        static const struct keywords keywords[] = {
                {"DoT",                 DOT},
+               {"acceptbogus",         ACCEPTBOGUS},
                {"authentication",      AUTHENTICATION},
                {"block",               BLOCK},
                {"dhcp",                DHCP},
                {"dot",                 DOT},
+               {"force",               FORCE},
                {"forwarder",           FORWARDER},
                {"include",             INCLUDE},
                {"list",                LIST},
Index: printconf.c
===================================================================
RCS file: /cvs/src/sbin/unwind/printconf.c,v
retrieving revision 1.15
diff -u -p -r1.15 printconf.c
--- printconf.c 28 Nov 2019 10:02:44 -0000      1.15
+++ printconf.c 28 Nov 2019 14:24:17 -0000
@@ -31,6 +31,7 @@ print_config(struct uw_conf *conf)
 {
        struct uw_forwarder     *uw_forwarder;
        int                      i;
+       enum uw_resolver_type    j;
 
        if (conf->res_pref.len > 0) {
                printf("preference {");
@@ -68,4 +69,25 @@ print_config(struct uw_conf *conf)
        if (conf->blocklist_file != NULL)
                printf("block list \"%s\"%s\n", conf->blocklist_file,
                    conf->blocklist_log ? " log" : "");
+       for (j = 0; j < UW_RES_NONE; j++) {
+               struct force_tree_entry *e;
+               int                      empty = 1;
+
+               RB_FOREACH(e, force_tree, &conf->force) {
+                       if (e->type != j)
+                               continue;
+                       empty = 0;
+                       break;
+               }
+               if (empty)
+                       continue;
+
+               printf("force %s {", uw_resolver_type_str[j]);
+               RB_FOREACH(e, force_tree, &conf->force) {
+                       if (e->type != j)
+                               continue;
+                       printf("\n\t%s", e->domain);
+               }
+               printf("\n}\n");
+       }
 }
Index: resolver.c
===================================================================
RCS file: /cvs/src/sbin/unwind/resolver.c,v
retrieving revision 1.82
diff -u -p -r1.82 resolver.c
--- resolver.c  28 Nov 2019 10:40:29 -0000      1.82
+++ resolver.c  28 Nov 2019 14:24:17 -0000
@@ -175,6 +175,10 @@ void                        
trust_anchor_resolve_done(struct
                             int, void *, int, int, char *);
 void                    replace_autoconf_forwarders(struct
                             imsg_rdns_proposal *);
+int                     force_tree_cmp(struct force_tree_entry *,
+                            struct force_tree_entry *);
+int                     find_force(struct force_tree *, char *,
+                            struct uw_resolver **);
 int64_t                         histogram_median(int64_t *);
 
 struct uw_conf                 *resolver_conf;
@@ -190,6 +194,8 @@ static struct trust_anchor_head      trust_a
 
 struct event_base              *ev_base;
 
+RB_GENERATE(force_tree, force_tree_entry, entry, force_tree_cmp)
+
 static const char * const       as112_zones[] = {
        /* RFC1918 */
        "10.in-addr.arpa. transparent",
@@ -600,6 +606,7 @@ resolver_dispatch_main(int fd, short eve
                case IMSG_RECONF_BLOCKLIST_FILE:
                case IMSG_RECONF_FORWARDER:
                case IMSG_RECONF_DOT_FORWARDER:
+               case IMSG_RECONF_FORCE:
                        imsg_receive_config(&imsg, &nconf);
                        break;
                case IMSG_RECONF_END:
@@ -656,6 +663,7 @@ void
 setup_query(struct query_imsg *query_imsg)
 {
        struct running_query    *rq;
+       struct uw_resolver      *res;
        int                      i;
 
        if (find_running_query(query_imsg->id) != NULL) {
@@ -673,7 +681,12 @@ setup_query(struct query_imsg *query_ims
        rq->query_imsg = query_imsg;
        rq->next_resolver = 0;
 
-       if (sort_resolver_types(&rq->res_pref) == -1) {
+       find_force(&resolver_conf->force, query_imsg->qname, &res);
+
+       if (res != NULL && res->state != DEAD) {
+               rq->res_pref.len = 1;
+               rq->res_pref.types[0] = res->type;
+       } else if (sort_resolver_types(&rq->res_pref) == -1) {
                log_warn("mergesort");
                free(rq->query_imsg);
                free(rq);
@@ -942,9 +955,10 @@ resolve_done(struct uw_resolver *res, vo
 
        query_imsg->err = 0;
 
-       if (res->state == VALIDATING)
-               query_imsg->bogus = sec == BOGUS;
-       else
+       if (res->state == VALIDATING && sec == BOGUS) {
+               query_imsg->bogus = find_force(&resolver_conf->force,
+                                   query_imsg->qname, NULL) == 0;
+       } else
                query_imsg->bogus = 0;
 
        if (rq) {
@@ -1002,6 +1016,7 @@ new_recursor(void)
                return;
 
        resolvers[UW_RES_RECURSOR] = create_resolver(UW_RES_RECURSOR, 0);
+
        check_resolver(resolvers[UW_RES_RECURSOR]);
 }
 
@@ -1321,7 +1336,7 @@ check_resolver(struct uw_resolver *resol
 
        log_debug("%s: create_resolver", __func__);
        if ((res = create_resolver(resolver_to_check->type, 0)) == NULL)
-               fatal("%s", __func__);
+               fatalx("%s", __func__);
 
        resolver_ref(resolver_to_check);
 
@@ -1346,7 +1361,7 @@ check_resolver(struct uw_resolver *resol
 
        log_debug("%s: create_resolver for oppdot", __func__);
        if ((res = create_resolver(resolver_to_check->type, 1)) == NULL)
-               fatal("%s", __func__);
+               fatalx("%s", __func__);
 
        resolver_ref(resolver_to_check);
 
@@ -1943,6 +1958,45 @@ replace_autoconf_forwarders(struct imsg_
                        free(tmp);
                }
        }
+}
+
+int
+force_tree_cmp(struct force_tree_entry *a, struct force_tree_entry *b)
+{
+       return strcmp(a->domain, b->domain);
+}
+
+int
+find_force(struct force_tree *tree, char *qname, struct uw_resolver **res)
+{
+       struct force_tree_entry *n, e;
+       char                            *p;
+
+       if (res)
+               *res = NULL;
+       if (RB_EMPTY(tree))
+               return 0;
+
+       p = qname;
+       do {
+               if (strlcpy(e.domain, p, sizeof(e.domain)) >= sizeof(e.domain))
+                       fatal("qname too large");
+               n = RB_FIND(force_tree, tree, &e);
+               log_debug("%s: %s -> %p[%s]", __func__, p, n,
+                   n != NULL ?  uw_resolver_type_str[n->type] : "-");
+               if (n != NULL) {
+                       if (res)
+                               *res = resolvers[n->type];
+                       return n->acceptbogus;
+               }
+               if (*p == '.')
+                       p++;
+               p = strchr(p, '.');
+               if (p != NULL && p[1] != '\0')
+                       p++;
+       } while (p != NULL);
+       return 0;
+
 }
 
 int64_t
Index: unwind.c
===================================================================
RCS file: /cvs/src/sbin/unwind/unwind.c,v
retrieving revision 1.41
diff -u -p -r1.41 unwind.c
--- unwind.c    27 Nov 2019 17:11:00 -0000      1.41
+++ unwind.c    28 Nov 2019 14:24:17 -0000
@@ -577,7 +577,8 @@ main_reload(void)
 int
 main_imsg_send_config(struct uw_conf *xconf)
 {
-       struct uw_forwarder     *uw_forwarder;
+       struct uw_forwarder             *uw_forwarder;
+       struct force_tree_entry *force_entry;
 
        /* Send fixed part of config to children. */
        if (main_sendall(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1)
@@ -604,6 +605,11 @@ main_imsg_send_config(struct uw_conf *xc
                    sizeof(*uw_forwarder)) == -1)
                        return (-1);
        }
+       RB_FOREACH(force_entry, force_tree, &xconf->force) {
+               if (main_sendall(IMSG_RECONF_FORCE, force_entry,
+                   sizeof(*force_entry)) == -1)
+                       return (-1);
+       }
 
        /* Tell children the revised config is now complete. */
        if (main_sendall(IMSG_RECONF_END, NULL, 0) == -1)
@@ -625,7 +631,8 @@ main_sendall(enum imsg_type type, void *
 void
 merge_config(struct uw_conf *conf, struct uw_conf *xconf)
 {
-       struct uw_forwarder     *uw_forwarder;
+       struct uw_forwarder             *uw_forwarder;
+       struct force_tree_entry *n, *nxt;
 
        /* Remove & discard existing forwarders. */
        while ((uw_forwarder = TAILQ_FIRST(&conf->uw_forwarder_list)) !=
@@ -639,6 +646,13 @@ merge_config(struct uw_conf *conf, struc
                free(uw_forwarder);
        }
 
+       /* Remove & discard existing force tree. */
+       for (n = RB_MIN(force_tree, &conf->force); n != NULL; n = nxt) {
+               nxt = RB_NEXT(force_tree, &conf->force, n);
+               RB_REMOVE(force_tree, &conf->force, n);
+               free(n);
+       }
+
        memcpy(&conf->res_pref, &xconf->res_pref,
            sizeof(conf->res_pref));
 
@@ -647,12 +661,14 @@ merge_config(struct uw_conf *conf, struc
        conf->blocklist_log = xconf->blocklist_log;
 
        /* Add new forwarders. */
+       /* XXX TAILQ_CONCAT ? */
        while ((uw_forwarder = TAILQ_FIRST(&xconf->uw_forwarder_list)) !=
            NULL) {
                TAILQ_REMOVE(&xconf->uw_forwarder_list, uw_forwarder, entry);
                TAILQ_INSERT_TAIL(&conf->uw_forwarder_list,
                    uw_forwarder, entry);
        }
+       /* XXX TAILQ_CONCAT ? */
        while ((uw_forwarder = TAILQ_FIRST(&xconf->uw_dot_forwarder_list)) !=
            NULL) {
                TAILQ_REMOVE(&xconf->uw_dot_forwarder_list, uw_forwarder,
@@ -660,6 +676,12 @@ merge_config(struct uw_conf *conf, struc
                TAILQ_INSERT_TAIL(&conf->uw_dot_forwarder_list,
                    uw_forwarder, entry);
        }
+       
+       for (n = RB_MIN(force_tree, &xconf->force); n != NULL; n = nxt) {
+               nxt = RB_NEXT(force_tree, &xconf->force, n);    
+               RB_REMOVE(force_tree, &xconf->force, n);
+               RB_INSERT(force_tree, &conf->force, n);
+       }
 
        free(xconf);
 }
@@ -686,6 +708,8 @@ config_new_empty(void)
        TAILQ_INIT(&xconf->uw_forwarder_list);
        TAILQ_INIT(&xconf->uw_dot_forwarder_list);
 
+       RB_INIT(&xconf->force);
+
        return (xconf);
 }
 
@@ -788,8 +812,9 @@ send_blocklist_fd(void)
 void
 imsg_receive_config(struct imsg *imsg, struct uw_conf **xconf)
 {
-       struct uw_conf          *nconf;
-       struct uw_forwarder     *uw_forwarder;
+       struct uw_conf                  *nconf;
+       struct uw_forwarder             *uw_forwarder;
+       struct force_tree_entry *force_entry;
 
        nconf = *xconf;
 
@@ -807,6 +832,7 @@ imsg_receive_config(struct imsg *imsg, s
                memcpy(nconf, imsg->data, sizeof(struct uw_conf));
                TAILQ_INIT(&nconf->uw_forwarder_list);
                TAILQ_INIT(&nconf->uw_dot_forwarder_list);
+               RB_INIT(&nconf->force);
                break;
        case IMSG_RECONF_BLOCKLIST_FILE:
                /* make sure this is a string */
@@ -839,6 +865,18 @@ imsg_receive_config(struct imsg *imsg, s
                    uw_forwarder));
                TAILQ_INSERT_TAIL(&nconf->uw_dot_forwarder_list,
                    uw_forwarder, entry);
+               break;
+       case IMSG_RECONF_FORCE:
+               if (IMSG_DATA_SIZE(*imsg) != sizeof(struct force_tree_entry))
+                       fatalx("%s: IMSG_RECONF_FORCE wrong "
+                           "length: %lu", __func__,
+                           IMSG_DATA_SIZE(*imsg));
+               if ((force_entry = malloc(sizeof(struct
+                   force_tree_entry))) == NULL)
+                       fatal(NULL);
+               memcpy(force_entry, imsg->data, sizeof(struct
+                   force_tree_entry));
+               RB_INSERT(force_tree, &nconf->force, force_entry);
                break;
        default:
                log_debug("%s: error handling imsg %d", __func__,
Index: unwind.conf.5
===================================================================
RCS file: /cvs/src/sbin/unwind/unwind.conf.5,v
retrieving revision 1.19
diff -u -p -r1.19 unwind.conf.5
--- unwind.conf.5       28 Nov 2019 14:05:17 -0000      1.19
+++ unwind.conf.5       28 Nov 2019 14:24:17 -0000
@@ -113,6 +113,14 @@ itself recursively resolves names.
 .Pp
 The default preference is
 .Ic DoT forwarder recursor dhcp stub .
+.It Ic force Oo Cm acceptbogus Oc Ar type Brq Ar name ...
+Force resolving of
+.Ar name
+and its subdomains by the given resolver
+.Ar type .
+If
+.Cm acceptbogus
+is specified validation is not enforced.
 .El
 .Sh FILES
 .Bl -tag -width "/etc/unwind.conf" -compact
Index: unwind.h
===================================================================
RCS file: /cvs/src/sbin/unwind/unwind.h,v
retrieving revision 1.39
diff -u -p -r1.39 unwind.h
--- unwind.h    28 Nov 2019 10:02:44 -0000      1.39
+++ unwind.h    28 Nov 2019 14:24:18 -0000
@@ -19,6 +19,7 @@
  */
 
 #include <sys/types.h>
+#include <sys/tree.h>
 #include <netinet/in.h>        /* INET6_ADDRSTRLEN */
 #include <event.h>
 #include <imsg.h>
@@ -87,6 +88,7 @@ enum imsg_type {
        IMSG_RECONF_BLOCKLIST_FILE,
        IMSG_RECONF_FORWARDER,
        IMSG_RECONF_DOT_FORWARDER,
+       IMSG_RECONF_FORCE,
        IMSG_RECONF_END,
        IMSG_UDP4SOCK,
        IMSG_UDP6SOCK,
@@ -123,6 +125,15 @@ struct uw_forwarder {
        int                                      src;
 };
 
+struct force_tree_entry {
+       RB_ENTRY(force_tree_entry)       entry;
+       char                             domain[NI_MAXHOST];
+       enum uw_resolver_type            type;
+       int                              acceptbogus;
+};
+
+RB_HEAD(force_tree, force_tree_entry);
+
 struct resolver_preference {
        enum uw_resolver_type                    types[UW_RES_NONE];
        int                                      len;
@@ -132,6 +143,7 @@ TAILQ_HEAD(uw_forwarder_head, uw_forward
 struct uw_conf {
        struct uw_forwarder_head         uw_forwarder_list;
        struct uw_forwarder_head         uw_dot_forwarder_list;
+       struct force_tree                force;
        struct resolver_preference       res_pref;
        char                            *blocklist_file;
        int                              blocklist_log;
@@ -168,3 +180,5 @@ void        print_config(struct uw_conf *);
 /* parse.y */
 struct uw_conf *parse_config(char *);
 int             cmdline_symset(char *);
+
+RB_PROTOTYPE(force_tree, force_tree_entry, entry, force_tree_cmp);

Reply via email to