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