mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 10:10:06 +00:00
3741. [func] "delve" (domain entity lookup and validation engine): A new tool with dig-like semantics for performing DNS lookups, with internal DNSSEC validation, using the same resolver and validator logic as named. This allows easy validation of DNSSEC data in environments with untrustworthy resolvers, and assists with troubleshooting of DNSSEC problems. (Note: not yet available on win32.) [RT #32406]
3142 lines
78 KiB
C
3142 lines
78 KiB
C
/*
|
|
* Copyright (C) 2009-2013 Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/* $Id: client.c,v 1.14 2011/03/12 04:59:47 tbox Exp $ */
|
|
|
|
#include <config.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <isc/app.h>
|
|
#include <isc/buffer.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/mutex.h>
|
|
#include <isc/sockaddr.h>
|
|
#include <isc/socket.h>
|
|
#include <isc/task.h>
|
|
#include <isc/timer.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/adb.h>
|
|
#include <dns/client.h>
|
|
#include <dns/db.h>
|
|
#include <dns/dispatch.h>
|
|
#include <dns/events.h>
|
|
#include <dns/forward.h>
|
|
#include <dns/keytable.h>
|
|
#include <dns/message.h>
|
|
#include <dns/name.h>
|
|
#include <dns/rdata.h>
|
|
#include <dns/rdatalist.h>
|
|
#include <dns/rdataset.h>
|
|
#include <dns/rdatatype.h>
|
|
#include <dns/rdatasetiter.h>
|
|
#include <dns/rdatastruct.h>
|
|
#include <dns/request.h>
|
|
#include <dns/resolver.h>
|
|
#include <dns/result.h>
|
|
#include <dns/tsec.h>
|
|
#include <dns/tsig.h>
|
|
#include <dns/view.h>
|
|
|
|
#include <dst/dst.h>
|
|
|
|
#define DNS_CLIENT_MAGIC ISC_MAGIC('D', 'N', 'S', 'c')
|
|
#define DNS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, DNS_CLIENT_MAGIC)
|
|
|
|
#define RCTX_MAGIC ISC_MAGIC('R', 'c', 't', 'x')
|
|
#define RCTX_VALID(c) ISC_MAGIC_VALID(c, RCTX_MAGIC)
|
|
|
|
#define REQCTX_MAGIC ISC_MAGIC('R', 'q', 'c', 'x')
|
|
#define REQCTX_VALID(c) ISC_MAGIC_VALID(c, REQCTX_MAGIC)
|
|
|
|
#define UCTX_MAGIC ISC_MAGIC('U', 'c', 't', 'x')
|
|
#define UCTX_VALID(c) ISC_MAGIC_VALID(c, UCTX_MAGIC)
|
|
|
|
#define MAX_RESTARTS 16
|
|
|
|
/*%
|
|
* DNS client object
|
|
*/
|
|
struct dns_client {
|
|
/* Unlocked */
|
|
unsigned int magic;
|
|
unsigned int attributes;
|
|
isc_mutex_t lock;
|
|
isc_mem_t *mctx;
|
|
isc_appctx_t *actx;
|
|
isc_taskmgr_t *taskmgr;
|
|
isc_task_t *task;
|
|
isc_socketmgr_t *socketmgr;
|
|
isc_timermgr_t *timermgr;
|
|
dns_dispatchmgr_t *dispatchmgr;
|
|
dns_dispatch_t *dispatchv4;
|
|
dns_dispatch_t *dispatchv6;
|
|
|
|
unsigned int update_timeout;
|
|
unsigned int update_udptimeout;
|
|
unsigned int update_udpretries;
|
|
unsigned int find_timeout;
|
|
unsigned int find_udpretries;
|
|
|
|
/* Locked */
|
|
unsigned int references;
|
|
dns_viewlist_t viewlist;
|
|
ISC_LIST(struct resctx) resctxs;
|
|
ISC_LIST(struct reqctx) reqctxs;
|
|
ISC_LIST(struct updatectx) updatectxs;
|
|
};
|
|
|
|
/*%
|
|
* Timeout/retry constants for dynamic update borrowed from nsupdate
|
|
*/
|
|
#define DEF_UPDATE_TIMEOUT 300
|
|
#define MIN_UPDATE_TIMEOUT 30
|
|
#define DEF_UPDATE_UDPTIMEOUT 3
|
|
#define DEF_UPDATE_UDPRETRIES 3
|
|
|
|
#define DEF_FIND_TIMEOUT 5
|
|
#define DEF_FIND_UDPRETRIES 3
|
|
|
|
#define DNS_CLIENTATTR_OWNCTX 0x01
|
|
|
|
#define DNS_CLIENTVIEW_NAME "dnsclient"
|
|
|
|
/*%
|
|
* Internal state for a single name resolution procedure
|
|
*/
|
|
typedef struct resctx {
|
|
/* Unlocked */
|
|
unsigned int magic;
|
|
isc_mutex_t lock;
|
|
dns_client_t *client;
|
|
isc_boolean_t want_dnssec;
|
|
isc_boolean_t want_validation;
|
|
isc_boolean_t want_cdflag;
|
|
|
|
/* Locked */
|
|
ISC_LINK(struct resctx) link;
|
|
isc_task_t *task;
|
|
dns_view_t *view;
|
|
unsigned int restarts;
|
|
dns_fixedname_t name;
|
|
dns_rdatatype_t type;
|
|
dns_fetch_t *fetch;
|
|
dns_namelist_t namelist;
|
|
isc_result_t result;
|
|
dns_clientresevent_t *event;
|
|
isc_boolean_t canceled;
|
|
dns_rdataset_t *rdataset;
|
|
dns_rdataset_t *sigrdataset;
|
|
} resctx_t;
|
|
|
|
/*%
|
|
* Argument of an internal event for synchronous name resolution.
|
|
*/
|
|
typedef struct resarg {
|
|
/* Unlocked */
|
|
isc_appctx_t *actx;
|
|
dns_client_t *client;
|
|
isc_mutex_t lock;
|
|
|
|
/* Locked */
|
|
isc_result_t result;
|
|
isc_result_t vresult;
|
|
dns_namelist_t *namelist;
|
|
dns_clientrestrans_t *trans;
|
|
isc_boolean_t canceled;
|
|
} resarg_t;
|
|
|
|
/*%
|
|
* Internal state for a single DNS request
|
|
*/
|
|
typedef struct reqctx {
|
|
/* Unlocked */
|
|
unsigned int magic;
|
|
isc_mutex_t lock;
|
|
dns_client_t *client;
|
|
unsigned int parseoptions;
|
|
|
|
/* Locked */
|
|
ISC_LINK(struct reqctx) link;
|
|
isc_boolean_t canceled;
|
|
dns_tsigkey_t *tsigkey;
|
|
dns_request_t *request;
|
|
dns_clientreqevent_t *event;
|
|
} reqctx_t;
|
|
|
|
/*%
|
|
* Argument of an internal event for synchronous DNS request.
|
|
*/
|
|
typedef struct reqarg {
|
|
/* Unlocked */
|
|
isc_appctx_t *actx;
|
|
dns_client_t *client;
|
|
isc_mutex_t lock;
|
|
|
|
/* Locked */
|
|
isc_result_t result;
|
|
dns_clientreqtrans_t *trans;
|
|
isc_boolean_t canceled;
|
|
} reqarg_t;
|
|
|
|
/*%
|
|
* Argument of an internal event for synchronous name resolution.
|
|
*/
|
|
typedef struct updatearg {
|
|
/* Unlocked */
|
|
isc_appctx_t *actx;
|
|
dns_client_t *client;
|
|
isc_mutex_t lock;
|
|
|
|
/* Locked */
|
|
isc_result_t result;
|
|
dns_clientupdatetrans_t *trans;
|
|
isc_boolean_t canceled;
|
|
} updatearg_t;
|
|
|
|
/*%
|
|
* Internal state for a single dynamic update procedure
|
|
*/
|
|
typedef struct updatectx {
|
|
/* Unlocked */
|
|
unsigned int magic;
|
|
isc_mutex_t lock;
|
|
dns_client_t *client;
|
|
|
|
/* Locked */
|
|
dns_request_t *updatereq;
|
|
dns_request_t *soareq;
|
|
dns_clientrestrans_t *restrans;
|
|
dns_clientrestrans_t *restrans2;
|
|
isc_boolean_t canceled;
|
|
|
|
/* Task Locked */
|
|
ISC_LINK(struct updatectx) link;
|
|
dns_clientupdatestate_t state;
|
|
dns_rdataclass_t rdclass;
|
|
dns_view_t *view;
|
|
dns_message_t *updatemsg;
|
|
dns_message_t *soaquery;
|
|
dns_clientupdateevent_t *event;
|
|
dns_tsigkey_t *tsigkey;
|
|
dst_key_t *sig0key;
|
|
dns_name_t *firstname;
|
|
dns_name_t soaqname;
|
|
dns_fixedname_t zonefname;
|
|
dns_name_t *zonename;
|
|
isc_sockaddrlist_t servers;
|
|
unsigned int nservers;
|
|
isc_sockaddr_t *currentserver;
|
|
struct updatectx *bp4;
|
|
struct updatectx *bp6;
|
|
} updatectx_t;
|
|
|
|
static isc_result_t request_soa(updatectx_t *uctx);
|
|
static void client_resfind(resctx_t *rctx, dns_fetchevent_t *event);
|
|
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_sockaddr_t *localaddr)
|
|
{
|
|
unsigned int attrs, attrmask;
|
|
dns_dispatch_t *disp;
|
|
unsigned buffersize, maxbuffers, maxrequests, buckets, increment;
|
|
isc_result_t result;
|
|
isc_sockaddr_t anyaddr;
|
|
|
|
attrs = 0;
|
|
attrs |= DNS_DISPATCHATTR_UDP;
|
|
switch (family) {
|
|
case AF_INET:
|
|
attrs |= DNS_DISPATCHATTR_IPV4;
|
|
break;
|
|
case AF_INET6:
|
|
attrs |= DNS_DISPATCHATTR_IPV6;
|
|
break;
|
|
default:
|
|
INSIST(0);
|
|
}
|
|
attrmask = 0;
|
|
attrmask |= DNS_DISPATCHATTR_UDP;
|
|
attrmask |= DNS_DISPATCHATTR_TCP;
|
|
attrmask |= DNS_DISPATCHATTR_IPV4;
|
|
attrmask |= DNS_DISPATCHATTR_IPV6;
|
|
|
|
if (localaddr == NULL) {
|
|
localaddr = &anyaddr;
|
|
isc_sockaddr_anyofpf(localaddr, family);
|
|
}
|
|
|
|
buffersize = 4096;
|
|
maxbuffers = is_shared ? 1000 : 8;
|
|
maxrequests = 32768;
|
|
buckets = is_shared ? 16411 : 3;
|
|
increment = is_shared ? 16433 : 5;
|
|
|
|
disp = NULL;
|
|
result = dns_dispatch_getudp(dispatchmgr, socketmgr,
|
|
taskmgr, localaddr,
|
|
buffersize, maxbuffers, maxrequests,
|
|
buckets, increment,
|
|
attrs, attrmask, &disp);
|
|
if (result == ISC_R_SUCCESS)
|
|
*dispp = disp;
|
|
|
|
return (result);
|
|
}
|
|
|
|
static isc_result_t
|
|
createview(isc_mem_t *mctx, dns_rdataclass_t rdclass,
|
|
unsigned int options, isc_taskmgr_t *taskmgr,
|
|
unsigned int ntasks, isc_socketmgr_t *socketmgr,
|
|
isc_timermgr_t *timermgr, dns_dispatchmgr_t *dispatchmgr,
|
|
dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6,
|
|
dns_view_t **viewp)
|
|
{
|
|
isc_result_t result;
|
|
dns_view_t *view = NULL;
|
|
const char *dbtype;
|
|
|
|
result = dns_view_create(mctx, rdclass, DNS_CLIENTVIEW_NAME, &view);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
/* Initialize view security roots */
|
|
result = dns_view_initsecroots(view, mctx);
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_view_detach(&view);
|
|
return (result);
|
|
}
|
|
|
|
result = dns_view_createresolver(view, taskmgr, ntasks, 1,
|
|
socketmgr, timermgr, 0,
|
|
dispatchmgr, dispatchv4, dispatchv6);
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_view_detach(&view);
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Set cache DB.
|
|
* XXX: it may be better if specific DB implementations can be
|
|
* specified via some configuration knob.
|
|
*/
|
|
if ((options & DNS_CLIENTCREATEOPT_USECACHE) != 0)
|
|
dbtype = "rbt";
|
|
else
|
|
dbtype = "ecdb";
|
|
result = dns_db_create(mctx, dbtype, dns_rootname, dns_dbtype_cache,
|
|
rdclass, 0, NULL, &view->cachedb);
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_view_detach(&view);
|
|
return (result);
|
|
}
|
|
|
|
*viewp = view;
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_create(dns_client_t **clientp, unsigned int options) {
|
|
isc_result_t result;
|
|
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;
|
|
#if 0
|
|
/* XXXMPA add debug logging support */
|
|
isc_log_t *lctx = NULL;
|
|
isc_logconfig_t *logconfig = NULL;
|
|
unsigned int logdebuglevel = 0;
|
|
#endif
|
|
|
|
result = isc_mem_create(0, 0, &mctx);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
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;
|
|
#if 0
|
|
result = isc_log_create(mctx, &lctx, &logconfig);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
isc_log_setcontext(lctx);
|
|
dns_log_init(lctx);
|
|
dns_log_setcontext(lctx);
|
|
result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
isc_log_setdebuglevel(lctx, logdebuglevel);
|
|
#endif
|
|
result = dns_client_createx(mctx, actx, taskmgr, socketmgr, timermgr,
|
|
options, clientp);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
(*clientp)->attributes |= DNS_CLIENTATTR_OWNCTX;
|
|
|
|
/* client has its own reference to mctx, so we can detach it here */
|
|
isc_mem_detach(&mctx);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
cleanup:
|
|
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);
|
|
|
|
return (result);
|
|
}
|
|
|
|
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;
|
|
dns_dispatchmgr_t *dispatchmgr = NULL;
|
|
dns_dispatch_t *dispatchv4 = NULL;
|
|
dns_dispatch_t *dispatchv6 = NULL;
|
|
dns_view_t *view = NULL;
|
|
|
|
REQUIRE(mctx != NULL);
|
|
REQUIRE(taskmgr != NULL);
|
|
REQUIRE(timermgr != NULL);
|
|
REQUIRE(socketmgr != NULL);
|
|
REQUIRE(clientp != NULL && *clientp == NULL);
|
|
|
|
client = isc_mem_get(mctx, sizeof(*client));
|
|
if (client == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
result = isc_mutex_init(&client->lock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_mem_put(mctx, client, sizeof(*client));
|
|
return (result);
|
|
}
|
|
|
|
client->actx = actx;
|
|
client->taskmgr = taskmgr;
|
|
client->socketmgr = socketmgr;
|
|
client->timermgr = timermgr;
|
|
|
|
client->task = NULL;
|
|
result = isc_task_create(client->taskmgr, 0, &client->task);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
result = dns_dispatchmgr_create(mctx, NULL, &dispatchmgr);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
client->dispatchmgr = dispatchmgr;
|
|
|
|
/*
|
|
* 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;
|
|
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) {
|
|
INSIST(result != ISC_R_SUCCESS);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Create the default view for class IN */
|
|
result = createview(mctx, dns_rdataclass_in, options, taskmgr, 31,
|
|
socketmgr, timermgr, dispatchmgr,
|
|
dispatchv4, dispatchv6, &view);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
ISC_LIST_INIT(client->viewlist);
|
|
ISC_LIST_APPEND(client->viewlist, view, link);
|
|
|
|
dns_view_freeze(view); /* too early? */
|
|
|
|
ISC_LIST_INIT(client->resctxs);
|
|
ISC_LIST_INIT(client->reqctxs);
|
|
ISC_LIST_INIT(client->updatectxs);
|
|
|
|
client->mctx = NULL;
|
|
isc_mem_attach(mctx, &client->mctx);
|
|
|
|
client->update_timeout = DEF_UPDATE_TIMEOUT;
|
|
client->update_udptimeout = DEF_UPDATE_UDPTIMEOUT;
|
|
client->update_udpretries = DEF_UPDATE_UDPRETRIES;
|
|
client->find_timeout = DEF_FIND_TIMEOUT;
|
|
client->find_udpretries = DEF_FIND_UDPRETRIES;
|
|
client->attributes = 0;
|
|
|
|
client->references = 1;
|
|
client->magic = DNS_CLIENT_MAGIC;
|
|
|
|
*clientp = client;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
cleanup:
|
|
if (dispatchv4 != NULL)
|
|
dns_dispatch_detach(&dispatchv4);
|
|
if (dispatchv6 != NULL)
|
|
dns_dispatch_detach(&dispatchv6);
|
|
if (dispatchmgr != NULL)
|
|
dns_dispatchmgr_destroy(&dispatchmgr);
|
|
if (client->task != NULL)
|
|
isc_task_detach(&client->task);
|
|
isc_mem_put(mctx, client, sizeof(*client));
|
|
|
|
return (result);
|
|
}
|
|
|
|
static void
|
|
destroyclient(dns_client_t **clientp) {
|
|
dns_client_t *client = *clientp;
|
|
dns_view_t *view;
|
|
|
|
while ((view = ISC_LIST_HEAD(client->viewlist)) != NULL) {
|
|
ISC_LIST_UNLINK(client->viewlist, view, link);
|
|
dns_view_detach(&view);
|
|
}
|
|
|
|
if (client->dispatchv4 != NULL)
|
|
dns_dispatch_detach(&client->dispatchv4);
|
|
if (client->dispatchv6 != NULL)
|
|
dns_dispatch_detach(&client->dispatchv6);
|
|
|
|
dns_dispatchmgr_destroy(&client->dispatchmgr);
|
|
|
|
isc_task_detach(&client->task);
|
|
|
|
/*
|
|
* If the client has created its own running environments,
|
|
* destroy them.
|
|
*/
|
|
if ((client->attributes & DNS_CLIENTATTR_OWNCTX) != 0) {
|
|
isc_taskmgr_destroy(&client->taskmgr);
|
|
isc_timermgr_destroy(&client->timermgr);
|
|
isc_socketmgr_destroy(&client->socketmgr);
|
|
|
|
isc_app_ctxfinish(client->actx);
|
|
isc_appctx_destroy(&client->actx);
|
|
}
|
|
|
|
DESTROYLOCK(&client->lock);
|
|
client->magic = 0;
|
|
|
|
isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
|
|
|
|
*clientp = NULL;
|
|
}
|
|
|
|
void
|
|
dns_client_destroy(dns_client_t **clientp) {
|
|
dns_client_t *client;
|
|
isc_boolean_t destroyok = ISC_FALSE;
|
|
|
|
REQUIRE(clientp != NULL);
|
|
client = *clientp;
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
|
|
LOCK(&client->lock);
|
|
client->references--;
|
|
if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
|
|
ISC_LIST_EMPTY(client->reqctxs) &&
|
|
ISC_LIST_EMPTY(client->updatectxs)) {
|
|
destroyok = ISC_TRUE;
|
|
}
|
|
UNLOCK(&client->lock);
|
|
|
|
if (destroyok)
|
|
destroyclient(&client);
|
|
|
|
*clientp = NULL;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_setservers(dns_client_t *client, dns_rdataclass_t rdclass,
|
|
dns_name_t *namespace, isc_sockaddrlist_t *addrs)
|
|
{
|
|
isc_result_t result;
|
|
dns_view_t *view = NULL;
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
REQUIRE(addrs != NULL);
|
|
|
|
if (namespace == NULL)
|
|
namespace = dns_rootname;
|
|
|
|
LOCK(&client->lock);
|
|
result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
|
|
rdclass, &view);
|
|
if (result != ISC_R_SUCCESS) {
|
|
UNLOCK(&client->lock);
|
|
return (result);
|
|
}
|
|
UNLOCK(&client->lock);
|
|
|
|
result = dns_fwdtable_add(view->fwdtable, namespace, addrs,
|
|
dns_fwdpolicy_only);
|
|
|
|
dns_view_detach(&view);
|
|
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_clearservers(dns_client_t *client, dns_rdataclass_t rdclass,
|
|
dns_name_t *namespace)
|
|
{
|
|
isc_result_t result;
|
|
dns_view_t *view = NULL;
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
|
|
if (namespace == NULL)
|
|
namespace = dns_rootname;
|
|
|
|
LOCK(&client->lock);
|
|
result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
|
|
rdclass, &view);
|
|
if (result != ISC_R_SUCCESS) {
|
|
UNLOCK(&client->lock);
|
|
return (result);
|
|
}
|
|
UNLOCK(&client->lock);
|
|
|
|
result = dns_fwdtable_delete(view->fwdtable, namespace);
|
|
|
|
dns_view_detach(&view);
|
|
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_setdlv(dns_client_t *client, dns_rdataclass_t rdclass,
|
|
const char *dlvname)
|
|
{
|
|
isc_result_t result;
|
|
isc_buffer_t b;
|
|
dns_view_t *view = NULL;
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
|
|
LOCK(&client->lock);
|
|
result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
|
|
rdclass, &view);
|
|
UNLOCK(&client->lock);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
if (dlvname == NULL)
|
|
view->dlv = NULL;
|
|
else {
|
|
dns_name_t *newdlv;
|
|
|
|
isc_buffer_constinit(&b, dlvname, strlen(dlvname));
|
|
isc_buffer_add(&b, strlen(dlvname));
|
|
newdlv = dns_fixedname_name(&view->dlv_fixed);
|
|
result = dns_name_fromtext(newdlv, &b, dns_rootname,
|
|
DNS_NAME_DOWNCASE, NULL);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
view->dlv = dns_fixedname_name(&view->dlv_fixed);
|
|
}
|
|
|
|
cleanup:
|
|
if (view != NULL)
|
|
dns_view_detach(&view);
|
|
|
|
return (result);
|
|
}
|
|
|
|
static isc_result_t
|
|
getrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) {
|
|
dns_rdataset_t *rdataset;
|
|
|
|
REQUIRE(mctx != NULL);
|
|
REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
|
|
|
|
rdataset = isc_mem_get(mctx, sizeof(*rdataset));
|
|
if (rdataset == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
dns_rdataset_init(rdataset);
|
|
|
|
*rdatasetp = rdataset;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
putrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) {
|
|
dns_rdataset_t *rdataset;
|
|
|
|
REQUIRE(rdatasetp != NULL);
|
|
rdataset = *rdatasetp;
|
|
REQUIRE(rdataset != NULL);
|
|
|
|
if (dns_rdataset_isassociated(rdataset))
|
|
dns_rdataset_disassociate(rdataset);
|
|
|
|
isc_mem_put(mctx, rdataset, sizeof(*rdataset));
|
|
|
|
*rdatasetp = NULL;
|
|
}
|
|
|
|
static void
|
|
fetch_done(isc_task_t *task, isc_event_t *event) {
|
|
resctx_t *rctx = event->ev_arg;
|
|
dns_fetchevent_t *fevent;
|
|
|
|
REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
|
|
REQUIRE(RCTX_VALID(rctx));
|
|
REQUIRE(rctx->task == task);
|
|
fevent = (dns_fetchevent_t *)event;
|
|
|
|
client_resfind(rctx, fevent);
|
|
}
|
|
|
|
static inline isc_result_t
|
|
start_fetch(resctx_t *rctx) {
|
|
isc_result_t result;
|
|
int fopts = 0;
|
|
|
|
/*
|
|
* The caller must be holding the rctx's lock.
|
|
*/
|
|
|
|
REQUIRE(rctx->fetch == NULL);
|
|
|
|
if (!rctx->want_cdflag)
|
|
fopts |= DNS_FETCHOPT_NOCDFLAG;
|
|
if (!rctx->want_validation)
|
|
fopts |= DNS_FETCHOPT_NOVALIDATE;
|
|
|
|
result = dns_resolver_createfetch(rctx->view->resolver,
|
|
dns_fixedname_name(&rctx->name),
|
|
rctx->type,
|
|
NULL, NULL, NULL, fopts,
|
|
rctx->task, fetch_done, rctx,
|
|
rctx->rdataset,
|
|
rctx->sigrdataset,
|
|
&rctx->fetch);
|
|
|
|
return (result);
|
|
}
|
|
|
|
static isc_result_t
|
|
view_find(resctx_t *rctx, dns_db_t **dbp, dns_dbnode_t **nodep,
|
|
dns_name_t *foundname)
|
|
{
|
|
isc_result_t result;
|
|
dns_name_t *name = dns_fixedname_name(&rctx->name);
|
|
dns_rdatatype_t type;
|
|
|
|
if (rctx->type == dns_rdatatype_rrsig)
|
|
type = dns_rdatatype_any;
|
|
else
|
|
type = rctx->type;
|
|
|
|
result = dns_view_find(rctx->view, name, type, 0, 0, ISC_FALSE,
|
|
dbp, nodep, foundname, rctx->rdataset,
|
|
rctx->sigrdataset);
|
|
|
|
return (result);
|
|
}
|
|
|
|
static void
|
|
client_resfind(resctx_t *rctx, dns_fetchevent_t *event) {
|
|
isc_mem_t *mctx;
|
|
isc_result_t tresult, result = ISC_R_SUCCESS;
|
|
isc_result_t vresult = ISC_R_SUCCESS;
|
|
isc_boolean_t want_restart;
|
|
isc_boolean_t send_event = ISC_FALSE;
|
|
dns_name_t *name, *prefix;
|
|
dns_fixedname_t foundname, fixed;
|
|
dns_rdataset_t *trdataset;
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
unsigned int nlabels;
|
|
int order;
|
|
dns_namereln_t namereln;
|
|
dns_rdata_cname_t cname;
|
|
dns_rdata_dname_t dname;
|
|
|
|
REQUIRE(RCTX_VALID(rctx));
|
|
|
|
LOCK(&rctx->lock);
|
|
|
|
mctx = rctx->view->mctx;
|
|
|
|
name = dns_fixedname_name(&rctx->name);
|
|
|
|
do {
|
|
dns_name_t *fname = NULL;
|
|
dns_name_t *ansname = NULL;
|
|
dns_db_t *db = NULL;
|
|
dns_dbnode_t *node = NULL;
|
|
|
|
rctx->restarts++;
|
|
want_restart = ISC_FALSE;
|
|
|
|
if (event == NULL && !rctx->canceled) {
|
|
dns_fixedname_init(&foundname);
|
|
fname = dns_fixedname_name(&foundname);
|
|
INSIST(!dns_rdataset_isassociated(rctx->rdataset));
|
|
INSIST(rctx->sigrdataset == NULL ||
|
|
!dns_rdataset_isassociated(rctx->sigrdataset));
|
|
result = view_find(rctx, &db, &node, fname);
|
|
if (result == ISC_R_NOTFOUND) {
|
|
/*
|
|
* We don't know anything about the name.
|
|
* Launch a fetch.
|
|
*/
|
|
if (node != NULL) {
|
|
INSIST(db != NULL);
|
|
dns_db_detachnode(db, &node);
|
|
}
|
|
if (db != NULL)
|
|
dns_db_detach(&db);
|
|
result = start_fetch(rctx);
|
|
if (result != ISC_R_SUCCESS) {
|
|
putrdataset(mctx, &rctx->rdataset);
|
|
if (rctx->sigrdataset != NULL)
|
|
putrdataset(mctx,
|
|
&rctx->sigrdataset);
|
|
send_event = ISC_TRUE;
|
|
}
|
|
goto done;
|
|
}
|
|
} else {
|
|
INSIST(event != NULL);
|
|
INSIST(event->fetch == rctx->fetch);
|
|
dns_resolver_destroyfetch(&rctx->fetch);
|
|
db = event->db;
|
|
node = event->node;
|
|
result = event->result;
|
|
vresult = event->vresult;
|
|
fname = dns_fixedname_name(&event->foundname);
|
|
INSIST(event->rdataset == rctx->rdataset);
|
|
INSIST(event->sigrdataset == rctx->sigrdataset);
|
|
}
|
|
|
|
/*
|
|
* If we've been canceled, forget about the result.
|
|
*/
|
|
if (rctx->canceled)
|
|
result = ISC_R_CANCELED;
|
|
else {
|
|
/*
|
|
* Otherwise, get some resource for copying the
|
|
* result.
|
|
*/
|
|
ansname = isc_mem_get(mctx, sizeof(*ansname));
|
|
if (ansname == NULL)
|
|
tresult = ISC_R_NOMEMORY;
|
|
else {
|
|
dns_name_t *aname;
|
|
|
|
aname = dns_fixedname_name(&rctx->name);
|
|
dns_name_init(ansname, NULL);
|
|
tresult = dns_name_dup(aname, mctx, ansname);
|
|
if (tresult != ISC_R_SUCCESS)
|
|
isc_mem_put(mctx, ansname,
|
|
sizeof(*ansname));
|
|
}
|
|
if (tresult != ISC_R_SUCCESS)
|
|
result = tresult;
|
|
}
|
|
|
|
switch (result) {
|
|
case ISC_R_SUCCESS:
|
|
send_event = ISC_TRUE;
|
|
/*
|
|
* This case is handled in the main line below.
|
|
*/
|
|
break;
|
|
case DNS_R_CNAME:
|
|
/*
|
|
* Add the CNAME to the answer list.
|
|
*/
|
|
trdataset = rctx->rdataset;
|
|
ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
|
|
rctx->rdataset = NULL;
|
|
if (rctx->sigrdataset != NULL) {
|
|
ISC_LIST_APPEND(ansname->list,
|
|
rctx->sigrdataset, link);
|
|
rctx->sigrdataset = NULL;
|
|
}
|
|
ISC_LIST_APPEND(rctx->namelist, ansname, link);
|
|
ansname = NULL;
|
|
|
|
/*
|
|
* Copy the CNAME's target into the lookup's
|
|
* query name and start over.
|
|
*/
|
|
tresult = dns_rdataset_first(trdataset);
|
|
if (tresult != ISC_R_SUCCESS)
|
|
goto done;
|
|
dns_rdataset_current(trdataset, &rdata);
|
|
tresult = dns_rdata_tostruct(&rdata, &cname, NULL);
|
|
dns_rdata_reset(&rdata);
|
|
if (tresult != ISC_R_SUCCESS)
|
|
goto done;
|
|
tresult = dns_name_copy(&cname.cname, name, NULL);
|
|
dns_rdata_freestruct(&cname);
|
|
if (tresult == ISC_R_SUCCESS)
|
|
want_restart = ISC_TRUE;
|
|
else
|
|
result = tresult;
|
|
goto done;
|
|
case DNS_R_DNAME:
|
|
/*
|
|
* Add the DNAME to the answer list.
|
|
*/
|
|
trdataset = rctx->rdataset;
|
|
ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
|
|
rctx->rdataset = NULL;
|
|
if (rctx->sigrdataset != NULL) {
|
|
ISC_LIST_APPEND(ansname->list,
|
|
rctx->sigrdataset, link);
|
|
rctx->sigrdataset = NULL;
|
|
}
|
|
ISC_LIST_APPEND(rctx->namelist, ansname, link);
|
|
ansname = NULL;
|
|
|
|
namereln = dns_name_fullcompare(name, fname, &order,
|
|
&nlabels);
|
|
INSIST(namereln == dns_namereln_subdomain);
|
|
/*
|
|
* Get the target name of the DNAME.
|
|
*/
|
|
tresult = dns_rdataset_first(trdataset);
|
|
if (tresult != ISC_R_SUCCESS) {
|
|
result = tresult;
|
|
goto done;
|
|
}
|
|
dns_rdataset_current(trdataset, &rdata);
|
|
tresult = dns_rdata_tostruct(&rdata, &dname, NULL);
|
|
dns_rdata_reset(&rdata);
|
|
if (tresult != ISC_R_SUCCESS) {
|
|
result = tresult;
|
|
goto done;
|
|
}
|
|
/*
|
|
* Construct the new query name and start over.
|
|
*/
|
|
dns_fixedname_init(&fixed);
|
|
prefix = dns_fixedname_name(&fixed);
|
|
dns_name_split(name, nlabels, prefix, NULL);
|
|
tresult = dns_name_concatenate(prefix, &dname.dname,
|
|
name, NULL);
|
|
dns_rdata_freestruct(&dname);
|
|
if (tresult == ISC_R_SUCCESS)
|
|
want_restart = ISC_TRUE;
|
|
else
|
|
result = tresult;
|
|
goto done;
|
|
case DNS_R_NCACHENXDOMAIN:
|
|
case DNS_R_NCACHENXRRSET:
|
|
ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
|
|
ISC_LIST_APPEND(rctx->namelist, ansname, link);
|
|
ansname = NULL;
|
|
rctx->rdataset = NULL;
|
|
/* What about sigrdataset? */
|
|
if (rctx->sigrdataset != NULL)
|
|
putrdataset(mctx, &rctx->sigrdataset);
|
|
send_event = ISC_TRUE;
|
|
goto done;
|
|
default:
|
|
if (rctx->rdataset != NULL)
|
|
putrdataset(mctx, &rctx->rdataset);
|
|
if (rctx->sigrdataset != NULL)
|
|
putrdataset(mctx, &rctx->sigrdataset);
|
|
send_event = ISC_TRUE;
|
|
goto done;
|
|
}
|
|
|
|
if (rctx->type == dns_rdatatype_any) {
|
|
int n = 0;
|
|
dns_rdatasetiter_t *rdsiter = NULL;
|
|
|
|
tresult = dns_db_allrdatasets(db, node, NULL, 0,
|
|
&rdsiter);
|
|
if (tresult != ISC_R_SUCCESS) {
|
|
result = tresult;
|
|
goto done;
|
|
}
|
|
|
|
tresult = dns_rdatasetiter_first(rdsiter);
|
|
while (tresult == ISC_R_SUCCESS) {
|
|
dns_rdatasetiter_current(rdsiter,
|
|
rctx->rdataset);
|
|
if (rctx->rdataset->type != 0) {
|
|
ISC_LIST_APPEND(ansname->list,
|
|
rctx->rdataset,
|
|
link);
|
|
n++;
|
|
rctx->rdataset = NULL;
|
|
} else {
|
|
/*
|
|
* We're not interested in this
|
|
* rdataset.
|
|
*/
|
|
dns_rdataset_disassociate(
|
|
rctx->rdataset);
|
|
}
|
|
tresult = dns_rdatasetiter_next(rdsiter);
|
|
|
|
if (tresult == ISC_R_SUCCESS &&
|
|
rctx->rdataset == NULL) {
|
|
tresult = getrdataset(mctx,
|
|
&rctx->rdataset);
|
|
if (tresult != ISC_R_SUCCESS) {
|
|
result = tresult;
|
|
POST(result);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (n == 0) {
|
|
/*
|
|
* We didn't match any rdatasets (which means
|
|
* something went wrong in this
|
|
* implementation).
|
|
*/
|
|
result = DNS_R_SERVFAIL; /* better code? */
|
|
POST(result);
|
|
} else {
|
|
ISC_LIST_APPEND(rctx->namelist, ansname, link);
|
|
ansname = NULL;
|
|
}
|
|
dns_rdatasetiter_destroy(&rdsiter);
|
|
if (tresult != ISC_R_NOMORE)
|
|
result = DNS_R_SERVFAIL; /* ditto */
|
|
else
|
|
result = ISC_R_SUCCESS;
|
|
goto done;
|
|
} else {
|
|
/*
|
|
* This is the "normal" case -- an ordinary question
|
|
* to which we've got the answer.
|
|
*/
|
|
ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
|
|
rctx->rdataset = NULL;
|
|
if (rctx->sigrdataset != NULL) {
|
|
ISC_LIST_APPEND(ansname->list,
|
|
rctx->sigrdataset, link);
|
|
rctx->sigrdataset = NULL;
|
|
}
|
|
ISC_LIST_APPEND(rctx->namelist, ansname, link);
|
|
ansname = NULL;
|
|
}
|
|
|
|
done:
|
|
/*
|
|
* Free temporary resources
|
|
*/
|
|
if (ansname != NULL) {
|
|
dns_rdataset_t *rdataset;
|
|
|
|
while ((rdataset = ISC_LIST_HEAD(ansname->list))
|
|
!= NULL) {
|
|
ISC_LIST_UNLINK(ansname->list, rdataset, link);
|
|
putrdataset(mctx, &rdataset);
|
|
}
|
|
dns_name_free(ansname, mctx);
|
|
isc_mem_put(mctx, ansname, sizeof(*ansname));
|
|
}
|
|
|
|
if (node != NULL)
|
|
dns_db_detachnode(db, &node);
|
|
if (db != NULL)
|
|
dns_db_detach(&db);
|
|
if (event != NULL)
|
|
isc_event_free(ISC_EVENT_PTR(&event));
|
|
|
|
/*
|
|
* Limit the number of restarts.
|
|
*/
|
|
if (want_restart && rctx->restarts == MAX_RESTARTS) {
|
|
want_restart = ISC_FALSE;
|
|
result = ISC_R_QUOTA;
|
|
send_event = ISC_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Prepare further find with new resources
|
|
*/
|
|
if (want_restart) {
|
|
INSIST(rctx->rdataset == NULL &&
|
|
rctx->sigrdataset == NULL);
|
|
|
|
result = getrdataset(mctx, &rctx->rdataset);
|
|
if (result == ISC_R_SUCCESS && rctx->want_dnssec) {
|
|
result = getrdataset(mctx, &rctx->sigrdataset);
|
|
if (result != ISC_R_SUCCESS) {
|
|
putrdataset(mctx, &rctx->rdataset);
|
|
}
|
|
}
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
want_restart = ISC_FALSE;
|
|
send_event = ISC_TRUE;
|
|
}
|
|
}
|
|
} while (want_restart);
|
|
|
|
if (send_event) {
|
|
isc_task_t *task;
|
|
|
|
while ((name = ISC_LIST_HEAD(rctx->namelist)) != NULL) {
|
|
ISC_LIST_UNLINK(rctx->namelist, name, link);
|
|
ISC_LIST_APPEND(rctx->event->answerlist, name, link);
|
|
}
|
|
|
|
rctx->event->result = result;
|
|
rctx->event->vresult = vresult;
|
|
task = rctx->event->ev_sender;
|
|
rctx->event->ev_sender = rctx;
|
|
isc_task_sendanddetach(&task, ISC_EVENT_PTR(&rctx->event));
|
|
}
|
|
|
|
UNLOCK(&rctx->lock);
|
|
}
|
|
|
|
|
|
static void
|
|
suspend(isc_task_t *task, isc_event_t *event) {
|
|
isc_appctx_t *actx = event->ev_arg;
|
|
|
|
UNUSED(task);
|
|
|
|
isc_app_ctxsuspend(actx);
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static void
|
|
resolve_done(isc_task_t *task, isc_event_t *event) {
|
|
resarg_t *resarg = event->ev_arg;
|
|
dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
|
|
dns_name_t *name;
|
|
isc_result_t result;
|
|
|
|
UNUSED(task);
|
|
|
|
LOCK(&resarg->lock);
|
|
|
|
resarg->result = rev->result;
|
|
resarg->vresult = rev->vresult;
|
|
while ((name = ISC_LIST_HEAD(rev->answerlist)) != NULL) {
|
|
ISC_LIST_UNLINK(rev->answerlist, name, link);
|
|
ISC_LIST_APPEND(*resarg->namelist, name, link);
|
|
}
|
|
|
|
dns_client_destroyrestrans(&resarg->trans);
|
|
isc_event_free(&event);
|
|
|
|
if (!resarg->canceled) {
|
|
UNLOCK(&resarg->lock);
|
|
|
|
/*
|
|
* We may or may not be running. isc__appctx_onrun will
|
|
* fail if we are currently running otherwise we post a
|
|
* action to call isc_app_ctxsuspend when we do start
|
|
* running.
|
|
*/
|
|
result = isc_app_ctxonrun(resarg->actx, resarg->client->mctx,
|
|
task, suspend, resarg->actx);
|
|
if (result == ISC_R_ALREADYRUNNING)
|
|
isc_app_ctxsuspend(resarg->actx);
|
|
} else {
|
|
/*
|
|
* We have already exited from the loop (due to some
|
|
* unexpected event). Just clean the arg up.
|
|
*/
|
|
UNLOCK(&resarg->lock);
|
|
DESTROYLOCK(&resarg->lock);
|
|
isc_mem_put(resarg->client->mctx, resarg, sizeof(*resarg));
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_resolve(dns_client_t *client, dns_name_t *name,
|
|
dns_rdataclass_t rdclass, dns_rdatatype_t type,
|
|
unsigned int options, dns_namelist_t *namelist)
|
|
{
|
|
isc_result_t result;
|
|
isc_appctx_t *actx;
|
|
resarg_t *resarg;
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
REQUIRE(namelist != NULL && ISC_LIST_EMPTY(*namelist));
|
|
|
|
if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
|
|
(options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) {
|
|
/*
|
|
* If the client is run under application's control, we need
|
|
* to create a new running (sub)environment for this
|
|
* particular resolution.
|
|
*/
|
|
return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
|
|
} else
|
|
actx = client->actx;
|
|
|
|
resarg = isc_mem_get(client->mctx, sizeof(*resarg));
|
|
if (resarg == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
result = isc_mutex_init(&resarg->lock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_mem_put(client->mctx, resarg, sizeof(*resarg));
|
|
return (result);
|
|
}
|
|
|
|
resarg->actx = actx;
|
|
resarg->client = client;
|
|
resarg->result = DNS_R_SERVFAIL;
|
|
resarg->namelist = namelist;
|
|
resarg->trans = NULL;
|
|
resarg->canceled = ISC_FALSE;
|
|
result = dns_client_startresolve(client, name, rdclass, type, options,
|
|
client->task, resolve_done, resarg,
|
|
&resarg->trans);
|
|
if (result != ISC_R_SUCCESS) {
|
|
DESTROYLOCK(&resarg->lock);
|
|
isc_mem_put(client->mctx, resarg, sizeof(*resarg));
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Start internal event loop. It blocks until the entire process
|
|
* is completed.
|
|
*/
|
|
result = isc_app_ctxrun(actx);
|
|
|
|
LOCK(&resarg->lock);
|
|
if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
|
|
result = resarg->result;
|
|
if (result != ISC_R_SUCCESS && resarg->vresult != ISC_R_SUCCESS) {
|
|
/*
|
|
* If this lookup failed due to some error in DNSSEC
|
|
* validation, return the validation error code.
|
|
* XXX: or should we pass the validation result separately?
|
|
*/
|
|
result = resarg->vresult;
|
|
}
|
|
if (resarg->trans != NULL) {
|
|
/*
|
|
* Unusual termination (perhaps due to signal). We need some
|
|
* tricky cleanup process.
|
|
*/
|
|
resarg->canceled = ISC_TRUE;
|
|
dns_client_cancelresolve(resarg->trans);
|
|
|
|
UNLOCK(&resarg->lock);
|
|
|
|
/* resarg will be freed in the event handler. */
|
|
} else {
|
|
UNLOCK(&resarg->lock);
|
|
|
|
DESTROYLOCK(&resarg->lock);
|
|
isc_mem_put(client->mctx, resarg, sizeof(*resarg));
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_startresolve(dns_client_t *client, dns_name_t *name,
|
|
dns_rdataclass_t rdclass, dns_rdatatype_t type,
|
|
unsigned int options, isc_task_t *task,
|
|
isc_taskaction_t action, void *arg,
|
|
dns_clientrestrans_t **transp)
|
|
{
|
|
dns_view_t *view = NULL;
|
|
dns_clientresevent_t *event = NULL;
|
|
resctx_t *rctx = NULL;
|
|
isc_task_t *clone = NULL;
|
|
isc_mem_t *mctx;
|
|
isc_result_t result;
|
|
dns_rdataset_t *rdataset, *sigrdataset;
|
|
isc_boolean_t want_dnssec, want_validation, want_cdflag;
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
REQUIRE(transp != NULL && *transp == NULL);
|
|
|
|
LOCK(&client->lock);
|
|
result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
|
|
rdclass, &view);
|
|
UNLOCK(&client->lock);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
mctx = client->mctx;
|
|
rdataset = NULL;
|
|
sigrdataset = NULL;
|
|
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);
|
|
|
|
/*
|
|
* Prepare some intermediate resources
|
|
*/
|
|
clone = NULL;
|
|
isc_task_attach(task, &clone);
|
|
event = (dns_clientresevent_t *)
|
|
isc_event_allocate(mctx, clone, DNS_EVENT_CLIENTRESDONE,
|
|
action, arg, sizeof(*event));
|
|
if (event == NULL) {
|
|
result = ISC_R_NOMEMORY;
|
|
goto cleanup;
|
|
}
|
|
event->result = DNS_R_SERVFAIL;
|
|
ISC_LIST_INIT(event->answerlist);
|
|
|
|
rctx = isc_mem_get(mctx, sizeof(*rctx));
|
|
if (rctx == NULL)
|
|
result = ISC_R_NOMEMORY;
|
|
else {
|
|
result = isc_mutex_init(&rctx->lock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_mem_put(mctx, rctx, sizeof(*rctx));
|
|
rctx = NULL;
|
|
}
|
|
}
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
result = getrdataset(mctx, &rdataset);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
rctx->rdataset = rdataset;
|
|
|
|
if (want_dnssec) {
|
|
result = getrdataset(mctx, &sigrdataset);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
}
|
|
rctx->sigrdataset = sigrdataset;
|
|
|
|
dns_fixedname_init(&rctx->name);
|
|
result = dns_name_copy(name, dns_fixedname_name(&rctx->name), NULL);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
rctx->client = client;
|
|
ISC_LINK_INIT(rctx, link);
|
|
rctx->canceled = ISC_FALSE;
|
|
rctx->task = client->task;
|
|
rctx->type = type;
|
|
rctx->view = view;
|
|
rctx->restarts = 0;
|
|
rctx->fetch = NULL;
|
|
rctx->want_dnssec = want_dnssec;
|
|
rctx->want_validation = want_validation;
|
|
rctx->want_cdflag = want_cdflag;
|
|
ISC_LIST_INIT(rctx->namelist);
|
|
rctx->event = event;
|
|
|
|
rctx->magic = RCTX_MAGIC;
|
|
|
|
LOCK(&client->lock);
|
|
ISC_LIST_APPEND(client->resctxs, rctx, link);
|
|
UNLOCK(&client->lock);
|
|
|
|
*transp = (dns_clientrestrans_t *)rctx;
|
|
client_resfind(rctx, NULL);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
cleanup:
|
|
if (rdataset != NULL)
|
|
putrdataset(client->mctx, &rdataset);
|
|
if (sigrdataset != NULL)
|
|
putrdataset(client->mctx, &sigrdataset);
|
|
if (rctx != NULL) {
|
|
DESTROYLOCK(&rctx->lock);
|
|
isc_mem_put(mctx, rctx, sizeof(*rctx));
|
|
}
|
|
if (event != NULL)
|
|
isc_event_free(ISC_EVENT_PTR(&event));
|
|
isc_task_detach(&clone);
|
|
dns_view_detach(&view);
|
|
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
dns_client_cancelresolve(dns_clientrestrans_t *trans) {
|
|
resctx_t *rctx;
|
|
|
|
REQUIRE(trans != NULL);
|
|
rctx = (resctx_t *)trans;
|
|
REQUIRE(RCTX_VALID(rctx));
|
|
|
|
LOCK(&rctx->lock);
|
|
|
|
if (!rctx->canceled) {
|
|
rctx->canceled = ISC_TRUE;
|
|
if (rctx->fetch != NULL)
|
|
dns_resolver_cancelfetch(rctx->fetch);
|
|
}
|
|
|
|
UNLOCK(&rctx->lock);
|
|
}
|
|
|
|
void
|
|
dns_client_freeresanswer(dns_client_t *client, dns_namelist_t *namelist) {
|
|
dns_name_t *name;
|
|
dns_rdataset_t *rdataset;
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
REQUIRE(namelist != NULL);
|
|
|
|
while ((name = ISC_LIST_HEAD(*namelist)) != NULL) {
|
|
ISC_LIST_UNLINK(*namelist, name, link);
|
|
while ((rdataset = ISC_LIST_HEAD(name->list)) != NULL) {
|
|
ISC_LIST_UNLINK(name->list, rdataset, link);
|
|
putrdataset(client->mctx, &rdataset);
|
|
}
|
|
dns_name_free(name, client->mctx);
|
|
isc_mem_put(client->mctx, name, sizeof(*name));
|
|
}
|
|
}
|
|
|
|
void
|
|
dns_client_destroyrestrans(dns_clientrestrans_t **transp) {
|
|
resctx_t *rctx;
|
|
isc_mem_t *mctx;
|
|
dns_client_t *client;
|
|
isc_boolean_t need_destroyclient = ISC_FALSE;
|
|
|
|
REQUIRE(transp != NULL);
|
|
rctx = (resctx_t *)*transp;
|
|
REQUIRE(RCTX_VALID(rctx));
|
|
REQUIRE(rctx->fetch == NULL);
|
|
REQUIRE(rctx->event == NULL);
|
|
client = rctx->client;
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
|
|
mctx = client->mctx;
|
|
dns_view_detach(&rctx->view);
|
|
|
|
LOCK(&client->lock);
|
|
|
|
INSIST(ISC_LINK_LINKED(rctx, link));
|
|
ISC_LIST_UNLINK(client->resctxs, rctx, link);
|
|
|
|
if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
|
|
ISC_LIST_EMPTY(client->reqctxs) &&
|
|
ISC_LIST_EMPTY(client->updatectxs))
|
|
need_destroyclient = ISC_TRUE;
|
|
|
|
UNLOCK(&client->lock);
|
|
|
|
INSIST(ISC_LIST_EMPTY(rctx->namelist));
|
|
|
|
DESTROYLOCK(&rctx->lock);
|
|
rctx->magic = 0;
|
|
|
|
isc_mem_put(mctx, rctx, sizeof(*rctx));
|
|
|
|
if (need_destroyclient)
|
|
destroyclient(&client);
|
|
|
|
*transp = NULL;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass,
|
|
dns_name_t *keyname, isc_buffer_t *keydatabuf)
|
|
{
|
|
isc_result_t result;
|
|
dns_view_t *view = NULL;
|
|
dst_key_t *dstkey = NULL;
|
|
dns_keytable_t *secroots = NULL;
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
|
|
LOCK(&client->lock);
|
|
result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
|
|
rdclass, &view);
|
|
UNLOCK(&client->lock);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
result = dns_view_getsecroots(view, &secroots);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
result = dst_key_fromdns(keyname, rdclass, keydatabuf, client->mctx,
|
|
&dstkey);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
result = dns_keytable_add(secroots, ISC_FALSE, &dstkey);
|
|
|
|
cleanup:
|
|
if (dstkey != NULL)
|
|
dst_key_free(&dstkey);
|
|
if (view != NULL)
|
|
dns_view_detach(&view);
|
|
if (secroots != NULL)
|
|
dns_keytable_detach(&secroots);
|
|
return (result);
|
|
}
|
|
|
|
/*%
|
|
* Simple request routines
|
|
*/
|
|
static void
|
|
request_done(isc_task_t *task, isc_event_t *event) {
|
|
dns_requestevent_t *reqev = NULL;
|
|
dns_request_t *request;
|
|
isc_result_t result, eresult;
|
|
reqctx_t *ctx;
|
|
|
|
UNUSED(task);
|
|
|
|
REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
|
|
reqev = (dns_requestevent_t *)event;
|
|
request = reqev->request;
|
|
result = eresult = reqev->result;
|
|
ctx = reqev->ev_arg;
|
|
REQUIRE(REQCTX_VALID(ctx));
|
|
|
|
isc_event_free(&event);
|
|
|
|
LOCK(&ctx->lock);
|
|
|
|
if (eresult == ISC_R_SUCCESS) {
|
|
result = dns_request_getresponse(request, ctx->event->rmessage,
|
|
ctx->parseoptions);
|
|
}
|
|
|
|
if (ctx->tsigkey != NULL)
|
|
dns_tsigkey_detach(&ctx->tsigkey);
|
|
|
|
if (ctx->canceled)
|
|
ctx->event->result = ISC_R_CANCELED;
|
|
else
|
|
ctx->event->result = result;
|
|
task = ctx->event->ev_sender;
|
|
ctx->event->ev_sender = ctx;
|
|
isc_task_sendanddetach(&task, ISC_EVENT_PTR(&ctx->event));
|
|
|
|
UNLOCK(&ctx->lock);
|
|
}
|
|
|
|
static void
|
|
localrequest_done(isc_task_t *task, isc_event_t *event) {
|
|
reqarg_t *reqarg = event->ev_arg;
|
|
dns_clientreqevent_t *rev =(dns_clientreqevent_t *)event;
|
|
|
|
UNUSED(task);
|
|
|
|
REQUIRE(event->ev_type == DNS_EVENT_CLIENTREQDONE);
|
|
|
|
LOCK(&reqarg->lock);
|
|
|
|
reqarg->result = rev->result;
|
|
dns_client_destroyreqtrans(&reqarg->trans);
|
|
isc_event_free(&event);
|
|
|
|
if (!reqarg->canceled) {
|
|
UNLOCK(&reqarg->lock);
|
|
|
|
/* Exit from the internal event loop */
|
|
isc_app_ctxsuspend(reqarg->actx);
|
|
} else {
|
|
/*
|
|
* We have already exited from the loop (due to some
|
|
* unexpected event). Just clean the arg up.
|
|
*/
|
|
UNLOCK(&reqarg->lock);
|
|
DESTROYLOCK(&reqarg->lock);
|
|
isc_mem_put(reqarg->client->mctx, reqarg, sizeof(*reqarg));
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_request(dns_client_t *client, dns_message_t *qmessage,
|
|
dns_message_t *rmessage, isc_sockaddr_t *server,
|
|
unsigned int options, unsigned int parseoptions,
|
|
dns_tsec_t *tsec, unsigned int timeout,
|
|
unsigned int udptimeout, unsigned int udpretries)
|
|
{
|
|
isc_appctx_t *actx;
|
|
reqarg_t *reqarg;
|
|
isc_result_t result;
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
REQUIRE(qmessage != NULL);
|
|
REQUIRE(rmessage != NULL);
|
|
|
|
if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
|
|
(options & DNS_CLIENTREQOPT_ALLOWRUN) == 0) {
|
|
/*
|
|
* If the client is run under application's control, we need
|
|
* to create a new running (sub)environment for this
|
|
* particular resolution.
|
|
*/
|
|
return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
|
|
} else
|
|
actx = client->actx;
|
|
|
|
reqarg = isc_mem_get(client->mctx, sizeof(*reqarg));
|
|
if (reqarg == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
result = isc_mutex_init(&reqarg->lock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
|
|
return (result);
|
|
}
|
|
|
|
reqarg->actx = actx;
|
|
reqarg->client = client;
|
|
reqarg->trans = NULL;
|
|
reqarg->canceled = ISC_FALSE;
|
|
|
|
result = dns_client_startrequest(client, qmessage, rmessage, server,
|
|
options, parseoptions, tsec, timeout,
|
|
udptimeout, udpretries,
|
|
client->task, localrequest_done,
|
|
reqarg, &reqarg->trans);
|
|
if (result != ISC_R_SUCCESS) {
|
|
DESTROYLOCK(&reqarg->lock);
|
|
isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Start internal event loop. It blocks until the entire process
|
|
* is completed.
|
|
*/
|
|
result = isc_app_ctxrun(actx);
|
|
|
|
LOCK(&reqarg->lock);
|
|
if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
|
|
result = reqarg->result;
|
|
if (reqarg->trans != NULL) {
|
|
/*
|
|
* Unusual termination (perhaps due to signal). We need some
|
|
* tricky cleanup process.
|
|
*/
|
|
reqarg->canceled = ISC_TRUE;
|
|
dns_client_cancelresolve(reqarg->trans);
|
|
|
|
UNLOCK(&reqarg->lock);
|
|
|
|
/* reqarg will be freed in the event handler. */
|
|
} else {
|
|
UNLOCK(&reqarg->lock);
|
|
|
|
DESTROYLOCK(&reqarg->lock);
|
|
isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_startrequest(dns_client_t *client, dns_message_t *qmessage,
|
|
dns_message_t *rmessage, isc_sockaddr_t *server,
|
|
unsigned int options, unsigned int parseoptions,
|
|
dns_tsec_t *tsec, unsigned int timeout,
|
|
unsigned int udptimeout, unsigned int udpretries,
|
|
isc_task_t *task, isc_taskaction_t action, void *arg,
|
|
dns_clientreqtrans_t **transp)
|
|
{
|
|
isc_result_t result;
|
|
dns_view_t *view = NULL;
|
|
isc_task_t *clone = NULL;
|
|
dns_clientreqevent_t *event = NULL;
|
|
reqctx_t *ctx = NULL;
|
|
dns_tsectype_t tsectype = dns_tsectype_none;
|
|
|
|
UNUSED(options);
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
REQUIRE(qmessage != NULL);
|
|
REQUIRE(rmessage != NULL);
|
|
REQUIRE(transp != NULL && *transp == NULL);
|
|
|
|
if (tsec != NULL) {
|
|
tsectype = dns_tsec_gettype(tsec);
|
|
if (tsectype != dns_tsectype_tsig)
|
|
return (ISC_R_NOTIMPLEMENTED); /* XXX */
|
|
}
|
|
|
|
LOCK(&client->lock);
|
|
result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
|
|
qmessage->rdclass, &view);
|
|
UNLOCK(&client->lock);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
clone = NULL;
|
|
isc_task_attach(task, &clone);
|
|
event = (dns_clientreqevent_t *)
|
|
isc_event_allocate(client->mctx, clone,
|
|
DNS_EVENT_CLIENTREQDONE,
|
|
action, arg, sizeof(*event));
|
|
if (event == NULL) {
|
|
result = ISC_R_NOMEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
ctx = isc_mem_get(client->mctx, sizeof(*ctx));
|
|
if (ctx == NULL)
|
|
result = ISC_R_NOMEMORY;
|
|
else {
|
|
result = isc_mutex_init(&ctx->lock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_mem_put(client->mctx, ctx, sizeof(*ctx));
|
|
ctx = NULL;
|
|
}
|
|
}
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
ctx->client = client;
|
|
ISC_LINK_INIT(ctx, link);
|
|
ctx->parseoptions = parseoptions;
|
|
ctx->canceled = ISC_FALSE;
|
|
ctx->event = event;
|
|
ctx->event->rmessage = rmessage;
|
|
ctx->tsigkey = NULL;
|
|
if (tsec != NULL)
|
|
dns_tsec_getkey(tsec, &ctx->tsigkey);
|
|
|
|
ctx->magic = REQCTX_MAGIC;
|
|
|
|
LOCK(&client->lock);
|
|
ISC_LIST_APPEND(client->reqctxs, ctx, link);
|
|
UNLOCK(&client->lock);
|
|
|
|
ctx->request = NULL;
|
|
result = dns_request_createvia3(view->requestmgr, qmessage, NULL,
|
|
server, options, ctx->tsigkey,
|
|
timeout, udptimeout, udpretries,
|
|
client->task, request_done, ctx,
|
|
&ctx->request);
|
|
if (result == ISC_R_SUCCESS) {
|
|
dns_view_detach(&view);
|
|
*transp = (dns_clientreqtrans_t *)ctx;
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
cleanup:
|
|
if (ctx != NULL) {
|
|
LOCK(&client->lock);
|
|
ISC_LIST_UNLINK(client->reqctxs, ctx, link);
|
|
UNLOCK(&client->lock);
|
|
DESTROYLOCK(&ctx->lock);
|
|
isc_mem_put(client->mctx, ctx, sizeof(*ctx));
|
|
}
|
|
if (event != NULL)
|
|
isc_event_free(ISC_EVENT_PTR(&event));
|
|
isc_task_detach(&clone);
|
|
dns_view_detach(&view);
|
|
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
dns_client_cancelrequest(dns_clientreqtrans_t *trans) {
|
|
reqctx_t *ctx;
|
|
|
|
REQUIRE(trans != NULL);
|
|
ctx = (reqctx_t *)trans;
|
|
REQUIRE(REQCTX_VALID(ctx));
|
|
|
|
LOCK(&ctx->lock);
|
|
|
|
if (!ctx->canceled) {
|
|
ctx->canceled = ISC_TRUE;
|
|
if (ctx->request != NULL)
|
|
dns_request_cancel(ctx->request);
|
|
}
|
|
|
|
UNLOCK(&ctx->lock);
|
|
}
|
|
|
|
void
|
|
dns_client_destroyreqtrans(dns_clientreqtrans_t **transp) {
|
|
reqctx_t *ctx;
|
|
isc_mem_t *mctx;
|
|
dns_client_t *client;
|
|
isc_boolean_t need_destroyclient = ISC_FALSE;
|
|
|
|
REQUIRE(transp != NULL);
|
|
ctx = (reqctx_t *)*transp;
|
|
REQUIRE(REQCTX_VALID(ctx));
|
|
client = ctx->client;
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
REQUIRE(ctx->event == NULL);
|
|
REQUIRE(ctx->request != NULL);
|
|
|
|
dns_request_destroy(&ctx->request);
|
|
mctx = client->mctx;
|
|
|
|
LOCK(&client->lock);
|
|
|
|
INSIST(ISC_LINK_LINKED(ctx, link));
|
|
ISC_LIST_UNLINK(client->reqctxs, ctx, link);
|
|
|
|
if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
|
|
ISC_LIST_EMPTY(client->reqctxs) &&
|
|
ISC_LIST_EMPTY(client->updatectxs)) {
|
|
need_destroyclient = ISC_TRUE;
|
|
}
|
|
|
|
UNLOCK(&client->lock);
|
|
|
|
DESTROYLOCK(&ctx->lock);
|
|
ctx->magic = 0;
|
|
|
|
isc_mem_put(mctx, ctx, sizeof(*ctx));
|
|
|
|
if (need_destroyclient)
|
|
destroyclient(&client);
|
|
|
|
*transp = NULL;
|
|
}
|
|
|
|
/*%
|
|
* Dynamic update routines
|
|
*/
|
|
static isc_result_t
|
|
rcode2result(dns_rcode_t rcode) {
|
|
/* XXX: isn't there a similar function? */
|
|
switch (rcode) {
|
|
case dns_rcode_formerr:
|
|
return (DNS_R_FORMERR);
|
|
case dns_rcode_servfail:
|
|
return (DNS_R_SERVFAIL);
|
|
case dns_rcode_nxdomain:
|
|
return (DNS_R_NXDOMAIN);
|
|
case dns_rcode_notimp:
|
|
return (DNS_R_NOTIMP);
|
|
case dns_rcode_refused:
|
|
return (DNS_R_REFUSED);
|
|
case dns_rcode_yxdomain:
|
|
return (DNS_R_YXDOMAIN);
|
|
case dns_rcode_yxrrset:
|
|
return (DNS_R_YXRRSET);
|
|
case dns_rcode_nxrrset:
|
|
return (DNS_R_NXRRSET);
|
|
case dns_rcode_notauth:
|
|
return (DNS_R_NOTAUTH);
|
|
case dns_rcode_notzone:
|
|
return (DNS_R_NOTZONE);
|
|
case dns_rcode_badvers:
|
|
return (DNS_R_BADVERS);
|
|
}
|
|
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
|
|
static void
|
|
update_sendevent(updatectx_t *uctx, isc_result_t result) {
|
|
isc_task_t *task;
|
|
|
|
dns_message_destroy(&uctx->updatemsg);
|
|
if (uctx->tsigkey != NULL)
|
|
dns_tsigkey_detach(&uctx->tsigkey);
|
|
if (uctx->sig0key != NULL)
|
|
dst_key_free(&uctx->sig0key);
|
|
|
|
if (uctx->canceled)
|
|
uctx->event->result = ISC_R_CANCELED;
|
|
else
|
|
uctx->event->result = result;
|
|
uctx->event->state = uctx->state;
|
|
task = uctx->event->ev_sender;
|
|
uctx->event->ev_sender = uctx;
|
|
isc_task_sendanddetach(&task, ISC_EVENT_PTR(&uctx->event));
|
|
}
|
|
|
|
static void
|
|
update_done(isc_task_t *task, isc_event_t *event) {
|
|
isc_result_t result;
|
|
dns_requestevent_t *reqev = NULL;
|
|
dns_request_t *request;
|
|
dns_message_t *answer = NULL;
|
|
updatectx_t *uctx = event->ev_arg;
|
|
dns_client_t *client;
|
|
unsigned int timeout;
|
|
|
|
UNUSED(task);
|
|
|
|
REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
|
|
reqev = (dns_requestevent_t *)event;
|
|
request = reqev->request;
|
|
REQUIRE(UCTX_VALID(uctx));
|
|
client = uctx->client;
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
|
|
result = reqev->result;
|
|
if (result != ISC_R_SUCCESS)
|
|
goto out;
|
|
|
|
result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
|
|
&answer);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto out;
|
|
uctx->state = dns_clientupdatestate_done;
|
|
result = dns_request_getresponse(request, answer,
|
|
DNS_MESSAGEPARSE_PRESERVEORDER);
|
|
if (result == ISC_R_SUCCESS && answer->rcode != dns_rcode_noerror)
|
|
result = rcode2result(answer->rcode);
|
|
|
|
out:
|
|
if (answer != NULL)
|
|
dns_message_destroy(&answer);
|
|
isc_event_free(&event);
|
|
|
|
LOCK(&uctx->lock);
|
|
uctx->currentserver = ISC_LIST_NEXT(uctx->currentserver, link);
|
|
dns_request_destroy(&uctx->updatereq);
|
|
if (result != ISC_R_SUCCESS && !uctx->canceled &&
|
|
uctx->currentserver != NULL) {
|
|
dns_message_renderreset(uctx->updatemsg);
|
|
dns_message_settsigkey(uctx->updatemsg, NULL);
|
|
|
|
timeout = client->update_timeout / uctx->nservers;
|
|
if (timeout < MIN_UPDATE_TIMEOUT)
|
|
timeout = MIN_UPDATE_TIMEOUT;
|
|
result = dns_request_createvia3(uctx->view->requestmgr,
|
|
uctx->updatemsg,
|
|
NULL,
|
|
uctx->currentserver, 0,
|
|
uctx->tsigkey,
|
|
timeout,
|
|
client->update_udptimeout,
|
|
client->update_udpretries,
|
|
client->task,
|
|
update_done, uctx,
|
|
&uctx->updatereq);
|
|
UNLOCK(&uctx->lock);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
/* XXX: should we keep the 'done' state here? */
|
|
uctx->state = dns_clientupdatestate_sent;
|
|
return;
|
|
}
|
|
} else
|
|
UNLOCK(&uctx->lock);
|
|
|
|
update_sendevent(uctx, result);
|
|
}
|
|
|
|
static isc_result_t
|
|
send_update(updatectx_t *uctx) {
|
|
isc_result_t result;
|
|
dns_name_t *name = NULL;
|
|
dns_rdataset_t *rdataset = NULL;
|
|
dns_client_t *client = uctx->client;
|
|
unsigned int timeout;
|
|
|
|
REQUIRE(uctx->zonename != NULL && uctx->currentserver != NULL);
|
|
|
|
result = dns_message_gettempname(uctx->updatemsg, &name);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
dns_name_init(name, NULL);
|
|
dns_name_clone(uctx->zonename, name);
|
|
result = dns_message_gettemprdataset(uctx->updatemsg, &rdataset);
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_message_puttempname(uctx->updatemsg, &name);
|
|
return (result);
|
|
}
|
|
dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa);
|
|
ISC_LIST_INIT(name->list);
|
|
ISC_LIST_APPEND(name->list, rdataset, link);
|
|
dns_message_addname(uctx->updatemsg, name, DNS_SECTION_ZONE);
|
|
if (uctx->tsigkey == NULL && uctx->sig0key != NULL) {
|
|
result = dns_message_setsig0key(uctx->updatemsg,
|
|
uctx->sig0key);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
}
|
|
timeout = client->update_timeout / uctx->nservers;
|
|
if (timeout < MIN_UPDATE_TIMEOUT)
|
|
timeout = MIN_UPDATE_TIMEOUT;
|
|
result = dns_request_createvia3(uctx->view->requestmgr,
|
|
uctx->updatemsg,
|
|
NULL, uctx->currentserver, 0,
|
|
uctx->tsigkey, timeout,
|
|
client->update_udptimeout,
|
|
client->update_udpretries,
|
|
client->task, update_done, uctx,
|
|
&uctx->updatereq);
|
|
if (result == ISC_R_SUCCESS &&
|
|
uctx->state == dns_clientupdatestate_prepare) {
|
|
uctx->state = dns_clientupdatestate_sent;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
static void
|
|
resolveaddr_done(isc_task_t *task, isc_event_t *event) {
|
|
isc_result_t result;
|
|
int family;
|
|
dns_rdatatype_t qtype;
|
|
dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
|
|
dns_name_t *name;
|
|
dns_rdataset_t *rdataset;
|
|
updatectx_t *uctx;
|
|
isc_boolean_t completed = ISC_FALSE;
|
|
|
|
UNUSED(task);
|
|
|
|
REQUIRE(event->ev_arg != NULL);
|
|
uctx = *(updatectx_t **)event->ev_arg;
|
|
REQUIRE(UCTX_VALID(uctx));
|
|
|
|
if (event->ev_arg == &uctx->bp4) {
|
|
family = AF_INET;
|
|
qtype = dns_rdatatype_a;
|
|
LOCK(&uctx->lock);
|
|
dns_client_destroyrestrans(&uctx->restrans);
|
|
UNLOCK(&uctx->lock);
|
|
} else {
|
|
INSIST(event->ev_arg == &uctx->bp6);
|
|
family = AF_INET6;
|
|
qtype = dns_rdatatype_aaaa;
|
|
LOCK(&uctx->lock);
|
|
dns_client_destroyrestrans(&uctx->restrans2);
|
|
UNLOCK(&uctx->lock);
|
|
}
|
|
|
|
result = rev->result;
|
|
if (result != ISC_R_SUCCESS)
|
|
goto done;
|
|
|
|
for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
|
|
name = ISC_LIST_NEXT(name, link)) {
|
|
for (rdataset = ISC_LIST_HEAD(name->list);
|
|
rdataset != NULL;
|
|
rdataset = ISC_LIST_NEXT(rdataset, link)) {
|
|
if (!dns_rdataset_isassociated(rdataset))
|
|
continue;
|
|
if (rdataset->type != qtype)
|
|
continue;
|
|
|
|
for (result = dns_rdataset_first(rdataset);
|
|
result == ISC_R_SUCCESS;
|
|
result = dns_rdataset_next(rdataset)) {
|
|
dns_rdata_t rdata;
|
|
dns_rdata_in_a_t rdata_a;
|
|
dns_rdata_in_aaaa_t rdata_aaaa;
|
|
isc_sockaddr_t *sa;
|
|
|
|
sa = isc_mem_get(uctx->client->mctx,
|
|
sizeof(*sa));
|
|
if (sa == NULL) {
|
|
/*
|
|
* If we fail to get a sockaddr,
|
|
we simply move forward with the
|
|
* addresses we've got so far.
|
|
*/
|
|
goto done;
|
|
}
|
|
|
|
dns_rdata_init(&rdata);
|
|
switch (family) {
|
|
case AF_INET:
|
|
dns_rdataset_current(rdataset, &rdata);
|
|
result = dns_rdata_tostruct(&rdata, &rdata_a,
|
|
NULL);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
isc_sockaddr_fromin(sa,
|
|
&rdata_a.in_addr,
|
|
53);
|
|
dns_rdata_freestruct(&rdata_a);
|
|
break;
|
|
case AF_INET6:
|
|
dns_rdataset_current(rdataset, &rdata);
|
|
result = dns_rdata_tostruct(&rdata, &rdata_aaaa,
|
|
NULL);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
isc_sockaddr_fromin6(sa,
|
|
&rdata_aaaa.in6_addr,
|
|
53);
|
|
dns_rdata_freestruct(&rdata_aaaa);
|
|
break;
|
|
}
|
|
|
|
ISC_LINK_INIT(sa, link);
|
|
ISC_LIST_APPEND(uctx->servers, sa, link);
|
|
uctx->nservers++;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
dns_client_freeresanswer(uctx->client, &rev->answerlist);
|
|
isc_event_free(&event);
|
|
|
|
LOCK(&uctx->lock);
|
|
if (uctx->restrans == NULL && uctx->restrans2 == NULL)
|
|
completed = ISC_TRUE;
|
|
UNLOCK(&uctx->lock);
|
|
|
|
if (completed) {
|
|
INSIST(uctx->currentserver == NULL);
|
|
uctx->currentserver = ISC_LIST_HEAD(uctx->servers);
|
|
if (uctx->currentserver != NULL && !uctx->canceled)
|
|
send_update(uctx);
|
|
else {
|
|
if (result == ISC_R_SUCCESS)
|
|
result = ISC_R_NOTFOUND;
|
|
update_sendevent(uctx, result);
|
|
}
|
|
}
|
|
}
|
|
|
|
static isc_result_t
|
|
process_soa(updatectx_t *uctx, dns_rdataset_t *soaset, dns_name_t *soaname) {
|
|
isc_result_t result;
|
|
dns_rdata_t soarr = DNS_RDATA_INIT;
|
|
dns_rdata_soa_t soa;
|
|
dns_name_t primary;
|
|
|
|
result = dns_rdataset_first(soaset);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
dns_rdata_init(&soarr);
|
|
dns_rdataset_current(soaset, &soarr);
|
|
result = dns_rdata_tostruct(&soarr, &soa, NULL);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
dns_name_init(&primary, NULL);
|
|
dns_name_clone(&soa.origin, &primary);
|
|
|
|
if (uctx->zonename == NULL) {
|
|
uctx->zonename = dns_fixedname_name(&uctx->zonefname);
|
|
result = dns_name_copy(soaname, uctx->zonename, NULL);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto out;
|
|
}
|
|
|
|
if (uctx->currentserver != NULL)
|
|
result = send_update(uctx);
|
|
else {
|
|
/*
|
|
* Get addresses of the primary server. We don't use the ADB
|
|
* feature so that we could avoid caching data.
|
|
*/
|
|
LOCK(&uctx->lock);
|
|
uctx->bp4 = uctx;
|
|
result = dns_client_startresolve(uctx->client, &primary,
|
|
uctx->rdclass,
|
|
dns_rdatatype_a,
|
|
0, uctx->client->task,
|
|
resolveaddr_done, &uctx->bp4,
|
|
&uctx->restrans);
|
|
if (result == ISC_R_SUCCESS) {
|
|
uctx->bp6 = uctx;
|
|
result = dns_client_startresolve(uctx->client,
|
|
&primary,
|
|
uctx->rdclass,
|
|
dns_rdatatype_aaaa,
|
|
0, uctx->client->task,
|
|
resolveaddr_done,
|
|
&uctx->bp6,
|
|
&uctx->restrans2);
|
|
}
|
|
UNLOCK(&uctx->lock);
|
|
}
|
|
|
|
out:
|
|
dns_rdata_freestruct(&soa);
|
|
|
|
return (result);
|
|
}
|
|
|
|
static void
|
|
receive_soa(isc_task_t *task, isc_event_t *event) {
|
|
dns_requestevent_t *reqev = NULL;
|
|
updatectx_t *uctx;
|
|
dns_client_t *client;
|
|
isc_result_t result, eresult;
|
|
dns_request_t *request;
|
|
dns_message_t *rcvmsg = NULL;
|
|
dns_section_t section;
|
|
dns_rdataset_t *soaset = NULL;
|
|
int pass = 0;
|
|
dns_name_t *name;
|
|
dns_message_t *soaquery = NULL;
|
|
isc_sockaddr_t *addr;
|
|
isc_boolean_t seencname = ISC_FALSE;
|
|
isc_boolean_t droplabel = ISC_FALSE;
|
|
dns_name_t tname;
|
|
unsigned int nlabels;
|
|
|
|
UNUSED(task);
|
|
|
|
REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
|
|
reqev = (dns_requestevent_t *)event;
|
|
request = reqev->request;
|
|
result = eresult = reqev->result;
|
|
POST(result);
|
|
uctx = reqev->ev_arg;
|
|
client = uctx->client;
|
|
soaquery = uctx->soaquery;
|
|
addr = uctx->currentserver;
|
|
INSIST(addr != NULL);
|
|
|
|
isc_event_free(&event);
|
|
|
|
if (eresult != ISC_R_SUCCESS) {
|
|
result = eresult;
|
|
goto out;
|
|
}
|
|
|
|
result = dns_message_create(uctx->client->mctx,
|
|
DNS_MESSAGE_INTENTPARSE, &rcvmsg);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto out;
|
|
result = dns_request_getresponse(request, rcvmsg,
|
|
DNS_MESSAGEPARSE_PRESERVEORDER);
|
|
|
|
if (result == DNS_R_TSIGERRORSET) {
|
|
dns_request_t *newrequest = NULL;
|
|
|
|
/* Retry SOA request without TSIG */
|
|
dns_message_destroy(&rcvmsg);
|
|
dns_message_renderreset(uctx->soaquery);
|
|
result = dns_request_createvia3(uctx->view->requestmgr,
|
|
uctx->soaquery, NULL, addr, 0,
|
|
NULL,
|
|
client->find_timeout * 20,
|
|
client->find_timeout, 3,
|
|
uctx->client->task,
|
|
receive_soa, uctx,
|
|
&newrequest);
|
|
if (result == ISC_R_SUCCESS) {
|
|
LOCK(&uctx->lock);
|
|
dns_request_destroy(&uctx->soareq);
|
|
uctx->soareq = newrequest;
|
|
UNLOCK(&uctx->lock);
|
|
|
|
return;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
section = DNS_SECTION_ANSWER;
|
|
POST(section);
|
|
|
|
if (rcvmsg->rcode != dns_rcode_noerror &&
|
|
rcvmsg->rcode != dns_rcode_nxdomain) {
|
|
result = rcode2result(rcvmsg->rcode);
|
|
goto out;
|
|
}
|
|
|
|
lookforsoa:
|
|
if (pass == 0)
|
|
section = DNS_SECTION_ANSWER;
|
|
else if (pass == 1)
|
|
section = DNS_SECTION_AUTHORITY;
|
|
else {
|
|
droplabel = ISC_TRUE;
|
|
goto out;
|
|
}
|
|
|
|
result = dns_message_firstname(rcvmsg, section);
|
|
if (result != ISC_R_SUCCESS) {
|
|
pass++;
|
|
goto lookforsoa;
|
|
}
|
|
while (result == ISC_R_SUCCESS) {
|
|
name = NULL;
|
|
dns_message_currentname(rcvmsg, section, &name);
|
|
soaset = NULL;
|
|
result = dns_message_findtype(name, dns_rdatatype_soa, 0,
|
|
&soaset);
|
|
if (result == ISC_R_SUCCESS)
|
|
break;
|
|
if (section == DNS_SECTION_ANSWER) {
|
|
dns_rdataset_t *tset = NULL;
|
|
if (dns_message_findtype(name, dns_rdatatype_cname, 0,
|
|
&tset) == ISC_R_SUCCESS
|
|
||
|
|
dns_message_findtype(name, dns_rdatatype_dname, 0,
|
|
&tset) == ISC_R_SUCCESS
|
|
)
|
|
{
|
|
seencname = ISC_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = dns_message_nextname(rcvmsg, section);
|
|
}
|
|
|
|
if (soaset == NULL && !seencname) {
|
|
pass++;
|
|
goto lookforsoa;
|
|
}
|
|
|
|
if (seencname) {
|
|
droplabel = ISC_TRUE;
|
|
goto out;
|
|
}
|
|
|
|
result = process_soa(uctx, soaset, name);
|
|
|
|
out:
|
|
if (droplabel) {
|
|
result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION);
|
|
INSIST(result == ISC_R_SUCCESS);
|
|
name = NULL;
|
|
dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name);
|
|
nlabels = dns_name_countlabels(name);
|
|
if (nlabels == 1)
|
|
result = DNS_R_SERVFAIL; /* is there a better error? */
|
|
else {
|
|
dns_name_init(&tname, NULL);
|
|
dns_name_getlabelsequence(name, 1, nlabels - 1,
|
|
&tname);
|
|
dns_name_clone(&tname, name);
|
|
dns_request_destroy(&request);
|
|
LOCK(&uctx->lock);
|
|
uctx->soareq = NULL;
|
|
UNLOCK(&uctx->lock);
|
|
dns_message_renderreset(soaquery);
|
|
dns_message_settsigkey(soaquery, NULL);
|
|
result = dns_request_createvia3(uctx->view->requestmgr,
|
|
soaquery, NULL,
|
|
uctx->currentserver, 0,
|
|
uctx->tsigkey,
|
|
client->find_timeout *
|
|
20,
|
|
client->find_timeout,
|
|
3, client->task,
|
|
receive_soa, uctx,
|
|
&uctx->soareq);
|
|
}
|
|
}
|
|
|
|
if (!droplabel || result != ISC_R_SUCCESS) {
|
|
dns_message_destroy(&uctx->soaquery);
|
|
LOCK(&uctx->lock);
|
|
dns_request_destroy(&uctx->soareq);
|
|
UNLOCK(&uctx->lock);
|
|
}
|
|
|
|
if (rcvmsg != NULL)
|
|
dns_message_destroy(&rcvmsg);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
update_sendevent(uctx, result);
|
|
}
|
|
|
|
static isc_result_t
|
|
request_soa(updatectx_t *uctx) {
|
|
isc_result_t result;
|
|
dns_message_t *soaquery = uctx->soaquery;
|
|
dns_name_t *name = NULL;
|
|
dns_rdataset_t *rdataset = NULL;
|
|
|
|
if (soaquery == NULL) {
|
|
result = dns_message_create(uctx->client->mctx,
|
|
DNS_MESSAGE_INTENTRENDER,
|
|
&soaquery);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
}
|
|
soaquery->flags |= DNS_MESSAGEFLAG_RD;
|
|
result = dns_message_gettempname(soaquery, &name);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
result = dns_message_gettemprdataset(soaquery, &rdataset);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa);
|
|
dns_name_clone(uctx->firstname, name);
|
|
ISC_LIST_APPEND(name->list, rdataset, link);
|
|
dns_message_addname(soaquery, name, DNS_SECTION_QUESTION);
|
|
rdataset = NULL;
|
|
name = NULL;
|
|
|
|
result = dns_request_createvia3(uctx->view->requestmgr,
|
|
soaquery, NULL, uctx->currentserver, 0,
|
|
uctx->tsigkey,
|
|
uctx->client->find_timeout * 20,
|
|
uctx->client->find_timeout, 3,
|
|
uctx->client->task, receive_soa, uctx,
|
|
&uctx->soareq);
|
|
if (result == ISC_R_SUCCESS) {
|
|
uctx->soaquery = soaquery;
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
fail:
|
|
if (rdataset != NULL) {
|
|
ISC_LIST_UNLINK(name->list, rdataset, link); /* for safety */
|
|
dns_message_puttemprdataset(soaquery, &rdataset);
|
|
}
|
|
if (name != NULL)
|
|
dns_message_puttempname(soaquery, &name);
|
|
dns_message_destroy(&soaquery);
|
|
|
|
return (result);
|
|
}
|
|
|
|
static void
|
|
resolvesoa_done(isc_task_t *task, isc_event_t *event) {
|
|
dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
|
|
updatectx_t *uctx;
|
|
dns_name_t *name, tname;
|
|
dns_rdataset_t *rdataset = NULL;
|
|
isc_result_t result = rev->result;
|
|
unsigned int nlabels;
|
|
|
|
UNUSED(task);
|
|
|
|
uctx = event->ev_arg;
|
|
REQUIRE(UCTX_VALID(uctx));
|
|
|
|
LOCK(&uctx->lock);
|
|
dns_client_destroyrestrans(&uctx->restrans);
|
|
UNLOCK(&uctx->lock);
|
|
|
|
uctx = event->ev_arg;
|
|
if (result != ISC_R_SUCCESS &&
|
|
result != DNS_R_NCACHENXDOMAIN &&
|
|
result != DNS_R_NCACHENXRRSET) {
|
|
/* XXX: what about DNSSEC failure? */
|
|
goto out;
|
|
}
|
|
|
|
for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
|
|
name = ISC_LIST_NEXT(name, link)) {
|
|
for (rdataset = ISC_LIST_HEAD(name->list);
|
|
rdataset != NULL;
|
|
rdataset = ISC_LIST_NEXT(rdataset, link)) {
|
|
if (dns_rdataset_isassociated(rdataset) &&
|
|
rdataset->type == dns_rdatatype_soa)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rdataset == NULL) {
|
|
/* Drop one label and retry resolution. */
|
|
nlabels = dns_name_countlabels(&uctx->soaqname);
|
|
if (nlabels == 1) {
|
|
result = DNS_R_SERVFAIL; /* is there a better error? */
|
|
goto out;
|
|
}
|
|
dns_name_init(&tname, NULL);
|
|
dns_name_getlabelsequence(&uctx->soaqname, 1, nlabels - 1,
|
|
&tname);
|
|
dns_name_clone(&tname, &uctx->soaqname);
|
|
|
|
result = dns_client_startresolve(uctx->client, &uctx->soaqname,
|
|
uctx->rdclass,
|
|
dns_rdatatype_soa, 0,
|
|
uctx->client->task,
|
|
resolvesoa_done, uctx,
|
|
&uctx->restrans);
|
|
} else
|
|
result = process_soa(uctx, rdataset, &uctx->soaqname);
|
|
|
|
out:
|
|
dns_client_freeresanswer(uctx->client, &rev->answerlist);
|
|
isc_event_free(&event);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
update_sendevent(uctx, result);
|
|
}
|
|
|
|
static isc_result_t
|
|
copy_name(isc_mem_t *mctx, dns_message_t *msg, dns_name_t *name,
|
|
dns_name_t **newnamep)
|
|
{
|
|
isc_result_t result;
|
|
dns_name_t *newname = NULL;
|
|
isc_region_t r;
|
|
isc_buffer_t *namebuf = NULL, *rdatabuf = NULL;
|
|
dns_rdatalist_t *rdatalist;
|
|
dns_rdataset_t *rdataset, *newrdataset;
|
|
dns_rdata_t rdata = DNS_RDATA_INIT, *newrdata;
|
|
|
|
result = dns_message_gettempname(msg, &newname);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
result = isc_buffer_allocate(mctx, &namebuf, DNS_NAME_MAXWIRE);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
dns_name_init(newname, NULL);
|
|
dns_name_setbuffer(newname, namebuf);
|
|
dns_message_takebuffer(msg, &namebuf);
|
|
result = dns_name_copy(name, newname, NULL);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
|
|
for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
|
|
rdataset = ISC_LIST_NEXT(rdataset, link)) {
|
|
rdatalist = NULL;
|
|
result = dns_message_gettemprdatalist(msg, &rdatalist);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
dns_rdatalist_init(rdatalist);
|
|
rdatalist->type = rdataset->type;
|
|
rdatalist->rdclass = rdataset->rdclass;
|
|
rdatalist->covers = rdataset->covers;
|
|
rdatalist->ttl = rdataset->ttl;
|
|
|
|
result = dns_rdataset_first(rdataset);
|
|
while (result == ISC_R_SUCCESS) {
|
|
dns_rdata_reset(&rdata);
|
|
dns_rdataset_current(rdataset, &rdata);
|
|
|
|
newrdata = NULL;
|
|
result = dns_message_gettemprdata(msg, &newrdata);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
dns_rdata_toregion(&rdata, &r);
|
|
rdatabuf = NULL;
|
|
result = isc_buffer_allocate(mctx, &rdatabuf,
|
|
r.length);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
isc_buffer_putmem(rdatabuf, r.base, r.length);
|
|
isc_buffer_usedregion(rdatabuf, &r);
|
|
dns_rdata_init(newrdata);
|
|
dns_rdata_fromregion(newrdata, rdata.rdclass,
|
|
rdata.type, &r);
|
|
newrdata->flags = rdata.flags;
|
|
|
|
ISC_LIST_APPEND(rdatalist->rdata, newrdata, link);
|
|
dns_message_takebuffer(msg, &rdatabuf);
|
|
|
|
result = dns_rdataset_next(rdataset);
|
|
}
|
|
|
|
newrdataset = NULL;
|
|
result = dns_message_gettemprdataset(msg, &newrdataset);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
dns_rdataset_init(newrdataset);
|
|
dns_rdatalist_tordataset(rdatalist, newrdataset);
|
|
|
|
ISC_LIST_APPEND(newname->list, newrdataset, link);
|
|
}
|
|
|
|
*newnamep = newname;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
fail:
|
|
dns_message_puttempname(msg, &newname);
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
static void
|
|
internal_update_callback(isc_task_t *task, isc_event_t *event) {
|
|
updatearg_t *uarg = event->ev_arg;
|
|
dns_clientupdateevent_t *uev = (dns_clientupdateevent_t *)event;
|
|
|
|
UNUSED(task);
|
|
|
|
LOCK(&uarg->lock);
|
|
|
|
uarg->result = uev->result;
|
|
|
|
dns_client_destroyupdatetrans(&uarg->trans);
|
|
isc_event_free(&event);
|
|
|
|
if (!uarg->canceled) {
|
|
UNLOCK(&uarg->lock);
|
|
|
|
/* Exit from the internal event loop */
|
|
isc_app_ctxsuspend(uarg->actx);
|
|
} else {
|
|
/*
|
|
* We have already exited from the loop (due to some
|
|
* unexpected event). Just clean the arg up.
|
|
*/
|
|
UNLOCK(&uarg->lock);
|
|
DESTROYLOCK(&uarg->lock);
|
|
isc_mem_put(uarg->client->mctx, uarg, sizeof(*uarg));
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_update(dns_client_t *client, dns_rdataclass_t rdclass,
|
|
dns_name_t *zonename, dns_namelist_t *prerequisites,
|
|
dns_namelist_t *updates, isc_sockaddrlist_t *servers,
|
|
dns_tsec_t *tsec, unsigned int options)
|
|
{
|
|
isc_result_t result;
|
|
isc_appctx_t *actx;
|
|
updatearg_t *uarg;
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
|
|
if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
|
|
(options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) {
|
|
/*
|
|
* If the client is run under application's control, we need
|
|
* to create a new running (sub)environment for this
|
|
* particular resolution.
|
|
*/
|
|
return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
|
|
} else
|
|
actx = client->actx;
|
|
|
|
uarg = isc_mem_get(client->mctx, sizeof(*uarg));
|
|
if (uarg == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
result = isc_mutex_init(&uarg->lock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_mem_put(client->mctx, uarg, sizeof(*uarg));
|
|
return (result);
|
|
}
|
|
|
|
uarg->actx = actx;
|
|
uarg->client = client;
|
|
uarg->result = ISC_R_FAILURE;
|
|
uarg->trans = NULL;
|
|
uarg->canceled = ISC_FALSE;
|
|
|
|
result = dns_client_startupdate(client, rdclass, zonename,
|
|
prerequisites, updates, servers,
|
|
tsec, options, client->task,
|
|
internal_update_callback, uarg,
|
|
&uarg->trans);
|
|
if (result != ISC_R_SUCCESS) {
|
|
DESTROYLOCK(&uarg->lock);
|
|
isc_mem_put(client->mctx, uarg, sizeof(*uarg));
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Start internal event loop. It blocks until the entire process
|
|
* is completed.
|
|
*/
|
|
result = isc_app_ctxrun(actx);
|
|
|
|
LOCK(&uarg->lock);
|
|
if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
|
|
result = uarg->result;
|
|
|
|
if (uarg->trans != NULL) {
|
|
/*
|
|
* Unusual termination (perhaps due to signal). We need some
|
|
* tricky cleanup process.
|
|
*/
|
|
uarg->canceled = ISC_TRUE;
|
|
dns_client_cancelupdate(uarg->trans);
|
|
|
|
UNLOCK(&uarg->lock);
|
|
|
|
/* uarg will be freed in the event handler. */
|
|
} else {
|
|
UNLOCK(&uarg->lock);
|
|
|
|
DESTROYLOCK(&uarg->lock);
|
|
isc_mem_put(client->mctx, uarg, sizeof(*uarg));
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass,
|
|
dns_name_t *zonename, dns_namelist_t *prerequisites,
|
|
dns_namelist_t *updates, isc_sockaddrlist_t *servers,
|
|
dns_tsec_t *tsec, unsigned int options,
|
|
isc_task_t *task, isc_taskaction_t action, void *arg,
|
|
dns_clientupdatetrans_t **transp)
|
|
{
|
|
dns_view_t *view = NULL;
|
|
isc_result_t result;
|
|
dns_name_t *name, *newname;
|
|
updatectx_t *uctx;
|
|
isc_task_t *clone = NULL;
|
|
dns_section_t section = DNS_SECTION_UPDATE;
|
|
isc_sockaddr_t *server, *sa = NULL;
|
|
dns_tsectype_t tsectype = dns_tsectype_none;
|
|
|
|
UNUSED(options);
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
REQUIRE(transp != NULL && *transp == NULL);
|
|
REQUIRE(updates != NULL);
|
|
REQUIRE(task != NULL);
|
|
|
|
if (tsec != NULL) {
|
|
tsectype = dns_tsec_gettype(tsec);
|
|
if (tsectype != dns_tsectype_tsig)
|
|
return (ISC_R_NOTIMPLEMENTED); /* XXX */
|
|
}
|
|
|
|
LOCK(&client->lock);
|
|
result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
|
|
rdclass, &view);
|
|
UNLOCK(&client->lock);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
/* Create a context and prepare some resources */
|
|
uctx = isc_mem_get(client->mctx, sizeof(*uctx));
|
|
if (uctx == NULL) {
|
|
dns_view_detach(&view);
|
|
return (ISC_R_NOMEMORY);
|
|
}
|
|
result = isc_mutex_init(&uctx->lock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_view_detach(&view);
|
|
isc_mem_put(client->mctx, uctx, sizeof(*uctx));
|
|
return (ISC_R_NOMEMORY);
|
|
}
|
|
clone = NULL;
|
|
isc_task_attach(task, &clone);
|
|
uctx->client = client;
|
|
ISC_LINK_INIT(uctx, link);
|
|
uctx->state = dns_clientupdatestate_prepare;
|
|
uctx->view = view;
|
|
uctx->rdclass = rdclass;
|
|
uctx->canceled = ISC_FALSE;
|
|
uctx->updatemsg = NULL;
|
|
uctx->soaquery = NULL;
|
|
uctx->updatereq = NULL;
|
|
uctx->restrans = NULL;
|
|
uctx->restrans2 = NULL;
|
|
uctx->bp4 = NULL;
|
|
uctx->bp6 = NULL;
|
|
uctx->soareq = NULL;
|
|
uctx->event = NULL;
|
|
uctx->tsigkey = NULL;
|
|
uctx->sig0key = NULL;
|
|
uctx->zonename = NULL;
|
|
dns_name_init(&uctx->soaqname, NULL);
|
|
ISC_LIST_INIT(uctx->servers);
|
|
uctx->nservers = 0;
|
|
uctx->currentserver = NULL;
|
|
dns_fixedname_init(&uctx->zonefname);
|
|
if (tsec != NULL)
|
|
dns_tsec_getkey(tsec, &uctx->tsigkey);
|
|
uctx->event = (dns_clientupdateevent_t *)
|
|
isc_event_allocate(client->mctx, clone, DNS_EVENT_UPDATEDONE,
|
|
action, arg, sizeof(*uctx->event));
|
|
if (uctx->event == NULL)
|
|
goto fail;
|
|
if (zonename != NULL) {
|
|
uctx->zonename = dns_fixedname_name(&uctx->zonefname);
|
|
result = dns_name_copy(zonename, uctx->zonename, NULL);
|
|
}
|
|
if (servers != NULL) {
|
|
for (server = ISC_LIST_HEAD(*servers);
|
|
server != NULL;
|
|
server = ISC_LIST_NEXT(server, link)) {
|
|
sa = isc_mem_get(client->mctx, sizeof(*sa));
|
|
if (sa == NULL)
|
|
goto fail;
|
|
sa->type = server->type;
|
|
sa->length = server->length;
|
|
ISC_LINK_INIT(sa, link);
|
|
ISC_LIST_APPEND(uctx->servers, sa, link);
|
|
if (uctx->currentserver == NULL)
|
|
uctx->currentserver = sa;
|
|
uctx->nservers++;
|
|
}
|
|
}
|
|
|
|
/* Make update message */
|
|
result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTRENDER,
|
|
&uctx->updatemsg);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
uctx->updatemsg->opcode = dns_opcode_update;
|
|
|
|
if (prerequisites != NULL) {
|
|
for (name = ISC_LIST_HEAD(*prerequisites); name != NULL;
|
|
name = ISC_LIST_NEXT(name, link)) {
|
|
newname = NULL;
|
|
result = copy_name(client->mctx, uctx->updatemsg,
|
|
name, &newname);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
dns_message_addname(uctx->updatemsg, newname,
|
|
DNS_SECTION_PREREQUISITE);
|
|
}
|
|
}
|
|
|
|
for (name = ISC_LIST_HEAD(*updates); name != NULL;
|
|
name = ISC_LIST_NEXT(name, link)) {
|
|
newname = NULL;
|
|
result = copy_name(client->mctx, uctx->updatemsg, name,
|
|
&newname);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
dns_message_addname(uctx->updatemsg, newname,
|
|
DNS_SECTION_UPDATE);
|
|
}
|
|
|
|
uctx->firstname = NULL;
|
|
result = dns_message_firstname(uctx->updatemsg, section);
|
|
if (result == ISC_R_NOMORE) {
|
|
section = DNS_SECTION_PREREQUISITE;
|
|
result = dns_message_firstname(uctx->updatemsg, section);
|
|
}
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
dns_message_currentname(uctx->updatemsg, section, &uctx->firstname);
|
|
|
|
uctx->magic = UCTX_MAGIC;
|
|
|
|
LOCK(&client->lock);
|
|
ISC_LIST_APPEND(client->updatectxs, uctx, link);
|
|
UNLOCK(&client->lock);
|
|
|
|
if (uctx->zonename != NULL && uctx->currentserver != NULL) {
|
|
result = send_update(uctx);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
} else if (uctx->currentserver != NULL) {
|
|
result = request_soa(uctx);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
} else {
|
|
dns_name_clone(uctx->firstname, &uctx->soaqname);
|
|
result = dns_client_startresolve(uctx->client, &uctx->soaqname,
|
|
uctx->rdclass,
|
|
dns_rdatatype_soa, 0,
|
|
client->task, resolvesoa_done,
|
|
uctx, &uctx->restrans);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto fail;
|
|
}
|
|
|
|
*transp = (dns_clientupdatetrans_t *)uctx;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
fail:
|
|
if (ISC_LINK_LINKED(uctx, link)) {
|
|
LOCK(&client->lock);
|
|
ISC_LIST_UNLINK(client->updatectxs, uctx, link);
|
|
UNLOCK(&client->lock);
|
|
}
|
|
if (uctx->updatemsg != NULL)
|
|
dns_message_destroy(&uctx->updatemsg);
|
|
while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) {
|
|
ISC_LIST_UNLINK(uctx->servers, sa, link);
|
|
isc_mem_put(client->mctx, sa, sizeof(*sa));
|
|
}
|
|
if (uctx->event != NULL)
|
|
isc_event_free(ISC_EVENT_PTR(&uctx->event));
|
|
if (uctx->tsigkey != NULL)
|
|
dns_tsigkey_detach(&uctx->tsigkey);
|
|
isc_task_detach(&clone);
|
|
DESTROYLOCK(&uctx->lock);
|
|
uctx->magic = 0;
|
|
isc_mem_put(client->mctx, uctx, sizeof(*uctx));
|
|
dns_view_detach(&view);
|
|
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
dns_client_cancelupdate(dns_clientupdatetrans_t *trans) {
|
|
updatectx_t *uctx;
|
|
|
|
REQUIRE(trans != NULL);
|
|
uctx = (updatectx_t *)trans;
|
|
REQUIRE(UCTX_VALID(uctx));
|
|
|
|
LOCK(&uctx->lock);
|
|
|
|
if (!uctx->canceled) {
|
|
uctx->canceled = ISC_TRUE;
|
|
if (uctx->updatereq != NULL)
|
|
dns_request_cancel(uctx->updatereq);
|
|
if (uctx->soareq != NULL)
|
|
dns_request_cancel(uctx->soareq);
|
|
if (uctx->restrans != NULL)
|
|
dns_client_cancelresolve(uctx->restrans);
|
|
if (uctx->restrans2 != NULL)
|
|
dns_client_cancelresolve(uctx->restrans2);
|
|
}
|
|
|
|
UNLOCK(&uctx->lock);
|
|
}
|
|
|
|
void
|
|
dns_client_destroyupdatetrans(dns_clientupdatetrans_t **transp) {
|
|
updatectx_t *uctx;
|
|
isc_mem_t *mctx;
|
|
dns_client_t *client;
|
|
isc_boolean_t need_destroyclient = ISC_FALSE;
|
|
isc_sockaddr_t *sa;
|
|
|
|
REQUIRE(transp != NULL);
|
|
uctx = (updatectx_t *)*transp;
|
|
REQUIRE(UCTX_VALID(uctx));
|
|
client = uctx->client;
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
REQUIRE(uctx->updatereq == NULL && uctx->updatemsg == NULL &&
|
|
uctx->soareq == NULL && uctx->soaquery == NULL &&
|
|
uctx->event == NULL && uctx->tsigkey == NULL &&
|
|
uctx->sig0key == NULL);
|
|
|
|
mctx = client->mctx;
|
|
dns_view_detach(&uctx->view);
|
|
while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) {
|
|
ISC_LIST_UNLINK(uctx->servers, sa, link);
|
|
isc_mem_put(mctx, sa, sizeof(*sa));
|
|
}
|
|
|
|
LOCK(&client->lock);
|
|
|
|
INSIST(ISC_LINK_LINKED(uctx, link));
|
|
ISC_LIST_UNLINK(client->updatectxs, uctx, link);
|
|
|
|
if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
|
|
ISC_LIST_EMPTY(client->reqctxs) &&
|
|
ISC_LIST_EMPTY(client->updatectxs))
|
|
need_destroyclient = ISC_TRUE;
|
|
|
|
UNLOCK(&client->lock);
|
|
|
|
DESTROYLOCK(&uctx->lock);
|
|
uctx->magic = 0;
|
|
|
|
isc_mem_put(mctx, uctx, sizeof(*uctx));
|
|
|
|
if (need_destroyclient)
|
|
destroyclient(&client);
|
|
|
|
*transp = NULL;
|
|
}
|
|
|
|
isc_mem_t *
|
|
dns_client_mctx(dns_client_t *client) {
|
|
|
|
REQUIRE(DNS_CLIENT_VALID(client));
|
|
return (client->mctx);
|
|
}
|
|
|
|
typedef struct {
|
|
isc_buffer_t buffer;
|
|
dns_rdataset_t rdataset;
|
|
dns_rdatalist_t rdatalist;
|
|
dns_rdata_t rdata;
|
|
size_t size;
|
|
isc_mem_t * mctx;
|
|
unsigned char data[FLEXIBLE_ARRAY_MEMBER];
|
|
} dns_client_updaterec_t;
|
|
|
|
isc_result_t
|
|
dns_client_updaterec(dns_client_updateop_t op, dns_name_t *owner,
|
|
dns_rdatatype_t type, dns_rdata_t *source,
|
|
dns_ttl_t ttl, dns_name_t *target,
|
|
dns_rdataset_t *rdataset, dns_rdatalist_t *rdatalist,
|
|
dns_rdata_t *rdata, isc_mem_t *mctx)
|
|
{
|
|
dns_client_updaterec_t *updaterec = NULL;
|
|
size_t size = offsetof(dns_client_updaterec_t, data);
|
|
|
|
REQUIRE(op < updateop_max);
|
|
REQUIRE(owner != NULL);
|
|
REQUIRE((rdataset != NULL && rdatalist != NULL && rdata != NULL) ||
|
|
(rdataset == NULL && rdatalist == NULL && rdata == NULL &&
|
|
mctx != NULL));
|
|
if (op == updateop_add)
|
|
REQUIRE(source != NULL);
|
|
if (source != NULL) {
|
|
REQUIRE(source->type == type);
|
|
REQUIRE(op == updateop_add || op == updateop_delete ||
|
|
op == updateop_exist);
|
|
}
|
|
|
|
size += owner->length;
|
|
if (source != NULL)
|
|
size += source->length;
|
|
|
|
if (rdataset == NULL) {
|
|
updaterec = isc_mem_get(mctx, size);
|
|
if (updaterec == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
rdataset = &updaterec->rdataset;
|
|
rdatalist = &updaterec->rdatalist;
|
|
rdata = &updaterec->rdata;
|
|
dns_rdataset_init(rdataset);
|
|
dns_rdatalist_init(&updaterec->rdatalist);
|
|
dns_rdata_init(&updaterec->rdata);
|
|
isc_buffer_init(&updaterec->buffer, updaterec->data,
|
|
size - offsetof(dns_client_updaterec_t, data));
|
|
dns_name_copy(owner, target, &updaterec->buffer);
|
|
if (source != NULL) {
|
|
isc_region_t r;
|
|
dns_rdata_clone(source, rdata);
|
|
dns_rdata_toregion(rdata, &r);
|
|
rdata->data = isc_buffer_used(&updaterec->buffer);
|
|
isc_buffer_copyregion(&updaterec->buffer, &r);
|
|
}
|
|
updaterec->mctx = NULL;
|
|
isc_mem_attach(mctx, &updaterec->mctx);
|
|
} else if (source != NULL)
|
|
dns_rdata_clone(source, rdata);
|
|
|
|
switch (op) {
|
|
case updateop_add:
|
|
break;
|
|
case updateop_delete:
|
|
if (source != NULL) {
|
|
ttl = 0;
|
|
dns_rdata_makedelete(rdata);
|
|
} else
|
|
dns_rdata_deleterrset(rdata, type);
|
|
break;
|
|
case updateop_notexist:
|
|
dns_rdata_notexist(rdata, type);
|
|
break;
|
|
case updateop_exist:
|
|
if (source == NULL) {
|
|
ttl = 0;
|
|
dns_rdata_exists(rdata, type);
|
|
}
|
|
case updateop_none:
|
|
break;
|
|
default:
|
|
INSIST(0);
|
|
}
|
|
|
|
rdatalist->type = rdata->type;
|
|
rdatalist->rdclass = rdata->rdclass;
|
|
if (source != NULL) {
|
|
rdatalist->covers = dns_rdata_covers(rdata);
|
|
rdatalist->ttl = ttl;
|
|
}
|
|
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
|
|
dns_rdatalist_tordataset(rdatalist, rdataset);
|
|
ISC_LIST_APPEND(target->list, rdataset, link);
|
|
if (updaterec != NULL) {
|
|
target->attributes |= DNS_NAMEATTR_HASUPDATEREC;
|
|
dns_name_setbuffer(target, &updaterec->buffer);
|
|
}
|
|
if (op == updateop_add || op == updateop_delete)
|
|
target->attributes |= DNS_NAMEATTR_UPDATE;
|
|
else
|
|
target->attributes |= DNS_NAMEATTR_PREREQUISITE;
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
void
|
|
dns_client_freeupdate(dns_name_t **namep) {
|
|
dns_client_updaterec_t *updaterec;
|
|
dns_rdatalist_t *rdatalist;
|
|
dns_rdataset_t *rdataset;
|
|
dns_rdata_t *rdata;
|
|
dns_name_t *name;
|
|
|
|
REQUIRE(namep != NULL && *namep != NULL);
|
|
|
|
name = *namep;
|
|
for (rdataset = ISC_LIST_HEAD(name->list);
|
|
rdataset != NULL;
|
|
rdataset = ISC_LIST_HEAD(name->list)) {
|
|
ISC_LIST_UNLINK(name->list, rdataset, link);
|
|
rdatalist = NULL;
|
|
dns_rdatalist_fromrdataset(rdataset, &rdatalist);
|
|
if (rdatalist == NULL) {
|
|
dns_rdataset_disassociate(rdataset);
|
|
continue;
|
|
}
|
|
for (rdata = ISC_LIST_HEAD(rdatalist->rdata);
|
|
rdata != NULL;
|
|
rdata = ISC_LIST_HEAD(rdatalist->rdata))
|
|
ISC_LIST_UNLINK(rdatalist->rdata, rdata, link);
|
|
dns_rdataset_disassociate(rdataset);
|
|
}
|
|
|
|
if ((name->attributes & DNS_NAMEATTR_HASUPDATEREC) != 0) {
|
|
updaterec = (dns_client_updaterec_t *)name->buffer;
|
|
INSIST(updaterec != NULL);
|
|
isc_mem_putanddetach(&updaterec->mctx, updaterec,
|
|
updaterec->size);
|
|
*namep = NULL;
|
|
}
|
|
}
|