philipp / rpms / dhcp

Forked from rpms/dhcp 4 years ago
Clone

Blame SOURCES/dhcp-4.2.5-standard_ddns.patch

45d60a
diff --git a/client/dhclient.8 b/client/dhclient.8
45d60a
index a29757a..c66a912 100644
45d60a
--- a/client/dhclient.8
45d60a
+++ b/client/dhclient.8
45d60a
@@ -56,6 +56,12 @@ dhclient - Dynamic Host Configuration Protocol Client
45d60a
 ]
45d60a
 ]
45d60a
 [
45d60a
+.B -i
45d60a
+]
45d60a
+[
45d60a
+.B -C
45d60a
+]
45d60a
+[
45d60a
 .B -D
45d60a
 .I LL|LLT
45d60a
 ]
45d60a
@@ -441,6 +447,17 @@ Set the giaddr field of all packets to the \fIrelay\fR IP address
45d60a
 simulating a relay agent.  This is for testing pruposes only and
45d60a
 should not be expected to work in any consistent or useful way.
45d60a
 .TP
45d60a
+.BI \-i
45d60a
+Use a DUID with DHCPv4 clients.  If no DUID is available in the
45d60a
+lease file one will be constructed and saved.  The DUID will be
45d60a
+used to contstuct a RFC4361 style client id that will be included
45d60a
+in the client's messages.  This client id can be overridden by
45d60a
+setting a client id in the configuration file.  Overridding the
45d60a
+client id in this fashion is discouraged.
45d60a
+.TP
45d60a
+.BI \-C
45d60a
+Use the standard DDNS scheme from RFCs 4701 & 4702.  
45d60a
+.TP
45d60a
 .BI \--version
45d60a
 Print version number and exit.
45d60a
 .PP
45d60a
@@ -470,8 +487,10 @@ DHCPv6 \fBdhclient\fR creates an identifier based on the link-layer address
45d60a
 (DUID-LL) if it is running in stateless mode (with \fB\-S\fR, not
45d60a
 requesting an address), or it creates an identifier based on the
45d60a
 link-layer address plus a timestamp (DUID-LLT) if it is running in
45d60a
-stateful mode (without \fB\-S\fR, requesting an address).  \fB\-D\fR
45d60a
-overrides this default, with a value of either \fILL\fR or \fILLT\fR.
45d60a
+stateful mode (without \fB\-S\fR, requesting an address).  When DHCPv4
45d60a
+is configued to use a DUID using \fB\-i\fR option the default is to use
45d60a
+a DUID-LLT.  \fB\-D\fR
45d60a
+overrides these default, with a value of either \fILL\fR or \fILLT\fR.
45d60a
 .TP
45d60a
 .BI \-N
45d60a
 .\" TODO: is this for telling an already running dhclient?
45d60a
diff --git a/client/dhclient.c b/client/dhclient.c
45d60a
index 0db4703..6403754 100644
45d60a
--- a/client/dhclient.c
45d60a
+++ b/client/dhclient.c
45d60a
@@ -79,6 +79,8 @@ struct sockaddr_in sockaddr_broadcast;
45d60a
 struct in_addr giaddr;
45d60a
 struct data_string default_duid;
45d60a
 int duid_type = 0;
45d60a
+int duid_v4 = 0;
45d60a
+int std_dhcid = 0;
45d60a
 
45d60a
 /* ASSERT_STATE() does nothing now; it used to be
45d60a
    assert (state_is == state_shouldbe). */
45d60a
@@ -325,12 +327,9 @@ main(int argc, char **argv) {
45d60a
 				wanted_ia_na = 0;
45d60a
 			}
45d60a
 			wanted_ia_pd++;
45d60a
+#endif /* DHCPv6 */
45d60a
 		} else if (!strcmp(argv[i], "-D")) {
45d60a
-			if (local_family_set && (local_family == AF_INET)) {
45d60a
-				usage();
45d60a
-			}
45d60a
-			local_family_set = 1;
45d60a
-			local_family = AF_INET6;
45d60a
+			duid_v4 = 1;
45d60a
 			if (++i == argc)
45d60a
 				usage();
45d60a
 			if (!strcasecmp(argv[i], "LL")) {
45d60a
@@ -340,7 +339,12 @@ main(int argc, char **argv) {
45d60a
 			} else {
45d60a
 				usage();
45d60a
 			}
45d60a
-#endif /* DHCPv6 */
45d60a
+		} else if (!strcmp(argv[i], "-i")) {
45d60a
+			/* enable DUID support for DHCPv4 clients */
45d60a
+			duid_v4 = 1;
45d60a
+		} else if (!strcmp(argv[i], "-C")) {
45d60a
+			/* enable standard DHCID support for DDNS updates */
45d60a
+			std_dhcid = 1;
45d60a
 		} else if (!strcmp(argv[i], "-v")) {
45d60a
 			quiet = 0;
45d60a
 		} else if (!strcmp(argv[i], "--version")) {
45d60a
@@ -970,12 +974,13 @@ main(int argc, char **argv) {
45d60a
 		}
45d60a
 	}
45d60a
 
45d60a
-	/* Start a configuration state machine for each interface. */
45d60a
-#ifdef DHCPv6
45d60a
-	if (local_family == AF_INET6) {
45d60a
-		/* Establish a default DUID.  This may be moved to the
45d60a
-		 * DHCPv4 area later.
45d60a
-		 */
45d60a
+
45d60a
+	/*
45d60a
+	 * Establish a default DUID.  We always do so for v6 and
45d60a
+	 * do so if desired for v4 via the -D or -i options
45d60a
+	 */
45d60a
+	if ((local_family == AF_INET6) ||
45d60a
+	    ((local_family == AF_INET) && (duid_v4 == 1))) {
45d60a
 		if (default_duid.len == 0) {
45d60a
 			if (default_duid.buffer != NULL)
45d60a
 				data_string_forget(&default_duid, MDL);
45d60a
@@ -983,7 +988,11 @@ main(int argc, char **argv) {
45d60a
 			if (form_duid(&default_duid, MDL) == ISC_R_SUCCESS)
45d60a
 				write_duid(&default_duid);
45d60a
 		}
45d60a
+	}
45d60a
 
45d60a
+	/* Start a configuration state machine for each interface. */
45d60a
+#ifdef DHCPv6
45d60a
+	if (local_family == AF_INET6) {
45d60a
 		for (ip = interfaces ; ip != NULL ; ip = ip->next) {
45d60a
 			for (client = ip->client ; client != NULL ;
45d60a
 			     client = client->next) {
45d60a
@@ -1115,9 +1124,9 @@ static void usage()
45d60a
 
45d60a
 	log_fatal("Usage: dhclient "
45d60a
 #ifdef DHCPv6
45d60a
-		  "[-4|-6] [-SNTP1dvrx] [-nw] [-p <port>] [-D LL|LLT]\n"
45d60a
+		  "[-4|-6] [-SNTPI1dvrxc] [-nw] [-p <port>] [-D LL|LLT] \n"
45d60a
 #else /* DHCPv6 */
45d60a
-		  "[-1dvrx] [-nw] [-p <port>]\n"
45d60a
+		  "[-C1dvrxc] [-nw] [-p <port>] [-D LL|LLT] \n"
45d60a
 #endif /* DHCPv6 */
45d60a
 		  "                [-s server-addr] [-cf config-file] "
45d60a
 		  "[-lf lease-file]\n"
45d60a
@@ -2823,24 +2832,24 @@ make_client_options(struct client_state *client, struct client_lease *lease,
45d60a
 	unsigned i;
45d60a
 	struct option_cache *oc;
45d60a
 	struct option *option = NULL;
45d60a
-	struct buffer *bp = (struct buffer *)0;
45d60a
+	struct buffer *bp = NULL;
45d60a
 
45d60a
 	/* If there are any leftover options, get rid of them. */
45d60a
 	if (*op)
45d60a
-		option_state_dereference (op, MDL);
45d60a
+		option_state_dereference(op, MDL);
45d60a
 
45d60a
 	/* Allocate space for options. */
45d60a
-	option_state_allocate (op, MDL);
45d60a
+	option_state_allocate(op, MDL);
45d60a
 
45d60a
 	/* Send the server identifier if provided. */
45d60a
 	if (sid)
45d60a
-		save_option (&dhcp_universe, *op, sid);
45d60a
+		save_option(&dhcp_universe, *op, sid);
45d60a
 
45d60a
-	oc = (struct option_cache *)0;
45d60a
+	oc = NULL;
45d60a
 
45d60a
 	/* Send the requested address if provided. */
45d60a
 	if (rip) {
45d60a
-		client -> requested_address = *rip;
45d60a
+		client->requested_address = *rip;
45d60a
 		i = DHO_DHCP_REQUESTED_ADDRESS;
45d60a
 		if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash,
45d60a
 					      &i, 0, MDL) &&
45d60a
@@ -2848,22 +2857,22 @@ make_client_options(struct client_state *client, struct client_lease *lease,
45d60a
 					      option, MDL)))
45d60a
 			log_error ("can't make requested address cache.");
45d60a
 		else {
45d60a
-			save_option (&dhcp_universe, *op, oc);
45d60a
-			option_cache_dereference (&oc, MDL);
45d60a
+			save_option(&dhcp_universe, *op, oc);
45d60a
+			option_cache_dereference(&oc, MDL);
45d60a
 		}
45d60a
 		option_dereference(&option, MDL);
45d60a
 	} else {
45d60a
-		client -> requested_address.len = 0;
45d60a
+		client->requested_address.len = 0;
45d60a
 	}
45d60a
 
45d60a
 	i = DHO_DHCP_MESSAGE_TYPE;
45d60a
 	if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, &i, 0,
45d60a
 				      MDL) &&
45d60a
 	      make_const_option_cache(&oc, NULL, type, 1, option, MDL)))
45d60a
-		log_error ("can't make message type.");
45d60a
+		log_error("can't make message type.");
45d60a
 	else {
45d60a
-		save_option (&dhcp_universe, *op, oc);
45d60a
-		option_cache_dereference (&oc, MDL);
45d60a
+		save_option(&dhcp_universe, *op, oc);
45d60a
+		option_cache_dereference(&oc, MDL);
45d60a
 	}
45d60a
 	option_dereference(&option, MDL);
45d60a
 
45d60a
@@ -2876,8 +2885,8 @@ make_client_options(struct client_state *client, struct client_lease *lease,
45d60a
 			if (prl[i]->universe == &dhcp_universe)
45d60a
 				len++;
45d60a
 
45d60a
-		if (!buffer_allocate (&bp, len, MDL))
45d60a
-			log_error ("can't make parameter list buffer.");
45d60a
+		if (!buffer_allocate(&bp, len, MDL))
45d60a
+			log_error("can't make parameter list buffer.");
45d60a
 		else {
45d60a
 			unsigned code = DHO_DHCP_PARAMETER_REQUEST_LIST;
45d60a
 
45d60a
@@ -2891,15 +2900,69 @@ make_client_options(struct client_state *client, struct client_lease *lease,
45d60a
 						      &code, 0, MDL) &&
45d60a
 			      make_const_option_cache(&oc, &bp, NULL, len,
45d60a
 						      option, MDL)))
45d60a
-				log_error ("can't make option cache");
45d60a
+				log_error("can't make option cache");
45d60a
 			else {
45d60a
-				save_option (&dhcp_universe, *op, oc);
45d60a
-				option_cache_dereference (&oc, MDL);
45d60a
+				save_option(&dhcp_universe, *op, oc);
45d60a
+				option_cache_dereference(&oc, MDL);
45d60a
 			}
45d60a
 			option_dereference(&option, MDL);
45d60a
 		}
45d60a
 	}
45d60a
 
45d60a
+	/*
45d60a
+	 * If requested (duid_v4 == 1) add an RFC4361 compliant client-identifier
45d60a
+	 * This can be overridden by including a client id in the configuration
45d60a
+	 * file.
45d60a
+	 */
45d60a
+ 	if (duid_v4 == 1) {
45d60a
+		struct data_string client_identifier;
45d60a
+		int hw_idx, hw_len;
45d60a
+
45d60a
+		memset(&client_identifier, 0, sizeof(client_identifier));
45d60a
+		client_identifier.len = 1 + 4 + default_duid.len;
45d60a
+		if (!buffer_allocate(&client_identifier.buffer,
45d60a
+				     client_identifier.len, MDL))
45d60a
+			log_fatal("no memory for default DUID!");
45d60a
+		client_identifier.data = client_identifier.buffer->data;
45d60a
+
45d60a
+		i = DHO_DHCP_CLIENT_IDENTIFIER;
45d60a
+
45d60a
+		/* Client-identifier type : 1 byte */
45d60a
+		*client_identifier.buffer->data = 255;
45d60a
+		
45d60a
+		/* IAID : 4 bytes
45d60a
+		 * we use the low 4 bytes from the interface address
45d60a
+		 */
45d60a
+		if (client->interface->hw_address.hlen > 4) {
45d60a
+			hw_idx = client->interface->hw_address.hlen - 4;
45d60a
+			hw_len = 4;
45d60a
+		} else {
45d60a
+			hw_idx = 0;
45d60a
+			hw_len = client->interface->hw_address.hlen;
45d60a
+		}
45d60a
+		memcpy(&client_identifier.buffer->data + 5 - hw_len,
45d60a
+		       client->interface->hw_address.hbuf + hw_idx,
45d60a
+		       hw_len);
45d60a
+	
45d60a
+		/* Add the default duid */
45d60a
+		memcpy(&client_identifier.buffer->data+(1+4),
45d60a
+		       default_duid.data, default_duid.len);
45d60a
+
45d60a
+		/* And save the option */
45d60a
+		if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash,
45d60a
+					      &i, 0, MDL) &&
45d60a
+		      make_const_option_cache(&oc, NULL,
45d60a
+					      (u_int8_t *)client_identifier.data,
45d60a
+					      client_identifier.len,
45d60a
+					      option, MDL)))
45d60a
+			log_error ("can't make requested client id cache..");
45d60a
+		else {
45d60a
+			save_option (&dhcp_universe, *op, oc);
45d60a
+			option_cache_dereference (&oc, MDL);
45d60a
+		}
45d60a
+		option_dereference(&option, MDL);
45d60a
+	}
45d60a
+
45d60a
 	/* Run statements that need to be run on transmission. */
45d60a
 	if (client -> config -> on_transmission)
45d60a
 		execute_statements_in_scope
45d60a
@@ -4522,6 +4585,7 @@ client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb)
45d60a
 	struct option_cache *oc;
45d60a
 	int ignorep;
45d60a
 	int result;
45d60a
+	int ddns_v4_type;
45d60a
 	isc_result_t rcode;
45d60a
 
45d60a
 	/* If we didn't send an FQDN option, we certainly aren't going to
45d60a
@@ -4564,47 +4628,82 @@ client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb)
45d60a
 				    &global_scope, oc, MDL))
45d60a
 		return ISC_R_SUCCESS;
45d60a
 
45d60a
-	/* If this is a DHCPv6 client update, make a dhcid string out of
45d60a
-	 * the DUID.  If this is a DHCPv4 client update, choose either
45d60a
-	 * the client identifier, if there is one, or the interface's
45d60a
-	 * MAC address.
45d60a
+        /*
45d60a
+	 * Construct the DHCID value for use in the DDNS update process
45d60a
+	 * We have the newer standard version and the older interim version
45d60a
+	 * chosen by the '-C' option.  The interim version is left as is
45d60a
+	 * for backwards compatibility.  The standard version is based on
45d60a
+	 * RFC 4701 section 3.3
45d60a
 	 */
45d60a
+
45d60a
 	result = 0;
45d60a
 	memset(&client_identifier, 0, sizeof(client_identifier));
