1*f407d929Schristos /* $NetBSD: ddns.c,v 1.4 2022/04/03 01:10:59 christos Exp $ */
26fb29d29Schristos
36fb29d29Schristos /* ddns.c
46fb29d29Schristos
56fb29d29Schristos Dynamic DNS updates. */
66fb29d29Schristos
76fb29d29Schristos /*
86fb29d29Schristos *
9*f407d929Schristos * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
106fb29d29Schristos * Copyright (c) 2000-2003 by Internet Software Consortium
116fb29d29Schristos *
126fb29d29Schristos * This Source Code Form is subject to the terms of the Mozilla Public
136fb29d29Schristos * License, v. 2.0. If a copy of the MPL was not distributed with this
146fb29d29Schristos * file, You can obtain one at http://mozilla.org/MPL/2.0/.
156fb29d29Schristos *
166fb29d29Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
176fb29d29Schristos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
186fb29d29Schristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
196fb29d29Schristos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
206fb29d29Schristos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
216fb29d29Schristos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
226fb29d29Schristos * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
236fb29d29Schristos *
246fb29d29Schristos * Internet Systems Consortium, Inc.
25*f407d929Schristos * PO Box 360
26*f407d929Schristos * Newmarket, NH 03857 USA
276fb29d29Schristos * <info@isc.org>
286fb29d29Schristos * https://www.isc.org/
296fb29d29Schristos *
306fb29d29Schristos * This software has been donated to Internet Systems Consortium
316fb29d29Schristos * by Damien Neil of Nominum, Inc.
326fb29d29Schristos *
336fb29d29Schristos * To learn more about Internet Systems Consortium, see
346fb29d29Schristos * ``https://www.isc.org/''.
356fb29d29Schristos */
366fb29d29Schristos
376fb29d29Schristos #include <sys/cdefs.h>
38*f407d929Schristos __RCSID("$NetBSD: ddns.c,v 1.4 2022/04/03 01:10:59 christos Exp $");
396fb29d29Schristos
406fb29d29Schristos #include "dhcpd.h"
416fb29d29Schristos #include <dns/result.h>
426fb29d29Schristos
436fb29d29Schristos char *ddns_standard_tag = "ddns-dhcid";
446fb29d29Schristos char *ddns_interim_tag = "ddns-txt";
456fb29d29Schristos
466fb29d29Schristos #ifdef NSUPDATE
476fb29d29Schristos
486fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
496fb29d29Schristos static char* dump_ddns_cb_func(void *func);
506fb29d29Schristos static char* dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb);
516fb29d29Schristos
526fb29d29Schristos extern struct enumeration_value ddns_styles_values[];
536fb29d29Schristos #endif
546fb29d29Schristos
556fb29d29Schristos static void ddns_fwd_srv_connector(struct lease *lease,
566fb29d29Schristos struct iasubopt *lease6,
576fb29d29Schristos struct binding_scope **inscope,
586fb29d29Schristos dhcp_ddns_cb_t *ddns_cb,
596fb29d29Schristos isc_result_t eresult);
606fb29d29Schristos
616fb29d29Schristos static void copy_conflict_flags(u_int16_t *target, u_int16_t source);
626fb29d29Schristos
636fb29d29Schristos static void ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult);
646fb29d29Schristos
656fb29d29Schristos /*
666fb29d29Schristos * ddns_cb_free() is part of common lib, while ia_* routines are known
676fb29d29Schristos * only in the server. Use this wrapper instead of ddns_cb_free() directly.
686fb29d29Schristos */
696fb29d29Schristos static void
destroy_ddns_cb(struct dhcp_ddns_cb * ddns_cb,char * file,int line)706fb29d29Schristos destroy_ddns_cb(struct dhcp_ddns_cb *ddns_cb, char* file, int line) {
716fb29d29Schristos if (!ddns_cb) {
726fb29d29Schristos return;
736fb29d29Schristos }
746fb29d29Schristos
756fb29d29Schristos if (ddns_cb->fixed6_ia) {
766fb29d29Schristos ia_dereference(&ddns_cb->fixed6_ia, MDL);
776fb29d29Schristos }
786fb29d29Schristos
796fb29d29Schristos ddns_cb_free(ddns_cb, file, line);
806fb29d29Schristos
816fb29d29Schristos }
826fb29d29Schristos
836fb29d29Schristos
846fb29d29Schristos /* DN: No way of checking that there is enough space in a data_string's
856fb29d29Schristos buffer. Be certain to allocate enough!
866fb29d29Schristos TL: This is why the expression evaluation code allocates a *new*
876fb29d29Schristos data_string. :') */
data_string_append(struct data_string * ds1,struct data_string * ds2)886fb29d29Schristos static void data_string_append (struct data_string *ds1,
896fb29d29Schristos struct data_string *ds2)
906fb29d29Schristos {
916fb29d29Schristos memcpy (ds1 -> buffer -> data + ds1 -> len,
926fb29d29Schristos ds2 -> data,
936fb29d29Schristos ds2 -> len);
946fb29d29Schristos ds1 -> len += ds2 -> len;
956fb29d29Schristos }
966fb29d29Schristos
976fb29d29Schristos
986fb29d29Schristos /* Determine what, if any, forward and reverse updates need to be
996fb29d29Schristos * performed, and carry them through.
1006fb29d29Schristos */
1016fb29d29Schristos int
ddns_updates(struct packet * packet,struct lease * lease,struct lease * old,struct iasubopt * lease6,struct iasubopt * old6,struct option_state * options)1026fb29d29Schristos ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
1036fb29d29Schristos struct iasubopt *lease6, struct iasubopt *old6,
1046fb29d29Schristos struct option_state *options)
1056fb29d29Schristos {
1066fb29d29Schristos unsigned long ddns_ttl = DEFAULT_DDNS_TTL;
1076fb29d29Schristos struct data_string ddns_hostname;
1086fb29d29Schristos struct data_string ddns_domainname;
1096fb29d29Schristos struct data_string old_ddns_fwd_name;
1106fb29d29Schristos struct data_string ddns_fwd_name;
1116fb29d29Schristos struct data_string ddns_dhcid;
1126fb29d29Schristos struct binding_scope **scope = NULL;
1136fb29d29Schristos struct data_string d1;
1146fb29d29Schristos struct option_cache *oc;
1156fb29d29Schristos int s1, s2;
1166fb29d29Schristos int result = 0;
1176fb29d29Schristos int server_updates_a = 1;
1186fb29d29Schristos struct buffer *bp = (struct buffer *)0;
1196fb29d29Schristos int ignorep = 0, client_ignorep = 0;
1206fb29d29Schristos int rev_name_len;
1216fb29d29Schristos int i;
1226fb29d29Schristos
1236fb29d29Schristos dhcp_ddns_cb_t *ddns_cb;
1246fb29d29Schristos int do_remove = 0;
1256fb29d29Schristos
1266fb29d29Schristos if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) &&
1276fb29d29Schristos (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM))
1286fb29d29Schristos return (0);
1296fb29d29Schristos
1306fb29d29Schristos /*
1316fb29d29Schristos * sigh, I want to cancel any previous udpates before we do anything
1326fb29d29Schristos * else but this means we need to deal with the lease vs lease6
1336fb29d29Schristos * question twice.
1346fb29d29Schristos * If there is a ddns request already outstanding cancel it.
1356fb29d29Schristos */
1366fb29d29Schristos
1376fb29d29Schristos if (lease != NULL) {
1386fb29d29Schristos if ((old != NULL) && (old->ddns_cb != NULL)) {
1396fb29d29Schristos ddns_cancel(old->ddns_cb, MDL);
1406fb29d29Schristos old->ddns_cb = NULL;
1416fb29d29Schristos }
1426fb29d29Schristos } else if (lease6 != NULL) {
1436fb29d29Schristos if ((old6 != NULL) && (old6->ddns_cb != NULL)) {
1446fb29d29Schristos ddns_cancel(old6->ddns_cb, MDL);
1456fb29d29Schristos old6->ddns_cb = NULL;
1466fb29d29Schristos }
1476fb29d29Schristos } else {
1486fb29d29Schristos log_fatal("Impossible condition at %s:%d.", MDL);
1496fb29d29Schristos /* Silence compiler warnings. */
1506fb29d29Schristos result = 0;
1516fb29d29Schristos return(0);
1526fb29d29Schristos }
1536fb29d29Schristos
1546fb29d29Schristos /* allocate our control block */
1556fb29d29Schristos ddns_cb = ddns_cb_alloc(MDL);
1566fb29d29Schristos if (ddns_cb == NULL) {
1576fb29d29Schristos return(0);
1586fb29d29Schristos }
1596fb29d29Schristos /*
1606fb29d29Schristos * Assume that we shall update both the A and ptr records and,
1616fb29d29Schristos * as this is an update, set the active flag
1626fb29d29Schristos */
1636fb29d29Schristos ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR |
1646fb29d29Schristos DDNS_ACTIVE_LEASE;
1656fb29d29Schristos
1666fb29d29Schristos /*
1676fb29d29Schristos * For v4 we flag static leases so we don't try
1686fb29d29Schristos * and manipulate the lease later. For v6 we don't
1696fb29d29Schristos * get static leases and don't need to flag them.
1706fb29d29Schristos */
1716fb29d29Schristos if (lease != NULL) {
1726fb29d29Schristos scope = &(lease->scope);
1736fb29d29Schristos ddns_cb->address = lease->ip_addr;
1746fb29d29Schristos if (lease->flags & STATIC_LEASE)
1756fb29d29Schristos ddns_cb->flags |= DDNS_STATIC_LEASE;
1766fb29d29Schristos } else if (lease6 != NULL) {
1776fb29d29Schristos scope = &(lease6->scope);
1786fb29d29Schristos memcpy(ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
1796fb29d29Schristos ddns_cb->address.len = 16;
1806fb29d29Schristos
1816fb29d29Schristos if (lease6->static_lease) {
1826fb29d29Schristos /* We add a reference to keep ia && iasubopt alive
1836fb29d29Schristos * since static v6s are retained anywhere */
1846fb29d29Schristos ia_reference(&ddns_cb->fixed6_ia, lease6->ia, MDL);
1856fb29d29Schristos ddns_cb->flags |= DDNS_STATIC_LEASE;
1866fb29d29Schristos }
1876fb29d29Schristos }
1886fb29d29Schristos
1896fb29d29Schristos memset (&d1, 0, sizeof(d1));
1906fb29d29Schristos memset (&ddns_hostname, 0, sizeof (ddns_hostname));
1916fb29d29Schristos memset (&ddns_domainname, 0, sizeof (ddns_domainname));
1926fb29d29Schristos memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
1936fb29d29Schristos memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
1946fb29d29Schristos memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
1956fb29d29Schristos
1966fb29d29Schristos /* If we are allowed to accept the client's update of its own A
1976fb29d29Schristos record, see if the client wants to update its own A record. */
1986fb29d29Schristos if (!(oc = lookup_option(&server_universe, options,
1996fb29d29Schristos SV_CLIENT_UPDATES)) ||
2006fb29d29Schristos evaluate_boolean_option_cache(&client_ignorep, packet, lease, NULL,
2016fb29d29Schristos packet->options, options, scope,
2026fb29d29Schristos oc, MDL)) {
2036fb29d29Schristos /* If there's no fqdn.no-client-update or if it's
2046fb29d29Schristos nonzero, don't try to use the client-supplied
2056fb29d29Schristos XXX */
2066fb29d29Schristos if (!(oc = lookup_option (&fqdn_universe, packet -> options,
2076fb29d29Schristos FQDN_SERVER_UPDATE)) ||
2086fb29d29Schristos evaluate_boolean_option_cache(&ignorep, packet, lease,
2096fb29d29Schristos NULL, packet->options,
2106fb29d29Schristos options, scope, oc, MDL))
2116fb29d29Schristos goto noclient;
2126fb29d29Schristos /* Win98 and Win2k will happily claim to be willing to
2136fb29d29Schristos update an unqualified domain name. */
2146fb29d29Schristos if (!(oc = lookup_option (&fqdn_universe, packet -> options,
2156fb29d29Schristos FQDN_DOMAINNAME)))
2166fb29d29Schristos goto noclient;
2176fb29d29Schristos if (!(oc = lookup_option (&fqdn_universe, packet -> options,
2186fb29d29Schristos FQDN_FQDN)) ||
2196fb29d29Schristos !evaluate_option_cache(&ddns_fwd_name, packet, lease,
2206fb29d29Schristos NULL, packet->options,
2216fb29d29Schristos options, scope, oc, MDL))
2226fb29d29Schristos goto noclient;
2236fb29d29Schristos ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
2246fb29d29Schristos server_updates_a = 0;
2256fb29d29Schristos goto client_updates;
2266fb29d29Schristos }
2276fb29d29Schristos noclient:
2286fb29d29Schristos /* If do-forward-updates is disabled, this basically means don't
2296fb29d29Schristos do an update unless the client is participating, so if we get
2306fb29d29Schristos here and do-forward-updates is disabled, we can stop. */
2316fb29d29Schristos if ((oc = lookup_option (&server_universe, options,
2326fb29d29Schristos SV_DO_FORWARD_UPDATES)) &&
2336fb29d29Schristos !evaluate_boolean_option_cache(&ignorep, packet, lease,
2346fb29d29Schristos NULL, packet->options,
2356fb29d29Schristos options, scope, oc, MDL)) {
2366fb29d29Schristos goto out;
2376fb29d29Schristos }
2386fb29d29Schristos
2396fb29d29Schristos /* If it's a static lease, then don't do the DNS update unless we're
2406fb29d29Schristos specifically configured to do so. If the client asked to do its
2416fb29d29Schristos own update and we allowed that, we don't do this test. */
2426fb29d29Schristos /* XXX: note that we cannot detect static DHCPv6 leases. */
2436fb29d29Schristos if ((lease != NULL) && (lease->flags & STATIC_LEASE)) {
2446fb29d29Schristos if (!(oc = lookup_option(&server_universe, options,
2456fb29d29Schristos SV_UPDATE_STATIC_LEASES)) ||
2466fb29d29Schristos !evaluate_boolean_option_cache(&ignorep, packet, lease,
2476fb29d29Schristos NULL, packet->options,
2486fb29d29Schristos options, scope, oc, MDL))
2496fb29d29Schristos goto out;
2506fb29d29Schristos }
2516fb29d29Schristos
2526fb29d29Schristos /*
2536fb29d29Schristos * Compute the name for the A record.
2546fb29d29Schristos */
2556fb29d29Schristos oc = lookup_option(&server_universe, options, SV_DDNS_HOST_NAME);
2566fb29d29Schristos if (oc)
2576fb29d29Schristos s1 = evaluate_option_cache(&ddns_hostname, packet, lease,
2586fb29d29Schristos NULL, packet->options,
2596fb29d29Schristos options, scope, oc, MDL);
2606fb29d29Schristos else
2616fb29d29Schristos s1 = 0;
2626fb29d29Schristos
2636fb29d29Schristos /* If we don't have a host name based on ddns-hostname then use
2646fb29d29Schristos * the host declaration name if there is one and use-host-decl-names
2656fb29d29Schristos * is turned on. */
2666fb29d29Schristos if ((s1 == 0) && (lease && lease->host && lease->host->name)) {
2676fb29d29Schristos oc = lookup_option(&server_universe, options,
2686fb29d29Schristos SV_USE_HOST_DECL_NAMES);
2696fb29d29Schristos if (evaluate_boolean_option_cache(NULL, packet, lease,
2706fb29d29Schristos NULL, packet->options,
2716fb29d29Schristos options, scope, oc, MDL)) {
2726fb29d29Schristos s1 = ((data_string_new(&ddns_hostname,
2736fb29d29Schristos lease->host->name,
2746fb29d29Schristos strlen(lease->host->name),
2756fb29d29Schristos MDL) && ddns_hostname.len > 0));
2766fb29d29Schristos }
2776fb29d29Schristos }
2786fb29d29Schristos
2796fb29d29Schristos oc = lookup_option(&server_universe, options, SV_DDNS_DOMAIN_NAME);
2806fb29d29Schristos if (oc)
2816fb29d29Schristos s2 = evaluate_option_cache(&ddns_domainname, packet, lease,
2826fb29d29Schristos NULL, packet->options,
2836fb29d29Schristos options, scope, oc, MDL);
2846fb29d29Schristos else
2856fb29d29Schristos s2 = 0;
2866fb29d29Schristos
2876fb29d29Schristos if (s1 && s2) {
2886fb29d29Schristos if (ddns_hostname.len + ddns_domainname.len > 253) {
2896fb29d29Schristos log_error ("ddns_update: host.domain name too long");
2906fb29d29Schristos
2916fb29d29Schristos goto out;
2926fb29d29Schristos }
2936fb29d29Schristos
2946fb29d29Schristos if (buffer_allocate (&ddns_fwd_name.buffer,
2956fb29d29Schristos ddns_hostname.len +
2966fb29d29Schristos ddns_domainname.len + 2, MDL)) {
2976fb29d29Schristos ddns_fwd_name.data = ddns_fwd_name.buffer->data;
2986fb29d29Schristos data_string_append (&ddns_fwd_name, &ddns_hostname);
2996fb29d29Schristos ddns_fwd_name.buffer->data[ddns_fwd_name.len] = '.';
3006fb29d29Schristos ddns_fwd_name.len++;
3016fb29d29Schristos data_string_append (&ddns_fwd_name, &ddns_domainname);
3026fb29d29Schristos ddns_fwd_name.buffer->data[ddns_fwd_name.len] ='\0';
3036fb29d29Schristos ddns_fwd_name.terminated = 1;
3046fb29d29Schristos }
3056fb29d29Schristos }
3066fb29d29Schristos client_updates:
3076fb29d29Schristos
3086fb29d29Schristos /* See if there's a name already stored on the lease. */
3096fb29d29Schristos if (find_bound_string(&old_ddns_fwd_name, *scope, "ddns-fwd-name")) {
3106fb29d29Schristos /* If there is, see if it's different. */
3116fb29d29Schristos if (old_ddns_fwd_name.len != ddns_fwd_name.len ||
3126fb29d29Schristos memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
3136fb29d29Schristos old_ddns_fwd_name.len)) {
3146fb29d29Schristos /*
3156fb29d29Schristos * If the name is different, mark the old record
3166fb29d29Schristos * for deletion and continue getting the new info.
3176fb29d29Schristos */
3186fb29d29Schristos do_remove = 1;
3196fb29d29Schristos goto in;
3206fb29d29Schristos }
3216fb29d29Schristos
3226fb29d29Schristos #if defined (DDNS_UPDATE_SLOW_TRANSITION)
3236fb29d29Schristos /*
3246fb29d29Schristos * If the slow transition code is enabled check to see
3256fb29d29Schristos * if the stored type (standard or interim doesn't
3266fb29d29Schristos * match the type currently in use. If it doesn't
3276fb29d29Schristos * try to remove and replace the DNS record
3286fb29d29Schristos */
3296fb29d29Schristos if (((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) &&
3306fb29d29Schristos find_bound_string(&ddns_dhcid, *scope, ddns_interim_tag)) ||
3316fb29d29Schristos ((ddns_update_style == DDNS_UPDATE_STYLE_INTERIM) &&
3326fb29d29Schristos find_bound_string(&ddns_dhcid, *scope, ddns_standard_tag))) {
3336fb29d29Schristos data_string_forget(&ddns_dhcid, MDL);
3346fb29d29Schristos do_remove = 1;
3356fb29d29Schristos goto in;
3366fb29d29Schristos }
3376fb29d29Schristos #endif
3386fb29d29Schristos
3396fb29d29Schristos /* See if the administrator wants to do updates even
3406fb29d29Schristos in cases where the update already appears to have been
3416fb29d29Schristos done. */
3426fb29d29Schristos if (!(oc = lookup_option(&server_universe, options,
3436fb29d29Schristos SV_UPDATE_OPTIMIZATION)) ||
3446fb29d29Schristos evaluate_boolean_option_cache(&ignorep, packet, lease,
3456fb29d29Schristos NULL, packet->options,
3466fb29d29Schristos options, scope, oc, MDL)) {
3476fb29d29Schristos result = 1;
3486fb29d29Schristos goto noerror;
3496fb29d29Schristos }
3506fb29d29Schristos /* If there's no "ddns-fwd-name" on the lease record, see if
3516fb29d29Schristos * there's a ddns-client-fqdn indicating a previous client
3526fb29d29Schristos * update (if it changes, we need to adjust the PTR).
3536fb29d29Schristos */
3546fb29d29Schristos } else if (find_bound_string(&old_ddns_fwd_name, *scope,
3556fb29d29Schristos "ddns-client-fqdn")) {
3566fb29d29Schristos /* If the name is not different, no need to update
3576fb29d29Schristos the PTR record. */
3586fb29d29Schristos if (old_ddns_fwd_name.len == ddns_fwd_name.len &&
3596fb29d29Schristos !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
3606fb29d29Schristos old_ddns_fwd_name.len) &&
3616fb29d29Schristos (!(oc = lookup_option(&server_universe, options,
3626fb29d29Schristos SV_UPDATE_OPTIMIZATION)) ||
3636fb29d29Schristos evaluate_boolean_option_cache(&ignorep, packet, lease,
3646fb29d29Schristos NULL, packet->options,
3656fb29d29Schristos options, scope, oc, MDL))) {
3666fb29d29Schristos goto noerror;
3676fb29d29Schristos }
3686fb29d29Schristos }
3696fb29d29Schristos in:
3706fb29d29Schristos
3716fb29d29Schristos /* If we don't have a name that the client has been assigned, we
3726fb29d29Schristos can just skip all this. */
3736fb29d29Schristos
3746fb29d29Schristos if ((!ddns_fwd_name.len) || (ddns_fwd_name.len > 255)) {
3756fb29d29Schristos if (ddns_fwd_name.len > 255) {
3766fb29d29Schristos log_error ("client provided fqdn: too long");
3776fb29d29Schristos }
3786fb29d29Schristos
3796fb29d29Schristos /* If desired do the removals */
3806fb29d29Schristos if (do_remove != 0) {
3816fb29d29Schristos (void) ddns_removals(lease, lease6, NULL, ISC_TRUE);
3826fb29d29Schristos }
3836fb29d29Schristos goto out;
3846fb29d29Schristos }
3856fb29d29Schristos
3866fb29d29Schristos /*
3876fb29d29Schristos * Compute the RR TTL.
3886fb29d29Schristos *
3896fb29d29Schristos * We have two ways of computing the TTL.
3906fb29d29Schristos * The old behavior was to allow for the customer to set up
3916fb29d29Schristos * the option or to default things. For v4 this was 1/2
3926fb29d29Schristos * of the lease time, for v6 this was DEFAULT_DDNS_TTL.
3936fb29d29Schristos * The new behavior continues to allow the customer to set
3946fb29d29Schristos * up an option but the defaults are a little different.
3956fb29d29Schristos * We now use 1/2 of the (preferred) lease time for both
3966fb29d29Schristos * v4 and v6 and cap them at a maximum value.
3976fb29d29Schristos * If the customer chooses to use an experession that references
3986fb29d29Schristos * part of the lease the v6 value will be the default as there
3996fb29d29Schristos * isn't a lease available for v6.
4006fb29d29Schristos */
4016fb29d29Schristos
4026fb29d29Schristos ddns_ttl = DEFAULT_DDNS_TTL;
4036fb29d29Schristos if (lease != NULL) {
4046fb29d29Schristos if (lease->ends <= cur_time) {
4056fb29d29Schristos ddns_ttl = 0;
4066fb29d29Schristos } else {
4076fb29d29Schristos ddns_ttl = (lease->ends - cur_time)/2;
4086fb29d29Schristos }
4096fb29d29Schristos }
4106fb29d29Schristos #ifndef USE_OLD_DDNS_TTL
4116fb29d29Schristos else if (lease6 != NULL) {
4126fb29d29Schristos ddns_ttl = lease6->prefer/2;
4136fb29d29Schristos }
4146fb29d29Schristos
4156fb29d29Schristos if (ddns_ttl > MAX_DEFAULT_DDNS_TTL) {
4166fb29d29Schristos ddns_ttl = MAX_DEFAULT_DDNS_TTL;
4176fb29d29Schristos }
4186fb29d29Schristos #endif
4196fb29d29Schristos
4206fb29d29Schristos if ((oc = lookup_option(&server_universe, options, SV_DDNS_TTL))) {
4216fb29d29Schristos if (evaluate_option_cache(&d1, packet, lease, NULL,
4226fb29d29Schristos packet->options, options,
4236fb29d29Schristos scope, oc, MDL)) {
4246fb29d29Schristos if (d1.len == sizeof (u_int32_t))
4256fb29d29Schristos ddns_ttl = getULong (d1.data);
4266fb29d29Schristos data_string_forget (&d1, MDL);
4276fb29d29Schristos }
4286fb29d29Schristos }
4296fb29d29Schristos
4306fb29d29Schristos ddns_cb->ttl = ddns_ttl;
4316fb29d29Schristos
4326fb29d29Schristos /*
4336fb29d29Schristos * Compute the reverse IP name, starting with the domain name.
4346fb29d29Schristos */
4356fb29d29Schristos oc = lookup_option(&server_universe, options, SV_DDNS_REV_DOMAIN_NAME);
4366fb29d29Schristos if (oc)
4376fb29d29Schristos s1 = evaluate_option_cache(&d1, packet, lease, NULL,
4386fb29d29Schristos packet->options, options,
4396fb29d29Schristos scope, oc, MDL);
4406fb29d29Schristos else
4416fb29d29Schristos s1 = 0;
4426fb29d29Schristos
4436fb29d29Schristos /*
4446fb29d29Schristos * Figure out the length of the part of the name that depends
4456fb29d29Schristos * on the address.
4466fb29d29Schristos */
4476fb29d29Schristos if (ddns_cb->address.len == 4) {
4486fb29d29Schristos char buf[17];
4496fb29d29Schristos /* XXX: WOW this is gross. */
4506fb29d29Schristos rev_name_len = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.",
4516fb29d29Schristos ddns_cb->address.iabuf[3] & 0xff,
4526fb29d29Schristos ddns_cb->address.iabuf[2] & 0xff,
4536fb29d29Schristos ddns_cb->address.iabuf[1] & 0xff,
4546fb29d29Schristos ddns_cb->address.iabuf[0] & 0xff) + 1;
4556fb29d29Schristos
4566fb29d29Schristos if (s1) {
4576fb29d29Schristos rev_name_len += d1.len;
4586fb29d29Schristos
4596fb29d29Schristos if (rev_name_len > 255) {
4606fb29d29Schristos log_error("ddns_update: Calculated rev domain "
4616fb29d29Schristos "name too long.");
4626fb29d29Schristos s1 = 0;
4636fb29d29Schristos data_string_forget(&d1, MDL);
4646fb29d29Schristos }
4656fb29d29Schristos }
4666fb29d29Schristos } else if (ddns_cb->address.len == 16) {
4676fb29d29Schristos /*
4686fb29d29Schristos * IPv6 reverse names are always the same length, with
4696fb29d29Schristos * 32 hex characters separated by dots.
4706fb29d29Schristos */
4716fb29d29Schristos rev_name_len = sizeof("0.1.2.3.4.5.6.7."
4726fb29d29Schristos "8.9.a.b.c.d.e.f."
4736fb29d29Schristos "0.1.2.3.4.5.6.7."
4746fb29d29Schristos "8.9.a.b.c.d.e.f."
4756fb29d29Schristos "ip6.arpa.");
4766fb29d29Schristos
4776fb29d29Schristos /* Set s1 to make sure we gate into updates. */
4786fb29d29Schristos s1 = 1;
4796fb29d29Schristos } else {
4806fb29d29Schristos log_fatal("invalid address length %d", ddns_cb->address.len);
4816fb29d29Schristos /* Silence compiler warnings. */
4826fb29d29Schristos return 0;
4836fb29d29Schristos }
4846fb29d29Schristos
4856fb29d29Schristos /* See if we are configured NOT to do reverse ptr updates */
4866fb29d29Schristos if ((oc = lookup_option(&server_universe, options,
4876fb29d29Schristos SV_DO_REVERSE_UPDATES)) &&
4886fb29d29Schristos !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
4896fb29d29Schristos packet->options, options,
4906fb29d29Schristos scope, oc, MDL)) {
4916fb29d29Schristos ddns_cb->flags &= ~DDNS_UPDATE_PTR;
4926fb29d29Schristos }
4936fb29d29Schristos
4946fb29d29Schristos if (s1) {
4956fb29d29Schristos if (buffer_allocate(&ddns_cb->rev_name.buffer,
4966fb29d29Schristos rev_name_len, MDL)) {
4976fb29d29Schristos struct data_string *rname = &ddns_cb->rev_name;
4986fb29d29Schristos rname->data = rname->buffer->data;
4996fb29d29Schristos
5006fb29d29Schristos if (ddns_cb->address.len == 4) {
5016fb29d29Schristos rname->len =
5026fb29d29Schristos sprintf((char *)rname->buffer->data,
5036fb29d29Schristos "%u.%u.%u.%u.",
5046fb29d29Schristos ddns_cb->address.iabuf[3] & 0xff,
5056fb29d29Schristos ddns_cb->address.iabuf[2] & 0xff,
5066fb29d29Schristos ddns_cb->address.iabuf[1] & 0xff,
5076fb29d29Schristos ddns_cb->address.iabuf[0] & 0xff);
5086fb29d29Schristos
5096fb29d29Schristos /*
5106fb29d29Schristos * d1.data may be opaque, garbage bytes, from
5116fb29d29Schristos * user (mis)configuration.
5126fb29d29Schristos */
5136fb29d29Schristos data_string_append(rname, &d1);
5146fb29d29Schristos rname->buffer->data[rname->len] = '\0';
5156fb29d29Schristos } else if (ddns_cb->address.len == 16) {
5166fb29d29Schristos char *p = (char *)&rname->buffer->data;
5176fb29d29Schristos unsigned char *a = ddns_cb->address.iabuf + 15;
5186fb29d29Schristos for (i=0; i<16; i++) {
5196fb29d29Schristos sprintf(p, "%x.%x.",
5206fb29d29Schristos (*a & 0xF), ((*a >> 4) & 0xF));
5216fb29d29Schristos p += 4;
5226fb29d29Schristos a -= 1;
5236fb29d29Schristos }
5246fb29d29Schristos strcat(p, "ip6.arpa.");
5256fb29d29Schristos rname->len = strlen((const char *)rname->data);
5266fb29d29Schristos }
5276fb29d29Schristos
5286fb29d29Schristos rname->terminated = 1;
5296fb29d29Schristos }
5306fb29d29Schristos
5316fb29d29Schristos if (d1.data != NULL)
5326fb29d29Schristos data_string_forget(&d1, MDL);
5336fb29d29Schristos }
5346fb29d29Schristos
5356fb29d29Schristos /*
5366fb29d29Schristos * copy the string now so we can pass it to the dhcid routines
5376fb29d29Schristos * via the ddns_cb pointer
5386fb29d29Schristos */
5396fb29d29Schristos data_string_copy(&ddns_cb->fwd_name, &ddns_fwd_name, MDL);
5406fb29d29Schristos
5416fb29d29Schristos /*
5426fb29d29Schristos * If we are updating the A record, compute the DHCID value.
5436fb29d29Schristos * We have two options for computing the DHCID value, the older
5446fb29d29Schristos * interim version and the newer standard version. The interim
5456fb29d29Schristos * has some issues but is left as is to avoid compatibility issues.
5466fb29d29Schristos *
5476fb29d29Schristos * We select the type of DHCID to construct and the information to
5486fb29d29Schristos * use for the digest based on 4701 section 3.3
5496fb29d29Schristos */
5506fb29d29Schristos if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
5516fb29d29Schristos int ddns_type;
5526fb29d29Schristos int ddns_len;
5536fb29d29Schristos if (ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) {
5546fb29d29Schristos /* The standard style */
5556fb29d29Schristos ddns_cb->lease_tag = ddns_standard_tag;
5566fb29d29Schristos ddns_cb->dhcid_class = dns_rdatatype_dhcid;
5576fb29d29Schristos ddns_cb->other_dhcid_class = dns_rdatatype_txt;
5586fb29d29Schristos ddns_type = 1;
5596fb29d29Schristos ddns_len = 4;
5606fb29d29Schristos } else {
5616fb29d29Schristos /* The older interim style */
5626fb29d29Schristos ddns_cb->lease_tag = ddns_interim_tag;
5636fb29d29Schristos ddns_cb->dhcid_class = dns_rdatatype_txt;
5646fb29d29Schristos ddns_cb->other_dhcid_class = dns_rdatatype_dhcid;
5656fb29d29Schristos /* for backwards compatibility */
5666fb29d29Schristos ddns_type = DHO_DHCP_CLIENT_IDENTIFIER;
5676fb29d29Schristos /* IAID incorrectly included */
5686fb29d29Schristos ddns_len = 0;
5696fb29d29Schristos }
5706fb29d29Schristos
5716fb29d29Schristos
5726fb29d29Schristos if (lease6 != NULL) {
5736fb29d29Schristos if (lease6->ia->iaid_duid.len < ddns_len)
5746fb29d29Schristos goto badfqdn;
5756fb29d29Schristos result = get_dhcid(ddns_cb, 2,
5766fb29d29Schristos lease6->ia->iaid_duid.data + ddns_len,
5776fb29d29Schristos lease6->ia->iaid_duid.len - ddns_len);
5786fb29d29Schristos } else if ((lease != NULL) &&
5796fb29d29Schristos (lease->uid != NULL) &&
5806fb29d29Schristos (lease->uid_len != 0)) {
5816fb29d29Schristos /* If this is standard check for an RFC 4361
5826fb29d29Schristos * compliant client identifier
5836fb29d29Schristos */
5846fb29d29Schristos if ((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) &&
5856fb29d29Schristos (lease->uid[0] == 255)) {
5866fb29d29Schristos if (lease->uid_len < 5)
5876fb29d29Schristos goto badfqdn;
5886fb29d29Schristos result = get_dhcid(ddns_cb, 2,
5896fb29d29Schristos lease->uid + 5,
5906fb29d29Schristos lease->uid_len - 5);
5916fb29d29Schristos } else {
5926fb29d29Schristos result = get_dhcid(ddns_cb, ddns_type,
5936fb29d29Schristos lease->uid,
5946fb29d29Schristos lease->uid_len);
5956fb29d29Schristos }
5966fb29d29Schristos } else if (lease != NULL)
5976fb29d29Schristos result = get_dhcid(ddns_cb, 0,
5986fb29d29Schristos lease->hardware_addr.hbuf,
5996fb29d29Schristos lease->hardware_addr.hlen);
6006fb29d29Schristos else
6016fb29d29Schristos log_fatal("Impossible condition at %s:%d.", MDL);
6026fb29d29Schristos
6036fb29d29Schristos if (!result)
6046fb29d29Schristos goto badfqdn;
6056fb29d29Schristos }
6066fb29d29Schristos
6076fb29d29Schristos /*
6086fb29d29Schristos * Perform updates.
6096fb29d29Schristos */
6106fb29d29Schristos
6116fb29d29Schristos if (ddns_cb->flags & DDNS_UPDATE_ADDR) {
6126fb29d29Schristos copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask);
6136fb29d29Schristos }
6146fb29d29Schristos
6156fb29d29Schristos /*
6166fb29d29Schristos * Previously if we failed during the removal operations
6176fb29d29Schristos * we skipped the fqdn option processing. I'm not sure
6186fb29d29Schristos * if we want to continue with that if we fail before sending
6196fb29d29Schristos * the ddns messages. Currently we don't.
6206fb29d29Schristos */
6216fb29d29Schristos if (do_remove) {
6226fb29d29Schristos /*
6236fb29d29Schristos * We should log a more specific error closer to the actual
6246fb29d29Schristos * error if we want one. ddns_removal failure not logged here.
6256fb29d29Schristos */
6266fb29d29Schristos (void) ddns_removals(lease, lease6, ddns_cb, ISC_TRUE);
6276fb29d29Schristos }
6286fb29d29Schristos else {
6296fb29d29Schristos ddns_fwd_srv_connector(lease, lease6, scope, ddns_cb,
6306fb29d29Schristos ISC_R_SUCCESS);
6316fb29d29Schristos }
6326fb29d29Schristos ddns_cb = NULL;
6336fb29d29Schristos
6346fb29d29Schristos noerror:
6356fb29d29Schristos /*
6366fb29d29Schristos * If fqdn-reply option is disabled in dhcpd.conf, then don't
6376fb29d29Schristos * send the client an FQDN option at all, even if one was requested.
6386fb29d29Schristos * (WinXP clients allegedly misbehave if the option is present,
6396fb29d29Schristos * refusing to handle PTR updates themselves).
6406fb29d29Schristos */
6416fb29d29Schristos if ((oc = lookup_option (&server_universe, options, SV_FQDN_REPLY)) &&
6426fb29d29Schristos !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
6436fb29d29Schristos packet->options, options,
6446fb29d29Schristos scope, oc, MDL)) {
6456fb29d29Schristos goto badfqdn;
6466fb29d29Schristos
6476fb29d29Schristos /* If we're ignoring client updates, then we tell a sort of 'white
6486fb29d29Schristos * lie'. We've already updated the name the server wants (per the
6496fb29d29Schristos * config written by the server admin). Now let the client do as
6506fb29d29Schristos * it pleases with the name they supplied (if any).
6516fb29d29Schristos *
6526fb29d29Schristos * We only form an FQDN option this way if the client supplied an
6536fb29d29Schristos * FQDN option that had FQDN_SERVER_UPDATE set false.
6546fb29d29Schristos */
6556fb29d29Schristos } else if (client_ignorep &&
6566fb29d29Schristos (oc = lookup_option(&fqdn_universe, packet->options,
6576fb29d29Schristos FQDN_SERVER_UPDATE)) &&
6586fb29d29Schristos !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
6596fb29d29Schristos packet->options, options,
6606fb29d29Schristos scope, oc, MDL)) {
6616fb29d29Schristos oc = lookup_option(&fqdn_universe, packet->options, FQDN_FQDN);
6626fb29d29Schristos if (oc && evaluate_option_cache(&d1, packet, lease, NULL,
6636fb29d29Schristos packet->options, options,
6646fb29d29Schristos scope, oc, MDL)) {
6656fb29d29Schristos if (d1.len == 0 ||
6666fb29d29Schristos !buffer_allocate(&bp, d1.len + 5, MDL))
6676fb29d29Schristos goto badfqdn;
6686fb29d29Schristos
6696fb29d29Schristos /* Server pretends it is not updating. */
6706fb29d29Schristos bp->data[0] = 0;
6716fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
6726fb29d29Schristos bp, &bp->data[0], 1,
6736fb29d29Schristos FQDN_SERVER_UPDATE, 0))
6746fb29d29Schristos goto badfqdn;
6756fb29d29Schristos
6766fb29d29Schristos /* Client is encouraged to update. */
6776fb29d29Schristos bp->data[1] = 0;
6786fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
6796fb29d29Schristos bp, &bp->data[1], 1,
6806fb29d29Schristos FQDN_NO_CLIENT_UPDATE, 0))
6816fb29d29Schristos goto badfqdn;
6826fb29d29Schristos
6836fb29d29Schristos /* Use the encoding of client's FQDN option. */
6846fb29d29Schristos oc = lookup_option(&fqdn_universe, packet->options,
6856fb29d29Schristos FQDN_ENCODED);
6866fb29d29Schristos if (oc &&
6876fb29d29Schristos evaluate_boolean_option_cache(&ignorep, packet,
6886fb29d29Schristos lease, NULL,
6896fb29d29Schristos packet->options,
6906fb29d29Schristos options, scope,
6916fb29d29Schristos oc, MDL))
6926fb29d29Schristos bp->data[2] = 1; /* FQDN is encoded. */
6936fb29d29Schristos else
6946fb29d29Schristos bp->data[2] = 0; /* FQDN is not encoded. */
6956fb29d29Schristos
6966fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
6976fb29d29Schristos bp, &bp->data[2], 1,
6986fb29d29Schristos FQDN_ENCODED, 0))
6996fb29d29Schristos goto badfqdn;
7006fb29d29Schristos
7016fb29d29Schristos /* Current FQDN drafts indicate 255 is mandatory. */
7026fb29d29Schristos bp->data[3] = 255;
7036fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
7046fb29d29Schristos bp, &bp->data[3], 1,
7056fb29d29Schristos FQDN_RCODE1, 0))
7066fb29d29Schristos goto badfqdn;
7076fb29d29Schristos
7086fb29d29Schristos bp->data[4] = 255;
7096fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
7106fb29d29Schristos bp, &bp->data[4], 1,
7116fb29d29Schristos FQDN_RCODE2, 0))
7126fb29d29Schristos goto badfqdn;
7136fb29d29Schristos
7146fb29d29Schristos /* Copy in the FQDN supplied by the client. Note well
7156fb29d29Schristos * that the format of this option in the cache is going
7166fb29d29Schristos * to be in text format. If the fqdn supplied by the
7176fb29d29Schristos * client is encoded, it is decoded into the option
7186fb29d29Schristos * cache when parsed out of the packet. It will be
7196fb29d29Schristos * re-encoded when the option is assembled to be
7206fb29d29Schristos * transmitted if the client elects that encoding.
7216fb29d29Schristos */
7226fb29d29Schristos memcpy(&bp->data[5], d1.data, d1.len);
7236fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
7246fb29d29Schristos bp, &bp->data[5], d1.len,
7256fb29d29Schristos FQDN_FQDN, 0))
7266fb29d29Schristos goto badfqdn;
7276fb29d29Schristos
7286fb29d29Schristos data_string_forget(&d1, MDL);
7296fb29d29Schristos }
7306fb29d29Schristos /* Set up the outgoing FQDN option if there was an incoming
7316fb29d29Schristos * FQDN option. If there's a valid FQDN option, there MUST
7326fb29d29Schristos * be an FQDN_SERVER_UPDATES suboption, it's part of the fixed
7336fb29d29Schristos * length head of the option contents, so we test the latter
7346fb29d29Schristos * to detect the presence of the former.
7356fb29d29Schristos */
7366fb29d29Schristos } else if ((oc = lookup_option(&fqdn_universe, packet->options,
7376fb29d29Schristos FQDN_ENCODED)) &&
7386fb29d29Schristos buffer_allocate(&bp, ddns_fwd_name.len + 5, MDL)) {
7396fb29d29Schristos bp -> data [0] = server_updates_a;
7406fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
7416fb29d29Schristos bp, &bp->data [0], 1,
7426fb29d29Schristos FQDN_SERVER_UPDATE, 0))
7436fb29d29Schristos goto badfqdn;
7446fb29d29Schristos bp -> data [1] = server_updates_a;
7456fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
7466fb29d29Schristos bp, &bp->data [1], 1,
7476fb29d29Schristos FQDN_NO_CLIENT_UPDATE, 0))
7486fb29d29Schristos goto badfqdn;
7496fb29d29Schristos
7506fb29d29Schristos /* Do the same encoding the client did. */
7516fb29d29Schristos if (evaluate_boolean_option_cache(&ignorep, packet, lease,
7526fb29d29Schristos NULL, packet->options,
7536fb29d29Schristos options, scope, oc, MDL))
7546fb29d29Schristos bp -> data [2] = 1;
7556fb29d29Schristos else
7566fb29d29Schristos bp -> data [2] = 0;
7576fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
7586fb29d29Schristos bp, &bp->data [2], 1,
7596fb29d29Schristos FQDN_ENCODED, 0))
7606fb29d29Schristos goto badfqdn;
7616fb29d29Schristos bp -> data [3] = 255;//isc_rcode_to_ns (rcode1);
7626fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
7636fb29d29Schristos bp, &bp->data [3], 1,
7646fb29d29Schristos FQDN_RCODE1, 0))
7656fb29d29Schristos goto badfqdn;
7666fb29d29Schristos bp -> data [4] = 255;//isc_rcode_to_ns (rcode2);
7676fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
7686fb29d29Schristos bp, &bp->data [4], 1,
7696fb29d29Schristos FQDN_RCODE2, 0))
7706fb29d29Schristos goto badfqdn;
7716fb29d29Schristos if (ddns_fwd_name.len) {
7726fb29d29Schristos memcpy (&bp -> data [5],
7736fb29d29Schristos ddns_fwd_name.data, ddns_fwd_name.len);
7746fb29d29Schristos if (!save_option_buffer(&fqdn_universe, options,
7756fb29d29Schristos bp, &bp->data [5],
7766fb29d29Schristos ddns_fwd_name.len,
7776fb29d29Schristos FQDN_FQDN, 0))
7786fb29d29Schristos goto badfqdn;
7796fb29d29Schristos }
7806fb29d29Schristos }
7816fb29d29Schristos
7826fb29d29Schristos badfqdn:
7836fb29d29Schristos out:
7846fb29d29Schristos /*
7856fb29d29Schristos * Final cleanup.
7866fb29d29Schristos */
7876fb29d29Schristos if (ddns_cb != NULL) {
7886fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
7896fb29d29Schristos }
7906fb29d29Schristos
7916fb29d29Schristos data_string_forget(&d1, MDL);
7926fb29d29Schristos data_string_forget(&ddns_hostname, MDL);
7936fb29d29Schristos data_string_forget(&ddns_domainname, MDL);
7946fb29d29Schristos data_string_forget(&old_ddns_fwd_name, MDL);
7956fb29d29Schristos data_string_forget(&ddns_fwd_name, MDL);
7966fb29d29Schristos if (bp)
7976fb29d29Schristos buffer_dereference(&bp, MDL);
7986fb29d29Schristos
7996fb29d29Schristos return result;
8006fb29d29Schristos }
8016fb29d29Schristos
8026fb29d29Schristos /*%<
8036fb29d29Schristos * Utility function to update text strings within a lease.
8046fb29d29Schristos *
8056fb29d29Schristos * The first issue is to find the proper scope. Sometimes we shall be
8066fb29d29Schristos * called with a pointer to the scope in other cases we need to find
8076fb29d29Schristos * the proper lease and then get the scope. Once we have the scope we update
8086fb29d29Schristos * the proper strings, as indicated by the state value in the control block.
8096fb29d29Schristos * Lastly, if we needed to find the scope we write it out, if we used a
8106fb29d29Schristos * scope that was passed as an argument we don't write it, assuming that
8116fb29d29Schristos * our caller (or his ...) will do the write.
8126fb29d29Schristos *
8136fb29d29Schristos *\li ddns_cb - the control block for the DDNS request
8146fb29d29Schristos *
8156fb29d29Schristos *\li inscope - a pointer to the scope to update. This may be NULL
8166fb29d29Schristos * in which case we use the control block to find the lease and
8176fb29d29Schristos * then the scope.
8186fb29d29Schristos *
8196fb29d29Schristos * Returns
8206fb29d29Schristos *\li ISC_R_SUCCESS
8216fb29d29Schristos *
8226fb29d29Schristos *\li ISC_R_FAILURE - The routine was unable to find an expected scope.
8236fb29d29Schristos * In some cases (static and inactive leases) we don't expect a scope
8246fb29d29Schristos * and return success.
8256fb29d29Schristos */
8266fb29d29Schristos
827ce893c7eSchristos static isc_result_t
ddns_update_lease_text(dhcp_ddns_cb_t * ddns_cb,struct binding_scope ** inscope)8286fb29d29Schristos ddns_update_lease_text(dhcp_ddns_cb_t *ddns_cb,
8296fb29d29Schristos struct binding_scope **inscope)
8306fb29d29Schristos {
8316fb29d29Schristos struct binding_scope **scope = NULL;
8326fb29d29Schristos struct lease *lease = NULL;
8336fb29d29Schristos struct iasubopt *lease6 = NULL;
8346fb29d29Schristos struct ipv6_pool *pool = NULL;
8356fb29d29Schristos struct in6_addr addr;
8366fb29d29Schristos struct data_string lease_dhcid;
8376fb29d29Schristos
8386fb29d29Schristos /*
8396fb29d29Schristos * If the lease was static (for a fixed address)
8406fb29d29Schristos * we don't need to do any work.
8416fb29d29Schristos */
8426fb29d29Schristos if (ddns_cb->flags & DDNS_STATIC_LEASE)
8436fb29d29Schristos return (ISC_R_SUCCESS);
8446fb29d29Schristos
8456fb29d29Schristos /*
8466fb29d29Schristos * If we are processing an expired or released v6 lease
8476fb29d29Schristos * or some types of v4 leases we don't actually have a
8486fb29d29Schristos * scope to update
8496fb29d29Schristos */
8506fb29d29Schristos if ((ddns_cb->flags & DDNS_ACTIVE_LEASE) == 0)
8516fb29d29Schristos return (ISC_R_SUCCESS);
8526fb29d29Schristos
8536fb29d29Schristos if (inscope != NULL) {
8546fb29d29Schristos scope = inscope;
8556fb29d29Schristos } else if (ddns_cb->address.len == 4) {
8566fb29d29Schristos if (find_lease_by_ip_addr(&lease, ddns_cb->address, MDL) != 0){
8576fb29d29Schristos scope = &(lease->scope);
8586fb29d29Schristos }
8596fb29d29Schristos } else if (ddns_cb->address.len == 16) {
8606fb29d29Schristos memcpy(&addr, &ddns_cb->address.iabuf, 16);
8616fb29d29Schristos if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) ==
8626fb29d29Schristos ISC_R_SUCCESS) ||
8636fb29d29Schristos (find_ipv6_pool(&pool, D6O_IA_NA, &addr) ==
8646fb29d29Schristos ISC_R_SUCCESS)) {
8656fb29d29Schristos if (iasubopt_hash_lookup(&lease6, pool->leases,
8666fb29d29Schristos &addr, 16, MDL)) {
8676fb29d29Schristos scope = &(lease6->scope);
8686fb29d29Schristos }
8696fb29d29Schristos ipv6_pool_dereference(&pool, MDL);
8706fb29d29Schristos }
8716fb29d29Schristos } else {
8726fb29d29Schristos log_fatal("Impossible condition at %s:%d.", MDL);
8736fb29d29Schristos }
8746fb29d29Schristos
8756fb29d29Schristos if (scope == NULL) {
8766fb29d29Schristos /* If necessary get rid of the lease */
8776fb29d29Schristos if (lease) {
8786fb29d29Schristos lease_dereference(&lease, MDL);
8796fb29d29Schristos }
8806fb29d29Schristos else if (lease6) {
8816fb29d29Schristos iasubopt_dereference(&lease6, MDL);
8826fb29d29Schristos }
8836fb29d29Schristos
8846fb29d29Schristos return(ISC_R_FAILURE);
8856fb29d29Schristos }
8866fb29d29Schristos
8876fb29d29Schristos /* We now have a scope and can proceed to update it */
8886fb29d29Schristos switch(ddns_cb->state) {
8896fb29d29Schristos case DDNS_STATE_REM_PTR:
8906fb29d29Schristos unset(*scope, "ddns-rev-name");
8916fb29d29Schristos if ((ddns_cb->flags & DDNS_CLIENT_DID_UPDATE) != 0) {
8926fb29d29Schristos unset(*scope, "ddns-client-fqdn");
8936fb29d29Schristos }
8946fb29d29Schristos break;
8956fb29d29Schristos
8966fb29d29Schristos case DDNS_STATE_ADD_PTR:
8976fb29d29Schristos case DDNS_STATE_CLEANUP:
8986fb29d29Schristos bind_ds_value(scope, "ddns-rev-name", &ddns_cb->rev_name);
8996fb29d29Schristos if ((ddns_cb->flags & DDNS_UPDATE_ADDR) == 0) {
9006fb29d29Schristos bind_ds_value(scope, "ddns-client-fqdn",
9016fb29d29Schristos &ddns_cb->fwd_name);
9026fb29d29Schristos }
9036fb29d29Schristos break;
9046fb29d29Schristos
9056fb29d29Schristos case DDNS_STATE_ADD_FW_YXDHCID:
9066fb29d29Schristos case DDNS_STATE_ADD_FW_NXDOMAIN:
9076fb29d29Schristos case DDNS_STATE_DSMM_FW_ADD3:
9086fb29d29Schristos bind_ds_value(scope, "ddns-fwd-name", &ddns_cb->fwd_name);
9096fb29d29Schristos
9106fb29d29Schristos if (ddns_cb->lease_tag == ddns_standard_tag) {
9116fb29d29Schristos bind_ds_value(scope, ddns_standard_tag,
9126fb29d29Schristos &ddns_cb->dhcid);
9136fb29d29Schristos } else {
9146fb29d29Schristos /* convert from dns version to lease version of dhcid */
9156fb29d29Schristos memset(&lease_dhcid, 0, sizeof(lease_dhcid));
9166fb29d29Schristos dhcid_tolease(&ddns_cb->dhcid, &lease_dhcid);
9176fb29d29Schristos bind_ds_value(scope, ddns_interim_tag, &lease_dhcid);
9186fb29d29Schristos data_string_forget(&lease_dhcid, MDL);
9196fb29d29Schristos }
9206fb29d29Schristos break;
9216fb29d29Schristos
9226fb29d29Schristos case DDNS_STATE_REM_FW_NXRR:
9236fb29d29Schristos case DDNS_STATE_REM_FW_YXDHCID:
9246fb29d29Schristos case DDNS_STATE_REM_FW_DSMM_OTHER:
9256fb29d29Schristos unset(*scope, "ddns-fwd-name");
9266fb29d29Schristos unset(*scope, ddns_cb->lease_tag);
9276fb29d29Schristos break;
9286fb29d29Schristos }
9296fb29d29Schristos
9306fb29d29Schristos /* If necessary write it out and get rid of the lease */
9316fb29d29Schristos if (lease) {
9326fb29d29Schristos write_lease(lease);
9336fb29d29Schristos lease_dereference(&lease, MDL);
9346fb29d29Schristos } else if (lease6) {
9356fb29d29Schristos write_ia(lease6->ia);
9366fb29d29Schristos iasubopt_dereference(&lease6, MDL);
9376fb29d29Schristos }
9386fb29d29Schristos
9396fb29d29Schristos return(ISC_R_SUCCESS);
9406fb29d29Schristos }
9416fb29d29Schristos
942ce893c7eSchristos #ifdef notdef
9436fb29d29Schristos /*
9446fb29d29Schristos * This function should be called when update_lease_ptr function fails.
9456fb29d29Schristos * It does inform user about the condition, provides some hints how to
9466fb29d29Schristos * resolve this and dies gracefully. This can happend in at least three
9476fb29d29Schristos * cases (all are configuration mistakes):
9486fb29d29Schristos * a) IPv4: user have duplicate fixed-address entries (the same
9496fb29d29Schristos * address is defined twice). We may have found wrong lease.
9506fb29d29Schristos * b) IPv6: user have overlapping pools (we tried to find
9516fb29d29Schristos * a lease in a wrong pool)
9526fb29d29Schristos * c) IPv6: user have duplicate fixed-address6 entires (the same
9536fb29d29Schristos * address is defined twice). We may have found wrong lease.
9546fb29d29Schristos *
9556fb29d29Schristos * Comment: while it would be possible to recover from both cases
9566fb29d29Schristos * by forcibly searching for leases in *all* following pools, that would
9576fb29d29Schristos * only hide the real problem - a misconfiguration. Proper solution
9586fb29d29Schristos * is to log the problem, die and let the user fix his config file.
9596fb29d29Schristos */
9606fb29d29Schristos void
update_lease_failed(struct lease * lease,struct iasubopt * lease6,dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_cb_t * ddns_cb_set,const char * file,int line)9616fb29d29Schristos update_lease_failed(struct lease *lease,
9626fb29d29Schristos struct iasubopt *lease6,
9636fb29d29Schristos dhcp_ddns_cb_t *ddns_cb,
9646fb29d29Schristos dhcp_ddns_cb_t *ddns_cb_set,
9656fb29d29Schristos const char * file, int line)
9666fb29d29Schristos {
9676fb29d29Schristos char lease_address[MAX_ADDRESS_STRING_LEN + 64];
9686fb29d29Schristos char reason[128]; /* likely reason */
9696fb29d29Schristos
9706fb29d29Schristos sprintf(reason, "unknown");
9716fb29d29Schristos sprintf(lease_address, "unknown");
9726fb29d29Schristos
9736fb29d29Schristos /*
9746fb29d29Schristos * let's pretend that everything is ok, so we can continue for
9756fb29d29Schristos * information gathering purposes
9766fb29d29Schristos */
9776fb29d29Schristos
9786fb29d29Schristos if (ddns_cb != NULL) {
9796fb29d29Schristos strncpy(lease_address, piaddr(ddns_cb->address),
9806fb29d29Schristos MAX_ADDRESS_STRING_LEN);
9816fb29d29Schristos
9826fb29d29Schristos if (ddns_cb->address.len == 4) {
9836fb29d29Schristos sprintf(reason, "duplicate IPv4 fixed-address entry");
9846fb29d29Schristos } else if (ddns_cb->address.len == 16) {
9856fb29d29Schristos sprintf(reason, "duplicate IPv6 fixed-address6 entry "
9866fb29d29Schristos "or overlapping pools");
9876fb29d29Schristos } else {
9886fb29d29Schristos /*
9896fb29d29Schristos * Should not happen. We have non-IPv4, non-IPv6
9906fb29d29Schristos * address. Something is very wrong here.
9916fb29d29Schristos */
9926fb29d29Schristos sprintf(reason, "corrupted ddns_cb structure (address "
9936fb29d29Schristos "length is %d)", ddns_cb->address.len);
9946fb29d29Schristos }
9956fb29d29Schristos }
9966fb29d29Schristos
9976fb29d29Schristos log_error("Failed to properly update internal lease structure with "
9986fb29d29Schristos "DDNS");
9996fb29d29Schristos log_error("control block structures. Tried to update lease for"
10006fb29d29Schristos "%s address, ddns_cb=%p.", lease_address, ddns_cb);
10016fb29d29Schristos
10026fb29d29Schristos log_error("%s", "");
10036fb29d29Schristos log_error("This condition can occur, if DHCP server configuration is "
10046fb29d29Schristos "inconsistent.");
10056fb29d29Schristos log_error("In particular, please do check that your configuration:");
10066fb29d29Schristos log_error("a) does not have overlapping pools (especially containing");
10076fb29d29Schristos log_error(" %s address).", lease_address);
10086fb29d29Schristos log_error("b) there are no duplicate fixed-address or fixed-address6");
10096fb29d29Schristos log_error("entries for the %s address.", lease_address);
10106fb29d29Schristos log_error("%s", "");
10116fb29d29Schristos log_error("Possible reason for this failure: %s", reason);
10126fb29d29Schristos
10136fb29d29Schristos log_fatal("%s(%d): Failed to update lease database with DDNS info for "
10146fb29d29Schristos "address %s. Lease database inconsistent. Unable to recover."
10156fb29d29Schristos " Terminating.", file, line, lease_address);
10166fb29d29Schristos }
1017ce893c7eSchristos #endif
10186fb29d29Schristos
10196fb29d29Schristos /*
10206fb29d29Schristos * utility function to update found lease. It does extra checks
10216fb29d29Schristos * that we are indeed updating the right lease. It may happen
10226fb29d29Schristos * that user have duplicate fixed-address entries, so we attempt
10236fb29d29Schristos * to update wrong lease. See also safe_lease6_update.
10246fb29d29Schristos */
10256fb29d29Schristos
1026ce893c7eSchristos static void
safe_lease_update(struct lease * lease,dhcp_ddns_cb_t * oldcb,dhcp_ddns_cb_t * newcb,const char * file,int line)10276fb29d29Schristos safe_lease_update(struct lease *lease,
10286fb29d29Schristos dhcp_ddns_cb_t *oldcb,
10296fb29d29Schristos dhcp_ddns_cb_t *newcb,
10306fb29d29Schristos const char *file, int line)
10316fb29d29Schristos {
10326fb29d29Schristos if (lease == NULL) {
10336fb29d29Schristos /* should never get here */
10346fb29d29Schristos log_fatal("Impossible condition at %s:%d (called from %s:%d).",
10356fb29d29Schristos MDL, file, line);
10366fb29d29Schristos }
10376fb29d29Schristos
10386fb29d29Schristos if ( (lease->ddns_cb == NULL) && (newcb == NULL) ) {
10396fb29d29Schristos /*
10406fb29d29Schristos * Trying to clean up pointer that is already null. We
10416fb29d29Schristos * are most likely trying to update wrong lease here.
10426fb29d29Schristos */
10436fb29d29Schristos
10446fb29d29Schristos /*
10456fb29d29Schristos * Previously this error message popped out during
10466fb29d29Schristos * DNS update for fixed leases. As we no longer
10476fb29d29Schristos * try to update the lease for a fixed (static) lease
10486fb29d29Schristos * this should not be a problem.
10496fb29d29Schristos */
10506fb29d29Schristos log_error("%s(%d): Invalid lease update. Tried to "
10516fb29d29Schristos "clear already NULL DDNS control block "
10526fb29d29Schristos "pointer for lease %s.",
10536fb29d29Schristos file, line, piaddr(lease->ip_addr) );
10546fb29d29Schristos
10556fb29d29Schristos #if defined (DNS_UPDATES_MEMORY_CHECKS)
10566fb29d29Schristos update_lease_failed(lease, NULL, oldcb, newcb, file, line);
10576fb29d29Schristos #endif
10586fb29d29Schristos /*
10596fb29d29Schristos * May not reach this: update_lease_failed calls
10606fb29d29Schristos * log_fatal.
10616fb29d29Schristos */
10626fb29d29Schristos return;
10636fb29d29Schristos }
10646fb29d29Schristos
10656fb29d29Schristos if ( (lease->ddns_cb != NULL) && (lease->ddns_cb != oldcb) ) {
10666fb29d29Schristos /*
10676fb29d29Schristos * There is existing cb structure, but it differs from
10686fb29d29Schristos * what we expected to see there. Most likely we are
10696fb29d29Schristos * trying to update wrong lease.
10706fb29d29Schristos */
10716fb29d29Schristos log_error("%s(%d): Failed to update internal lease "
10726fb29d29Schristos "structure with DDNS control block. Existing"
10736fb29d29Schristos " ddns_cb structure does not match "
10746fb29d29Schristos "expectations.IPv4=%s, old ddns_cb=%p, tried"
10756fb29d29Schristos "to update to new ddns_cb=%p", file, line,
10766fb29d29Schristos piaddr(lease->ip_addr), oldcb, newcb);
10776fb29d29Schristos
10786fb29d29Schristos #if defined (DNS_UPDATES_MEMORY_CHECKS)
10796fb29d29Schristos update_lease_failed(lease, NULL, oldcb, newcb, file, line);
10806fb29d29Schristos #endif
10816fb29d29Schristos /*
10826fb29d29Schristos * May not reach this: update_lease_failed calls
10836fb29d29Schristos * log_fatal.
10846fb29d29Schristos */
10856fb29d29Schristos return;
10866fb29d29Schristos }
10876fb29d29Schristos
10886fb29d29Schristos /* additional IPv4 specific checks may be added here */
10896fb29d29Schristos
10906fb29d29Schristos /* update the lease */
10916fb29d29Schristos lease->ddns_cb = newcb;
10926fb29d29Schristos }
10936fb29d29Schristos
1094ce893c7eSchristos static void
safe_lease6_update(struct iasubopt * lease6,dhcp_ddns_cb_t * oldcb,dhcp_ddns_cb_t * newcb,const char * file,int line)10956fb29d29Schristos safe_lease6_update(struct iasubopt *lease6,
10966fb29d29Schristos dhcp_ddns_cb_t *oldcb,
10976fb29d29Schristos dhcp_ddns_cb_t *newcb,
10986fb29d29Schristos const char *file, int line)
10996fb29d29Schristos {
11006fb29d29Schristos char addrbuf[MAX_ADDRESS_STRING_LEN];
11016fb29d29Schristos
11026fb29d29Schristos if (lease6 == NULL) {
11036fb29d29Schristos /* should never get here */
11046fb29d29Schristos log_fatal("Impossible condition at %s:%d (called from %s:%d).",
11056fb29d29Schristos MDL, file, line);
11066fb29d29Schristos }
11076fb29d29Schristos
11086fb29d29Schristos if ( (lease6->ddns_cb == NULL) && (newcb == NULL) ) {
11096fb29d29Schristos inet_ntop(AF_INET6, &lease6->addr, addrbuf,
11106fb29d29Schristos MAX_ADDRESS_STRING_LEN);
11116fb29d29Schristos /*
11126fb29d29Schristos * Trying to clean up pointer that is already null. We
11136fb29d29Schristos * are most likely trying to update wrong lease here.
11146fb29d29Schristos */
11156fb29d29Schristos log_error("%s(%d): Failed to update internal lease "
11166fb29d29Schristos "structure. Tried to clear already NULL "
11176fb29d29Schristos "DDNS control block pointer for lease %s.",
11186fb29d29Schristos file, line, addrbuf);
11196fb29d29Schristos
11206fb29d29Schristos #if defined (DNS_UPDATES_MEMORY_CHECKS)
11216fb29d29Schristos update_lease_failed(NULL, lease6, oldcb, newcb, file, line);
11226fb29d29Schristos #endif
11236fb29d29Schristos
11246fb29d29Schristos /*
11256fb29d29Schristos * May not reach this: update_lease_failed calls
11266fb29d29Schristos * log_fatal.
11276fb29d29Schristos */
11286fb29d29Schristos return;
11296fb29d29Schristos }
11306fb29d29Schristos
11316fb29d29Schristos if ( (lease6->ddns_cb != NULL) && (lease6->ddns_cb != oldcb) ) {
11326fb29d29Schristos /*
11336fb29d29Schristos * there is existing cb structure, but it differs from
11346fb29d29Schristos * what we expected to see there. Most likely we are
11356fb29d29Schristos * trying to update wrong lease.
11366fb29d29Schristos */
11376fb29d29Schristos inet_ntop(AF_INET6, &lease6->addr, addrbuf,
11386fb29d29Schristos MAX_ADDRESS_STRING_LEN);
11396fb29d29Schristos
11406fb29d29Schristos log_error("%s(%d): Failed to update internal lease "
11416fb29d29Schristos "structure with DDNS control block. Existing"
11426fb29d29Schristos " ddns_cb structure does not match "
11436fb29d29Schristos "expectations.IPv6=%s, old ddns_cb=%p, tried"
11446fb29d29Schristos "to update to new ddns_cb=%p", file, line,
11456fb29d29Schristos addrbuf, oldcb, newcb);
11466fb29d29Schristos
11476fb29d29Schristos #if defined (DNS_UPDATES_MEMORY_CHECKS)
11486fb29d29Schristos update_lease_failed(NULL, lease6, oldcb, newcb, file, line);
11496fb29d29Schristos #endif
11506fb29d29Schristos /*
11516fb29d29Schristos * May not reach this: update_lease_failed calls
11526fb29d29Schristos * log_fatal.
11536fb29d29Schristos */
11546fb29d29Schristos return;
11556fb29d29Schristos }
11566fb29d29Schristos /* additional IPv6 specific checks may be added here */
11576fb29d29Schristos
11586fb29d29Schristos /* update the lease */
11596fb29d29Schristos lease6->ddns_cb = newcb;
11606fb29d29Schristos }
11616fb29d29Schristos
11626fb29d29Schristos /*
11636fb29d29Schristos * Utility function to update the pointer to the DDNS control block
11646fb29d29Schristos * in a lease.
11656fb29d29Schristos * SUCCESS - able to update the pointer
11666fb29d29Schristos * FAILURE - lease didn't exist or sanity checks failed
11676fb29d29Schristos * lease and lease6 may be empty in which case we attempt to find
11686fb29d29Schristos * the lease from the ddns_cb information.
11696fb29d29Schristos * ddns_cb is the control block to use if a lookup is necessary
11706fb29d29Schristos * ddns_cb_set is the pointer to insert into the lease and may be NULL
11716fb29d29Schristos * The last two arguments may look odd as they will be the same much of the
11726fb29d29Schristos * time, but I need an argument to tell me if I'm setting or clearing in
11736fb29d29Schristos * addition to the address information from the cb to look up the lease.
11746fb29d29Schristos * using the same value twice allows me more flexibility.
11756fb29d29Schristos */
11766fb29d29Schristos
1177ce893c7eSchristos static isc_result_t
ddns_update_lease_ptr(struct lease * lease,struct iasubopt * lease6,dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_cb_t * ddns_cb_set,const char * file,int line)11786fb29d29Schristos ddns_update_lease_ptr(struct lease *lease,
11796fb29d29Schristos struct iasubopt *lease6,
11806fb29d29Schristos dhcp_ddns_cb_t *ddns_cb,
11816fb29d29Schristos dhcp_ddns_cb_t *ddns_cb_set,
11826fb29d29Schristos const char * file, int line)
11836fb29d29Schristos {
11846fb29d29Schristos char ddns_address[MAX_ADDRESS_STRING_LEN];
11856fb29d29Schristos sprintf(ddns_address, "unknown");
11866fb29d29Schristos if (ddns_cb == NULL) {
11876fb29d29Schristos log_info("%s(%d): No control block for lease update",
11886fb29d29Schristos file, line);
11896fb29d29Schristos return (ISC_R_FAILURE);
11906fb29d29Schristos }
11916fb29d29Schristos else {
11926fb29d29Schristos strcpy(ddns_address, piaddr(ddns_cb->address));
11936fb29d29Schristos }
11946fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
11956fb29d29Schristos log_info("%s(%d): Updating lease_ptr for ddns_cp=%p (addr=%s)",
11966fb29d29Schristos file, line, ddns_cb, ddns_address );
11976fb29d29Schristos #endif
11986fb29d29Schristos
11996fb29d29Schristos /*
12006fb29d29Schristos * If the lease was static (for a fixed address)
12016fb29d29Schristos * we don't need to do any work.
12026fb29d29Schristos */
12036fb29d29Schristos if (ddns_cb->flags & DDNS_STATIC_LEASE) {
12046fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
12056fb29d29Schristos log_info("lease is static, returning");
12066fb29d29Schristos #endif
12076fb29d29Schristos return (ISC_R_SUCCESS);
12086fb29d29Schristos }
12096fb29d29Schristos
12106fb29d29Schristos /*
12116fb29d29Schristos * If we are processing an expired or released v6 lease
12126fb29d29Schristos * we don't actually have a lease to update
12136fb29d29Schristos */
12146fb29d29Schristos if ((ddns_cb->address.len == 16) &&
12156fb29d29Schristos ((ddns_cb->flags & DDNS_ACTIVE_LEASE) == 0)) {
12166fb29d29Schristos return (ISC_R_SUCCESS);
12176fb29d29Schristos }
12186fb29d29Schristos
12196fb29d29Schristos if (lease != NULL) {
12206fb29d29Schristos safe_lease_update(lease, ddns_cb, ddns_cb_set,
12216fb29d29Schristos file, line);
12226fb29d29Schristos } else if (lease6 != NULL) {
12236fb29d29Schristos safe_lease6_update(lease6, ddns_cb, ddns_cb_set,
12246fb29d29Schristos file, line);
12256fb29d29Schristos } else if (ddns_cb->address.len == 4) {
12266fb29d29Schristos struct lease *find_lease = NULL;
12276fb29d29Schristos if (find_lease_by_ip_addr(&find_lease,
12286fb29d29Schristos ddns_cb->address, MDL) != 0) {
12296fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
12306fb29d29Schristos log_info("%s(%d): find_lease_by_ip_addr(%s) successful:"
12316fb29d29Schristos "lease=%p", file, line, ddns_address,
12326fb29d29Schristos find_lease);
12336fb29d29Schristos #endif
12346fb29d29Schristos
12356fb29d29Schristos safe_lease_update(find_lease, ddns_cb,
12366fb29d29Schristos ddns_cb_set, file, line);
12376fb29d29Schristos lease_dereference(&find_lease, MDL);
12386fb29d29Schristos }
12396fb29d29Schristos else {
12406fb29d29Schristos log_error("%s(%d): ddns_update_lease_ptr failed. "
12416fb29d29Schristos "Lease for %s not found.",
12426fb29d29Schristos file, line, piaddr(ddns_cb->address));
12436fb29d29Schristos
12446fb29d29Schristos #if defined (DNS_UPDATES_MEMORY_CHECKS)
12456fb29d29Schristos update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
12466fb29d29Schristos file, line);
12476fb29d29Schristos #endif
12486fb29d29Schristos /*
12496fb29d29Schristos * may not reach this. update_lease_failed
12506fb29d29Schristos * calls log_fatal.
12516fb29d29Schristos */
12526fb29d29Schristos return(ISC_R_FAILURE);
12536fb29d29Schristos
12546fb29d29Schristos }
12556fb29d29Schristos } else if (ddns_cb->address.len == 16) {
12566fb29d29Schristos struct iasubopt *find_lease6 = NULL;
12576fb29d29Schristos struct ipv6_pool *pool = NULL;
12586fb29d29Schristos struct in6_addr addr;
12596fb29d29Schristos char addrbuf[MAX_ADDRESS_STRING_LEN];
12606fb29d29Schristos
12616fb29d29Schristos memcpy(&addr, &ddns_cb->address.iabuf, 16);
12626fb29d29Schristos if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) !=
12636fb29d29Schristos ISC_R_SUCCESS) &&
12646fb29d29Schristos (find_ipv6_pool(&pool, D6O_IA_NA, &addr) !=
12656fb29d29Schristos ISC_R_SUCCESS)) {
12666fb29d29Schristos inet_ntop(AF_INET6, &addr, addrbuf,
12676fb29d29Schristos MAX_ADDRESS_STRING_LEN);
12686fb29d29Schristos log_error("%s(%d): Pool for lease %s not found.",
12696fb29d29Schristos file, line, addrbuf);
12706fb29d29Schristos #if defined (DNS_UPDATES_MEMORY_CHECKS)
12716fb29d29Schristos update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
12726fb29d29Schristos file, line);
12736fb29d29Schristos #endif
12746fb29d29Schristos /*
12756fb29d29Schristos * never reached. update_lease_failed
12766fb29d29Schristos * calls log_fatal.
12776fb29d29Schristos */
12786fb29d29Schristos return(ISC_R_FAILURE);
12796fb29d29Schristos }
12806fb29d29Schristos
12816fb29d29Schristos if (iasubopt_hash_lookup(&find_lease6, pool->leases,
12826fb29d29Schristos &addr, 16, MDL)) {
12836fb29d29Schristos find_lease6->ddns_cb = ddns_cb_set;
12846fb29d29Schristos iasubopt_dereference(&find_lease6, MDL);
12856fb29d29Schristos } else {
12866fb29d29Schristos inet_ntop(AF_INET6, &addr, addrbuf,
12876fb29d29Schristos MAX_ADDRESS_STRING_LEN);
12886fb29d29Schristos log_error("%s(%d): Lease %s not found within pool.",
12896fb29d29Schristos file, line, addrbuf);
12906fb29d29Schristos #if defined (DNS_UPDATES_MEMORY_CHECKS)
12916fb29d29Schristos update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
12926fb29d29Schristos file, line);
12936fb29d29Schristos #endif
12946fb29d29Schristos /*
129556e2dc54Schristos * not reached when update_lease_failed is called,
129656e2dc54Schristos * it calls log_fatal.
12976fb29d29Schristos */
129856e2dc54Schristos ipv6_pool_dereference(&pool, MDL);
12996fb29d29Schristos return(ISC_R_FAILURE);
13006fb29d29Schristos }
13016fb29d29Schristos ipv6_pool_dereference(&pool, MDL);
13026fb29d29Schristos } else {
13036fb29d29Schristos /* shouldn't get here */
13046fb29d29Schristos log_fatal("Impossible condition at %s:%d, called from %s:%d.",
13056fb29d29Schristos MDL, file, line);
13066fb29d29Schristos }
13076fb29d29Schristos
13086fb29d29Schristos return(ISC_R_SUCCESS);
13096fb29d29Schristos }
13106fb29d29Schristos
1311ce893c7eSchristos static void
ddns_ptr_add(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)13126fb29d29Schristos ddns_ptr_add(dhcp_ddns_cb_t *ddns_cb,
13136fb29d29Schristos isc_result_t eresult)
13146fb29d29Schristos {
13156fb29d29Schristos if (eresult == ISC_R_SUCCESS) {
13166fb29d29Schristos log_info("Added reverse map from %.*s to %.*s",
13176fb29d29Schristos (int)ddns_cb->rev_name.len,
13186fb29d29Schristos (const char *)ddns_cb->rev_name.data,
13196fb29d29Schristos (int)ddns_cb->fwd_name.len,
13206fb29d29Schristos (const char *)ddns_cb->fwd_name.data);
13216fb29d29Schristos
13226fb29d29Schristos ddns_update_lease_text(ddns_cb, NULL);
13236fb29d29Schristos } else {
13246fb29d29Schristos log_error("Unable to add reverse map from %.*s to %.*s: %s",
13256fb29d29Schristos (int)ddns_cb->rev_name.len,
13266fb29d29Schristos (const char *)ddns_cb->rev_name.data,
13276fb29d29Schristos (int)ddns_cb->fwd_name.len,
13286fb29d29Schristos (const char *)ddns_cb->fwd_name.data,
13296fb29d29Schristos isc_result_totext (eresult));
13306fb29d29Schristos }
13316fb29d29Schristos
13326fb29d29Schristos ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
13336fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
13346fb29d29Schristos /*
13356fb29d29Schristos * A single DDNS operation may require several calls depending on
13366fb29d29Schristos * the current state as the prerequisites for the first message
13376fb29d29Schristos * may not succeed requiring a second operation and potentially
13386fb29d29Schristos * a ptr operation after that. The commit_leases operation is
13396fb29d29Schristos * invoked at the end of this set of operations in order to require
13406fb29d29Schristos * a single write for all of the changes. We call commit_leases
13416fb29d29Schristos * here rather than immediately after the call to update the lease
13426fb29d29Schristos * text in order to save any previously written data.
13436fb29d29Schristos */
13446fb29d29Schristos commit_leases();
13456fb29d29Schristos return;
13466fb29d29Schristos }
13476fb29d29Schristos
13486fb29d29Schristos /*
13496fb29d29Schristos * action routine when trying to remove a pointer
13506fb29d29Schristos * this will be called after the ddns queries have completed
13516fb29d29Schristos * if we succeeded in removing the pointer we go to the next step (if any)
13526fb29d29Schristos * if not we cleanup and leave.
13536fb29d29Schristos */
13546fb29d29Schristos
1355ce893c7eSchristos static void
ddns_ptr_remove(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)13566fb29d29Schristos ddns_ptr_remove(dhcp_ddns_cb_t *ddns_cb,
13576fb29d29Schristos isc_result_t eresult)
13586fb29d29Schristos {
13596fb29d29Schristos isc_result_t result = eresult;
13606fb29d29Schristos
13616fb29d29Schristos switch(eresult) {
13626fb29d29Schristos case ISC_R_SUCCESS:
13636fb29d29Schristos log_info("Removed reverse map on %.*s",
13646fb29d29Schristos (int)ddns_cb->rev_name.len,
13656fb29d29Schristos (const char *)ddns_cb->rev_name.data);
13666fb29d29Schristos /* fall through */
13676fb29d29Schristos case DNS_R_NXRRSET:
13686fb29d29Schristos case DNS_R_NXDOMAIN:
13696fb29d29Schristos /* No entry is the same as success.
13706fb29d29Schristos * Remove the information from the lease and
13716fb29d29Schristos * continue with any next step */
13726fb29d29Schristos ddns_update_lease_text(ddns_cb, NULL);
13736fb29d29Schristos
13746fb29d29Schristos /* trigger any add operation */
13756fb29d29Schristos result = ISC_R_SUCCESS;
13766fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
13776fb29d29Schristos log_info("DDNS: removed map or no reverse map to remove %.*s",
13786fb29d29Schristos (int)ddns_cb->rev_name.len,
13796fb29d29Schristos (const char *)ddns_cb->rev_name.data);
13806fb29d29Schristos #endif
13816fb29d29Schristos break;
13826fb29d29Schristos
13836fb29d29Schristos default:
13846fb29d29Schristos log_error("Can't remove reverse map on %.*s: %s",
13856fb29d29Schristos (int)ddns_cb->rev_name.len,
13866fb29d29Schristos (const char *)ddns_cb->rev_name.data,
13876fb29d29Schristos isc_result_totext (eresult));
13886fb29d29Schristos break;
13896fb29d29Schristos }
13906fb29d29Schristos
13916fb29d29Schristos /* If we aren't suppossed to do the next step, set the result
13926fb29d29Schristos * flag so ddns_fwd_srv_connector won't do much
13936fb29d29Schristos */
13946fb29d29Schristos if ((ddns_cb->flags & DDNS_EXECUTE_NEXT) == 0)
13956fb29d29Schristos result = ISC_R_FAILURE;
13966fb29d29Schristos
13976fb29d29Schristos ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
13986fb29d29Schristos ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, result);
13996fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
14006fb29d29Schristos return;
14016fb29d29Schristos }
14026fb29d29Schristos
14036fb29d29Schristos
14046fb29d29Schristos /*
14056fb29d29Schristos * If the first query succeeds, the updater can conclude that it
14066fb29d29Schristos * has added a new name whose only RRs are the A and DHCID RR records.
14076fb29d29Schristos * The A RR update is now complete (and a client updater is finished,
14086fb29d29Schristos * while a server might proceed to perform a PTR RR update).
14096fb29d29Schristos * -- "Interaction between DHCP and DNS"
14106fb29d29Schristos *
14116fb29d29Schristos * If the second query succeeds, the updater can conclude that the current
14126fb29d29Schristos * client was the last client associated with the domain name, and that
14136fb29d29Schristos * the name now contains the updated A RR. The A RR update is now
14146fb29d29Schristos * complete (and a client updater is finished, while a server would
14156fb29d29Schristos * then proceed to perform a PTR RR update).
14166fb29d29Schristos * -- "Interaction between DHCP and DNS"
14176fb29d29Schristos *
14186fb29d29Schristos * If the second query fails with NXRRSET, the updater must conclude
14196fb29d29Schristos * that the client's desired name is in use by another host. If
14206fb29d29Schristos * Dual Stack Mixed Mode (DSMM) is enabled and we proceed to a
14216fb29d29Schristos * third stage forward update attempt specific to DSMM rules. If not,
14226fb29d29Schristos * then the existing entries are left intact:
14236fb29d29Schristos *
14246fb29d29Schristos * At this juncture, the updater can decide (based on some administrative
14256fb29d29Schristos * configuration outside of the scope of this document) whether to let
14266fb29d29Schristos * the existing owner of the name keep that name, and to (possibly)
14276fb29d29Schristos * perform some name disambiguation operation on behalf of the current
14286fb29d29Schristos * client, or to replace the RRs on the name with RRs that represent
14296fb29d29Schristos * the current client. If the configured policy allows replacement of
14306fb29d29Schristos * existing records, the updater submits a query that deletes the
14316fb29d29Schristos * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
14326fb29d29Schristos * represent the IP address and client-identity of the new client.
14336fb29d29Schristos * -- "Interaction between DHCP and DNS"
14346fb29d29Schristos */
14356fb29d29Schristos
1436ce893c7eSchristos static void
ddns_fwd_srv_add2(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)14376fb29d29Schristos ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb,
14386fb29d29Schristos isc_result_t eresult)
14396fb29d29Schristos {
14406fb29d29Schristos isc_result_t result;
14416fb29d29Schristos const char *logstr = NULL;
14426fb29d29Schristos char ddns_address[MAX_ADDRESS_STRING_LEN];
14436fb29d29Schristos
14446fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
14456fb29d29Schristos log_info ("DDNS:ddns_fwd_srv_add2: %s eresult: %d",
14466fb29d29Schristos dump_ddns_cb(ddns_cb), eresult);
14476fb29d29Schristos #endif
14486fb29d29Schristos
14496fb29d29Schristos /* Construct a printable form of the address for logging */
14506fb29d29Schristos strcpy(ddns_address, piaddr(ddns_cb->address));
14516fb29d29Schristos
14526fb29d29Schristos switch(eresult) {
14536fb29d29Schristos case ISC_R_SUCCESS:
14546fb29d29Schristos log_info("Added new forward map from %.*s to %s",
14556fb29d29Schristos (int)ddns_cb->fwd_name.len,
14566fb29d29Schristos (const char *)ddns_cb->fwd_name.data,
14576fb29d29Schristos ddns_address);
14586fb29d29Schristos
14596fb29d29Schristos ddns_update_lease_text(ddns_cb, NULL);
14606fb29d29Schristos
14616fb29d29Schristos if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
14626fb29d29Schristos /* if we have zone information get rid of it */
14636fb29d29Schristos if (ddns_cb->zone != NULL) {
14646fb29d29Schristos ddns_cb_forget_zone(ddns_cb);
14656fb29d29Schristos }
14666fb29d29Schristos
14676fb29d29Schristos ddns_cb->state = DDNS_STATE_ADD_PTR;
14686fb29d29Schristos ddns_cb->cur_func = ddns_ptr_add;
14696fb29d29Schristos
14706fb29d29Schristos result = ddns_modify_ptr(ddns_cb, MDL);
14716fb29d29Schristos if (result == ISC_R_SUCCESS) {
14726fb29d29Schristos return;
14736fb29d29Schristos }
14746fb29d29Schristos }
14756fb29d29Schristos break;
14766fb29d29Schristos
14776fb29d29Schristos case DNS_R_YXRRSET:
14786fb29d29Schristos case DNS_R_YXDOMAIN:
14796fb29d29Schristos logstr = "DHCID mismatch, belongs to another client.";
14806fb29d29Schristos break;
14816fb29d29Schristos
14826fb29d29Schristos case DNS_R_NXDOMAIN:
14836fb29d29Schristos case DNS_R_NXRRSET:
14846fb29d29Schristos /* If DSMM is on we need to try forward add3 */
14856fb29d29Schristos if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) {
14866fb29d29Schristos ddns_cb->state = DDNS_STATE_DSMM_FW_ADD3;
14876fb29d29Schristos ddns_cb->cur_func = ddns_fwd_srv_add3;
14886fb29d29Schristos
14896fb29d29Schristos result = ddns_modify_fwd(ddns_cb, MDL);
14906fb29d29Schristos if (result == ISC_R_SUCCESS) {
14916fb29d29Schristos return;
14926fb29d29Schristos }
14936fb29d29Schristos
14946fb29d29Schristos break;
14956fb29d29Schristos }
14966fb29d29Schristos
14976fb29d29Schristos logstr = "Has an address record but no DHCID, not mine.";
14986fb29d29Schristos break;
14996fb29d29Schristos
15006fb29d29Schristos default:
15016fb29d29Schristos logstr = isc_result_totext(eresult);
15026fb29d29Schristos break;
15036fb29d29Schristos }
15046fb29d29Schristos
15056fb29d29Schristos if (logstr != NULL) {
15066fb29d29Schristos log_error("Forward map from %.*s to %s FAILED: %s",
15076fb29d29Schristos (int)ddns_cb->fwd_name.len,
15086fb29d29Schristos (const char *)ddns_cb->fwd_name.data,
15096fb29d29Schristos ddns_address, logstr);
15106fb29d29Schristos }
15116fb29d29Schristos
15126fb29d29Schristos ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
15136fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
15146fb29d29Schristos /*
15156fb29d29Schristos * A single DDNS operation may require several calls depending on
15166fb29d29Schristos * the current state as the prerequisites for the first message
15176fb29d29Schristos * may not succeed requiring a second operation and potentially
15186fb29d29Schristos * a ptr operation after that. The commit_leases operation is
15196fb29d29Schristos * invoked at the end of this set of operations in order to require
15206fb29d29Schristos * a single write for all of the changes. We call commit_leases
15216fb29d29Schristos * here rather than immediately after the call to update the lease
15226fb29d29Schristos * text in order to save any previously written data.
15236fb29d29Schristos */
15246fb29d29Schristos commit_leases();
15256fb29d29Schristos return;
15266fb29d29Schristos }
15276fb29d29Schristos
1528ce893c7eSchristos static void
ddns_fwd_srv_add1(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)15296fb29d29Schristos ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb,
15306fb29d29Schristos isc_result_t eresult)
15316fb29d29Schristos {
15326fb29d29Schristos isc_result_t result;
15336fb29d29Schristos char ddns_address[MAX_ADDRESS_STRING_LEN];
15346fb29d29Schristos
15356fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
15366fb29d29Schristos log_info ("DDNS: ddns_fwd_srv_add1: %s eresult: %d",
15376fb29d29Schristos dump_ddns_cb(ddns_cb), eresult);
15386fb29d29Schristos #endif
15396fb29d29Schristos
15406fb29d29Schristos /* Construct a printable form of the address for logging */
15416fb29d29Schristos strcpy(ddns_address, piaddr(ddns_cb->address));
15426fb29d29Schristos
15436fb29d29Schristos switch(eresult) {
15446fb29d29Schristos case ISC_R_SUCCESS:
15456fb29d29Schristos log_info ("Added new forward map from %.*s to %s",
15466fb29d29Schristos (int)ddns_cb->fwd_name.len,
15476fb29d29Schristos (const char *)ddns_cb->fwd_name.data,
15486fb29d29Schristos ddns_address);
15496fb29d29Schristos
15506fb29d29Schristos ddns_update_lease_text(ddns_cb, NULL);
15516fb29d29Schristos
15526fb29d29Schristos if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
15536fb29d29Schristos /* if we have zone information get rid of it */
15546fb29d29Schristos if (ddns_cb->zone != NULL) {
15556fb29d29Schristos ddns_cb_forget_zone(ddns_cb);
15566fb29d29Schristos }
15576fb29d29Schristos
15586fb29d29Schristos ddns_cb->state = DDNS_STATE_ADD_PTR;
15596fb29d29Schristos ddns_cb->cur_func = ddns_ptr_add;
15606fb29d29Schristos
15616fb29d29Schristos result = ddns_modify_ptr(ddns_cb, MDL);
15626fb29d29Schristos if (result == ISC_R_SUCCESS) {
15636fb29d29Schristos return;
15646fb29d29Schristos }
15656fb29d29Schristos }
15666fb29d29Schristos break;
15676fb29d29Schristos
15686fb29d29Schristos case DNS_R_YXDOMAIN:
15696fb29d29Schristos /* we can reuse the zone information */
15706fb29d29Schristos ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID;
15716fb29d29Schristos ddns_cb->cur_func = ddns_fwd_srv_add2;
15726fb29d29Schristos
15736fb29d29Schristos result = ddns_modify_fwd(ddns_cb, MDL);
15746fb29d29Schristos if (result == ISC_R_SUCCESS) {
15756fb29d29Schristos return;
15766fb29d29Schristos }
15776fb29d29Schristos break;
15786fb29d29Schristos default:
15796fb29d29Schristos log_error ("Unable to add forward map from %.*s to %s: %s",
15806fb29d29Schristos (int)ddns_cb->fwd_name.len,
15816fb29d29Schristos (const char *)ddns_cb->fwd_name.data,
15826fb29d29Schristos ddns_address,
15836fb29d29Schristos isc_result_totext (eresult));
15846fb29d29Schristos break;
15856fb29d29Schristos }
15866fb29d29Schristos
15876fb29d29Schristos ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
15886fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
15896fb29d29Schristos /*
15906fb29d29Schristos * A single DDNS operation may require several calls depending on
15916fb29d29Schristos * the current state as the prerequisites for the first message
15926fb29d29Schristos * may not succeed requiring a second operation and potentially
15936fb29d29Schristos * a ptr operation after that. The commit_leases operation is
15946fb29d29Schristos * invoked at the end of this set of operations in order to require
15956fb29d29Schristos * a single write for all of the changes. We call commit_leases
15966fb29d29Schristos * here rather than immediately after the call to update the lease
15976fb29d29Schristos * text in order to save any previously written data.
15986fb29d29Schristos */
15996fb29d29Schristos commit_leases();
16006fb29d29Schristos return;
16016fb29d29Schristos }
16026fb29d29Schristos
16036fb29d29Schristos /*
16046fb29d29Schristos * This action routine is invoked after the DSMM third add stage is
16056fb29d29Schristos * attempted. If we succeeded we attempt to update the reverse DNS,
16066fb29d29Schristos * if not we cleanup and leave.
16076fb29d29Schristos */
16086fb29d29Schristos void
ddns_fwd_srv_add3(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)16096fb29d29Schristos ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb,
16106fb29d29Schristos isc_result_t eresult)
16116fb29d29Schristos {
16126fb29d29Schristos isc_result_t result;
16136fb29d29Schristos const char *logstr = NULL;
16146fb29d29Schristos char ddns_address[MAX_ADDRESS_STRING_LEN+1];
16156fb29d29Schristos
16166fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
16176fb29d29Schristos log_info ("DDNS: ddns_fwd_srv_add3: %s eresult: %d",
16186fb29d29Schristos dump_ddns_cb(ddns_cb), eresult);
16196fb29d29Schristos #endif
16206fb29d29Schristos
16216fb29d29Schristos /* Construct a printable form of the address for logging */
16226fb29d29Schristos memset(ddns_address, 0x0, sizeof(ddns_address));
16236fb29d29Schristos strncpy(ddns_address, piaddr(ddns_cb->address),
16246fb29d29Schristos sizeof(ddns_address) - 1);
16256fb29d29Schristos
16266fb29d29Schristos switch(eresult) {
16276fb29d29Schristos case ISC_R_SUCCESS:
16286fb29d29Schristos log_info("Added new forward map from %.*s to %s",
16296fb29d29Schristos (int)ddns_cb->fwd_name.len,
16306fb29d29Schristos (const char *)ddns_cb->fwd_name.data,
16316fb29d29Schristos ddns_address);
16326fb29d29Schristos
16336fb29d29Schristos ddns_update_lease_text(ddns_cb, NULL);
16346fb29d29Schristos
16356fb29d29Schristos if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
16366fb29d29Schristos /* if we have zone information get rid of it */
16376fb29d29Schristos if (ddns_cb->zone != NULL) {
16386fb29d29Schristos ddns_cb_forget_zone(ddns_cb);
16396fb29d29Schristos }
16406fb29d29Schristos
16416fb29d29Schristos ddns_cb->state = DDNS_STATE_ADD_PTR;
16426fb29d29Schristos ddns_cb->cur_func = ddns_ptr_add;
16436fb29d29Schristos
16446fb29d29Schristos result = ddns_modify_ptr(ddns_cb, MDL);
16456fb29d29Schristos if (result == ISC_R_SUCCESS) {
16466fb29d29Schristos return;
16476fb29d29Schristos }
16486fb29d29Schristos }
16496fb29d29Schristos break;
16506fb29d29Schristos
16516fb29d29Schristos case DNS_R_YXRRSET:
16526fb29d29Schristos logstr = "an entry that is either static or "
16536fb29d29Schristos "owned by another client exists.";
16546fb29d29Schristos break;
16556fb29d29Schristos
16566fb29d29Schristos case DNS_R_NXRRSET:
16576fb29d29Schristos logstr = "static entry of the other protocol type exists.";
16586fb29d29Schristos break;
16596fb29d29Schristos
16606fb29d29Schristos default:
16616fb29d29Schristos logstr = isc_result_totext(eresult);
16626fb29d29Schristos break;
16636fb29d29Schristos }
16646fb29d29Schristos
16656fb29d29Schristos if (logstr != NULL) {
16666fb29d29Schristos log_error("Forward map from %.*s to %s FAILED: %s",
16676fb29d29Schristos (int)ddns_cb->fwd_name.len,
16686fb29d29Schristos (const char *)ddns_cb->fwd_name.data,
16696fb29d29Schristos ddns_address, logstr);
16706fb29d29Schristos }
16716fb29d29Schristos
16726fb29d29Schristos ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
16736fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
16746fb29d29Schristos /*
16756fb29d29Schristos * A single DDNS operation may require several calls depending on
16766fb29d29Schristos * the current state as the prerequisites for the first message
16776fb29d29Schristos * may not succeed requiring a second operation and potentially
16786fb29d29Schristos * a ptr operation after that. The commit_leases operation is
16796fb29d29Schristos * invoked at the end of this set of operations in order to require
16806fb29d29Schristos * a single write for all of the changes. We call commit_leases
16816fb29d29Schristos * here rather than immediately after the call to update the lease
16826fb29d29Schristos * text in order to save any previously written data.
16836fb29d29Schristos */
16846fb29d29Schristos commit_leases();
16856fb29d29Schristos return;
16866fb29d29Schristos }
16876fb29d29Schristos
16886fb29d29Schristos static void
ddns_fwd_srv_connector(struct lease * lease,struct iasubopt * lease6,struct binding_scope ** inscope,dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)16896fb29d29Schristos ddns_fwd_srv_connector(struct lease *lease,
16906fb29d29Schristos struct iasubopt *lease6,
16916fb29d29Schristos struct binding_scope **inscope,
16926fb29d29Schristos dhcp_ddns_cb_t *ddns_cb,
16936fb29d29Schristos isc_result_t eresult)
16946fb29d29Schristos {
16956fb29d29Schristos isc_result_t result = ISC_R_FAILURE;
16966fb29d29Schristos
16976fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
16986fb29d29Schristos log_info ("DDNS: ddns_fwd_srv_connector: %s eresult: %d",
16996fb29d29Schristos dump_ddns_cb(ddns_cb), eresult);
17006fb29d29Schristos #endif
17016fb29d29Schristos
17026fb29d29Schristos if (ddns_cb == NULL) {
17036fb29d29Schristos /* nothing to do */
17046fb29d29Schristos return;
17056fb29d29Schristos }
17066fb29d29Schristos
17076fb29d29Schristos if (eresult == ISC_R_SUCCESS) {
17086fb29d29Schristos /*
17096fb29d29Schristos * If we have updates dispatch as appropriate,
17106fb29d29Schristos * if not do FQDN binding if desired.
17116fb29d29Schristos */
17126fb29d29Schristos
17136fb29d29Schristos if (ddns_cb->flags & DDNS_UPDATE_ADDR) {
17146fb29d29Schristos ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN;
17156fb29d29Schristos ddns_cb->cur_func = ddns_fwd_srv_add1;
17166fb29d29Schristos result = ddns_modify_fwd(ddns_cb, MDL);
17176fb29d29Schristos } else if ((ddns_cb->flags & DDNS_UPDATE_PTR) &&
17186fb29d29Schristos (ddns_cb->rev_name.len != 0)) {
17196fb29d29Schristos ddns_cb->state = DDNS_STATE_ADD_PTR;
17206fb29d29Schristos ddns_cb->cur_func = ddns_ptr_add;
17216fb29d29Schristos result = ddns_modify_ptr(ddns_cb, MDL);
17226fb29d29Schristos } else {
17236fb29d29Schristos ddns_update_lease_text(ddns_cb, inscope);
17246fb29d29Schristos }
17256fb29d29Schristos }
17266fb29d29Schristos
17276fb29d29Schristos
17286fb29d29Schristos if (result == ISC_R_SUCCESS) {
17296fb29d29Schristos ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL);
17306fb29d29Schristos } else {
17316fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
17326fb29d29Schristos }
17336fb29d29Schristos
17346fb29d29Schristos return;
17356fb29d29Schristos }
17366fb29d29Schristos
17376fb29d29Schristos /*
17386fb29d29Schristos * If the first query fails, the updater MUST NOT delete the DNS name. It
17396fb29d29Schristos * may be that the host whose lease on the server has expired has moved
17406fb29d29Schristos * to another network and obtained a lease from a different server,
17416fb29d29Schristos * which has caused the client's A RR to be replaced. It may also be
17426fb29d29Schristos * that some other client has been configured with a name that matches
17436fb29d29Schristos * the name of the DHCP client, and the policy was that the last client
17446fb29d29Schristos * to specify the name would get the name. In this case, the DHCID RR
17456fb29d29Schristos * will no longer match the updater's notion of the client-identity of
17466fb29d29Schristos * the host pointed to by the DNS name.
17476fb29d29Schristos * -- "Interaction between DHCP and DNS"
17486fb29d29Schristos */
17496fb29d29Schristos
1750ce893c7eSchristos static void
ddns_fwd_srv_rem2(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)17516fb29d29Schristos ddns_fwd_srv_rem2(dhcp_ddns_cb_t *ddns_cb,
17526fb29d29Schristos isc_result_t eresult)
17536fb29d29Schristos {
17546fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
17556fb29d29Schristos log_info ("DDNS: ddns_fwd_srv_rem2: %s eresult: %d",
17566fb29d29Schristos dump_ddns_cb(ddns_cb), eresult);
17576fb29d29Schristos #endif
17586fb29d29Schristos
17596fb29d29Schristos /*
17606fb29d29Schristos * To get here we have already managed to remove the A/AAAA
17616fb29d29Schristos * record and are trying to remove the DHCID/TXT record as well.
17626fb29d29Schristos * On success (removed DHCID/TXT) or YXRRSET (DHCID/TXT still in
17636fb29d29Schristos * use by something else) we clean up the lease.
17646fb29d29Schristos * On some other error we don't clean up the lease and hope that
17656fb29d29Schristos * if we try this again it will work. An example would be if we
17666fb29d29Schristos * got a timeout as the DNS server halted between the first and
17676fb29d29Schristos * second steps. The DNS server would still have the DHCID/TXT
17686fb29d29Schristos * and we would like to remove that in the future.
17696fb29d29Schristos *
17706fb29d29Schristos * On success set the EXECUTE_NEXT flag which triggers any
17716fb29d29Schristos * add that is next in the chain.
17726fb29d29Schristos */
17736fb29d29Schristos if ((eresult == ISC_R_SUCCESS) ||
17746fb29d29Schristos (eresult == DNS_R_YXRRSET)) {
17756fb29d29Schristos ddns_update_lease_text(ddns_cb, NULL);
17766fb29d29Schristos eresult = ISC_R_SUCCESS;
17776fb29d29Schristos }
17786fb29d29Schristos
17796fb29d29Schristos /* Do the next operation */
17806fb29d29Schristos if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
17816fb29d29Schristos /* if we have zone information get rid of it */
17826fb29d29Schristos if (ddns_cb->zone != NULL) {
17836fb29d29Schristos ddns_cb_forget_zone(ddns_cb);
17846fb29d29Schristos }
17856fb29d29Schristos
17866fb29d29Schristos ddns_cb->state = DDNS_STATE_REM_PTR;
17876fb29d29Schristos ddns_cb->cur_func = ddns_ptr_remove;
17886fb29d29Schristos if (eresult == ISC_R_SUCCESS)
17896fb29d29Schristos ddns_cb->flags |= DDNS_EXECUTE_NEXT;
17906fb29d29Schristos
17916fb29d29Schristos eresult = ddns_modify_ptr(ddns_cb, MDL);
17926fb29d29Schristos if (eresult == ISC_R_SUCCESS) {
17936fb29d29Schristos return;
17946fb29d29Schristos }
17956fb29d29Schristos }
17966fb29d29Schristos
17976fb29d29Schristos ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
17986fb29d29Schristos ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
17996fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
18006fb29d29Schristos return;
18016fb29d29Schristos }
18026fb29d29Schristos
18036fb29d29Schristos
18046fb29d29Schristos /*
18056fb29d29Schristos * First action routine when trying to remove a fwd
18066fb29d29Schristos * this will be called after the ddns queries have completed
18076fb29d29Schristos * if we succeeded in removing the fwd we go to the next step (if any)
18086fb29d29Schristos * if not we cleanup and leave.
18096fb29d29Schristos */
18106fb29d29Schristos
1811ce893c7eSchristos static void
ddns_fwd_srv_rem1(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)18126fb29d29Schristos ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb,
18136fb29d29Schristos isc_result_t eresult)
18146fb29d29Schristos {
18156fb29d29Schristos isc_result_t result = eresult;
18166fb29d29Schristos char ddns_address[MAX_ADDRESS_STRING_LEN];
18176fb29d29Schristos
18186fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
18196fb29d29Schristos log_info ("DDNS: ddns_fwd_srv_rem1: %s eresult: %d",
18206fb29d29Schristos dump_ddns_cb(ddns_cb), eresult);
18216fb29d29Schristos #endif
18226fb29d29Schristos
18236fb29d29Schristos switch(eresult) {
18246fb29d29Schristos case ISC_R_SUCCESS:
18256fb29d29Schristos /* Construct a printable form of the address for logging */
18266fb29d29Schristos strcpy(ddns_address, piaddr(ddns_cb->address));
18276fb29d29Schristos log_info("Removed forward map from %.*s to %s",
18286fb29d29Schristos (int)ddns_cb->fwd_name.len,
18296fb29d29Schristos (const char*)ddns_cb->fwd_name.data,
18306fb29d29Schristos ddns_address);
18316fb29d29Schristos
18326fb29d29Schristos /* Do the second step of the FWD removal */
18336fb29d29Schristos ddns_cb->state = DDNS_STATE_REM_FW_NXRR;
18346fb29d29Schristos ddns_cb->cur_func = ddns_fwd_srv_rem2;
18356fb29d29Schristos result = ddns_modify_fwd(ddns_cb, MDL);
18366fb29d29Schristos if (result == ISC_R_SUCCESS) {
18376fb29d29Schristos return;
18386fb29d29Schristos }
18396fb29d29Schristos break;
18406fb29d29Schristos
18416fb29d29Schristos case DNS_R_NXRRSET:
18426fb29d29Schristos case DNS_R_NXDOMAIN:
18436fb29d29Schristos /* A result of not found means rem1 did not find a guard of
18446fb29d29Schristos * our * type. From this we assume either there are no address
18456fb29d29Schristos * record(s) of our type to delete or they are unguarded and
18466fb29d29Schristos * therefore presumed to be static. Either way, we're done
18476fb29d29Schristos * unless we're in DSMM and ddns-other-guard-is-dynamic is on.
18486fb29d29Schristos * In which case we need to see if a guard of the other type
18496fb29d29Schristos * exists, which permits us to delete any address records of
18506fb29d29Schristos * our type. */
18516fb29d29Schristos #define DSMM_OGD (DDNS_DUAL_STACK_MIXED_MODE | \
18526fb29d29Schristos DDNS_OTHER_GUARD_IS_DYNAMIC)
18536fb29d29Schristos if ((ddns_cb->flags & DSMM_OGD) == DSMM_OGD) {
18546fb29d29Schristos ddns_cb->state = DDNS_STATE_REM_FW_DSMM_OTHER;
18556fb29d29Schristos ddns_cb->cur_func = ddns_fwd_srv_rem2;
18566fb29d29Schristos result = ddns_modify_fwd(ddns_cb, MDL);
18576fb29d29Schristos if (result == ISC_R_SUCCESS) {
18586fb29d29Schristos return;
18596fb29d29Schristos }
18606fb29d29Schristos break;
18616fb29d29Schristos }
18626fb29d29Schristos
18636fb29d29Schristos ddns_update_lease_text(ddns_cb, NULL);
18646fb29d29Schristos
18656fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
18666fb29d29Schristos log_info("DDNS: no forward map to remove. %p", ddns_cb);
18676fb29d29Schristos #endif
18686fb29d29Schristos /* Trigger the add operation */
18696fb29d29Schristos eresult = ISC_R_SUCCESS;
18706fb29d29Schristos
18716fb29d29Schristos /* Fall through */
18726fb29d29Schristos default:
18736fb29d29Schristos
18746fb29d29Schristos /* We do the remove operation in most cases
18756fb29d29Schristos * but we don't want to continue with adding a forward
18766fb29d29Schristos * record if the forward removal had issues so we
18776fb29d29Schristos * check the eresult and set the EXECUTE_NEXT flag on
18786fb29d29Schristos * success.
18796fb29d29Schristos */
18806fb29d29Schristos
18816fb29d29Schristos /* Do the remove operation */
18826fb29d29Schristos if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
18836fb29d29Schristos /* if we have zone information get rid of it */
18846fb29d29Schristos if (ddns_cb->zone != NULL) {
18856fb29d29Schristos ddns_cb_forget_zone(ddns_cb);
18866fb29d29Schristos }
18876fb29d29Schristos
18886fb29d29Schristos ddns_cb->state = DDNS_STATE_REM_PTR;
18896fb29d29Schristos ddns_cb->cur_func = ddns_ptr_remove;
18906fb29d29Schristos if (eresult == ISC_R_SUCCESS)
18916fb29d29Schristos ddns_cb->flags |= DDNS_EXECUTE_NEXT;
18926fb29d29Schristos
18936fb29d29Schristos result = ddns_modify_ptr(ddns_cb, MDL);
18946fb29d29Schristos if (result == ISC_R_SUCCESS) {
18956fb29d29Schristos return;
18966fb29d29Schristos }
18976fb29d29Schristos }
18986fb29d29Schristos break;
18996fb29d29Schristos }
19006fb29d29Schristos
19016fb29d29Schristos ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
19026fb29d29Schristos ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
19036fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
19046fb29d29Schristos }
19056fb29d29Schristos
19066fb29d29Schristos /*%<
19076fb29d29Schristos * Remove relevant entries from DNS.
19086fb29d29Schristos *
19096fb29d29Schristos * \li lease - lease to start with if this is for v4
19106fb29d29Schristos *
19116fb29d29Schristos * \li lease6 - lease to start with if this is for v6
19126fb29d29Schristos *
19136fb29d29Schristos * \li add_ddns_cb - control block for additional DDNS work. This
19146fb29d29Schristos * is used when the code is going to add a DDNS entry after removing
19156fb29d29Schristos * the current entry.
19166fb29d29Schristos *
19176fb29d29Schristos * \li active - indication about the status of the lease. It is
19186fb29d29Schristos * ISC_TRUE if the lease is still active, and FALSE if the lease
19196fb29d29Schristos * is inactive. This is used to indicate if the lease is inactive or going
19206fb29d29Schristos * to inactive so we can avoid trying to update the lease with cb pointers
19216fb29d29Schristos * and text information if it isn't useful.
19226fb29d29Schristos *
19236fb29d29Schristos * Returns
19246fb29d29Schristos * \li #ISC_R_FAILURE - badness occurred and we weren't able to do what was wanted
19256fb29d29Schristos * \li #ISC_R_SUCCESS - we were able to do stuff but it's in progress
19266fb29d29Schristos *
19276fb29d29Schristos * in both cases any additional block has been passed on to it's handler
19286fb29d29Schristos */
19296fb29d29Schristos
19306fb29d29Schristos isc_result_t
ddns_removals(struct lease * lease,struct iasubopt * lease6,dhcp_ddns_cb_t * add_ddns_cb,isc_boolean_t active)19316fb29d29Schristos ddns_removals(struct lease *lease,
19326fb29d29Schristos struct iasubopt *lease6,
19336fb29d29Schristos dhcp_ddns_cb_t *add_ddns_cb,
19346fb29d29Schristos isc_boolean_t active)
19356fb29d29Schristos {
19366fb29d29Schristos isc_result_t rcode, execute_add = ISC_R_FAILURE;
19376fb29d29Schristos struct binding_scope **scope = NULL;
19386fb29d29Schristos isc_result_t result = ISC_R_FAILURE;
19396fb29d29Schristos dhcp_ddns_cb_t *ddns_cb = NULL;
19406fb29d29Schristos struct data_string leaseid;
19416fb29d29Schristos
19426fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
19436fb29d29Schristos log_info ("DDNS: ddns_removals: %s",
19446fb29d29Schristos dump_ddns_cb(add_ddns_cb));
19456fb29d29Schristos #endif
19466fb29d29Schristos
19476fb29d29Schristos /*
19486fb29d29Schristos * See if we need to cancel an outstanding request. Mostly this is
19496fb29d29Schristos * used to handle the case where this routine is called twice for
19506fb29d29Schristos * the same release or abandon event.
19516fb29d29Schristos *
19526fb29d29Schristos * When called from the dns code as part of an update request
19536fb29d29Schristos * (add_ddns_cb != NULL) any outstanding requests will have already
19546fb29d29Schristos * been cancelled.
19556fb29d29Schristos *
19566fb29d29Schristos * If the new request is just a removal and we have an outstanding
19576fb29d29Schristos * request we have several options:
19586fb29d29Schristos *
19596fb29d29Schristos * - we are doing an update or we are doing a removal and the active
19606fb29d29Schristos * flag has changed from TRUE to FALSE. In these cases we need to
19616fb29d29Schristos * cancel the old request and start the new one.
19626fb29d29Schristos *
19636fb29d29Schristos * - other wise we are doing a removal with the active flag unchanged.
19646fb29d29Schristos * In this case we can let the current removal continue and do not need
19656fb29d29Schristos * to start a new one. If the old request included an update to be
19666fb29d29Schristos * done after the removal we need to kill the update part of the
19676fb29d29Schristos * request.
19686fb29d29Schristos */
19696fb29d29Schristos
19706fb29d29Schristos if (add_ddns_cb == NULL) {
19716fb29d29Schristos if ((lease != NULL) && (lease->ddns_cb != NULL)) {
19726fb29d29Schristos ddns_cb = lease->ddns_cb;
19736fb29d29Schristos
19746fb29d29Schristos /*
19756fb29d29Schristos * Is the old request an update or did the
19766fb29d29Schristos * the active flag change?
19776fb29d29Schristos */
19786fb29d29Schristos if (((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
19796fb29d29Schristos (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) ||
19806fb29d29Schristos (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) ||
19816fb29d29Schristos ((active == ISC_FALSE) &&
19826fb29d29Schristos ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) {
19836fb29d29Schristos /* Cancel the current request */
19846fb29d29Schristos ddns_cancel(lease->ddns_cb, MDL);
19856fb29d29Schristos lease->ddns_cb = NULL;
19866fb29d29Schristos } else {
19876fb29d29Schristos /* Remvoval, check and remove updates */
19886fb29d29Schristos if (ddns_cb->next_op != NULL) {
19896fb29d29Schristos destroy_ddns_cb(ddns_cb->next_op, MDL);
19906fb29d29Schristos ddns_cb->next_op = NULL;
19916fb29d29Schristos }
19926fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
19936fb29d29Schristos log_info("DDNS %s(%d): removal already in "
19946fb29d29Schristos "progress new ddns_cb=%p",
19956fb29d29Schristos MDL, ddns_cb);
19966fb29d29Schristos #endif
19976fb29d29Schristos return (ISC_R_SUCCESS);
19986fb29d29Schristos }
19996fb29d29Schristos } else if ((lease6 != NULL) && (lease6->ddns_cb != NULL)) {
20006fb29d29Schristos ddns_cb = lease6->ddns_cb;
20016fb29d29Schristos
20026fb29d29Schristos /*
20036fb29d29Schristos * Is the old request an update or did the
20046fb29d29Schristos * the active flag change?
20056fb29d29Schristos */
20066fb29d29Schristos if (((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
20076fb29d29Schristos (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) ||
20086fb29d29Schristos (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) ||
20096fb29d29Schristos ((active == ISC_FALSE) &&
20106fb29d29Schristos ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) {
20116fb29d29Schristos /* Cancel the current request */
20126fb29d29Schristos ddns_cancel(lease6->ddns_cb, MDL);
20136fb29d29Schristos lease6->ddns_cb = NULL;
20146fb29d29Schristos } else {
20156fb29d29Schristos /* Remvoval, check and remove updates */
20166fb29d29Schristos if (ddns_cb->next_op != NULL) {
20176fb29d29Schristos destroy_ddns_cb(ddns_cb->next_op, MDL);
20186fb29d29Schristos ddns_cb->next_op = NULL;
20196fb29d29Schristos }
20206fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
20216fb29d29Schristos log_info("DDNS %s(%d): removal already in "
20226fb29d29Schristos "progress new ddns_cb=%p",
20236fb29d29Schristos MDL, ddns_cb);
20246fb29d29Schristos #endif
20256fb29d29Schristos return (ISC_R_SUCCESS);
20266fb29d29Schristos }
20276fb29d29Schristos }
20286fb29d29Schristos ddns_cb = NULL;
20296fb29d29Schristos }
20306fb29d29Schristos
20316fb29d29Schristos /* allocate our control block */
20326fb29d29Schristos ddns_cb = ddns_cb_alloc(MDL);
20336fb29d29Schristos if (ddns_cb == NULL) {
20346fb29d29Schristos goto cleanup;
20356fb29d29Schristos }
20366fb29d29Schristos
20376fb29d29Schristos /* Set the conflict detection flags based on global configuration */
20386fb29d29Schristos copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask);
20396fb29d29Schristos
20406fb29d29Schristos /*
20416fb29d29Schristos * For v4 we flag static leases so we don't try
20426fb29d29Schristos * and manipulate the lease later. For v6 we don't
20436fb29d29Schristos * get static leases and don't need to flag them.
20446fb29d29Schristos */
20456fb29d29Schristos if (lease != NULL) {
20466fb29d29Schristos scope = &(lease->scope);
20476fb29d29Schristos ddns_cb->address = lease->ip_addr;
20486fb29d29Schristos if (lease->flags & STATIC_LEASE)
20496fb29d29Schristos ddns_cb->flags |= DDNS_STATIC_LEASE;
20506fb29d29Schristos } else if (lease6 != NULL) {
20516fb29d29Schristos scope = &(lease6->scope);
20526fb29d29Schristos memcpy(&ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
20536fb29d29Schristos ddns_cb->address.len = 16;
20546fb29d29Schristos } else
20556fb29d29Schristos goto cleanup;
20566fb29d29Schristos
20576fb29d29Schristos /*
20586fb29d29Schristos * Set the flag bit if the lease is active, that is it isn't
20596fb29d29Schristos * expired or released. This is used to determine if we need
20606fb29d29Schristos * to update the scope information for both v4 and v6 and
20616fb29d29Schristos * the lease information for v6 when the response
20626fb29d29Schristos * from the DNS code is processed.
20636fb29d29Schristos */
20646fb29d29Schristos if (active == ISC_TRUE) {
20656fb29d29Schristos ddns_cb->flags |= DDNS_ACTIVE_LEASE;
20666fb29d29Schristos }
20676fb29d29Schristos
20686fb29d29Schristos /* No scope implies that DDNS has not been performed for this lease. */
20696fb29d29Schristos if (*scope == NULL)
20706fb29d29Schristos goto cleanup;
20716fb29d29Schristos
20726fb29d29Schristos if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) &&
20736fb29d29Schristos (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM))
20746fb29d29Schristos goto cleanup;
20756fb29d29Schristos
20766fb29d29Schristos /* Assume that we are removing both records */
20776fb29d29Schristos ddns_cb->flags |= DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR;
20786fb29d29Schristos
20796fb29d29Schristos /* and that we want to do the add call */
20806fb29d29Schristos execute_add = ISC_R_SUCCESS;
20816fb29d29Schristos
20826fb29d29Schristos /*
20836fb29d29Schristos * Look up stored names.
20846fb29d29Schristos */
20856fb29d29Schristos
20866fb29d29Schristos /*
20876fb29d29Schristos * Find the fwd name and copy it to the control block. If we don't
20886fb29d29Schristos * have it we can't delete the fwd record but we can still try to
20896fb29d29Schristos * remove the ptr record and cleanup the lease information if the
20906fb29d29Schristos * client did the fwd update.
20916fb29d29Schristos */
20926fb29d29Schristos if (!find_bound_string(&ddns_cb->fwd_name, *scope, "ddns-fwd-name")) {
20936fb29d29Schristos /* don't try and delete the A, or do the add */
20946fb29d29Schristos ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
20956fb29d29Schristos execute_add = ISC_R_FAILURE;
20966fb29d29Schristos
20976fb29d29Schristos /* Check if client did update */
20986fb29d29Schristos if (find_bound_string(&ddns_cb->fwd_name, *scope,
20996fb29d29Schristos "ddns-client-fqdn")) {
21006fb29d29Schristos ddns_cb->flags |= DDNS_CLIENT_DID_UPDATE;
21016fb29d29Schristos }
21026fb29d29Schristos }
21036fb29d29Schristos
21046fb29d29Schristos /*
21056fb29d29Schristos * Find the txt or dhcid tag and copy it to the control block. If we
21066fb29d29Schristos * don't have one this isn't an interim or standard record so we can't
21076fb29d29Schristos * delete the A record using this mechanism but we can delete the ptr
21086fb29d29Schristos * record. In this case we will attempt to do any requested next step.
21096fb29d29Schristos */
21106fb29d29Schristos memset(&leaseid, 0, sizeof(leaseid));
21116fb29d29Schristos if (find_bound_string (&leaseid, *scope, ddns_standard_tag)) {
21126fb29d29Schristos /* We have a standard tag */
21136fb29d29Schristos ddns_cb->lease_tag = ddns_standard_tag;
21146fb29d29Schristos ddns_cb->dhcid_class = dns_rdatatype_dhcid;
21156fb29d29Schristos ddns_cb->other_dhcid_class = dns_rdatatype_txt;
21166fb29d29Schristos data_string_copy(&ddns_cb->dhcid, &leaseid, MDL);
21176fb29d29Schristos data_string_forget(&leaseid, MDL);
21186fb29d29Schristos } else if (find_bound_string (&leaseid, *scope, ddns_interim_tag)) {
21196fb29d29Schristos /* we have an interim tag */
21206fb29d29Schristos ddns_cb->lease_tag = ddns_interim_tag;
21216fb29d29Schristos ddns_cb->dhcid_class = dns_rdatatype_txt;
21226fb29d29Schristos ddns_cb->other_dhcid_class = dns_rdatatype_dhcid;
21236fb29d29Schristos if (dhcid_fromlease(&ddns_cb->dhcid, &leaseid) !=
21246fb29d29Schristos ISC_R_SUCCESS) {
21256fb29d29Schristos /* We couldn't convert the dhcid from the lease
21266fb29d29Schristos * version to the dns version. We can't delete
21276fb29d29Schristos * the A record but can continue to the ptr
21286fb29d29Schristos */
21296fb29d29Schristos ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
21306fb29d29Schristos }
21316fb29d29Schristos data_string_forget(&leaseid, MDL);
21326fb29d29Schristos } else {
21336fb29d29Schristos ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
21346fb29d29Schristos }
21356fb29d29Schristos
21366fb29d29Schristos /*
21376fb29d29Schristos * Find the rev name and copy it to the control block. If we don't
21386fb29d29Schristos * have it we can't get rid of it but we can try to remove the fwd
21396fb29d29Schristos * pointer if desired.
21406fb29d29Schristos */
21416fb29d29Schristos if (!find_bound_string(&ddns_cb->rev_name, *scope, "ddns-rev-name")) {
21426fb29d29Schristos ddns_cb->flags &= ~DDNS_UPDATE_PTR;
21436fb29d29Schristos }
21446fb29d29Schristos
21456fb29d29Schristos
21466fb29d29Schristos /*
21476fb29d29Schristos * If we have a second control block for doing an add
21486fb29d29Schristos * after the remove finished attach it to our control block.
21496fb29d29Schristos */
21506fb29d29Schristos ddns_cb->next_op = add_ddns_cb;
21516fb29d29Schristos
21526fb29d29Schristos /*
21536fb29d29Schristos * Now that we've collected the information we can try to process it.
21546fb29d29Schristos * If necessary we call an appropriate routine to send a message and
21556fb29d29Schristos * provide it with an action routine to run on the control block given
21566fb29d29Schristos * the results of the message. We have three entry points from here,
21576fb29d29Schristos * one for removing the A record, the next for removing the PTR and
21586fb29d29Schristos * the third for doing any requested add.
21596fb29d29Schristos */
21606fb29d29Schristos if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
21616fb29d29Schristos if (ddns_cb->fwd_name.len != 0) {
21626fb29d29Schristos ddns_cb->state = DDNS_STATE_REM_FW_YXDHCID;
21636fb29d29Schristos ddns_cb->cur_func = ddns_fwd_srv_rem1;
21646fb29d29Schristos
21656fb29d29Schristos rcode = ddns_modify_fwd(ddns_cb, MDL);
21666fb29d29Schristos if (rcode == ISC_R_SUCCESS) {
21676fb29d29Schristos ddns_update_lease_ptr(lease, lease6, ddns_cb,
21686fb29d29Schristos ddns_cb, MDL);
21696fb29d29Schristos return (ISC_R_SUCCESS);
21706fb29d29Schristos }
21716fb29d29Schristos
21726fb29d29Schristos /*
21736fb29d29Schristos * We weren't able to process the request tag the
21746fb29d29Schristos * add so we won't execute it.
21756fb29d29Schristos */
21766fb29d29Schristos execute_add = ISC_R_FAILURE;
21776fb29d29Schristos goto cleanup;
21786fb29d29Schristos }
21796fb29d29Schristos else {
21806fb29d29Schristos /*remove info from scope */
21816fb29d29Schristos unset(*scope, "ddns-fwd-name");
21826fb29d29Schristos unset(*scope, ddns_cb->lease_tag);
21836fb29d29Schristos }
21846fb29d29Schristos }
21856fb29d29Schristos
21866fb29d29Schristos if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
21876fb29d29Schristos ddns_cb->state = DDNS_STATE_REM_PTR;
21886fb29d29Schristos ddns_cb->cur_func = ddns_ptr_remove;
21896fb29d29Schristos ddns_cb->flags |= DDNS_EXECUTE_NEXT;
21906fb29d29Schristos
21916fb29d29Schristos /*
21926fb29d29Schristos * if execute add isn't success remove the control block so
21936fb29d29Schristos * it won't be processed when the remove completes. We
21946fb29d29Schristos * also arrange to clean it up and get rid of it.
21956fb29d29Schristos */
21966fb29d29Schristos if (execute_add != ISC_R_SUCCESS) {
21976fb29d29Schristos ddns_cb->next_op = NULL;
21986fb29d29Schristos ddns_fwd_srv_connector(lease, lease6, scope,
21996fb29d29Schristos add_ddns_cb, execute_add);
22006fb29d29Schristos add_ddns_cb = NULL;
22016fb29d29Schristos }
22026fb29d29Schristos else {
22036fb29d29Schristos result = ISC_R_SUCCESS;
22046fb29d29Schristos }
22056fb29d29Schristos
22066fb29d29Schristos rcode = ddns_modify_ptr(ddns_cb, MDL);
22076fb29d29Schristos if (rcode == ISC_R_SUCCESS) {
22086fb29d29Schristos ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb,
22096fb29d29Schristos MDL);
22106fb29d29Schristos return (result);
22116fb29d29Schristos }
22126fb29d29Schristos
22136fb29d29Schristos /* We weren't able to process the request tag the
22146fb29d29Schristos * add so we won't execute it */
22156fb29d29Schristos execute_add = ISC_R_FAILURE;
22166fb29d29Schristos goto cleanup;
22176fb29d29Schristos }
22186fb29d29Schristos
22196fb29d29Schristos cleanup:
22206fb29d29Schristos /*
22216fb29d29Schristos * We've gotten here because we didn't need to send a message or
22226fb29d29Schristos * we failed when trying to do so. We send the additional cb
22236fb29d29Schristos * off to handle sending and/or cleanup and cleanup anything
22246fb29d29Schristos * we allocated here.
22256fb29d29Schristos */
22266fb29d29Schristos ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add);
22276fb29d29Schristos if (ddns_cb != NULL)
22286fb29d29Schristos destroy_ddns_cb(ddns_cb, MDL);
22296fb29d29Schristos
22306fb29d29Schristos return (result);
22316fb29d29Schristos }
22326fb29d29Schristos
22336fb29d29Schristos /* Convenience function for setting flag bits in a mask */
2234ce893c7eSchristos static void
set_flag(u_int16_t * flags,u_int16_t flag,u_int16_t value)2235ce893c7eSchristos set_flag (u_int16_t *flags,
22366fb29d29Schristos u_int16_t flag,
22376fb29d29Schristos u_int16_t value) {
22386fb29d29Schristos if (flags) {
22396fb29d29Schristos if (value) {
22406fb29d29Schristos *flags |= flag;
22416fb29d29Schristos } else {
22426fb29d29Schristos *flags &= ~flag;
22436fb29d29Schristos }
22446fb29d29Schristos }
22456fb29d29Schristos }
22466fb29d29Schristos
22476fb29d29Schristos /*
22486fb29d29Schristos * Convenience function which replicates the conflict flags set in one
22496fb29d29Schristos * mask to another, while preserving all other flags.
22506fb29d29Schristos */
copy_conflict_flags(u_int16_t * target,u_int16_t source)22516fb29d29Schristos void copy_conflict_flags(u_int16_t *target,
22526fb29d29Schristos u_int16_t source) {
22536fb29d29Schristos if (target) {
22546fb29d29Schristos /* Preserve non conflict flags */
22556fb29d29Schristos *target &= ~CONFLICT_BITS;
22566fb29d29Schristos
22576fb29d29Schristos /* Enable conflict flags per source */
22586fb29d29Schristos *target |= source & CONFLICT_BITS;
22596fb29d29Schristos }
22606fb29d29Schristos }
22616fb29d29Schristos
22626fb29d29Schristos /*
22636fb29d29Schristos * Given an option_state, create a mask of conflict detection flags based
22646fb29d29Schristos * on the appropriate configuration parameters within the option state.
22656fb29d29Schristos */
22666fb29d29Schristos u_int16_t
get_conflict_mask(struct option_state * options)22676fb29d29Schristos get_conflict_mask(struct option_state *options) {
22686fb29d29Schristos
22696fb29d29Schristos int ddns_update_conflict_detection = 1; /* default on */
22706fb29d29Schristos int ddns_dual_stack_mixed_mode = 0; /* default off */
22716fb29d29Schristos int ddns_guard_id_must_match = 1; /* default on */
22726fb29d29Schristos int ddns_other_guard_is_dynamic = 0; /* default off */
22736fb29d29Schristos struct option_cache *oc = NULL;
22746fb29d29Schristos
22756fb29d29Schristos u_int16_t mask = 0;
22766fb29d29Schristos oc = lookup_option(&server_universe, options, SV_DDNS_CONFLICT_DETECT);
22776fb29d29Schristos if (oc) {
22786fb29d29Schristos ddns_update_conflict_detection =
22796fb29d29Schristos evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
22806fb29d29Schristos NULL, &global_scope, oc, MDL);
22816fb29d29Schristos }
22826fb29d29Schristos
22836fb29d29Schristos set_flag(&mask, DDNS_CONFLICT_DETECTION,
22846fb29d29Schristos ddns_update_conflict_detection);
22856fb29d29Schristos
22866fb29d29Schristos if (!ddns_update_conflict_detection) {
22876fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
22886fb29d29Schristos log_info ("DDNS conflict detection: off");
22896fb29d29Schristos #endif
22906fb29d29Schristos /* Turn the rest of the conflict related flags off */
22916fb29d29Schristos set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE, 0);
22926fb29d29Schristos set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH, 0);
22936fb29d29Schristos set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC, 0);
22946fb29d29Schristos return (mask);
22956fb29d29Schristos }
22966fb29d29Schristos
22976fb29d29Schristos // Get the values
22986fb29d29Schristos oc = lookup_option(&server_universe, options,
22996fb29d29Schristos SV_DDNS_DUAL_STACK_MIXED_MODE);
23006fb29d29Schristos if (oc) {
23016fb29d29Schristos ddns_dual_stack_mixed_mode =
23026fb29d29Schristos evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
23036fb29d29Schristos NULL, &global_scope, oc, MDL);
23046fb29d29Schristos }
23056fb29d29Schristos
23066fb29d29Schristos oc = lookup_option(&server_universe, options,
23076fb29d29Schristos SV_DDNS_GUARD_ID_MUST_MATCH);
23086fb29d29Schristos if (oc) {
23096fb29d29Schristos ddns_guard_id_must_match =
23106fb29d29Schristos evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
23116fb29d29Schristos NULL, &global_scope, oc, MDL);
23126fb29d29Schristos }
23136fb29d29Schristos
23146fb29d29Schristos oc = lookup_option(&server_universe, options,
23156fb29d29Schristos SV_DDNS_OTHER_GUARD_IS_DYNAMIC);
23166fb29d29Schristos if (oc) {
23176fb29d29Schristos ddns_other_guard_is_dynamic =
23186fb29d29Schristos evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
23196fb29d29Schristos NULL, &global_scope, oc, MDL);
23206fb29d29Schristos }
23216fb29d29Schristos
23226fb29d29Schristos // Set the flags
23236fb29d29Schristos set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE,
23246fb29d29Schristos ddns_dual_stack_mixed_mode);
23256fb29d29Schristos
23266fb29d29Schristos set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH,
23276fb29d29Schristos ddns_guard_id_must_match);
23286fb29d29Schristos
23296fb29d29Schristos set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC,
23306fb29d29Schristos ddns_other_guard_is_dynamic);
23316fb29d29Schristos
23326fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
23336fb29d29Schristos log_info ("DDNS conflict behavior:\n"
23346fb29d29Schristos "\tddns-update-style: %s\n"
23356fb29d29Schristos "\tupdate-conflict-detection: %d\n"
23366fb29d29Schristos "\tddns-dual-stack-mixed-mode: %d\n"
23376fb29d29Schristos "\tddns-guard-id-must-match %d\n"
23386fb29d29Schristos "\tddns-other-guard-is-dynamic: %d\n",
23396fb29d29Schristos ddns_styles_values[ddns_update_style].name,
23406fb29d29Schristos ddns_update_conflict_detection,
23416fb29d29Schristos ddns_dual_stack_mixed_mode,
23426fb29d29Schristos ddns_guard_id_must_match,
23436fb29d29Schristos ddns_other_guard_is_dynamic);
23446fb29d29Schristos #endif
23456fb29d29Schristos return (mask);
23466fb29d29Schristos }
23476fb29d29Schristos
23486fb29d29Schristos #if defined (DEBUG_DNS_UPDATES)
23496fb29d29Schristos /* Type used for creating lists of function pointers and their names */
23506fb29d29Schristos typedef struct {
23516fb29d29Schristos void *ptr;
23526fb29d29Schristos char *name;
23536fb29d29Schristos } LabeledPtr;
23546fb29d29Schristos
23556fb29d29Schristos /* Returns the name of the function referred to by the given address */
23566fb29d29Schristos char*
dump_ddns_cb_func(void * func)23576fb29d29Schristos dump_ddns_cb_func(void *func) {
23586fb29d29Schristos static LabeledPtr funcs[] = {
23596fb29d29Schristos { ddns_ptr_add, "ddns_ptr_add" },
23606fb29d29Schristos { ddns_fwd_srv_add2, "ddns_fwd_srv_add2" },
23616fb29d29Schristos { ddns_fwd_srv_add1, "ddns_fwd_srv_add1" },
23626fb29d29Schristos { ddns_ptr_remove, "ddns_ptr_remove" },
23636fb29d29Schristos { ddns_fwd_srv_rem2, "ddns_fwd_srv_rem2" },
23646fb29d29Schristos { ddns_fwd_srv_rem1, "ddns_fwd_srv_rem1" },
23656fb29d29Schristos { ddns_fwd_srv_add3, "ddns_fwd_srv_adde" },
23666fb29d29Schristos { NULL, "unknown" }
23676fb29d29Schristos };
23686fb29d29Schristos
23696fb29d29Schristos LabeledPtr* lp = funcs;
23706fb29d29Schristos if (!func) {
23716fb29d29Schristos return ("<null>");
23726fb29d29Schristos }
23736fb29d29Schristos
23746fb29d29Schristos while ((lp->ptr) && (lp->ptr != func)) {
23756fb29d29Schristos ++lp;
23766fb29d29Schristos }
23776fb29d29Schristos
23786fb29d29Schristos return (lp->name);
23796fb29d29Schristos }
23806fb29d29Schristos
23816fb29d29Schristos /* Dumps basic control block info to the log */
23826fb29d29Schristos char*
dump_ddns_cb(dhcp_ddns_cb_t * ddns_cb)23836fb29d29Schristos dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb) {
23846fb29d29Schristos static char output_buf[4096];
23856fb29d29Schristos if (!ddns_cb) {
23866fb29d29Schristos return ("<ddns_cb is null>");
23876fb29d29Schristos }
23886fb29d29Schristos
23896fb29d29Schristos sprintf (output_buf, "ddns_cb: %p flags: %x state: %s cur_func: %s",
23906fb29d29Schristos ddns_cb, ddns_cb->flags,
23916fb29d29Schristos ddns_state_name(ddns_cb->state),
23926fb29d29Schristos dump_ddns_cb_func(ddns_cb->cur_func));
23936fb29d29Schristos
23946fb29d29Schristos return(output_buf);
23956fb29d29Schristos }
23966fb29d29Schristos #endif /* DEBUG_DNS_UPDATES */
23976fb29d29Schristos
23986fb29d29Schristos #endif /* NSUPDATE */
2399