diff --git a/CHANGES b/CHANGES index 4cdea72d61..726379d43f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +4009. [func] delv: added a +tcp option. [RT #37855] + 4008. [contrib] Updated zkt to latest version (1.1.3). [RT #37886] 4007. [doc] Remove acl forward reference restriction. [RT #37772] diff --git a/bin/delv/delv.c b/bin/delv/delv.c index 955169167d..7dd8870b4b 100644 --- a/bin/delv/delv.c +++ b/bin/delv/delv.c @@ -131,6 +131,8 @@ static isc_boolean_t root_validation = ISC_TRUE, dlv_validation = ISC_TRUE; +static isc_boolean_t use_tcp = ISC_FALSE; + static char *anchorfile = NULL; static char *trust_anchor = NULL; static char *dlv_anchor = NULL; @@ -183,6 +185,7 @@ usage(void) { "comments)\n" " +[no]short (Short form answer)\n" " +[no]split=## (Split hex/base64 fields into chunks)\n" +" +[no]tcp (TCP mode)\n" " +[no]ttl (Control display of ttls in records)\n" " +[no]trust (Control display of trust level)\n" " +[no]rtrace (Trace resolver fetches)\n" @@ -1134,6 +1137,10 @@ plus_option(char *option) { break; case 't': switch (cmd[1]) { + case 'c': /* tcp */ + FULLCHECK("tcp"); + use_tcp = state; + break; case 'r': /* trust */ FULLCHECK("trust"); showtrust = state; @@ -1627,6 +1634,8 @@ main(int argc, char *argv[]) { resopt |= DNS_CLIENTRESOPT_NOVALIDATE; if (cdflag) resopt &= ~DNS_CLIENTRESOPT_NOCDFLAG; + if (use_tcp) + resopt |= DNS_CLIENTRESOPT_TCP; /* Perform resolution */ ISC_LIST_INIT(namelist); diff --git a/bin/delv/delv.docbook b/bin/delv/delv.docbook index 7d05b59c46..76c329f4b0 100644 --- a/bin/delv/delv.docbook +++ b/bin/delv/delv.docbook @@ -647,6 +647,17 @@ + + + + + + Controls whether to use TCP when sending queries. + The default is to use UDP unless a truncated + response has been received. + + + diff --git a/bin/tests/system/delv/tests.sh b/bin/tests/system/delv/tests.sh index 2b0d1f98bf..5492efd829 100644 --- a/bin/tests/system/delv/tests.sh +++ b/bin/tests/system/delv/tests.sh @@ -37,8 +37,8 @@ if [ -x ${DELV} ] ; then if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` - echo "I:checking delv with IPv6 on IPv4 does not work ($n)" n=`expr $n + 1` + echo "I:checking delv with IPv6 on IPv4 does not work ($n)" if $TESTSOCK6 fd92:7065:b8e:ffff::3 then ret=0 @@ -66,6 +66,14 @@ if [ -x ${DELV} ] ; then if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` + n=`expr $n + 1` + echo "I:checking delv over TCP works ($n)" + ret=0 + $DELV $DELVOPTS +tcp @10.53.0.3 a a.example > delv.out.test$n || ret=1 + grep "10\.0\.0\.1$" < delv.out.test$n > /dev/null || ret=1 + if [ $ret != 0 ]; then echo "I:failed"; fi + status=`expr $status + $ret` + exit $status else echo "$DELV is needed, so skipping these delv tests" diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 42a5a65dd7..8df5216f20 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -233,6 +233,12 @@ buffered yes; when creating a channel. + + + delv +tcp will exclusively use TCP when + sending queries. + + diff --git a/lib/dns/client.c b/lib/dns/client.c index 3db8b93210..82fe2b7945 100644 --- a/lib/dns/client.c +++ b/lib/dns/client.c @@ -132,6 +132,7 @@ typedef struct resctx { isc_boolean_t want_dnssec; isc_boolean_t want_validation; isc_boolean_t want_cdflag; + isc_boolean_t want_tcp; /* Locked */ ISC_LINK(struct resctx) link; @@ -222,6 +223,7 @@ typedef struct updatectx { unsigned int magic; isc_mutex_t lock; dns_client_t *client; + isc_boolean_t want_tcp; /* Locked */ dns_request_t *updatereq; @@ -784,6 +786,8 @@ start_fetch(resctx_t *rctx) { fopts |= DNS_FETCHOPT_NOCDFLAG; if (!rctx->want_validation) fopts |= DNS_FETCHOPT_NOVALIDATE; + if (rctx->want_tcp) + fopts |= DNS_FETCHOPT_TCP; result = dns_resolver_createfetch(rctx->view->resolver, dns_fixedname_name(&rctx->name), @@ -1331,7 +1335,7 @@ dns_client_startresolve(dns_client_t *client, dns_name_t *name, isc_mem_t *mctx; isc_result_t result; dns_rdataset_t *rdataset, *sigrdataset; - isc_boolean_t want_dnssec, want_validation, want_cdflag; + isc_boolean_t want_dnssec, want_validation, want_cdflag, want_tcp; REQUIRE(DNS_CLIENT_VALID(client)); REQUIRE(transp != NULL && *transp == NULL); @@ -1349,6 +1353,7 @@ dns_client_startresolve(dns_client_t *client, dns_name_t *name, want_dnssec = ISC_TF((options & DNS_CLIENTRESOPT_NODNSSEC) == 0); want_validation = ISC_TF((options & DNS_CLIENTRESOPT_NOVALIDATE) == 0); want_cdflag = ISC_TF((options & DNS_CLIENTRESOPT_NOCDFLAG) == 0); + want_tcp = ISC_TF((options & DNS_CLIENTRESOPT_TCP) != 0); /* * Prepare some intermediate resources @@ -1406,6 +1411,7 @@ dns_client_startresolve(dns_client_t *client, dns_name_t *name, rctx->want_dnssec = want_dnssec; rctx->want_validation = want_validation; rctx->want_cdflag = want_cdflag; + rctx->want_tcp = want_tcp; ISC_LIST_INIT(rctx->namelist); rctx->event = event; @@ -1727,8 +1733,7 @@ dns_client_startrequest(dns_client_t *client, dns_message_t *qmessage, dns_clientreqevent_t *event = NULL; reqctx_t *ctx = NULL; dns_tsectype_t tsectype = dns_tsectype_none; - - UNUSED(options); + unsigned int reqoptions; REQUIRE(DNS_CLIENT_VALID(client)); REQUIRE(qmessage != NULL); @@ -1748,6 +1753,10 @@ dns_client_startrequest(dns_client_t *client, dns_message_t *qmessage, if (result != ISC_R_SUCCESS) return (result); + reqoptions = 0; + if ((options & DNS_CLIENTREQOPT_TCP) != 0) + reqoptions |= DNS_REQUESTOPT_TCP; + clone = NULL; isc_task_attach(task, &clone); event = (dns_clientreqevent_t *) @@ -1790,7 +1799,7 @@ dns_client_startrequest(dns_client_t *client, dns_message_t *qmessage, ctx->request = NULL; result = dns_request_createvia3(view->requestmgr, qmessage, NULL, - server, options, ctx->tsigkey, + server, reqoptions, ctx->tsigkey, timeout, udptimeout, udpretries, client->task, request_done, ctx, &ctx->request); @@ -1939,7 +1948,7 @@ update_done(isc_task_t *task, isc_event_t *event) { dns_message_t *answer = NULL; updatectx_t *uctx = event->ev_arg; dns_client_t *client; - unsigned int timeout; + unsigned int timeout, reqoptions; UNUSED(task); @@ -1980,10 +1989,14 @@ update_done(isc_task_t *task, isc_event_t *event) { timeout = client->update_timeout / uctx->nservers; if (timeout < MIN_UPDATE_TIMEOUT) timeout = MIN_UPDATE_TIMEOUT; + reqoptions = 0; + if (uctx->want_tcp) + reqoptions |= DNS_REQUESTOPT_TCP; result = dns_request_createvia3(uctx->view->requestmgr, uctx->updatemsg, NULL, - uctx->currentserver, 0, + uctx->currentserver, + reqoptions, uctx->tsigkey, timeout, client->update_udptimeout, @@ -2010,7 +2023,7 @@ send_update(updatectx_t *uctx) { dns_name_t *name = NULL; dns_rdataset_t *rdataset = NULL; dns_client_t *client = uctx->client; - unsigned int timeout; + unsigned int timeout, reqoptions; REQUIRE(uctx->zonename != NULL && uctx->currentserver != NULL); @@ -2037,10 +2050,13 @@ send_update(updatectx_t *uctx) { timeout = client->update_timeout / uctx->nservers; if (timeout < MIN_UPDATE_TIMEOUT) timeout = MIN_UPDATE_TIMEOUT; + reqoptions = 0; + if (uctx->want_tcp) + reqoptions |= DNS_REQUESTOPT_TCP; result = dns_request_createvia3(uctx->view->requestmgr, uctx->updatemsg, - NULL, uctx->currentserver, 0, - uctx->tsigkey, timeout, + NULL, uctx->currentserver, + reqoptions, uctx->tsigkey, timeout, client->update_udptimeout, client->update_udpretries, client->task, update_done, uctx, @@ -2177,6 +2193,7 @@ process_soa(updatectx_t *uctx, dns_rdataset_t *soaset, dns_name_t *soaname) { dns_rdata_t soarr = DNS_RDATA_INIT; dns_rdata_soa_t soa; dns_name_t primary; + unsigned int resoptions; result = dns_rdataset_first(soaset); if (result != ISC_R_SUCCESS) @@ -2206,10 +2223,14 @@ process_soa(updatectx_t *uctx, dns_rdataset_t *soaset, dns_name_t *soaname) { */ LOCK(&uctx->lock); uctx->bp4 = uctx; + resoptions = 0; + if (uctx->want_tcp) + resoptions |= DNS_CLIENTRESOPT_TCP; result = dns_client_startresolve(uctx->client, &primary, uctx->rdclass, dns_rdatatype_a, - 0, uctx->client->task, + resoptions, + uctx->client->task, resolveaddr_done, &uctx->bp4, &uctx->restrans); if (result == ISC_R_SUCCESS) { @@ -2218,7 +2239,8 @@ process_soa(updatectx_t *uctx, dns_rdataset_t *soaset, dns_name_t *soaname) { &primary, uctx->rdclass, dns_rdatatype_aaaa, - 0, uctx->client->task, + resoptions, + uctx->client->task, resolveaddr_done, &uctx->bp6, &uctx->restrans2); @@ -2249,7 +2271,7 @@ receive_soa(isc_task_t *task, isc_event_t *event) { isc_boolean_t seencname = ISC_FALSE; isc_boolean_t droplabel = ISC_FALSE; dns_name_t tname; - unsigned int nlabels; + unsigned int nlabels, reqoptions; UNUSED(task); @@ -2284,9 +2306,12 @@ receive_soa(isc_task_t *task, isc_event_t *event) { /* Retry SOA request without TSIG */ dns_message_destroy(&rcvmsg); dns_message_renderreset(uctx->soaquery); + reqoptions = 0; + if (uctx->want_tcp) + reqoptions |= DNS_REQUESTOPT_TCP; result = dns_request_createvia3(uctx->view->requestmgr, - uctx->soaquery, NULL, addr, 0, - NULL, + uctx->soaquery, NULL, addr, + reqoptions, NULL, client->find_timeout * 20, client->find_timeout, 3, uctx->client->task, @@ -2384,9 +2409,13 @@ receive_soa(isc_task_t *task, isc_event_t *event) { UNLOCK(&uctx->lock); dns_message_renderreset(soaquery); dns_message_settsigkey(soaquery, NULL); + reqoptions = 0; + if (uctx->want_tcp) + reqoptions |= DNS_REQUESTOPT_TCP; result = dns_request_createvia3(uctx->view->requestmgr, soaquery, NULL, - uctx->currentserver, 0, + uctx->currentserver, + reqoptions, uctx->tsigkey, client->find_timeout * 20, @@ -2417,6 +2446,7 @@ request_soa(updatectx_t *uctx) { dns_message_t *soaquery = uctx->soaquery; dns_name_t *name = NULL; dns_rdataset_t *rdataset = NULL; + unsigned int reqoptions; if (soaquery == NULL) { result = dns_message_create(uctx->client->mctx, @@ -2438,10 +2468,13 @@ request_soa(updatectx_t *uctx) { dns_message_addname(soaquery, name, DNS_SECTION_QUESTION); rdataset = NULL; name = NULL; + reqoptions = 0; + if (uctx->want_tcp) + reqoptions |= DNS_REQUESTOPT_TCP; result = dns_request_createvia3(uctx->view->requestmgr, - soaquery, NULL, uctx->currentserver, 0, - uctx->tsigkey, + soaquery, NULL, uctx->currentserver, + reqoptions, uctx->tsigkey, uctx->client->find_timeout * 20, uctx->client->find_timeout, 3, uctx->client->task, receive_soa, uctx, @@ -2470,7 +2503,7 @@ resolvesoa_done(isc_task_t *task, isc_event_t *event) { dns_name_t *name, tname; dns_rdataset_t *rdataset = NULL; isc_result_t result = rev->result; - unsigned int nlabels; + unsigned int nlabels, resoptions; UNUSED(task); @@ -2511,10 +2544,14 @@ resolvesoa_done(isc_task_t *task, isc_event_t *event) { dns_name_getlabelsequence(&uctx->soaqname, 1, nlabels - 1, &tname); dns_name_clone(&tname, &uctx->soaqname); + resoptions = 0; + if (uctx->want_tcp) + resoptions |= DNS_CLIENTRESOPT_TCP; result = dns_client_startresolve(uctx->client, &uctx->soaqname, uctx->rdclass, - dns_rdatatype_soa, 0, + dns_rdatatype_soa, + resoptions, uctx->client->task, resolvesoa_done, uctx, &uctx->restrans); @@ -2658,11 +2695,11 @@ dns_client_update(dns_client_t *client, dns_rdataclass_t rdclass, REQUIRE(DNS_CLIENT_VALID(client)); if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 && - (options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) { + (options & DNS_CLIENTUPDOPT_ALLOWRUN) == 0) { /* * If the client is run under application's control, we need * to create a new running (sub)environment for this - * particular resolution. + * particular update. */ return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */ } else @@ -2742,6 +2779,8 @@ dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass, dns_section_t section = DNS_SECTION_UPDATE; isc_sockaddr_t *server, *sa = NULL; dns_tsectype_t tsectype = dns_tsectype_none; + isc_boolean_t want_tcp; + unsigned int resoptions; UNUSED(options); @@ -2762,6 +2801,7 @@ dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass, UNLOCK(&client->lock); if (result != ISC_R_SUCCESS) return (result); + want_tcp = ISC_TF((options & DNS_CLIENTUPDOPT_TCP) != 0); /* Create a context and prepare some resources */ uctx = isc_mem_get(client->mctx, sizeof(*uctx)); @@ -2795,6 +2835,7 @@ dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass, uctx->tsigkey = NULL; uctx->sig0key = NULL; uctx->zonename = NULL; + uctx->want_tcp = want_tcp; dns_name_init(&uctx->soaqname, NULL); ISC_LIST_INIT(uctx->servers); uctx->nservers = 0; @@ -2884,10 +2925,13 @@ dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass, if (result != ISC_R_SUCCESS) goto fail; } else { + resoptions = 0; + if (want_tcp) + resoptions |= DNS_CLIENTRESOPT_TCP; dns_name_clone(uctx->firstname, &uctx->soaqname); result = dns_client_startresolve(uctx->client, &uctx->soaqname, uctx->rdclass, - dns_rdatatype_soa, 0, + dns_rdatatype_soa, resoptions, client->task, resolvesoa_done, uctx, &uctx->restrans); if (result != ISC_R_SUCCESS) diff --git a/lib/dns/include/dns/client.h b/lib/dns/include/dns/client.h index f60bad64b6..21a16ed616 100644 --- a/lib/dns/include/dns/client.h +++ b/lib/dns/include/dns/client.h @@ -85,12 +85,24 @@ ISC_LANG_BEGINDECLS #define DNS_CLIENTRESOPT_NOVALIDATE 0x04 /*%< Don't set the CD flag on upstream queries. */ #define DNS_CLIENTRESOPT_NOCDFLAG 0x08 +/*%< Use TCP transport. */ +#define DNS_CLIENTRESOPT_TCP 0x10 /*% * Optional flags for dns_client_(start)request. */ /*%< Allow running external context. */ #define DNS_CLIENTREQOPT_ALLOWRUN 0x01 +/*%< Use TCP transport. */ +#define DNS_CLIENTREQOPT_TCP 0x02 + +/*% + * Optional flags for dns_client_(start)update. + */ +/*%< Allow running external context. */ +#define DNS_CLIENTUPDOPT_ALLOWRUN 0x01 +/*%< Use TCP transport. */ +#define DNS_CLIENTUPDOPT_TCP 0x02 /*% * A dns_clientresevent_t is sent when name resolution performed by a client @@ -301,9 +313,12 @@ dns_client_startresolve(dns_client_t *client, dns_name_t *name, * * If any trusted keys are configured and the query name is considered to * belong to a secure zone, these functions also validate the responses - * using DNSSEC by default. If the DNS_CLIENTRESOPT_NODNSSEC flag is set + * using DNSSEC by default. If the DNS_CLIENTRESOPT_NOVALIDATE flag is set * in 'options', DNSSEC validation is disabled regardless of the configured - * trusted keys or the query name. + * trusted keys or the query name. With DNS_CLIENTRESOPT_NODNSSEC + * DNSSEC data is not returned with response. DNS_CLIENTRESOPT_NOCDFLAG + * disables the CD flag on queries, DNS_CLIENTRESOPT_TCP switches to + * the TCP (vs. UDP) transport. * * dns_client_resolve() provides a synchronous service. This function starts * name resolution internally and blocks until it completes. On success, @@ -465,6 +480,8 @@ dns_client_startrequest(dns_client_t *client, dns_message_t *qmessage, * the response message (on success). On return, '*transp' is set to an opaque * transaction ID so that the caller can cancel this request. * + * DNS_CLIENTREQOPT_TCP switches to the TCP (vs. UDP) transport. + * * Requires: * *\li 'client' is a valid client. @@ -567,6 +584,12 @@ dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass, * NULL, in which case the library tries the update without any transaction * authentication. * + * It is typically expected that the client object passed to + * dns_client_update() was created via dns_client_create() and has its own + * managers and contexts. However, if the DNS_CLIENTUPDOPT_ALLOWRUN flag is + * set in 'options', this function performs the synchronous service even if + * it does not have its own manager and context structures. + * * dns_client_update() provides a synchronous service. This function blocks * until the entire update procedure completes, including the additional * queries when necessary. @@ -581,8 +604,7 @@ dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass, * structure. On return, '*transp' is set to an opaque transaction ID so that * the caller can cancel this update process. * - * Notes: - *\li No options are currently defined. + * DNS_CLIENTUPDOPT_TCP switches to the TCP (vs. UDP) transport. * * Requires: *