]> git.ipfire.org Git - thirdparty/dhcp.git/blob - common/dns.c
012e4df8b82f4abb83240cc279dbcd1f389f5b7e
[thirdparty/dhcp.git] / common / dns.c
1 /* dns.c
2
3 Domain Name Service subroutines. */
4
5 /*
6 * Copyright (c) 2004-2019 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 2001-2003 by Internet Software Consortium
8 *
9 * This Source Code Form is subject to the terms of the Mozilla Public
10 * License, v. 2.0. If a copy of the MPL was not distributed with this
11 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 *
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.
20 *
21 * Internet Systems Consortium, Inc.
22 * PO Box 360
23 * Newmarket, NH 03857 USA
24 * <info@isc.org>
25 * https://www.isc.org/
26 *
27 */
28
29 /*! \file common/dns.c
30 */
31 #include "dhcpd.h"
32 #include "arpa/nameser.h"
33 #include <isc/md5.h>
34 #include <isc/sha2.h>
35 #include <dns/result.h>
36
37 /*
38 * This file contains code to connect the DHCP code to the libdns modules.
39 * As part of that function it maintains a database of zone cuts that can
40 * be used to figure out which server should be contacted to update any
41 * given domain name. Included in the zone information may be a pointer
42 * to a key in which case that key is used for the update. If no zone
43 * is found then the DNS code determines the zone on its own.
44 *
45 * The way this works is that you define the domain name to which an
46 * SOA corresponds, and the addresses of some primaries for that domain name:
47 *
48 * zone FOO.COM {
49 * primary 10.0.17.1;
50 * secondary 10.0.22.1, 10.0.23.1;
51 * key "FOO.COM Key";
52 * }
53 *
54 * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name
55 * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM",
56 * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*,
57 * looks for "FOO.COM", finds it. So it
58 * attempts the update to the primary for FOO.COM. If that times out, it
59 * tries the secondaries. You can list multiple primaries if you have some
60 * kind of magic name server that supports that. You shouldn't list
61 * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't
62 * support update forwarding, AFAIK). If no TSIG key is listed, the update
63 * is attempted without TSIG.
64 *
65 * You can also include IPv6 addresses via the primary6 and secondary6
66 * options. The search order for the addresses is primary, primary6,
67 * secondary and lastly secondary6, with a limit on the number of
68 * addresses used. Currently this limit is 3.
69 *
70 * The DHCP server tries to find an existing zone for any given name by
71 * trying to look up a local zone structure for each domain containing
72 * that name, all the way up to '.'. If it finds one cached, it tries
73 * to use that one to do the update. That's why it tries to update
74 * "FOO.COM" above, even though theoretically it should try GAZANGA...
75 * and TOPANGA... first.
76 *
77 * If the update fails with a predefined zone the zone is marked as bad
78 * and another search of the predefined zones is done. If no predefined
79 * zone is found finding a zone is left to the DNS module via examination
80 * of SOA records. If the DNS module finds a zone it may cache the zone
81 * but the zone won't be cached here.
82 *
83 * TSIG updates are not performed on zones found by the DNS module - if
84 * you want TSIG updates you _must_ write a zone definition linking the
85 * key to the zone. In cases where you know for sure what the key is
86 * but do not want to hardcode the IP addresses of the primary or
87 * secondaries, a zone declaration can be made that doesn't include any
88 * primary or secondary declarations. When the DHCP server encounters
89 * this while hunting up a matching zone for a name, it looks up the SOA,
90 * 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
93 * as if the zone record hadn't been found. Zones without IP addresses
94 * don't match when initially hunting for a zone to update.
95 *
96 * When an update is attempted and no predefined 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 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.
102 *
103 * TSIG keys are defined like this:
104 *
105 * key "FOO.COM Key" {
106 * algorithm HMAC-MD5.SIG-ALG.REG.INT;
107 * secret <Base64>;
108 * }
109 *
110 * <Base64> is a number expressed in base64 that represents the key.
111 * It's also permissible to use a quoted string here - this will be
112 * translated as the ASCII bytes making up the string, and will not
113 * include any NUL termination. The key name can be any text string,
114 * and the key type must be one of the key types defined in the draft
115 * or by the IANA. Currently only the HMAC-MD5... key type is
116 * supported.
117 *
118 * The DDNS processing has been split into two areas. One is the
119 * control code that determines what should be done. That code is found
120 * in the client or server directories. The other is the common code
121 * that performs functions such as properly formatting the arguments.
122 * That code is found in this file. The basic processing flow for a
123 * DDNS update is:
124 * In the client or server code determine what needs to be done and
125 * collect the necesary information then pass it to a function from
126 * this file.
127 * In this code lookup the zone and extract the zone and key information
128 * (if available) and prepare the arguments for the DNS module.
129 * When the DNS module completes its work (times out or gets a reply)
130 * it will trigger another function here which does generic processing
131 * and then passes control back to the code from the server or client.
132 * The server or client code then determines the next step which may
133 * result in another call to this module in which case the process repeats.
134 */
135
136 dns_zone_hash_t *dns_zone_hash;
137
138 /*
139 * DHCP dns structures
140 * Normally the relationship between these structures isn't one to one
141 * but in the DHCP case it (mostly) is. To make the allocations, frees,
142 * and passing of the memory easier we make a single structure with all
143 * the pieces.
144 *
145 * The maximum size of the data buffer should be large enough for any
146 * items DHCP will generate
147 */
148
149 typedef struct dhcp_ddns_rdata {
150 dns_rdata_t rdata;
151 dns_rdatalist_t rdatalist;
152 dns_rdataset_t rdataset;
153 } dhcp_ddns_data_t;
154
155 /* Function pointer type for functions which build DDNS update contents */
156 typedef isc_result_t (*builder_func_t)(dhcp_ddns_cb_t *ddns_cb,
157 dhcp_ddns_data_t *dataspace,
158 dns_name_t *pname, dns_name_t *uname);
159
160 #if defined (NSUPDATE)
161 #if defined (DNS_ZONE_LOOKUP)
162
163 /*
164 * The structure used to find a nameserver if there wasn't a zone entry.
165 * Currently we assume we won't have many of these outstanding at any
166 * time so we go with a simple linked list.
167 * In use find_zone_start() will fill in the oname with the name
168 * requested by the DDNS code. zname will point to it and be
169 * advanced as labels are removed. If the DNS client code returns
170 * a set of name servers eventp and rdataset will be set. Then
171 * the code will walk through the nameservers in namelist and
172 * find addresses that are stored in addrs and addrs6.
173 */
174
175 typedef struct dhcp_ddns_ns {
176 struct dhcp_ddns_ns *next;
177 struct data_string oname; /* the original name for DDNS */
178 char *zname; /* a pointer into the original name for
179 the zone we are checking */
180 dns_clientresevent_t *eventp; /* pointer to the event that provided the
181 namelist, we can't free the eventp
182 until we free the namelist */
183 dns_name_t *ns_name; /* current name server we are examining */
184 dns_rdataset_t *rdataset;
185 dns_rdatatype_t rdtype; /* type of address we want */
186
187 struct in_addr addrs[DHCP_MAXNS]; /* space for v4 addresses */
188 struct in6_addr addrs6[DHCP_MAXNS]; /* space for v6 addresses */
189 int num_addrs;
190 int num_addrs6;
191 int ttl;
192
193 void *transaction; /* transaction id for DNS calls */
194 } dhcp_ddns_ns_t;
195
196 /*
197 * The list of DDNS names for which we are attempting to find a name server.
198 * This list is used for finding the name server, it doesn't include the
199 * information necessary to do the DDNS request after finding a name server.
200 * The code attempts to minimize duplicate requests by examining the list
201 * to see if we are already trying to find a substring of the new request.
202 * For example imagine the first request is "a.b.c.d.e." and the server has
203 * already discarded the first two lables and is trying "c.d.e.". If the
204 * next request is for "x.y.c.d.e." the code assumes the in progress
205 * request is sufficient and doesn't add a new request for the second name.
206 * If the next request was for "x.y.z.d.e." the code doesn't assume they
207 * will use the same nameserver and starts a second request.
208 * This strategy will not eliminate all duplicates but is simple and
209 * should be sufficient.
210 */
211 dhcp_ddns_ns_t *dns_outstanding_ns = NULL;
212
213 /*
214 * Routines to manipulate the list of outstanding searches
215 *
216 * add_to_ns_queue() - adds the given control block to the queue
217 *
218 * remove_from_ns_queue() - removes the given control block from
219 * the queue
220 *
221 * find_in_ns_queue() compares the name from the given control
222 * block with the control blocks in the queue. It returns
223 * success if a matching entry is found. In order to match
224 * the entry already on the queue must be shorter than the
225 * incoming name must match the ending substring of the name.
226 */
227
228 void
229 add_to_ns_queue(dhcp_ddns_ns_t *ns_cb)
230 {
231 ns_cb->next = dns_outstanding_ns;
232 dns_outstanding_ns = ns_cb;
233 }
234
235
236 void
237 remove_from_ns_queue(dhcp_ddns_ns_t *ns_cb)
238 {
239 dhcp_ddns_ns_t **foo;
240
241 foo = &dns_outstanding_ns;
242 while (*foo) {
243 if (*foo == ns_cb) {
244 *foo = ns_cb->next;
245 break;
246 }
247 foo = &((*foo)->next);
248 }
249 ns_cb->next = NULL;
250 }
251
252 isc_result_t
253 find_in_ns_queue(dhcp_ddns_ns_t *ns_cb)
254 {
255 dhcp_ddns_ns_t *temp_cb;
256 int in_len, temp_len;
257
258 in_len = strlen(ns_cb->zname);
259
260 for(temp_cb = dns_outstanding_ns;
261 temp_cb != NULL;
262 temp_cb = temp_cb->next) {
263 temp_len = strlen(temp_cb->zname);
264 if (temp_len > in_len)
265 continue;
266 if (strcmp(temp_cb->zname,
267 ns_cb->zname + (in_len - temp_len)) == 0)
268 return(ISC_R_SUCCESS);
269 }
270 return(ISC_R_NOTFOUND);
271 }
272
273 void cache_found_zone (dhcp_ddns_ns_t *);
274 #endif
275
276 void ddns_interlude(isc_task_t *, isc_event_t *);
277
278 #if defined (TRACING)
279 /*
280 * Code to support tracing DDNS packets. We trace packets going to and
281 * coming from the libdns code but don't try to track the packets
282 * exchanged between the libdns code and the dns server(s) it contacts.
283 *
284 * The code is split into two sets of routines
285 * input refers to messages received from the dns module
286 * output refers to messages sent to the dns module
287 * Currently there are three routines in each set
288 * write is used to write information about the message to the trace file
289 * this routine is called directly from the proper place in the code.
290 * read is used to read information about a message from the trace file
291 * this routine is called from the trace loop as it reads through
292 * the file and is registered via the trace_type_register routine.
293 * When playing back a trace file we shall absorb records of output
294 * messages as part of processing the write function, therefore
295 * any output messages we encounter are flagged as errors.
296 * stop isn't currently used in this code but is needed for the register
297 * routine.
298 *
299 * We pass a pointer to a control block to the dns module which it returns
300 * to use as part of the result. As the pointer may vary between traces
301 * we need to map between those from the trace file and the new ones during
302 * playback.
303 *
304 * The mapping is complicated a little as a pointer could be 4 or 8 bytes
305 * long. We treat the old pointer as an 8 byte quantity and pad and compare
306 * as necessary.
307 */
308
309 /*
310 * Structure used to map old pointers to new pointers.
311 * Old pointers are 8 bytes long as we don't know if the trace was
312 * done on a 64 bit or 32 bit machine.
313 */
314 #define TRACE_PTR_LEN 8
315
316 typedef struct dhcp_ddns_map {
317 char old_pointer[TRACE_PTR_LEN];
318 void *new_pointer;
319 struct dhcp_ddns_map *next;
320 } dhcp_ddns_map_t;
321
322 /* The starting point for the map structure */
323 static dhcp_ddns_map_t *ddns_map;
324
325 trace_type_t *trace_ddns_input;
326 trace_type_t *trace_ddns_output;
327
328 /*
329 * The data written to the trace file is:
330 * 32 bits result from dns
331 * 64 bits pointer of cb
332 */
333
334 void
335 trace_ddns_input_write(dhcp_ddns_cb_t *ddns_cb, isc_result_t result)
336 {
337 trace_iov_t iov[2];
338 u_int32_t old_result;
339 char old_pointer[TRACE_PTR_LEN];
340
341 old_result = htonl((u_int32_t)result);
342 memset(old_pointer, 0, TRACE_PTR_LEN);
343 memcpy(old_pointer, &ddns_cb, sizeof(ddns_cb));
344
345 iov[0].len = sizeof(old_result);
346 iov[0].buf = (char *)&old_result;
347 iov[1].len = TRACE_PTR_LEN;
348 iov[1].buf = old_pointer;
349 trace_write_packet_iov(trace_ddns_input, 2, iov, MDL);
350 }
351
352 /*
353 * Process the result and pointer from the trace file.
354 * We use the pointer map to find the proper pointer for this instance.
355 * Then we need to construct an event to pass along to the interlude
356 * function.
357 */
358 static void
359 trace_ddns_input_read(trace_type_t *ttype, unsigned length,
360 char *buf)
361 {
362 u_int32_t old_result;
363 char old_pointer[TRACE_PTR_LEN];
364 dns_clientupdateevent_t *eventp;
365 void *new_pointer;
366 dhcp_ddns_map_t *ddns_map_ptr;
367
368 if (length < (sizeof(old_result) + TRACE_PTR_LEN)) {
369 log_error("trace_ddns_input_read: data too short");
370 return;
371 }
372
373 memcpy(&old_result, buf, sizeof(old_result));
374 memcpy(old_pointer, buf + sizeof(old_result), TRACE_PTR_LEN);
375
376 /* map the old pointer to a new pointer */
377 for (ddns_map_ptr = ddns_map;
378 ddns_map_ptr != NULL;
379 ddns_map_ptr = ddns_map_ptr->next) {
380 if ((ddns_map_ptr->new_pointer != NULL) &&
381 memcmp(ddns_map_ptr->old_pointer,
382 old_pointer, TRACE_PTR_LEN) == 0) {
383 new_pointer = ddns_map_ptr->new_pointer;
384 ddns_map_ptr->new_pointer = NULL;
385 memset(ddns_map_ptr->old_pointer, 0, TRACE_PTR_LEN);
386 break;
387 }
388 }
389 if (ddns_map_ptr == NULL) {
390 log_error("trace_dns_input_read: unable to map cb pointer");
391 return;
392 }
393
394 eventp = (dns_clientupdateevent_t *)
395 isc_event_allocate(dhcp_gbl_ctx.mctx,
396 dhcp_gbl_ctx.task,
397 0,
398 ddns_interlude,
399 new_pointer,
400 sizeof(dns_clientupdateevent_t));
401 if (eventp == NULL) {
402 log_error("trace_ddns_input_read: unable to allocate event");
403 return;
404 }
405 eventp->result = ntohl(old_result);
406
407
408 ddns_interlude(dhcp_gbl_ctx.task, (isc_event_t *)eventp);
409
410 return;
411 }
412
413 static void
414 trace_ddns_input_stop(trace_type_t *ttype)
415 {
416 }
417
418 /*
419 * We use the same arguments as for the dns startupdate function to
420 * allows us to choose between the two via a macro. If tracing isn't
421 * in use we simply call the dns function directly.
422 *
423 * If we are doing playback we read the next packet from the file
424 * and compare the type. If it matches we extract the results and pointer
425 * from the trace file. The results are returned to the caller as if
426 * they had called the dns routine. The pointer is used to construct a
427 * map for when the "reply" is processed.
428 *
429 * The data written to trace file is:
430 * 32 bits result
431 * 64 bits pointer of cb (DDNS Control block)
432 * contents of cb
433 */
434
435 isc_result_t
436 trace_ddns_output_write(dns_client_t *client, dns_rdataclass_t rdclass,
437 dns_name_t *zonename, dns_namelist_t *prerequisites,
438 dns_namelist_t *updates, isc_sockaddrlist_t *servers,
439 dns_tsec_t *tsec, unsigned int options,
440 isc_task_t *task, isc_taskaction_t action, void *arg,
441 dns_clientupdatetrans_t **transp)
442 {
443 isc_result_t result;
444 u_int32_t old_result;
445 char old_pointer[TRACE_PTR_LEN];
446 dhcp_ddns_map_t *ddns_map_ptr;
447
448 if (trace_playback() != 0) {
449 /* We are doing playback, extract the entry from the file */
450 unsigned buflen = 0;
451 char *inbuf = NULL;
452
453 result = trace_get_packet(&trace_ddns_output,
454 &buflen, &inbuf);
455 if (result != ISC_R_SUCCESS) {
456 log_error("trace_ddns_output_write: no input found");
457 return (ISC_R_FAILURE);
458 }
459 if (buflen < (sizeof(old_result) + TRACE_PTR_LEN)) {
460 log_error("trace_ddns_output_write: data too short");
461 dfree(inbuf, MDL);
462 return (ISC_R_FAILURE);
463 }
464 memcpy(&old_result, inbuf, sizeof(old_result));
465 result = ntohl(old_result);
466 memcpy(old_pointer, inbuf + sizeof(old_result), TRACE_PTR_LEN);
467 dfree(inbuf, MDL);
468
469 /* add the pointer to the pointer map */
470 for (ddns_map_ptr = ddns_map;
471 ddns_map_ptr != NULL;
472 ddns_map_ptr = ddns_map_ptr->next) {
473 if (ddns_map_ptr->new_pointer == NULL) {
474 break;
475 }
476 }
477
478 /*
479 * If we didn't find an empty entry, allocate an entry and
480 * link it into the list. The list isn't ordered.
481 */
482 if (ddns_map_ptr == NULL) {
483 ddns_map_ptr = dmalloc(sizeof(*ddns_map_ptr), MDL);
484 if (ddns_map_ptr == NULL) {
485 log_error("trace_ddns_output_write: "
486 "unable to allocate map entry");
487 return(ISC_R_FAILURE);
488 }
489 ddns_map_ptr->next = ddns_map;
490 ddns_map = ddns_map_ptr;
491 }
492
493 memcpy(ddns_map_ptr->old_pointer, old_pointer, TRACE_PTR_LEN);
494 ddns_map_ptr->new_pointer = arg;
495 }
496 else {
497 /* We aren't doing playback, make the actual call */
498 result = dns_client_startupdate(client, rdclass, zonename,
499 prerequisites, updates,
500 servers, tsec, options,
501 task, action, arg, transp);
502 }
503
504 if (trace_record() != 0) {
505 /* We are recording, save the information to the file */
506 trace_iov_t iov[3];
507 old_result = htonl((u_int32_t)result);
508 memset(old_pointer, 0, TRACE_PTR_LEN);
509 memcpy(old_pointer, &arg, sizeof(arg));
510 iov[0].len = sizeof(old_result);
511 iov[0].buf = (char *)&old_result;
512 iov[1].len = TRACE_PTR_LEN;
513 iov[1].buf = old_pointer;
514
515 /* Write out the entire cb, in case we want to look at it */
516 iov[2].len = sizeof(dhcp_ddns_cb_t);
517 iov[2].buf = (char *)arg;
518
519 trace_write_packet_iov(trace_ddns_output, 3, iov, MDL);
520 }
521
522 return(result);
523 }
524
525 static void
526 trace_ddns_output_read(trace_type_t *ttype, unsigned length,
527 char *buf)
528 {
529 log_error("unaccounted for ddns output.");
530 }
531
532 static void
533 trace_ddns_output_stop(trace_type_t *ttype)
534 {
535 }
536
537 void
538 trace_ddns_init()
539 {
540 trace_ddns_output = trace_type_register("ddns-output", NULL,
541 trace_ddns_output_read,
542 trace_ddns_output_stop, MDL);
543 trace_ddns_input = trace_type_register("ddns-input", NULL,
544 trace_ddns_input_read,
545 trace_ddns_input_stop, MDL);
546 ddns_map = NULL;
547 }
548
549 #define ddns_update trace_ddns_output_write
550 #else
551 #define ddns_update dns_client_startupdate
552 #endif /* TRACING */
553
554 #define zone_resolve dns_client_startresolve
555
556 /*
557 * Code to allocate and free a dddns control block. This block is used
558 * to pass and track the information associated with a DDNS update request.
559 */
560 dhcp_ddns_cb_t *
561 ddns_cb_alloc(const char *file, int line)
562 {
563 dhcp_ddns_cb_t *ddns_cb;
564 int i;
565
566 ddns_cb = dmalloc(sizeof(*ddns_cb), file, line);
567 if (ddns_cb != NULL) {
568 ISC_LIST_INIT(ddns_cb->zone_server_list);
569 for (i = 0; i < DHCP_MAXNS; i++) {
570 ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
571 }
572 }
573
574 #if defined (DEBUG_DNS_UPDATES)
575 log_info("%s(%d): Allocating ddns_cb=%p", file, line, ddns_cb);
576 #endif
577
578 return(ddns_cb);
579 }
580
581 void
582 ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
583 {
584 #if defined (DEBUG_DNS_UPDATES)
585 log_info("%s(%d): freeing ddns_cb=%p", file, line, ddns_cb);
586 #endif
587
588 data_string_forget(&ddns_cb->fwd_name, file, line);
589 data_string_forget(&ddns_cb->rev_name, file, line);
590 data_string_forget(&ddns_cb->dhcid, file, line);
591
592 if (ddns_cb->zone != NULL) {
593 forget_zone((struct dns_zone **)&ddns_cb->zone);
594 }
595
596 /* Should be freed by now, check just in case. */
597 if (ddns_cb->transaction != NULL) {
598 log_error("Impossible memory leak at %s:%d (attempt to free "
599 "DDNS Control Block before transaction).", MDL);
600 }
601
602 /* Should be freed by now, check just in case. */
603 if (ddns_cb->fixed6_ia) {
604 log_error("Possible memory leak at %s:%d (attempt to free "
605 "DDNS Control Block before fxed6_ia).", MDL);
606 }
607
608 dfree(ddns_cb, file, line);
609 }
610
611 void
612 ddns_cb_forget_zone(dhcp_ddns_cb_t *ddns_cb)
613 {
614 int i;
615
616 forget_zone(&ddns_cb->zone);
617 ddns_cb->zone_name[0] = 0;
618 ISC_LIST_INIT(ddns_cb->zone_server_list);
619 for (i = 0; i < DHCP_MAXNS; i++) {
620 ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
621 }
622 }
623 #endif
624
625 isc_result_t remove_dns_zone (struct dns_zone *zone)
626 {
627 struct dns_zone *tz = NULL;
628
629 if (dns_zone_hash) {
630 dns_zone_hash_lookup(&tz, dns_zone_hash, zone->name, 0, MDL);
631 if (tz != NULL) {
632 dns_zone_hash_delete(dns_zone_hash, tz->name, 0, MDL);
633 dns_zone_dereference(&tz, MDL);
634 }
635 }
636
637 return (ISC_R_SUCCESS);
638 }
639
640 isc_result_t enter_dns_zone (struct dns_zone *zone)
641 {
642 struct dns_zone *tz = (struct dns_zone *)0;
643
644 if (dns_zone_hash) {
645 dns_zone_hash_lookup (&tz,
646 dns_zone_hash, zone -> name, 0, MDL);
647 if (tz == zone) {
648 dns_zone_dereference (&tz, MDL);
649 return ISC_R_SUCCESS;
650 }
651 if (tz) {
652 dns_zone_hash_delete (dns_zone_hash,
653 zone -> name, 0, MDL);
654 dns_zone_dereference (&tz, MDL);
655 }
656 } else {
657 if (!dns_zone_new_hash(&dns_zone_hash, DNS_HASH_SIZE, MDL))
658 return ISC_R_NOMEMORY;
659 }
660
661 dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
662 return ISC_R_SUCCESS;
663 }
664
665 isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
666 {
667 int len;
668 char *tname = (char *)0;
669 isc_result_t status;
670
671 if (!dns_zone_hash)
672 return ISC_R_NOTFOUND;
673
674 len = strlen (name);
675 if (name [len - 1] != '.') {
676 tname = dmalloc ((unsigned)len + 2, MDL);
677 if (!tname)
678 return ISC_R_NOMEMORY;
679 strcpy (tname, name);
680 tname [len] = '.';
681 tname [len + 1] = 0;
682 name = tname;
683 }
684 if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL))
685 status = ISC_R_NOTFOUND;
686 else if ((*zone)->timeout && (*zone)->timeout < cur_time) {
687 dns_zone_hash_delete(dns_zone_hash, (*zone)->name, 0, MDL);
688 dns_zone_dereference(zone, MDL);
689 status = ISC_R_NOTFOUND;
690 } else
691 status = ISC_R_SUCCESS;
692
693 if (tname)
694 dfree (tname, MDL);
695 return status;
696 }
697
698 int dns_zone_dereference (ptr, file, line)
699 struct dns_zone **ptr;
700 const char *file;
701 int line;
702 {
703 struct dns_zone *dns_zone;
704
705 if ((ptr == NULL) || (*ptr == NULL)) {
706 log_error("%s(%d): null pointer", file, line);
707 #if defined (POINTER_DEBUG)
708 abort();
709 #else
710 return (0);
711 #endif
712 }
713
714 dns_zone = *ptr;
715 *ptr = NULL;
716 --dns_zone->refcnt;
717 rc_register(file, line, ptr, dns_zone, dns_zone->refcnt, 1, RC_MISC);
718 if (dns_zone->refcnt > 0)
719 return (1);
720
721 if (dns_zone->refcnt < 0) {
722 log_error("%s(%d): negative refcnt!", file, line);
723 #if defined (DEBUG_RC_HISTORY)
724 dump_rc_history(dns_zone);
725 #endif
726 #if defined (POINTER_DEBUG)
727 abort();
728 #else
729 return (0);
730 #endif
731 }
732
733 if (dns_zone->name)
734 dfree(dns_zone->name, file, line);
735 if (dns_zone->key)
736 omapi_auth_key_dereference(&dns_zone->key, file, line);
737 if (dns_zone->primary)
738 option_cache_dereference(&dns_zone->primary, file, line);
739 if (dns_zone->secondary)
740 option_cache_dereference(&dns_zone->secondary, file, line);
741 if (dns_zone->primary6)
742 option_cache_dereference(&dns_zone->primary6, file, line);
743 if (dns_zone->secondary6)
744 option_cache_dereference(&dns_zone->secondary6, file, line);
745 dfree(dns_zone, file, line);
746 return (1);
747 }
748
749 #if defined (NSUPDATE)
750 #if defined (DNS_ZONE_LOOKUP)
751
752 /* Helper function to copy the address from an rdataset to
753 * the nameserver control block. Mostly to avoid really long
754 * lines in the nested for loops
755 */
756 void
757 zone_addr_to_ns(dhcp_ddns_ns_t *ns_cb,
758 dns_rdataset_t *rdataset)
759 {
760 dns_rdata_t rdata;
761 dns_rdata_in_a_t a;
762 dns_rdata_in_aaaa_t aaaa;
763
764 dns_rdata_init(&rdata);
765 dns_rdataset_current(rdataset, &rdata);
766 switch (rdataset->type) {
767 case dns_rdatatype_a:
768 (void) dns_rdata_tostruct(&rdata, &a, NULL);
769 memcpy(&ns_cb->addrs[ns_cb->num_addrs], &a.in_addr, 4);
770 ns_cb->num_addrs++;
771 dns_rdata_freestruct(&a);
772 break;
773 case dns_rdatatype_aaaa:
774 (void) dns_rdata_tostruct(&rdata, &aaaa, NULL);
775 memcpy(&ns_cb->addrs6[ns_cb->num_addrs6], &aaaa.in6_addr, 16);
776 ns_cb->num_addrs6++;
777 dns_rdata_freestruct(&aaaa);
778 break;
779 default:
780 break;
781 }
782
783 if ((ns_cb->ttl == 0) || (ns_cb->ttl > rdataset->ttl))
784 ns_cb->ttl = rdataset->ttl;
785 }
786
787 /*
788 * The following three routines co-operate to find the addresses of
789 * the nameservers to use for a zone if we don't have a zone statement.
790 * We strongly suggest the use of a zone statement to avoid problmes
791 * and to allow for the use of TSIG and therefore better security, but
792 * include this functionality for those that don't want such statements.
793 *
794 * find_zone_start(ddns_cb, direction)
795 * This is the first of the routines, it is called from the rest of
796 * the ddns code when we have received a request for DDNS for a name
797 * and don't have a zone entry that would cover that name. The name
798 * is in the ddns_cb as specified by the direction (forward or reverse).
799 * The start function pulls the name out and constructs the name server
800 * block then starts the process by calling the DNS client code.
801 *
802 * find_zone_ns(taskp, eventp)
803 * This is the second step of the process. The DNS client code will
804 * call this when it has gotten a response or timed out. If the response
805 * doesn't have a list of nameservers we remove another label from the
806 * zone name and try again. If the response does include a list of
807 * nameservers we start walking through the list attempting to get
808 * addresses for the nameservers.
809 *
810 * find_zone_addrs(taskp, eventp)
811 * This is the third step of the process. In find_zone_ns we got
812 * a list of nameserves and started walking through them. This continues
813 * the walk and if we get back any addresses it adds them to our list.
814 * When we get enough addresses or run out of nameservers we construct
815 * a zone entry and insert it into the zone hash for the rest of the
816 * DDNS code to use.
817 */
818 void
819 find_zone_addrs(isc_task_t *taskp,
820 isc_event_t *eventp)
821 {
822 dns_clientresevent_t *ddns_event = (dns_clientresevent_t *)eventp;
823 dhcp_ddns_ns_t *ns_cb = (dhcp_ddns_ns_t *)eventp->ev_arg;
824 dns_name_t *ns_name = NULL;
825 dns_rdataset_t *rdataset;
826 isc_result_t result;
827 dns_name_t *name;
828 dns_rdata_t rdata = DNS_RDATA_INIT;
829 dns_rdata_ns_t ns;
830
831
832 /* the transaction is done, get rid of the tag */
833 dns_client_destroyrestrans(&ns_cb->transaction);
834
835 /* If we succeeded we try and extract the addresses, if we can
836 * and we have enough we are done. If we didn't succeed or
837 * we don't have enough addresses afterwards we drop through
838 * and try the next item on the list.
839 */
840 if (ddns_event->result == ISC_R_SUCCESS) {
841
842 for (name = ISC_LIST_HEAD(ddns_event->answerlist);
843 name != NULL;
844 name = ISC_LIST_NEXT(name, link)) {
845
846 for (rdataset = ISC_LIST_HEAD(name->list);
847 rdataset != NULL;
848 rdataset = ISC_LIST_NEXT(rdataset, link)) {
849
850 for (result = dns_rdataset_first(rdataset);
851 result == ISC_R_SUCCESS;
852 result = dns_rdataset_next(rdataset)) {
853
854 /* add address to cb */
855 zone_addr_to_ns(ns_cb, rdataset);
856
857 /* We are done if we have
858 * enough addresses
859 */
860 if (ns_cb->num_addrs +
861 ns_cb->num_addrs6 >= DHCP_MAXNS)
862 goto done;
863 }
864 }
865 }
866 }
867
868 /* We need more addresses.
869 * We restart the loop we were in before.
870 */
871
872 for (ns_name = ns_cb->ns_name;
873 ns_name != NULL;
874 ns_name = ISC_LIST_NEXT(ns_name, link)) {
875
876 if (ns_name == ns_cb->ns_name) {
877 /* first time through, use saved state */
878 rdataset = ns_cb->rdataset;
879 } else {
880 rdataset = ISC_LIST_HEAD(ns_name->list);
881 }
882
883 for (;
884 rdataset != NULL;
885 rdataset = ISC_LIST_NEXT(rdataset, link)) {
886
887 if (rdataset->type != dns_rdatatype_ns)
888 continue;
889 dns_rdata_init(&rdata);
890
891 if (rdataset == ns_cb->rdataset) {
892 /* first time through use the saved state */
893 if (ns_cb->rdtype == dns_rdatatype_a) {
894 ns_cb->rdtype = dns_rdatatype_aaaa;
895 } else {
896 ns_cb->rdtype = dns_rdatatype_a;
897 if (dns_rdataset_next(rdataset) !=
898 ISC_R_SUCCESS)
899 continue;
900 }
901 } else {
902 if ((!dns_rdataset_isassociated(rdataset)) ||
903 (dns_rdataset_first(rdataset) !=
904 ISC_R_SUCCESS))
905 continue;
906 }
907
908 dns_rdataset_current(rdataset, &rdata);
909 if (dns_rdata_tostruct(&rdata, &ns, NULL) !=
910 ISC_R_SUCCESS)
911 continue;
912
913 /* Save our current state */
914 ns_cb->ns_name = ns_name;
915 ns_cb->rdataset = rdataset;
916
917 /* And call out to DNS */
918 result = zone_resolve(dhcp_gbl_ctx.dnsclient, &ns.name,
919 dns_rdataclass_in,
920 ns_cb->rdtype,
921 DNS_CLIENTRESOPT_NODNSSEC,
922 dhcp_gbl_ctx.task,
923 find_zone_addrs,
924 (void *)ns_cb,
925 &ns_cb->transaction);
926
927 /* do we need to clean this? */
928 dns_rdata_freestruct(&ns);
929
930 if (result == ISC_R_SUCCESS)
931 /* we have started the next step, cleanup
932 * the structures associated with this call
933 * but leave the cb for the next round
934 */
935 goto cleanup;
936
937 log_error("find_zone_ns: unable to continue "
938 "resolve: %s %s",
939 ns_cb->zname,
940 isc_result_totext(result));
941
942 /* The call to start a resolve transaction failed,
943 * should we try to continue with any other names?
944 * For now let's not, but let's use whatever we
945 * may already have.
946 */
947 goto done;
948 }
949 }
950
951 done:
952 /* we've either gotten our max number of addresses or
953 * run out of nameservers to try. Convert the cb into
954 * a zone and insert it into the zone hash. Then
955 * we need to clean up the saved state.
956 */
957 if ((ns_cb->num_addrs != 0) ||
958 (ns_cb->num_addrs6 != 0))
959 cache_found_zone(ns_cb);
960
961 dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient,
962 &ns_cb->eventp->answerlist);
963 isc_event_free((isc_event_t **)&ns_cb->eventp);
964
965 remove_from_ns_queue(ns_cb);
966 data_string_forget(&ns_cb->oname, MDL);
967 dfree(ns_cb, MDL);
968
969 cleanup:
970 /* cleanup any of the new state information */
971
972 dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient,
973 &ddns_event->answerlist);
974 isc_event_free(&eventp);
975
976 return;
977
978 }
979
980 /*
981 * Routine to continue the process of finding a nameserver via the DNS
982 * This is routine is called when we are still trying to get a list
983 * of nameservers to process.
984 */
985
986 void
987 find_zone_ns(isc_task_t *taskp,
988 isc_event_t *eventp)
989 {
990 dns_clientresevent_t *ddns_event = (dns_clientresevent_t *)eventp;
991 dhcp_ddns_ns_t *ns_cb = (dhcp_ddns_ns_t *)eventp->ev_arg;
992 dns_fixedname_t zname0;
993 dns_name_t *zname = NULL, *ns_name = NULL;
994 dns_rdataset_t *rdataset;
995 isc_result_t result;
996 dns_rdata_t rdata = DNS_RDATA_INIT;
997 dns_rdata_ns_t ns;
998
999 /* the transaction is done, get rid of the tag */
1000 dns_client_destroyrestrans(&ns_cb->transaction);
1001
1002 if (ddns_event->result != ISC_R_SUCCESS) {
1003 /* We didn't find any nameservers, try again */
1004
1005 /* Remove a label and continue */
1006 ns_cb->zname = strchr(ns_cb->zname, '.');
1007 if ((ns_cb->zname == NULL) ||
1008 (ns_cb->zname[1] == 0)) {
1009 /* No more labels, all done */
1010 goto cleanup;
1011 }
1012 ns_cb->zname++;
1013
1014 /* Create a DNS version of the zone name and call the
1015 * resolver code */
1016 if (((result = dhcp_isc_name((unsigned char *)ns_cb->zname,
1017 &zname0, &zname))
1018 != ISC_R_SUCCESS) ||
1019 ((result = zone_resolve(dhcp_gbl_ctx.dnsclient,
1020 zname, dns_rdataclass_in,
1021 dns_rdatatype_ns,
1022 DNS_CLIENTRESOPT_NODNSSEC,
1023 dhcp_gbl_ctx.task,
1024 find_zone_ns,
1025 (void *)ns_cb,
1026 &ns_cb->transaction))
1027 != ISC_R_SUCCESS)) {
1028 log_error("find_zone_ns: Unable to build "
1029 "name or start resolve: %s %s",
1030 ns_cb->zname,
1031 isc_result_totext(result));
1032 goto cleanup;
1033 }
1034
1035 /* we have successfully started the next iteration
1036 * of this step, clean up from the call and continue */
1037 dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient,
1038 &ddns_event->answerlist);
1039 isc_event_free(&eventp);
1040 return;
1041 }
1042
1043 /* We did get a set of nameservers, save the information and
1044 * start trying to get addresses
1045 */
1046 ns_cb->eventp = ddns_event;
1047 for (ns_name = ISC_LIST_HEAD(ddns_event->answerlist);
1048 ns_name != NULL;
1049 ns_name = ISC_LIST_NEXT(ns_name, link)) {
1050
1051 for (rdataset = ISC_LIST_HEAD(ns_name->list);
1052 rdataset != NULL;
1053 rdataset = ISC_LIST_NEXT(rdataset, link)) {
1054
1055 if (rdataset->type != dns_rdatatype_ns)
1056 continue;
1057
1058 if ((!dns_rdataset_isassociated(rdataset)) ||
1059 (dns_rdataset_first(rdataset) !=
1060 ISC_R_SUCCESS))
1061 continue;
1062
1063 dns_rdataset_current(rdataset, &rdata);
1064 if (dns_rdata_tostruct(&rdata, &ns, NULL) !=
1065 ISC_R_SUCCESS)
1066 continue;
1067
1068 /* Save our current state */
1069 ns_cb->ns_name = ns_name;
1070 ns_cb->rdataset = rdataset;
1071
1072 /* And call out to DNS */
1073 result = zone_resolve(dhcp_gbl_ctx.dnsclient, &ns.name,
1074 dns_rdataclass_in,
1075 ns_cb->rdtype,
1076 DNS_CLIENTRESOPT_NODNSSEC,
1077 dhcp_gbl_ctx.task,
1078 find_zone_addrs,
1079 (void *)ns_cb,
1080 &ns_cb->transaction);
1081
1082 /* do we need to clean this? */
1083 dns_rdata_freestruct(&ns);
1084
1085 if (result == ISC_R_SUCCESS)
1086 /* We have successfully started the next step
1087 * we don't cleanup the eventp block as we are
1088 * still using it.
1089 */
1090 return;
1091
1092 log_error("find_zone_ns: unable to continue "
1093 "resolve: %s %s",
1094 ns_cb->zname,
1095 isc_result_totext(result));
1096
1097 /* The call to start a resolve transaction failed,
1098 * should we try to continue with any other names?
1099 * For now let's not
1100 */
1101 goto cleanup;
1102 }
1103 }
1104
1105 cleanup:
1106 /* When we add a queue to manage the DDNS
1107 * requests we will need to remove any that
1108 * were waiting for this resolution */
1109
1110 dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient,
1111 &ddns_event->answerlist);
1112 isc_event_free(&eventp);
1113
1114 remove_from_ns_queue(ns_cb);
1115
1116 data_string_forget(&ns_cb->oname, MDL);
1117 dfree(ns_cb, MDL);
1118 return;
1119
1120 }
1121
1122 /*
1123 * Start the process of finding nameservers via the DNS because
1124 * we don't have a zone entry already.
1125 * We construct a control block and fill in the DDNS name. As
1126 * the process continues we shall move the zname pointer to
1127 * indicate which labels we are still using. The rest of
1128 * the control block will be filled in as we continue processing.
1129 */
1130 isc_result_t
1131 find_zone_start(dhcp_ddns_cb_t *ddns_cb, int direction)
1132 {
1133 isc_result_t status = ISC_R_NOTFOUND;
1134 dhcp_ddns_ns_t *ns_cb;
1135 dns_fixedname_t zname0;
1136 dns_name_t *zname = NULL;
1137
1138 /*
1139 * We don't validate np as that was already done in find_cached_zone()
1140 */
1141
1142 /* Allocate the control block for this request */
1143 ns_cb = dmalloc(sizeof(*ns_cb), MDL);
1144 if (ns_cb == NULL) {
1145 log_error("find_zone_start: unable to allocate cb");
1146 return(ISC_R_FAILURE);
1147 }
1148 ns_cb->rdtype = dns_rdatatype_a;
1149
1150 /* Copy the data string so the NS lookup is independent of the DDNS */
1151 if (direction == FIND_FORWARD) {
1152 data_string_copy(&ns_cb->oname, &ddns_cb->fwd_name, MDL);
1153 } else {
1154 data_string_copy(&ns_cb->oname, &ddns_cb->rev_name, MDL);
1155 }
1156 ns_cb->zname = (char *)ns_cb->oname.data;
1157
1158 /*
1159 * Check the dns_outstanding_ns queue to see if we are
1160 * already processing something that would cover this name
1161 */
1162 if (find_in_ns_queue(ns_cb) == ISC_R_SUCCESS) {
1163 data_string_forget(&ns_cb->oname, MDL);
1164 dfree(ns_cb, MDL);
1165 return (ISC_R_SUCCESS);
1166 }
1167
1168 /* Create a DNS version of the zone name and call the
1169 * resolver code */
1170 if (((status = dhcp_isc_name((unsigned char *)ns_cb->zname,
1171 &zname0, &zname))
1172 != ISC_R_SUCCESS) ||
1173 ((status = zone_resolve(dhcp_gbl_ctx.dnsclient,
1174 zname, dns_rdataclass_in,
1175 dns_rdatatype_ns,
1176 DNS_CLIENTRESOPT_NODNSSEC,
1177 dhcp_gbl_ctx.task,
1178 find_zone_ns,
1179 (void *)ns_cb,
1180 &ns_cb->transaction))
1181 != ISC_R_SUCCESS)) {
1182 log_error("find_zone_start: Unable to build "
1183 "name or start resolve: %s %s",
1184 ns_cb->zname,
1185 isc_result_totext(status));
1186
1187 /* We failed to start the process, clean up */
1188 data_string_forget(&ns_cb->oname, MDL);
1189 dfree(ns_cb, MDL);
1190 } else {
1191 /* We started the process, attach the control block
1192 * to the queue */
1193 add_to_ns_queue(ns_cb);
1194 }
1195
1196 return (status);
1197 }
1198 #endif
1199
1200 isc_result_t
1201 find_cached_zone(dhcp_ddns_cb_t *ddns_cb, int direction)
1202 {
1203 isc_result_t status = ISC_R_NOTFOUND;
1204 const char *np;
1205 struct dns_zone *zone = NULL;
1206 struct data_string nsaddrs;
1207 struct in_addr zone_addr;
1208 struct in6_addr zone_addr6;
1209 int ix;
1210
1211 if (direction == FIND_FORWARD) {
1212 np = (const char *)ddns_cb->fwd_name.data;
1213 } else {
1214 np = (const char *)ddns_cb->rev_name.data;
1215 }
1216
1217 /* We can't look up a null zone. */
1218 if ((np == NULL) || (*np == '\0')) {
1219 return (DHCP_R_INVALIDARG);
1220 }
1221
1222 /*
1223 * For each subzone, try to find a cached zone.
1224 */
1225 for (;;) {
1226 status = dns_zone_lookup(&zone, np);
1227 if (status == ISC_R_SUCCESS)
1228 break;
1229
1230 np = strchr(np, '.');
1231 if (np == NULL)
1232 break;
1233 np++;
1234 }
1235
1236 if (status != ISC_R_SUCCESS)
1237 return (status);
1238
1239 /* Make sure the zone is valid, we've already gotten
1240 * rid of expired dynamic zones. Check to see if
1241 * we repudiated this zone. If so give up.
1242 */
1243 if ((zone->flags & DNS_ZONE_INACTIVE) != 0) {
1244 dns_zone_dereference(&zone, MDL);
1245 return (ISC_R_FAILURE);
1246 }
1247
1248 /* Make sure the zone name will fit. */
1249 if (strlen(zone->name) >= sizeof(ddns_cb->zone_name)) {
1250 dns_zone_dereference(&zone, MDL);
1251 return (ISC_R_NOSPACE);
1252 }
1253 strcpy((char *)&ddns_cb->zone_name[0], zone->name);
1254
1255 memset (&nsaddrs, 0, sizeof nsaddrs);
1256 ix = 0;
1257
1258 if (zone->primary) {
1259 if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
1260 NULL, NULL, &global_scope,
1261 zone->primary, MDL)) {
1262 int ip = 0;
1263 while (ix < DHCP_MAXNS) {
1264 if (ip + 4 > nsaddrs.len)
1265 break;
1266 memcpy(&zone_addr, &nsaddrs.data[ip], 4);
1267 isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
1268 &zone_addr,
1269 NS_DEFAULTPORT);
1270 ISC_LIST_APPEND(ddns_cb->zone_server_list,
1271 &ddns_cb->zone_addrs[ix],
1272 link);
1273 ip += 4;
1274 ix++;
1275 }
1276 data_string_forget(&nsaddrs, MDL);
1277 }
1278 }
1279
1280 if (zone->primary6) {
1281 if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
1282 NULL, NULL, &global_scope,
1283 zone->primary6, MDL)) {
1284 int ip = 0;
1285 while (ix < DHCP_MAXNS) {
1286 if (ip + 16 > nsaddrs.len)
1287 break;
1288 memcpy(&zone_addr6, &nsaddrs.data[ip], 16);
1289 isc_sockaddr_fromin6(&ddns_cb->zone_addrs[ix],
1290 &zone_addr6,
1291 NS_DEFAULTPORT);
1292 ISC_LIST_APPEND(ddns_cb->zone_server_list,
1293 &ddns_cb->zone_addrs[ix],
1294 link);
1295 ip += 16;
1296 ix++;
1297 }
1298 data_string_forget(&nsaddrs, MDL);
1299 }
1300 }
1301
1302 if (zone->secondary) {
1303 if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
1304 NULL, NULL, &global_scope,
1305 zone->secondary, MDL)) {
1306 int ip = 0;
1307 while (ix < DHCP_MAXNS) {
1308 if (ip + 4 > nsaddrs.len)
1309 break;
1310 memcpy(&zone_addr, &nsaddrs.data[ip], 4);
1311 isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
1312 &zone_addr,
1313 NS_DEFAULTPORT);
1314 ISC_LIST_APPEND(ddns_cb->zone_server_list,
1315 &ddns_cb->zone_addrs[ix],
1316 link);
1317 ip += 4;
1318 ix++;
1319 }
1320 data_string_forget (&nsaddrs, MDL);
1321 }
1322 }
1323
1324 if (zone->secondary6) {
1325 if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
1326 NULL, NULL, &global_scope,
1327 zone->secondary6, MDL)) {
1328 int ip = 0;
1329 while (ix < DHCP_MAXNS) {
1330 if (ip + 16 > nsaddrs.len)
1331 break;
1332 memcpy(&zone_addr6, &nsaddrs.data[ip], 16);
1333 isc_sockaddr_fromin6(&ddns_cb->zone_addrs[ix],
1334 &zone_addr6,
1335 NS_DEFAULTPORT);
1336 ISC_LIST_APPEND(ddns_cb->zone_server_list,
1337 &ddns_cb->zone_addrs[ix],
1338 link);
1339 ip += 16;
1340 ix++;
1341 }
1342 data_string_forget (&nsaddrs, MDL);
1343 }
1344 }
1345
1346 dns_zone_reference(&ddns_cb->zone, zone, MDL);
1347 dns_zone_dereference (&zone, MDL);
1348 return ISC_R_SUCCESS;
1349 }
1350
1351 void forget_zone (struct dns_zone **zone)
1352 {
1353 dns_zone_dereference (zone, MDL);
1354 }
1355
1356 void repudiate_zone (struct dns_zone **zone)
1357 {
1358 /* verify that we have a pointer at least */
1359 if ((zone == NULL) || (*zone == NULL)) {
1360 log_info("Null argument to repudiate zone");
1361 return;
1362 }
1363
1364 (*zone)->flags |= DNS_ZONE_INACTIVE;
1365 dns_zone_dereference(zone, MDL);
1366 }
1367
1368 #if defined (DNS_ZONE_LOOKUP)
1369 void cache_found_zone(dhcp_ddns_ns_t *ns_cb)
1370 {
1371 struct dns_zone *zone = NULL;
1372 int len, remove_zone = 0;
1373
1374 /* See if there's already such a zone. */
1375 if (dns_zone_lookup(&zone, ns_cb->zname) == ISC_R_SUCCESS) {
1376 /* If it's not a dynamic zone, leave it alone. */
1377 if (zone->timeout == 0) {
1378 goto cleanup;
1379 }
1380
1381 /* Remove any old addresses in case they've changed */
1382 if (zone->primary)
1383 option_cache_dereference(&zone->primary, MDL);
1384 if (zone->primary6)
1385 option_cache_dereference(&zone->primary6, MDL);
1386
1387 /* Set the flag to remove the zone from the hash if
1388 we have problems */
1389 remove_zone = 1;
1390 } else if (dns_zone_allocate(&zone, MDL) == 0) {
1391 return;
1392 } else {
1393 /* We've just allocated the zone, now we need
1394 * to allocate space for the name and addresses
1395 */
1396
1397 /* allocate space for the name */
1398 len = strlen(ns_cb->zname);
1399 zone->name = dmalloc(len + 2, MDL);
1400 if (zone->name == NULL) {
1401 goto cleanup;
1402 }
1403
1404 /* Copy the name and add a trailing '.' if necessary */
1405 strcpy(zone->name, ns_cb->zname);
1406 if (zone->name[len-1] != '.') {
1407 zone->name[len] = '.';
1408 zone->name[len+1] = 0;
1409 }
1410 }
1411
1412 zone->timeout = cur_time + ns_cb->ttl;
1413
1414 if (ns_cb->num_addrs != 0) {
1415 len = ns_cb->num_addrs * sizeof(struct in_addr);
1416 if ((!option_cache_allocate(&zone->primary, MDL)) ||
1417 (!buffer_allocate(&zone->primary->data.buffer,
1418 len, MDL))) {
1419 if (remove_zone == 1)
1420 remove_dns_zone(zone);
1421 goto cleanup;
1422 }
1423 memcpy(zone->primary->data.buffer->data, ns_cb->addrs, len);
1424 zone->primary->data.data =
1425 &zone->primary->data.buffer->data[0];
1426 zone->primary->data.len = len;
1427 }
1428 if (ns_cb->num_addrs6 != 0) {
1429 len = ns_cb->num_addrs6 * sizeof(struct in6_addr);
1430 if ((!option_cache_allocate(&zone->primary6, MDL)) ||
1431 (!buffer_allocate(&zone->primary6->data.buffer,
1432 len, MDL))) {
1433 if (remove_zone == 1)
1434 remove_dns_zone(zone);
1435 goto cleanup;
1436 }
1437 memcpy(zone->primary6->data.buffer->data, ns_cb->addrs6, len);
1438 zone->primary6->data.data =
1439 &zone->primary6->data.buffer->data[0];
1440 zone->primary6->data.len = len;
1441 }
1442
1443 enter_dns_zone(zone);
1444
1445 cleanup:
1446 dns_zone_dereference(&zone, MDL);
1447 return;
1448 }
1449 #endif
1450
1451 /*!
1452 * \brief Create an id for a client
1453 *
1454 * This function is used to create an id for a client to use with DDNS
1455 * This version of the function is for the standard style, RFC 4701
1456 *
1457 * This function takes information from the type and data fields and
1458 * mangles it into a dhcid string which it places in ddns_cb. It also
1459 * sets a field in ddns_cb to specify the class that should be used
1460 * when sending the dhcid, in this case it is a DHCID record so we use
1461 * dns_rdatatype_dhcid
1462 *
1463 * The DHCID we construct is:
1464 * 2 bytes - identifier type (see 4701 and IANA)
1465 * 1 byte - digest type, currently only SHA256 (1)
1466 * n bytes - digest, length depends on digest type, currently 32 for
1467 * SHA256
1468 *
1469 * What we base the digest on is up to the calling code for an id type of
1470 * 0 - 1 octet htype followed by hlen octets of chaddr from v4 client request
1471 * 1 - data octets from a dhcpv4 client's client identifier option
1472 * 2 - the client DUID from a v4 or v6 client's client id option
1473 * This identifier is concatenated with the fqdn and the result is digested.
1474 */
1475 int get_std_dhcid(dhcp_ddns_cb_t *ddns_cb,
1476 int type,
1477 const u_int8_t *identifier,
1478 unsigned id_len)
1479 {
1480 struct data_string *id = &ddns_cb->dhcid;
1481 isc_sha256_t sha256;
1482 unsigned char buf[ISC_SHA256_DIGESTLENGTH];
1483 unsigned char fwd_buf[256];
1484 unsigned fwd_buflen = 0;
1485
1486 /* Types can only be 0..(2^16)-1. */
1487 if (type < 0 || type > 65535)
1488 return (0);
1489
1490 /* We need to convert the fwd name to wire representation */
1491 if (MRns_name_pton((char *)ddns_cb->fwd_name.data, fwd_buf, 256) == -1)
1492 return (0);
1493 while(fwd_buf[fwd_buflen] != 0) {
1494 fwd_buflen += fwd_buf[fwd_buflen] + 1;
1495 }
1496 fwd_buflen++;
1497
1498 if (!buffer_allocate(&id->buffer,
1499 ISC_SHA256_DIGESTLENGTH + 2 + 1,
1500 MDL))
1501 return (0);
1502 id->data = id->buffer->data;
1503
1504 /* The two first bytes contain the type identifier. */
1505 putUShort(id->buffer->data, (unsigned)type);
1506
1507 /* The next is the digest type, SHA-256 is 1 */
1508 putUChar(id->buffer->data + 2, 1u);
1509
1510 /* Computing the digest */
1511 isc_sha256_init(&sha256);
1512 isc_sha256_update(&sha256, identifier, id_len);
1513 isc_sha256_update(&sha256, fwd_buf, fwd_buflen);
1514 isc_sha256_final(buf, &sha256);
1515
1516 memcpy(id->buffer->data + 3, &buf, ISC_SHA256_DIGESTLENGTH);
1517
1518 id->len = ISC_SHA256_DIGESTLENGTH + 2 + 1;
1519
1520 return (1);
1521 }
1522
1523 /*!
1524 *
1525 * \brief Create an id for a client
1526 *
1527 * This function is used to create an id for a client to use with DDNS
1528 * This version of the function is for the interim style. It is retained
1529 * to allow users to continue using the interim style but they should
1530 * switch to the standard style (which uses get_std_dhcid) for better
1531 * interoperability.
1532 *
1533 * This function takes information from the type and data fields and
1534 * mangles it into a dhcid string which it places in ddns_cb. It also
1535 * sets a field in ddns_cb to specify the class that should be used
1536 * when sending the dhcid, in this case it is a txt record so we use
1537 * dns_rdata_type_txt
1538 *
1539 * NOTE WELL: this function has issues with how it calculates the
1540 * dhcid, they can't be changed now as that would break the records
1541 * already in use.
1542 */
1543
1544 int get_int_dhcid (dhcp_ddns_cb_t *ddns_cb,
1545 int type,
1546 const u_int8_t *data,
1547 unsigned len)
1548 {
1549 struct data_string *id = &ddns_cb->dhcid;
1550 unsigned char buf[ISC_MD5_DIGESTLENGTH];
1551 isc_md5_t md5;
1552 int i;
1553
1554 /* Types can only be 0..(2^16)-1. */
1555 if (type < 0 || type > 65535)
1556 return (0);
1557
1558 /*
1559 * Hexadecimal MD5 digest plus two byte type, NUL,
1560 * and one byte for length for dns.
1561 */
1562 if (!buffer_allocate(&id -> buffer,
1563 (ISC_MD5_DIGESTLENGTH * 2) + 4, MDL))
1564 return (0);
1565 id->data = id->buffer->data;
1566
1567 /*
1568 * We put the length into the first byte to turn
1569 * this into a dns text string. This avoid needing to
1570 * copy the string to add the byte later.
1571 */
1572 id->buffer->data[0] = ISC_MD5_DIGESTLENGTH * 2 + 2;
1573
1574 /* Put the type in the next two bytes. */
1575 id->buffer->data[1] = "0123456789abcdef"[(type >> 4) & 0xf];
1576 /* This should have been [type & 0xf] but now that
1577 * it is in use we need to leave it this way in order
1578 * to avoid disturbing customer's lease files
1579 */
1580 id->buffer->data[2] = "0123456789abcdef"[type % 15];
1581
1582 /* Mash together an MD5 hash of the identifier. */
1583 isc_md5_init(&md5);
1584 isc_md5_update(&md5, data, len);
1585 isc_md5_final(&md5, buf);
1586
1587 /* Convert into ASCII. */
1588 for (i = 0; i < ISC_MD5_DIGESTLENGTH; i++) {
1589 id->buffer->data[i * 2 + 3] =
1590 "0123456789abcdef"[(buf[i] >> 4) & 0xf];
1591 id->buffer->data[i * 2 + 4] =
1592 "0123456789abcdef"[buf[i] & 0xf];
1593 }
1594
1595 id->len = ISC_MD5_DIGESTLENGTH * 2 + 3;
1596 id->buffer->data[id->len] = 0;
1597 id->terminated = 1;
1598
1599 return (1);
1600 }
1601
1602 int get_dhcid(dhcp_ddns_cb_t *ddns_cb,
1603 int type,
1604 const u_int8_t *identifier,
1605 unsigned id_len)
1606 {
1607 if (ddns_cb->dhcid_class == dns_rdatatype_dhcid)
1608 return get_std_dhcid(ddns_cb, type, identifier, id_len);
1609 else
1610 return get_int_dhcid(ddns_cb, type, identifier, id_len);
1611 }
1612
1613 /*
1614 * The dhcid (text version) that we pass to DNS includes a length byte
1615 * at the start but the text we store in the lease doesn't include the
1616 * length byte. The following routines are to convert between the two
1617 * styles.
1618 *
1619 * When converting from a dhcid to a leaseid we reuse the buffer and
1620 * simply adjust the data pointer and length fields in the data string.
1621 * This avoids any prolems with allocating space.
1622 */
1623
1624 void
1625 dhcid_tolease(struct data_string *dhcid,
1626 struct data_string *leaseid)
1627 {
1628 /* copy the data string then update the fields */
1629 data_string_copy(leaseid, dhcid, MDL);
1630 leaseid->data++;
1631 leaseid->len--;
1632 }
1633
1634 isc_result_t
1635 dhcid_fromlease(struct data_string *dhcid,
1636 struct data_string *leaseid)
1637 {
1638 if (!buffer_allocate(&dhcid->buffer, leaseid->len + 2, MDL)) {
1639 return(ISC_R_FAILURE);
1640 }
1641
1642 dhcid->data = dhcid->buffer->data;
1643
1644 dhcid->buffer->data[0] = leaseid->len;
1645 memcpy(dhcid->buffer->data + 1, leaseid->data, leaseid->len);
1646 dhcid->len = leaseid->len + 1;
1647 if (leaseid->terminated == 1) {
1648 dhcid->buffer->data[dhcid->len] = 0;
1649 dhcid->terminated = 1;
1650 }
1651
1652 return(ISC_R_SUCCESS);
1653 }
1654
1655 /*
1656 * Construct the dataset for this item.
1657 * This is a fairly simple arrangement as the operations we do are simple.
1658 * If there is data we simply have the rdata point to it - the formatting
1659 * must be correct already. We then link the rdatalist to the rdata and
1660 * create a rdataset from the rdatalist.
1661 */
1662
1663 static isc_result_t
1664 make_dns_dataset(dns_rdataclass_t dataclass,
1665 dns_rdatatype_t datatype,
1666 dhcp_ddns_data_t *dataspace,
1667 unsigned char *data,
1668 int datalen,
1669 int ttl)
1670 {
1671 dns_rdata_t *rdata = &dataspace->rdata;
1672 dns_rdatalist_t *rdatalist = &dataspace->rdatalist;
1673 dns_rdataset_t *rdataset = &dataspace->rdataset;
1674
1675 isc_region_t region;
1676
1677 /* set up the rdata */
1678 dns_rdata_init(rdata);
1679
1680 if (data == NULL) {
1681 /* No data, set up the rdata fields we care about */
1682 rdata->flags = DNS_RDATA_UPDATE;
1683 rdata->type = datatype;
1684 rdata->rdclass = dataclass;
1685 } else {
1686 switch(datatype) {
1687 case dns_rdatatype_a:
1688 case dns_rdatatype_aaaa:
1689 case dns_rdatatype_txt:
1690 case dns_rdatatype_dhcid:
1691 case dns_rdatatype_ptr:
1692 /* The data must be in the right format we simply
1693 * need to supply it via the correct structure */
1694 region.base = data;
1695 region.length = datalen;
1696 dns_rdata_fromregion(rdata, dataclass, datatype,
1697 &region);
1698 break;
1699 default:
1700 return(DHCP_R_INVALIDARG);
1701 break;
1702 }
1703 }
1704
1705 /* setup the datalist and attach the rdata to it */
1706 dns_rdatalist_init(rdatalist);
1707 rdatalist->type = datatype;
1708 rdatalist->rdclass = dataclass;
1709 rdatalist->ttl = ttl;
1710 ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1711
1712 /* convert the datalist to a dataset */
1713 dns_rdataset_init(rdataset);
1714 dns_rdatalist_tordataset(rdatalist, rdataset);
1715
1716 return(ISC_R_SUCCESS);
1717 }
1718
1719 #if defined (DEBUG_DNS_UPDATES)
1720 static void log_call(char *text, dns_name_t* pname, dns_name_t* uname) {
1721 char buf1[512];
1722 char buf2[512];
1723 if (pname) {
1724 dns_name_format(pname, buf1, 512);
1725 } else {
1726 *buf1=0;
1727 }
1728
1729 if (uname) {
1730 dns_name_format(uname, buf2, 512);
1731 } else {
1732 *buf2=0;
1733 }
1734
1735 log_info ("DDNS: %s: pname:[%s] uname:[%s]", text, buf1, buf2);
1736 }
1737 #endif
1738
1739
1740 /*
1741 * When a DHCP client or server intends to update an A RR, it first
1742 * prepares a DNS UPDATE query which includes as a prerequisite the
1743 * assertion that the name does not exist. The update section of the
1744 * query attempts to add the new name and its IP address mapping (an A
1745 * RR), and the DHCID RR with its unique client-identity.
1746 * -- "Interaction between DHCP and DNS"
1747 *
1748 * There are two cases, one for the server and one for the client.
1749 *
1750 * For the server the first step will have a request of:
1751 * The name is not in use
1752 * Add an A RR
1753 * Add a DHCID RR
1754 *
1755 * For the client the first step will have a request of:
1756 * The A RR does not exist
1757 * Add an A RR
1758 * Add a DHCID RR
1759 */
1760
1761 static isc_result_t
1762 build_fwd_add1(dhcp_ddns_cb_t *ddns_cb,
1763 dhcp_ddns_data_t *dataspace,
1764 dns_name_t *pname,
1765 dns_name_t *uname)
1766 {
1767 isc_result_t result;
1768
1769 #if defined (DEBUG_DNS_UPDATES)
1770 log_call("build_fwd_add1", pname, uname);
1771 #endif
1772
1773 /* Construct the prerequisite list */
1774 if ((ddns_cb->flags & DDNS_INCLUDE_RRSET) != 0) {
1775 /* The A RR shouldn't exist */
1776 result = make_dns_dataset(dns_rdataclass_none,
1777 ddns_cb->address_type,
1778 dataspace, NULL, 0, 0);
1779 } else {
1780 /* The name is not in use */
1781 result = make_dns_dataset(dns_rdataclass_none,
1782 dns_rdatatype_any,
1783 dataspace, NULL, 0, 0);
1784 }
1785 if (result != ISC_R_SUCCESS) {
1786 return(result);
1787 }
1788 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1789 dataspace++;
1790
1791 /* Construct the update list */
1792 /* Add the A RR */
1793 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
1794 dataspace,
1795 (unsigned char *)ddns_cb->address.iabuf,
1796 ddns_cb->address.len, ddns_cb->ttl);
1797 if (result != ISC_R_SUCCESS) {
1798 return(result);
1799 }
1800 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1801 dataspace++;
1802
1803 /* Add the DHCID RR */
1804 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
1805 dataspace,
1806 (unsigned char *)ddns_cb->dhcid.data,
1807 ddns_cb->dhcid.len, ddns_cb->ttl);
1808 if (result != ISC_R_SUCCESS) {
1809 return(result);
1810 }
1811 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1812
1813 return(ISC_R_SUCCESS);
1814 }
1815
1816 /*
1817 * If the first update operation fails with YXDOMAIN, the updater can
1818 * conclude that the intended name is in use. The updater then
1819 * attempts to confirm that the DNS name is not being used by some
1820 * other host. The updater prepares a second UPDATE query in which the
1821 * prerequisite is that the desired name has attached to it a DHCID RR
1822 * whose contents match the client identity. The update section of
1823 * this query deletes the existing A records on the name, and adds the
1824 * A record that matches the DHCP binding and the DHCID RR with the
1825 * client identity.
1826 * -- "Interaction between DHCP and DNS"
1827 *
1828 * The message for the second step depends on if we are doing conflict
1829 * resolution. If we are we include the prerequisite. The prerequiste
1830 * will either:
1831 * A. require the data value of the DHCID RR to match that of the client
1832 * or
1833 * B. required only that the DHCID RR of the configured class (DHCID or
1834 * TXT) exist
1835 *
1836 * based on whether DDNS_GUARD_ID_MUST_MATCH is on (default) or off.
1837 *
1838 * The prerequisite is omitted if conflict detection is off.
1839 *
1840 * If not we delete the DHCID in addition to all A rrsets.
1841 *
1842 * Conflict resolution:
1843 * DHCID RR exists, and matches client identity.
1844 * Delete A RRset.
1845 * Add A RR.
1846 *
1847 * Conflict override:
1848 * Delete DHCID RRs.
1849 * Add DHCID RR
1850 * Delete A RRset.
1851 * Add A RR.
1852 */
1853
1854 static isc_result_t
1855 build_fwd_add2(dhcp_ddns_cb_t *ddns_cb,
1856 dhcp_ddns_data_t *dataspace,
1857 dns_name_t *pname,
1858 dns_name_t *uname)
1859 {
1860 isc_result_t result = ISC_R_SUCCESS;
1861
1862 #if defined (DEBUG_DNS_UPDATES)
1863 log_call("build_fwd_add2", pname, uname);
1864 #endif
1865
1866 /*
1867 * If we are doing conflict detection we use a prereq list.
1868 * If not we delete the DHCID in addition to all A rrsets.
1869 */
1870 if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) {
1871 /* Construct the prereq list */
1872 /* The DHCID RR exists and optionally matches the client's
1873 * identity. If matching is turned off, we use the presence
1874 * of a DHCID RR to signal that this is a dynamic entry and
1875 * thus eligible for us to overwrite. If matching is on
1876 * then we can only replace the entries if they belong to
1877 * this client. */
1878 unsigned char *match_id = NULL;
1879 int match_id_len = 0;
1880 int match_class = dns_rdataclass_any;
1881 if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
1882 match_id = (unsigned char*)(ddns_cb->dhcid.data);
1883 match_id_len = ddns_cb->dhcid.len;
1884 match_class = dns_rdataclass_in;
1885 }
1886
1887 result = make_dns_dataset(match_class,
1888 ddns_cb->dhcid_class,
1889 dataspace,
1890 match_id, match_id_len, 0);
1891 if (result != ISC_R_SUCCESS) {
1892 return(result);
1893 }
1894 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1895 dataspace++;
1896 } else {
1897 /* Start constructing the update list.
1898 * Conflict detection override: delete DHCID RRs */
1899 result = make_dns_dataset(dns_rdataclass_any,
1900 ddns_cb->dhcid_class,
1901 dataspace, NULL, 0, 0);
1902 if (result != ISC_R_SUCCESS) {
1903 return(result);
1904 }
1905 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1906 dataspace++;
1907
1908 /* Add current DHCID RR, always include client id */
1909 result = make_dns_dataset(dns_rdataclass_in,
1910 ddns_cb->dhcid_class,
1911 dataspace,
1912 (unsigned char *)ddns_cb->dhcid.data,
1913 ddns_cb->dhcid.len, ddns_cb->ttl);
1914 if (result != ISC_R_SUCCESS) {
1915 return(result);
1916 }
1917 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1918 dataspace++;
1919 }
1920
1921 /* Start or continue constructing the update list */
1922 /* Delete the address RRset */
1923 result = make_dns_dataset(dns_rdataclass_any, ddns_cb->address_type,
1924 dataspace, NULL, 0, 0);
1925 if (result != ISC_R_SUCCESS) {
1926 return(result);
1927 }
1928 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1929 dataspace++;
1930
1931 /* Add the address RR */
1932 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
1933 dataspace,
1934 (unsigned char *)ddns_cb->address.iabuf,
1935 ddns_cb->address.len, ddns_cb->ttl);
1936 if (result != ISC_R_SUCCESS) {
1937 return(result);
1938 }
1939 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1940
1941 return(ISC_R_SUCCESS);
1942 }
1943
1944 /*
1945 * Creates the DNS foward update add used for DSMM add attempt #3 and
1946 * ddns-other-guard-is-dynamic is off
1947 *
1948 *
1949 * If the second update failed with NXRRSET, this indicates that:
1950 *
1951 * 1. our FQDN is in use
1952 * 2 no guard record (DHCID RR) for that FQDN, of our class (and optionally
1953 * client id) exists
1954 *
1955 * In Dual Stack Mixed Mode, we need to attempt a third add, to distinguish
1956 * between static entries that we cannot modify and dynamic entries belonging
1957 * to the "other" side of dual stack. The prerequisites for this add are:
1958 *
1959 * 1. No address record of my type exists
1960 * 2. No guard record of my type exists
1961 * 3. A guard record of the other type exists
1962 *
1963 * and updates which will add the new address and guard record:
1964 *
1965 * prereq nxrrset <name> <addr_t> # no address record of my type
1966 * prereq nxrrset <name> <guard_t> # no guard record of my type
1967 * prereq yxrrset <name> <other_guard_t> # other guard type does exist
1968 * update add <name> <addr_t> <address> # add the new address record
1969 * update add <name> <guard_t> <client-id> # add the new address record
1970 */
1971 static isc_result_t
1972 build_dsmm_fwd_add3(dhcp_ddns_cb_t *ddns_cb,
1973 dhcp_ddns_data_t *dataspace,
1974 dns_name_t *pname,
1975 dns_name_t *uname)
1976 {
1977 isc_result_t result = ISC_R_SUCCESS;
1978
1979 #if defined (DEBUG_DNS_UPDATES)
1980 log_call("build_fwd_add3", pname, uname);
1981 #endif
1982 /* Construct the prereq list */
1983 /* No address record of my type exists */
1984 result = make_dns_dataset(dns_rdataclass_none,
1985 ddns_cb->address_type,
1986 dataspace, NULL, 0, 0);
1987 if (result != ISC_R_SUCCESS) {
1988 return(result);
1989 }
1990 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1991 dataspace++;
1992
1993 /* No guard record of my type exists */
1994 result = make_dns_dataset(dns_rdataclass_none,
1995 ddns_cb->dhcid_class,
1996 dataspace, NULL, 0, 0);
1997 if (result != ISC_R_SUCCESS) {
1998 return(result);
1999 }
2000 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2001 dataspace++;
2002
2003 /* Guard record of the other type DOES exist */
2004 result = make_dns_dataset(dns_rdataclass_any,
2005 ddns_cb->other_dhcid_class,
2006 dataspace, NULL, 0, 0);
2007 if (result != ISC_R_SUCCESS) {
2008 return(result);
2009 }
2010 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2011 dataspace++;
2012
2013 /* Start constructing the update list. */
2014 /* Add the address RR */
2015 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
2016 dataspace,
2017 (unsigned char *)ddns_cb->address.iabuf,
2018 ddns_cb->address.len, ddns_cb->ttl);
2019 if (result != ISC_R_SUCCESS) {
2020 return(result);
2021 }
2022 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2023 dataspace++;
2024
2025 /* Add current DHCID RR */
2026 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
2027 dataspace,
2028 (unsigned char *)ddns_cb->dhcid.data,
2029 ddns_cb->dhcid.len, ddns_cb->ttl);
2030 if (result != ISC_R_SUCCESS) {
2031 return(result);
2032 }
2033 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2034
2035 return(ISC_R_SUCCESS);
2036 }
2037
2038 /*
2039 * Creates the DNS foward update add used for DSMM add attempt #3 and
2040 * ddns-other-guard-is-dynamic is ON
2041 *
2042 * If the second update failed with NXRRSET, this indicates that:
2043 *
2044 * 1. our FQDN is in use
2045 * 2 no guard record (DHCID RR) for that FQDN, of our class (and optionally
2046 * client id) exists
2047 *
2048 * When we're In Dual Stack Mixed Mode and ddns-other-guard-is-dynamic is ON
2049 * we need only determine if a guard record of the other type exists, to know
2050 * if we can add/replace and address record of our type. In other words,
2051 * the presence of a dynamic entry belonging to the "other" stack means
2052 * all entries for this name should be dynamic and we overwrite an unguarded
2053 * address record of our type.
2054 *
2055 * The udpate will contain a single prequisite for a guard record of the
2056 * other type, an update to delete any address records of our type, and
2057 * updates to add the address and guard records:
2058 *
2059 * prereq yxrrset <name> <other_guard_t> # other guard type exists
2060 * update delete <name> <addr_t> # delete existing address record
2061 * # (if one)
2062 * update add <name> <addr_t> <address> # add new address record
2063 * update add <name> <guard_t> <client-id> # add new guard record
2064 */
2065 static isc_result_t
2066 build_dsmm_fwd_add3_other(dhcp_ddns_cb_t *ddns_cb,
2067 dhcp_ddns_data_t *dataspace,
2068 dns_name_t *pname,
2069 dns_name_t *uname)
2070 {
2071 isc_result_t result = ISC_R_SUCCESS;
2072
2073 #if defined (DEBUG_DNS_UPDATES)
2074 log_call("build_fwd_add3_other", pname, uname);
2075 #endif
2076 /* Construct the prereq list */
2077
2078 // If ID matching is on, a result of NXRRSET from add2 means
2079 // either there is no guard of my type, or there is but
2080 // it does not match this client. We need to distinguish
2081 // between those two cases here and only allow this add
2082 // if there is no guard of my type.
2083 if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
2084 /* No guard record of my type exists */
2085 result = make_dns_dataset(dns_rdataclass_none,
2086 ddns_cb->dhcid_class,
2087 dataspace, NULL, 0, 0);
2088 if (result != ISC_R_SUCCESS) {
2089 return(result);
2090 }
2091
2092 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2093 dataspace++;
2094 }
2095
2096 /* A guard record of the other type exists */
2097 result = make_dns_dataset(dns_rdataclass_any,
2098 ddns_cb->other_dhcid_class,
2099 dataspace, NULL, 0, 0);
2100 if (result != ISC_R_SUCCESS) {
2101 return(result);
2102 }
2103 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2104 dataspace++;
2105
2106 /* Start constructing the update list. */
2107 /* Delete the existing address record of my type (if one) */
2108 result = make_dns_dataset(dns_rdataclass_any,
2109 ddns_cb->address_type,
2110 dataspace, NULL, 0, 0);
2111 if (result != ISC_R_SUCCESS) {
2112 return(result);
2113 }
2114 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2115 dataspace++;
2116
2117 /* Add the address RR */
2118 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
2119 dataspace,
2120 (unsigned char *)ddns_cb->address.iabuf,
2121 ddns_cb->address.len, ddns_cb->ttl);
2122 if (result != ISC_R_SUCCESS) {
2123 return(result);
2124 }
2125 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2126 dataspace++;
2127
2128 /* Add current DHCID RR */
2129 result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
2130 dataspace,
2131 (unsigned char *)ddns_cb->dhcid.data,
2132 ddns_cb->dhcid.len, ddns_cb->ttl);
2133 if (result != ISC_R_SUCCESS) {
2134 return(result);
2135 }
2136 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2137
2138 return(ISC_R_SUCCESS);
2139 }
2140
2141 /*
2142 * The entity chosen to handle the A record for this client (either the
2143 * client or the server) SHOULD delete the A (or AAAA) record that was
2144 * added when the lease was made to the client.
2145 *
2146 * If we are doing conflict resolution, the udpate will contain a prequisite
2147 * that will either:
2148 * A. require that a guard record of the configure class (DHCID or TXT) with
2149 * a data value matching that the client exist (per RFC 4703)
2150 * or
2151 * B. require only that the guard record of the configured class exist
2152 *
2153 * based on whether DDNS_GUARD_ID_MUST_MATCH is on (default) or off.
2154 *
2155 * The prerequisite is omitted if conflict detection is off.
2156 *
2157 */
2158 static isc_result_t
2159 build_fwd_rem1(dhcp_ddns_cb_t *ddns_cb,
2160 dhcp_ddns_data_t *dataspace,
2161 dns_name_t *pname,
2162 dns_name_t *uname)
2163 {
2164 isc_result_t result = ISC_R_SUCCESS;
2165
2166 #if defined (DEBUG_DNS_UPDATES)
2167 log_call("build_fwd_rem1", pname, uname);
2168 #endif
2169
2170 /* If we're doing conflict detection, add the guard record pre-req */
2171 if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) {
2172 /* Construct the prereq list */
2173 /* The guard record exists and optionally matches the client's
2174 * identity. If matching is turned off, we use the presence
2175 * of a DHCID RR to signal that this is a dynamic entry and
2176 * thus eligible for us to overwrite. If matching is on
2177 * then we can only delete the entries if they belong to
2178 * this client. */
2179 unsigned char *match_id = NULL;
2180 int match_id_len = 0;
2181 int match_class = dns_rdataclass_any;
2182 if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
2183 match_id = (unsigned char*)(ddns_cb->dhcid.data);
2184 match_id_len = ddns_cb->dhcid.len;
2185 match_class = dns_rdataclass_in;
2186 }
2187
2188 result = make_dns_dataset(match_class,
2189 ddns_cb->dhcid_class,
2190 dataspace,
2191 match_id, match_id_len, 0);
2192 if (result != ISC_R_SUCCESS) {
2193 return(result);
2194 }
2195 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2196 dataspace++;
2197 }
2198
2199 /* Construct the update list */
2200 /* Delete A RRset */
2201 result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type,
2202 dataspace,
2203 (unsigned char *)ddns_cb->address.iabuf,
2204 ddns_cb->address.len, 0);
2205 if (result != ISC_R_SUCCESS) {
2206 return(result);
2207 }
2208 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2209
2210 return(ISC_R_SUCCESS);
2211 }
2212
2213 /*
2214 * If the deletion of the A succeeded, and there are no A or AAAA
2215 * records left for this domain, then we can blow away the DHCID
2216 * record as well. We can't blow away the DHCID record above
2217 * because it's possible that more than one record has been added
2218 * to this domain name.
2219 *
2220 * Second query has:
2221 * A RR does not exist.
2222 * AAAA RR does not exist.
2223 * Delete appropriate DHCID RR.
2224 */
2225 static isc_result_t
2226 build_fwd_rem2(dhcp_ddns_cb_t *ddns_cb,
2227 dhcp_ddns_data_t *dataspace,
2228 dns_name_t *pname,
2229 dns_name_t *uname)
2230 {
2231 isc_result_t result;
2232 unsigned char *match_id = NULL;
2233 int match_id_len = 0;
2234 int match_class = dns_rdataclass_any;
2235
2236 #if defined (DEBUG_DNS_UPDATES)
2237 log_call("build_fwd_rem2", pname, uname);
2238 #endif
2239
2240 /* Construct the prereq list */
2241 /* The A RR does not exist */
2242 result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_a,
2243 dataspace, NULL, 0, 0);
2244 if (result != ISC_R_SUCCESS) {
2245 return(result);
2246 }
2247 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2248 dataspace++;
2249
2250 /* The AAAA RR does not exist */
2251 result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_aaaa,
2252 dataspace, NULL, 0, 0);
2253 if (result != ISC_R_SUCCESS) {
2254 return(result);
2255 }
2256 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2257 dataspace++;
2258
2259 /* Construct the update list */
2260 /* Delete DHCID RR */
2261
2262 /* We'll specify the client id in the guard record delete if
2263 * matching is enabled, otherwise we leave it off. */
2264 if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
2265 match_id = (unsigned char*)(ddns_cb->dhcid.data);
2266 match_id_len = ddns_cb->dhcid.len;
2267 match_class = dns_rdataclass_none;
2268 }
2269
2270 result = make_dns_dataset(match_class, ddns_cb->dhcid_class,
2271 dataspace,
2272 match_id, match_id_len, 0);
2273 if (result != ISC_R_SUCCESS) {
2274 return(result);
2275 }
2276 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2277
2278 return(ISC_R_SUCCESS);
2279 }
2280
2281 /*
2282 * Constructs the second stage forward remove, when the first stage
2283 * succeeds and DSMM is enabled, and ddns-other-guard-is-dynamic is OFF
2284 *
2285 * Normal conflict detection requires that the guard record of the
2286 * configured type only be deleted if there are no address records of
2287 * any type. In Dual Stack Mixed Mode, we are only concerned with whether
2288 * there any records or our configured address type remaining.
2289 *
2290 * This update consists of a single prequisite that there be no address
2291 * records of our type followed by a delete of the guard record of our type
2292 * and optionally matching client-id.
2293 *
2294 * prereq nxrrset name <addr_t> # no records of this address type exist
2295 * update delete name <guard_t> <client_id> # delete the existing guard record
2296 */
2297 static isc_result_t
2298 build_fwd_rem2_dsmm (dhcp_ddns_cb_t *ddns_cb,
2299 dhcp_ddns_data_t *dataspace,
2300 dns_name_t *pname,
2301 dns_name_t *uname)
2302 {
2303 isc_result_t result;
2304 unsigned char *match_id = NULL;
2305 int match_id_len = 0;
2306 int match_class = dns_rdataclass_any;
2307
2308 #if defined (DEBUG_DNS_UPDATES)
2309 log_call("build_fwd_rem2_dsmm", pname, uname);
2310 #endif
2311
2312 /* Construct the prereq list */
2313 /* The address RR does not exist */
2314 result = make_dns_dataset(dns_rdataclass_none,
2315 ddns_cb->address_type,
2316 dataspace, NULL, 0, 0);
2317 if (result != ISC_R_SUCCESS) {
2318 return(result);
2319 }
2320 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2321 dataspace++;
2322
2323 /* Construct the update list */
2324 /* Delete DHCID RR */
2325
2326 /* We'll specify the client id in the guard record delete if
2327 * matching is enabled, otherwise we leave it off. */
2328 if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
2329 match_id = (unsigned char*)(ddns_cb->dhcid.data);
2330 match_id_len = ddns_cb->dhcid.len;
2331 match_class = dns_rdataclass_none;
2332 }
2333
2334 result = make_dns_dataset(match_class, ddns_cb->dhcid_class,
2335 dataspace,
2336 match_id, match_id_len, 0);
2337 if (result != ISC_R_SUCCESS) {
2338 return(result);
2339 }
2340 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2341
2342 return(ISC_R_SUCCESS);
2343 }
2344
2345 /*
2346 * Constructs the second stage forward remove, when the first stage
2347 * succeeds and DSMM is enabled and ddns-other-guard-is-dynamic is ON
2348 *
2349 * This update addresses the case when an address record of our type exists
2350 * without a guard record of our type, yet a dynamic entry of the other type
2351 * exists. The presence of a guard of the other type indicates that all
2352 * entries for this name should be treated as dynamic, thus permitting us to
2353 * remove the address record of our type.
2354 *
2355 * prereq nxrrset <name> <guard_t> # my guard type does not exist
2356 * prereq yxrrset <name> <other_guard_t> # other guard type does exist
2357 * update delete <name> <addr_t> address # delete the existing address record
2358 *
2359 */
2360 static isc_result_t
2361 build_fwd_rem2_dsmm_other(dhcp_ddns_cb_t *ddns_cb,
2362 dhcp_ddns_data_t *dataspace,
2363 dns_name_t *pname,
2364 dns_name_t *uname)
2365 {
2366 isc_result_t result;
2367
2368 #if defined (DEBUG_DNS_UPDATES)
2369 log_call("build_fwd_rem2_dsmm_other", pname, uname);
2370 #endif
2371
2372 /* Construct the prereq list */
2373 /* No guard record of my type exists */
2374 result = make_dns_dataset(dns_rdataclass_none, ddns_cb->dhcid_class,
2375 dataspace, NULL, 0, 0);
2376 if (result != ISC_R_SUCCESS) {
2377 return(result);
2378 }
2379 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2380 dataspace++;
2381
2382 /* Guard record of the OTHER type DOES exist */
2383 result = make_dns_dataset(dns_rdataclass_any,
2384 ddns_cb->other_dhcid_class,
2385 dataspace, NULL, 0, 0);
2386 if (result != ISC_R_SUCCESS) {
2387 return(result);
2388 }
2389 ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2390 dataspace++;
2391
2392 /* Construct the update list */
2393 /* Delete the address RRset */
2394 result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type,
2395 dataspace,
2396 (unsigned char *)ddns_cb->address.iabuf,
2397 ddns_cb->address.len, 0);
2398 if (result != ISC_R_SUCCESS) {
2399 return(result);
2400 }
2401 ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2402
2403 return(ISC_R_SUCCESS);
2404 }
2405
2406 /*
2407 * This routine converts from the task action call into something
2408 * easier to work with. It also handles the common case of a signature
2409 * or zone not being correct.
2410 */
2411 void ddns_interlude(isc_task_t *taskp,
2412 isc_event_t *eventp)
2413 {
2414 dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)eventp->ev_arg;
2415 dns_clientupdateevent_t *ddns_event = (dns_clientupdateevent_t *)eventp;
2416 isc_result_t eresult = ddns_event->result;
2417 isc_result_t result;
2418
2419 /* We've extracted the information we want from it, get rid of
2420 * the event block.*/
2421 isc_event_free(&eventp);
2422
2423 #if defined (TRACING)
2424 if (trace_record()) {
2425 trace_ddns_input_write(ddns_cb, eresult);
2426 }
2427 #endif
2428
2429 #if defined (DEBUG_DNS_UPDATES)
2430 print_dns_status(DDNS_PRINT_INBOUND, ddns_cb, eresult);
2431 #endif
2432
2433 /* This transaction is complete, clear the value */
2434 dns_client_destroyupdatetrans(&ddns_cb->transaction);
2435
2436 /* If we cancelled or tried to cancel the operation we just
2437 * need to clean up. */
2438 if ((eresult == ISC_R_CANCELED) ||
2439 ((ddns_cb->flags & DDNS_ABORT) != 0)) {
2440 #if defined (DEBUG_DNS_UPDATES)
2441 log_info("DDNS: completeing transaction cancellation cb=%p, "
2442 "flags=%x, %s",
2443 ddns_cb, ddns_cb->flags, isc_result_totext(eresult));
2444 #endif
2445 if ((ddns_cb->flags & DDNS_ABORT) == 0) {
2446 log_info("DDNS: cleaning up lease pointer for a cancel "
2447 "cb=%p", ddns_cb);
2448 /*
2449 * We shouldn't actually be able to get here but
2450 * we are. This means we haven't cleaned up
2451 * the lease pointer so we need to do that before
2452 * freeing the cb.
2453 */
2454 ddns_cb->cur_func(ddns_cb, eresult);
2455 return;
2456 }
2457
2458 if (ddns_cb->next_op != NULL) {
2459 /* if necessary cleanup up next op block */
2460 ddns_cb_free(ddns_cb->next_op, MDL);
2461 }
2462 ddns_cb_free(ddns_cb, MDL);
2463 return;
2464 }
2465
2466 /* If we had a problem with our key or zone try again */
2467 if ((eresult == DNS_R_NOTAUTH) ||
2468 (eresult == DNS_R_NOTZONE)) {
2469 int i;
2470 /* Our zone information was questionable,
2471 * repudiate it and try again */
2472 log_error("DDNS: bad zone information, repudiating zone %s",
2473 ddns_cb->zone_name);
2474 repudiate_zone(&ddns_cb->zone);
2475 ddns_cb->zone_name[0] = 0;
2476 ISC_LIST_INIT(ddns_cb->zone_server_list);
2477 for (i = 0; i < DHCP_MAXNS; i++) {
2478 ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
2479 }
2480
2481 if ((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
2482 (ddns_cb->state == DDNS_STATE_REM_PTR)) {
2483 result = ddns_modify_ptr(ddns_cb, MDL);
2484 } else {
2485 result = ddns_modify_fwd(ddns_cb, MDL);
2486 }
2487
2488 if (result != ISC_R_SUCCESS) {
2489 /* if we couldn't redo the query log it and
2490 * let the next function clean it up */
2491 log_info("DDNS: Failed to retry after zone failure");
2492 ddns_cb->cur_func(ddns_cb, result);
2493 }
2494 return;
2495 } else {
2496 /* pass it along to be processed */
2497 ddns_cb->cur_func(ddns_cb, eresult);
2498 }
2499
2500 return;
2501 }
2502
2503 /*
2504 * This routine does the generic work for sending a ddns message to
2505 * modify the forward record (A or AAAA) and calls one of a set of
2506 * routines to build the specific message.
2507 */
2508
2509 isc_result_t
2510 ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
2511 {
2512 isc_result_t result;
2513 dns_tsec_t *tsec_key = NULL;
2514
2515 #if defined (DEBUG_DNS_UPDATES)
2516 log_info("DDNS: ddns_modify_fwd");
2517 #endif
2518
2519 unsigned char *clientname;
2520 dhcp_ddns_data_t *dataspace = NULL;
2521 dns_namelist_t prereqlist, updatelist;
2522 dns_fixedname_t zname0, pname0, uname0;
2523 dns_name_t *zname = NULL, *pname, *uname;
2524
2525 isc_sockaddrlist_t *zlist = NULL;
2526
2527 /* Creates client context if we need to */
2528 result = dns_client_init();
2529 if (result != ISC_R_SUCCESS) {
2530 return result;
2531 }
2532
2533 /* Get a pointer to the clientname to make things easier. */
2534 clientname = (unsigned char *)ddns_cb->fwd_name.data;
2535
2536 /* Extract and validate the type of the address. */
2537 if (ddns_cb->address.len == 4) {
2538 ddns_cb->address_type = dns_rdatatype_a;
2539 } else if (ddns_cb->address.len == 16) {
2540 ddns_cb->address_type = dns_rdatatype_aaaa;
2541 } else {
2542 return DHCP_R_INVALIDARG;
2543 }
2544
2545 /*
2546 * If we already have a zone use it, otherwise try to lookup the
2547 * zone in our cache. If we find one we will have a pointer to
2548 * the zone that needs to be dereferenced when we are done with it.
2549 * If we don't find one that is okay we'll let the DNS code try and
2550 * find the information for us.
2551 */
2552
2553 if (ddns_cb->zone == NULL) {
2554 result = find_cached_zone(ddns_cb, FIND_FORWARD);
2555 #if defined (DNS_ZONE_LOOKUP)
2556 if (result == ISC_R_NOTFOUND) {
2557 /*
2558 * We didn't find a cached zone, see if we can
2559 * can find a nameserver and create a zone.
2560 */
2561 if (find_zone_start(ddns_cb, FIND_FORWARD)
2562 == ISC_R_SUCCESS) {
2563 /*
2564 * We have started the process to find a zone
2565 * queue the ddns_cb for processing after we
2566 * create the zone
2567 */
2568 /* sar - not yet implemented, currently we just
2569 * arrange for things to get cleaned up
2570 */
2571 goto cleanup;
2572 }
2573 }
2574 #endif
2575 if (result != ISC_R_SUCCESS)
2576 goto cleanup;
2577 }
2578
2579 /*
2580 * If we have a zone try to get any information we need
2581 * from it - name, addresses and the key. The address
2582 * and key may be empty the name can't be.
2583 */
2584 if (ddns_cb->zone) {
2585 /* Set up the zone name for use by DNS */
2586 result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
2587 if (result != ISC_R_SUCCESS) {
2588 log_error("Unable to build name for zone for "
2589 "fwd update: %s %s",
2590 ddns_cb->zone_name,
2591 isc_result_totext(result));
2592 goto cleanup;
2593 }
2594
2595 if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
2596 /* If we have any addresses get them */
2597 zlist = &ddns_cb->zone_server_list;
2598 }
2599
2600
2601 if (ddns_cb->zone->key != NULL) {
2602 /*
2603 * Not having a key is fine, having a key
2604 * but not a tsec is odd so we warn the user.
2605 */
2606 /*sar*/
2607 /* should we do the warning? */
2608 tsec_key = ddns_cb->zone->key->tsec_key;
2609 if (tsec_key == NULL) {
2610 log_error("No tsec for use with key %s",
2611 ddns_cb->zone->key->name);
2612 }
2613 }
2614 }
2615
2616 /* Set up the DNS names for the prereq and update lists */
2617 if (((result = dhcp_isc_name(clientname, &pname0, &pname))
2618 != ISC_R_SUCCESS) ||
2619 ((result = dhcp_isc_name(clientname, &uname0, &uname))
2620 != ISC_R_SUCCESS)) {
2621 log_error("Unable to build name for fwd update: %s %s",
2622 clientname, isc_result_totext(result));
2623 goto cleanup;
2624 }
2625
2626 /* Allocate the various isc dns library structures we may require. */
2627 dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 4);
2628 if (dataspace == NULL) {
2629 log_error("Unable to allocate memory for fwd update");
2630 result = ISC_R_NOMEMORY;
2631 goto cleanup;
2632 }
2633
2634 ISC_LIST_INIT(prereqlist);
2635 ISC_LIST_INIT(updatelist);
2636
2637 switch(ddns_cb->state) {
2638 case DDNS_STATE_ADD_FW_NXDOMAIN:
2639 result = build_fwd_add1(ddns_cb, dataspace, pname, uname);
2640 if (result != ISC_R_SUCCESS) {
2641 goto cleanup;
2642 }
2643 ISC_LIST_APPEND(prereqlist, pname, link);
2644 break;
2645
2646 case DDNS_STATE_ADD_FW_YXDHCID:
2647 result = build_fwd_add2(ddns_cb, dataspace, pname, uname);
2648 if (result != ISC_R_SUCCESS) {
2649 goto cleanup;
2650 }
2651
2652 /* If we are doing conflict detection we have entries
2653 * in the pname list and we need to attach it to the
2654 * prereqlist */
2655
2656 if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) {
2657 ISC_LIST_APPEND(prereqlist, pname, link);
2658 }
2659
2660 break;
2661
2662 case DDNS_STATE_DSMM_FW_ADD3: {
2663 /* We should only be here if we're doing DSMM */
2664 builder_func_t builder;
2665 if (ddns_cb->flags & DDNS_OTHER_GUARD_IS_DYNAMIC) {
2666 builder = build_dsmm_fwd_add3_other;
2667 } else {
2668 builder = build_dsmm_fwd_add3;
2669 }
2670
2671 result = (*builder)(ddns_cb, dataspace, pname, uname);
2672 if (result != ISC_R_SUCCESS) {
2673 goto cleanup;
2674 }
2675
2676 ISC_LIST_APPEND(prereqlist, pname, link);
2677 break;
2678 }
2679
2680 case DDNS_STATE_REM_FW_YXDHCID:
2681 result = build_fwd_rem1(ddns_cb, dataspace, pname, uname);
2682 if (result != ISC_R_SUCCESS) {
2683 goto cleanup;
2684 }
2685 ISC_LIST_APPEND(prereqlist, pname, link);
2686 break;
2687
2688 case DDNS_STATE_REM_FW_NXRR: {
2689 builder_func_t builder;
2690
2691 if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) {
2692 builder = build_fwd_rem2_dsmm;
2693 } else {
2694 builder = build_fwd_rem2;
2695 }
2696
2697 result = (*builder)(ddns_cb, dataspace, pname, uname);
2698 if (result != ISC_R_SUCCESS) {
2699 goto cleanup; }
2700 ISC_LIST_APPEND(prereqlist, pname, link);
2701 break;
2702 }
2703
2704 case DDNS_STATE_REM_FW_DSMM_OTHER: {
2705 result = build_fwd_rem2_dsmm_other(ddns_cb, dataspace,
2706 pname, uname);
2707 if (result != ISC_R_SUCCESS) {
2708 goto cleanup; }
2709 ISC_LIST_APPEND(prereqlist, pname, link);
2710 break;
2711 }
2712
2713 default:
2714 log_error("ddns_modify_fwd: Invalid state: %d", ddns_cb->state);
2715 result = DHCP_R_INVALIDARG;
2716 goto cleanup;
2717 break;
2718 }
2719
2720 /*
2721 * We always have an update list but may not have a prereqlist
2722 * if we are doing conflict override.
2723 */
2724 ISC_LIST_APPEND(updatelist, uname, link);
2725
2726 /* send the message, cleanup and return the result */
2727 result = ddns_update(dhcp_gbl_ctx.dnsclient,
2728 dns_rdataclass_in, zname,
2729 &prereqlist, &updatelist,
2730 zlist, tsec_key,
2731 DNS_CLIENTUPDOPT_ALLOWRUN,
2732 dhcp_gbl_ctx.task,
2733 ddns_interlude,
2734 (void *)ddns_cb,
2735 &ddns_cb->transaction);
2736 if (result == ISC_R_FAMILYNOSUPPORT) {
2737 log_info("Unable to perform DDNS update, "
2738 "address family not supported");
2739 }
2740
2741 #if defined (DEBUG_DNS_UPDATES)
2742 print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
2743 #endif
2744
2745 cleanup:
2746 #if defined (DEBUG_DNS_UPDATES)
2747 if (result != ISC_R_SUCCESS) {
2748 log_info("DDNS: %s(%d): error in ddns_modify_fwd %s for %p",
2749 file, line, isc_result_totext(result), ddns_cb);
2750 }
2751 #endif
2752
2753 if (dataspace != NULL) {
2754 isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
2755 sizeof(*dataspace) * 4);
2756 }
2757 return(result);
2758 }
2759
2760
2761 isc_result_t
2762 ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
2763 {
2764 isc_result_t result;
2765 dns_tsec_t *tsec_key = NULL;
2766 unsigned char *ptrname;
2767 dhcp_ddns_data_t *dataspace = NULL;
2768 dns_namelist_t updatelist;
2769 dns_fixedname_t zname0, uname0;
2770 dns_name_t *zname = NULL, *uname;
2771 isc_sockaddrlist_t *zlist = NULL;
2772 unsigned char buf[256];
2773 int buflen;
2774
2775 #if defined (DEBUG_DNS_UPDATES)
2776 log_info("DDNS: ddns_modify_ptr");
2777 #endif
2778
2779 /* Creates client context if we need to */
2780 result = dns_client_init();
2781 if (result != ISC_R_SUCCESS) {
2782 return result;
2783 }
2784
2785 /*
2786 * Try to lookup the zone in the zone cache. As with the forward
2787 * case it's okay if we don't have one, the DNS code will try to
2788 * find something also if we succeed we will need to dereference
2789 * the zone later. Unlike with the forward case we assume we won't
2790 * have a pre-existing zone.
2791 */
2792 result = find_cached_zone(ddns_cb, FIND_REVERSE);
2793
2794 #if defined (DNS_ZONE_LOOKUP)
2795 if (result == ISC_R_NOTFOUND) {
2796 /*
2797 * We didn't find a cached zone, see if we can
2798 * can find a nameserver and create a zone.
2799 */
2800 if (find_zone_start(ddns_cb, FIND_REVERSE) == ISC_R_SUCCESS) {
2801 /*
2802 * We have started the process to find a zone
2803 * queue the ddns_cb for processing after we
2804 * create the zone
2805 */
2806 /* sar - not yet implemented, currently we just
2807 * arrange for things to get cleaned up
2808 */
2809 goto cleanup;
2810 }
2811 }
2812 #endif
2813 if (result != ISC_R_SUCCESS)
2814 goto cleanup;
2815
2816
2817 if ((result == ISC_R_SUCCESS) &&
2818 !(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
2819 /* Set up the zone name for use by DNS */
2820 result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
2821 if (result != ISC_R_SUCCESS) {
2822 log_error("Unable to build name for zone for "
2823 "fwd update: %s %s",
2824 ddns_cb->zone_name,
2825 isc_result_totext(result));
2826 goto cleanup;
2827 }
2828 /* If we have any addresses get them */
2829 if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
2830 zlist = &ddns_cb->zone_server_list;
2831 }
2832
2833 /*
2834 * If we now have a zone try to get the key, NULL is okay,
2835 * having a key but not a tsec is odd so we warn.
2836 */
2837 /*sar*/
2838 /* should we do the warning if we have a key but no tsec? */
2839 if ((ddns_cb->zone != NULL) && (ddns_cb->zone->key != NULL)) {
2840 tsec_key = ddns_cb->zone->key->tsec_key;
2841 if (tsec_key == NULL) {
2842 log_error("No tsec for use with key %s",
2843 ddns_cb->zone->key->name);
2844 }
2845 }
2846 }
2847
2848 /* We must have a name for the update list */
2849 /* Get a pointer to the ptrname to make things easier. */
2850 ptrname = (unsigned char *)ddns_cb->rev_name.data;
2851
2852 if ((result = dhcp_isc_name(ptrname, &uname0, &uname))
2853 != ISC_R_SUCCESS) {
2854 log_error("Unable to build name for fwd update: %s %s",
2855 ptrname, isc_result_totext(result));
2856 goto cleanup;
2857 }
2858
2859 /*
2860 * Allocate the various isc dns library structures we may require.
2861 * Allocating one blob avoids being halfway through the process
2862 * and being unable to allocate as well as making the free easy.
2863 */
2864 dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 2);
2865 if (dataspace == NULL) {
2866 log_error("Unable to allocate memory for fwd update");
2867 result = ISC_R_NOMEMORY;
2868 goto cleanup;
2869 }
2870
2871 ISC_LIST_INIT(updatelist);
2872
2873 /*
2874 * Construct the update list
2875 * We always delete what's currently there
2876 * Delete PTR RR.
2877 */
2878 result = make_dns_dataset(dns_rdataclass_any, dns_rdatatype_ptr,
2879 &dataspace[0], NULL, 0, 0);
2880 if (result != ISC_R_SUCCESS) {
2881 goto cleanup;
2882 }
2883 ISC_LIST_APPEND(uname->list, &dataspace[0].rdataset, link);
2884
2885 /*
2886 * If we are updating the pointer we then add the new one
2887 * Add PTR RR.
2888 */
2889 if (ddns_cb->state == DDNS_STATE_ADD_PTR) {
2890 /*
2891 * Need to convert pointer into on the wire representation
2892 */
2893 if (MRns_name_pton((char *)ddns_cb->fwd_name.data,
2894 buf, 256) == -1) {
2895 goto cleanup;
2896 }
2897 buflen = 0;
2898 while (buf[buflen] != 0) {
2899 buflen += buf[buflen] + 1;
2900 }
2901 buflen++;
2902
2903 result = make_dns_dataset(dns_rdataclass_in,
2904 dns_rdatatype_ptr,
2905 &dataspace[1],
2906 buf, buflen, ddns_cb->ttl);
2907 if (result != ISC_R_SUCCESS) {
2908 goto cleanup;
2909 }
2910 ISC_LIST_APPEND(uname->list, &dataspace[1].rdataset, link);
2911 }
2912
2913 ISC_LIST_APPEND(updatelist, uname, link);
2914
2915 /*sar*/
2916 /*
2917 * for now I'll cleanup the dataset immediately, it would be
2918 * more efficient to keep it around in case the signaturure failed
2919 * and we wanted to retry it.
2920 */
2921 /* send the message, cleanup and return the result */
2922 result = ddns_update((dns_client_t *)dhcp_gbl_ctx.dnsclient,
2923 dns_rdataclass_in, zname,
2924 NULL, &updatelist,
2925 zlist, tsec_key,
2926 DNS_CLIENTUPDOPT_ALLOWRUN,
2927 dhcp_gbl_ctx.task,
2928 ddns_interlude, (void *)ddns_cb,
2929 &ddns_cb->transaction);
2930 if (result == ISC_R_FAMILYNOSUPPORT) {
2931 log_info("Unable to perform DDNS update, "
2932 "address family not supported");
2933 }
2934
2935 #if defined (DEBUG_DNS_UPDATES)
2936 print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
2937 #endif
2938
2939 cleanup:
2940 #if defined (DEBUG_DNS_UPDATES)
2941 if (result != ISC_R_SUCCESS) {
2942 log_info("DDNS: %s(%d): error in ddns_modify_ptr %s for %p",
2943 file, line, isc_result_totext(result), ddns_cb);
2944 }
2945 #endif
2946
2947 if (dataspace != NULL) {
2948 isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
2949 sizeof(*dataspace) * 2);
2950 }
2951 return(result);
2952 }
2953
2954 void
2955 ddns_cancel(dhcp_ddns_cb_t *ddns_cb, const char *file, int line) {
2956 ddns_cb->flags |= DDNS_ABORT;
2957 if (ddns_cb->transaction != NULL) {
2958 dns_client_cancelupdate((dns_clientupdatetrans_t *)
2959 ddns_cb->transaction);
2960 }
2961 ddns_cb->lease = NULL;
2962
2963 #if defined (DEBUG_DNS_UPDATES)
2964 log_info("DDNS: %s(%d): cancelling transaction for %p",
2965 file, line, ddns_cb);
2966 #endif
2967 }
2968
2969 #endif /* NSUPDATE */
2970
2971 HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
2972 dns_zone_reference, dns_zone_dereference, do_case_hash)
2973
2974 #if defined (NSUPDATE)
2975 #if defined (DEBUG_DNS_UPDATES)
2976 /* Defines a type for creating list of labeled integers */
2977 typedef struct {
2978 int val;
2979 char *name;
2980 } LabeledInt;
2981
2982 char*
2983 ddns_state_name(int state) {
2984 static LabeledInt ints[] = {
2985 { DDNS_STATE_CLEANUP, "DDNS_STATE_CLEANUP" },
2986 { DDNS_STATE_ADD_FW_NXDOMAIN, "DDNS_STATE_ADD_FW_NXDOMAIN" },
2987 { DDNS_STATE_ADD_FW_YXDHCID, "DDNS_STATE_ADD_FW_YXDHCID" },
2988 { DDNS_STATE_ADD_PTR, "DDNS_STATE_ADD_PTR" },
2989 { DDNS_STATE_DSMM_FW_ADD3, "DDNS_STATE_DSMM_FW_ADD3" },
2990 { DDNS_STATE_REM_FW_YXDHCID, "DDNS_STATE_REM_FW_YXDHCID" },
2991 { DDNS_STATE_REM_FW_NXRR, "DDNS_STATE_FW_NXRR" },
2992 { DDNS_STATE_REM_PTR, "DDNS_STATE_REM_PTR" },
2993 { -1, "unknown" },
2994 };
2995
2996 LabeledInt* li = ints;
2997 while (li->val != -1 && li->val != state) {
2998 ++li;
2999 }
3000
3001 return (li->name);
3002 }
3003
3004 int
3005 add_nstring(char **orig, char *max, char *add, int add_len) {
3006 if (*orig && (*orig + add_len < max)) {
3007 strncpy(*orig, add, add_len);
3008 *orig += add_len;
3009 **orig = 0;
3010 return (0);
3011 }
3012
3013 return (-1);
3014 }
3015
3016 int
3017 add_string(char **orig, char *max, char *add) {
3018 return (add_nstring(orig, max, add, strlen(add)));
3019 }
3020
3021 /*
3022 * direction outbound (messages to the dns server)
3023 * inbound (messages from the dns server)
3024 * ddns_cb is the control block associated with the message
3025 * result is the result from the dns code. For outbound calls
3026 * it is from the call to pass the message to the dns library.
3027 * For inbound calls it is from the event returned by the library.
3028 *
3029 * For outbound messages we print whatever we think is interesting
3030 * from the control block.
3031 * For inbound messages we only print the transaction id pointer
3032 * and the result and expect that the user will match them up as
3033 * necessary. Note well: the transaction information is opaque to
3034 * us so we simply print the pointer to it. This should be sufficient
3035 * to match requests and replys in a short sequence but is awkward
3036 * when trying to use it for longer sequences.
3037 */
3038 void
3039 print_dns_status (int direction,
3040 struct dhcp_ddns_cb *ddns_cb,
3041 isc_result_t result)
3042 {
3043 char obuf[1024];
3044 char *s = obuf, *end = &obuf[sizeof(obuf)-2];
3045 char *en;
3046 const char *result_str;
3047 char ddns_address[
3048 sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
3049
3050 if (direction == DDNS_PRINT_INBOUND) {
3051 log_info("DDNS reply: id ptr %p, result: %s",
3052 ddns_cb->transaction, isc_result_totext(result));
3053 return;
3054 }
3055
3056 /*
3057 * To avoid having to figure out if any of the strings
3058 * aren't NULL terminated, just 0 the whole string
3059 */
3060 memset(obuf, 0, 1024);
3061
3062 en = "DDNS request: id ptr ";
3063 if (s + strlen(en) + 16 < end) {
3064 sprintf(s, "%s%p", en, ddns_cb->transaction);
3065 s += strlen(s);
3066 } else {
3067 goto bailout;
3068 }
3069
3070 en = ddns_state_name(ddns_cb->state);
3071
3072 switch (ddns_cb->state) {
3073 case DDNS_STATE_ADD_FW_NXDOMAIN:
3074 case DDNS_STATE_ADD_FW_YXDHCID:
3075 case DDNS_STATE_REM_FW_YXDHCID:
3076 case DDNS_STATE_REM_FW_NXRR:
3077 case DDNS_STATE_DSMM_FW_ADD3:
3078 strcpy(ddns_address, piaddr(ddns_cb->address));
3079 if (s + strlen(en) + strlen(ddns_address) +
3080 ddns_cb->fwd_name.len + 7 < end) {
3081 sprintf(s, " %s %s for %.*s", en, ddns_address,
3082 ddns_cb->fwd_name.len,
3083 ddns_cb->fwd_name.data);
3084 s += strlen(s);
3085 } else {
3086 goto bailout;
3087 }
3088 break;
3089
3090 case DDNS_STATE_ADD_PTR:
3091 case DDNS_STATE_REM_PTR:
3092 if (s + strlen(en) + ddns_cb->fwd_name.len +
3093 ddns_cb->rev_name.len + 7 < end) {
3094 sprintf(s, " %s %.*s for %.*s", en,
3095 ddns_cb->fwd_name.len,
3096 ddns_cb->fwd_name.data,
3097 ddns_cb->rev_name.len,
3098 ddns_cb->rev_name.data);
3099 s += strlen(s);
3100 } else {
3101 goto bailout;
3102 }
3103 break;
3104
3105 case DDNS_STATE_CLEANUP:
3106 default:
3107 if (s + strlen(en) < end) {
3108 sprintf(s, "%s", en);
3109 s += strlen(s);
3110 } else {
3111 goto bailout;
3112 }
3113 break;
3114 }
3115
3116 en = " zone: ";
3117 if (s + strlen(en) + strlen((char *)ddns_cb->zone_name) < end) {
3118 sprintf(s, "%s%s", en, ddns_cb->zone_name);
3119 s += strlen(s);
3120 } else {
3121 goto bailout;
3122 }
3123
3124 /* @todo replace with format that matches bind9 zone file */
3125 if (ddns_cb->dhcid_class == dns_rdatatype_dhcid) {
3126 char *idbuf = NULL;
3127 if (add_string(&s, end, "dhcid: [")) {
3128 goto bailout;
3129 }
3130
3131 idbuf = buf_to_hex(ddns_cb->dhcid.data,
3132 ddns_cb->dhcid.len, MDL);
3133 if (idbuf) {
3134 int ret = add_string(&s, end, idbuf);
3135 dfree(idbuf, MDL);
3136 if (!ret) {
3137 goto bailout;
3138 }
3139 }
3140
3141 if (add_string(&s, end, "]")) {
3142 goto bailout;
3143 }
3144 } else {
3145 /* 1st byte of a txt dhcid is length, so we skip printing it
3146 * In the event it's empty, we end up not adding anything */
3147 int skip_length_byte = (ddns_cb->dhcid.len > 0 ? 1 : 0);
3148 if (add_string (&s, end, "txt: [") ||
3149 add_nstring (&s, end,
3150 (char *)ddns_cb->dhcid.data + skip_length_byte,
3151 ddns_cb->dhcid.len - skip_length_byte) ||
3152 add_string (&s, end, "]")) {
3153 goto bailout;
3154 }
3155 }
3156
3157 en = " ttl: ";
3158 if (s + strlen(en) + 10 < end) {
3159 sprintf(s, "%s%ld", en, ddns_cb->ttl);
3160 s += strlen(s);
3161 } else {
3162 goto bailout;
3163 }
3164
3165 en = " result: ";
3166 result_str = isc_result_totext(result);
3167 if (s + strlen(en) + strlen(result_str) < end) {
3168 sprintf(s, "%s%s", en, result_str);
3169 s += strlen(s);
3170 } else {
3171 goto bailout;
3172 }
3173
3174 bailout:
3175 /*
3176 * We either finished building the string or ran out
3177 * of space, print whatever we have in case it is useful
3178 */
3179 log_info("%s", obuf);
3180
3181 return;
3182 }
3183 #endif /* DEBUG_DNS_UPDATES */
3184 #endif /* NSUPDATE */