diff --git a/CHANGES b/CHANGES index 04305d1a74..a02ae9b357 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +3672. [func] Local address can now be specified when using + dns_client API. [RT #34811] + 3671. [bug] Don't allow dnssec-importkey overwrite a existing non-imported private key. diff --git a/bin/tests/system/resolver/ns1/named.conf b/bin/tests/system/resolver/ns1/named.conf index 411f4def35..95d233e8c6 100644 --- a/bin/tests/system/resolver/ns1/named.conf +++ b/bin/tests/system/resolver/ns1/named.conf @@ -34,6 +34,7 @@ options { deny-answer-aliases { "example.org"; } except-from { "goodcname.example.net"; "gooddname.example.net"; }; + allow-query {!10.53.0.8; any; }; }; zone "." { diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh old mode 100644 new mode 100755 index 5e72fc0758..189173f893 --- a/bin/tests/system/resolver/tests.sh +++ b/bin/tests/system/resolver/tests.sh @@ -39,6 +39,22 @@ echo "I:checking non-cachable NXDOMAIN response handling using dns_client" status=`expr $status + $ret` fi +if [ -x ${RESOLVE} ] ; then +echo "I:checking that local bound address can be set (Can't query from a denied address)" + ret=0 + ${RESOLVE} -b 10.53.0.8 -p 5300 -t a -s 10.53.0.1 www.example.org 2> resolve.out || ret=1 + grep "resolution failed: failure" resolve.out > /dev/null || ret=1 + if [ $ret != 0 ]; then echo "I:failed"; fi + status=`expr $status + $ret` + +echo "I:checking that local bound address can be set (Can query from an allowed address)" + ret=0 + ${RESOLVE} -b 10.53.0.1 -p 5300 -t a -s 10.53.0.1 www.example.org > resolve.out || ret=1 + grep "www.example.org..*.192.0.2.1" resolve.out > /dev/null || ret=1 + if [ $ret != 0 ]; then echo "I:failed"; fi + status=`expr $status + $ret` +fi + echo "I:checking non-cachable NODATA response handling" ret=0 $DIG +tcp nodata.example.net @10.53.0.1 a -p 5300 > dig.out || ret=1 diff --git a/lib/dns/client.c b/lib/dns/client.c index e9e8bde28c..cee4fb268d 100644 --- a/lib/dns/client.c +++ b/lib/dns/client.c @@ -249,13 +249,14 @@ static isc_result_t send_update(updatectx_t *uctx); static isc_result_t getudpdispatch(int family, dns_dispatchmgr_t *dispatchmgr, isc_socketmgr_t *socketmgr, isc_taskmgr_t *taskmgr, - isc_boolean_t is_shared, dns_dispatch_t **dispp) + isc_boolean_t is_shared, dns_dispatch_t **dispp, + isc_sockaddr_t *localaddr) { unsigned int attrs, attrmask; - isc_sockaddr_t sa; dns_dispatch_t *disp; unsigned buffersize, maxbuffers, maxrequests, buckets, increment; isc_result_t result; + isc_sockaddr_t anyaddr; attrs = 0; attrs |= DNS_DISPATCHATTR_UDP; @@ -275,7 +276,10 @@ getudpdispatch(int family, dns_dispatchmgr_t *dispatchmgr, attrmask |= DNS_DISPATCHATTR_IPV4; attrmask |= DNS_DISPATCHATTR_IPV6; - isc_sockaddr_anyofpf(&sa, family); + if (localaddr == NULL) { + localaddr = &anyaddr; + isc_sockaddr_anyofpf(localaddr, family); + } buffersize = 4096; maxbuffers = is_shared ? 1000 : 8; @@ -285,7 +289,7 @@ getudpdispatch(int family, dns_dispatchmgr_t *dispatchmgr, disp = NULL; result = dns_dispatch_getudp(dispatchmgr, socketmgr, - taskmgr, &sa, + taskmgr, localaddr, buffersize, maxbuffers, maxrequests, buckets, increment, attrs, attrmask, &disp); @@ -421,6 +425,19 @@ isc_result_t dns_client_createx(isc_mem_t *mctx, isc_appctx_t *actx, isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, unsigned int options, dns_client_t **clientp) +{ + isc_result_t result; + result = dns_client_createx2(mctx, actx, taskmgr, socketmgr, timermgr, + options, clientp, NULL, NULL); + return (result); +} + +isc_result_t +dns_client_createx2(isc_mem_t *mctx, isc_appctx_t *actx, + isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr, + isc_timermgr_t *timermgr, unsigned int options, + dns_client_t **clientp, isc_sockaddr_t *localaddr4, + isc_sockaddr_t *localaddr6) { dns_client_t *client; isc_result_t result; @@ -460,17 +477,27 @@ dns_client_createx(isc_mem_t *mctx, isc_appctx_t *actx, isc_taskmgr_t *taskmgr, goto cleanup; client->dispatchmgr = dispatchmgr; - /* TODO: whether to use dispatch v4 or v6 should be configurable */ + /* + * If only one address family is specified, use it. + * If neither family is specified, or if both are, use both. + */ client->dispatchv4 = NULL; + if (localaddr4 != NULL || localaddr6 == NULL) { + result = getudpdispatch(AF_INET, dispatchmgr, socketmgr, + taskmgr, ISC_TRUE, + &dispatchv4, localaddr4); + if (result == ISC_R_SUCCESS) + client->dispatchv4 = dispatchv4; + } + client->dispatchv6 = NULL; - result = getudpdispatch(AF_INET, dispatchmgr, socketmgr, - taskmgr, ISC_TRUE, &dispatchv4); - if (result == ISC_R_SUCCESS) - client->dispatchv4 = dispatchv4; - result = getudpdispatch(AF_INET6, dispatchmgr, socketmgr, - taskmgr, ISC_TRUE, &dispatchv6); - if (result == ISC_R_SUCCESS) - client->dispatchv6 = dispatchv6; + if (localaddr6 != NULL || localaddr4 == NULL) { + result = getudpdispatch(AF_INET6, dispatchmgr, socketmgr, + taskmgr, ISC_TRUE, + &dispatchv6, localaddr6); + if (result == ISC_R_SUCCESS) + client->dispatchv6 = dispatchv6; + } /* We need at least one of the dispatchers */ if (dispatchv4 == NULL && dispatchv6 == NULL) { diff --git a/lib/dns/include/dns/client.h b/lib/dns/include/dns/client.h index d21dff788d..d8fdccc188 100644 --- a/lib/dns/include/dns/client.h +++ b/lib/dns/include/dns/client.h @@ -149,6 +149,13 @@ isc_result_t dns_client_createx(isc_mem_t *mctx, isc_appctx_t *actx, isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, unsigned int options, dns_client_t **clientp); + +isc_result_t +dns_client_createx2(isc_mem_t *mctx, isc_appctx_t *actx, + isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr, + isc_timermgr_t *timermgr, unsigned int options, + dns_client_t **clientp, + isc_sockaddr_t *localaddr4, isc_sockaddr_t *localaddr6); /*%< * Create a DNS client. These functions create a new client object with * minimal internal resources such as the default 'view' for the IN class and @@ -161,6 +168,12 @@ dns_client_createx(isc_mem_t *mctx, isc_appctx_t *actx, isc_taskmgr_t *taskmgr, * dns_client_create() is expected to be used by an application that only needs * simple synchronous services or by a thread-based application. * + * dns_client_createx2 takes two additional parameters, 'localaddr4' and + * 'localaddr6', to specify the local address to use for each family. If + * both are set to NULL, then wildcard addresses will be used for both + * families. If only one is NULL, then the other address will be used + * as the local address, and the other protocol family will not be used. + * * If the DNS_CLIENTCREATEOPT_USECACHE flag is set in 'options', * dns_client_create(x) will create a cache database with the view. * diff --git a/lib/samples/resolve.c b/lib/samples/resolve.c index 8aefd6d008..ca68f30a6c 100644 --- a/lib/samples/resolve.c +++ b/lib/samples/resolve.c @@ -35,6 +35,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -87,7 +91,7 @@ usage(void) { fprintf(stderr, "resolve [-t RRtype] " "[[-a algorithm] [-e] -k keyname -K keystring] " "[-S domain:serveraddr_for_domain ] [-s server_address]" - "hostname\n"); + "[-b address[#port]] hostname\n"); exit(1); } @@ -251,8 +255,16 @@ main(int argc, char *argv[]) { isc_boolean_t is_sep = ISC_FALSE; const char *port = "53"; isc_mem_t *mctx = NULL; + isc_appctx_t *actx = NULL; + isc_taskmgr_t *taskmgr = NULL; + isc_socketmgr_t *socketmgr = NULL; + isc_timermgr_t *timermgr = NULL; + struct in_addr in4; + struct in6_addr in6; + isc_sockaddr_t a4, a6; + isc_sockaddr_t *addr4 = NULL, *addr6 = NULL; - while ((ch = getopt(argc, argv, "a:es:t:k:K:p:S:")) != -1) { + while ((ch = getopt(argc, argv, "a:b:es:t:k:K:p:S:")) != -1) { switch (ch) { case 't': tr.base = optarg; @@ -267,6 +279,29 @@ main(int argc, char *argv[]) { case 'a': algname = optarg; break; + case 'b': + if (inet_pton(AF_INET, optarg, &in4) == 1) { + if (addr4 != NULL) { + fprintf(stderr, "only one local " + "address per family " + "can be specified\n"); + exit(1); + } + isc_sockaddr_fromin(&a4, &in4, 0); + addr4 = &a4; + } else if (inet_pton(AF_INET6, optarg, &in6) == 1) { + if (addr6 != NULL) { + fprintf(stderr, "only one local " + "address per family " + "can be specified\n"); + exit(1); + } + isc_sockaddr_fromin6(&a6, &in6, 0); + addr6 = &a6; + } else { + fprintf(stderr, "invalid address %s\n", optarg); + exit(1); + } case 'e': is_sep = ISC_TRUE; break; @@ -334,10 +369,28 @@ main(int argc, char *argv[]) { exit(1); } + result = isc_appctx_create(mctx, &actx); + if (result != ISC_R_SUCCESS) + goto cleanup; + result = isc_app_ctxstart(actx); + if (result != ISC_R_SUCCESS) + goto cleanup; + result = isc_taskmgr_createinctx(mctx, actx, 1, 0, &taskmgr); + if (result != ISC_R_SUCCESS) + goto cleanup; + result = isc_socketmgr_createinctx(mctx, actx, &socketmgr); + if (result != ISC_R_SUCCESS) + goto cleanup; + result = isc_timermgr_createinctx(mctx, actx, &timermgr); + if (result != ISC_R_SUCCESS) + goto cleanup; + clientopt = 0; - result = dns_client_create(&client, clientopt); + result = dns_client_createx2(mctx, actx, taskmgr, socketmgr, timermgr, + clientopt, &client, addr4, addr6); if (result != ISC_R_SUCCESS) { - fprintf(stderr, "dns_client_create failed: %d\n", result); + fprintf(stderr, "dns_client_create failed: %d, %s\n", result, + isc_result_totext(result)); exit(1); } @@ -356,10 +409,12 @@ main(int argc, char *argv[]) { result = dns_client_setservers(client, dns_rdataclass_in, NULL, nameservers); if (result != ISC_R_SUCCESS) { + irs_resconf_destroy(&resconf); fprintf(stderr, "dns_client_setservers failed: %d\n", result); exit(1); } + irs_resconf_destroy(&resconf); } else { addserver(client, server, port, NULL); } @@ -390,7 +445,7 @@ main(int argc, char *argv[]) { fprintf(stderr, "failed to convert qname: %d\n", result); /* Perform resolution */ - resopt = 0; + resopt = DNS_CLIENTRESOPT_ALLOWRUN; if (keynamestr == NULL) resopt |= DNS_CLIENTRESOPT_NODNSSEC; ISC_LIST_INIT(namelist); @@ -413,7 +468,19 @@ main(int argc, char *argv[]) { dns_client_freeresanswer(client, &namelist); /* Cleanup */ +cleanup: dns_client_destroy(&client); + + if (taskmgr != NULL) + isc_taskmgr_destroy(&taskmgr); + if (timermgr != NULL) + isc_timermgr_destroy(&timermgr); + if (socketmgr != NULL) + isc_socketmgr_destroy(&socketmgr); + if (actx != NULL) + isc_appctx_destroy(&actx); + isc_mem_detach(&mctx); + if (keynamestr != NULL) isc_mem_destroy(&keymctx); dns_lib_shutdown();