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