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