45d60a
-	if (client->active_lease != NULL) {
45d60a
-		if (((oc =
45d60a
-		      lookup_option(&dhcpv6_universe, client->sent_options,
45d60a
-				    D6O_CLIENTID)) != NULL) &&
45d60a
-		    evaluate_option_cache(&client_identifier, NULL, NULL,
45d60a
-					  client, client->sent_options, NULL,
45d60a
+
45d60a
+        if (std_dhcid == 1) {
45d60a
+          /* standard style */
45d60a
+          ddns_cb->dhcid_class = dns_rdatatype_dhcid;
45d60a
+          ddns_v4_type = 1;
45d60a
+	} else {
45d60a
+          /* interim style */
45d60a
+          ddns_cb->dhcid_class = dns_rdatatype_txt;
45d60a
+          /* for backwards compatibility */
45d60a
+          ddns_v4_type = DHO_DHCP_CLIENT_IDENTIFIER;
45d60a
+	}
45d60a
+
45d60a
+        	if (client->active_lease != NULL) {
45d60a
+		/* V6 request, get the client identifier, then
45d60a
+		 * construct the dhcid for either standard 
45d60a
+		 * or interim */
45d60a
+		if (((oc = lookup_option(&dhcpv6_universe,
45d60a
+					 client->sent_options,
45d60a
+					 D6O_CLIENTID)) != NULL) &&
45d60a
+		    evaluate_option_cache(&client_identifier, NULL,
45d60a
+					  NULL, client,
45d60a
+					  client->sent_options, NULL,
45d60a
 					  &global_scope, oc, MDL)) {
45d60a
-			/* RFC4701 defines type '2' as being for the DUID
45d60a
-			 * field.  We aren't using RFC4701 DHCID RR's yet,
45d60a
-			 * but this is as good a value as any.
45d60a
-			 */
45d60a
-			result = get_dhcid(&ddns_cb->dhcid, 2,
45d60a
+			result = get_dhcid(ddns_cb, 2,
45d60a
 					   client_identifier.data,
45d60a
 					   client_identifier.len);
45d60a
 			data_string_forget(&client_identifier, MDL);
45d60a
 		} else
45d60a
 			log_fatal("Impossible condition at %s:%d.", MDL);
45d60a
 	} else {
45d60a
-		if (((oc =
45d60a
-		      lookup_option(&dhcp_universe, client->sent_options,
45d60a
-				    DHO_DHCP_CLIENT_IDENTIFIER)) != NULL) &&
45d60a
-		    evaluate_option_cache(&client_identifier, NULL, NULL,
45d60a
-					  client, client->sent_options, NULL,
45d60a
+		/*
45d60a
+		 * V4 request, use the client id if there is one or the
45d60a
+		 * mac address if there isn't.  If we have a client id
45d60a
+		 * we check to see if it is an embedded DUID.
45d60a
+		 */
45d60a
+		if (((oc = lookup_option(&dhcp_universe,
45d60a
+					 client->sent_options,
45d60a
+					 DHO_DHCP_CLIENT_IDENTIFIER)) != NULL) &&
45d60a
+		    evaluate_option_cache(&client_identifier, NULL,
45d60a
+					  NULL, client,
45d60a
+					  client->sent_options, NULL,
45d60a
 					  &global_scope, oc, MDL)) {
45d60a
-			result = get_dhcid(&ddns_cb->dhcid,
45d60a
-					   DHO_DHCP_CLIENT_IDENTIFIER,
45d60a
-					   client_identifier.data,
45d60a
-					   client_identifier.len);
45d60a
+			if ((std_dhcid == 1) && (duid_v4 == 1) &&
45d60a
+			    (client_identifier.data[0] == 255)) {
45d60a
+				/*
45d60a
+				 * This appears to be an embedded DUID,
45d60a
+				 * extract it and treat it as such
45d60a
+				 */
45d60a
+				if (client_identifier.len <= 5)
45d60a
+					log_fatal("Impossible condition at %s:%d.",
45d60a
+						  MDL);
45d60a
+				result = get_dhcid(ddns_cb, 2,
45d60a
+						   client_identifier.data + 5,
45d60a
+						   client_identifier.len - 5);
45d60a
+			} else {
45d60a
+				result = get_dhcid(ddns_cb, ddns_v4_type,
45d60a
+						   client_identifier.data,
45d60a
+						   client_identifier.len);
45d60a
+			}
45d60a
 			data_string_forget(&client_identifier, MDL);
45d60a
 		} else
45d60a
-			result = get_dhcid(&ddns_cb->dhcid, 0,
45d60a
+			result = get_dhcid(ddns_cb, 0,
45d60a
 					   client->interface->hw_address.hbuf,
45d60a
 					   client->interface->hw_address.hlen);
45d60a
 	}
45d60a
+
45d60a
 	if (!result) {
45d60a
 		return ISC_R_SUCCESS;
45d60a
 	}
45d60a
@@ -4886,3 +4985,4 @@ dhclient_ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, char* file, int line) {
45d60a
         ddns_cb_free(ddns_cb, file, line);
45d60a
     }
45d60a
 }
45d60a
+
45d60a
diff --git a/common/conflex.c b/common/conflex.c
45d60a
index 4611616..c99732e 100644
45d60a
--- a/common/conflex.c
45d60a
+++ b/common/conflex.c
45d60a
@@ -879,10 +879,6 @@ intern(char *atom, enum dhcp_token dfv) {
45d60a
 	      case 'd':
45d60a
 		if (!strcasecmp(atom + 1, "b-time-format"))
45d60a
 			return DB_TIME_FORMAT;
45d60a
-		if (!strcasecmp (atom + 1, "ns-update"))
45d60a
-			return DNS_UPDATE;
45d60a
-		if (!strcasecmp (atom + 1, "ns-delete"))
45d60a
-			return DNS_DELETE;
45d60a
 		if (!strcasecmp (atom + 1, "omain"))
45d60a
 			return DOMAIN;
45d60a
 		if (!strncasecmp (atom + 1, "omain-", 6)) {
45d60a
@@ -1178,8 +1174,6 @@ intern(char *atom, enum dhcp_token dfv) {
45d60a
 			return TOKEN_NOT;
45d60a
 		if (!strcasecmp (atom + 1, "o"))
45d60a
 			return TOKEN_NO;
45d60a
-		if (!strcasecmp (atom + 1, "s-update"))
45d60a
-			return NS_UPDATE;
45d60a
 		if (!strcasecmp (atom + 1, "oerror"))
45d60a
 			return NS_NOERROR;
45d60a
 		if (!strcasecmp (atom + 1, "otauth"))
45d60a
@@ -1496,8 +1490,6 @@ intern(char *atom, enum dhcp_token dfv) {
45d60a
 		}
45d60a
 		if (!strcasecmp (atom + 1, "nauthenticated"))
45d60a
 			return UNAUTHENTICATED;
45d60a
-		if (!strcasecmp (atom + 1, "pdated-dns-rr"))
45d60a
-			return UPDATED_DNS_RR;
45d60a
 		if (!strcasecmp (atom + 1, "pdate"))
45d60a
 			return UPDATE;
45d60a
 		break;
45d60a
diff --git a/common/dns.c b/common/dns.c
45d60a
index d3ac966..a04c61d 100644
45d60a
--- a/common/dns.c
45d60a
+++ b/common/dns.c
45d60a
@@ -30,10 +30,12 @@
45d60a
  * asynchronous DNS routines.
45d60a
  */
45d60a
 
45d60a
+/*! \file common/dns.c
45d60a
+ */
45d60a
 #include "dhcpd.h"
45d60a
 #include "arpa/nameser.h"
45d60a
 #include <isc/md5.h>
45d60a
-
45d60a
+#include <isc/sha2.h>
45d60a
 #include <dns/result.h>
45d60a
 
45d60a
 /*
45d60a
@@ -823,45 +825,123 @@ void repudiate_zone (struct dns_zone **zone)
45d60a
 	dns_zone_dereference (zone, MDL);
45d60a
 }
45d60a
 
45d60a
-/* Have to use TXT records for now. */
45d60a
-#define T_DHCID T_TXT
45d60a
+/*!
45d60a
+ * \brief Create an id for a client
45d60a
+ *
45d60a
+ * This function is used to create an id for a client to use with DDNS
45d60a
+ * This version of the function is for the standard style, RFC 4701
45d60a
+ *
45d60a
+ * This function takes information from the type and data fields and
45d60a
+ * mangles it into a dhcid string which it places in ddns_cb.  It also
45d60a
+ * sets a field in ddns_cb to specify the class that should be used
45d60a
+ * when sending the dhcid, in this case it is a DHCID record so we use
45d60a
+ * dns_rdatatype_dhcid
45d60a
+ *
45d60a
+ * The DHCID we construct is:
45d60a
+ *  2 bytes - identifier type (see 4701 and IANA)
45d60a
+ *  1 byte  - digest type, currently only SHA256 (1)
45d60a
+ *  n bytes - digest, length depends on digest type, currently 32 for
45d60a
+ *            SHA256
45d60a
+ *
45d60a
+ * What we base the digest on is up to the calling code for an id type of
45d60a
+ * 0 - 1 octet htype followed by hlen octets of chaddr from v4 client request
45d60a
+ * 1 - data octets from a dhcpv4 client's client identifier option
45d60a
+ * 2 - the client DUID from a v4 or v6 client's client id option
45d60a
+ * This identifier is concatenated with the fqdn and the result is digested.
45d60a
+ */
45d60a
+int get_std_dhcid(dhcp_ddns_cb_t *ddns_cb,
45d60a
+		  int type,
45d60a
+		  const u_int8_t *identifier,
45d60a
+		  unsigned id_len)
45d60a
+{
45d60a
+	struct data_string *id = &ddns_cb->dhcid;
45d60a
+	isc_sha256_t sha256;
45d60a
+	unsigned char buf[ISC_SHA256_DIGESTLENGTH];
45d60a
+	unsigned char fwd_buf[256];
45d60a
+	unsigned fwd_buflen = 0;
45d60a
+
45d60a
+	/* Types can only be 0..(2^16)-1. */
45d60a
+	if (type < 0 || type > 65535)
45d60a
+		return (0);
45d60a
+
45d60a
+	/* We need to convert the fwd name to wire representation */
45d60a
+	if (MRns_name_pton((char *)ddns_cb->fwd_name.data, fwd_buf, 256) == -1)
45d60a
+		return (0);
45d60a
+	while(fwd_buf[fwd_buflen] != 0) {
45d60a
+		fwd_buflen += fwd_buf[fwd_buflen] + 1;
45d60a
+	}
45d60a
+	fwd_buflen++;
45d60a
+
45d60a
+	if (!buffer_allocate(&id->buffer,
45d60a
+			     ISC_SHA256_DIGESTLENGTH + 2 + 1,
45d60a
+			     MDL))
45d60a
+		return (0);
45d60a
+	id->data = id->buffer->data;
45d60a
+
45d60a
+	/* The two first bytes contain the type identifier. */
45d60a
+	putUShort(id->buffer->data, (unsigned)type);
45d60a
+
45d60a
+	/* The next is the digest type, SHA-256 is 1 */
45d60a
+	putUChar(id->buffer->data + 2, 1u);
45d60a
+
45d60a
+	/* Computing the digest */
45d60a
+	isc_sha256_init(&sha256);
45d60a
+	isc_sha256_update(&sha256, identifier, id_len);
45d60a
+	isc_sha256_update(&sha256, fwd_buf, fwd_buflen);
45d60a
+	isc_sha256_final(buf, &sha256);
45d60a
 
45d60a
-int get_dhcid (struct data_string *id,
45d60a
-	       int type, const u_int8_t *data, unsigned len)
45d60a
+	memcpy(id->buffer->data + 3, &buf, ISC_SHA256_DIGESTLENGTH);
45d60a
+
45d60a
+	id->len = ISC_SHA256_DIGESTLENGTH + 2 + 1;
45d60a
+
45d60a
+	return (1);
45d60a
+}
45d60a
+
45d60a
+/*!
45d60a
+ *
45d60a
+ * \brief Create an id for a client
45d60a
+ *
45d60a
+ * This function is used to create an id for a client to use with DDNS
45d60a
+ * This version of the function is for the interim style.  It is retained
45d60a
+ * to allow users to continue using the interim style but they should
45d60a
+ * switch to the standard style (which uses get_std_dhcid) for better
45d60a
+ * interoperability.  
45d60a
+ *
45d60a
+ * This function takes information from the type and data fields and
45d60a
+ * mangles it into a dhcid string which it places in ddns_cb.  It also
45d60a
+ * sets a field in ddns_cb to specify the class that should be used
45d60a
+ * when sending the dhcid, in this case it is a txt record so we use
45d60a
+ * dns_rdata_type_txt
45d60a
+ *
45d60a
+ * NOTE WELL: this function has issues with how it calculates the
45d60a
+ * dhcid, they can't be changed now as that would break the records
45d60a
+ * already in use.
45d60a
+ */
45d60a
+
45d60a
+int get_int_dhcid (dhcp_ddns_cb_t *ddns_cb,
45d60a
+		   int type,
45d60a
+		   const u_int8_t *data,
45d60a
+		   unsigned len)
45d60a
 {
45d60a
+	struct data_string *id = &ddns_cb->dhcid;
45d60a
 	unsigned char buf[ISC_MD5_DIGESTLENGTH];
45d60a
 	isc_md5_t md5;
45d60a
 	int i;
45d60a
 
45d60a
 	/* Types can only be 0..(2^16)-1. */
45d60a
 	if (type < 0 || type > 65535)
45d60a
-		return 0;
45d60a
+		return (0);
45d60a
 
45d60a
 	/*
45d60a
 	 * Hexadecimal MD5 digest plus two byte type, NUL,
45d60a
 	 * and one byte for length for dns.
45d60a
 	 */
45d60a
-	if (!buffer_allocate (&id -> buffer,
45d60a
-			      (ISC_MD5_DIGESTLENGTH * 2) + 4, MDL))
45d60a
-		return 0;
45d60a
-	id -> data = id -> buffer -> data;
45d60a
+	if (!buffer_allocate(&id -> buffer,
45d60a
+			     (ISC_MD5_DIGESTLENGTH * 2) + 4, MDL))
45d60a
+		return (0);
45d60a
+	id->data = id->buffer->data;
45d60a
 
45d60a
 	/*
45d60a
-	 * DHCP clients and servers should use the following forms of client
45d60a
-	 * identification, starting with the most preferable, and finishing
45d60a
-	 * with the least preferable.  If the client does not send any of these
45d60a
-	 * forms of identification, the DHCP/DDNS interaction is not defined by
45d60a
-	 * this specification.  The most preferable form of identification is
45d60a
-	 * the Globally Unique Identifier Option [TBD].  Next is the DHCP
45d60a
-	 * Client Identifier option.  Last is the client's link-layer address,
45d60a
-	 * as conveyed in its DHCPREQUEST message.  Implementors should note
45d60a
-	 * that the link-layer address cannot be used if there are no
45d60a
-	 * significant bytes in the chaddr field of the DHCP client's request,
45d60a
-	 * because this does not constitute a unique identifier.
45d60a
-	 *   -- "Interaction between DHCP and DNS"
45d60a
-	 *      <draft-ietf-dhc-dhcp-dns-12.txt>
45d60a
-	 *      M. Stapp, Y. Rekhter
45d60a
-	 *
45d60a
 	 * We put the length into the first byte to turn 
45d60a
 	 * this into a dns text string.  This avoid needing to
45d60a
 	 * copy the string to add the byte later.
45d60a
@@ -893,7 +973,18 @@ int get_dhcid (struct data_string *id,
45d60a
 	id->buffer->data[id->len] = 0;
45d60a
 	id->terminated = 1;
45d60a
 
45d60a
-	return 1;
45d60a
+	return (1);
45d60a
+}
45d60a
+
45d60a
+int get_dhcid(dhcp_ddns_cb_t *ddns_cb,
45d60a
+	      int type,
45d60a
+	      const u_int8_t *identifier,
45d60a
+	      unsigned id_len)
45d60a
+{
45d60a
+	if (ddns_cb->dhcid_class == dns_rdatatype_dhcid)
45d60a
+		return get_std_dhcid(ddns_cb, type, identifier, id_len);
45d60a
+	else 
45d60a
+		return get_int_dhcid(ddns_cb, type, identifier, id_len);
45d60a
 }
45d60a
 
45d60a
 /*
45d60a
@@ -1015,12 +1106,12 @@ make_dns_dataset(dns_rdataclass_t  dataclass,
45d60a
  * For the server the first step will have a request of:
45d60a
  * The name is not in use
45d60a
  * Add an A RR
45d60a
- * Add a DHCID RR (currently txt)
45d60a
+ * Add a DHCID RR
45d60a
  *
45d60a
  * For the client the first step will have a request of:
45d60a
  * The A RR does not exist
45d60a
  * Add an A RR
45d60a
- * Add a DHCID RR (currently txt)
45d60a
+ * Add a DHCID RR
45d60a
  */
45d60a
 
45d60a
 static isc_result_t
45d60a
@@ -1062,7 +1153,7 @@ ddns_modify_fwd_add1(dhcp_ddns_cb_t   *ddns_cb,
45d60a
 	dataspace++;
45d60a
 
45d60a
 	/* Add the DHCID RR */
45d60a
-	result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
45d60a
+	result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
45d60a
 				  dataspace, 
45d60a
 				  (unsigned char *)ddns_cb->dhcid.data,
45d60a
 				  ddns_cb->dhcid.len, ddns_cb->ttl);
45d60a
@@ -1108,7 +1199,7 @@ ddns_modify_fwd_add2(dhcp_ddns_cb_t   *ddns_cb,
45d60a
 		     dns_name_t       *pname,
45d60a
 		     dns_name_t       *uname)
45d60a
 {
45d60a
-	isc_result_t result;
45d60a
+	isc_result_t result = ISC_R_SUCCESS;
45d60a
 
45d60a
 	/*
45d60a
 	 * If we are doing conflict resolution (unset) we use a prereq list.
45d60a
@@ -1117,7 +1208,7 @@ ddns_modify_fwd_add2(dhcp_ddns_cb_t   *ddns_cb,
45d60a
 	if ((ddns_cb->flags & DDNS_CONFLICT_OVERRIDE) == 0) {
45d60a
 		/* Construct the prereq list */
45d60a
 		/* The DHCID RR exists and matches the client identity */
45d60a
-		result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
45d60a
+		result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
45d60a
 					  dataspace, 
45d60a
 					  (unsigned char *)ddns_cb->dhcid.data,
45d60a
 					  ddns_cb->dhcid.len, 0);
45d60a
@@ -1130,7 +1221,7 @@ ddns_modify_fwd_add2(dhcp_ddns_cb_t   *ddns_cb,
45d60a
 		/* Start constructing the update list.
45d60a
 		 * Conflict detection override: delete DHCID RRs */
45d60a
 		result = make_dns_dataset(dns_rdataclass_any,
45d60a
-					  dns_rdatatype_txt,
45d60a
+					  ddns_cb->dhcid_class,
45d60a
 					  dataspace, NULL, 0, 0);
45d60a
 		if (result != ISC_R_SUCCESS) {
45d60a
 			return(result);
45d60a
@@ -1139,7 +1230,7 @@ ddns_modify_fwd_add2(dhcp_ddns_cb_t   *ddns_cb,
45d60a
 		dataspace++;
45d60a
 
45d60a
 		/* Add current DHCID RR */
45d60a
-		result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
45d60a
+		result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
45d60a
 					  dataspace, 
45d60a
 					  (unsigned char *)ddns_cb->dhcid.data,
45d60a
 					  ddns_cb->dhcid.len, ddns_cb->ttl);
45d60a
@@ -1201,11 +1292,11 @@ ddns_modify_fwd_rem1(dhcp_ddns_cb_t   *ddns_cb,
45d60a
 		     dns_name_t       *pname,
45d60a
 		     dns_name_t       *uname)
45d60a
 {
45d60a
-	isc_result_t result;
45d60a
+	isc_result_t result = ISC_R_SUCCESS;
45d60a
 
45d60a
 	/* Consruct the prereq list */
45d60a
 	/* The DHCID RR exists and matches the client identity */
45d60a
-	result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
45d60a
+	result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
45d60a
 				  dataspace, 
45d60a
 				  (unsigned char *)ddns_cb->dhcid.data,
45d60a
 				  ddns_cb->dhcid.len, 0);
45d60a
@@ -1271,7 +1362,7 @@ ddns_modify_fwd_rem2(dhcp_ddns_cb_t   *ddns_cb,
45d60a
 
45d60a
 	/* Construct the update list */
45d60a
 	/* Delete DHCID RR */
45d60a
-	result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_txt,
45d60a
+	result = make_dns_dataset(dns_rdataclass_none, ddns_cb->dhcid_class,
45d60a
 				  dataspace,
45d60a
 				  (unsigned char *)ddns_cb->dhcid.data,
45d60a
 				  ddns_cb->dhcid.len, 0);
45d60a
diff --git a/common/parse.c b/common/parse.c
45d60a
index fc51327..7477543 100644
45d60a
--- a/common/parse.c
45d60a
+++ b/common/parse.c
45d60a
@@ -3558,42 +3558,7 @@ int parse_numeric_expression (expr, cfile, lose)
45d60a
 	}
45d60a
 	return 1;
45d60a
 }
45d60a
-#if defined (NSUPDATE_OLD)
45d60a
-/*
45d60a
- * dns-expression :==
45d60a
- *	UPDATE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
45d60a
- *				data-expression COMMA numeric-expression RPAREN
45d60a
- *	DELETE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
45d60a
- *				data-expression RPAREN
45d60a
- *	EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
45d60a
- *				data-expression RPAREN
45d60a
- *	NOT EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
45d60a
- *				data-expression RPAREN
45d60a
- * ns-class :== IN | CHAOS | HS | NUMBER
45d60a
- * ns-type :== A | PTR | MX | TXT | NUMBER
45d60a
- */
45d60a
-
45d60a
-int parse_dns_expression (expr, cfile, lose)
45d60a
-	struct expression **expr;
45d60a
-	struct parse *cfile;
45d60a
-	int *lose;
45d60a
-{
45d60a
-	/* Parse an expression... */
45d60a
-	if (!parse_expression (expr, cfile, lose, context_dns,
45d60a
-			       (struct expression **)0, expr_none))
45d60a
-		return 0;
45d60a
 
45d60a
-	if (!is_dns_expression (*expr) &&
45d60a
-	    (*expr) -> op != expr_variable_reference &&
45d60a
-	    (*expr) -> op != expr_funcall) {
45d60a
-		expression_dereference (expr, MDL);
45d60a
-		parse_warn (cfile, "Expecting a dns update subexpression.");
45d60a
-		*lose = 1;
45d60a
-		return 0;
45d60a
-	}
45d60a
-	return 1;
45d60a
-}
45d60a
-#endif /* NSUPDATE_OLD */
45d60a
 /* Parse a subexpression that does not contain a binary operator. */
45d60a
 
45d60a
 int parse_non_binary (expr, cfile, lose, context)
45d60a
@@ -3608,11 +3573,6 @@ int parse_non_binary (expr, cfile, lose, context)
45d60a
 	struct expression *nexp, **ep;
45d60a
 	int known;
45d60a
 	char *cptr;
45d60a
-#if defined (NSUPDATE_OLD)
45d60a
-	enum expr_op opcode;
45d60a
-	const char *s;
45d60a
-	unsigned long u;
45d60a
-#endif 
45d60a
 	isc_result_t status;
45d60a
 	unsigned len;
45d60a
 
45d60a
@@ -3645,12 +3605,7 @@ int parse_non_binary (expr, cfile, lose, context)
45d60a
 
45d60a
 	      case TOKEN_NOT:
45d60a
 		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-#if defined(NSUPDATE_OLD)
45d60a
-		if (context == context_dns) {
45d60a
-			token = peek_token (&val, (unsigned *)0, cfile);
45d60a
-			goto not_exists;
45d60a
-		}
45d60a
-#endif
45d60a
+
45d60a
 		if (!expression_allocate (expr, MDL))
45d60a
 			log_fatal ("can't allocate expression");
45d60a
 		(*expr) -> op = expr_not;
45d60a
@@ -3662,7 +3617,7 @@ int parse_non_binary (expr, cfile, lose, context)
45d60a
 			}
45d60a
 			*lose = 1;
45d60a
 			expression_dereference (expr, MDL);
45d60a
-			return 0;
45d60a
+			return (0);
45d60a
 		}
45d60a
 		if (!is_boolean_expression ((*expr) -> data.not)) {
45d60a
 			*lose = 1;
45d60a
@@ -3694,10 +3649,6 @@ int parse_non_binary (expr, cfile, lose, context)
45d60a
 		break;
45d60a
 
45d60a
 	      case EXISTS:
45d60a
-#if defined(NSUPDATE_OLD)
45d60a
-		if (context == context_dns)
45d60a
-			goto ns_exists;
45d60a
-#endif
45d60a
 		token = next_token (&val, (unsigned *)0, cfile);
45d60a
 		if (!expression_allocate (expr, MDL))
45d60a
 			log_fatal ("can't allocate expression");
45d60a
@@ -3710,7 +3661,7 @@ int parse_non_binary (expr, cfile, lose, context)
45d60a
 		    (*expr)->data.option == NULL) {
45d60a
 			*lose = 1;
45d60a
 			expression_dereference (expr, MDL);
45d60a
-			return 0;
45d60a
+			return (0);
45d60a
 		}
45d60a
 		break;
45d60a
 
45d60a
@@ -4011,285 +3962,7 @@ int parse_non_binary (expr, cfile, lose, context)
45d60a
 			goto norparen;
45d60a
 		break;
45d60a
 
45d60a
-#if defined(NSUPDATE_OLD)
45d60a
-		/* dns-update and dns-delete are present for historical
45d60a
-		   purposes, but are deprecated in favor of ns-update
45d60a
-		   in combination with update, delete, exists and not
45d60a
-		   exists. */
45d60a
-	      case DNS_UPDATE:
45d60a
-	      case DNS_DELETE:
45d60a
-#if !defined (NSUPDATE)
45d60a
-		parse_warn (cfile,
45d60a
-			    "Please rebuild dhcpd with --with-nsupdate.");
45d60a
-#endif
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token == DNS_UPDATE)
45d60a
-			opcode = expr_ns_add;
45d60a
-		else
45d60a
-			opcode = expr_ns_delete;
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != LPAREN)
45d60a
-			goto nolparen;
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != STRING) {
45d60a
-			parse_warn (cfile,
45d60a
-				    "parse_expression: expecting string.");
45d60a
-		      badnsupdate:
45d60a
-			skip_to_semi (cfile);
45d60a
-			*lose = 1;
45d60a
-			return 0;
45d60a
-		}
45d60a
-			
45d60a
-		if (!strcasecmp (val, "a"))
45d60a
-			u = T_A;
45d60a
-		else if (!strcasecmp (val, "aaaa"))
45d60a
-			u = T_AAAA;
45d60a
-		else if (!strcasecmp (val, "ptr"))
45d60a
-			u = T_PTR;
45d60a
-		else if (!strcasecmp (val, "mx"))
45d60a
-			u = T_MX;
45d60a
-		else if (!strcasecmp (val, "cname"))
45d60a
-			u = T_CNAME;
45d60a
-		else if (!strcasecmp (val, "TXT"))
45d60a
-			u = T_TXT;
45d60a
-		else {
45d60a
-			parse_warn (cfile, "unexpected rrtype: %s", val);
45d60a
-			goto badnsupdate;
45d60a
-		}
45d60a
-
45d60a
-		s = (opcode == expr_ns_add
45d60a
-		     ? "old-dns-update"
45d60a
-		     : "old-dns-delete");
45d60a
-		cptr = dmalloc (strlen (s) + 1, MDL);
45d60a
-		if (!cptr)
45d60a
-			log_fatal ("can't allocate name for %s", s);
45d60a
-		strcpy (cptr, s);
45d60a
-		if (!expression_allocate (expr, MDL))
45d60a
-			log_fatal ("can't allocate expression");
45d60a
-		(*expr) -> op = expr_funcall;
45d60a
-		(*expr) -> data.funcall.name = cptr;
45d60a
-
45d60a
-		/* Fake up a function call. */
45d60a
-		ep = &(*expr) -> data.funcall.arglist;
45d60a
-		if (!expression_allocate (ep, MDL))
45d60a
-			log_fatal ("can't allocate expression");
45d60a
-		(*ep) -> op = expr_arg;
45d60a
-		if (!make_const_int (&(*ep) -> data.arg.val, u))
45d60a
-			log_fatal ("can't allocate rrtype value.");
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != COMMA)
45d60a
-			goto nocomma;
45d60a
-		ep = &((*ep) -> data.arg.next);
45d60a
-		if (!expression_allocate (ep, MDL))
45d60a
-			log_fatal ("can't allocate expression");
45d60a
-		(*ep) -> op = expr_arg;
45d60a
-		if (!(parse_data_expression (&(*ep) -> data.arg.val,
45d60a
-					     cfile, lose)))
45d60a
-			goto nodata;
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != COMMA)
45d60a
-			goto nocomma;
45d60a
-
45d60a
-		ep = &((*ep) -> data.arg.next);
45d60a
-		if (!expression_allocate (ep, MDL))
45d60a
-			log_fatal ("can't allocate expression");
45d60a
-		(*ep) -> op = expr_arg;
45d60a
-		if (!(parse_data_expression (&(*ep) -> data.arg.val,
45d60a
-					     cfile, lose)))
45d60a
-			goto nodata;
45d60a
-
45d60a
-		if (opcode == expr_ns_add) {
45d60a
-			token = next_token (&val, (unsigned *)0, cfile);
45d60a
-			if (token != COMMA)
45d60a
-				goto nocomma;
45d60a
-			
45d60a
-			ep = &((*ep) -> data.arg.next);
45d60a
-			if (!expression_allocate (ep, MDL))
45d60a
-				log_fatal ("can't allocate expression");
45d60a
-			(*ep) -> op = expr_arg;
45d60a
-			if (!(parse_numeric_expression (&(*ep) -> data.arg.val,
45d60a
-							cfile, lose))) {
45d60a
-				parse_warn (cfile,
45d60a
-					    "expecting numeric expression.");
45d60a
-				goto badnsupdate;
45d60a
-			}
45d60a
-		}
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != RPAREN)
45d60a
-			goto norparen;
45d60a
-		break;
45d60a
-
45d60a
-	      case NS_UPDATE:
45d60a
-#if !defined (NSUPDATE)
45d60a
-		parse_warn (cfile,
45d60a
-			    "Please rebuild dhcpd with --with-nsupdate.");
45d60a
-#endif
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (!expression_allocate (expr, MDL))
45d60a
-			log_fatal ("can't allocate expression");
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != LPAREN)
45d60a
-			goto nolparen;
45d60a
-
45d60a
-		nexp = *expr;
45d60a
-		do {
45d60a
-			nexp -> op = expr_dns_transaction;
45d60a
-			if (!(parse_dns_expression
45d60a
-			      (&nexp -> data.dns_transaction.car,
45d60a
-			       cfile, lose)))
45d60a
-			{
45d60a
-				if (!*lose)
45d60a
-					parse_warn
45d60a
-						(cfile,
45d60a
-						 "expecting dns expression.");
45d60a
-				expression_dereference (expr, MDL);
45d60a
-				*lose = 1;
45d60a
-				return 0;
45d60a
-			}
45d60a
-
45d60a
-			token = next_token (&val, (unsigned *)0, cfile);
45d60a
-			
45d60a
-			if (token == COMMA) {
45d60a
-				if (!(expression_allocate
45d60a
-				      (&nexp -> data.dns_transaction.cdr,
45d60a
-				       MDL)))
45d60a
-					log_fatal
45d60a
-						("can't allocate expression");
45d60a
-				nexp = nexp -> data.dns_transaction.cdr;
45d60a
-			}
45d60a
-		} while (token == COMMA);
45d60a
-
45d60a
-		if (token != RPAREN)
45d60a
-			goto norparen;
45d60a
-		break;
45d60a
-
45d60a
-		/* NOT EXISTS is special cased above... */
45d60a
-	      not_exists:
45d60a
-		token = peek_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != EXISTS) {
45d60a
-			parse_warn (cfile, "expecting DNS prerequisite.");
45d60a
-			*lose = 1;
45d60a
-			return 0;
45d60a
-		}
45d60a
-		opcode = expr_ns_not_exists;
45d60a
-		goto nsupdatecode;
45d60a
-	      case TOKEN_ADD:
45d60a
-		opcode = expr_ns_add;
45d60a
-		goto nsupdatecode;
45d60a
-	      case TOKEN_DELETE:
45d60a
-		opcode = expr_ns_delete;
45d60a
-		goto nsupdatecode;
45d60a
-	      ns_exists:
45d60a
-		opcode = expr_ns_exists;
45d60a
-	      nsupdatecode:
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-
45d60a
-#if !defined (NSUPDATE)
45d60a
-		parse_warn (cfile,
45d60a
-			    "Please rebuild dhcpd with --with-nsupdate.");
45d60a
-#endif
45d60a
-		if (!expression_allocate (expr, MDL))
45d60a
-			log_fatal ("can't allocate expression");
45d60a
-		(*expr) -> op = opcode;
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != LPAREN)
45d60a
-			goto nolparen;
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (!is_identifier (token) && token != NUMBER) {
45d60a
-			parse_warn (cfile, "expecting identifier or number.");
45d60a
-		      badnsop:
45d60a
-			expression_dereference (expr, MDL);
45d60a
-			skip_to_semi (cfile);
45d60a
-			*lose = 1;
45d60a
-			return 0;
45d60a
-		}
45d60a
-			
45d60a
-		if (token == NUMBER)
45d60a
-			(*expr) -> data.ns_add.rrclass = atoi (val);
45d60a
-		else if (!strcasecmp (val, "in"))
45d60a
-			(*expr) -> data.ns_add.rrclass = C_IN;
45d60a
-		else if (!strcasecmp (val, "chaos"))
45d60a
-			(*expr) -> data.ns_add.rrclass = C_CHAOS;
45d60a
-		else if (!strcasecmp (val, "hs"))
45d60a
-			(*expr) -> data.ns_add.rrclass = C_HS;
45d60a
-		else {
45d60a
-			parse_warn (cfile, "unexpected rrclass: %s", val);
45d60a
-			goto badnsop;
45d60a
-		}
45d60a
-		
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != COMMA)
45d60a
-			goto nocomma;
45d60a
 
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (!is_identifier (token) && token != NUMBER) {
45d60a
-			parse_warn (cfile, "expecting identifier or number.");
45d60a
-			goto badnsop;
45d60a
-		}
45d60a
-			
45d60a
-		if (token == NUMBER)
45d60a
-			(*expr) -> data.ns_add.rrtype = atoi (val);
45d60a
-		else if (!strcasecmp (val, "a"))
45d60a
-			(*expr) -> data.ns_add.rrtype = T_A;
45d60a
-		else if (!strcasecmp (val, "aaaa"))
45d60a
-			(*expr) -> data.ns_add.rrtype = T_AAAA;
45d60a
-		else if (!strcasecmp (val, "ptr"))
45d60a
-			(*expr) -> data.ns_add.rrtype = T_PTR;
45d60a
-		else if (!strcasecmp (val, "mx"))
45d60a
-			(*expr) -> data.ns_add.rrtype = T_MX;
45d60a
-		else if (!strcasecmp (val, "cname"))
45d60a
-			(*expr) -> data.ns_add.rrtype = T_CNAME;
45d60a
-		else if (!strcasecmp (val, "TXT"))
45d60a
-			(*expr) -> data.ns_add.rrtype = T_TXT;
45d60a
-		else {
45d60a
-			parse_warn (cfile, "unexpected rrtype: %s", val);
45d60a
-			goto badnsop;
45d60a
-		}
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != COMMA)
45d60a
-			goto nocomma;
45d60a
-
45d60a
-		if (!(parse_data_expression
45d60a
-		      (&(*expr) -> data.ns_add.rrname, cfile, lose)))
45d60a
-			goto nodata;
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != COMMA)
45d60a
-			goto nocomma;
45d60a
-
45d60a
-		if (!(parse_data_expression
45d60a
-		      (&(*expr) -> data.ns_add.rrdata, cfile, lose)))
45d60a
-			goto nodata;
45d60a
-
45d60a
-		if (opcode == expr_ns_add) {
45d60a
-			token = next_token (&val, (unsigned *)0, cfile);
45d60a
-			if (token != COMMA)
45d60a
-				goto nocomma;
45d60a
-			
45d60a
-			if (!(parse_numeric_expression
45d60a
-			      (&(*expr) -> data.ns_add.ttl, cfile,
45d60a
-			       lose))) {
45d60a
-			    if (!*lose)
45d60a
-				parse_warn (cfile,
45d60a
-					    "expecting numeric expression.");
45d60a
-			    goto badnsupdate;
45d60a
-			}
45d60a
-		}
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != RPAREN)
45d60a
-			goto norparen;
45d60a
-		break;
45d60a
-#endif /* NSUPDATE_OLD */
45d60a
 	      case OPTION:
