Annotation of embedaddon/dhcp/common/dns.c, revision 1.1

1.1     ! misho       1: /* dns.c
        !             2: 
        !             3:    Domain Name Service subroutines. */
        !             4: 
        !             5: /*
        !             6:  * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
        !             7:  * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
        !             8:  * Copyright (c) 2001-2003 by Internet Software Consortium
        !             9:  *
        !            10:  * Permission to use, copy, modify, and distribute this software for any
        !            11:  * purpose with or without fee is hereby granted, provided that the above
        !            12:  * copyright notice and this permission notice appear in all copies.
        !            13:  *
        !            14:  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
        !            15:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            16:  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
        !            17:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            18:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            19:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
        !            20:  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            21:  *
        !            22:  *   Internet Systems Consortium, Inc.
        !            23:  *   950 Charter Street
        !            24:  *   Redwood City, CA 94063
        !            25:  *   <info@isc.org>
        !            26:  *   https://www.isc.org/
        !            27:  *
        !            28:  * This software has been written for Internet Systems Consortium
        !            29:  * by Ted Lemon in cooperation with Nominum, Inc.
        !            30:  * To learn more about Internet Systems Consortium, see
        !            31:  * ``https://www.isc.org/''.  To learn more about Nominum, Inc., see
        !            32:  * ``http://www.nominum.com''.
        !            33:  */
        !            34: 
        !            35: #include "dhcpd.h"
        !            36: #include "arpa/nameser.h"
        !            37: #include "dst/md5.h"
        !            38: 
        !            39: /* This file is kind of a crutch for the BIND 8 nsupdate code, which has
        !            40:  * itself been cruelly hacked from its original state.   What this code
        !            41:  * does is twofold: first, it maintains a database of zone cuts that can
        !            42:  * be used to figure out which server should be contacted to update any
        !            43:  * given domain name.   Secondly, it maintains a set of named TSIG keys,
        !            44:  * and associates those keys with zones.   When an update is requested for
        !            45:  * a particular zone, the key associated with that zone is used for the
        !            46:  * update.
        !            47:  *
        !            48:  * The way this works is that you define the domain name to which an
        !            49:  * SOA corresponds, and the addresses of some primaries for that domain name:
        !            50:  *
        !            51:  *     zone FOO.COM {
        !            52:  *       primary 10.0.17.1;
        !            53:  *       secondary 10.0.22.1, 10.0.23.1;
        !            54:  *       key "FOO.COM Key";
        !            55:  *     }
        !            56:  *
        !            57:  * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name
        !            58:  * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM",
        !            59:  * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*,
        !            60:  * looks for "FOO.COM", finds it. So it
        !            61:  * attempts the update to the primary for FOO.COM.   If that times out, it
        !            62:  * tries the secondaries.   You can list multiple primaries if you have some
        !            63:  * kind of magic name server that supports that.   You shouldn't list
        !            64:  * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't
        !            65:  * support update forwarding, AFAIK).   If no TSIG key is listed, the update
        !            66:  * is attempted without TSIG.
        !            67:  *
        !            68:  * The DHCP server tries to find an existing zone for any given name by
        !            69:  * trying to look up a local zone structure for each domain containing
        !            70:  * that name, all the way up to '.'.   If it finds one cached, it tries
        !            71:  * to use that one to do the update.   That's why it tries to update
        !            72:  * "FOO.COM" above, even though theoretically it should try GAZANGA...
        !            73:  * and TOPANGA... first.
        !            74:  *
        !            75:  * If the update fails with a predefined or cached zone (we'll get to
        !            76:  * those in a second), then it tries to find a more specific zone.   This
        !            77:  * is done by looking first for an SOA for GAZANGA.TOPANGA.FOO.COM.   Then
        !            78:  * an SOA for TOPANGA.FOO.COM is sought.   If during this search a predefined
        !            79:  * or cached zone is found, the update fails - there's something wrong
        !            80:  * somewhere.
        !            81:  *
        !            82:  * If a more specific zone _is_ found, that zone is cached for the length of
        !            83:  * its TTL in the same database as that described above.   TSIG updates are
        !            84:  * never done for cached zones - if you want TSIG updates you _must_
        !            85:  * write a zone definition linking the key to the zone.   In cases where you
        !            86:  * know for sure what the key is but do not want to hardcode the IP addresses
        !            87:  * of the primary or secondaries, a zone declaration can be made that doesn't
        !            88:  * include any primary or secondary declarations.   When the DHCP server
        !            89:  * encounters this while hunting up a matching zone for a name, it looks up
        !            90:  * the SOA, fills in the IP addresses, and uses that record for the update.
        !            91:  * If the SOA lookup returns NXRRSET, a warning is printed and the zone is
        !            92:  * discarded, TSIG key and all.   The search for the zone then continues as if
        !            93:  * the zone record hadn't been found.   Zones without IP addresses don't
        !            94:  * match when initially hunting for a predefined or cached zone to update.
        !            95:  *
        !            96:  * When an update is attempted and no predefined or cached zone is found
        !            97:  * that matches any enclosing domain of the domain being updated, the DHCP
        !            98:  * server goes through the same process that is done when the update to a
        !            99:  * predefined or cached zone fails - starting with the most specific domain
        !           100:  * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root),
        !           101:  * it tries to look up an SOA record.   When it finds one, it creates a cached
        !           102:  * zone and attempts an update, and gives up if the update fails.
        !           103:  *
        !           104:  * TSIG keys are defined like this:
        !           105:  *
        !           106:  *     key "FOO.COM Key" {
        !           107:  *             algorithm HMAC-MD5.SIG-ALG.REG.INT;
        !           108:  *             secret <Base64>;
        !           109:  *     }
        !           110:  *
        !           111:  * <Base64> is a number expressed in base64 that represents the key.
        !           112:  * It's also permissible to use a quoted string here - this will be
        !           113:  * translated as the ASCII bytes making up the string, and will not
        !           114:  * include any NUL termination.  The key name can be any text string,
        !           115:  * and the key type must be one of the key types defined in the draft
        !           116:  * or by the IANA.  Currently only the HMAC-MD5... key type is
        !           117:  * supported.
        !           118:  */
        !           119: 
        !           120: dns_zone_hash_t *dns_zone_hash;
        !           121: 
        !           122: #if defined (NSUPDATE)
        !           123: isc_result_t find_tsig_key (ns_tsig_key **key, const char *zname,
        !           124:                            struct dns_zone *zone)
        !           125: {
        !           126:        ns_tsig_key *tkey;
        !           127: 
        !           128:        if (!zone)
        !           129:                return ISC_R_NOTFOUND;
        !           130: 
        !           131:        if (!zone -> key) {
        !           132:                return ISC_R_KEY_UNKNOWN;
        !           133:        }
        !           134:        
        !           135:        if ((!zone -> key -> name ||
        !           136:             strlen (zone -> key -> name) > NS_MAXDNAME) ||
        !           137:            (!zone -> key -> algorithm ||
        !           138:             strlen (zone -> key -> algorithm) > NS_MAXDNAME) ||
        !           139:            (!zone -> key) ||
        !           140:            (!zone -> key -> key) ||
        !           141:            (zone -> key -> key -> len == 0)) {
        !           142:                return ISC_R_INVALIDKEY;
        !           143:        }
        !           144:        tkey = dmalloc (sizeof *tkey, MDL);
        !           145:        if (!tkey) {
        !           146:              nomem:
        !           147:                return ISC_R_NOMEMORY;
        !           148:        }
        !           149:        memset (tkey, 0, sizeof *tkey);
        !           150:        tkey -> data = dmalloc (zone -> key -> key -> len, MDL);
        !           151:        if (!tkey -> data) {
        !           152:                dfree (tkey, MDL);
        !           153:                goto nomem;
        !           154:        }
        !           155:        strcpy (tkey -> name, zone -> key -> name);
        !           156:        strcpy (tkey -> alg, zone -> key -> algorithm);
        !           157:        memcpy (tkey -> data,
        !           158:                zone -> key -> key -> value, zone -> key -> key -> len);
        !           159:        tkey -> len = zone -> key -> key -> len;
        !           160:        *key = tkey;
        !           161:        return ISC_R_SUCCESS;
        !           162: }
        !           163: 
        !           164: void tkey_free (ns_tsig_key **key)
        !           165: {
        !           166:        if ((*key) -> data)
        !           167:                dfree ((*key) -> data, MDL);
        !           168:        dfree ((*key), MDL);
        !           169:        *key = (ns_tsig_key *)0;
        !           170: }
        !           171: #endif
        !           172: 
        !           173: isc_result_t enter_dns_zone (struct dns_zone *zone)
        !           174: {
        !           175:        struct dns_zone *tz = (struct dns_zone *)0;
        !           176: 
        !           177:        if (dns_zone_hash) {
        !           178:                dns_zone_hash_lookup (&tz,
        !           179:                                      dns_zone_hash, zone -> name, 0, MDL);
        !           180:                if (tz == zone) {
        !           181:                        dns_zone_dereference (&tz, MDL);
        !           182:                        return ISC_R_SUCCESS;
        !           183:                }
        !           184:                if (tz) {
        !           185:                        dns_zone_hash_delete (dns_zone_hash,
        !           186:                                              zone -> name, 0, MDL);
        !           187:                        dns_zone_dereference (&tz, MDL);
        !           188:                }
        !           189:        } else {
        !           190:                if (!dns_zone_new_hash(&dns_zone_hash, DNS_HASH_SIZE, MDL))
        !           191:                        return ISC_R_NOMEMORY;
        !           192:        }
        !           193:        dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
        !           194:        return ISC_R_SUCCESS;
        !           195: }
        !           196: 
        !           197: isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
        !           198: {
        !           199:        int len;
        !           200:        char *tname = (char *)0;
        !           201:        isc_result_t status;
        !           202: 
        !           203:        if (!dns_zone_hash)
        !           204:                return ISC_R_NOTFOUND;
        !           205: 
        !           206:        len = strlen (name);
        !           207:        if (name [len - 1] != '.') {
        !           208:                tname = dmalloc ((unsigned)len + 2, MDL);
        !           209:                if (!tname)
        !           210:                        return ISC_R_NOMEMORY;
        !           211:                strcpy (tname, name);
        !           212:                tname [len] = '.';
        !           213:                tname [len + 1] = 0;
        !           214:                name = tname;
        !           215:        }
        !           216:        if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL))
        !           217:                status = ISC_R_NOTFOUND;
        !           218:        else
        !           219:                status = ISC_R_SUCCESS;
        !           220: 
        !           221:        if (tname)
        !           222:                dfree (tname, MDL);
        !           223:        return status;
        !           224: }
        !           225: 
        !           226: int dns_zone_dereference (ptr, file, line)
        !           227:        struct dns_zone **ptr;
        !           228:        const char *file;
        !           229:        int line;
        !           230: {
        !           231:        struct dns_zone *dns_zone;
        !           232: 
        !           233:        if (!ptr || !*ptr) {
        !           234:                log_error ("%s(%d): null pointer", file, line);
        !           235: #if defined (POINTER_DEBUG)
        !           236:                abort ();
        !           237: #else
        !           238:                return 0;
        !           239: #endif
        !           240:        }
        !           241: 
        !           242:        dns_zone = *ptr;
        !           243:        *ptr = (struct dns_zone *)0;
        !           244:        --dns_zone -> refcnt;
        !           245:        rc_register (file, line, ptr, dns_zone, dns_zone -> refcnt, 1, RC_MISC);
        !           246:        if (dns_zone -> refcnt > 0)
        !           247:                return 1;
        !           248: 
        !           249:        if (dns_zone -> refcnt < 0) {
        !           250:                log_error ("%s(%d): negative refcnt!", file, line);
        !           251: #if defined (DEBUG_RC_HISTORY)
        !           252:                dump_rc_history (dns_zone);
        !           253: #endif
        !           254: #if defined (POINTER_DEBUG)
        !           255:                abort ();
        !           256: #else
        !           257:                return 0;
        !           258: #endif
        !           259:        }
        !           260: 
        !           261:        if (dns_zone -> name)
        !           262:                dfree (dns_zone -> name, file, line);
        !           263:        if (dns_zone -> key)
        !           264:                omapi_auth_key_dereference (&dns_zone -> key, file, line);
        !           265:        if (dns_zone -> primary)
        !           266:                option_cache_dereference (&dns_zone -> primary, file, line);
        !           267:        if (dns_zone -> secondary)
        !           268:                option_cache_dereference (&dns_zone -> secondary, file, line);
        !           269:        dfree (dns_zone, file, line);
        !           270:        return 1;
        !           271: }
        !           272: 
        !           273: #if defined (NSUPDATE)
        !           274: isc_result_t find_cached_zone (const char *dname, ns_class class,
        !           275:                               char *zname, size_t zsize,
        !           276:                               struct in_addr *addrs,
        !           277:                               int naddrs, int *naddrout,
        !           278:                               struct dns_zone **zcookie)
        !           279: {
        !           280:        isc_result_t status = ISC_R_NOTFOUND;
        !           281:        const char *np;
        !           282:        struct dns_zone *zone = (struct dns_zone *)0;
        !           283:        struct data_string nsaddrs;
        !           284:        int ix;
        !           285: 
        !           286:        /* The absence of the zcookie pointer indicates that we
        !           287:           succeeded previously, but the update itself failed, meaning
        !           288:           that we shouldn't use the cached zone. */
        !           289:        if (!zcookie)
        !           290:                return ISC_R_NOTFOUND;
        !           291: 
        !           292:        /* We can't look up a null zone. */
        !           293:        if (!dname || !*dname)
        !           294:                return ISC_R_INVALIDARG;
        !           295: 
        !           296:        /*
        !           297:         * For each subzone, try to find a cached zone.
        !           298:         */
        !           299:        for (np = dname;;) {
        !           300:                status = dns_zone_lookup (&zone, np);
        !           301:                if (status == ISC_R_SUCCESS)
        !           302:                        break;
        !           303: 
        !           304:                np = strchr(np, '.');
        !           305:                if (np == NULL)
        !           306:                        break;
        !           307:                np++;
        !           308:        }
        !           309: 
        !           310:        if (status != ISC_R_SUCCESS)
        !           311:                return status;
        !           312: 
        !           313:        /* Make sure the zone is valid. */
        !           314:        if (zone -> timeout && zone -> timeout < cur_time) {
        !           315:                dns_zone_dereference (&zone, MDL);
        !           316:                return ISC_R_CANCELED;
        !           317:        }
        !           318: 
        !           319:        /* Make sure the zone name will fit. */
        !           320:        if (strlen (zone -> name) > zsize) {
        !           321:                dns_zone_dereference (&zone, MDL);
        !           322:                return ISC_R_NOSPACE;
        !           323:        }
        !           324:        strcpy (zname, zone -> name);
        !           325: 
        !           326:        memset (&nsaddrs, 0, sizeof nsaddrs);
        !           327:        ix = 0;
        !           328: 
        !           329:        if (zone -> primary) {
        !           330:                if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
        !           331:                                           (struct lease *)0,
        !           332:                                           (struct client_state *)0,
        !           333:                                           (struct option_state *)0,
        !           334:                                           (struct option_state *)0,
        !           335:                                           &global_scope,
        !           336:                                           zone -> primary, MDL)) {
        !           337:                        int ip = 0;
        !           338:                        while (ix < naddrs) {
        !           339:                                if (ip + 4 > nsaddrs.len)
        !           340:                                        break;
        !           341:                                memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
        !           342:                                ip += 4;
        !           343:                                ix++;
        !           344:                        }
        !           345:                        data_string_forget (&nsaddrs, MDL);
        !           346:                }
        !           347:        }
        !           348:        if (zone -> secondary) {
        !           349:                if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
        !           350:                                           (struct lease *)0,
        !           351:                                           (struct client_state *)0,
        !           352:                                           (struct option_state *)0,
        !           353:                                           (struct option_state *)0,
        !           354:                                           &global_scope,
        !           355:                                           zone -> secondary, MDL)) {
        !           356:                        int ip = 0;
        !           357:                        while (ix < naddrs) {
        !           358:                                if (ip + 4 > nsaddrs.len)
        !           359:                                        break;
        !           360:                                memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
        !           361:                                ip += 4;
        !           362:                                ix++;
        !           363:                        }
        !           364:                        data_string_forget (&nsaddrs, MDL);
        !           365:                }
        !           366:        }
        !           367: 
        !           368:        /* It's not an error for zcookie to have a value here - actually,
        !           369:           it's quite likely, because res_nupdate cycles through all the
        !           370:           names in the update looking for their zones. */
        !           371:        if (!*zcookie)
        !           372:                dns_zone_reference (zcookie, zone, MDL);
        !           373:        dns_zone_dereference (&zone, MDL);
        !           374:        if (naddrout)
        !           375:                *naddrout = ix;
        !           376:        return ISC_R_SUCCESS;
        !           377: }
        !           378: 
        !           379: void forget_zone (struct dns_zone **zone)
        !           380: {
        !           381:        dns_zone_dereference (zone, MDL);
        !           382: }
        !           383: 
        !           384: void repudiate_zone (struct dns_zone **zone)
        !           385: {
        !           386:        /* XXX Currently we're not differentiating between a cached
        !           387:           XXX zone and a zone that's been repudiated, which means
        !           388:           XXX that if we reap cached zones, we blow away repudiated
        !           389:           XXX zones.   This isn't a big problem since we're not yet
        !           390:           XXX caching zones... :'} */
        !           391: 
        !           392:        (*zone) -> timeout = cur_time - 1;
        !           393:        dns_zone_dereference (zone, MDL);
        !           394: }
        !           395: 
        !           396: void cache_found_zone (ns_class class,
        !           397:                       char *zname, struct in_addr *addrs, int naddrs)
        !           398: {
        !           399:        struct dns_zone *zone = (struct dns_zone *)0;
        !           400:        int ix = strlen (zname);
        !           401: 
        !           402:        if (zname [ix - 1] == '.')
        !           403:                ix = 0;
        !           404: 
        !           405:        /* See if there's already such a zone. */
        !           406:        if (dns_zone_lookup (&zone, zname) == ISC_R_SUCCESS) {
        !           407:                /* If it's not a dynamic zone, leave it alone. */
        !           408:                if (!zone -> timeout)
        !           409:                        return;
        !           410:                /* Address may have changed, so just blow it away. */
        !           411:                if (zone -> primary)
        !           412:                        option_cache_dereference (&zone -> primary, MDL);
        !           413:                if (zone -> secondary)
        !           414:                        option_cache_dereference (&zone -> secondary, MDL);
        !           415:        } else if (!dns_zone_allocate (&zone, MDL))
        !           416:                return;
        !           417: 
        !           418:        if (!zone -> name) {
        !           419:                zone -> name =
        !           420:                        dmalloc (strlen (zname) + 1 + (ix != 0), MDL);
        !           421:                if (!zone -> name) {
        !           422:                        dns_zone_dereference (&zone, MDL);
        !           423:                        return;
        !           424:                }
        !           425:                strcpy (zone -> name, zname);
        !           426:                /* Add a trailing '.' if it was missing. */
        !           427:                if (ix) {
        !           428:                        zone -> name [ix] = '.';
        !           429:                        zone -> name [ix + 1] = 0;
        !           430:                }
        !           431:        }
        !           432: 
        !           433:        /* XXX Need to get the lower-level code to push the actual zone
        !           434:           XXX TTL up to us. */
        !           435:        zone -> timeout = cur_time + 1800;
        !           436:        
        !           437:        if (!option_cache_allocate (&zone -> primary, MDL)) {
        !           438:                dns_zone_dereference (&zone, MDL);
        !           439:                return;
        !           440:        }
        !           441:        if (!buffer_allocate (&zone -> primary -> data.buffer,
        !           442:                              naddrs * sizeof (struct in_addr), MDL)) {
        !           443:                dns_zone_dereference (&zone, MDL);
        !           444:                return;
        !           445:        }
        !           446:        memcpy (zone -> primary -> data.buffer -> data,
        !           447:                addrs, naddrs * sizeof *addrs);
        !           448:        zone -> primary -> data.data =
        !           449:                &zone -> primary -> data.buffer -> data [0];
        !           450:        zone -> primary -> data.len = naddrs * sizeof *addrs;
        !           451: 
        !           452:        enter_dns_zone (zone);
        !           453: }
        !           454: 
        !           455: /* Have to use TXT records for now. */
        !           456: #define T_DHCID T_TXT
        !           457: 
        !           458: int get_dhcid (struct data_string *id,
        !           459:               int type, const u_int8_t *data, unsigned len)
        !           460: {
        !           461:        unsigned char buf[MD5_DIGEST_LENGTH];
        !           462:        MD5_CTX md5;
        !           463:        int i;
        !           464: 
        !           465:        /* Types can only be 0..(2^16)-1. */
        !           466:        if (type < 0 || type > 65535)
        !           467:                return 0;
        !           468: 
        !           469:        /* Hexadecimal MD5 digest plus two byte type and NUL. */
        !           470:        if (!buffer_allocate (&id -> buffer,
        !           471:                              (MD5_DIGEST_LENGTH * 2) + 3, MDL))
        !           472:                return 0;
        !           473:        id -> data = id -> buffer -> data;
        !           474: 
        !           475:        /*
        !           476:         * DHCP clients and servers should use the following forms of client
        !           477:         * identification, starting with the most preferable, and finishing
        !           478:         * with the least preferable.  If the client does not send any of these
        !           479:         * forms of identification, the DHCP/DDNS interaction is not defined by
        !           480:         * this specification.  The most preferable form of identification is
        !           481:         * the Globally Unique Identifier Option [TBD].  Next is the DHCP
        !           482:         * Client Identifier option.  Last is the client's link-layer address,
        !           483:         * as conveyed in its DHCPREQUEST message.  Implementors should note
        !           484:         * that the link-layer address cannot be used if there are no
        !           485:         * significant bytes in the chaddr field of the DHCP client's request,
        !           486:         * because this does not constitute a unique identifier.
        !           487:         *   -- "Interaction between DHCP and DNS"
        !           488:         *      <draft-ietf-dhc-dhcp-dns-12.txt>
        !           489:         *      M. Stapp, Y. Rekhter
        !           490:         */
        !           491: 
        !           492:        /* Put the type in the first two bytes. */
        !           493:        id->buffer->data[0] = "0123456789abcdef"[(type >> 4) & 0xf];
        !           494:        /* This should have been [type & 0xf] but now that
        !           495:         * it is in use we need to leave it this way in order
        !           496:         * to avoid disturbing customer's lease files
        !           497:         */
        !           498:        id -> buffer -> data [1] = "0123456789abcdef" [type % 15];
        !           499: 
        !           500:        /* Mash together an MD5 hash of the identifier. */
        !           501:        MD5_Init (&md5);
        !           502:        MD5_Update (&md5, data, len);
        !           503:        MD5_Final (buf, &md5);
        !           504: 
        !           505:        /* Convert into ASCII. */
        !           506:        for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
        !           507:                id -> buffer -> data [i * 2 + 2] =
        !           508:                        "0123456789abcdef" [(buf [i] >> 4) & 0xf];
        !           509:                id -> buffer -> data [i * 2 + 3] =
        !           510:                        "0123456789abcdef" [buf [i] & 0xf];
        !           511:        }
        !           512:        id -> len = MD5_DIGEST_LENGTH * 2 + 2;
        !           513:        id -> buffer -> data [id -> len] = 0;
        !           514:        id -> terminated = 1;
        !           515: 
        !           516:        return 1;
        !           517: }
        !           518: 
        !           519: /* Now for the DDNS update code that is shared between client and
        !           520:    server... */
        !           521: 
        !           522: isc_result_t
        !           523: ddns_update_fwd(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
        !           524:                struct data_string *ddns_dhcid, unsigned long ttl,
        !           525:                unsigned rrsetp, unsigned conflict) {
        !           526:        ns_updque updqueue;
        !           527:        ns_updrec *updrec;
        !           528:        isc_result_t result;
        !           529:        const char *logstr;
        !           530:        char ddns_address[
        !           531:                sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
        !           532:        int ddns_address_type;
        !           533: 
        !           534:        /* 
        !           535:         * We want to delete either A or AAAA records, depending on
        !           536:         * whether we have an IPv4 or an IPv6 address.
        !           537:         */
        !           538:        if (ddns_addr.len == 4) {
        !           539:                ddns_address_type = T_A;
        !           540:        } else if (ddns_addr.len == 16) {
        !           541:                ddns_address_type = T_AAAA;
        !           542:        } else {
        !           543:                return ISC_R_INVALIDARG;
        !           544:        }
        !           545:        strcpy(ddns_address, piaddr(ddns_addr));
        !           546: 
        !           547:        /*
        !           548:         * When a DHCP client or server intends to update an A RR, it first
        !           549:         * prepares a DNS UPDATE query which includes as a prerequisite the
        !           550:         * assertion that the name does not exist.  The update section of the
        !           551:         * query attempts to add the new name and its IP address mapping (an A
        !           552:         * RR), and the DHCID RR with its unique client-identity.
        !           553:         *   -- "Interaction between DHCP and DNS"
        !           554:         */
        !           555: 
        !           556:        ISC_LIST_INIT (updqueue);
        !           557: 
        !           558:        /*
        !           559:         * A RR does not exist.
        !           560:         */
        !           561:        updrec = minires_mkupdrec (S_PREREQ,
        !           562:                                   (const char *)ddns_fwd_name -> data,
        !           563:                                   C_IN, ddns_address_type, 0);
        !           564:        if (!updrec) {
        !           565:                result = ISC_R_NOMEMORY;
        !           566:                goto error;
        !           567:        }
        !           568: 
        !           569:        updrec -> r_data = (unsigned char *)0;
        !           570:        updrec -> r_size = 0;
        !           571:        updrec -> r_opcode = rrsetp ? NXRRSET : NXDOMAIN;
        !           572: 
        !           573:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           574: 
        !           575: 
        !           576:        /*
        !           577:         * Add A RR.
        !           578:         */
        !           579:        updrec = minires_mkupdrec (S_UPDATE,
        !           580:                                   (const char *)ddns_fwd_name -> data,
        !           581:                                   C_IN, ddns_address_type, ttl);
        !           582:        if (!updrec) {
        !           583:                result = ISC_R_NOMEMORY;
        !           584:                goto error;
        !           585:        }
        !           586: 
        !           587:        updrec -> r_data = (unsigned char *)ddns_address;
        !           588:        updrec -> r_size = strlen (ddns_address);
        !           589:        updrec -> r_opcode = ADD;
        !           590: 
        !           591:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           592: 
        !           593: 
        !           594:        /*
        !           595:         * Add DHCID RR.
        !           596:         */
        !           597:        updrec = minires_mkupdrec (S_UPDATE,
        !           598:                                   (const char *)ddns_fwd_name -> data,
        !           599:                                   C_IN, T_DHCID, ttl);
        !           600:        if (!updrec) {
        !           601:                result = ISC_R_NOMEMORY;
        !           602:                goto error;
        !           603:        }
        !           604: 
        !           605:        updrec -> r_data = ddns_dhcid -> data;
        !           606:        updrec -> r_size = ddns_dhcid -> len;
        !           607:        updrec -> r_opcode = ADD;
        !           608: 
        !           609:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           610: 
        !           611: 
        !           612:        /*
        !           613:         * Attempt to perform the update.
        !           614:         */
        !           615:        result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
        !           616: 
        !           617: #ifdef DEBUG_DNS_UPDATES
        !           618:        print_dns_status ((int)result, &updqueue);
        !           619: #endif
        !           620: 
        !           621:        /*
        !           622:         * If this update operation succeeds, the updater can conclude that it
        !           623:         * has added a new name whose only RRs are the A and DHCID RR records.
        !           624:         * The A RR update is now complete (and a client updater is finished,
        !           625:         * while a server might proceed to perform a PTR RR update).
        !           626:         *   -- "Interaction between DHCP and DNS"
        !           627:         */
        !           628: 
        !           629:        if (result == ISC_R_SUCCESS) {
        !           630:                log_info ("Added new forward map from %.*s to %s",
        !           631:                          (int)ddns_fwd_name -> len,
        !           632:                          (const char *)ddns_fwd_name -> data, ddns_address);
        !           633:                goto error;
        !           634:        }
        !           635: 
        !           636: 
        !           637:        /*
        !           638:         * If the first update operation fails with YXDOMAIN, the updater can
        !           639:         * conclude that the intended name is in use.  The updater then
        !           640:         * attempts to confirm that the DNS name is not being used by some
        !           641:         * other host. The updater prepares a second UPDATE query in which the
        !           642:         * prerequisite is that the desired name has attached to it a DHCID RR
        !           643:         * whose contents match the client identity.  The update section of
        !           644:         * this query deletes the existing A records on the name, and adds the
        !           645:         * A record that matches the DHCP binding and the DHCID RR with the
        !           646:         * client identity.
        !           647:         *   -- "Interaction between DHCP and DNS"
        !           648:         */
        !           649: 
        !           650:        if (result != (rrsetp ? ISC_R_YXRRSET : ISC_R_YXDOMAIN)) {
        !           651:                log_error ("Unable to add forward map from %.*s to %s: %s",
        !           652:                           (int)ddns_fwd_name -> len,
        !           653:                           (const char *)ddns_fwd_name -> data, ddns_address,
        !           654:                           isc_result_totext (result));
        !           655:                goto error;
        !           656:        }
        !           657: 
        !           658:        while (!ISC_LIST_EMPTY (updqueue)) {
        !           659:                updrec = ISC_LIST_HEAD (updqueue);
        !           660:                ISC_LIST_UNLINK (updqueue, updrec, r_link);
        !           661:                minires_freeupdrec (updrec);
        !           662:        }
        !           663: 
        !           664:        /* If we're doing conflict resolution, we use a set of prereqs.  If
        !           665:         * not, we delete the DHCID in addition to all A rrsets.
        !           666:         */
        !           667:        if (conflict) {
        !           668:                /*
        !           669:                 * DHCID RR exists, and matches client identity.
        !           670:                 */
        !           671:                updrec = minires_mkupdrec (S_PREREQ,
        !           672:                                           (const char *)ddns_fwd_name -> data,
        !           673:                                           C_IN, T_DHCID, 0);
        !           674:                if (!updrec) {
        !           675:                        result = ISC_R_NOMEMORY;
        !           676:                        goto error;
        !           677:                }
        !           678:        
        !           679:                updrec -> r_data = ddns_dhcid -> data;
        !           680:                updrec -> r_size = ddns_dhcid -> len;
        !           681:                updrec -> r_opcode = YXRRSET;
        !           682: 
        !           683:                ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           684:        } else {
        !           685:                /*
        !           686:                 * Conflict detection override: delete DHCID RRs.
        !           687:                 */
        !           688:                updrec = minires_mkupdrec(S_UPDATE, 
        !           689:                                          (const char *)ddns_fwd_name->data,
        !           690:                                          C_IN, T_DHCID, 0);
        !           691: 
        !           692:                if (!updrec) {
        !           693:                        result = ISC_R_NOMEMORY;
        !           694:                        goto error;
        !           695:                }
        !           696: 
        !           697:                updrec->r_data = NULL;
        !           698:                updrec->r_size = 0;
        !           699:                updrec->r_opcode = DELETE;
        !           700: 
        !           701:                ISC_LIST_APPEND(updqueue, updrec, r_link);
        !           702: 
        !           703: 
        !           704:                /*
        !           705:                 * With all other DHCID RR's deleted, add this client's
        !           706:                 * DHCID unconditionally (as update-conflict-detection is
        !           707:                 * disabled).
        !           708:                 */
        !           709:                updrec = minires_mkupdrec(S_UPDATE,
        !           710:                                          (const char *)ddns_fwd_name->data,
        !           711:                                          C_IN, T_DHCID, ttl);
        !           712:                if (!updrec) {
        !           713:                        result = ISC_R_NOMEMORY;
        !           714:                        goto error;
        !           715:                }
        !           716:  
        !           717:                updrec->r_data = ddns_dhcid->data;
        !           718:                updrec->r_size = ddns_dhcid->len;
        !           719:                updrec->r_opcode = ADD;
        !           720:  
        !           721:                ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           722:        }
        !           723: 
        !           724: 
        !           725:        /*
        !           726:         * Delete A RRset.
        !           727:         */
        !           728:        updrec = minires_mkupdrec (S_UPDATE,
        !           729:                                   (const char *)ddns_fwd_name -> data,
        !           730:                                   C_IN, ddns_address_type, 0);
        !           731:        if (!updrec) {
        !           732:                result = ISC_R_NOMEMORY;
        !           733:                goto error;
        !           734:        }
        !           735: 
        !           736:        updrec -> r_data = (unsigned char *)0;
        !           737:        updrec -> r_size = 0;
        !           738:        updrec -> r_opcode = DELETE;
        !           739: 
        !           740:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           741: 
        !           742: 
        !           743:        /*
        !           744:         * Add A RR.
        !           745:         */
        !           746:        updrec = minires_mkupdrec (S_UPDATE,
        !           747:                                   (const char *)ddns_fwd_name -> data,
        !           748:                                   C_IN, ddns_address_type, ttl);
        !           749:        if (!updrec) {
        !           750:                result = ISC_R_NOMEMORY;
        !           751:                goto error;
        !           752:        }
        !           753: 
        !           754:        updrec -> r_data = (unsigned char *)ddns_address;
        !           755:        updrec -> r_size = strlen (ddns_address);
        !           756:        updrec -> r_opcode = ADD;
        !           757: 
        !           758:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           759: 
        !           760: 
        !           761:        /*
        !           762:         * Attempt to perform the update.
        !           763:         */
        !           764:        result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
        !           765: 
        !           766:        switch (result) {
        !           767:            case ISC_R_SUCCESS:
        !           768:                logstr = NULL;
        !           769:                break;
        !           770: 
        !           771:            case ISC_R_YXRRSET:
        !           772:            case ISC_R_YXDOMAIN:
        !           773:                logstr = "DHCID mismatch, belongs to another client.";
        !           774:                break;
        !           775: 
        !           776:            case ISC_R_NXRRSET:
        !           777:            case ISC_R_NXDOMAIN:
        !           778:                logstr = "Has an address record but no DHCID, not mine.";
        !           779:                break;
        !           780: 
        !           781:            default:
        !           782:                logstr = isc_result_totext(result);
        !           783:                break;
        !           784:        }
        !           785: 
        !           786:        if (logstr != NULL)
        !           787:                log_error("Forward map from %.*s to %s FAILED: %s",
        !           788:                                   (int)ddns_fwd_name -> len,
        !           789:                                   (const char *)ddns_fwd_name -> data,
        !           790:                                   ddns_address, logstr);
        !           791:        else
        !           792:                log_info("Added new forward map from %.*s to %s",
        !           793:                          (int)ddns_fwd_name -> len,
        !           794:                          (const char *)ddns_fwd_name -> data, ddns_address);
        !           795: 
        !           796: #if defined (DEBUG_DNS_UPDATES)
        !           797:        print_dns_status ((int)result, &updqueue);
        !           798: #endif
        !           799: 
        !           800:        /*
        !           801:         * If this query succeeds, the updater can conclude that the current
        !           802:         * client was the last client associated with the domain name, and that
        !           803:         * the name now contains the updated A RR. The A RR update is now
        !           804:         * complete (and a client updater is finished, while a server would
        !           805:         * then proceed to perform a PTR RR update).
        !           806:         *   -- "Interaction between DHCP and DNS"
        !           807:         */
        !           808: 
        !           809:        /*
        !           810:         * If the second query fails with NXRRSET, the updater must conclude
        !           811:         * that the client's desired name is in use by another host.  At this
        !           812:         * juncture, the updater can decide (based on some administrative
        !           813:         * configuration outside of the scope of this document) whether to let
        !           814:         * the existing owner of the name keep that name, and to (possibly)
        !           815:         * perform some name disambiguation operation on behalf of the current
        !           816:         * client, or to replace the RRs on the name with RRs that represent
        !           817:         * the current client. If the configured policy allows replacement of
        !           818:         * existing records, the updater submits a query that deletes the
        !           819:         * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
        !           820:         * represent the IP address and client-identity of the new client.
        !           821:         *   -- "Interaction between DHCP and DNS"
        !           822:         */
        !           823: 
        !           824:   error:
        !           825:        while (!ISC_LIST_EMPTY (updqueue)) {
        !           826:                updrec = ISC_LIST_HEAD (updqueue);
        !           827:                ISC_LIST_UNLINK (updqueue, updrec, r_link);
        !           828:                minires_freeupdrec (updrec);
        !           829:        }
        !           830: 
        !           831:        return result;
        !           832: }
        !           833: 
        !           834: isc_result_t
        !           835: ddns_remove_fwd(struct data_string *ddns_fwd_name,
        !           836:                struct iaddr ddns_addr, 
        !           837:                struct data_string *ddns_dhcid) {
        !           838:        ns_updque updqueue;
        !           839:        ns_updrec *updrec;
        !           840:        isc_result_t result = SERVFAIL;
        !           841:        char ddns_address[
        !           842:                sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
        !           843:        int ddns_address_type;
        !           844: 
        !           845:        /* 
        !           846:         * We want to delete either A or AAAA records, depending on
        !           847:         * whether we have an IPv4 or an IPv6 address.
        !           848:         */
        !           849:        if (ddns_addr.len == 4) {
        !           850:                ddns_address_type = T_A;
        !           851:        } else if (ddns_addr.len == 16) {
        !           852:                ddns_address_type = T_AAAA;
        !           853:        } else {
        !           854:                return ISC_R_INVALIDARG;
        !           855:        }
        !           856:        strcpy(ddns_address, piaddr(ddns_addr));
        !           857: 
        !           858:        /*
        !           859:         * The entity chosen to handle the A record for this client (either the
        !           860:         * client or the server) SHOULD delete the A record that was added when
        !           861:         * the lease was made to the client.
        !           862:         *
        !           863:         * In order to perform this delete, the updater prepares an UPDATE
        !           864:         * query which contains two prerequisites.  The first prerequisite
        !           865:         * asserts that the DHCID RR exists whose data is the client identity
        !           866:         * described in Section 4.3. The second prerequisite asserts that the
        !           867:         * data in the A RR contains the IP address of the lease that has
        !           868:         * expired or been released.
        !           869:         *   -- "Interaction between DHCP and DNS"
        !           870:         */
        !           871: 
        !           872:        ISC_LIST_INIT (updqueue);
        !           873: 
        !           874:        /*
        !           875:         * DHCID RR exists, and matches client identity.
        !           876:         */
        !           877:        updrec = minires_mkupdrec (S_PREREQ,
        !           878:                                   (const char *)ddns_fwd_name -> data,
        !           879:                                   C_IN, T_DHCID,0);
        !           880:        if (!updrec) {
        !           881:                result = ISC_R_NOMEMORY;
        !           882:                goto error;
        !           883:        }
        !           884: 
        !           885:        updrec -> r_data = ddns_dhcid -> data;
        !           886:        updrec -> r_size = ddns_dhcid -> len;
        !           887:        updrec -> r_opcode = YXRRSET;
        !           888: 
        !           889:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           890: 
        !           891: 
        !           892:        /*
        !           893:         * Address RR (A/AAAA) matches the expiring lease.
        !           894:         */
        !           895:        updrec = minires_mkupdrec (S_PREREQ,
        !           896:                                   (const char *)ddns_fwd_name -> data,
        !           897:                                   C_IN, ddns_address_type, 0);
        !           898:        if (!updrec) {
        !           899:                result = ISC_R_NOMEMORY;
        !           900:                goto error;
        !           901:        }
        !           902: 
        !           903:        updrec -> r_data = (unsigned char *)ddns_address;
        !           904:        updrec -> r_size = strlen (ddns_address);
        !           905:        updrec -> r_opcode = YXRRSET;
        !           906: 
        !           907:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           908: 
        !           909: 
        !           910:        /*
        !           911:         * Delete appropriate Address RR (A/AAAA).
        !           912:         */
        !           913:        updrec = minires_mkupdrec (S_UPDATE,
        !           914:                                   (const char *)ddns_fwd_name -> data,
        !           915:                                   C_IN, ddns_address_type, 0);
        !           916:        if (!updrec) {
        !           917:                result = ISC_R_NOMEMORY;
        !           918:                goto error;
        !           919:        }
        !           920: 
        !           921:        updrec -> r_data = (unsigned char *)ddns_address;
        !           922:        updrec -> r_size = strlen (ddns_address);
        !           923:        updrec -> r_opcode = DELETE;
        !           924: 
        !           925:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           926: 
        !           927:        /*
        !           928:         * Attempt to perform the update.
        !           929:         */
        !           930:        result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
        !           931:        print_dns_status ((int)result, &updqueue);
        !           932: 
        !           933:        /*
        !           934:         * If the query fails, the updater MUST NOT delete the DNS name.  It
        !           935:         * may be that the host whose lease on the server has expired has moved
        !           936:         * to another network and obtained a lease from a different server,
        !           937:         * which has caused the client's A RR to be replaced. It may also be
        !           938:         * that some other client has been configured with a name that matches
        !           939:         * the name of the DHCP client, and the policy was that the last client
        !           940:         * to specify the name would get the name.  In this case, the DHCID RR
        !           941:         * will no longer match the updater's notion of the client-identity of
        !           942:         * the host pointed to by the DNS name.
        !           943:         *   -- "Interaction between DHCP and DNS"
        !           944:         */
        !           945: 
        !           946:        if (result != ISC_R_SUCCESS) {
        !           947:                /* If the rrset isn't there, we didn't need to do the
        !           948:                   delete, which is success. */
        !           949:                if (result == ISC_R_NXRRSET || result == ISC_R_NXDOMAIN)
        !           950:                        result = ISC_R_SUCCESS; 
        !           951:                goto error;
        !           952:        }
        !           953: 
        !           954:        while (!ISC_LIST_EMPTY (updqueue)) {
        !           955:                updrec = ISC_LIST_HEAD (updqueue);
        !           956:                ISC_LIST_UNLINK (updqueue, updrec, r_link);
        !           957:                minires_freeupdrec (updrec);
        !           958:        }
        !           959: 
        !           960:        /*
        !           961:         * If the deletion of the desired address succeeded (its A or AAAA
        !           962:         * RR was removed above), and there are zero other A or AAAA records
        !           963:         * left for this domain, then we can delete the DHCID record as well.
        !           964:         * We can't delete the DHCID record above because it's possible the
        !           965:         * client has more than one valid address added to this domain name,
        !           966:         * by this or other DHCP servers.
        !           967:         *
        !           968:         * Essentially, this final update is a cleanup operation that is only
        !           969:         * intended to succeed after the last address has been removed from
        !           970:         * DNS (which is only expected to happen after the client is not
        !           971:         * reasonably in possession of those addresses).
        !           972:         */
        !           973:        ISC_LIST_INIT (updqueue);
        !           974: 
        !           975:        /*
        !           976:         * A RR does not exist.
        !           977:         */
        !           978:        updrec = minires_mkupdrec(S_PREREQ, (const char *)ddns_fwd_name->data,
        !           979:                                  C_IN, T_A, 0);
        !           980:        if (updrec == NULL) {
        !           981:                result = ISC_R_NOMEMORY;
        !           982:                goto error;
        !           983:        }
        !           984: 
        !           985:        updrec->r_data = NULL;
        !           986:        updrec->r_size = 0;
        !           987:        updrec->r_opcode = NXRRSET;
        !           988: 
        !           989:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           990: 
        !           991:        /*
        !           992:         * AAAA RR does not exist.
        !           993:         */
        !           994:        updrec = minires_mkupdrec(S_PREREQ, (const char *)ddns_fwd_name->data,
        !           995:                                  C_IN, T_AAAA, 0);
        !           996: 
        !           997:        if (updrec == NULL) {
        !           998:                result = ISC_R_NOMEMORY;
        !           999:                goto error;
        !          1000:        }
        !          1001: 
        !          1002:        updrec->r_data = NULL;
        !          1003:        updrec->r_size = 0;
        !          1004:        updrec->r_opcode = NXRRSET;
        !          1005: 
        !          1006:        ISC_LIST_APPEND(updqueue, updrec, r_link);
        !          1007: 
        !          1008:        /*
        !          1009:         * Delete appropriate DHCID RR.
        !          1010:         */
        !          1011:        updrec = minires_mkupdrec (S_UPDATE,
        !          1012:                                  (const char *)ddns_fwd_name -> data,
        !          1013:                                   C_IN, T_DHCID, 0);
        !          1014:        if (!updrec) {
        !          1015:                result = ISC_R_NOMEMORY;
        !          1016:                goto error;
        !          1017:        }
        !          1018: 
        !          1019:        updrec -> r_data = ddns_dhcid -> data;
        !          1020:        updrec -> r_size = ddns_dhcid -> len;
        !          1021:        updrec -> r_opcode = DELETE;
        !          1022: 
        !          1023:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !          1024: 
        !          1025:        /*
        !          1026:         * Attempt to perform the update.
        !          1027:         */
        !          1028:        result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
        !          1029:        print_dns_status ((int)result, &updqueue);
        !          1030: 
        !          1031:        /* Fall through. */
        !          1032:   error:
        !          1033: 
        !          1034:        while (!ISC_LIST_EMPTY (updqueue)) {
        !          1035:                updrec = ISC_LIST_HEAD (updqueue);
        !          1036:                ISC_LIST_UNLINK (updqueue, updrec, r_link);
        !          1037:                minires_freeupdrec (updrec);
        !          1038:        }
        !          1039: 
        !          1040:        return result;
        !          1041: }
        !          1042: 
        !          1043: 
        !          1044: #endif /* NSUPDATE */
        !          1045: 
        !          1046: HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
        !          1047:                dns_zone_reference, dns_zone_dereference, do_case_hash)

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>