Merge from vendor branch BIND:
[dragonfly.git] / contrib / dhcp-3.0 / common / dns.c
1 /* dns.c
2
3    Domain Name Service subroutines. */
4
5 /*
6  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 2001-2003 by Internet Software Consortium
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  *
21  *   Internet Systems Consortium, Inc.
22  *   950 Charter Street
23  *   Redwood City, CA 94063
24  *   <info@isc.org>
25  *   http://www.isc.org/
26  *
27  * This software has been written for Internet Systems Consortium
28  * by Ted Lemon in cooperation with Nominum, Inc.
29  * To learn more about Internet Systems Consortium, see
30  * ``http://www.isc.org/''.  To learn more about Nominum, Inc., see
31  * ``http://www.nominum.com''.
32  */
33
34 #ifndef lint
35 static char copyright[] =
36 "$Id: dns.c,v 1.35.2.16 2004/06/17 20:54:38 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
37 #endif /* not lint */
38
39 #include "dhcpd.h"
40 #include "arpa/nameser.h"
41 #include "dst/md5.h"
42
43 /* This file is kind of a crutch for the BIND 8 nsupdate code, which has
44  * itself been cruelly hacked from its original state.   What this code
45  * does is twofold: first, it maintains a database of zone cuts that can
46  * be used to figure out which server should be contacted to update any
47  * given domain name.   Secondly, it maintains a set of named TSIG keys,
48  * and associates those keys with zones.   When an update is requested for
49  * a particular zone, the key associated with that zone is used for the
50  * update.
51  *
52  * The way this works is that you define the domain name to which an
53  * SOA corresponds, and the addresses of some primaries for that domain name:
54  *
55  *      zone FOO.COM {
56  *        primary 10.0.17.1;
57  *        secondary 10.0.22.1, 10.0.23.1;
58  *        key "FOO.COM Key";
59  *      }
60  *
61  * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name
62  * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM",
63  * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*,
64  * looks for "FOO.COM", finds it. So it
65  * attempts the update to the primary for FOO.COM.   If that times out, it
66  * tries the secondaries.   You can list multiple primaries if you have some
67  * kind of magic name server that supports that.   You shouldn't list
68  * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't
69  * support update forwarding, AFAIK).   If no TSIG key is listed, the update
70  * is attempted without TSIG.
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 or cached zone (we'll get to
80  * those in a second), then it tries to find a more specific zone.   This
81  * is done by looking first for an SOA for GAZANGA.TOPANGA.FOO.COM.   Then
82  * an SOA for TOPANGA.FOO.COM is sought.   If during this search a predefined
83  * or cached zone is found, the update fails - there's something wrong
84  * somewhere.
85  *
86  * If a more specific zone _is_ found, that zone is cached for the length of
87  * its TTL in the same database as that described above.   TSIG updates are
88  * never done for cached zones - if you want TSIG updates you _must_
89  * write a zone definition linking the key to the zone.   In cases where you
90  * know for sure what the key is but do not want to hardcode the IP addresses
91  * of the primary or secondaries, a zone declaration can be made that doesn't
92  * include any primary or secondary declarations.   When the DHCP server
93  * encounters this while hunting up a matching zone for a name, it looks up
94  * the SOA, fills in the IP addresses, and uses that record for the update.
95  * If the SOA lookup returns NXRRSET, a warning is printed and the zone is
96  * discarded, TSIG key and all.   The search for the zone then continues as if
97  * the zone record hadn't been found.   Zones without IP addresses don't
98  * match when initially hunting for a predefined or cached zone to update.
99  *
100  * When an update is attempted and no predefined or cached zone is found
101  * that matches any enclosing domain of the domain being updated, the DHCP
102  * server goes through the same process that is done when the update to a
103  * predefined or cached zone fails - starting with the most specific domain
104  * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root),
105  * it tries to look up an SOA record.   When it finds one, it creates a cached
106  * zone and attempts an update, and gives up if the update fails.
107  *
108  * TSIG keys are defined like this:
109  *
110  *      key "FOO.COM Key" {
111  *              algorithm HMAC-MD5.SIG-ALG.REG.INT;
112  *              secret <Base64>;
113  *      }
114  *
115  * <Base64> is a number expressed in base64 that represents the key.
116  * It's also permissible to use a quoted string here - this will be
117  * translated as the ASCII bytes making up the string, and will not
118  * include any NUL termination.  The key name can be any text string,
119  * and the key type must be one of the key types defined in the draft
120  * or by the IANA.  Currently only the HMAC-MD5... key type is
121  * supported.
122  */
123
124 dns_zone_hash_t *dns_zone_hash;
125
126 #if defined (NSUPDATE)
127 isc_result_t find_tsig_key (ns_tsig_key **key, const char *zname,
128                             struct dns_zone *zone)
129 {
130         isc_result_t status;
131         ns_tsig_key *tkey;
132
133         if (!zone)
134                 return ISC_R_NOTFOUND;
135
136         if (!zone -> key) {
137                 return ISC_R_KEY_UNKNOWN;
138         }
139         
140         if ((!zone -> key -> name ||
141              strlen (zone -> key -> name) > NS_MAXDNAME) ||
142             (!zone -> key -> algorithm ||
143              strlen (zone -> key -> algorithm) > NS_MAXDNAME) ||
144             (!zone -> key) ||
145             (!zone -> key -> key) ||
146             (zone -> key -> key -> len == 0)) {
147                 return ISC_R_INVALIDKEY;
148         }
149         tkey = dmalloc (sizeof *tkey, MDL);
150         if (!tkey) {
151               nomem:
152                 return ISC_R_NOMEMORY;
153         }
154         memset (tkey, 0, sizeof *tkey);
155         tkey -> data = dmalloc (zone -> key -> key -> len, MDL);
156         if (!tkey -> data) {
157                 dfree (tkey, MDL);
158                 goto nomem;
159         }
160         strcpy (tkey -> name, zone -> key -> name);
161         strcpy (tkey -> alg, zone -> key -> algorithm);
162         memcpy (tkey -> data,
163                 zone -> key -> key -> value, zone -> key -> key -> len);
164         tkey -> len = zone -> key -> key -> len;
165         *key = tkey;
166         return ISC_R_SUCCESS;
167 }
168
169 void tkey_free (ns_tsig_key **key)
170 {
171         if ((*key) -> data)
172                 dfree ((*key) -> data, MDL);
173         dfree ((*key), MDL);
174         *key = (ns_tsig_key *)0;
175 }
176 #endif
177
178 isc_result_t enter_dns_zone (struct dns_zone *zone)
179 {
180         struct dns_zone *tz = (struct dns_zone *)0;
181
182         if (dns_zone_hash) {
183                 dns_zone_hash_lookup (&tz,
184                                       dns_zone_hash, zone -> name, 0, MDL);
185                 if (tz == zone) {
186                         dns_zone_dereference (&tz, MDL);
187                         return ISC_R_SUCCESS;
188                 }
189                 if (tz) {
190                         dns_zone_hash_delete (dns_zone_hash,
191                                               zone -> name, 0, MDL);
192                         dns_zone_dereference (&tz, MDL);
193                 }
194         } else {
195                 if (!dns_zone_new_hash (&dns_zone_hash, 1, MDL))
196                         return ISC_R_NOMEMORY;
197         }
198         dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
199         return ISC_R_SUCCESS;
200 }
201
202 isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
203 {
204         struct dns_zone *tz = (struct dns_zone *)0;
205         int len;
206         char *tname = (char *)0;
207         isc_result_t status;
208
209         if (!dns_zone_hash)
210                 return ISC_R_NOTFOUND;
211
212         len = strlen (name);
213         if (name [len - 1] != '.') {
214                 tname = dmalloc ((unsigned)len + 2, MDL);
215                 if (!tname)
216                         return ISC_R_NOMEMORY;;
217                 strcpy (tname, name);
218                 tname [len] = '.';
219                 tname [len + 1] = 0;
220                 name = tname;
221         }
222         if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL))
223                 status = ISC_R_NOTFOUND;
224         else
225                 status = ISC_R_SUCCESS;
226
227         if (tname)
228                 dfree (tname, MDL);
229         return status;
230 }
231
232 int dns_zone_dereference (ptr, file, line)
233         struct dns_zone **ptr;
234         const char *file;
235         int line;
236 {
237         int i;
238         struct dns_zone *dns_zone;
239
240         if (!ptr || !*ptr) {
241                 log_error ("%s(%d): null pointer", file, line);
242 #if defined (POINTER_DEBUG)
243                 abort ();
244 #else
245                 return 0;
246 #endif
247         }
248
249         dns_zone = *ptr;
250         *ptr = (struct dns_zone *)0;
251         --dns_zone -> refcnt;
252         rc_register (file, line, ptr, dns_zone, dns_zone -> refcnt, 1, RC_MISC);
253         if (dns_zone -> refcnt > 0)
254                 return 1;
255
256         if (dns_zone -> refcnt < 0) {
257                 log_error ("%s(%d): negative refcnt!", file, line);
258 #if defined (DEBUG_RC_HISTORY)
259                 dump_rc_history (dns_zone);
260 #endif
261 #if defined (POINTER_DEBUG)
262                 abort ();
263 #else
264                 return 0;
265 #endif
266         }
267
268         if (dns_zone -> name)
269                 dfree (dns_zone -> name, file, line);
270         if (dns_zone -> key)
271                 omapi_auth_key_dereference (&dns_zone -> key, file, line);
272         if (dns_zone -> primary)
273                 option_cache_dereference (&dns_zone -> primary, file, line);
274         if (dns_zone -> secondary)
275                 option_cache_dereference (&dns_zone -> secondary, file, line);
276         dfree (dns_zone, file, line);
277         return 1;
278 }
279
280 #if defined (NSUPDATE)
281 isc_result_t find_cached_zone (const char *dname, ns_class class,
282                                char *zname, size_t zsize,
283                                struct in_addr *addrs,
284                                int naddrs, int *naddrout,
285                                struct dns_zone **zcookie)
286 {
287         isc_result_t status = ISC_R_NOTFOUND;
288         const char *np;
289         struct dns_zone *zone = (struct dns_zone *)0;
290         struct data_string nsaddrs;
291         int ix;
292
293         /* The absence of the zcookie pointer indicates that we
294            succeeded previously, but the update itself failed, meaning
295            that we shouldn't use the cached zone. */
296         if (!zcookie)
297                 return ISC_R_NOTFOUND;
298
299         /* We can't look up a null zone. */
300         if (!dname || !*dname)
301                 return ISC_R_INVALIDARG;
302
303         /* For each subzone, try to find a cached zone. */
304         for (np = dname; np; np = strchr (np, '.')) {
305                 np++;
306                 status = dns_zone_lookup (&zone, np);
307                 if (status == ISC_R_SUCCESS)
308                         break;
309         }
310
311         if (status != ISC_R_SUCCESS)
312                 return status;
313
314         /* Make sure the zone is valid. */
315         if (zone -> timeout && zone -> timeout < cur_time) {
316                 dns_zone_dereference (&zone, MDL);
317                 return ISC_R_CANCELED;
318         }
319
320         /* Make sure the zone name will fit. */
321         if (strlen (zone -> name) > zsize) {
322                 dns_zone_dereference (&zone, MDL);
323                 return ISC_R_NOSPACE;
324         }
325         strcpy (zname, zone -> name);
326
327         memset (&nsaddrs, 0, sizeof nsaddrs);
328         ix = 0;
329
330         if (zone -> primary) {
331                 if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
332                                            (struct lease *)0,
333                                            (struct client_state *)0,
334                                            (struct option_state *)0,
335                                            (struct option_state *)0,
336                                            &global_scope,
337                                            zone -> primary, MDL)) {
338                         int ip = 0;
339                         while (ix < naddrs) {
340                                 if (ip + 4 > nsaddrs.len)
341                                         break;
342                                 memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
343                                 ip += 4;
344                                 ix++;
345                         }
346                         data_string_forget (&nsaddrs, MDL);
347                 }
348         }
349         if (zone -> secondary) {
350                 if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
351                                            (struct lease *)0,
352                                            (struct client_state *)0,
353                                            (struct option_state *)0,
354                                            (struct option_state *)0,
355                                            &global_scope,
356                                            zone -> secondary, MDL)) {
357                         int ip = 0;
358                         while (ix < naddrs) {
359                                 if (ip + 4 > nsaddrs.len)
360                                         break;
361                                 memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
362                                 ip += 4;
363                                 ix++;
364                         }
365                         data_string_forget (&nsaddrs, MDL);
366                 }
367         }
368
369         /* It's not an error for zcookie to have a value here - actually,
370            it's quite likely, because res_nupdate cycles through all the
371            names in the update looking for their zones. */
372         if (!*zcookie)
373                 dns_zone_reference (zcookie, zone, MDL);
374         dns_zone_dereference (&zone, MDL);
375         if (naddrout)
376                 *naddrout = ix;
377         return ISC_R_SUCCESS;
378 }
379
380 void forget_zone (struct dns_zone **zone)
381 {
382         dns_zone_dereference (zone, MDL);
383 }
384
385 void repudiate_zone (struct dns_zone **zone)
386 {
387         /* XXX Currently we're not differentiating between a cached
388            XXX zone and a zone that's been repudiated, which means
389            XXX that if we reap cached zones, we blow away repudiated
390            XXX zones.   This isn't a big problem since we're not yet
391            XXX caching zones... :'} */
392
393         (*zone) -> timeout = cur_time - 1;
394         dns_zone_dereference (zone, MDL);
395 }
396
397 void cache_found_zone (ns_class class,
398                        char *zname, struct in_addr *addrs, int naddrs)
399 {
400         isc_result_t status = ISC_R_NOTFOUND;
401         struct dns_zone *zone = (struct dns_zone *)0;
402         struct data_string nsaddrs;
403         int ix = strlen (zname);
404
405         if (zname [ix - 1] == '.')
406                 ix = 0;
407
408         /* See if there's already such a zone. */
409         if (dns_zone_lookup (&zone, zname) == ISC_R_SUCCESS) {
410                 /* If it's not a dynamic zone, leave it alone. */
411                 if (!zone -> timeout)
412                         return;
413                 /* Address may have changed, so just blow it away. */
414                 if (zone -> primary)
415                         option_cache_dereference (&zone -> primary, MDL);
416                 if (zone -> secondary)
417                         option_cache_dereference (&zone -> secondary, MDL);
418         } else if (!dns_zone_allocate (&zone, MDL))
419                 return;
420
421         if (!zone -> name) {
422                 zone -> name =
423                         dmalloc (strlen (zname) + 1 + (ix != 0), MDL);
424                 if (!zone -> name) {
425                         dns_zone_dereference (&zone, MDL);
426                         return;
427                 }
428                 strcpy (zone -> name, zname);
429                 /* Add a trailing '.' if it was missing. */
430                 if (ix) {
431                         zone -> name [ix] = '.';
432                         zone -> name [ix + 1] = 0;
433                 }
434         }
435
436         /* XXX Need to get the lower-level code to push the actual zone
437            XXX TTL up to us. */
438         zone -> timeout = cur_time + 1800;
439         
440         if (!option_cache_allocate (&zone -> primary, MDL)) {
441                 dns_zone_dereference (&zone, MDL);
442                 return;
443         }
444         if (!buffer_allocate (&zone -> primary -> data.buffer,
445                               naddrs * sizeof (struct in_addr), MDL)) {
446                 dns_zone_dereference (&zone, MDL);
447                 return;
448         }
449         memcpy (zone -> primary -> data.buffer -> data,
450                 addrs, naddrs * sizeof *addrs);
451         zone -> primary -> data.data =
452                 &zone -> primary -> data.buffer -> data [0];
453         zone -> primary -> data.len = naddrs * sizeof *addrs;
454
455         enter_dns_zone (zone);
456 }
457
458 /* Have to use TXT records for now. */
459 #define T_DHCID T_TXT
460
461 int get_dhcid (struct data_string *id,
462                int type, const u_int8_t *data, unsigned len)
463 {
464         unsigned char buf[MD5_DIGEST_LENGTH];
465         MD5_CTX md5;
466         int i;
467
468         /* Types can only be 0..(2^16)-1. */
469         if (type < 0 || type > 65535)
470                 return 0;
471
472         /* Hexadecimal MD5 digest plus two byte type and NUL. */
473         if (!buffer_allocate (&id -> buffer,
474                               (MD5_DIGEST_LENGTH * 2) + 3, MDL))
475                 return 0;
476         id -> data = id -> buffer -> data;
477
478         /*
479          * DHCP clients and servers should use the following forms of client
480          * identification, starting with the most preferable, and finishing
481          * with the least preferable.  If the client does not send any of these
482          * forms of identification, the DHCP/DDNS interaction is not defined by
483          * this specification.  The most preferable form of identification is
484          * the Globally Unique Identifier Option [TBD].  Next is the DHCP
485          * Client Identifier option.  Last is the client's link-layer address,
486          * as conveyed in its DHCPREQUEST message.  Implementors should note
487          * that the link-layer address cannot be used if there are no
488          * significant bytes in the chaddr field of the DHCP client's request,
489          * because this does not constitute a unique identifier.
490          *   -- "Interaction between DHCP and DNS"
491          *      <draft-ietf-dhc-dhcp-dns-12.txt>
492          *      M. Stapp, Y. Rekhter
493          */
494
495         /* Put the type in the first two bytes. */
496         id -> buffer -> data [0] = "0123456789abcdef" [type >> 4];
497         id -> buffer -> data [1] = "0123456789abcdef" [type % 15];
498
499         /* Mash together an MD5 hash of the identifier. */
500         MD5_Init (&md5);
501         MD5_Update (&md5, data, len);
502         MD5_Final (buf, &md5);
503
504         /* Convert into ASCII. */
505         for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
506                 id -> buffer -> data [i * 2 + 2] =
507                         "0123456789abcdef" [(buf [i] >> 4) & 0xf];
508                 id -> buffer -> data [i * 2 + 3] =
509                         "0123456789abcdef" [buf [i] & 0xf];
510         }
511         id -> len = MD5_DIGEST_LENGTH * 2 + 2;
512         id -> buffer -> data [id -> len] = 0;
513         id -> terminated = 1;
514
515         return 1;
516 }
517
518 /* Now for the DDNS update code that is shared between client and
519    server... */
520
521 isc_result_t ddns_update_a (struct data_string *ddns_fwd_name,
522                             struct iaddr ddns_addr,
523                             struct data_string *ddns_dhcid,
524                             unsigned long ttl, int rrsetp)
525 {
526         ns_updque updqueue;
527         ns_updrec *updrec;
528         isc_result_t result;
529         char ddns_address [16];
530
531         if (ddns_addr.len != 4)
532                 return ISC_R_INVALIDARG;
533
534         /* %Audit% Cannot exceed 16 bytes. %2004.06.17,Safe% */
535         sprintf (ddns_address, "%u.%u.%u.%u",
536                   ddns_addr.iabuf[0], ddns_addr.iabuf[1],
537                   ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
538
539         /*
540          * When a DHCP client or server intends to update an A RR, it first
541          * prepares a DNS UPDATE query which includes as a prerequisite the
542          * assertion that the name does not exist.  The update section of the
543          * query attempts to add the new name and its IP address mapping (an A
544          * RR), and the DHCID RR with its unique client-identity.
545          *   -- "Interaction between DHCP and DNS"
546          */
547
548         ISC_LIST_INIT (updqueue);
549
550         /*
551          * A RR does not exist.
552          */
553         updrec = minires_mkupdrec (S_PREREQ,
554                                    (const char *)ddns_fwd_name -> data,
555                                    C_IN, T_A, 0);
556         if (!updrec) {
557                 result = ISC_R_NOMEMORY;
558                 goto error;
559         }
560
561         updrec -> r_data = (unsigned char *)0;
562         updrec -> r_size = 0;
563         updrec -> r_opcode = rrsetp ? NXRRSET : NXDOMAIN;
564
565         ISC_LIST_APPEND (updqueue, updrec, r_link);
566
567
568         /*
569          * Add A RR.
570          */
571         updrec = minires_mkupdrec (S_UPDATE,
572                                    (const char *)ddns_fwd_name -> data,
573                                    C_IN, T_A, ttl);
574         if (!updrec) {
575                 result = ISC_R_NOMEMORY;
576                 goto error;
577         }
578
579         updrec -> r_data = (unsigned char *)ddns_address;
580         updrec -> r_size = strlen (ddns_address);
581         updrec -> r_opcode = ADD;
582
583         ISC_LIST_APPEND (updqueue, updrec, r_link);
584
585
586         /*
587          * Add DHCID RR.
588          */
589         updrec = minires_mkupdrec (S_UPDATE,
590                                    (const char *)ddns_fwd_name -> data,
591                                    C_IN, T_DHCID, ttl);
592         if (!updrec) {
593                 result = ISC_R_NOMEMORY;
594                 goto error;
595         }
596
597         updrec -> r_data = ddns_dhcid -> data;
598         updrec -> r_size = ddns_dhcid -> len;
599         updrec -> r_opcode = ADD;
600
601         ISC_LIST_APPEND (updqueue, updrec, r_link);
602
603
604         /*
605          * Attempt to perform the update.
606          */
607         result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
608
609 #ifdef DEBUG_DNS_UPDATES
610         print_dns_status ((int)result, &updqueue);
611 #endif
612
613         /*
614          * If this update operation succeeds, the updater can conclude that it
615          * has added a new name whose only RRs are the A and DHCID RR records.
616          * The A RR update is now complete (and a client updater is finished,
617          * while a server might proceed to perform a PTR RR update).
618          *   -- "Interaction between DHCP and DNS"
619          */
620
621         if (result == ISC_R_SUCCESS) {
622                 log_info ("Added new forward map from %.*s to %s",
623                           (int)ddns_fwd_name -> len,
624                           (const char *)ddns_fwd_name -> data, ddns_address);
625                 goto error;
626         }
627
628
629         /*
630          * If the first update operation fails with YXDOMAIN, the updater can
631          * conclude that the intended name is in use.  The updater then
632          * attempts to confirm that the DNS name is not being used by some
633          * other host. The updater prepares a second UPDATE query in which the
634          * prerequisite is that the desired name has attached to it a DHCID RR
635          * whose contents match the client identity.  The update section of
636          * this query deletes the existing A records on the name, and adds the
637          * A record that matches the DHCP binding and the DHCID RR with the
638          * client identity.
639          *   -- "Interaction between DHCP and DNS"
640          */
641
642         if (result != (rrsetp ? ISC_R_YXRRSET : ISC_R_YXDOMAIN)) {
643                 log_error ("Unable to add forward map from %.*s to %s: %s",
644                            (int)ddns_fwd_name -> len,
645                            (const char *)ddns_fwd_name -> data, ddns_address,
646                            isc_result_totext (result));
647                 goto error;
648         }
649
650         while (!ISC_LIST_EMPTY (updqueue)) {
651                 updrec = ISC_LIST_HEAD (updqueue);
652                 ISC_LIST_UNLINK (updqueue, updrec, r_link);
653                 minires_freeupdrec (updrec);
654         }
655
656         /*
657          * DHCID RR exists, and matches client identity.
658          */
659         updrec = minires_mkupdrec (S_PREREQ,
660                                    (const char *)ddns_fwd_name -> data,
661                                    C_IN, T_DHCID, 0);
662         if (!updrec) {
663                 result = ISC_R_NOMEMORY;
664                 goto error;
665         }
666
667         updrec -> r_data = ddns_dhcid -> data;
668         updrec -> r_size = ddns_dhcid -> len;
669         updrec -> r_opcode = YXRRSET;
670
671         ISC_LIST_APPEND (updqueue, updrec, r_link);
672
673
674         /*
675          * Delete A RRset.
676          */
677         updrec = minires_mkupdrec (S_UPDATE,
678                                    (const char *)ddns_fwd_name -> data,
679                                    C_IN, T_A, 0);
680         if (!updrec) {
681                 result = ISC_R_NOMEMORY;
682                 goto error;
683         }
684
685         updrec -> r_data = (unsigned char *)0;
686         updrec -> r_size = 0;
687         updrec -> r_opcode = DELETE;
688
689         ISC_LIST_APPEND (updqueue, updrec, r_link);
690
691
692         /*
693          * Add A RR.
694          */
695         updrec = minires_mkupdrec (S_UPDATE,
696                                    (const char *)ddns_fwd_name -> data,
697                                    C_IN, T_A, ttl);
698         if (!updrec) {
699                 result = ISC_R_NOMEMORY;
700                 goto error;
701         }
702
703         updrec -> r_data = (unsigned char *)ddns_address;
704         updrec -> r_size = strlen (ddns_address);
705         updrec -> r_opcode = ADD;
706
707         ISC_LIST_APPEND (updqueue, updrec, r_link);
708
709
710         /*
711          * Attempt to perform the update.
712          */
713         result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
714
715         if (result != ISC_R_SUCCESS) {
716                 if (result == YXRRSET || result == YXDOMAIN ||
717                     result == NXRRSET || result == NXDOMAIN)
718                         log_error ("Forward map from %.*s to %s already in use",
719                                    (int)ddns_fwd_name -> len,
720                                    (const char *)ddns_fwd_name -> data,
721                                    ddns_address);
722                 else
723                         log_error ("Can't update forward map %.*s to %s: %s",
724                                    (int)ddns_fwd_name -> len,
725                                    (const char *)ddns_fwd_name -> data,
726                                    ddns_address, isc_result_totext (result));
727
728         } else {
729                 log_info ("Added new forward map from %.*s to %s",
730                           (int)ddns_fwd_name -> len,
731                           (const char *)ddns_fwd_name -> data, ddns_address);
732         }
733 #if defined (DEBUG_DNS_UPDATES)
734         print_dns_status ((int)result, &updqueue);
735 #endif
736
737         /*
738          * If this query succeeds, the updater can conclude that the current
739          * client was the last client associated with the domain name, and that
740          * the name now contains the updated A RR. The A RR update is now
741          * complete (and a client updater is finished, while a server would
742          * then proceed to perform a PTR RR update).
743          *   -- "Interaction between DHCP and DNS"
744          */
745
746         /*
747          * If the second query fails with NXRRSET, the updater must conclude
748          * that the client's desired name is in use by another host.  At this
749          * juncture, the updater can decide (based on some administrative
750          * configuration outside of the scope of this document) whether to let
751          * the existing owner of the name keep that name, and to (possibly)
752          * perform some name disambiguation operation on behalf of the current
753          * client, or to replace the RRs on the name with RRs that represent
754          * the current client. If the configured policy allows replacement of
755          * existing records, the updater submits a query that deletes the
756          * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
757          * represent the IP address and client-identity of the new client.
758          *   -- "Interaction between DHCP and DNS"
759          */
760
761   error:
762         while (!ISC_LIST_EMPTY (updqueue)) {
763                 updrec = ISC_LIST_HEAD (updqueue);
764                 ISC_LIST_UNLINK (updqueue, updrec, r_link);
765                 minires_freeupdrec (updrec);
766         }
767
768         return result;
769 }
770
771 isc_result_t ddns_remove_a (struct data_string *ddns_fwd_name,
772                             struct iaddr ddns_addr,
773                             struct data_string *ddns_dhcid)
774 {
775         ns_updque updqueue;
776         ns_updrec *updrec;
777         isc_result_t result = SERVFAIL;
778         char ddns_address [16];
779
780         if (ddns_addr.len != 4)
781                 return ISC_R_INVALIDARG;
782
783         /* %Audit% Cannot exceed 16 bytes. %2004.06.17,Safe% */
784         sprintf (ddns_address, "%u.%u.%u.%u",
785                   ddns_addr.iabuf[0], ddns_addr.iabuf[1],
786                   ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
787
788         /*
789          * The entity chosen to handle the A record for this client (either the
790          * client or the server) SHOULD delete the A record that was added when
791          * the lease was made to the client.
792          *
793          * In order to perform this delete, the updater prepares an UPDATE
794          * query which contains two prerequisites.  The first prerequisite
795          * asserts that the DHCID RR exists whose data is the client identity
796          * described in Section 4.3. The second prerequisite asserts that the
797          * data in the A RR contains the IP address of the lease that has
798          * expired or been released.
799          *   -- "Interaction between DHCP and DNS"
800          */
801
802         ISC_LIST_INIT (updqueue);
803
804         /*
805          * DHCID RR exists, and matches client identity.
806          */
807         updrec = minires_mkupdrec (S_PREREQ,
808                                    (const char *)ddns_fwd_name -> data,
809                                    C_IN, T_DHCID,0);
810         if (!updrec) {
811                 result = ISC_R_NOMEMORY;
812                 goto error;
813         }
814
815         updrec -> r_data = ddns_dhcid -> data;
816         updrec -> r_size = ddns_dhcid -> len;
817         updrec -> r_opcode = YXRRSET;
818
819         ISC_LIST_APPEND (updqueue, updrec, r_link);
820
821
822         /*
823          * A RR matches the expiring lease.
824          */
825         updrec = minires_mkupdrec (S_PREREQ,
826                                    (const char *)ddns_fwd_name -> data,
827                                    C_IN, T_A, 0);
828         if (!updrec) {
829                 result = ISC_R_NOMEMORY;
830                 goto error;
831         }
832
833         updrec -> r_data = (unsigned char *)ddns_address;
834         updrec -> r_size = strlen (ddns_address);
835         updrec -> r_opcode = YXRRSET;
836
837         ISC_LIST_APPEND (updqueue, updrec, r_link);
838
839
840         /*
841          * Delete appropriate A RR.
842          */
843         updrec = minires_mkupdrec (S_UPDATE,
844                                    (const char *)ddns_fwd_name -> data,
845                                    C_IN, T_A, 0);
846         if (!updrec) {
847                 result = ISC_R_NOMEMORY;
848                 goto error;
849         }
850
851         updrec -> r_data = (unsigned char *)ddns_address;
852         updrec -> r_size = strlen (ddns_address);
853         updrec -> r_opcode = DELETE;
854
855         ISC_LIST_APPEND (updqueue, updrec, r_link);
856
857         /*
858          * Attempt to perform the update.
859          */
860         result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
861         print_dns_status ((int)result, &updqueue);
862
863         /*
864          * If the query fails, the updater MUST NOT delete the DNS name.  It
865          * may be that the host whose lease on the server has expired has moved
866          * to another network and obtained a lease from a different server,
867          * which has caused the client's A RR to be replaced. It may also be
868          * that some other client has been configured with a name that matches
869          * the name of the DHCP client, and the policy was that the last client
870          * to specify the name would get the name.  In this case, the DHCID RR
871          * will no longer match the updater's notion of the client-identity of
872          * the host pointed to by the DNS name.
873          *   -- "Interaction between DHCP and DNS"
874          */
875
876         if (result != ISC_R_SUCCESS) {
877                 /* If the rrset isn't there, we didn't need to do the
878                    delete, which is success. */
879                 if (result == ISC_R_NXRRSET || result == ISC_R_NXDOMAIN)
880                         result = ISC_R_SUCCESS; 
881                 goto error;
882         }
883
884         while (!ISC_LIST_EMPTY (updqueue)) {
885                 updrec = ISC_LIST_HEAD (updqueue);
886                 ISC_LIST_UNLINK (updqueue, updrec, r_link);
887                 minires_freeupdrec (updrec);
888         }
889
890         /* If the deletion of the A succeeded, and there are no A records
891            left for this domain, then we can blow away the DHCID record
892            as well.   We can't blow away the DHCID record above because
893            it's possible that more than one A has been added to this
894            domain name. */
895         ISC_LIST_INIT (updqueue);
896
897         /*
898          * A RR does not exist.
899          */
900         updrec = minires_mkupdrec (S_PREREQ,
901                                    (const char *)ddns_fwd_name -> data,
902                                    C_IN, T_A, 0);
903         if (!updrec) {
904                 result = ISC_R_NOMEMORY;
905                 goto error;
906         }
907
908         updrec -> r_data = (unsigned char *)0;
909         updrec -> r_size = 0;
910         updrec -> r_opcode = NXRRSET;
911
912         ISC_LIST_APPEND (updqueue, updrec, r_link);
913
914         /*
915          * Delete appropriate DHCID RR.
916          */
917         updrec = minires_mkupdrec (S_UPDATE,
918                                   (const char *)ddns_fwd_name -> data,
919                                    C_IN, T_DHCID, 0);
920         if (!updrec) {
921                 result = ISC_R_NOMEMORY;
922                 goto error;
923         }
924
925         updrec -> r_data = ddns_dhcid -> data;
926         updrec -> r_size = ddns_dhcid -> len;
927         updrec -> r_opcode = DELETE;
928
929         ISC_LIST_APPEND (updqueue, updrec, r_link);
930
931         /*
932          * Attempt to perform the update.
933          */
934         result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
935         print_dns_status ((int)result, &updqueue);
936
937         /* Fall through. */
938   error:
939
940         while (!ISC_LIST_EMPTY (updqueue)) {
941                 updrec = ISC_LIST_HEAD (updqueue);
942                 ISC_LIST_UNLINK (updqueue, updrec, r_link);
943                 minires_freeupdrec (updrec);
944         }
945
946         return result;
947 }
948
949
950 #endif /* NSUPDATE */
951
952 HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
953                 dns_zone_reference, dns_zone_dereference)