Fix a number of core kernel issues related to HAMMER operation.
[dragonfly.git] / contrib / dhcp-3.0 / common / dns.c
blobe80df9ab7caa91d703a882fb5e4608f06d476135
1 /* dns.c
3 Domain Name Service subroutines. */
5 /*
6 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 2001-2003 by Internet Software Consortium
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.
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.
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * http://www.isc.org/
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''.
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 */
39 #include "dhcpd.h"
40 #include "arpa/nameser.h"
41 #include "dst/md5.h"
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.
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:
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 * }
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.
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.
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.
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.
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.
108 * TSIG keys are defined like this:
110 * key "FOO.COM Key" {
111 * algorithm HMAC-MD5.SIG-ALG.REG.INT;
112 * secret <Base64>;
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.
124 dns_zone_hash_t *dns_zone_hash;
126 #if defined (NSUPDATE)
127 isc_result_t find_tsig_key (ns_tsig_key **key, const char *zname,
128 struct dns_zone *zone)
130 isc_result_t status;
131 ns_tsig_key *tkey;
133 if (!zone)
134 return ISC_R_NOTFOUND;
136 if (!zone -> key) {
137 return ISC_R_KEY_UNKNOWN;
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;
149 tkey = dmalloc (sizeof *tkey, MDL);
150 if (!tkey) {
151 nomem:
152 return ISC_R_NOMEMORY;
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;
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;
169 void tkey_free (ns_tsig_key **key)
171 if ((*key) -> data)
172 dfree ((*key) -> data, MDL);
173 dfree ((*key), MDL);
174 *key = (ns_tsig_key *)0;
176 #endif
178 isc_result_t enter_dns_zone (struct dns_zone *zone)
180 struct dns_zone *tz = (struct dns_zone *)0;
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;
189 if (tz) {
190 dns_zone_hash_delete (dns_zone_hash,
191 zone -> name, 0, MDL);
192 dns_zone_dereference (&tz, MDL);
194 } else {
195 if (!dns_zone_new_hash (&dns_zone_hash, 1, MDL))
196 return ISC_R_NOMEMORY;
198 dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
199 return ISC_R_SUCCESS;
202 isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
204 struct dns_zone *tz = (struct dns_zone *)0;
205 int len;
206 char *tname = (char *)0;
207 isc_result_t status;
209 if (!dns_zone_hash)
210 return ISC_R_NOTFOUND;
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;
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;
227 if (tname)
228 dfree (tname, MDL);
229 return status;
232 int dns_zone_dereference (ptr, file, line)
233 struct dns_zone **ptr;
234 const char *file;
235 int line;
237 int i;
238 struct dns_zone *dns_zone;
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
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;
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
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;
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)
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;
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;
299 /* We can't look up a null zone. */
300 if (!dname || !*dname)
301 return ISC_R_INVALIDARG;
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;
311 if (status != ISC_R_SUCCESS)
312 return status;
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;
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;
325 strcpy (zname, zone -> name);
327 memset (&nsaddrs, 0, sizeof nsaddrs);
328 ix = 0;
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++;
346 data_string_forget (&nsaddrs, MDL);
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++;
365 data_string_forget (&nsaddrs, MDL);
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;
380 void forget_zone (struct dns_zone **zone)
382 dns_zone_dereference (zone, MDL);
385 void repudiate_zone (struct dns_zone **zone)
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... :'} */
393 (*zone) -> timeout = cur_time - 1;
394 dns_zone_dereference (zone, MDL);
397 void cache_found_zone (ns_class class,
398 char *zname, struct in_addr *addrs, int naddrs)
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);
405 if (zname [ix - 1] == '.')
406 ix = 0;
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;
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;
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;
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;
440 if (!option_cache_allocate (&zone -> primary, MDL)) {
441 dns_zone_dereference (&zone, MDL);
442 return;
444 if (!buffer_allocate (&zone -> primary -> data.buffer,
445 naddrs * sizeof (struct in_addr), MDL)) {
446 dns_zone_dereference (&zone, MDL);
447 return;
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;
455 enter_dns_zone (zone);
458 /* Have to use TXT records for now. */
459 #define T_DHCID T_TXT
461 int get_dhcid (struct data_string *id,
462 int type, const u_int8_t *data, unsigned len)
464 unsigned char buf[MD5_DIGEST_LENGTH];
465 MD5_CTX md5;
466 int i;
468 /* Types can only be 0..(2^16)-1. */
469 if (type < 0 || type > 65535)
470 return 0;
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;
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
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];
499 /* Mash together an MD5 hash of the identifier. */
500 MD5_Init (&md5);
501 MD5_Update (&md5, data, len);
502 MD5_Final (buf, &md5);
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];
511 id -> len = MD5_DIGEST_LENGTH * 2 + 2;
512 id -> buffer -> data [id -> len] = 0;
513 id -> terminated = 1;
515 return 1;
518 /* Now for the DDNS update code that is shared between client and
519 server... */
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)
526 ns_updque updqueue;
527 ns_updrec *updrec;
528 isc_result_t result;
529 char ddns_address [16];
531 if (ddns_addr.len != 4)
532 return ISC_R_INVALIDARG;
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]);
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"
548 ISC_LIST_INIT (updqueue);
551 * A RR does not exist.
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;
561 updrec -> r_data = (unsigned char *)0;
562 updrec -> r_size = 0;
563 updrec -> r_opcode = rrsetp ? NXRRSET : NXDOMAIN;
565 ISC_LIST_APPEND (updqueue, updrec, r_link);
569 * Add A RR.
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;
579 updrec -> r_data = (unsigned char *)ddns_address;
580 updrec -> r_size = strlen (ddns_address);
581 updrec -> r_opcode = ADD;
583 ISC_LIST_APPEND (updqueue, updrec, r_link);
587 * Add DHCID RR.
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;
597 updrec -> r_data = ddns_dhcid -> data;
598 updrec -> r_size = ddns_dhcid -> len;
599 updrec -> r_opcode = ADD;
601 ISC_LIST_APPEND (updqueue, updrec, r_link);
605 * Attempt to perform the update.
607 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
609 #ifdef DEBUG_DNS_UPDATES
610 print_dns_status ((int)result, &updqueue);
611 #endif
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"
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;
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"
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;
650 while (!ISC_LIST_EMPTY (updqueue)) {
651 updrec = ISC_LIST_HEAD (updqueue);
652 ISC_LIST_UNLINK (updqueue, updrec, r_link);
653 minires_freeupdrec (updrec);
657 * DHCID RR exists, and matches client identity.
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;
667 updrec -> r_data = ddns_dhcid -> data;
668 updrec -> r_size = ddns_dhcid -> len;
669 updrec -> r_opcode = YXRRSET;
671 ISC_LIST_APPEND (updqueue, updrec, r_link);
675 * Delete A RRset.
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;
685 updrec -> r_data = (unsigned char *)0;
686 updrec -> r_size = 0;
687 updrec -> r_opcode = DELETE;
689 ISC_LIST_APPEND (updqueue, updrec, r_link);
693 * Add A RR.
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;
703 updrec -> r_data = (unsigned char *)ddns_address;
704 updrec -> r_size = strlen (ddns_address);
705 updrec -> r_opcode = ADD;
707 ISC_LIST_APPEND (updqueue, updrec, r_link);
711 * Attempt to perform the update.
713 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
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));
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);
733 #if defined (DEBUG_DNS_UPDATES)
734 print_dns_status ((int)result, &updqueue);
735 #endif
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"
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"
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);
768 return result;
771 isc_result_t ddns_remove_a (struct data_string *ddns_fwd_name,
772 struct iaddr ddns_addr,
773 struct data_string *ddns_dhcid)
775 ns_updque updqueue;
776 ns_updrec *updrec;
777 isc_result_t result = SERVFAIL;
778 char ddns_address [16];
780 if (ddns_addr.len != 4)
781 return ISC_R_INVALIDARG;
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]);
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.
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"
802 ISC_LIST_INIT (updqueue);
805 * DHCID RR exists, and matches client identity.
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;
815 updrec -> r_data = ddns_dhcid -> data;
816 updrec -> r_size = ddns_dhcid -> len;
817 updrec -> r_opcode = YXRRSET;
819 ISC_LIST_APPEND (updqueue, updrec, r_link);
823 * A RR matches the expiring lease.
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;
833 updrec -> r_data = (unsigned char *)ddns_address;
834 updrec -> r_size = strlen (ddns_address);
835 updrec -> r_opcode = YXRRSET;
837 ISC_LIST_APPEND (updqueue, updrec, r_link);
841 * Delete appropriate A RR.
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;
851 updrec -> r_data = (unsigned char *)ddns_address;
852 updrec -> r_size = strlen (ddns_address);
853 updrec -> r_opcode = DELETE;
855 ISC_LIST_APPEND (updqueue, updrec, r_link);
858 * Attempt to perform the update.
860 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
861 print_dns_status ((int)result, &updqueue);
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"
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;
884 while (!ISC_LIST_EMPTY (updqueue)) {
885 updrec = ISC_LIST_HEAD (updqueue);
886 ISC_LIST_UNLINK (updqueue, updrec, r_link);
887 minires_freeupdrec (updrec);
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);
898 * A RR does not exist.
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;
908 updrec -> r_data = (unsigned char *)0;
909 updrec -> r_size = 0;
910 updrec -> r_opcode = NXRRSET;
912 ISC_LIST_APPEND (updqueue, updrec, r_link);
915 * Delete appropriate DHCID RR.
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;
925 updrec -> r_data = ddns_dhcid -> data;
926 updrec -> r_size = ddns_dhcid -> len;
927 updrec -> r_opcode = DELETE;
929 ISC_LIST_APPEND (updqueue, updrec, r_link);
932 * Attempt to perform the update.
934 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
935 print_dns_status ((int)result, &updqueue);
937 /* Fall through. */
938 error:
940 while (!ISC_LIST_EMPTY (updqueue)) {
941 updrec = ISC_LIST_HEAD (updqueue);
942 ISC_LIST_UNLINK (updqueue, updrec, r_link);
943 minires_freeupdrec (updrec);
946 return result;
950 #endif /* NSUPDATE */
952 HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
953 dns_zone_reference, dns_zone_dereference)