]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/lib/dns/client.c
MFC r363988:
[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 static void
1124 suspend(isc_task_t *task, isc_event_t *event) {
1125         isc_appctx_t *actx = event->ev_arg;
1126
1127         UNUSED(task);
1128
1129         isc_app_ctxsuspend(actx);
1130         isc_event_free(&event);
1131 }
1132
1133 static void
1134 resolve_done(isc_task_t *task, isc_event_t *event) {
1135         resarg_t *resarg = event->ev_arg;
1136         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
1137         dns_name_t *name;
1138         isc_result_t result;
1139
1140         UNUSED(task);
1141
1142         LOCK(&resarg->lock);
1143
1144         resarg->result = rev->result;
1145         resarg->vresult = rev->vresult;
1146         while ((name = ISC_LIST_HEAD(rev->answerlist)) != NULL) {
1147                 ISC_LIST_UNLINK(rev->answerlist, name, link);
1148                 ISC_LIST_APPEND(*resarg->namelist, name, link);
1149         }
1150
1151         dns_client_destroyrestrans(&resarg->trans);
1152         isc_event_free(&event);
1153
1154         if (!resarg->canceled) {
1155                 UNLOCK(&resarg->lock);
1156
1157                 /*
1158                  * We may or may not be running.  isc__appctx_onrun will
1159                  * fail if we are currently running otherwise we post a
1160                  * action to call isc_app_ctxsuspend when we do start
1161                  * running.
1162                  */
1163                 result = isc_app_ctxonrun(resarg->actx, resarg->client->mctx,
1164                                            task, suspend, resarg->actx);
1165                 if (result == ISC_R_ALREADYRUNNING)
1166                         isc_app_ctxsuspend(resarg->actx);
1167         } else {
1168                 /*
1169                  * We have already exited from the loop (due to some
1170                  * unexpected event).  Just clean the arg up.
1171                  */
1172                 UNLOCK(&resarg->lock);
1173                 DESTROYLOCK(&resarg->lock);
1174                 isc_mem_put(resarg->client->mctx, resarg, sizeof(*resarg));
1175         }
1176 }
1177
1178 isc_result_t
1179 dns_client_resolve(dns_client_t *client, dns_name_t *name,
1180                    dns_rdataclass_t rdclass, dns_rdatatype_t type,
1181                    unsigned int options, dns_namelist_t *namelist)
1182 {
1183         isc_result_t result;
1184         isc_appctx_t *actx;
1185         resarg_t *resarg;
1186
1187         REQUIRE(DNS_CLIENT_VALID(client));
1188         REQUIRE(namelist != NULL && ISC_LIST_EMPTY(*namelist));
1189
1190         if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
1191             (options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) {
1192                 /*
1193                  * If the client is run under application's control, we need
1194                  * to create a new running (sub)environment for this
1195                  * particular resolution.
1196                  */
1197                 return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
1198         } else
1199                 actx = client->actx;
1200
1201         resarg = isc_mem_get(client->mctx, sizeof(*resarg));
1202         if (resarg == NULL)
1203                 return (ISC_R_NOMEMORY);
1204
1205         result = isc_mutex_init(&resarg->lock);
1206         if (result != ISC_R_SUCCESS) {
1207                 isc_mem_put(client->mctx, resarg, sizeof(*resarg));
1208                 return (result);
1209         }
1210
1211         resarg->actx = actx;
1212         resarg->client = client;
1213         resarg->result = DNS_R_SERVFAIL;
1214         resarg->namelist = namelist;
1215         resarg->trans = NULL;
1216         resarg->canceled = ISC_FALSE;
1217         result = dns_client_startresolve(client, name, rdclass, type, options,
1218                                          client->task, resolve_done, resarg,
1219                                          &resarg->trans);
1220         if (result != ISC_R_SUCCESS) {
1221                 DESTROYLOCK(&resarg->lock);
1222                 isc_mem_put(client->mctx, resarg, sizeof(*resarg));
1223                 return (result);
1224         }
1225
1226         /*
1227          * Start internal event loop.  It blocks until the entire process
1228          * is completed.
1229          */
1230         result = isc_app_ctxrun(actx);
1231
1232         LOCK(&resarg->lock);
1233         if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
1234                 result = resarg->result;
1235         if (result != ISC_R_SUCCESS && resarg->vresult != ISC_R_SUCCESS) {
1236                 /*
1237                  * If this lookup failed due to some error in DNSSEC
1238                  * validation, return the validation error code.
1239                  * XXX: or should we pass the validation result separately?
1240                  */
1241                 result = resarg->vresult;
1242         }
1243         if (resarg->trans != NULL) {
1244                 /*
1245                  * Unusual termination (perhaps due to signal).  We need some
1246                  * tricky cleanup process.
1247                  */
1248                 resarg->canceled = ISC_TRUE;
1249                 dns_client_cancelresolve(resarg->trans);
1250
1251                 UNLOCK(&resarg->lock);
1252
1253                 /* resarg will be freed in the event handler. */
1254         } else {
1255                 UNLOCK(&resarg->lock);
1256
1257                 DESTROYLOCK(&resarg->lock);
1258                 isc_mem_put(client->mctx, resarg, sizeof(*resarg));
1259         }
1260
1261         return (result);
1262 }
1263
1264 isc_result_t
1265 dns_client_startresolve(dns_client_t *client, dns_name_t *name,
1266                         dns_rdataclass_t rdclass, dns_rdatatype_t type,
1267                         unsigned int options, isc_task_t *task,
1268                         isc_taskaction_t action, void *arg,
1269                         dns_clientrestrans_t **transp)
1270 {
1271         dns_view_t *view = NULL;
1272         dns_clientresevent_t *event = NULL;
1273         resctx_t *rctx = NULL;
1274         isc_task_t *clone = NULL;
1275         isc_mem_t *mctx;
1276         isc_result_t result;
1277         dns_rdataset_t *rdataset, *sigrdataset;
1278         isc_boolean_t want_dnssec;
1279
1280         REQUIRE(DNS_CLIENT_VALID(client));
1281         REQUIRE(transp != NULL && *transp == NULL);
1282
1283         LOCK(&client->lock);
1284         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
1285                                    rdclass, &view);
1286         UNLOCK(&client->lock);
1287         if (result != ISC_R_SUCCESS)
1288                 return (result);
1289
1290         mctx = client->mctx;
1291         rdataset = NULL;
1292         sigrdataset = NULL;
1293         want_dnssec = ISC_TF((options & DNS_CLIENTRESOPT_NODNSSEC) == 0);
1294
1295         /*
1296          * Prepare some intermediate resources
1297          */
1298         clone = NULL;
1299         isc_task_attach(task, &clone);
1300         event = (dns_clientresevent_t *)
1301                 isc_event_allocate(mctx, clone, DNS_EVENT_CLIENTRESDONE,
1302                                    action, arg, sizeof(*event));
1303         if (event == NULL) {
1304                 result = ISC_R_NOMEMORY;
1305                 goto cleanup;
1306         }
1307         event->result = DNS_R_SERVFAIL;
1308         ISC_LIST_INIT(event->answerlist);
1309
1310         rctx = isc_mem_get(mctx, sizeof(*rctx));
1311         if (rctx == NULL)
1312                 result = ISC_R_NOMEMORY;
1313         else {
1314                 result = isc_mutex_init(&rctx->lock);
1315                 if (result != ISC_R_SUCCESS) {
1316                         isc_mem_put(mctx, rctx, sizeof(*rctx));
1317                         rctx = NULL;
1318                 }
1319         }
1320         if (result != ISC_R_SUCCESS)
1321                 goto cleanup;
1322
1323         result = getrdataset(mctx, &rdataset);
1324         if (result != ISC_R_SUCCESS)
1325                 goto cleanup;
1326         rctx->rdataset = rdataset;
1327
1328         if (want_dnssec) {
1329                 result = getrdataset(mctx, &sigrdataset);
1330                 if (result != ISC_R_SUCCESS)
1331                         goto cleanup;
1332         }
1333         rctx->sigrdataset = sigrdataset;
1334
1335         dns_fixedname_init(&rctx->name);
1336         result = dns_name_copy(name, dns_fixedname_name(&rctx->name), NULL);
1337         if (result != ISC_R_SUCCESS)
1338                 goto cleanup;
1339
1340         rctx->client = client;
1341         ISC_LINK_INIT(rctx, link);
1342         rctx->canceled = ISC_FALSE;
1343         rctx->task = client->task;
1344         rctx->type = type;
1345         rctx->view = view;
1346         rctx->restarts = 0;
1347         rctx->fetch = NULL;
1348         rctx->want_dnssec = want_dnssec;
1349         ISC_LIST_INIT(rctx->namelist);
1350         rctx->event = event;
1351
1352         rctx->magic = RCTX_MAGIC;
1353
1354         LOCK(&client->lock);
1355         ISC_LIST_APPEND(client->resctxs, rctx, link);
1356         UNLOCK(&client->lock);
1357
1358         *transp = (dns_clientrestrans_t *)rctx;
1359         client_resfind(rctx, NULL);
1360
1361         return (ISC_R_SUCCESS);
1362
1363  cleanup:
1364         if (rdataset != NULL)
1365                 putrdataset(client->mctx, &rdataset);
1366         if (sigrdataset != NULL)
1367                 putrdataset(client->mctx, &sigrdataset);
1368         if (rctx != NULL) {
1369                 DESTROYLOCK(&rctx->lock);
1370                 isc_mem_put(mctx, rctx, sizeof(*rctx));
1371         }
1372         if (event != NULL)
1373                 isc_event_free(ISC_EVENT_PTR(&event));
1374         isc_task_detach(&clone);
1375         dns_view_detach(&view);
1376
1377         return (result);
1378 }
1379
1380 void
1381 dns_client_cancelresolve(dns_clientrestrans_t *trans) {
1382         resctx_t *rctx;
1383
1384         REQUIRE(trans != NULL);
1385         rctx = (resctx_t *)trans;
1386         REQUIRE(RCTX_VALID(rctx));
1387
1388         LOCK(&rctx->lock);
1389
1390         if (!rctx->canceled) {
1391                 rctx->canceled = ISC_TRUE;
1392                 if (rctx->fetch != NULL)
1393                         dns_resolver_cancelfetch(rctx->fetch);
1394         }
1395
1396         UNLOCK(&rctx->lock);
1397 }
1398
1399 void
1400 dns_client_freeresanswer(dns_client_t *client, dns_namelist_t *namelist) {
1401         dns_name_t *name;
1402         dns_rdataset_t *rdataset;
1403
1404         REQUIRE(DNS_CLIENT_VALID(client));
1405         REQUIRE(namelist != NULL);
1406
1407         while ((name = ISC_LIST_HEAD(*namelist)) != NULL) {
1408                 ISC_LIST_UNLINK(*namelist, name, link);
1409                 while ((rdataset = ISC_LIST_HEAD(name->list)) != NULL) {
1410                         ISC_LIST_UNLINK(name->list, rdataset, link);
1411                         putrdataset(client->mctx, &rdataset);
1412                 }
1413                 dns_name_free(name, client->mctx);
1414                 isc_mem_put(client->mctx, name, sizeof(*name));
1415         }
1416 }
1417
1418 void
1419 dns_client_destroyrestrans(dns_clientrestrans_t **transp) {
1420         resctx_t *rctx;
1421         isc_mem_t *mctx;
1422         dns_client_t *client;
1423         isc_boolean_t need_destroyclient = ISC_FALSE;
1424
1425         REQUIRE(transp != NULL);
1426         rctx = (resctx_t *)*transp;
1427         REQUIRE(RCTX_VALID(rctx));
1428         REQUIRE(rctx->fetch == NULL);
1429         REQUIRE(rctx->event == NULL);
1430         client = rctx->client;
1431         REQUIRE(DNS_CLIENT_VALID(client));
1432
1433         mctx = client->mctx;
1434         dns_view_detach(&rctx->view);
1435
1436         /*
1437          * Wait for the lock in client_resfind to be released before
1438          * destroying the lock.
1439          */
1440         LOCK(&rctx->lock);
1441         UNLOCK(&rctx->lock);
1442
1443         LOCK(&client->lock);
1444
1445         INSIST(ISC_LINK_LINKED(rctx, link));
1446         ISC_LIST_UNLINK(client->resctxs, rctx, link);
1447
1448         if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
1449             ISC_LIST_EMPTY(client->reqctxs) &&
1450             ISC_LIST_EMPTY(client->updatectxs))
1451                 need_destroyclient = ISC_TRUE;
1452
1453         UNLOCK(&client->lock);
1454
1455         INSIST(ISC_LIST_EMPTY(rctx->namelist));
1456
1457         DESTROYLOCK(&rctx->lock);
1458         rctx->magic = 0;
1459
1460         isc_mem_put(mctx, rctx, sizeof(*rctx));
1461
1462         if (need_destroyclient)
1463                 destroyclient(&client);
1464
1465         *transp = NULL;
1466 }
1467
1468 isc_result_t
1469 dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass,
1470                          dns_name_t *keyname, isc_buffer_t *keydatabuf)
1471 {
1472         isc_result_t result;
1473         dns_view_t *view = NULL;
1474         dst_key_t *dstkey = NULL;
1475         dns_keytable_t *secroots = NULL;
1476
1477         REQUIRE(DNS_CLIENT_VALID(client));
1478
1479         LOCK(&client->lock);
1480         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
1481                                    rdclass, &view);
1482         UNLOCK(&client->lock);
1483         if (result != ISC_R_SUCCESS)
1484                 goto cleanup;
1485
1486         result = dns_view_getsecroots(view, &secroots);
1487         if (result != ISC_R_SUCCESS)
1488                 goto cleanup;
1489
1490         result = dst_key_fromdns(keyname, rdclass, keydatabuf, client->mctx,
1491                                  &dstkey);
1492         if (result != ISC_R_SUCCESS)
1493                 goto cleanup;
1494
1495         result = dns_keytable_add(secroots, ISC_FALSE, &dstkey);
1496
1497  cleanup:
1498         if (dstkey != NULL)
1499                 dst_key_free(&dstkey);
1500         if (view != NULL)
1501                 dns_view_detach(&view);
1502         if (secroots != NULL)
1503                 dns_keytable_detach(&secroots);
1504         return (result);
1505 }
1506
1507 /*%
1508  * Simple request routines
1509  */
1510 static void
1511 request_done(isc_task_t *task, isc_event_t *event) {
1512         dns_requestevent_t *reqev = NULL;
1513         dns_request_t *request;
1514         isc_result_t result, eresult;
1515         reqctx_t *ctx;
1516
1517         UNUSED(task);
1518
1519         REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
1520         reqev = (dns_requestevent_t *)event;
1521         request = reqev->request;
1522         result = eresult = reqev->result;
1523         ctx = reqev->ev_arg;
1524         REQUIRE(REQCTX_VALID(ctx));
1525
1526         isc_event_free(&event);
1527
1528         LOCK(&ctx->lock);
1529
1530         if (eresult == ISC_R_SUCCESS) {
1531                 result = dns_request_getresponse(request, ctx->event->rmessage,
1532                                                  ctx->parseoptions);
1533         }
1534
1535         if (ctx->tsigkey != NULL)
1536                 dns_tsigkey_detach(&ctx->tsigkey);
1537
1538         if (ctx->canceled)
1539                 ctx->event->result = ISC_R_CANCELED;
1540         else
1541                 ctx->event->result = result;
1542         task = ctx->event->ev_sender;
1543         ctx->event->ev_sender = ctx;
1544         isc_task_sendanddetach(&task, ISC_EVENT_PTR(&ctx->event));
1545
1546         UNLOCK(&ctx->lock);
1547 }
1548
1549 static void
1550 localrequest_done(isc_task_t *task, isc_event_t *event) {
1551         reqarg_t *reqarg = event->ev_arg;
1552         dns_clientreqevent_t *rev =(dns_clientreqevent_t *)event;
1553
1554         UNUSED(task);
1555
1556         REQUIRE(event->ev_type == DNS_EVENT_CLIENTREQDONE);
1557
1558         LOCK(&reqarg->lock);
1559
1560         reqarg->result = rev->result;
1561         dns_client_destroyreqtrans(&reqarg->trans);
1562         isc_event_free(&event);
1563
1564         if (!reqarg->canceled) {
1565                 UNLOCK(&reqarg->lock);
1566
1567                 /* Exit from the internal event loop */
1568                 isc_app_ctxsuspend(reqarg->actx);
1569         } else {
1570                 /*
1571                  * We have already exited from the loop (due to some
1572                  * unexpected event).  Just clean the arg up.
1573                  */
1574                 UNLOCK(&reqarg->lock);
1575                 DESTROYLOCK(&reqarg->lock);
1576                 isc_mem_put(reqarg->client->mctx, reqarg, sizeof(*reqarg));
1577         }
1578 }
1579
1580 isc_result_t
1581 dns_client_request(dns_client_t *client, dns_message_t *qmessage,
1582                    dns_message_t *rmessage, isc_sockaddr_t *server,
1583                    unsigned int options, unsigned int parseoptions,
1584                    dns_tsec_t *tsec, unsigned int timeout,
1585                    unsigned int udptimeout, unsigned int udpretries)
1586 {
1587         isc_appctx_t *actx;
1588         reqarg_t *reqarg;
1589         isc_result_t result;
1590
1591         REQUIRE(DNS_CLIENT_VALID(client));
1592         REQUIRE(qmessage != NULL);
1593         REQUIRE(rmessage != NULL);
1594
1595         if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
1596             (options & DNS_CLIENTREQOPT_ALLOWRUN) == 0) {
1597                 /*
1598                  * If the client is run under application's control, we need
1599                  * to create a new running (sub)environment for this
1600                  * particular resolution.
1601                  */
1602                 return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
1603         } else
1604                 actx = client->actx;
1605
1606         reqarg = isc_mem_get(client->mctx, sizeof(*reqarg));
1607         if (reqarg == NULL)
1608                 return (ISC_R_NOMEMORY);
1609
1610         result = isc_mutex_init(&reqarg->lock);
1611         if (result != ISC_R_SUCCESS) {
1612                 isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
1613                 return (result);
1614         }
1615
1616         reqarg->actx = actx;
1617         reqarg->client = client;
1618         reqarg->trans = NULL;
1619         reqarg->canceled = ISC_FALSE;
1620
1621         result = dns_client_startrequest(client, qmessage, rmessage, server,
1622                                          options, parseoptions, tsec, timeout,
1623                                          udptimeout, udpretries,
1624                                          client->task, localrequest_done,
1625                                          reqarg, &reqarg->trans);
1626         if (result != ISC_R_SUCCESS) {
1627                 DESTROYLOCK(&reqarg->lock);
1628                 isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
1629                 return (result);
1630         }
1631
1632         /*
1633          * Start internal event loop.  It blocks until the entire process
1634          * is completed.
1635          */
1636         result = isc_app_ctxrun(actx);
1637
1638         LOCK(&reqarg->lock);
1639         if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
1640                 result = reqarg->result;
1641         if (reqarg->trans != NULL) {
1642                 /*
1643                  * Unusual termination (perhaps due to signal).  We need some
1644                  * tricky cleanup process.
1645                  */
1646                 reqarg->canceled = ISC_TRUE;
1647                 dns_client_cancelresolve(reqarg->trans);
1648
1649                 UNLOCK(&reqarg->lock);
1650
1651                 /* reqarg will be freed in the event handler. */
1652         } else {
1653                 UNLOCK(&reqarg->lock);
1654
1655                 DESTROYLOCK(&reqarg->lock);
1656                 isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
1657         }
1658
1659         return (result);
1660 }
1661
1662 isc_result_t
1663 dns_client_startrequest(dns_client_t *client, dns_message_t *qmessage,
1664                         dns_message_t *rmessage, isc_sockaddr_t *server,
1665                         unsigned int options, unsigned int parseoptions,
1666                         dns_tsec_t *tsec, unsigned int timeout,
1667                         unsigned int udptimeout, unsigned int udpretries,
1668                         isc_task_t *task, isc_taskaction_t action, void *arg,
1669                         dns_clientreqtrans_t **transp)
1670 {
1671         isc_result_t result;
1672         dns_view_t *view = NULL;
1673         isc_task_t *clone = NULL;
1674         dns_clientreqevent_t *event = NULL;
1675         reqctx_t *ctx = NULL;
1676         dns_tsectype_t tsectype = dns_tsectype_none;
1677
1678         UNUSED(options);
1679
1680         REQUIRE(DNS_CLIENT_VALID(client));
1681         REQUIRE(qmessage != NULL);
1682         REQUIRE(rmessage != NULL);
1683         REQUIRE(transp != NULL && *transp == NULL);
1684
1685         if (tsec != NULL) {
1686                 tsectype = dns_tsec_gettype(tsec);
1687                 if (tsectype != dns_tsectype_tsig)
1688                         return (ISC_R_NOTIMPLEMENTED); /* XXX */
1689         }
1690
1691         LOCK(&client->lock);
1692         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
1693                                    qmessage->rdclass, &view);
1694         UNLOCK(&client->lock);
1695         if (result != ISC_R_SUCCESS)
1696                 return (result);
1697
1698         clone = NULL;
1699         isc_task_attach(task, &clone);
1700         event = (dns_clientreqevent_t *)
1701                 isc_event_allocate(client->mctx, clone,
1702                                    DNS_EVENT_CLIENTREQDONE,
1703                                    action, arg, sizeof(*event));
1704         if (event == NULL) {
1705                 result = ISC_R_NOMEMORY;
1706                 goto cleanup;
1707         }
1708
1709         ctx = isc_mem_get(client->mctx, sizeof(*ctx));
1710         if (ctx == NULL)
1711                 result = ISC_R_NOMEMORY;
1712         else {
1713                 result = isc_mutex_init(&ctx->lock);
1714                 if (result != ISC_R_SUCCESS) {
1715                         isc_mem_put(client->mctx, ctx, sizeof(*ctx));
1716                         ctx = NULL;
1717                 }
1718         }
1719         if (result != ISC_R_SUCCESS)
1720                 goto cleanup;
1721
1722         ctx->client = client;
1723         ISC_LINK_INIT(ctx, link);
1724         ctx->parseoptions = parseoptions;
1725         ctx->canceled = ISC_FALSE;
1726         ctx->event = event;
1727         ctx->event->rmessage = rmessage;
1728         ctx->tsigkey = NULL;
1729         if (tsec != NULL)
1730                 dns_tsec_getkey(tsec, &ctx->tsigkey);
1731
1732         ctx->magic = REQCTX_MAGIC;
1733
1734         LOCK(&client->lock);
1735         ISC_LIST_APPEND(client->reqctxs, ctx, link);
1736         UNLOCK(&client->lock);
1737
1738         ctx->request = NULL;
1739         result = dns_request_createvia3(view->requestmgr, qmessage, NULL,
1740                                         server, options, ctx->tsigkey,
1741                                         timeout, udptimeout, udpretries,
1742                                         client->task, request_done, ctx,
1743                                         &ctx->request);
1744         if (result == ISC_R_SUCCESS) {
1745                 dns_view_detach(&view);
1746                 *transp = (dns_clientreqtrans_t *)ctx;
1747                 return (ISC_R_SUCCESS);
1748         }
1749
1750  cleanup:
1751         if (ctx != NULL) {
1752                 LOCK(&client->lock);
1753                 ISC_LIST_UNLINK(client->reqctxs, ctx, link);
1754                 UNLOCK(&client->lock);
1755                 DESTROYLOCK(&ctx->lock);
1756                 isc_mem_put(client->mctx, ctx, sizeof(*ctx));
1757         }
1758         if (event != NULL)
1759                 isc_event_free(ISC_EVENT_PTR(&event));
1760         isc_task_detach(&clone);
1761         dns_view_detach(&view);
1762
1763         return (result);
1764 }
1765
1766 void
1767 dns_client_cancelrequest(dns_clientreqtrans_t *trans) {
1768         reqctx_t *ctx;
1769
1770         REQUIRE(trans != NULL);
1771         ctx = (reqctx_t *)trans;
1772         REQUIRE(REQCTX_VALID(ctx));
1773
1774         LOCK(&ctx->lock);
1775
1776         if (!ctx->canceled) {
1777                 ctx->canceled = ISC_TRUE;
1778                 if (ctx->request != NULL)
1779                         dns_request_cancel(ctx->request);
1780         }
1781
1782         UNLOCK(&ctx->lock);
1783 }
1784
1785 void
1786 dns_client_destroyreqtrans(dns_clientreqtrans_t **transp) {
1787         reqctx_t *ctx;
1788         isc_mem_t *mctx;
1789         dns_client_t *client;
1790         isc_boolean_t need_destroyclient = ISC_FALSE;
1791
1792         REQUIRE(transp != NULL);
1793         ctx = (reqctx_t *)*transp;
1794         REQUIRE(REQCTX_VALID(ctx));
1795         client = ctx->client;
1796         REQUIRE(DNS_CLIENT_VALID(client));
1797         REQUIRE(ctx->event == NULL);
1798         REQUIRE(ctx->request != NULL);
1799
1800         dns_request_destroy(&ctx->request);
1801         mctx = client->mctx;
1802
1803         LOCK(&client->lock);
1804
1805         INSIST(ISC_LINK_LINKED(ctx, link));
1806         ISC_LIST_UNLINK(client->reqctxs, ctx, link);
1807
1808         if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
1809             ISC_LIST_EMPTY(client->reqctxs) &&
1810             ISC_LIST_EMPTY(client->updatectxs)) {
1811                 need_destroyclient = ISC_TRUE;
1812         }
1813
1814         UNLOCK(&client->lock);
1815
1816         DESTROYLOCK(&ctx->lock);
1817         ctx->magic = 0;
1818
1819         isc_mem_put(mctx, ctx, sizeof(*ctx));
1820
1821         if (need_destroyclient)
1822                 destroyclient(&client);
1823
1824         *transp = NULL;
1825 }
1826
1827 /*%
1828  * Dynamic update routines
1829  */
1830 static isc_result_t
1831 rcode2result(dns_rcode_t rcode) {
1832         /* XXX: isn't there a similar function? */
1833         switch (rcode) {
1834         case dns_rcode_formerr:
1835                 return (DNS_R_FORMERR);
1836         case dns_rcode_servfail:
1837                 return (DNS_R_SERVFAIL);
1838         case dns_rcode_nxdomain:
1839                 return (DNS_R_NXDOMAIN);
1840         case dns_rcode_notimp:
1841                 return (DNS_R_NOTIMP);
1842         case dns_rcode_refused:
1843                 return (DNS_R_REFUSED);
1844         case dns_rcode_yxdomain:
1845                 return (DNS_R_YXDOMAIN);
1846         case dns_rcode_yxrrset:
1847                 return (DNS_R_YXRRSET);
1848         case dns_rcode_nxrrset:
1849                 return (DNS_R_NXRRSET);
1850         case dns_rcode_notauth:
1851                 return (DNS_R_NOTAUTH);
1852         case dns_rcode_notzone:
1853                 return (DNS_R_NOTZONE);
1854         case dns_rcode_badvers:
1855                 return (DNS_R_BADVERS);
1856         }
1857
1858         return (ISC_R_FAILURE);
1859 }
1860
1861 static void
1862 update_sendevent(updatectx_t *uctx, isc_result_t result) {
1863         isc_task_t *task;
1864
1865         dns_message_destroy(&uctx->updatemsg);
1866         if (uctx->tsigkey != NULL)
1867                 dns_tsigkey_detach(&uctx->tsigkey);
1868         if (uctx->sig0key != NULL)
1869                 dst_key_free(&uctx->sig0key);
1870
1871         if (uctx->canceled)
1872                 uctx->event->result = ISC_R_CANCELED;
1873         else
1874                 uctx->event->result = result;
1875         uctx->event->state = uctx->state;
1876         task = uctx->event->ev_sender;
1877         uctx->event->ev_sender = uctx;
1878         isc_task_sendanddetach(&task, ISC_EVENT_PTR(&uctx->event));
1879 }
1880
1881 static void
1882 update_done(isc_task_t *task, isc_event_t *event) {
1883         isc_result_t result;
1884         dns_requestevent_t *reqev = NULL;
1885         dns_request_t *request;
1886         dns_message_t *answer = NULL;
1887         updatectx_t *uctx = event->ev_arg;
1888         dns_client_t *client;
1889         unsigned int timeout;
1890
1891         UNUSED(task);
1892
1893         REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
1894         reqev = (dns_requestevent_t *)event;
1895         request = reqev->request;
1896         REQUIRE(UCTX_VALID(uctx));
1897         client = uctx->client;
1898         REQUIRE(DNS_CLIENT_VALID(client));
1899
1900         result = reqev->result;
1901         if (result != ISC_R_SUCCESS)
1902                 goto out;
1903
1904         result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
1905                                     &answer);
1906         if (result != ISC_R_SUCCESS)
1907                 goto out;
1908         uctx->state = dns_clientupdatestate_done;
1909         result = dns_request_getresponse(request, answer,
1910                                          DNS_MESSAGEPARSE_PRESERVEORDER);
1911         if (result == ISC_R_SUCCESS && answer->rcode != dns_rcode_noerror)
1912                 result = rcode2result(answer->rcode);
1913
1914  out:
1915         if (answer != NULL)
1916                 dns_message_destroy(&answer);
1917         isc_event_free(&event);
1918
1919         LOCK(&uctx->lock);
1920         uctx->currentserver = ISC_LIST_NEXT(uctx->currentserver, link);
1921         dns_request_destroy(&uctx->updatereq);
1922         if (result != ISC_R_SUCCESS && !uctx->canceled &&
1923             uctx->currentserver != NULL) {
1924                 dns_message_renderreset(uctx->updatemsg);
1925                 dns_message_settsigkey(uctx->updatemsg, NULL);
1926
1927                 timeout = client->update_timeout / uctx->nservers;
1928                 if (timeout < MIN_UPDATE_TIMEOUT)
1929                         timeout = MIN_UPDATE_TIMEOUT;
1930                 result = dns_request_createvia3(uctx->view->requestmgr,
1931                                                 uctx->updatemsg,
1932                                                 NULL,
1933                                                 uctx->currentserver, 0,
1934                                                 uctx->tsigkey,
1935                                                 timeout,
1936                                                 client->update_udptimeout,
1937                                                 client->update_udpretries,
1938                                                 client->task,
1939                                                 update_done, uctx,
1940                                                 &uctx->updatereq);
1941                 UNLOCK(&uctx->lock);
1942
1943                 if (result == ISC_R_SUCCESS) {
1944                         /* XXX: should we keep the 'done' state here? */
1945                         uctx->state = dns_clientupdatestate_sent;
1946                         return;
1947                 }
1948         } else
1949                 UNLOCK(&uctx->lock);
1950
1951         update_sendevent(uctx, result);
1952 }
1953
1954 static isc_result_t
1955 send_update(updatectx_t *uctx) {
1956         isc_result_t result;
1957         dns_name_t *name = NULL;
1958         dns_rdataset_t *rdataset = NULL;
1959         dns_client_t *client = uctx->client;
1960         unsigned int timeout;
1961
1962         REQUIRE(uctx->zonename != NULL && uctx->currentserver != NULL);
1963
1964         result = dns_message_gettempname(uctx->updatemsg, &name);
1965         if (result != ISC_R_SUCCESS)
1966                 return (result);
1967         dns_name_init(name, NULL);
1968         dns_name_clone(uctx->zonename, name);
1969         result = dns_message_gettemprdataset(uctx->updatemsg, &rdataset);
1970         if (result != ISC_R_SUCCESS) {
1971                 dns_message_puttempname(uctx->updatemsg, &name);
1972                 return (result);
1973         }
1974         dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa);
1975         ISC_LIST_INIT(name->list);
1976         ISC_LIST_APPEND(name->list, rdataset, link);
1977         dns_message_addname(uctx->updatemsg, name, DNS_SECTION_ZONE);
1978         if (uctx->tsigkey == NULL && uctx->sig0key != NULL) {
1979                 result = dns_message_setsig0key(uctx->updatemsg,
1980                                                 uctx->sig0key);
1981                 if (result != ISC_R_SUCCESS)
1982                         return (result);
1983         }
1984         timeout = client->update_timeout / uctx->nservers;
1985         if (timeout < MIN_UPDATE_TIMEOUT)
1986                 timeout = MIN_UPDATE_TIMEOUT;
1987         result = dns_request_createvia3(uctx->view->requestmgr,
1988                                         uctx->updatemsg,
1989                                         NULL, uctx->currentserver, 0,
1990                                         uctx->tsigkey, timeout,
1991                                         client->update_udptimeout,
1992                                         client->update_udpretries,
1993                                         client->task, update_done, uctx,
1994                                         &uctx->updatereq);
1995         if (result == ISC_R_SUCCESS &&
1996             uctx->state == dns_clientupdatestate_prepare) {
1997                 uctx->state = dns_clientupdatestate_sent;
1998         }
1999
2000         return (result);
2001 }
2002
2003 static void
2004 resolveaddr_done(isc_task_t *task, isc_event_t *event) {
2005         isc_result_t result;
2006         int family;
2007         dns_rdatatype_t qtype;
2008         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
2009         dns_name_t *name;
2010         dns_rdataset_t *rdataset;
2011         updatectx_t *uctx;
2012         isc_boolean_t completed = ISC_FALSE;
2013
2014         UNUSED(task);
2015
2016         REQUIRE(event->ev_arg != NULL);
2017         uctx = *(updatectx_t **)event->ev_arg;
2018         REQUIRE(UCTX_VALID(uctx));
2019
2020         if (event->ev_arg == &uctx->bp4) {
2021                 family = AF_INET;
2022                 qtype = dns_rdatatype_a;
2023                 LOCK(&uctx->lock);
2024                 dns_client_destroyrestrans(&uctx->restrans);
2025                 UNLOCK(&uctx->lock);
2026         } else {
2027                 INSIST(event->ev_arg == &uctx->bp6);
2028                 family = AF_INET6;
2029                 qtype = dns_rdatatype_aaaa;
2030                 LOCK(&uctx->lock);
2031                 dns_client_destroyrestrans(&uctx->restrans2);
2032                 UNLOCK(&uctx->lock);
2033         }
2034
2035         result = rev->result;
2036         if (result != ISC_R_SUCCESS)
2037                 goto done;
2038
2039         for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
2040              name = ISC_LIST_NEXT(name, link)) {
2041                 for (rdataset = ISC_LIST_HEAD(name->list);
2042                      rdataset != NULL;
2043                      rdataset = ISC_LIST_NEXT(rdataset, link)) {
2044                         if (!dns_rdataset_isassociated(rdataset))
2045                                 continue;
2046                         if (rdataset->type != qtype)
2047                                 continue;
2048
2049                         for (result = dns_rdataset_first(rdataset);
2050                              result == ISC_R_SUCCESS;
2051                              result = dns_rdataset_next(rdataset)) {
2052                                 dns_rdata_t rdata;
2053                                 dns_rdata_in_a_t rdata_a;
2054                                 dns_rdata_in_aaaa_t rdata_aaaa;
2055                                 isc_sockaddr_t *sa;
2056
2057                                 sa = isc_mem_get(uctx->client->mctx,
2058                                                  sizeof(*sa));
2059                                 if (sa == NULL) {
2060                                         /*
2061                                          * If we fail to get a sockaddr,
2062                                          we simply move forward with the
2063                                          * addresses we've got so far.
2064                                          */
2065                                         goto done;
2066                                 }
2067
2068                                 dns_rdata_init(&rdata);
2069                                 switch (family) {
2070                                 case AF_INET:
2071                                         dns_rdataset_current(rdataset, &rdata);
2072                                         result = dns_rdata_tostruct(&rdata, &rdata_a,
2073                                                                     NULL);
2074                                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
2075                                         isc_sockaddr_fromin(sa,
2076                                                             &rdata_a.in_addr,
2077                                                             53);
2078                                         dns_rdata_freestruct(&rdata_a);
2079                                         break;
2080                                 case AF_INET6:
2081                                         dns_rdataset_current(rdataset, &rdata);
2082                                         result = dns_rdata_tostruct(&rdata, &rdata_aaaa,
2083                                                                     NULL);
2084                                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
2085                                         isc_sockaddr_fromin6(sa,
2086                                                              &rdata_aaaa.in6_addr,
2087                                                              53);
2088                                         dns_rdata_freestruct(&rdata_aaaa);
2089                                         break;
2090                                 }
2091
2092                                 ISC_LINK_INIT(sa, link);
2093                                 ISC_LIST_APPEND(uctx->servers, sa, link);
2094                                 uctx->nservers++;
2095                         }
2096                 }
2097         }
2098
2099  done:
2100         dns_client_freeresanswer(uctx->client, &rev->answerlist);
2101         isc_event_free(&event);
2102
2103         LOCK(&uctx->lock);
2104         if (uctx->restrans == NULL && uctx->restrans2 == NULL)
2105                 completed = ISC_TRUE;
2106         UNLOCK(&uctx->lock);
2107
2108         if (completed) {
2109                 INSIST(uctx->currentserver == NULL);
2110                 uctx->currentserver = ISC_LIST_HEAD(uctx->servers);
2111                 if (uctx->currentserver != NULL && !uctx->canceled)
2112                         send_update(uctx);
2113                 else {
2114                         if (result == ISC_R_SUCCESS)
2115                                 result = ISC_R_NOTFOUND;
2116                         update_sendevent(uctx, result);
2117                 }
2118         }
2119 }
2120
2121 static isc_result_t
2122 process_soa(updatectx_t *uctx, dns_rdataset_t *soaset, dns_name_t *soaname) {
2123         isc_result_t result;
2124         dns_rdata_t soarr = DNS_RDATA_INIT;
2125         dns_rdata_soa_t soa;
2126         dns_name_t primary;
2127
2128         result = dns_rdataset_first(soaset);
2129         if (result != ISC_R_SUCCESS)
2130                 return (result);
2131         dns_rdata_init(&soarr);
2132         dns_rdataset_current(soaset, &soarr);
2133         result = dns_rdata_tostruct(&soarr, &soa, NULL);
2134         if (result != ISC_R_SUCCESS)
2135                 return (result);
2136
2137         dns_name_init(&primary, NULL);
2138         dns_name_clone(&soa.origin, &primary);
2139
2140         if (uctx->zonename == NULL) {
2141                 uctx->zonename = dns_fixedname_name(&uctx->zonefname);
2142                 result = dns_name_copy(soaname, uctx->zonename, NULL);
2143                 if (result != ISC_R_SUCCESS)
2144                         goto out;
2145         }
2146
2147         if (uctx->currentserver != NULL)
2148                 result = send_update(uctx);
2149         else {
2150                 /*
2151                  * Get addresses of the primary server.  We don't use the ADB
2152                  * feature so that we could avoid caching data.
2153                  */
2154                 LOCK(&uctx->lock);
2155                 uctx->bp4 = uctx;
2156                 result = dns_client_startresolve(uctx->client, &primary,
2157                                                  uctx->rdclass,
2158                                                  dns_rdatatype_a,
2159                                                  0, uctx->client->task,
2160                                                  resolveaddr_done, &uctx->bp4,
2161                                                  &uctx->restrans);
2162                 if (result == ISC_R_SUCCESS) {
2163                         uctx->bp6 = uctx;
2164                         result = dns_client_startresolve(uctx->client,
2165                                                          &primary,
2166                                                          uctx->rdclass,
2167                                                          dns_rdatatype_aaaa,
2168                                                          0, uctx->client->task,
2169                                                          resolveaddr_done,
2170                                                          &uctx->bp6,
2171                                                          &uctx->restrans2);
2172                 }
2173                 UNLOCK(&uctx->lock);
2174         }
2175
2176  out:
2177         dns_rdata_freestruct(&soa);
2178
2179         return (result);
2180 }
2181
2182 static void
2183 receive_soa(isc_task_t *task, isc_event_t *event) {
2184         dns_requestevent_t *reqev = NULL;
2185         updatectx_t *uctx;
2186         dns_client_t *client;
2187         isc_result_t result, eresult;
2188         dns_request_t *request;
2189         dns_message_t *rcvmsg = NULL;
2190         dns_section_t section;
2191         dns_rdataset_t *soaset = NULL;
2192         int pass = 0;
2193         dns_name_t *name;
2194         dns_message_t *soaquery = NULL;
2195         isc_sockaddr_t *addr;
2196         isc_boolean_t seencname = ISC_FALSE;
2197         isc_boolean_t droplabel = ISC_FALSE;
2198         dns_name_t tname;
2199         unsigned int nlabels;
2200
2201         UNUSED(task);
2202
2203         REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2204         reqev = (dns_requestevent_t *)event;
2205         request = reqev->request;
2206         result = eresult = reqev->result;
2207         POST(result);
2208         uctx = reqev->ev_arg;
2209         client = uctx->client;
2210         soaquery = uctx->soaquery;
2211         addr = uctx->currentserver;
2212         INSIST(addr != NULL);
2213
2214         isc_event_free(&event);
2215
2216         if (eresult != ISC_R_SUCCESS) {
2217                 result = eresult;
2218                 goto out;
2219         }
2220
2221         result = dns_message_create(uctx->client->mctx,
2222                                     DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2223         if (result != ISC_R_SUCCESS)
2224                 goto out;
2225         result = dns_request_getresponse(request, rcvmsg,
2226                                          DNS_MESSAGEPARSE_PRESERVEORDER);
2227
2228         if (result == DNS_R_TSIGERRORSET) {
2229                 dns_request_t *newrequest = NULL;
2230
2231                 /* Retry SOA request without TSIG */
2232                 dns_message_destroy(&rcvmsg);
2233                 dns_message_renderreset(uctx->soaquery);
2234                 result = dns_request_createvia3(uctx->view->requestmgr,
2235                                                 uctx->soaquery, NULL, addr, 0,
2236                                                 NULL,
2237                                                 client->find_timeout * 20,
2238                                                 client->find_timeout, 3,
2239                                                 uctx->client->task,
2240                                                 receive_soa, uctx,
2241                                                 &newrequest);
2242                 if (result == ISC_R_SUCCESS) {
2243                         LOCK(&uctx->lock);
2244                         dns_request_destroy(&uctx->soareq);
2245                         uctx->soareq = newrequest;
2246                         UNLOCK(&uctx->lock);
2247
2248                         return;
2249                 }
2250                 goto out;
2251         }
2252
2253         section = DNS_SECTION_ANSWER;
2254         POST(section);
2255
2256         if (rcvmsg->rcode != dns_rcode_noerror &&
2257             rcvmsg->rcode != dns_rcode_nxdomain) {
2258                 result = rcode2result(rcvmsg->rcode);
2259                 goto out;
2260         }
2261
2262  lookforsoa:
2263         if (pass == 0)
2264                 section = DNS_SECTION_ANSWER;
2265         else if (pass == 1)
2266                 section = DNS_SECTION_AUTHORITY;
2267         else {
2268                 droplabel = ISC_TRUE;
2269                 goto out;
2270         }
2271
2272         result = dns_message_firstname(rcvmsg, section);
2273         if (result != ISC_R_SUCCESS) {
2274                 pass++;
2275                 goto lookforsoa;
2276         }
2277         while (result == ISC_R_SUCCESS) {
2278                 name = NULL;
2279                 dns_message_currentname(rcvmsg, section, &name);
2280                 soaset = NULL;
2281                 result = dns_message_findtype(name, dns_rdatatype_soa, 0,
2282                                               &soaset);
2283                 if (result == ISC_R_SUCCESS)
2284                         break;
2285                 if (section == DNS_SECTION_ANSWER) {
2286                         dns_rdataset_t *tset = NULL;
2287                         if (dns_message_findtype(name, dns_rdatatype_cname, 0,
2288                                                  &tset) == ISC_R_SUCCESS
2289                             ||
2290                             dns_message_findtype(name, dns_rdatatype_dname, 0,
2291                                                  &tset) == ISC_R_SUCCESS
2292                             )
2293                         {
2294                                 seencname = ISC_TRUE;
2295                                 break;
2296                         }
2297                 }
2298
2299                 result = dns_message_nextname(rcvmsg, section);
2300         }
2301
2302         if (soaset == NULL && !seencname) {
2303                 pass++;
2304                 goto lookforsoa;
2305         }
2306
2307         if (seencname) {
2308                 droplabel = ISC_TRUE;
2309                 goto out;
2310         }
2311
2312         result = process_soa(uctx, soaset, name);
2313
2314  out:
2315         if (droplabel) {
2316                 result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION);
2317                 INSIST(result == ISC_R_SUCCESS);
2318                 name = NULL;
2319                 dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name);
2320                 nlabels = dns_name_countlabels(name);
2321                 if (nlabels == 1)
2322                         result = DNS_R_SERVFAIL; /* is there a better error? */
2323                 else {
2324                         dns_name_init(&tname, NULL);
2325                         dns_name_getlabelsequence(name, 1, nlabels - 1,
2326                                                   &tname);
2327                         dns_name_clone(&tname, name);
2328                         dns_request_destroy(&request);
2329                         LOCK(&uctx->lock);
2330                         uctx->soareq = NULL;
2331                         UNLOCK(&uctx->lock);
2332                         dns_message_renderreset(soaquery);
2333                         dns_message_settsigkey(soaquery, NULL);
2334                         result = dns_request_createvia3(uctx->view->requestmgr,
2335                                                         soaquery, NULL,
2336                                                         uctx->currentserver, 0,
2337                                                         uctx->tsigkey,
2338                                                         client->find_timeout *
2339                                                         20,
2340                                                         client->find_timeout,
2341                                                         3, client->task,
2342                                                         receive_soa, uctx,
2343                                                         &uctx->soareq);
2344                 }
2345         }
2346
2347         if (!droplabel || result != ISC_R_SUCCESS) {
2348                 dns_message_destroy(&uctx->soaquery);
2349                 LOCK(&uctx->lock);
2350                 dns_request_destroy(&uctx->soareq);
2351                 UNLOCK(&uctx->lock);
2352         }
2353
2354         if (rcvmsg != NULL)
2355                 dns_message_destroy(&rcvmsg);
2356
2357         if (result != ISC_R_SUCCESS)
2358                 update_sendevent(uctx, result);
2359 }
2360
2361 static isc_result_t
2362 request_soa(updatectx_t *uctx) {
2363         isc_result_t result;
2364         dns_message_t *soaquery = uctx->soaquery;
2365         dns_name_t *name = NULL;
2366         dns_rdataset_t *rdataset = NULL;
2367
2368         if (soaquery == NULL) {
2369                 result = dns_message_create(uctx->client->mctx,
2370                                             DNS_MESSAGE_INTENTRENDER,
2371                                             &soaquery);
2372                 if (result != ISC_R_SUCCESS)
2373                         return (result);
2374         }
2375         soaquery->flags |= DNS_MESSAGEFLAG_RD;
2376         result = dns_message_gettempname(soaquery, &name);
2377         if (result != ISC_R_SUCCESS)
2378                 goto fail;
2379         result = dns_message_gettemprdataset(soaquery, &rdataset);
2380         if (result != ISC_R_SUCCESS)
2381                 goto fail;
2382         dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa);
2383         dns_name_clone(uctx->firstname, name);
2384         ISC_LIST_APPEND(name->list, rdataset, link);
2385         dns_message_addname(soaquery, name, DNS_SECTION_QUESTION);
2386         rdataset = NULL;
2387         name = NULL;
2388
2389         result = dns_request_createvia3(uctx->view->requestmgr,
2390                                         soaquery, NULL, uctx->currentserver, 0,
2391                                         uctx->tsigkey,
2392                                         uctx->client->find_timeout * 20,
2393                                         uctx->client->find_timeout, 3,
2394                                         uctx->client->task, receive_soa, uctx,
2395                                         &uctx->soareq);
2396         if (result == ISC_R_SUCCESS) {
2397                 uctx->soaquery = soaquery;
2398                 return (ISC_R_SUCCESS);
2399         }
2400
2401  fail:
2402         if (rdataset != NULL) {
2403                 ISC_LIST_UNLINK(name->list, rdataset, link); /* for safety */
2404                 dns_message_puttemprdataset(soaquery, &rdataset);
2405         }
2406         if (name != NULL)
2407                 dns_message_puttempname(soaquery, &name);
2408         dns_message_destroy(&soaquery);
2409
2410         return (result);
2411 }
2412
2413 static void
2414 resolvesoa_done(isc_task_t *task, isc_event_t *event) {
2415         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
2416         updatectx_t *uctx;
2417         dns_name_t *name, tname;
2418         dns_rdataset_t *rdataset = NULL;
2419         isc_result_t result = rev->result;
2420         unsigned int nlabels;
2421
2422         UNUSED(task);
2423
2424         uctx = event->ev_arg;
2425         REQUIRE(UCTX_VALID(uctx));
2426
2427         LOCK(&uctx->lock);
2428         dns_client_destroyrestrans(&uctx->restrans);
2429         UNLOCK(&uctx->lock);
2430
2431         uctx = event->ev_arg;
2432         if (result != ISC_R_SUCCESS &&
2433             result != DNS_R_NCACHENXDOMAIN &&
2434             result != DNS_R_NCACHENXRRSET) {
2435                 /* XXX: what about DNSSEC failure? */
2436                 goto out;
2437         }
2438
2439         for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
2440              name = ISC_LIST_NEXT(name, link)) {
2441                 for (rdataset = ISC_LIST_HEAD(name->list);
2442                      rdataset != NULL;
2443                      rdataset = ISC_LIST_NEXT(rdataset, link)) {
2444                         if (dns_rdataset_isassociated(rdataset) &&
2445                             rdataset->type == dns_rdatatype_soa)
2446                                 break;
2447                 }
2448         }
2449
2450         if (rdataset == NULL) {
2451                 /* Drop one label and retry resolution. */
2452                 nlabels = dns_name_countlabels(&uctx->soaqname);
2453                 if (nlabels == 1) {
2454                         result = DNS_R_SERVFAIL; /* is there a better error? */
2455                         goto out;
2456                 }
2457                 dns_name_init(&tname, NULL);
2458                 dns_name_getlabelsequence(&uctx->soaqname, 1, nlabels - 1,
2459                                           &tname);
2460                 dns_name_clone(&tname, &uctx->soaqname);
2461
2462                 result = dns_client_startresolve(uctx->client, &uctx->soaqname,
2463                                                  uctx->rdclass,
2464                                                  dns_rdatatype_soa, 0,
2465                                                  uctx->client->task,
2466                                                  resolvesoa_done, uctx,
2467                                                  &uctx->restrans);
2468         } else
2469                 result = process_soa(uctx, rdataset, &uctx->soaqname);
2470
2471  out:
2472         dns_client_freeresanswer(uctx->client, &rev->answerlist);
2473         isc_event_free(&event);
2474
2475         if (result != ISC_R_SUCCESS)
2476                 update_sendevent(uctx, result);
2477 }
2478
2479 static isc_result_t
2480 copy_name(isc_mem_t *mctx, dns_message_t *msg, dns_name_t *name,
2481           dns_name_t **newnamep)
2482 {
2483         isc_result_t result;
2484         dns_name_t *newname = NULL;
2485         isc_region_t r;
2486         isc_buffer_t *namebuf = NULL, *rdatabuf = NULL;
2487         dns_rdatalist_t *rdatalist;
2488         dns_rdataset_t *rdataset, *newrdataset;
2489         dns_rdata_t rdata = DNS_RDATA_INIT, *newrdata;
2490
2491         result = dns_message_gettempname(msg, &newname);
2492         if (result != ISC_R_SUCCESS)
2493                 return (result);
2494         result = isc_buffer_allocate(mctx, &namebuf, DNS_NAME_MAXWIRE);
2495         if (result != ISC_R_SUCCESS)
2496                 goto fail;
2497         dns_name_init(newname, NULL);
2498         dns_name_setbuffer(newname, namebuf);
2499         dns_message_takebuffer(msg, &namebuf);
2500         result = dns_name_copy(name, newname, NULL);
2501         if (result != ISC_R_SUCCESS)
2502                 goto fail;
2503
2504         for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
2505              rdataset = ISC_LIST_NEXT(rdataset, link)) {
2506                 rdatalist = NULL;
2507                 result = dns_message_gettemprdatalist(msg, &rdatalist);
2508                 if (result != ISC_R_SUCCESS)
2509                         goto fail;
2510                 dns_rdatalist_init(rdatalist);
2511                 rdatalist->type = rdataset->type;
2512                 rdatalist->rdclass = rdataset->rdclass;
2513                 rdatalist->covers = rdataset->covers;
2514                 rdatalist->ttl = rdataset->ttl;
2515
2516                 result = dns_rdataset_first(rdataset);
2517                 while (result == ISC_R_SUCCESS) {
2518                         dns_rdata_reset(&rdata);
2519                         dns_rdataset_current(rdataset, &rdata);
2520
2521                         newrdata = NULL;
2522                         result = dns_message_gettemprdata(msg, &newrdata);
2523                         if (result != ISC_R_SUCCESS)
2524                                 goto fail;
2525                         dns_rdata_toregion(&rdata, &r);
2526                         rdatabuf = NULL;
2527                         result = isc_buffer_allocate(mctx, &rdatabuf,
2528                                                      r.length);
2529                         if (result != ISC_R_SUCCESS)
2530                                 goto fail;
2531                         isc_buffer_putmem(rdatabuf, r.base, r.length);
2532                         isc_buffer_usedregion(rdatabuf, &r);
2533                         dns_rdata_init(newrdata);
2534                         dns_rdata_fromregion(newrdata, rdata.rdclass,
2535                                              rdata.type, &r);
2536                         newrdata->flags = rdata.flags;
2537
2538                         ISC_LIST_APPEND(rdatalist->rdata, newrdata, link);
2539                         dns_message_takebuffer(msg, &rdatabuf);
2540
2541                         result = dns_rdataset_next(rdataset);
2542                 }
2543
2544                 newrdataset = NULL;
2545                 result = dns_message_gettemprdataset(msg, &newrdataset);
2546                 if (result != ISC_R_SUCCESS)
2547                         goto fail;
2548                 dns_rdataset_init(newrdataset);
2549                 dns_rdatalist_tordataset(rdatalist, newrdataset);
2550
2551                 ISC_LIST_APPEND(newname->list, newrdataset, link);
2552         }
2553
2554         *newnamep = newname;
2555
2556         return (ISC_R_SUCCESS);
2557
2558  fail:
2559         dns_message_puttempname(msg, &newname);
2560
2561         return (result);
2562
2563 }
2564
2565 static void
2566 internal_update_callback(isc_task_t *task, isc_event_t *event) {
2567         updatearg_t *uarg = event->ev_arg;
2568         dns_clientupdateevent_t *uev = (dns_clientupdateevent_t *)event;
2569
2570         UNUSED(task);
2571
2572         LOCK(&uarg->lock);
2573
2574         uarg->result = uev->result;
2575
2576         dns_client_destroyupdatetrans(&uarg->trans);
2577         isc_event_free(&event);
2578
2579         if (!uarg->canceled) {
2580                 UNLOCK(&uarg->lock);
2581
2582                 /* Exit from the internal event loop */
2583                 isc_app_ctxsuspend(uarg->actx);
2584         } else {
2585                 /*
2586                  * We have already exited from the loop (due to some
2587                  * unexpected event).  Just clean the arg up.
2588                  */
2589                 UNLOCK(&uarg->lock);
2590                 DESTROYLOCK(&uarg->lock);
2591                 isc_mem_put(uarg->client->mctx, uarg, sizeof(*uarg));
2592         }
2593 }
2594
2595 isc_result_t
2596 dns_client_update(dns_client_t *client, dns_rdataclass_t rdclass,
2597                   dns_name_t *zonename, dns_namelist_t *prerequisites,
2598                   dns_namelist_t *updates, isc_sockaddrlist_t *servers,
2599                   dns_tsec_t *tsec, unsigned int options)
2600 {
2601         isc_result_t result;
2602         isc_appctx_t *actx;
2603         updatearg_t *uarg;
2604
2605         REQUIRE(DNS_CLIENT_VALID(client));
2606
2607         if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
2608             (options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) {
2609                 /*
2610                  * If the client is run under application's control, we need
2611                  * to create a new running (sub)environment for this
2612                  * particular resolution.
2613                  */
2614                 return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
2615         } else
2616                 actx = client->actx;
2617
2618         uarg = isc_mem_get(client->mctx, sizeof(*uarg));
2619         if (uarg == NULL)
2620                 return (ISC_R_NOMEMORY);
2621
2622         result = isc_mutex_init(&uarg->lock);
2623         if (result != ISC_R_SUCCESS) {
2624                 isc_mem_put(client->mctx, uarg, sizeof(*uarg));
2625                 return (result);
2626         }
2627
2628         uarg->actx = actx;
2629         uarg->client = client;
2630         uarg->result = ISC_R_FAILURE;
2631         uarg->trans = NULL;
2632         uarg->canceled = ISC_FALSE;
2633
2634         result = dns_client_startupdate(client, rdclass, zonename,
2635                                         prerequisites, updates, servers,
2636                                         tsec, options, client->task,
2637                                         internal_update_callback, uarg,
2638                                         &uarg->trans);
2639         if (result != ISC_R_SUCCESS) {
2640                 DESTROYLOCK(&uarg->lock);
2641                 isc_mem_put(client->mctx, uarg, sizeof(*uarg));
2642                 return (result);
2643         }
2644
2645         /*
2646          * Start internal event loop.  It blocks until the entire process
2647          * is completed.
2648          */
2649         result = isc_app_ctxrun(actx);
2650
2651         LOCK(&uarg->lock);
2652         if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
2653                 result = uarg->result;
2654
2655         if (uarg->trans != NULL) {
2656                 /*
2657                  * Unusual termination (perhaps due to signal).  We need some
2658                  * tricky cleanup process.
2659                  */
2660                 uarg->canceled = ISC_TRUE;
2661                 dns_client_cancelupdate(uarg->trans);
2662
2663                 UNLOCK(&uarg->lock);
2664
2665                 /* uarg will be freed in the event handler. */
2666         } else {
2667                 UNLOCK(&uarg->lock);
2668
2669                 DESTROYLOCK(&uarg->lock);
2670                 isc_mem_put(client->mctx, uarg, sizeof(*uarg));
2671         }
2672
2673         return (result);
2674 }
2675
2676 isc_result_t
2677 dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass,
2678                        dns_name_t *zonename, dns_namelist_t *prerequisites,
2679                        dns_namelist_t *updates, isc_sockaddrlist_t *servers,
2680                        dns_tsec_t *tsec, unsigned int options,
2681                        isc_task_t *task, isc_taskaction_t action, void *arg,
2682                        dns_clientupdatetrans_t **transp)
2683 {
2684         dns_view_t *view = NULL;
2685         isc_result_t result;
2686         dns_name_t *name, *newname;
2687         updatectx_t *uctx;
2688         isc_task_t *clone = NULL;
2689         dns_section_t section = DNS_SECTION_UPDATE;
2690         isc_sockaddr_t *server, *sa = NULL;
2691         dns_tsectype_t tsectype = dns_tsectype_none;
2692
2693         UNUSED(options);
2694
2695         REQUIRE(DNS_CLIENT_VALID(client));
2696         REQUIRE(transp != NULL && *transp == NULL);
2697         REQUIRE(updates != NULL);
2698         REQUIRE(task != NULL);
2699
2700         if (tsec != NULL) {
2701                 tsectype = dns_tsec_gettype(tsec);
2702                 if (tsectype != dns_tsectype_tsig)
2703                         return (ISC_R_NOTIMPLEMENTED); /* XXX */
2704         }
2705
2706         LOCK(&client->lock);
2707         result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
2708                                    rdclass, &view);
2709         UNLOCK(&client->lock);
2710         if (result != ISC_R_SUCCESS)
2711                 return (result);
2712
2713         /* Create a context and prepare some resources */
2714         uctx = isc_mem_get(client->mctx, sizeof(*uctx));
2715         if (uctx == NULL) {
2716                 dns_view_detach(&view);
2717                 return (ISC_R_NOMEMORY);
2718         }
2719         result = isc_mutex_init(&uctx->lock);
2720         if (result != ISC_R_SUCCESS) {
2721                 dns_view_detach(&view);
2722                 isc_mem_put(client->mctx, uctx, sizeof(*uctx));
2723                 return (ISC_R_NOMEMORY);
2724         }
2725         clone = NULL;
2726         isc_task_attach(task, &clone);
2727         uctx->client = client;
2728         ISC_LINK_INIT(uctx, link);
2729         uctx->state = dns_clientupdatestate_prepare;
2730         uctx->view = view;
2731         uctx->rdclass = rdclass;
2732         uctx->canceled = ISC_FALSE;
2733         uctx->updatemsg = NULL;
2734         uctx->soaquery = NULL;
2735         uctx->updatereq = NULL;
2736         uctx->restrans = NULL;
2737         uctx->restrans2 = NULL;
2738         uctx->bp4 = NULL;
2739         uctx->bp6 = NULL;
2740         uctx->soareq = NULL;
2741         uctx->event = NULL;
2742         uctx->tsigkey = NULL;
2743         uctx->sig0key = NULL;
2744         uctx->zonename = NULL;
2745         dns_name_init(&uctx->soaqname, NULL);
2746         ISC_LIST_INIT(uctx->servers);
2747         uctx->nservers = 0;
2748         uctx->currentserver = NULL;
2749         dns_fixedname_init(&uctx->zonefname);
2750         if (tsec != NULL)
2751                 dns_tsec_getkey(tsec, &uctx->tsigkey);
2752         uctx->event = (dns_clientupdateevent_t *)
2753                 isc_event_allocate(client->mctx, clone, DNS_EVENT_UPDATEDONE,
2754                                    action, arg, sizeof(*uctx->event));
2755         if (uctx->event == NULL)
2756                 goto fail;
2757         if (zonename != NULL) {
2758                 uctx->zonename = dns_fixedname_name(&uctx->zonefname);
2759                 result = dns_name_copy(zonename, uctx->zonename, NULL);
2760         }
2761         if (servers != NULL) {
2762                 for (server = ISC_LIST_HEAD(*servers);
2763                      server != NULL;
2764                      server = ISC_LIST_NEXT(server, link)) {
2765                         sa = isc_mem_get(client->mctx, sizeof(*sa));
2766                         if (sa == NULL)
2767                                 goto fail;
2768                         sa->type = server->type;
2769                         sa->length = server->length;
2770                         ISC_LINK_INIT(sa, link);
2771                         ISC_LIST_APPEND(uctx->servers, sa, link);
2772                         if (uctx->currentserver == NULL)
2773                                 uctx->currentserver = sa;
2774                         uctx->nservers++;
2775                 }
2776         }
2777
2778         /* Make update message */
2779         result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTRENDER,
2780                                     &uctx->updatemsg);
2781         if (result != ISC_R_SUCCESS)
2782                 goto fail;
2783         uctx->updatemsg->opcode = dns_opcode_update;
2784
2785         if (prerequisites != NULL) {
2786                 for (name = ISC_LIST_HEAD(*prerequisites); name != NULL;
2787                      name = ISC_LIST_NEXT(name, link)) {
2788                         newname = NULL;
2789                         result = copy_name(client->mctx, uctx->updatemsg,
2790                                            name, &newname);
2791                         if (result != ISC_R_SUCCESS)
2792                                 goto fail;
2793                         dns_message_addname(uctx->updatemsg, newname,
2794                                             DNS_SECTION_PREREQUISITE);
2795                 }
2796         }
2797
2798         for (name = ISC_LIST_HEAD(*updates); name != NULL;
2799              name = ISC_LIST_NEXT(name, link)) {
2800                 newname = NULL;
2801                 result = copy_name(client->mctx, uctx->updatemsg, name,
2802                                    &newname);
2803                 if (result != ISC_R_SUCCESS)
2804                         goto fail;
2805                 dns_message_addname(uctx->updatemsg, newname,
2806                                     DNS_SECTION_UPDATE);
2807         }
2808
2809         uctx->firstname = NULL;
2810         result = dns_message_firstname(uctx->updatemsg, section);
2811         if (result == ISC_R_NOMORE) {
2812                 section = DNS_SECTION_PREREQUISITE;
2813                 result = dns_message_firstname(uctx->updatemsg, section);
2814         }
2815         if (result != ISC_R_SUCCESS)
2816                 goto fail;
2817         dns_message_currentname(uctx->updatemsg, section, &uctx->firstname);
2818
2819         uctx->magic = UCTX_MAGIC;
2820
2821         LOCK(&client->lock);
2822         ISC_LIST_APPEND(client->updatectxs, uctx, link);
2823         UNLOCK(&client->lock);
2824
2825         if (uctx->zonename != NULL && uctx->currentserver != NULL) {
2826                 result = send_update(uctx);
2827                 if (result != ISC_R_SUCCESS)
2828                         goto fail;
2829         } else if (uctx->currentserver != NULL) {
2830                 result = request_soa(uctx);
2831                 if (result != ISC_R_SUCCESS)
2832                         goto fail;
2833         } else {
2834                 dns_name_clone(uctx->firstname, &uctx->soaqname);
2835                 result = dns_client_startresolve(uctx->client, &uctx->soaqname,
2836                                                  uctx->rdclass,
2837                                                  dns_rdatatype_soa, 0,
2838                                                  client->task, resolvesoa_done,
2839                                                  uctx, &uctx->restrans);
2840                 if (result != ISC_R_SUCCESS)
2841                         goto fail;
2842         }
2843
2844         *transp = (dns_clientupdatetrans_t *)uctx;
2845
2846         return (ISC_R_SUCCESS);
2847
2848  fail:
2849         if (ISC_LINK_LINKED(uctx, link)) {
2850                 LOCK(&client->lock);
2851                 ISC_LIST_UNLINK(client->updatectxs, uctx, link);
2852                 UNLOCK(&client->lock);
2853         }
2854         if (uctx->updatemsg != NULL)
2855                 dns_message_destroy(&uctx->updatemsg);
2856         while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) {
2857                 ISC_LIST_UNLINK(uctx->servers, sa, link);
2858                 isc_mem_put(client->mctx, sa, sizeof(*sa));
2859         }
2860         if (uctx->event != NULL)
2861                 isc_event_free(ISC_EVENT_PTR(&uctx->event));
2862         if (uctx->tsigkey != NULL)
2863                 dns_tsigkey_detach(&uctx->tsigkey);
2864         isc_task_detach(&clone);
2865         DESTROYLOCK(&uctx->lock);
2866         uctx->magic = 0;
2867         isc_mem_put(client->mctx, uctx, sizeof(*uctx));
2868         dns_view_detach(&view);
2869
2870         return (result);
2871 }
2872
2873 void
2874 dns_client_cancelupdate(dns_clientupdatetrans_t *trans) {
2875         updatectx_t *uctx;
2876
2877         REQUIRE(trans != NULL);
2878         uctx = (updatectx_t *)trans;
2879         REQUIRE(UCTX_VALID(uctx));
2880
2881         LOCK(&uctx->lock);
2882
2883         if (!uctx->canceled) {
2884                 uctx->canceled = ISC_TRUE;
2885                 if (uctx->updatereq != NULL)
2886                         dns_request_cancel(uctx->updatereq);
2887                 if (uctx->soareq != NULL)
2888                         dns_request_cancel(uctx->soareq);
2889                 if (uctx->restrans != NULL)
2890                         dns_client_cancelresolve(uctx->restrans);
2891                 if (uctx->restrans2 != NULL)
2892                         dns_client_cancelresolve(uctx->restrans2);
2893         }
2894
2895         UNLOCK(&uctx->lock);
2896 }
2897
2898 void
2899 dns_client_destroyupdatetrans(dns_clientupdatetrans_t **transp) {
2900         updatectx_t *uctx;
2901         isc_mem_t *mctx;
2902         dns_client_t *client;
2903         isc_boolean_t need_destroyclient = ISC_FALSE;
2904         isc_sockaddr_t *sa;
2905
2906         REQUIRE(transp != NULL);
2907         uctx = (updatectx_t *)*transp;
2908         REQUIRE(UCTX_VALID(uctx));
2909         client = uctx->client;
2910         REQUIRE(DNS_CLIENT_VALID(client));
2911         REQUIRE(uctx->updatereq == NULL && uctx->updatemsg == NULL &&
2912                 uctx->soareq == NULL && uctx->soaquery == NULL &&
2913                 uctx->event == NULL && uctx->tsigkey == NULL &&
2914                 uctx->sig0key == NULL);
2915
2916         mctx = client->mctx;
2917         dns_view_detach(&uctx->view);
2918         while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) {
2919                 ISC_LIST_UNLINK(uctx->servers, sa, link);
2920                 isc_mem_put(mctx, sa, sizeof(*sa));
2921         }
2922
2923         LOCK(&client->lock);
2924
2925         INSIST(ISC_LINK_LINKED(uctx, link));
2926         ISC_LIST_UNLINK(client->updatectxs, uctx, link);
2927
2928         if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
2929             ISC_LIST_EMPTY(client->reqctxs) &&
2930             ISC_LIST_EMPTY(client->updatectxs))
2931                 need_destroyclient = ISC_TRUE;
2932
2933         UNLOCK(&client->lock);
2934
2935         DESTROYLOCK(&uctx->lock);
2936         uctx->magic = 0;
2937
2938         isc_mem_put(mctx, uctx, sizeof(*uctx));
2939
2940         if (need_destroyclient)
2941                 destroyclient(&client);
2942
2943         *transp = NULL;
2944 }
2945
2946 isc_mem_t *
2947 dns_client_mctx(dns_client_t *client) {
2948
2949         REQUIRE(DNS_CLIENT_VALID(client));
2950         return (client->mctx);
2951 }
2952
2953 typedef struct {
2954         isc_buffer_t    buffer;
2955         dns_rdataset_t  rdataset;
2956         dns_rdatalist_t rdatalist;
2957         dns_rdata_t     rdata;
2958         size_t          size;
2959         isc_mem_t *     mctx;
2960         unsigned char   data[FLEXIBLE_ARRAY_MEMBER];
2961 } dns_client_updaterec_t;
2962
2963 isc_result_t
2964 dns_client_updaterec(dns_client_updateop_t op, dns_name_t *owner,
2965                      dns_rdatatype_t type, dns_rdata_t *source,
2966                      dns_ttl_t ttl, dns_name_t *target,
2967                      dns_rdataset_t *rdataset, dns_rdatalist_t *rdatalist,
2968                      dns_rdata_t *rdata, isc_mem_t *mctx)
2969 {
2970         dns_client_updaterec_t *updaterec = NULL;
2971         size_t size = offsetof(dns_client_updaterec_t, data);
2972
2973         REQUIRE(op < updateop_max);
2974         REQUIRE(owner != NULL);
2975         REQUIRE((rdataset != NULL && rdatalist != NULL && rdata != NULL) ||
2976                 (rdataset == NULL && rdatalist == NULL && rdata == NULL &&
2977                  mctx != NULL));
2978         if (op == updateop_add)
2979                 REQUIRE(source != NULL);
2980         if (source != NULL) {
2981                 REQUIRE(source->type == type);
2982                 REQUIRE(op == updateop_add || op == updateop_delete ||
2983                         op == updateop_exist);
2984         }
2985
2986         size += owner->length;
2987         if (source != NULL)
2988                 size += source->length;
2989
2990         if (rdataset == NULL) {
2991                 updaterec = isc_mem_get(mctx, size);
2992                 if (updaterec == NULL)
2993                         return (ISC_R_NOMEMORY);
2994                 rdataset = &updaterec->rdataset;
2995                 rdatalist = &updaterec->rdatalist;
2996                 rdata = &updaterec->rdata;
2997                 dns_rdataset_init(rdataset);
2998                 dns_rdatalist_init(&updaterec->rdatalist);
2999                 dns_rdata_init(&updaterec->rdata);
3000                 isc_buffer_init(&updaterec->buffer, updaterec->data,
3001                                 size - offsetof(dns_client_updaterec_t, data));
3002                 dns_name_copy(owner, target, &updaterec->buffer);
3003                 if (source != NULL) {
3004                         isc_region_t r;
3005                         dns_rdata_clone(source, rdata);
3006                         dns_rdata_toregion(rdata, &r);
3007                         rdata->data = isc_buffer_used(&updaterec->buffer);
3008                         isc_buffer_copyregion(&updaterec->buffer, &r);
3009                 }
3010                 updaterec->mctx = NULL;
3011                 isc_mem_attach(mctx, &updaterec->mctx);
3012         } else if (source != NULL)
3013                 dns_rdata_clone(source, rdata);
3014
3015         switch (op) {
3016         case updateop_add:
3017                 break;
3018         case updateop_delete:
3019                 if (source != NULL) {
3020                         ttl = 0;
3021                         dns_rdata_makedelete(rdata);
3022                 } else
3023                         dns_rdata_deleterrset(rdata, type);
3024                 break;
3025         case updateop_notexist:
3026                 dns_rdata_notexist(rdata, type);
3027                 break;
3028         case updateop_exist:
3029                 if (source == NULL) {
3030                         ttl = 0;
3031                         dns_rdata_exists(rdata, type);
3032                 }
3033         case updateop_none:
3034                 break;
3035         default:
3036                 INSIST(0);
3037         }
3038
3039         rdatalist->type = rdata->type;
3040         rdatalist->rdclass = rdata->rdclass;
3041         if (source != NULL) {
3042                 rdatalist->covers = dns_rdata_covers(rdata);
3043                 rdatalist->ttl = ttl;
3044         }
3045         ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
3046         dns_rdatalist_tordataset(rdatalist, rdataset);
3047         ISC_LIST_APPEND(target->list, rdataset, link);
3048         if (updaterec != NULL) {
3049                 target->attributes |= DNS_NAMEATTR_HASUPDATEREC;
3050                 dns_name_setbuffer(target, &updaterec->buffer);
3051         }
3052         if (op == updateop_add || op == updateop_delete)
3053                 target->attributes |= DNS_NAMEATTR_UPDATE;
3054         else
3055                 target->attributes |= DNS_NAMEATTR_PREREQUISITE;
3056         return (ISC_R_SUCCESS);
3057 }
3058
3059 void
3060 dns_client_freeupdate(dns_name_t **namep) {
3061         dns_client_updaterec_t *updaterec;
3062         dns_rdatalist_t *rdatalist;
3063         dns_rdataset_t *rdataset;
3064         dns_rdata_t *rdata;
3065         dns_name_t *name;
3066
3067         REQUIRE(namep != NULL && *namep != NULL);
3068
3069         name = *namep;
3070         for (rdataset = ISC_LIST_HEAD(name->list);
3071              rdataset != NULL;
3072              rdataset = ISC_LIST_HEAD(name->list)) {
3073                 ISC_LIST_UNLINK(name->list, rdataset, link);
3074                 rdatalist = NULL;
3075                 dns_rdatalist_fromrdataset(rdataset, &rdatalist);
3076                 if (rdatalist == NULL) {
3077                         dns_rdataset_disassociate(rdataset);
3078                         continue;
3079                 }
3080                 for (rdata = ISC_LIST_HEAD(rdatalist->rdata);
3081                      rdata != NULL;
3082                      rdata = ISC_LIST_HEAD(rdatalist->rdata))
3083                         ISC_LIST_UNLINK(rdatalist->rdata, rdata, link);
3084                 dns_rdataset_disassociate(rdataset);
3085         }
3086
3087         if ((name->attributes & DNS_NAMEATTR_HASUPDATEREC) != 0) {
3088                 updaterec = (dns_client_updaterec_t *)name->buffer;
3089                 INSIST(updaterec != NULL);
3090                 isc_mem_putanddetach(&updaterec->mctx, updaterec,
3091                                      updaterec->size);
3092                 *namep = NULL;
3093         }
3094 }