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