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