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