]> git.ipfire.org Git - thirdparty/dhcp.git/blob - common/dns.c
Add some debugging output for use with the DDNS code. [ISC-Bugs 20916]
[thirdparty/dhcp.git] / common / dns.c
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 * The original software was written for Internet Systems Consortium
29 * by Ted Lemon it has since been extensively modified to use the
30 * asynchronous DNS routines.
31 */
32
33 #include "dhcpd.h"
34 #include "arpa/nameser.h"
35 #include <isc/md5.h>
36
37 #include <dns/result.h>
38
39 /*
40 * This file contains code to connect the DHCP code to the libdns modules.
41 * As part of that function 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. Included in the zone information may be a pointer
44 * to a key in which case that key is used for the update. If no zone
45 * is found then the DNS code determines the zone on its own.
46 *
47 * The way this works is that you define the domain name to which an
48 * SOA corresponds, and the addresses of some primaries for that domain name:
49 *
50 * zone FOO.COM {
51 * primary 10.0.17.1;
52 * secondary 10.0.22.1, 10.0.23.1;
53 * key "FOO.COM Key";
54 * }
55 *
56 * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name
57 * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM",
58 * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*,
59 * looks for "FOO.COM", finds it. So it
60 * attempts the update to the primary for FOO.COM. If that times out, it
61 * tries the secondaries. You can list multiple primaries if you have some
62 * kind of magic name server that supports that. You shouldn't list
63 * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't
64 * support update forwarding, AFAIK). If no TSIG key is listed, the update
65 * is attempted without TSIG.
66 *
67 * The DHCP server tries to find an existing zone for any given name by
68 * trying to look up a local zone structure for each domain containing
69 * that name, all the way up to '.'. If it finds one cached, it tries
70 * to use that one to do the update. That's why it tries to update
71 * "FOO.COM" above, even though theoretically it should try GAZANGA...
72 * and TOPANGA... first.
73 *
74 * If the update fails with a predefined zone the zone is marked as bad
75 * and another search of the predefined zones is done. If no predefined
76 * zone is found finding a zone is left to the DNS module via examination
77 * of SOA records. If the DNS module finds a zone it may cache the zone
78 * but the zone won't be cached here.
79 *
80 * TSIG updates are not performed on zones found by the DNS module - if
81 * you want TSIG updates you _must_ write a zone definition linking the
82 * key to the zone. In cases where you know for sure what the key is
83 * but do not want to hardcode the IP addresses of the primary or
84 * secondaries, a zone declaration can be made that doesn't include any
85 * primary or secondary declarations. When the DHCP server encounters
86 * this while hunting up a matching zone for a name, it looks up the SOA,
87 * fills in the IP addresses, and uses that record for the update.
88 * If the SOA lookup returns NXRRSET, a warning is printed and the zone is
89 * discarded, TSIG key and all. The search for the zone then continues
90 * as if the zone record hadn't been found. Zones without IP addresses
91 * don't match when initially hunting for a zone to update.
92 *
93 * When an update is attempted and no predefined zone is found
94 * that matches any enclosing domain of the domain being updated, the DHCP
95 * server goes through the same process that is done when the update to a
96 * predefined zone fails - starting with the most specific domain
97 * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root),
98 * it tries to look up an SOA record.
99 *
100 * TSIG keys are defined like this:
101 *
102 * key "FOO.COM Key" {
103 * algorithm HMAC-MD5.SIG-ALG.REG.INT;
104 * secret <Base64>;
105 * }
106 *
107 * <Base64> is a number expressed in base64 that represents the key.
108 * It's also permissible to use a quoted string here - this will be
109 * translated as the ASCII bytes making up the string, and will not
110 * include any NUL termination. The key name can be any text string,
111 * and the key type must be one of the key types defined in the draft
112 * or by the IANA. Currently only the HMAC-MD5... key type is
113 * supported.
114 *
115 * The DDNS processing has been split into two areas. One is the
116 * control code that determines what should be done. That code is found
117 * in the client or server directories. The other is the common code
118 * that performs functions such as properly formatting the arguments.
119 * That code is found in this file. The basic processing flow for a
120 * DDNS update is:
121 * In the client or server code determine what needs to be done and
122 * collect the necesary information then pass it to a function from
123 * this file.
124 * In this code lookup the zone and extract the zone and key information
125 * (if available) and prepare the arguments for the DNS module.
126 * When the DNS module completes its work (times out or gets a reply)
127 * it will trigger another function here which does generic processing
128 * and then passes control back to the code from the server or client.
129 * The server or client code then determines the next step which may
130 * result in another call to this module in which case the process repeats.
131 */
132
133 dns_zone_hash_t *dns_zone_hash;
134
135 /*
136 * DHCP dns structures
137 * Normally the relationship between these structures isn't one to one
138 * but in the DHCP case it (mostly) is. To make the allocations, frees,
139 * and passing of the memory easier we make a single structure with all
140 * the pieces.
141 *
142 * The maximum size of the data buffer should be large enough for any
143 * items DHCP will generate
144 */
145
146 typedef struct dhcp_ddns_rdata {
147 dns_rdata_t rdata;
148 dns_rdatalist_t rdatalist;
149 dns_rdataset_t rdataset;
150 } dhcp_ddns_data_t;
151
152 #if defined (NSUPDATE)
153
154 void ddns_interlude(isc_task_t *, isc_event_t *);
155
156
157 #if defined (TRACING)
158 /*
159 * Code to support tracing DDNS packets. We trace packets going to and
160 * coming from the libdns code but don't try to track the packets
161 * exchanged between the libdns code and the dns server(s) it contacts.
162 *
163 * The code is split into two sets of routines
164 * input refers to messages received from the dns module
165 * output refers to messages sent to the dns module
166 * Currently there are three routines in each set
167 * write is used to write information about the message to the trace file
168 * this routine is called directly from the proper place in the code.
169 * read is used to read information about a message from the trace file
170 * this routine is called from the trace loop as it reads through
171 * the file and is registered via the trace_type_register routine.
172 * When playing back a trace file we shall absorb records of output
173 * messages as part of processing the write function, therefore
174 * any output messages we encounter are flagged as errors.
175 * stop isn't currently used in this code but is needed for the register
176 * routine.
177 *
178 * We pass a pointer to a control block to the dns module which it returns
179 * to use as part of the result. As the pointer may vary between traces
180 * we need to map between those from the trace file and the new ones during
181 * playback.
182 *
183 * The mapping is complicated a little as a pointer could be 4 or 8 bytes
184 * long. We treat the old pointer as an 8 byte quantity and pad and compare
185 * as necessary.
186 */
187
188 /*
189 * Structure used to map old pointers to new pointers.
190 * Old pointers are 8 bytes long as we don't know if the trace was
191 * done on a 64 bit or 32 bit machine.
192 */
193 #define TRACE_PTR_LEN 8
194
195 typedef struct dhcp_ddns_map {
196 char old_pointer[TRACE_PTR_LEN];
197 void *new_pointer;
198 struct dhcp_ddns_map *next;
199 } dhcp_ddns_map_t;
200
201 /* The starting point for the map structure */
202 static dhcp_ddns_map_t *ddns_map;
203
204 trace_type_t *trace_ddns_input;
205 trace_type_t *trace_ddns_output;
206
207 /*
208 * The data written to the trace file is:
209 * 32 bits result from dns
210 * 64 bits pointer of cb
211 */
212
213 void
214 trace_ddns_input_write(dhcp_ddns_cb_t *ddns_cb, isc_result_t result)
215 {
216 trace_iov_t iov[2];
217 u_int32_t old_result;
218 char old_pointer[TRACE_PTR_LEN];
219
220 old_result = htonl((u_int32_t)result);
221 memset(old_pointer, 0, TRACE_PTR_LEN);
222 memcpy(old_pointer, &ddns_cb, sizeof(ddns_cb));
223
224 iov[0].len = sizeof(old_result);
225 iov[0].buf = (char *)&old_result;
226 iov[1].len = TRACE_PTR_LEN;
227 iov[1].buf = old_pointer;
228 trace_write_packet_iov(trace_ddns_input, 2, iov, MDL);
229 }
230
231 /*
232 * Process the result and pointer from the trace file.
233 * We use the pointer map to find the proper pointer for this instance.
234 * Then we need to construct an event to pass along to the interlude
235 * function.
236 */
237 static void
238 trace_ddns_input_read(trace_type_t *ttype, unsigned length,
239 char *buf)
240 {
241 u_int32_t old_result;
242 char old_pointer[TRACE_PTR_LEN];
243 dns_clientupdateevent_t *eventp;
244 void *new_pointer;
245 dhcp_ddns_map_t *ddns_map_ptr;
246
247 if (length < (sizeof(old_result) + TRACE_PTR_LEN)) {
248 log_error("trace_ddns_input_read: data too short");
249 return;
250 }
251
252 memcpy(&old_result, buf, sizeof(old_result));
253 memcpy(old_pointer, buf + sizeof(old_result), TRACE_PTR_LEN);
254
255 /* map the old pointer to a new pointer */
256 for (ddns_map_ptr = ddns_map;
257 ddns_map_ptr != NULL;
258 ddns_map_ptr = ddns_map_ptr->next) {
259 if ((ddns_map_ptr->new_pointer != NULL) &&
260 memcmp(ddns_map_ptr->old_pointer,
261 old_pointer, TRACE_PTR_LEN) == 0) {
262 new_pointer = ddns_map_ptr->new_pointer;
263 ddns_map_ptr->new_pointer = NULL;
264 memset(ddns_map_ptr->old_pointer, 0, TRACE_PTR_LEN);
265 break;
266 }
267 }
268 if (ddns_map_ptr == NULL) {
269 log_error("trace_dns_input_read: unable to map cb pointer");
270 return;
271 }
272
273 eventp = (dns_clientupdateevent_t *)
274 isc_event_allocate(dhcp_gbl_ctx.mctx,
275 dhcp_gbl_ctx.task,
276 0,
277 ddns_interlude,
278 new_pointer,
279 sizeof(dns_clientupdateevent_t));
280 if (eventp == NULL) {
281 log_error("trace_ddns_input_read: unable to allocate event");
282 return;
283 }
284 eventp->result = ntohl(old_result);
285
286
287 ddns_interlude(dhcp_gbl_ctx.task, (isc_event_t *)eventp);
288
289 return;
290 }
291
292 static void
293 trace_ddns_input_stop(trace_type_t *ttype)
294 {
295 }
296
297 /*
298 * We use the same arguments as for the dns startupdate function to
299 * allows us to choose between the two via a macro. If tracing isn't
300 * in use we simply call the dns function directly.
301 *
302 * If we are doing playback we read the next packet from the file
303 * and compare the type. If it matches we extract the results and pointer
304 * from the trace file. The results are returned to the caller as if
305 * they had called the dns routine. The pointer is used to construct a
306 * map for when the "reply" is processed.
307 *
308 * The data written to trace file is:
309 * 32 bits result
310 * 64 bits pointer of cb (DDNS Control block)
311 * contents of cb
312 */
313
314 isc_result_t
315 trace_ddns_output_write(dns_client_t *client, dns_rdataclass_t rdclass,
316 dns_name_t *zonename, dns_namelist_t *prerequisites,
317 dns_namelist_t *updates, isc_sockaddrlist_t *servers,
318 dns_tsec_t *tsec, unsigned int options,
319 isc_task_t *task, isc_taskaction_t action, void *arg,
320 dns_clientupdatetrans_t **transp)
321 {
322 isc_result_t result;
323 u_int32_t old_result;
324 char old_pointer[TRACE_PTR_LEN];
325 dhcp_ddns_map_t *ddns_map_ptr;
326
327 if (trace_playback() != 0) {
328 /* We are doing playback, extract the entry from the file */
329 unsigned buflen = 0;
330 char *inbuf = NULL;
331
332 result = trace_get_packet(&trace_ddns_output,
333 &buflen, &inbuf);
334 if (result != ISC_R_SUCCESS) {
335 log_error("trace_ddns_output_write: no input found");
336 return (ISC_R_FAILURE);
337 }
338 if (buflen < (sizeof(old_result) + TRACE_PTR_LEN)) {
339 log_error("trace_ddns_output_write: data too short");
340 dfree(inbuf, MDL);
341 return (ISC_R_FAILURE);
342 }
343 memcpy(&old_result, inbuf, sizeof(old_result));
344 result = ntohl(old_result);
345 memcpy(old_pointer, inbuf + sizeof(old_result), TRACE_PTR_LEN);
346 dfree(inbuf, MDL);
347
348 /* add the pointer to the pointer map */
349 for (ddns_map_ptr = ddns_map;
350 ddns_map_ptr != NULL;
351 ddns_map_ptr = ddns_map_ptr->next) {
352 if (ddns_map_ptr->new_pointer == NULL) {
353 break;
354 }
355 }
356
357 /*
358 * If we didn't find an empty entry, allocate an entry and
359 * link it into the list. The list isn't ordered.
360 */
361 if (ddns_map_ptr == NULL) {
362 ddns_map_ptr = dmalloc(sizeof(*ddns_map_ptr), MDL);
363 if (ddns_map_ptr == NULL) {
364 log_error("trace_ddns_output_write: "
365 "unable to allocate map entry");
366 return(ISC_R_FAILURE);
367 }
368 ddns_map_ptr->next = ddns_map;
369 ddns_map = ddns_map_ptr;
370 }
371
372 memcpy(ddns_map_ptr->old_pointer, old_pointer, TRACE_PTR_LEN);
373 ddns_map_ptr->new_pointer = arg;
374 }
375 else {
376 /* We aren't doing playback, make the actual call */
377 result = dns_client_startupdate(client, rdclass, zonename,
378 prerequisites, updates,
379 servers, tsec, options,
380 task, action, arg, transp);
381 }
382
383 if (trace_record() != 0) {
384 /* We are recording, save the information to the file */
385 trace_iov_t iov[3];
386 old_result = htonl((u_int32_t)result);
387 memset(old_pointer, 0, TRACE_PTR_LEN);
388 memcpy(old_pointer, &arg, sizeof(arg));
389 iov[0].len = sizeof(old_result);
390 iov[0].buf = (char *)&old_result;
391 iov[1].len = TRACE_PTR_LEN;
392 iov[1].buf = old_pointer;
393
394 /* Write out the entire cb, in case we want to look at it */
395 iov[2].len = sizeof(dhcp_ddns_cb_t);
396 iov[2].buf = (char *)arg;
397
398 trace_write_packet_iov(trace_ddns_output, 3, iov, MDL);
399 }
400
401 return(result);
402 }
403
404 static void
405 trace_ddns_output_read(trace_type_t *ttype, unsigned length,
406 char *buf)
407 {
408 log_error("unaccounted for ddns output.");
409 }
410
411 static void
412 trace_ddns_output_stop(trace_type_t *ttype)
413 {
414 }
415
416 void
417 trace_ddns_init()
418 {
419 trace_ddns_output = trace_type_register("ddns-output", NULL,
420 trace_ddns_output_read,
421 trace_ddns_output_stop, MDL);
422 trace_ddns_input = trace_type_register("ddns-input", NULL,
423 trace_ddns_input_read,
424 trace_ddns_input_stop, MDL);
425 ddns_map = NULL;
426 }
427
428 #define ddns_update trace_ddns_output_write
429 #else
430 #define ddns_update dns_client_startupdate
431 #endif /* TRACING */
432
433 /*
434 * Code to allocate and free a dddns control block. This block is used
435 * to pass and track the information associated with a DDNS update request.
436 */
437 dhcp_ddns_cb_t *
438 ddns_cb_alloc(const char *file, int line)
439 {
440 dhcp_ddns_cb_t *ddns_cb;
441 int i;
442
443 ddns_cb = dmalloc(sizeof(*ddns_cb), file, line);
444 if (ddns_cb != NULL) {
445 ISC_LIST_INIT(ddns_cb->zone_server_list);
446 for (i = 0; i < DHCP_MAXNS; i++) {
447 ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
448 }
449 }
450
451 return(ddns_cb);
452 }
453
454 void
455 ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
456 {
457 data_string_forget(&ddns_cb->fwd_name, file, line);
458 data_string_forget(&ddns_cb->rev_name, file, line);
459 data_string_forget(&ddns_cb->dhcid, file, line);
460
461 if (ddns_cb->zone != NULL) {
462 forget_zone((struct dns_zone **)&ddns_cb->zone);
463 }
464
465 /* Should be freed by now, check just in case. */
466 if (ddns_cb->transaction != NULL)
467 log_error("Impossible memory leak at %s:%d (attempt to free "
468 "DDNS Control Block before transaction).", MDL);
469
470 dfree(ddns_cb, file, line);
471 }
472
473 void
474 ddns_cb_forget_zone(dhcp_ddns_cb_t *ddns_cb)
475 {
476 int i;
477
478 forget_zone(&ddns_cb->zone);
479 ddns_cb->zone_name[0] = 0;
480 ISC_LIST_INIT(ddns_cb->zone_server_list);
481 for (i = 0; i < DHCP_MAXNS; i++) {
482 ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
483 }
484 }
485
486 isc_result_t find_tsig_key (ns_tsig_key **key, const char *zname,
487 struct dns_zone *zone)
488 {
489 ns_tsig_key *tkey;
490
491 if (!zone)
492 return ISC_R_NOTFOUND;
493
494 if (!zone -> key) {
495 return DHCP_R_KEY_UNKNOWN;
496 }
497
498 if ((!zone -> key -> name ||
499 strlen (zone -> key -> name) > NS_MAXDNAME) ||
500 (!zone -> key -> algorithm ||
501 strlen (zone -> key -> algorithm) > NS_MAXDNAME) ||
502 (!zone -> key) ||
503 (!zone -> key -> key) ||
504 (zone -> key -> key -> len == 0)) {
505 return DHCP_R_INVALIDKEY;
506 }
507 tkey = dmalloc (sizeof *tkey, MDL);
508 if (!tkey) {
509 nomem:
510 return ISC_R_NOMEMORY;
511 }
512 memset (tkey, 0, sizeof *tkey);
513 tkey -> data = dmalloc (zone -> key -> key -> len, MDL);
514 if (!tkey -> data) {
515 dfree (tkey, MDL);
516 goto nomem;
517 }
518 strcpy (tkey -> name, zone -> key -> name);
519 strcpy (tkey -> alg, zone -> key -> algorithm);
520 memcpy (tkey -> data,
521 zone -> key -> key -> value, zone -> key -> key -> len);
522 tkey -> len = zone -> key -> key -> len;
523 *key = tkey;
524 return ISC_R_SUCCESS;
525 }
526
527 void tkey_free (ns_tsig_key **key)
528 {
529 if ((*key) -> data)
530 dfree ((*key) -> data, MDL);
531 dfree ((*key), MDL);
532 *key = (ns_tsig_key *)0;
533 }
534 #endif
535
536 isc_result_t enter_dns_zone (struct dns_zone *zone)
537 {
538 struct dns_zone *tz = (struct dns_zone *)0;
539
540 if (dns_zone_hash) {
541 dns_zone_hash_lookup (&tz,
542 dns_zone_hash, zone -> name, 0, MDL);
543 if (tz == zone) {
544 dns_zone_dereference (&tz, MDL);
545 return ISC_R_SUCCESS;
546 }
547 if (tz) {
548 dns_zone_hash_delete (dns_zone_hash,
549 zone -> name, 0, MDL);
550 dns_zone_dereference (&tz, MDL);
551 }
552 } else {
553 if (!dns_zone_new_hash(&dns_zone_hash, DNS_HASH_SIZE, MDL))
554 return ISC_R_NOMEMORY;
555 }
556
557 dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
558 return ISC_R_SUCCESS;
559 }
560
561 isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
562 {
563 int len;
564 char *tname = (char *)0;
565 isc_result_t status;
566
567 if (!dns_zone_hash)
568 return ISC_R_NOTFOUND;
569
570 len = strlen (name);
571 if (name [len - 1] != '.') {
572 tname = dmalloc ((unsigned)len + 2, MDL);
573 if (!tname)
574 return ISC_R_NOMEMORY;
575 strcpy (tname, name);
576 tname [len] = '.';
577 tname [len + 1] = 0;
578 name = tname;
579 }
580 if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL))
581 status = ISC_R_NOTFOUND;
582 else
583 status = ISC_R_SUCCESS;
584
585 if (tname)
586 dfree (tname, MDL);
587 return status;
588 }
589
590 int dns_zone_dereference (ptr, file, line)
591 struct dns_zone **ptr;
592 const char *file;
593 int line;
594 {
595 struct dns_zone *dns_zone;
596
597 if (!ptr || !*ptr) {
598 log_error ("%s(%d): null pointer", file, line);
599 #if defined (POINTER_DEBUG)
600 abort ();
601 #else
602 return 0;
603 #endif
604 }
605
606 dns_zone = *ptr;
607 *ptr = (struct dns_zone *)0;
608 --dns_zone -> refcnt;
609 rc_register (file, line, ptr, dns_zone, dns_zone -> refcnt, 1, RC_MISC);
610 if (dns_zone -> refcnt > 0)
611 return 1;
612
613 if (dns_zone -> refcnt < 0) {
614 log_error ("%s(%d): negative refcnt!", file, line);
615 #if defined (DEBUG_RC_HISTORY)
616 dump_rc_history (dns_zone);
617 #endif
618 #if defined (POINTER_DEBUG)
619 abort ();
620 #else
621 return 0;
622 #endif
623 }
624
625 if (dns_zone -> name)
626 dfree (dns_zone -> name, file, line);
627 if (dns_zone -> key)
628 omapi_auth_key_dereference (&dns_zone -> key, file, line);
629 if (dns_zone -> primary)
630 option_cache_dereference (&dns_zone -> primary, file, line);
631 if (dns_zone -> secondary)
632 option_cache_dereference (&dns_zone -> secondary, file, line);
633 dfree (dns_zone, file, line);
634 return 1;
635 }
636
637 #if defined (NSUPDATE)
638 isc_result_t
639 find_cached_zone(dhcp_ddns_cb_t *ddns_cb, int direction)
640 {
641 isc_result_t status = ISC_R_NOTFOUND;
642 const char *np;
643 struct dns_zone *zone = (struct dns_zone *)0;
644 struct data_string nsaddrs;
645 struct in_addr zone_addr;
646 int ix;
647
648 if (direction == FIND_FORWARD) {
649 np = (const char *)ddns_cb->fwd_name.data;
650 } else {
651 np = (const char *)ddns_cb->rev_name.data;
652 }
653
654 /* We can't look up a null zone. */
655 if ((np == NULL) || (*np == '\0')) {
656 return DHCP_R_INVALIDARG;
657 }
658
659 /*
660 * For each subzone, try to find a cached zone.
661 * Skip the first zone as that shouldn't work.
662 */
663 for (np = strchr(np, '.'); np != NULL; np = strchr(np, '.')) {
664 np++;
665 status = dns_zone_lookup (&zone, np);
666 if (status == ISC_R_SUCCESS)
667 break;
668 }
669
670 if (status != ISC_R_SUCCESS)
671 return status;
672
673 /* Make sure the zone is valid. */
674 if (zone -> timeout && zone -> timeout < cur_time) {
675 dns_zone_dereference (&zone, MDL);
676 return ISC_R_CANCELED;
677 }
678
679 /* Make sure the zone name will fit. */
680 if (strlen(zone->name) > sizeof(ddns_cb->zone_name)) {
681 dns_zone_dereference (&zone, MDL);
682 return ISC_R_NOSPACE;
683 }
684 strcpy((char *)&ddns_cb->zone_name[0], zone->name);
685
686 memset (&nsaddrs, 0, sizeof nsaddrs);
687 ix = 0;
688
689 if (zone -> primary) {
690 if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
691 (struct lease *)0,
692 (struct client_state *)0,
693 (struct option_state *)0,
694 (struct option_state *)0,
695 &global_scope,
696 zone -> primary, MDL)) {
697 int ip = 0;
698 while (ix < DHCP_MAXNS) {
699 if (ip + 4 > nsaddrs.len)
700 break;
701 memcpy(&zone_addr, &nsaddrs.data[ip], 4);
702 isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
703 &zone_addr,
704 NS_DEFAULTPORT);
705 ISC_LIST_APPEND(ddns_cb->zone_server_list,
706 &ddns_cb->zone_addrs[ix],
707 link);
708 ip += 4;
709 ix++;
710 }
711 data_string_forget (&nsaddrs, MDL);
712 }
713 }
714 if (zone -> secondary) {
715 if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
716 (struct lease *)0,
717 (struct client_state *)0,
718 (struct option_state *)0,
719 (struct option_state *)0,
720 &global_scope,
721 zone -> secondary, MDL)) {
722 int ip = 0;
723 while (ix < DHCP_MAXNS) {
724 if (ip + 4 > nsaddrs.len)
725 break;
726 memcpy(&zone_addr, &nsaddrs.data[ip], 4);
727 isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
728 &zone_addr,
729 NS_DEFAULTPORT);
730 ISC_LIST_APPEND(ddns_cb->zone_server_list,
731 &ddns_cb->zone_addrs[ix],
732 link);
733 ip += 4;
734 ix++;
735 }
736 data_string_forget (&nsaddrs, MDL);
737 }
738 }
739
740 dns_zone_reference(&ddns_cb->zone, zone, MDL);
741 dns_zone_dereference (&zone, MDL);
742 return ISC_R_SUCCESS;
743 }
744
745 void forget_zone (struct dns_zone **zone)
746 {
747 dns_zone_dereference (zone, MDL);
748 }
749
750 void repudiate_zone (struct dns_zone **zone)
751 {
752 /* XXX Currently we're not differentiating between a cached
753 XXX zone and a zone that's been repudiated, which means
754 XXX that if we reap cached zones, we blow away repudiated
755 XXX zones. This isn't a big problem since we're not yet
756 XXX caching zones... :'} */
757
758 (*zone) -> timeout = cur_time - 1;
759 dns_zone_dereference (zone, MDL);
760 }
761
762 /* Have to use TXT records for now. */
763 #define T_DHCID T_TXT
764
765 int get_dhcid (struct data_string *id,
766 int type, const u_int8_t *data, unsigned len)
767 {
768 unsigned char buf[ISC_MD5_DIGESTLENGTH];
769 isc_md5_t md5;
770 int i;
771
772 /* Types can only be 0..(2^16)-1. */
773 if (type < 0 || type > 65535)
774 return 0;
775
776 /*
777 * Hexadecimal MD5 digest plus two byte type, NUL,
778 * and one byte for length for dns.
779 */
780 if (!buffer_allocate (&id -> buffer,
781 (ISC_MD5_DIGESTLENGTH * 2) + 4, MDL))
782 return 0;
783 id -> data = id -> buffer -> data;
784
785 /*
786 * DHCP clients and servers should use the following forms of client
787 * identification, starting with the most preferable, and finishing
788 * with the least preferable. If the client does not send any of these
789 * forms of identification, the DHCP/DDNS interaction is not defined by
790 * this specification. The most preferable form of identification is
791 * the Globally Unique Identifier Option [TBD]. Next is the DHCP
792 * Client Identifier option. Last is the client's link-layer address,
793 * as conveyed in its DHCPREQUEST message. Implementors should note
794 * that the link-layer address cannot be used if there are no
795 * significant bytes in the chaddr field of the DHCP client's request,
796 * because this does not constitute a unique identifier.
797 * -- "Interaction between DHCP and DNS"
798 * <draft-ietf-dhc-dhcp-dns-12.txt>
799 * M. Stapp, Y. Rekhter
800 *
801 * We put the length into the first byte to turn
802 * this into a dns text string. This avoid needing to
803 * copy the string to add the byte later.
804 */
805 id->buffer->data[0] = ISC_MD5_DIGESTLENGTH * 2 + 2;
806
807 /* Put the type in the next two bytes. */
808 id->buffer->data[1] = "0123456789abcdef"[type >> 4];
809 id->buffer->data[2] = "0123456789abcdef"[type % 15];
810
811 /* Mash together an MD5 hash of the identifier. */
812 isc_md5_init(&md5);
813 isc_md5_update(&md5, data, len);
814 isc_md5_final(&md5, buf);
815
816 /* Convert into ASCII. */
817 for (i = 0; i < ISC_MD5_DIGESTLENGTH; i++) {
818 id->buffer->data[i * 2 + 3] =
819 "0123456789abcdef"[(buf[i] >> 4) & 0xf];
820 id->buffer->data[i * 2 + 4] =
821 "0123456789abcdef"[buf[i] & 0xf];
822 }
823
824 id->len = ISC_MD5_DIGESTLENGTH * 2 + 3;
825 id->buffer->data[id->len] = 0;
826 id->terminated = 1;
827
828 return 1;
829 }
830
831 /*
832 * The dhcid (text version) that we pass to DNS includes a length byte
833 * at the start but the text we store in the lease doesn't include the
834 * length byte. The following routines are to convert between the two
835 * styles.
836 *
837 * When converting from a dhcid to a leaseid we reuse the buffer and
838 * simply adjust the data pointer and length fields in the data string.
839 * This avoids any prolems with allocating space.
840 */
841
842 void
843 dhcid_tolease(struct data_string *dhcid,
844 struct data_string *leaseid)
845 {
846 /* copy the data string then update the fields */
847 data_string_copy(leaseid, dhcid, MDL);
848 leaseid->data++;
849 leaseid->len--;
850 }
851
852 isc_result_t
853 dhcid_fromlease(struct data_string *dhcid,
854 struct data_string *leaseid)
855 {
856 if (!buffer_allocate(&dhcid->buffer, leaseid->len + 2, MDL)) {
857 return(ISC_R_FAILURE);
858 }
859
860 dhcid->data = dhcid->buffer->data;
861
862 dhcid->buffer->data[0] = leaseid->len;
863 memcpy(dhcid->buffer->data + 1, leaseid->data, leaseid->len);
864 dhcid->len = leaseid->len + 1;
865 if (leaseid->terminated == 1) {
866 dhcid->buffer->data[dhcid->len] = 0;
867 dhcid->terminated = 1;
868 }
869
870 return(ISC_R_SUCCESS);
871 }
872
873 /*
874 * Construct the dataset for this item.
875 * This is a fairly simple arrangement as the operations we do are simple.
876 * If there is data we simply have the rdata point to it - the formatting
877 * must be correct already. We then link the rdatalist to the rdata and
878 * create a rdataset from the rdatalist.
879 */
880
881 static isc_result_t
882 make_dns_dataset(dns_rdataclass_t dataclass,
883 dns_rdatatype_t datatype,
884 dhcp_ddns_data_t *dataspace,
885 unsigned char *data,
886 int datalen,
887 int ttl)
888 {
889 dns_rdata_t *rdata = &dataspace->rdata;
890 dns_rdatalist_t *rdatalist = &dataspace->rdatalist;
891 dns_rdataset_t *rdataset = &dataspace->rdataset;
892
893 isc_region_t region;
894
895 /* set up the rdata */
896 dns_rdata_init(rdata);
897
898 if (data == NULL) {
899 /* No data, set up the rdata fields we care about */
900 rdata->flags = DNS_RDATA_UPDATE;
901 rdata->type = datatype;
902 rdata->rdclass = dataclass;
903 } else {
904 switch(datatype) {
905 case dns_rdatatype_a:
906 case dns_rdatatype_aaaa:
907 case dns_rdatatype_txt:
908 case dns_rdatatype_dhcid:
909 case dns_rdatatype_ptr:
910 /* The data must be in the right format we simply
911 * need to supply it via the correct structure */
912 region.base = data;
913 region.length = datalen;
914 dns_rdata_fromregion(rdata, dataclass, datatype,
915 &region);
916 break;
917 default:
918 return(DHCP_R_INVALIDARG);
919 break;
920 }
921 }
922
923 /* setup the datalist and attach the rdata to it */
924 dns_rdatalist_init(rdatalist);
925 rdatalist->type = datatype;
926 rdatalist->rdclass = dataclass;
927 rdatalist->ttl = ttl;
928 ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
929
930 /* convert the datalist to a dataset */
931 dns_rdataset_init(rdataset);
932 dns_rdatalist_tordataset(rdatalist, rdataset);
933
934 return(ISC_R_SUCCESS);
935 }
936
937 /*
938 * When a DHCP client or server intends to update an A RR, it first
939 * prepares a DNS UPDATE query which includes as a prerequisite the
940 * assertion that the name does not exist. The update section of the
941 * query attempts to add the new name and its IP address mapping (an A
942 * RR), and the DHCID RR with its unique client-identity.
943 * -- "Interaction between DHCP and DNS"
944 *
945 * There are two cases, one for the server and one for the client.
946 *
947 * For the server the first step will have a request of:
948 * The name is not in use
949 * Add an A RR
950 * Add a DHCID RR (currently txt)
951 *
952 * For the client the first step will have a request of:
953 * The A RR does not exist
954 * Add an A RR
955 * Add a DHCID RR (currently txt)
956 */
957
958 static isc_result_t
959 ddns_modify_fwd_add1(dhcp_ddns_cb_t *ddns_cb,
960 dhcp_ddns_data_t *dataspace,
961 dns_name_t *pname,
962 dns_name_t *uname)
963 {
964 isc_result_t result;
965
966 /* Construct the prerequisite list */
967 if ((ddns_cb->flags & DDNS_INCLUDE_RRSET) != 0) {
968 /* The A RR shouldn't exist */
969 result = make_dns_dataset(dns_rdataclass_none,
970 ddns_cb->address_type,
971 dataspace, NULL, 0, 0);
972 } else {
973 /* The name is not in use */
974 result = make_dns_dataset(dns_rdataclass_none,
975 dns_rdatatype_any,
976 dataspace, NULL, 0, 0);
977 }
978 if (result != ISC_R_SUCCESS) {
979 return(result);
980 }
981 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
982 dataspace++;
983
984 /* Construct the update list */
985 /* Add the A RR */
986 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
987 dataspace,
988 (unsigned char *)ddns_cb->address.iabuf,
989 ddns_cb->address.len, ddns_cb->ttl);
990 if (result != ISC_R_SUCCESS) {
991 return(result);
992 }
993 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
994 dataspace++;
995
996 /* Add the DHCID RR */
997 result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
998 dataspace,
999 (unsigned char *)ddns_cb->dhcid.data,
1000 ddns_cb->dhcid.len, ddns_cb->ttl);
1001 if (result != ISC_R_SUCCESS) {
1002 return(result);
1003 }
1004 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1005
1006 return(ISC_R_SUCCESS);
1007 }
1008
1009 /*
1010 * If the first update operation fails with YXDOMAIN, the updater can
1011 * conclude that the intended name is in use. The updater then
1012 * attempts to confirm that the DNS name is not being used by some
1013 * other host. The updater prepares a second UPDATE query in which the
1014 * prerequisite is that the desired name has attached to it a DHCID RR
1015 * whose contents match the client identity. The update section of
1016 * this query deletes the existing A records on the name, and adds the
1017 * A record that matches the DHCP binding and the DHCID RR with the
1018 * client identity.
1019 * -- "Interaction between DHCP and DNS"
1020 *
1021 * The message for the second step depends on if we are doing conflict
1022 * resolution. If we are we include a prerequisite. If not we delete
1023 * the DHCID in addition to all A rrsets.
1024 *
1025 * Conflict resolution:
1026 * DHCID RR exists, and matches client identity.
1027 * Delete A RRset.
1028 * Add A RR.
1029 *
1030 * Conflict override:
1031 * Delete DHCID RRs.
1032 * Add DHCID RR
1033 * Delete A RRset.
1034 * Add A RR.
1035 */
1036
1037 static isc_result_t
1038 ddns_modify_fwd_add2(dhcp_ddns_cb_t *ddns_cb,
1039 dhcp_ddns_data_t *dataspace,
1040 dns_name_t *pname,
1041 dns_name_t *uname)
1042 {
1043 isc_result_t result;
1044
1045 /*
1046 * If we are doing conflict resolution (unset) we use a prereq list.
1047 * If not we delete the DHCID in addition to all A rrsets.
1048 */
1049 if ((ddns_cb->flags & DDNS_CONFLICT_OVERRIDE) == 0) {
1050 /* Construct the prereq list */
1051 /* The DHCID RR exists and matches the client identity */
1052 result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
1053 dataspace,
1054 (unsigned char *)ddns_cb->dhcid.data,
1055 ddns_cb->dhcid.len, 0);
1056 if (result != ISC_R_SUCCESS) {
1057 return(result);
1058 }
1059 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1060 dataspace++;
1061 } else {
1062 /* Start constructing the update list.
1063 * Conflict detection override: delete DHCID RRs */
1064 result = make_dns_dataset(dns_rdataclass_any,
1065 dns_rdatatype_txt,
1066 dataspace, NULL, 0, 0);
1067 if (result != ISC_R_SUCCESS) {
1068 return(result);
1069 }
1070 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1071 dataspace++;
1072
1073 /* Add current DHCID RR */
1074 result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
1075 dataspace,
1076 (unsigned char *)ddns_cb->dhcid.data,
1077 ddns_cb->dhcid.len, ddns_cb->ttl);
1078 if (result != ISC_R_SUCCESS) {
1079 return(result);
1080 }
1081 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1082 dataspace++;
1083 }
1084
1085 /* Start or continue constructing the update list */
1086 /* Delete the A RRset */
1087 result = make_dns_dataset(dns_rdataclass_any, ddns_cb->address_type,
1088 dataspace, NULL, 0, 0);
1089 if (result != ISC_R_SUCCESS) {
1090 return(result);
1091 }
1092 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1093 dataspace++;
1094
1095 /* Add the A RR */
1096 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
1097 dataspace,
1098 (unsigned char *)ddns_cb->address.iabuf,
1099 ddns_cb->address.len, ddns_cb->ttl);
1100 if (result != ISC_R_SUCCESS) {
1101 return(result);
1102 }
1103 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1104
1105 return(ISC_R_SUCCESS);
1106 }
1107
1108 /*
1109 * The entity chosen to handle the A record for this client (either the
1110 * client or the server) SHOULD delete the A record that was added when
1111 * the lease was made to the client.
1112 *
1113 * In order to perform this delete, the updater prepares an UPDATE
1114 * query which contains two prerequisites. The first prerequisite
1115 * asserts that the DHCID RR exists whose data is the client identity
1116 * described in Section 4.3. The second prerequisite asserts that the
1117 * data in the A RR contains the IP address of the lease that has
1118 * expired or been released.
1119 * -- "Interaction between DHCP and DNS"
1120 *
1121 * First try has:
1122 * DHCID RR exists, and matches client identity.
1123 * A RR matches the expiring lease.
1124 * Delete appropriate A RR.
1125 */
1126
1127 static isc_result_t
1128 ddns_modify_fwd_rem1(dhcp_ddns_cb_t *ddns_cb,
1129 dhcp_ddns_data_t *dataspace,
1130 dns_name_t *pname,
1131 dns_name_t *uname)
1132 {
1133 isc_result_t result;
1134
1135 /* Consruct the prereq list */
1136 /* The DHCID RR exists and matches the client identity */
1137 result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
1138 dataspace,
1139 (unsigned char *)ddns_cb->dhcid.data,
1140 ddns_cb->dhcid.len, 0);
1141 if (result != ISC_R_SUCCESS) {
1142 return(result);
1143 }
1144 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1145 dataspace++;
1146
1147 /* The A RR matches the expiring lease */
1148 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
1149 dataspace,
1150 (unsigned char *)ddns_cb->address.iabuf,
1151 ddns_cb->address.len, 0);
1152 if (result != ISC_R_SUCCESS) {
1153 return(result);
1154 }
1155 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1156 dataspace++;
1157
1158 /* Construct the update list */
1159 /* Delete A RRset */
1160 result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type,
1161 dataspace,
1162 (unsigned char *)ddns_cb->address.iabuf,
1163 ddns_cb->address.len, 0);
1164 if (result != ISC_R_SUCCESS) {
1165 return(result);
1166 }
1167 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1168
1169 return(ISC_R_SUCCESS);
1170 }
1171
1172 /*
1173 * If the deletion of the A succeeded, and there are no A or AAAA
1174 * records left for this domain, then we can blow away the DHCID
1175 * record as well. We can't blow away the DHCID record above
1176 * because it's possible that more than one record has been added
1177 * to this domain name.
1178 *
1179 * Second query has:
1180 * A RR does not exist.
1181 * AAAA RR does not exist.
1182 * Delete appropriate DHCID RR.
1183 */
1184
1185 static isc_result_t
1186 ddns_modify_fwd_rem2(dhcp_ddns_cb_t *ddns_cb,
1187 dhcp_ddns_data_t *dataspace,
1188 dns_name_t *pname,
1189 dns_name_t *uname)
1190 {
1191 isc_result_t result;
1192
1193 /* Construct the prereq list */
1194 /* The A RR does not exist */
1195 result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_a,
1196 dataspace, NULL, 0, 0);
1197 if (result != ISC_R_SUCCESS) {
1198 return(result);
1199 }
1200 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1201 dataspace++;
1202
1203 /* The AAAA RR does not exist */
1204 result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_aaaa,
1205 dataspace, NULL, 0, 0);
1206 if (result != ISC_R_SUCCESS) {
1207 return(result);
1208 }
1209 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1210 dataspace++;
1211
1212 /* Construct the update list */
1213 /* Delete DHCID RR */
1214 result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_txt,
1215 dataspace,
1216 (unsigned char *)ddns_cb->dhcid.data,
1217 ddns_cb->dhcid.len, 0);
1218 if (result != ISC_R_SUCCESS) {
1219 return(result);
1220 }
1221 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1222
1223 return(ISC_R_SUCCESS);
1224 }
1225
1226 /*
1227 * This routine converts from the task action call into something
1228 * easier to work with. It also handles the common case of a signature
1229 * or zone not being correct.
1230 */
1231 void ddns_interlude(isc_task_t *taskp,
1232 isc_event_t *eventp)
1233 {
1234 dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)eventp->ev_arg;
1235 dns_clientupdateevent_t *ddns_event = (dns_clientupdateevent_t *)eventp;
1236 isc_result_t eresult = ddns_event->result;
1237 isc_result_t result;
1238
1239 /* We've extracted the information we want from it, get rid of
1240 * the event block.*/
1241 isc_event_free(&eventp);
1242
1243 #if defined (TRACING)
1244 if (trace_record()) {
1245 trace_ddns_input_write(ddns_cb, eresult);
1246 }
1247 #endif
1248
1249 #if defined (DEBUG_DNS_UPDATES)
1250 print_dns_status(DDNS_PRINT_INBOUND, ddns_cb, eresult);
1251 #endif
1252
1253 /* This transaction is complete, clear the value */
1254 dns_client_destroyupdatetrans(&ddns_cb->transaction);
1255
1256 /* If we cancelled or tried to cancel the operation we just
1257 * need to clean up. */
1258 if ((eresult == ISC_R_CANCELED) ||
1259 ((ddns_cb->flags & DDNS_ABORT) != 0)) {
1260 if (ddns_cb->next_op != NULL) {
1261 /* if necessary cleanup up next op block */
1262 ddns_cb_free(ddns_cb->next_op, MDL);
1263 }
1264 ddns_cb_free(ddns_cb, MDL);
1265 return;
1266 }
1267
1268 /* If we had a problem with our key or zone try again */
1269 if ((eresult == DNS_R_NOTAUTH) ||
1270 (eresult == DNS_R_NOTZONE)) {
1271 int i;
1272 /* Our zone information was questionable,
1273 * repudiate it and try again */
1274 repudiate_zone(&ddns_cb->zone);
1275 ddns_cb->zone_name[0] = 0;
1276 ISC_LIST_INIT(ddns_cb->zone_server_list);
1277 for (i = 0; i < DHCP_MAXNS; i++) {
1278 ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
1279 }
1280
1281 if ((ddns_cb->state &
1282 (DDNS_STATE_ADD_PTR | DDNS_STATE_REM_PTR)) != 0) {
1283 result = ddns_modify_ptr(ddns_cb);
1284 } else {
1285 result = ddns_modify_fwd(ddns_cb);
1286 }
1287
1288 if (result != ISC_R_SUCCESS) {
1289 /* if we couldn't redo the query toss it */
1290 if (ddns_cb->next_op != NULL) {
1291 /* cleanup up next op block */
1292 ddns_cb_free(ddns_cb->next_op, MDL);
1293 }
1294 ddns_cb_free(ddns_cb, MDL);
1295 }
1296 return;
1297 } else {
1298 /* pass it along to be processed */
1299 ddns_cb->cur_func(ddns_cb, eresult);
1300 }
1301
1302 return;
1303 }
1304
1305 /*
1306 * This routine does the generic work for sending a ddns message to
1307 * modify the forward record (A or AAAA) and calls one of a set of
1308 * routines to build the specific message.
1309 */
1310
1311 isc_result_t
1312 ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb)
1313 {
1314 isc_result_t result;
1315 dns_tsec_t *tsec_key = NULL;
1316
1317 unsigned char *clientname;
1318 dhcp_ddns_data_t *dataspace = NULL;
1319 dns_namelist_t prereqlist, updatelist;
1320 dns_fixedname_t zname0, pname0, uname0;
1321 dns_name_t *zname = NULL, *pname, *uname;
1322
1323 isc_sockaddrlist_t *zlist = NULL;
1324
1325 /* Get a pointer to the clientname to make things easier. */
1326 clientname = (unsigned char *)ddns_cb->fwd_name.data;
1327
1328 /* Extract and validate the type of the address. */
1329 if (ddns_cb->address.len == 4) {
1330 ddns_cb->address_type = dns_rdatatype_a;
1331 } else if (ddns_cb->address.len == 16) {
1332 ddns_cb->address_type = dns_rdatatype_aaaa;
1333 } else {
1334 return DHCP_R_INVALIDARG;
1335 }
1336
1337 /*
1338 * If we already have a zone use it, otherwise try to lookup the
1339 * zone in our cache. If we find one we will have a pointer to
1340 * the zone that needs to be dereferenced when we are done with it.
1341 * If we don't find one that is okay we'll let the DNS code try and
1342 * find the information for us.
1343 */
1344
1345 if (ddns_cb->zone == NULL) {
1346 result = find_cached_zone(ddns_cb, FIND_FORWARD);
1347 }
1348
1349 /*
1350 * If we have a zone try to get any information we need
1351 * from it - name, addresses and the key. The address
1352 * and key may be empty the name can't be.
1353 */
1354 if (ddns_cb->zone) {
1355 /* Set up the zone name for use by DNS */
1356 result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
1357 if (result != ISC_R_SUCCESS) {
1358 log_error("Unable to build name for zone for "
1359 "fwd update: %s %s",
1360 ddns_cb->zone_name,
1361 isc_result_totext(result));
1362 goto cleanup;
1363 }
1364
1365 if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
1366 /* If we have any addresses get them */
1367 zlist = &ddns_cb->zone_server_list;
1368 }
1369
1370
1371 if (ddns_cb->zone->key != NULL) {
1372 /*
1373 * Not having a key is fine, having a key
1374 * but not a tsec is odd so we warn the user.
1375 */
1376 /*sar*/
1377 /* should we do the warning? */
1378 tsec_key = ddns_cb->zone->key->tsec_key;
1379 if (tsec_key == NULL) {
1380 log_error("No tsec for use with key %s",
1381 ddns_cb->zone->key->name);
1382 }
1383 }
1384 }
1385
1386 /* Set up the DNS names for the prereq and update lists */
1387 if (((result = dhcp_isc_name(clientname, &pname0, &pname))
1388 != ISC_R_SUCCESS) ||
1389 ((result = dhcp_isc_name(clientname, &uname0, &uname))
1390 != ISC_R_SUCCESS)) {
1391 log_error("Unable to build name for fwd update: %s %s",
1392 clientname, isc_result_totext(result));
1393 goto cleanup;
1394 }
1395
1396 /* Allocate the various isc dns library structures we may require. */
1397 dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 4);
1398 if (dataspace == NULL) {
1399 log_error("Unable to allocate memory for fwd update");
1400 result = ISC_R_NOMEMORY;
1401 goto cleanup;
1402 }
1403
1404 ISC_LIST_INIT(prereqlist);
1405 ISC_LIST_INIT(updatelist);
1406
1407 switch(ddns_cb->state) {
1408 case DDNS_STATE_ADD_FW_NXDOMAIN:
1409 result = ddns_modify_fwd_add1(ddns_cb, dataspace,
1410 pname, uname);
1411 if (result != ISC_R_SUCCESS) {
1412 goto cleanup;
1413 }
1414 ISC_LIST_APPEND(prereqlist, pname, link);
1415 break;
1416 case DDNS_STATE_ADD_FW_YXDHCID:
1417 result = ddns_modify_fwd_add2(ddns_cb, dataspace,
1418 pname, uname);
1419 if (result != ISC_R_SUCCESS) {
1420 goto cleanup;
1421 }
1422
1423 /* If we aren't doing conflict override we have entries
1424 * in the pname list and we need to attach it to the
1425 * prereqlist */
1426
1427 if ((ddns_cb->flags & DDNS_CONFLICT_OVERRIDE) == 0) {
1428 ISC_LIST_APPEND(prereqlist, pname, link);
1429 }
1430
1431 break;
1432 case DDNS_STATE_REM_FW_YXDHCID:
1433 result = ddns_modify_fwd_rem1(ddns_cb, dataspace,
1434 pname, uname);
1435 if (result != ISC_R_SUCCESS) {
1436 goto cleanup;
1437 }
1438 ISC_LIST_APPEND(prereqlist, pname, link);
1439 break;
1440 case DDNS_STATE_REM_FW_NXRR:
1441 result = ddns_modify_fwd_rem2(ddns_cb, dataspace,
1442 pname, uname);
1443 if (result != ISC_R_SUCCESS) {
1444 goto cleanup;
1445 }
1446 ISC_LIST_APPEND(prereqlist, pname, link);
1447 break;
1448
1449 default:
1450 log_error("Invalid operation in ddns code.");
1451 result = DHCP_R_INVALIDARG;
1452 goto cleanup;
1453 break;
1454 }
1455
1456 /*
1457 * We always have an update list but may not have a prereqlist
1458 * if we are doing conflict override.
1459 */
1460 ISC_LIST_APPEND(updatelist, uname, link);
1461
1462 /* send the message, cleanup and return the result */
1463 result = ddns_update(dhcp_gbl_ctx.dnsclient,
1464 dns_rdataclass_in, zname,
1465 &prereqlist, &updatelist,
1466 zlist, tsec_key,
1467 DNS_CLIENTRESOPT_ALLOWRUN,
1468 dhcp_gbl_ctx.task,
1469 ddns_interlude,
1470 (void *)ddns_cb,
1471 &ddns_cb->transaction);
1472 if (result == ISC_R_FAMILYNOSUPPORT) {
1473 log_info("Unable to perform DDNS update, "
1474 "address family not supported");
1475 }
1476
1477 #if defined (DEBUG_DNS_UPDATES)
1478 print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
1479 #endif
1480
1481 cleanup:
1482 if (dataspace != NULL) {
1483 isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
1484 sizeof(*dataspace) * 4);
1485 }
1486 return(result);
1487 }
1488
1489
1490 isc_result_t
1491 ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb)
1492 {
1493 isc_result_t result;
1494 dns_tsec_t *tsec_key = NULL;
1495 unsigned char *ptrname;
1496 dhcp_ddns_data_t *dataspace = NULL;
1497 dns_namelist_t updatelist;
1498 dns_fixedname_t zname0, uname0;
1499 dns_name_t *zname = NULL, *uname;
1500 isc_sockaddrlist_t *zlist = NULL;
1501 unsigned char buf[256];
1502 int buflen;
1503
1504 /*
1505 * Try to lookup the zone in the zone cache. As with the forward
1506 * case it's okay if we don't have one, the DNS code will try to
1507 * find something also if we succeed we will need to dereference
1508 * the zone later. Unlike with the forward case we assume we won't
1509 * have a pre-existing zone.
1510 */
1511 result = find_cached_zone(ddns_cb, FIND_REVERSE);
1512 if ((result == ISC_R_SUCCESS) &&
1513 !(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
1514 /* Set up the zone name for use by DNS */
1515 result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
1516 if (result != ISC_R_SUCCESS) {
1517 log_error("Unable to build name for zone for "
1518 "fwd update: %s %s",
1519 ddns_cb->zone_name,
1520 isc_result_totext(result));
1521 goto cleanup;
1522 }
1523 /* If we have any addresses get them */
1524 if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
1525 zlist = &ddns_cb->zone_server_list;
1526 }
1527
1528 /*
1529 * If we now have a zone try to get the key, NULL is okay,
1530 * having a key but not a tsec is odd so we warn.
1531 */
1532 /*sar*/
1533 /* should we do the warning if we have a key but no tsec? */
1534 if ((ddns_cb->zone != NULL) && (ddns_cb->zone->key != NULL)) {
1535 tsec_key = ddns_cb->zone->key->tsec_key;
1536 if (tsec_key == NULL) {
1537 log_error("No tsec for use with key %s",
1538 ddns_cb->zone->key->name);
1539 }
1540 }
1541 }
1542
1543 /* We must have a name for the update list */
1544 /* Get a pointer to the ptrname to make things easier. */
1545 ptrname = (unsigned char *)ddns_cb->rev_name.data;
1546
1547 if ((result = dhcp_isc_name(ptrname, &uname0, &uname))
1548 != ISC_R_SUCCESS) {
1549 log_error("Unable to build name for fwd update: %s %s",
1550 ptrname, isc_result_totext(result));
1551 goto cleanup;
1552 }
1553
1554 /*
1555 * Allocate the various isc dns library structures we may require.
1556 * Allocating one blob avoids being halfway through the process
1557 * and being unable to allocate as well as making the free easy.
1558 */
1559 dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 2);
1560 if (dataspace == NULL) {
1561 log_error("Unable to allocate memory for fwd update");
1562 result = ISC_R_NOMEMORY;
1563 goto cleanup;
1564 }
1565
1566 ISC_LIST_INIT(updatelist);
1567
1568 /*
1569 * Construct the update list
1570 * We always delete what's currently there
1571 * Delete PTR RR.
1572 */
1573 result = make_dns_dataset(dns_rdataclass_any, dns_rdatatype_ptr,
1574 &dataspace[0], NULL, 0, 0);
1575 if (result != ISC_R_SUCCESS) {
1576 goto cleanup;
1577 }
1578 ISC_LIST_APPEND(uname->list, &dataspace[0].rdataset, link);
1579
1580 /*
1581 * If we are updating the pointer we then add the new one
1582 * Add PTR RR.
1583 */
1584 if (ddns_cb->state == DDNS_STATE_ADD_PTR) {
1585 #if 0
1586 /*
1587 * I've left this dead code in the file for now in case
1588 * we decide to try and get rid of the ns_name functions.
1589 * sar
1590 */
1591
1592 /*
1593 * Need to convert pointer into on the wire representation
1594 * We replace the '.' characters with the lengths of the
1595 * next name and add a length to the beginning for the first
1596 * name.
1597 */
1598 if (ddns_cb->fwd_name.len == 1) {
1599 /* the root */
1600 buf[0] = 0;
1601 buflen = 1;
1602 } else {
1603 unsigned char *cp;
1604 buf[0] = '.';
1605 memcpy(&buf[1], ddns_cb->fwd_name.data,
1606 ddns_cb->fwd_name.len);
1607 for(cp = buf + ddns_cb->fwd_name.len, buflen = 0;
1608 cp != buf;
1609 cp--) {
1610 if (*cp == '.') {
1611 *cp = buflen;
1612 buflen = 0;
1613 } else {
1614 buflen++;
1615 }
1616 }
1617 *cp = buflen;
1618 buflen = ddns_cb->fwd_name.len + 1;
1619 }
1620 #endif
1621 /*
1622 * Need to convert pointer into on the wire representation
1623 */
1624 if (MRns_name_pton((char *)ddns_cb->fwd_name.data,
1625 buf, 256) == -1) {
1626 goto cleanup;
1627 }
1628 buflen = 0;
1629 while (buf[buflen] != 0) {
1630 buflen += buf[buflen] + 1;
1631 }
1632 buflen++;
1633
1634 result = make_dns_dataset(dns_rdataclass_in,
1635 dns_rdatatype_ptr,
1636 &dataspace[1],
1637 buf, buflen, ddns_cb->ttl);
1638 if (result != ISC_R_SUCCESS) {
1639 goto cleanup;
1640 }
1641 ISC_LIST_APPEND(uname->list, &dataspace[1].rdataset, link);
1642 }
1643
1644 ISC_LIST_APPEND(updatelist, uname, link);
1645
1646 /*sar*/
1647 /*
1648 * for now I'll cleanup the dataset immediately, it would be
1649 * more efficient to keep it around in case the signaturure failed
1650 * and we wanted to retry it.
1651 */
1652 /* send the message, cleanup and return the result */
1653 result = ddns_update((dns_client_t *)dhcp_gbl_ctx.dnsclient,
1654 dns_rdataclass_in, zname,
1655 NULL, &updatelist,
1656 zlist, tsec_key,
1657 DNS_CLIENTRESOPT_ALLOWRUN,
1658 dhcp_gbl_ctx.task,
1659 ddns_interlude, (void *)ddns_cb,
1660 &ddns_cb->transaction);
1661 if (result == ISC_R_FAMILYNOSUPPORT) {
1662 log_info("Unable to perform DDNS update, "
1663 "address family not supported");
1664 }
1665
1666 #if defined (DEBUG_DNS_UPDATES)
1667 print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
1668 #endif
1669
1670 cleanup:
1671 if (dataspace != NULL) {
1672 isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
1673 sizeof(*dataspace) * 2);
1674 }
1675 return(result);
1676 }
1677
1678 void
1679 ddns_cancel(dhcp_ddns_cb_t *ddns_cb) {
1680 ddns_cb->flags |= DDNS_ABORT;
1681 if (ddns_cb->transaction != NULL) {
1682 dns_client_cancelupdate((dns_clientupdatetrans_t *)
1683 ddns_cb->transaction);
1684 }
1685 ddns_cb->lease = NULL;
1686 }
1687
1688 #endif /* NSUPDATE */
1689
1690 HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
1691 dns_zone_reference, dns_zone_dereference, do_case_hash)