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