45d60a
 	      case CONFIG_OPTION:
45d60a
 		if (!expression_allocate (expr, MDL))
45d60a
@@ -4366,44 +4039,7 @@ int parse_non_binary (expr, cfile, lose, context)
45d60a
 		(*expr) -> op = expr_host_decl_name;
45d60a
 		break;
45d60a
 
45d60a
-#if defined(NSUPDATE_OLD)
45d60a
-	      case UPDATED_DNS_RR:
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != LPAREN)
45d60a
-			goto nolparen;
45d60a
 
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != STRING) {
45d60a
-			parse_warn (cfile, "expecting string.");
45d60a
-		      bad_rrtype:
45d60a
-			*lose = 1;
45d60a
-			return 0;
45d60a
-		}
45d60a
-		if (!strcasecmp (val, "a"))
45d60a
-			s = "ddns-fwd-name";
45d60a
-		else if (!strcasecmp (val, "ptr"))
45d60a
-			s = "ddns-rev-name";
45d60a
-		else {
45d60a
-			parse_warn (cfile, "invalid DNS rrtype: %s", val);
45d60a
-			goto bad_rrtype;
45d60a
-		}
45d60a
-
45d60a
-		token = next_token (&val, (unsigned *)0, cfile);
45d60a
-		if (token != RPAREN)
45d60a
-			goto norparen;
45d60a
-
45d60a
-		if (!expression_allocate (expr, MDL))
45d60a
-			log_fatal ("can't allocate expression");
45d60a
-		(*expr) -> op = expr_variable_reference;
45d60a
-		(*expr) -> data.variable =
45d60a
-			dmalloc (strlen (s) + 1, MDL);
45d60a
-		if (!(*expr) -> data.variable)
45d60a
-			log_fatal ("can't allocate variable name.");
45d60a
-		strcpy ((*expr) -> data.variable, s);
45d60a
-		break;
45d60a
-#endif /* NSUPDATE_OLD */
45d60a
 	      case PACKET:
