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 140dns_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 153typedef 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 */ 160typedef 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 179typedef 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 */ 215dhcp_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 232static void 233add_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 240static void 241remove_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 256static isc_result_t 257find_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 277void cache_found_zone (dhcp_ddns_ns_t *); 278#endif 279 280void 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 320typedef 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 */ 327static dhcp_ddns_map_t *ddns_map; 328 329trace_type_t *trace_ddns_input; 330trace_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 338static void 339trace_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 */ 362static void 363trace_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 417static void 418trace_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 439static isc_result_t 440trace_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 529static void 530trace_ddns_output_read(trace_type_t *ttype, unsigned length, 531 char *buf) 532{ 533 log_error("unaccounted for ddns output."); 534} 535 536static void 537trace_ddns_output_stop(trace_type_t *ttype) 538{ 539} 540 541void 542trace_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 */ 564dhcp_ddns_cb_t * 565ddns_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 585void 586ddns_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 615void 616ddns_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 629static 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 644isc_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 669isc_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 702int 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 */ 760static void 761zone_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 */ 822static void 823find_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 990static void 991find_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 */ 1134static isc_result_t 1135find_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 1204isc_result_t 1205find_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 1355void forget_zone (struct dns_zone **zone) 1356{ 1357 dns_zone_dereference (zone, MDL); 1358} 1359 1360void 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) 1373void 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 */ 1479static 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); 1550end: 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 1578static 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); 1654end: 1655 if (md != NULL) { 1656 isc_md_free(md); 1657 } 1658 return (0); 1659} 1660 1661int 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 1683void 1684dhcid_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 1693isc_result_t 1694dhcid_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 1722static isc_result_t 1723make_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) 1779static 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 1820static isc_result_t 1821build_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 1913static isc_result_t 1914build_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 */ 2030static isc_result_t 2031build_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 */ 2124static isc_result_t 2125build_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 */ 2217static isc_result_t 2218build_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 */ 2284static isc_result_t 2285build_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 */ 2356static isc_result_t 2357build_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 */ 2419static isc_result_t 2420build_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 */ 2470void 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 2568isc_result_t 2569ddns_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 2820isc_result_t 2821ddns_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 3013void 3014ddns_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 3030HASH_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 */ 3036typedef struct { 3037 int val; 3038 char *name; 3039} LabeledInt; 3040 3041char* 3042ddns_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 3063int 3064add_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 3075int 3076add_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 */ 3097void 3098print_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