Commit 98bf1607 authored by Shawn Routhier's avatar Shawn Routhier

Support for asynchronous ddns per ticket 19216 - convert to using isclib and

dnslib.
parent 571c38b0
......@@ -16,9 +16,11 @@ EXTRA_DIST = RELNOTES LICENSE \
doc/ja_JP.eucJP/dhclient-script.8 doc/ja_JP.eucJP/dhclient.8 \
doc/ja_JP.eucJP/dhclient.conf.5 doc/ja_JP.eucJP/dhclient.leases.5 \
doc/ja_JP.eucJP/dhcp-eval.5 doc/ja_JP.eucJP/dhcp-options.5 \
doc/examples/dhclient-dhcpv6.conf doc/examples/dhcpd-dhcpv6.conf
doc/examples/dhclient-dhcpv6.conf doc/examples/dhcpd-dhcpv6.conf \
util/bind.sh util/bindlib.sh util/bindcus.sh \
bind/bind.tar.gz bind/version.tmp
SUBDIRS = includes tests common minires dst omapip client dhcpctl relay server
SUBDIRS = includes tests common dst omapip client dhcpctl relay server
nobase_include_HEADERS = dhcpctl/dhcpctl.h
......@@ -134,6 +134,17 @@ the tar utility and the gzip command - type something like:
gunzip dhcp-4.1.0.tar.gz
tar xvf dhcp-4.1.0.tar
BUILDING BIND LIBRARIES
To build the BIND libraries used by DHCP cd to the dhcp-4.1.0 subdirectory
that you've just created and run the bindcus.sh from the the util
subdirectory - something like this:
sh util/bindcus.sh
In order to build the necessary libraries you will need to have "gmake"
available on your build system.
CONFIGURING IT
Now, cd to the dhcp-4.1.0 subdirectory that you've just created and
......@@ -150,8 +161,8 @@ your own.
DYNAMIC DNS UPDATES
A fully-featured implementation of dynamic DNS updates is included in
this release. There are no build dependencies with any BIND version
- this version can and should just use the resolver in your C library.
this release. It uses libraries from BIND and, to avoid issues with
different versions, includes the necessary BIND version.
There is documentation for the DDNS support in the dhcpd.conf manual
page - see the beginning of this document for information on finding
......
......@@ -9,6 +9,10 @@
ISC DHCP 4.2.x includes features that were not included in DHCP 4.1.x.
These include:
Processing the DHCP to DNS server transactions in an asyncrhonous fashion.
The DHCP server or client can now continue with it's processing while
awaiting replies from the DNS server.
There are a number of DHCPv6 limitations and features missing in this
release, which will be addressed in the future:
......@@ -71,6 +75,10 @@ work on other platforms. Please report any problems and suggested fixes to
calculations. Invalid renew and rebinding times (e.g., greater than the
determined lease time) are omitted.
- Processing the DHCP to DNS server transactions in an asyncrhonous fashion.
The DHCP server or client can now continue with it's processing while
awaiting replies from the DNS server.
Changes since 4.1.0 (bug fixes)
- Remove infinite loop in token_print_indent_concat().
......
......@@ -4,8 +4,8 @@ dhclient_SOURCES = clparse.c dhclient.c dhc6.c \
scripts/bsdos scripts/freebsd scripts/linux scripts/macos \
scripts/netbsd scripts/nextstep scripts/openbsd \
scripts/solaris scripts/openwrt
dhclient_LDADD = ../common/libdhcp.a ../minires/libres.a \
../omapip/libomapi.a ../dst/libdst.a
dhclient_LDADD = ../common/libdhcp.a ../omapip/libomapi.a \
../bind/lib/libdns.a ../bind/lib/libisc.a
man_MANS = dhclient.8 dhclient-script.8 dhclient.conf.5 dhclient.leases.5
EXTRA_DIST = $(man_MANS)
......
......@@ -226,7 +226,7 @@ int read_client_conf_file (const char *name, struct interface_info *ip,
} while (1);
token = next_token (&val, (unsigned *)0, cfile);
status = (cfile -> warnings_occurred
? ISC_R_BADPARSE
? DHCP_R_BADPARSE
: ISC_R_SUCCESS);
end_parse (&cfile);
return status;
......
......@@ -744,7 +744,7 @@ dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet,
MDL);
dfree(ia, MDL);
data_string_forget(&ds, MDL);
return ISC_R_BADPARSE;
return DHCP_R_BADPARSE;
}
}
data_string_forget(&ds, MDL);
......@@ -829,7 +829,7 @@ dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet,
MDL);
dfree(ia, MDL);
data_string_forget(&ds, MDL);
return ISC_R_BADPARSE;
return DHCP_R_BADPARSE;
}
}
data_string_forget(&ds, MDL);
......@@ -933,7 +933,7 @@ dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet,
MDL);
dfree(ia, MDL);
data_string_forget(&ds, MDL);
return ISC_R_BADPARSE;
return DHCP_R_BADPARSE;
}
}
data_string_forget(&ds, MDL);
......@@ -1039,7 +1039,7 @@ dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet,
MDL);
dfree(addr, MDL);
data_string_forget(&ds, MDL);
return ISC_R_BADPARSE;
return DHCP_R_BADPARSE;
}
}
......@@ -1145,7 +1145,7 @@ dhc6_parse_prefixes(struct dhc6_addr **ppfx, struct packet *packet,
MDL);
dfree(pfx, MDL);
data_string_forget(&ds, MDL);
return ISC_R_BADPARSE;
return DHCP_R_BADPARSE;
}
}
......@@ -2325,10 +2325,10 @@ dhc6_get_status_code(struct option_state *options, unsigned *code,
isc_result_t rval = ISC_R_SUCCESS;
if ((options == NULL) || (code == NULL))
return ISC_R_INVALIDARG;
return DHCP_R_INVALIDARG;
if ((msg != NULL) && (msg->len != 0))
return ISC_R_INVALIDARG;
return DHCP_R_INVALIDARG;
memset(&ds, 0, sizeof(ds));
......@@ -2341,7 +2341,7 @@ dhc6_get_status_code(struct option_state *options, unsigned *code,
NULL, &global_scope, oc, MDL)) {
if (ds.len < 2) {
log_error("Invalid status code length %d.", ds.len);
rval = ISC_R_FORMERR;
rval = DHCP_R_FORMERR;
} else
*code = getUShort(ds.data);
......@@ -2368,7 +2368,7 @@ dhc6_check_status(isc_result_t rval, struct option_state *options,
isc_result_t status;
if ((scope == NULL) || (code == NULL))
return ISC_R_INVALIDARG;
return DHCP_R_INVALIDARG;
/* If we don't find a code, we assume success. */
*code = STATUS_Success;
......@@ -2452,7 +2452,7 @@ dhc6_init_action(struct client_state *client, isc_result_t *rvalp,
log_fatal("Impossible condition at %s:%d.", MDL);
if (client == NULL) {
*rvalp = ISC_R_INVALIDARG;
*rvalp = DHCP_R_INVALIDARG;
return ISC_FALSE;
}
......@@ -2478,7 +2478,7 @@ dhc6_select_action(struct client_state *client, isc_result_t *rvalp,
log_fatal("Impossible condition at %s:%d.", MDL);
if (client == NULL) {
*rvalp = ISC_R_INVALIDARG;
*rvalp = DHCP_R_INVALIDARG;
return ISC_FALSE;
}
rval = *rvalp;
......@@ -2605,7 +2605,7 @@ dhc6_reply_action(struct client_state *client, isc_result_t *rvalp,
log_fatal("Impossible condition at %s:%d.", MDL);
if (client == NULL) {
*rvalp = ISC_R_INVALIDARG;
*rvalp = DHCP_R_INVALIDARG;
return ISC_FALSE;
}
rval = *rvalp;
......@@ -2706,7 +2706,7 @@ dhc6_stop_action(struct client_state *client, isc_result_t *rvalp,
log_fatal("Impossible condition at %s:%d.", MDL);
if (client == NULL) {
*rvalp = ISC_R_INVALIDARG;
*rvalp = DHCP_R_INVALIDARG;
return ISC_FALSE;
}
rval = *rvalp;
......@@ -2772,7 +2772,7 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
int nscore, sscore;
if ((client == NULL) || (new == NULL))
return ISC_R_INVALIDARG;
return DHCP_R_INVALIDARG;
switch (client->state) {
case S_INIT:
......@@ -2819,7 +2819,7 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
break;
default:
log_error("dhc6_check_reply: no type.");
return ISC_R_INVALIDARG;
return DHCP_R_INVALIDARG;
}
rval = dhc6_check_status(rval, ia->options,
scope, &code);
......@@ -4392,7 +4392,9 @@ start_bound(struct client_state *client)
struct dhc6_addr *addr, *oldaddr;
struct dhc6_lease *lease, *old;
const char *reason;
#if defined (NSUPDATE)
TIME dns_update_offset = 1;
#endif
lease = client->active_lease;
if (lease == NULL) {
......@@ -4453,10 +4455,12 @@ start_bound(struct client_state *client)
} else
oldaddr = NULL;
#if defined (NSUPDATE)
if ((oldaddr == NULL) && (ia->ia_type == D6O_IA_NA))
dhclient_schedule_updates(client,
&addr->address,
dns_update_offset++);
#endif
/* Shell out to setup the new binding. */
script_init(client, reason, NULL);
......@@ -4787,11 +4791,13 @@ do_depref(void *input)
piaddr(addr->address),
(unsigned) addr->plen);
#if defined (NSUPDATE)
/* Remove DDNS bindings at depref time. */
if ((ia->ia_type == D6O_IA_NA) &&
client->config->do_forward_update)
client_dns_update(client, 0, 0,
client_dns_remove(client,
&addr->address);
#endif
}
}
}
......@@ -4838,6 +4844,7 @@ do_expire(void *input)
piaddr(addr->address),
(unsigned) addr->plen);
#if defined (NSUPDATE)
/* We remove DNS records at depref time, but
* it is possible that we might get here
* without depreffing.
......@@ -4845,8 +4852,9 @@ do_expire(void *input)
if ((ia->ia_type == D6O_IA_NA) &&
client->config->do_forward_update &&
!(addr->flags & DHC6_ADDR_DEPREFFED))
client_dns_update(client, 0, 0,
client_dns_remove(client,
&addr->address);
#endif
continue;
}
......@@ -4905,9 +4913,11 @@ unconfigure6(struct client_state *client, const char *reason)
client->active_lease, ia, addr);
script_go(client);
#if defined (NSUPDATE)
if ((ia->ia_type == D6O_IA_NA) &&
client->config->do_forward_update)
client_dns_update(client, 0, 0, &addr->address);
client_dns_remove(client, &addr->address);
#endif
}
}
}
......
......@@ -37,6 +37,7 @@
#include <sys/time.h>
#include <sys/wait.h>
#include <limits.h>
#include <dns/result.h>
TIME default_lease_time = 43200; /* 12 hours... */
TIME max_lease_time = 86400; /* 24 hours... */
......@@ -134,6 +135,12 @@ main(int argc, char **argv) {
setlogmask(LOG_UPTO(LOG_INFO));
#endif
/* Set up the isc and dns library managers */
status = dhcp_context_create();
if (status != ISC_R_SUCCESS)
log_fatal("Can't initialize context: %s",
isc_result_totext(status));
/* Set up the OMAPI. */
status = omapi_init();
if (status != ISC_R_SUCCESS)
......@@ -1186,9 +1193,10 @@ void bind_lease (client)
client -> state = S_BOUND;
reinitialize_interfaces ();
go_daemon ();
#if defined (NSUPDATE)
if (client->config->do_forward_update)
dhclient_schedule_updates(client, &client->active->address,
1);
dhclient_schedule_updates(client, &client->active->address, 1);
#endif
}
/* state_bound is called when we've successfully bound to a particular
......@@ -2676,7 +2684,7 @@ write_duid(struct data_string *duid)
int stat;
if ((duid == NULL) || (duid->len <= 2))
return ISC_R_INVALIDARG;
return DHCP_R_INVALIDARG;
if (leaseFile == NULL) { /* XXX? */
leaseFile = fopen(path_dhclient_db, "w");
......@@ -2724,7 +2732,7 @@ write_client6_lease(struct client_state *client, struct dhc6_lease *lease,
}
if (client == NULL || lease == NULL)
return ISC_R_INVALIDARG;
return DHCP_R_INVALIDARG;
if (leaseFile == NULL) { /* XXX? */
leaseFile = fopen(path_dhclient_db, "w");
......@@ -3570,6 +3578,75 @@ static void shutdown_exit (void *foo)
exit (0);
}
#if defined (NSUPDATE)
/*
* If the first query fails, the updater MUST NOT delete the DNS name. It
* may be that the host whose lease on the server has expired has moved
* to another network and obtained a lease from a different server,
* which has caused the client's A RR to be replaced. It may also be
* that some other client has been configured with a name that matches
* the name of the DHCP client, and the policy was that the last client
* to specify the name would get the name. In this case, the DHCID RR
* will no longer match the updater's notion of the client-identity of
* the host pointed to by the DNS name.
* -- "Interaction between DHCP and DNS"
*/
/* The first and second stages are pretty similar so we combine them */
void
client_dns_remove_action(dhcp_ddns_cb_t *ddns_cb,
isc_result_t eresult)
{
isc_result_t result;
if ((eresult == ISC_R_SUCCESS) &&
(ddns_cb->state == DDNS_STATE_REM_FW_YXDHCID)) {
/* Do the second stage of the FWD removal */
ddns_cb->state = DDNS_STATE_REM_FW_NXRR;
result = ddns_modify_fwd(ddns_cb);
if (result == ISC_R_SUCCESS) {
return;
}
}
/* If we are done or have an error clean up */
ddns_cb_free(ddns_cb, MDL);
return;
}
void
client_dns_remove(struct client_state *client,
struct iaddr *addr)
{
dhcp_ddns_cb_t *ddns_cb;
isc_result_t result;
/* if we have an old ddns request for this client, cancel it */
if (client->ddns_cb != NULL) {
ddns_cancel(client->ddns_cb);
client->ddns_cb = NULL;
}
ddns_cb = ddns_cb_alloc(MDL);
if (ddns_cb != NULL) {
ddns_cb->address = *addr;
ddns_cb->timeout = 0;
ddns_cb->state = DDNS_STATE_REM_FW_YXDHCID;
ddns_cb->flags = DDNS_UPDATE_ADDR;
ddns_cb->cur_func = client_dns_remove_action;
result = client_dns_update(client, ddns_cb);
if (result != ISC_R_TIMEDOUT) {
ddns_cb_free(ddns_cb, MDL);
}
}
}
#endif
isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
control_object_state_t newstate)
{
......@@ -3590,9 +3667,12 @@ isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
case server_shutdown:
if (client -> active &&
client -> active -> expiry > cur_time) {
if (client -> config -> do_forward_update)
client_dns_update(client, 0, 0,
&client->active->address);
#if defined (NSUPDATE)
if (client->config->do_forward_update) {
client_dns_remove(client,
&client->active->address);
}
#endif
do_release (client);
}
break;
......@@ -3616,71 +3696,136 @@ isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
return ISC_R_SUCCESS;
}
/* Schedule updates to retry occasionally until it no longer times out.
#if defined (NSUPDATE)
/*
* Called after a timeout if the DNS update failed on the previous try.
* Starts the retry process. If the retry times out it will schedule
* this routine to run again after a 10x wait.
*/
void
dhclient_schedule_updates(struct client_state *client, struct iaddr *addr,
int offset)
client_dns_update_timeout (void *cp)
{
struct dns_update_state *ustate;
struct timeval tv;
if (!client->config->do_forward_update)
return;
ustate = dmalloc(sizeof(*ustate), MDL);
dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)cp;
struct client_state *client = (struct client_state *)ddns_cb->lease;
isc_result_t status = ISC_R_FAILURE;
if (ustate != NULL) {
ustate->client = client;
ustate->address = *addr;
ustate->dns_update_timeout = 1;
if ((client != NULL) &&
((client->active != NULL) ||
(client->active_lease != NULL)))
status = client_dns_update(client, ddns_cb);
tv.tv_sec = cur_time + offset;
tv.tv_usec = 0;
add_timeout(&tv, client_dns_update_timeout,
ustate, NULL, NULL);
} else {
log_error("Unable to allocate dns update state for %s.",
piaddr(*addr));
/*
* A status of timedout indicates that we started the update and
* have released control of the control block. Any other status
* indicates that we should clean up the control block. We either
* got a success which indicates that we didn't really need to
* send an update or some other error in which case we weren't able
* to start the update process. In both cases we still own
* the control block and should free it.
*/
if (status != ISC_R_TIMEDOUT) {
if (client != NULL) {
client->ddns_cb = NULL;
}
ddns_cb_free(ddns_cb, MDL);
}
}
/* Called after a timeout if the DNS update failed on the previous try.
Retries the update, and if it times out, schedules a retry after
ten times as long of a wait. */
/*
* If the first query succeeds, the updater can conclude that it
* has added a new name whose only RRs are the A and DHCID RR records.
* The A RR update is now complete (and a client updater is finished,
* while a server might proceed to perform a PTR RR update).
* -- "Interaction between DHCP and DNS"
*
* If the second query succeeds, the updater can conclude that the current
* client was the last client associated with the domain name, and that
* the name now contains the updated A RR. The A RR update is now
* complete (and a client updater is finished, while a server would
* then proceed to perform a PTR RR update).
* -- "Interaction between DHCP and DNS"
*
* If the second query fails with NXRRSET, the updater must conclude
* that the client's desired name is in use by another host. At this
* juncture, the updater can decide (based on some administrative
* configuration outside of the scope of this document) whether to let
* the existing owner of the name keep that name, and to (possibly)
* perform some name disambiguation operation on behalf of the current
* client, or to replace the RRs on the name with RRs that represent
* the current client. If the configured policy allows replacement of
* existing records, the updater submits a query that deletes the
* existing A RR and the existing DHCID RR, adding A and DHCID RRs that
* represent the IP address and client-identity of the new client.
* -- "Interaction between DHCP and DNS"
*/
void client_dns_update_timeout (void *cp)
/* The first and second stages are pretty similar so we combine them */
void
client_dns_update_action(dhcp_ddns_cb_t *ddns_cb,
isc_result_t eresult)
{
struct dns_update_state *ustate = cp;
isc_result_t status = ISC_R_FAILURE;
isc_result_t result;
struct timeval tv;
/* XXX: DNS TTL is a problem we need to solve properly. Until
* that time, 300 is a placeholder default for something that is
* less insane than a value scaled by lease timeout.
*/
if ((ustate->client->active != NULL) ||
(ustate->client->active_lease != NULL))
status = client_dns_update(ustate->client, 1, 300,
&ustate->address);
if (status == ISC_R_TIMEDOUT) {
if (ustate->dns_update_timeout < 3600)
ustate->dns_update_timeout *= 10;
tv.tv_sec = cur_time + ustate->dns_update_timeout;
switch(eresult) {
case ISC_R_SUCCESS:
default:
/* Either we succeeded or broke in a bad way, clean up */
break;
case DNS_R_YXRRSET:
/*
* This is the only difference between the two stages,
* check to see if it is the first stage, in which case
* start the second stage
*/
if (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) {
ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID;
ddns_cb->cur_func = client_dns_update_action;
result = ddns_modify_fwd(ddns_cb);
if (result == ISC_R_SUCCESS) {
return;
}
}
break;
case ISC_R_TIMEDOUT:
/*
* We got a timeout response from the DNS module. Schedule
* another attempt for later. We forget the name, dhcid and
* zone so if it gets changed we will get the new information.
*/
data_string_forget(&ddns_cb->fwd_name, MDL);
data_string_forget(&ddns_cb->dhcid, MDL);
if (ddns_cb->zone != NULL) {
forget_zone((struct dns_zone **)&ddns_cb->zone);
}
/* Reset to doing the first stage */
ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN;
ddns_cb->cur_func = client_dns_update_action;
/* and update our timer */
if (ddns_cb->timeout < 3600)
ddns_cb->timeout *= 10;
tv.tv_sec = cur_time + ddns_cb->timeout;
tv.tv_usec = 0;
add_timeout(&tv, client_dns_update_timeout,
ustate, NULL, NULL);
} else
dfree(ustate, MDL);
ddns_cb, NULL, NULL);
return;
}
ddns_cb_free(ddns_cb, MDL);
return;
}
/* See if we should do a DNS update, and if so, do it. */
isc_result_t client_dns_update (struct client_state *client, int addp,
int ttl, struct iaddr *address)
isc_result_t
client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb)
{
struct data_string ddns_fwd_name, ddns_dhcid, client_identifier;
struct data_string client_identifier;
struct option_cache *oc;
int ignorep;
int result;
......@@ -3717,10 +3862,9 @@ isc_result_t client_dns_update (struct client_state *client, int addp,
return ISC_R_SUCCESS;
/* If no FQDN option was supplied, don't do the update. */
memset (&ddns_fwd_name, 0, sizeof ddns_fwd_name);
if (!(oc = lookup_option (&fqdn_universe, client -> sent_options,
FQDN_FQDN)) ||
!evaluate_option_cache (&ddns_fwd_name, (struct packet *)0,
!evaluate_option_cache (&ddns_cb->fwd_name, (struct packet *)0,
(struct lease *)0, client,
client -> sent_options,
(struct option_state *)0,
......@@ -3732,8 +3876,6 @@ isc_result_t client_dns_update (struct client_state *client, int addp,
* the client identifier, if there is one, or the interface's
* MAC address.
*/
memset (&ddns_dhcid, 0, sizeof ddns_dhcid);
result = 0;
memset(&client_identifier, 0, sizeof(client_identifier));
if (client->active_lease != NULL) {
......@@ -3747,7 +3889,7 @@ isc_result_t client_dns_update (struct client_state *client, int addp,
* field. We aren't using RFC4701 DHCID RR's yet,
* but this is as good a value as any.
*/
result = get_dhcid(&ddns_dhcid, 2,
result = get_dhcid(&ddns_cb->dhcid, 2,