diff options
Diffstat (limited to 'server/ddns.c')
-rw-r--r-- | server/ddns.c | 537 |
1 files changed, 432 insertions, 105 deletions
diff --git a/server/ddns.c b/server/ddns.c index d370bbed..396eda5d 100644 --- a/server/ddns.c +++ b/server/ddns.c @@ -3,7 +3,7 @@ Dynamic DNS updates. */ /* - * + * * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2000-2003 by Internet Software Consortium * @@ -40,12 +40,23 @@ char *ddns_interim_tag = "ddns-txt"; #ifdef NSUPDATE +#if defined (DEBUG_DNS_UPDATES) +static char* dump_ddns_cb_func(void *func); +static char* dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb); + +extern struct enumeration_value ddns_styles_values[]; +#endif + static void ddns_fwd_srv_connector(struct lease *lease, struct iasubopt *lease6, struct binding_scope **inscope, dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult); +static void copy_conflict_flags(u_int16_t *target, u_int16_t source); + +static void ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult); + /* DN: No way of checking that there is enough space in a data_string's buffer. Be certain to allocate enough! TL: This is why the expression evaluation code allocates a *new* @@ -123,7 +134,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, } /* * Assume that we shall update both the A and ptr records and, - * as this is an update, set the active flag + * as this is an update, set the active flag */ ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR | DDNS_ACTIVE_LEASE; @@ -293,7 +304,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, goto in; } #endif - + /* See if the administrator wants to do updates even in cases where the update already appears to have been done. */ @@ -325,7 +336,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, } } in: - + /* If we don't have a name that the client has been assigned, we can just skip all this. */ @@ -351,7 +362,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, * The new behavior continues to allow the customer to set * up an option but the defaults are a little different. * We now use 1/2 of the (preferred) lease time for both - * v4 and v6 and cap them at a maximum value. + * v4 and v6 and cap them at a maximum value. * If the customer chooses to use an experession that references * part of the lease the v6 value will be the default as there * isn't a lease available for v6. @@ -373,7 +384,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, if (ddns_ttl > MAX_DEFAULT_DDNS_TTL) { ddns_ttl = MAX_DEFAULT_DDNS_TTL; } -#endif +#endif if ((oc = lookup_option(&server_universe, options, SV_DDNS_TTL))) { if (evaluate_option_cache(&d1, packet, lease, NULL, @@ -398,8 +409,8 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, else s1 = 0; - /* - * Figure out the length of the part of the name that depends + /* + * Figure out the length of the part of the name that depends * on the address. */ if (ddns_cb->address.len == 4) { @@ -422,8 +433,8 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, } } } else if (ddns_cb->address.len == 16) { - /* - * IPv6 reverse names are always the same length, with + /* + * IPv6 reverse names are always the same length, with * 32 hex characters separated by dots. */ rev_name_len = sizeof("0.1.2.3.4.5.6.7." @@ -458,7 +469,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, if (ddns_cb->address.len == 4) { rname->len = sprintf((char *)rname->buffer->data, - "%u.%u.%u.%u.", + "%u.%u.%u.%u.", ddns_cb->address.iabuf[3] & 0xff, ddns_cb->address.iabuf[2] & 0xff, ddns_cb->address.iabuf[1] & 0xff, @@ -474,7 +485,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, char *p = (char *)&rname->buffer->data; unsigned char *a = ddns_cb->address.iabuf + 15; for (i=0; i<16; i++) { - sprintf(p, "%x.%x.", + sprintf(p, "%x.%x.", (*a & 0xF), ((*a >> 4) & 0xF)); p += 4; a -= 1; @@ -512,12 +523,14 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, /* The standard style */ ddns_cb->lease_tag = ddns_standard_tag; ddns_cb->dhcid_class = dns_rdatatype_dhcid; + ddns_cb->other_dhcid_class = dns_rdatatype_txt; ddns_type = 1; ddns_len = 4; } else { /* The older interim style */ ddns_cb->lease_tag = ddns_interim_tag; ddns_cb->dhcid_class = dns_rdatatype_txt; + ddns_cb->other_dhcid_class = dns_rdatatype_dhcid; /* for backwards compatibility */ ddns_type = DHO_DHCP_CLIENT_IDENTIFIER; /* IAID incorrectly included */ @@ -565,14 +578,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, */ if (ddns_cb->flags & DDNS_UPDATE_ADDR) { - oc = lookup_option(&server_universe, options, - SV_DDNS_CONFLICT_DETECT); - if (oc && - !evaluate_boolean_option_cache(&ignorep, packet, lease, - NULL, packet->options, - options, scope, oc, MDL)) - ddns_cb->flags |= DDNS_CONFLICT_OVERRIDE; - + copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask); } /* @@ -772,7 +778,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, * Lastly, if we needed to find the scope we write it out, if we used a * scope that was passed as an argument we don't write it, assuming that * our caller (or his ...) will do the write. - * + * *\li ddns_cb - the control block for the DDNS request * *\li inscope - a pointer to the scope to update. This may be NULL @@ -797,7 +803,7 @@ ddns_update_lease_text(dhcp_ddns_cb_t *ddns_cb, struct ipv6_pool *pool = NULL; struct in6_addr addr; struct data_string lease_dhcid; - + /* * If the lease was static (for a fixed address) * we don't need to do any work. @@ -805,7 +811,7 @@ ddns_update_lease_text(dhcp_ddns_cb_t *ddns_cb, if (ddns_cb->flags & DDNS_STATIC_LEASE) return (ISC_R_SUCCESS); - /* + /* * If we are processing an expired or released v6 lease * or some types of v4 leases we don't actually have a * scope to update @@ -823,7 +829,7 @@ ddns_update_lease_text(dhcp_ddns_cb_t *ddns_cb, memcpy(&addr, &ddns_cb->address.iabuf, 16); if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) == ISC_R_SUCCESS) || - (find_ipv6_pool(&pool, D6O_IA_NA, &addr) == + (find_ipv6_pool(&pool, D6O_IA_NA, &addr) == ISC_R_SUCCESS)) { if (iasubopt_hash_lookup(&lease6, pool->leases, &addr, 16, MDL)) { @@ -867,10 +873,12 @@ ddns_update_lease_text(dhcp_ddns_cb_t *ddns_cb, case DDNS_STATE_ADD_FW_YXDHCID: case DDNS_STATE_ADD_FW_NXDOMAIN: + case DDNS_STATE_DSMM_FW_ADD3: bind_ds_value(scope, "ddns-fwd-name", &ddns_cb->fwd_name); if (ddns_cb->lease_tag == ddns_standard_tag) { - bind_ds_value(scope, ddns_standard_tag, &ddns_cb->dhcid); + bind_ds_value(scope, ddns_standard_tag, + &ddns_cb->dhcid); } else { /* convert from dns version to lease version of dhcid */ memset(&lease_dhcid, 0, sizeof(lease_dhcid)); @@ -882,11 +890,12 @@ ddns_update_lease_text(dhcp_ddns_cb_t *ddns_cb, case DDNS_STATE_REM_FW_NXRR: case DDNS_STATE_REM_FW_YXDHCID: + case DDNS_STATE_REM_FW_DSMM_OTHER: unset(*scope, "ddns-fwd-name"); unset(*scope, ddns_cb->lease_tag); break; } - + /* If necessary write it out and get rid of the lease */ if (lease) { write_lease(lease); @@ -906,7 +915,7 @@ ddns_update_lease_text(dhcp_ddns_cb_t *ddns_cb, * cases (all are configuration mistakes): * a) IPv4: user have duplicate fixed-address entries (the same * address is defined twice). We may have found wrong lease. - * b) IPv6: user have overlapping pools (we tried to find + * b) IPv6: user have overlapping pools (we tried to find * a lease in a wrong pool) * c) IPv6: user have duplicate fixed-address6 entires (the same * address is defined twice). We may have found wrong lease. @@ -930,29 +939,29 @@ update_lease_failed(struct lease *lease, sprintf(lease_address, "unknown"); /* - * let's pretend that everything is ok, so we can continue for + * let's pretend that everything is ok, so we can continue for * information gathering purposes */ - + if (ddns_cb != NULL) { - strncpy(lease_address, piaddr(ddns_cb->address), + strncpy(lease_address, piaddr(ddns_cb->address), MAX_ADDRESS_STRING_LEN); - + if (ddns_cb->address.len == 4) { sprintf(reason, "duplicate IPv4 fixed-address entry"); } else if (ddns_cb->address.len == 16) { sprintf(reason, "duplicate IPv6 fixed-address6 entry " "or overlapping pools"); } else { - /* - * Should not happen. We have non-IPv4, non-IPv6 + /* + * Should not happen. We have non-IPv4, non-IPv6 * address. Something is very wrong here. */ sprintf(reason, "corrupted ddns_cb structure (address " "length is %d)", ddns_cb->address.len); } } - + log_error("Failed to properly update internal lease structure with " "DDNS"); log_error("control block structures. Tried to update lease for" @@ -989,7 +998,7 @@ safe_lease_update(struct lease *lease, { if (lease == NULL) { /* should never get here */ - log_fatal("Impossible condition at %s:%d (called from %s:%d).", + log_fatal("Impossible condition at %s:%d (called from %s:%d).", MDL, file, line); } @@ -999,7 +1008,7 @@ safe_lease_update(struct lease *lease, * are most likely trying to update wrong lease here. */ - /* + /* * Previously this error message popped out during * DNS update for fixed leases. As we no longer * try to update the lease for a fixed (static) lease @@ -1013,18 +1022,18 @@ safe_lease_update(struct lease *lease, #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(lease, NULL, oldcb, newcb, file, line); #endif - /* - * May not reach this: update_lease_failed calls + /* + * May not reach this: update_lease_failed calls * log_fatal. - */ + */ return; } if ( (lease->ddns_cb != NULL) && (lease->ddns_cb != oldcb) ) { - /* + /* * There is existing cb structure, but it differs from - * what we expected to see there. Most likely we are - * trying to update wrong lease. + * what we expected to see there. Most likely we are + * trying to update wrong lease. */ log_error("%s(%d): Failed to update internal lease " "structure with DDNS control block. Existing" @@ -1036,11 +1045,11 @@ safe_lease_update(struct lease *lease, #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(lease, NULL, oldcb, newcb, file, line); #endif - /* - * May not reach this: update_lease_failed calls + /* + * May not reach this: update_lease_failed calls * log_fatal. - */ - return; + */ + return; } /* additional IPv4 specific checks may be added here */ @@ -1059,12 +1068,12 @@ safe_lease6_update(struct iasubopt *lease6, if (lease6 == NULL) { /* should never get here */ - log_fatal("Impossible condition at %s:%d (called from %s:%d).", + log_fatal("Impossible condition at %s:%d (called from %s:%d).", MDL, file, line); } if ( (lease6->ddns_cb == NULL) && (newcb == NULL) ) { - inet_ntop(AF_INET6, &lease6->addr, addrbuf, + inet_ntop(AF_INET6, &lease6->addr, addrbuf, MAX_ADDRESS_STRING_LEN); /* * Trying to clean up pointer that is already null. We @@ -1079,20 +1088,20 @@ safe_lease6_update(struct iasubopt *lease6, update_lease_failed(NULL, lease6, oldcb, newcb, file, line); #endif - /* - * May not reach this: update_lease_failed calls + /* + * May not reach this: update_lease_failed calls * log_fatal. - */ - return; + */ + return; } if ( (lease6->ddns_cb != NULL) && (lease6->ddns_cb != oldcb) ) { - /* + /* * there is existing cb structure, but it differs from - * what we expected to see there. Most likely we are - * trying to update wrong lease. + * what we expected to see there. Most likely we are + * trying to update wrong lease. */ - inet_ntop(AF_INET6, &lease6->addr, addrbuf, + inet_ntop(AF_INET6, &lease6->addr, addrbuf, MAX_ADDRESS_STRING_LEN); log_error("%s(%d): Failed to update internal lease " @@ -1105,14 +1114,14 @@ safe_lease6_update(struct iasubopt *lease6, #if defined (DNS_UPDATES_MEMORY_CHECKS) update_lease_failed(NULL, lease6, oldcb, newcb, file, line); #endif - /* - * May not reach this: update_lease_failed calls + /* + * May not reach this: update_lease_failed calls * log_fatal. - */ + */ return; } /* additional IPv6 specific checks may be added here */ - + /* update the lease */ lease6->ddns_cb = newcb; } @@ -1132,7 +1141,7 @@ safe_lease6_update(struct iasubopt *lease6, * using the same value twice allows me more flexibility. */ -isc_result_t +isc_result_t ddns_update_lease_ptr(struct lease *lease, struct iasubopt *lease6, dhcp_ddns_cb_t *ddns_cb, @@ -1165,7 +1174,7 @@ ddns_update_lease_ptr(struct lease *lease, return (ISC_R_SUCCESS); } - /* + /* * If we are processing an expired or released v6 lease * we don't actually have a lease to update */ @@ -1175,10 +1184,10 @@ ddns_update_lease_ptr(struct lease *lease, } if (lease != NULL) { - safe_lease_update(lease, ddns_cb, ddns_cb_set, + safe_lease_update(lease, ddns_cb, ddns_cb_set, file, line); } else if (lease6 != NULL) { - safe_lease6_update(lease6, ddns_cb, ddns_cb_set, + safe_lease6_update(lease6, ddns_cb, ddns_cb_set, file, line); } else if (ddns_cb->address.len == 4) { struct lease *find_lease = NULL; @@ -1186,10 +1195,10 @@ ddns_update_lease_ptr(struct lease *lease, ddns_cb->address, MDL) != 0) { #if defined (DEBUG_DNS_UPDATES) log_info("%s(%d): find_lease_by_ip_addr(%s) successful:" - "lease=%p", file, line, ddns_address, + "lease=%p", file, line, ddns_address, find_lease); #endif - + safe_lease_update(find_lease, ddns_cb, ddns_cb_set, file, line); lease_dereference(&find_lease, MDL); @@ -1204,11 +1213,11 @@ ddns_update_lease_ptr(struct lease *lease, file, line); #endif /* - * may not reach this. update_lease_failed - * calls log_fatal. + * may not reach this. update_lease_failed + * calls log_fatal. */ - return(ISC_R_FAILURE); - + return(ISC_R_FAILURE); + } } else if (ddns_cb->address.len == 16) { struct iasubopt *find_lease6 = NULL; @@ -1217,9 +1226,9 @@ ddns_update_lease_ptr(struct lease *lease, char addrbuf[MAX_ADDRESS_STRING_LEN]; memcpy(&addr, &ddns_cb->address.iabuf, 16); - if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) != + if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) != ISC_R_SUCCESS) && - (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != + (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS)) { inet_ntop(AF_INET6, &addr, addrbuf, MAX_ADDRESS_STRING_LEN); @@ -1230,8 +1239,8 @@ ddns_update_lease_ptr(struct lease *lease, file, line); #endif /* - * never reached. update_lease_failed - * calls log_fatal. + * never reached. update_lease_failed + * calls log_fatal. */ return(ISC_R_FAILURE); } @@ -1250,20 +1259,20 @@ ddns_update_lease_ptr(struct lease *lease, file, line); #endif /* - * never reached. update_lease_failed - * calls log_fatal. + * never reached. update_lease_failed + * calls log_fatal. */ return(ISC_R_FAILURE); } ipv6_pool_dereference(&pool, MDL); } else { /* shouldn't get here */ - log_fatal("Impossible condition at %s:%d, called from %s:%d.", + log_fatal("Impossible condition at %s:%d, called from %s:%d.", MDL, file, line); } return(ISC_R_SUCCESS); -} +} void ddns_ptr_add(dhcp_ddns_cb_t *ddns_cb, @@ -1373,8 +1382,12 @@ ddns_ptr_remove(dhcp_ddns_cb_t *ddns_cb, * -- "Interaction between DHCP and DNS" * * If the second query fails with NXRRSET, the updater must conclude - * that the client's desired name is in use by another host. At this - * juncture, the updater can decide (based on some administrative + * that the client's desired name is in use by another host. If + * Dual Stack Mixed Mode (DSMM) is enabled and we proceed to a + * third stage forward update attempt specific to DSMM rules. If not, + * then the existing entries are left intact: + * + * At this juncture, the updater can decide (based on some administrative * configuration outside of the scope of this document) whether to let * the existing owner of the name keep that name, and to (possibly) * perform some name disambiguation operation on behalf of the current @@ -1394,6 +1407,11 @@ ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb, const char *logstr = NULL; char ddns_address[MAX_ADDRESS_STRING_LEN]; +#if defined (DEBUG_DNS_UPDATES) + log_info ("DDNS:ddns_fwd_srv_add2: %s eresult: %d", + dump_ddns_cb(ddns_cb), eresult); +#endif + /* Construct a printable form of the address for logging */ strcpy(ddns_address, piaddr(ddns_cb->address)); @@ -1414,7 +1432,7 @@ ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb, ddns_cb->state = DDNS_STATE_ADD_PTR; ddns_cb->cur_func = ddns_ptr_add; - + result = ddns_modify_ptr(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; @@ -1427,8 +1445,21 @@ ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb, logstr = "DHCID mismatch, belongs to another client."; break; - case DNS_R_NXRRSET: case DNS_R_NXDOMAIN: + case DNS_R_NXRRSET: + /* If DSMM is on we need to try forward add3 */ + if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) { + ddns_cb->state = DDNS_STATE_DSMM_FW_ADD3; + ddns_cb->cur_func = ddns_fwd_srv_add3; + + result = ddns_modify_fwd(ddns_cb, MDL); + if (result == ISC_R_SUCCESS) { + return; + } + + break; + } + logstr = "Has an address record but no DHCID, not mine."; break; @@ -1467,6 +1498,11 @@ ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb, isc_result_t result; char ddns_address[MAX_ADDRESS_STRING_LEN]; +#if defined (DEBUG_DNS_UPDATES) + log_info ("DDNS: ddns_fwd_srv_add1: %s eresult: %d", + dump_ddns_cb(ddns_cb), eresult); +#endif + /* Construct a printable form of the address for logging */ strcpy(ddns_address, piaddr(ddns_cb->address)); @@ -1487,7 +1523,7 @@ ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb, ddns_cb->state = DDNS_STATE_ADD_PTR; ddns_cb->cur_func = ddns_ptr_add; - + result = ddns_modify_ptr(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; @@ -1499,13 +1535,12 @@ ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb, /* we can reuse the zone information */ ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID; ddns_cb->cur_func = ddns_fwd_srv_add2; - + result = ddns_modify_fwd(ddns_cb, MDL); if (result == ISC_R_SUCCESS) { return; } break; - default: log_error ("Unable to add forward map from %.*s to %s: %s", (int)ddns_cb->fwd_name.len, @@ -1531,6 +1566,89 @@ ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb, return; } +/* + * This action routine is invoked after the DSMM third add stage is + * attempted. If we succeeded we attempt to update the reverse DNS, + * if not we cleanup and leave. + */ +void +ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb, + isc_result_t eresult) +{ + isc_result_t result; + const char *logstr = NULL; + char ddns_address[MAX_ADDRESS_STRING_LEN]; + +#if defined (DEBUG_DNS_UPDATES) + log_info ("DDNS: ddns_fwd_srv_add3: %s eresult: %d", + dump_ddns_cb(ddns_cb), eresult); +#endif + + /* Construct a printable form of the address for logging */ + strcpy(ddns_address, piaddr(ddns_cb->address)); + + switch(eresult) { + case ISC_R_SUCCESS: + log_info("Added new forward map from %.*s to %s", + (int)ddns_cb->fwd_name.len, + (const char *)ddns_cb->fwd_name.data, + ddns_address); + + ddns_update_lease_text(ddns_cb, NULL); + + if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) { + /* if we have zone information get rid of it */ + if (ddns_cb->zone != NULL) { + ddns_cb_forget_zone(ddns_cb); + } + + ddns_cb->state = DDNS_STATE_ADD_PTR; + ddns_cb->cur_func = ddns_ptr_add; + + result = ddns_modify_ptr(ddns_cb, MDL); + if (result == ISC_R_SUCCESS) { + return; + } + } + break; + + case DNS_R_YXRRSET: + logstr = "an entry that is either static or " + "owned by another client exists."; + break; + + case DNS_R_NXRRSET: + logstr = "static entry of the other protocol type exists."; + break; + + default: + logstr = isc_result_totext(eresult); + break; + } + + if (logstr != NULL) { + log_error("Forward map from %.*s to %s FAILED: %s", + (int)ddns_cb->fwd_name.len, + (const char *)ddns_cb->fwd_name.data, + ddns_address, logstr); + } + + ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL); + ddns_cb_free(ddns_cb, MDL); + /* + * A single DDNS operation may require several calls depending on + * the current state as the prerequisites for the first message + * may not succeed requiring a second operation and potentially + * a ptr operation after that. The commit_leases operation is + * invoked at the end of this set of operations in order to require + * a single write for all of the changes. We call commit_leases + * here rather than immediately after the call to update the lease + * text in order to save any previously written data. + */ + commit_leases(); + return; +} + static void ddns_fwd_srv_connector(struct lease *lease, struct iasubopt *lease6, @@ -1540,6 +1658,11 @@ ddns_fwd_srv_connector(struct lease *lease, { isc_result_t result = ISC_R_FAILURE; +#if defined (DEBUG_DNS_UPDATES) + log_info ("DDNS: ddns_fwd_srv_connector: %s eresult: %d", + dump_ddns_cb(ddns_cb), eresult); +#endif + if (ddns_cb == NULL) { /* nothing to do */ return; @@ -1565,6 +1688,7 @@ ddns_fwd_srv_connector(struct lease *lease, } } + if (result == ISC_R_SUCCESS) { ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL); } else { @@ -1591,6 +1715,10 @@ void ddns_fwd_srv_rem2(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult) { +#if defined (DEBUG_DNS_UPDATES) + log_info ("DDNS: ddns_fwd_srv_rem2: %s eresult: %d", + dump_ddns_cb(ddns_cb), eresult); +#endif /* * To get here we have already managed to remove the A/AAAA @@ -1651,12 +1779,17 @@ ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb, isc_result_t result = eresult; char ddns_address[MAX_ADDRESS_STRING_LEN]; +#if defined (DEBUG_DNS_UPDATES) + log_info ("DDNS: ddns_fwd_srv_rem1: %s eresult: %d", + dump_ddns_cb(ddns_cb), eresult); +#endif + switch(eresult) { case ISC_R_SUCCESS: /* Construct a printable form of the address for logging */ strcpy(ddns_address, piaddr(ddns_cb->address)); log_info("Removed forward map from %.*s to %s", - (int)ddns_cb->fwd_name.len, + (int)ddns_cb->fwd_name.len, (const char*)ddns_cb->fwd_name.data, ddns_address); @@ -1671,12 +1804,31 @@ ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb, case DNS_R_NXRRSET: case DNS_R_NXDOMAIN: + /* A result of not found means rem1 did not find a guard of + * our * type. From this we assume either there are no address + * record(s) of our type to delete or they are unguarded and + * therefore presumed to be static. Either way, we're done + * unless we're in DSMM and ddns-other-guard-is-dynamic is on. + * In which case we need to see if a guard of the other type + * exists, which permits us to delete any address records of + * our type. */ + #define DSMM_OGD (DDNS_DUAL_STACK_MIXED_MODE | \ + DDNS_OTHER_GUARD_IS_DYNAMIC) + if ((ddns_cb->flags & DSMM_OGD) == DSMM_OGD) { + ddns_cb->state = DDNS_STATE_REM_FW_DSMM_OTHER; + ddns_cb->cur_func = ddns_fwd_srv_rem2; + result = ddns_modify_fwd(ddns_cb, MDL); + if (result == ISC_R_SUCCESS) { + return; + } + break; + } + ddns_update_lease_text(ddns_cb, NULL); #if defined (DEBUG_DNS_UPDATES) log_info("DDNS: no forward map to remove. %p", ddns_cb); #endif - /* Trigger the add operation */ eresult = ISC_R_SUCCESS; @@ -1719,19 +1871,19 @@ ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb, * Remove relevant entries from DNS. * * \li lease - lease to start with if this is for v4 - * + * * \li lease6 - lease to start with if this is for v6 - * + * * \li add_ddns_cb - control block for additional DDNS work. This * is used when the code is going to add a DDNS entry after removing * the current entry. - * + * * \li active - indication about the status of the lease. It is * ISC_TRUE if the lease is still active, and FALSE if the lease * is inactive. This is used to indicate if the lease is inactive or going * to inactive so we can avoid trying to update the lease with cb pointers * and text information if it isn't useful. - * + * * Returns * \li #ISC_R_FAILURE - badness occurred and we weren't able to do what was wanted * \li #ISC_R_SUCCESS - we were able to do stuff but it's in progress @@ -1751,15 +1903,20 @@ ddns_removals(struct lease *lease, dhcp_ddns_cb_t *ddns_cb = NULL; struct data_string leaseid; +#if defined (DEBUG_DNS_UPDATES) + log_info ("DDNS: ddns_removals: %s", + dump_ddns_cb(add_ddns_cb)); +#endif + /* * See if we need to cancel an outstanding request. Mostly this is * used to handle the case where this routine is called twice for * the same release or abandon event. - * + * * When called from the dns code as part of an update request * (add_ddns_cb != NULL) any outstanding requests will have already * been cancelled. - * + * * If the new request is just a removal and we have an outstanding * request we have several options: * @@ -1773,13 +1930,13 @@ ddns_removals(struct lease *lease, * done after the removal we need to kill the update part of the * request. */ - + if (add_ddns_cb == NULL) { if ((lease != NULL) && (lease->ddns_cb != NULL)) { ddns_cb = lease->ddns_cb; /* - * Is the old request an update or did the + * Is the old request an update or did the * the active flag change? */ if (((ddns_cb->state == DDNS_STATE_ADD_PTR) || @@ -1807,7 +1964,7 @@ ddns_removals(struct lease *lease, ddns_cb = lease6->ddns_cb; /* - * Is the old request an update or did the + * Is the old request an update or did the * the active flag change? */ if (((ddns_cb->state == DDNS_STATE_ADD_PTR) || @@ -1841,6 +1998,9 @@ ddns_removals(struct lease *lease, goto cleanup; } + /* Set the conflict detection flags based on global configuration */ + copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask); + /* * For v4 we flag static leases so we don't try * and manipulate the lease later. For v6 we don't @@ -1906,23 +2066,25 @@ ddns_removals(struct lease *lease, } /* - * Find the txt or dhcid tag and copy it to the control block. If we don't - * have one this isn't an interim or standard record so we can't delete - * the A record using this mechanism but we can delete the ptr record. - * In this case we will attempt to do any requested next step. + * Find the txt or dhcid tag and copy it to the control block. If we + * don't have one this isn't an interim or standard record so we can't + * delete the A record using this mechanism but we can delete the ptr + * record. In this case we will attempt to do any requested next step. */ memset(&leaseid, 0, sizeof(leaseid)); if (find_bound_string (&leaseid, *scope, ddns_standard_tag)) { /* We have a standard tag */ ddns_cb->lease_tag = ddns_standard_tag; ddns_cb->dhcid_class = dns_rdatatype_dhcid; + ddns_cb->other_dhcid_class = dns_rdatatype_txt; data_string_copy(&ddns_cb->dhcid, &leaseid, MDL); data_string_forget(&leaseid, MDL); } else if (find_bound_string (&leaseid, *scope, ddns_interim_tag)) { /* we have an interim tag */ ddns_cb->lease_tag = ddns_interim_tag; ddns_cb->dhcid_class = dns_rdatatype_txt; - if (dhcid_fromlease(&ddns_cb->dhcid, &leaseid) != + ddns_cb->other_dhcid_class = dns_rdatatype_dhcid; + if (dhcid_fromlease(&ddns_cb->dhcid, &leaseid) != ISC_R_SUCCESS) { /* We couldn't convert the dhcid from the lease * version to the dns version. We can't delete @@ -1933,7 +2095,7 @@ ddns_removals(struct lease *lease, data_string_forget(&leaseid, MDL); } else { ddns_cb->flags &= ~DDNS_UPDATE_ADDR; - } + } /* * Find the rev name and copy it to the control block. If we don't @@ -1943,7 +2105,8 @@ ddns_removals(struct lease *lease, if (!find_bound_string(&ddns_cb->rev_name, *scope, "ddns-rev-name")) { ddns_cb->flags &= ~DDNS_UPDATE_PTR; } - + + /* * If we have a second control block for doing an add * after the remove finished attach it to our control block. @@ -1996,7 +2159,7 @@ ddns_removals(struct lease *lease, */ if (execute_add != ISC_R_SUCCESS) { ddns_cb->next_op = NULL; - ddns_fwd_srv_connector(lease, lease6, scope, + ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add); add_ddns_cb = NULL; } @@ -2025,10 +2188,174 @@ ddns_removals(struct lease *lease, * we allocated here. */ ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add); - if (ddns_cb != NULL) + if (ddns_cb != NULL) ddns_cb_free(ddns_cb, MDL); return (result); } +/* Convenience function for setting flag bits in a mask */ +void set_flag (u_int16_t *flags, + u_int16_t flag, + u_int16_t value) { + if (flags) { + if (value) { + *flags |= flag; + } else { + *flags &= ~flag; + } + } +} + +/* + * Convenience function which replicates the conflict flags set in one + * mask to another, while preserving all other flags. + */ +void copy_conflict_flags(u_int16_t *target, + u_int16_t source) { + if (target) { + /* Preserve non conflict flags */ + *target &= ~CONFLICT_BITS; + + /* Enable conflict flags per source */ + *target |= source & CONFLICT_BITS; + } +} + +/* + * Given an option_state, create a mask of conflict detection flags based + * on the appropriate configuration parameters within the option state. + */ +u_int16_t +get_conflict_mask(struct option_state *options) { + + int ddns_update_conflict_detection = 1; /* default on */ + int ddns_dual_stack_mixed_mode = 0; /* default off */ + int ddns_guard_id_must_match = 1; /* default on */ + int ddns_other_guard_is_dynamic = 0; /* default off */ + struct option_cache *oc = NULL; + + u_int16_t mask = 0; + oc = lookup_option(&server_universe, options, SV_DDNS_CONFLICT_DETECT); + if (oc) { + ddns_update_conflict_detection = + evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, + NULL, &global_scope, oc, MDL); + } + + set_flag(&mask, DDNS_CONFLICT_DETECTION, + ddns_update_conflict_detection); + + if (!ddns_update_conflict_detection) { +#if defined (DEBUG_DNS_UPDATES) + log_info ("DDNS conflict detection: off"); +#endif + /* Turn the rest of the conflict related flags off */ + set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE, 0); + set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH, 0); + set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC, 0); + return (mask); + } + + // Get the values + oc = lookup_option(&server_universe, options, + SV_DDNS_DUAL_STACK_MIXED_MODE); + if (oc) { + ddns_dual_stack_mixed_mode = + evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, + NULL, &global_scope, oc, MDL); + } + + oc = lookup_option(&server_universe, options, + SV_DDNS_GUARD_ID_MUST_MATCH); + if (oc) { + ddns_guard_id_must_match = + evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, + NULL, &global_scope, oc, MDL); + } + + oc = lookup_option(&server_universe, options, + SV_DDNS_OTHER_GUARD_IS_DYNAMIC); + if (oc) { + ddns_other_guard_is_dynamic = + evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, + NULL, &global_scope, oc, MDL); + } + + // Set the flags + set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE, + ddns_dual_stack_mixed_mode); + + set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH, + ddns_guard_id_must_match); + + set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC, + ddns_other_guard_is_dynamic); + +#if defined (DEBUG_DNS_UPDATES) + log_info ("DDNS conflict behavior:\n" + "\tddns-update-style: %s\n" + "\tupdate-conflict-detection: %d\n" + "\tddns-dual-stack-mixed-mode: %d\n" + "\tddns-guard-id-must-match %d\n" + "\tddns-other-guard-is-dynamic: %d\n", + ddns_styles_values[ddns_update_style].name, + ddns_update_conflict_detection, + ddns_dual_stack_mixed_mode, + ddns_guard_id_must_match, + ddns_other_guard_is_dynamic); +#endif + return (mask); +} + +#if defined (DEBUG_DNS_UPDATES) +/* Type used for creating lists of function pointers and their names */ +typedef struct { + void *ptr; + char *name; +} LabeledPtr; + +/* Returns the name of the function referred to by the given address */ +char* +dump_ddns_cb_func(void *func) { + static LabeledPtr funcs[] = { + { ddns_ptr_add, "ddns_ptr_add" }, + { ddns_fwd_srv_add2, "ddns_fwd_srv_add2" }, + { ddns_fwd_srv_add1, "ddns_fwd_srv_add1" }, + { ddns_ptr_remove, "ddns_ptr_remove" }, + { ddns_fwd_srv_rem2, "ddns_fwd_srv_rem2" }, + { ddns_fwd_srv_rem1, "ddns_fwd_srv_rem1" }, + { ddns_fwd_srv_add3, "ddns_fwd_srv_adde" }, + { NULL, "unknown" } + }; + + LabeledPtr* lp = funcs; + if (!func) { + return ("<null>"); + } + + while ((lp->ptr) && (lp->ptr != func)) { + ++lp; + } + + return (lp->name); +} + +/* Dumps basic control block info to the log */ +char* +dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb) { + static char output_buf[4096]; + if (!ddns_cb) { + return ("<ddns_cb is null>"); + } + + sprintf (output_buf, "ddns_cb: %p flags: %x state: %s cur_func: %s", + ddns_cb, ddns_cb->flags, + ddns_state_name(ddns_cb->state), + dump_ddns_cb_func(ddns_cb->cur_func)); + + return(output_buf); +} +#endif /* DEBUG_DNS_UPDATES */ + #endif /* NSUPDATE */ |