Annotation of embedaddon/dhcp/server/ddns.c, revision 1.1

1.1     ! misho       1: /* ddns.c
        !             2: 
        !             3:    Dynamic DNS updates. */
        !             4: 
        !             5: /*
        !             6:  * Copyright (c) 2004-2007,2009-2010 by
        !             7:  *                                 Internet Systems Consortium, Inc. ("ISC")
        !             8:  * Copyright (c) 2000-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 donated to Internet Systems Consortium
        !            29:  * by Damien Neil of Nominum, Inc.
        !            30:  *
        !            31:  * To learn more about Internet Systems Consortium, see
        !            32:  * ``https://www.isc.org/''.   To learn more about Nominum, Inc., see
        !            33:  * ``http://www.nominum.com''.
        !            34:  */
        !            35: 
        !            36: #include "dhcpd.h"
        !            37: #include "dst/md5.h"
        !            38: #include "minires/minires.h"
        !            39: 
        !            40: #ifdef NSUPDATE
        !            41: 
        !            42: /* DN: No way of checking that there is enough space in a data_string's
        !            43:    buffer.  Be certain to allocate enough!
        !            44:    TL: This is why the expression evaluation code allocates a *new*
        !            45:    data_string.   :') */
        !            46: static void data_string_append (struct data_string *ds1,
        !            47:                                struct data_string *ds2)
        !            48: {
        !            49:        memcpy (ds1 -> buffer -> data + ds1 -> len,
        !            50:                ds2 -> data,
        !            51:                ds2 -> len);
        !            52:        ds1 -> len += ds2 -> len;
        !            53: }
        !            54: 
        !            55: static isc_result_t ddns_update_ptr (struct data_string *ddns_fwd_name,
        !            56:                                     struct data_string *ddns_rev_name,
        !            57:                                     unsigned long ttl)
        !            58: {
        !            59:        ns_updque updqueue;
        !            60:        ns_updrec *updrec;
        !            61:        isc_result_t result = ISC_R_UNEXPECTED;
        !            62: 
        !            63:        /*
        !            64:         * The DHCP server submits a DNS query which deletes all of the PTR RRs
        !            65:         * associated with the lease IP address, and adds a PTR RR whose data
        !            66:         * is the client's (possibly disambiguated) host name. The server also
        !            67:         * adds a DHCID RR specified in Section 4.3.
        !            68:         *   -- "Interaction between DHCP and DNS"
        !            69:         */
        !            70: 
        !            71:        ISC_LIST_INIT (updqueue);
        !            72: 
        !            73:        /*
        !            74:         * Delete all PTR RRs.
        !            75:         */
        !            76:        updrec = minires_mkupdrec (S_UPDATE,
        !            77:                                   (const char *)ddns_rev_name -> data,
        !            78:                                   C_IN, T_PTR, 0);
        !            79:        if (!updrec) {
        !            80:                result = ISC_R_NOMEMORY;
        !            81:                goto error;
        !            82:        }
        !            83: 
        !            84:        updrec -> r_data = (unsigned char *)0;
        !            85:        updrec -> r_size = 0;
        !            86:        updrec -> r_opcode = DELETE;
        !            87: 
        !            88:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !            89: 
        !            90:        /*
        !            91:         * Add PTR RR.
        !            92:         */
        !            93:        updrec = minires_mkupdrec (S_UPDATE,
        !            94:                                   (const char *)ddns_rev_name -> data,
        !            95:                                   C_IN, T_PTR, ttl);
        !            96:        if (!updrec) {
        !            97:                result = ISC_R_NOMEMORY;
        !            98:                goto error;
        !            99:        }
        !           100: 
        !           101:        updrec -> r_data = ddns_fwd_name -> data;
        !           102:        updrec -> r_size = ddns_fwd_name -> len;
        !           103:        updrec -> r_opcode = ADD;
        !           104: 
        !           105:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           106: 
        !           107:        /*
        !           108:         * Attempt to perform the update.
        !           109:         */
        !           110:        result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
        !           111: #if defined (DEBUG)
        !           112:        print_dns_status ((int)result, &updqueue);
        !           113: #endif
        !           114:        if (result == ISC_R_SUCCESS) {
        !           115:                log_info ("added reverse map from %.*s to %.*s",
        !           116:                          (int)ddns_rev_name -> len,
        !           117:                          (const char *)ddns_rev_name -> data,
        !           118:                          (int)ddns_fwd_name -> len,
        !           119:                          (const char *)ddns_fwd_name -> data);
        !           120:        } else {
        !           121:                log_error ("unable to add reverse map from %.*s to %.*s: %s",
        !           122:                           (int)ddns_rev_name -> len,
        !           123:                           (const char *)ddns_rev_name -> data,
        !           124:                           (int)ddns_fwd_name -> len,
        !           125:                           (const char *)ddns_fwd_name -> data,
        !           126:                           isc_result_totext (result));
        !           127:        }
        !           128: 
        !           129:        /* Fall through. */
        !           130:       error:
        !           131: 
        !           132:        while (!ISC_LIST_EMPTY (updqueue)) {
        !           133:                updrec = ISC_LIST_HEAD (updqueue);
        !           134:                ISC_LIST_UNLINK (updqueue, updrec, r_link);
        !           135:                minires_freeupdrec (updrec);
        !           136:        }
        !           137: 
        !           138:        return result;
        !           139: }
        !           140: 
        !           141: 
        !           142: static isc_result_t ddns_remove_ptr (struct data_string *ddns_rev_name)
        !           143: {
        !           144:        ns_updque updqueue;
        !           145:        ns_updrec *updrec;
        !           146:        isc_result_t result;
        !           147: 
        !           148:        /*
        !           149:         * When a lease expires or a DHCP client issues a DHCPRELEASE request,
        !           150:         * the DHCP server SHOULD delete the PTR RR that matches the DHCP
        !           151:         * binding, if one was successfully added. The server's update query
        !           152:         * SHOULD assert that the name in the PTR record matches the name of
        !           153:         * the client whose lease has expired or been released.
        !           154:         *   -- "Interaction between DHCP and DNS"
        !           155:         */
        !           156: 
        !           157:        ISC_LIST_INIT (updqueue);
        !           158: 
        !           159:        /*
        !           160:         * Delete the PTR RRset for the leased address.
        !           161:         */
        !           162:        updrec = minires_mkupdrec (S_UPDATE,
        !           163:                                   (const char *)ddns_rev_name -> data,
        !           164:                                   C_IN, T_PTR, 0);
        !           165:        if (!updrec) {
        !           166:                result = ISC_R_NOMEMORY;
        !           167:                goto error;
        !           168:        }
        !           169: 
        !           170:        updrec -> r_data = (unsigned char *)0;
        !           171:        updrec -> r_size = 0;
        !           172:        updrec -> r_opcode = DELETE;
        !           173: 
        !           174:        ISC_LIST_APPEND (updqueue, updrec, r_link);
        !           175: 
        !           176:        /*
        !           177:         * Attempt to perform the update.
        !           178:         */
        !           179:        result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
        !           180: #if defined (DEBUG)
        !           181:        print_dns_status ((int)result, &updqueue);
        !           182: #endif
        !           183:        if (result == ISC_R_SUCCESS) {
        !           184:                log_info ("removed reverse map on %.*s",
        !           185:                          (int)ddns_rev_name -> len,
        !           186:                          (const char *)ddns_rev_name -> data);
        !           187:        } else {
        !           188:                if (result != ISC_R_NXRRSET && result != ISC_R_NXDOMAIN)
        !           189:                        log_error ("can't remove reverse map on %.*s: %s",
        !           190:                                   (int)ddns_rev_name -> len,
        !           191:                                   (const char *)ddns_rev_name -> data,
        !           192:                                   isc_result_totext (result));
        !           193:        }
        !           194: 
        !           195:        /* Not there is success. */
        !           196:        if (result == ISC_R_NXRRSET || result == ISC_R_NXDOMAIN)
        !           197:                result = ISC_R_SUCCESS;
        !           198: 
        !           199:        /* Fall through. */
        !           200:       error:
        !           201: 
        !           202:        while (!ISC_LIST_EMPTY (updqueue)) {
        !           203:                updrec = ISC_LIST_HEAD (updqueue);
        !           204:                ISC_LIST_UNLINK (updqueue, updrec, r_link);
        !           205:                minires_freeupdrec (updrec);
        !           206:        }
        !           207: 
        !           208:        return result;
        !           209: }
        !           210: 
        !           211: 
        !           212: /* Determine what, if any, forward and reverse updates need to be
        !           213:  * performed, and carry them through.
        !           214:  */
        !           215: int
        !           216: ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
        !           217:             struct iasubopt *lease6, struct iasubopt *old6,
        !           218:             struct option_state *options)
        !           219: {
        !           220:        unsigned long ddns_ttl = DEFAULT_DDNS_TTL;
        !           221:        struct data_string ddns_hostname;
        !           222:        struct data_string ddns_domainname;
        !           223:        struct data_string old_ddns_fwd_name;
        !           224:        struct data_string ddns_fwd_name;
        !           225:        struct data_string ddns_rev_name;
        !           226:        struct data_string ddns_dhcid;
        !           227:        struct binding_scope **scope;
        !           228:        struct iaddr addr;
        !           229:        struct data_string d1;
        !           230:        struct option_cache *oc;
        !           231:        int s1, s2;
        !           232:        int result = 0;
        !           233:        isc_result_t rcode1 = ISC_R_SUCCESS, rcode2 = ISC_R_SUCCESS;
        !           234:        int server_updates_a = 1;
        !           235:        int server_updates_ptr = 1;
        !           236:        struct buffer *bp = (struct buffer *)0;
        !           237:        int ignorep = 0, client_ignorep = 0;
        !           238:        int rev_name_len;
        !           239:        int i;
        !           240: 
        !           241:        if (ddns_update_style != 2)
        !           242:                return 0;
        !           243: 
        !           244:        if (lease != NULL) {
        !           245:                scope = &(lease->scope);
        !           246:                addr = lease->ip_addr;
        !           247:        } else if (lease6 != NULL) {
        !           248:                scope = &(lease6->scope);
        !           249:                memcpy(addr.iabuf, lease6->addr.s6_addr, 16);
        !           250:                addr.len = 16;
        !           251:        } else {
        !           252:                log_fatal("Impossible condition at %s:%d.", MDL);
        !           253:                /* Silence compiler warnings. */
        !           254:                return 0;
        !           255:        }
        !           256: 
        !           257:        memset(&d1, 0, sizeof(d1));
        !           258:        memset (&ddns_hostname, 0, sizeof (ddns_hostname));
        !           259:        memset (&ddns_domainname, 0, sizeof (ddns_domainname));
        !           260:        memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
        !           261:        memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
        !           262:        memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
        !           263:        memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
        !           264: 
        !           265:        /* If we are allowed to accept the client's update of its own A
        !           266:           record, see if the client wants to update its own A record. */
        !           267:        if (!(oc = lookup_option(&server_universe, options,
        !           268:                                 SV_CLIENT_UPDATES)) ||
        !           269:            evaluate_boolean_option_cache(&client_ignorep, packet, lease, NULL,
        !           270:                                          packet->options, options, scope,
        !           271:                                          oc, MDL)) {
        !           272:                /* If there's no fqdn.no-client-update or if it's
        !           273:                   nonzero, don't try to use the client-supplied
        !           274:                   XXX */
        !           275:                if (!(oc = lookup_option (&fqdn_universe, packet -> options,
        !           276:                                          FQDN_SERVER_UPDATE)) ||
        !           277:                    evaluate_boolean_option_cache(&ignorep, packet, lease,
        !           278:                                                  NULL, packet->options,
        !           279:                                                  options, scope, oc, MDL))
        !           280:                        goto noclient;
        !           281:                /* Win98 and Win2k will happily claim to be willing to
        !           282:                   update an unqualified domain name. */
        !           283:                if (!(oc = lookup_option (&fqdn_universe, packet -> options,
        !           284:                                          FQDN_DOMAINNAME)))
        !           285:                        goto noclient;
        !           286:                if (!(oc = lookup_option (&fqdn_universe, packet -> options,
        !           287:                                          FQDN_FQDN)) ||
        !           288:                    !evaluate_option_cache(&ddns_fwd_name, packet, lease,
        !           289:                                           NULL, packet->options,
        !           290:                                           options, scope, oc, MDL))
        !           291:                        goto noclient;
        !           292:                server_updates_a = 0;
        !           293:                goto client_updates;
        !           294:        }
        !           295:       noclient:
        !           296:        /* If do-forward-updates is disabled, this basically means don't
        !           297:           do an update unless the client is participating, so if we get
        !           298:           here and do-forward-updates is disabled, we can stop. */
        !           299:        if ((oc = lookup_option (&server_universe, options,
        !           300:                                 SV_DO_FORWARD_UPDATES)) &&
        !           301:            !evaluate_boolean_option_cache(&ignorep, packet, lease,
        !           302:                                           NULL, packet->options,
        !           303:                                           options, scope, oc, MDL)) {
        !           304:                return 0;
        !           305:        }
        !           306: 
        !           307:        /* If it's a static lease, then don't do the DNS update unless we're
        !           308:           specifically configured to do so.   If the client asked to do its
        !           309:           own update and we allowed that, we don't do this test. */
        !           310:        /* XXX: note that we cannot detect static DHCPv6 leases. */
        !           311:        if ((lease != NULL) && (lease->flags & STATIC_LEASE)) {
        !           312:                if (!(oc = lookup_option(&server_universe, options,
        !           313:                                         SV_UPDATE_STATIC_LEASES)) ||
        !           314:                    !evaluate_boolean_option_cache(&ignorep, packet, lease,
        !           315:                                                   NULL, packet->options,
        !           316:                                                   options, scope, oc, MDL))
        !           317:                        return 0;
        !           318:        }
        !           319: 
        !           320:        /*
        !           321:         * Compute the name for the A record.
        !           322:         */
        !           323:        oc = lookup_option(&server_universe, options, SV_DDNS_HOST_NAME);
        !           324:        if (oc)
        !           325:                s1 = evaluate_option_cache(&ddns_hostname, packet, lease,
        !           326:                                           NULL, packet->options,
        !           327:                                           options, scope, oc, MDL);
        !           328:        else
        !           329:                s1 = 0;
        !           330: 
        !           331:        oc = lookup_option(&server_universe, options, SV_DDNS_DOMAIN_NAME);
        !           332:        if (oc)
        !           333:                s2 = evaluate_option_cache(&ddns_domainname, packet, lease,
        !           334:                                           NULL, packet->options,
        !           335:                                           options, scope, oc, MDL);
        !           336:        else
        !           337:                s2 = 0;
        !           338: 
        !           339:        if (s1 && s2) {
        !           340:                if (ddns_hostname.len + ddns_domainname.len > 253) {
        !           341:                        log_error ("ddns_update: host.domain name too long");
        !           342: 
        !           343:                        goto out;
        !           344:                }
        !           345: 
        !           346:                buffer_allocate (&ddns_fwd_name.buffer,
        !           347:                                 ddns_hostname.len + ddns_domainname.len + 2,
        !           348:                                 MDL);
        !           349:                if (ddns_fwd_name.buffer) {
        !           350:                        ddns_fwd_name.data = ddns_fwd_name.buffer -> data;
        !           351:                        data_string_append (&ddns_fwd_name, &ddns_hostname);
        !           352:                        ddns_fwd_name.buffer -> data [ddns_fwd_name.len] = '.';
        !           353:                        ddns_fwd_name.len++;
        !           354:                        data_string_append (&ddns_fwd_name, &ddns_domainname);
        !           355:                        ddns_fwd_name.buffer -> data [ddns_fwd_name.len] ='\0';
        !           356:                        ddns_fwd_name.terminated = 1;
        !           357:                }
        !           358:        }
        !           359:       client_updates:
        !           360: 
        !           361:        /* See if there's a name already stored on the lease. */
        !           362:        if (find_bound_string(&old_ddns_fwd_name, *scope, "ddns-fwd-name")) {
        !           363:                /* If there is, see if it's different. */
        !           364:                if (old_ddns_fwd_name.len != ddns_fwd_name.len ||
        !           365:                    memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
        !           366:                            old_ddns_fwd_name.len)) {
        !           367:                        /* If the name is different, try to delete
        !           368:                           the old A record. */
        !           369:                        if (!ddns_removals(lease, lease6))
        !           370:                                goto out;
        !           371:                        /* If the delete succeeded, go install the new
        !           372:                           record. */
        !           373:                        goto in;
        !           374:                }
        !           375: 
        !           376:                /* See if there's a DHCID on the lease, and if not
        !           377:                 * then potentially look for 'on events' for ad-hoc ddns.
        !           378:                 */
        !           379:                if (!find_bound_string(&ddns_dhcid, *scope, "ddns-txt") &&
        !           380:                    (old != NULL)) {
        !           381:                        /* If there's no DHCID, the update was probably
        !           382:                           done with the old-style ad-hoc DDNS updates.
        !           383:                           So if the expiry and release events look like
        !           384:                           they're the same, run them.   This should delete
        !           385:                           the old DDNS data. */
        !           386:                        if (old -> on_expiry == old -> on_release) {
        !           387:                                execute_statements(NULL, NULL, lease, NULL,
        !           388:                                                   NULL, NULL, scope,
        !           389:                                                   old->on_expiry);
        !           390:                                if (old -> on_expiry)
        !           391:                                        executable_statement_dereference
        !           392:                                                (&old -> on_expiry, MDL);
        !           393:                                if (old -> on_release)
        !           394:                                        executable_statement_dereference
        !           395:                                                (&old -> on_release, MDL);
        !           396:                                /* Now, install the DDNS data the new way. */
        !           397:                                goto in;
        !           398:                        }
        !           399:                } else
        !           400:                        data_string_forget(&ddns_dhcid, MDL);
        !           401: 
        !           402:                /* See if the administrator wants to do updates even
        !           403:                   in cases where the update already appears to have been
        !           404:                   done. */
        !           405:                if (!(oc = lookup_option(&server_universe, options,
        !           406:                                         SV_UPDATE_OPTIMIZATION)) ||
        !           407:                    evaluate_boolean_option_cache(&ignorep, packet, lease,
        !           408:                                                  NULL, packet->options,
        !           409:                                                  options, scope, oc, MDL)) {
        !           410:                        result = 1;
        !           411:                        goto noerror;
        !           412:                }
        !           413:        /* If there's no "ddns-fwd-name" on the lease record, see if
        !           414:         * there's a ddns-client-fqdn indicating a previous client
        !           415:         * update (if it changes, we need to adjust the PTR).
        !           416:         */
        !           417:        } else if (find_bound_string(&old_ddns_fwd_name, *scope,
        !           418:                                     "ddns-client-fqdn")) {
        !           419:                /* If the name is not different, no need to update
        !           420:                   the PTR record. */
        !           421:                if (old_ddns_fwd_name.len == ddns_fwd_name.len &&
        !           422:                    !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
        !           423:                             old_ddns_fwd_name.len) &&
        !           424:                    (!(oc = lookup_option(&server_universe, options,
        !           425:                                          SV_UPDATE_OPTIMIZATION)) ||
        !           426:                     evaluate_boolean_option_cache(&ignorep, packet, lease,
        !           427:                                                   NULL, packet->options,
        !           428:                                                   options, scope, oc, MDL))) {
        !           429:                        goto noerror;
        !           430:                }
        !           431:        }
        !           432:       in:
        !           433:                
        !           434:        /* If we don't have a name that the client has been assigned, we
        !           435:           can just skip all this. */
        !           436:        if (!ddns_fwd_name.len)
        !           437:                goto out;
        !           438: 
        !           439:        if (ddns_fwd_name.len > 255) {
        !           440:                log_error ("client provided fqdn: too long");
        !           441:                goto out;
        !           442:        }
        !           443: 
        !           444:        /*
        !           445:         * Compute the RR TTL.
        !           446:         */
        !           447:        ddns_ttl = DEFAULT_DDNS_TTL;
        !           448:        if ((oc = lookup_option(&server_universe, options, SV_DDNS_TTL))) {
        !           449:                if (evaluate_option_cache(&d1, packet, lease, NULL,
        !           450:                                          packet->options, options, scope,
        !           451:                                          oc, MDL)) {
        !           452:                        if (d1.len == sizeof (u_int32_t))
        !           453:                                ddns_ttl = getULong (d1.data);
        !           454:                        data_string_forget (&d1, MDL);
        !           455:                }
        !           456:        }
        !           457: 
        !           458:        /*
        !           459:         * Compute the reverse IP name, starting with the domain name.
        !           460:         */
        !           461:        oc = lookup_option(&server_universe, options, SV_DDNS_REV_DOMAIN_NAME);
        !           462:        if (oc)
        !           463:                s1 = evaluate_option_cache(&d1, packet, lease, NULL,
        !           464:                                           packet->options, options,
        !           465:                                           scope, oc, MDL);
        !           466:        else
        !           467:                s1 = 0;
        !           468: 
        !           469:        /* 
        !           470:         * Figure out the length of the part of the name that depends 
        !           471:         * on the address.
        !           472:         */
        !           473:        if (addr.len == 4) {
        !           474:                char buf[17];
        !           475:                /* XXX: WOW this is gross. */
        !           476:                rev_name_len = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.",
        !           477:                                        addr.iabuf[3] & 0xff,
        !           478:                                        addr.iabuf[2] & 0xff,
        !           479:                                        addr.iabuf[1] & 0xff,
        !           480:                                        addr.iabuf[0] & 0xff) + 1;
        !           481: 
        !           482:                if (s1) {
        !           483:                        rev_name_len += d1.len;
        !           484: 
        !           485:                        if (rev_name_len > 255) {
        !           486:                                log_error("ddns_update: Calculated rev domain "
        !           487:                                          "name too long.");
        !           488:                                s1 = 0;
        !           489:                                data_string_forget(&d1, MDL);
        !           490:                        }
        !           491:                }
        !           492:        } else if (addr.len == 16) {
        !           493:                /* 
        !           494:                 * IPv6 reverse names are always the same length, with 
        !           495:                 * 32 hex characters separated by dots.
        !           496:                 */
        !           497:                rev_name_len = sizeof("0.1.2.3.4.5.6.7."
        !           498:                                      "8.9.a.b.c.d.e.f."
        !           499:                                      "0.1.2.3.4.5.6.7."
        !           500:                                      "8.9.a.b.c.d.e.f."
        !           501:                                      "ip6.arpa.");
        !           502: 
        !           503:                /* Set s1 to make sure we gate into updates. */
        !           504:                s1 = 1;
        !           505:        } else {
        !           506:                log_fatal("invalid address length %d", addr.len);
        !           507:                /* Silence compiler warnings. */
        !           508:                return 0;
        !           509:        }
        !           510: 
        !           511:        /* See if we are configured NOT to do reverse ptr updates */
        !           512:        if ((oc = lookup_option(&server_universe, options,
        !           513:                                SV_DO_REVERSE_UPDATES)) &&
        !           514:            !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
        !           515:                                           packet->options, options,
        !           516:                                           scope, oc, MDL)) {
        !           517:                server_updates_ptr = 0;
        !           518:        }
        !           519: 
        !           520:        if (s1) {
        !           521:                buffer_allocate(&ddns_rev_name.buffer, rev_name_len, MDL);
        !           522:                if (ddns_rev_name.buffer != NULL) {
        !           523:                        ddns_rev_name.data = ddns_rev_name.buffer->data;
        !           524: 
        !           525:                        if (addr.len == 4) {
        !           526:                                ddns_rev_name.len =
        !           527:                                    sprintf((char *)ddns_rev_name.buffer->data,
        !           528:                                            "%u.%u.%u.%u.", 
        !           529:                                            addr.iabuf[3] & 0xff,
        !           530:                                            addr.iabuf[2] & 0xff,
        !           531:                                            addr.iabuf[1] & 0xff,
        !           532:                                            addr.iabuf[0] & 0xff);
        !           533: 
        !           534:                                /*
        !           535:                                 * d1.data may be opaque, garbage bytes, from
        !           536:                                 * user (mis)configuration.
        !           537:                                 */
        !           538:                                data_string_append(&ddns_rev_name, &d1);
        !           539:                                ddns_rev_name.buffer->data[ddns_rev_name.len] =
        !           540:                                        '\0';
        !           541:                        } else if (addr.len == 16) {
        !           542:                                char *p = (char *)&ddns_rev_name.buffer->data;
        !           543:                                unsigned char *a = addr.iabuf + 15;
        !           544:                                for (i=0; i<16; i++) {
        !           545:                                        sprintf(p, "%x.%x.", 
        !           546:                                                (*a & 0xF), ((*a >> 4) & 0xF));
        !           547:                                        p += 4;
        !           548:                                        a -= 1;
        !           549:                                }
        !           550:                                strcat(p, "ip6.arpa.");
        !           551:                                ddns_rev_name.len =
        !           552:                                    strlen((const char *)ddns_rev_name.data);
        !           553:                        }
        !           554: 
        !           555:                        ddns_rev_name.terminated = 1;
        !           556:                }
        !           557: 
        !           558:                if (d1.data != NULL)
        !           559:                        data_string_forget(&d1, MDL);
        !           560:        }
        !           561: 
        !           562:        /*
        !           563:         * If we are updating the A record, compute the DHCID value.
        !           564:         */
        !           565:        if (server_updates_a) {
        !           566:                memset (&ddns_dhcid, 0, sizeof ddns_dhcid);
        !           567:                if (lease6 != NULL)
        !           568:                        result = get_dhcid(&ddns_dhcid, 2,
        !           569:                                           lease6->ia->iaid_duid.data,
        !           570:                                           lease6->ia->iaid_duid.len);
        !           571:                else if ((lease != NULL) && (lease->uid != NULL) &&
        !           572:                         (lease->uid_len != 0))
        !           573:                        result = get_dhcid (&ddns_dhcid,
        !           574:                                            DHO_DHCP_CLIENT_IDENTIFIER,
        !           575:                                            lease -> uid, lease -> uid_len);
        !           576:                else if (lease != NULL)
        !           577:                        result = get_dhcid (&ddns_dhcid, 0,
        !           578:                                            lease -> hardware_addr.hbuf,
        !           579:                                            lease -> hardware_addr.hlen);
        !           580:                else
        !           581:                        log_fatal("Impossible condition at %s:%d.", MDL);
        !           582: 
        !           583:                if (!result)
        !           584:                        goto badfqdn;
        !           585:        }
        !           586: 
        !           587:        /*
        !           588:         * Start the resolver, if necessary.
        !           589:         */
        !           590:        if (!resolver_inited) {
        !           591:                minires_ninit (&resolver_state);
        !           592:                resolver_inited = 1;
        !           593:                resolver_state.retrans = 1;
        !           594:                resolver_state.retry = 1;
        !           595:        }
        !           596: 
        !           597:        /*
        !           598:         * Perform updates.
        !           599:         */
        !           600:        if (ddns_fwd_name.len && ddns_dhcid.len) {
        !           601:                unsigned conflict;
        !           602: 
        !           603:                oc = lookup_option(&server_universe, options,
        !           604:                                   SV_DDNS_CONFLICT_DETECT);
        !           605:                if (!oc ||
        !           606:                    evaluate_boolean_option_cache(&ignorep, packet, lease,
        !           607:                                                  NULL, packet->options,
        !           608:                                                  options, scope, oc, MDL))
        !           609:                        conflict = 1;
        !           610:                else
        !           611:                        conflict = 0;
        !           612: 
        !           613:                rcode1 = ddns_update_fwd(&ddns_fwd_name, addr, &ddns_dhcid,
        !           614:                                         ddns_ttl, 0, conflict);
        !           615:        }
        !           616: 
        !           617:        if (rcode1 == ISC_R_SUCCESS && server_updates_ptr) {
        !           618:                if (ddns_fwd_name.len && ddns_rev_name.len)
        !           619:                        rcode2 = ddns_update_ptr (&ddns_fwd_name,
        !           620:                                                  &ddns_rev_name, ddns_ttl);
        !           621:        } else
        !           622:                rcode2 = rcode1;
        !           623: 
        !           624:        if (rcode1 == ISC_R_SUCCESS &&
        !           625:            (server_updates_a || rcode2 == ISC_R_SUCCESS)) {
        !           626:                bind_ds_value(scope, server_updates_a ? "ddns-fwd-name"
        !           627:                                                       : "ddns-client-fqdn",
        !           628:                              &ddns_fwd_name);
        !           629:                if (server_updates_a)
        !           630:                        bind_ds_value(scope, "ddns-txt", &ddns_dhcid);
        !           631:        }
        !           632: 
        !           633:        if (rcode2 == ISC_R_SUCCESS && server_updates_ptr) {
        !           634:                bind_ds_value(scope, "ddns-rev-name", &ddns_rev_name);
        !           635:        }
        !           636: 
        !           637:       noerror:
        !           638:        /*
        !           639:         * If fqdn-reply option is disabled in dhcpd.conf, then don't
        !           640:         * send the client an FQDN option at all, even if one was requested.
        !           641:         * (WinXP clients allegedly misbehave if the option is present,
        !           642:         * refusing to handle PTR updates themselves).
        !           643:         */
        !           644:        if ((oc = lookup_option (&server_universe, options, SV_FQDN_REPLY)) &&
        !           645:            !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
        !           646:                                           packet->options, options,
        !           647:                                           scope, oc, MDL)) {
        !           648:                goto badfqdn;
        !           649: 
        !           650:        /* If we're ignoring client updates, then we tell a sort of 'white
        !           651:         * lie'.  We've already updated the name the server wants (per the
        !           652:         * config written by the server admin).  Now let the client do as
        !           653:         * it pleases with the name they supplied (if any).
        !           654:         *
        !           655:         * We only form an FQDN option this way if the client supplied an
        !           656:         * FQDN option that had FQDN_SERVER_UPDATE set false.
        !           657:         */
        !           658:        } else if (client_ignorep &&
        !           659:            (oc = lookup_option(&fqdn_universe, packet->options,
        !           660:                                FQDN_SERVER_UPDATE)) &&
        !           661:            !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
        !           662:                                           packet->options, options,
        !           663:                                           scope, oc, MDL)) {
        !           664:                oc = lookup_option(&fqdn_universe, packet->options, FQDN_FQDN);
        !           665:                if (oc && evaluate_option_cache(&d1, packet, lease, NULL,
        !           666:                                                packet->options, options,
        !           667:                                                scope, oc, MDL)) {
        !           668:                        if (d1.len == 0 ||
        !           669:                            !buffer_allocate(&bp, d1.len + 5, MDL))
        !           670:                                goto badfqdn;
        !           671: 
        !           672:                        /* Server pretends it is not updating. */
        !           673:                        bp->data[0] = 0;
        !           674:                        if (!save_option_buffer(&fqdn_universe, options,
        !           675:                                                bp, &bp->data[0], 1,
        !           676:                                                FQDN_SERVER_UPDATE, 0))
        !           677:                                goto badfqdn;
        !           678: 
        !           679:                        /* Client is encouraged to update. */
        !           680:                        bp->data[1] = 0;
        !           681:                        if (!save_option_buffer(&fqdn_universe, options,
        !           682:                                                bp, &bp->data[1], 1,
        !           683:                                                FQDN_NO_CLIENT_UPDATE, 0))
        !           684:                                goto badfqdn;
        !           685: 
        !           686:                        /* Use the encoding of client's FQDN option. */
        !           687:                        oc = lookup_option(&fqdn_universe, packet->options,
        !           688:                                           FQDN_ENCODED);
        !           689:                        if (oc &&
        !           690:                            evaluate_boolean_option_cache(&ignorep, packet,
        !           691:                                                          lease, NULL,
        !           692:                                                          packet->options,
        !           693:                                                          options, scope,
        !           694:                                                          oc, MDL))
        !           695:                                bp->data[2] = 1; /* FQDN is encoded. */
        !           696:                        else
        !           697:                                bp->data[2] = 0; /* FQDN is not encoded. */
        !           698: 
        !           699:                        if (!save_option_buffer(&fqdn_universe, options,
        !           700:                                                bp, &bp->data[2], 1,
        !           701:                                                FQDN_ENCODED, 0))
        !           702:                                goto badfqdn;
        !           703: 
        !           704:                        /* Current FQDN drafts indicate 255 is mandatory. */
        !           705:                        bp->data[3] = 255;
        !           706:                        if (!save_option_buffer(&fqdn_universe, options,
        !           707:                                                bp, &bp->data[3], 1,
        !           708:                                                FQDN_RCODE1, 0))
        !           709:                                goto badfqdn;
        !           710: 
        !           711:                        bp->data[4] = 255;
        !           712:                        if (!save_option_buffer(&fqdn_universe, options,
        !           713:                                                bp, &bp->data[4], 1,
        !           714:                                                FQDN_RCODE2, 0))
        !           715:                                goto badfqdn;
        !           716: 
        !           717:                        /* Copy in the FQDN supplied by the client.  Note well
        !           718:                         * that the format of this option in the cache is going
        !           719:                         * to be in text format.  If the fqdn supplied by the
        !           720:                         * client is encoded, it is decoded into the option
        !           721:                         * cache when parsed out of the packet.  It will be
        !           722:                         * re-encoded when the option is assembled to be
        !           723:                         * transmitted if the client elects that encoding.
        !           724:                         */
        !           725:                        memcpy(&bp->data[5], d1.data, d1.len);
        !           726:                        if (!save_option_buffer(&fqdn_universe, options,
        !           727:                                                bp, &bp->data[5], d1.len,
        !           728:                                                FQDN_FQDN, 0))
        !           729:                                goto badfqdn;
        !           730: 
        !           731:                        data_string_forget(&d1, MDL);
        !           732:                }
        !           733:        /* Set up the outgoing FQDN option if there was an incoming
        !           734:         * FQDN option.  If there's a valid FQDN option, there MUST
        !           735:         * be an FQDN_SERVER_UPDATES suboption, it's part of the fixed
        !           736:         * length head of the option contents, so we test the latter
        !           737:         * to detect the presence of the former.
        !           738:         */
        !           739:        } else if ((oc = lookup_option(&fqdn_universe, packet->options,
        !           740:                                       FQDN_ENCODED)) &&
        !           741:                   buffer_allocate(&bp, ddns_fwd_name.len + 5, MDL)) {
        !           742:                bp -> data [0] = server_updates_a;
        !           743:                if (!save_option_buffer(&fqdn_universe, options,
        !           744:                                        bp, &bp->data [0], 1,
        !           745:                                        FQDN_SERVER_UPDATE, 0))
        !           746:                        goto badfqdn;
        !           747:                bp -> data [1] = server_updates_a;
        !           748:                if (!save_option_buffer(&fqdn_universe, options,
        !           749:                                         bp, &bp->data [1], 1,
        !           750:                                         FQDN_NO_CLIENT_UPDATE, 0))
        !           751:                        goto badfqdn;
        !           752: 
        !           753:                /* Do the same encoding the client did. */
        !           754:                if (evaluate_boolean_option_cache(&ignorep, packet, lease,
        !           755:                                                  NULL, packet->options,
        !           756:                                                  options, scope, oc, MDL))
        !           757:                        bp -> data [2] = 1;
        !           758:                else
        !           759:                        bp -> data [2] = 0;
        !           760:                if (!save_option_buffer(&fqdn_universe, options,
        !           761:                                        bp, &bp->data [2], 1,
        !           762:                                        FQDN_ENCODED, 0))
        !           763:                        goto badfqdn;
        !           764:                bp -> data [3] = isc_rcode_to_ns (rcode1);
        !           765:                if (!save_option_buffer(&fqdn_universe, options,
        !           766:                                        bp, &bp->data [3], 1,
        !           767:                                        FQDN_RCODE1, 0))
        !           768:                        goto badfqdn;
        !           769:                bp -> data [4] = isc_rcode_to_ns (rcode2);
        !           770:                if (!save_option_buffer(&fqdn_universe, options,
        !           771:                                        bp, &bp->data [4], 1,
        !           772:                                        FQDN_RCODE2, 0))
        !           773:                        goto badfqdn;
        !           774:                if (ddns_fwd_name.len) {
        !           775:                    memcpy (&bp -> data [5],
        !           776:                            ddns_fwd_name.data, ddns_fwd_name.len);
        !           777:                    if (!save_option_buffer(&fqdn_universe, options,
        !           778:                                             bp, &bp->data [5],
        !           779:                                             ddns_fwd_name.len,
        !           780:                                             FQDN_FQDN, 0))
        !           781:                        goto badfqdn;
        !           782:                }
        !           783:        }
        !           784: 
        !           785:       badfqdn:
        !           786:       out:
        !           787:        /*
        !           788:         * Final cleanup.
        !           789:         */
        !           790:        data_string_forget(&d1, MDL);
        !           791:        data_string_forget(&ddns_hostname, MDL);
        !           792:        data_string_forget(&ddns_domainname, MDL);
        !           793:        data_string_forget(&old_ddns_fwd_name, MDL);
        !           794:        data_string_forget(&ddns_fwd_name, MDL);
        !           795:        data_string_forget(&ddns_rev_name, MDL);
        !           796:        data_string_forget(&ddns_dhcid, MDL);
        !           797:        if (bp)
        !           798:                buffer_dereference(&bp, MDL);
        !           799: 
        !           800:        return result;
        !           801: }
        !           802: 
        !           803: /* Remove relevant entries from DNS. */
        !           804: int
        !           805: ddns_removals(struct lease *lease, struct iasubopt *lease6)
        !           806: {
        !           807:        struct data_string ddns_fwd_name;
        !           808:        struct data_string ddns_rev_name;
        !           809:        struct data_string ddns_dhcid;
        !           810:        isc_result_t rcode;
        !           811:        struct binding_scope **scope;
        !           812:        struct iaddr addr;
        !           813:        int result = 0;
        !           814:        int client_updated = 0;
        !           815: 
        !           816:        if (lease != NULL) {
        !           817:                scope = &(lease->scope);
        !           818:                addr = lease->ip_addr;
        !           819:        } else if (lease6 != NULL) {
        !           820:                scope = &(lease6->scope);
        !           821:                memcpy(addr.iabuf, lease6->addr.s6_addr, 16);
        !           822:                addr.len = 16;
        !           823:        } else
        !           824:                return 0;
        !           825: 
        !           826:        /* No scope implies that DDNS has not been performed for this lease. */
        !           827:        if (*scope == NULL)
        !           828:                return 0;
        !           829: 
        !           830:        if (ddns_update_style != 2)
        !           831:                return 0;
        !           832: 
        !           833:        /*
        !           834:         * Look up stored names.
        !           835:         */
        !           836:        memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
        !           837:        memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
        !           838:        memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
        !           839: 
        !           840:        /*
        !           841:         * Start the resolver, if necessary.
        !           842:         */
        !           843:        if (!resolver_inited) {
        !           844:                minires_ninit (&resolver_state);
        !           845:                resolver_inited = 1;
        !           846:                resolver_state.retrans = 1;
        !           847:                resolver_state.retry = 1;
        !           848:        }
        !           849: 
        !           850:        /* We need the fwd name whether we are deleting both records or just
        !           851:           the PTR record, so if it's not there, we can't proceed. */
        !           852:        if (!find_bound_string(&ddns_fwd_name, *scope, "ddns-fwd-name")) {
        !           853:                /* If there's no ddns-fwd-name, look for the client fqdn,
        !           854:                   in case the client did the update. */
        !           855:                if (find_bound_string(&ddns_fwd_name, *scope,
        !           856:                                       "ddns-client-fqdn"))
        !           857:                        client_updated = 1;
        !           858:                goto try_rev;
        !           859:        }
        !           860: 
        !           861:        /* If the ddns-txt binding isn't there, this isn't an interim
        !           862:           or rfc3??? record, so we can't delete the A record using
        !           863:           this mechanism, but we can delete the PTR record. */
        !           864:        if (!find_bound_string (&ddns_dhcid, *scope, "ddns-txt")) {
        !           865:                result = 1;
        !           866:                goto try_rev;
        !           867:        }
        !           868: 
        !           869:        /*
        !           870:         * Perform removals.
        !           871:         */
        !           872:        if (ddns_fwd_name.len)
        !           873:                rcode = ddns_remove_fwd(&ddns_fwd_name, addr, &ddns_dhcid);
        !           874:        else
        !           875:                rcode = ISC_R_SUCCESS;
        !           876: 
        !           877:        if (rcode == ISC_R_SUCCESS) {
        !           878:                result = 1;
        !           879:                unset(*scope, "ddns-fwd-name");
        !           880:                unset(*scope, "ddns-txt");
        !           881:              try_rev:
        !           882:                if (find_bound_string(&ddns_rev_name, *scope,
        !           883:                                      "ddns-rev-name")) {
        !           884:                        if (ddns_remove_ptr(&ddns_rev_name) == ISC_R_SUCCESS) {
        !           885:                                unset(*scope, "ddns-rev-name");
        !           886:                                if (client_updated)
        !           887:                                        unset(*scope, "ddns-client-fqdn");
        !           888:                                /* XXX this is to compensate for a bug in
        !           889:                                   XXX 3.0rc8, and should be removed before
        !           890:                                   XXX 3.0pl1. */
        !           891:                                else if (!ddns_fwd_name.len)
        !           892:                                        unset(*scope, "ddns-text");
        !           893:                        } else
        !           894:                                result = 0;
        !           895:                }
        !           896:        }
        !           897: 
        !           898:        data_string_forget (&ddns_fwd_name, MDL);
        !           899:        data_string_forget (&ddns_rev_name, MDL);
        !           900:        data_string_forget (&ddns_dhcid, MDL);
        !           901: 
        !           902:        return result;
        !           903: }
        !           904: 
        !           905: #endif /* NSUPDATE */

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