45d60a
 		token = next_token (&val, (unsigned *)0, cfile);
45d60a
 		if (!expression_allocate (expr, MDL))
45d60a
diff --git a/common/tree.c b/common/tree.c
45d60a
index 8c2056c..26e0add 100644
45d60a
--- a/common/tree.c
45d60a
+++ b/common/tree.c
45d60a
@@ -645,15 +645,6 @@ int evaluate_expression (result, packet, lease, client_state,
45d60a
 		status = (evaluate_data_expression
45d60a
 			  (&bv -> value.data, packet, lease, client_state,
45d60a
 			   in_options, cfg_options, scope, expr, MDL));
45d60a
-#if defined (NSUPDATE_OLD)
45d60a
-	} else if (is_dns_expression (expr)) {
45d60a
-		if (!binding_value_allocate (&bv, MDL))
45d60a
-			return 0;
45d60a
-		bv -> type = binding_dns;
45d60a
-		status = (evaluate_dns_expression
45d60a
-			  (&bv -> value.dns, packet, lease, client_state,
45d60a
-			   in_options, cfg_options, scope, expr));
45d60a
-#endif
45d60a
 	} else {
45d60a
 		log_error ("%s: invalid expression type: %d",
45d60a
 			   "evaluate_expression", expr -> op);
45d60a
@@ -699,19 +690,6 @@ int binding_value_dereference (struct binding_value **v,
45d60a
 		if (bv -> value.data.buffer)
45d60a
 			data_string_forget (&bv -> value.data, file, line);
45d60a
 		break;
45d60a
-	      case binding_dns:
45d60a
-#if defined (NSUPDATE_OLD)
45d60a
-		if (bv -> value.dns) {
45d60a
-			if (bv -> value.dns -> r_data) {
45d60a
-				dfree (bv -> value.dns -> r_data_ephem, MDL);
45d60a
-				bv -> value.dns -> r_data = (unsigned char *)0;
45d60a
-				bv -> value.dns -> r_data_ephem =
45d60a
-					(unsigned char *)0;
45d60a
-			}
45d60a
-			minires_freeupdrec (bv -> value.dns);
45d60a
-		}
45d60a
-		break;
45d60a
-#endif
45d60a
 	      default:
45d60a
 		log_error ("%s(%d): invalid binding type: %d",
45d60a
 			   file, line, bv -> type);
45d60a
@@ -721,270 +699,6 @@ int binding_value_dereference (struct binding_value **v,
45d60a
 	return 1;
45d60a
 }
45d60a
 
45d60a
-#if defined (NSUPDATE_OLD)
45d60a
-int evaluate_dns_expression (result, packet, lease, client_state, in_options,
45d60a
-			     cfg_options, scope, expr)
45d60a
-	ns_updrec **result;
45d60a
-	struct packet *packet;
45d60a
-	struct lease *lease;
45d60a
-	struct client_state *client_state;
45d60a
-	struct option_state *in_options;
45d60a
-	struct option_state *cfg_options;
45d60a
-	struct binding_scope **scope;
45d60a
-	struct expression *expr;
45d60a
-{
45d60a
-	unsigned long ttl = 0;
45d60a
-	char *tname;
45d60a
-	struct data_string name, data;
45d60a
-	int r0, r1, r2;
45d60a
-
45d60a
-	if (!result || *result) {
45d60a
-		log_error ("evaluate_dns_expression called with non-null %s",
45d60a
-			   "result pointer");
45d60a
-#if defined (POINTER_DEBUG)
45d60a
-		abort ();
45d60a
-#else
45d60a
-		return 0;
45d60a
-#endif
45d60a
-	}
45d60a
-		
45d60a
-	switch (expr -> op) {
45d60a
-#if defined (NSUPDATE)
45d60a
-	      case expr_ns_add:
45d60a
-		r0 = evaluate_numeric_expression (&ttl, packet, lease,
45d60a
-						  client_state,
45d60a
-						  in_options, cfg_options,
45d60a
-						  scope,
45d60a
-						  expr -> data.ns_add.ttl);
45d60a
-		goto nsfinish;
45d60a
-
45d60a
-	      case expr_ns_exists:
45d60a
-		ttl = 1;
45d60a
-
45d60a
-	      case expr_ns_delete:
45d60a
-	      case expr_ns_not_exists:
45d60a
-		r0 = 1;
45d60a
-	      nsfinish:
45d60a
-		memset (&name, 0, sizeof name);
45d60a
-		r1 = evaluate_data_expression (&name, packet, lease,
45d60a
-					       client_state,
45d60a
-					       in_options, cfg_options, scope,
45d60a
-					       expr -> data.ns_add.rrname,
45d60a
-					       MDL);
45d60a
-		if (r1) {
45d60a
-			/* The result of the evaluation may or may not
45d60a
-			   be NUL-terminated, but we need it
45d60a
-			   terminated for sure, so we have to allocate
45d60a
-			   a buffer and terminate it. */
45d60a
-			tname = dmalloc (name.len + 1, MDL);
45d60a
-			if (!tname) {
45d60a
-				r2 = 0;
45d60a
-				r1 = 0;
45d60a
-				data_string_forget (&name, MDL);
45d60a
-			} else {
45d60a
-				memcpy (tname, name.data, name.len);
45d60a
-				tname [name.len] = 0;
45d60a
-				memset (&data, 0, sizeof data);
45d60a
-				r2 = evaluate_data_expression
45d60a
-					(&data, packet, lease, client_state,
45d60a
-					 in_options, cfg_options, scope,
45d60a
-					 expr -> data.ns_add.rrdata, MDL);
45d60a
-			}
45d60a
-		} else {
45d60a
-			r2 = 0;
45d60a
-			tname = NULL;
45d60a
-		}
45d60a
-		if (r0 && r1 && (r2 || expr -> op != expr_ns_add)) {
45d60a
-		    *result = minires_mkupdrec (((expr -> op == expr_ns_add ||
45d60a
-						  expr -> op == expr_ns_delete)
45d60a
-						 ? S_UPDATE : S_PREREQ),
45d60a
-						tname,
45d60a
-						expr -> data.ns_add.rrclass,
45d60a
-						expr -> data.ns_add.rrtype,
45d60a
-						ttl);
45d60a
-		    if (!*result) {
45d60a
-			  ngood:
45d60a
-			    if (r2) {
45d60a
-				data_string_forget (&data, MDL);
45d60a
-				r2 = 0;
45d60a
-			    }
45d60a
-		    } else {
45d60a
-			if (data.len) {
45d60a
-				/* As a special case, if we get exactly
45d60a
-				   four bytes of data, it's an IP address
45d60a
-				   represented as a 32-bit quantity, which
45d60a
-				   is actually what we *should* be getting
45d60a
-				   here.   Because res_mkupdrec is currently
45d60a
-				   broken and expects a dotted quad, convert
45d60a
-				   it.   This should be fixed when the new
45d60a
-				   resolver is merged. */
45d60a
-				if (data.len == 4) {
45d60a
-				    (*result) -> r_data_ephem =
45d60a
-					    dmalloc (16, MDL);
45d60a
-				    if (!(*result) -> r_data_ephem)
45d60a
-					goto dpngood;
45d60a
-				    (*result) -> r_data =
45d60a
-					    (*result) -> r_data_ephem;
45d60a
-				    /*%Audit% 16 bytes max. %2004.06.17,Safe%*/
45d60a
-				    sprintf ((char *)(*result) -> r_data_ephem,
45d60a
-					     "%u.%u.%u.%u",
45d60a
-					     data.data [0] & 0xff,
45d60a
-					     data.data [1] & 0xff,
45d60a
-					     data.data [2] & 0xff,
45d60a
-					     data.data [3] & 0xff);
45d60a
-				    (*result) -> r_size = 
45d60a
-					    strlen ((const char *)
45d60a
-						    (*result) -> r_data);
45d60a
-				} else {
45d60a
-				    (*result) -> r_size = data.len;
45d60a
-				    (*result) -> r_data_ephem =
45d60a
-					    dmalloc (data.len, MDL);
45d60a
-				    if (!(*result) -> r_data_ephem) {
45d60a
-				      dpngood: /* double plus ungood. */
45d60a
-					minires_freeupdrec (*result);
45d60a
-					*result = 0;
45d60a
-					goto ngood;
45d60a
-				    }
45d60a
-				    (*result) -> r_data =
45d60a
-					    (*result) -> r_data_ephem;
45d60a
-				    memcpy ((*result) -> r_data_ephem,
45d60a
-					    data.data, data.len);
45d60a
-				}
45d60a
-			} else {
45d60a
-				(*result) -> r_data = 0;
45d60a
-				(*result) -> r_size = 0;
45d60a
-			}
45d60a
-			switch (expr -> op) {
45d60a
-			      case expr_ns_add:
45d60a
-				(*result) -> r_opcode = ADD;
45d60a
-				break;
45d60a
-			      case expr_ns_delete:
45d60a
-				(*result) -> r_opcode = DELETE;
45d60a
-				break;
45d60a
-			      case expr_ns_exists:
45d60a
-				(*result) -> r_opcode = YXRRSET;
45d60a
-				break;
45d60a
-			      case expr_ns_not_exists:
45d60a
-				(*result) -> r_opcode = NXRRSET;
45d60a
-				break;
45d60a
-
45d60a
-				/* Can't happen, but satisfy gcc. */
45d60a
-			      default:
45d60a
-				break;
45d60a
-			}
45d60a
-		    }
45d60a
-		}
45d60a
-		if (r1) {
45d60a
-			data_string_forget (&name, MDL);
45d60a
-			dfree (tname, MDL);
45d60a
-		}
45d60a
-		if (r2)
45d60a
-			data_string_forget (&data, MDL);
45d60a
-		/* One flaw in the thinking here: an IP address and an
45d60a
-		   ASCII string both look like data expressions, but
45d60a
-		   for A records, we want an ASCII string, not a
45d60a
-		   binary IP address.  Do I need to turn binary IP
45d60a
-		   addresses into a separate type?  */
45d60a
-		return (r0 && r1 &&
45d60a
-			(r2 || expr -> op != expr_ns_add) && *result);
45d60a
-
45d60a
-#else
45d60a
-	      case expr_ns_add:
45d60a
-	      case expr_ns_delete:
45d60a
-	      case expr_ns_exists:
45d60a
-	      case expr_ns_not_exists:
45d60a
-		return 0;
45d60a
-#endif
45d60a
-	      case expr_funcall:
45d60a
-		log_error ("%s: dns values for functions not supported.",
45d60a
-			   expr -> data.funcall.name);
45d60a
-		break;
45d60a
-
45d60a
-	      case expr_variable_reference:
45d60a
-		log_error ("%s: dns values for variables not supported.",
45d60a
-			   expr -> data.variable);
45d60a
-		break;
45d60a
-
45d60a
-	      case expr_check:
45d60a
-	      case expr_equal:
45d60a
-	      case expr_not_equal:
45d60a
-	      case expr_regex_match:
45d60a
-	      case expr_iregex_match:
45d60a
-	      case expr_and:
45d60a
-	      case expr_or:
45d60a
-	      case expr_not:
45d60a
-	      case expr_match:
45d60a
-	      case expr_static:
45d60a
-	      case expr_known:
45d60a
-	      case expr_exists:
45d60a
-	      case expr_variable_exists:
45d60a
-		log_error ("Boolean opcode in evaluate_dns_expression: %d",
45d60a
-		      expr -> op);
45d60a
-		return 0;
45d60a
-
45d60a
-	      case expr_none:
45d60a
-	      case expr_substring:
45d60a
-	      case expr_suffix:
45d60a
-	      case expr_lcase:
45d60a
-	      case expr_ucase:
45d60a
-	      case expr_option:
45d60a
-	      case expr_hardware:
45d60a
-	      case expr_const_data:
45d60a
-	      case expr_packet:
45d60a
-	      case expr_concat:
45d60a
-	      case expr_encapsulate:
45d60a
-	      case expr_host_lookup:
45d60a
-	      case expr_encode_int8:
45d60a
-	      case expr_encode_int16:
45d60a
-	      case expr_encode_int32:
45d60a
-	      case expr_binary_to_ascii:
45d60a
-	      case expr_reverse:
45d60a
-	      case expr_filename:
45d60a
-	      case expr_sname:
45d60a
-	      case expr_pick_first_value:
45d60a
-	      case expr_host_decl_name:
45d60a
-	      case expr_config_option:
45d60a
-	      case expr_leased_address:
45d60a
-	      case expr_null:
45d60a
-	      case expr_gethostname:
45d60a
-		log_error ("Data opcode in evaluate_dns_expression: %d",
45d60a
-		      expr -> op);
45d60a
-		return 0;
45d60a
-
45d60a
-	      case expr_extract_int8:
45d60a
-	      case expr_extract_int16:
45d60a
-	      case expr_extract_int32:
45d60a
-	      case expr_const_int:
45d60a
-	      case expr_lease_time:
45d60a
-	      case expr_dns_transaction:
45d60a
-	      case expr_add:
45d60a
-	      case expr_subtract:
45d60a
-	      case expr_multiply:
45d60a
-	      case expr_divide:
45d60a
-	      case expr_remainder:
45d60a
-	      case expr_binary_and:
45d60a
-	      case expr_binary_or:
45d60a
-	      case expr_binary_xor:
45d60a
-	      case expr_client_state:
45d60a
-		log_error ("Numeric opcode in evaluate_dns_expression: %d",
45d60a
-		      expr -> op);
45d60a
-		return 0;
45d60a
-
45d60a
-	      case expr_function:
45d60a
-		log_error ("Function opcode in evaluate_dns_expression: %d",
45d60a
-		      expr -> op);
45d60a
-		return 0;
45d60a
-
45d60a
-	      case expr_arg:
45d60a
-		break;
45d60a
-	}
45d60a
-
45d60a
-	log_error ("Bogus opcode in evaluate_dns_expression: %d",
45d60a
-		   expr -> op);
45d60a
-	return 0;
45d60a
-}
45d60a
-#endif /* defined (NSUPDATE_OLD) */
45d60a
-
45d60a
 int evaluate_boolean_expression (result, packet, lease, client_state,
45d60a
 				 in_options, cfg_options, scope, expr)
45d60a
 	int *result;
45d60a
@@ -1056,20 +770,7 @@ int evaluate_boolean_expression (result, packet, lease, client_state,
45d60a
 			    else
45d60a
 				*result = expr -> op == expr_not_equal;
45d60a
 			    break;
45d60a
-#if defined (NSUPDATE_OLD)
45d60a
-			  case binding_dns:
45d60a
-#if defined (NSUPDATE)
45d60a
-			    /* XXX This should be a comparison for equal
45d60a
-			       XXX values, not for identity. */
45d60a
-			    if (bv -> value.dns == obv -> value.dns)
45d60a
-				*result = expr -> op == expr_equal;
45d60a
-			    else
45d60a
-				*result = expr -> op == expr_not_equal;
45d60a
-#else
45d60a
-				*result = expr -> op == expr_not_equal;
45d60a
-#endif
45d60a
-			    break;
45d60a
-#endif /* NSUPDATE_OLD */
45d60a
+
45d60a
 			  case binding_function:
45d60a
 			    if (bv -> value.fundef == obv -> value.fundef)
45d60a
 				*result = expr -> op == expr_equal;
45d60a
@@ -2369,7 +2070,7 @@ int evaluate_data_expression (result, packet, lease, client_state,
45d60a
 	      case expr_ns_delete:
45d60a
 	      case expr_ns_exists:
45d60a
 	      case expr_ns_not_exists:
45d60a
-		log_error ("dns update opcode in evaluate_data_expression: %d",
45d60a
+		log_error ("dns opcode in evaluate_boolean_expression: %d",
45d60a
 		      expr -> op);
45d60a
 		return 0;
45d60a
 
45d60a
@@ -2398,11 +2099,6 @@ int evaluate_numeric_expression (result, packet, lease, client_state,
45d60a
 {
45d60a
 	struct data_string data;
45d60a
 	int status, sleft, sright;
45d60a
-#if defined (NSUPDATE_OLD)
45d60a
-	ns_updrec *nut;
45d60a
-	ns_updque uq;
45d60a
-	struct expression *cur, *next;
45d60a
-#endif
45d60a
 
45d60a
 	struct binding *binding;
45d60a
 	struct binding_value *bv;
45d60a
@@ -2541,53 +2237,6 @@ int evaluate_numeric_expression (result, packet, lease, client_state,
45d60a
 #endif
45d60a
 		return (1);
45d60a
  
45d60a
-	      case expr_dns_transaction:
45d60a
-#if !defined (NSUPDATE_OLD)
45d60a
-		return 0;
45d60a
-#else
45d60a
-		if (!resolver_inited) {
45d60a
-			minires_ninit (&resolver_state);
45d60a
-			resolver_inited = 1;
45d60a
-			resolver_state.retrans = 1;
45d60a
-			resolver_state.retry = 1;
45d60a
-		}
45d60a
-		ISC_LIST_INIT (uq);
45d60a
-		cur = expr;
45d60a
-		do {
45d60a
-		    next = cur -> data.dns_transaction.cdr;
45d60a
-		    nut = 0;
45d60a
-		    status = (evaluate_dns_expression
45d60a
-			      (&nut, packet,
45d60a
-			       lease, client_state, in_options, cfg_options,
45d60a
-			       scope, cur -> data.dns_transaction.car));
45d60a
-		    if (!status)
45d60a
-			    goto dns_bad;
45d60a
-		    ISC_LIST_APPEND (uq, nut, r_link);
45d60a
-		    cur = next;
45d60a
-		} while (next);
45d60a
-
45d60a
-		/* Do the update and record the error code, if there was
45d60a
-		   an error; otherwise set it to NOERROR. */
45d60a
-		*result = minires_nupdate (&resolver_state,
45d60a
-					   ISC_LIST_HEAD (uq));
45d60a
-		status = 1;
45d60a
-
45d60a
-		print_dns_status ((int)*result, &uq;;
45d60a
-
45d60a
-	      dns_bad:
45d60a
-		while (!ISC_LIST_EMPTY (uq)) {
45d60a
-			ns_updrec *tmp = ISC_LIST_HEAD (uq);
45d60a
-			ISC_LIST_UNLINK (uq, tmp, r_link);
45d60a
-			if (tmp -> r_data_ephem) {
45d60a
-				dfree (tmp -> r_data_ephem, MDL);
45d60a
-				tmp -> r_data = (unsigned char *)0;
45d60a
-				tmp -> r_data_ephem = (unsigned char *)0;
45d60a
-			}
45d60a
-			minires_freeupdrec (tmp);
45d60a
-		}
45d60a
-		return status;
45d60a
-#endif /* NSUPDATE_OLD */
45d60a
-
45d60a
 	      case expr_variable_reference:
45d60a
 		if (scope && *scope) {
45d60a
 		    binding = find_binding (*scope, expr -> data.variable);
45d60a
@@ -2877,14 +2526,6 @@ int evaluate_numeric_expression (result, packet, lease, client_state,
45d60a
 			return 0;
45d60a
 		}
45d60a
 
45d60a
-	      case expr_ns_add:
45d60a
-	      case expr_ns_delete:
45d60a
-	      case expr_ns_exists:
45d60a
-	      case expr_ns_not_exists:
45d60a
-		log_error ("dns opcode in evaluate_numeric_expression: %d",
45d60a
-		      expr -> op);
45d60a
-		return 0;
45d60a
-
45d60a
 	      case expr_function:
45d60a
 		log_error ("function definition in evaluate_numeric_expr");
45d60a
 		return 0;
45d60a
@@ -3182,38 +2823,6 @@ void expression_dereference (eptr, file, line)
45d60a
 				(&expr -> data.reverse.buffer, file, line);
45d60a
 		break;
45d60a
 
45d60a
-	      case expr_dns_transaction:
45d60a
-		if (expr -> data.dns_transaction.car)
45d60a
-		    expression_dereference (&expr -> data.dns_transaction.car,
45d60a
-					    file, line);
45d60a
-		if (expr -> data.dns_transaction.cdr)
45d60a
-		    expression_dereference (&expr -> data.dns_transaction.cdr,
45d60a
-					    file, line);
45d60a
-		break;
45d60a
-
45d60a
-	      case expr_ns_add:
45d60a
-		if (expr -> data.ns_add.rrname)
45d60a
-		    expression_dereference (&expr -> data.ns_add.rrname,
45d60a
-					    file, line);
45d60a
-		if (expr -> data.ns_add.rrdata)
45d60a
-		    expression_dereference (&expr -> data.ns_add.rrdata,
45d60a
-					    file, line);
45d60a
-		if (expr -> data.ns_add.ttl)
45d60a
-		    expression_dereference (&expr -> data.ns_add.ttl,
45d60a
-					    file, line);
45d60a
-		break;
45d60a
-
45d60a
-	      case expr_ns_delete:
45d60a
-	      case expr_ns_exists:
45d60a
-	      case expr_ns_not_exists:
45d60a
-		if (expr -> data.ns_delete.rrname)
45d60a
-		    expression_dereference (&expr -> data.ns_delete.rrname,
45d60a
-					    file, line);
45d60a
-		if (expr -> data.ns_delete.rrdata)
45d60a
-		    expression_dereference (&expr -> data.ns_delete.rrdata,
45d60a
-					    file, line);
45d60a
-		break;
45d60a
-
45d60a
 	      case expr_variable_reference:
45d60a
 	      case expr_variable_exists:
45d60a
 		if (expr -> data.variable)
45d60a
@@ -3262,15 +2871,6 @@ void expression_dereference (eptr, file, line)
45d60a
 	free_expression (expr, MDL);
45d60a
 }
45d60a
 
45d60a
-int is_dns_expression (expr)
45d60a
-	struct expression *expr;
45d60a
-{
45d60a
-      return (expr -> op == expr_ns_add ||
45d60a
-	      expr -> op == expr_ns_delete ||
45d60a
-	      expr -> op == expr_ns_exists ||
45d60a
-	      expr -> op == expr_ns_not_exists);
45d60a
-}
45d60a
-
45d60a
 int is_boolean_expression (expr)
45d60a
 	struct expression *expr;
45d60a
 {
45d60a
@@ -3325,7 +2925,6 @@ int is_numeric_expression (expr)
45d60a
 		expr -> op == expr_extract_int32 ||
45d60a
 		expr -> op == expr_const_int ||
45d60a
 		expr -> op == expr_lease_time ||
45d60a
-		expr -> op == expr_dns_transaction ||
45d60a
 		expr -> op == expr_add ||
45d60a
 		expr -> op == expr_subtract ||
45d60a
 		expr -> op == expr_multiply ||
45d60a
@@ -3340,11 +2939,7 @@ int is_numeric_expression (expr)
45d60a
 int is_compound_expression (expr)
45d60a
 	struct expression *expr;
45d60a
 {
45d60a
-	return (expr -> op == expr_ns_add ||
45d60a
-		expr -> op == expr_ns_delete ||
45d60a
-		expr -> op == expr_ns_exists ||
45d60a
-		expr -> op == expr_ns_not_exists ||
45d60a
-		expr -> op == expr_substring ||
45d60a
+	return (expr -> op == expr_substring ||
45d60a
 		expr -> op == expr_suffix ||
45d60a
 		expr -> op == expr_option ||
45d60a
 		expr -> op == expr_concat ||
45d60a
@@ -3357,8 +2952,7 @@ int is_compound_expression (expr)
45d60a
 		expr -> op == expr_config_option ||
45d60a
 		expr -> op == expr_extract_int8 ||
45d60a
 		expr -> op == expr_extract_int16 ||
45d60a
-		expr -> op == expr_extract_int32 ||
45d60a
-		expr -> op == expr_dns_transaction);
45d60a
+		expr -> op == expr_extract_int32);
45d60a
 }
45d60a
 
45d60a
 static int op_val (enum expr_op);
45d60a
@@ -3456,8 +3050,6 @@ enum expression_context expression_context (struct expression *expr)
45d60a
 		return context_numeric;
45d60a
 	if (is_boolean_expression (expr))
45d60a
 		return context_boolean;
45d60a
-	if (is_dns_expression (expr))
45d60a
-		return context_dns;
45d60a
 	return context_any;
45d60a
 }
45d60a
 
45d60a
@@ -3928,99 +3520,6 @@ int write_expression (file, expr, col, indent, firstp)
45d60a
 					  "lease-time");
45d60a
 		break;
45d60a
 
45d60a
-	      case expr_dns_transaction:
45d60a
-		col = token_print_indent (file, col, indent, "", "",
45d60a
-					  "ns-update");
45d60a
-		col = token_print_indent (file, col, indent, " ", "",
45d60a
-					  "(");
45d60a
-		scol = 0;
45d60a
-		for (e = expr;
45d60a
-		     e && e -> op == expr_dns_transaction;
45d60a
-		     e = e -> data.dns_transaction.cdr) {
45d60a
-			if (!scol) {
45d60a
-				scol = col;
45d60a
-				firstp = 1;
45d60a
-			} else
45d60a
-				firstp = 0;
45d60a
-			col = write_expression (file,
45d60a
-						e -> data.dns_transaction.car,
45d60a
-						col, scol, firstp);
45d60a
-			if (e -> data.dns_transaction.cdr)
45d60a
-				col = token_print_indent (file, col, scol,
45d60a
-							  "", " ", ",");
45d60a
-		}
45d60a
-		if (e)
45d60a
-			col = write_expression (file, e, col, scol, 0);
45d60a
-		col = token_print_indent (file, col, indent, "", "", ")");
45d60a
-		break;
45d60a
-
45d60a
-	      case expr_ns_add:
45d60a
-		col = token_print_indent (file, col, indent, "", "",
45d60a
-					  "update");
45d60a
-		col = token_print_indent (file, col, indent, " ", "",
45d60a
-					  "(");
45d60a
-		scol = col;
45d60a
-		sprintf (obuf, "%d", expr -> data.ns_add.rrclass);
45d60a
-		col = token_print_indent (file, col, scol, "", "", obuf);
45d60a
-		col = token_print_indent (file, col, scol, "", " ",
45d60a
-					  ",");
45d60a
-		sprintf (obuf, "%d", expr -> data.ns_add.rrtype);
45d60a
-		col = token_print_indent (file, col, scol, "", "", obuf);
45d60a
-		col = token_print_indent (file, col, scol, "", " ",
45d60a
-					  ",");
45d60a
-		col = write_expression (file, expr -> data.ns_add.rrname,
45d60a
-					col, scol, 0);
45d60a
-		col = token_print_indent (file, col, scol, "", " ",
45d60a
-					  ",");
45d60a
-		col = write_expression (file, expr -> data.ns_add.rrdata,
45d60a
-					col, scol, 0);
45d60a
-		col = token_print_indent (file, col, scol, "", " ",
45d60a
-					  ",");
45d60a
-		col = write_expression (file, expr -> data.ns_add.ttl,
45d60a
-					col, scol, 0);
45d60a
-		col = token_print_indent (file, col, indent, "", "",
45d60a
-					  ")");
45d60a
-		break;
45d60a
-
45d60a
-	      case expr_ns_delete:
45d60a
-		col = token_print_indent (file, col, indent, "", "",
45d60a
-					  "delete");
45d60a
-		col = token_print_indent (file, col, indent, " ", "",
45d60a
-					  "(");
45d60a
-	      finish_ns_small:
45d60a
-		scol = col;
45d60a
-		sprintf (obuf, "%d", expr -> data.ns_add.rrclass);
45d60a
-		col = token_print_indent (file, col, scol, "", "", obuf);
45d60a
-		col = token_print_indent (file, col, scol, "", " ",
45d60a
-					  ",");
45d60a
-		sprintf (obuf, "%d", expr -> data.ns_add.rrtype);
45d60a
-		col = token_print_indent (file, col, scol, "", "", obuf);
45d60a
-		col = token_print_indent (file, col, scol, "", " ",
45d60a
-					  ",");
45d60a
-		col = write_expression (file, expr -> data.ns_add.rrname,
45d60a
-					col, scol, 0);
45d60a
-		col = token_print_indent (file, col, scol, "", " ",
45d60a
-					  ",");
45d60a
-		col = write_expression (file, expr -> data.ns_add.rrdata,
45d60a
-					col, scol, 0);
45d60a
-		col = token_print_indent (file, col, indent, "", "",
45d60a
-					  ")");
45d60a
-		break;
45d60a
-
45d60a
-	      case expr_ns_exists:
45d60a
-		col = token_print_indent (file, col, indent, "", "",
45d60a
-					  "exists");
45d60a
-		col = token_print_indent (file, col, indent, " ", "",
45d60a
-					  "(");
45d60a
-		goto finish_ns_small;
45d60a
-
45d60a
-	      case expr_ns_not_exists:
45d60a
-		col = token_print_indent (file, col, indent, "", "",
45d60a
-					  "not exists");
45d60a
-		col = token_print_indent (file, col, indent, " ", "",
45d60a
-					  "(");
45d60a
-		goto finish_ns_small;
45d60a
-
45d60a
 	      case expr_static:
45d60a
 		col = token_print_indent (file, col, indent, "", "",
45d60a
 					  "static");
45d60a
@@ -4293,12 +3792,7 @@ int data_subexpression_length (int *rv,
45d60a
 	      case expr_const_int:
45d60a
 	      case expr_exists:
45d60a
 	      case expr_known:
45d60a
-	      case expr_dns_transaction:
45d60a
 	      case expr_static:
45d60a
-	      case expr_ns_add:
45d60a
-	      case expr_ns_delete:
45d60a
-	      case expr_ns_exists:
45d60a
-	      case expr_ns_not_exists:
45d60a
 	      case expr_not_equal:
45d60a
 	      case expr_null:
45d60a
 	      case expr_variable_exists:
45d60a
@@ -4349,12 +3843,6 @@ int expr_valid_for_context (struct expression *expr,
45d60a
 			return 1;
45d60a
 		return 0;
45d60a
 
45d60a
-	      case context_dns:
45d60a
-		if (is_dns_expression (expr)) {
45d60a
-			return 1;
45d60a
-		}
45d60a
-		return 0;
45d60a
-
45d60a
 	      case context_data_or_numeric:
45d60a
 		if (is_numeric_expression (expr) ||
45d60a
 		    is_data_expression (expr)) {
45d60a
diff --git a/includes/dhcpd.h b/includes/dhcpd.h
45d60a
index 1d2bf2c..7e756e0 100644
45d60a
--- a/includes/dhcpd.h
45d60a
+++ b/includes/dhcpd.h
45d60a
@@ -638,6 +638,7 @@ struct lease_state {
45d60a
 #define DDNS_UPDATE_STYLE_NONE		0
45d60a
 #define DDNS_UPDATE_STYLE_AD_HOC	1
45d60a
 #define DDNS_UPDATE_STYLE_INTERIM	2
45d60a
+#define DDNS_UPDATE_STYLE_STANDARD	3
45d60a
 
45d60a
 /* Server option names. */
45d60a
 
45d60a
@@ -1627,6 +1628,9 @@ typedef struct dhcp_ddns_cb {
45d60a
 
45d60a
 	void *transaction;
45d60a
 	void *dataspace;
45d60a
+
45d60a
+	dns_rdataclass_t dhcid_class;
45d60a
+	char *lease_tag;
45d60a
 } dhcp_ddns_cb_t;
45d60a
 
45d60a
 extern struct ipv6_pool **pools;
45d60a
@@ -2047,11 +2051,6 @@ struct expression *parse_domain_list(struct parse *cfile, int);
45d60a
 
45d60a
 
45d60a
 /* tree.c */
45d60a
-#if defined (NSUPDATE)
45d60a
-extern struct __res_state resolver_state;
45d60a
-extern int resolver_inited;
45d60a
-#endif
45d60a
-
45d60a
 extern struct binding_scope *global_scope;
45d60a
 pair cons (caddr_t, pair);
45d60a
 int make_const_option_cache (struct option_cache **, struct buffer **,
45d60a
@@ -2079,15 +2078,6 @@ int evaluate_expression (struct binding_value **, struct packet *,
45d60a
 			 struct binding_scope **, struct expression *,
45d60a
 			 const char *, int);
45d60a
 int binding_value_dereference (struct binding_value **, const char *, int);
45d60a
-#if defined (NSUPDATE_OLD)
45d60a
-int evaluate_dns_expression (ns_updrec **, struct packet *,
45d60a
-			     struct lease *,
45d60a
-			     struct client_state *,
45d60a
-			     struct option_state *,
45d60a
-			     struct option_state *,
45d60a
-			     struct binding_scope **,
45d60a
-			     struct expression *);
45d60a
-#endif
45d60a
 int evaluate_boolean_expression (int *,
45d60a
 				 struct packet *,  struct lease *,
45d60a
 				 struct client_state *,
45d60a
@@ -2913,21 +2903,18 @@ int icmp_echorequest (struct iaddr *);
45d60a
 isc_result_t icmp_echoreply (omapi_object_t *);
45d60a
 
45d60a
 /* dns.c */
45d60a
-#if defined (NSUPDATE)
45d60a
-isc_result_t find_tsig_key (ns_tsig_key **, const char *, struct dns_zone *);
45d60a
-void tkey_free (ns_tsig_key **);
45d60a
-#endif
45d60a
 isc_result_t enter_dns_zone (struct dns_zone *);
45d60a
 isc_result_t dns_zone_lookup (struct dns_zone **, const char *);
45d60a
 int dns_zone_dereference (struct dns_zone **, const char *, int);
45d60a
 #if defined (NSUPDATE)
45d60a
 #define FIND_FORWARD 0
45d60a
 #define FIND_REVERSE 1
45d60a
+isc_result_t find_tsig_key (ns_tsig_key **, const char *, struct dns_zone *);
45d60a
+void tkey_free (ns_tsig_key **);
45d60a
 isc_result_t find_cached_zone (dhcp_ddns_cb_t *, int);
45d60a
 void forget_zone (struct dns_zone **);
45d60a
 void repudiate_zone (struct dns_zone **);
45d60a
-//void cache_found_zone (ns_class, char *, struct in_addr *, int);
45d60a
-int get_dhcid (struct data_string *, int, const u_int8_t *, unsigned);
45d60a
+int get_dhcid (dhcp_ddns_cb_t *, int, const u_int8_t *, unsigned);
45d60a
 void dhcid_tolease (struct data_string *, struct data_string *);
45d60a
 isc_result_t dhcid_fromlease (struct data_string *, struct data_string *);
45d60a
 isc_result_t ddns_update_fwd(struct data_string *, struct iaddr,
45d60a
@@ -2937,6 +2924,16 @@ isc_result_t ddns_remove_fwd(struct data_string *,
45d60a
 			     struct iaddr, struct data_string *);
45d60a
 #endif /* NSUPDATE */
45d60a
 
45d60a
+dhcp_ddns_cb_t *ddns_cb_alloc(const char *file, int line);
45d60a
+void ddns_cb_free (dhcp_ddns_cb_t *ddns_cb, const char *file, int line);
45d60a
+void ddns_cb_forget_zone (dhcp_ddns_cb_t *ddns_cb);
45d60a
+isc_result_t
45d60a
+ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line);
45d60a
+isc_result_t
45d60a
+ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb, const char *file, int line);
45d60a
+void
45d60a
+ddns_cancel(dhcp_ddns_cb_t *ddns_cb, const char *file, int line);
45d60a
+
45d60a
 /* resolv.c */
45d60a
 extern char path_resolv_conf [];
45d60a
 extern struct name_server *name_servers;
45d60a
@@ -3302,21 +3299,6 @@ void dump_subnets (void);
45d60a
 void free_everything (void);
45d60a
 #endif
45d60a
 
45d60a
-/* nsupdate.c */
45d60a
-char *ddns_rev_name (struct lease *, struct lease_state *, struct packet *);
45d60a
-char *ddns_fwd_name (struct lease *, struct lease_state *, struct packet *);
45d60a
-int nsupdateA (const char *, const unsigned char *, u_int32_t, int);
45d60a
-int nsupdatePTR (const char *, const unsigned char *, u_int32_t, int);
45d60a
-void nsupdate (struct lease *, struct lease_state *, struct packet *, int);
45d60a
-int updateA (const struct data_string *, const struct data_string *,
45d60a
-	     unsigned int, struct lease *);
45d60a
-int updatePTR (const struct data_string *, const struct data_string *,
45d60a
-	       unsigned int, struct lease *);
45d60a
-int deleteA (const struct data_string *, const struct data_string *,
45d60a
-	     struct lease *);
45d60a
-int deletePTR (const struct data_string *, const struct data_string *,
45d60a
-	       struct lease *);
45d60a
-
45d60a
 /* failover.c */
45d60a
 #if defined (FAILOVER_PROTOCOL)
45d60a
 extern dhcp_failover_state_t *failover_states;
45d60a
@@ -3576,20 +3558,5 @@ void mark_hosts_unavailable(void);
45d60a
 void mark_phosts_unavailable(void);
45d60a
 void mark_interfaces_unavailable(void);
45d60a
 
45d60a
-dhcp_ddns_cb_t *ddns_cb_alloc(const char *file, int line);
45d60a
-void ddns_cb_free (dhcp_ddns_cb_t *ddns_cb, const char *file, int line);
45d60a
-void ddns_cb_forget_zone (dhcp_ddns_cb_t *ddns_cb);
45d60a
-
45d60a
-//void *key_from_zone(struct dns_zone *zone);
45d60a
-
45d60a
-isc_result_t
45d60a
-ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line);
45d60a
-
45d60a
-isc_result_t
45d60a
-ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb, const char *file, int line);
45d60a
-
45d60a
-void
45d60a
-ddns_cancel(dhcp_ddns_cb_t *ddns_cb, const char *file, int line);
45d60a
-
45d60a
 #define MAX_ADDRESS_STRING_LEN \
45d60a
    (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
45d60a
diff --git a/includes/dhctoken.h b/includes/dhctoken.h
45d60a
index 3d9a21d..a75eb97 100644
45d60a
--- a/includes/dhctoken.h
45d60a
+++ b/includes/dhctoken.h
45d60a
@@ -32,6 +32,11 @@
45d60a
  * ``http://www.nominum.com''.
45d60a
  */
45d60a
 
45d60a
+/*
45d60a
+ * The following tokens have been deprecated and aren't in use anymore.
45d60a
+ * They have been left in place to avoid disturbing the code.
45d60a
+ * DNS_UPDATE, DNS_DELETE, NS_UPDATE, UPDATED_DNS_RR
45d60a
+ */
45d60a
 enum dhcp_token {
45d60a
 	SEMI = ';',
45d60a
 	DOT = '.',
45d60a
diff --git a/includes/site.h b/includes/site.h
45d60a
index 8ff2834..1c7ec96 100644
45d60a
--- a/includes/site.h
45d60a
+++ b/includes/site.h
45d60a
@@ -281,3 +281,17 @@
45d60a
    limit the number of TCP connections that the server will
45d60a
    allow at one time.  A value of 0 means there is no limit.*/
45d60a
 #define MAX_FD_VALUE 200
45d60a
+
45d60a
+
45d60a
+/* Include code to do a slow transition of DDNS records
45d60a
+   from the interim to the standard version, or backwards.
45d60a
+   The normal code will handle removing an old style record
45d60a
+   when the name on a lease is being changed.  This adds code
45d60a
+   to handle the case where the name isn't being changed but
45d60a
+   the old record should be removed to allow a new record to
45d60a
+   be added.  This is the slow transition as leases are only
45d60a
+   updated as a client touches them.  A fast transition would
45d60a
+   entail updating all the records at once, probably at start
45d60a
+   up. */
45d60a
+#define DDNS_UPDATE_SLOW_TRANSITION
45d60a
+   
45d60a
diff --git a/includes/tree.h b/includes/tree.h
45d60a
index 291c0f6..746d31c 100644
45d60a
--- a/includes/tree.h
45d60a
+++ b/includes/tree.h
45d60a
@@ -116,9 +116,6 @@ struct binding_value {
45d60a
 		struct data_string data;
45d60a
 		unsigned long intval;
45d60a
 		int boolean;
45d60a
-#if defined (NSUPDATE_OLD)
45d60a
-		ns_updrec *dns;
45d60a
-#endif
45d60a
 		struct fundef *fundef;
45d60a
 		struct binding_value *bv;
45d60a
 	} value;
45d60a
diff --git a/server/ddns.c b/server/ddns.c
45d60a
index 2a64bc9..3cf15ce 100644
45d60a
--- a/server/ddns.c
45d60a
+++ b/server/ddns.c
45d60a
@@ -36,6 +36,9 @@
45d60a
 #include "dhcpd.h"
45d60a
 #include <dns/result.h>
45d60a
 
45d60a
+char *ddns_standard_tag = "ddns-dhcid";
45d60a
+char *ddns_interim_tag  = "ddns-txt";
45d60a
+
45d60a
 #ifdef NSUPDATE
45d60a
 
45d60a
 static void ddns_fwd_srv_connector(struct lease          *lease,
45d60a
@@ -71,16 +74,13 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
45d60a
 	struct data_string ddns_domainname;
45d60a
 	struct data_string old_ddns_fwd_name;
45d60a
 	struct data_string ddns_fwd_name;
45d60a
-	//struct data_string ddns_rev_name;
45d60a
 	struct data_string ddns_dhcid;
45d60a
 	struct binding_scope **scope = NULL;
45d60a
-	//struct iaddr addr;
45d60a
 	struct data_string d1;
45d60a
 	struct option_cache *oc;
45d60a
 	int s1, s2;
45d60a
 	int result = 0;
45d60a
 	int server_updates_a = 1;
45d60a
-	//int server_updates_ptr = 1;
45d60a
 	struct buffer *bp = (struct buffer *)0;
45d60a
 	int ignorep = 0, client_ignorep = 0;
45d60a
 	int rev_name_len;
45d60a
@@ -89,8 +89,9 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
45d60a
 	dhcp_ddns_cb_t *ddns_cb;
45d60a
 	int do_remove = 0;
45d60a
 
45d60a
-	if (ddns_update_style != 2)
45d60a
-		return 0;
45d60a
+	if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) &&
45d60a
+	    (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM))
45d60a
+		return (0);
45d60a
 
45d60a
 	/*
45d60a
 	 * sigh, I want to cancel any previous udpates before we do anything
45d60a
@@ -149,7 +150,6 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
45d60a
 	memset (&ddns_domainname, 0, sizeof (ddns_domainname));
45d60a
 	memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
45d60a
 	memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
45d60a
-	//memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
45d60a
 	memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
45d60a
 
45d60a
 	/* If we are allowed to accept the client's update of its own A
45d60a
@@ -263,31 +263,22 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
45d60a
 			goto in;
45d60a
 		}
45d60a
 
45d60a
-		/* See if there's a DHCID on the lease, and if not
45d60a
-		 * then potentially look for 'on events' for ad-hoc ddns.
45d60a
+#if defined  (DDNS_UPDATE_SLOW_TRANSITION)
45d60a
+		/*
45d60a
+		 * If the slow transition code is enabled check to see
45d60a
+		 * if the stored type (standard or interim doesn't
45d60a
+		 * match the type currently in use.  If it doesn't
45d60a
+		 * try to remove and replace the DNS record
45d60a
 		 */
45d60a
-		if (!find_bound_string(&ddns_dhcid, *scope, "ddns-txt") &&
45d60a
-		    (old != NULL)) {
45d60a
-			/* If there's no DHCID, the update was probably
45d60a
-			   done with the old-style ad-hoc DDNS updates.
45d60a
-			   So if the expiry and release events look like
45d60a
-			   they're the same, run them.   This should delete
45d60a
-			   the old DDNS data. */
45d60a
-			if (old -> on_expiry == old -> on_release) {
45d60a
-				execute_statements(NULL, NULL, lease, NULL,
45d60a
-						   NULL, NULL, scope,
45d60a
-						   old->on_expiry);
45d60a
-				if (old -> on_expiry)
45d60a
-					executable_statement_dereference
45d60a
-						(&old -> on_expiry, MDL);
45d60a
-				if (old -> on_release)
45d60a
-					executable_statement_dereference
45d60a
-						(&old -> on_release, MDL);
45d60a
-				/* Now, install the DDNS data the new way. */
45d60a
-				goto in;
45d60a
-			}
45d60a
-		} else
45d60a
+		if (((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) &&
45d60a
+		     find_bound_string(&ddns_dhcid, *scope, ddns_interim_tag)) ||
45d60a
+		    ((ddns_update_style == DDNS_UPDATE_STYLE_INTERIM) &&
45d60a
+		     find_bound_string(&ddns_dhcid, *scope, ddns_standard_tag))) {
45d60a
 			data_string_forget(&ddns_dhcid, MDL);
45d60a
+			do_remove = 1;
45d60a
+			goto in;
45d60a
+		}
45d60a
+#endif
45d60a
 
45d60a
 		/* See if the administrator wants to do updates even
45d60a
 		   in cases where the update already appears to have been
45d60a
@@ -486,22 +477,68 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
45d60a
 	}
45d60a
 
45d60a
 	/*
45d60a
+	 * copy the string now so we can pass it to the dhcid routines
45d60a
+	 * via the ddns_cb pointer
45d60a
+	 */
45d60a
+	data_string_copy(&ddns_cb->fwd_name, &ddns_fwd_name, MDL);
45d60a
+
45d60a
+	/*
45d60a
 	 * If we are updating the A record, compute the DHCID value.
45d60a
+	 * We have two options for computing the DHCID value, the older
45d60a
+	 * interim version and the newer standard version.  The interim
45d60a
+	 * has some issues but is left as is to avoid compatibility issues.
45d60a
+	 *
45d60a
+	 * We select the type of DHCID to construct and the information to
45d60a
+	 * use for the digest based on 4701 section 3.3
45d60a
 	 */
45d60a
 	if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
45d60a
-		if (lease6 != NULL)
45d60a
-			result = get_dhcid(&ddns_cb->dhcid, 2,
45d60a
-					   lease6->ia->iaid_duid.data,
45d60a
-					   lease6->ia->iaid_duid.len);
45d60a
-		else if ((lease != NULL) && (lease->uid != NULL) &&
45d60a
-			 (lease->uid_len != 0))
45d60a
-			result = get_dhcid (&ddns_cb->dhcid,
45d60a
-					    DHO_DHCP_CLIENT_IDENTIFIER,
45d60a
-					    lease -> uid, lease -> uid_len);
45d60a
-		else if (lease != NULL)
45d60a
-			result = get_dhcid (&ddns_cb->dhcid, 0,
45d60a
-					    lease -> hardware_addr.hbuf,
45d60a
-					    lease -> hardware_addr.hlen);
45d60a
+		int ddns_type;
45d60a
+		int ddns_len;
45d60a
+		if (ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) {
45d60a
+			/* The standard style */
45d60a
+			ddns_cb->lease_tag = ddns_standard_tag;
45d60a
+			ddns_cb->dhcid_class = dns_rdatatype_dhcid;
45d60a
+			ddns_type = 1;
45d60a
+			ddns_len = 4;
45d60a
+		} else {
45d60a
+			/* The older interim style */
45d60a
+			ddns_cb->lease_tag = ddns_interim_tag;
45d60a
+			ddns_cb->dhcid_class = dns_rdatatype_txt;
45d60a
+			/* for backwards compatibility */
45d60a
+			ddns_type = DHO_DHCP_CLIENT_IDENTIFIER;
45d60a
+			/* IAID incorrectly included */
45d60a
+			ddns_len = 0;
45d60a
+		}
45d60a
+
45d60a
+
45d60a
+		if (lease6 != NULL) {
45d60a
+			if (lease6->ia->iaid_duid.len < ddns_len)
45d60a
+				goto badfqdn;
45d60a
+			result = get_dhcid(ddns_cb, 2,
45d60a
+					   lease6->ia->iaid_duid.data + ddns_len,
45d60a
+					   lease6->ia->iaid_duid.len - ddns_len);
45d60a
+		} else if ((lease != NULL) &&
45d60a
+			   (lease->uid != NULL) &&
45d60a
+			   (lease->uid_len != 0)) {
45d60a
+			/* If this is standard check for an RFC 4361
45d60a
+			 * compliant client identifier
45d60a
+			 */
45d60a
+			if ((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) &&
45d60a
+			    (lease->uid[0] == 255)) {
45d60a
+				if (lease->uid_len < 5)
45d60a
+					goto badfqdn;
45d60a
+				result = get_dhcid(ddns_cb, 2,
45d60a
+						   lease->uid + 5,
45d60a
+						   lease->uid_len - 5);
45d60a
+			} else {
45d60a
+				result = get_dhcid(ddns_cb, ddns_type,
45d60a
+						   lease->uid,
45d60a
+						   lease->uid_len);
45d60a
+			}
45d60a
+		} else if (lease != NULL)
45d60a
+			result = get_dhcid(ddns_cb, 0,
45d60a
+					   lease->hardware_addr.hbuf,
45d60a
+					   lease->hardware_addr.hlen);
45d60a
 		else
45d60a
 			log_fatal("Impossible condition at %s:%d.", MDL);
45d60a
 
45d60a
@@ -513,8 +550,6 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
45d60a
 	 * Perform updates.
45d60a
 	 */
45d60a
 
45d60a
-	data_string_copy(&ddns_cb->fwd_name, &ddns_fwd_name, MDL);
45d60a
-
45d60a
 	if (ddns_cb->flags && DDNS_UPDATE_ADDR) {
45d60a
 		oc = lookup_option(&server_universe, options,
45d60a
 				   SV_DDNS_CONFLICT_DETECT);
45d60a
@@ -707,8 +742,6 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
45d60a
 	data_string_forget(&ddns_domainname, MDL);
45d60a
 	data_string_forget(&old_ddns_fwd_name, MDL);
45d60a
 	data_string_forget(&ddns_fwd_name, MDL);
45d60a
-	//data_string_forget(&ddns_rev_name, MDL);
45d60a
-	//data_string_forget(&ddns_dhcid, MDL);
45d60a
 	if (bp)
45d60a
 		buffer_dereference(&bp, MDL);
45d60a
 
45d60a
@@ -822,18 +855,21 @@ ddns_update_lease_text(dhcp_ddns_cb_t        *ddns_cb,
45d60a
 	case DDNS_STATE_ADD_FW_NXDOMAIN:
45d60a
 		bind_ds_value(scope, "ddns-fwd-name", &ddns_cb->fwd_name);
45d60a
 
45d60a
-		/* convert from dns version to lease version of dhcid */
45d60a
-		memset(&lease_dhcid, 0, sizeof(lease_dhcid));
45d60a
-		dhcid_tolease(&ddns_cb->dhcid, &lease_dhcid);
45d60a
-		bind_ds_value(scope, "ddns-txt", &lease_dhcid);
45d60a
-		data_string_forget(&lease_dhcid, MDL);
45d60a
-
45d60a
+		if (ddns_cb->lease_tag == ddns_standard_tag) {
45d60a
+			bind_ds_value(scope, ddns_standard_tag, &ddns_cb->dhcid);
45d60a
+		} else {
45d60a
+			/* convert from dns version to lease version of dhcid */
45d60a
+			memset(&lease_dhcid, 0, sizeof(lease_dhcid));
45d60a
+			dhcid_tolease(&ddns_cb->dhcid, &lease_dhcid);
45d60a
+			bind_ds_value(scope, ddns_interim_tag, &lease_dhcid);
45d60a
+			data_string_forget(&lease_dhcid, MDL);
45d60a
+		}
45d60a
 		break;
45d60a
 
45d60a
 	case DDNS_STATE_REM_FW_NXRR:
45d60a
 	case DDNS_STATE_REM_FW_YXDHCID:
45d60a
 		unset(*scope, "ddns-fwd-name");
45d60a
-		unset(*scope, "ddns-txt");
45d60a
+		unset(*scope, ddns_cb->lease_tag);
45d60a
 		break;
45d60a
 	}
45d60a
 		
45d60a
@@ -1791,7 +1827,8 @@ ddns_removals(struct lease    *lease,
45d60a
 	if (*scope == NULL)
45d60a
 		goto cleanup;
45d60a
 
45d60a
-	if (ddns_update_style != 2)
45d60a
+	if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) &&
45d60a
+	    (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM))
45d60a
 		goto cleanup;
45d60a
 
45d60a
 	/* Assume that we are removing both records */
45d60a
@@ -1823,15 +1860,22 @@ ddns_removals(struct lease    *lease,
45d60a
 	}
45d60a
 
45d60a
 	/*
45d60a
-	 * Find the ptr name and copy it to the control block.  If we don't
45d60a
-	 * have it this isn't an interim or rfc3??? record so we can't delete
45d60a
+	 * Find the txt or dhcid tag and copy it to the control block.  If we don't
45d60a
+	 * have one this isn't an interim or standard record so we can't delete
45d60a
 	 * the A record using this mechanism but we can delete the ptr record.
45d60a
 	 * In this case we will attempt to do any requested next step.
45d60a
 	 */
45d60a
 	memset(&leaseid, 0, sizeof(leaseid));
45d60a
-	if (!find_bound_string (&leaseid, *scope, "ddns-txt")) {
45d60a
-		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
45d60a
-	} else {
45d60a
+	if (find_bound_string (&leaseid, *scope, ddns_standard_tag)) {
45d60a
+		/* We have a standard tag */
45d60a
+		ddns_cb->lease_tag = ddns_standard_tag;
45d60a
+		ddns_cb->dhcid_class = dns_rdatatype_dhcid;
45d60a
+		data_string_copy(&ddns_cb->dhcid, &leaseid, MDL);
45d60a
+		data_string_forget(&leaseid, MDL);
45d60a
+	} else 	if (find_bound_string (&leaseid, *scope, ddns_interim_tag)) {
45d60a
+		/* we have an interim tag */
45d60a
+		ddns_cb->lease_tag = ddns_interim_tag;
45d60a
+		ddns_cb->dhcid_class = dns_rdatatype_txt;
45d60a
 		if (dhcid_fromlease(&ddns_cb->dhcid, &leaseid) != 
45d60a
 		    ISC_R_SUCCESS) {
45d60a
 			/* We couldn't convert the dhcid from the lease
45d60a
@@ -1841,7 +1885,9 @@ ddns_removals(struct lease    *lease,
45d60a
 			ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
45d60a
 		}
45d60a
 		data_string_forget(&leaseid, MDL);
45d60a
-	}
45d60a
+	} else {
45d60a
+		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
45d60a
+	}		
45d60a
 
45d60a
 	/*
45d60a
 	 * Find the rev name and copy it to the control block.  If we don't
45d60a
@@ -1888,7 +1934,7 @@ ddns_removals(struct lease    *lease,
45d60a
 		else {
45d60a
 			/*remove info from scope */
45d60a
 			unset(*scope, "ddns-fwd-name");
45d60a
-			unset(*scope, "ddns-txt");
45d60a
+			unset(*scope, ddns_cb->lease_tag);
45d60a
 		}
45d60a
 	}
45d60a
 
45d60a
diff --git a/server/dhcpd.c b/server/dhcpd.c
45d60a
index 67fec83..9617d75 100644
45d60a
--- a/server/dhcpd.c
45d60a
+++ b/server/dhcpd.c
45d60a
@@ -82,86 +82,6 @@ option server.ddns-hostname =						    \n\
45d60a
 option server.ddns-domainname =	config-option domain-name;		    \n\
45d60a
 option server.ddns-rev-domainname = \"in-addr.arpa.\";";
45d60a
 
45d60a
-/* This is the old-style name service updater that is executed
45d60a
-   whenever a lease is committed.  It does not follow the DHCP-DNS
45d60a
-   draft at all. */
45d60a
-
45d60a
-char old_nsupdate [] = "						    \n\
45d60a
-on commit {								    \n\
45d60a
-  if (not static and							    \n\
45d60a
-      ((config-option server.ddns-updates = null) or			    \n\
45d60a
-       (config-option server.ddns-updates != 0))) {			    \n\
45d60a
-    set new-ddns-fwd-name =						    \n\
45d60a
-      concat (pick (config-option server.ddns-hostname,			    \n\
45d60a
-		    option host-name), \".\",				    \n\
45d60a
-	      pick (config-option server.ddns-domainname,		    \n\
45d60a
-		    config-option domain-name));			    \n\
45d60a
-    if (defined (ddns-fwd-name) and ddns-fwd-name != new-ddns-fwd-name) {   \n\
45d60a
-      switch (ns-update (delete (IN, A, ddns-fwd-name, leased-address))) {  \n\
45d60a
-      case NOERROR:							    \n\
45d60a
-	unset ddns-fwd-name;						    \n\
45d60a
-	on expiry or release {						    \n\
45d60a
-	}								    \n\
45d60a
-      }									    \n\
45d60a
-    }									    \n\
45d60a
-									    \n\
45d60a
-    if (not defined (ddns-fwd-name)) {					    \n\
45d60a
-      set ddns-fwd-name = new-ddns-fwd-name;				    \n\
45d60a
-      if defined (ddns-fwd-name) {					    \n\
45d60a
-	switch (ns-update (not exists (IN, A, ddns-fwd-name, null),	    \n\
45d60a
-			   add (IN, A, ddns-fwd-name, leased-address,	    \n\
45d60a
-				lease-time / 2))) {			    \n\
45d60a
-	default:							    \n\
45d60a
-	  unset ddns-fwd-name;						    \n\
45d60a
-	  break;							    \n\
45d60a
-									    \n\
45d60a
-	case NOERROR:							    \n\
45d60a
-	  set ddns-rev-name =						    \n\
45d60a
-	    concat (binary-to-ascii (10, 8, \".\",			    \n\
45d60a
-				     reverse (1,			    \n\
45d60a
-					      leased-address)), \".\",	    \n\
45d60a
-		    pick (config-option server.ddns-rev-domainname,	    \n\
45d60a
-			  \"in-addr.arpa.\"));				    \n\
45d60a
-	  switch (ns-update (delete (IN, PTR, ddns-rev-name, null),	    \n\
45d60a
-			     add (IN, PTR, ddns-rev-name, ddns-fwd-name,    \n\
45d60a
-				  lease-time / 2)))			    \n\
45d60a
-	    {								    \n\
45d60a
-	    default:							    \n\
45d60a
-	      unset ddns-rev-name;					    \n\
45d60a
-	      on release or expiry {					    \n\
45d60a
-		switch (ns-update (delete (IN, A, ddns-fwd-name,	    \n\
45d60a
-					   leased-address))) {		    \n\
45d60a
-		case NOERROR:						    \n\
45d60a
-		  unset ddns-fwd-name;					    \n\
45d60a
-		  break;						    \n\
45d60a
-		}							    \n\
45d60a
-		on release or expiry;					    \n\
45d60a
-	      }								    \n\
45d60a
-	      break;							    \n\
45d60a
-									    \n\
45d60a
-	    case NOERROR:						    \n\
45d60a
-	      on release or expiry {					    \n\
45d60a
-		switch (ns-update (delete (IN, PTR, ddns-rev-name, null))) {\n\
45d60a
-		case NOERROR:						    \n\
45d60a
-		  unset ddns-rev-name;					    \n\
45d60a
-		  break;						    \n\
45d60a
-		}							    \n\
45d60a
-		switch (ns-update (delete (IN, A, ddns-fwd-name,	    \n\
45d60a
-					   leased-address))) {		    \n\
45d60a
-		case NOERROR:						    \n\
45d60a
-		  unset ddns-fwd-name;					    \n\
45d60a
-		  break;						    \n\
45d60a
-		}							    \n\
45d60a
-		on release or expiry;					    \n\
45d60a
-	      }								    \n\
45d60a
-	    }								    \n\
45d60a
-	}								    \n\
45d60a
-      }									    \n\
45d60a
-    }									    \n\
45d60a
-    unset new-ddns-fwd-name;						    \n\
45d60a
-  }									    \n\
45d60a
-}";
45d60a
-
45d60a
 #endif /* NSUPDATE */
45d60a
 int ddns_update_style;
45d60a
 
45d60a
@@ -897,9 +817,6 @@ void postconf_initialization (int quiet)
45d60a
 	struct option_cache *oc;
45d60a
 	char *s;
45d60a
 	isc_result_t result;
45d60a
-#if defined (NSUPDATE)
45d60a
-	struct parse *parse;
45d60a
-#endif
45d60a
 	int tmp;
45d60a
 
45d60a
 	/* Now try to get the lease file name. */
45d60a
@@ -1160,49 +1077,6 @@ void postconf_initialization (int quiet)
45d60a
 
45d60a
 	/* Don't need the options anymore. */
45d60a
 	option_state_dereference (&options, MDL);
45d60a
-	
45d60a
-#if defined (NSUPDATE)
45d60a
-	/* If old-style ddns updates have been requested, parse the
45d60a
-	   old-style ddns updater. */
45d60a
-	if (ddns_update_style == 1) {
45d60a
-		struct executable_statement **e, *s;
45d60a
-
45d60a
-		if (root_group -> statements) {
45d60a
-			s = (struct executable_statement *)0;
45d60a
-			if (!executable_statement_allocate (&s, MDL))
45d60a
-				log_fatal ("no memory for ddns updater");
45d60a
-			executable_statement_reference
45d60a
-				(&s -> next, root_group -> statements, MDL);
45d60a
-			executable_statement_dereference
45d60a
-				(&root_group -> statements, MDL);
45d60a
-			executable_statement_reference
45d60a
-				(&root_group -> statements, s, MDL);
45d60a
-			s -> op = statements_statement;
45d60a
-			e = &s -> data.statements;
45d60a
-			executable_statement_dereference (&s, MDL);
45d60a
-		} else {
45d60a
-			e = &root_group -> statements;
45d60a
-		}
45d60a
-
45d60a
-		/* Set up the standard name service updater routine. */
45d60a
-		parse = NULL;
45d60a
-		result = new_parse(&parse, -1, old_nsupdate,
45d60a
-				   sizeof(old_nsupdate) - 1,
45d60a
-				   "old name service update routine", 0);
45d60a
-		if (result != ISC_R_SUCCESS)
45d60a
-			log_fatal ("can't begin parsing old ddns updater!");
45d60a
-
45d60a
-		if (parse != NULL) {
45d60a
-			tmp = 0;
45d60a
-			if (!(parse_executable_statements(e, parse, &tmp,
45d60a
-							  context_any))) {
45d60a
-				end_parse(&parse;;
45d60a
-				log_fatal("can't parse standard ddns updater!");
45d60a
-			}
45d60a
-		}
45d60a
-		end_parse(&parse;;
45d60a
-	}
45d60a
-#endif
45d60a
 }
45d60a
 
45d60a
 void postdb_startup (void)
45d60a
diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5
45d60a
index 74393c2..2351e21 100644
45d60a
--- a/server/dhcpd.conf.5
45d60a
+++ b/server/dhcpd.conf.5
45d60a
@@ -1076,115 +1076,24 @@ the Domain Name System to be updated.  These updates are RFC 2136
45d60a
 compliant so any DNS server supporting RFC 2136 should be able to
45d60a
 accept updates from the DHCP server.
45d60a
 .PP
45d60a
-Two DNS update schemes are currently implemented, and another is
45d60a
-planned.  The two that are currently implemented are the ad-hoc DNS
45d60a
-update mode and the interim DHCP-DNS interaction draft update mode.
45d60a
-In the future we plan to add a third mode which will be the standard
45d60a
-DNS update method based on the RFCS for DHCP-DNS interaction and DHCID
45d60a
-The DHCP server must be configured to use one of the two
45d60a
-currently-supported methods, or not to do dns updates.
45d60a
-This can be done with the
45d60a
+There are two DNS schemes implemented.  The interim option is
45d60a
+based on draft revisions of the DDNS documents while the standard
45d60a
+option is based on the RFCs for DHCP-DNS interaction and DHCIDs.
45d60a
+A third option, ad-hoc, was deprecated and has now been removed
45d60a
+from the code base.  The DHCP server must be configured to use
45d60a
+one of the two currently-supported methods, or not to do DNS updates.
45d60a
+.PP
45d60a
+New installations should use the standard option. Older
45d60a
+installations may want to continue using the interim option for
45d60a
+backwards compatibility with the DNS database until the database
45d60a
+can be updated.  This can be done with the
45d60a
 .I ddns-update-style
45d60a
 configuration parameter.
45d60a
-.SH THE AD-HOC DNS UPDATE SCHEME
45d60a
-The ad-hoc Dynamic DNS update scheme is
45d60a
-.B now deprecated
45d60a
-and
45d60a
-.B
45d60a
-does not work.
45d60a
-In future releases of the ISC DHCP server, this scheme will not likely be
45d60a
-available.  The interim scheme works, allows for failover, and should now be
45d60a
-used.  The following description is left here for informational purposes
45d60a
-only.
45d60a
-.PP
45d60a
-The ad-hoc Dynamic DNS update scheme implemented in this version of
45d60a
-the ISC DHCP server is a prototype design, which does not
45d60a
-have much to do with the standard update method that is being
45d60a
-standardized in the IETF DHC working group, but rather implements some
45d60a
-very basic, yet useful, update capabilities.  This mode
45d60a
-.B does not work
45d60a
-with the
45d60a
-.I failover protocol
45d60a
-because it does not account for the possibility of two different DHCP
45d60a
-servers updating the same set of DNS records.
45d60a
-.PP
45d60a
-For the ad-hoc DNS update method, the client's FQDN is derived in two
45d60a
-parts.  First, the hostname is determined.  Then, the domain name is
45d60a
-determined, and appended to the hostname.
45d60a
-.PP
45d60a
-The DHCP server determines the client's hostname by first looking for
45d60a
-a \fIddns-hostname\fR configuration option, and using that if it is
45d60a
-present.  If no such option is present, the server looks for a
45d60a
-valid hostname in the FQDN option sent by the client.  If one is
45d60a
-found, it is used; otherwise, if the client sent a host-name option,
45d60a
-that is used.  Otherwise, if there is a host declaration that applies
45d60a
-to the client, the name from that declaration will be used.  If none
45d60a
-of these applies, the server will not have a hostname for the client,
45d60a
-and will not be able to do a DNS update.
45d60a
-.PP
45d60a
-The domain name is determined from the
45d60a
-.I ddns-domainname
45d60a
-configuration option.  The default configuration for this option is:
45d60a
-.nf
45d60a
-.sp 1
45d60a
-  option server.ddns-domainname = config-option domain-name;
45d60a
-
45d60a
-.fi
45d60a
-So if this configuration option is not configured to a different
45d60a
-value (over-riding the above default), or if a domain-name option
45d60a
-has not been configured for the client's scope, then the server will
45d60a
-not attempt to perform a DNS update.
45d60a
-.PP
45d60a
-The client's fully-qualified domain name, derived as we have
45d60a
-described, is used as the name on which an "A" record will be stored.
45d60a
-The A record will contain the IP address that the client was assigned
45d60a
-in its lease.  If there is already an A record with the same name in
45d60a
-the DNS server, no update of either the A or PTR records will occur -
45d60a
-this prevents a client from claiming that its hostname is the name of
45d60a
-some network server.  For example, if you have a fileserver called
45d60a
-"fs.sneedville.edu", and the client claims its hostname is "fs", no
45d60a
-DNS update will be done for that client, and an error message will be
45d60a
-logged.
45d60a
-.PP
45d60a
-If the A record update succeeds, a PTR record update for the assigned
45d60a
-IP address will be done, pointing to the A record.  This update is
45d60a
-unconditional - it will be done even if another PTR record of the same
45d60a
-name exists.  Since the IP address has been assigned to the DHCP
45d60a
-server, this should be safe.
45d60a
-.PP
45d60a
-Please note that the current implementation assumes clients only have
45d60a
-a single network interface.  A client with two network interfaces
45d60a
-will see unpredictable behavior.  This is considered a bug, and will
45d60a
-be fixed in a later release.  It may be helpful to enable the
45d60a
-.I one-lease-per-client
45d60a
-parameter so that roaming clients do not trigger this same behavior.
45d60a
-.PP
45d60a
-The DHCP protocol normally involves a four-packet exchange - first the
45d60a
-client sends a DHCPDISCOVER message, then the server sends a
45d60a
-DHCPOFFER, then the client sends a DHCPREQUEST, then the server sends
45d60a
-a DHCPACK.  In the current version of the server, the server will do
45d60a
-a DNS update after it has received the DHCPREQUEST, and before it has
45d60a
-sent the DHCPACK.  It only sends the DNS update if it has not sent
45d60a
-one for the client's address before, in order to minimize the impact
45d60a
-on the DHCP server.
45d60a
-.PP
45d60a
-When the client's lease expires, the DHCP server (if it is operating
45d60a
-at the time, or when next it operates) will remove the client's A and
45d60a
-PTR records from the DNS database.  If the client releases its lease
45d60a
-by sending a DHCPRELEASE message, the server will likewise remove the
45d60a
-A and PTR records.
45d60a
-.SH THE INTERIM DNS UPDATE SCHEME
45d60a
-The interim DNS update scheme operates mostly according to several
45d60a
-drafts considered by the IETF.  While the drafts have since become
45d60a
-RFCs the code was written before they were finalized and there are
45d60a
-some differences between our code and the final RFCs.  We plan to
45d60a
-update our code, probably adding a standard DNS update option, at
45d60a
-some time.  The basic framework is similar with the main material
45d60a
-difference being that a DHCID RR was assigned in the RFCs whereas
45d60a
-our code continues to use an experimental TXT record.  The format
45d60a
-of the TXT record bears a resemblance to the DHCID RR but it is not
45d60a
-equivalent (MD5 vs SHA1, field length differences etc).
45d60a
-The standard RFCs are:
45d60a
+.SH THE DNS UPDATE SCHEME
45d60a
+the interim and standard DNS update schemes operate mostly according
45d60a
+to work from the IETF.  The interim version was based on the drafts
45d60a
+in progress at the time while the standard is based on the completed
45d60a
+RFCs.  The standard RFCs are:
45d60a
 .PP
45d60a
 .nf
45d60a
 .ce 3
45d60a
@@ -1202,15 +1111,17 @@ draft-ietf-dhc-fqdn-option-??.txt
45d60a
 draft-ietf-dhc-ddns-resolution-??.txt
45d60a
 .fi
45d60a
 .PP
45d60a
-Because our implementation is slightly different than the standard, we
45d60a
-will briefly document the operation of this update style here.
45d60a
+The basic framework for the two schemes is similar with the main
45d60a
+material difference being that a DHCID RR is used in the standard
45d60a
+version while the interim versions uses a TXT RR.  The format
45d60a
+of the TXT record bears a resemblance to the DHCID RR but it is not
45d60a
+equivalent (MD5 vs SHA2, field length differences etc).
45d60a
 .PP
45d60a
-The first point to understand about this style of DNS update is that
45d60a
-unlike the ad-hoc style, the DHCP server does not necessarily
45d60a
+In these two schemes the DHCP server does not necessarily
45d60a
 always update both the A and the PTR records.  The FQDN option
45d60a
 includes a flag which, when sent by the client, indicates that the
45d60a
 client wishes to update its own A record.  In that case, the server
45d60a
-can be configured either to honor the client's intentions or ignore
45d60a
+can be configured either to honor the client\'s intentions or ignore
45d60a
 them.  This is done with the statement \fIallow client-updates;\fR or
45d60a
 the statement \fIignore client-updates;\fR.  By default, client
45d60a
 updates are allowed.
45d60a
@@ -1230,15 +1141,14 @@ IP address, it can update its own A record, assuming that the
45d60a
 "radish.org" DNS server will allow it to do so.
45d60a
 .PP
45d60a
 If the server is configured not to allow client updates, or if the
45d60a
-client doesn't want to do its own update, the server will simply
45d60a
+client doesn\'t want to do its own update, the server will simply
45d60a
 choose a name for the client from either the fqdn option (if present)
45d60a
 or the hostname option (if present).  It will use its own
45d60a
-domain name for the client, just as in the ad-hoc update scheme.
45d60a
-It will then update both the A and PTR record, using the name that it
45d60a
-chose for the client.  If the client sends a fully-qualified domain
45d60a
-name in the fqdn option, the server uses only the leftmost part of the
45d60a
-domain name - in the example above, "jschmoe" instead of
45d60a
-"jschmoe.radish.org".
45d60a
+domain name for the client.  It will then update both the A and PTR
45d60a
+record, using the name that it chose for the client.  If the client
45d60a
+sends a fully-qualified domain name in the \fBfqdn\fR option, the
45d60a
+server uses only the leftmost part of the domain name - in the
45d60a
+example above, "jschmoe" instead of "jschmoe.radish.org".
45d60a
 .PP
45d60a
 Further, if the \fIignore client-updates;\fR directive is used, then
45d60a
 the server will in addition send a response in the DHCP packet, using
45d60a
@@ -1248,49 +1158,41 @@ response is sent which indicates the client may not perform updates.
45d60a
 .PP
45d60a
 Also, if the
45d60a
 .I use-host-decl-names
45d60a
-configuration option is enabled, then the host declaration's
45d60a
+configuration option is enabled, then the host declaration\'s
45d60a
 .I hostname
45d60a
 will be used in place of the
45d60a
 .I hostname
45d60a
 option, and the same rules will apply as described above.
45d60a
 .PP
45d60a
-The other difference between the ad-hoc scheme and the interim
45d60a
-scheme is that with the interim scheme, a method is used that
45d60a
-allows more than one DHCP server to update the DNS database without
45d60a
-accidentally deleting A records that shouldn't be deleted nor failing
45d60a
-to add A records that should be added.  The scheme works as follows:
45d60a
+Both the standard and interim options also include a method to 
45d60a
+allow more than one DHCP server to update the DNS database without
45d60a
+accidentally deleting A records that shouldn\'t be deleted nor failing
45d60a
+to add A records that should be added.  For the standard option the
45d60a
+method works as follows:
45d60a
 .PP
45d60a
 When the DHCP server issues a client a new lease, it creates a text
45d60a
-string that is an MD5 hash over the DHCP client's identification (see
45d60a
-draft-ietf-dnsext-dhcid-rr-??.txt for details).  The update adds an A
45d60a
-record with the name the server chose and a TXT record containing the
45d60a
+string that is an SHA hash over the DHCP client\'s identification (see
45d60a
+RFCs 4701 & 4702 for details).  The update attempts to add an A
45d60a
+record with the name the server chose and a DHCID record containing the
45d60a
 hashed identifier string (hashid).  If this update succeeds, the
45d60a
 server is done.
45d60a
 .PP
45d60a
 If the update fails because the A record already exists, then the DHCP
45d60a
 server attempts to add the A record with the prerequisite that there
45d60a
-must be a TXT record in the same name as the new A record, and that
45d60a
-TXT record's contents must be equal to hashid.  If this update
45d60a
+must be a DHCID record in the same name as the new A record, and that
45d60a
+DHCID record\'s contents must be equal to hashid.  If this update
45d60a
 succeeds, then the client has its A record and PTR record.  If it
45d60a
 fails, then the name the client has been assigned (or requested) is in
45d60a
-use, and can't be used by the client.  At this point the DHCP server
45d60a
+use, and can\'t be used by the client.  At this point the DHCP server
45d60a
 gives up trying to do a DNS update for the client until the client
45d60a
 chooses a new name.
45d60a
 .PP
45d60a
-The interim DNS update scheme is called interim for two reasons.
45d60a
-First, it does not quite follow the RFCs.  The RFCs call for a
45d60a
-new DHCID RRtype while he interim DNS update scheme uses a TXT record.
45d60a
-The ddns-resolution draft called for the DHCP server to put a DHCID RR
45d60a
-on the PTR record, but the \fIinterim\fR update method does not do this.
45d60a
-In the final RFC this requirement was relaxed such that a server may
45d60a
-add a DHCID RR to the PTR record.
45d60a
-.PP
45d60a
-In addition to these differences, the server also does not update very
45d60a
-aggressively.  Because each DNS update involves a round trip to the
45d60a
-DNS server, there is a cost associated with doing updates even if they
45d60a
-do not actually modify the DNS database.  So the DHCP server tracks
45d60a
-whether or not it has updated the record in the past (this information
45d60a
-is stored on the lease) and does not attempt to update records that it
45d60a
+The server also does not update very aggressively.  Because each
45d60a
+DNS update involves a round trip to the DNS server, there is a cost
45d60a
+associated with doing updates even if they do not actually modify
45d60a
+the DNS database.  So the DHCP server tracks whether or not it has
45d60a
+updated the record in the past (this information is stored on the
45d60a
+lease) and does not attempt to update records that it
45d60a
 thinks it has already updated.
45d60a
 .PP
45d60a
 This can lead to cases where the DHCP server adds a record, and then
45d60a
@@ -1299,6 +1201,15 @@ never again updates the DNS because it thinks the data is already
45d60a
 there.  In this case the data can be removed from the lease through
45d60a
 operator intervention, and once this has been done, the DNS will be
45d60a
 updated the next time the client renews.
45d60a
+.PP
45d60a
+The interim DNS update scheme was written before the RFCs were finalized
45d60a
+and does not quite follow them.  The RFCs call for a new DHCID RRtype
45d60a
+while he interim DNS update scheme uses a TXT record.  In addition
45d60a
+the ddns-resolution draft called for the DHCP server to put a DHCID RR
45d60a
+on the PTR record, but the \fIinterim\fR update method does not do this.
45d60a
+In the final RFC this requirement was relaxed such that a server may
45d60a
+add a DHCID RR to the PTR record.
45d60a
+.PP
45d60a
 .SH DYNAMIC DNS UPDATE SECURITY
45d60a
 .PP
45d60a
 When you set your DNS server up to allow updates from the DHCP server,
45d60a
@@ -1380,24 +1291,15 @@ Also keep in mind that zone names in your DHCP configuration should end in a
45d60a
 configuration, zone names are not encapsulated in quotes where there are in
45d60a
 the DNS configuration.
45d60a
 .PP
45d60a
-You should choose your own secret key, of course.  The ISC BIND 8 and
45d60a
-9 distributions come with a program for generating secret keys called
45d60a
-dnssec-keygen.  The version that comes with BIND 9 is likely to produce a
45d60a
-substantially more random key, so we recommend you use that one even
45d60a
-if you are not using BIND 9 as your DNS server.  If you are using BIND 9's
45d60a
+You should choose your own secret key, of course.  The ISC BIND 9
45d60a
+distribution comes with a program for generating secret keys called
45d60a
+dnssec-keygen.  If you are using BIND 9\'s
45d60a
 dnssec-keygen, the above key would be created as follows:
45d60a
 .PP
45d60a
 .nf
45d60a
 	dnssec-keygen -a HMAC-MD5 -b 128 -n USER DHCP_UPDATER
45d60a
 .fi
45d60a
 .PP
45d60a
-If you are using the BIND 8 dnskeygen program, the following command will
45d60a
-generate a key as seen above:
45d60a
-.PP
45d60a
-.nf
45d60a
-	dnskeygen -H 128 -u -c -n DHCP_UPDATER
45d60a
-.fi
45d60a
-.PP
45d60a
 The key name, algorithm, and secret must match that being used by the DNS
45d60a
 server. The DHCP server currently supports the following algorithms:
45d60a
 .nf
45d60a
@@ -1451,15 +1353,7 @@ and the expiry event, when the commitment expires.
45d60a
 To declare a set of statements to execute when an event happens, you
45d60a
 must use the \fBon\fR statement, followed by the name of the event,
45d60a
 followed by a series of statements to execute when the event happens,
45d60a
-enclosed in braces.  Events are used to implement DNS
45d60a
-updates, so you should not define your own event handlers if you are
45d60a
-using the built-in DNS update mechanism.
45d60a
-.PP
45d60a
-The built-in version of the DNS update mechanism is in a text
45d60a
-string towards the top of server/dhcpd.c.  If you want to use events
45d60a
-for things other than DNS updates, and you also want DNS updates, you
45d60a
-will have to start out by copying this code into your dhcpd.conf file
45d60a
-and modifying it.
45d60a
+enclosed in braces.
45d60a
 .SH REFERENCE: DECLARATIONS
45d60a
 .PP
45d60a
 .B The
45d60a
@@ -2109,7 +2003,7 @@ The \fIddns-update-style\fR parameter
45d60a
 .PP
45d60a
 The
45d60a
 .I style
45d60a
-parameter must be one of \fBad-hoc\fR, \fBinterim\fR or \fBnone\fR.
45d60a
+parameter must be one of \fBstandard\fR, \fBinterim\fR or \fBnone\fR.
45d60a
 The \fIddns-update-style\fR statement is only meaningful in the outer
45d60a
 scope - it is evaluated once after reading the dhcpd.conf file, rather
45d60a
 than each time a client is assigned an IP address, so there is no way
45d60a
@@ -2186,16 +2080,15 @@ statement
45d60a
 .B do-forward-updates \fIflag\fB;\fR
45d60a
 .PP
45d60a
 The \fIdo-forward-updates\fR statement instructs the DHCP server as
45d60a
-to whether it should attempt to update a DHCP client's A record
45d60a
+to whether it should attempt to update a DHCP client\'s A record
45d60a
 when the client acquires or renews a lease.  This statement has no
45d60a
-effect unless DNS updates are enabled and \fBddns-update-style\fR is
45d60a
-set to \fBinterim\fR.  Forward updates are enabled by default.  If
45d60a
-this statement is used to disable forward updates, the DHCP server
45d60a
-will never attempt to update the client's A record, and will only ever
45d60a
-attempt to update the client's PTR record if the client supplies an
45d60a
-FQDN that should be placed in the PTR record using the fqdn option.
45d60a
-If forward updates are enabled, the DHCP server will still honor the
45d60a
-setting of the \fBclient-updates\fR flag.
45d60a
+effect unless DNS updates are enabled.  Forward updates are enabled
45d60a
+by default.  If this statement is used to disable forward updates,
45d60a
+the DHCP server will never attempt to update the client\'s A record,
45d60a
+and will only ever attempt to update the client\'s PTR record if the
45d60a
+client supplies an FQDN that should be placed in the PTR record using
45d60a
+the \fBfqdn\fR option.  If forward updates are enabled, the DHCP server
45d60a
+will still honor the setting of the \fBclient-updates\fR flag.
45d60a
 .RE
45d60a
 .PP
45d60a
 The
45d60a
@@ -2747,7 +2640,7 @@ on which the request arrived.
45d60a
 The usual case where the
45d60a
 \fIserver-identifier\fR statement needs to be sent is when a physical
45d60a
 interface has more than one IP address, and the one being sent by default
45d60a
-isn't appropriate for some or all clients served by that interface.
45d60a
+isn\'t appropriate for some or all clients served by that interface.
45d60a
 Another common case is when an alias is defined for the purpose of
45d60a
 having a consistent IP address for the DHCP server, and it is desired
45d60a
 that the clients use this IP address when contacting the server.
45d60a
diff --git a/server/stables.c b/server/stables.c
45d60a
index 6a900c8..8ef8bf2 100644
45d60a
--- a/server/stables.c
45d60a
+++ b/server/stables.c
45d60a
@@ -3,7 +3,7 @@
45d60a
    Tables of information only used by server... */
45d60a
 
45d60a
 /*
45d60a
- * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
45d60a
+ * Copyright (c) 2004-2011,2013 by Internet Systems Consortium, Inc. ("ISC")
45d60a
  * Copyright (c) 1995-2003 by Internet Software Consortium
45d60a
  *
45d60a
  * Permission to use, copy, modify, and distribute this software for any
45d60a
@@ -330,6 +330,7 @@ struct enumeration_value ddns_styles_values [] = {
45d60a
 	{ "none", 0 },
45d60a
 	{ "ad-hoc", 1 },
45d60a
 	{ "interim", 2 },
45d60a
+	{ "standard", 3 },
45d60a
 	{ (char *)0, 0 }
45d60a
 };
45d60a