]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/lib/dns/client.c
Update BIND to 9.9.8
[FreeBSD/stable/9.git] / contrib / bind9 / lib / dns / client.c
1 /*
2  * Copyright (C) 2009-2013, 2015  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <config.h>
18
19 #include <stddef.h>
20
21 #include <isc/app.h>
22 #include <isc/mem.h>
23 #include <isc/mutex.h>
24 #include <isc/safe.h>
25 #include <isc/sockaddr.h>
26 #include <isc/socket.h>
27 #include <isc/task.h>
28 #include <isc/timer.h>
29 #include <isc/util.h>
30
31 #include <dns/adb.h>
32 #include <dns/client.h>
33 #include <dns/db.h>
34 #include <dns/dispatch.h>
35 #include <dns/events.h>
36 #include <dns/forward.h>
37 #include <dns/keytable.h>
38 #include <dns/message.h>
39 #include <dns/name.h>
40 #include <dns/rdata.h>
41 #include <dns/rdatalist.h>
42 #include <dns/rdataset.h>
43 #include <dns/rdatatype.h>
44 #include <dns/rdatasetiter.h>
45 #include <dns/rdatastruct.h>
46 #include <dns/request.h>
47 #include <dns/resolver.h>
48 #include <dns/result.h>
49 #include <dns/tsec.h>
50 #include <dns/tsig.h>
51 #include <dns/view.h>
52
53 #include <dst/dst.h>
54
55 #define DNS_CLIENT_MAGIC                ISC_MAGIC('D', 'N', 'S', 'c')
56 #define DNS_CLIENT_VALID(c)             ISC_MAGIC_VALID(c, DNS_CLIENT_MAGIC)
57
58 #define RCTX_MAGIC                      ISC_MAGIC('R', 'c', 't', 'x')
59 #define RCTX_VALID(c)                   ISC_MAGIC_VALID(c, RCTX_MAGIC)
60
61 #define REQCTX_MAGIC                    ISC_MAGIC('R', 'q', 'c', 'x')
62 #define REQCTX_VALID(c)                 ISC_MAGIC_VALID(c, REQCTX_MAGIC)
63
64 #define UCTX_MAGIC                      ISC_MAGIC('U', 'c', 't', 'x')
65 #define UCTX_VALID(c)                   ISC_MAGIC_VALID(c, UCTX_MAGIC)
66
67 #define MAX_RESTARTS 16
68
69 /*%
70  * DNS client object
71  */
72 struct dns_client {
73         /* Unlocked */
74         unsigned int                    magic;
75         unsigned int                    attributes;
76         isc_mutex_t                     lock;
77         isc_mem_t                       *mctx;
78         isc_appctx_t                    *actx;
79         isc_taskmgr_t                   *taskmgr;
80         isc_task_t                      *task;
81         isc_socketmgr_t                 *socketmgr;
82         isc_timermgr_t                  *timermgr;
83         dns_dispatchmgr_t               *dispatchmgr;
84         dns_dispatch_t                  *dispatchv4;
85         dns_dispatch_t                  *dispatchv6;
86
87         unsigned int                    update_timeout;
88         unsigned int                    update_udptimeout;
89         unsigned int                    update_udpretries;
90         unsigned int                    find_timeout;
91         unsigned int                    find_udpretries;
92
93         /* Locked */
94         unsigned int                    references;
95         dns_viewlist_t                  viewlist;
96         ISC_LIST(struct resctx)         resctxs;
97         ISC_LIST(struct reqctx)         reqctxs;
98         ISC_LIST(struct updatectx)      updatectxs;
99 };
100
101 /*%
102  * Timeout/retry constants for dynamic update borrowed from nsupdate
103  */
104 #define DEF_UPDATE_TIMEOUT      300
105 #define MIN_UPDATE_TIMEOUT      30
106 #define DEF_UPDATE_UDPTIMEOUT   3
107 #define DEF_UPDATE_UDPRETRIES   3
108
109 #define DEF_FIND_TIMEOUT        5
110 #define DEF_FIND_UDPRETRIES     3
111
112 #define DNS_CLIENTATTR_OWNCTX                   0x01
113
114 #define DNS_CLIENTVIEW_NAME                     "dnsclient"
115
116 /*%
117  * Internal state for a single name resolution procedure
118  */
119 typedef struct resctx {
120         /* Unlocked */
121         unsigned int            magic;
122         isc_mutex_t             lock;
123         dns_client_t            *client;
124         isc_boolean_t           want_dnssec;
125
126         /* Locked */
127         ISC_LINK(struct resctx) link;
128         isc_task_t              *task;
129         dns_view_t              *view;
130         unsigned int            restarts;
131         dns_fixedname_t         name;
132         dns_rdatatype_t         type;
133         dns_fetch_t             *fetch;
134         dns_namelist_t          namelist;
135         isc_result_t            result;
136         dns_clientresevent_t    *event;
137         isc_boolean_t           canceled;
138         dns_rdataset_t          *rdataset;
139         dns_rdataset_t          *sigrdataset;
140 } resctx_t;
141
142 /*%
143  * Argument of an internal event for synchronous name resolution.
144  */
145 typedef struct resarg {
146         /* Unlocked */
147         isc_appctx_t            *actx;
148         dns_client_t            *client;
149         isc_mutex_t             lock;
150
151         /* Locked */
152         isc_result_t            result;
153         isc_result_t            vresult;
154         dns_namelist_t          *namelist;
155         dns_clientrestrans_t    *trans;
156         isc_boolean_t           canceled;
157 } resarg_t;
158
159 /*%
160  * Internal state for a single DNS request
161  */
162 typedef struct reqctx {
163         /* Unlocked */
164         unsigned int            magic;
165         isc_mutex_t             lock;
166         dns_client_t            *client;
167         unsigned int            parseoptions;
168
169         /* Locked */
170         ISC_LINK(struct reqctx) link;
171         isc_boolean_t           canceled;
172         dns_tsigkey_t           *tsigkey;
173         dns_request_t           *request;
174         dns_clientreqevent_t    *event;
175 } reqctx_t;
176
177 /*%
178  * Argument of an internal event for synchronous DNS request.
179  */
180 typedef struct reqarg {
181         /* Unlocked */
182         isc_appctx_t            *actx;
183         dns_client_t            *client;
184         isc_mutex_t             lock;
185
186         /* Locked */
187         isc_result_t            result;
188         dns_clientreqtrans_t    *trans;
189         isc_boolean_t           canceled;
190 } reqarg_t;
191
192 /*%
193  * Argument of an internal event for synchronous name resolution.
194  */
195 typedef struct updatearg {
196         /* Unlocked */
197         isc_appctx_t            *actx;
198         dns_client_t            *client;
199         isc_mutex_t             lock;
200
201         /* Locked */
202         isc_result_t            result;
203         dns_clientupdatetrans_t *trans;
204         isc_boolean_t           canceled;
205 } updatearg_t;
206
207 /*%
208  * Internal state for a single dynamic update procedure
209  */
210 typedef struct updatectx {
211         /* Unlocked */
212         unsigned int                    magic;
213         isc_mutex_t                     lock;
214         dns_client_t                    *client;
215
216         /* Locked */
217         dns_request_t                   *updatereq;
218         dns_request_t                   *soareq;
219         dns_clientrestrans_t            *restrans;
220         dns_clientrestrans_t            *restrans2;
221         isc_boolean_t                   canceled;
222
223         /* Task Locked */
224         ISC_LINK(struct updatectx)      link;
225         dns_clientupdatestate_t         state;
226         dns_rdataclass_t                rdclass;
227         dns_view_t                      *view;
228         dns_message_t                   *updatemsg;
229         dns_message_t                   *soaquery;
230         dns_clientupdateevent_t         *event;
231         dns_tsigkey_t                   *tsigkey;
232         dst_key_t                       *sig0key;
233         dns_name_t                      *firstname;
234         dns_name_t                      soaqname;
235         dns_fixedname_t                 zonefname;
236         dns_name_t                      *zonename;
237         isc_sockaddrlist_t              servers;
238         unsigned int                    nservers;
239         isc_sockaddr_t                  *currentserver;
240         struct updatectx                *bp4;
241         struct updatectx                *bp6;
242 } updatectx_t;
243
244 static isc_result_t request_soa(updatectx_t *uctx);
245 static void client_resfind(resctx_t *rctx, dns_fetchevent_t *event);
246 static isc_result_t send_update(updatectx_t *uctx);
247
248 static isc_result_t
249 getudpdispatch(int family, dns_dispatchmgr_t *dispatchmgr,
250                isc_socketmgr_t *socketmgr, isc_taskmgr_t *taskmgr,
251                isc_boolean_t is_shared, dns_dispatch_t **dispp,
252                isc_sockaddr_t *localaddr)
253 {
254         unsigned int attrs, attrmask;
255         dns_dispatch_t *disp;
256         unsigned buffersize, maxbuffers, maxrequests, buckets, increment;
257         isc_result_t result;
258         isc_sockaddr_t anyaddr;
259
260         attrs = 0;
261         attrs |= DNS_DISPATCHATTR_UDP;
262         switch (family) {
263         case AF_INET:
264                 attrs |= DNS_DISPATCHATTR_IPV4;
265                 break;
266         case AF_INET6:
267                 attrs |= DNS_DISPATCHATTR_IPV6;
268                 break;
269         default:
270                 INSIST(0);
271         }
272         attrmask = 0;
273         attrmask |= DNS_DISPATCHATTR_UDP;
274         attrmask |= DNS_DISPATCHATTR_TCP;
275         attrmask |= DNS_DISPATCHATTR_IPV4;
276         attrmask |= DNS_DISPATCHATTR_IPV6;
277
278         if (localaddr == NULL) {
279                 localaddr = &anyaddr;
280                 isc_sockaddr_anyofpf(localaddr, family);
281         }
282
283         buffersize = 4096;
284         maxbuffers = is_shared ? 1000 : 8;
285         maxrequests = 32768;
286         buckets = is_shared ? 16411 : 3;
287         increment = is_shared ? 16433 : 5;
288
289         disp = NULL;
290         result = dns_dispatch_getudp(dispatchmgr, socketmgr,
291                                      taskmgr, localaddr,
292                                      buffersize, maxbuffers, maxrequests,
293                                      buckets, increment,
294                                      attrs, attrmask, &disp);
295         if (result == ISC_R_SUCCESS)
296                 *dispp = disp;
297
298         return (result);
299 }
300
301 static isc_result_t
302 dns_client_createview(isc_mem_t *mctx, dns_rdataclass_t rdclass,
303                       unsigned int options, isc_taskmgr_t *taskmgr,
304                       unsigned int ntasks, isc_socketmgr_t *socketmgr,
305                       isc_timermgr_t *timermgr, dns_dispatchmgr_t *dispatchmgr,
306                       dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6,
307                       dns_view_t **viewp)
308 {
309         isc_result_t result;
310         dns_view_t *view = NULL;
311         const char *dbtype;
312
313         result = dns_view_create(mctx, rdclass, DNS_CLIENTVIEW_NAME, &view);
314         if (result != ISC_R_SUCCESS)
315                 return (result);
316
317         /* Initialize view security roots */
318         result = dns_view_initsecroots(view, mctx);
319         if (result != ISC_R_SUCCESS) {
320                 dns_view_detach(&view);
321                 return (result);
322         }
323
324         result = dns_view_createresolver(view, taskmgr, ntasks, 1, socketmgr,
325                                          timermgr, 0, dispatchmgr,
326                                          dispatchv4, dispatchv6);
327         if (result != ISC_R_SUCCESS) {
328                 dns_view_detach(&view);
329                 return (result);
330         }
331
332         /*
333          * Set cache DB.
334          * XXX: it may be better if specific DB implementations can be
335          * specified via some configuration knob.
336          */
337         if ((options & DNS_CLIENTCREATEOPT_USECACHE) != 0)
338                 dbtype = "rbt";
339         else
340                 dbtype = "ecdb";
341         result = dns_db_create(mctx, dbtype, dns_rootname, dns_dbtype_cache,
342                                rdclass, 0, NULL, &view->cachedb);
343         if (result != ISC_R_SUCCESS) {
344                 dns_view_detach(&view);
345                 return (result);
346         }
347
348         *viewp = view;
349         return (ISC_R_SUCCESS);
350 }
351
352 isc_result_t
353 dns_client_create(dns_client_t **clientp, unsigned int options) {
354         isc_result_t result;
355         isc_mem_t *mctx = NULL;
356         isc_appctx_t *actx = NULL;
357         isc_taskmgr_t *taskmgr = NULL;
358         isc_socketmgr_t *socketmgr = NULL;
359         isc_timermgr_t *timermgr = NULL;
360 #if 0
361         /* XXXMPA add debug logging support */
362         isc_log_t *lctx = NULL;
363         isc_logconfig_t *logconfig = NULL;
364         unsigned int logdebuglevel = 0;
365 #endif
366
367         result = isc_mem_create(0, 0, &mctx);
368         if (result != ISC_R_SUCCESS)
369                 return (result);
370         result = isc_appctx_create(mctx, &actx);
371         if (result != ISC_R_SUCCESS)
372                 goto cleanup;
373         result = isc_app_ctxstart(actx);
374         if (result != ISC_R_SUCCESS)
375                 goto cleanup;
376         result = isc_taskmgr_createinctx(mctx, actx, 1, 0, &taskmgr);
377         if (result != ISC_R_SUCCESS)
378                 goto cleanup;
379         result = isc_socketmgr_createinctx(mctx, actx, &socketmgr);
380         if (result != ISC_R_SUCCESS)
381                 goto cleanup;
382         result = isc_timermgr_createinctx(mctx, actx, &timermgr);
383         if (result != ISC_R_SUCCESS)
384                 goto cleanup;
385 #if 0
386         result = isc_log_create(mctx, &lctx, &logconfig);
387         if (result != ISC_R_SUCCESS)
388                 goto cleanup;
389         isc_log_setcontext(lctx);
390         dns_log_init(lctx);
391         dns_log_setcontext(lctx);
392         result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL);
393         if (result != ISC_R_SUCCESS)
394                 goto cleanup;
395         isc_log_setdebuglevel(lctx, logdebuglevel);
396 #endif
397         result = dns_client_createx(mctx, actx, taskmgr, socketmgr, timermgr,
398                                     options, clientp);
399         if (result != ISC_R_SUCCESS)
400                 goto cleanup;
401
402         (*clientp)->attributes |= DNS_CLIENTATTR_OWNCTX;
403
404         /* client has its own reference to mctx, so we can detach it here */
405         isc_mem_detach(&mctx);
406
407         return (ISC_R_SUCCESS);
408
409  cleanup:
410         if (taskmgr != NULL)
411                 isc_taskmgr_destroy(&taskmgr);
412         if (timermgr != NULL)
413                 isc_timermgr_destroy(&timermgr);
414         if (socketmgr != NULL)
415                 isc_socketmgr_destroy(&socketmgr);
416         if (actx != NULL)
417                 isc_appctx_destroy(&actx);
418         isc_mem_detach(&mctx);
419
420         return (result);
421 }
422
423 isc_result_t
424 dns_client_createx(isc_mem_t *mctx, isc_appctx_t *actx, isc_taskmgr_t *taskmgr,
425                    isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr,
426                    unsigned int options, dns_client_t **clientp)
427 {
428         isc_result_t result;
429         result = dns_client_createx2(mctx, actx, taskmgr, socketmgr, timermgr,
430                                      options, clientp, NULL, NULL);
431         return (result);
432 }
433
434 isc_result_t
435 dns_client_createx2(isc_mem_t *mctx, isc_appctx_t *actx,
436                     isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr,
437                     isc_timermgr_t *timermgr, unsigned int options,
438                     dns_client_t **clientp, isc_sockaddr_t *localaddr4,
439                     isc_sockaddr_t *localaddr6)
440 {
441         dns_client_t *client;
442         isc_result_t result;
443         dns_dispatchmgr_t *dispatchmgr = NULL;
444         dns_dispatch_t *dispatchv4 = NULL;
445         dns_dispatch_t *dispatchv6 = NULL;
446         dns_view_t *view = NULL;
447
448         REQUIRE(mctx != NULL);
449         REQUIRE(taskmgr != NULL);
450         REQUIRE(timermgr != NULL);
451         REQUIRE(socketmgr != NULL);
452         REQUIRE(clientp != NULL && *clientp == NULL);
453
454         client = isc_mem_get(mctx, sizeof(*client));
455         if (client == NULL)
456                 return (ISC_R_NOMEMORY);
457
458         result = isc_mutex_init(&client->lock);
459         if (result != ISC_R_SUCCESS) {
460                 isc_mem_put(mctx, client, sizeof(*client));
461                 return (result);
462         }
463
464         client->actx = actx;
465         client->taskmgr = taskmgr;
466         client->socketmgr = socketmgr;
467         client->timermgr = timermgr;
468
469         client->task = NULL;
470         result = isc_task_create(client->taskmgr, 0, &client->task);
471         if (result != ISC_R_SUCCESS)
472                 goto cleanup;
473
474         result = dns_dispatchmgr_create(mctx, NULL, &dispatchmgr);
475         if (result != ISC_R_SUCCESS)
476                 goto cleanup;
477         client->dispatchmgr = dispatchmgr;
478
479         /*
480          * If only one address family is specified, use it.
481          * If neither family is specified, or if both are, use both.
482          */
483         client->dispatchv4 = NULL;
484         if (localaddr4 != NULL || localaddr6 == NULL) {
485                 result = getudpdispatch(AF_INET, dispatchmgr, socketmgr,
486                                         taskmgr, ISC_TRUE,
487                                         &dispatchv4, localaddr4);
488                 if (result == ISC_R_SUCCESS)
489                         client->dispatchv4 = dispatchv4;
490         }
491
492         client->dispatchv6 = NULL;
493         if (localaddr6 != NULL || localaddr4 == NULL) {
494                 result = getudpdispatch(AF_INET6, dispatchmgr, socketmgr,
495                                         taskmgr, ISC_TRUE,
496                                         &dispatchv6, localaddr6);
497                 if (result == ISC_R_SUCCESS)
498                         client->dispatchv6 = dispatchv6;
499         }
500
501         /* We need at least one of the dispatchers */
502         if (dispatchv4 == NULL && dispatchv6 == NULL) {
503                 INSIST(result != ISC_R_SUCCESS);
504                 goto cleanup;
505         }
506
507         /* Create the default view for class IN */
508         result = dns_client_createview(mctx, dns_rdataclass_in, options,
509                                        taskmgr, 31, socketmgr, timermgr,
510                                        dispatchmgr, dispatchv4, dispatchv6,
511                                        &view);
512         if (result != ISC_R_SUCCESS)
513                 goto cleanup;
514         ISC_LIST_INIT(client->viewlist);
515         ISC_LIST_APPEND(client->viewlist, view, link);
516
517         dns_view_freeze(view); /* too early? */
518
519         ISC_LIST_INIT(client->resctxs);
520         ISC_LIST_INIT(client->reqctxs);
521         ISC_LIST_INIT(client->updatectxs);
522
523         client->mctx = NULL;
524         isc_mem_attach(mctx, &client->mctx);
525
526         client->update_timeout = DEF_UPDATE_TIMEOUT;
527         client->update_udptimeout = DEF_UPDATE_UDPTIMEOUT;
528         client->update_udpretries = DEF_UPDATE_UDPRETRIES;
529         client->find_timeout = DEF_FIND_TIMEOUT;
530         client->find_udpretries = DEF_FIND_UDPRETRIES;
531         client->attributes = 0;
532
533         client->references = 1;
534         client->magic = DNS_CLIENT_MAGIC;
535
536         *clientp = client;
537
538         return (ISC_R_SUCCESS);
539
540  cleanup:
541         if (dispatchv4 != NULL)
542                 dns_dispatch_detach(&dispatchv4);
543         if (dispatchv6 != NULL)
544                 dns_dispatch_detach(&dispatchv6);
545         if (dispatchmgr != NULL)
546                 dns_dispatchmgr_destroy(&dispatchmgr);
547         if (client->task != NULL)
548                 isc_task_detach(&client->task);
549         isc_mem_put(mctx, client, sizeof(*client));
550
551         return (result);
552 }
553
554 static void
555 destroyclient(dns_client_t **clientp) {
556         dns_client_t *client = *clientp;
557         dns_view_t *view;
558
559         while ((view = ISC_LIST_HEAD(client->viewlist)) != NULL) {
560                 ISC_LIST_UNLINK(client->viewlist, view, link);
561                 dns_view_detach(&view);
562         }
563
564         if (client->dispatchv4 != NULL)
565                 dns_dispatch_detach(&client->dispatchv4);
566         if (client->dispatchv6 != NULL)
567                 dns_dispatch_detach(&client->dispatchv6);
568
569         dns_dispatchmgr_destroy(&client->dispatchmgr);
570
571         isc_task_detach(&client->task);
572
573         /*
574          * If the client has created its own running environments,
575          * destroy them.
576          */
577         if ((client->attributes & DNS_CLIENTATTR_OWNCTX) != 0) {
578                 isc_taskmgr_destroy(&client->taskmgr);
579                 isc_timermgr_destroy(&client->timermgr);
580                 isc_socketmgr_destroy(&client->socketmgr);
581
582                 isc_app_ctxfinish(client->actx);
583                 isc_appctx_destroy(&client->actx);
584         }
585
586         DESTROYLOCK(&client->lock);
587         client->magic = 0;
588
589         isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
590
591         *clientp = NULL;
592 }
593
594 void
595 dns_client_destroy(dns_client_t **clientp) {
596         dns_client_t *client;
597         isc_boolean_t destroyok = ISC_FALSE;
598
599         REQUIRE(clientp != NULL);
600         client = *clientp;
601         REQUIRE(DNS_CLIENT_VALID(client));
602
603         LOCK(&client->lock);
604         client->references--;
605         if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
606             ISC_LIST_EMPTY(client->reqctxs) &&
607             ISC_LIST_EMPTY(client->updatectxs)) {
608                 destroyok = ISC_TRUE;
609         }
610         UNLOCK(&client->lock);
611
612         if (destroyok)
613                 destroyclient(&client);
614
615         *clientp = NULL;
616 }
617
618 isc_result_t
619 dns_client_setservers(dns_client_t *client, dns_rdataclass_t rdclass,
620                       dns_name_t *namespace, isc_sockaddrlist_t *addrs)
621 {
622         isc_result_t result;
623         dns_view_t *view = NULL;
624
625         REQUIRE(DNS_CLIENT_VALID(client));
626         REQUIRE(addrs != NULL);
627
628         if (namespace == NULL)
629                 namespace = dns_rootname;
630
631         LOCK(&client->lock);
632         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
633                                    rdclass, &view);
634         if (result != ISC_R_SUCCESS) {
635                 UNLOCK(&client->lock);
636                 return (result);
637         }
638         UNLOCK(&client->lock);
639
640         result = dns_fwdtable_add(view->fwdtable, namespace, addrs,
641                                   dns_fwdpolicy_only);
642
643         dns_view_detach(&view);
644
645         return (result);
646 }
647
648 isc_result_t
649 dns_client_clearservers(dns_client_t *client, dns_rdataclass_t rdclass,
650                         dns_name_t *namespace)
651 {
652         isc_result_t result;
653         dns_view_t *view = NULL;
654
655         REQUIRE(DNS_CLIENT_VALID(client));
656
657         if (namespace == NULL)
658                 namespace = dns_rootname;
659
660         LOCK(&client->lock);
661         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
662                                    rdclass, &view);
663         if (result != ISC_R_SUCCESS) {
664                 UNLOCK(&client->lock);
665                 return (result);
666         }
667         UNLOCK(&client->lock);
668
669         result = dns_fwdtable_delete(view->fwdtable, namespace);
670
671         dns_view_detach(&view);
672
673         return (result);
674 }
675
676 static isc_result_t
677 getrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) {
678         dns_rdataset_t *rdataset;
679
680         REQUIRE(mctx != NULL);
681         REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
682
683         rdataset = isc_mem_get(mctx, sizeof(*rdataset));
684         if (rdataset == NULL)
685                 return (ISC_R_NOMEMORY);
686
687         dns_rdataset_init(rdataset);
688
689         *rdatasetp = rdataset;
690
691         return (ISC_R_SUCCESS);
692 }
693
694 static void
695 putrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) {
696         dns_rdataset_t *rdataset;
697
698         REQUIRE(rdatasetp != NULL);
699         rdataset = *rdatasetp;
700         REQUIRE(rdataset != NULL);
701
702         if (dns_rdataset_isassociated(rdataset))
703                 dns_rdataset_disassociate(rdataset);
704
705         isc_mem_put(mctx, rdataset, sizeof(*rdataset));
706
707         *rdatasetp = NULL;
708 }
709
710 static void
711 fetch_done(isc_task_t *task, isc_event_t *event) {
712         resctx_t *rctx = event->ev_arg;
713         dns_fetchevent_t *fevent;
714
715         REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
716         REQUIRE(RCTX_VALID(rctx));
717         REQUIRE(rctx->task == task);
718         fevent = (dns_fetchevent_t *)event;
719
720         client_resfind(rctx, fevent);
721 }
722
723 static inline isc_result_t
724 start_fetch(resctx_t *rctx) {
725         isc_result_t result;
726
727         /*
728          * The caller must be holding the rctx's lock.
729          */
730
731         REQUIRE(rctx->fetch == NULL);
732
733         result = dns_resolver_createfetch(rctx->view->resolver,
734                                           dns_fixedname_name(&rctx->name),
735                                           rctx->type,
736                                           NULL, NULL, NULL, 0,
737                                           rctx->task, fetch_done, rctx,
738                                           rctx->rdataset,
739                                           rctx->sigrdataset,
740                                           &rctx->fetch);
741
742         return (result);
743 }
744
745 static isc_result_t
746 view_find(resctx_t *rctx, dns_db_t **dbp, dns_dbnode_t **nodep,
747           dns_name_t *foundname)
748 {
749         isc_result_t result;
750         dns_name_t *name = dns_fixedname_name(&rctx->name);
751         dns_rdatatype_t type;
752
753         if (rctx->type == dns_rdatatype_rrsig)
754                 type = dns_rdatatype_any;
755         else
756                 type = rctx->type;
757
758         result = dns_view_find(rctx->view, name, type, 0, 0, ISC_FALSE,
759                                dbp, nodep, foundname, rctx->rdataset,
760                                rctx->sigrdataset);
761
762         return (result);
763 }
764
765 static void
766 client_resfind(resctx_t *rctx, dns_fetchevent_t *event) {
767         isc_mem_t *mctx;
768         isc_result_t tresult, result = ISC_R_SUCCESS;
769         isc_result_t vresult = ISC_R_SUCCESS;
770         isc_boolean_t want_restart;
771         isc_boolean_t send_event = ISC_FALSE;
772         dns_name_t *name, *prefix;
773         dns_fixedname_t foundname, fixed;
774         dns_rdataset_t *trdataset;
775         dns_rdata_t rdata = DNS_RDATA_INIT;
776         unsigned int nlabels;
777         int order;
778         dns_namereln_t namereln;
779         dns_rdata_cname_t cname;
780         dns_rdata_dname_t dname;
781
782         REQUIRE(RCTX_VALID(rctx));
783
784         LOCK(&rctx->lock);
785
786         mctx = rctx->view->mctx;
787
788         name = dns_fixedname_name(&rctx->name);
789
790         do {
791                 dns_name_t *fname = NULL;
792                 dns_name_t *ansname = NULL;
793                 dns_db_t *db = NULL;
794                 dns_dbnode_t *node = NULL;
795
796                 rctx->restarts++;
797                 want_restart = ISC_FALSE;
798
799                 if (event == NULL && !rctx->canceled) {
800                         dns_fixedname_init(&foundname);
801                         fname = dns_fixedname_name(&foundname);
802                         INSIST(!dns_rdataset_isassociated(rctx->rdataset));
803                         INSIST(rctx->sigrdataset == NULL ||
804                                !dns_rdataset_isassociated(rctx->sigrdataset));
805                         result = view_find(rctx, &db, &node, fname);
806                         if (result == ISC_R_NOTFOUND) {
807                                 /*
808                                  * We don't know anything about the name.
809                                  * Launch a fetch.
810                                  */
811                                 if (node != NULL) {
812                                         INSIST(db != NULL);
813                                         dns_db_detachnode(db, &node);
814                                 }
815                                 if (db != NULL)
816                                         dns_db_detach(&db);
817                                 result = start_fetch(rctx);
818                                 if (result != ISC_R_SUCCESS) {
819                                         putrdataset(mctx, &rctx->rdataset);
820                                         if (rctx->sigrdataset != NULL)
821                                                 putrdataset(mctx,
822                                                             &rctx->sigrdataset);
823                                         send_event = ISC_TRUE;
824                                 }
825                                 goto done;
826                         }
827                 } else {
828                         INSIST(event != NULL);
829                         INSIST(event->fetch == rctx->fetch);
830                         dns_resolver_destroyfetch(&rctx->fetch);
831                         db = event->db;
832                         node = event->node;
833                         result = event->result;
834                         vresult = event->vresult;
835                         fname = dns_fixedname_name(&event->foundname);
836                         INSIST(event->rdataset == rctx->rdataset);
837                         INSIST(event->sigrdataset == rctx->sigrdataset);
838                 }
839
840                 /*
841                  * If we've been canceled, forget about the result.
842                  */
843                 if (rctx->canceled)
844                         result = ISC_R_CANCELED;
845                 else {
846                         /*
847                          * Otherwise, get some resource for copying the
848                          * result.
849                          */
850                         ansname = isc_mem_get(mctx, sizeof(*ansname));
851                         if (ansname == NULL)
852                                 tresult = ISC_R_NOMEMORY;
853                         else {
854                                 dns_name_t *aname;
855
856                                 aname = dns_fixedname_name(&rctx->name);
857                                 dns_name_init(ansname, NULL);
858                                 tresult = dns_name_dup(aname, mctx, ansname);
859                                 if (tresult != ISC_R_SUCCESS)
860                                         isc_mem_put(mctx, ansname,
861                                                     sizeof(*ansname));
862                         }
863                         if (tresult != ISC_R_SUCCESS)
864                                 result = tresult;
865                 }
866
867                 switch (result) {
868                 case ISC_R_SUCCESS:
869                         send_event = ISC_TRUE;
870                         /*
871                          * This case is handled in the main line below.
872                          */
873                         break;
874                 case DNS_R_CNAME:
875                         /*
876                          * Add the CNAME to the answer list.
877                          */
878                         trdataset = rctx->rdataset;
879                         ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
880                         rctx->rdataset = NULL;
881                         if (rctx->sigrdataset != NULL) {
882                                 ISC_LIST_APPEND(ansname->list,
883                                                 rctx->sigrdataset, link);
884                                 rctx->sigrdataset = NULL;
885                         }
886                         ISC_LIST_APPEND(rctx->namelist, ansname, link);
887                         ansname = NULL;
888
889                         /*
890                          * Copy the CNAME's target into the lookup's
891                          * query name and start over.
892                          */
893                         tresult = dns_rdataset_first(trdataset);
894                         if (tresult != ISC_R_SUCCESS)
895                                 goto done;
896                         dns_rdataset_current(trdataset, &rdata);
897                         tresult = dns_rdata_tostruct(&rdata, &cname, NULL);
898                         dns_rdata_reset(&rdata);
899                         if (tresult != ISC_R_SUCCESS)
900                                 goto done;
901                         tresult = dns_name_copy(&cname.cname, name, NULL);
902                         dns_rdata_freestruct(&cname);
903                         if (tresult == ISC_R_SUCCESS)
904                                 want_restart = ISC_TRUE;
905                         else
906                                 result = tresult;
907                         goto done;
908                 case DNS_R_DNAME:
909                         /*
910                          * Add the DNAME to the answer list.
911                          */
912                         trdataset = rctx->rdataset;
913                         ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
914                         rctx->rdataset = NULL;
915                         if (rctx->sigrdataset != NULL) {
916                                 ISC_LIST_APPEND(ansname->list,
917                                                 rctx->sigrdataset, link);
918                                 rctx->sigrdataset = NULL;
919                         }
920                         ISC_LIST_APPEND(rctx->namelist, ansname, link);
921                         ansname = NULL;
922
923                         namereln = dns_name_fullcompare(name, fname, &order,
924                                                         &nlabels);
925                         INSIST(namereln == dns_namereln_subdomain);
926                         /*
927                          * Get the target name of the DNAME.
928                          */
929                         tresult = dns_rdataset_first(trdataset);
930                         if (tresult != ISC_R_SUCCESS) {
931                                 result = tresult;
932                                 goto done;
933                         }
934                         dns_rdataset_current(trdataset, &rdata);
935                         tresult = dns_rdata_tostruct(&rdata, &dname, NULL);
936                         dns_rdata_reset(&rdata);
937                         if (tresult != ISC_R_SUCCESS) {
938                                 result = tresult;
939                                 goto done;
940                         }
941                         /*
942                          * Construct the new query name and start over.
943                          */
944                         dns_fixedname_init(&fixed);
945                         prefix = dns_fixedname_name(&fixed);
946                         dns_name_split(name, nlabels, prefix, NULL);
947                         tresult = dns_name_concatenate(prefix, &dname.dname,
948                                                       name, NULL);
949                         dns_rdata_freestruct(&dname);
950                         if (tresult == ISC_R_SUCCESS)
951                                 want_restart = ISC_TRUE;
952                         else
953                                 result = tresult;
954                         goto done;
955                 case DNS_R_NCACHENXDOMAIN:
956                 case DNS_R_NCACHENXRRSET:
957                         ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
958                         ISC_LIST_APPEND(rctx->namelist, ansname, link);
959                         ansname = NULL;
960                         rctx->rdataset = NULL;
961                         /* What about sigrdataset? */
962                         if (rctx->sigrdataset != NULL)
963                                 putrdataset(mctx, &rctx->sigrdataset);
964                         send_event = ISC_TRUE;
965                         goto done;
966                 default:
967                         if (rctx->rdataset != NULL)
968                                 putrdataset(mctx, &rctx->rdataset);
969                         if (rctx->sigrdataset != NULL)
970                                 putrdataset(mctx, &rctx->sigrdataset);
971                         send_event = ISC_TRUE;
972                         goto done;
973                 }
974
975                 if (rctx->type == dns_rdatatype_any) {
976                         int n = 0;
977                         dns_rdatasetiter_t *rdsiter = NULL;
978
979                         tresult = dns_db_allrdatasets(db, node, NULL, 0,
980                                                       &rdsiter);
981                         if (tresult != ISC_R_SUCCESS) {
982                                 result = tresult;
983                                 goto done;
984                         }
985
986                         tresult = dns_rdatasetiter_first(rdsiter);
987                         while (tresult == ISC_R_SUCCESS) {
988                                 dns_rdatasetiter_current(rdsiter,
989                                                          rctx->rdataset);
990                                 if (rctx->rdataset->type != 0) {
991                                         ISC_LIST_APPEND(ansname->list,
992                                                         rctx->rdataset,
993                                                         link);
994                                         n++;
995                                         rctx->rdataset = NULL;
996                                 } else {
997                                         /*
998                                          * We're not interested in this
999                                          * rdataset.
1000                                          */
1001                                         dns_rdataset_disassociate(
1002                                                 rctx->rdataset);
1003                                 }
1004                                 tresult = dns_rdatasetiter_next(rdsiter);
1005
1006                                 if (tresult == ISC_R_SUCCESS &&
1007                                     rctx->rdataset == NULL) {
1008                                         tresult = getrdataset(mctx,
1009                                                               &rctx->rdataset);
1010                                         if (tresult != ISC_R_SUCCESS) {
1011                                                 result = tresult;
1012                                                 POST(result);
1013                                                 break;
1014                                         }
1015                                 }
1016                         }
1017                         if (n == 0) {
1018                                 /*
1019                                  * We didn't match any rdatasets (which means
1020                                  * something went wrong in this
1021                                  * implementation).
1022                                  */
1023                                 result = DNS_R_SERVFAIL; /* better code? */
1024                                 POST(result);
1025                         } else {
1026                                 ISC_LIST_APPEND(rctx->namelist, ansname, link);
1027                                 ansname = NULL;
1028                         }
1029                         dns_rdatasetiter_destroy(&rdsiter);
1030                         if (tresult != ISC_R_NOMORE)
1031                                 result = DNS_R_SERVFAIL; /* ditto */
1032                         else
1033                                 result = ISC_R_SUCCESS;
1034                         goto done;
1035                 } else {
1036                         /*
1037                          * This is the "normal" case -- an ordinary question
1038                          * to which we've got the answer.
1039                          */
1040                         ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
1041                         rctx->rdataset = NULL;
1042                         if (rctx->sigrdataset != NULL) {
1043                                 ISC_LIST_APPEND(ansname->list,
1044                                                 rctx->sigrdataset, link);
1045                                 rctx->sigrdataset = NULL;
1046                         }
1047                         ISC_LIST_APPEND(rctx->namelist, ansname, link);
1048                         ansname = NULL;
1049                 }
1050
1051         done:
1052                 /*
1053                  * Free temporary resources
1054                  */
1055                 if (ansname != NULL) {
1056                         dns_rdataset_t *rdataset;
1057
1058                         while ((rdataset = ISC_LIST_HEAD(ansname->list))
1059                                != NULL) {
1060                                 ISC_LIST_UNLINK(ansname->list, rdataset, link);
1061                                 putrdataset(mctx, &rdataset);
1062                         }
1063                         dns_name_free(ansname, mctx);
1064                         isc_mem_put(mctx, ansname, sizeof(*ansname));
1065                 }
1066
1067                 if (node != NULL)
1068                         dns_db_detachnode(db, &node);
1069                 if (db != NULL)
1070                         dns_db_detach(&db);
1071                 if (event != NULL)
1072                         isc_event_free(ISC_EVENT_PTR(&event));
1073
1074                 /*
1075                  * Limit the number of restarts.
1076                  */
1077                 if (want_restart && rctx->restarts == MAX_RESTARTS) {
1078                         want_restart = ISC_FALSE;
1079                         result = ISC_R_QUOTA;
1080                         send_event = ISC_TRUE;
1081                 }
1082
1083                 /*
1084                  * Prepare further find with new resources
1085                  */
1086                 if (want_restart) {
1087                         INSIST(rctx->rdataset == NULL &&
1088                                rctx->sigrdataset == NULL);
1089
1090                         result = getrdataset(mctx, &rctx->rdataset);
1091                         if (result == ISC_R_SUCCESS && rctx->want_dnssec) {
1092                                 result = getrdataset(mctx, &rctx->sigrdataset);
1093                                 if (result != ISC_R_SUCCESS) {
1094                                         putrdataset(mctx, &rctx->rdataset);
1095                                 }
1096                         }
1097
1098                         if (result != ISC_R_SUCCESS) {
1099                                 want_restart = ISC_FALSE;
1100                                 send_event = ISC_TRUE;
1101                         }
1102                 }
1103         } while (want_restart);
1104
1105         if (send_event) {
1106                 isc_task_t *task;
1107
1108                 while ((name = ISC_LIST_HEAD(rctx->namelist)) != NULL) {
1109                         ISC_LIST_UNLINK(rctx->namelist, name, link);
1110                         ISC_LIST_APPEND(rctx->event->answerlist, name, link);
1111                 }
1112
1113                 rctx->event->result = result;
1114                 rctx->event->vresult = vresult;
1115                 task = rctx->event->ev_sender;
1116                 rctx->event->ev_sender = rctx;
1117                 isc_task_sendanddetach(&task, ISC_EVENT_PTR(&rctx->event));
1118         }
1119
1120         UNLOCK(&rctx->lock);
1121 }
1122
1123
1124 static void
1125 suspend(isc_task_t *task, isc_event_t *event) {
1126         isc_appctx_t *actx = event->ev_arg;
1127
1128         UNUSED(task);
1129
1130         isc_app_ctxsuspend(actx);
1131         isc_event_free(&event);
1132 }
1133
1134 static void
1135 resolve_done(isc_task_t *task, isc_event_t *event) {
1136         resarg_t *resarg = event->ev_arg;
1137         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
1138         dns_name_t *name;
1139         isc_result_t result;
1140
1141         UNUSED(task);
1142
1143         LOCK(&resarg->lock);
1144
1145         resarg->result = rev->result;
1146         resarg->vresult = rev->vresult;
1147         while ((name = ISC_LIST_HEAD(rev->answerlist)) != NULL) {
1148                 ISC_LIST_UNLINK(rev->answerlist, name, link);
1149                 ISC_LIST_APPEND(*resarg->namelist, name, link);
1150         }
1151
1152         dns_client_destroyrestrans(&resarg->trans);
1153         isc_event_free(&event);
1154
1155         if (!resarg->canceled) {
1156                 UNLOCK(&resarg->lock);
1157
1158                 /*
1159                  * We may or may not be running.  isc__appctx_onrun will
1160                  * fail if we are currently running otherwise we post a
1161                  * action to call isc_app_ctxsuspend when we do start
1162                  * running.
1163                  */
1164                 result = isc_app_ctxonrun(resarg->actx, resarg->client->mctx,
1165                                            task, suspend, resarg->actx);
1166                 if (result == ISC_R_ALREADYRUNNING)
1167                         isc_app_ctxsuspend(resarg->actx);
1168         } else {
1169                 /*
1170                  * We have already exited from the loop (due to some
1171                  * unexpected event).  Just clean the arg up.
1172                  */
1173                 UNLOCK(&resarg->lock);
1174                 DESTROYLOCK(&resarg->lock);
1175                 isc_mem_put(resarg->client->mctx, resarg, sizeof(*resarg));
1176         }
1177 }
1178
1179 isc_result_t
1180 dns_client_resolve(dns_client_t *client, dns_name_t *name,
1181                    dns_rdataclass_t rdclass, dns_rdatatype_t type,
1182                    unsigned int options, dns_namelist_t *namelist)
1183 {
1184         isc_result_t result;
1185         isc_appctx_t *actx;
1186         resarg_t *resarg;
1187
1188         REQUIRE(DNS_CLIENT_VALID(client));
1189         REQUIRE(namelist != NULL && ISC_LIST_EMPTY(*namelist));
1190
1191         if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
1192             (options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) {
1193                 /*
1194                  * If the client is run under application's control, we need
1195                  * to create a new running (sub)environment for this
1196                  * particular resolution.
1197                  */
1198                 return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
1199         } else
1200                 actx = client->actx;
1201
1202         resarg = isc_mem_get(client->mctx, sizeof(*resarg));
1203         if (resarg == NULL)
1204                 return (ISC_R_NOMEMORY);
1205
1206         result = isc_mutex_init(&resarg->lock);
1207         if (result != ISC_R_SUCCESS) {
1208                 isc_mem_put(client->mctx, resarg, sizeof(*resarg));
1209                 return (result);
1210         }
1211
1212         resarg->actx = actx;
1213         resarg->client = client;
1214         resarg->result = DNS_R_SERVFAIL;
1215         resarg->namelist = namelist;
1216         resarg->trans = NULL;
1217         resarg->canceled = ISC_FALSE;
1218         result = dns_client_startresolve(client, name, rdclass, type, options,
1219                                          client->task, resolve_done, resarg,
1220                                          &resarg->trans);
1221         if (result != ISC_R_SUCCESS) {
1222                 DESTROYLOCK(&resarg->lock);
1223                 isc_mem_put(client->mctx, resarg, sizeof(*resarg));
1224                 return (result);
1225         }
1226
1227         /*
1228          * Start internal event loop.  It blocks until the entire process
1229          * is completed.
1230          */
1231         result = isc_app_ctxrun(actx);
1232
1233         LOCK(&resarg->lock);
1234         if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
1235                 result = resarg->result;
1236         if (result != ISC_R_SUCCESS && resarg->vresult != ISC_R_SUCCESS) {
1237                 /*
1238                  * If this lookup failed due to some error in DNSSEC
1239                  * validation, return the validation error code.
1240                  * XXX: or should we pass the validation result separately?
1241                  */
1242                 result = resarg->vresult;
1243         }
1244         if (resarg->trans != NULL) {
1245                 /*
1246                  * Unusual termination (perhaps due to signal).  We need some
1247                  * tricky cleanup process.
1248                  */
1249                 resarg->canceled = ISC_TRUE;
1250                 dns_client_cancelresolve(resarg->trans);
1251
1252                 UNLOCK(&resarg->lock);
1253
1254                 /* resarg will be freed in the event handler. */
1255         } else {
1256                 UNLOCK(&resarg->lock);
1257
1258                 DESTROYLOCK(&resarg->lock);
1259                 isc_mem_put(client->mctx, resarg, sizeof(*resarg));
1260         }
1261
1262         return (result);
1263 }
1264
1265 isc_result_t
1266 dns_client_startresolve(dns_client_t *client, dns_name_t *name,
1267                         dns_rdataclass_t rdclass, dns_rdatatype_t type,
1268                         unsigned int options, isc_task_t *task,
1269                         isc_taskaction_t action, void *arg,
1270                         dns_clientrestrans_t **transp)
1271 {
1272         dns_view_t *view = NULL;
1273         dns_clientresevent_t *event = NULL;
1274         resctx_t *rctx = NULL;
1275         isc_task_t *clone = NULL;
1276         isc_mem_t *mctx;
1277         isc_result_t result;
1278         dns_rdataset_t *rdataset, *sigrdataset;
1279         isc_boolean_t want_dnssec;
1280
1281         REQUIRE(DNS_CLIENT_VALID(client));
1282         REQUIRE(transp != NULL && *transp == NULL);
1283
1284         LOCK(&client->lock);
1285         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
1286                                    rdclass, &view);
1287         UNLOCK(&client->lock);
1288         if (result != ISC_R_SUCCESS)
1289                 return (result);
1290
1291         mctx = client->mctx;
1292         rdataset = NULL;
1293         sigrdataset = NULL;
1294         want_dnssec = ISC_TF((options & DNS_CLIENTRESOPT_NODNSSEC) == 0);
1295
1296         /*
1297          * Prepare some intermediate resources
1298          */
1299         clone = NULL;
1300         isc_task_attach(task, &clone);
1301         event = (dns_clientresevent_t *)
1302                 isc_event_allocate(mctx, clone, DNS_EVENT_CLIENTRESDONE,
1303                                    action, arg, sizeof(*event));
1304         if (event == NULL) {
1305                 result = ISC_R_NOMEMORY;
1306                 goto cleanup;
1307         }
1308         event->result = DNS_R_SERVFAIL;
1309         ISC_LIST_INIT(event->answerlist);
1310
1311         rctx = isc_mem_get(mctx, sizeof(*rctx));
1312         if (rctx == NULL)
1313                 result = ISC_R_NOMEMORY;
1314         else {
1315                 result = isc_mutex_init(&rctx->lock);
1316                 if (result != ISC_R_SUCCESS) {
1317                         isc_mem_put(mctx, rctx, sizeof(*rctx));
1318                         rctx = NULL;
1319                 }
1320         }
1321         if (result != ISC_R_SUCCESS)
1322                 goto cleanup;
1323
1324         result = getrdataset(mctx, &rdataset);
1325         if (result != ISC_R_SUCCESS)
1326                 goto cleanup;
1327         rctx->rdataset = rdataset;
1328
1329         if (want_dnssec) {
1330                 result = getrdataset(mctx, &sigrdataset);
1331                 if (result != ISC_R_SUCCESS)
1332                         goto cleanup;
1333         }
1334         rctx->sigrdataset = sigrdataset;
1335
1336         dns_fixedname_init(&rctx->name);
1337         result = dns_name_copy(name, dns_fixedname_name(&rctx->name), NULL);
1338         if (result != ISC_R_SUCCESS)
1339                 goto cleanup;
1340
1341         rctx->client = client;
1342         ISC_LINK_INIT(rctx, link);
1343         rctx->canceled = ISC_FALSE;
1344         rctx->task = client->task;
1345         rctx->type = type;
1346         rctx->view = view;
1347         rctx->restarts = 0;
1348         rctx->fetch = NULL;
1349         rctx->want_dnssec = want_dnssec;
1350         ISC_LIST_INIT(rctx->namelist);
1351         rctx->event = event;
1352
1353         rctx->magic = RCTX_MAGIC;
1354
1355         LOCK(&client->lock);
1356         ISC_LIST_APPEND(client->resctxs, rctx, link);
1357         UNLOCK(&client->lock);
1358
1359         *transp = (dns_clientrestrans_t *)rctx;
1360         client_resfind(rctx, NULL);
1361
1362         return (ISC_R_SUCCESS);
1363
1364  cleanup:
1365         if (rdataset != NULL)
1366                 putrdataset(client->mctx, &rdataset);
1367         if (sigrdataset != NULL)
1368                 putrdataset(client->mctx, &sigrdataset);
1369         if (rctx != NULL) {
1370                 DESTROYLOCK(&rctx->lock);
1371                 isc_mem_put(mctx, rctx, sizeof(*rctx));
1372         }
1373         if (event != NULL)
1374                 isc_event_free(ISC_EVENT_PTR(&event));
1375         isc_task_detach(&clone);
1376         dns_view_detach(&view);
1377
1378         return (result);
1379 }
1380
1381 void
1382 dns_client_cancelresolve(dns_clientrestrans_t *trans) {
1383         resctx_t *rctx;
1384
1385         REQUIRE(trans != NULL);
1386         rctx = (resctx_t *)trans;
1387         REQUIRE(RCTX_VALID(rctx));
1388
1389         LOCK(&rctx->lock);
1390
1391         if (!rctx->canceled) {
1392                 rctx->canceled = ISC_TRUE;
1393                 if (rctx->fetch != NULL)
1394                         dns_resolver_cancelfetch(rctx->fetch);
1395         }
1396
1397         UNLOCK(&rctx->lock);
1398 }
1399
1400 void
1401 dns_client_freeresanswer(dns_client_t *client, dns_namelist_t *namelist) {
1402         dns_name_t *name;
1403         dns_rdataset_t *rdataset;
1404
1405         REQUIRE(DNS_CLIENT_VALID(client));
1406         REQUIRE(namelist != NULL);
1407
1408         while ((name = ISC_LIST_HEAD(*namelist)) != NULL) {
1409                 ISC_LIST_UNLINK(*namelist, name, link);
1410                 while ((rdataset = ISC_LIST_HEAD(name->list)) != NULL) {
1411                         ISC_LIST_UNLINK(name->list, rdataset, link);
1412                         putrdataset(client->mctx, &rdataset);
1413                 }
1414                 dns_name_free(name, client->mctx);
1415                 isc_mem_put(client->mctx, name, sizeof(*name));
1416         }
1417 }
1418
1419 void
1420 dns_client_destroyrestrans(dns_clientrestrans_t **transp) {
1421         resctx_t *rctx;
1422         isc_mem_t *mctx;
1423         dns_client_t *client;
1424         isc_boolean_t need_destroyclient = ISC_FALSE;
1425
1426         REQUIRE(transp != NULL);
1427         rctx = (resctx_t *)*transp;
1428         REQUIRE(RCTX_VALID(rctx));
1429         REQUIRE(rctx->fetch == NULL);
1430         REQUIRE(rctx->event == NULL);
1431         client = rctx->client;
1432         REQUIRE(DNS_CLIENT_VALID(client));
1433
1434         mctx = client->mctx;
1435         dns_view_detach(&rctx->view);
1436
1437         LOCK(&client->lock);
1438
1439         INSIST(ISC_LINK_LINKED(rctx, link));
1440         ISC_LIST_UNLINK(client->resctxs, rctx, link);
1441
1442         if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
1443             ISC_LIST_EMPTY(client->reqctxs) &&
1444             ISC_LIST_EMPTY(client->updatectxs))
1445                 need_destroyclient = ISC_TRUE;
1446
1447         UNLOCK(&client->lock);
1448
1449         INSIST(ISC_LIST_EMPTY(rctx->namelist));
1450
1451         DESTROYLOCK(&rctx->lock);
1452         rctx->magic = 0;
1453
1454         isc_mem_put(mctx, rctx, sizeof(*rctx));
1455
1456         if (need_destroyclient)
1457                 destroyclient(&client);
1458
1459         *transp = NULL;
1460 }
1461
1462 isc_result_t
1463 dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass,
1464                          dns_name_t *keyname, isc_buffer_t *keydatabuf)
1465 {
1466         isc_result_t result;
1467         dns_view_t *view = NULL;
1468         dst_key_t *dstkey = NULL;
1469         dns_keytable_t *secroots = NULL;
1470
1471         REQUIRE(DNS_CLIENT_VALID(client));
1472
1473         LOCK(&client->lock);
1474         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
1475                                    rdclass, &view);
1476         UNLOCK(&client->lock);
1477         if (result != ISC_R_SUCCESS)
1478                 goto cleanup;
1479
1480         result = dns_view_getsecroots(view, &secroots);
1481         if (result != ISC_R_SUCCESS)
1482                 goto cleanup;
1483
1484         result = dst_key_fromdns(keyname, rdclass, keydatabuf, client->mctx,
1485                                  &dstkey);
1486         if (result != ISC_R_SUCCESS)
1487                 goto cleanup;
1488
1489         result = dns_keytable_add(secroots, ISC_FALSE, &dstkey);
1490
1491  cleanup:
1492         if (dstkey != NULL)
1493                 dst_key_free(&dstkey);
1494         if (view != NULL)
1495                 dns_view_detach(&view);
1496         if (secroots != NULL)
1497                 dns_keytable_detach(&secroots);
1498         return (result);
1499 }
1500
1501 /*%
1502  * Simple request routines
1503  */
1504 static void
1505 request_done(isc_task_t *task, isc_event_t *event) {
1506         dns_requestevent_t *reqev = NULL;
1507         dns_request_t *request;
1508         isc_result_t result, eresult;
1509         reqctx_t *ctx;
1510
1511         UNUSED(task);
1512
1513         REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
1514         reqev = (dns_requestevent_t *)event;
1515         request = reqev->request;
1516         result = eresult = reqev->result;
1517         ctx = reqev->ev_arg;
1518         REQUIRE(REQCTX_VALID(ctx));
1519
1520         isc_event_free(&event);
1521
1522         LOCK(&ctx->lock);
1523
1524         if (eresult == ISC_R_SUCCESS) {
1525                 result = dns_request_getresponse(request, ctx->event->rmessage,
1526                                                  ctx->parseoptions);
1527         }
1528
1529         if (ctx->tsigkey != NULL)
1530                 dns_tsigkey_detach(&ctx->tsigkey);
1531
1532         if (ctx->canceled)
1533                 ctx->event->result = ISC_R_CANCELED;
1534         else
1535                 ctx->event->result = result;
1536         task = ctx->event->ev_sender;
1537         ctx->event->ev_sender = ctx;
1538         isc_task_sendanddetach(&task, ISC_EVENT_PTR(&ctx->event));
1539
1540         UNLOCK(&ctx->lock);
1541 }
1542
1543 static void
1544 localrequest_done(isc_task_t *task, isc_event_t *event) {
1545         reqarg_t *reqarg = event->ev_arg;
1546         dns_clientreqevent_t *rev =(dns_clientreqevent_t *)event;
1547
1548         UNUSED(task);
1549
1550         REQUIRE(event->ev_type == DNS_EVENT_CLIENTREQDONE);
1551
1552         LOCK(&reqarg->lock);
1553
1554         reqarg->result = rev->result;
1555         dns_client_destroyreqtrans(&reqarg->trans);
1556         isc_event_free(&event);
1557
1558         if (!reqarg->canceled) {
1559                 UNLOCK(&reqarg->lock);
1560
1561                 /* Exit from the internal event loop */
1562                 isc_app_ctxsuspend(reqarg->actx);
1563         } else {
1564                 /*
1565                  * We have already exited from the loop (due to some
1566                  * unexpected event).  Just clean the arg up.
1567                  */
1568                 UNLOCK(&reqarg->lock);
1569                 DESTROYLOCK(&reqarg->lock);
1570                 isc_mem_put(reqarg->client->mctx, reqarg, sizeof(*reqarg));
1571         }
1572 }
1573
1574 isc_result_t
1575 dns_client_request(dns_client_t *client, dns_message_t *qmessage,
1576                    dns_message_t *rmessage, isc_sockaddr_t *server,
1577                    unsigned int options, unsigned int parseoptions,
1578                    dns_tsec_t *tsec, unsigned int timeout,
1579                    unsigned int udptimeout, unsigned int udpretries)
1580 {
1581         isc_appctx_t *actx;
1582         reqarg_t *reqarg;
1583         isc_result_t result;
1584
1585         REQUIRE(DNS_CLIENT_VALID(client));
1586         REQUIRE(qmessage != NULL);
1587         REQUIRE(rmessage != NULL);
1588
1589         if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
1590             (options & DNS_CLIENTREQOPT_ALLOWRUN) == 0) {
1591                 /*
1592                  * If the client is run under application's control, we need
1593                  * to create a new running (sub)environment for this
1594                  * particular resolution.
1595                  */
1596                 return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
1597         } else
1598                 actx = client->actx;
1599
1600         reqarg = isc_mem_get(client->mctx, sizeof(*reqarg));
1601         if (reqarg == NULL)
1602                 return (ISC_R_NOMEMORY);
1603
1604         result = isc_mutex_init(&reqarg->lock);
1605         if (result != ISC_R_SUCCESS) {
1606                 isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
1607                 return (result);
1608         }
1609
1610         reqarg->actx = actx;
1611         reqarg->client = client;
1612         reqarg->trans = NULL;
1613         reqarg->canceled = ISC_FALSE;
1614
1615         result = dns_client_startrequest(client, qmessage, rmessage, server,
1616                                          options, parseoptions, tsec, timeout,
1617                                          udptimeout, udpretries,
1618                                          client->task, localrequest_done,
1619                                          reqarg, &reqarg->trans);
1620         if (result != ISC_R_SUCCESS) {
1621                 DESTROYLOCK(&reqarg->lock);
1622                 isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
1623                 return (result);
1624         }
1625
1626         /*
1627          * Start internal event loop.  It blocks until the entire process
1628          * is completed.
1629          */
1630         result = isc_app_ctxrun(actx);
1631
1632         LOCK(&reqarg->lock);
1633         if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
1634                 result = reqarg->result;
1635         if (reqarg->trans != NULL) {
1636                 /*
1637                  * Unusual termination (perhaps due to signal).  We need some
1638                  * tricky cleanup process.
1639                  */
1640                 reqarg->canceled = ISC_TRUE;
1641                 dns_client_cancelresolve(reqarg->trans);
1642
1643                 UNLOCK(&reqarg->lock);
1644
1645                 /* reqarg will be freed in the event handler. */
1646         } else {
1647                 UNLOCK(&reqarg->lock);
1648
1649                 DESTROYLOCK(&reqarg->lock);
1650                 isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
1651         }
1652
1653         return (result);
1654 }
1655
1656 isc_result_t
1657 dns_client_startrequest(dns_client_t *client, dns_message_t *qmessage,
1658                         dns_message_t *rmessage, isc_sockaddr_t *server,
1659                         unsigned int options, unsigned int parseoptions,
1660                         dns_tsec_t *tsec, unsigned int timeout,
1661                         unsigned int udptimeout, unsigned int udpretries,
1662                         isc_task_t *task, isc_taskaction_t action, void *arg,
1663                         dns_clientreqtrans_t **transp)
1664 {
1665         isc_result_t result;
1666         dns_view_t *view = NULL;
1667         isc_task_t *clone = NULL;
1668         dns_clientreqevent_t *event = NULL;
1669         reqctx_t *ctx = NULL;
1670         dns_tsectype_t tsectype = dns_tsectype_none;
1671
1672         UNUSED(options);
1673
1674         REQUIRE(DNS_CLIENT_VALID(client));
1675         REQUIRE(qmessage != NULL);
1676         REQUIRE(rmessage != NULL);
1677         REQUIRE(transp != NULL && *transp == NULL);
1678
1679         if (tsec != NULL) {
1680                 tsectype = dns_tsec_gettype(tsec);
1681                 if (tsectype != dns_tsectype_tsig)
1682                         return (ISC_R_NOTIMPLEMENTED); /* XXX */
1683         }
1684
1685         LOCK(&client->lock);
1686         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
1687                                    qmessage->rdclass, &view);
1688         UNLOCK(&client->lock);
1689         if (result != ISC_R_SUCCESS)
1690                 return (result);
1691
1692         clone = NULL;
1693         isc_task_attach(task, &clone);
1694         event = (dns_clientreqevent_t *)
1695                 isc_event_allocate(client->mctx, clone,
1696                                    DNS_EVENT_CLIENTREQDONE,
1697                                    action, arg, sizeof(*event));
1698         if (event == NULL) {
1699                 result = ISC_R_NOMEMORY;
1700                 goto cleanup;
1701         }
1702
1703         ctx = isc_mem_get(client->mctx, sizeof(*ctx));
1704         if (ctx == NULL)
1705                 result = ISC_R_NOMEMORY;
1706         else {
1707                 result = isc_mutex_init(&ctx->lock);
1708                 if (result != ISC_R_SUCCESS) {
1709                         isc_mem_put(client->mctx, ctx, sizeof(*ctx));
1710                         ctx = NULL;
1711                 }
1712         }
1713         if (result != ISC_R_SUCCESS)
1714                 goto cleanup;
1715
1716         ctx->client = client;
1717         ISC_LINK_INIT(ctx, link);
1718         ctx->parseoptions = parseoptions;
1719         ctx->canceled = ISC_FALSE;
1720         ctx->event = event;
1721         ctx->event->rmessage = rmessage;
1722         ctx->tsigkey = NULL;
1723         if (tsec != NULL)
1724                 dns_tsec_getkey(tsec, &ctx->tsigkey);
1725
1726         ctx->magic = REQCTX_MAGIC;
1727
1728         LOCK(&client->lock);
1729         ISC_LIST_APPEND(client->reqctxs, ctx, link);
1730         UNLOCK(&client->lock);
1731
1732         ctx->request = NULL;
1733         result = dns_request_createvia3(view->requestmgr, qmessage, NULL,
1734                                         server, options, ctx->tsigkey,
1735                                         timeout, udptimeout, udpretries,
1736                                         client->task, request_done, ctx,
1737                                         &ctx->request);
1738         if (result == ISC_R_SUCCESS) {
1739                 dns_view_detach(&view);
1740                 *transp = (dns_clientreqtrans_t *)ctx;
1741                 return (ISC_R_SUCCESS);
1742         }
1743
1744  cleanup:
1745         if (ctx != NULL) {
1746                 LOCK(&client->lock);
1747                 ISC_LIST_UNLINK(client->reqctxs, ctx, link);
1748                 UNLOCK(&client->lock);
1749                 DESTROYLOCK(&ctx->lock);
1750                 isc_mem_put(client->mctx, ctx, sizeof(*ctx));
1751         }
1752         if (event != NULL)
1753                 isc_event_free(ISC_EVENT_PTR(&event));
1754         isc_task_detach(&clone);
1755         dns_view_detach(&view);
1756
1757         return (result);
1758 }
1759
1760 void
1761 dns_client_cancelrequest(dns_clientreqtrans_t *trans) {
1762         reqctx_t *ctx;
1763
1764         REQUIRE(trans != NULL);
1765         ctx = (reqctx_t *)trans;
1766         REQUIRE(REQCTX_VALID(ctx));
1767
1768         LOCK(&ctx->lock);
1769
1770         if (!ctx->canceled) {
1771                 ctx->canceled = ISC_TRUE;
1772                 if (ctx->request != NULL)
1773                         dns_request_cancel(ctx->request);
1774         }
1775
1776         UNLOCK(&ctx->lock);
1777 }
1778
1779 void
1780 dns_client_destroyreqtrans(dns_clientreqtrans_t **transp) {
1781         reqctx_t *ctx;
1782         isc_mem_t *mctx;
1783         dns_client_t *client;
1784         isc_boolean_t need_destroyclient = ISC_FALSE;
1785
1786         REQUIRE(transp != NULL);
1787         ctx = (reqctx_t *)*transp;
1788         REQUIRE(REQCTX_VALID(ctx));
1789         client = ctx->client;
1790         REQUIRE(DNS_CLIENT_VALID(client));
1791         REQUIRE(ctx->event == NULL);
1792         REQUIRE(ctx->request != NULL);
1793
1794         dns_request_destroy(&ctx->request);
1795         mctx = client->mctx;
1796
1797         LOCK(&client->lock);
1798
1799         INSIST(ISC_LINK_LINKED(ctx, link));
1800         ISC_LIST_UNLINK(client->reqctxs, ctx, link);
1801
1802         if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
1803             ISC_LIST_EMPTY(client->reqctxs) &&
1804             ISC_LIST_EMPTY(client->updatectxs)) {
1805                 need_destroyclient = ISC_TRUE;
1806         }
1807
1808         UNLOCK(&client->lock);
1809
1810         DESTROYLOCK(&ctx->lock);
1811         ctx->magic = 0;
1812
1813         isc_mem_put(mctx, ctx, sizeof(*ctx));
1814
1815         if (need_destroyclient)
1816                 destroyclient(&client);
1817
1818         *transp = NULL;
1819 }
1820
1821 /*%
1822  * Dynamic update routines
1823  */
1824 static isc_result_t
1825 rcode2result(dns_rcode_t rcode) {
1826         /* XXX: isn't there a similar function? */
1827         switch (rcode) {
1828         case dns_rcode_formerr:
1829                 return (DNS_R_FORMERR);
1830         case dns_rcode_servfail:
1831                 return (DNS_R_SERVFAIL);
1832         case dns_rcode_nxdomain:
1833                 return (DNS_R_NXDOMAIN);
1834         case dns_rcode_notimp:
1835                 return (DNS_R_NOTIMP);
1836         case dns_rcode_refused:
1837                 return (DNS_R_REFUSED);
1838         case dns_rcode_yxdomain:
1839                 return (DNS_R_YXDOMAIN);
1840         case dns_rcode_yxrrset:
1841                 return (DNS_R_YXRRSET);
1842         case dns_rcode_nxrrset:
1843                 return (DNS_R_NXRRSET);
1844         case dns_rcode_notauth:
1845                 return (DNS_R_NOTAUTH);
1846         case dns_rcode_notzone:
1847                 return (DNS_R_NOTZONE);
1848         case dns_rcode_badvers:
1849                 return (DNS_R_BADVERS);
1850         }
1851
1852         return (ISC_R_FAILURE);
1853 }
1854
1855 static void
1856 update_sendevent(updatectx_t *uctx, isc_result_t result) {
1857         isc_task_t *task;
1858
1859         dns_message_destroy(&uctx->updatemsg);
1860         if (uctx->tsigkey != NULL)
1861                 dns_tsigkey_detach(&uctx->tsigkey);
1862         if (uctx->sig0key != NULL)
1863                 dst_key_free(&uctx->sig0key);
1864
1865         if (uctx->canceled)
1866                 uctx->event->result = ISC_R_CANCELED;
1867         else
1868                 uctx->event->result = result;
1869         uctx->event->state = uctx->state;
1870         task = uctx->event->ev_sender;
1871         uctx->event->ev_sender = uctx;
1872         isc_task_sendanddetach(&task, ISC_EVENT_PTR(&uctx->event));
1873 }
1874
1875 static void
1876 update_done(isc_task_t *task, isc_event_t *event) {
1877         isc_result_t result;
1878         dns_requestevent_t *reqev = NULL;
1879         dns_request_t *request;
1880         dns_message_t *answer = NULL;
1881         updatectx_t *uctx = event->ev_arg;
1882         dns_client_t *client;
1883         unsigned int timeout;
1884
1885         UNUSED(task);
1886
1887         REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
1888         reqev = (dns_requestevent_t *)event;
1889         request = reqev->request;
1890         REQUIRE(UCTX_VALID(uctx));
1891         client = uctx->client;
1892         REQUIRE(DNS_CLIENT_VALID(client));
1893
1894         result = reqev->result;
1895         if (result != ISC_R_SUCCESS)
1896                 goto out;
1897
1898         result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
1899                                     &answer);
1900         if (result != ISC_R_SUCCESS)
1901                 goto out;
1902         uctx->state = dns_clientupdatestate_done;
1903         result = dns_request_getresponse(request, answer,
1904                                          DNS_MESSAGEPARSE_PRESERVEORDER);
1905         if (result == ISC_R_SUCCESS && answer->rcode != dns_rcode_noerror)
1906                 result = rcode2result(answer->rcode);
1907
1908  out:
1909         if (answer != NULL)
1910                 dns_message_destroy(&answer);
1911         isc_event_free(&event);
1912
1913         LOCK(&uctx->lock);
1914         uctx->currentserver = ISC_LIST_NEXT(uctx->currentserver, link);
1915         dns_request_destroy(&uctx->updatereq);
1916         if (result != ISC_R_SUCCESS && !uctx->canceled &&
1917             uctx->currentserver != NULL) {
1918                 dns_message_renderreset(uctx->updatemsg);
1919                 dns_message_settsigkey(uctx->updatemsg, NULL);
1920
1921                 timeout = client->update_timeout / uctx->nservers;
1922                 if (timeout < MIN_UPDATE_TIMEOUT)
1923                         timeout = MIN_UPDATE_TIMEOUT;
1924                 result = dns_request_createvia3(uctx->view->requestmgr,
1925                                                 uctx->updatemsg,
1926                                                 NULL,
1927                                                 uctx->currentserver, 0,
1928                                                 uctx->tsigkey,
1929                                                 timeout,
1930                                                 client->update_udptimeout,
1931                                                 client->update_udpretries,
1932                                                 client->task,
1933                                                 update_done, uctx,
1934                                                 &uctx->updatereq);
1935                 UNLOCK(&uctx->lock);
1936
1937                 if (result == ISC_R_SUCCESS) {
1938                         /* XXX: should we keep the 'done' state here? */
1939                         uctx->state = dns_clientupdatestate_sent;
1940                         return;
1941                 }
1942         } else
1943                 UNLOCK(&uctx->lock);
1944
1945         update_sendevent(uctx, result);
1946 }
1947
1948 static isc_result_t
1949 send_update(updatectx_t *uctx) {
1950         isc_result_t result;
1951         dns_name_t *name = NULL;
1952         dns_rdataset_t *rdataset = NULL;
1953         dns_client_t *client = uctx->client;
1954         unsigned int timeout;
1955
1956         REQUIRE(uctx->zonename != NULL && uctx->currentserver != NULL);
1957
1958         result = dns_message_gettempname(uctx->updatemsg, &name);
1959         if (result != ISC_R_SUCCESS)
1960                 return (result);
1961         dns_name_init(name, NULL);
1962         dns_name_clone(uctx->zonename, name);
1963         result = dns_message_gettemprdataset(uctx->updatemsg, &rdataset);
1964         if (result != ISC_R_SUCCESS) {
1965                 dns_message_puttempname(uctx->updatemsg, &name);
1966                 return (result);
1967         }
1968         dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa);
1969         ISC_LIST_INIT(name->list);
1970         ISC_LIST_APPEND(name->list, rdataset, link);
1971         dns_message_addname(uctx->updatemsg, name, DNS_SECTION_ZONE);
1972         if (uctx->tsigkey == NULL && uctx->sig0key != NULL) {
1973                 result = dns_message_setsig0key(uctx->updatemsg,
1974                                                 uctx->sig0key);
1975                 if (result != ISC_R_SUCCESS)
1976                         return (result);
1977         }
1978         timeout = client->update_timeout / uctx->nservers;
1979         if (timeout < MIN_UPDATE_TIMEOUT)
1980                 timeout = MIN_UPDATE_TIMEOUT;
1981         result = dns_request_createvia3(uctx->view->requestmgr,
1982                                         uctx->updatemsg,
1983                                         NULL, uctx->currentserver, 0,
1984                                         uctx->tsigkey, timeout,
1985                                         client->update_udptimeout,
1986                                         client->update_udpretries,
1987                                         client->task, update_done, uctx,
1988                                         &uctx->updatereq);
1989         if (result == ISC_R_SUCCESS &&
1990             uctx->state == dns_clientupdatestate_prepare) {
1991                 uctx->state = dns_clientupdatestate_sent;
1992         }
1993
1994         return (result);
1995 }
1996
1997 static void
1998 resolveaddr_done(isc_task_t *task, isc_event_t *event) {
1999         isc_result_t result;
2000         int family;
2001         dns_rdatatype_t qtype;
2002         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
2003         dns_name_t *name;
2004         dns_rdataset_t *rdataset;
2005         updatectx_t *uctx;
2006         isc_boolean_t completed = ISC_FALSE;
2007
2008         UNUSED(task);
2009
2010         REQUIRE(event->ev_arg != NULL);
2011         uctx = *(updatectx_t **)event->ev_arg;
2012         REQUIRE(UCTX_VALID(uctx));
2013
2014         if (event->ev_arg == &uctx->bp4) {
2015                 family = AF_INET;
2016                 qtype = dns_rdatatype_a;
2017                 LOCK(&uctx->lock);
2018                 dns_client_destroyrestrans(&uctx->restrans);
2019                 UNLOCK(&uctx->lock);
2020         } else {
2021                 INSIST(event->ev_arg == &uctx->bp6);
2022                 family = AF_INET6;
2023                 qtype = dns_rdatatype_aaaa;
2024                 LOCK(&uctx->lock);
2025                 dns_client_destroyrestrans(&uctx->restrans2);
2026                 UNLOCK(&uctx->lock);
2027         }
2028
2029         result = rev->result;
2030         if (result != ISC_R_SUCCESS)
2031                 goto done;
2032
2033         for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
2034              name = ISC_LIST_NEXT(name, link)) {
2035                 for (rdataset = ISC_LIST_HEAD(name->list);
2036                      rdataset != NULL;
2037                      rdataset = ISC_LIST_NEXT(rdataset, link)) {
2038                         if (!dns_rdataset_isassociated(rdataset))
2039                                 continue;
2040                         if (rdataset->type != qtype)
2041                                 continue;
2042
2043                         for (result = dns_rdataset_first(rdataset);
2044                              result == ISC_R_SUCCESS;
2045                              result = dns_rdataset_next(rdataset)) {
2046                                 dns_rdata_t rdata;
2047                                 dns_rdata_in_a_t rdata_a;
2048                                 dns_rdata_in_aaaa_t rdata_aaaa;
2049                                 isc_sockaddr_t *sa;
2050
2051                                 sa = isc_mem_get(uctx->client->mctx,
2052                                                  sizeof(*sa));
2053                                 if (sa == NULL) {
2054                                         /*
2055                                          * If we fail to get a sockaddr,
2056                                          we simply move forward with the
2057                                          * addresses we've got so far.
2058                                          */
2059                                         goto done;
2060                                 }
2061
2062                                 dns_rdata_init(&rdata);
2063                                 switch (family) {
2064                                 case AF_INET:
2065                                         dns_rdataset_current(rdataset, &rdata);
2066                                         result = dns_rdata_tostruct(&rdata, &rdata_a,
2067                                                                     NULL);
2068                                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
2069                                         isc_sockaddr_fromin(sa,
2070                                                             &rdata_a.in_addr,
2071                                                             53);
2072                                         dns_rdata_freestruct(&rdata_a);
2073                                         break;
2074                                 case AF_INET6:
2075                                         dns_rdataset_current(rdataset, &rdata);
2076                                         result = dns_rdata_tostruct(&rdata, &rdata_aaaa,
2077                                                                     NULL);
2078                                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
2079                                         isc_sockaddr_fromin6(sa,
2080                                                              &rdata_aaaa.in6_addr,
2081                                                              53);
2082                                         dns_rdata_freestruct(&rdata_aaaa);
2083                                         break;
2084                                 }
2085
2086                                 ISC_LINK_INIT(sa, link);
2087                                 ISC_LIST_APPEND(uctx->servers, sa, link);
2088                                 uctx->nservers++;
2089                         }
2090                 }
2091         }
2092
2093  done:
2094         dns_client_freeresanswer(uctx->client, &rev->answerlist);
2095         isc_event_free(&event);
2096
2097         LOCK(&uctx->lock);
2098         if (uctx->restrans == NULL && uctx->restrans2 == NULL)
2099                 completed = ISC_TRUE;
2100         UNLOCK(&uctx->lock);
2101
2102         if (completed) {
2103                 INSIST(uctx->currentserver == NULL);
2104                 uctx->currentserver = ISC_LIST_HEAD(uctx->servers);
2105                 if (uctx->currentserver != NULL && !uctx->canceled)
2106                         send_update(uctx);
2107                 else {
2108                         if (result == ISC_R_SUCCESS)
2109                                 result = ISC_R_NOTFOUND;
2110                         update_sendevent(uctx, result);
2111                 }
2112         }
2113 }
2114
2115 static isc_result_t
2116 process_soa(updatectx_t *uctx, dns_rdataset_t *soaset, dns_name_t *soaname) {
2117         isc_result_t result;
2118         dns_rdata_t soarr = DNS_RDATA_INIT;
2119         dns_rdata_soa_t soa;
2120         dns_name_t primary;
2121
2122         result = dns_rdataset_first(soaset);
2123         if (result != ISC_R_SUCCESS)
2124                 return (result);
2125         dns_rdata_init(&soarr);
2126         dns_rdataset_current(soaset, &soarr);
2127         result = dns_rdata_tostruct(&soarr, &soa, NULL);
2128         if (result != ISC_R_SUCCESS)
2129                 return (result);
2130
2131         dns_name_init(&primary, NULL);
2132         dns_name_clone(&soa.origin, &primary);
2133
2134         if (uctx->zonename == NULL) {
2135                 uctx->zonename = dns_fixedname_name(&uctx->zonefname);
2136                 result = dns_name_copy(soaname, uctx->zonename, NULL);
2137                 if (result != ISC_R_SUCCESS)
2138                         goto out;
2139         }
2140
2141         if (uctx->currentserver != NULL)
2142                 result = send_update(uctx);
2143         else {
2144                 /*
2145                  * Get addresses of the primary server.  We don't use the ADB
2146                  * feature so that we could avoid caching data.
2147                  */
2148                 LOCK(&uctx->lock);
2149                 uctx->bp4 = uctx;
2150                 result = dns_client_startresolve(uctx->client, &primary,
2151                                                  uctx->rdclass,
2152                                                  dns_rdatatype_a,
2153                                                  0, uctx->client->task,
2154                                                  resolveaddr_done, &uctx->bp4,
2155                                                  &uctx->restrans);
2156                 if (result == ISC_R_SUCCESS) {
2157                         uctx->bp6 = uctx;
2158                         result = dns_client_startresolve(uctx->client,
2159                                                          &primary,
2160                                                          uctx->rdclass,
2161                                                          dns_rdatatype_aaaa,
2162                                                          0, uctx->client->task,
2163                                                          resolveaddr_done,
2164                                                          &uctx->bp6,
2165                                                          &uctx->restrans2);
2166                 }
2167                 UNLOCK(&uctx->lock);
2168         }
2169
2170  out:
2171         dns_rdata_freestruct(&soa);
2172
2173         return (result);
2174 }
2175
2176 static void
2177 receive_soa(isc_task_t *task, isc_event_t *event) {
2178         dns_requestevent_t *reqev = NULL;
2179         updatectx_t *uctx;
2180         dns_client_t *client;
2181         isc_result_t result, eresult;
2182         dns_request_t *request;
2183         dns_message_t *rcvmsg = NULL;
2184         dns_section_t section;
2185         dns_rdataset_t *soaset = NULL;
2186         int pass = 0;
2187         dns_name_t *name;
2188         dns_message_t *soaquery = NULL;
2189         isc_sockaddr_t *addr;
2190         isc_boolean_t seencname = ISC_FALSE;
2191         isc_boolean_t droplabel = ISC_FALSE;
2192         dns_name_t tname;
2193         unsigned int nlabels;
2194
2195         UNUSED(task);
2196
2197         REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2198         reqev = (dns_requestevent_t *)event;
2199         request = reqev->request;
2200         result = eresult = reqev->result;
2201         POST(result);
2202         uctx = reqev->ev_arg;
2203         client = uctx->client;
2204         soaquery = uctx->soaquery;
2205         addr = uctx->currentserver;
2206         INSIST(addr != NULL);
2207
2208         isc_event_free(&event);
2209
2210         if (eresult != ISC_R_SUCCESS) {
2211                 result = eresult;
2212                 goto out;
2213         }
2214
2215         result = dns_message_create(uctx->client->mctx,
2216                                     DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2217         if (result != ISC_R_SUCCESS)
2218                 goto out;
2219         result = dns_request_getresponse(request, rcvmsg,
2220                                          DNS_MESSAGEPARSE_PRESERVEORDER);
2221
2222         if (result == DNS_R_TSIGERRORSET) {
2223                 dns_request_t *newrequest = NULL;
2224
2225                 /* Retry SOA request without TSIG */
2226                 dns_message_destroy(&rcvmsg);
2227                 dns_message_renderreset(uctx->soaquery);
2228                 result = dns_request_createvia3(uctx->view->requestmgr,
2229                                                 uctx->soaquery, NULL, addr, 0,
2230                                                 NULL,
2231                                                 client->find_timeout * 20,
2232                                                 client->find_timeout, 3,
2233                                                 uctx->client->task,
2234                                                 receive_soa, uctx,
2235                                                 &newrequest);
2236                 if (result == ISC_R_SUCCESS) {
2237                         LOCK(&uctx->lock);
2238                         dns_request_destroy(&uctx->soareq);
2239                         uctx->soareq = newrequest;
2240                         UNLOCK(&uctx->lock);
2241
2242                         return;
2243                 }
2244                 goto out;
2245         }
2246
2247         section = DNS_SECTION_ANSWER;
2248         POST(section);
2249
2250         if (rcvmsg->rcode != dns_rcode_noerror &&
2251             rcvmsg->rcode != dns_rcode_nxdomain) {
2252                 result = rcode2result(rcvmsg->rcode);
2253                 goto out;
2254         }
2255
2256  lookforsoa:
2257         if (pass == 0)
2258                 section = DNS_SECTION_ANSWER;
2259         else if (pass == 1)
2260                 section = DNS_SECTION_AUTHORITY;
2261         else {
2262                 droplabel = ISC_TRUE;
2263                 goto out;
2264         }
2265
2266         result = dns_message_firstname(rcvmsg, section);
2267         if (result != ISC_R_SUCCESS) {
2268                 pass++;
2269                 goto lookforsoa;
2270         }
2271         while (result == ISC_R_SUCCESS) {
2272                 name = NULL;
2273                 dns_message_currentname(rcvmsg, section, &name);
2274                 soaset = NULL;
2275                 result = dns_message_findtype(name, dns_rdatatype_soa, 0,
2276                                               &soaset);
2277                 if (result == ISC_R_SUCCESS)
2278                         break;
2279                 if (section == DNS_SECTION_ANSWER) {
2280                         dns_rdataset_t *tset = NULL;
2281                         if (dns_message_findtype(name, dns_rdatatype_cname, 0,
2282                                                  &tset) == ISC_R_SUCCESS
2283                             ||
2284                             dns_message_findtype(name, dns_rdatatype_dname, 0,
2285                                                  &tset) == ISC_R_SUCCESS
2286                             )
2287                         {
2288                                 seencname = ISC_TRUE;
2289                                 break;
2290                         }
2291                 }
2292
2293                 result = dns_message_nextname(rcvmsg, section);
2294         }
2295
2296         if (soaset == NULL && !seencname) {
2297                 pass++;
2298                 goto lookforsoa;
2299         }
2300
2301         if (seencname) {
2302                 droplabel = ISC_TRUE;
2303                 goto out;
2304         }
2305
2306         result = process_soa(uctx, soaset, name);
2307
2308  out:
2309         if (droplabel) {
2310                 result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION);
2311                 INSIST(result == ISC_R_SUCCESS);
2312                 name = NULL;
2313                 dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name);
2314                 nlabels = dns_name_countlabels(name);
2315                 if (nlabels == 1)
2316                         result = DNS_R_SERVFAIL; /* is there a better error? */
2317                 else {
2318                         dns_name_init(&tname, NULL);
2319                         dns_name_getlabelsequence(name, 1, nlabels - 1,
2320                                                   &tname);
2321                         dns_name_clone(&tname, name);
2322                         dns_request_destroy(&request);
2323                         LOCK(&uctx->lock);
2324                         uctx->soareq = NULL;
2325                         UNLOCK(&uctx->lock);
2326                         dns_message_renderreset(soaquery);
2327                         dns_message_settsigkey(soaquery, NULL);
2328                         result = dns_request_createvia3(uctx->view->requestmgr,
2329                                                         soaquery, NULL,
2330                                                         uctx->currentserver, 0,
2331                                                         uctx->tsigkey,
2332                                                         client->find_timeout *
2333                                                         20,
2334                                                         client->find_timeout,
2335                                                         3, client->task,
2336                                                         receive_soa, uctx,
2337                                                         &uctx->soareq);
2338                 }
2339         }
2340
2341         if (!droplabel || result != ISC_R_SUCCESS) {
2342                 dns_message_destroy(&uctx->soaquery);
2343                 LOCK(&uctx->lock);
2344                 dns_request_destroy(&uctx->soareq);
2345                 UNLOCK(&uctx->lock);
2346         }
2347
2348         if (rcvmsg != NULL)
2349                 dns_message_destroy(&rcvmsg);
2350
2351         if (result != ISC_R_SUCCESS)
2352                 update_sendevent(uctx, result);
2353 }
2354
2355 static isc_result_t
2356 request_soa(updatectx_t *uctx) {
2357         isc_result_t result;
2358         dns_message_t *soaquery = uctx->soaquery;
2359         dns_name_t *name = NULL;
2360         dns_rdataset_t *rdataset = NULL;
2361
2362         if (soaquery == NULL) {
2363                 result = dns_message_create(uctx->client->mctx,
2364                                             DNS_MESSAGE_INTENTRENDER,
2365                                             &soaquery);
2366                 if (result != ISC_R_SUCCESS)
2367                         return (result);
2368         }
2369         soaquery->flags |= DNS_MESSAGEFLAG_RD;
2370         result = dns_message_gettempname(soaquery, &name);
2371         if (result != ISC_R_SUCCESS)
2372                 goto fail;
2373         result = dns_message_gettemprdataset(soaquery, &rdataset);
2374         if (result != ISC_R_SUCCESS)
2375                 goto fail;
2376         dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa);
2377         dns_name_clone(uctx->firstname, name);
2378         ISC_LIST_APPEND(name->list, rdataset, link);
2379         dns_message_addname(soaquery, name, DNS_SECTION_QUESTION);
2380         rdataset = NULL;
2381         name = NULL;
2382
2383         result = dns_request_createvia3(uctx->view->requestmgr,
2384                                         soaquery, NULL, uctx->currentserver, 0,
2385                                         uctx->tsigkey,
2386                                         uctx->client->find_timeout * 20,
2387                                         uctx->client->find_timeout, 3,
2388                                         uctx->client->task, receive_soa, uctx,
2389                                         &uctx->soareq);
2390         if (result == ISC_R_SUCCESS) {
2391                 uctx->soaquery = soaquery;
2392                 return (ISC_R_SUCCESS);
2393         }
2394
2395  fail:
2396         if (rdataset != NULL) {
2397                 ISC_LIST_UNLINK(name->list, rdataset, link); /* for safety */
2398                 dns_message_puttemprdataset(soaquery, &rdataset);
2399         }
2400         if (name != NULL)
2401                 dns_message_puttempname(soaquery, &name);
2402         dns_message_destroy(&soaquery);
2403
2404         return (result);
2405 }
2406
2407 static void
2408 resolvesoa_done(isc_task_t *task, isc_event_t *event) {
2409         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
2410         updatectx_t *uctx;
2411         dns_name_t *name, tname;
2412         dns_rdataset_t *rdataset = NULL;
2413         isc_result_t result = rev->result;
2414         unsigned int nlabels;
2415
2416         UNUSED(task);
2417
2418         uctx = event->ev_arg;
2419         REQUIRE(UCTX_VALID(uctx));
2420
2421         LOCK(&uctx->lock);
2422         dns_client_destroyrestrans(&uctx->restrans);
2423         UNLOCK(&uctx->lock);
2424
2425         uctx = event->ev_arg;
2426         if (result != ISC_R_SUCCESS &&
2427             result != DNS_R_NCACHENXDOMAIN &&
2428             result != DNS_R_NCACHENXRRSET) {
2429                 /* XXX: what about DNSSEC failure? */
2430                 goto out;
2431         }
2432
2433         for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
2434              name = ISC_LIST_NEXT(name, link)) {
2435                 for (rdataset = ISC_LIST_HEAD(name->list);
2436                      rdataset != NULL;
2437                      rdataset = ISC_LIST_NEXT(rdataset, link)) {
2438                         if (dns_rdataset_isassociated(rdataset) &&
2439                             rdataset->type == dns_rdatatype_soa)
2440                                 break;
2441                 }
2442         }
2443
2444         if (rdataset == NULL) {
2445                 /* Drop one label and retry resolution. */
2446                 nlabels = dns_name_countlabels(&uctx->soaqname);
2447                 if (nlabels == 1) {
2448                         result = DNS_R_SERVFAIL; /* is there a better error? */
2449                         goto out;
2450                 }
2451                 dns_name_init(&tname, NULL);
2452                 dns_name_getlabelsequence(&uctx->soaqname, 1, nlabels - 1,
2453                                           &tname);
2454                 dns_name_clone(&tname, &uctx->soaqname);
2455
2456                 result = dns_client_startresolve(uctx->client, &uctx->soaqname,
2457                                                  uctx->rdclass,
2458                                                  dns_rdatatype_soa, 0,
2459                                                  uctx->client->task,
2460                                                  resolvesoa_done, uctx,
2461                                                  &uctx->restrans);
2462         } else
2463                 result = process_soa(uctx, rdataset, &uctx->soaqname);
2464
2465  out:
2466         dns_client_freeresanswer(uctx->client, &rev->answerlist);
2467         isc_event_free(&event);
2468
2469         if (result != ISC_R_SUCCESS)
2470                 update_sendevent(uctx, result);
2471 }
2472
2473 static isc_result_t
2474 copy_name(isc_mem_t *mctx, dns_message_t *msg, dns_name_t *name,
2475           dns_name_t **newnamep)
2476 {
2477         isc_result_t result;
2478         dns_name_t *newname = NULL;
2479         isc_region_t r;
2480         isc_buffer_t *namebuf = NULL, *rdatabuf = NULL;
2481         dns_rdatalist_t *rdatalist;
2482         dns_rdataset_t *rdataset, *newrdataset;
2483         dns_rdata_t rdata = DNS_RDATA_INIT, *newrdata;
2484
2485         result = dns_message_gettempname(msg, &newname);
2486         if (result != ISC_R_SUCCESS)
2487                 return (result);
2488         result = isc_buffer_allocate(mctx, &namebuf, DNS_NAME_MAXWIRE);
2489         if (result != ISC_R_SUCCESS)
2490                 goto fail;
2491         dns_name_init(newname, NULL);
2492         dns_name_setbuffer(newname, namebuf);
2493         dns_message_takebuffer(msg, &namebuf);
2494         result = dns_name_copy(name, newname, NULL);
2495         if (result != ISC_R_SUCCESS)
2496                 goto fail;
2497
2498         for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
2499              rdataset = ISC_LIST_NEXT(rdataset, link)) {
2500                 rdatalist = NULL;
2501                 result = dns_message_gettemprdatalist(msg, &rdatalist);
2502                 if (result != ISC_R_SUCCESS)
2503                         goto fail;
2504                 dns_rdatalist_init(rdatalist);
2505                 rdatalist->type = rdataset->type;
2506                 rdatalist->rdclass = rdataset->rdclass;
2507                 rdatalist->covers = rdataset->covers;
2508                 rdatalist->ttl = rdataset->ttl;
2509
2510                 result = dns_rdataset_first(rdataset);
2511                 while (result == ISC_R_SUCCESS) {
2512                         dns_rdata_reset(&rdata);
2513                         dns_rdataset_current(rdataset, &rdata);
2514
2515                         newrdata = NULL;
2516                         result = dns_message_gettemprdata(msg, &newrdata);
2517                         if (result != ISC_R_SUCCESS)
2518                                 goto fail;
2519                         dns_rdata_toregion(&rdata, &r);
2520                         rdatabuf = NULL;
2521                         result = isc_buffer_allocate(mctx, &rdatabuf,
2522                                                      r.length);
2523                         if (result != ISC_R_SUCCESS)
2524                                 goto fail;
2525                         isc_buffer_putmem(rdatabuf, r.base, r.length);
2526                         isc_buffer_usedregion(rdatabuf, &r);
2527                         dns_rdata_init(newrdata);
2528                         dns_rdata_fromregion(newrdata, rdata.rdclass,
2529                                              rdata.type, &r);
2530                         newrdata->flags = rdata.flags;
2531
2532                         ISC_LIST_APPEND(rdatalist->rdata, newrdata, link);
2533                         dns_message_takebuffer(msg, &rdatabuf);
2534
2535                         result = dns_rdataset_next(rdataset);
2536                 }
2537
2538                 newrdataset = NULL;
2539                 result = dns_message_gettemprdataset(msg, &newrdataset);
2540                 if (result != ISC_R_SUCCESS)
2541                         goto fail;
2542                 dns_rdataset_init(newrdataset);
2543                 dns_rdatalist_tordataset(rdatalist, newrdataset);
2544
2545                 ISC_LIST_APPEND(newname->list, newrdataset, link);
2546         }
2547
2548         *newnamep = newname;
2549
2550         return (ISC_R_SUCCESS);
2551
2552  fail:
2553         dns_message_puttempname(msg, &newname);
2554
2555         return (result);
2556
2557 }
2558
2559 static void
2560 internal_update_callback(isc_task_t *task, isc_event_t *event) {
2561         updatearg_t *uarg = event->ev_arg;
2562         dns_clientupdateevent_t *uev = (dns_clientupdateevent_t *)event;
2563
2564         UNUSED(task);
2565
2566         LOCK(&uarg->lock);
2567
2568         uarg->result = uev->result;
2569
2570         dns_client_destroyupdatetrans(&uarg->trans);
2571         isc_event_free(&event);
2572
2573         if (!uarg->canceled) {
2574                 UNLOCK(&uarg->lock);
2575
2576                 /* Exit from the internal event loop */
2577                 isc_app_ctxsuspend(uarg->actx);
2578         } else {
2579                 /*
2580                  * We have already exited from the loop (due to some
2581                  * unexpected event).  Just clean the arg up.
2582                  */
2583                 UNLOCK(&uarg->lock);
2584                 DESTROYLOCK(&uarg->lock);
2585                 isc_mem_put(uarg->client->mctx, uarg, sizeof(*uarg));
2586         }
2587 }
2588
2589 isc_result_t
2590 dns_client_update(dns_client_t *client, dns_rdataclass_t rdclass,
2591                   dns_name_t *zonename, dns_namelist_t *prerequisites,
2592                   dns_namelist_t *updates, isc_sockaddrlist_t *servers,
2593                   dns_tsec_t *tsec, unsigned int options)
2594 {
2595         isc_result_t result;
2596         isc_appctx_t *actx;
2597         updatearg_t *uarg;
2598
2599         REQUIRE(DNS_CLIENT_VALID(client));
2600
2601         if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
2602             (options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) {
2603                 /*
2604                  * If the client is run under application's control, we need
2605                  * to create a new running (sub)environment for this
2606                  * particular resolution.
2607                  */
2608                 return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
2609         } else
2610                 actx = client->actx;
2611
2612         uarg = isc_mem_get(client->mctx, sizeof(*uarg));
2613         if (uarg == NULL)
2614                 return (ISC_R_NOMEMORY);
2615
2616         result = isc_mutex_init(&uarg->lock);
2617         if (result != ISC_R_SUCCESS) {
2618                 isc_mem_put(client->mctx, uarg, sizeof(*uarg));
2619                 return (result);
2620         }
2621
2622         uarg->actx = actx;
2623         uarg->client = client;
2624         uarg->result = ISC_R_FAILURE;
2625         uarg->trans = NULL;
2626         uarg->canceled = ISC_FALSE;
2627
2628         result = dns_client_startupdate(client, rdclass, zonename,
2629                                         prerequisites, updates, servers,
2630                                         tsec, options, client->task,
2631                                         internal_update_callback, uarg,
2632                                         &uarg->trans);
2633         if (result != ISC_R_SUCCESS) {
2634                 DESTROYLOCK(&uarg->lock);
2635                 isc_mem_put(client->mctx, uarg, sizeof(*uarg));
2636                 return (result);
2637         }
2638
2639         /*
2640          * Start internal event loop.  It blocks until the entire process
2641          * is completed.
2642          */
2643         result = isc_app_ctxrun(actx);
2644
2645         LOCK(&uarg->lock);
2646         if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
2647                 result = uarg->result;
2648
2649         if (uarg->trans != NULL) {
2650                 /*
2651                  * Unusual termination (perhaps due to signal).  We need some
2652                  * tricky cleanup process.
2653                  */
2654                 uarg->canceled = ISC_TRUE;
2655                 dns_client_cancelupdate(uarg->trans);
2656
2657                 UNLOCK(&uarg->lock);
2658
2659                 /* uarg will be freed in the event handler. */
2660         } else {
2661                 UNLOCK(&uarg->lock);
2662
2663                 DESTROYLOCK(&uarg->lock);
2664                 isc_mem_put(client->mctx, uarg, sizeof(*uarg));
2665         }
2666
2667         return (result);
2668 }
2669
2670 isc_result_t
2671 dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass,
2672                        dns_name_t *zonename, dns_namelist_t *prerequisites,
2673                        dns_namelist_t *updates, isc_sockaddrlist_t *servers,
2674                        dns_tsec_t *tsec, unsigned int options,
2675                        isc_task_t *task, isc_taskaction_t action, void *arg,
2676                        dns_clientupdatetrans_t **transp)
2677 {
2678         dns_view_t *view = NULL;
2679         isc_result_t result;
2680         dns_name_t *name, *newname;
2681         updatectx_t *uctx;
2682         isc_task_t *clone = NULL;
2683         dns_section_t section = DNS_SECTION_UPDATE;
2684         isc_sockaddr_t *server, *sa = NULL;
2685         dns_tsectype_t tsectype = dns_tsectype_none;
2686
2687         UNUSED(options);
2688
2689         REQUIRE(DNS_CLIENT_VALID(client));
2690         REQUIRE(transp != NULL && *transp == NULL);
2691         REQUIRE(updates != NULL);
2692         REQUIRE(task != NULL);
2693
2694         if (tsec != NULL) {
2695                 tsectype = dns_tsec_gettype(tsec);
2696                 if (tsectype != dns_tsectype_tsig)
2697                         return (ISC_R_NOTIMPLEMENTED); /* XXX */
2698         }
2699
2700         LOCK(&client->lock);
2701         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
2702                                    rdclass, &view);
2703         UNLOCK(&client->lock);
2704         if (result != ISC_R_SUCCESS)
2705                 return (result);
2706
2707         /* Create a context and prepare some resources */
2708         uctx = isc_mem_get(client->mctx, sizeof(*uctx));
2709         if (uctx == NULL) {
2710                 dns_view_detach(&view);
2711                 return (ISC_R_NOMEMORY);
2712         }
2713         result = isc_mutex_init(&uctx->lock);
2714         if (result != ISC_R_SUCCESS) {
2715                 dns_view_detach(&view);
2716                 isc_mem_put(client->mctx, uctx, sizeof(*uctx));
2717                 return (ISC_R_NOMEMORY);
2718         }
2719         clone = NULL;
2720         isc_task_attach(task, &clone);
2721         uctx->client = client;
2722         ISC_LINK_INIT(uctx, link);
2723         uctx->state = dns_clientupdatestate_prepare;
2724         uctx->view = view;
2725         uctx->rdclass = rdclass;
2726         uctx->canceled = ISC_FALSE;
2727         uctx->updatemsg = NULL;
2728         uctx->soaquery = NULL;
2729         uctx->updatereq = NULL;
2730         uctx->restrans = NULL;
2731         uctx->restrans2 = NULL;
2732         uctx->bp4 = NULL;
2733         uctx->bp6 = NULL;
2734         uctx->soareq = NULL;
2735         uctx->event = NULL;
2736         uctx->tsigkey = NULL;
2737         uctx->sig0key = NULL;
2738         uctx->zonename = NULL;
2739         dns_name_init(&uctx->soaqname, NULL);
2740         ISC_LIST_INIT(uctx->servers);
2741         uctx->nservers = 0;
2742         uctx->currentserver = NULL;
2743         dns_fixedname_init(&uctx->zonefname);
2744         if (tsec != NULL)
2745                 dns_tsec_getkey(tsec, &uctx->tsigkey);
2746         uctx->event = (dns_clientupdateevent_t *)
2747                 isc_event_allocate(client->mctx, clone, DNS_EVENT_UPDATEDONE,
2748                                    action, arg, sizeof(*uctx->event));
2749         if (uctx->event == NULL)
2750                 goto fail;
2751         if (zonename != NULL) {
2752                 uctx->zonename = dns_fixedname_name(&uctx->zonefname);
2753                 result = dns_name_copy(zonename, uctx->zonename, NULL);
2754         }
2755         if (servers != NULL) {
2756                 for (server = ISC_LIST_HEAD(*servers);
2757                      server != NULL;
2758                      server = ISC_LIST_NEXT(server, link)) {
2759                         sa = isc_mem_get(client->mctx, sizeof(*sa));
2760                         if (sa == NULL)
2761                                 goto fail;
2762                         sa->type = server->type;
2763                         sa->length = server->length;
2764                         ISC_LINK_INIT(sa, link);
2765                         ISC_LIST_APPEND(uctx->servers, sa, link);
2766                         if (uctx->currentserver == NULL)
2767                                 uctx->currentserver = sa;
2768                         uctx->nservers++;
2769                 }
2770         }
2771
2772         /* Make update message */
2773         result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTRENDER,
2774                                     &uctx->updatemsg);
2775         if (result != ISC_R_SUCCESS)
2776                 goto fail;
2777         uctx->updatemsg->opcode = dns_opcode_update;
2778
2779         if (prerequisites != NULL) {
2780                 for (name = ISC_LIST_HEAD(*prerequisites); name != NULL;
2781                      name = ISC_LIST_NEXT(name, link)) {
2782                         newname = NULL;
2783                         result = copy_name(client->mctx, uctx->updatemsg,
2784                                            name, &newname);
2785                         if (result != ISC_R_SUCCESS)
2786                                 goto fail;
2787                         dns_message_addname(uctx->updatemsg, newname,
2788                                             DNS_SECTION_PREREQUISITE);
2789                 }
2790         }
2791
2792         for (name = ISC_LIST_HEAD(*updates); name != NULL;
2793              name = ISC_LIST_NEXT(name, link)) {
2794                 newname = NULL;
2795                 result = copy_name(client->mctx, uctx->updatemsg, name,
2796                                    &newname);
2797                 if (result != ISC_R_SUCCESS)
2798                         goto fail;
2799                 dns_message_addname(uctx->updatemsg, newname,
2800                                     DNS_SECTION_UPDATE);
2801         }
2802
2803         uctx->firstname = NULL;
2804         result = dns_message_firstname(uctx->updatemsg, section);
2805         if (result == ISC_R_NOMORE) {
2806                 section = DNS_SECTION_PREREQUISITE;
2807                 result = dns_message_firstname(uctx->updatemsg, section);
2808         }
2809         if (result != ISC_R_SUCCESS)
2810                 goto fail;
2811         dns_message_currentname(uctx->updatemsg, section, &uctx->firstname);
2812
2813         uctx->magic = UCTX_MAGIC;
2814
2815         LOCK(&client->lock);
2816         ISC_LIST_APPEND(client->updatectxs, uctx, link);
2817         UNLOCK(&client->lock);
2818
2819         if (uctx->zonename != NULL && uctx->currentserver != NULL) {
2820                 result = send_update(uctx);
2821                 if (result != ISC_R_SUCCESS)
2822                         goto fail;
2823         } else if (uctx->currentserver != NULL) {
2824                 result = request_soa(uctx);
2825                 if (result != ISC_R_SUCCESS)
2826                         goto fail;
2827         } else {
2828                 dns_name_clone(uctx->firstname, &uctx->soaqname);
2829                 result = dns_client_startresolve(uctx->client, &uctx->soaqname,
2830                                                  uctx->rdclass,
2831                                                  dns_rdatatype_soa, 0,
2832                                                  client->task, resolvesoa_done,
2833                                                  uctx, &uctx->restrans);
2834                 if (result != ISC_R_SUCCESS)
2835                         goto fail;
2836         }
2837
2838         *transp = (dns_clientupdatetrans_t *)uctx;
2839
2840         return (ISC_R_SUCCESS);
2841
2842  fail:
2843         if (ISC_LINK_LINKED(uctx, link)) {
2844                 LOCK(&client->lock);
2845                 ISC_LIST_UNLINK(client->updatectxs, uctx, link);
2846                 UNLOCK(&client->lock);
2847         }
2848         if (uctx->updatemsg != NULL)
2849                 dns_message_destroy(&uctx->updatemsg);
2850         while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) {
2851                 ISC_LIST_UNLINK(uctx->servers, sa, link);
2852                 isc_mem_put(client->mctx, sa, sizeof(*sa));
2853         }
2854         if (uctx->event != NULL)
2855                 isc_event_free(ISC_EVENT_PTR(&uctx->event));
2856         if (uctx->tsigkey != NULL)
2857                 dns_tsigkey_detach(&uctx->tsigkey);
2858         isc_task_detach(&clone);
2859         DESTROYLOCK(&uctx->lock);
2860         uctx->magic = 0;
2861         isc_mem_put(client->mctx, uctx, sizeof(*uctx));
2862         dns_view_detach(&view);
2863
2864         return (result);
2865 }
2866
2867 void
2868 dns_client_cancelupdate(dns_clientupdatetrans_t *trans) {
2869         updatectx_t *uctx;
2870
2871         REQUIRE(trans != NULL);
2872         uctx = (updatectx_t *)trans;
2873         REQUIRE(UCTX_VALID(uctx));
2874
2875         LOCK(&uctx->lock);
2876
2877         if (!uctx->canceled) {
2878                 uctx->canceled = ISC_TRUE;
2879                 if (uctx->updatereq != NULL)
2880                         dns_request_cancel(uctx->updatereq);
2881                 if (uctx->soareq != NULL)
2882                         dns_request_cancel(uctx->soareq);
2883                 if (uctx->restrans != NULL)
2884                         dns_client_cancelresolve(uctx->restrans);
2885                 if (uctx->restrans2 != NULL)
2886                         dns_client_cancelresolve(uctx->restrans2);
2887         }
2888
2889         UNLOCK(&uctx->lock);
2890 }
2891
2892 void
2893 dns_client_destroyupdatetrans(dns_clientupdatetrans_t **transp) {
2894         updatectx_t *uctx;
2895         isc_mem_t *mctx;
2896         dns_client_t *client;
2897         isc_boolean_t need_destroyclient = ISC_FALSE;
2898         isc_sockaddr_t *sa;
2899
2900         REQUIRE(transp != NULL);
2901         uctx = (updatectx_t *)*transp;
2902         REQUIRE(UCTX_VALID(uctx));
2903         client = uctx->client;
2904         REQUIRE(DNS_CLIENT_VALID(client));
2905         REQUIRE(uctx->updatereq == NULL && uctx->updatemsg == NULL &&
2906                 uctx->soareq == NULL && uctx->soaquery == NULL &&
2907                 uctx->event == NULL && uctx->tsigkey == NULL &&
2908                 uctx->sig0key == NULL);
2909
2910         mctx = client->mctx;
2911         dns_view_detach(&uctx->view);
2912         while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) {
2913                 ISC_LIST_UNLINK(uctx->servers, sa, link);
2914                 isc_mem_put(mctx, sa, sizeof(*sa));
2915         }
2916
2917         LOCK(&client->lock);
2918
2919         INSIST(ISC_LINK_LINKED(uctx, link));
2920         ISC_LIST_UNLINK(client->updatectxs, uctx, link);
2921
2922         if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
2923             ISC_LIST_EMPTY(client->reqctxs) &&
2924             ISC_LIST_EMPTY(client->updatectxs))
2925                 need_destroyclient = ISC_TRUE;
2926
2927         UNLOCK(&client->lock);
2928
2929         DESTROYLOCK(&uctx->lock);
2930         uctx->magic = 0;
2931
2932         isc_mem_put(mctx, uctx, sizeof(*uctx));
2933
2934         if (need_destroyclient)
2935                 destroyclient(&client);
2936
2937         *transp = NULL;
2938 }
2939
2940 isc_mem_t *
2941 dns_client_mctx(dns_client_t *client) {
2942
2943         REQUIRE(DNS_CLIENT_VALID(client));
2944         return (client->mctx);
2945 }
2946
2947 typedef struct {
2948         isc_buffer_t    buffer;
2949         dns_rdataset_t  rdataset;
2950         dns_rdatalist_t rdatalist;
2951         dns_rdata_t     rdata;
2952         size_t          size;
2953         isc_mem_t *     mctx;
2954         unsigned char   data[FLEXIBLE_ARRAY_MEMBER];
2955 } dns_client_updaterec_t;
2956
2957 isc_result_t
2958 dns_client_updaterec(dns_client_updateop_t op, dns_name_t *owner,
2959                      dns_rdatatype_t type, dns_rdata_t *source,
2960                      dns_ttl_t ttl, dns_name_t *target,
2961                      dns_rdataset_t *rdataset, dns_rdatalist_t *rdatalist,
2962                      dns_rdata_t *rdata, isc_mem_t *mctx)
2963 {
2964         dns_client_updaterec_t *updaterec = NULL;
2965         size_t size = offsetof(dns_client_updaterec_t, data);
2966
2967         REQUIRE(op < updateop_max);
2968         REQUIRE(owner != NULL);
2969         REQUIRE((rdataset != NULL && rdatalist != NULL && rdata != NULL) ||
2970                 (rdataset == NULL && rdatalist == NULL && rdata == NULL &&
2971                  mctx != NULL));
2972         if (op == updateop_add)
2973                 REQUIRE(source != NULL);
2974         if (source != NULL) {
2975                 REQUIRE(source->type == type);
2976                 REQUIRE(op == updateop_add || op == updateop_delete ||
2977                         op == updateop_exist);
2978         }
2979
2980         size += owner->length;
2981         if (source != NULL)
2982                 size += source->length;
2983
2984         if (rdataset == NULL) {
2985                 updaterec = isc_mem_get(mctx, size);
2986                 if (updaterec == NULL)
2987                         return (ISC_R_NOMEMORY);
2988                 rdataset = &updaterec->rdataset;
2989                 rdatalist = &updaterec->rdatalist;
2990                 rdata = &updaterec->rdata;
2991                 dns_rdataset_init(rdataset);
2992                 dns_rdatalist_init(&updaterec->rdatalist);
2993                 dns_rdata_init(&updaterec->rdata);
2994                 isc_buffer_init(&updaterec->buffer, updaterec->data,
2995                                 size - offsetof(dns_client_updaterec_t, data));
2996                 dns_name_copy(owner, target, &updaterec->buffer);
2997                 if (source != NULL) {
2998                         isc_region_t r;
2999                         dns_rdata_clone(source, rdata);
3000                         dns_rdata_toregion(rdata, &r);
3001                         rdata->data = isc_buffer_used(&updaterec->buffer);
3002                         isc_buffer_copyregion(&updaterec->buffer, &r);
3003                 }
3004                 updaterec->mctx = NULL;
3005                 isc_mem_attach(mctx, &updaterec->mctx);
3006         } else if (source != NULL)
3007                 dns_rdata_clone(source, rdata);
3008
3009         switch (op) {
3010         case updateop_add:
3011                 break;
3012         case updateop_delete:
3013                 if (source != NULL) {
3014                         ttl = 0;
3015                         dns_rdata_makedelete(rdata);
3016                 } else
3017                         dns_rdata_deleterrset(rdata, type);
3018                 break;
3019         case updateop_notexist:
3020                 dns_rdata_notexist(rdata, type);
3021                 break;
3022         case updateop_exist:
3023                 if (source == NULL) {
3024                         ttl = 0;
3025                         dns_rdata_exists(rdata, type);
3026                 }
3027         case updateop_none:
3028                 break;
3029         default:
3030                 INSIST(0);
3031         }
3032
3033         rdatalist->type = rdata->type;
3034         rdatalist->rdclass = rdata->rdclass;
3035         if (source != NULL) {
3036                 rdatalist->covers = dns_rdata_covers(rdata);
3037                 rdatalist->ttl = ttl;
3038         }
3039         ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
3040         dns_rdatalist_tordataset(rdatalist, rdataset);
3041         ISC_LIST_APPEND(target->list, rdataset, link);
3042         if (updaterec != NULL) {
3043                 target->attributes |= DNS_NAMEATTR_HASUPDATEREC;
3044                 dns_name_setbuffer(target, &updaterec->buffer);
3045         }
3046         if (op == updateop_add || op == updateop_delete)
3047                 target->attributes |= DNS_NAMEATTR_UPDATE;
3048         else
3049                 target->attributes |= DNS_NAMEATTR_PREREQUISITE;
3050         return (ISC_R_SUCCESS);
3051 }
3052
3053 void
3054 dns_client_freeupdate(dns_name_t **namep) {
3055         dns_client_updaterec_t *updaterec;
3056         dns_rdatalist_t *rdatalist;
3057         dns_rdataset_t *rdataset;
3058         dns_rdata_t *rdata;
3059         dns_name_t *name;
3060
3061         REQUIRE(namep != NULL && *namep != NULL);
3062
3063         name = *namep;
3064         for (rdataset = ISC_LIST_HEAD(name->list);
3065              rdataset != NULL;
3066              rdataset = ISC_LIST_HEAD(name->list)) {
3067                 ISC_LIST_UNLINK(name->list, rdataset, link);
3068                 rdatalist = NULL;
3069                 dns_rdatalist_fromrdataset(rdataset, &rdatalist);
3070                 if (rdatalist == NULL) {
3071                         dns_rdataset_disassociate(rdataset);
3072                         continue;
3073                 }
3074                 for (rdata = ISC_LIST_HEAD(rdatalist->rdata);
3075                      rdata != NULL;
3076                      rdata = ISC_LIST_HEAD(rdatalist->rdata))
3077                         ISC_LIST_UNLINK(rdatalist->rdata, rdata, link);
3078                 dns_rdataset_disassociate(rdataset);
3079         }
3080
3081         if ((name->attributes & DNS_NAMEATTR_HASUPDATEREC) != 0) {
3082                 updaterec = (dns_client_updaterec_t *)name->buffer;
3083                 INSIST(updaterec != NULL);
3084                 isc_mem_putanddetach(&updaterec->mctx, updaterec,
3085                                      updaterec->size);
3086                 *namep = NULL;
3087         }
3088 }