summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Markwalder <tmark@isc.org>2017-12-08 11:56:10 -0500
committerThomas Markwalder <tmark@isc.org>2017-12-08 11:56:10 -0500
commit905c58b9cac7b734c0518786476bd7c2e31b8977 (patch)
tree2710dcf3cd0c36bc686ce38e4dc8c7827cfbd8b7
parent1aca5897f269bdfa922a000ec3f3e4b588a7b966 (diff)
downloadisc-dhcp-905c58b9cac7b734c0518786476bd7c2e31b8977.tar.gz
[master] Adds DDNS Dual Stack Mixed Mode
Merges in rt42620.
-rw-r--r--RELNOTES11
-rw-r--r--common/dns.c884
-rw-r--r--common/print.c185
-rw-r--r--includes/dhcpd.h65
-rw-r--r--includes/site.h1
-rw-r--r--server/ddns.c537
-rw-r--r--server/dhcpd.c43
-rw-r--r--server/dhcpd.conf.5115
-rw-r--r--server/stables.c3
9 files changed, 1373 insertions, 471 deletions
diff --git a/RELNOTES b/RELNOTES
index 3928d522..b3f08f1d 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -278,6 +278,17 @@ dhcp-users@lists.isc.org.
pool types: NA, TA, and PD.
[ISC-Bugs #45292]
+- Added three new server configuration parameters which influence DDNS:
+ 1. ddns-dual-stack-mixed-mode - alters DNS conflict resolution behavior
+ to mitigate issues with non-compliant clients in dual stack environments.
+
+ 2. ddns-guard-id-must-match - relaxes the DHCID RR client id matching
+ requirement of DNS conflict resolution.
+
+ 3. ddns-other-guard is-dynamic - alters dual-stack-mixed-mode behavior to
+ allow unguarded DNS entries to be overwritten in certain cases
+ [ISC-Bugs #42620]
+
Changes since 4.3.0 (bug fixes)
- Tidy up several small tickets.
diff --git a/common/dns.c b/common/dns.c
index 63bbc860..86a1ecd1 100644
--- a/common/dns.c
+++ b/common/dns.c
@@ -64,7 +64,7 @@
*
* You can also include IPv6 addresses via the primary6 and secondary6
* options. The search order for the addresses is primary, primary6,
- * secondary and lastly secondary6, with a limit on the number of
+ * secondary and lastly secondary6, with a limit on the number of
* addresses used. Currently this limit is 3.
*
* The DHCP server tries to find an existing zone for any given name by
@@ -89,8 +89,8 @@
* this while hunting up a matching zone for a name, it looks up the SOA,
* fills in the IP addresses, and uses that record for the update.
* If the SOA lookup returns NXRRSET, a warning is printed and the zone is
- * discarded, TSIG key and all. The search for the zone then continues
- * as if the zone record hadn't been found. Zones without IP addresses
+ * discarded, TSIG key and all. The search for the zone then continues
+ * as if the zone record hadn't been found. Zones without IP addresses
* don't match when initially hunting for a zone to update.
*
* When an update is attempted and no predefined zone is found
@@ -152,6 +152,11 @@ typedef struct dhcp_ddns_rdata {
dns_rdataset_t rdataset;
} dhcp_ddns_data_t;
+/* Function pointer type for functions which build DDNS update contents */
+typedef isc_result_t (*builder_func_t)(dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_data_t *dataspace,
+ dns_name_t *pname, dns_name_t *uname);
+
#if defined (NSUPDATE)
#if defined (DNS_ZONE_LOOKUP)
@@ -168,7 +173,7 @@ typedef struct dhcp_ddns_rdata {
*/
typedef struct dhcp_ddns_ns {
- struct dhcp_ddns_ns *next;
+ struct dhcp_ddns_ns *next;
struct data_string oname; /* the original name for DDNS */
char *zname; /* a pointer into the original name for
the zone we are checking */
@@ -176,7 +181,7 @@ typedef struct dhcp_ddns_ns {
namelist, we can't free the eventp
until we free the namelist */
dns_name_t *ns_name; /* current name server we are examining */
- dns_rdataset_t *rdataset;
+ dns_rdataset_t *rdataset;
dns_rdatatype_t rdtype; /* type of address we want */
struct in_addr addrs[DHCP_MAXNS]; /* space for v4 addresses */
@@ -303,8 +308,8 @@ void ddns_interlude(isc_task_t *, isc_event_t *);
/*
* Structure used to map old pointers to new pointers.
- * Old pointers are 8 bytes long as we don't know if the trace was
- * done on a 64 bit or 32 bit machine.
+ * Old pointers are 8 bytes long as we don't know if the trace was
+ * done on a 64 bit or 32 bit machine.
*/
#define TRACE_PTR_LEN 8
@@ -332,7 +337,7 @@ trace_ddns_input_write(dhcp_ddns_cb_t *ddns_cb, isc_result_t result)
trace_iov_t iov[2];
u_int32_t old_result;
char old_pointer[TRACE_PTR_LEN];
-
+
old_result = htonl((u_int32_t)result);
memset(old_pointer, 0, TRACE_PTR_LEN);
memcpy(old_pointer, &ddns_cb, sizeof(ddns_cb));
@@ -384,7 +389,7 @@ trace_ddns_input_read(trace_type_t *ttype, unsigned length,
if (ddns_map_ptr == NULL) {
log_error("trace_dns_input_read: unable to map cb pointer");
return;
- }
+ }
eventp = (dns_clientupdateevent_t *)
isc_event_allocate(dhcp_gbl_ctx.mctx,
@@ -418,7 +423,7 @@ trace_ddns_input_stop(trace_type_t *ttype)
* If we are doing playback we read the next packet from the file
* and compare the type. If it matches we extract the results and pointer
* from the trace file. The results are returned to the caller as if
- * they had called the dns routine. The pointer is used to construct a
+ * they had called the dns routine. The pointer is used to construct a
* map for when the "reply" is processed.
*
* The data written to trace file is:
@@ -439,7 +444,7 @@ trace_ddns_output_write(dns_client_t *client, dns_rdataclass_t rdclass,
u_int32_t old_result;
char old_pointer[TRACE_PTR_LEN];
dhcp_ddns_map_t *ddns_map_ptr;
-
+
if (trace_playback() != 0) {
/* We are doing playback, extract the entry from the file */
unsigned buflen = 0;
@@ -477,7 +482,7 @@ trace_ddns_output_write(dns_client_t *client, dns_rdataclass_t rdclass,
if (ddns_map_ptr == NULL) {
ddns_map_ptr = dmalloc(sizeof(*ddns_map_ptr), MDL);
if (ddns_map_ptr == NULL) {
- log_error("trace_ddns_output_write: "
+ log_error("trace_ddns_output_write: "
"unable to allocate map entry");
return(ISC_R_FAILURE);
}
@@ -572,7 +577,7 @@ ddns_cb_alloc(const char *file, int line)
return(ddns_cb);
}
-
+
void
ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
{
@@ -583,7 +588,7 @@ ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
data_string_forget(&ddns_cb->fwd_name, file, line);
data_string_forget(&ddns_cb->rev_name, file, line);
data_string_forget(&ddns_cb->dhcid, file, line);
-
+
if (ddns_cb->zone != NULL) {
forget_zone((struct dns_zone **)&ddns_cb->zone);
}
@@ -675,7 +680,7 @@ isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
dns_zone_hash_delete(dns_zone_hash, (*zone)->name, 0, MDL);
dns_zone_dereference(zone, MDL);
status = ISC_R_NOTFOUND;
- } else
+ } else
status = ISC_R_SUCCESS;
if (tname)
@@ -737,7 +742,7 @@ int dns_zone_dereference (ptr, file, line)
#if defined (NSUPDATE)
#if defined (DNS_ZONE_LOOKUP)
-/* Helper function to copy the address from an rdataset to
+/* Helper function to copy the address from an rdataset to
* the nameserver control block. Mostly to avoid really long
* lines in the nested for loops
*/
@@ -748,7 +753,7 @@ zone_addr_to_ns(dhcp_ddns_ns_t *ns_cb,
dns_rdata_t rdata;
dns_rdata_in_a_t a;
dns_rdata_in_aaaa_t aaaa;
-
+
dns_rdata_init(&rdata);
dns_rdataset_current(rdataset, &rdata);
switch (rdataset->type) {
@@ -815,7 +820,7 @@ find_zone_addrs(isc_task_t *taskp,
dns_name_t *name;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_ns_t ns;
-
+
/* the transaction is done, get rid of the tag */
dns_client_destroyrestrans(&ns_cb->transaction);
@@ -871,7 +876,7 @@ find_zone_addrs(isc_task_t *taskp,
for (;
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
-
+
if (rdataset->type != dns_rdatatype_ns)
continue;
dns_rdata_init(&rdata);
@@ -888,10 +893,10 @@ find_zone_addrs(isc_task_t *taskp,
}
} else {
if ((!dns_rdataset_isassociated(rdataset)) ||
- (dns_rdataset_first(rdataset) !=
+ (dns_rdataset_first(rdataset) !=
ISC_R_SUCCESS))
continue;
- }
+ }
dns_rdataset_current(rdataset, &rdata);
if (dns_rdata_tostruct(&rdata, &ns, NULL) !=
@@ -962,7 +967,7 @@ find_zone_addrs(isc_task_t *taskp,
isc_event_free(&eventp);
return;
-
+
}
/*
@@ -970,7 +975,7 @@ find_zone_addrs(isc_task_t *taskp,
* This is routine is called when we are still trying to get a list
* of nameservers to process.
*/
-
+
void
find_zone_ns(isc_task_t *taskp,
isc_event_t *eventp)
@@ -1019,7 +1024,7 @@ find_zone_ns(isc_task_t *taskp,
isc_result_totext(result));
goto cleanup;
}
-
+
/* we have successfully started the next iteration
* of this step, clean up from the call and continue */
dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient,
@@ -1044,7 +1049,7 @@ find_zone_ns(isc_task_t *taskp,
continue;
if ((!dns_rdataset_isassociated(rdataset)) ||
- (dns_rdataset_first(rdataset) !=
+ (dns_rdataset_first(rdataset) !=
ISC_R_SUCCESS))
continue;
@@ -1104,7 +1109,7 @@ find_zone_ns(isc_task_t *taskp,
data_string_forget(&ns_cb->oname, MDL);
dfree(ns_cb, MDL);
return;
-
+
}
/*
@@ -1116,7 +1121,7 @@ find_zone_ns(isc_task_t *taskp,
* the control block will be filled in as we continue processing.
*/
isc_result_t
-find_zone_start(dhcp_ddns_cb_t *ddns_cb, int direction)
+find_zone_start(dhcp_ddns_cb_t *ddns_cb, int direction)
{
isc_result_t status = ISC_R_NOTFOUND;
dhcp_ddns_ns_t *ns_cb;
@@ -1186,7 +1191,7 @@ find_zone_start(dhcp_ddns_cb_t *ddns_cb, int direction)
#endif
isc_result_t
-find_cached_zone(dhcp_ddns_cb_t *ddns_cb, int direction)
+find_cached_zone(dhcp_ddns_cb_t *ddns_cb, int direction)
{
isc_result_t status = ISC_R_NOTFOUND;
const char *np;
@@ -1408,7 +1413,7 @@ void cache_found_zone(dhcp_ddns_ns_t *ns_cb)
goto cleanup;
}
memcpy(zone->primary->data.buffer->data, ns_cb->addrs, len);
- zone->primary->data.data =
+ zone->primary->data.data =
&zone->primary->data.buffer->data[0];
zone->primary->data.len = len;
}
@@ -1422,7 +1427,7 @@ void cache_found_zone(dhcp_ddns_ns_t *ns_cb)
goto cleanup;
}
memcpy(zone->primary6->data.buffer->data, ns_cb->addrs6, len);
- zone->primary6->data.data =
+ zone->primary6->data.data =
&zone->primary6->data.buffer->data[0];
zone->primary6->data.len = len;
}
@@ -1515,7 +1520,7 @@ int get_std_dhcid(dhcp_ddns_cb_t *ddns_cb,
* This version of the function is for the interim style. It is retained
* to allow users to continue using the interim style but they should
* switch to the standard style (which uses get_std_dhcid) for better
- * interoperability.
+ * interoperability.
*
* This function takes information from the type and data fields and
* mangles it into a dhcid string which it places in ddns_cb. It also
@@ -1552,7 +1557,7 @@ int get_int_dhcid (dhcp_ddns_cb_t *ddns_cb,
id->data = id->buffer->data;
/*
- * We put the length into the first byte to turn
+ * We put the length into the first byte to turn
* this into a dns text string. This avoid needing to
* copy the string to add the byte later.
*/
@@ -1565,7 +1570,7 @@ int get_int_dhcid (dhcp_ddns_cb_t *ddns_cb,
* to avoid disturbing customer's lease files
*/
id->buffer->data[2] = "0123456789abcdef"[type % 15];
-
+
/* Mash together an MD5 hash of the identifier. */
isc_md5_init(&md5);
isc_md5_update(&md5, data, len);
@@ -1593,7 +1598,7 @@ int get_dhcid(dhcp_ddns_cb_t *ddns_cb,
{
if (ddns_cb->dhcid_class == dns_rdatatype_dhcid)
return get_std_dhcid(ddns_cb, type, identifier, id_len);
- else
+ else
return get_int_dhcid(ddns_cb, type, identifier, id_len);
}
@@ -1639,7 +1644,7 @@ dhcid_fromlease(struct data_string *dhcid,
return(ISC_R_SUCCESS);
}
-/*
+/*
* Construct the dataset for this item.
* This is a fairly simple arrangement as the operations we do are simple.
* If there is data we simply have the rdata point to it - the formatting
@@ -1703,6 +1708,27 @@ make_dns_dataset(dns_rdataclass_t dataclass,
return(ISC_R_SUCCESS);
}
+#if defined (DEBUG_DNS_UPDATES)
+static void log_call(char *text, dns_name_t* pname, dns_name_t* uname) {
+ char buf1[512];
+ char buf2[512];
+ if (pname) {
+ dns_name_format(pname, buf1, 512);
+ } else {
+ *buf1=0;
+ }
+
+ if (uname) {
+ dns_name_format(uname, buf2, 512);
+ } else {
+ *buf2=0;
+ }
+
+ log_info ("DDNS: %s: pname:[%s] uname:[%s]", text, buf1, buf2);
+}
+#endif
+
+
/*
* When a DHCP client or server intends to update an A RR, it first
* prepares a DNS UPDATE query which includes as a prerequisite the
@@ -1725,13 +1751,17 @@ make_dns_dataset(dns_rdataclass_t dataclass,
*/
static isc_result_t
-ddns_modify_fwd_add1(dhcp_ddns_cb_t *ddns_cb,
+build_fwd_add1(dhcp_ddns_cb_t *ddns_cb,
dhcp_ddns_data_t *dataspace,
dns_name_t *pname,
dns_name_t *uname)
{
isc_result_t result;
+#if defined (DEBUG_DNS_UPDATES)
+ log_call("build_fwd_add1", pname, uname);
+#endif
+
/* Construct the prerequisite list */
if ((ddns_cb->flags & DDNS_INCLUDE_RRSET) != 0) {
/* The A RR shouldn't exist */
@@ -1764,7 +1794,7 @@ ddns_modify_fwd_add1(dhcp_ddns_cb_t *ddns_cb,
/* Add the DHCID RR */
result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
- dataspace,
+ dataspace,
(unsigned char *)ddns_cb->dhcid.data,
ddns_cb->dhcid.len, ddns_cb->ttl);
if (result != ISC_R_SUCCESS) {
@@ -1788,8 +1818,18 @@ ddns_modify_fwd_add1(dhcp_ddns_cb_t *ddns_cb,
* -- "Interaction between DHCP and DNS"
*
* The message for the second step depends on if we are doing conflict
- * resolution. If we are we include a prerequisite. If not we delete
- * the DHCID in addition to all A rrsets.
+ * resolution. If we are we include the prerequisite. The prerequiste
+ * will either:
+ * A. require the data value of the DHCID RR to match that of the client
+ * or
+ * B. required only that the DHCID RR of the configured class (DHCID or
+ * TXT) exist
+ *
+ * based on whether DDNS_GUARD_ID_MUST_MATCH is on (default) or off.
+ *
+ * The prerequisite is omitted if conflict detection is off.
+ *
+ * If not we delete the DHCID in addition to all A rrsets.
*
* Conflict resolution:
* DHCID RR exists, and matches client identity.
@@ -1804,24 +1844,42 @@ ddns_modify_fwd_add1(dhcp_ddns_cb_t *ddns_cb,
*/
static isc_result_t
-ddns_modify_fwd_add2(dhcp_ddns_cb_t *ddns_cb,
+build_fwd_add2(dhcp_ddns_cb_t *ddns_cb,
dhcp_ddns_data_t *dataspace,
dns_name_t *pname,
dns_name_t *uname)
{
isc_result_t result = ISC_R_SUCCESS;
+#if defined (DEBUG_DNS_UPDATES)
+ log_call("build_fwd_add2", pname, uname);
+#endif
+
/*
- * If we are doing conflict resolution (unset) we use a prereq list.
+ * If we are doing conflict detection we use a prereq list.
* If not we delete the DHCID in addition to all A rrsets.
*/
- if ((ddns_cb->flags & DDNS_CONFLICT_OVERRIDE) == 0) {
+ if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) {
/* Construct the prereq list */
- /* The DHCID RR exists and matches the client identity */
- result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
- dataspace,
- (unsigned char *)ddns_cb->dhcid.data,
- ddns_cb->dhcid.len, 0);
+ /* The DHCID RR exists and optionally matches the client's
+ * identity. If matching is turned off, we use the presence
+ * of a DHCID RR to signal that this is a dynamic entry and
+ * thus eligible for us to overwrite. If matching is on
+ * then we can only replace the entries if they belong to
+ * this client. */
+ unsigned char *match_id = NULL;
+ int match_id_len = 0;
+ int match_class = dns_rdataclass_any;
+ if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
+ match_id = (unsigned char*)(ddns_cb->dhcid.data);
+ match_id_len = ddns_cb->dhcid.len;
+ match_class = dns_rdataclass_in;
+ }
+
+ result = make_dns_dataset(match_class,
+ ddns_cb->dhcid_class,
+ dataspace,
+ match_id, match_id_len, 0);
if (result != ISC_R_SUCCESS) {
return(result);
}
@@ -1839,9 +1897,10 @@ ddns_modify_fwd_add2(dhcp_ddns_cb_t *ddns_cb,
ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
dataspace++;
- /* Add current DHCID RR */
- result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
- dataspace,
+ /* Add current DHCID RR, always include client id */
+ result = make_dns_dataset(dns_rdataclass_in,
+ ddns_cb->dhcid_class,
+ dataspace,
(unsigned char *)ddns_cb->dhcid.data,
ddns_cb->dhcid.len, ddns_cb->ttl);
if (result != ISC_R_SUCCESS) {
@@ -1852,7 +1911,7 @@ ddns_modify_fwd_add2(dhcp_ddns_cb_t *ddns_cb,
}
/* Start or continue constructing the update list */
- /* Delete the A RRset */
+ /* Delete the address RRset */
result = make_dns_dataset(dns_rdataclass_any, ddns_cb->address_type,
dataspace, NULL, 0, 0);
if (result != ISC_R_SUCCESS) {
@@ -1861,9 +1920,9 @@ ddns_modify_fwd_add2(dhcp_ddns_cb_t *ddns_cb,
ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
dataspace++;
- /* Add the A RR */
+ /* Add the address RR */
result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
- dataspace,
+ dataspace,
(unsigned char *)ddns_cb->address.iabuf,
ddns_cb->address.len, ddns_cb->ttl);
if (result != ISC_R_SUCCESS) {
@@ -1875,47 +1934,241 @@ ddns_modify_fwd_add2(dhcp_ddns_cb_t *ddns_cb,
}
/*
- * The entity chosen to handle the A record for this client (either the
- * client or the server) SHOULD delete the A record that was added when
- * the lease was made to the client.
- *
- * In order to perform this delete, the updater prepares an UPDATE
- * query which contains two prerequisites. The first prerequisite
- * asserts that the DHCID RR exists whose data is the client identity
- * described in Section 4.3. The second prerequisite asserts that the
- * data in the A RR contains the IP address of the lease that has
- * expired or been released.
- * -- "Interaction between DHCP and DNS"
+ * Creates the DNS foward update add used for DSMM add attempt #3 and
+ * ddns-other-guard-is-dynamic is off
*
- * RFC 4703 has relaxed the prereqisites to only checking the DHCID RR
- * and we have adopted that to minizmie problems due to interruptions
- * when doing a deletion.
*
- * First try has:
- * DHCID RR exists, and matches client identity.
- * Delete appropriate A RR.
+ * If the second update failed with NXRRSET, this indicates that:
+ *
+ * 1. our FQDN is in use
+ * 2 no guard record (DHCID RR) for that FQDN, of our class (and optionally
+ * client id) exists
+ *
+ * In Dual Stack Mixed Mode, we need to attempt a third add, to distinguish
+ * between static entries that we cannot modify and dynamic entries belonging
+ * to the "other" side of dual stack. The prerequisites for this add are:
+ *
+ * 1. No address record of my type exists
+ * 2. No guard record of my type exists
+ * 3. A guard record of the other type exists
+ *
+ * and updates which will add the new address and guard record:
+ *
+ * prereq nxrrset <name> <addr_t> # no address record of my type
+ * prereq nxrrset <name> <guard_t> # no guard record of my type
+ * prereq yxrrset <name> <other_guard_t> # other guard type does exist
+ * update add <name> <addr_t> <address> # add the new address record
+ * update add <name> <guard_t> <client-id> # add the new address record
*/
-
static isc_result_t
-ddns_modify_fwd_rem1(dhcp_ddns_cb_t *ddns_cb,
+build_dsmm_fwd_add3(dhcp_ddns_cb_t *ddns_cb,
dhcp_ddns_data_t *dataspace,
dns_name_t *pname,
dns_name_t *uname)
{
isc_result_t result = ISC_R_SUCCESS;
- /* Consruct the prereq list */
- /* The DHCID RR exists and matches the client identity */
+#if defined (DEBUG_DNS_UPDATES)
+ log_call("build_fwd_add3", pname, uname);
+#endif
+ /* Construct the prereq list */
+ /* No address record of my type exists */
+ result = make_dns_dataset(dns_rdataclass_none,
+ ddns_cb->address_type,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* No guard record of my type exists */
+ result = make_dns_dataset(dns_rdataclass_none,
+ ddns_cb->dhcid_class,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Guard record of the other type DOES exist */
+ result = make_dns_dataset(dns_rdataclass_any,
+ ddns_cb->other_dhcid_class,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Start constructing the update list. */
+ /* Add the address RR */
+ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
+ dataspace,
+ (unsigned char *)ddns_cb->address.iabuf,
+ ddns_cb->address.len, ddns_cb->ttl);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Add current DHCID RR */
result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
- dataspace,
+ dataspace,
(unsigned char *)ddns_cb->dhcid.data,
- ddns_cb->dhcid.len, 0);
+ ddns_cb->dhcid.len, ddns_cb->ttl);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * Creates the DNS foward update add used for DSMM add attempt #3 and
+ * ddns-other-guard-is-dynamic is ON
+ *
+ * If the second update failed with NXRRSET, this indicates that:
+ *
+ * 1. our FQDN is in use
+ * 2 no guard record (DHCID RR) for that FQDN, of our class (and optionally
+ * client id) exists
+ *
+ * When we're In Dual Stack Mixed Mode and ddns-other-guard-is-dynamic is ON
+ * we need only determine if a guard record of the other type exists, to know
+ * if we can add/replace and address record of our type. In other words,
+ * the presence of a dynamic entry made belonging to the "other" stack means
+ * all entries for this name should be dynamic and we overwrite an unguarded
+ * address record of our type.
+ *
+ * The udpate will contain a single prequisite for a guard record of the
+ * other type, an update to delete any address records of our type, and
+ * updates to add the address and guard records:
+ *
+ * prereq yxrrset <name> <other_guard_t> # other guard type exists
+ * update delete <name> <addr_t> # delete existing address record
+ * # (if one)
+ * update add <name> <addr_t> <address> # add new address record
+ * update add <name> <guard_t> <client-id> # add new guard record
+ */
+static isc_result_t
+build_dsmm_fwd_add3_other(dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_data_t *dataspace,
+ dns_name_t *pname,
+ dns_name_t *uname)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+
+#if defined (DEBUG_DNS_UPDATES)
+ log_call("build_fwd_add3_other", pname, uname);
+#endif
+ /* Construct the prereq list */
+ /* A guard record of the other type exists */
+ result = make_dns_dataset(dns_rdataclass_any,
+ ddns_cb->other_dhcid_class,
+ dataspace, NULL, 0, 0);
if (result != ISC_R_SUCCESS) {
return(result);
}
ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
dataspace++;
+ /* Start constructing the update list. */
+ /* Delete the existing address record of my type (if one) */
+ result = make_dns_dataset(dns_rdataclass_any,
+ ddns_cb->address_type,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Add the address RR */
+ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
+ dataspace,
+ (unsigned char *)ddns_cb->address.iabuf,
+ ddns_cb->address.len, ddns_cb->ttl);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Add current DHCID RR */
+ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
+ dataspace,
+ (unsigned char *)ddns_cb->dhcid.data,
+ ddns_cb->dhcid.len, ddns_cb->ttl);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * The entity chosen to handle the A record for this client (either the
+ * client or the server) SHOULD delete the A (or AAAA) record that was
+ * added when the lease was made to the client.
+ *
+ * If we are doing conflict resolution, the udpate will contain a prequisite
+ * that will either:
+ * A. require that a guard record of the configure class (DHCID or TXT) with
+ * a data value matching that the client exist (per RFC 4703)
+ * or
+ * B. require only that the guard record of the configured class exist
+ *
+ * based on whether DDNS_GUARD_ID_MUST_MATCH is on (default) or off.
+ *
+ * The prerequisite is omitted if conflict detection is off.
+ *
+ */
+static isc_result_t
+build_fwd_rem1(dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_data_t *dataspace,
+ dns_name_t *pname,
+ dns_name_t *uname)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+
+#if defined (DEBUG_DNS_UPDATES)
+ log_call("build_fwd_rem1", pname, uname);
+#endif
+
+ /* If we're doing conflict detection, add the guard record pre-req */
+ if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) {
+ /* Construct the prereq list */
+ /* The guard record exists and optionally matches the client's
+ * identity. If matching is turned off, we use the presence
+ * of a DHCID RR to signal that this is a dynamic entry and
+ * thus eligible for us to overwrite. If matching is on
+ * then we can only delete the entries if they belong to
+ * this client. */
+ unsigned char *match_id = NULL;
+ int match_id_len = 0;
+ int match_class = dns_rdataclass_any;
+ if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
+ match_id = (unsigned char*)(ddns_cb->dhcid.data);
+ match_id_len = ddns_cb->dhcid.len;
+ match_class = dns_rdataclass_in;
+ }
+
+ result = make_dns_dataset(match_class,
+ ddns_cb->dhcid_class,
+ dataspace,
+ match_id, match_id_len, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+ }
+
/* Construct the update list */
/* Delete A RRset */
result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type,
@@ -1942,14 +2195,20 @@ ddns_modify_fwd_rem1(dhcp_ddns_cb_t *ddns_cb,
* AAAA RR does not exist.
* Delete appropriate DHCID RR.
*/
-
static isc_result_t
-ddns_modify_fwd_rem2(dhcp_ddns_cb_t *ddns_cb,
+build_fwd_rem2(dhcp_ddns_cb_t *ddns_cb,
dhcp_ddns_data_t *dataspace,
dns_name_t *pname,
dns_name_t *uname)
{
isc_result_t result;
+ unsigned char *match_id = NULL;
+ int match_id_len = 0;
+ int match_class = dns_rdataclass_any;
+
+#if defined (DEBUG_DNS_UPDATES)
+ log_call("build_fwd_rem2", pname, uname);
+#endif
/* Construct the prereq list */
/* The A RR does not exist */
@@ -1972,10 +2231,143 @@ ddns_modify_fwd_rem2(dhcp_ddns_cb_t *ddns_cb,
/* Construct the update list */
/* Delete DHCID RR */
+
+ /* We'll specify the client id in the guard record delete if
+ * matching is enabled, otherwise we leave it off. */
+ if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
+ match_id = (unsigned char*)(ddns_cb->dhcid.data);
+ match_id_len = ddns_cb->dhcid.len;
+ match_class = dns_rdataclass_none;
+ }
+
+ result = make_dns_dataset(match_class, ddns_cb->dhcid_class,
+ dataspace,
+ match_id, match_id_len, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * Constructs the second stage forward remove, when the first stage
+ * succeeds and DSMM is enabled, and ddns-other-guard-is-dynamic is OFF
+ *
+ * Normal conflict detection requires that the guard record of the
+ * configured type only be deleted if there are no address records of
+ * any type. In Dual Stack Mixed Mode, we are only concerned with whether
+ * there any records or our configured address type remaining.
+ *
+ * This update consists of a single prequisite that there be no address
+ * records of our type followed by a delete of the guard record of our type
+ * and optionally matching client-id.
+ *
+ * prereq nxrrset name <addr_t> # no records of this address type exist
+ * update delete name <guard_t> <client_id> # delete the existing guard record
+ */
+static isc_result_t
+build_fwd_rem2_dsmm (dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_data_t *dataspace,
+ dns_name_t *pname,
+ dns_name_t *uname)
+{
+ isc_result_t result;
+ unsigned char *match_id = NULL;
+ int match_id_len = 0;
+ int match_class = dns_rdataclass_any;
+
+#if defined (DEBUG_DNS_UPDATES)
+ log_call("build_fwd_rem2_dsmm", pname, uname);
+#endif
+
+ /* Construct the prereq list */
+ /* The address RR does not exist */
+ result = make_dns_dataset(dns_rdataclass_none,
+ ddns_cb->address_type,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Construct the update list */
+ /* Delete DHCID RR */
+
+ /* We'll specify the client id in the guard record delete if
+ * matching is enabled, otherwise we leave it off. */
+ if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
+ match_id = (unsigned char*)(ddns_cb->dhcid.data);
+ match_id_len = ddns_cb->dhcid.len;
+ match_class = dns_rdataclass_none;
+ }
+
+ result = make_dns_dataset(match_class, ddns_cb->dhcid_class,
+ dataspace,
+ match_id, match_id_len, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * Constructs the second stage forward remove, when the first stage
+ * succeeds and DSMM is enabled and ddns-other-guard-is-dynamic is ON
+ *
+ * This update addresses the case when an address record of our type exists
+ * without a guard record of our type, yet a dynamic entry of the other type
+ * exists. The presence of a guard of the other type indicates that all
+ * entries for this name should be treated as dynamic, thus permitting us to
+ * remove the address record of our type.
+ *
+ * prereq nxrrset <name> <guard_t> # my guard type does not exist
+ * prereq yxrrset <name> <other_guard_t> # other guard type does exist
+ * update delete <name> <addr_t> address # delete the existing address record
+ *
+ */
+static isc_result_t
+build_fwd_rem2_dsmm_other(dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_data_t *dataspace,
+ dns_name_t *pname,
+ dns_name_t *uname)
+{
+ isc_result_t result;
+
+#if defined (DEBUG_DNS_UPDATES)
+ log_call("build_fwd_rem2_dsmm_other", pname, uname);
+#endif
+
+ /* Construct the prereq list */
+ /* No guard record of my type exists */
result = make_dns_dataset(dns_rdataclass_none, ddns_cb->dhcid_class,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Guard record of the OTHER type DOES exist */
+ result = make_dns_dataset(dns_rdataclass_any,
+ ddns_cb->other_dhcid_class,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Construct the update list */
+ /* Delete the address RRset */
+ result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type,
dataspace,
- (unsigned char *)ddns_cb->dhcid.data,
- ddns_cb->dhcid.len, 0);
+ (unsigned char *)ddns_cb->address.iabuf,
+ ddns_cb->address.len, 0);
if (result != ISC_R_SUCCESS) {
return(result);
}
@@ -2026,11 +2418,11 @@ void ddns_interlude(isc_task_t *taskp,
if ((ddns_cb->flags & DDNS_ABORT) == 0) {
log_info("DDNS: cleaning up lease pointer for a cancel "
"cb=%p", ddns_cb);
- /*
+ /*
* We shouldn't actually be able to get here but
* we are. This means we haven't cleaned up
* the lease pointer so we need to do that before
- * freeing the cb.
+ * freeing the cb.
*/
ddns_cb->cur_func(ddns_cb, eresult);
return;
@@ -2077,7 +2469,7 @@ void ddns_interlude(isc_task_t *taskp,
/* pass it along to be processed */
ddns_cb->cur_func(ddns_cb, eresult);
}
-
+
return;
}
@@ -2093,6 +2485,10 @@ ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
isc_result_t result;
dns_tsec_t *tsec_key = NULL;
+#if defined (DEBUG_DNS_UPDATES)
+ log_info("DDNS: ddns_modify_fwd");
+#endif
+
unsigned char *clientname;
dhcp_ddns_data_t *dataspace = NULL;
dns_namelist_t prereqlist, updatelist;
@@ -2155,7 +2551,7 @@ ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
/*
* If we have a zone try to get any information we need
- * from it - name, addresses and the key. The address
+ * from it - name, addresses and the key. The address
* and key may be empty the name can't be.
*/
if (ddns_cb->zone) {
@@ -2173,7 +2569,7 @@ ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
/* If we have any addresses get them */
zlist = &ddns_cb->zone_server_list;
}
-
+
if (ddns_cb->zone->key != NULL) {
/*
@@ -2204,7 +2600,7 @@ ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 4);
if (dataspace == NULL) {
log_error("Unable to allocate memory for fwd update");
- result = ISC_R_NOMEMORY;
+ result = ISC_R_NOMEMORY;
goto cleanup;
}
@@ -2213,48 +2609,82 @@ ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
switch(ddns_cb->state) {
case DDNS_STATE_ADD_FW_NXDOMAIN:
- result = ddns_modify_fwd_add1(ddns_cb, dataspace,
- pname, uname);
+ result = build_fwd_add1(ddns_cb, dataspace, pname, uname);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
ISC_LIST_APPEND(prereqlist, pname, link);
break;
+
case DDNS_STATE_ADD_FW_YXDHCID:
- result = ddns_modify_fwd_add2(ddns_cb, dataspace,
- pname, uname);
+ result = build_fwd_add2(ddns_cb, dataspace, pname, uname);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
- /* If we aren't doing conflict override we have entries
+ /* If we are doing conflict detection we have entries
* in the pname list and we need to attach it to the
* prereqlist */
- if ((ddns_cb->flags & DDNS_CONFLICT_OVERRIDE) == 0) {
+ if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) {
ISC_LIST_APPEND(prereqlist, pname, link);
}
break;
- case DDNS_STATE_REM_FW_YXDHCID:
- result = ddns_modify_fwd_rem1(ddns_cb, dataspace,
- pname, uname);
+
+ case DDNS_STATE_DSMM_FW_ADD3: {
+ /* We should only be here if we're doing DSMM */
+ builder_func_t builder;
+ if (ddns_cb->flags & DDNS_OTHER_GUARD_IS_DYNAMIC) {
+ builder = build_dsmm_fwd_add3_other;
+ } else {
+ builder = build_dsmm_fwd_add3;
+ }
+
+ result = (*builder)(ddns_cb, dataspace, pname, uname);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
+
ISC_LIST_APPEND(prereqlist, pname, link);
break;
- case DDNS_STATE_REM_FW_NXRR:
- result = ddns_modify_fwd_rem2(ddns_cb, dataspace,
- pname, uname);
+ }
+
+ case DDNS_STATE_REM_FW_YXDHCID:
+ result = build_fwd_rem1(ddns_cb, dataspace, pname, uname);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
ISC_LIST_APPEND(prereqlist, pname, link);
break;
+ case DDNS_STATE_REM_FW_NXRR: {
+ builder_func_t builder;
+
+ if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) {
+ builder = build_fwd_rem2_dsmm;
+ } else {
+ builder = build_fwd_rem2;
+ }
+
+ result = (*builder)(ddns_cb, dataspace, pname, uname);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup; }
+ ISC_LIST_APPEND(prereqlist, pname, link);
+ break;
+ }
+
+ case DDNS_STATE_REM_FW_DSMM_OTHER: {
+ result = build_fwd_rem2_dsmm_other(ddns_cb, dataspace,
+ pname, uname);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup; }
+ ISC_LIST_APPEND(prereqlist, pname, link);
+ break;
+ }
+
default:
- log_error("Invalid operation in ddns code.");
+ log_error("ddns_modify_fwd: Invalid state: %d", ddns_cb->state);
result = DHCP_R_INVALIDARG;
goto cleanup;
break;
@@ -2315,6 +2745,10 @@ ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
unsigned char buf[256];
int buflen;
+#if defined (DEBUG_DNS_UPDATES)
+ log_info("DDNS: ddns_modify_ptr");
+#endif
+
/* Creates client context if we need to */
result = dns_client_init();
if (result != ISC_R_SUCCESS) {
@@ -2403,7 +2837,7 @@ ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 2);
if (dataspace == NULL) {
log_error("Unable to allocate memory for fwd update");
- result = ISC_R_NOMEMORY;
+ result = ISC_R_NOMEMORY;
goto cleanup;
}
@@ -2422,46 +2856,10 @@ ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
ISC_LIST_APPEND(uname->list, &dataspace[0].rdataset, link);
/*
- * If we are updating the pointer we then add the new one
+ * If we are updating the pointer we then add the new one
* Add PTR RR.
*/
if (ddns_cb->state == DDNS_STATE_ADD_PTR) {
-#if 0
- /*
- * I've left this dead code in the file for now in case
- * we decide to try and get rid of the ns_name functions.
- * sar
- */
-
- /*
- * Need to convert pointer into on the wire representation
- * We replace the '.' characters with the lengths of the
- * next name and add a length to the beginning for the first
- * name.
- */
- if (ddns_cb->fwd_name.len == 1) {
- /* the root */
- buf[0] = 0;
- buflen = 1;
- } else {
- unsigned char *cp;
- buf[0] = '.';
- memcpy(&buf[1], ddns_cb->fwd_name.data,
- ddns_cb->fwd_name.len);
- for(cp = buf + ddns_cb->fwd_name.len, buflen = 0;
- cp != buf;
- cp--) {
- if (*cp == '.') {
- *cp = buflen;
- buflen = 0;
- } else {
- buflen++;
- }
- }
- *cp = buflen;
- buflen = ddns_cb->fwd_name.len + 1;
- }
-#endif
/*
* Need to convert pointer into on the wire representation
*/
@@ -2545,3 +2943,215 @@ ddns_cancel(dhcp_ddns_cb_t *ddns_cb, const char *file, int line) {
HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
dns_zone_reference, dns_zone_dereference, do_case_hash)
+
+#if defined (NSUPDATE)
+#if defined (DEBUG_DNS_UPDATES)
+/* Defines a type for creating list of labeled integers */
+typedef struct {
+ int val;
+ char *name;
+} LabeledInt;
+
+char*
+ddns_state_name(int state) {
+ static LabeledInt ints[] = {
+ { DDNS_STATE_CLEANUP, "DDNS_STATE_CLEANUP" },
+ { DDNS_STATE_ADD_FW_NXDOMAIN, "DDNS_STATE_ADD_FW_NXDOMAIN" },
+ { DDNS_STATE_ADD_FW_YXDHCID, "DDNS_STATE_ADD_FW_YXDHCID" },
+ { DDNS_STATE_ADD_PTR, "DDNS_STATE_ADD_PTR" },
+ { DDNS_STATE_DSMM_FW_ADD3, "DDNS_STATE_DSMM_FW_ADD3" },
+ { DDNS_STATE_REM_FW_YXDHCID, "DDNS_STATE_REM_FW_YXDHCID" },
+ { DDNS_STATE_REM_FW_NXRR, "DDNS_STATE_FW_NXRR" },
+ { DDNS_STATE_REM_PTR, "DDNS_STATE_REM_PTR" },
+ { -1, "unknown" },
+ };
+
+ LabeledInt* li = ints;
+ while (li->val != -1 && li->val != state) {
+ ++li;
+ }
+
+ return (li->name);
+}
+
+int
+add_nstring(char **orig, char *max, char *add, int add_len) {
+ if (*orig && (*orig + add_len < max)) {
+ strncpy(*orig, add, add_len);
+ *orig += add_len;
+ **orig = 0;
+ return (0);
+ }
+
+ return (-1);
+}
+
+int
+add_string(char **orig, char *max, char *add) {
+ return (add_nstring(orig, max, add, strlen(add)));
+}
+
+/*
+ * direction outbound (messages to the dns server)
+ * inbound (messages from the dns server)
+ * ddns_cb is the control block associated with the message
+ * result is the result from the dns code. For outbound calls
+ * it is from the call to pass the message to the dns library.
+ * For inbound calls it is from the event returned by the library.
+ *
+ * For outbound messages we print whatever we think is interesting
+ * from the control block.
+ * For inbound messages we only print the transaction id pointer
+ * and the result and expect that the user will match them up as
+ * necessary. Note well: the transaction information is opaque to
+ * us so we simply print the pointer to it. This should be sufficient
+ * to match requests and replys in a short sequence but is awkward
+ * when trying to use it for longer sequences.
+ */
+void
+print_dns_status (int direction,
+ struct dhcp_ddns_cb *ddns_cb,
+ isc_result_t result)
+{
+ char obuf[1024];
+ char *s = obuf, *end = &obuf[sizeof(obuf)-2];
+ char *en;
+ const char *result_str;
+ char ddns_address[
+ sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+
+ if (direction == DDNS_PRINT_INBOUND) {
+ log_info("DDNS reply: id ptr %p, result: %s",
+ ddns_cb->transaction, isc_result_totext(result));
+ return;
+ }
+
+ /*
+ * To avoid having to figure out if any of the strings
+ * aren't NULL terminated, just 0 the whole string
+ */
+ memset(obuf, 0, 1024);
+
+ en = "DDNS request: id ptr ";
+ if (s + strlen(en) + 16 < end) {
+ sprintf(s, "%s%p", en, ddns_cb->transaction);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ en = ddns_state_name(ddns_cb->state);
+
+ switch (ddns_cb->state) {
+ case DDNS_STATE_ADD_FW_NXDOMAIN:
+ case DDNS_STATE_ADD_FW_YXDHCID:
+ case DDNS_STATE_REM_FW_YXDHCID:
+ case DDNS_STATE_REM_FW_NXRR:
+ case DDNS_STATE_DSMM_FW_ADD3:
+ strcpy(ddns_address, piaddr(ddns_cb->address));
+ if (s + strlen(en) + strlen(ddns_address) +
+ ddns_cb->fwd_name.len + 7 < end) {
+ sprintf(s, " %s %s for %.*s", en, ddns_address,
+ ddns_cb->fwd_name.len,
+ ddns_cb->fwd_name.data);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ break;
+
+ case DDNS_STATE_ADD_PTR:
+ case DDNS_STATE_REM_PTR:
+ if (s + strlen(en) + ddns_cb->fwd_name.len +
+ ddns_cb->rev_name.len + 7 < end) {
+ sprintf(s, " %s %.*s for %.*s", en,
+ ddns_cb->fwd_name.len,
+ ddns_cb->fwd_name.data,
+ ddns_cb->rev_name.len,
+ ddns_cb->rev_name.data);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ break;
+
+ case DDNS_STATE_CLEANUP:
+ default:
+ if (s + strlen(en) < end) {
+ sprintf(s, "%s", en);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ break;
+ }
+
+ en = " zone: ";
+ if (s + strlen(en) + strlen((char *)ddns_cb->zone_name) < end) {
+ sprintf(s, "%s%s", en, ddns_cb->zone_name);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ /* @todo replace with format that matches bind9 zone file */
+ if (ddns_cb->dhcid_class == dns_rdatatype_dhcid) {
+ char *idbuf = NULL;
+ if (add_string(&s, end, "dhcid: [")) {
+ goto bailout;
+ }
+
+ idbuf = buf_to_hex(ddns_cb->dhcid.data,
+ ddns_cb->dhcid.len, MDL);
+ if (idbuf) {
+ int ret = add_string(&s, end, idbuf);
+ dfree(idbuf, MDL);
+ if (!ret) {
+ goto bailout;
+ }
+ }
+
+ if (add_string(&s, end, "]")) {
+ goto bailout;
+ }
+ } else {
+ /* 1st byte of a txt dhcid is length, so we skip printing it
+ * In the event it's empty, we end up not adding anything */
+ int skip_length_byte = (ddns_cb->dhcid.len > 0 ? 1 : 0);
+ if (add_string (&s, end, "txt: [") ||
+ add_nstring (&s, end,
+ (char *)ddns_cb->dhcid.data + skip_length_byte,
+ ddns_cb->dhcid.len - skip_length_byte) ||
+ add_string (&s, end, "]")) {
+ goto bailout;
+ }
+ }
+
+ en = " ttl: ";
+ if (s + strlen(en) + 10 < end) {
+ sprintf(s, "%s%ld", en, ddns_cb->ttl);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ en = " result: ";
+ result_str = isc_result_totext(result);
+ if (s + strlen(en) + strlen(result_str) < end) {
+ sprintf(s, "%s%s", en, result_str);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ bailout:
+ /*
+ * We either finished building the string or ran out
+ * of space, print whatever we have in case it is useful
+ */
+ log_info("%s", obuf);
+
+ return;
+}
+#endif /* DEBUG_DNS_UPDATES */
+#endif /* NSUPDATE */
diff --git a/common/print.c b/common/print.c
index 2926e6cb..5993c0e2 100644
--- a/common/print.c
+++ b/common/print.c
@@ -1305,191 +1305,6 @@ void indent_spaces (FILE *file, int indent)
fputc (' ', file);
}
-#if defined (NSUPDATE)
-#if defined (DEBUG_DNS_UPDATES)
-/*
- * direction outbound (messages to the dns server)
- * inbound (messages from the dns server)
- * ddns_cb is the control block associated with the message
- * result is the result from the dns code. For outbound calls
- * it is from the call to pass the message to the dns library.
- * For inbound calls it is from the event returned by the library.
- *
- * For outbound messages we print whatever we think is interesting
- * from the control block.
- * For inbound messages we only print the transaction id pointer
- * and the result and expect that the user will match them up as
- * necessary. Note well: the transaction information is opaque to
- * us so we simply print the pointer to it. This should be sufficient
- * to match requests and replys in a short sequence but is awkward
- * when trying to use it for longer sequences.
- */
-void
-print_dns_status (int direction,
- struct dhcp_ddns_cb *ddns_cb,
- isc_result_t result)
-{
- char obuf[1024];
- char *s = obuf, *end = &obuf[sizeof(obuf)-2];
- char *en;
- const char *result_str;
- char ddns_address[
- sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
-
- if (direction == DDNS_PRINT_INBOUND) {
- log_info("DDNS reply: id ptr %p, result: %s",
- ddns_cb->transaction, isc_result_totext(result));
- return;
- }
-
- /*
- * To avoid having to figure out if any of the strings
- * aren't NULL terminated, just 0 the whole string
- */
- memset(obuf, 0, 1024);
-
- en = "DDNS request: id ptr ";
- if (s + strlen(en) + 16 < end) {
- sprintf(s, "%s%p", en, ddns_cb->transaction);
- s += strlen(s);
- } else {
- goto bailout;
- }
-
- switch (ddns_cb->state) {
- case DDNS_STATE_ADD_FW_NXDOMAIN:
- en = " add forward ";
- break;
- case DDNS_STATE_ADD_FW_YXDHCID:
- en = " modify forward ";
- break;
-
- case DDNS_STATE_ADD_PTR:
- en = " add reverse ";
- break;
-
- case DDNS_STATE_REM_FW_YXDHCID:
- en = " remove forward ";
- break;
-
- case DDNS_STATE_REM_FW_NXRR:
- en = " remove rrset ";
- break;
-
- case DDNS_STATE_REM_PTR:
- en = " remove reverse ";
- break;
-
- case DDNS_STATE_CLEANUP:
- en = " cleanup ";
- break;
-
- default:
- en = " unknown state ";
- break;
- }
-
- switch (ddns_cb->state) {
- case DDNS_STATE_ADD_FW_NXDOMAIN:
- case DDNS_STATE_ADD_FW_YXDHCID:
- case DDNS_STATE_REM_FW_YXDHCID:
- case DDNS_STATE_REM_FW_NXRR:
- strcpy(ddns_address, piaddr(ddns_cb->address));
- if (s + strlen(en) + strlen(ddns_address) +
- ddns_cb->fwd_name.len + 5 < end) {
- sprintf(s, "%s%s for %.*s", en, ddns_address,
- ddns_cb->fwd_name.len,
- ddns_cb->fwd_name.data);
- s += strlen(s);
- } else {
- goto bailout;
- }
- break;
-
- case DDNS_STATE_ADD_PTR:
- case DDNS_STATE_REM_PTR:
- if (s + strlen(en) + ddns_cb->fwd_name.len +
- ddns_cb->rev_name.len + 5 < end) {
- sprintf(s, "%s%.*s for %.*s", en,
- ddns_cb->fwd_name.len,
- ddns_cb->fwd_name.data,
- ddns_cb->rev_name.len,
- ddns_cb->rev_name.data);
- s += strlen(s);
- } else {
- goto bailout;
- }
- break;
-
- case DDNS_STATE_CLEANUP:
- default:
- if (s + strlen(en) < end) {
- sprintf(s, "%s", en);
- s += strlen(s);
- } else {
- goto bailout;
- }
- break;
- }
-
- en = " zone: ";
- if (s + strlen(en) + strlen((char *)ddns_cb->zone_name) < end) {
- sprintf(s, "%s%s", en, ddns_cb->zone_name);
- s += strlen(s);
- } else {
- goto bailout;
- }
-
- en = " dhcid: ";
- if (ddns_cb->dhcid.len > 0) {
- if (s + strlen(en) + ddns_cb->dhcid.len-1 < end) {
- strcpy(s, en);
- s += strlen(s);
- strncpy(s, (char *)ddns_cb->dhcid.data+1,
- ddns_cb->dhcid.len-1);
- s += strlen(s);
- } else {
- goto bailout;
- }
- } else {
- en = " dhcid: <empty>";
- if (s + strlen(en) < end) {
- strcpy(s, en);
- s += strlen(s);
- } else {
- goto bailout;
- }
- }
-
- en = " ttl: ";
- if (s + strlen(en) + 10 < end) {
- sprintf(s, "%s%ld", en, ddns_cb->ttl);
- s += strlen(s);
- } else {
- goto bailout;
- }
-
- en = " result: ";
- result_str = isc_result_totext(result);
- if (s + strlen(en) + strlen(result_str) < end) {
- sprintf(s, "%s%s", en, result_str);
- s += strlen(s);
- } else {
- goto bailout;
- }
-
- bailout:
- /*
- * We either finished building the string or ran out
- * of space, print whatever we have in case it is useful
- */
- log_info("%s", obuf);
-
- return;
-}
-#endif
-#endif /* NSUPDATE */
-
/* Format the given time as "A; # B", where A is the format
* used by the parser, and B is the local time, for humans.
*/
diff --git a/includes/dhcpd.h b/includes/dhcpd.h
index 1a6e8ef3..44d543d1 100644
--- a/includes/dhcpd.h
+++ b/includes/dhcpd.h
@@ -805,6 +805,9 @@ struct lease_state {
#if defined (FAILOVER_PROTOCOL)
#define SV_CHECK_SECS_BYTE_ORDER 91
#endif
+#define SV_DDNS_DUAL_STACK_MIXED_MODE 92
+#define SV_DDNS_GUARD_ID_MUST_MATCH 93
+#define SV_DDNS_OTHER_GUARD_IS_DYNAMIC 94
#if !defined (DEFAULT_PING_TIMEOUT)
# define DEFAULT_PING_TIMEOUT 1
@@ -1738,29 +1741,37 @@ struct ipv6_pond {
*/
#define POND_TRACK_MAX ISC_UINT64_MAX
-/* Flags and state for dhcp_ddns_cb_t */
-#define DDNS_UPDATE_ADDR 0x01
-#define DDNS_UPDATE_PTR 0x02
-#define DDNS_INCLUDE_RRSET 0x04
-#define DDNS_CONFLICT_OVERRIDE 0x08
-#define DDNS_CLIENT_DID_UPDATE 0x10
-#define DDNS_EXECUTE_NEXT 0x20
-#define DDNS_ABORT 0x40
-#define DDNS_STATIC_LEASE 0x80
-#define DDNS_ACTIVE_LEASE 0x100
-/*
- * The following two groups are separate and we could reuse
- * values but not reusing them may be useful in the future.
- */
-#define DDNS_STATE_CLEANUP 0 // The previous step failed, cleanup
-
-#define DDNS_STATE_ADD_FW_NXDOMAIN 1
-#define DDNS_STATE_ADD_FW_YXDHCID 2
-#define DDNS_STATE_ADD_PTR 3
-
-#define DDNS_STATE_REM_FW_YXDHCID 17
-#define DDNS_STATE_REM_FW_NXRR 18
-#define DDNS_STATE_REM_PTR 19
+/* Flags for dhcp_ddns_cb_t */
+#define DDNS_UPDATE_ADDR 0x0001
+#define DDNS_UPDATE_PTR 0x0002
+#define DDNS_INCLUDE_RRSET 0x0004
+#define DDNS_CONFLICT_DETECTION 0x0008
+#define DDNS_CLIENT_DID_UPDATE 0x0010
+#define DDNS_EXECUTE_NEXT 0x0020
+#define DDNS_ABORT 0x0040
+#define DDNS_STATIC_LEASE 0x0080
+#define DDNS_ACTIVE_LEASE 0x0100
+#define DDNS_DUAL_STACK_MIXED_MODE 0x0200
+#define DDNS_GUARD_ID_MUST_MATCH 0x0400
+#define DDNS_OTHER_GUARD_IS_DYNAMIC 0x0800
+
+#define CONFLICT_BITS (DDNS_CONFLICT_DETECTION|\
+ DDNS_DUAL_STACK_MIXED_MODE|\
+ DDNS_GUARD_ID_MUST_MATCH|\
+ DDNS_OTHER_GUARD_IS_DYNAMIC)
+
+/* States for dhcp_ddns_cb_t */
+#define DDNS_STATE_CLEANUP 0 /* startup or the previous step failed, cleanup */
+
+#define DDNS_STATE_ADD_FW_NXDOMAIN 1
+#define DDNS_STATE_ADD_FW_YXDHCID 2
+#define DDNS_STATE_ADD_PTR 3
+#define DDNS_STATE_DSMM_FW_ADD3 4
+
+#define DDNS_STATE_REM_FW_YXDHCID 17
+#define DDNS_STATE_REM_FW_NXRR 18
+#define DDNS_STATE_REM_PTR 19
+#define DDNS_STATE_REM_FW_DSMM_OTHER 20
/*
* Flags for the dns print function
@@ -1803,11 +1814,12 @@ typedef struct dhcp_ddns_cb {
void *dataspace;
dns_rdataclass_t dhcid_class;
+ dns_rdataclass_t other_dhcid_class;
char *lease_tag;
} dhcp_ddns_cb_t;
extern struct ipv6_pool **pools;
-extern int num_pools;
+
/* External definitions... */
@@ -2081,6 +2093,9 @@ extern struct timeval cur_tv;
#define cur_time cur_tv.tv_sec
extern int ddns_update_style;
+#if defined (NSUPDATE)
+extern u_int16_t ddns_conflict_mask;
+#endif
extern int dont_use_fsync;
extern int server_id_check;
@@ -2193,6 +2208,7 @@ int ddns_updates(struct packet *, struct lease *, struct lease *,
struct iasubopt *, struct iasubopt *, struct option_state *);
isc_result_t ddns_removals(struct lease *, struct iasubopt *,
struct dhcp_ddns_cb *, isc_boolean_t);
+u_int16_t get_conflict_mask(struct option_state *input_options);
#if defined (TRACING)
void trace_ddns_init(void);
#endif
@@ -3156,6 +3172,7 @@ isc_result_t ddns_update_fwd(struct data_string *, struct iaddr,
unsigned);
isc_result_t ddns_remove_fwd(struct data_string *,
struct iaddr, struct data_string *);
+char *ddns_state_name(int state);
#endif /* NSUPDATE */
dhcp_ddns_cb_t *ddns_cb_alloc(const char *file, int line);
diff --git a/includes/site.h b/includes/site.h
index 220d31a5..461eaccf 100644
--- a/includes/site.h
+++ b/includes/site.h
@@ -105,7 +105,6 @@
/* Define this if you want to see the requests and replies between the
DHCP code and the DNS library code. */
-
/* #define DEBUG_DNS_UPDATES */
/* Define this if you want to debug the host part of the inform processing */
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 */
diff --git a/server/dhcpd.c b/server/dhcpd.c
index fbbb37c0..bf0036ca 100644
--- a/server/dhcpd.c
+++ b/server/dhcpd.c
@@ -48,7 +48,7 @@ static const char url [] =
# include <unistd.h>
# include <pwd.h>
/* get around the ISC declaration of group */
-# define group real_group
+# define group real_group
# include <grp.h>
# undef group
@@ -73,7 +73,10 @@ option server.ddns-hostname = \n\
option server.ddns-domainname = config-option domain-name; \n\
option server.ddns-rev-domainname = \"in-addr.arpa.\";";
+/* Stores configured DDNS conflict detection flags */
+u_int16_t ddns_conflict_mask;
#endif /* NSUPDATE */
+
int ddns_update_style;
int dont_use_fsync = 0; /* 0 = default, use fsync, 1 = don't use fsync */
int server_id_check = 0; /* 0 = default, don't check server id, 1 = do check */
@@ -237,7 +240,7 @@ static void setup_chroot (char *chroot_dir) {
}
#endif /* PARANOIA */
-int
+int
main(int argc, char **argv) {
int fd;
int i, status;
@@ -598,7 +601,7 @@ main(int argc, char **argv) {
const char *path = path_dhcpd_db;
path_dhcpd_db = realpath(path_dhcpd_db, NULL);
if (path_dhcpd_db == NULL)
- log_fatal("Failed to get realpath for %s: %s", path,
+ log_fatal("Failed to get realpath for %s: %s", path,
strerror(errno));
}
@@ -696,7 +699,7 @@ main(int argc, char **argv) {
#endif
}
}
-
+
if (local_family == AF_INET) {
remote_port = htons(ntohs(local_port) + 1);
} else {
@@ -789,13 +792,13 @@ main(int argc, char **argv) {
log_error ("** You must specify a lease file with -lf.");
log_error (" Dhcpd will not overwrite your default");
log_fatal (" lease file when playing back a trace. **");
- }
+ }
trace_file_replay (traceinfile);
#if defined (DEBUG_MEMORY_LEAKAGE) && \
defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
free_everything ();
- omapi_print_dmalloc_usage_by_caller ();
+ omapi_print_dmalloc_usage_by_caller ();
#endif
exit (0);
@@ -844,7 +847,7 @@ main(int argc, char **argv) {
#endif
/* test option should cause an early exit */
- if (cftest && !lftest)
+ if (cftest && !lftest)
exit(0);
/*
@@ -905,8 +908,8 @@ main(int argc, char **argv) {
* Remove addresses from our pools that we should not issue
* to clients.
*
- * We currently have no support for this in IPv4. It is not
- * as important in IPv4, as making pools with ranges that
+ * We currently have no support for this in IPv4. It is not
+ * as important in IPv4, as making pools with ranges that
* leave out interfaces and hosts is fairly straightforward
* using range notation, but not so handy with CIDR notation.
*/
@@ -985,7 +988,7 @@ main(int argc, char **argv) {
log_fatal ("setgroups: %m");
if (setgid (set_gid))
log_fatal ("setgid(%d): %m", (int) set_gid);
- }
+ }
if (set_uid) {
if (setuid (set_uid))
@@ -1268,6 +1271,12 @@ void postconf_initialization (int quiet)
log_fatal("Unable to complete ddns initialization");
}
}
+
+ /* Set the conflict detection flag mask based on globally
+ * defined DDNS configuration params. This mask should be
+ * to init ddns_cb::flags before for every DDNS transaction. */
+ ddns_conflict_mask = get_conflict_mask(options);
+
#else
/* If we don't have support for updates compiled in tell the user */
if (ddns_update_style != DDNS_UPDATE_STYLE_NONE) {
@@ -1352,7 +1361,7 @@ void postconf_initialization (int quiet)
}
oc = lookup_option(&server_universe, options, SV_PREFIX_LEN_MODE);
- if ((oc != NULL) &&
+ if ((oc != NULL) &&
evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
&global_scope, oc, MDL)) {
if (db.len == 1) {
@@ -1518,7 +1527,7 @@ int dhcpd_interface_setup_hook (struct interface_info *ip, struct iaddr *ia)
interface_reference (&subnet -> interface, ip, MDL);
subnet -> interface_address = *ia;
} else if (subnet -> interface != ip) {
- log_error ("Multiple interfaces match the %s: %s %s",
+ log_error ("Multiple interfaces match the %s: %s %s",
"same subnet",
subnet -> interface -> name, ip -> name);
}
@@ -1532,11 +1541,11 @@ int dhcpd_interface_setup_hook (struct interface_info *ip, struct iaddr *ia)
shared_network_reference
(&ip -> shared_network, share, MDL);
}
-
+
if (!share -> interface) {
interface_reference (&share -> interface, ip, MDL);
} else if (share -> interface != ip) {
- log_error ("Multiple interfaces match the %s: %s %s",
+ log_error ("Multiple interfaces match the %s: %s %s",
"same shared network",
share -> interface -> name, ip -> name);
}
@@ -1657,13 +1666,13 @@ static isc_result_t dhcp_io_shutdown_countdown (void *vlp)
if (no_pid_file == ISC_FALSE)
(void) unlink(path_dhcpd_pid);
exit (0);
- }
+ }
#else
if (shutdown_state == shutdown_done) {
#if defined (DEBUG_MEMORY_LEAKAGE) && \
defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
free_everything ();
- omapi_print_dmalloc_usage_by_caller ();
+ omapi_print_dmalloc_usage_by_caller ();
#endif
if (no_pid_file == ISC_FALSE)
(void) unlink(path_dhcpd_pid);
@@ -1707,7 +1716,7 @@ isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
/* Called on signal. */
log_info("Received signal %d, initiating shutdown.", shutdown_signal);
shutdown_signal = SIGUSR1;
-
+
/*
* Prompt the shutdown event onto the timer queue
* and return to the dispatch loop.
diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5
index 076a35fb..2c064fa6 100644
--- a/server/dhcpd.conf.5
+++ b/server/dhcpd.conf.5
@@ -1227,6 +1227,74 @@ on the PTR record, but the \fIinterim\fR update method does not do this.
In the final RFC this requirement was relaxed such that a server may
add a DHCID RR to the PTR record.
.PP
+.SH DDNS IN DUAL STACK ENVIRONMENTS
+As described in RFC 4703, section 5.2, in order to perform DDNS in dual
+stack environments, both IPv4 and IPv6 servers would need to be configured
+to use the standard update style and participating IPv4 clients MUST
+convey DUIDs as described in RFC 4361, section 6.1., in their
+dhcp-client-identifiers.
+.PP
+In a nutshell, this mechanism is intended to use globally unique DUIDs
+to idenfity both IPv4 and IPv6 clients, and where a device has both
+IPv4 and IPv6 leases it is identified by the same DUID. This allows
+a dual stack client to use the same FQDN for both mappings, while
+being protected from updates for other clients by the rules of conflict
+detection.
+.PP
+However, not all IPv4 clients implement this behavior which makes
+supporting them dual stack environments problematic. In order to
+address this issue ISC DHCP (as of 4.4.0) supports a new mode of
+DDNS conflict resolution referred to as Dual Stack Mixed Mode (DSMM).
+.PP
+The concept behind DSMM is relatively simple. All dhcp servers of one
+protocol (IPv4 or v6) use one ddns-update-style (interim or standard)
+while all servers of the "other" protocol will use the "other"
+ddns-udpate-style. In this way, all servers of a given protocol are
+using the same record type (TXT or DHCID) for their DHCID RR entries.
+This allows conflict detection to be enforced within each protocol
+without interferring with the other's entries.
+.PP
+DSMM modifications now ensure that IPv4 DSMM servers only ever modify
+A records, their associated PTR records and DHCID records, while DSMM
+IPv6 severs only modify AAAA records, their associated PTR records,
+and DHCID records.
+.PP
+Note that DSMM is not a perfect solution, it is a compromise that can
+work well provided all participating DNS updaters play by DSMM rules.
+As with anything else in life, it only works as well as those who
+particpate behave.
+.PP
+While conflict detection is enabled by default, DSMM is not. To enable
+DSMM, both update-conflict-detection and ddns-dual-stack-mixed-mode must
+be true.
+.PP
+.SH PROTECTING DNS ENTRIES FOR STATIC CLIENTS
+Built into conflict resolution is the protection of manually made entries
+for static clients. Per the rules of conflict resolution, a DNS updater
+may not alter forward DNS entries unless there is a DHCID RR which matches
+for whom the update is being made. Therefore, any forward DNS entries
+without a corresponding DHCID RR cannot be altered by such an updater.
+.PP
+In some environments, it may be desirable to use only this aspect of conflict
+resolution and allow DNS updaters to overwrite entries for dynamic clients
+regardless of what client owns them. In other words, the presence or lack
+of a DHCID RR is used to determine whether entries may or may not be
+overwritten. Whether or not the client matches the data value of the DHCID
+RR is irrelevant. This behavior, off by default, can be configured through
+the parameter, ddns-guard-id-must-match. As with DSMM, this behavior is
+can only be enabled if conflict resolution is enabled. This behavior should
+be considered carefully before electing to use it.
+.PP
+There is an additional parameter that can be used with DSMM
+ddns-other-guard-is-dynamic. When enabled along with DSMM, a server will
+regard the presence of a DHCID RR of the other style type as indicating that
+the forward DNS entries for that FQDN should be dynamic and may be overwritten.
+For example, such a server using interim style could overwrite the DNS entries
+for an FQDN if there is only a DHDID type DHDID RR for the FQDN. Essentially,
+if there are dynamic entries for one protocol, that is enough to overcome the
+static protection of entries for the other protocol. This behavior warrants
+careful consideration before electing to use it.
+.PP
.SH DYNAMIC DNS UPDATE SECURITY
.PP
When you set your DNS server up to allow updates from the DHCP server,
@@ -2070,6 +2138,34 @@ appended to the client's hostname to form a fully-qualified
domain-name (FQDN).
.RE
.PP
+The \fIddns-dual-stack-mixed-mode\fR statement
+.RS 0.25i
+.PP
+.B ddns-dual-stack-mixed-mode \fIflag\fB;\fR
+.PP
+The \fIddns-dual-stack-mixed-mode\fR parameter controls whether or not the
+server applies Dual Stack Mixed Mode rules during DDNS conflict resolution.
+This parameter is off by default, has no effect unless
+update-conflict-detection is enabled, and may only be specified at the
+global scope.
+.RE
+.PP
+The \fIddns-guard-id-must-match\fR statement
+.RS 0.25i
+.PP
+.B ddns-guard-id-must-match \fIflag\fB;\fR
+.PP
+The \fIddns-guard-id-must-match\fR parameter controls whether or not a
+the client id within a DHCID RR must match that of the DNS update's client
+to permit DNS entries associated with that DHCID RR to be ovewritten.
+Proper conflict resolution requires ID matching and should only be disabled
+after careful consideration. When disabled, it is allows any DNS updater to
+replace DNS entries that have an associated DHCID RR, regardless of client
+identity. This parameter is on by default, has no effect unless
+update-conflict-detection is enabled, and may only be specified at the global
+scope.
+.RE
+.PP
The \fddns-local-address4\fR and \fddns-local-address6\fR statements
.RS 0.25i
.PP
@@ -2082,6 +2178,20 @@ the server should use as the from address when sending DDNS update
requests.
.RE
.PP
+The \fIddns-other-guard-is-dynamic\fR statement
+.RS 0.25i
+.PP
+.B ddns-other-guard-is-dynamic \fIflag\fB;\fR
+.PP
+The \fIddns-other-guard-is-dynamic\fR parameter controls whether or not a
+a server running DSMM will consider the presence of the other update style
+DHCID RR as an indcation that a DNS entries may be overwritten. It should
+only be enabled after careful study as it allows DNS entries that would
+otherwise be protected as static, to be overwritten in certain cases. This
+paramater is off by default, has no effect unless ddns-dual-stack-mixed-mode
+is enabled, and may only be specified at the global scope.
+.RE
+.PP
The \fIddns-rev-domainname\fR statement
.RS 0.25i
.PP
@@ -3125,7 +3235,7 @@ server will use dhcp-renewal-time and dhcp-rebinding-time, respectively.
A value of zero tells the client it may choose its own value.
When those options are not defined then values will be set to zero unless the
-global \fIdhcpv6-set-tee-times\R is enabled. When this option is enabled the
+global \fIdhcpv6-set-tee-times\fR is enabled. When this option is enabled the
times are calculated as recommended by RFC 3315, Section 22.4:
T1 will be set to 0.5 times the shortest preferred lifetime
@@ -3199,7 +3309,8 @@ If the \fIupdate-conflict-detection\fR parameter is true, the server will
perform standard DHCID multiple-client, one-name conflict detection. If
the parameter has been set false, the server will skip this check and
instead simply tear down any previous bindings to install the new
-binding without question. The default is true.
+binding without question. The default is true and this parameter may only
+be specified at the global scope.
.RE
.PP
The
diff --git a/server/stables.c b/server/stables.c
index 59df5e83..107728d2 100644
--- a/server/stables.c
+++ b/server/stables.c
@@ -283,6 +283,9 @@ static struct option server_options[] = {
#if defined (FAILOVER_PROTOCOL)
{ "check-secs-byte-order", "f", &server_universe, SV_CHECK_SECS_BYTE_ORDER, 1 },
#endif
+ { "ddns-dual-stack-mixed-mode", "f", &server_universe, SV_DDNS_DUAL_STACK_MIXED_MODE, 1 },
+ { "ddns-guard-id-must-match", "f", &server_universe, SV_DDNS_GUARD_ID_MUST_MATCH, 1 },
+ { "ddns-other-guard-is-dynamic", "f", &server_universe, SV_DDNS_OTHER_GUARD_IS_DYNAMIC, 1 },
{ NULL, NULL, NULL, 0, 0 }
};