1224090Sdougb/* 2254402Serwin * Copyright (C) 2009-2013 Internet Systems Consortium, Inc. ("ISC") 3224090Sdougb * 4224090Sdougb * Permission to use, copy, modify, and/or distribute this software for any 5224090Sdougb * purpose with or without fee is hereby granted, provided that the above 6224090Sdougb * copyright notice and this permission notice appear in all copies. 7224090Sdougb * 8224090Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9224090Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10224090Sdougb * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11224090Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12224090Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13224090Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14224090Sdougb * PERFORMANCE OF THIS SOFTWARE. 15224090Sdougb */ 16224090Sdougb 17254897Serwin/* $Id: client.c,v 1.14 2011/03/12 04:59:47 tbox Exp $ */ 18224090Sdougb 19224090Sdougb#include <config.h> 20224090Sdougb 21224090Sdougb#include <stddef.h> 22224090Sdougb 23224090Sdougb#include <isc/app.h> 24224090Sdougb#include <isc/mem.h> 25224090Sdougb#include <isc/mutex.h> 26224090Sdougb#include <isc/sockaddr.h> 27224090Sdougb#include <isc/socket.h> 28224090Sdougb#include <isc/task.h> 29224090Sdougb#include <isc/timer.h> 30224090Sdougb#include <isc/util.h> 31224090Sdougb 32224090Sdougb#include <dns/adb.h> 33224090Sdougb#include <dns/client.h> 34224090Sdougb#include <dns/db.h> 35224090Sdougb#include <dns/dispatch.h> 36224090Sdougb#include <dns/events.h> 37224090Sdougb#include <dns/forward.h> 38224090Sdougb#include <dns/keytable.h> 39224090Sdougb#include <dns/message.h> 40224090Sdougb#include <dns/name.h> 41224090Sdougb#include <dns/rdata.h> 42224090Sdougb#include <dns/rdatalist.h> 43224090Sdougb#include <dns/rdataset.h> 44224090Sdougb#include <dns/rdatatype.h> 45224090Sdougb#include <dns/rdatasetiter.h> 46224090Sdougb#include <dns/rdatastruct.h> 47224090Sdougb#include <dns/request.h> 48224090Sdougb#include <dns/resolver.h> 49224090Sdougb#include <dns/result.h> 50224090Sdougb#include <dns/tsec.h> 51224090Sdougb#include <dns/tsig.h> 52224090Sdougb#include <dns/view.h> 53224090Sdougb 54224090Sdougb#include <dst/dst.h> 55224090Sdougb 56224090Sdougb#define DNS_CLIENT_MAGIC ISC_MAGIC('D', 'N', 'S', 'c') 57224090Sdougb#define DNS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, DNS_CLIENT_MAGIC) 58224090Sdougb 59224090Sdougb#define RCTX_MAGIC ISC_MAGIC('R', 'c', 't', 'x') 60224090Sdougb#define RCTX_VALID(c) ISC_MAGIC_VALID(c, RCTX_MAGIC) 61224090Sdougb 62224090Sdougb#define REQCTX_MAGIC ISC_MAGIC('R', 'q', 'c', 'x') 63224090Sdougb#define REQCTX_VALID(c) ISC_MAGIC_VALID(c, REQCTX_MAGIC) 64224090Sdougb 65224090Sdougb#define UCTX_MAGIC ISC_MAGIC('U', 'c', 't', 'x') 66224090Sdougb#define UCTX_VALID(c) ISC_MAGIC_VALID(c, UCTX_MAGIC) 67224090Sdougb 68224090Sdougb#define MAX_RESTARTS 16 69224090Sdougb 70224090Sdougb/*% 71224090Sdougb * DNS client object 72224090Sdougb */ 73224090Sdougbstruct dns_client { 74224090Sdougb /* Unlocked */ 75224090Sdougb unsigned int magic; 76224090Sdougb unsigned int attributes; 77224090Sdougb isc_mutex_t lock; 78224090Sdougb isc_mem_t *mctx; 79224090Sdougb isc_appctx_t *actx; 80224090Sdougb isc_taskmgr_t *taskmgr; 81224090Sdougb isc_task_t *task; 82224090Sdougb isc_socketmgr_t *socketmgr; 83224090Sdougb isc_timermgr_t *timermgr; 84224090Sdougb dns_dispatchmgr_t *dispatchmgr; 85224090Sdougb dns_dispatch_t *dispatchv4; 86224090Sdougb dns_dispatch_t *dispatchv6; 87224090Sdougb 88224090Sdougb unsigned int update_timeout; 89224090Sdougb unsigned int update_udptimeout; 90224090Sdougb unsigned int update_udpretries; 91224090Sdougb unsigned int find_timeout; 92224090Sdougb unsigned int find_udpretries; 93224090Sdougb 94224090Sdougb /* Locked */ 95224090Sdougb unsigned int references; 96224090Sdougb dns_viewlist_t viewlist; 97224090Sdougb ISC_LIST(struct resctx) resctxs; 98224090Sdougb ISC_LIST(struct reqctx) reqctxs; 99224090Sdougb ISC_LIST(struct updatectx) updatectxs; 100224090Sdougb}; 101224090Sdougb 102224090Sdougb/*% 103224090Sdougb * Timeout/retry constants for dynamic update borrowed from nsupdate 104224090Sdougb */ 105224090Sdougb#define DEF_UPDATE_TIMEOUT 300 106224090Sdougb#define MIN_UPDATE_TIMEOUT 30 107224090Sdougb#define DEF_UPDATE_UDPTIMEOUT 3 108224090Sdougb#define DEF_UPDATE_UDPRETRIES 3 109224090Sdougb 110224090Sdougb#define DEF_FIND_TIMEOUT 5 111224090Sdougb#define DEF_FIND_UDPRETRIES 3 112224090Sdougb 113224090Sdougb#define DNS_CLIENTATTR_OWNCTX 0x01 114224090Sdougb 115224090Sdougb#define DNS_CLIENTVIEW_NAME "dnsclient" 116224090Sdougb 117224090Sdougb/*% 118224090Sdougb * Internal state for a single name resolution procedure 119224090Sdougb */ 120224090Sdougbtypedef struct resctx { 121224090Sdougb /* Unlocked */ 122224090Sdougb unsigned int magic; 123224090Sdougb isc_mutex_t lock; 124224090Sdougb dns_client_t *client; 125224090Sdougb isc_boolean_t want_dnssec; 126224090Sdougb 127224090Sdougb /* Locked */ 128224090Sdougb ISC_LINK(struct resctx) link; 129224090Sdougb isc_task_t *task; 130224090Sdougb dns_view_t *view; 131224090Sdougb unsigned int restarts; 132224090Sdougb dns_fixedname_t name; 133224090Sdougb dns_rdatatype_t type; 134224090Sdougb dns_fetch_t *fetch; 135224090Sdougb dns_namelist_t namelist; 136224090Sdougb isc_result_t result; 137224090Sdougb dns_clientresevent_t *event; 138224090Sdougb isc_boolean_t canceled; 139224090Sdougb dns_rdataset_t *rdataset; 140224090Sdougb dns_rdataset_t *sigrdataset; 141224090Sdougb} resctx_t; 142224090Sdougb 143224090Sdougb/*% 144224090Sdougb * Argument of an internal event for synchronous name resolution. 145224090Sdougb */ 146224090Sdougbtypedef struct resarg { 147224090Sdougb /* Unlocked */ 148224090Sdougb isc_appctx_t *actx; 149224090Sdougb dns_client_t *client; 150224090Sdougb isc_mutex_t lock; 151224090Sdougb 152224090Sdougb /* Locked */ 153224090Sdougb isc_result_t result; 154224090Sdougb isc_result_t vresult; 155224090Sdougb dns_namelist_t *namelist; 156224090Sdougb dns_clientrestrans_t *trans; 157224090Sdougb isc_boolean_t canceled; 158224090Sdougb} resarg_t; 159224090Sdougb 160224090Sdougb/*% 161224090Sdougb * Internal state for a single DNS request 162224090Sdougb */ 163224090Sdougbtypedef struct reqctx { 164224090Sdougb /* Unlocked */ 165224090Sdougb unsigned int magic; 166224090Sdougb isc_mutex_t lock; 167224090Sdougb dns_client_t *client; 168224090Sdougb unsigned int parseoptions; 169224090Sdougb 170224090Sdougb /* Locked */ 171224090Sdougb ISC_LINK(struct reqctx) link; 172224090Sdougb isc_boolean_t canceled; 173224090Sdougb dns_tsigkey_t *tsigkey; 174224090Sdougb dns_request_t *request; 175224090Sdougb dns_clientreqevent_t *event; 176224090Sdougb} reqctx_t; 177224090Sdougb 178224090Sdougb/*% 179224090Sdougb * Argument of an internal event for synchronous DNS request. 180224090Sdougb */ 181224090Sdougbtypedef struct reqarg { 182224090Sdougb /* Unlocked */ 183224090Sdougb isc_appctx_t *actx; 184224090Sdougb dns_client_t *client; 185224090Sdougb isc_mutex_t lock; 186224090Sdougb 187224090Sdougb /* Locked */ 188224090Sdougb isc_result_t result; 189224090Sdougb dns_clientreqtrans_t *trans; 190224090Sdougb isc_boolean_t canceled; 191224090Sdougb} reqarg_t; 192224090Sdougb 193224090Sdougb/*% 194224090Sdougb * Argument of an internal event for synchronous name resolution. 195224090Sdougb */ 196224090Sdougbtypedef struct updatearg { 197224090Sdougb /* Unlocked */ 198224090Sdougb isc_appctx_t *actx; 199224090Sdougb dns_client_t *client; 200224090Sdougb isc_mutex_t lock; 201224090Sdougb 202224090Sdougb /* Locked */ 203224090Sdougb isc_result_t result; 204224090Sdougb dns_clientupdatetrans_t *trans; 205224090Sdougb isc_boolean_t canceled; 206224090Sdougb} updatearg_t; 207224090Sdougb 208224090Sdougb/*% 209224090Sdougb * Internal state for a single dynamic update procedure 210224090Sdougb */ 211224090Sdougbtypedef struct updatectx { 212224090Sdougb /* Unlocked */ 213224090Sdougb unsigned int magic; 214224090Sdougb isc_mutex_t lock; 215224090Sdougb dns_client_t *client; 216224090Sdougb 217224090Sdougb /* Locked */ 218224090Sdougb dns_request_t *updatereq; 219224090Sdougb dns_request_t *soareq; 220224090Sdougb dns_clientrestrans_t *restrans; 221224090Sdougb dns_clientrestrans_t *restrans2; 222224090Sdougb isc_boolean_t canceled; 223224090Sdougb 224224090Sdougb /* Task Locked */ 225224090Sdougb ISC_LINK(struct updatectx) link; 226224090Sdougb dns_clientupdatestate_t state; 227224090Sdougb dns_rdataclass_t rdclass; 228224090Sdougb dns_view_t *view; 229224090Sdougb dns_message_t *updatemsg; 230224090Sdougb dns_message_t *soaquery; 231224090Sdougb dns_clientupdateevent_t *event; 232224090Sdougb dns_tsigkey_t *tsigkey; 233224090Sdougb dst_key_t *sig0key; 234224090Sdougb dns_name_t *firstname; 235224090Sdougb dns_name_t soaqname; 236224090Sdougb dns_fixedname_t zonefname; 237224090Sdougb dns_name_t *zonename; 238224090Sdougb isc_sockaddrlist_t servers; 239224090Sdougb unsigned int nservers; 240224090Sdougb isc_sockaddr_t *currentserver; 241224090Sdougb struct updatectx *bp4; 242224090Sdougb struct updatectx *bp6; 243224090Sdougb} updatectx_t; 244224090Sdougb 245224090Sdougbstatic isc_result_t request_soa(updatectx_t *uctx); 246224090Sdougbstatic void client_resfind(resctx_t *rctx, dns_fetchevent_t *event); 247224090Sdougbstatic isc_result_t send_update(updatectx_t *uctx); 248224090Sdougb 249224090Sdougbstatic isc_result_t 250224090Sdougbgetudpdispatch(int family, dns_dispatchmgr_t *dispatchmgr, 251224090Sdougb isc_socketmgr_t *socketmgr, isc_taskmgr_t *taskmgr, 252262706Serwin isc_boolean_t is_shared, dns_dispatch_t **dispp, 253262706Serwin isc_sockaddr_t *localaddr) 254224090Sdougb{ 255224090Sdougb unsigned int attrs, attrmask; 256224090Sdougb dns_dispatch_t *disp; 257224090Sdougb unsigned buffersize, maxbuffers, maxrequests, buckets, increment; 258224090Sdougb isc_result_t result; 259262706Serwin isc_sockaddr_t anyaddr; 260224090Sdougb 261224090Sdougb attrs = 0; 262224090Sdougb attrs |= DNS_DISPATCHATTR_UDP; 263224090Sdougb switch (family) { 264224090Sdougb case AF_INET: 265224090Sdougb attrs |= DNS_DISPATCHATTR_IPV4; 266224090Sdougb break; 267224090Sdougb case AF_INET6: 268224090Sdougb attrs |= DNS_DISPATCHATTR_IPV6; 269224090Sdougb break; 270224090Sdougb default: 271224090Sdougb INSIST(0); 272224090Sdougb } 273224090Sdougb attrmask = 0; 274224090Sdougb attrmask |= DNS_DISPATCHATTR_UDP; 275224090Sdougb attrmask |= DNS_DISPATCHATTR_TCP; 276224090Sdougb attrmask |= DNS_DISPATCHATTR_IPV4; 277224090Sdougb attrmask |= DNS_DISPATCHATTR_IPV6; 278224090Sdougb 279262706Serwin if (localaddr == NULL) { 280262706Serwin localaddr = &anyaddr; 281262706Serwin isc_sockaddr_anyofpf(localaddr, family); 282262706Serwin } 283224090Sdougb 284224090Sdougb buffersize = 4096; 285224090Sdougb maxbuffers = is_shared ? 1000 : 8; 286224090Sdougb maxrequests = 32768; 287224090Sdougb buckets = is_shared ? 16411 : 3; 288224090Sdougb increment = is_shared ? 16433 : 5; 289224090Sdougb 290224090Sdougb disp = NULL; 291224090Sdougb result = dns_dispatch_getudp(dispatchmgr, socketmgr, 292262706Serwin taskmgr, localaddr, 293224090Sdougb buffersize, maxbuffers, maxrequests, 294224090Sdougb buckets, increment, 295224090Sdougb attrs, attrmask, &disp); 296224090Sdougb if (result == ISC_R_SUCCESS) 297224090Sdougb *dispp = disp; 298224090Sdougb 299224090Sdougb return (result); 300224090Sdougb} 301224090Sdougb 302224090Sdougbstatic isc_result_t 303224090Sdougbdns_client_createview(isc_mem_t *mctx, dns_rdataclass_t rdclass, 304224090Sdougb unsigned int options, isc_taskmgr_t *taskmgr, 305224090Sdougb unsigned int ntasks, isc_socketmgr_t *socketmgr, 306224090Sdougb isc_timermgr_t *timermgr, dns_dispatchmgr_t *dispatchmgr, 307224090Sdougb dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6, 308224090Sdougb dns_view_t **viewp) 309224090Sdougb{ 310224090Sdougb isc_result_t result; 311224090Sdougb dns_view_t *view = NULL; 312224090Sdougb const char *dbtype; 313224090Sdougb 314224090Sdougb result = dns_view_create(mctx, rdclass, DNS_CLIENTVIEW_NAME, &view); 315224090Sdougb if (result != ISC_R_SUCCESS) 316224090Sdougb return (result); 317224090Sdougb 318224090Sdougb /* Initialize view security roots */ 319224090Sdougb result = dns_view_initsecroots(view, mctx); 320224090Sdougb if (result != ISC_R_SUCCESS) { 321224090Sdougb dns_view_detach(&view); 322224090Sdougb return (result); 323224090Sdougb } 324224090Sdougb 325254897Serwin result = dns_view_createresolver(view, taskmgr, ntasks, 1, socketmgr, 326224090Sdougb timermgr, 0, dispatchmgr, 327224090Sdougb dispatchv4, dispatchv6); 328224090Sdougb if (result != ISC_R_SUCCESS) { 329224090Sdougb dns_view_detach(&view); 330224090Sdougb return (result); 331224090Sdougb } 332224090Sdougb 333224090Sdougb /* 334224090Sdougb * Set cache DB. 335224090Sdougb * XXX: it may be better if specific DB implementations can be 336224090Sdougb * specified via some configuration knob. 337224090Sdougb */ 338224090Sdougb if ((options & DNS_CLIENTCREATEOPT_USECACHE) != 0) 339224090Sdougb dbtype = "rbt"; 340224090Sdougb else 341224090Sdougb dbtype = "ecdb"; 342224090Sdougb result = dns_db_create(mctx, dbtype, dns_rootname, dns_dbtype_cache, 343224090Sdougb rdclass, 0, NULL, &view->cachedb); 344224090Sdougb if (result != ISC_R_SUCCESS) { 345224090Sdougb dns_view_detach(&view); 346224090Sdougb return (result); 347224090Sdougb } 348224090Sdougb 349224090Sdougb *viewp = view; 350224090Sdougb return (ISC_R_SUCCESS); 351224090Sdougb} 352224090Sdougb 353224090Sdougbisc_result_t 354224090Sdougbdns_client_create(dns_client_t **clientp, unsigned int options) { 355224090Sdougb isc_result_t result; 356224090Sdougb isc_mem_t *mctx = NULL; 357224090Sdougb isc_appctx_t *actx = NULL; 358224090Sdougb isc_taskmgr_t *taskmgr = NULL; 359224090Sdougb isc_socketmgr_t *socketmgr = NULL; 360224090Sdougb isc_timermgr_t *timermgr = NULL; 361254402Serwin#if 0 362254402Serwin /* XXXMPA add debug logging support */ 363254402Serwin isc_log_t *lctx = NULL; 364254402Serwin isc_logconfig_t *logconfig = NULL; 365254402Serwin unsigned int logdebuglevel = 0; 366254402Serwin#endif 367224090Sdougb 368224090Sdougb result = isc_mem_create(0, 0, &mctx); 369224090Sdougb if (result != ISC_R_SUCCESS) 370224090Sdougb return (result); 371224090Sdougb result = isc_appctx_create(mctx, &actx); 372224090Sdougb if (result != ISC_R_SUCCESS) 373224090Sdougb goto cleanup; 374224090Sdougb result = isc_app_ctxstart(actx); 375224090Sdougb if (result != ISC_R_SUCCESS) 376224090Sdougb goto cleanup; 377224090Sdougb result = isc_taskmgr_createinctx(mctx, actx, 1, 0, &taskmgr); 378224090Sdougb if (result != ISC_R_SUCCESS) 379224090Sdougb goto cleanup; 380224090Sdougb result = isc_socketmgr_createinctx(mctx, actx, &socketmgr); 381224090Sdougb if (result != ISC_R_SUCCESS) 382224090Sdougb goto cleanup; 383224090Sdougb result = isc_timermgr_createinctx(mctx, actx, &timermgr); 384224090Sdougb if (result != ISC_R_SUCCESS) 385224090Sdougb goto cleanup; 386254402Serwin#if 0 387254402Serwin result = isc_log_create(mctx, &lctx, &logconfig); 388254402Serwin if (result != ISC_R_SUCCESS) 389254402Serwin goto cleanup; 390254402Serwin isc_log_setcontext(lctx); 391254402Serwin dns_log_init(lctx); 392254402Serwin dns_log_setcontext(lctx); 393254402Serwin result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL); 394254402Serwin if (result != ISC_R_SUCCESS) 395254402Serwin goto cleanup; 396254402Serwin isc_log_setdebuglevel(lctx, logdebuglevel); 397254402Serwin#endif 398224090Sdougb result = dns_client_createx(mctx, actx, taskmgr, socketmgr, timermgr, 399224090Sdougb options, clientp); 400224090Sdougb if (result != ISC_R_SUCCESS) 401224090Sdougb goto cleanup; 402224090Sdougb 403224090Sdougb (*clientp)->attributes |= DNS_CLIENTATTR_OWNCTX; 404224090Sdougb 405224090Sdougb /* client has its own reference to mctx, so we can detach it here */ 406224090Sdougb isc_mem_detach(&mctx); 407224090Sdougb 408224090Sdougb return (ISC_R_SUCCESS); 409224090Sdougb 410224090Sdougb cleanup: 411224090Sdougb if (taskmgr != NULL) 412224090Sdougb isc_taskmgr_destroy(&taskmgr); 413224090Sdougb if (timermgr != NULL) 414224090Sdougb isc_timermgr_destroy(&timermgr); 415224090Sdougb if (socketmgr != NULL) 416224090Sdougb isc_socketmgr_destroy(&socketmgr); 417224090Sdougb if (actx != NULL) 418224090Sdougb isc_appctx_destroy(&actx); 419224090Sdougb isc_mem_detach(&mctx); 420224090Sdougb 421224090Sdougb return (result); 422224090Sdougb} 423224090Sdougb 424224090Sdougbisc_result_t 425224090Sdougbdns_client_createx(isc_mem_t *mctx, isc_appctx_t *actx, isc_taskmgr_t *taskmgr, 426224090Sdougb isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, 427224090Sdougb unsigned int options, dns_client_t **clientp) 428224090Sdougb{ 429262706Serwin isc_result_t result; 430262706Serwin result = dns_client_createx2(mctx, actx, taskmgr, socketmgr, timermgr, 431262706Serwin options, clientp, NULL, NULL); 432262706Serwin return (result); 433262706Serwin} 434262706Serwin 435262706Serwinisc_result_t 436262706Serwindns_client_createx2(isc_mem_t *mctx, isc_appctx_t *actx, 437262706Serwin isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr, 438262706Serwin isc_timermgr_t *timermgr, unsigned int options, 439262706Serwin dns_client_t **clientp, isc_sockaddr_t *localaddr4, 440262706Serwin isc_sockaddr_t *localaddr6) 441262706Serwin{ 442224090Sdougb dns_client_t *client; 443224090Sdougb isc_result_t result; 444224090Sdougb dns_dispatchmgr_t *dispatchmgr = NULL; 445224090Sdougb dns_dispatch_t *dispatchv4 = NULL; 446224090Sdougb dns_dispatch_t *dispatchv6 = NULL; 447224090Sdougb dns_view_t *view = NULL; 448224090Sdougb 449224090Sdougb REQUIRE(mctx != NULL); 450224090Sdougb REQUIRE(taskmgr != NULL); 451224090Sdougb REQUIRE(timermgr != NULL); 452224090Sdougb REQUIRE(socketmgr != NULL); 453224090Sdougb REQUIRE(clientp != NULL && *clientp == NULL); 454224090Sdougb 455224090Sdougb client = isc_mem_get(mctx, sizeof(*client)); 456224090Sdougb if (client == NULL) 457224090Sdougb return (ISC_R_NOMEMORY); 458224090Sdougb 459224090Sdougb result = isc_mutex_init(&client->lock); 460224090Sdougb if (result != ISC_R_SUCCESS) { 461224090Sdougb isc_mem_put(mctx, client, sizeof(*client)); 462224090Sdougb return (result); 463224090Sdougb } 464224090Sdougb 465224090Sdougb client->actx = actx; 466224090Sdougb client->taskmgr = taskmgr; 467224090Sdougb client->socketmgr = socketmgr; 468224090Sdougb client->timermgr = timermgr; 469224090Sdougb 470224090Sdougb client->task = NULL; 471224090Sdougb result = isc_task_create(client->taskmgr, 0, &client->task); 472224090Sdougb if (result != ISC_R_SUCCESS) 473224090Sdougb goto cleanup; 474224090Sdougb 475224090Sdougb result = dns_dispatchmgr_create(mctx, NULL, &dispatchmgr); 476224090Sdougb if (result != ISC_R_SUCCESS) 477224090Sdougb goto cleanup; 478224090Sdougb client->dispatchmgr = dispatchmgr; 479224090Sdougb 480262706Serwin /* 481262706Serwin * If only one address family is specified, use it. 482262706Serwin * If neither family is specified, or if both are, use both. 483262706Serwin */ 484224090Sdougb client->dispatchv4 = NULL; 485262706Serwin if (localaddr4 != NULL || localaddr6 == NULL) { 486262706Serwin result = getudpdispatch(AF_INET, dispatchmgr, socketmgr, 487262706Serwin taskmgr, ISC_TRUE, 488262706Serwin &dispatchv4, localaddr4); 489262706Serwin if (result == ISC_R_SUCCESS) 490262706Serwin client->dispatchv4 = dispatchv4; 491262706Serwin } 492262706Serwin 493224090Sdougb client->dispatchv6 = NULL; 494262706Serwin if (localaddr6 != NULL || localaddr4 == NULL) { 495262706Serwin result = getudpdispatch(AF_INET6, dispatchmgr, socketmgr, 496262706Serwin taskmgr, ISC_TRUE, 497262706Serwin &dispatchv6, localaddr6); 498262706Serwin if (result == ISC_R_SUCCESS) 499262706Serwin client->dispatchv6 = dispatchv6; 500262706Serwin } 501224090Sdougb 502224090Sdougb /* We need at least one of the dispatchers */ 503224090Sdougb if (dispatchv4 == NULL && dispatchv6 == NULL) { 504224090Sdougb INSIST(result != ISC_R_SUCCESS); 505224090Sdougb goto cleanup; 506224090Sdougb } 507224090Sdougb 508224090Sdougb /* Create the default view for class IN */ 509224090Sdougb result = dns_client_createview(mctx, dns_rdataclass_in, options, 510224090Sdougb taskmgr, 31, socketmgr, timermgr, 511224090Sdougb dispatchmgr, dispatchv4, dispatchv6, 512224090Sdougb &view); 513224090Sdougb if (result != ISC_R_SUCCESS) 514224090Sdougb goto cleanup; 515224090Sdougb ISC_LIST_INIT(client->viewlist); 516224090Sdougb ISC_LIST_APPEND(client->viewlist, view, link); 517224090Sdougb 518224090Sdougb dns_view_freeze(view); /* too early? */ 519224090Sdougb 520224090Sdougb ISC_LIST_INIT(client->resctxs); 521224090Sdougb ISC_LIST_INIT(client->reqctxs); 522224090Sdougb ISC_LIST_INIT(client->updatectxs); 523224090Sdougb 524224090Sdougb client->mctx = NULL; 525224090Sdougb isc_mem_attach(mctx, &client->mctx); 526224090Sdougb 527224090Sdougb client->update_timeout = DEF_UPDATE_TIMEOUT; 528224090Sdougb client->update_udptimeout = DEF_UPDATE_UDPTIMEOUT; 529224090Sdougb client->update_udpretries = DEF_UPDATE_UDPRETRIES; 530224090Sdougb client->find_timeout = DEF_FIND_TIMEOUT; 531224090Sdougb client->find_udpretries = DEF_FIND_UDPRETRIES; 532254402Serwin client->attributes = 0; 533224090Sdougb 534224090Sdougb client->references = 1; 535224090Sdougb client->magic = DNS_CLIENT_MAGIC; 536224090Sdougb 537224090Sdougb *clientp = client; 538224090Sdougb 539224090Sdougb return (ISC_R_SUCCESS); 540224090Sdougb 541224090Sdougb cleanup: 542224090Sdougb if (dispatchv4 != NULL) 543224090Sdougb dns_dispatch_detach(&dispatchv4); 544224090Sdougb if (dispatchv6 != NULL) 545224090Sdougb dns_dispatch_detach(&dispatchv6); 546224090Sdougb if (dispatchmgr != NULL) 547224090Sdougb dns_dispatchmgr_destroy(&dispatchmgr); 548224090Sdougb if (client->task != NULL) 549224090Sdougb isc_task_detach(&client->task); 550224090Sdougb isc_mem_put(mctx, client, sizeof(*client)); 551224090Sdougb 552224090Sdougb return (result); 553224090Sdougb} 554224090Sdougb 555224090Sdougbstatic void 556224090Sdougbdestroyclient(dns_client_t **clientp) { 557224090Sdougb dns_client_t *client = *clientp; 558224090Sdougb dns_view_t *view; 559224090Sdougb 560224090Sdougb while ((view = ISC_LIST_HEAD(client->viewlist)) != NULL) { 561224090Sdougb ISC_LIST_UNLINK(client->viewlist, view, link); 562224090Sdougb dns_view_detach(&view); 563224090Sdougb } 564224090Sdougb 565224090Sdougb if (client->dispatchv4 != NULL) 566224090Sdougb dns_dispatch_detach(&client->dispatchv4); 567224090Sdougb if (client->dispatchv6 != NULL) 568224090Sdougb dns_dispatch_detach(&client->dispatchv6); 569224090Sdougb 570224090Sdougb dns_dispatchmgr_destroy(&client->dispatchmgr); 571224090Sdougb 572224090Sdougb isc_task_detach(&client->task); 573224090Sdougb 574224090Sdougb /* 575224090Sdougb * If the client has created its own running environments, 576224090Sdougb * destroy them. 577224090Sdougb */ 578224090Sdougb if ((client->attributes & DNS_CLIENTATTR_OWNCTX) != 0) { 579224090Sdougb isc_taskmgr_destroy(&client->taskmgr); 580224090Sdougb isc_timermgr_destroy(&client->timermgr); 581224090Sdougb isc_socketmgr_destroy(&client->socketmgr); 582224090Sdougb 583224090Sdougb isc_app_ctxfinish(client->actx); 584224090Sdougb isc_appctx_destroy(&client->actx); 585224090Sdougb } 586224090Sdougb 587224090Sdougb DESTROYLOCK(&client->lock); 588224090Sdougb client->magic = 0; 589224090Sdougb 590224090Sdougb isc_mem_putanddetach(&client->mctx, client, sizeof(*client)); 591224090Sdougb 592224090Sdougb *clientp = NULL; 593224090Sdougb} 594224090Sdougb 595224090Sdougbvoid 596224090Sdougbdns_client_destroy(dns_client_t **clientp) { 597224090Sdougb dns_client_t *client; 598224090Sdougb isc_boolean_t destroyok = ISC_FALSE; 599224090Sdougb 600224090Sdougb REQUIRE(clientp != NULL); 601224090Sdougb client = *clientp; 602224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 603224090Sdougb 604224090Sdougb LOCK(&client->lock); 605224090Sdougb client->references--; 606224090Sdougb if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) && 607224090Sdougb ISC_LIST_EMPTY(client->reqctxs) && 608224090Sdougb ISC_LIST_EMPTY(client->updatectxs)) { 609224090Sdougb destroyok = ISC_TRUE; 610224090Sdougb } 611224090Sdougb UNLOCK(&client->lock); 612224090Sdougb 613224090Sdougb if (destroyok) 614224090Sdougb destroyclient(&client); 615224090Sdougb 616224090Sdougb *clientp = NULL; 617224090Sdougb} 618224090Sdougb 619224090Sdougbisc_result_t 620224090Sdougbdns_client_setservers(dns_client_t *client, dns_rdataclass_t rdclass, 621224090Sdougb dns_name_t *namespace, isc_sockaddrlist_t *addrs) 622224090Sdougb{ 623224090Sdougb isc_result_t result; 624224090Sdougb dns_view_t *view = NULL; 625224090Sdougb 626224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 627224090Sdougb REQUIRE(addrs != NULL); 628224090Sdougb 629224090Sdougb if (namespace == NULL) 630224090Sdougb namespace = dns_rootname; 631224090Sdougb 632224090Sdougb LOCK(&client->lock); 633224090Sdougb result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME, 634224090Sdougb rdclass, &view); 635224090Sdougb if (result != ISC_R_SUCCESS) { 636224090Sdougb UNLOCK(&client->lock); 637224090Sdougb return (result); 638224090Sdougb } 639224090Sdougb UNLOCK(&client->lock); 640224090Sdougb 641224090Sdougb result = dns_fwdtable_add(view->fwdtable, namespace, addrs, 642224090Sdougb dns_fwdpolicy_only); 643224090Sdougb 644224090Sdougb dns_view_detach(&view); 645224090Sdougb 646224090Sdougb return (result); 647224090Sdougb} 648224090Sdougb 649224090Sdougbisc_result_t 650224090Sdougbdns_client_clearservers(dns_client_t *client, dns_rdataclass_t rdclass, 651224090Sdougb dns_name_t *namespace) 652224090Sdougb{ 653224090Sdougb isc_result_t result; 654224090Sdougb dns_view_t *view = NULL; 655224090Sdougb 656224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 657224090Sdougb 658224090Sdougb if (namespace == NULL) 659224090Sdougb namespace = dns_rootname; 660224090Sdougb 661224090Sdougb LOCK(&client->lock); 662224090Sdougb result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME, 663224090Sdougb rdclass, &view); 664224090Sdougb if (result != ISC_R_SUCCESS) { 665224090Sdougb UNLOCK(&client->lock); 666224090Sdougb return (result); 667224090Sdougb } 668224090Sdougb UNLOCK(&client->lock); 669224090Sdougb 670224090Sdougb result = dns_fwdtable_delete(view->fwdtable, namespace); 671224090Sdougb 672224090Sdougb dns_view_detach(&view); 673224090Sdougb 674224090Sdougb return (result); 675224090Sdougb} 676224090Sdougb 677224090Sdougbstatic isc_result_t 678224090Sdougbgetrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) { 679224090Sdougb dns_rdataset_t *rdataset; 680224090Sdougb 681224090Sdougb REQUIRE(mctx != NULL); 682224090Sdougb REQUIRE(rdatasetp != NULL && *rdatasetp == NULL); 683224090Sdougb 684224090Sdougb rdataset = isc_mem_get(mctx, sizeof(*rdataset)); 685224090Sdougb if (rdataset == NULL) 686224090Sdougb return (ISC_R_NOMEMORY); 687224090Sdougb 688224090Sdougb dns_rdataset_init(rdataset); 689224090Sdougb 690224090Sdougb *rdatasetp = rdataset; 691224090Sdougb 692224090Sdougb return (ISC_R_SUCCESS); 693224090Sdougb} 694224090Sdougb 695224090Sdougbstatic void 696224090Sdougbputrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) { 697224090Sdougb dns_rdataset_t *rdataset; 698224090Sdougb 699224090Sdougb REQUIRE(rdatasetp != NULL); 700224090Sdougb rdataset = *rdatasetp; 701224090Sdougb REQUIRE(rdataset != NULL); 702224090Sdougb 703224090Sdougb if (dns_rdataset_isassociated(rdataset)) 704224090Sdougb dns_rdataset_disassociate(rdataset); 705224090Sdougb 706224090Sdougb isc_mem_put(mctx, rdataset, sizeof(*rdataset)); 707224090Sdougb 708224090Sdougb *rdatasetp = NULL; 709224090Sdougb} 710224090Sdougb 711224090Sdougbstatic void 712224090Sdougbfetch_done(isc_task_t *task, isc_event_t *event) { 713224090Sdougb resctx_t *rctx = event->ev_arg; 714224090Sdougb dns_fetchevent_t *fevent; 715224090Sdougb 716224090Sdougb REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); 717224090Sdougb REQUIRE(RCTX_VALID(rctx)); 718224090Sdougb REQUIRE(rctx->task == task); 719224090Sdougb fevent = (dns_fetchevent_t *)event; 720224090Sdougb 721224090Sdougb client_resfind(rctx, fevent); 722224090Sdougb} 723224090Sdougb 724224090Sdougbstatic inline isc_result_t 725224090Sdougbstart_fetch(resctx_t *rctx) { 726224090Sdougb isc_result_t result; 727224090Sdougb 728224090Sdougb /* 729224090Sdougb * The caller must be holding the rctx's lock. 730224090Sdougb */ 731224090Sdougb 732224090Sdougb REQUIRE(rctx->fetch == NULL); 733224090Sdougb 734224090Sdougb result = dns_resolver_createfetch(rctx->view->resolver, 735224090Sdougb dns_fixedname_name(&rctx->name), 736224090Sdougb rctx->type, 737224090Sdougb NULL, NULL, NULL, 0, 738224090Sdougb rctx->task, fetch_done, rctx, 739224090Sdougb rctx->rdataset, 740224090Sdougb rctx->sigrdataset, 741224090Sdougb &rctx->fetch); 742224090Sdougb 743224090Sdougb return (result); 744224090Sdougb} 745224090Sdougb 746224090Sdougbstatic isc_result_t 747224090Sdougbview_find(resctx_t *rctx, dns_db_t **dbp, dns_dbnode_t **nodep, 748224090Sdougb dns_name_t *foundname) 749224090Sdougb{ 750224090Sdougb isc_result_t result; 751224090Sdougb dns_name_t *name = dns_fixedname_name(&rctx->name); 752224090Sdougb dns_rdatatype_t type; 753224090Sdougb 754224090Sdougb if (rctx->type == dns_rdatatype_rrsig) 755224090Sdougb type = dns_rdatatype_any; 756224090Sdougb else 757224090Sdougb type = rctx->type; 758224090Sdougb 759224090Sdougb result = dns_view_find(rctx->view, name, type, 0, 0, ISC_FALSE, 760224090Sdougb dbp, nodep, foundname, rctx->rdataset, 761224090Sdougb rctx->sigrdataset); 762224090Sdougb 763224090Sdougb return (result); 764224090Sdougb} 765224090Sdougb 766224090Sdougbstatic void 767224090Sdougbclient_resfind(resctx_t *rctx, dns_fetchevent_t *event) { 768224090Sdougb isc_mem_t *mctx; 769225361Sdougb isc_result_t tresult, result = ISC_R_SUCCESS; 770224090Sdougb isc_result_t vresult = ISC_R_SUCCESS; 771224090Sdougb isc_boolean_t want_restart; 772224090Sdougb isc_boolean_t send_event = ISC_FALSE; 773224090Sdougb dns_name_t *name, *prefix; 774224090Sdougb dns_fixedname_t foundname, fixed; 775224090Sdougb dns_rdataset_t *trdataset; 776224090Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 777224090Sdougb unsigned int nlabels; 778224090Sdougb int order; 779224090Sdougb dns_namereln_t namereln; 780224090Sdougb dns_rdata_cname_t cname; 781224090Sdougb dns_rdata_dname_t dname; 782224090Sdougb 783224090Sdougb REQUIRE(RCTX_VALID(rctx)); 784224090Sdougb 785224090Sdougb LOCK(&rctx->lock); 786224090Sdougb 787224090Sdougb mctx = rctx->view->mctx; 788224090Sdougb 789224090Sdougb name = dns_fixedname_name(&rctx->name); 790224090Sdougb 791224090Sdougb do { 792224090Sdougb dns_name_t *fname = NULL; 793224090Sdougb dns_name_t *ansname = NULL; 794224090Sdougb dns_db_t *db = NULL; 795224090Sdougb dns_dbnode_t *node = NULL; 796224090Sdougb 797224090Sdougb rctx->restarts++; 798224090Sdougb want_restart = ISC_FALSE; 799224090Sdougb 800224090Sdougb if (event == NULL && !rctx->canceled) { 801224090Sdougb dns_fixedname_init(&foundname); 802224090Sdougb fname = dns_fixedname_name(&foundname); 803224090Sdougb INSIST(!dns_rdataset_isassociated(rctx->rdataset)); 804224090Sdougb INSIST(rctx->sigrdataset == NULL || 805224090Sdougb !dns_rdataset_isassociated(rctx->sigrdataset)); 806224090Sdougb result = view_find(rctx, &db, &node, fname); 807224090Sdougb if (result == ISC_R_NOTFOUND) { 808224090Sdougb /* 809224090Sdougb * We don't know anything about the name. 810224090Sdougb * Launch a fetch. 811224090Sdougb */ 812224090Sdougb if (node != NULL) { 813224090Sdougb INSIST(db != NULL); 814224090Sdougb dns_db_detachnode(db, &node); 815224090Sdougb } 816224090Sdougb if (db != NULL) 817224090Sdougb dns_db_detach(&db); 818224090Sdougb result = start_fetch(rctx); 819224090Sdougb if (result != ISC_R_SUCCESS) { 820224090Sdougb putrdataset(mctx, &rctx->rdataset); 821224090Sdougb if (rctx->sigrdataset != NULL) 822224090Sdougb putrdataset(mctx, 823224090Sdougb &rctx->sigrdataset); 824224090Sdougb send_event = ISC_TRUE; 825224090Sdougb } 826224090Sdougb goto done; 827224090Sdougb } 828224090Sdougb } else { 829225361Sdougb INSIST(event != NULL); 830224090Sdougb INSIST(event->fetch == rctx->fetch); 831224090Sdougb dns_resolver_destroyfetch(&rctx->fetch); 832224090Sdougb db = event->db; 833224090Sdougb node = event->node; 834224090Sdougb result = event->result; 835224090Sdougb vresult = event->vresult; 836224090Sdougb fname = dns_fixedname_name(&event->foundname); 837224090Sdougb INSIST(event->rdataset == rctx->rdataset); 838224090Sdougb INSIST(event->sigrdataset == rctx->sigrdataset); 839224090Sdougb } 840224090Sdougb 841224090Sdougb /* 842224090Sdougb * If we've been canceled, forget about the result. 843224090Sdougb */ 844224090Sdougb if (rctx->canceled) 845224090Sdougb result = ISC_R_CANCELED; 846224090Sdougb else { 847224090Sdougb /* 848224090Sdougb * Otherwise, get some resource for copying the 849224090Sdougb * result. 850224090Sdougb */ 851224090Sdougb ansname = isc_mem_get(mctx, sizeof(*ansname)); 852224090Sdougb if (ansname == NULL) 853224090Sdougb tresult = ISC_R_NOMEMORY; 854224090Sdougb else { 855224090Sdougb dns_name_t *aname; 856224090Sdougb 857224090Sdougb aname = dns_fixedname_name(&rctx->name); 858224090Sdougb dns_name_init(ansname, NULL); 859224090Sdougb tresult = dns_name_dup(aname, mctx, ansname); 860224090Sdougb if (tresult != ISC_R_SUCCESS) 861224090Sdougb isc_mem_put(mctx, ansname, 862224090Sdougb sizeof(*ansname)); 863224090Sdougb } 864224090Sdougb if (tresult != ISC_R_SUCCESS) 865224090Sdougb result = tresult; 866224090Sdougb } 867224090Sdougb 868224090Sdougb switch (result) { 869224090Sdougb case ISC_R_SUCCESS: 870224090Sdougb send_event = ISC_TRUE; 871224090Sdougb /* 872224090Sdougb * This case is handled in the main line below. 873224090Sdougb */ 874224090Sdougb break; 875224090Sdougb case DNS_R_CNAME: 876224090Sdougb /* 877224090Sdougb * Add the CNAME to the answer list. 878224090Sdougb */ 879224090Sdougb trdataset = rctx->rdataset; 880224090Sdougb ISC_LIST_APPEND(ansname->list, rctx->rdataset, link); 881224090Sdougb rctx->rdataset = NULL; 882224090Sdougb if (rctx->sigrdataset != NULL) { 883224090Sdougb ISC_LIST_APPEND(ansname->list, 884224090Sdougb rctx->sigrdataset, link); 885224090Sdougb rctx->sigrdataset = NULL; 886224090Sdougb } 887224090Sdougb ISC_LIST_APPEND(rctx->namelist, ansname, link); 888224090Sdougb ansname = NULL; 889224090Sdougb 890224090Sdougb /* 891224090Sdougb * Copy the CNAME's target into the lookup's 892224090Sdougb * query name and start over. 893224090Sdougb */ 894224090Sdougb tresult = dns_rdataset_first(trdataset); 895224090Sdougb if (tresult != ISC_R_SUCCESS) 896224090Sdougb goto done; 897224090Sdougb dns_rdataset_current(trdataset, &rdata); 898224090Sdougb tresult = dns_rdata_tostruct(&rdata, &cname, NULL); 899224090Sdougb dns_rdata_reset(&rdata); 900224090Sdougb if (tresult != ISC_R_SUCCESS) 901224090Sdougb goto done; 902224090Sdougb tresult = dns_name_copy(&cname.cname, name, NULL); 903224090Sdougb dns_rdata_freestruct(&cname); 904224090Sdougb if (tresult == ISC_R_SUCCESS) 905224090Sdougb want_restart = ISC_TRUE; 906224090Sdougb else 907224090Sdougb result = tresult; 908224090Sdougb goto done; 909224090Sdougb case DNS_R_DNAME: 910224090Sdougb /* 911224090Sdougb * Add the DNAME to the answer list. 912224090Sdougb */ 913224090Sdougb trdataset = rctx->rdataset; 914224090Sdougb ISC_LIST_APPEND(ansname->list, rctx->rdataset, link); 915224090Sdougb rctx->rdataset = NULL; 916224090Sdougb if (rctx->sigrdataset != NULL) { 917224090Sdougb ISC_LIST_APPEND(ansname->list, 918224090Sdougb rctx->sigrdataset, link); 919224090Sdougb rctx->sigrdataset = NULL; 920224090Sdougb } 921224090Sdougb ISC_LIST_APPEND(rctx->namelist, ansname, link); 922224090Sdougb ansname = NULL; 923224090Sdougb 924224090Sdougb namereln = dns_name_fullcompare(name, fname, &order, 925224090Sdougb &nlabels); 926224090Sdougb INSIST(namereln == dns_namereln_subdomain); 927224090Sdougb /* 928224090Sdougb * Get the target name of the DNAME. 929224090Sdougb */ 930224090Sdougb tresult = dns_rdataset_first(trdataset); 931224090Sdougb if (tresult != ISC_R_SUCCESS) { 932224090Sdougb result = tresult; 933224090Sdougb goto done; 934224090Sdougb } 935224090Sdougb dns_rdataset_current(trdataset, &rdata); 936224090Sdougb tresult = dns_rdata_tostruct(&rdata, &dname, NULL); 937224090Sdougb dns_rdata_reset(&rdata); 938224090Sdougb if (tresult != ISC_R_SUCCESS) { 939224090Sdougb result = tresult; 940224090Sdougb goto done; 941224090Sdougb } 942224090Sdougb /* 943224090Sdougb * Construct the new query name and start over. 944224090Sdougb */ 945224090Sdougb dns_fixedname_init(&fixed); 946224090Sdougb prefix = dns_fixedname_name(&fixed); 947224090Sdougb dns_name_split(name, nlabels, prefix, NULL); 948224090Sdougb tresult = dns_name_concatenate(prefix, &dname.dname, 949224090Sdougb name, NULL); 950224090Sdougb dns_rdata_freestruct(&dname); 951224090Sdougb if (tresult == ISC_R_SUCCESS) 952224090Sdougb want_restart = ISC_TRUE; 953224090Sdougb else 954224090Sdougb result = tresult; 955224090Sdougb goto done; 956224090Sdougb case DNS_R_NCACHENXDOMAIN: 957224090Sdougb case DNS_R_NCACHENXRRSET: 958224090Sdougb ISC_LIST_APPEND(ansname->list, rctx->rdataset, link); 959224090Sdougb ISC_LIST_APPEND(rctx->namelist, ansname, link); 960224090Sdougb ansname = NULL; 961224090Sdougb rctx->rdataset = NULL; 962224090Sdougb /* What about sigrdataset? */ 963224090Sdougb if (rctx->sigrdataset != NULL) 964224090Sdougb putrdataset(mctx, &rctx->sigrdataset); 965224090Sdougb send_event = ISC_TRUE; 966224090Sdougb goto done; 967224090Sdougb default: 968224090Sdougb if (rctx->rdataset != NULL) 969224090Sdougb putrdataset(mctx, &rctx->rdataset); 970224090Sdougb if (rctx->sigrdataset != NULL) 971224090Sdougb putrdataset(mctx, &rctx->sigrdataset); 972224090Sdougb send_event = ISC_TRUE; 973224090Sdougb goto done; 974224090Sdougb } 975224090Sdougb 976224090Sdougb if (rctx->type == dns_rdatatype_any) { 977224090Sdougb int n = 0; 978224090Sdougb dns_rdatasetiter_t *rdsiter = NULL; 979224090Sdougb 980224090Sdougb tresult = dns_db_allrdatasets(db, node, NULL, 0, 981224090Sdougb &rdsiter); 982224090Sdougb if (tresult != ISC_R_SUCCESS) { 983224090Sdougb result = tresult; 984224090Sdougb goto done; 985224090Sdougb } 986224090Sdougb 987224090Sdougb tresult = dns_rdatasetiter_first(rdsiter); 988224090Sdougb while (tresult == ISC_R_SUCCESS) { 989224090Sdougb dns_rdatasetiter_current(rdsiter, 990224090Sdougb rctx->rdataset); 991224090Sdougb if (rctx->rdataset->type != 0) { 992224090Sdougb ISC_LIST_APPEND(ansname->list, 993224090Sdougb rctx->rdataset, 994224090Sdougb link); 995224090Sdougb n++; 996224090Sdougb rctx->rdataset = NULL; 997224090Sdougb } else { 998224090Sdougb /* 999224090Sdougb * We're not interested in this 1000224090Sdougb * rdataset. 1001224090Sdougb */ 1002224090Sdougb dns_rdataset_disassociate( 1003224090Sdougb rctx->rdataset); 1004224090Sdougb } 1005224090Sdougb tresult = dns_rdatasetiter_next(rdsiter); 1006224090Sdougb 1007224090Sdougb if (tresult == ISC_R_SUCCESS && 1008224090Sdougb rctx->rdataset == NULL) { 1009224090Sdougb tresult = getrdataset(mctx, 1010224090Sdougb &rctx->rdataset); 1011224090Sdougb if (tresult != ISC_R_SUCCESS) { 1012224090Sdougb result = tresult; 1013225361Sdougb POST(result); 1014224090Sdougb break; 1015224090Sdougb } 1016224090Sdougb } 1017224090Sdougb } 1018224090Sdougb if (n == 0) { 1019224090Sdougb /* 1020224090Sdougb * We didn't match any rdatasets (which means 1021224090Sdougb * something went wrong in this 1022224090Sdougb * implementation). 1023224090Sdougb */ 1024224090Sdougb result = DNS_R_SERVFAIL; /* better code? */ 1025225361Sdougb POST(result); 1026224090Sdougb } else { 1027224090Sdougb ISC_LIST_APPEND(rctx->namelist, ansname, link); 1028224090Sdougb ansname = NULL; 1029224090Sdougb } 1030224090Sdougb dns_rdatasetiter_destroy(&rdsiter); 1031224090Sdougb if (tresult != ISC_R_NOMORE) 1032224090Sdougb result = DNS_R_SERVFAIL; /* ditto */ 1033224090Sdougb else 1034224090Sdougb result = ISC_R_SUCCESS; 1035224090Sdougb goto done; 1036224090Sdougb } else { 1037224090Sdougb /* 1038224090Sdougb * This is the "normal" case -- an ordinary question 1039224090Sdougb * to which we've got the answer. 1040224090Sdougb */ 1041224090Sdougb ISC_LIST_APPEND(ansname->list, rctx->rdataset, link); 1042224090Sdougb rctx->rdataset = NULL; 1043224090Sdougb if (rctx->sigrdataset != NULL) { 1044224090Sdougb ISC_LIST_APPEND(ansname->list, 1045224090Sdougb rctx->sigrdataset, link); 1046224090Sdougb rctx->sigrdataset = NULL; 1047224090Sdougb } 1048224090Sdougb ISC_LIST_APPEND(rctx->namelist, ansname, link); 1049224090Sdougb ansname = NULL; 1050224090Sdougb } 1051224090Sdougb 1052224090Sdougb done: 1053224090Sdougb /* 1054224090Sdougb * Free temporary resources 1055224090Sdougb */ 1056224090Sdougb if (ansname != NULL) { 1057224090Sdougb dns_rdataset_t *rdataset; 1058224090Sdougb 1059224090Sdougb while ((rdataset = ISC_LIST_HEAD(ansname->list)) 1060224090Sdougb != NULL) { 1061224090Sdougb ISC_LIST_UNLINK(ansname->list, rdataset, link); 1062224090Sdougb putrdataset(mctx, &rdataset); 1063224090Sdougb } 1064224090Sdougb dns_name_free(ansname, mctx); 1065224090Sdougb isc_mem_put(mctx, ansname, sizeof(*ansname)); 1066224090Sdougb } 1067224090Sdougb 1068224090Sdougb if (node != NULL) 1069224090Sdougb dns_db_detachnode(db, &node); 1070224090Sdougb if (db != NULL) 1071224090Sdougb dns_db_detach(&db); 1072224090Sdougb if (event != NULL) 1073224090Sdougb isc_event_free(ISC_EVENT_PTR(&event)); 1074224090Sdougb 1075224090Sdougb /* 1076224090Sdougb * Limit the number of restarts. 1077224090Sdougb */ 1078224090Sdougb if (want_restart && rctx->restarts == MAX_RESTARTS) { 1079224090Sdougb want_restart = ISC_FALSE; 1080224090Sdougb result = ISC_R_QUOTA; 1081224090Sdougb send_event = ISC_TRUE; 1082224090Sdougb } 1083224090Sdougb 1084224090Sdougb /* 1085224090Sdougb * Prepare further find with new resources 1086224090Sdougb */ 1087224090Sdougb if (want_restart) { 1088224090Sdougb INSIST(rctx->rdataset == NULL && 1089224090Sdougb rctx->sigrdataset == NULL); 1090224090Sdougb 1091224090Sdougb result = getrdataset(mctx, &rctx->rdataset); 1092224090Sdougb if (result == ISC_R_SUCCESS && rctx->want_dnssec) { 1093224090Sdougb result = getrdataset(mctx, &rctx->sigrdataset); 1094224090Sdougb if (result != ISC_R_SUCCESS) { 1095224090Sdougb putrdataset(mctx, &rctx->rdataset); 1096224090Sdougb } 1097224090Sdougb } 1098224090Sdougb 1099224090Sdougb if (result != ISC_R_SUCCESS) { 1100224090Sdougb want_restart = ISC_FALSE; 1101224090Sdougb send_event = ISC_TRUE; 1102224090Sdougb } 1103224090Sdougb } 1104224090Sdougb } while (want_restart); 1105224090Sdougb 1106224090Sdougb if (send_event) { 1107224090Sdougb isc_task_t *task; 1108224090Sdougb 1109224090Sdougb while ((name = ISC_LIST_HEAD(rctx->namelist)) != NULL) { 1110224090Sdougb ISC_LIST_UNLINK(rctx->namelist, name, link); 1111224090Sdougb ISC_LIST_APPEND(rctx->event->answerlist, name, link); 1112224090Sdougb } 1113224090Sdougb 1114224090Sdougb rctx->event->result = result; 1115224090Sdougb rctx->event->vresult = vresult; 1116224090Sdougb task = rctx->event->ev_sender; 1117224090Sdougb rctx->event->ev_sender = rctx; 1118224090Sdougb isc_task_sendanddetach(&task, ISC_EVENT_PTR(&rctx->event)); 1119224090Sdougb } 1120224090Sdougb 1121224090Sdougb UNLOCK(&rctx->lock); 1122224090Sdougb} 1123224090Sdougb 1124262706Serwin 1125224090Sdougbstatic void 1126262706Serwinsuspend(isc_task_t *task, isc_event_t *event) { 1127262706Serwin isc_appctx_t *actx = event->ev_arg; 1128262706Serwin 1129262706Serwin UNUSED(task); 1130262706Serwin 1131262706Serwin isc_app_ctxsuspend(actx); 1132262706Serwin isc_event_free(&event); 1133262706Serwin} 1134262706Serwin 1135262706Serwinstatic void 1136224090Sdougbresolve_done(isc_task_t *task, isc_event_t *event) { 1137224090Sdougb resarg_t *resarg = event->ev_arg; 1138224090Sdougb dns_clientresevent_t *rev = (dns_clientresevent_t *)event; 1139224090Sdougb dns_name_t *name; 1140262706Serwin isc_result_t result; 1141224090Sdougb 1142224090Sdougb UNUSED(task); 1143224090Sdougb 1144224090Sdougb LOCK(&resarg->lock); 1145224090Sdougb 1146224090Sdougb resarg->result = rev->result; 1147224090Sdougb resarg->vresult = rev->vresult; 1148224090Sdougb while ((name = ISC_LIST_HEAD(rev->answerlist)) != NULL) { 1149224090Sdougb ISC_LIST_UNLINK(rev->answerlist, name, link); 1150224090Sdougb ISC_LIST_APPEND(*resarg->namelist, name, link); 1151224090Sdougb } 1152224090Sdougb 1153224090Sdougb dns_client_destroyrestrans(&resarg->trans); 1154224090Sdougb isc_event_free(&event); 1155224090Sdougb 1156224090Sdougb if (!resarg->canceled) { 1157224090Sdougb UNLOCK(&resarg->lock); 1158224090Sdougb 1159262706Serwin /* 1160262706Serwin * We may or may not be running. isc__appctx_onrun will 1161262706Serwin * fail if we are currently running otherwise we post a 1162262706Serwin * action to call isc_app_ctxsuspend when we do start 1163262706Serwin * running. 1164262706Serwin */ 1165262706Serwin result = isc_app_ctxonrun(resarg->actx, resarg->client->mctx, 1166262706Serwin task, suspend, resarg->actx); 1167262706Serwin if (result == ISC_R_ALREADYRUNNING) 1168262706Serwin isc_app_ctxsuspend(resarg->actx); 1169224090Sdougb } else { 1170224090Sdougb /* 1171224090Sdougb * We have already exited from the loop (due to some 1172224090Sdougb * unexpected event). Just clean the arg up. 1173224090Sdougb */ 1174224090Sdougb UNLOCK(&resarg->lock); 1175224090Sdougb DESTROYLOCK(&resarg->lock); 1176224090Sdougb isc_mem_put(resarg->client->mctx, resarg, sizeof(*resarg)); 1177224090Sdougb } 1178224090Sdougb} 1179224090Sdougb 1180224090Sdougbisc_result_t 1181224090Sdougbdns_client_resolve(dns_client_t *client, dns_name_t *name, 1182224090Sdougb dns_rdataclass_t rdclass, dns_rdatatype_t type, 1183224090Sdougb unsigned int options, dns_namelist_t *namelist) 1184224090Sdougb{ 1185224090Sdougb isc_result_t result; 1186224090Sdougb isc_appctx_t *actx; 1187224090Sdougb resarg_t *resarg; 1188224090Sdougb 1189224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 1190224090Sdougb REQUIRE(namelist != NULL && ISC_LIST_EMPTY(*namelist)); 1191224090Sdougb 1192224090Sdougb if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 && 1193224090Sdougb (options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) { 1194224090Sdougb /* 1195224090Sdougb * If the client is run under application's control, we need 1196224090Sdougb * to create a new running (sub)environment for this 1197224090Sdougb * particular resolution. 1198224090Sdougb */ 1199224090Sdougb return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */ 1200224090Sdougb } else 1201224090Sdougb actx = client->actx; 1202224090Sdougb 1203224090Sdougb resarg = isc_mem_get(client->mctx, sizeof(*resarg)); 1204224090Sdougb if (resarg == NULL) 1205224090Sdougb return (ISC_R_NOMEMORY); 1206224090Sdougb 1207224090Sdougb result = isc_mutex_init(&resarg->lock); 1208224090Sdougb if (result != ISC_R_SUCCESS) { 1209224090Sdougb isc_mem_put(client->mctx, resarg, sizeof(*resarg)); 1210224090Sdougb return (result); 1211224090Sdougb } 1212224090Sdougb 1213224090Sdougb resarg->actx = actx; 1214224090Sdougb resarg->client = client; 1215224090Sdougb resarg->result = DNS_R_SERVFAIL; 1216224090Sdougb resarg->namelist = namelist; 1217224090Sdougb resarg->trans = NULL; 1218224090Sdougb resarg->canceled = ISC_FALSE; 1219224090Sdougb result = dns_client_startresolve(client, name, rdclass, type, options, 1220224090Sdougb client->task, resolve_done, resarg, 1221224090Sdougb &resarg->trans); 1222224090Sdougb if (result != ISC_R_SUCCESS) { 1223224090Sdougb DESTROYLOCK(&resarg->lock); 1224224090Sdougb isc_mem_put(client->mctx, resarg, sizeof(*resarg)); 1225224090Sdougb return (result); 1226224090Sdougb } 1227224090Sdougb 1228224090Sdougb /* 1229224090Sdougb * Start internal event loop. It blocks until the entire process 1230224090Sdougb * is completed. 1231224090Sdougb */ 1232224090Sdougb result = isc_app_ctxrun(actx); 1233224090Sdougb 1234224090Sdougb LOCK(&resarg->lock); 1235224090Sdougb if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND) 1236224090Sdougb result = resarg->result; 1237224090Sdougb if (result != ISC_R_SUCCESS && resarg->vresult != ISC_R_SUCCESS) { 1238224090Sdougb /* 1239224090Sdougb * If this lookup failed due to some error in DNSSEC 1240224090Sdougb * validation, return the validation error code. 1241224090Sdougb * XXX: or should we pass the validation result separately? 1242224090Sdougb */ 1243224090Sdougb result = resarg->vresult; 1244224090Sdougb } 1245224090Sdougb if (resarg->trans != NULL) { 1246224090Sdougb /* 1247224090Sdougb * Unusual termination (perhaps due to signal). We need some 1248224090Sdougb * tricky cleanup process. 1249224090Sdougb */ 1250224090Sdougb resarg->canceled = ISC_TRUE; 1251224090Sdougb dns_client_cancelresolve(resarg->trans); 1252224090Sdougb 1253224090Sdougb UNLOCK(&resarg->lock); 1254224090Sdougb 1255224090Sdougb /* resarg will be freed in the event handler. */ 1256224090Sdougb } else { 1257224090Sdougb UNLOCK(&resarg->lock); 1258224090Sdougb 1259224090Sdougb DESTROYLOCK(&resarg->lock); 1260224090Sdougb isc_mem_put(client->mctx, resarg, sizeof(*resarg)); 1261224090Sdougb } 1262224090Sdougb 1263224090Sdougb return (result); 1264224090Sdougb} 1265224090Sdougb 1266224090Sdougbisc_result_t 1267224090Sdougbdns_client_startresolve(dns_client_t *client, dns_name_t *name, 1268224090Sdougb dns_rdataclass_t rdclass, dns_rdatatype_t type, 1269224090Sdougb unsigned int options, isc_task_t *task, 1270224090Sdougb isc_taskaction_t action, void *arg, 1271224090Sdougb dns_clientrestrans_t **transp) 1272224090Sdougb{ 1273224090Sdougb dns_view_t *view = NULL; 1274224090Sdougb dns_clientresevent_t *event = NULL; 1275224090Sdougb resctx_t *rctx = NULL; 1276224090Sdougb isc_task_t *clone = NULL; 1277224090Sdougb isc_mem_t *mctx; 1278224090Sdougb isc_result_t result; 1279224090Sdougb dns_rdataset_t *rdataset, *sigrdataset; 1280224090Sdougb isc_boolean_t want_dnssec; 1281224090Sdougb 1282224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 1283224090Sdougb REQUIRE(transp != NULL && *transp == NULL); 1284224090Sdougb 1285224090Sdougb LOCK(&client->lock); 1286224090Sdougb result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME, 1287224090Sdougb rdclass, &view); 1288224090Sdougb UNLOCK(&client->lock); 1289224090Sdougb if (result != ISC_R_SUCCESS) 1290224090Sdougb return (result); 1291224090Sdougb 1292224090Sdougb mctx = client->mctx; 1293224090Sdougb rdataset = NULL; 1294224090Sdougb sigrdataset = NULL; 1295224090Sdougb want_dnssec = ISC_TF((options & DNS_CLIENTRESOPT_NODNSSEC) == 0); 1296224090Sdougb 1297224090Sdougb /* 1298224090Sdougb * Prepare some intermediate resources 1299224090Sdougb */ 1300224090Sdougb clone = NULL; 1301224090Sdougb isc_task_attach(task, &clone); 1302224090Sdougb event = (dns_clientresevent_t *) 1303224090Sdougb isc_event_allocate(mctx, clone, DNS_EVENT_CLIENTRESDONE, 1304224090Sdougb action, arg, sizeof(*event)); 1305224090Sdougb if (event == NULL) { 1306224090Sdougb result = ISC_R_NOMEMORY; 1307224090Sdougb goto cleanup; 1308224090Sdougb } 1309224090Sdougb event->result = DNS_R_SERVFAIL; 1310224090Sdougb ISC_LIST_INIT(event->answerlist); 1311224090Sdougb 1312224090Sdougb rctx = isc_mem_get(mctx, sizeof(*rctx)); 1313224090Sdougb if (rctx == NULL) 1314224090Sdougb result = ISC_R_NOMEMORY; 1315224090Sdougb else { 1316224090Sdougb result = isc_mutex_init(&rctx->lock); 1317224090Sdougb if (result != ISC_R_SUCCESS) { 1318224090Sdougb isc_mem_put(mctx, rctx, sizeof(*rctx)); 1319224090Sdougb rctx = NULL; 1320224090Sdougb } 1321224090Sdougb } 1322224090Sdougb if (result != ISC_R_SUCCESS) 1323224090Sdougb goto cleanup; 1324224090Sdougb 1325224090Sdougb result = getrdataset(mctx, &rdataset); 1326224090Sdougb if (result != ISC_R_SUCCESS) 1327224090Sdougb goto cleanup; 1328224090Sdougb rctx->rdataset = rdataset; 1329224090Sdougb 1330224090Sdougb if (want_dnssec) { 1331224090Sdougb result = getrdataset(mctx, &sigrdataset); 1332224090Sdougb if (result != ISC_R_SUCCESS) 1333224090Sdougb goto cleanup; 1334224090Sdougb } 1335224090Sdougb rctx->sigrdataset = sigrdataset; 1336224090Sdougb 1337224090Sdougb dns_fixedname_init(&rctx->name); 1338224090Sdougb result = dns_name_copy(name, dns_fixedname_name(&rctx->name), NULL); 1339224090Sdougb if (result != ISC_R_SUCCESS) 1340224090Sdougb goto cleanup; 1341224090Sdougb 1342224090Sdougb rctx->client = client; 1343224090Sdougb ISC_LINK_INIT(rctx, link); 1344224090Sdougb rctx->canceled = ISC_FALSE; 1345224090Sdougb rctx->task = client->task; 1346224090Sdougb rctx->type = type; 1347224090Sdougb rctx->view = view; 1348224090Sdougb rctx->restarts = 0; 1349224090Sdougb rctx->fetch = NULL; 1350224090Sdougb rctx->want_dnssec = want_dnssec; 1351224090Sdougb ISC_LIST_INIT(rctx->namelist); 1352224090Sdougb rctx->event = event; 1353224090Sdougb 1354224090Sdougb rctx->magic = RCTX_MAGIC; 1355224090Sdougb 1356224090Sdougb LOCK(&client->lock); 1357224090Sdougb ISC_LIST_APPEND(client->resctxs, rctx, link); 1358224090Sdougb UNLOCK(&client->lock); 1359224090Sdougb 1360262706Serwin *transp = (dns_clientrestrans_t *)rctx; 1361224090Sdougb client_resfind(rctx, NULL); 1362224090Sdougb 1363224090Sdougb return (ISC_R_SUCCESS); 1364224090Sdougb 1365224090Sdougb cleanup: 1366224090Sdougb if (rdataset != NULL) 1367224090Sdougb putrdataset(client->mctx, &rdataset); 1368224090Sdougb if (sigrdataset != NULL) 1369224090Sdougb putrdataset(client->mctx, &sigrdataset); 1370224090Sdougb if (rctx != NULL) { 1371224090Sdougb DESTROYLOCK(&rctx->lock); 1372224090Sdougb isc_mem_put(mctx, rctx, sizeof(*rctx)); 1373224090Sdougb } 1374224090Sdougb if (event != NULL) 1375224090Sdougb isc_event_free(ISC_EVENT_PTR(&event)); 1376224090Sdougb isc_task_detach(&clone); 1377224090Sdougb dns_view_detach(&view); 1378224090Sdougb 1379224090Sdougb return (result); 1380224090Sdougb} 1381224090Sdougb 1382224090Sdougbvoid 1383224090Sdougbdns_client_cancelresolve(dns_clientrestrans_t *trans) { 1384224090Sdougb resctx_t *rctx; 1385224090Sdougb 1386224090Sdougb REQUIRE(trans != NULL); 1387224090Sdougb rctx = (resctx_t *)trans; 1388224090Sdougb REQUIRE(RCTX_VALID(rctx)); 1389224090Sdougb 1390224090Sdougb LOCK(&rctx->lock); 1391224090Sdougb 1392224090Sdougb if (!rctx->canceled) { 1393224090Sdougb rctx->canceled = ISC_TRUE; 1394224090Sdougb if (rctx->fetch != NULL) 1395224090Sdougb dns_resolver_cancelfetch(rctx->fetch); 1396224090Sdougb } 1397224090Sdougb 1398224090Sdougb UNLOCK(&rctx->lock); 1399224090Sdougb} 1400224090Sdougb 1401224090Sdougbvoid 1402224090Sdougbdns_client_freeresanswer(dns_client_t *client, dns_namelist_t *namelist) { 1403224090Sdougb dns_name_t *name; 1404224090Sdougb dns_rdataset_t *rdataset; 1405224090Sdougb 1406224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 1407224090Sdougb REQUIRE(namelist != NULL); 1408224090Sdougb 1409224090Sdougb while ((name = ISC_LIST_HEAD(*namelist)) != NULL) { 1410224090Sdougb ISC_LIST_UNLINK(*namelist, name, link); 1411224090Sdougb while ((rdataset = ISC_LIST_HEAD(name->list)) != NULL) { 1412224090Sdougb ISC_LIST_UNLINK(name->list, rdataset, link); 1413224090Sdougb putrdataset(client->mctx, &rdataset); 1414224090Sdougb } 1415224090Sdougb dns_name_free(name, client->mctx); 1416224090Sdougb isc_mem_put(client->mctx, name, sizeof(*name)); 1417224090Sdougb } 1418224090Sdougb} 1419224090Sdougb 1420224090Sdougbvoid 1421224090Sdougbdns_client_destroyrestrans(dns_clientrestrans_t **transp) { 1422224090Sdougb resctx_t *rctx; 1423224090Sdougb isc_mem_t *mctx; 1424224090Sdougb dns_client_t *client; 1425224090Sdougb isc_boolean_t need_destroyclient = ISC_FALSE; 1426224090Sdougb 1427224090Sdougb REQUIRE(transp != NULL); 1428224090Sdougb rctx = (resctx_t *)*transp; 1429224090Sdougb REQUIRE(RCTX_VALID(rctx)); 1430224090Sdougb REQUIRE(rctx->fetch == NULL); 1431224090Sdougb REQUIRE(rctx->event == NULL); 1432224090Sdougb client = rctx->client; 1433224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 1434224090Sdougb 1435224090Sdougb mctx = client->mctx; 1436224090Sdougb dns_view_detach(&rctx->view); 1437224090Sdougb 1438224090Sdougb LOCK(&client->lock); 1439224090Sdougb 1440224090Sdougb INSIST(ISC_LINK_LINKED(rctx, link)); 1441224090Sdougb ISC_LIST_UNLINK(client->resctxs, rctx, link); 1442224090Sdougb 1443224090Sdougb if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) && 1444224090Sdougb ISC_LIST_EMPTY(client->reqctxs) && 1445224090Sdougb ISC_LIST_EMPTY(client->updatectxs)) 1446224090Sdougb need_destroyclient = ISC_TRUE; 1447224090Sdougb 1448224090Sdougb UNLOCK(&client->lock); 1449224090Sdougb 1450224090Sdougb INSIST(ISC_LIST_EMPTY(rctx->namelist)); 1451224090Sdougb 1452224090Sdougb DESTROYLOCK(&rctx->lock); 1453224090Sdougb rctx->magic = 0; 1454224090Sdougb 1455224090Sdougb isc_mem_put(mctx, rctx, sizeof(*rctx)); 1456224090Sdougb 1457224090Sdougb if (need_destroyclient) 1458224090Sdougb destroyclient(&client); 1459224090Sdougb 1460224090Sdougb *transp = NULL; 1461224090Sdougb} 1462224090Sdougb 1463224090Sdougbisc_result_t 1464224090Sdougbdns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass, 1465224090Sdougb dns_name_t *keyname, isc_buffer_t *keydatabuf) 1466224090Sdougb{ 1467224090Sdougb isc_result_t result; 1468224090Sdougb dns_view_t *view = NULL; 1469224090Sdougb dst_key_t *dstkey = NULL; 1470224090Sdougb dns_keytable_t *secroots = NULL; 1471224090Sdougb 1472224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 1473224090Sdougb 1474224090Sdougb LOCK(&client->lock); 1475224090Sdougb result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME, 1476224090Sdougb rdclass, &view); 1477224090Sdougb UNLOCK(&client->lock); 1478224090Sdougb if (result != ISC_R_SUCCESS) 1479224090Sdougb goto cleanup; 1480224090Sdougb 1481224090Sdougb result = dns_view_getsecroots(view, &secroots); 1482224090Sdougb if (result != ISC_R_SUCCESS) 1483224090Sdougb goto cleanup; 1484224090Sdougb 1485224090Sdougb result = dst_key_fromdns(keyname, rdclass, keydatabuf, client->mctx, 1486224090Sdougb &dstkey); 1487224090Sdougb if (result != ISC_R_SUCCESS) 1488224090Sdougb goto cleanup; 1489224090Sdougb 1490224090Sdougb result = dns_keytable_add(secroots, ISC_FALSE, &dstkey); 1491224090Sdougb 1492224090Sdougb cleanup: 1493224090Sdougb if (dstkey != NULL) 1494224090Sdougb dst_key_free(&dstkey); 1495224090Sdougb if (view != NULL) 1496224090Sdougb dns_view_detach(&view); 1497224090Sdougb if (secroots != NULL) 1498224090Sdougb dns_keytable_detach(&secroots); 1499224090Sdougb return (result); 1500224090Sdougb} 1501224090Sdougb 1502224090Sdougb/*% 1503224090Sdougb * Simple request routines 1504224090Sdougb */ 1505224090Sdougbstatic void 1506224090Sdougbrequest_done(isc_task_t *task, isc_event_t *event) { 1507224090Sdougb dns_requestevent_t *reqev = NULL; 1508224090Sdougb dns_request_t *request; 1509224090Sdougb isc_result_t result, eresult; 1510224090Sdougb reqctx_t *ctx; 1511224090Sdougb 1512224090Sdougb UNUSED(task); 1513224090Sdougb 1514224090Sdougb REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE); 1515224090Sdougb reqev = (dns_requestevent_t *)event; 1516224090Sdougb request = reqev->request; 1517224090Sdougb result = eresult = reqev->result; 1518224090Sdougb ctx = reqev->ev_arg; 1519224090Sdougb REQUIRE(REQCTX_VALID(ctx)); 1520224090Sdougb 1521224090Sdougb isc_event_free(&event); 1522224090Sdougb 1523224090Sdougb LOCK(&ctx->lock); 1524224090Sdougb 1525224090Sdougb if (eresult == ISC_R_SUCCESS) { 1526224090Sdougb result = dns_request_getresponse(request, ctx->event->rmessage, 1527224090Sdougb ctx->parseoptions); 1528224090Sdougb } 1529224090Sdougb 1530224090Sdougb if (ctx->tsigkey != NULL) 1531224090Sdougb dns_tsigkey_detach(&ctx->tsigkey); 1532224090Sdougb 1533224090Sdougb if (ctx->canceled) 1534224090Sdougb ctx->event->result = ISC_R_CANCELED; 1535224090Sdougb else 1536224090Sdougb ctx->event->result = result; 1537224090Sdougb task = ctx->event->ev_sender; 1538224090Sdougb ctx->event->ev_sender = ctx; 1539224090Sdougb isc_task_sendanddetach(&task, ISC_EVENT_PTR(&ctx->event)); 1540224090Sdougb 1541224090Sdougb UNLOCK(&ctx->lock); 1542224090Sdougb} 1543224090Sdougb 1544224090Sdougbstatic void 1545224090Sdougblocalrequest_done(isc_task_t *task, isc_event_t *event) { 1546224090Sdougb reqarg_t *reqarg = event->ev_arg; 1547224090Sdougb dns_clientreqevent_t *rev =(dns_clientreqevent_t *)event; 1548224090Sdougb 1549224090Sdougb UNUSED(task); 1550224090Sdougb 1551224090Sdougb REQUIRE(event->ev_type == DNS_EVENT_CLIENTREQDONE); 1552224090Sdougb 1553224090Sdougb LOCK(&reqarg->lock); 1554224090Sdougb 1555224090Sdougb reqarg->result = rev->result; 1556224090Sdougb dns_client_destroyreqtrans(&reqarg->trans); 1557224090Sdougb isc_event_free(&event); 1558224090Sdougb 1559224090Sdougb if (!reqarg->canceled) { 1560224090Sdougb UNLOCK(&reqarg->lock); 1561224090Sdougb 1562224090Sdougb /* Exit from the internal event loop */ 1563224090Sdougb isc_app_ctxsuspend(reqarg->actx); 1564224090Sdougb } else { 1565224090Sdougb /* 1566224090Sdougb * We have already exited from the loop (due to some 1567224090Sdougb * unexpected event). Just clean the arg up. 1568224090Sdougb */ 1569224090Sdougb UNLOCK(&reqarg->lock); 1570224090Sdougb DESTROYLOCK(&reqarg->lock); 1571224090Sdougb isc_mem_put(reqarg->client->mctx, reqarg, sizeof(*reqarg)); 1572224090Sdougb } 1573224090Sdougb} 1574224090Sdougb 1575224090Sdougbisc_result_t 1576224090Sdougbdns_client_request(dns_client_t *client, dns_message_t *qmessage, 1577224090Sdougb dns_message_t *rmessage, isc_sockaddr_t *server, 1578224090Sdougb unsigned int options, unsigned int parseoptions, 1579224090Sdougb dns_tsec_t *tsec, unsigned int timeout, 1580224090Sdougb unsigned int udptimeout, unsigned int udpretries) 1581224090Sdougb{ 1582224090Sdougb isc_appctx_t *actx; 1583224090Sdougb reqarg_t *reqarg; 1584224090Sdougb isc_result_t result; 1585224090Sdougb 1586224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 1587224090Sdougb REQUIRE(qmessage != NULL); 1588224090Sdougb REQUIRE(rmessage != NULL); 1589224090Sdougb 1590224090Sdougb if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 && 1591224090Sdougb (options & DNS_CLIENTREQOPT_ALLOWRUN) == 0) { 1592224090Sdougb /* 1593224090Sdougb * If the client is run under application's control, we need 1594224090Sdougb * to create a new running (sub)environment for this 1595224090Sdougb * particular resolution. 1596224090Sdougb */ 1597224090Sdougb return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */ 1598224090Sdougb } else 1599224090Sdougb actx = client->actx; 1600224090Sdougb 1601224090Sdougb reqarg = isc_mem_get(client->mctx, sizeof(*reqarg)); 1602224090Sdougb if (reqarg == NULL) 1603224090Sdougb return (ISC_R_NOMEMORY); 1604224090Sdougb 1605224090Sdougb result = isc_mutex_init(&reqarg->lock); 1606224090Sdougb if (result != ISC_R_SUCCESS) { 1607224090Sdougb isc_mem_put(client->mctx, reqarg, sizeof(*reqarg)); 1608224090Sdougb return (result); 1609224090Sdougb } 1610224090Sdougb 1611224090Sdougb reqarg->actx = actx; 1612224090Sdougb reqarg->client = client; 1613224090Sdougb reqarg->trans = NULL; 1614224090Sdougb reqarg->canceled = ISC_FALSE; 1615224090Sdougb 1616224090Sdougb result = dns_client_startrequest(client, qmessage, rmessage, server, 1617224090Sdougb options, parseoptions, tsec, timeout, 1618224090Sdougb udptimeout, udpretries, 1619224090Sdougb client->task, localrequest_done, 1620224090Sdougb reqarg, &reqarg->trans); 1621224090Sdougb if (result != ISC_R_SUCCESS) { 1622224090Sdougb DESTROYLOCK(&reqarg->lock); 1623224090Sdougb isc_mem_put(client->mctx, reqarg, sizeof(*reqarg)); 1624224090Sdougb return (result); 1625224090Sdougb } 1626224090Sdougb 1627224090Sdougb /* 1628224090Sdougb * Start internal event loop. It blocks until the entire process 1629224090Sdougb * is completed. 1630224090Sdougb */ 1631224090Sdougb result = isc_app_ctxrun(actx); 1632224090Sdougb 1633224090Sdougb LOCK(&reqarg->lock); 1634224090Sdougb if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND) 1635224090Sdougb result = reqarg->result; 1636224090Sdougb if (reqarg->trans != NULL) { 1637224090Sdougb /* 1638224090Sdougb * Unusual termination (perhaps due to signal). We need some 1639224090Sdougb * tricky cleanup process. 1640224090Sdougb */ 1641224090Sdougb reqarg->canceled = ISC_TRUE; 1642224090Sdougb dns_client_cancelresolve(reqarg->trans); 1643224090Sdougb 1644224090Sdougb UNLOCK(&reqarg->lock); 1645224090Sdougb 1646224090Sdougb /* reqarg will be freed in the event handler. */ 1647224090Sdougb } else { 1648224090Sdougb UNLOCK(&reqarg->lock); 1649224090Sdougb 1650224090Sdougb DESTROYLOCK(&reqarg->lock); 1651224090Sdougb isc_mem_put(client->mctx, reqarg, sizeof(*reqarg)); 1652224090Sdougb } 1653224090Sdougb 1654224090Sdougb return (result); 1655224090Sdougb} 1656224090Sdougb 1657224090Sdougbisc_result_t 1658224090Sdougbdns_client_startrequest(dns_client_t *client, dns_message_t *qmessage, 1659224090Sdougb dns_message_t *rmessage, isc_sockaddr_t *server, 1660224090Sdougb unsigned int options, unsigned int parseoptions, 1661224090Sdougb dns_tsec_t *tsec, unsigned int timeout, 1662224090Sdougb unsigned int udptimeout, unsigned int udpretries, 1663224090Sdougb isc_task_t *task, isc_taskaction_t action, void *arg, 1664224090Sdougb dns_clientreqtrans_t **transp) 1665224090Sdougb{ 1666224090Sdougb isc_result_t result; 1667224090Sdougb dns_view_t *view = NULL; 1668224090Sdougb isc_task_t *clone = NULL; 1669224090Sdougb dns_clientreqevent_t *event = NULL; 1670224090Sdougb reqctx_t *ctx = NULL; 1671224090Sdougb dns_tsectype_t tsectype = dns_tsectype_none; 1672224090Sdougb 1673224090Sdougb UNUSED(options); 1674224090Sdougb 1675224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 1676224090Sdougb REQUIRE(qmessage != NULL); 1677224090Sdougb REQUIRE(rmessage != NULL); 1678224090Sdougb REQUIRE(transp != NULL && *transp == NULL); 1679224090Sdougb 1680224090Sdougb if (tsec != NULL) { 1681224090Sdougb tsectype = dns_tsec_gettype(tsec); 1682224090Sdougb if (tsectype != dns_tsectype_tsig) 1683224090Sdougb return (ISC_R_NOTIMPLEMENTED); /* XXX */ 1684224090Sdougb } 1685224090Sdougb 1686224090Sdougb LOCK(&client->lock); 1687224090Sdougb result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME, 1688224090Sdougb qmessage->rdclass, &view); 1689224090Sdougb UNLOCK(&client->lock); 1690224090Sdougb if (result != ISC_R_SUCCESS) 1691224090Sdougb return (result); 1692224090Sdougb 1693224090Sdougb clone = NULL; 1694224090Sdougb isc_task_attach(task, &clone); 1695224090Sdougb event = (dns_clientreqevent_t *) 1696224090Sdougb isc_event_allocate(client->mctx, clone, 1697224090Sdougb DNS_EVENT_CLIENTREQDONE, 1698224090Sdougb action, arg, sizeof(*event)); 1699224090Sdougb if (event == NULL) { 1700224090Sdougb result = ISC_R_NOMEMORY; 1701224090Sdougb goto cleanup; 1702224090Sdougb } 1703224090Sdougb 1704224090Sdougb ctx = isc_mem_get(client->mctx, sizeof(*ctx)); 1705224090Sdougb if (ctx == NULL) 1706224090Sdougb result = ISC_R_NOMEMORY; 1707224090Sdougb else { 1708224090Sdougb result = isc_mutex_init(&ctx->lock); 1709224090Sdougb if (result != ISC_R_SUCCESS) { 1710224090Sdougb isc_mem_put(client->mctx, ctx, sizeof(*ctx)); 1711224090Sdougb ctx = NULL; 1712224090Sdougb } 1713224090Sdougb } 1714224090Sdougb if (result != ISC_R_SUCCESS) 1715224090Sdougb goto cleanup; 1716224090Sdougb 1717224090Sdougb ctx->client = client; 1718224090Sdougb ISC_LINK_INIT(ctx, link); 1719224090Sdougb ctx->parseoptions = parseoptions; 1720224090Sdougb ctx->canceled = ISC_FALSE; 1721224090Sdougb ctx->event = event; 1722224090Sdougb ctx->event->rmessage = rmessage; 1723224090Sdougb ctx->tsigkey = NULL; 1724224090Sdougb if (tsec != NULL) 1725224090Sdougb dns_tsec_getkey(tsec, &ctx->tsigkey); 1726224090Sdougb 1727224090Sdougb ctx->magic = REQCTX_MAGIC; 1728224090Sdougb 1729224090Sdougb LOCK(&client->lock); 1730224090Sdougb ISC_LIST_APPEND(client->reqctxs, ctx, link); 1731224090Sdougb UNLOCK(&client->lock); 1732224090Sdougb 1733224090Sdougb ctx->request = NULL; 1734224090Sdougb result = dns_request_createvia3(view->requestmgr, qmessage, NULL, 1735224090Sdougb server, options, ctx->tsigkey, 1736224090Sdougb timeout, udptimeout, udpretries, 1737224090Sdougb client->task, request_done, ctx, 1738224090Sdougb &ctx->request); 1739224090Sdougb if (result == ISC_R_SUCCESS) { 1740224090Sdougb dns_view_detach(&view); 1741224090Sdougb *transp = (dns_clientreqtrans_t *)ctx; 1742224090Sdougb return (ISC_R_SUCCESS); 1743224090Sdougb } 1744224090Sdougb 1745224090Sdougb cleanup: 1746224090Sdougb if (ctx != NULL) { 1747224090Sdougb LOCK(&client->lock); 1748224090Sdougb ISC_LIST_UNLINK(client->reqctxs, ctx, link); 1749224090Sdougb UNLOCK(&client->lock); 1750224090Sdougb DESTROYLOCK(&ctx->lock); 1751224090Sdougb isc_mem_put(client->mctx, ctx, sizeof(*ctx)); 1752224090Sdougb } 1753224090Sdougb if (event != NULL) 1754224090Sdougb isc_event_free(ISC_EVENT_PTR(&event)); 1755224090Sdougb isc_task_detach(&clone); 1756224090Sdougb dns_view_detach(&view); 1757224090Sdougb 1758224090Sdougb return (result); 1759224090Sdougb} 1760224090Sdougb 1761224090Sdougbvoid 1762224090Sdougbdns_client_cancelrequest(dns_clientreqtrans_t *trans) { 1763224090Sdougb reqctx_t *ctx; 1764224090Sdougb 1765224090Sdougb REQUIRE(trans != NULL); 1766224090Sdougb ctx = (reqctx_t *)trans; 1767224090Sdougb REQUIRE(REQCTX_VALID(ctx)); 1768224090Sdougb 1769224090Sdougb LOCK(&ctx->lock); 1770224090Sdougb 1771224090Sdougb if (!ctx->canceled) { 1772224090Sdougb ctx->canceled = ISC_TRUE; 1773224090Sdougb if (ctx->request != NULL) 1774224090Sdougb dns_request_cancel(ctx->request); 1775224090Sdougb } 1776224090Sdougb 1777224090Sdougb UNLOCK(&ctx->lock); 1778224090Sdougb} 1779224090Sdougb 1780224090Sdougbvoid 1781224090Sdougbdns_client_destroyreqtrans(dns_clientreqtrans_t **transp) { 1782224090Sdougb reqctx_t *ctx; 1783224090Sdougb isc_mem_t *mctx; 1784224090Sdougb dns_client_t *client; 1785224090Sdougb isc_boolean_t need_destroyclient = ISC_FALSE; 1786224090Sdougb 1787224090Sdougb REQUIRE(transp != NULL); 1788224090Sdougb ctx = (reqctx_t *)*transp; 1789224090Sdougb REQUIRE(REQCTX_VALID(ctx)); 1790224090Sdougb client = ctx->client; 1791224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 1792224090Sdougb REQUIRE(ctx->event == NULL); 1793224090Sdougb REQUIRE(ctx->request != NULL); 1794224090Sdougb 1795224090Sdougb dns_request_destroy(&ctx->request); 1796224090Sdougb mctx = client->mctx; 1797224090Sdougb 1798224090Sdougb LOCK(&client->lock); 1799224090Sdougb 1800224090Sdougb INSIST(ISC_LINK_LINKED(ctx, link)); 1801224090Sdougb ISC_LIST_UNLINK(client->reqctxs, ctx, link); 1802224090Sdougb 1803224090Sdougb if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) && 1804224090Sdougb ISC_LIST_EMPTY(client->reqctxs) && 1805224090Sdougb ISC_LIST_EMPTY(client->updatectxs)) { 1806224090Sdougb need_destroyclient = ISC_TRUE; 1807224090Sdougb } 1808224090Sdougb 1809224090Sdougb UNLOCK(&client->lock); 1810224090Sdougb 1811224090Sdougb DESTROYLOCK(&ctx->lock); 1812224090Sdougb ctx->magic = 0; 1813224090Sdougb 1814224090Sdougb isc_mem_put(mctx, ctx, sizeof(*ctx)); 1815224090Sdougb 1816224090Sdougb if (need_destroyclient) 1817224090Sdougb destroyclient(&client); 1818224090Sdougb 1819224090Sdougb *transp = NULL; 1820224090Sdougb} 1821224090Sdougb 1822224090Sdougb/*% 1823224090Sdougb * Dynamic update routines 1824224090Sdougb */ 1825224090Sdougbstatic isc_result_t 1826224090Sdougbrcode2result(dns_rcode_t rcode) { 1827224090Sdougb /* XXX: isn't there a similar function? */ 1828224090Sdougb switch (rcode) { 1829224090Sdougb case dns_rcode_formerr: 1830224090Sdougb return (DNS_R_FORMERR); 1831224090Sdougb case dns_rcode_servfail: 1832224090Sdougb return (DNS_R_SERVFAIL); 1833224090Sdougb case dns_rcode_nxdomain: 1834224090Sdougb return (DNS_R_NXDOMAIN); 1835224090Sdougb case dns_rcode_notimp: 1836224090Sdougb return (DNS_R_NOTIMP); 1837224090Sdougb case dns_rcode_refused: 1838224090Sdougb return (DNS_R_REFUSED); 1839224090Sdougb case dns_rcode_yxdomain: 1840224090Sdougb return (DNS_R_YXDOMAIN); 1841224090Sdougb case dns_rcode_yxrrset: 1842224090Sdougb return (DNS_R_YXRRSET); 1843224090Sdougb case dns_rcode_nxrrset: 1844224090Sdougb return (DNS_R_NXRRSET); 1845224090Sdougb case dns_rcode_notauth: 1846224090Sdougb return (DNS_R_NOTAUTH); 1847224090Sdougb case dns_rcode_notzone: 1848224090Sdougb return (DNS_R_NOTZONE); 1849224090Sdougb case dns_rcode_badvers: 1850224090Sdougb return (DNS_R_BADVERS); 1851224090Sdougb } 1852224090Sdougb 1853224090Sdougb return (ISC_R_FAILURE); 1854224090Sdougb} 1855224090Sdougb 1856224090Sdougbstatic void 1857224090Sdougbupdate_sendevent(updatectx_t *uctx, isc_result_t result) { 1858224090Sdougb isc_task_t *task; 1859224090Sdougb 1860224090Sdougb dns_message_destroy(&uctx->updatemsg); 1861224090Sdougb if (uctx->tsigkey != NULL) 1862224090Sdougb dns_tsigkey_detach(&uctx->tsigkey); 1863224090Sdougb if (uctx->sig0key != NULL) 1864224090Sdougb dst_key_free(&uctx->sig0key); 1865224090Sdougb 1866224090Sdougb if (uctx->canceled) 1867224090Sdougb uctx->event->result = ISC_R_CANCELED; 1868224090Sdougb else 1869224090Sdougb uctx->event->result = result; 1870224090Sdougb uctx->event->state = uctx->state; 1871224090Sdougb task = uctx->event->ev_sender; 1872224090Sdougb uctx->event->ev_sender = uctx; 1873224090Sdougb isc_task_sendanddetach(&task, ISC_EVENT_PTR(&uctx->event)); 1874224090Sdougb} 1875224090Sdougb 1876224090Sdougbstatic void 1877224090Sdougbupdate_done(isc_task_t *task, isc_event_t *event) { 1878224090Sdougb isc_result_t result; 1879224090Sdougb dns_requestevent_t *reqev = NULL; 1880224090Sdougb dns_request_t *request; 1881224090Sdougb dns_message_t *answer = NULL; 1882224090Sdougb updatectx_t *uctx = event->ev_arg; 1883224090Sdougb dns_client_t *client; 1884224090Sdougb unsigned int timeout; 1885224090Sdougb 1886224090Sdougb UNUSED(task); 1887224090Sdougb 1888224090Sdougb REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE); 1889224090Sdougb reqev = (dns_requestevent_t *)event; 1890224090Sdougb request = reqev->request; 1891224090Sdougb REQUIRE(UCTX_VALID(uctx)); 1892224090Sdougb client = uctx->client; 1893224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 1894224090Sdougb 1895224090Sdougb result = reqev->result; 1896224090Sdougb if (result != ISC_R_SUCCESS) 1897224090Sdougb goto out; 1898224090Sdougb 1899224090Sdougb result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE, 1900224090Sdougb &answer); 1901224090Sdougb if (result != ISC_R_SUCCESS) 1902224090Sdougb goto out; 1903224090Sdougb uctx->state = dns_clientupdatestate_done; 1904224090Sdougb result = dns_request_getresponse(request, answer, 1905224090Sdougb DNS_MESSAGEPARSE_PRESERVEORDER); 1906224090Sdougb if (result == ISC_R_SUCCESS && answer->rcode != dns_rcode_noerror) 1907224090Sdougb result = rcode2result(answer->rcode); 1908224090Sdougb 1909224090Sdougb out: 1910224090Sdougb if (answer != NULL) 1911224090Sdougb dns_message_destroy(&answer); 1912224090Sdougb isc_event_free(&event); 1913224090Sdougb 1914224090Sdougb LOCK(&uctx->lock); 1915224090Sdougb uctx->currentserver = ISC_LIST_NEXT(uctx->currentserver, link); 1916224090Sdougb dns_request_destroy(&uctx->updatereq); 1917224090Sdougb if (result != ISC_R_SUCCESS && !uctx->canceled && 1918224090Sdougb uctx->currentserver != NULL) { 1919224090Sdougb dns_message_renderreset(uctx->updatemsg); 1920224090Sdougb dns_message_settsigkey(uctx->updatemsg, NULL); 1921224090Sdougb 1922224090Sdougb timeout = client->update_timeout / uctx->nservers; 1923224090Sdougb if (timeout < MIN_UPDATE_TIMEOUT) 1924224090Sdougb timeout = MIN_UPDATE_TIMEOUT; 1925224090Sdougb result = dns_request_createvia3(uctx->view->requestmgr, 1926224090Sdougb uctx->updatemsg, 1927224090Sdougb NULL, 1928224090Sdougb uctx->currentserver, 0, 1929224090Sdougb uctx->tsigkey, 1930224090Sdougb timeout, 1931224090Sdougb client->update_udptimeout, 1932224090Sdougb client->update_udpretries, 1933224090Sdougb client->task, 1934224090Sdougb update_done, uctx, 1935224090Sdougb &uctx->updatereq); 1936224090Sdougb UNLOCK(&uctx->lock); 1937224090Sdougb 1938224090Sdougb if (result == ISC_R_SUCCESS) { 1939224090Sdougb /* XXX: should we keep the 'done' state here? */ 1940224090Sdougb uctx->state = dns_clientupdatestate_sent; 1941224090Sdougb return; 1942224090Sdougb } 1943224090Sdougb } else 1944224090Sdougb UNLOCK(&uctx->lock); 1945224090Sdougb 1946224090Sdougb update_sendevent(uctx, result); 1947224090Sdougb} 1948224090Sdougb 1949224090Sdougbstatic isc_result_t 1950224090Sdougbsend_update(updatectx_t *uctx) { 1951224090Sdougb isc_result_t result; 1952224090Sdougb dns_name_t *name = NULL; 1953224090Sdougb dns_rdataset_t *rdataset = NULL; 1954224090Sdougb dns_client_t *client = uctx->client; 1955224090Sdougb unsigned int timeout; 1956224090Sdougb 1957224090Sdougb REQUIRE(uctx->zonename != NULL && uctx->currentserver != NULL); 1958224090Sdougb 1959224090Sdougb result = dns_message_gettempname(uctx->updatemsg, &name); 1960224090Sdougb if (result != ISC_R_SUCCESS) 1961224090Sdougb return (result); 1962224090Sdougb dns_name_init(name, NULL); 1963224090Sdougb dns_name_clone(uctx->zonename, name); 1964224090Sdougb result = dns_message_gettemprdataset(uctx->updatemsg, &rdataset); 1965224090Sdougb if (result != ISC_R_SUCCESS) { 1966224090Sdougb dns_message_puttempname(uctx->updatemsg, &name); 1967224090Sdougb return (result); 1968224090Sdougb } 1969224090Sdougb dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa); 1970224090Sdougb ISC_LIST_INIT(name->list); 1971224090Sdougb ISC_LIST_APPEND(name->list, rdataset, link); 1972224090Sdougb dns_message_addname(uctx->updatemsg, name, DNS_SECTION_ZONE); 1973224090Sdougb if (uctx->tsigkey == NULL && uctx->sig0key != NULL) { 1974224090Sdougb result = dns_message_setsig0key(uctx->updatemsg, 1975224090Sdougb uctx->sig0key); 1976224090Sdougb if (result != ISC_R_SUCCESS) 1977224090Sdougb return (result); 1978224090Sdougb } 1979224090Sdougb timeout = client->update_timeout / uctx->nservers; 1980224090Sdougb if (timeout < MIN_UPDATE_TIMEOUT) 1981224090Sdougb timeout = MIN_UPDATE_TIMEOUT; 1982224090Sdougb result = dns_request_createvia3(uctx->view->requestmgr, 1983224090Sdougb uctx->updatemsg, 1984224090Sdougb NULL, uctx->currentserver, 0, 1985224090Sdougb uctx->tsigkey, timeout, 1986224090Sdougb client->update_udptimeout, 1987224090Sdougb client->update_udpretries, 1988224090Sdougb client->task, update_done, uctx, 1989224090Sdougb &uctx->updatereq); 1990224090Sdougb if (result == ISC_R_SUCCESS && 1991224090Sdougb uctx->state == dns_clientupdatestate_prepare) { 1992224090Sdougb uctx->state = dns_clientupdatestate_sent; 1993224090Sdougb } 1994224090Sdougb 1995224090Sdougb return (result); 1996224090Sdougb} 1997224090Sdougb 1998224090Sdougbstatic void 1999224090Sdougbresolveaddr_done(isc_task_t *task, isc_event_t *event) { 2000224090Sdougb isc_result_t result; 2001224090Sdougb int family; 2002224090Sdougb dns_rdatatype_t qtype; 2003224090Sdougb dns_clientresevent_t *rev = (dns_clientresevent_t *)event; 2004224090Sdougb dns_name_t *name; 2005224090Sdougb dns_rdataset_t *rdataset; 2006224090Sdougb updatectx_t *uctx; 2007224090Sdougb isc_boolean_t completed = ISC_FALSE; 2008224090Sdougb 2009224090Sdougb UNUSED(task); 2010224090Sdougb 2011224090Sdougb REQUIRE(event->ev_arg != NULL); 2012224090Sdougb uctx = *(updatectx_t **)event->ev_arg; 2013224090Sdougb REQUIRE(UCTX_VALID(uctx)); 2014224090Sdougb 2015224090Sdougb if (event->ev_arg == &uctx->bp4) { 2016224090Sdougb family = AF_INET; 2017224090Sdougb qtype = dns_rdatatype_a; 2018224090Sdougb LOCK(&uctx->lock); 2019224090Sdougb dns_client_destroyrestrans(&uctx->restrans); 2020224090Sdougb UNLOCK(&uctx->lock); 2021224090Sdougb } else { 2022224090Sdougb INSIST(event->ev_arg == &uctx->bp6); 2023224090Sdougb family = AF_INET6; 2024224090Sdougb qtype = dns_rdatatype_aaaa; 2025224090Sdougb LOCK(&uctx->lock); 2026224090Sdougb dns_client_destroyrestrans(&uctx->restrans2); 2027224090Sdougb UNLOCK(&uctx->lock); 2028224090Sdougb } 2029224090Sdougb 2030224090Sdougb result = rev->result; 2031224090Sdougb if (result != ISC_R_SUCCESS) 2032224090Sdougb goto done; 2033224090Sdougb 2034224090Sdougb for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; 2035224090Sdougb name = ISC_LIST_NEXT(name, link)) { 2036224090Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 2037224090Sdougb rdataset != NULL; 2038224090Sdougb rdataset = ISC_LIST_NEXT(rdataset, link)) { 2039224090Sdougb if (!dns_rdataset_isassociated(rdataset)) 2040224090Sdougb continue; 2041224090Sdougb if (rdataset->type != qtype) 2042224090Sdougb continue; 2043224090Sdougb 2044224090Sdougb for (result = dns_rdataset_first(rdataset); 2045224090Sdougb result == ISC_R_SUCCESS; 2046224090Sdougb result = dns_rdataset_next(rdataset)) { 2047224090Sdougb dns_rdata_t rdata; 2048224090Sdougb dns_rdata_in_a_t rdata_a; 2049224090Sdougb dns_rdata_in_aaaa_t rdata_aaaa; 2050224090Sdougb isc_sockaddr_t *sa; 2051224090Sdougb 2052224090Sdougb sa = isc_mem_get(uctx->client->mctx, 2053224090Sdougb sizeof(*sa)); 2054224090Sdougb if (sa == NULL) { 2055224090Sdougb /* 2056224090Sdougb * If we fail to get a sockaddr, 2057224090Sdougb we simply move forward with the 2058224090Sdougb * addresses we've got so far. 2059224090Sdougb */ 2060224090Sdougb goto done; 2061224090Sdougb } 2062224090Sdougb 2063224090Sdougb dns_rdata_init(&rdata); 2064224090Sdougb switch (family) { 2065224090Sdougb case AF_INET: 2066224090Sdougb dns_rdataset_current(rdataset, &rdata); 2067254402Serwin result = dns_rdata_tostruct(&rdata, &rdata_a, 2068254402Serwin NULL); 2069254402Serwin RUNTIME_CHECK(result == ISC_R_SUCCESS); 2070224090Sdougb isc_sockaddr_fromin(sa, 2071224090Sdougb &rdata_a.in_addr, 2072224090Sdougb 53); 2073224090Sdougb dns_rdata_freestruct(&rdata_a); 2074224090Sdougb break; 2075224090Sdougb case AF_INET6: 2076224090Sdougb dns_rdataset_current(rdataset, &rdata); 2077254402Serwin result = dns_rdata_tostruct(&rdata, &rdata_aaaa, 2078254402Serwin NULL); 2079254402Serwin RUNTIME_CHECK(result == ISC_R_SUCCESS); 2080224090Sdougb isc_sockaddr_fromin6(sa, 2081224090Sdougb &rdata_aaaa.in6_addr, 2082224090Sdougb 53); 2083224090Sdougb dns_rdata_freestruct(&rdata_aaaa); 2084224090Sdougb break; 2085224090Sdougb } 2086224090Sdougb 2087224090Sdougb ISC_LINK_INIT(sa, link); 2088224090Sdougb ISC_LIST_APPEND(uctx->servers, sa, link); 2089224090Sdougb uctx->nservers++; 2090224090Sdougb } 2091224090Sdougb } 2092224090Sdougb } 2093224090Sdougb 2094224090Sdougb done: 2095224090Sdougb dns_client_freeresanswer(uctx->client, &rev->answerlist); 2096224090Sdougb isc_event_free(&event); 2097224090Sdougb 2098224090Sdougb LOCK(&uctx->lock); 2099224090Sdougb if (uctx->restrans == NULL && uctx->restrans2 == NULL) 2100224090Sdougb completed = ISC_TRUE; 2101224090Sdougb UNLOCK(&uctx->lock); 2102224090Sdougb 2103224090Sdougb if (completed) { 2104224090Sdougb INSIST(uctx->currentserver == NULL); 2105224090Sdougb uctx->currentserver = ISC_LIST_HEAD(uctx->servers); 2106224090Sdougb if (uctx->currentserver != NULL && !uctx->canceled) 2107224090Sdougb send_update(uctx); 2108224090Sdougb else { 2109224090Sdougb if (result == ISC_R_SUCCESS) 2110224090Sdougb result = ISC_R_NOTFOUND; 2111224090Sdougb update_sendevent(uctx, result); 2112224090Sdougb } 2113224090Sdougb } 2114224090Sdougb} 2115224090Sdougb 2116224090Sdougbstatic isc_result_t 2117224090Sdougbprocess_soa(updatectx_t *uctx, dns_rdataset_t *soaset, dns_name_t *soaname) { 2118224090Sdougb isc_result_t result; 2119224090Sdougb dns_rdata_t soarr = DNS_RDATA_INIT; 2120224090Sdougb dns_rdata_soa_t soa; 2121224090Sdougb dns_name_t primary; 2122224090Sdougb 2123224090Sdougb result = dns_rdataset_first(soaset); 2124224090Sdougb if (result != ISC_R_SUCCESS) 2125224090Sdougb return (result); 2126224090Sdougb dns_rdata_init(&soarr); 2127224090Sdougb dns_rdataset_current(soaset, &soarr); 2128224090Sdougb result = dns_rdata_tostruct(&soarr, &soa, NULL); 2129224090Sdougb if (result != ISC_R_SUCCESS) 2130224090Sdougb return (result); 2131224090Sdougb 2132224090Sdougb dns_name_init(&primary, NULL); 2133224090Sdougb dns_name_clone(&soa.origin, &primary); 2134224090Sdougb 2135224090Sdougb if (uctx->zonename == NULL) { 2136224090Sdougb uctx->zonename = dns_fixedname_name(&uctx->zonefname); 2137224090Sdougb result = dns_name_copy(soaname, uctx->zonename, NULL); 2138224090Sdougb if (result != ISC_R_SUCCESS) 2139224090Sdougb goto out; 2140224090Sdougb } 2141224090Sdougb 2142224090Sdougb if (uctx->currentserver != NULL) 2143224090Sdougb result = send_update(uctx); 2144224090Sdougb else { 2145224090Sdougb /* 2146224090Sdougb * Get addresses of the primary server. We don't use the ADB 2147224090Sdougb * feature so that we could avoid caching data. 2148224090Sdougb */ 2149224090Sdougb LOCK(&uctx->lock); 2150224090Sdougb uctx->bp4 = uctx; 2151224090Sdougb result = dns_client_startresolve(uctx->client, &primary, 2152224090Sdougb uctx->rdclass, 2153224090Sdougb dns_rdatatype_a, 2154224090Sdougb 0, uctx->client->task, 2155224090Sdougb resolveaddr_done, &uctx->bp4, 2156224090Sdougb &uctx->restrans); 2157224090Sdougb if (result == ISC_R_SUCCESS) { 2158224090Sdougb uctx->bp6 = uctx; 2159224090Sdougb result = dns_client_startresolve(uctx->client, 2160224090Sdougb &primary, 2161224090Sdougb uctx->rdclass, 2162224090Sdougb dns_rdatatype_aaaa, 2163224090Sdougb 0, uctx->client->task, 2164224090Sdougb resolveaddr_done, 2165224090Sdougb &uctx->bp6, 2166224090Sdougb &uctx->restrans2); 2167224090Sdougb } 2168224090Sdougb UNLOCK(&uctx->lock); 2169224090Sdougb } 2170224090Sdougb 2171224090Sdougb out: 2172224090Sdougb dns_rdata_freestruct(&soa); 2173224090Sdougb 2174224090Sdougb return (result); 2175224090Sdougb} 2176224090Sdougb 2177224090Sdougbstatic void 2178224090Sdougbreceive_soa(isc_task_t *task, isc_event_t *event) { 2179224090Sdougb dns_requestevent_t *reqev = NULL; 2180224090Sdougb updatectx_t *uctx; 2181224090Sdougb dns_client_t *client; 2182224090Sdougb isc_result_t result, eresult; 2183224090Sdougb dns_request_t *request; 2184224090Sdougb dns_message_t *rcvmsg = NULL; 2185224090Sdougb dns_section_t section; 2186224090Sdougb dns_rdataset_t *soaset = NULL; 2187224090Sdougb int pass = 0; 2188224090Sdougb dns_name_t *name; 2189224090Sdougb dns_message_t *soaquery = NULL; 2190224090Sdougb isc_sockaddr_t *addr; 2191224090Sdougb isc_boolean_t seencname = ISC_FALSE; 2192224090Sdougb isc_boolean_t droplabel = ISC_FALSE; 2193224090Sdougb dns_name_t tname; 2194224090Sdougb unsigned int nlabels; 2195224090Sdougb 2196224090Sdougb UNUSED(task); 2197224090Sdougb 2198224090Sdougb REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE); 2199224090Sdougb reqev = (dns_requestevent_t *)event; 2200224090Sdougb request = reqev->request; 2201224090Sdougb result = eresult = reqev->result; 2202225361Sdougb POST(result); 2203224090Sdougb uctx = reqev->ev_arg; 2204224090Sdougb client = uctx->client; 2205224090Sdougb soaquery = uctx->soaquery; 2206224090Sdougb addr = uctx->currentserver; 2207224090Sdougb INSIST(addr != NULL); 2208224090Sdougb 2209224090Sdougb isc_event_free(&event); 2210224090Sdougb 2211224090Sdougb if (eresult != ISC_R_SUCCESS) { 2212224090Sdougb result = eresult; 2213224090Sdougb goto out; 2214224090Sdougb } 2215224090Sdougb 2216224090Sdougb result = dns_message_create(uctx->client->mctx, 2217224090Sdougb DNS_MESSAGE_INTENTPARSE, &rcvmsg); 2218224090Sdougb if (result != ISC_R_SUCCESS) 2219224090Sdougb goto out; 2220224090Sdougb result = dns_request_getresponse(request, rcvmsg, 2221224090Sdougb DNS_MESSAGEPARSE_PRESERVEORDER); 2222224090Sdougb 2223224090Sdougb if (result == DNS_R_TSIGERRORSET) { 2224224090Sdougb dns_request_t *newrequest = NULL; 2225224090Sdougb 2226224090Sdougb /* Retry SOA request without TSIG */ 2227224090Sdougb dns_message_destroy(&rcvmsg); 2228224090Sdougb dns_message_renderreset(uctx->soaquery); 2229224090Sdougb result = dns_request_createvia3(uctx->view->requestmgr, 2230224090Sdougb uctx->soaquery, NULL, addr, 0, 2231224090Sdougb NULL, 2232224090Sdougb client->find_timeout * 20, 2233224090Sdougb client->find_timeout, 3, 2234224090Sdougb uctx->client->task, 2235224090Sdougb receive_soa, uctx, 2236224090Sdougb &newrequest); 2237224090Sdougb if (result == ISC_R_SUCCESS) { 2238224090Sdougb LOCK(&uctx->lock); 2239224090Sdougb dns_request_destroy(&uctx->soareq); 2240224090Sdougb uctx->soareq = newrequest; 2241224090Sdougb UNLOCK(&uctx->lock); 2242224090Sdougb 2243224090Sdougb return; 2244224090Sdougb } 2245224090Sdougb goto out; 2246224090Sdougb } 2247224090Sdougb 2248224090Sdougb section = DNS_SECTION_ANSWER; 2249225361Sdougb POST(section); 2250224090Sdougb 2251224090Sdougb if (rcvmsg->rcode != dns_rcode_noerror && 2252224090Sdougb rcvmsg->rcode != dns_rcode_nxdomain) { 2253224090Sdougb result = rcode2result(rcvmsg->rcode); 2254224090Sdougb goto out; 2255224090Sdougb } 2256224090Sdougb 2257224090Sdougb lookforsoa: 2258224090Sdougb if (pass == 0) 2259224090Sdougb section = DNS_SECTION_ANSWER; 2260224090Sdougb else if (pass == 1) 2261224090Sdougb section = DNS_SECTION_AUTHORITY; 2262224090Sdougb else { 2263224090Sdougb droplabel = ISC_TRUE; 2264224090Sdougb goto out; 2265224090Sdougb } 2266224090Sdougb 2267224090Sdougb result = dns_message_firstname(rcvmsg, section); 2268224090Sdougb if (result != ISC_R_SUCCESS) { 2269224090Sdougb pass++; 2270224090Sdougb goto lookforsoa; 2271224090Sdougb } 2272224090Sdougb while (result == ISC_R_SUCCESS) { 2273224090Sdougb name = NULL; 2274224090Sdougb dns_message_currentname(rcvmsg, section, &name); 2275224090Sdougb soaset = NULL; 2276224090Sdougb result = dns_message_findtype(name, dns_rdatatype_soa, 0, 2277224090Sdougb &soaset); 2278224090Sdougb if (result == ISC_R_SUCCESS) 2279224090Sdougb break; 2280224090Sdougb if (section == DNS_SECTION_ANSWER) { 2281224090Sdougb dns_rdataset_t *tset = NULL; 2282224090Sdougb if (dns_message_findtype(name, dns_rdatatype_cname, 0, 2283224090Sdougb &tset) == ISC_R_SUCCESS 2284224090Sdougb || 2285224090Sdougb dns_message_findtype(name, dns_rdatatype_dname, 0, 2286224090Sdougb &tset) == ISC_R_SUCCESS 2287224090Sdougb ) 2288224090Sdougb { 2289224090Sdougb seencname = ISC_TRUE; 2290224090Sdougb break; 2291224090Sdougb } 2292224090Sdougb } 2293224090Sdougb 2294224090Sdougb result = dns_message_nextname(rcvmsg, section); 2295224090Sdougb } 2296224090Sdougb 2297224090Sdougb if (soaset == NULL && !seencname) { 2298224090Sdougb pass++; 2299224090Sdougb goto lookforsoa; 2300224090Sdougb } 2301224090Sdougb 2302224090Sdougb if (seencname) { 2303224090Sdougb droplabel = ISC_TRUE; 2304224090Sdougb goto out; 2305224090Sdougb } 2306224090Sdougb 2307224090Sdougb result = process_soa(uctx, soaset, name); 2308224090Sdougb 2309224090Sdougb out: 2310224090Sdougb if (droplabel) { 2311224090Sdougb result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION); 2312224090Sdougb INSIST(result == ISC_R_SUCCESS); 2313224090Sdougb name = NULL; 2314224090Sdougb dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name); 2315224090Sdougb nlabels = dns_name_countlabels(name); 2316224090Sdougb if (nlabels == 1) 2317224090Sdougb result = DNS_R_SERVFAIL; /* is there a better error? */ 2318224090Sdougb else { 2319224090Sdougb dns_name_init(&tname, NULL); 2320224090Sdougb dns_name_getlabelsequence(name, 1, nlabels - 1, 2321224090Sdougb &tname); 2322224090Sdougb dns_name_clone(&tname, name); 2323224090Sdougb dns_request_destroy(&request); 2324224090Sdougb LOCK(&uctx->lock); 2325224090Sdougb uctx->soareq = NULL; 2326224090Sdougb UNLOCK(&uctx->lock); 2327224090Sdougb dns_message_renderreset(soaquery); 2328224090Sdougb dns_message_settsigkey(soaquery, NULL); 2329224090Sdougb result = dns_request_createvia3(uctx->view->requestmgr, 2330224090Sdougb soaquery, NULL, 2331224090Sdougb uctx->currentserver, 0, 2332224090Sdougb uctx->tsigkey, 2333224090Sdougb client->find_timeout * 2334224090Sdougb 20, 2335224090Sdougb client->find_timeout, 2336224090Sdougb 3, client->task, 2337224090Sdougb receive_soa, uctx, 2338224090Sdougb &uctx->soareq); 2339224090Sdougb } 2340224090Sdougb } 2341224090Sdougb 2342224090Sdougb if (!droplabel || result != ISC_R_SUCCESS) { 2343224090Sdougb dns_message_destroy(&uctx->soaquery); 2344224090Sdougb LOCK(&uctx->lock); 2345224090Sdougb dns_request_destroy(&uctx->soareq); 2346224090Sdougb UNLOCK(&uctx->lock); 2347224090Sdougb } 2348224090Sdougb 2349224090Sdougb if (rcvmsg != NULL) 2350224090Sdougb dns_message_destroy(&rcvmsg); 2351224090Sdougb 2352224090Sdougb if (result != ISC_R_SUCCESS) 2353224090Sdougb update_sendevent(uctx, result); 2354224090Sdougb} 2355224090Sdougb 2356224090Sdougbstatic isc_result_t 2357224090Sdougbrequest_soa(updatectx_t *uctx) { 2358224090Sdougb isc_result_t result; 2359224090Sdougb dns_message_t *soaquery = uctx->soaquery; 2360224090Sdougb dns_name_t *name = NULL; 2361224090Sdougb dns_rdataset_t *rdataset = NULL; 2362224090Sdougb 2363224090Sdougb if (soaquery == NULL) { 2364224090Sdougb result = dns_message_create(uctx->client->mctx, 2365224090Sdougb DNS_MESSAGE_INTENTRENDER, 2366224090Sdougb &soaquery); 2367224090Sdougb if (result != ISC_R_SUCCESS) 2368224090Sdougb return (result); 2369224090Sdougb } 2370224090Sdougb soaquery->flags |= DNS_MESSAGEFLAG_RD; 2371224090Sdougb result = dns_message_gettempname(soaquery, &name); 2372224090Sdougb if (result != ISC_R_SUCCESS) 2373224090Sdougb goto fail; 2374224090Sdougb result = dns_message_gettemprdataset(soaquery, &rdataset); 2375224090Sdougb if (result != ISC_R_SUCCESS) 2376224090Sdougb goto fail; 2377224090Sdougb dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa); 2378224090Sdougb dns_name_clone(uctx->firstname, name); 2379224090Sdougb ISC_LIST_APPEND(name->list, rdataset, link); 2380224090Sdougb dns_message_addname(soaquery, name, DNS_SECTION_QUESTION); 2381224090Sdougb rdataset = NULL; 2382224090Sdougb name = NULL; 2383224090Sdougb 2384224090Sdougb result = dns_request_createvia3(uctx->view->requestmgr, 2385224090Sdougb soaquery, NULL, uctx->currentserver, 0, 2386224090Sdougb uctx->tsigkey, 2387224090Sdougb uctx->client->find_timeout * 20, 2388224090Sdougb uctx->client->find_timeout, 3, 2389224090Sdougb uctx->client->task, receive_soa, uctx, 2390224090Sdougb &uctx->soareq); 2391224090Sdougb if (result == ISC_R_SUCCESS) { 2392224090Sdougb uctx->soaquery = soaquery; 2393224090Sdougb return (ISC_R_SUCCESS); 2394224090Sdougb } 2395224090Sdougb 2396224090Sdougb fail: 2397224090Sdougb if (rdataset != NULL) { 2398224090Sdougb ISC_LIST_UNLINK(name->list, rdataset, link); /* for safety */ 2399224090Sdougb dns_message_puttemprdataset(soaquery, &rdataset); 2400224090Sdougb } 2401224090Sdougb if (name != NULL) 2402224090Sdougb dns_message_puttempname(soaquery, &name); 2403224090Sdougb dns_message_destroy(&soaquery); 2404224090Sdougb 2405224090Sdougb return (result); 2406224090Sdougb} 2407224090Sdougb 2408224090Sdougbstatic void 2409224090Sdougbresolvesoa_done(isc_task_t *task, isc_event_t *event) { 2410224090Sdougb dns_clientresevent_t *rev = (dns_clientresevent_t *)event; 2411224090Sdougb updatectx_t *uctx; 2412224090Sdougb dns_name_t *name, tname; 2413224090Sdougb dns_rdataset_t *rdataset = NULL; 2414224090Sdougb isc_result_t result = rev->result; 2415224090Sdougb unsigned int nlabels; 2416224090Sdougb 2417224090Sdougb UNUSED(task); 2418224090Sdougb 2419224090Sdougb uctx = event->ev_arg; 2420224090Sdougb REQUIRE(UCTX_VALID(uctx)); 2421224090Sdougb 2422224090Sdougb LOCK(&uctx->lock); 2423224090Sdougb dns_client_destroyrestrans(&uctx->restrans); 2424224090Sdougb UNLOCK(&uctx->lock); 2425224090Sdougb 2426224090Sdougb uctx = event->ev_arg; 2427224090Sdougb if (result != ISC_R_SUCCESS && 2428224090Sdougb result != DNS_R_NCACHENXDOMAIN && 2429224090Sdougb result != DNS_R_NCACHENXRRSET) { 2430224090Sdougb /* XXX: what about DNSSEC failure? */ 2431224090Sdougb goto out; 2432224090Sdougb } 2433224090Sdougb 2434224090Sdougb for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; 2435224090Sdougb name = ISC_LIST_NEXT(name, link)) { 2436224090Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 2437224090Sdougb rdataset != NULL; 2438224090Sdougb rdataset = ISC_LIST_NEXT(rdataset, link)) { 2439224090Sdougb if (dns_rdataset_isassociated(rdataset) && 2440224090Sdougb rdataset->type == dns_rdatatype_soa) 2441224090Sdougb break; 2442224090Sdougb } 2443224090Sdougb } 2444224090Sdougb 2445224090Sdougb if (rdataset == NULL) { 2446224090Sdougb /* Drop one label and retry resolution. */ 2447224090Sdougb nlabels = dns_name_countlabels(&uctx->soaqname); 2448224090Sdougb if (nlabels == 1) { 2449224090Sdougb result = DNS_R_SERVFAIL; /* is there a better error? */ 2450224090Sdougb goto out; 2451224090Sdougb } 2452224090Sdougb dns_name_init(&tname, NULL); 2453224090Sdougb dns_name_getlabelsequence(&uctx->soaqname, 1, nlabels - 1, 2454224090Sdougb &tname); 2455224090Sdougb dns_name_clone(&tname, &uctx->soaqname); 2456224090Sdougb 2457224090Sdougb result = dns_client_startresolve(uctx->client, &uctx->soaqname, 2458224090Sdougb uctx->rdclass, 2459224090Sdougb dns_rdatatype_soa, 0, 2460224090Sdougb uctx->client->task, 2461224090Sdougb resolvesoa_done, uctx, 2462224090Sdougb &uctx->restrans); 2463224090Sdougb } else 2464224090Sdougb result = process_soa(uctx, rdataset, &uctx->soaqname); 2465224090Sdougb 2466224090Sdougb out: 2467224090Sdougb dns_client_freeresanswer(uctx->client, &rev->answerlist); 2468224090Sdougb isc_event_free(&event); 2469224090Sdougb 2470224090Sdougb if (result != ISC_R_SUCCESS) 2471224090Sdougb update_sendevent(uctx, result); 2472224090Sdougb} 2473224090Sdougb 2474224090Sdougbstatic isc_result_t 2475224090Sdougbcopy_name(isc_mem_t *mctx, dns_message_t *msg, dns_name_t *name, 2476224090Sdougb dns_name_t **newnamep) 2477224090Sdougb{ 2478224090Sdougb isc_result_t result; 2479224090Sdougb dns_name_t *newname = NULL; 2480224090Sdougb isc_region_t r; 2481224090Sdougb isc_buffer_t *namebuf = NULL, *rdatabuf = NULL; 2482224090Sdougb dns_rdatalist_t *rdatalist; 2483224090Sdougb dns_rdataset_t *rdataset, *newrdataset; 2484224090Sdougb dns_rdata_t rdata = DNS_RDATA_INIT, *newrdata; 2485224090Sdougb 2486224090Sdougb result = dns_message_gettempname(msg, &newname); 2487224090Sdougb if (result != ISC_R_SUCCESS) 2488224090Sdougb return (result); 2489224090Sdougb result = isc_buffer_allocate(mctx, &namebuf, DNS_NAME_MAXWIRE); 2490224090Sdougb if (result != ISC_R_SUCCESS) 2491224090Sdougb goto fail; 2492224090Sdougb dns_name_init(newname, NULL); 2493224090Sdougb dns_name_setbuffer(newname, namebuf); 2494224090Sdougb dns_message_takebuffer(msg, &namebuf); 2495224090Sdougb result = dns_name_copy(name, newname, NULL); 2496224090Sdougb if (result != ISC_R_SUCCESS) 2497224090Sdougb goto fail; 2498224090Sdougb 2499224090Sdougb for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; 2500224090Sdougb rdataset = ISC_LIST_NEXT(rdataset, link)) { 2501224090Sdougb rdatalist = NULL; 2502224090Sdougb result = dns_message_gettemprdatalist(msg, &rdatalist); 2503224090Sdougb if (result != ISC_R_SUCCESS) 2504224090Sdougb goto fail; 2505224090Sdougb dns_rdatalist_init(rdatalist); 2506224090Sdougb rdatalist->type = rdataset->type; 2507224090Sdougb rdatalist->rdclass = rdataset->rdclass; 2508224090Sdougb rdatalist->covers = rdataset->covers; 2509224090Sdougb rdatalist->ttl = rdataset->ttl; 2510224090Sdougb 2511224090Sdougb result = dns_rdataset_first(rdataset); 2512224090Sdougb while (result == ISC_R_SUCCESS) { 2513224090Sdougb dns_rdata_reset(&rdata); 2514224090Sdougb dns_rdataset_current(rdataset, &rdata); 2515224090Sdougb 2516224090Sdougb newrdata = NULL; 2517224090Sdougb result = dns_message_gettemprdata(msg, &newrdata); 2518224090Sdougb if (result != ISC_R_SUCCESS) 2519224090Sdougb goto fail; 2520224090Sdougb dns_rdata_toregion(&rdata, &r); 2521224090Sdougb rdatabuf = NULL; 2522224090Sdougb result = isc_buffer_allocate(mctx, &rdatabuf, 2523224090Sdougb r.length); 2524224090Sdougb if (result != ISC_R_SUCCESS) 2525224090Sdougb goto fail; 2526224090Sdougb isc_buffer_putmem(rdatabuf, r.base, r.length); 2527224090Sdougb isc_buffer_usedregion(rdatabuf, &r); 2528224090Sdougb dns_rdata_init(newrdata); 2529224090Sdougb dns_rdata_fromregion(newrdata, rdata.rdclass, 2530224090Sdougb rdata.type, &r); 2531224090Sdougb newrdata->flags = rdata.flags; 2532224090Sdougb 2533224090Sdougb ISC_LIST_APPEND(rdatalist->rdata, newrdata, link); 2534224090Sdougb dns_message_takebuffer(msg, &rdatabuf); 2535224090Sdougb 2536224090Sdougb result = dns_rdataset_next(rdataset); 2537224090Sdougb } 2538224090Sdougb 2539224090Sdougb newrdataset = NULL; 2540224090Sdougb result = dns_message_gettemprdataset(msg, &newrdataset); 2541224090Sdougb if (result != ISC_R_SUCCESS) 2542224090Sdougb goto fail; 2543224090Sdougb dns_rdataset_init(newrdataset); 2544224090Sdougb dns_rdatalist_tordataset(rdatalist, newrdataset); 2545224090Sdougb 2546224090Sdougb ISC_LIST_APPEND(newname->list, newrdataset, link); 2547224090Sdougb } 2548224090Sdougb 2549224090Sdougb *newnamep = newname; 2550224090Sdougb 2551224090Sdougb return (ISC_R_SUCCESS); 2552224090Sdougb 2553224090Sdougb fail: 2554224090Sdougb dns_message_puttempname(msg, &newname); 2555224090Sdougb 2556224090Sdougb return (result); 2557224090Sdougb 2558224090Sdougb} 2559224090Sdougb 2560224090Sdougbstatic void 2561224090Sdougbinternal_update_callback(isc_task_t *task, isc_event_t *event) { 2562224090Sdougb updatearg_t *uarg = event->ev_arg; 2563224090Sdougb dns_clientupdateevent_t *uev = (dns_clientupdateevent_t *)event; 2564224090Sdougb 2565224090Sdougb UNUSED(task); 2566224090Sdougb 2567224090Sdougb LOCK(&uarg->lock); 2568224090Sdougb 2569224090Sdougb uarg->result = uev->result; 2570224090Sdougb 2571224090Sdougb dns_client_destroyupdatetrans(&uarg->trans); 2572224090Sdougb isc_event_free(&event); 2573224090Sdougb 2574224090Sdougb if (!uarg->canceled) { 2575224090Sdougb UNLOCK(&uarg->lock); 2576224090Sdougb 2577224090Sdougb /* Exit from the internal event loop */ 2578224090Sdougb isc_app_ctxsuspend(uarg->actx); 2579224090Sdougb } else { 2580224090Sdougb /* 2581224090Sdougb * We have already exited from the loop (due to some 2582224090Sdougb * unexpected event). Just clean the arg up. 2583224090Sdougb */ 2584224090Sdougb UNLOCK(&uarg->lock); 2585224090Sdougb DESTROYLOCK(&uarg->lock); 2586224090Sdougb isc_mem_put(uarg->client->mctx, uarg, sizeof(*uarg)); 2587224090Sdougb } 2588224090Sdougb} 2589224090Sdougb 2590224090Sdougbisc_result_t 2591224090Sdougbdns_client_update(dns_client_t *client, dns_rdataclass_t rdclass, 2592224090Sdougb dns_name_t *zonename, dns_namelist_t *prerequisites, 2593224090Sdougb dns_namelist_t *updates, isc_sockaddrlist_t *servers, 2594224090Sdougb dns_tsec_t *tsec, unsigned int options) 2595224090Sdougb{ 2596224090Sdougb isc_result_t result; 2597224090Sdougb isc_appctx_t *actx; 2598224090Sdougb updatearg_t *uarg; 2599224090Sdougb 2600224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 2601224090Sdougb 2602224090Sdougb if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 && 2603224090Sdougb (options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) { 2604224090Sdougb /* 2605224090Sdougb * If the client is run under application's control, we need 2606224090Sdougb * to create a new running (sub)environment for this 2607224090Sdougb * particular resolution. 2608224090Sdougb */ 2609224090Sdougb return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */ 2610224090Sdougb } else 2611224090Sdougb actx = client->actx; 2612224090Sdougb 2613224090Sdougb uarg = isc_mem_get(client->mctx, sizeof(*uarg)); 2614224090Sdougb if (uarg == NULL) 2615224090Sdougb return (ISC_R_NOMEMORY); 2616224090Sdougb 2617224090Sdougb result = isc_mutex_init(&uarg->lock); 2618224090Sdougb if (result != ISC_R_SUCCESS) { 2619224090Sdougb isc_mem_put(client->mctx, uarg, sizeof(*uarg)); 2620224090Sdougb return (result); 2621224090Sdougb } 2622224090Sdougb 2623224090Sdougb uarg->actx = actx; 2624224090Sdougb uarg->client = client; 2625224090Sdougb uarg->result = ISC_R_FAILURE; 2626224090Sdougb uarg->trans = NULL; 2627224090Sdougb uarg->canceled = ISC_FALSE; 2628224090Sdougb 2629224090Sdougb result = dns_client_startupdate(client, rdclass, zonename, 2630224090Sdougb prerequisites, updates, servers, 2631224090Sdougb tsec, options, client->task, 2632224090Sdougb internal_update_callback, uarg, 2633224090Sdougb &uarg->trans); 2634224090Sdougb if (result != ISC_R_SUCCESS) { 2635224090Sdougb DESTROYLOCK(&uarg->lock); 2636224090Sdougb isc_mem_put(client->mctx, uarg, sizeof(*uarg)); 2637224090Sdougb return (result); 2638224090Sdougb } 2639224090Sdougb 2640224090Sdougb /* 2641224090Sdougb * Start internal event loop. It blocks until the entire process 2642224090Sdougb * is completed. 2643224090Sdougb */ 2644224090Sdougb result = isc_app_ctxrun(actx); 2645224090Sdougb 2646224090Sdougb LOCK(&uarg->lock); 2647224090Sdougb if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND) 2648224090Sdougb result = uarg->result; 2649224090Sdougb 2650224090Sdougb if (uarg->trans != NULL) { 2651224090Sdougb /* 2652224090Sdougb * Unusual termination (perhaps due to signal). We need some 2653224090Sdougb * tricky cleanup process. 2654224090Sdougb */ 2655224090Sdougb uarg->canceled = ISC_TRUE; 2656224090Sdougb dns_client_cancelupdate(uarg->trans); 2657224090Sdougb 2658224090Sdougb UNLOCK(&uarg->lock); 2659224090Sdougb 2660224090Sdougb /* uarg will be freed in the event handler. */ 2661224090Sdougb } else { 2662224090Sdougb UNLOCK(&uarg->lock); 2663224090Sdougb 2664224090Sdougb DESTROYLOCK(&uarg->lock); 2665224090Sdougb isc_mem_put(client->mctx, uarg, sizeof(*uarg)); 2666224090Sdougb } 2667224090Sdougb 2668224090Sdougb return (result); 2669224090Sdougb} 2670224090Sdougb 2671224090Sdougbisc_result_t 2672224090Sdougbdns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass, 2673224090Sdougb dns_name_t *zonename, dns_namelist_t *prerequisites, 2674224090Sdougb dns_namelist_t *updates, isc_sockaddrlist_t *servers, 2675224090Sdougb dns_tsec_t *tsec, unsigned int options, 2676224090Sdougb isc_task_t *task, isc_taskaction_t action, void *arg, 2677224090Sdougb dns_clientupdatetrans_t **transp) 2678224090Sdougb{ 2679224090Sdougb dns_view_t *view = NULL; 2680224090Sdougb isc_result_t result; 2681224090Sdougb dns_name_t *name, *newname; 2682224090Sdougb updatectx_t *uctx; 2683224090Sdougb isc_task_t *clone = NULL; 2684224090Sdougb dns_section_t section = DNS_SECTION_UPDATE; 2685224090Sdougb isc_sockaddr_t *server, *sa = NULL; 2686224090Sdougb dns_tsectype_t tsectype = dns_tsectype_none; 2687224090Sdougb 2688224090Sdougb UNUSED(options); 2689224090Sdougb 2690224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 2691224090Sdougb REQUIRE(transp != NULL && *transp == NULL); 2692224090Sdougb REQUIRE(updates != NULL); 2693224090Sdougb REQUIRE(task != NULL); 2694224090Sdougb 2695224090Sdougb if (tsec != NULL) { 2696224090Sdougb tsectype = dns_tsec_gettype(tsec); 2697224090Sdougb if (tsectype != dns_tsectype_tsig) 2698224090Sdougb return (ISC_R_NOTIMPLEMENTED); /* XXX */ 2699224090Sdougb } 2700224090Sdougb 2701224090Sdougb LOCK(&client->lock); 2702224090Sdougb result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME, 2703224090Sdougb rdclass, &view); 2704224090Sdougb UNLOCK(&client->lock); 2705224090Sdougb if (result != ISC_R_SUCCESS) 2706224090Sdougb return (result); 2707224090Sdougb 2708224090Sdougb /* Create a context and prepare some resources */ 2709224090Sdougb uctx = isc_mem_get(client->mctx, sizeof(*uctx)); 2710224090Sdougb if (uctx == NULL) { 2711224090Sdougb dns_view_detach(&view); 2712224090Sdougb return (ISC_R_NOMEMORY); 2713224090Sdougb } 2714224090Sdougb result = isc_mutex_init(&uctx->lock); 2715224090Sdougb if (result != ISC_R_SUCCESS) { 2716224090Sdougb dns_view_detach(&view); 2717224090Sdougb isc_mem_put(client->mctx, uctx, sizeof(*uctx)); 2718224090Sdougb return (ISC_R_NOMEMORY); 2719224090Sdougb } 2720224090Sdougb clone = NULL; 2721224090Sdougb isc_task_attach(task, &clone); 2722224090Sdougb uctx->client = client; 2723224090Sdougb ISC_LINK_INIT(uctx, link); 2724224090Sdougb uctx->state = dns_clientupdatestate_prepare; 2725224090Sdougb uctx->view = view; 2726224090Sdougb uctx->rdclass = rdclass; 2727224090Sdougb uctx->canceled = ISC_FALSE; 2728224090Sdougb uctx->updatemsg = NULL; 2729224090Sdougb uctx->soaquery = NULL; 2730224090Sdougb uctx->updatereq = NULL; 2731224090Sdougb uctx->restrans = NULL; 2732224090Sdougb uctx->restrans2 = NULL; 2733224090Sdougb uctx->bp4 = NULL; 2734224090Sdougb uctx->bp6 = NULL; 2735224090Sdougb uctx->soareq = NULL; 2736224090Sdougb uctx->event = NULL; 2737224090Sdougb uctx->tsigkey = NULL; 2738224090Sdougb uctx->sig0key = NULL; 2739224090Sdougb uctx->zonename = NULL; 2740224090Sdougb dns_name_init(&uctx->soaqname, NULL); 2741224090Sdougb ISC_LIST_INIT(uctx->servers); 2742224090Sdougb uctx->nservers = 0; 2743224090Sdougb uctx->currentserver = NULL; 2744224090Sdougb dns_fixedname_init(&uctx->zonefname); 2745224090Sdougb if (tsec != NULL) 2746224090Sdougb dns_tsec_getkey(tsec, &uctx->tsigkey); 2747224090Sdougb uctx->event = (dns_clientupdateevent_t *) 2748224090Sdougb isc_event_allocate(client->mctx, clone, DNS_EVENT_UPDATEDONE, 2749224090Sdougb action, arg, sizeof(*uctx->event)); 2750224090Sdougb if (uctx->event == NULL) 2751224090Sdougb goto fail; 2752224090Sdougb if (zonename != NULL) { 2753224090Sdougb uctx->zonename = dns_fixedname_name(&uctx->zonefname); 2754224090Sdougb result = dns_name_copy(zonename, uctx->zonename, NULL); 2755224090Sdougb } 2756224090Sdougb if (servers != NULL) { 2757224090Sdougb for (server = ISC_LIST_HEAD(*servers); 2758224090Sdougb server != NULL; 2759224090Sdougb server = ISC_LIST_NEXT(server, link)) { 2760224090Sdougb sa = isc_mem_get(client->mctx, sizeof(*sa)); 2761224090Sdougb if (sa == NULL) 2762224090Sdougb goto fail; 2763224090Sdougb sa->type = server->type; 2764224090Sdougb sa->length = server->length; 2765224090Sdougb ISC_LINK_INIT(sa, link); 2766224090Sdougb ISC_LIST_APPEND(uctx->servers, sa, link); 2767224090Sdougb if (uctx->currentserver == NULL) 2768224090Sdougb uctx->currentserver = sa; 2769224090Sdougb uctx->nservers++; 2770224090Sdougb } 2771224090Sdougb } 2772224090Sdougb 2773224090Sdougb /* Make update message */ 2774224090Sdougb result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTRENDER, 2775224090Sdougb &uctx->updatemsg); 2776224090Sdougb if (result != ISC_R_SUCCESS) 2777224090Sdougb goto fail; 2778224090Sdougb uctx->updatemsg->opcode = dns_opcode_update; 2779224090Sdougb 2780224090Sdougb if (prerequisites != NULL) { 2781224090Sdougb for (name = ISC_LIST_HEAD(*prerequisites); name != NULL; 2782224090Sdougb name = ISC_LIST_NEXT(name, link)) { 2783224090Sdougb newname = NULL; 2784224090Sdougb result = copy_name(client->mctx, uctx->updatemsg, 2785224090Sdougb name, &newname); 2786224090Sdougb if (result != ISC_R_SUCCESS) 2787224090Sdougb goto fail; 2788224090Sdougb dns_message_addname(uctx->updatemsg, newname, 2789224090Sdougb DNS_SECTION_PREREQUISITE); 2790224090Sdougb } 2791224090Sdougb } 2792224090Sdougb 2793224090Sdougb for (name = ISC_LIST_HEAD(*updates); name != NULL; 2794224090Sdougb name = ISC_LIST_NEXT(name, link)) { 2795224090Sdougb newname = NULL; 2796224090Sdougb result = copy_name(client->mctx, uctx->updatemsg, name, 2797224090Sdougb &newname); 2798224090Sdougb if (result != ISC_R_SUCCESS) 2799224090Sdougb goto fail; 2800224090Sdougb dns_message_addname(uctx->updatemsg, newname, 2801224090Sdougb DNS_SECTION_UPDATE); 2802224090Sdougb } 2803224090Sdougb 2804224090Sdougb uctx->firstname = NULL; 2805224090Sdougb result = dns_message_firstname(uctx->updatemsg, section); 2806224090Sdougb if (result == ISC_R_NOMORE) { 2807224090Sdougb section = DNS_SECTION_PREREQUISITE; 2808224090Sdougb result = dns_message_firstname(uctx->updatemsg, section); 2809224090Sdougb } 2810224090Sdougb if (result != ISC_R_SUCCESS) 2811224090Sdougb goto fail; 2812224090Sdougb dns_message_currentname(uctx->updatemsg, section, &uctx->firstname); 2813224090Sdougb 2814224090Sdougb uctx->magic = UCTX_MAGIC; 2815224090Sdougb 2816224090Sdougb LOCK(&client->lock); 2817224090Sdougb ISC_LIST_APPEND(client->updatectxs, uctx, link); 2818224090Sdougb UNLOCK(&client->lock); 2819224090Sdougb 2820224090Sdougb if (uctx->zonename != NULL && uctx->currentserver != NULL) { 2821224090Sdougb result = send_update(uctx); 2822224090Sdougb if (result != ISC_R_SUCCESS) 2823224090Sdougb goto fail; 2824224090Sdougb } else if (uctx->currentserver != NULL) { 2825224090Sdougb result = request_soa(uctx); 2826224090Sdougb if (result != ISC_R_SUCCESS) 2827224090Sdougb goto fail; 2828224090Sdougb } else { 2829224090Sdougb dns_name_clone(uctx->firstname, &uctx->soaqname); 2830224090Sdougb result = dns_client_startresolve(uctx->client, &uctx->soaqname, 2831224090Sdougb uctx->rdclass, 2832224090Sdougb dns_rdatatype_soa, 0, 2833224090Sdougb client->task, resolvesoa_done, 2834224090Sdougb uctx, &uctx->restrans); 2835224090Sdougb if (result != ISC_R_SUCCESS) 2836224090Sdougb goto fail; 2837224090Sdougb } 2838224090Sdougb 2839224090Sdougb *transp = (dns_clientupdatetrans_t *)uctx; 2840224090Sdougb 2841224090Sdougb return (ISC_R_SUCCESS); 2842224090Sdougb 2843224090Sdougb fail: 2844224090Sdougb if (ISC_LINK_LINKED(uctx, link)) { 2845224090Sdougb LOCK(&client->lock); 2846224090Sdougb ISC_LIST_UNLINK(client->updatectxs, uctx, link); 2847224090Sdougb UNLOCK(&client->lock); 2848224090Sdougb } 2849224090Sdougb if (uctx->updatemsg != NULL) 2850224090Sdougb dns_message_destroy(&uctx->updatemsg); 2851224090Sdougb while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) { 2852224090Sdougb ISC_LIST_UNLINK(uctx->servers, sa, link); 2853224090Sdougb isc_mem_put(client->mctx, sa, sizeof(*sa)); 2854224090Sdougb } 2855224090Sdougb if (uctx->event != NULL) 2856224090Sdougb isc_event_free(ISC_EVENT_PTR(&uctx->event)); 2857224090Sdougb if (uctx->tsigkey != NULL) 2858224090Sdougb dns_tsigkey_detach(&uctx->tsigkey); 2859224090Sdougb isc_task_detach(&clone); 2860224090Sdougb DESTROYLOCK(&uctx->lock); 2861224090Sdougb uctx->magic = 0; 2862224090Sdougb isc_mem_put(client->mctx, uctx, sizeof(*uctx)); 2863224090Sdougb dns_view_detach(&view); 2864224090Sdougb 2865224090Sdougb return (result); 2866224090Sdougb} 2867224090Sdougb 2868224090Sdougbvoid 2869224090Sdougbdns_client_cancelupdate(dns_clientupdatetrans_t *trans) { 2870224090Sdougb updatectx_t *uctx; 2871224090Sdougb 2872224090Sdougb REQUIRE(trans != NULL); 2873224090Sdougb uctx = (updatectx_t *)trans; 2874224090Sdougb REQUIRE(UCTX_VALID(uctx)); 2875224090Sdougb 2876224090Sdougb LOCK(&uctx->lock); 2877224090Sdougb 2878224090Sdougb if (!uctx->canceled) { 2879224090Sdougb uctx->canceled = ISC_TRUE; 2880224090Sdougb if (uctx->updatereq != NULL) 2881224090Sdougb dns_request_cancel(uctx->updatereq); 2882224090Sdougb if (uctx->soareq != NULL) 2883224090Sdougb dns_request_cancel(uctx->soareq); 2884224090Sdougb if (uctx->restrans != NULL) 2885224090Sdougb dns_client_cancelresolve(uctx->restrans); 2886224090Sdougb if (uctx->restrans2 != NULL) 2887224090Sdougb dns_client_cancelresolve(uctx->restrans2); 2888224090Sdougb } 2889224090Sdougb 2890224090Sdougb UNLOCK(&uctx->lock); 2891224090Sdougb} 2892224090Sdougb 2893224090Sdougbvoid 2894224090Sdougbdns_client_destroyupdatetrans(dns_clientupdatetrans_t **transp) { 2895224090Sdougb updatectx_t *uctx; 2896224090Sdougb isc_mem_t *mctx; 2897224090Sdougb dns_client_t *client; 2898224090Sdougb isc_boolean_t need_destroyclient = ISC_FALSE; 2899224090Sdougb isc_sockaddr_t *sa; 2900224090Sdougb 2901224090Sdougb REQUIRE(transp != NULL); 2902224090Sdougb uctx = (updatectx_t *)*transp; 2903224090Sdougb REQUIRE(UCTX_VALID(uctx)); 2904224090Sdougb client = uctx->client; 2905224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 2906224090Sdougb REQUIRE(uctx->updatereq == NULL && uctx->updatemsg == NULL && 2907224090Sdougb uctx->soareq == NULL && uctx->soaquery == NULL && 2908224090Sdougb uctx->event == NULL && uctx->tsigkey == NULL && 2909224090Sdougb uctx->sig0key == NULL); 2910224090Sdougb 2911224090Sdougb mctx = client->mctx; 2912224090Sdougb dns_view_detach(&uctx->view); 2913224090Sdougb while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) { 2914224090Sdougb ISC_LIST_UNLINK(uctx->servers, sa, link); 2915224090Sdougb isc_mem_put(mctx, sa, sizeof(*sa)); 2916224090Sdougb } 2917224090Sdougb 2918224090Sdougb LOCK(&client->lock); 2919224090Sdougb 2920224090Sdougb INSIST(ISC_LINK_LINKED(uctx, link)); 2921224090Sdougb ISC_LIST_UNLINK(client->updatectxs, uctx, link); 2922224090Sdougb 2923224090Sdougb if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) && 2924224090Sdougb ISC_LIST_EMPTY(client->reqctxs) && 2925224090Sdougb ISC_LIST_EMPTY(client->updatectxs)) 2926224090Sdougb need_destroyclient = ISC_TRUE; 2927224090Sdougb 2928224090Sdougb UNLOCK(&client->lock); 2929224090Sdougb 2930224090Sdougb DESTROYLOCK(&uctx->lock); 2931224090Sdougb uctx->magic = 0; 2932224090Sdougb 2933224090Sdougb isc_mem_put(mctx, uctx, sizeof(*uctx)); 2934224090Sdougb 2935224090Sdougb if (need_destroyclient) 2936224090Sdougb destroyclient(&client); 2937224090Sdougb 2938224090Sdougb *transp = NULL; 2939224090Sdougb} 2940224090Sdougb 2941224090Sdougbisc_mem_t * 2942224090Sdougbdns_client_mctx(dns_client_t *client) { 2943224090Sdougb 2944224090Sdougb REQUIRE(DNS_CLIENT_VALID(client)); 2945224090Sdougb return (client->mctx); 2946224090Sdougb} 2947224090Sdougb 2948224090Sdougbtypedef struct { 2949224090Sdougb isc_buffer_t buffer; 2950224090Sdougb dns_rdataset_t rdataset; 2951224090Sdougb dns_rdatalist_t rdatalist; 2952224090Sdougb dns_rdata_t rdata; 2953224090Sdougb size_t size; 2954224090Sdougb isc_mem_t * mctx; 2955224090Sdougb unsigned char data[FLEXIBLE_ARRAY_MEMBER]; 2956224090Sdougb} dns_client_updaterec_t; 2957224090Sdougb 2958224090Sdougbisc_result_t 2959224090Sdougbdns_client_updaterec(dns_client_updateop_t op, dns_name_t *owner, 2960224090Sdougb dns_rdatatype_t type, dns_rdata_t *source, 2961224090Sdougb dns_ttl_t ttl, dns_name_t *target, 2962224090Sdougb dns_rdataset_t *rdataset, dns_rdatalist_t *rdatalist, 2963224090Sdougb dns_rdata_t *rdata, isc_mem_t *mctx) 2964224090Sdougb{ 2965224090Sdougb dns_client_updaterec_t *updaterec = NULL; 2966224090Sdougb size_t size = offsetof(dns_client_updaterec_t, data); 2967224090Sdougb 2968224090Sdougb REQUIRE(op < updateop_max); 2969224090Sdougb REQUIRE(owner != NULL); 2970224090Sdougb REQUIRE((rdataset != NULL && rdatalist != NULL && rdata != NULL) || 2971224090Sdougb (rdataset == NULL && rdatalist == NULL && rdata == NULL && 2972224090Sdougb mctx != NULL)); 2973224090Sdougb if (op == updateop_add) 2974224090Sdougb REQUIRE(source != NULL); 2975224090Sdougb if (source != NULL) { 2976224090Sdougb REQUIRE(source->type == type); 2977224090Sdougb REQUIRE(op == updateop_add || op == updateop_delete || 2978224090Sdougb op == updateop_exist); 2979224090Sdougb } 2980224090Sdougb 2981224090Sdougb size += owner->length; 2982224090Sdougb if (source != NULL) 2983224090Sdougb size += source->length; 2984224090Sdougb 2985224090Sdougb if (rdataset == NULL) { 2986224090Sdougb updaterec = isc_mem_get(mctx, size); 2987224090Sdougb if (updaterec == NULL) 2988224090Sdougb return (ISC_R_NOMEMORY); 2989224090Sdougb rdataset = &updaterec->rdataset; 2990224090Sdougb rdatalist = &updaterec->rdatalist; 2991224090Sdougb rdata = &updaterec->rdata; 2992224090Sdougb dns_rdataset_init(rdataset); 2993224090Sdougb dns_rdatalist_init(&updaterec->rdatalist); 2994224090Sdougb dns_rdata_init(&updaterec->rdata); 2995224090Sdougb isc_buffer_init(&updaterec->buffer, updaterec->data, 2996224090Sdougb size - offsetof(dns_client_updaterec_t, data)); 2997224090Sdougb dns_name_copy(owner, target, &updaterec->buffer); 2998224090Sdougb if (source != NULL) { 2999224090Sdougb isc_region_t r; 3000224090Sdougb dns_rdata_clone(source, rdata); 3001224090Sdougb dns_rdata_toregion(rdata, &r); 3002224090Sdougb rdata->data = isc_buffer_used(&updaterec->buffer); 3003224090Sdougb isc_buffer_copyregion(&updaterec->buffer, &r); 3004224090Sdougb } 3005224090Sdougb updaterec->mctx = NULL; 3006224090Sdougb isc_mem_attach(mctx, &updaterec->mctx); 3007224090Sdougb } else if (source != NULL) 3008224090Sdougb dns_rdata_clone(source, rdata); 3009224090Sdougb 3010224090Sdougb switch (op) { 3011224090Sdougb case updateop_add: 3012224090Sdougb break; 3013224090Sdougb case updateop_delete: 3014224090Sdougb if (source != NULL) { 3015224090Sdougb ttl = 0; 3016224090Sdougb dns_rdata_makedelete(rdata); 3017224090Sdougb } else 3018224090Sdougb dns_rdata_deleterrset(rdata, type); 3019224090Sdougb break; 3020224090Sdougb case updateop_notexist: 3021224090Sdougb dns_rdata_notexist(rdata, type); 3022224090Sdougb break; 3023224090Sdougb case updateop_exist: 3024224090Sdougb if (source == NULL) { 3025224090Sdougb ttl = 0; 3026224090Sdougb dns_rdata_exists(rdata, type); 3027224090Sdougb } 3028224090Sdougb case updateop_none: 3029224090Sdougb break; 3030224090Sdougb default: 3031224090Sdougb INSIST(0); 3032224090Sdougb } 3033224090Sdougb 3034224090Sdougb rdatalist->type = rdata->type; 3035224090Sdougb rdatalist->rdclass = rdata->rdclass; 3036224090Sdougb if (source != NULL) { 3037224090Sdougb rdatalist->covers = dns_rdata_covers(rdata); 3038224090Sdougb rdatalist->ttl = ttl; 3039224090Sdougb } 3040224090Sdougb ISC_LIST_APPEND(rdatalist->rdata, rdata, link); 3041224090Sdougb dns_rdatalist_tordataset(rdatalist, rdataset); 3042224090Sdougb ISC_LIST_APPEND(target->list, rdataset, link); 3043224090Sdougb if (updaterec != NULL) { 3044224090Sdougb target->attributes |= DNS_NAMEATTR_HASUPDATEREC; 3045224090Sdougb dns_name_setbuffer(target, &updaterec->buffer); 3046224090Sdougb } 3047224090Sdougb if (op == updateop_add || op == updateop_delete) 3048224090Sdougb target->attributes |= DNS_NAMEATTR_UPDATE; 3049224090Sdougb else 3050224090Sdougb target->attributes |= DNS_NAMEATTR_PREREQUISITE; 3051224090Sdougb return (ISC_R_SUCCESS); 3052224090Sdougb} 3053224090Sdougb 3054224090Sdougbvoid 3055224090Sdougbdns_client_freeupdate(dns_name_t **namep) { 3056224090Sdougb dns_client_updaterec_t *updaterec; 3057224090Sdougb dns_rdatalist_t *rdatalist; 3058224090Sdougb dns_rdataset_t *rdataset; 3059224090Sdougb dns_rdata_t *rdata; 3060224090Sdougb dns_name_t *name; 3061224090Sdougb 3062224090Sdougb REQUIRE(namep != NULL && *namep != NULL); 3063224090Sdougb 3064224090Sdougb name = *namep; 3065224090Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 3066224090Sdougb rdataset != NULL; 3067224090Sdougb rdataset = ISC_LIST_HEAD(name->list)) { 3068224090Sdougb ISC_LIST_UNLINK(name->list, rdataset, link); 3069224090Sdougb rdatalist = NULL; 3070224090Sdougb dns_rdatalist_fromrdataset(rdataset, &rdatalist); 3071224090Sdougb if (rdatalist == NULL) { 3072224090Sdougb dns_rdataset_disassociate(rdataset); 3073224090Sdougb continue; 3074224090Sdougb } 3075224090Sdougb for (rdata = ISC_LIST_HEAD(rdatalist->rdata); 3076224090Sdougb rdata != NULL; 3077224090Sdougb rdata = ISC_LIST_HEAD(rdatalist->rdata)) 3078224090Sdougb ISC_LIST_UNLINK(rdatalist->rdata, rdata, link); 3079224090Sdougb dns_rdataset_disassociate(rdataset); 3080224090Sdougb } 3081224090Sdougb 3082224090Sdougb if ((name->attributes & DNS_NAMEATTR_HASUPDATEREC) != 0) { 3083224090Sdougb updaterec = (dns_client_updaterec_t *)name->buffer; 3084224090Sdougb INSIST(updaterec != NULL); 3085224090Sdougb isc_mem_putanddetach(&updaterec->mctx, updaterec, 3086224090Sdougb updaterec->size); 3087224090Sdougb *namep = NULL; 3088224090Sdougb } 3089224090Sdougb} 3090