From ae45f46b9e086f477a10c22d4fca494a31328129 Mon Sep 17 00:00:00 2001 From: Tomasz Ziolkowski Date: Thu, 5 Aug 2021 08:44:18 +0200 Subject: [PATCH] Add (stub|forward)-tcp-upstream options which enable using tcp transport only for specified stub/forward zones --- daemon/worker.c | 8 ++-- doc/unbound.conf.5.in | 12 ++++- iterator/iter_delegpt.c | 1 + iterator/iter_delegpt.h | 2 + iterator/iter_fwd.c | 2 + iterator/iter_hints.c | 2 + iterator/iterator.c | 1 + libunbound/libworker.c | 6 +-- libunbound/worker.h | 4 +- smallapp/worker_cb.c | 4 +- .../fwd_udp_with_tcp_upstream.conf | 20 ++++++++ .../fwd_udp_with_tcp_upstream.dsc | 16 +++++++ .../fwd_udp_with_tcp_upstream.post | 10 ++++ .../fwd_udp_with_tcp_upstream.pre | 31 ++++++++++++ .../fwd_udp_with_tcp_upstream.test | 35 ++++++++++++++ .../fwd_udp_with_tcp_upstream.testns | 25 ++++++++++ .../stub_udp_with_tcp_upstream.conf | 19 ++++++++ .../stub_udp_with_tcp_upstream.dsc | 16 +++++++ .../stub_udp_with_tcp_upstream.post | 10 ++++ .../stub_udp_with_tcp_upstream.pre | 35 ++++++++++++++ .../stub_udp_with_tcp_upstream.test | 37 ++++++++++++++ .../stub_udp_with_tcp_upstream.testns | 48 +++++++++++++++++++ util/config_file.h | 2 + util/configlexer.lex | 2 + util/configparser.y | 25 +++++++++- util/fptr_wlist.c | 2 +- util/fptr_wlist.h | 2 +- util/module.h | 3 +- 28 files changed, 363 insertions(+), 17 deletions(-) create mode 100644 testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.conf create mode 100644 testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.dsc create mode 100644 testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.post create mode 100644 testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.pre create mode 100644 testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.test create mode 100644 testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.testns create mode 100644 testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.conf create mode 100644 testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.dsc create mode 100644 testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.post create mode 100644 testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.pre create mode 100644 testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.test create mode 100644 testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.testns diff --git a/daemon/worker.c b/daemon/worker.c index e9e163a04..b994645b4 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1988,8 +1988,8 @@ worker_delete(struct worker* worker) struct outbound_entry* worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* zone, size_t zonelen, int ssl_upstream, - char* tls_auth_name, struct module_qstate* q) + socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream, + int ssl_upstream, char* tls_auth_name, struct module_qstate* q) { struct worker* worker = q->env->worker; struct outbound_entry* e = (struct outbound_entry*)regional_alloc( @@ -1998,7 +1998,7 @@ worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec, return NULL; e->qstate = q; e->qsent = outnet_serviced_query(worker->back, qinfo, flags, dnssec, - want_dnssec, nocaps, q->env->cfg->tcp_upstream, + want_dnssec, nocaps, tcp_upstream, ssl_upstream, tls_auth_name, addr, addrlen, zone, zonelen, q, worker_handle_service_reply, e, worker->back->udp_buff, q->env); if(!e->qsent) { @@ -2045,7 +2045,7 @@ struct outbound_entry* libworker_send_query( uint16_t ATTR_UNUSED(flags), int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), - uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), + uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q)) { diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index d5315d53b..1f7e191ed 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -485,7 +485,9 @@ advertised timeout. .TP .B tcp\-upstream: \fI Enable or disable whether the upstream queries use TCP only for transport. -Default is no. Useful in tunneling scenarios. +Default is no. Useful in tunneling scenarios. If set to no you can specify +TCP transport only for selected forward or stub zones using forward-tcp-upstream +or stub-tcp-upstream respectively. .TP .B udp\-upstream\-without\-downstream: \fI Enable udp upstream even if do-udp is no. Default is no, and this does not @@ -1853,6 +1855,10 @@ Default is no. .B stub\-ssl\-upstream: \fI Alternate syntax for \fBstub\-tls\-upstream\fR. .TP +.B stub\-tcp\-upstream: \fI +If it is set to "yes" then upstream queries use TCP only for transport regardless of global flag tcp-upstream. +Default is no. +.TP .B stub\-no\-cache: \fI Default is no. If enabled, data inside the stub is not cached. This is useful when you want immediate changes to be visible. @@ -1905,6 +1911,10 @@ load CA certs, otherwise the connections cannot be authenticated. .B forward\-ssl\-upstream: \fI Alternate syntax for \fBforward\-tls\-upstream\fR. .TP +.B forward\-tcp\-upstream: \fI +If it is set to "yes" then upstream queries use TCP only for transport regardless of global flag tcp-upstream. +Default is no. +.TP .B forward\-no\-cache: \fI Default is no. If enabled, data inside the forward is not cached. This is useful when you want immediate changes to be visible. diff --git a/iterator/iter_delegpt.c b/iterator/iter_delegpt.c index 9a672b0af..bdac42b0d 100644 --- a/iterator/iter_delegpt.c +++ b/iterator/iter_delegpt.c @@ -73,6 +73,7 @@ struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region) copy->bogus = dp->bogus; copy->has_parent_side_NS = dp->has_parent_side_NS; copy->ssl_upstream = dp->ssl_upstream; + copy->tcp_upstream = dp->tcp_upstream; for(ns = dp->nslist; ns; ns = ns->next) { if(!delegpt_add_ns(copy, region, ns->name, ns->lame)) return NULL; diff --git a/iterator/iter_delegpt.h b/iterator/iter_delegpt.h index 138eb6e1b..9c8cfb281 100644 --- a/iterator/iter_delegpt.h +++ b/iterator/iter_delegpt.h @@ -83,6 +83,8 @@ struct delegpt { uint8_t dp_type_mlc; /** use SSL for upstream query */ uint8_t ssl_upstream; + /** use TCP for upstream query */ + uint8_t tcp_upstream; /** delegpt from authoritative zone that is locally hosted */ uint8_t auth_dp; /*** no cache */ diff --git a/iterator/iter_fwd.c b/iterator/iter_fwd.c index ea3d70e07..128007a04 100644 --- a/iterator/iter_fwd.c +++ b/iterator/iter_fwd.c @@ -276,6 +276,8 @@ read_forwards(struct iter_forwards* fwd, struct config_file* cfg) dp->no_cache = s->no_cache; /* use SSL for queries to this forwarder */ dp->ssl_upstream = (uint8_t)s->ssl_upstream; + /* use TCP for queries to this forwarder */ + dp->tcp_upstream = (uint8_t)s->tcp_upstream; verbose(VERB_QUERY, "Forward zone server list:"); delegpt_log(VERB_QUERY, dp); if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp)) diff --git a/iterator/iter_hints.c b/iterator/iter_hints.c index 60e518122..2af443d8c 100644 --- a/iterator/iter_hints.c +++ b/iterator/iter_hints.c @@ -287,6 +287,8 @@ read_stubs(struct iter_hints* hints, struct config_file* cfg) dp->no_cache = s->no_cache; /* ssl_upstream */ dp->ssl_upstream = (uint8_t)s->ssl_upstream; + /* tcp_upstream */ + dp->tcp_upstream = (uint8_t)s->tcp_upstream; delegpt_log(VERB_QUERY, dp); if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime)) return 0; diff --git a/iterator/iterator.c b/iterator/iterator.c index f0105ad4b..efc94c37b 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2666,6 +2666,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted( ie, iq), &target->addr, target->addrlen, iq->dp->name, iq->dp->namelen, + (iq->dp->tcp_upstream || qstate->env->cfg->tcp_upstream), (iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream), target->tls_auth_name, qstate); if(!outq) { diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 8a9ca9419..151f50cf5 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -881,7 +881,7 @@ void libworker_alloc_cleanup(void* arg) struct outbound_entry* libworker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, int ssl_upstream, char* tls_auth_name, + size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, struct module_qstate* q) { struct libworker* w = (struct libworker*)q->env->worker; @@ -891,7 +891,7 @@ struct outbound_entry* libworker_send_query(struct query_info* qinfo, return NULL; e->qstate = q; e->qsent = outnet_serviced_query(w->back, qinfo, flags, dnssec, - want_dnssec, nocaps, q->env->cfg->tcp_upstream, ssl_upstream, + want_dnssec, nocaps, tcp_upstream, ssl_upstream, tls_auth_name, addr, addrlen, zone, zonelen, q, libworker_handle_service_reply, e, w->back->udp_buff, q->env); if(!e->qsent) { @@ -975,7 +975,7 @@ struct outbound_entry* worker_send_query(struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags), int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), - uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), + uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q)) { diff --git a/libunbound/worker.h b/libunbound/worker.h index bf7473861..c1fc8e784 100644 --- a/libunbound/worker.h +++ b/libunbound/worker.h @@ -72,7 +72,7 @@ struct query_info; struct outbound_entry* libworker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, int ssl_upstream, char* tls_auth_name, + size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, struct module_qstate* q); /** process incoming serviced query replies from the network */ @@ -123,7 +123,7 @@ void worker_sighandler(int sig, void* arg); struct outbound_entry* worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, int ssl_upstream, char* tls_auth_name, + size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, struct module_qstate* q); /** diff --git a/smallapp/worker_cb.c b/smallapp/worker_cb.c index 473e32a60..78e773938 100644 --- a/smallapp/worker_cb.c +++ b/smallapp/worker_cb.c @@ -99,7 +99,7 @@ struct outbound_entry* worker_send_query( int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), - size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(ssl_upstream), + size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q)) { log_assert(0); @@ -131,7 +131,7 @@ struct outbound_entry* libworker_send_query( int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), - size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(ssl_upstream), + size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q)) { log_assert(0); diff --git a/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.conf b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.conf new file mode 100644 index 000000000..6daf2eeec --- /dev/null +++ b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.conf @@ -0,0 +1,20 @@ +server: + verbosity: 5 + # num-threads: 1 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no +forward-zone: + name: "tcp.example.com" + forward-addr: "127.0.0.1@@TOPORT@" + forward-tcp-upstream: "yes" +forward-zone: + name: "udp.example.com" + forward-addr: "127.0.0.1@@TOPORT@" + forward-tcp-upstream: "no" + diff --git a/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.dsc b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.dsc new file mode 100644 index 000000000..5b1f0d3d1 --- /dev/null +++ b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.dsc @@ -0,0 +1,16 @@ +BaseName: fwd_udp_with_tcp_upstream +Version: 1.0 +Description: Forward an UDP packet to upstream via TCP and return reply. +CreationDate: Thu Aug 5 07:44:41 CEST 2021 +Maintainer: ziollek +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: fwd_udp_with_tcp_upstream.pre +Post: fwd_udp_with_tcp_upstream.post +Test: fwd_udp_with_tcp_upstream.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.post b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.post new file mode 100644 index 000000000..0013eca71 --- /dev/null +++ b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.post @@ -0,0 +1,10 @@ +# #-- fwd_udp_with_tcp_upstream.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +. ../common.sh +kill_pid $FWD_PID +kill_pid $UNBOUND_PID diff --git a/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.pre b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.pre new file mode 100644 index 000000000..546787a5f --- /dev/null +++ b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.pre @@ -0,0 +1,31 @@ +# #-- fwd_udp_with_tcp_upstream.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +. ../common.sh +get_random_port 2 +UNBOUND_PORT=$RND_PORT +FWD_PORT=$(($RND_PORT + 1)) +echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test +echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test + +# start forwarder +get_ldns_testns +$LDNS_TESTNS -p $FWD_PORT fwd_udp_with_tcp_upstream.testns >fwd.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test + +# make config file +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' < fwd_udp_with_tcp_upstream.conf > ub.conf +# start unbound in the background +PRE="../.." +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test + +cat .tpkg.var.test +wait_ldns_testns_up fwd.log +wait_unbound_up unbound.log + diff --git a/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.test b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.test new file mode 100644 index 000000000..fad6497be --- /dev/null +++ b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.test @@ -0,0 +1,35 @@ +# #-- fwd_udp_with_tcp_upstream.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +PRE="../.." +# do the test +echo "> dig tcp.example.com." +dig @localhost -p $UNBOUND_PORT tcp.example.com. | tee outfile +echo "> cat logfiles" +cat fwd.log +cat unbound.log +echo "> check answer" +if grep "10.20.30.40" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +echo "> dig udp.example.com." +dig @localhost -p $UNBOUND_PORT udp.example.com. | tee outfile +echo "> cat logfiles" +cat fwd.log +cat unbound.log +echo "> check answer" +if grep "10.20.30.80" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +exit 0 diff --git a/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.testns b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.testns new file mode 100644 index 000000000..04089af0e --- /dev/null +++ b/testdata/fwd_udp_with_tcp_upstream.tdir/fwd_udp_with_tcp_upstream.testns @@ -0,0 +1,25 @@ +; nameserver test file +$ORIGIN example.com. +$TTL 3600 + +ENTRY_BEGIN +MATCH opcode qtype qname +MATCH TCP +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +tcp IN A +SECTION ANSWER +tcp IN A 10.20.30.40 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +MATCH UDP +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +udp IN A +SECTION ANSWER +udp IN A 10.20.30.80 +ENTRY_END diff --git a/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.conf b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.conf new file mode 100644 index 000000000..d57c787b1 --- /dev/null +++ b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.conf @@ -0,0 +1,19 @@ +server: + verbosity: 2 + # num-threads: 1 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no +stub-zone: + name: "tcp.example.com" + stub-addr: "127.0.0.1@@TOPORT@" + stub-tcp-upstream: "yes" +stub-zone: + name: "udp.example.com" + stub-addr: "127.0.0.1@@TOPORT@" + stub-tcp-upstream: "no" \ No newline at end of file diff --git a/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.dsc b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.dsc new file mode 100644 index 000000000..526ff67f9 --- /dev/null +++ b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.dsc @@ -0,0 +1,16 @@ +BaseName: stub_udp_with_tcp_upstream +Version: 1.0 +Description: Stub server contacted via UDP with tcp upstream. +CreationDate: Thu Aug 5 07:44:41 CEST 2021 +Maintainer: ziollek +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: stub_udp_with_tcp_upstream.pre +Post: stub_udp_with_tcp_upstream.post +Test: stub_udp_with_tcp_upstream.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.post b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.post new file mode 100644 index 000000000..c804b6c46 --- /dev/null +++ b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.post @@ -0,0 +1,10 @@ +# #-- stub_udp_with_tcp_upstream.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +. ../common.sh +kill_pid $FWD_PID +kill_pid $UNBOUND_PID diff --git a/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.pre b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.pre new file mode 100644 index 000000000..2bca63b9d --- /dev/null +++ b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.pre @@ -0,0 +1,35 @@ +# #-- stub_udp_with_tcp_upstream.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +get_random_port 2 +UNBOUND_PORT=$RND_PORT +FWD_PORT=$(($RND_PORT + 1)) +echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test +echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test + +# start forwarder +get_ldns_testns +$LDNS_TESTNS -p $FWD_PORT stub_udp_with_tcp_upstream.testns >fwd.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test + +# make config file +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' < stub_udp_with_tcp_upstream.conf > ub.conf +# start unbound in the background +PRE="../.." +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test + +cat .tpkg.var.test + +# wait for forwarder to come up +wait_ldns_testns_up fwd.log + +# wait for unbound to come up +wait_unbound_up unbound.log + diff --git a/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.test b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.test new file mode 100644 index 000000000..43591ac16 --- /dev/null +++ b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.test @@ -0,0 +1,37 @@ +# #-- stub_udp_with_tcp_upstream.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +PRE="../.." +# do the test +echo "> dig tcp.example.com." +dig @127.0.0.1 -p $UNBOUND_PORT tcp.example.com. | tee outfile +echo "> cat logfiles" +cat fwd.log +cat unbound.log +echo "> check answer" +if grep "10.20.30.40" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + + +# check if second stub is requested via udp +echo "> dig udp.example.com." +dig @127.0.0.1 -p $UNBOUND_PORT udp.example.com. | tee outfile +echo "> cat logfiles" +cat fwd.log +cat unbound.log +echo "> check answer" +if grep "10.20.30.80" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +exit 0 diff --git a/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.testns b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.testns new file mode 100644 index 000000000..f2155414e --- /dev/null +++ b/testdata/stub_udp_with_tcp_upstream.tdir/stub_udp_with_tcp_upstream.testns @@ -0,0 +1,48 @@ +; nameserver test file +$ORIGIN example.com. +$TTL 3600 + +ENTRY_BEGIN +MATCH opcode qtype qname +MATCH TCP +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +tcp IN A +SECTION ANSWER +tcp IN A 10.20.30.40 +SECTION AUTHORITY +@ IN NS ns.example.com. +SECTION ADDITIONAL +ns IN A 127.0.0.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +MATCH UDP +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +udp IN A +SECTION ANSWER +udp IN A 10.20.30.80 +SECTION AUTHORITY +@ IN NS ns.example.com. +SECTION ADDITIONAL +ns IN A 127.0.0.1 +ENTRY_END + +; root prime +ENTRY_BEGIN +MATCH opcode qtype qname +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS root.server. +SECTION AUTHORITY +SECTION ADDITIONAL +root.server. IN A 127.0.0.1 +ENTRY_END + diff --git a/util/config_file.h b/util/config_file.h index aed6812da..b868d9cc8 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -697,6 +697,8 @@ struct config_stub { int isprime; /** if forward-first is set (failover to without if fails) */ int isfirst; + /** use tcp for queries to this stub */ + int tcp_upstream; /** use SSL for queries to this stub */ int ssl_upstream; /*** no cache */ diff --git a/util/configlexer.lex b/util/configlexer.lex index dbfc17d49..b1dd3c1ed 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -331,6 +331,7 @@ stub-first{COLON} { YDVAR(1, VAR_STUB_FIRST) } stub-no-cache{COLON} { YDVAR(1, VAR_STUB_NO_CACHE) } stub-ssl-upstream{COLON} { YDVAR(1, VAR_STUB_SSL_UPSTREAM) } stub-tls-upstream{COLON} { YDVAR(1, VAR_STUB_SSL_UPSTREAM) } +stub-tcp-upstream{COLON} { YDVAR(1, VAR_STUB_TCP_UPSTREAM) } forward-zone{COLON} { YDVAR(0, VAR_FORWARD_ZONE) } forward-addr{COLON} { YDVAR(1, VAR_FORWARD_ADDR) } forward-host{COLON} { YDVAR(1, VAR_FORWARD_HOST) } @@ -338,6 +339,7 @@ forward-first{COLON} { YDVAR(1, VAR_FORWARD_FIRST) } forward-no-cache{COLON} { YDVAR(1, VAR_FORWARD_NO_CACHE) } forward-ssl-upstream{COLON} { YDVAR(1, VAR_FORWARD_SSL_UPSTREAM) } forward-tls-upstream{COLON} { YDVAR(1, VAR_FORWARD_SSL_UPSTREAM) } +forward-tcp-upstream{COLON} { YDVAR(1, VAR_FORWARD_TCP_UPSTREAM) } auth-zone{COLON} { YDVAR(0, VAR_AUTH_ZONE) } rpz{COLON} { YDVAR(0, VAR_RPZ) } tags{COLON} { YDVAR(1, VAR_TAGS) } diff --git a/util/configparser.y b/util/configparser.y index e22d48d41..be8fe2dac 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -113,6 +113,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_SSL_UPSTREAM VAR_TCP_AUTH_QUERY_TIMEOUT VAR_SSL_SERVICE_KEY %token VAR_SSL_SERVICE_PEM VAR_SSL_PORT VAR_FORWARD_FIRST %token VAR_STUB_SSL_UPSTREAM VAR_FORWARD_SSL_UPSTREAM VAR_TLS_CERT_BUNDLE +%token VAR_STUB_TCP_UPSTREAM VAR_FORWARD_TCP_UPSTREAM %token VAR_HTTPS_PORT VAR_HTTP_ENDPOINT VAR_HTTP_MAX_STREAMS %token VAR_HTTP_QUERY_BUFFER_SIZE VAR_HTTP_RESPONSE_BUFFER_SIZE %token VAR_HTTP_NODELAY VAR_HTTP_NOTLS_DOWNSTREAM @@ -324,7 +325,7 @@ stubstart: VAR_STUB_ZONE contents_stub: contents_stub content_stub | ; content_stub: stub_name | stub_host | stub_addr | stub_prime | stub_first | - stub_no_cache | stub_ssl_upstream + stub_no_cache | stub_ssl_upstream | stub_tcp_upstream ; forwardstart: VAR_FORWARD_ZONE { @@ -341,7 +342,7 @@ forwardstart: VAR_FORWARD_ZONE contents_forward: contents_forward content_forward | ; content_forward: forward_name | forward_host | forward_addr | forward_first | - forward_no_cache | forward_ssl_upstream + forward_no_cache | forward_ssl_upstream | forward_tcp_upstream ; viewstart: VAR_VIEW { @@ -2721,6 +2722,16 @@ stub_ssl_upstream: VAR_STUB_SSL_UPSTREAM STRING_ARG free($2); } ; +stub_tcp_upstream: VAR_STUB_TCP_UPSTREAM STRING_ARG + { + OUTYY(("P(stub-tcp-upstream:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->stubs->tcp_upstream = + (strcmp($2, "yes")==0); + free($2); + } + ; stub_prime: VAR_STUB_PRIME STRING_ARG { OUTYY(("P(stub-prime:%s)\n", $2)); @@ -2783,6 +2794,16 @@ forward_ssl_upstream: VAR_FORWARD_SSL_UPSTREAM STRING_ARG free($2); } ; +forward_tcp_upstream: VAR_FORWARD_TCP_UPSTREAM STRING_ARG + { + OUTYY(("P(forward-tcp-upstream:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->forwards->tcp_upstream = + (strcmp($2, "yes")==0); + free($2); + } + ; auth_name: VAR_NAME STRING_ARG { OUTYY(("P(name:%s)\n", $2)); diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index de6dbd02a..f8dac65c5 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -335,7 +335,7 @@ int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)( struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* zone, size_t zonelen, int ssl_upstream, char* tls_auth_name, + uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, struct module_qstate* q)) { if(fptr == &worker_send_query) return 1; diff --git a/util/fptr_wlist.h b/util/fptr_wlist.h index cd331febb..a54709925 100644 --- a/util/fptr_wlist.h +++ b/util/fptr_wlist.h @@ -212,7 +212,7 @@ int fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_type fptr); int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)( struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* zone, size_t zonelen, int ssl_upstream, char* tls_auth_name, + uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, struct module_qstate* q)); /** diff --git a/util/module.h b/util/module.h index 81a31a9cc..a46373687 100644 --- a/util/module.h +++ b/util/module.h @@ -354,6 +354,7 @@ struct module_env { * @param addrlen: length of addr. * @param zone: delegation point name. * @param zonelen: length of zone name. + * @param tcp_upstream: use TCP for upstream queries. * @param ssl_upstream: use SSL for upstream queries. * @param tls_auth_name: if ssl_upstream, use this name with TLS * authentication. @@ -366,7 +367,7 @@ struct module_env { struct outbound_entry* (*send_query)(struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* zone, size_t zonelen, int ssl_upstream, + uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, struct module_qstate* q); /** -- 2.47.2