2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-31 14:25:41 +00:00

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

dnslib.
This commit is contained in:
Shawn Routhier
2009-10-28 04:12:30 +00:00
parent 571c38b093
commit 98bf16077d
95 changed files with 4938 additions and 4043 deletions

View File

@@ -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;
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 (!client->config->do_forward_update)
return;
if ((client != NULL) &&
((client->active != NULL) ||
(client->active_lease != NULL)))
status = client_dns_update(client, ddns_cb);
ustate = dmalloc(sizeof(*ustate), MDL);
if (ustate != NULL) {
ustate->client = client;
ustate->address = *addr;
ustate->dns_update_timeout = 1;
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);
switch(eresult) {
case ISC_R_SUCCESS:
default:
/* Either we succeeded or broke in a bad way, clean up */
break;
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;
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,
client_identifier.data,
client_identifier.len);
data_string_forget(&client_identifier, MDL);
@@ -3760,47 +3902,93 @@ isc_result_t client_dns_update (struct client_state *client, int addp,
evaluate_option_cache(&client_identifier, NULL, NULL,
client, client->sent_options, NULL,
&global_scope, oc, MDL)) {
result = get_dhcid(&ddns_dhcid,
result = get_dhcid(&ddns_cb->dhcid,
DHO_DHCP_CLIENT_IDENTIFIER,
client_identifier.data,
client_identifier.len);
data_string_forget(&client_identifier, MDL);
} else
result = get_dhcid(&ddns_dhcid, 0,
result = get_dhcid(&ddns_cb->dhcid, 0,
client->interface->hw_address.hbuf,
client->interface->hw_address.hlen);
}
if (!result) {
data_string_forget(&ddns_fwd_name, MDL);
return ISC_R_SUCCESS;
}
/* Start the resolver, if necessary. */
if (!resolver_inited) {
minires_ninit (&resolver_state);
resolver_inited = 1;
resolver_state.retrans = 1;
resolver_state.retry = 1;
}
/*
* Perform updates.
*/
if (ddns_fwd_name.len && ddns_dhcid.len) {
if (addp)
rcode = ddns_update_fwd(&ddns_fwd_name, *address,
&ddns_dhcid, ttl, 1, 1);
else
rcode = ddns_remove_fwd(&ddns_fwd_name, *address,
&ddns_dhcid);
if (ddns_cb->fwd_name.len && ddns_cb->dhcid.len) {
rcode = ddns_modify_fwd(ddns_cb);
} else
rcode = ISC_R_FAILURE;
data_string_forget (&ddns_fwd_name, MDL);
data_string_forget (&ddns_dhcid, MDL);
/*
* A success from the modify routine means we are performing
* async processing, for which we use the timedout error message.
*/
if (rcode == ISC_R_SUCCESS) {
rcode = ISC_R_TIMEDOUT;
}
return rcode;
}
/*
* Schedule the first update. They will continue to retry occasionally
* until they no longer time out (or fail).
*/
void
dhclient_schedule_updates(struct client_state *client,
struct iaddr *addr,
int offset)
{
dhcp_ddns_cb_t *ddns_cb;
struct timeval tv;
if (!client->config->do_forward_update)
return;
/* cancel any outstanding ddns requests */
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->lease = (void *)client;
ddns_cb->address = *addr;
ddns_cb->timeout = 1;
/*
* 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.
*/
ddns_cb->ttl = 300;
ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN;
ddns_cb->cur_func = client_dns_update_action;
ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_INCLUDE_RRSET;
client->ddns_cb = ddns_cb;
tv.tv_sec = cur_time + offset;
tv.tv_usec = 0;
add_timeout(&tv, client_dns_update_timeout,
ddns_cb, NULL, NULL);
} else {
log_error("Unable to allocate dns update state for %s",
piaddr(*addr));
}
}
#endif
void
dhcpv4_client_assignments(void)
{