"Inital commit to Gerrit"
[profile/ivi/dhcp.git] / common / dns.c
1 /* dns.c
2
3    Domain Name Service subroutines. */
4
5 /*
6  * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
8  * Copyright (c) 2001-2003 by Internet Software Consortium
9  *
10  * Permission to use, copy, modify, and distribute this software for any
11  * purpose with or without fee is hereby granted, provided that the above
12  * copyright notice and this permission notice appear in all copies.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  *
22  *   Internet Systems Consortium, Inc.
23  *   950 Charter Street
24  *   Redwood City, CA 94063
25  *   <info@isc.org>
26  *   https://www.isc.org/
27  *
28  * The original software was written for Internet Systems Consortium
29  * by Ted Lemon it has since been extensively modified to use the
30  * asynchronous DNS routines.
31  */
32
33 #include "dhcpd.h"
34 #include "arpa/nameser.h"
35 #include <isc/md5.h>
36
37 #include <dns/result.h>
38
39 /*
40  * This file contains code to connect the DHCP code to the libdns modules.
41  * As part of that function it maintains a database of zone cuts that can
42  * be used to figure out which server should be contacted to update any
43  * given domain name.  Included in the zone information may be a pointer
44  * to a key in which case that key is used for the update.  If no zone
45  * is found then the DNS code determines the zone on its own.
46  *
47  * The way this works is that you define the domain name to which an
48  * SOA corresponds, and the addresses of some primaries for that domain name:
49  *
50  *      zone FOO.COM {
51  *        primary 10.0.17.1;
52  *        secondary 10.0.22.1, 10.0.23.1;
53  *        key "FOO.COM Key";
54  *      }
55  *
56  * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name
57  * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM",
58  * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*,
59  * looks for "FOO.COM", finds it. So it
60  * attempts the update to the primary for FOO.COM.   If that times out, it
61  * tries the secondaries.   You can list multiple primaries if you have some
62  * kind of magic name server that supports that.   You shouldn't list
63  * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't
64  * support update forwarding, AFAIK).   If no TSIG key is listed, the update
65  * is attempted without TSIG.
66  *
67  * You can also include IPv6 addresses via the primary6 and secondary6
68  * options.  The search order for the addresses is primary, primary6,
69  * secondary and lastly secondary6, with a limit on the number of 
70  * addresses used.  Currently this limit is 3.
71  *
72  * The DHCP server tries to find an existing zone for any given name by
73  * trying to look up a local zone structure for each domain containing
74  * that name, all the way up to '.'.   If it finds one cached, it tries
75  * to use that one to do the update.   That's why it tries to update
76  * "FOO.COM" above, even though theoretically it should try GAZANGA...
77  * and TOPANGA... first.
78  *
79  * If the update fails with a predefined zone the zone is marked as bad
80  * and another search of the predefined zones is done.  If no predefined
81  * zone is found finding a zone is left to the DNS module via examination
82  * of SOA records.  If the DNS module finds a zone it may cache the zone
83  * but the zone won't be cached here.
84  *
85  * TSIG updates are not performed on zones found by the DNS module - if
86  * you want TSIG updates you _must_ write a zone definition linking the
87  * key to the zone.   In cases where you know for sure what the key is
88  * but do not want to hardcode the IP addresses of the primary or
89  * secondaries, a zone declaration can be made that doesn't include any
90  * primary or secondary declarations.   When the DHCP server encounters
91  * this while hunting up a matching zone for a name, it looks up the SOA,
92  * fills in the IP addresses, and uses that record for the update.
93  * If the SOA lookup returns NXRRSET, a warning is printed and the zone is
94  * discarded, TSIG key and all.   The search for the zone then continues 
95  * as if the zone record hadn't been found.   Zones without IP addresses 
96  * don't match when initially hunting for a zone to update.
97  *
98  * When an update is attempted and no predefined zone is found
99  * that matches any enclosing domain of the domain being updated, the DHCP
100  * server goes through the same process that is done when the update to a
101  * predefined zone fails - starting with the most specific domain
102  * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root),
103  * it tries to look up an SOA record.
104  *
105  * TSIG keys are defined like this:
106  *
107  *      key "FOO.COM Key" {
108  *              algorithm HMAC-MD5.SIG-ALG.REG.INT;
109  *              secret <Base64>;
110  *      }
111  *
112  * <Base64> is a number expressed in base64 that represents the key.
113  * It's also permissible to use a quoted string here - this will be
114  * translated as the ASCII bytes making up the string, and will not
115  * include any NUL termination.  The key name can be any text string,
116  * and the key type must be one of the key types defined in the draft
117  * or by the IANA.  Currently only the HMAC-MD5... key type is
118  * supported.
119  *
120  * The DDNS processing has been split into two areas.  One is the
121  * control code that determines what should be done.  That code is found
122  * in the client or server directories.  The other is the common code
123  * that performs functions such as properly formatting the arguments.
124  * That code is found in this file.  The basic processing flow for a
125  * DDNS update is:
126  * In the client or server code determine what needs to be done and
127  * collect the necesary information then pass it to a function from
128  * this file.
129  * In this code lookup the zone and extract the zone and key information
130  * (if available) and prepare the arguments for the DNS module.
131  * When the DNS module completes its work (times out or gets a reply)
132  * it will trigger another function here which does generic processing
133  * and then passes control back to the code from the server or client.
134  * The server or client code then determines the next step which may
135  * result in another call to this module in which case the process repeats.
136  */
137
138 dns_zone_hash_t *dns_zone_hash;
139
140 /*
141  * DHCP dns structures
142  * Normally the relationship between these structures isn't one to one
143  * but in the DHCP case it (mostly) is.  To make the allocations, frees,
144  * and passing of the memory easier we make a single structure with all
145  * the pieces.
146  *
147  * The maximum size of the data buffer should be large enough for any
148  * items DHCP will generate
149  */
150
151 typedef struct dhcp_ddns_rdata {
152         dns_rdata_t     rdata;
153         dns_rdatalist_t rdatalist;
154         dns_rdataset_t  rdataset;
155 } dhcp_ddns_data_t;
156
157 #if defined (NSUPDATE)
158
159 void ddns_interlude(isc_task_t  *, isc_event_t *);
160
161
162 #if defined (TRACING)
163 /*
164  * Code to support tracing DDNS packets.  We trace packets going to and
165  * coming from the libdns code but don't try to track the packets
166  * exchanged between the libdns code and the dns server(s) it contacts.
167  *
168  * The code is split into two sets of routines
169  *  input refers to messages received from the dns module
170  *  output refers to messages sent to the dns module
171  * Currently there are three routines in each set
172  *  write is used to write information about the message to the trace file
173  *        this routine is called directly from the proper place in the code.
174  *  read is used to read information about a message from the trace file
175  *       this routine is called from the trace loop as it reads through
176  *       the file and is registered via the trace_type_register routine.
177  *       When playing back a trace file we shall absorb records of output
178  *       messages as part of processing the write function, therefore
179  *       any output messages we encounter are flagged as errors.
180  *  stop isn't currently used in this code but is needed for the register
181  *       routine.
182  *
183  * We pass a pointer to a control block to the dns module which it returns
184  * to use as part of the result.  As the pointer may vary between traces
185  * we need to map between those from the trace file and the new ones during
186  * playback.
187  *
188  * The mapping is complicated a little as a pointer could be 4 or 8 bytes
189  * long.  We treat the old pointer as an 8 byte quantity and pad and compare
190  * as necessary.
191  */
192
193 /*
194  * Structure used to map old pointers to new pointers.
195  * Old pointers are 8 bytes long as we don't know if the trace was 
196  * done on a 64 bit or 32 bit machine.  
197  */
198 #define TRACE_PTR_LEN 8
199
200 typedef struct dhcp_ddns_map {
201         char  old_pointer[TRACE_PTR_LEN];
202         void *new_pointer;
203         struct dhcp_ddns_map *next;
204 } dhcp_ddns_map_t;
205
206 /* The starting point for the map structure */
207 static dhcp_ddns_map_t *ddns_map;
208
209 trace_type_t *trace_ddns_input;
210 trace_type_t *trace_ddns_output;
211
212 /*
213  * The data written to the trace file is:
214  * 32 bits result from dns
215  * 64 bits pointer of cb
216  */
217
218 void
219 trace_ddns_input_write(dhcp_ddns_cb_t *ddns_cb, isc_result_t result)
220 {
221         trace_iov_t iov[2];
222         u_int32_t old_result;
223         char old_pointer[TRACE_PTR_LEN];
224         
225         old_result = htonl((u_int32_t)result);
226         memset(old_pointer, 0, TRACE_PTR_LEN);
227         memcpy(old_pointer, &ddns_cb, sizeof(ddns_cb));
228
229         iov[0].len = sizeof(old_result);
230         iov[0].buf = (char *)&old_result;
231         iov[1].len = TRACE_PTR_LEN;
232         iov[1].buf = old_pointer;
233         trace_write_packet_iov(trace_ddns_input, 2, iov, MDL);
234 }
235
236 /*
237  * Process the result and pointer from the trace file.
238  * We use the pointer map to find the proper pointer for this instance.
239  * Then we need to construct an event to pass along to the interlude
240  * function.
241  */
242 static void
243 trace_ddns_input_read(trace_type_t *ttype, unsigned length,
244                                   char *buf)
245 {
246         u_int32_t old_result;
247         char old_pointer[TRACE_PTR_LEN];
248         dns_clientupdateevent_t *eventp;
249         void *new_pointer;
250         dhcp_ddns_map_t *ddns_map_ptr;
251
252         if (length < (sizeof(old_result) + TRACE_PTR_LEN)) {
253                 log_error("trace_ddns_input_read: data too short");
254                 return;
255         }
256
257         memcpy(&old_result, buf, sizeof(old_result));
258         memcpy(old_pointer, buf + sizeof(old_result), TRACE_PTR_LEN);
259
260         /* map the old pointer to a new pointer */
261         for (ddns_map_ptr = ddns_map;
262              ddns_map_ptr != NULL;
263              ddns_map_ptr = ddns_map_ptr->next) {
264                 if ((ddns_map_ptr->new_pointer != NULL) &&
265                     memcmp(ddns_map_ptr->old_pointer,
266                            old_pointer, TRACE_PTR_LEN) == 0) {
267                         new_pointer = ddns_map_ptr->new_pointer;
268                         ddns_map_ptr->new_pointer = NULL;
269                         memset(ddns_map_ptr->old_pointer, 0, TRACE_PTR_LEN);
270                         break;
271                 }
272         }
273         if (ddns_map_ptr == NULL) {
274                 log_error("trace_dns_input_read: unable to map cb pointer");
275                 return;
276         }               
277
278         eventp = (dns_clientupdateevent_t *)
279                 isc_event_allocate(dhcp_gbl_ctx.mctx,
280                                    dhcp_gbl_ctx.task,
281                                    0,
282                                    ddns_interlude,
283                                    new_pointer,
284                                    sizeof(dns_clientupdateevent_t));
285         if (eventp == NULL) {
286                 log_error("trace_ddns_input_read: unable to allocate event");
287                 return;
288         }
289         eventp->result = ntohl(old_result);
290
291
292         ddns_interlude(dhcp_gbl_ctx.task, (isc_event_t *)eventp);
293
294         return;
295 }
296
297 static void
298 trace_ddns_input_stop(trace_type_t *ttype)
299 {
300 }
301
302 /*
303  * We use the same arguments as for the dns startupdate function to
304  * allows us to choose between the two via a macro.  If tracing isn't
305  * in use we simply call the dns function directly.
306  *
307  * If we are doing playback we read the next packet from the file
308  * and compare the type.  If it matches we extract the results and pointer
309  * from the trace file.  The results are returned to the caller as if
310  * they had called the dns routine.  The pointer is used to construct a 
311  * map for when the "reply" is processed.
312  *
313  * The data written to trace file is:
314  * 32 bits result
315  * 64 bits pointer of cb (DDNS Control block)
316  * contents of cb
317  */
318
319 isc_result_t
320 trace_ddns_output_write(dns_client_t *client, dns_rdataclass_t rdclass,
321                         dns_name_t *zonename, dns_namelist_t *prerequisites,
322                         dns_namelist_t *updates, isc_sockaddrlist_t *servers,
323                         dns_tsec_t *tsec, unsigned int options,
324                         isc_task_t *task, isc_taskaction_t action, void *arg,
325                         dns_clientupdatetrans_t **transp)
326 {
327         isc_result_t result;
328         u_int32_t old_result;
329         char old_pointer[TRACE_PTR_LEN];
330         dhcp_ddns_map_t *ddns_map_ptr;
331         
332         if (trace_playback() != 0) {
333                 /* We are doing playback, extract the entry from the file */
334                 unsigned buflen = 0;
335                 char *inbuf = NULL;
336
337                 result = trace_get_packet(&trace_ddns_output,
338                                           &buflen, &inbuf);
339                 if (result != ISC_R_SUCCESS) {
340                         log_error("trace_ddns_output_write: no input found");
341                         return (ISC_R_FAILURE);
342                 }
343                 if (buflen < (sizeof(old_result) + TRACE_PTR_LEN)) {
344                         log_error("trace_ddns_output_write: data too short");
345                         dfree(inbuf, MDL);
346                         return (ISC_R_FAILURE);
347                 }
348                 memcpy(&old_result, inbuf, sizeof(old_result));
349                 result = ntohl(old_result);
350                 memcpy(old_pointer, inbuf + sizeof(old_result), TRACE_PTR_LEN);
351                 dfree(inbuf, MDL);
352
353                 /* add the pointer to the pointer map */
354                 for (ddns_map_ptr = ddns_map;
355                      ddns_map_ptr != NULL;
356                      ddns_map_ptr = ddns_map_ptr->next) {
357                         if (ddns_map_ptr->new_pointer == NULL) {
358                                 break;
359                         }
360                 }
361
362                 /*
363                  * If we didn't find an empty entry, allocate an entry and
364                  * link it into the list.  The list isn't ordered.
365                  */
366                 if (ddns_map_ptr == NULL) {
367                         ddns_map_ptr = dmalloc(sizeof(*ddns_map_ptr), MDL);
368                         if (ddns_map_ptr == NULL) {
369                                 log_error("trace_ddns_output_write: " 
370                                           "unable to allocate map entry");
371                                 return(ISC_R_FAILURE);
372                                 }
373                         ddns_map_ptr->next = ddns_map;
374                         ddns_map = ddns_map_ptr;
375                 }
376
377                 memcpy(ddns_map_ptr->old_pointer, old_pointer, TRACE_PTR_LEN);
378                 ddns_map_ptr->new_pointer = arg;
379         }
380         else {
381                 /* We aren't doing playback, make the actual call */
382                 result = dns_client_startupdate(client, rdclass, zonename,
383                                                 prerequisites, updates,
384                                                 servers, tsec, options,
385                                                 task, action, arg, transp);
386         }
387
388         if (trace_record() != 0) {
389                 /* We are recording, save the information to the file */
390                 trace_iov_t iov[3];
391                 old_result = htonl((u_int32_t)result);
392                 memset(old_pointer, 0, TRACE_PTR_LEN);
393                 memcpy(old_pointer, &arg, sizeof(arg));
394                 iov[0].len = sizeof(old_result);
395                 iov[0].buf = (char *)&old_result;
396                 iov[1].len = TRACE_PTR_LEN;
397                 iov[1].buf = old_pointer;
398
399                 /* Write out the entire cb, in case we want to look at it */
400                 iov[2].len = sizeof(dhcp_ddns_cb_t);
401                 iov[2].buf = (char *)arg;
402
403                 trace_write_packet_iov(trace_ddns_output, 3, iov, MDL);
404         }
405
406         return(result);
407 }
408
409 static void
410 trace_ddns_output_read(trace_type_t *ttype, unsigned length,
411                                    char *buf)
412 {
413         log_error("unaccounted for ddns output.");
414 }
415
416 static void
417 trace_ddns_output_stop(trace_type_t *ttype)
418 {
419 }
420
421 void
422 trace_ddns_init()
423 {
424         trace_ddns_output = trace_type_register("ddns-output", NULL,
425                                                 trace_ddns_output_read,
426                                                 trace_ddns_output_stop, MDL);
427         trace_ddns_input  = trace_type_register("ddns-input", NULL,
428                                                 trace_ddns_input_read,
429                                                 trace_ddns_input_stop, MDL);
430         ddns_map = NULL;
431 }
432
433 #define ddns_update trace_ddns_output_write
434 #else
435 #define ddns_update dns_client_startupdate
436 #endif /* TRACING */
437
438 /*
439  * Code to allocate and free a dddns control block.  This block is used
440  * to pass and track the information associated with a DDNS update request.
441  */
442 dhcp_ddns_cb_t *
443 ddns_cb_alloc(const char *file, int line)
444 {
445         dhcp_ddns_cb_t *ddns_cb;
446         int i;
447
448         ddns_cb = dmalloc(sizeof(*ddns_cb), file, line);
449         if (ddns_cb != NULL) {
450                 ISC_LIST_INIT(ddns_cb->zone_server_list);
451                 for (i = 0; i < DHCP_MAXNS; i++) {
452                         ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
453                 }
454         }
455
456 #if defined (DEBUG_DNS_UPDATES)
457         log_info("%s(%d): Allocating ddns_cb=%p", file, line, ddns_cb);
458 #endif
459
460         return(ddns_cb);
461 }
462                 
463 void
464 ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
465 {
466 #if defined (DEBUG_DNS_UPDATES)
467         log_info("%s(%d): freeing ddns_cb=%p", file, line, ddns_cb);
468 #endif
469
470         data_string_forget(&ddns_cb->fwd_name, file, line);
471         data_string_forget(&ddns_cb->rev_name, file, line);
472         data_string_forget(&ddns_cb->dhcid, file, line);
473         
474         if (ddns_cb->zone != NULL) {
475                 forget_zone((struct dns_zone **)&ddns_cb->zone);
476         }
477
478         /* Should be freed by now, check just in case. */
479         if (ddns_cb->transaction != NULL)
480                 log_error("Impossible memory leak at %s:%d (attempt to free "
481                           "DDNS Control Block before transaction).", MDL);
482
483         dfree(ddns_cb, file, line);
484 }
485
486 void
487 ddns_cb_forget_zone(dhcp_ddns_cb_t *ddns_cb)
488 {
489         int i;
490
491         forget_zone(&ddns_cb->zone);
492         ddns_cb->zone_name[0] = 0;
493         ISC_LIST_INIT(ddns_cb->zone_server_list);
494         for (i = 0; i < DHCP_MAXNS; i++) {
495                 ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
496         }
497 }
498
499 isc_result_t find_tsig_key (ns_tsig_key **key, const char *zname,
500                             struct dns_zone *zone)
501 {
502         ns_tsig_key *tkey;
503
504         if (!zone)
505                 return ISC_R_NOTFOUND;
506
507         if (!zone -> key) {
508                 return DHCP_R_KEY_UNKNOWN;
509         }
510         
511         if ((!zone -> key -> name ||
512              strlen (zone -> key -> name) > NS_MAXDNAME) ||
513             (!zone -> key -> algorithm ||
514              strlen (zone -> key -> algorithm) > NS_MAXDNAME) ||
515             (!zone -> key) ||
516             (!zone -> key -> key) ||
517             (zone -> key -> key -> len == 0)) {
518                 return DHCP_R_INVALIDKEY;
519         }
520         tkey = dmalloc (sizeof *tkey, MDL);
521         if (!tkey) {
522               nomem:
523                 return ISC_R_NOMEMORY;
524         }
525         memset (tkey, 0, sizeof *tkey);
526         tkey -> data = dmalloc (zone -> key -> key -> len, MDL);
527         if (!tkey -> data) {
528                 dfree (tkey, MDL);
529                 goto nomem;
530         }
531         strcpy (tkey -> name, zone -> key -> name);
532         strcpy (tkey -> alg, zone -> key -> algorithm);
533         memcpy (tkey -> data,
534                 zone -> key -> key -> value, zone -> key -> key -> len);
535         tkey -> len = zone -> key -> key -> len;
536         *key = tkey;
537         return ISC_R_SUCCESS;
538 }
539
540 void tkey_free (ns_tsig_key **key)
541 {
542         if ((*key) -> data)
543                 dfree ((*key) -> data, MDL);
544         dfree ((*key), MDL);
545         *key = (ns_tsig_key *)0;
546 }
547 #endif
548
549 isc_result_t enter_dns_zone (struct dns_zone *zone)
550 {
551         struct dns_zone *tz = (struct dns_zone *)0;
552
553         if (dns_zone_hash) {
554                 dns_zone_hash_lookup (&tz,
555                                       dns_zone_hash, zone -> name, 0, MDL);
556                 if (tz == zone) {
557                         dns_zone_dereference (&tz, MDL);
558                         return ISC_R_SUCCESS;
559                 }
560                 if (tz) {
561                         dns_zone_hash_delete (dns_zone_hash,
562                                               zone -> name, 0, MDL);
563                         dns_zone_dereference (&tz, MDL);
564                 }
565         } else {
566                 if (!dns_zone_new_hash(&dns_zone_hash, DNS_HASH_SIZE, MDL))
567                         return ISC_R_NOMEMORY;
568         }
569
570         dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
571         return ISC_R_SUCCESS;
572 }
573
574 isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
575 {
576         int len;
577         char *tname = (char *)0;
578         isc_result_t status;
579
580         if (!dns_zone_hash)
581                 return ISC_R_NOTFOUND;
582
583         len = strlen (name);
584         if (name [len - 1] != '.') {
585                 tname = dmalloc ((unsigned)len + 2, MDL);
586                 if (!tname)
587                         return ISC_R_NOMEMORY;
588                 strcpy (tname, name);
589                 tname [len] = '.';
590                 tname [len + 1] = 0;
591                 name = tname;
592         }
593         if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL))
594                 status = ISC_R_NOTFOUND;
595         else
596                 status = ISC_R_SUCCESS;
597
598         if (tname)
599                 dfree (tname, MDL);
600         return status;
601 }
602
603 int dns_zone_dereference (ptr, file, line)
604         struct dns_zone **ptr;
605         const char *file;
606         int line;
607 {
608         struct dns_zone *dns_zone;
609
610         if ((ptr == NULL) || (*ptr == NULL)) {
611                 log_error("%s(%d): null pointer", file, line);
612 #if defined (POINTER_DEBUG)
613                 abort();
614 #else
615                 return (0);
616 #endif
617         }
618
619         dns_zone = *ptr;
620         *ptr = NULL;
621         --dns_zone->refcnt;
622         rc_register(file, line, ptr, dns_zone, dns_zone->refcnt, 1, RC_MISC);
623         if (dns_zone->refcnt > 0)
624                 return (1);
625
626         if (dns_zone->refcnt < 0) {
627                 log_error("%s(%d): negative refcnt!", file, line);
628 #if defined (DEBUG_RC_HISTORY)
629                 dump_rc_history(dns_zone);
630 #endif
631 #if defined (POINTER_DEBUG)
632                 abort();
633 #else
634                 return (0);
635 #endif
636         }
637
638         if (dns_zone->name)
639                 dfree(dns_zone->name, file, line);
640         if (dns_zone->key)
641                 omapi_auth_key_dereference(&dns_zone->key, file, line);
642         if (dns_zone->primary)
643                 option_cache_dereference(&dns_zone->primary, file, line);
644         if (dns_zone->secondary)
645                 option_cache_dereference(&dns_zone->secondary, file, line);
646         if (dns_zone->primary6)
647                 option_cache_dereference(&dns_zone->primary6, file, line);
648         if (dns_zone->secondary6)
649                 option_cache_dereference(&dns_zone->secondary6, file, line);
650         dfree(dns_zone, file, line);
651         return (1);
652 }
653
654 #if defined (NSUPDATE)
655 isc_result_t
656 find_cached_zone(dhcp_ddns_cb_t *ddns_cb, int direction) 
657 {
658         isc_result_t status = ISC_R_NOTFOUND;
659         const char *np;
660         struct dns_zone *zone = NULL;
661         struct data_string nsaddrs;
662         struct in_addr zone_addr;
663         struct in6_addr zone_addr6;
664         int ix;
665
666         if (direction == FIND_FORWARD) {
667                 np = (const char *)ddns_cb->fwd_name.data;
668         } else {
669                 np = (const char *)ddns_cb->rev_name.data;
670         }
671
672         /* We can't look up a null zone. */
673         if ((np == NULL) || (*np == '\0')) {
674                 return (DHCP_R_INVALIDARG);
675         }
676
677         /*
678          * For each subzone, try to find a cached zone.
679          */
680         for (;;) {
681                 status = dns_zone_lookup(&zone, np);
682                 if (status == ISC_R_SUCCESS)
683                         break;
684
685                 np = strchr(np, '.');
686                 if (np == NULL)
687                         break;
688                 np++;
689         }
690
691         if (status != ISC_R_SUCCESS)
692                 return (status);
693
694         /* Make sure the zone is valid. */
695         if (zone->timeout && zone->timeout < cur_time) {
696                 dns_zone_dereference(&zone, MDL);
697                 return (ISC_R_CANCELED);
698         }
699
700         /* Make sure the zone name will fit. */
701         if (strlen(zone->name) > sizeof(ddns_cb->zone_name)) {
702                 dns_zone_dereference(&zone, MDL);
703                 return (ISC_R_NOSPACE);
704         }
705         strcpy((char *)&ddns_cb->zone_name[0], zone->name);
706
707         memset (&nsaddrs, 0, sizeof nsaddrs);
708         ix = 0;
709
710         if (zone->primary) {
711                 if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
712                                           NULL, NULL, &global_scope,
713                                           zone->primary, MDL)) {
714                         int ip = 0;
715                         while (ix < DHCP_MAXNS) {
716                                 if (ip + 4 > nsaddrs.len)
717                                         break;
718                                 memcpy(&zone_addr, &nsaddrs.data[ip], 4);
719                                 isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
720                                                     &zone_addr,
721                                                     NS_DEFAULTPORT);
722                                 ISC_LIST_APPEND(ddns_cb->zone_server_list,
723                                                 &ddns_cb->zone_addrs[ix],
724                                                 link);
725                                 ip += 4;
726                                 ix++;
727                         }
728                         data_string_forget(&nsaddrs, MDL);
729                 }
730         }
731
732         if (zone->primary6) {
733                 if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
734                                           NULL, NULL, &global_scope,
735                                           zone->primary6, MDL)) {
736                         int ip = 0;
737                         while (ix < DHCP_MAXNS) {
738                                 if (ip + 16 > nsaddrs.len)
739                                         break;
740                                 memcpy(&zone_addr6, &nsaddrs.data[ip], 16);
741                                 isc_sockaddr_fromin6(&ddns_cb->zone_addrs[ix],
742                                                     &zone_addr6,
743                                                     NS_DEFAULTPORT);
744                                 ISC_LIST_APPEND(ddns_cb->zone_server_list,
745                                                 &ddns_cb->zone_addrs[ix],
746                                                 link);
747                                 ip += 16;
748                                 ix++;
749                         }
750                         data_string_forget(&nsaddrs, MDL);
751                 }
752         }
753
754         if (zone->secondary) {
755                 if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
756                                           NULL, NULL, &global_scope,
757                                           zone->secondary, MDL)) {
758                         int ip = 0;
759                         while (ix < DHCP_MAXNS) {
760                                 if (ip + 4 > nsaddrs.len)
761                                         break;
762                                 memcpy(&zone_addr, &nsaddrs.data[ip], 4);
763                                 isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
764                                                     &zone_addr,
765                                                     NS_DEFAULTPORT);
766                                 ISC_LIST_APPEND(ddns_cb->zone_server_list,
767                                                 &ddns_cb->zone_addrs[ix],
768                                                 link);
769                                 ip += 4;
770                                 ix++;
771                         }
772                         data_string_forget (&nsaddrs, MDL);
773                 }
774         }
775
776         if (zone->secondary6) {
777                 if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
778                                           NULL, NULL, &global_scope,
779                                           zone->secondary6, MDL)) {
780                         int ip = 0;
781                         while (ix < DHCP_MAXNS) {
782                                 if (ip + 16 > nsaddrs.len)
783                                         break;
784                                 memcpy(&zone_addr6, &nsaddrs.data[ip], 16);
785                                 isc_sockaddr_fromin6(&ddns_cb->zone_addrs[ix],
786                                                     &zone_addr6,
787                                                     NS_DEFAULTPORT);
788                                 ISC_LIST_APPEND(ddns_cb->zone_server_list,
789                                                 &ddns_cb->zone_addrs[ix],
790                                                 link);
791                                 ip += 16;
792                                 ix++;
793                         }
794                         data_string_forget (&nsaddrs, MDL);
795                 }
796         }
797
798         dns_zone_reference(&ddns_cb->zone, zone, MDL);
799         dns_zone_dereference (&zone, MDL);
800         return ISC_R_SUCCESS;
801 }
802
803 void forget_zone (struct dns_zone **zone)
804 {
805         dns_zone_dereference (zone, MDL);
806 }
807
808 void repudiate_zone (struct dns_zone **zone)
809 {
810         /* XXX Currently we're not differentiating between a cached
811            XXX zone and a zone that's been repudiated, which means
812            XXX that if we reap cached zones, we blow away repudiated
813            XXX zones.   This isn't a big problem since we're not yet
814            XXX caching zones... :'} */
815
816         (*zone) -> timeout = cur_time - 1;
817         dns_zone_dereference (zone, MDL);
818 }
819
820 /* Have to use TXT records for now. */
821 #define T_DHCID T_TXT
822
823 int get_dhcid (struct data_string *id,
824                int type, const u_int8_t *data, unsigned len)
825 {
826         unsigned char buf[ISC_MD5_DIGESTLENGTH];
827         isc_md5_t md5;
828         int i;
829
830         /* Types can only be 0..(2^16)-1. */
831         if (type < 0 || type > 65535)
832                 return 0;
833
834         /*
835          * Hexadecimal MD5 digest plus two byte type, NUL,
836          * and one byte for length for dns.
837          */
838         if (!buffer_allocate (&id -> buffer,
839                               (ISC_MD5_DIGESTLENGTH * 2) + 4, MDL))
840                 return 0;
841         id -> data = id -> buffer -> data;
842
843         /*
844          * DHCP clients and servers should use the following forms of client
845          * identification, starting with the most preferable, and finishing
846          * with the least preferable.  If the client does not send any of these
847          * forms of identification, the DHCP/DDNS interaction is not defined by
848          * this specification.  The most preferable form of identification is
849          * the Globally Unique Identifier Option [TBD].  Next is the DHCP
850          * Client Identifier option.  Last is the client's link-layer address,
851          * as conveyed in its DHCPREQUEST message.  Implementors should note
852          * that the link-layer address cannot be used if there are no
853          * significant bytes in the chaddr field of the DHCP client's request,
854          * because this does not constitute a unique identifier.
855          *   -- "Interaction between DHCP and DNS"
856          *      <draft-ietf-dhc-dhcp-dns-12.txt>
857          *      M. Stapp, Y. Rekhter
858          *
859          * We put the length into the first byte to turn 
860          * this into a dns text string.  This avoid needing to
861          * copy the string to add the byte later.
862          */
863         id->buffer->data[0] = ISC_MD5_DIGESTLENGTH * 2 + 2;
864
865         /* Put the type in the next two bytes. */
866         id->buffer->data[1] = "0123456789abcdef"[(type >> 4) & 0xf];
867         /* This should have been [type & 0xf] but now that
868          * it is in use we need to leave it this way in order
869          * to avoid disturbing customer's lease files
870          */
871         id->buffer->data[2] = "0123456789abcdef"[type % 15];
872   
873         /* Mash together an MD5 hash of the identifier. */
874         isc_md5_init(&md5);
875         isc_md5_update(&md5, data, len);
876         isc_md5_final(&md5, buf);
877
878         /* Convert into ASCII. */
879         for (i = 0; i < ISC_MD5_DIGESTLENGTH; i++) {
880                 id->buffer->data[i * 2 + 3] =
881                         "0123456789abcdef"[(buf[i] >> 4) & 0xf];
882                 id->buffer->data[i * 2 + 4] =
883                         "0123456789abcdef"[buf[i] & 0xf];
884         }
885
886         id->len = ISC_MD5_DIGESTLENGTH * 2 + 3;
887         id->buffer->data[id->len] = 0;
888         id->terminated = 1;
889
890         return 1;
891 }
892
893 /*
894  * The dhcid (text version) that we pass to DNS includes a length byte
895  * at the start but the text we store in the lease doesn't include the
896  * length byte.  The following routines are to convert between the two
897  * styles.
898  *
899  * When converting from a dhcid to a leaseid we reuse the buffer and
900  * simply adjust the data pointer and length fields in the data string.
901  * This avoids any prolems with allocating space.
902  */
903
904 void
905 dhcid_tolease(struct data_string *dhcid,
906               struct data_string *leaseid)
907 {
908         /* copy the data string then update the fields */
909         data_string_copy(leaseid, dhcid, MDL);
910         leaseid->data++;
911         leaseid->len--;
912 }
913
914 isc_result_t
915 dhcid_fromlease(struct data_string *dhcid,
916                 struct data_string *leaseid)
917 {
918         if (!buffer_allocate(&dhcid->buffer, leaseid->len + 2, MDL)) {
919                 return(ISC_R_FAILURE);
920         }
921
922         dhcid->data = dhcid->buffer->data;
923
924         dhcid->buffer->data[0] = leaseid->len;
925         memcpy(dhcid->buffer->data + 1, leaseid->data, leaseid->len);
926         dhcid->len = leaseid->len + 1;
927         if (leaseid->terminated == 1) {
928                 dhcid->buffer->data[dhcid->len] = 0;
929                 dhcid->terminated = 1;
930         }
931
932         return(ISC_R_SUCCESS);
933 }
934
935 /* 
936  * Construct the dataset for this item.
937  * This is a fairly simple arrangement as the operations we do are simple.
938  * If there is data we simply have the rdata point to it - the formatting
939  * must be correct already.  We then link the rdatalist to the rdata and
940  * create a rdataset from the rdatalist.
941  */
942
943 static isc_result_t
944 make_dns_dataset(dns_rdataclass_t  dataclass,
945                  dns_rdatatype_t   datatype,
946                  dhcp_ddns_data_t *dataspace,
947                  unsigned char    *data,
948                  int               datalen,
949                  int               ttl)
950 {
951         dns_rdata_t *rdata = &dataspace->rdata;
952         dns_rdatalist_t *rdatalist = &dataspace->rdatalist;
953         dns_rdataset_t *rdataset = &dataspace->rdataset;
954
955         isc_region_t region;
956
957         /* set up the rdata */
958         dns_rdata_init(rdata);
959
960         if (data == NULL) {
961                 /* No data, set up the rdata fields we care about */
962                 rdata->flags = DNS_RDATA_UPDATE;
963                 rdata->type = datatype;
964                 rdata->rdclass = dataclass;
965         } else {
966                 switch(datatype) {
967                 case dns_rdatatype_a:
968                 case dns_rdatatype_aaaa:
969                 case dns_rdatatype_txt:
970                 case dns_rdatatype_dhcid:
971                 case dns_rdatatype_ptr:
972                         /* The data must be in the right format we simply
973                          * need to supply it via the correct structure */
974                         region.base   = data;
975                         region.length = datalen;
976                         dns_rdata_fromregion(rdata, dataclass, datatype,
977                                              &region);
978                         break;
979                 default:
980                         return(DHCP_R_INVALIDARG);
981                         break;
982                 }
983         }
984
985         /* setup the datalist and attach the rdata to it */
986         dns_rdatalist_init(rdatalist);
987         rdatalist->type = datatype;
988         rdatalist->rdclass = dataclass;
989         rdatalist->ttl = ttl;
990         ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
991
992         /* convert the datalist to a dataset */
993         dns_rdataset_init(rdataset);
994         dns_rdatalist_tordataset(rdatalist, rdataset);
995
996         return(ISC_R_SUCCESS);
997 }
998
999 /*
1000  * When a DHCP client or server intends to update an A RR, it first
1001  * prepares a DNS UPDATE query which includes as a prerequisite the
1002  * assertion that the name does not exist.  The update section of the
1003  * query attempts to add the new name and its IP address mapping (an A
1004  * RR), and the DHCID RR with its unique client-identity.
1005  *   -- "Interaction between DHCP and DNS"
1006  *
1007  * There are two cases, one for the server and one for the client.
1008  *
1009  * For the server the first step will have a request of:
1010  * The name is not in use
1011  * Add an A RR
1012  * Add a DHCID RR (currently txt)
1013  *
1014  * For the client the first step will have a request of:
1015  * The A RR does not exist
1016  * Add an A RR
1017  * Add a DHCID RR (currently txt)
1018  */
1019
1020 static isc_result_t
1021 ddns_modify_fwd_add1(dhcp_ddns_cb_t   *ddns_cb,
1022                      dhcp_ddns_data_t *dataspace,
1023                      dns_name_t       *pname,
1024                      dns_name_t       *uname)
1025 {
1026         isc_result_t result;
1027
1028         /* Construct the prerequisite list */
1029         if ((ddns_cb->flags & DDNS_INCLUDE_RRSET) != 0) {
1030                 /* The A RR shouldn't exist */
1031                 result = make_dns_dataset(dns_rdataclass_none,
1032                                           ddns_cb->address_type,
1033                                           dataspace, NULL, 0, 0);
1034         } else {
1035                 /* The name is not in use */
1036                 result = make_dns_dataset(dns_rdataclass_none,
1037                                           dns_rdatatype_any,
1038                                           dataspace, NULL, 0, 0);
1039         }
1040         if (result != ISC_R_SUCCESS) {
1041                 return(result);
1042         }
1043         ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1044         dataspace++;
1045
1046         /* Construct the update list */
1047         /* Add the A RR */
1048         result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
1049                                   dataspace, 
1050                                   (unsigned char *)ddns_cb->address.iabuf,
1051                                   ddns_cb->address.len, ddns_cb->ttl);
1052         if (result != ISC_R_SUCCESS) {
1053                 return(result);
1054         }
1055         ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1056         dataspace++;
1057
1058         /* Add the DHCID RR */
1059         result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
1060                                   dataspace, 
1061                                   (unsigned char *)ddns_cb->dhcid.data,
1062                                   ddns_cb->dhcid.len, ddns_cb->ttl);
1063         if (result != ISC_R_SUCCESS) {
1064                 return(result);
1065         }
1066         ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1067
1068         return(ISC_R_SUCCESS);
1069 }
1070
1071 /*
1072  * If the first update operation fails with YXDOMAIN, the updater can
1073  * conclude that the intended name is in use.  The updater then
1074  * attempts to confirm that the DNS name is not being used by some
1075  * other host. The updater prepares a second UPDATE query in which the
1076  * prerequisite is that the desired name has attached to it a DHCID RR
1077  * whose contents match the client identity.  The update section of
1078  * this query deletes the existing A records on the name, and adds the
1079  * A record that matches the DHCP binding and the DHCID RR with the
1080  * client identity.
1081  *   -- "Interaction between DHCP and DNS"
1082  *
1083  * The message for the second step depends on if we are doing conflict
1084  * resolution.  If we are we include a prerequisite.  If not we delete
1085  * the DHCID in addition to all A rrsets.
1086  *
1087  * Conflict resolution:
1088  * DHCID RR exists, and matches client identity.
1089  * Delete A RRset.
1090  * Add A RR.
1091  *
1092  * Conflict override:
1093  * Delete DHCID RRs.
1094  * Add DHCID RR
1095  * Delete A RRset.
1096  * Add A RR.
1097  */
1098
1099 static isc_result_t
1100 ddns_modify_fwd_add2(dhcp_ddns_cb_t   *ddns_cb,
1101                      dhcp_ddns_data_t *dataspace,
1102                      dns_name_t       *pname,
1103                      dns_name_t       *uname)
1104 {
1105         isc_result_t result;
1106
1107         /*
1108          * If we are doing conflict resolution (unset) we use a prereq list.
1109          * If not we delete the DHCID in addition to all A rrsets.
1110          */
1111         if ((ddns_cb->flags & DDNS_CONFLICT_OVERRIDE) == 0) {
1112                 /* Construct the prereq list */
1113                 /* The DHCID RR exists and matches the client identity */
1114                 result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
1115                                           dataspace, 
1116                                           (unsigned char *)ddns_cb->dhcid.data,
1117                                           ddns_cb->dhcid.len, 0);
1118                 if (result != ISC_R_SUCCESS) {
1119                         return(result);
1120                 }
1121                 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1122                 dataspace++;
1123         } else {
1124                 /* Start constructing the update list.
1125                  * Conflict detection override: delete DHCID RRs */
1126                 result = make_dns_dataset(dns_rdataclass_any,
1127                                           dns_rdatatype_txt,
1128                                           dataspace, NULL, 0, 0);
1129                 if (result != ISC_R_SUCCESS) {
1130                         return(result);
1131                 }
1132                 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1133                 dataspace++;
1134
1135                 /* Add current DHCID RR */
1136                 result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
1137                                           dataspace, 
1138                                           (unsigned char *)ddns_cb->dhcid.data,
1139                                           ddns_cb->dhcid.len, ddns_cb->ttl);
1140                 if (result != ISC_R_SUCCESS) {
1141                         return(result);
1142                 }
1143                 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1144                 dataspace++;
1145         }
1146
1147         /* Start or continue constructing the update list */
1148         /* Delete the A RRset */
1149         result = make_dns_dataset(dns_rdataclass_any, ddns_cb->address_type,
1150                                   dataspace, NULL, 0, 0);
1151         if (result != ISC_R_SUCCESS) {
1152                 return(result);
1153         }
1154         ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1155         dataspace++;
1156
1157         /* Add the A RR */
1158         result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
1159                                   dataspace, 
1160                                   (unsigned char *)ddns_cb->address.iabuf,
1161                                   ddns_cb->address.len, ddns_cb->ttl);
1162         if (result != ISC_R_SUCCESS) {
1163                 return(result);
1164         }
1165         ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1166
1167         return(ISC_R_SUCCESS);
1168 }
1169
1170 /*
1171  * The entity chosen to handle the A record for this client (either the
1172  * client or the server) SHOULD delete the A record that was added when
1173  * the lease was made to the client.
1174  *
1175  * In order to perform this delete, the updater prepares an UPDATE
1176  * query which contains two prerequisites.  The first prerequisite
1177  * asserts that the DHCID RR exists whose data is the client identity
1178  * described in Section 4.3. The second prerequisite asserts that the
1179  * data in the A RR contains the IP address of the lease that has
1180  * expired or been released.
1181  *   -- "Interaction between DHCP and DNS"
1182  *
1183  * First try has:
1184  * DHCID RR exists, and matches client identity.
1185  * A RR matches the expiring lease.
1186  * Delete appropriate A RR.
1187  */
1188
1189 static isc_result_t
1190 ddns_modify_fwd_rem1(dhcp_ddns_cb_t   *ddns_cb,
1191                      dhcp_ddns_data_t *dataspace,
1192                      dns_name_t       *pname,
1193                      dns_name_t       *uname)
1194 {
1195         isc_result_t result;
1196
1197         /* Consruct the prereq list */
1198         /* The DHCID RR exists and matches the client identity */
1199         result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
1200                                   dataspace, 
1201                                   (unsigned char *)ddns_cb->dhcid.data,
1202                                   ddns_cb->dhcid.len, 0);
1203         if (result != ISC_R_SUCCESS) {
1204                 return(result);
1205         }
1206         ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1207         dataspace++;
1208
1209         /* The A RR matches the expiring lease */
1210         result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
1211                                   dataspace, 
1212                                   (unsigned char *)ddns_cb->address.iabuf,
1213                                   ddns_cb->address.len, 0);
1214         if (result != ISC_R_SUCCESS) {
1215                 return(result);
1216         }
1217         ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1218         dataspace++;
1219
1220         /* Construct the update list */
1221         /* Delete A RRset */
1222         result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type,
1223                                   dataspace,
1224                                   (unsigned char *)ddns_cb->address.iabuf,
1225                                   ddns_cb->address.len, 0);
1226         if (result != ISC_R_SUCCESS) {
1227                 return(result);
1228         }
1229         ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1230
1231         return(ISC_R_SUCCESS);
1232 }
1233
1234 /*
1235  * If the deletion of the A succeeded, and there are no A or AAAA
1236  * records left for this domain, then we can blow away the DHCID
1237  * record as well.   We can't blow away the DHCID record above
1238  * because it's possible that more than one record has been added
1239  * to this domain name.
1240  *
1241  * Second query has:
1242  * A RR does not exist.
1243  * AAAA RR does not exist.
1244  * Delete appropriate DHCID RR.
1245  */
1246
1247 static isc_result_t
1248 ddns_modify_fwd_rem2(dhcp_ddns_cb_t   *ddns_cb,
1249                      dhcp_ddns_data_t *dataspace,
1250                      dns_name_t       *pname,
1251                      dns_name_t       *uname)
1252 {
1253         isc_result_t result;
1254
1255         /* Construct the prereq list */
1256         /* The A RR does not exist */
1257         result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_a,
1258                                   dataspace, NULL, 0, 0);
1259         if (result != ISC_R_SUCCESS) {
1260                 return(result);
1261         }
1262         ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1263         dataspace++;
1264
1265         /* The AAAA RR does not exist */
1266         result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_aaaa,
1267                                   dataspace, NULL, 0, 0);
1268         if (result != ISC_R_SUCCESS) {
1269                 return(result);
1270         }
1271         ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1272         dataspace++;
1273
1274         /* Construct the update list */
1275         /* Delete DHCID RR */
1276         result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_txt,
1277                                   dataspace,
1278                                   (unsigned char *)ddns_cb->dhcid.data,
1279                                   ddns_cb->dhcid.len, 0);
1280         if (result != ISC_R_SUCCESS) {
1281                 return(result);
1282         }
1283         ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1284
1285         return(ISC_R_SUCCESS);
1286 }
1287
1288 /*
1289  * This routine converts from the task action call into something
1290  * easier to work with.  It also handles the common case of a signature
1291  * or zone not being correct.
1292  */
1293 void ddns_interlude(isc_task_t  *taskp,
1294                     isc_event_t *eventp)
1295 {
1296         dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)eventp->ev_arg;
1297         dns_clientupdateevent_t *ddns_event = (dns_clientupdateevent_t *)eventp;
1298         isc_result_t eresult = ddns_event->result;
1299         isc_result_t result;
1300
1301         /* We've extracted the information we want from it, get rid of
1302          * the event block.*/
1303         isc_event_free(&eventp);
1304
1305 #if defined (TRACING)
1306         if (trace_record()) {
1307                 trace_ddns_input_write(ddns_cb, eresult);
1308         }
1309 #endif
1310
1311 #if defined (DEBUG_DNS_UPDATES)
1312         print_dns_status(DDNS_PRINT_INBOUND, ddns_cb, eresult);
1313 #endif
1314
1315         /* This transaction is complete, clear the value */
1316         dns_client_destroyupdatetrans(&ddns_cb->transaction);
1317
1318         /* If we cancelled or tried to cancel the operation we just
1319          * need to clean up. */
1320         if ((eresult == ISC_R_CANCELED) ||
1321             ((ddns_cb->flags & DDNS_ABORT) != 0)) {
1322                 if (ddns_cb->next_op != NULL) {
1323                         /* if necessary cleanup up next op block */
1324                         ddns_cb_free(ddns_cb->next_op, MDL);
1325                 }
1326                 ddns_cb_free(ddns_cb, MDL);
1327                 return;
1328         }
1329
1330         /* If we had a problem with our key or zone try again */
1331         if ((eresult == DNS_R_NOTAUTH) ||
1332             (eresult == DNS_R_NOTZONE)) {
1333                 int i;
1334                 /* Our zone information was questionable,
1335                  * repudiate it and try again */
1336                 repudiate_zone(&ddns_cb->zone);
1337                 ddns_cb->zone_name[0]    = 0;
1338                 ISC_LIST_INIT(ddns_cb->zone_server_list);
1339                 for (i = 0; i < DHCP_MAXNS; i++) {
1340                         ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
1341                 }
1342
1343                 if ((ddns_cb->state &
1344                      (DDNS_STATE_ADD_PTR | DDNS_STATE_REM_PTR)) != 0) {
1345                         result = ddns_modify_ptr(ddns_cb);
1346                 } else {
1347                         result = ddns_modify_fwd(ddns_cb);
1348                 }
1349
1350                 if (result != ISC_R_SUCCESS) {
1351                         /* if we couldn't redo the query toss it */
1352                         if (ddns_cb->next_op != NULL) {
1353                                 /* cleanup up next op block */
1354                                 ddns_cb_free(ddns_cb->next_op, MDL);
1355                                 }
1356                         ddns_cb_free(ddns_cb, MDL);
1357                 }
1358                 return;
1359         } else {
1360                 /* pass it along to be processed */
1361                 ddns_cb->cur_func(ddns_cb, eresult);
1362         }
1363         
1364         return;
1365 }
1366
1367 /*
1368  * This routine does the generic work for sending a ddns message to
1369  * modify the forward record (A or AAAA) and calls one of a set of
1370  * routines to build the specific message.
1371  */
1372
1373 isc_result_t
1374 ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb)
1375 {
1376         isc_result_t result;
1377         dns_tsec_t *tsec_key = NULL;
1378
1379         unsigned char *clientname;
1380         dhcp_ddns_data_t *dataspace = NULL;
1381         dns_namelist_t prereqlist, updatelist;
1382         dns_fixedname_t zname0, pname0, uname0;
1383         dns_name_t *zname = NULL, *pname, *uname;
1384
1385         isc_sockaddrlist_t *zlist = NULL;
1386
1387         /* Get a pointer to the clientname to make things easier. */
1388         clientname = (unsigned char *)ddns_cb->fwd_name.data;
1389
1390         /* Extract and validate the type of the address. */
1391         if (ddns_cb->address.len == 4) {
1392                 ddns_cb->address_type = dns_rdatatype_a;
1393         } else if (ddns_cb->address.len == 16) {
1394                 ddns_cb->address_type = dns_rdatatype_aaaa;
1395         } else {
1396                 return DHCP_R_INVALIDARG;
1397         }
1398
1399         /*
1400          * If we already have a zone use it, otherwise try to lookup the
1401          * zone in our cache.  If we find one we will have a pointer to
1402          * the zone that needs to be dereferenced when we are done with it.
1403          * If we don't find one that is okay we'll let the DNS code try and
1404          * find the information for us.
1405          */
1406
1407         if (ddns_cb->zone == NULL) {
1408                 result = find_cached_zone(ddns_cb, FIND_FORWARD);
1409         }
1410
1411         /*
1412          * If we have a zone try to get any information we need
1413          * from it - name, addresses and the key.  The address 
1414          * and key may be empty the name can't be.
1415          */
1416         if (ddns_cb->zone) {
1417                 /* Set up the zone name for use by DNS */
1418                 result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
1419                 if (result != ISC_R_SUCCESS) {
1420                         log_error("Unable to build name for zone for "
1421                                   "fwd update: %s %s",
1422                                   ddns_cb->zone_name,
1423                                   isc_result_totext(result));
1424                         goto cleanup;
1425                 }
1426
1427                 if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
1428                         /* If we have any addresses get them */
1429                         zlist = &ddns_cb->zone_server_list;
1430                 }
1431                 
1432
1433                 if (ddns_cb->zone->key != NULL) {
1434                         /*
1435                          * Not having a key is fine, having a key
1436                          * but not a tsec is odd so we warn the user.
1437                          */
1438                         /*sar*/
1439                         /* should we do the warning? */
1440                         tsec_key = ddns_cb->zone->key->tsec_key;
1441                         if (tsec_key == NULL) {
1442                                 log_error("No tsec for use with key %s",
1443                                           ddns_cb->zone->key->name);
1444                         }
1445                 }
1446         }
1447
1448         /* Set up the DNS names for the prereq and update lists */
1449         if (((result = dhcp_isc_name(clientname, &pname0, &pname))
1450              != ISC_R_SUCCESS) ||
1451             ((result = dhcp_isc_name(clientname, &uname0, &uname))
1452              != ISC_R_SUCCESS)) {
1453                 log_error("Unable to build name for fwd update: %s %s",
1454                           clientname, isc_result_totext(result));
1455                 goto cleanup;
1456         }
1457
1458         /* Allocate the various isc dns library structures we may require. */
1459         dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 4);
1460         if (dataspace == NULL) {
1461                 log_error("Unable to allocate memory for fwd update");
1462                 result = ISC_R_NOMEMORY; 
1463                 goto cleanup;
1464         }
1465
1466         ISC_LIST_INIT(prereqlist);
1467         ISC_LIST_INIT(updatelist);
1468
1469         switch(ddns_cb->state) {
1470         case DDNS_STATE_ADD_FW_NXDOMAIN:
1471                 result = ddns_modify_fwd_add1(ddns_cb, dataspace,
1472                                               pname, uname);
1473                 if (result != ISC_R_SUCCESS) {
1474                         goto cleanup;
1475                 }
1476                 ISC_LIST_APPEND(prereqlist, pname, link);
1477                 break;
1478         case DDNS_STATE_ADD_FW_YXDHCID:
1479                 result = ddns_modify_fwd_add2(ddns_cb, dataspace,
1480                                                pname, uname);
1481                 if (result != ISC_R_SUCCESS) {
1482                         goto cleanup;
1483                 }
1484
1485                 /* If we aren't doing conflict override we have entries
1486                  * in the pname list and we need to attach it to the
1487                  * prereqlist */
1488
1489                 if ((ddns_cb->flags & DDNS_CONFLICT_OVERRIDE) == 0) {
1490                         ISC_LIST_APPEND(prereqlist, pname, link);
1491                 }
1492
1493                 break;
1494         case DDNS_STATE_REM_FW_YXDHCID:
1495                 result = ddns_modify_fwd_rem1(ddns_cb, dataspace,
1496                                               pname, uname);
1497                 if (result != ISC_R_SUCCESS) {
1498                         goto cleanup;
1499                 }
1500                 ISC_LIST_APPEND(prereqlist, pname, link);
1501                 break;
1502         case DDNS_STATE_REM_FW_NXRR:
1503                 result = ddns_modify_fwd_rem2(ddns_cb, dataspace,
1504                                               pname, uname);
1505                 if (result != ISC_R_SUCCESS) {
1506                         goto cleanup;
1507                 }
1508                 ISC_LIST_APPEND(prereqlist, pname, link);
1509                 break;
1510
1511         default:
1512                 log_error("Invalid operation in ddns code.");
1513                 result = DHCP_R_INVALIDARG;
1514                 goto cleanup;
1515                 break;
1516         }
1517
1518         /*
1519          * We always have an update list but may not have a prereqlist
1520          * if we are doing conflict override.
1521          */
1522         ISC_LIST_APPEND(updatelist, uname, link);
1523
1524         /* send the message, cleanup and return the result */
1525         result = ddns_update(dhcp_gbl_ctx.dnsclient,
1526                              dns_rdataclass_in, zname,
1527                              &prereqlist, &updatelist,
1528                              zlist, tsec_key,
1529                              DNS_CLIENTRESOPT_ALLOWRUN,
1530                              dhcp_gbl_ctx.task,
1531                              ddns_interlude,
1532                              (void *)ddns_cb,
1533                              &ddns_cb->transaction);
1534         if (result == ISC_R_FAMILYNOSUPPORT) {
1535                 log_info("Unable to perform DDNS update, "
1536                          "address family not supported");
1537         }
1538
1539 #if defined (DEBUG_DNS_UPDATES)
1540         print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
1541 #endif
1542
1543  cleanup:
1544         if (dataspace != NULL) {
1545                 isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
1546                             sizeof(*dataspace) * 4);
1547         }
1548         return(result);
1549 }
1550
1551
1552 isc_result_t
1553 ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb)
1554 {
1555         isc_result_t result;
1556         dns_tsec_t *tsec_key  = NULL;
1557         unsigned char *ptrname;
1558         dhcp_ddns_data_t *dataspace = NULL;
1559         dns_namelist_t updatelist;
1560         dns_fixedname_t zname0, uname0;
1561         dns_name_t *zname = NULL, *uname;
1562         isc_sockaddrlist_t *zlist = NULL;
1563         unsigned char buf[256];
1564         int buflen;
1565
1566         /*
1567          * Try to lookup the zone in the zone cache.  As with the forward
1568          * case it's okay if we don't have one, the DNS code will try to
1569          * find something also if we succeed we will need to dereference
1570          * the zone later.  Unlike with the forward case we assume we won't
1571          * have a pre-existing zone.
1572          */
1573         result = find_cached_zone(ddns_cb, FIND_REVERSE);
1574         if ((result == ISC_R_SUCCESS) &&
1575             !(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
1576                 /* Set up the zone name for use by DNS */
1577                 result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
1578                 if (result != ISC_R_SUCCESS) {
1579                         log_error("Unable to build name for zone for "
1580                                   "fwd update: %s %s",
1581                                   ddns_cb->zone_name,
1582                                   isc_result_totext(result));
1583                         goto cleanup;
1584                 }
1585                 /* If we have any addresses get them */
1586                 if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
1587                         zlist = &ddns_cb->zone_server_list;
1588                 }
1589
1590                 /*
1591                  * If we now have a zone try to get the key, NULL is okay,
1592                  * having a key but not a tsec is odd so we warn.
1593                  */
1594                 /*sar*/
1595                 /* should we do the warning if we have a key but no tsec? */
1596                 if ((ddns_cb->zone != NULL) && (ddns_cb->zone->key != NULL)) {
1597                         tsec_key = ddns_cb->zone->key->tsec_key;
1598                         if (tsec_key == NULL) {
1599                                 log_error("No tsec for use with key %s",
1600                                           ddns_cb->zone->key->name);
1601                         }
1602                 }
1603         }
1604
1605         /* We must have a name for the update list */
1606         /* Get a pointer to the ptrname to make things easier. */
1607         ptrname = (unsigned char *)ddns_cb->rev_name.data;
1608
1609         if ((result = dhcp_isc_name(ptrname, &uname0, &uname))
1610              != ISC_R_SUCCESS) {
1611                 log_error("Unable to build name for fwd update: %s %s",
1612                           ptrname, isc_result_totext(result));
1613                 goto cleanup;
1614         }
1615
1616         /*
1617          * Allocate the various isc dns library structures we may require.
1618          * Allocating one blob avoids being halfway through the process
1619          * and being unable to allocate as well as making the free easy.
1620          */
1621         dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 2);
1622         if (dataspace == NULL) {
1623                 log_error("Unable to allocate memory for fwd update");
1624                 result = ISC_R_NOMEMORY; 
1625                 goto cleanup;
1626         }
1627
1628         ISC_LIST_INIT(updatelist);
1629
1630         /*
1631          * Construct the update list
1632          * We always delete what's currently there
1633          * Delete PTR RR.
1634          */
1635         result = make_dns_dataset(dns_rdataclass_any, dns_rdatatype_ptr,
1636                                   &dataspace[0], NULL, 0, 0);
1637         if (result != ISC_R_SUCCESS) {
1638                 goto cleanup;
1639         }
1640         ISC_LIST_APPEND(uname->list, &dataspace[0].rdataset, link);
1641
1642         /*
1643          * If we are updating the pointer we then add the new one 
1644          * Add PTR RR.
1645          */
1646         if (ddns_cb->state == DDNS_STATE_ADD_PTR) {
1647 #if 0
1648                 /*
1649                  * I've left this dead code in the file  for now in case
1650                  * we decide to try and get rid of the ns_name functions.
1651                  * sar
1652                  */
1653
1654                 /*
1655                  * Need to convert pointer into on the wire representation
1656                  * We replace the '.' characters with the lengths of the
1657                  * next name and add a length to the beginning for the first
1658                  * name.
1659                  */
1660                 if (ddns_cb->fwd_name.len == 1) {
1661                         /* the root */
1662                         buf[0] = 0;
1663                         buflen = 1;
1664                 } else {
1665                         unsigned char *cp;
1666                         buf[0] = '.';
1667                         memcpy(&buf[1], ddns_cb->fwd_name.data,
1668                                ddns_cb->fwd_name.len);
1669                         for(cp = buf + ddns_cb->fwd_name.len, buflen = 0;
1670                             cp != buf;
1671                             cp--) {
1672                                 if (*cp == '.') {
1673                                         *cp = buflen;
1674                                         buflen = 0;
1675                                 } else {
1676                                         buflen++;
1677                                 }
1678                         }
1679                         *cp = buflen;
1680                         buflen = ddns_cb->fwd_name.len + 1;
1681                 }
1682 #endif
1683                 /*
1684                  * Need to convert pointer into on the wire representation
1685                  */
1686                 if (MRns_name_pton((char *)ddns_cb->fwd_name.data,
1687                                    buf, 256) == -1) {
1688                         goto cleanup;
1689                 }
1690                 buflen = 0;
1691                 while (buf[buflen] != 0) {
1692                         buflen += buf[buflen] + 1;
1693                 }
1694                 buflen++;
1695
1696                 result = make_dns_dataset(dns_rdataclass_in,
1697                                           dns_rdatatype_ptr,
1698                                           &dataspace[1],
1699                                           buf, buflen, ddns_cb->ttl);
1700                 if (result != ISC_R_SUCCESS) {
1701                         goto cleanup;
1702                 }
1703                 ISC_LIST_APPEND(uname->list, &dataspace[1].rdataset, link);
1704         }
1705
1706         ISC_LIST_APPEND(updatelist, uname, link);
1707
1708         /*sar*/
1709         /*
1710          * for now I'll cleanup the dataset immediately, it would be
1711          * more efficient to keep it around in case the signaturure failed
1712          * and we wanted to retry it.
1713          */
1714         /* send the message, cleanup and return the result */
1715         result = ddns_update((dns_client_t *)dhcp_gbl_ctx.dnsclient,
1716                              dns_rdataclass_in, zname,
1717                              NULL, &updatelist,
1718                              zlist, tsec_key,
1719                              DNS_CLIENTRESOPT_ALLOWRUN,
1720                              dhcp_gbl_ctx.task,
1721                              ddns_interlude, (void *)ddns_cb,
1722                              &ddns_cb->transaction);
1723         if (result == ISC_R_FAMILYNOSUPPORT) {
1724                 log_info("Unable to perform DDNS update, "
1725                          "address family not supported");
1726         }
1727
1728 #if defined (DEBUG_DNS_UPDATES)
1729         print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
1730 #endif
1731
1732  cleanup:
1733         if (dataspace != NULL) {
1734                 isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
1735                             sizeof(*dataspace) * 2);
1736         }
1737         return(result);
1738 }
1739
1740 void
1741 ddns_cancel(dhcp_ddns_cb_t *ddns_cb) {
1742         ddns_cb->flags |= DDNS_ABORT;
1743         if (ddns_cb->transaction != NULL) {
1744                 dns_client_cancelupdate((dns_clientupdatetrans_t *)
1745                                         ddns_cb->transaction);
1746         }
1747         ddns_cb->lease = NULL;
1748 }
1749
1750 #endif /* NSUPDATE */
1751
1752 HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
1753                 dns_zone_reference, dns_zone_dereference, do_case_hash)