Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / dhcp / common / dns.c
blob0c77f47816dc3c0660271985b1a45247db2011d0
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.6 2005/08/11 17:13:21 drochner 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 ns_tsig_key *tkey;
132 if (!zone)
133 return ISC_R_NOTFOUND;
135 if (!zone -> key) {
136 return ISC_R_KEY_UNKNOWN;
139 if ((!zone -> key -> name ||
140 strlen (zone -> key -> name) > NS_MAXDNAME) ||
141 (!zone -> key -> algorithm ||
142 strlen (zone -> key -> algorithm) > NS_MAXDNAME) ||
143 (!zone -> key) ||
144 (!zone -> key -> key) ||
145 (zone -> key -> key -> len == 0)) {
146 return ISC_R_INVALIDKEY;
148 tkey = dmalloc (sizeof *tkey, MDL);
149 if (!tkey) {
150 nomem:
151 return ISC_R_NOMEMORY;
153 memset (tkey, 0, sizeof *tkey);
154 tkey -> data = dmalloc (zone -> key -> key -> len, MDL);
155 if (!tkey -> data) {
156 dfree (tkey, MDL);
157 goto nomem;
159 strcpy (tkey -> name, zone -> key -> name);
160 strcpy (tkey -> alg, zone -> key -> algorithm);
161 memcpy (tkey -> data,
162 zone -> key -> key -> value, zone -> key -> key -> len);
163 tkey -> len = zone -> key -> key -> len;
164 *key = tkey;
165 return ISC_R_SUCCESS;
168 void tkey_free (ns_tsig_key **key)
170 if ((*key) -> data)
171 dfree ((*key) -> data, MDL);
172 dfree ((*key), MDL);
173 *key = (ns_tsig_key *)0;
175 #endif
177 isc_result_t enter_dns_zone (struct dns_zone *zone)
179 struct dns_zone *tz = (struct dns_zone *)0;
181 if (dns_zone_hash) {
182 dns_zone_hash_lookup (&tz,
183 dns_zone_hash, zone -> name, 0, MDL);
184 if (tz == zone) {
185 dns_zone_dereference (&tz, MDL);
186 return ISC_R_SUCCESS;
188 if (tz) {
189 dns_zone_hash_delete (dns_zone_hash,
190 zone -> name, 0, MDL);
191 dns_zone_dereference (&tz, MDL);
193 } else {
194 if (!dns_zone_new_hash (&dns_zone_hash, 1, MDL))
195 return ISC_R_NOMEMORY;
197 dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
198 return ISC_R_SUCCESS;
201 isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
203 int len;
204 char *tname = (char *)0;
205 isc_result_t status;
207 if (!dns_zone_hash)
208 return ISC_R_NOTFOUND;
210 len = strlen (name);
211 if (len == 0)
212 return ISC_R_NOTFOUND;
214 if (name [len - 1] != '.') {
215 tname = dmalloc ((unsigned)len + 2, MDL);
216 if (!tname)
217 return ISC_R_NOMEMORY;;
218 strcpy (tname, name);
219 tname [len] = '.';
220 tname [len + 1] = 0;
221 name = tname;
223 if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL))
224 status = ISC_R_NOTFOUND;
225 else
226 status = ISC_R_SUCCESS;
228 if (tname)
229 dfree (tname, MDL);
230 return status;
233 int dns_zone_dereference (ptr, file, line)
234 struct dns_zone **ptr;
235 const char *file;
236 int line;
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 !defined (SMALL)
271 if (dns_zone -> key)
272 omapi_auth_key_dereference (&dns_zone -> key, file, line);
273 #endif
274 if (dns_zone -> primary)
275 option_cache_dereference (&dns_zone -> primary, file, line);
276 if (dns_zone -> secondary)
277 option_cache_dereference (&dns_zone -> secondary, file, line);
278 dfree (dns_zone, file, line);
279 return 1;
282 #if defined (NSUPDATE)
283 isc_result_t find_cached_zone (const char *dname, ns_class class,
284 char *zname, size_t zsize,
285 struct in_addr *addrs,
286 int naddrs, int *naddrout,
287 struct dns_zone **zcookie)
289 isc_result_t status = ISC_R_NOTFOUND;
290 const char *np;
291 struct dns_zone *zone = (struct dns_zone *)0;
292 struct data_string nsaddrs;
293 int ix;
295 /* The absence of the zcookie pointer indicates that we
296 succeeded previously, but the update itself failed, meaning
297 that we shouldn't use the cached zone. */
298 if (!zcookie)
299 return ISC_R_NOTFOUND;
301 /* We can't look up a null zone. */
302 if (!dname || !*dname)
303 return ISC_R_INVALIDARG;
305 /* For each subzone, try to find a cached zone. */
306 for (np = dname; np; np = strchr (np, '.')) {
307 np++;
308 status = dns_zone_lookup (&zone, np);
309 if (status == ISC_R_SUCCESS)
310 break;
313 if (status != ISC_R_SUCCESS)
314 return status;
316 /* Make sure the zone is valid. */
317 if (zone -> timeout && zone -> timeout < cur_time) {
318 dns_zone_dereference (&zone, MDL);
319 return ISC_R_CANCELED;
322 /* Make sure the zone name will fit. */
323 if (strlen (zone -> name) > zsize) {
324 dns_zone_dereference (&zone, MDL);
325 return ISC_R_NOSPACE;
327 strcpy (zname, zone -> name);
329 memset (&nsaddrs, 0, sizeof nsaddrs);
330 ix = 0;
332 if (zone -> primary) {
333 if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
334 (struct lease *)0,
335 (struct client_state *)0,
336 (struct option_state *)0,
337 (struct option_state *)0,
338 &global_scope,
339 zone -> primary, MDL)) {
340 int ip = 0;
341 while (ix < naddrs) {
342 if (ip + 4 > nsaddrs.len)
343 break;
344 memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
345 ip += 4;
346 ix++;
348 data_string_forget (&nsaddrs, MDL);
351 if (zone -> secondary) {
352 if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
353 (struct lease *)0,
354 (struct client_state *)0,
355 (struct option_state *)0,
356 (struct option_state *)0,
357 &global_scope,
358 zone -> secondary, MDL)) {
359 int ip = 0;
360 while (ix < naddrs) {
361 if (ip + 4 > nsaddrs.len)
362 break;
363 memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
364 ip += 4;
365 ix++;
367 data_string_forget (&nsaddrs, MDL);
371 /* It's not an error for zcookie to have a value here - actually,
372 it's quite likely, because res_nupdate cycles through all the
373 names in the update looking for their zones. */
374 if (!*zcookie)
375 dns_zone_reference (zcookie, zone, MDL);
376 dns_zone_dereference (&zone, MDL);
377 if (naddrout)
378 *naddrout = ix;
379 return ISC_R_SUCCESS;
382 void forget_zone (struct dns_zone **zone)
384 dns_zone_dereference (zone, MDL);
387 void repudiate_zone (struct dns_zone **zone)
389 /* XXX Currently we're not differentiating between a cached
390 XXX zone and a zone that's been repudiated, which means
391 XXX that if we reap cached zones, we blow away repudiated
392 XXX zones. This isn't a big problem since we're not yet
393 XXX caching zones... :'} */
395 (*zone) -> timeout = cur_time - 1;
396 dns_zone_dereference (zone, MDL);
399 void cache_found_zone (ns_class class,
400 char *zname, struct in_addr *addrs, int naddrs)
402 struct dns_zone *zone = (struct dns_zone *)0;
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)