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

1.1     ! misho       1: /* options.c
        !             2: 
        !             3:    DHCP options parsing and reassembly. */
        !             4: 
        !             5: /*
        !             6:  * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
        !             7:  * Copyright (c) 1995-2003 by Internet Software Consortium
        !             8:  *
        !             9:  * Permission to use, copy, modify, and distribute this software for any
        !            10:  * purpose with or without fee is hereby granted, provided that the above
        !            11:  * copyright notice and this permission notice appear in all copies.
        !            12:  *
        !            13:  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
        !            14:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            15:  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
        !            16:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            17:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            18:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
        !            19:  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            20:  *
        !            21:  *   Internet Systems Consortium, Inc.
        !            22:  *   950 Charter Street
        !            23:  *   Redwood City, CA 94063
        !            24:  *   <info@isc.org>
        !            25:  *   https://www.isc.org/
        !            26:  *
        !            27:  * This software has been written for Internet Systems Consortium
        !            28:  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
        !            29:  * To learn more about Internet Systems Consortium, see
        !            30:  * ``https://www.isc.org/''.  To learn more about Vixie Enterprises,
        !            31:  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
        !            32:  * ``http://www.nominum.com''.
        !            33:  */
        !            34: 
        !            35: #define DHCP_OPTION_DATA
        !            36: #include "dhcpd.h"
        !            37: #include <omapip/omapip_p.h>
        !            38: #include <limits.h>
        !            39: 
        !            40: struct option *vendor_cfg_option;
        !            41: 
        !            42: static int pretty_text(char **, char *, const unsigned char **,
        !            43:                         const unsigned char *, int);
        !            44: static int pretty_domain(char **, char *, const unsigned char **,
        !            45:                         const unsigned char *);
        !            46: static int prepare_option_buffer(struct universe *universe, struct buffer *bp,
        !            47:                                 unsigned char *buffer, unsigned length,
        !            48:                                 unsigned code, int terminatep,
        !            49:                                 struct option_cache **opp);
        !            50: 
        !            51: /* Parse all available options out of the specified packet. */
        !            52: 
        !            53: int parse_options (packet)
        !            54:        struct packet *packet;
        !            55: {
        !            56:        struct option_cache *op = (struct option_cache *)0;
        !            57: 
        !            58:        /* Allocate a new option state. */
        !            59:        if (!option_state_allocate (&packet -> options, MDL)) {
        !            60:                packet -> options_valid = 0;
        !            61:                return 0;
        !            62:        }
        !            63: 
        !            64:        /* If we don't see the magic cookie, there's nothing to parse. */
        !            65:        if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) {
        !            66:                packet -> options_valid = 0;
        !            67:                return 1;
        !            68:        }
        !            69: 
        !            70:        /* Go through the options field, up to the end of the packet
        !            71:           or the End field. */
        !            72:        if (!parse_option_buffer (packet -> options,
        !            73:                                  &packet -> raw -> options [4],
        !            74:                                  (packet -> packet_length -
        !            75:                                   DHCP_FIXED_NON_UDP - 4),
        !            76:                                  &dhcp_universe)) {
        !            77: 
        !            78:                /* STSN servers have a bug where they send a mangled
        !            79:                   domain-name option, and whatever is beyond that in
        !            80:                   the packet is junk.   Microsoft clients accept this,
        !            81:                   which is probably why whoever implemented the STSN
        !            82:                   server isn't aware of the problem yet.   To work around
        !            83:                   this, we will accept corrupt packets from the server if
        !            84:                   they contain a valid DHCP_MESSAGE_TYPE option, but
        !            85:                   will not accept any corrupt client packets (the ISC DHCP
        !            86:                   server is sufficiently widely used that it is probably
        !            87:                   beneficial for it to be picky) and will not accept
        !            88:                   packets whose type can't be determined. */
        !            89: 
        !            90:                if ((op = lookup_option (&dhcp_universe, packet -> options,
        !            91:                                         DHO_DHCP_MESSAGE_TYPE))) {
        !            92:                        if (!op -> data.data ||
        !            93:                            (op -> data.data [0] != DHCPOFFER &&
        !            94:                             op -> data.data [0] != DHCPACK &&
        !            95:                             op -> data.data [0] != DHCPNAK))
        !            96:                                return 0;
        !            97:                } else
        !            98:                        return 0;
        !            99:        }
        !           100: 
        !           101:        /* If we parsed a DHCP Option Overload option, parse more
        !           102:           options out of the buffer(s) containing them. */
        !           103:        if ((op = lookup_option (&dhcp_universe, packet -> options,
        !           104:                                 DHO_DHCP_OPTION_OVERLOAD))) {
        !           105:                if (op -> data.data [0] & 1) {
        !           106:                        if (!parse_option_buffer
        !           107:                            (packet -> options,
        !           108:                             (unsigned char *)packet -> raw -> file,
        !           109:                             sizeof packet -> raw -> file,
        !           110:                             &dhcp_universe))
        !           111:                                return 0;
        !           112:                }
        !           113:                if (op -> data.data [0] & 2) {
        !           114:                        if (!parse_option_buffer
        !           115:                            (packet -> options,
        !           116:                             (unsigned char *)packet -> raw -> sname,
        !           117:                             sizeof packet -> raw -> sname,
        !           118:                             &dhcp_universe))
        !           119:                                return 0;
        !           120:                }
        !           121:        }
        !           122:        packet -> options_valid = 1;
        !           123:        return 1;
        !           124: }
        !           125: 
        !           126: /* Parse options out of the specified buffer, storing addresses of option
        !           127:  * values in packet->options.
        !           128:  */
        !           129: int parse_option_buffer (options, buffer, length, universe)
        !           130:        struct option_state *options;
        !           131:        const unsigned char *buffer;
        !           132:        unsigned length;
        !           133:        struct universe *universe;
        !           134: {
        !           135:        unsigned len, offset;
        !           136:        unsigned code;
        !           137:        struct option_cache *op = NULL, *nop = NULL;
        !           138:        struct buffer *bp = (struct buffer *)0;
        !           139:        struct option *option = NULL;
        !           140:        char *reason = "general failure";
        !           141: 
        !           142:        if (!buffer_allocate (&bp, length, MDL)) {
        !           143:                log_error ("no memory for option buffer.");
        !           144:                return 0;
        !           145:        }
        !           146:        memcpy (bp -> data, buffer, length);
        !           147: 
        !           148:        for (offset = 0;
        !           149:             (offset + universe->tag_size) <= length &&
        !           150:             (code = universe->get_tag(buffer + offset)) != universe->end; ) {
        !           151:                offset += universe->tag_size;
        !           152: 
        !           153:                /* Pad options don't have a length - just skip them. */
        !           154:                if (code == DHO_PAD)
        !           155:                        continue;
        !           156: 
        !           157:                /* Don't look for length if the buffer isn't that big. */
        !           158:                if ((offset + universe->length_size) > length) {
        !           159:                        reason = "code tag at end of buffer - missing "
        !           160:                                 "length field";
        !           161:                        goto bogus;
        !           162:                }
        !           163: 
        !           164:                /* All other fields (except PAD and END handled above)
        !           165:                 * have a length field, unless it's a DHCPv6 zero-length
        !           166:                 * options space (eg any of the enterprise-id'd options).
        !           167:                 *
        !           168:                 * Zero-length-size option spaces basically consume the
        !           169:                 * entire options buffer, so have at it.
        !           170:                 */
        !           171:                if (universe->get_length != NULL)
        !           172:                        len = universe->get_length(buffer + offset);
        !           173:                else if (universe->length_size == 0)
        !           174:                        len = length - universe->tag_size;
        !           175:                else {
        !           176:                        log_fatal("Improperly configured option space(%s): "
        !           177:                                  "may not have a nonzero length size "
        !           178:                                  "AND a NULL get_length function.",
        !           179:                                  universe->name);
        !           180: 
        !           181:                        /* Silence compiler warnings. */
        !           182:                        return 0;
        !           183:                }
        !           184: 
        !           185:                offset += universe->length_size;
        !           186: 
        !           187:                option_code_hash_lookup(&option, universe->code_hash, &code,
        !           188:                                        0, MDL);
        !           189: 
        !           190:                /* If the length is outrageous, the options are bad. */
        !           191:                if (offset + len > length) {
        !           192:                        reason = "option length exceeds option buffer length";
        !           193:                      bogus:
        !           194:                        log_error("parse_option_buffer: malformed option "
        !           195:                                  "%s.%s (code %u): %s.", universe->name,
        !           196:                                  option ? option->name : "<unknown>",
        !           197:                                  code, reason);
        !           198:                        buffer_dereference (&bp, MDL);
        !           199:                        return 0;
        !           200:                }
        !           201: 
        !           202:                /* If the option contains an encapsulation, parse it.   If
        !           203:                   the parse fails, or the option isn't an encapsulation (by
        !           204:                   far the most common case), or the option isn't entirely
        !           205:                   an encapsulation, keep the raw data as well. */
        !           206:                if (!(option &&
        !           207:                      (option->format[0] == 'e' ||
        !           208:                       option->format[0] == 'E') &&
        !           209:                      (parse_encapsulated_suboptions(options, option,
        !           210:                                                     bp->data + offset, len,
        !           211:                                                     universe, NULL)))) {
        !           212:                        op = lookup_option(universe, options, code);
        !           213: 
        !           214:                        if (op != NULL && universe->concat_duplicates) {
        !           215:                                struct data_string new;
        !           216:                                memset(&new, 0, sizeof new);
        !           217:                                if (!buffer_allocate(&new.buffer,
        !           218:                                                     op->data.len + len,
        !           219:                                                     MDL)) {
        !           220:                                        log_error("parse_option_buffer: "
        !           221:                                                  "No memory.");
        !           222:                                        buffer_dereference(&bp, MDL);
        !           223:                                        return 0;
        !           224:                                }
        !           225:                                /* Copy old option to new data object. */
        !           226:                                memcpy(new.buffer->data, op->data.data,
        !           227:                                        op->data.len);
        !           228:                                /* Concat new option behind old. */
        !           229:                                memcpy(new.buffer->data + op->data.len,
        !           230:                                        bp->data + offset, len);
        !           231:                                new.len = op->data.len + len;
        !           232:                                new.data = new.buffer->data;
        !           233:                                /* Save new concat'd object. */
        !           234:                                data_string_forget(&op->data, MDL);
        !           235:                                data_string_copy(&op->data, &new, MDL);
        !           236:                                data_string_forget(&new, MDL);
        !           237:                        } else if (op != NULL) {
        !           238:                                /* We must append this statement onto the
        !           239:                                 * end of the list.
        !           240:                                 */
        !           241:                                while (op->next != NULL)
        !           242:                                        op = op->next;
        !           243: 
        !           244:                                if (!option_cache_allocate(&nop, MDL)) {
        !           245:                                        log_error("parse_option_buffer: "
        !           246:                                                  "No memory.");
        !           247:                                        buffer_dereference(&bp, MDL);
        !           248:                                        return 0;
        !           249:                                }
        !           250: 
        !           251:                                option_reference(&nop->option, op->option, MDL);
        !           252: 
        !           253:                                nop->data.buffer = NULL;
        !           254:                                buffer_reference(&nop->data.buffer, bp, MDL);
        !           255:                                nop->data.data = bp->data + offset;
        !           256:                                nop->data.len = len;
        !           257: 
        !           258:                                option_cache_reference(&op->next, nop, MDL);
        !           259:                                option_cache_dereference(&nop, MDL);
        !           260:                        } else {
        !           261:                                save_option_buffer(universe, options, bp,
        !           262:                                                   bp->data + offset, len,
        !           263:                                                   code, 1);
        !           264:                        }
        !           265:                }
        !           266:                option_dereference(&option, MDL);
        !           267:                offset += len;
        !           268:        }
        !           269:        buffer_dereference (&bp, MDL);
        !           270:        return 1;
        !           271: }
        !           272: 
        !           273: /* If an option in an option buffer turns out to be an encapsulation,
        !           274:    figure out what to do.   If we don't know how to de-encapsulate it,
        !           275:    or it's not well-formed, return zero; otherwise, return 1, indicating
        !           276:    that we succeeded in de-encapsulating it. */
        !           277: 
        !           278: struct universe *find_option_universe (struct option *eopt, const char *uname)
        !           279: {
        !           280:        int i;
        !           281:        char *s, *t;
        !           282:        struct universe *universe = (struct universe *)0;
        !           283: 
        !           284:        /* Look for the E option in the option format. */
        !           285:        s = strchr (eopt -> format, 'E');
        !           286:        if (!s) {
        !           287:                log_error ("internal encapsulation format error 1.");
        !           288:                return 0;
        !           289:        }
        !           290:        /* Look for the universe name in the option format. */
        !           291:        t = strchr (++s, '.');
        !           292:        /* If there was no trailing '.', or there's something after the
        !           293:           trailing '.', the option is bogus and we can't use it. */
        !           294:        if (!t || t [1]) {
        !           295:                log_error ("internal encapsulation format error 2.");
        !           296:                return 0;
        !           297:        }
        !           298:        if (t == s && uname) {
        !           299:                for (i = 0; i < universe_count; i++) {
        !           300:                        if (!strcmp (universes [i] -> name, uname)) {
        !           301:                                universe = universes [i];
        !           302:                                break;
        !           303:                        }
        !           304:                }
        !           305:        } else if (t != s) {
        !           306:                for (i = 0; i < universe_count; i++) {
        !           307:                        if (strlen (universes [i] -> name) == t - s &&
        !           308:                            !memcmp (universes [i] -> name,
        !           309:                                     s, (unsigned)(t - s))) {
        !           310:                                universe = universes [i];
        !           311:                                break;
        !           312:                        }
        !           313:                }
        !           314:        }
        !           315:        return universe;
        !           316: }
        !           317: 
        !           318: /* If an option in an option buffer turns out to be an encapsulation,
        !           319:    figure out what to do.   If we don't know how to de-encapsulate it,
        !           320:    or it's not well-formed, return zero; otherwise, return 1, indicating
        !           321:    that we succeeded in de-encapsulating it. */
        !           322: 
        !           323: int parse_encapsulated_suboptions (struct option_state *options,
        !           324:                                   struct option *eopt,
        !           325:                                   const unsigned char *buffer,
        !           326:                                   unsigned len, struct universe *eu,
        !           327:                                   const char *uname)
        !           328: {
        !           329:        int i;
        !           330:        struct universe *universe = find_option_universe (eopt, uname);
        !           331: 
        !           332:        /* If we didn't find the universe, we can't do anything with it
        !           333:           right now (e.g., we can't decode vendor options until we've
        !           334:           decoded the packet and executed the scopes that it matches). */
        !           335:        if (!universe)
        !           336:                return 0;
        !           337:                
        !           338:        /* If we don't have a decoding function for it, we can't decode
        !           339:           it. */
        !           340:        if (!universe -> decode)
        !           341:                return 0;
        !           342: 
        !           343:        i = (*universe -> decode) (options, buffer, len, universe);
        !           344: 
        !           345:        /* If there is stuff before the suboptions, we have to keep it. */
        !           346:        if (eopt -> format [0] != 'E')
        !           347:                return 0;
        !           348:        /* Otherwise, return the status of the decode function. */
        !           349:        return i;
        !           350: }
        !           351: 
        !           352: int fqdn_universe_decode (struct option_state *options,
        !           353:                          const unsigned char *buffer,
        !           354:                          unsigned length, struct universe *u)
        !           355: {
        !           356:        struct buffer *bp = (struct buffer *)0;
        !           357: 
        !           358:        /* FQDN options have to be at least four bytes long. */
        !           359:        if (length < 3)
        !           360:                return 0;
        !           361: 
        !           362:        /* Save the contents of the option in a buffer. */
        !           363:        if (!buffer_allocate (&bp, length + 4, MDL)) {
        !           364:                log_error ("no memory for option buffer.");
        !           365:                return 0;
        !           366:        }
        !           367:        memcpy (&bp -> data [3], buffer + 1, length - 1);
        !           368: 
        !           369:        if (buffer [0] & 4)     /* encoded */
        !           370:                bp -> data [0] = 1;
        !           371:        else
        !           372:                bp -> data [0] = 0;
        !           373:        if (!save_option_buffer(&fqdn_universe, options, bp,
        !           374:                                bp->data, 1, FQDN_ENCODED, 0)) {
        !           375:              bad:
        !           376:                buffer_dereference (&bp, MDL);
        !           377:                return 0;
        !           378:        }
        !           379: 
        !           380:        if (buffer [0] & 1)     /* server-update */
        !           381:                bp -> data [2] = 1;
        !           382:        else
        !           383:                bp -> data [2] = 0;
        !           384:        if (buffer [0] & 2)     /* no-client-update */
        !           385:                bp -> data [1] = 1;
        !           386:        else
        !           387:                bp -> data [1] = 0;
        !           388: 
        !           389:        /* XXX Ideally we should store the name in DNS format, so if the
        !           390:           XXX label isn't in DNS format, we convert it to DNS format,
        !           391:           XXX rather than converting labels specified in DNS format to
        !           392:           XXX the plain ASCII representation.   But that's hard, so
        !           393:           XXX not now. */
        !           394: 
        !           395:        /* Not encoded using DNS format? */
        !           396:        if (!bp -> data [0]) {
        !           397:                unsigned i;
        !           398: 
        !           399:                /* Some broken clients NUL-terminate this option. */
        !           400:                if (buffer [length - 1] == 0) {
        !           401:                        --length;
        !           402:                        bp -> data [1] = 1;
        !           403:                }
        !           404: 
        !           405:                /* Determine the length of the hostname component of the
        !           406:                   name.  If the name contains no '.' character, it
        !           407:                   represents a non-qualified label. */
        !           408:                for (i = 3; i < length && buffer [i] != '.'; i++);
        !           409:                i -= 3;
        !           410: 
        !           411:                /* Note: If the client sends a FQDN, the first '.' will
        !           412:                   be used as a NUL terminator for the hostname. */
        !           413:                if (i && (!save_option_buffer(&fqdn_universe, options, bp,
        !           414:                                              &bp->data[5], i,
        !           415:                                              FQDN_HOSTNAME, 0)))
        !           416:                        goto bad;
        !           417:                /* Note: If the client sends a single label, the
        !           418:                   FQDN_DOMAINNAME option won't be set. */
        !           419:                if (length > 4 + i &&
        !           420:                    (!save_option_buffer(&fqdn_universe, options, bp,
        !           421:                                         &bp -> data[6 + i], length - 4 - i,
        !           422:                                         FQDN_DOMAINNAME, 1)))
        !           423:                        goto bad;
        !           424:                /* Also save the whole name. */
        !           425:                if (length > 3) {
        !           426:                        if (!save_option_buffer(&fqdn_universe, options, bp,
        !           427:                                                &bp -> data [5], length - 3,
        !           428:                                                FQDN_FQDN, 1))
        !           429:                                goto bad;
        !           430:                }
        !           431:        } else {
        !           432:                unsigned len;
        !           433:                unsigned total_len = 0;
        !           434:                unsigned first_len = 0;
        !           435:                int terminated = 0;
        !           436:                unsigned char *s;
        !           437: 
        !           438:                s = &bp -> data[5];
        !           439: 
        !           440:                while (s < &bp -> data[0] + length + 2) {
        !           441:                        len = *s;
        !           442:                        if (len > 63) {
        !           443:                                log_info ("fancy bits in fqdn option");
        !           444:                                return 0;
        !           445:                        }       
        !           446:                        if (len == 0) {
        !           447:                                terminated = 1;
        !           448:                                break;
        !           449:                        }
        !           450:                        if (s + len > &bp -> data [0] + length + 3) {
        !           451:                                log_info ("fqdn tag longer than buffer");
        !           452:                                return 0;
        !           453:                        }
        !           454: 
        !           455:                        if (first_len == 0) {
        !           456:                                first_len = len;
        !           457:                        }
        !           458: 
        !           459:                        *s = '.';
        !           460:                        s += len + 1;
        !           461:                        total_len += len + 1;
        !           462:                }
        !           463: 
        !           464:                /* We wind up with a length that's one too many because
        !           465:                   we shouldn't increment for the last label, but there's
        !           466:                   no way to tell we're at the last label until we exit
        !           467:                   the loop.   :'*/
        !           468:                if (total_len > 0)
        !           469:                        total_len--;
        !           470: 
        !           471:                if (!terminated) {
        !           472:                        first_len = total_len;
        !           473:                }
        !           474: 
        !           475:                if (first_len > 0 &&
        !           476:                    !save_option_buffer(&fqdn_universe, options, bp,
        !           477:                                        &bp -> data[6], first_len,
        !           478:                                        FQDN_HOSTNAME, 0))
        !           479:                        goto bad;
        !           480:                if (total_len > 0 && first_len != total_len) {
        !           481:                        if (!save_option_buffer(&fqdn_universe, options, bp,
        !           482:                                                &bp->data[6 + first_len],
        !           483:                                                total_len - first_len,
        !           484:                                                FQDN_DOMAINNAME, 1))
        !           485:                                goto bad;
        !           486:                }
        !           487:                if (total_len > 0)
        !           488:                        if (!save_option_buffer (&fqdn_universe, options, bp,
        !           489:                                                 &bp -> data [6], total_len,
        !           490:                                                 FQDN_FQDN, 1))
        !           491:                                goto bad;
        !           492:        }
        !           493: 
        !           494:        if (!save_option_buffer (&fqdn_universe, options, bp,
        !           495:                                 &bp -> data [1], 1,
        !           496:                                 FQDN_NO_CLIENT_UPDATE, 0))
        !           497:            goto bad;
        !           498:        if (!save_option_buffer (&fqdn_universe, options, bp,
        !           499:                                 &bp -> data [2], 1,
        !           500:                                 FQDN_SERVER_UPDATE, 0))
        !           501:                goto bad;
        !           502: 
        !           503:        if (!save_option_buffer (&fqdn_universe, options, bp,
        !           504:                                 &bp -> data [3], 1,
        !           505:                                 FQDN_RCODE1, 0))
        !           506:                goto bad;
        !           507:        if (!save_option_buffer (&fqdn_universe, options, bp,
        !           508:                                 &bp -> data [4], 1,
        !           509:                                 FQDN_RCODE2, 0))
        !           510:                goto bad;
        !           511: 
        !           512:        buffer_dereference (&bp, MDL);
        !           513:        return 1;
        !           514: }
        !           515: 
        !           516: /*
        !           517:  * Load all options into a buffer, and then split them out into the three
        !           518:  * separate fields in the dhcp packet (options, file, and sname) where
        !           519:  * options can be stored.
        !           520:  */
        !           521: int
        !           522: cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
        !           523:             struct lease *lease, struct client_state *client_state,
        !           524:             int mms, struct option_state *in_options,
        !           525:             struct option_state *cfg_options,
        !           526:             struct binding_scope **scope,
        !           527:             int overload_avail, int terminate, int bootpp,
        !           528:             struct data_string *prl, const char *vuname)
        !           529: {
        !           530: #define PRIORITY_COUNT 300
        !           531:        unsigned priority_list[PRIORITY_COUNT];
        !           532:        int priority_len;
        !           533:        unsigned char buffer[4096], agentopts[1024];
        !           534:        unsigned index = 0;
        !           535:        unsigned mb_size = 0, mb_max = 0;
        !           536:        unsigned option_size = 0, agent_size = 0;
        !           537:        unsigned length;
        !           538:        int i;
        !           539:        struct option_cache *op;
        !           540:        struct data_string ds;
        !           541:        pair pp, *hash;
        !           542:        int overload_used = 0;
        !           543:        int of1 = 0, of2 = 0;
        !           544: 
        !           545:        memset(&ds, 0, sizeof ds);
        !           546: 
        !           547:        /*
        !           548:         * If there's a Maximum Message Size option in the incoming packet
        !           549:         * and no alternate maximum message size has been specified, or
        !           550:         * if the one specified in the packet is shorter than the
        !           551:         * alternative, take the one in the packet.
        !           552:         */
        !           553: 
        !           554:        if (inpacket &&
        !           555:            (op = lookup_option(&dhcp_universe, inpacket->options,
        !           556:                                 DHO_DHCP_MAX_MESSAGE_SIZE))) {
        !           557:                evaluate_option_cache(&ds, inpacket,
        !           558:                                       lease, client_state, in_options,
        !           559:                                       cfg_options, scope, op, MDL);
        !           560:                if (ds.len >= sizeof (u_int16_t)) {
        !           561:                        i = getUShort(ds.data);
        !           562:                        if(!mms || (i < mms))
        !           563:                                mms = i;
        !           564:                }
        !           565:                data_string_forget(&ds, MDL);
        !           566:        }
        !           567: 
        !           568:        /*
        !           569:         * If the client has provided a maximum DHCP message size,
        !           570:         * use that, up to the MTU limit.  Otherwise, if it's BOOTP,
        !           571:         * only 64 bytes; otherwise use up to the minimum IP MTU size
        !           572:         * (576 bytes).
        !           573:         *
        !           574:         * XXX if a BOOTP client specifies a max message size, we will
        !           575:         * honor it.
        !           576:         */
        !           577:        if (mms) {
        !           578:                if (mms < DHCP_MTU_MIN)
        !           579:                        /* Enforce minimum packet size, per RFC 2132 */
        !           580:                        mb_size = DHCP_MIN_OPTION_LEN;
        !           581:                else if (mms > DHCP_MTU_MAX)
        !           582:                        /*
        !           583:                         * TODO: Packets longer than 1500 bytes really
        !           584:                         * should be allowed, but it requires upstream
        !           585:                         * changes to the way the packet is allocated.  For
        !           586:                         * now, we forbid them.  They won't be needed very
        !           587:                         * often anyway.
        !           588:                         */
        !           589:                        mb_size = DHCP_MAX_OPTION_LEN;
        !           590:                else
        !           591:                        mb_size = mms - DHCP_FIXED_LEN;
        !           592:        } else if (bootpp) {
        !           593:                mb_size = 64;
        !           594:                if (inpacket != NULL &&
        !           595:                    (inpacket->packet_length >= 64 + DHCP_FIXED_NON_UDP))
        !           596:                        mb_size = inpacket->packet_length - DHCP_FIXED_NON_UDP;
        !           597:        } else
        !           598:                mb_size = DHCP_MIN_OPTION_LEN;
        !           599: 
        !           600:        /*
        !           601:         * If answering a client message, see whether any relay agent
        !           602:         * options were included with the message.  If so, save them
        !           603:         * to copy back in later, and make space in the main buffer
        !           604:         * to accommodate them
        !           605:         */
        !           606:        if (client_state == NULL) {
        !           607:                priority_list[0] = DHO_DHCP_AGENT_OPTIONS;
        !           608:                priority_len = 1;
        !           609:                agent_size = store_options(NULL, agentopts, 0,
        !           610:                                           sizeof(agentopts),
        !           611:                                           inpacket, lease, client_state,
        !           612:                                           in_options, cfg_options, scope,
        !           613:                                           priority_list, priority_len,
        !           614:                                           0, 0, 0, NULL);
        !           615: 
        !           616:                mb_size += agent_size;
        !           617:                if (mb_size > DHCP_MAX_OPTION_LEN)
        !           618:                        mb_size = DHCP_MAX_OPTION_LEN;
        !           619:        }
        !           620: 
        !           621:        /*
        !           622:         * Set offsets for buffer data to be copied into filename
        !           623:         * and servername fields 
        !           624:         */
        !           625:        mb_max = mb_size;
        !           626: 
        !           627:        if (overload_avail & 1) {
        !           628:                of1 = mb_max;
        !           629:                mb_max += DHCP_FILE_LEN;
        !           630:        }
        !           631: 
        !           632:        if (overload_avail & 2) {
        !           633:                of2 = mb_max;
        !           634:                mb_max += DHCP_SNAME_LEN;
        !           635:        }
        !           636:                
        !           637:        /*
        !           638:         * Preload the option priority list with protocol-mandatory options.
        !           639:         * This effectively gives these options the highest priority.
        !           640:         * This provides the order for any available options, the option
        !           641:         * must be in the option cache in order to actually be included.
        !           642:         */
        !           643:        priority_len = 0;
        !           644:        priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
        !           645:        priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
        !           646:        priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
        !           647:        priority_list[priority_len++] = DHO_DHCP_MESSAGE;
        !           648:        priority_list[priority_len++] = DHO_DHCP_REQUESTED_ADDRESS;
        !           649:        priority_list[priority_len++] = DHO_ASSOCIATED_IP;
        !           650: 
        !           651:        if (prl != NULL && prl->len > 0) {
        !           652:                if ((op = lookup_option(&dhcp_universe, cfg_options,
        !           653:                                         DHO_SUBNET_SELECTION))) {
        !           654:                        if (priority_len < PRIORITY_COUNT)
        !           655:                                priority_list[priority_len++] =
        !           656:                                        DHO_SUBNET_SELECTION;
        !           657:                }
        !           658: 
        !           659:                data_string_truncate(prl, (PRIORITY_COUNT - priority_len));
        !           660: 
        !           661:                for (i = 0; i < prl->len; i++) {
        !           662:                        /*
        !           663:                         * Prevent client from changing order of delivery
        !           664:                         * of relay agent information option.
        !           665:                         */
        !           666:                        if (prl->data[i] != DHO_DHCP_AGENT_OPTIONS)
        !           667:                                priority_list[priority_len++] = prl->data[i];
        !           668:                }
        !           669: 
        !           670:                /*
        !           671:                 * If the client doesn't request the FQDN option explicitly,
        !           672:                 * to indicate priority, consider it lowest priority.  Fit
        !           673:                 * in the packet if there is space.  Note that the option
        !           674:                 * may only be included if the client supplied one.
        !           675:                 */
        !           676:                if ((priority_len < PRIORITY_COUNT) &&
        !           677:                    (lookup_option(&fqdn_universe, inpacket->options,
        !           678:                                   FQDN_ENCODED) != NULL))
        !           679:                        priority_list[priority_len++] = DHO_FQDN;
        !           680: 
        !           681:                /*
        !           682:                 * Some DHCP Servers will give the subnet-mask option if
        !           683:                 * it is not on the parameter request list - so some client
        !           684:                 * implementations have come to rely on this - so we will
        !           685:                 * also make sure we supply this, at lowest priority.
        !           686:                 *
        !           687:                 * This is only done in response to DHCPDISCOVER or
        !           688:                 * DHCPREQUEST messages, to avoid providing the option on
        !           689:                 * DHCPINFORM or DHCPLEASEQUERY responses (if the client
        !           690:                 * didn't request it).
        !           691:                 */
        !           692:                if ((priority_len < PRIORITY_COUNT) &&
        !           693:                    ((inpacket->packet_type == DHCPDISCOVER) ||
        !           694:                     (inpacket->packet_type == DHCPREQUEST)))
        !           695:                        priority_list[priority_len++] = DHO_SUBNET_MASK;
        !           696:        } else {
        !           697:                /*
        !           698:                 * First, hardcode some more options that ought to be
        !           699:                 * sent first...these are high priority to have in the
        !           700:                 * packet.
        !           701:                 */
        !           702:                priority_list[priority_len++] = DHO_SUBNET_MASK;
        !           703:                priority_list[priority_len++] = DHO_ROUTERS;
        !           704:                priority_list[priority_len++] = DHO_DOMAIN_NAME_SERVERS;
        !           705:                priority_list[priority_len++] = DHO_HOST_NAME;
        !           706:                priority_list[priority_len++] = DHO_FQDN;
        !           707: 
        !           708:                /*
        !           709:                 * Append a list of the standard DHCP options from the
        !           710:                 * standard DHCP option space.  Actually, if a site
        !           711:                 * option space hasn't been specified, we wind up
        !           712:                 * treating the dhcp option space as the site option
        !           713:                 * space, and the first for loop is skipped, because
        !           714:                 * it's slightly more general to do it this way,
        !           715:                 * taking the 1Q99 DHCP futures work into account.
        !           716:                 */
        !           717:                if (cfg_options->site_code_min) {
        !           718:                    for (i = 0; i < OPTION_HASH_SIZE; i++) {
        !           719:                        hash = cfg_options->universes[dhcp_universe.index];
        !           720:                        if (hash) {
        !           721:                            for (pp = hash[i]; pp; pp = pp->cdr) {
        !           722:                                op = (struct option_cache *)(pp->car);
        !           723:                                if (op->option->code <
        !           724:                                     cfg_options->site_code_min &&
        !           725:                                    priority_len < PRIORITY_COUNT &&
        !           726:                                    op->option->code != DHO_DHCP_AGENT_OPTIONS)
        !           727:                                        priority_list[priority_len++] =
        !           728:                                                op->option->code;
        !           729:                            }
        !           730:                        }
        !           731:                    }
        !           732:                }
        !           733: 
        !           734:                /*
        !           735:                 * Now cycle through the site option space, or if there
        !           736:                 * is no site option space, we'll be cycling through the
        !           737:                 * dhcp option space.
        !           738:                 */
        !           739:                for (i = 0; i < OPTION_HASH_SIZE; i++) {
        !           740:                    hash = cfg_options->universes[cfg_options->site_universe];
        !           741:                    if (hash != NULL)
        !           742:                        for (pp = hash[i]; pp; pp = pp->cdr) {
        !           743:                                op = (struct option_cache *)(pp->car);
        !           744:                                if (op->option->code >=
        !           745:                                     cfg_options->site_code_min &&
        !           746:                                    priority_len < PRIORITY_COUNT &&
        !           747:                                    op->option->code != DHO_DHCP_AGENT_OPTIONS)
        !           748:                                        priority_list[priority_len++] =
        !           749:                                                op->option->code;
        !           750:                        }
        !           751:                }
        !           752: 
        !           753:                /*
        !           754:                 * Put any spaces that are encapsulated on the list,
        !           755:                 * sort out whether they contain values later.
        !           756:                 */
        !           757:                for (i = 0; i < cfg_options->universe_count; i++) {
        !           758:                    if (universes[i]->enc_opt &&
        !           759:                        priority_len < PRIORITY_COUNT &&
        !           760:                        universes[i]->enc_opt->universe == &dhcp_universe) {
        !           761:                            if (universes[i]->enc_opt->code !=
        !           762:                                DHO_DHCP_AGENT_OPTIONS)
        !           763:                                    priority_list[priority_len++] =
        !           764:                                            universes[i]->enc_opt->code;
        !           765:                    }
        !           766:                }
        !           767: 
        !           768:                /*
        !           769:                 * The vendor option space can't stand on its own, so always
        !           770:                 * add it to the list.
        !           771:                 */
        !           772:                if (priority_len < PRIORITY_COUNT)
        !           773:                        priority_list[priority_len++] =
        !           774:                                DHO_VENDOR_ENCAPSULATED_OPTIONS;
        !           775:        }
        !           776: 
        !           777:        /* Put the cookie up front... */
        !           778:        memcpy(buffer, DHCP_OPTIONS_COOKIE, 4);
        !           779:        index += 4;
        !           780: 
        !           781:        /* Copy the options into the big buffer... */
        !           782:        option_size = store_options(&overload_used, buffer, index, mb_max,
        !           783:                                    inpacket, lease, client_state,
        !           784:                                    in_options, cfg_options, scope,
        !           785:                                    priority_list, priority_len,
        !           786:                                    of1, of2, terminate, vuname);
        !           787: 
        !           788:        /* If store_options() failed */
        !           789:        if (option_size == 0)
        !           790:                return 0;
        !           791: 
        !           792:        /* How much was stored in the main buffer? */
        !           793:        index += option_size;
        !           794: 
        !           795:        /*
        !           796:         * If we're going to have to overload, store the overload
        !           797:         * option first.
        !           798:         */
        !           799:        if (overload_used) {
        !           800:                if (mb_size - agent_size - index < 3)
        !           801:                        return 0;
        !           802: 
        !           803:                buffer[index++] = DHO_DHCP_OPTION_OVERLOAD;
        !           804:                buffer[index++] = 1;
        !           805:                buffer[index++] = overload_used;
        !           806: 
        !           807:                if (overload_used & 1)
        !           808:                        memcpy(outpacket->file, &buffer[of1], DHCP_FILE_LEN);
        !           809: 
        !           810:                if (overload_used & 2)
        !           811:                        memcpy(outpacket->sname, &buffer[of2], DHCP_SNAME_LEN);
        !           812:        }
        !           813: 
        !           814:        /* Now copy in preserved agent options, if any */
        !           815:        if (agent_size) {
        !           816:                if (mb_size - index >= agent_size) {
        !           817:                        memcpy(&buffer[index], agentopts, agent_size);
        !           818:                        index += agent_size;
        !           819:                } else
        !           820:                        log_error("Unable to store relay agent information "
        !           821:                                  "in reply packet.");
        !           822:        }
        !           823: 
        !           824:        /* Tack a DHO_END option onto the packet if we need to. */
        !           825:        if (index < mb_size)
        !           826:                buffer[index++] = DHO_END;
        !           827: 
        !           828:        /* Copy main buffer into the options buffer of the packet */
        !           829:        memcpy(outpacket->options, buffer, index);
        !           830: 
        !           831:        /* Figure out the length. */
        !           832:        length = DHCP_FIXED_NON_UDP + index;
        !           833:        return length;
        !           834: }
        !           835: 
        !           836: /*
        !           837:  * XXX: We currently special case collecting VSIO options.
        !           838:  *      We should be able to handle this in a more generic fashion, by
        !           839:  *      including any encapsulated options that are present and desired.
        !           840:  *      This will look something like the VSIO handling VSIO code.
        !           841:  *      We may also consider handling the ORO-like options within
        !           842:  *      encapsulated spaces.
        !           843:  */
        !           844: 
        !           845: struct vsio_state {
        !           846:        char *buf;
        !           847:        int buflen;
        !           848:        int bufpos;
        !           849: };
        !           850: 
        !           851: static void
        !           852: vsio_options(struct option_cache *oc,
        !           853:             struct packet *packet,
        !           854:             struct lease *dummy_lease, 
        !           855:             struct client_state *dummy_client_state,
        !           856:             struct option_state *dummy_opt_state,
        !           857:             struct option_state *opt_state,
        !           858:             struct binding_scope **dummy_binding_scope,
        !           859:             struct universe *universe, 
        !           860:             void *void_vsio_state) {
        !           861:        struct vsio_state *vs = (struct vsio_state *)void_vsio_state;
        !           862:        struct data_string ds;
        !           863:        int total_len;
        !           864: 
        !           865:        memset(&ds, 0, sizeof(ds));
        !           866:        if (evaluate_option_cache(&ds, packet, NULL,
        !           867:                                  NULL, opt_state, NULL, 
        !           868:                                  &global_scope, oc, MDL)) {
        !           869:                total_len = ds.len + universe->tag_size + universe->length_size;
        !           870:                if (total_len <= (vs->buflen - vs->bufpos)) {
        !           871:                        if (universe->tag_size == 1) {
        !           872:                                vs->buf[vs->bufpos++] = oc->option->code;
        !           873:                        } else if (universe->tag_size == 2) {
        !           874:                                putUShort((unsigned char *)vs->buf+vs->bufpos,
        !           875:                                          oc->option->code);
        !           876:                                vs->bufpos += 2;
        !           877:                        } else if (universe->tag_size == 4) {
        !           878:                                putULong((unsigned char *)vs->buf+vs->bufpos,
        !           879:                                         oc->option->code);
        !           880:                                vs->bufpos += 4;
        !           881:                        }
        !           882:                        if (universe->length_size == 1) {
        !           883:                                vs->buf[vs->bufpos++] = ds.len;
        !           884:                        } else if (universe->length_size == 2) {
        !           885:                                putUShort((unsigned char *)vs->buf+vs->bufpos, 
        !           886:                                          ds.len);
        !           887:                                vs->bufpos += 2;
        !           888:                        } else if (universe->length_size == 4) {
        !           889:                                putULong((unsigned char *)vs->buf+vs->bufpos, 
        !           890:                                         ds.len);
        !           891:                                vs->bufpos += 4;
        !           892:                        }
        !           893:                        memcpy(vs->buf + vs->bufpos, ds.data, ds.len);
        !           894:                        vs->bufpos += ds.len;
        !           895:                } else {
        !           896:                        log_debug("No space for option %d in VSIO space %s.",
        !           897:                                oc->option->code, universe->name);
        !           898:                }
        !           899:                data_string_forget(&ds, MDL);
        !           900:        } else {
        !           901:                log_error("Error evaluating option %d in VSIO space %s.",
        !           902:                        oc->option->code, universe->name);
        !           903:        }
        !           904: }
        !           905: 
        !           906: /*
        !           907:  * Stores the options from the DHCPv6 universe into the buffer given.
        !           908:  *
        !           909:  * Required options are given as a 0-terminated list of option codes.
        !           910:  * Once those are added, the ORO is consulted.
        !           911:  */
        !           912: 
        !           913: int
        !           914: store_options6(char *buf, int buflen, 
        !           915:               struct option_state *opt_state, 
        !           916:               struct packet *packet,
        !           917:               const int *required_opts,
        !           918:               struct data_string *oro) {
        !           919:        int i, j;
        !           920:        struct option_cache *oc;
        !           921:        struct option *o;
        !           922:        struct data_string ds;
        !           923:        int bufpos;
        !           924:        int oro_size;
        !           925:        u_int16_t code;
        !           926:        int in_required_opts;
        !           927:        int vsio_option_code;
        !           928:        int vsio_wanted;
        !           929:        struct vsio_state vs;
        !           930:        unsigned char *tmp;
        !           931: 
        !           932:        bufpos = 0;
        !           933:        vsio_wanted = 0;
        !           934: 
        !           935:        /*
        !           936:         * Find the option code for the VSIO universe.
        !           937:         */
        !           938:        vsio_option_code = 0;
        !           939:        o = vsio_universe.enc_opt;
        !           940:        while (o != NULL) { 
        !           941:                if (o->universe == &dhcpv6_universe) {
        !           942:                        vsio_option_code = o->code;
        !           943:                        break;
        !           944:                } 
        !           945:                o = o->universe->enc_opt;
        !           946:        }
        !           947:        if (vsio_option_code == 0) {
        !           948:                log_fatal("No VSIO option code found.");
        !           949:        }
        !           950: 
        !           951:        if (required_opts != NULL) {
        !           952:                for (i=0; required_opts[i] != 0; i++) {
        !           953:                        if (required_opts[i] == vsio_option_code) {
        !           954:                                vsio_wanted = 1;
        !           955:                        }
        !           956: 
        !           957:                        oc = lookup_option(&dhcpv6_universe, 
        !           958:                                           opt_state, required_opts[i]);
        !           959:                        if (oc == NULL) {
        !           960:                                continue;
        !           961:                        }
        !           962:                        memset(&ds, 0, sizeof(ds));
        !           963:                        for (; oc != NULL ; oc = oc->next) {
        !           964:                                if (evaluate_option_cache(&ds, packet, NULL,
        !           965:                                                          NULL, opt_state,
        !           966:                                                          NULL, &global_scope,
        !           967:                                                          oc, MDL)) {
        !           968:                                        if ((ds.len + 4) <=
        !           969:                                            (buflen - bufpos)) {
        !           970:                                                tmp = (unsigned char *)buf;
        !           971:                                                tmp += bufpos;
        !           972:                                                /* option tag */
        !           973:                                                putUShort(tmp,
        !           974:                                                          required_opts[i]);
        !           975:                                                /* option length */
        !           976:                                                putUShort(tmp+2, ds.len);
        !           977:                                                /* option data */
        !           978:                                                memcpy(tmp+4, ds.data, ds.len);
        !           979:                                                /* update position */
        !           980:                                                bufpos += (4 + ds.len);
        !           981:                                        } else {
        !           982:                                                log_debug("No space for "
        !           983:                                                          "option %d",
        !           984:                                                          required_opts[i]);
        !           985:                                        }
        !           986:                                        data_string_forget(&ds, MDL);
        !           987:                                } else {
        !           988:                                        log_error("Error evaluating option %d",
        !           989:                                                required_opts[i]);
        !           990:                                }
        !           991:                        }
        !           992:                }
        !           993:        }
        !           994: 
        !           995:        if (oro == NULL) {
        !           996:                oro_size = 0;
        !           997:        } else {
        !           998:                oro_size = oro->len / 2;
        !           999:        }
        !          1000:        for (i=0; i<oro_size; i++) {
        !          1001:                memcpy(&code, oro->data+(i*2), 2);
        !          1002:                code = ntohs(code);
        !          1003: 
        !          1004:                /* 
        !          1005:                 * See if we've already included this option because
        !          1006:                 * it is required.
        !          1007:                 */
        !          1008:                in_required_opts = 0;
        !          1009:                if (required_opts != NULL) {
        !          1010:                        for (j=0; required_opts[j] != 0; j++) {
        !          1011:                                if (required_opts[j] == code) {
        !          1012:                                        in_required_opts = 1;
        !          1013:                                        break;
        !          1014:                                }
        !          1015:                        }
        !          1016:                }
        !          1017:                if (in_required_opts) {
        !          1018:                        continue;
        !          1019:                }
        !          1020: 
        !          1021:                /*
        !          1022:                 * See if this is the VSIO option.
        !          1023:                 */
        !          1024:                if (code == vsio_option_code) {
        !          1025:                        vsio_wanted = 1;
        !          1026:                }
        !          1027: 
        !          1028:                /* 
        !          1029:                 * Not already added, find this option.
        !          1030:                 */
        !          1031:                oc = lookup_option(&dhcpv6_universe, opt_state, code);
        !          1032:                memset(&ds, 0, sizeof(ds));
        !          1033:                for (; oc != NULL ; oc = oc->next) {
        !          1034:                        if (evaluate_option_cache(&ds, packet, NULL, NULL,
        !          1035:                                                  opt_state, NULL,
        !          1036:                                                  &global_scope, oc, MDL)) {
        !          1037:                                if ((ds.len + 4) <= (buflen - bufpos)) {
        !          1038:                                        tmp = (unsigned char *)buf + bufpos;
        !          1039:                                        /* option tag */
        !          1040:                                        putUShort(tmp, code);
        !          1041:                                        /* option length */
        !          1042:                                        putUShort(tmp+2, ds.len);
        !          1043:                                        /* option data */
        !          1044:                                        memcpy(tmp+4, ds.data, ds.len);
        !          1045:                                        /* update position */
        !          1046:                                        bufpos += (4 + ds.len);
        !          1047:                                } else {
        !          1048:                                        log_debug("No space for option %d",
        !          1049:                                                  code);
        !          1050:                                }
        !          1051:                                data_string_forget(&ds, MDL);
        !          1052:                        } else {
        !          1053:                                log_error("Error evaluating option %d", code);
        !          1054:                        }
        !          1055:                }
        !          1056:        }
        !          1057: 
        !          1058:        if (vsio_wanted) {
        !          1059:                for (i=0; i < opt_state->universe_count; i++) {
        !          1060:                        if (opt_state->universes[i] != NULL) {
        !          1061:                                o = universes[i]->enc_opt;
        !          1062:                                if ((o != NULL) && 
        !          1063:                                    (o->universe == &vsio_universe)) {
        !          1064:                                        /*
        !          1065:                                         * Add the data from this VSIO option.
        !          1066:                                         */
        !          1067:                                        vs.buf = buf;
        !          1068:                                        vs.buflen = buflen;
        !          1069:                                        vs.bufpos = bufpos+8;
        !          1070:                                        option_space_foreach(packet, NULL,
        !          1071:                                                             NULL, 
        !          1072:                                                             NULL, opt_state,
        !          1073:                                                             NULL, 
        !          1074:                                                             universes[i], 
        !          1075:                                                             (void *)&vs,
        !          1076:                                                             vsio_options);
        !          1077: 
        !          1078:                                        /* 
        !          1079:                                         * If there was actually data here,
        !          1080:                                         * add the "header".
        !          1081:                                         */
        !          1082:                                        if (vs.bufpos > bufpos+8) {
        !          1083:                                                tmp = (unsigned char *)buf +
        !          1084:                                                      bufpos;
        !          1085:                                                putUShort(tmp,
        !          1086:                                                          vsio_option_code);
        !          1087:                                                putUShort(tmp+2,
        !          1088:                                                          vs.bufpos-bufpos-4);
        !          1089:                                                putULong(tmp+4, o->code);
        !          1090: 
        !          1091:                                                bufpos = vs.bufpos;
        !          1092:                                        }
        !          1093:                                }
        !          1094:                        }
        !          1095:                }
        !          1096:        }
        !          1097: 
        !          1098:        return bufpos;
        !          1099: }
        !          1100: 
        !          1101: /*
        !          1102:  * Store all the requested options into the requested buffer.
        !          1103:  * XXX: ought to be static
        !          1104:  */
        !          1105: int
        !          1106: store_options(int *ocount,
        !          1107:              unsigned char *buffer, unsigned index, unsigned buflen,
        !          1108:              struct packet *packet, struct lease *lease,
        !          1109:              struct client_state *client_state,
        !          1110:              struct option_state *in_options,
        !          1111:              struct option_state *cfg_options,
        !          1112:              struct binding_scope **scope,
        !          1113:              unsigned *priority_list, int priority_len,
        !          1114:              unsigned first_cutoff, int second_cutoff, int terminate,
        !          1115:              const char *vuname)
        !          1116: {
        !          1117:        int bufix = 0, six = 0, tix = 0;
        !          1118:        int i;
        !          1119:        int ix;
        !          1120:        int tto;
        !          1121:        int bufend, sbufend;
        !          1122:        struct data_string od;
        !          1123:        struct option_cache *oc;
        !          1124:        struct option *option = NULL;
        !          1125:        unsigned code;
        !          1126: 
        !          1127:        /*
        !          1128:         * These arguments are relative to the start of the buffer, so 
        !          1129:         * reduce them by the current buffer index, and advance the
        !          1130:         * buffer pointer to where we're going to start writing.
        !          1131:         */
        !          1132:        buffer = &buffer[index];
        !          1133:        buflen -= index;
        !          1134:        if (first_cutoff)
        !          1135:                first_cutoff -= index;
        !          1136:        if (second_cutoff)
        !          1137:                second_cutoff -= index;
        !          1138: 
        !          1139:        /* Calculate the start and end of each section of the buffer */
        !          1140:        bufend = sbufend = buflen;
        !          1141:        if (first_cutoff) {
        !          1142:            if (first_cutoff >= buflen)
        !          1143:                log_fatal("%s:%d:store_options: Invalid first cutoff.", MDL);
        !          1144:            bufend = first_cutoff;
        !          1145: 
        !          1146:            if (second_cutoff) {
        !          1147:                if (second_cutoff >= buflen)
        !          1148:                    log_fatal("%s:%d:store_options: Invalid second cutoff.",
        !          1149:                              MDL);
        !          1150:                sbufend = second_cutoff;
        !          1151:            }
        !          1152:        } else if (second_cutoff) {
        !          1153:            if (second_cutoff >= buflen)
        !          1154:                log_fatal("%s:%d:store_options: Invalid second cutoff.", MDL);
        !          1155:            bufend = second_cutoff;
        !          1156:        }
        !          1157: 
        !          1158:        memset (&od, 0, sizeof od);
        !          1159: 
        !          1160:        /* Eliminate duplicate options from the parameter request list.
        !          1161:         * Enforce RFC-mandated ordering of options that are present.
        !          1162:         */
        !          1163:        for (i = 0; i < priority_len - 1; i++) {
        !          1164:                /* Eliminate duplicates. */
        !          1165:                tto = 0;
        !          1166:                for (ix = i + 1; ix < priority_len + tto; ix++) {
        !          1167:                        if (tto)
        !          1168:                                priority_list [ix - tto] =
        !          1169:                                        priority_list [ix];
        !          1170:                        if (priority_list [i] == priority_list [ix]) {
        !          1171:                                tto++;
        !          1172:                                priority_len--;
        !          1173:                        }
        !          1174:                }
        !          1175: 
        !          1176:                /* Enforce ordering of SUBNET_MASK options, according to
        !          1177:                 * RFC2132 Section 3.3:
        !          1178:                 *
        !          1179:                 *   If both the subnet mask and the router option are
        !          1180:                 *   specified in a DHCP reply, the subnet mask option MUST
        !          1181:                 *   be first.
        !          1182:                 *
        !          1183:                 * This guidance does not specify what to do if the client
        !          1184:                 * PRL explicitly requests the options out of order, it is
        !          1185:                 * a general statement.
        !          1186:                 */
        !          1187:                if (priority_list[i] == DHO_SUBNET_MASK) {
        !          1188:                        for (ix = i - 1 ; ix >= 0 ; ix--) {
        !          1189:                                if (priority_list[ix] == DHO_ROUTERS) {
        !          1190:                                         /* swap */
        !          1191:                                        priority_list[ix] = DHO_SUBNET_MASK;
        !          1192:                                        priority_list[i] = DHO_ROUTERS;
        !          1193:                                        break;
        !          1194:                                }
        !          1195:                        }
        !          1196:                }
        !          1197:        }
        !          1198: 
        !          1199:        /* Copy out the options in the order that they appear in the
        !          1200:           priority list... */
        !          1201:        for (i = 0; i < priority_len; i++) {
        !          1202:            /* Number of bytes left to store (some may already
        !          1203:               have been stored by a previous pass). */
        !          1204:            unsigned length;
        !          1205:            int optstart, soptstart, toptstart;
        !          1206:            struct universe *u;
        !          1207:            int have_encapsulation = 0;
        !          1208:            struct data_string encapsulation;
        !          1209:            int splitup;
        !          1210: 
        !          1211:            memset (&encapsulation, 0, sizeof encapsulation);
        !          1212:            have_encapsulation = 0;
        !          1213: 
        !          1214:            if (option != NULL)
        !          1215:                option_dereference(&option, MDL);
        !          1216: 
        !          1217:            /* Code for next option to try to store. */
        !          1218:            code = priority_list [i];
        !          1219:            
        !          1220:            /* Look up the option in the site option space if the code
        !          1221:               is above the cutoff, otherwise in the DHCP option space. */
        !          1222:            if (code >= cfg_options -> site_code_min)
        !          1223:                    u = universes [cfg_options -> site_universe];
        !          1224:            else
        !          1225:                    u = &dhcp_universe;
        !          1226: 
        !          1227:            oc = lookup_option (u, cfg_options, code);
        !          1228: 
        !          1229:            if (oc && oc->option)
        !          1230:                option_reference(&option, oc->option, MDL);
        !          1231:            else
        !          1232:                option_code_hash_lookup(&option, u->code_hash, &code, 0, MDL);
        !          1233: 
        !          1234:            /* If it's a straight encapsulation, and the user supplied a
        !          1235:             * value for the entire option, use that.  Otherwise, search
        !          1236:             * the encapsulated space.
        !          1237:             *
        !          1238:             * If it's a limited encapsulation with preceding data, and the
        !          1239:             * user supplied values for the preceding bytes, search the
        !          1240:             * encapsulated space.
        !          1241:             */
        !          1242:            if ((option != NULL) &&
        !          1243:                (((oc == NULL) && (option->format[0] == 'E')) ||
        !          1244:                 ((oc != NULL) && (option->format[0] == 'e')))) {
        !          1245:                static char *s, *t;
        !          1246:                struct option_cache *tmp;
        !          1247:                struct data_string name;
        !          1248: 
        !          1249:                s = strchr (option->format, 'E');
        !          1250:                if (s)
        !          1251:                    t = strchr (++s, '.');
        !          1252:                if (s && t) {
        !          1253:                    memset (&name, 0, sizeof name);
        !          1254: 
        !          1255:                    /* A zero-length universe name means the vendor
        !          1256:                       option space, if one is defined. */
        !          1257:                    if (t == s) {
        !          1258:                        if (vendor_cfg_option) {
        !          1259:                            tmp = lookup_option (vendor_cfg_option -> universe,
        !          1260:                                                 cfg_options,
        !          1261:                                                 vendor_cfg_option -> code);
        !          1262:                            if (tmp)
        !          1263:                                evaluate_option_cache (&name, packet, lease,
        !          1264:                                                       client_state,
        !          1265:                                                       in_options,
        !          1266:                                                       cfg_options,
        !          1267:                                                       scope, tmp, MDL);
        !          1268:                        } else if (vuname) {
        !          1269:                            name.data = (unsigned char *)s;
        !          1270:                            name.len = strlen (s);
        !          1271:                        }
        !          1272:                    } else {
        !          1273:                        name.data = (unsigned char *)s;
        !          1274:                        name.len = t - s;
        !          1275:                    }
        !          1276:                        
        !          1277:                    /* If we found a universe, and there are options configured
        !          1278:                       for that universe, try to encapsulate it. */
        !          1279:                    if (name.len) {
        !          1280:                        have_encapsulation =
        !          1281:                                (option_space_encapsulate
        !          1282:                                 (&encapsulation, packet, lease, client_state,
        !          1283:                                  in_options, cfg_options, scope, &name));
        !          1284:                        data_string_forget (&name, MDL);
        !          1285:                    }
        !          1286:                }
        !          1287:            }
        !          1288: 
        !          1289:            /* In order to avoid memory leaks, we have to get to here
        !          1290:               with any option cache that we allocated in tmp not being
        !          1291:               referenced by tmp, and whatever option cache is referenced
        !          1292:               by oc being an actual reference.   lookup_option doesn't
        !          1293:               generate a reference (this needs to be fixed), so the
        !          1294:               preceding goop ensures that if we *didn't* generate a new
        !          1295:               option cache, oc still winds up holding an actual reference. */
        !          1296: 
        !          1297:            /* If no data is available for this option, skip it. */
        !          1298:            if (!oc && !have_encapsulation) {
        !          1299:                    continue;
        !          1300:            }
        !          1301:            
        !          1302:            /* Find the value of the option... */
        !          1303:            od.len = 0;
        !          1304:            if (oc) {
        !          1305:                evaluate_option_cache (&od, packet,
        !          1306:                                       lease, client_state, in_options,
        !          1307:                                       cfg_options, scope, oc, MDL);
        !          1308: 
        !          1309:                /* If we have encapsulation for this option, and an oc
        !          1310:                 * lookup succeeded, but the evaluation failed, it is
        !          1311:                 * either because this is a complex atom (atoms before
        !          1312:                 * E on format list) and the top half of the option is
        !          1313:                 * not configured, or this is a simple encapsulated
        !          1314:                 * space and the evaluator is giving us a NULL.  Prefer
        !          1315:                 * the evaluator's opinion over the subspace.
        !          1316:                 */
        !          1317:                if (!od.len) {
        !          1318:                    data_string_forget (&encapsulation, MDL);
        !          1319:                    data_string_forget (&od, MDL);
        !          1320:                    continue;
        !          1321:                }
        !          1322:            }
        !          1323: 
        !          1324:            /* We should now have a constant length for the option. */
        !          1325:            length = od.len;
        !          1326:            if (have_encapsulation) {
        !          1327:                    length += encapsulation.len;
        !          1328: 
        !          1329:                    /* od.len can be nonzero if we got here without an
        !          1330:                     * oc (cache lookup failed), but did have an encapsulated
        !          1331:                     * simple encapsulation space.
        !          1332:                     */
        !          1333:                    if (!od.len) {
        !          1334:                            data_string_copy (&od, &encapsulation, MDL);
        !          1335:                            data_string_forget (&encapsulation, MDL);
        !          1336:                    } else {
        !          1337:                            struct buffer *bp = (struct buffer *)0;
        !          1338:                            if (!buffer_allocate (&bp, length, MDL)) {
        !          1339:                                    option_cache_dereference (&oc, MDL);
        !          1340:                                    data_string_forget (&od, MDL);
        !          1341:                                    data_string_forget (&encapsulation, MDL);
        !          1342:                                    continue;
        !          1343:                            }
        !          1344:                            memcpy (&bp -> data [0], od.data, od.len);
        !          1345:                            memcpy (&bp -> data [od.len], encapsulation.data,
        !          1346:                                    encapsulation.len);
        !          1347:                            data_string_forget (&od, MDL);
        !          1348:                            data_string_forget (&encapsulation, MDL);
        !          1349:                            od.data = &bp -> data [0];
        !          1350:                            buffer_reference (&od.buffer, bp, MDL);
        !          1351:                            buffer_dereference (&bp, MDL);
        !          1352:                            od.len = length;
        !          1353:                            od.terminated = 0;
        !          1354:                    }
        !          1355:            }
        !          1356: 
        !          1357:            /* Do we add a NUL? */
        !          1358:            if (terminate && option && format_has_text(option->format)) {
        !          1359:                    length++;
        !          1360:                    tto = 1;
        !          1361:            } else {
        !          1362:                    tto = 0;
        !          1363:            }
        !          1364: 
        !          1365:            /* Try to store the option. */
        !          1366:            
        !          1367:            /* If the option's length is more than 255, we must store it
        !          1368:               in multiple hunks.   Store 255-byte hunks first.  However,
        !          1369:               in any case, if the option data will cross a buffer
        !          1370:               boundary, split it across that boundary. */
        !          1371: 
        !          1372:            if (length > 255)
        !          1373:                splitup = 1;
        !          1374:            else
        !          1375:                splitup = 0;
        !          1376: 
        !          1377:            ix = 0;
        !          1378:            optstart = bufix;
        !          1379:            soptstart = six;
        !          1380:            toptstart = tix;
        !          1381:            while (length) {
        !          1382:                    unsigned incr = length;
        !          1383:                    int *pix;
        !          1384:                    unsigned char *base;
        !          1385: 
        !          1386:                    /* Try to fit it in the options buffer. */
        !          1387:                    if (!splitup &&
        !          1388:                        ((!six && !tix && (i == priority_len - 1) &&
        !          1389:                          (bufix + 2 + length < bufend)) ||
        !          1390:                         (bufix + 5 + length < bufend))) {
        !          1391:                        base = buffer;
        !          1392:                        pix = &bufix;
        !          1393:                    /* Try to fit it in the second buffer. */
        !          1394:                    } else if (!splitup && first_cutoff &&
        !          1395:                               (first_cutoff + six + 3 + length < sbufend)) {
        !          1396:                        base = &buffer[first_cutoff];
        !          1397:                        pix = &six;
        !          1398:                    /* Try to fit it in the third buffer. */
        !          1399:                    } else if (!splitup && second_cutoff &&
        !          1400:                               (second_cutoff + tix + 3 + length < buflen)) {
        !          1401:                        base = &buffer[second_cutoff];
        !          1402:                        pix = &tix;
        !          1403:                    /* Split the option up into the remaining space. */
        !          1404:                    } else {
        !          1405:                        splitup = 1;
        !          1406: 
        !          1407:                        /* Use any remaining options space. */
        !          1408:                        if (bufix + 6 < bufend) {
        !          1409:                            incr = bufend - bufix - 5;
        !          1410:                            base = buffer;
        !          1411:                            pix = &bufix;
        !          1412:                        /* Use any remaining first_cutoff space. */
        !          1413:                        } else if (first_cutoff &&
        !          1414:                                   (first_cutoff + six + 4 < sbufend)) {
        !          1415:                            incr = sbufend - (first_cutoff + six) - 3;
        !          1416:                            base = &buffer[first_cutoff];
        !          1417:                            pix = &six;
        !          1418:                        /* Use any remaining second_cutoff space. */
        !          1419:                        } else if (second_cutoff &&
        !          1420:                                   (second_cutoff + tix + 4 < buflen)) {
        !          1421:                            incr = buflen - (second_cutoff + tix) - 3;
        !          1422:                            base = &buffer[second_cutoff];
        !          1423:                            pix = &tix;
        !          1424:                        /* Give up, roll back this option. */
        !          1425:                        } else {
        !          1426:                            bufix = optstart;
        !          1427:                            six = soptstart;
        !          1428:                            tix = toptstart;
        !          1429:                            break;
        !          1430:                        }
        !          1431:                    }
        !          1432: 
        !          1433:                    if (incr > length)
        !          1434:                        incr = length;
        !          1435:                    if (incr > 255)
        !          1436:                        incr = 255;
        !          1437: 
        !          1438:                    /* Everything looks good - copy it in! */
        !          1439:                    base [*pix] = code;
        !          1440:                    base [*pix + 1] = (unsigned char)incr;
        !          1441:                    if (tto && incr == length) {
        !          1442:                            if (incr > 1)
        !          1443:                                memcpy (base + *pix + 2,
        !          1444:                                        od.data + ix, (unsigned)(incr - 1));
        !          1445:                            base [*pix + 2 + incr - 1] = 0;
        !          1446:                    } else {
        !          1447:                            memcpy (base + *pix + 2,
        !          1448:                                    od.data + ix, (unsigned)incr);
        !          1449:                    }
        !          1450:                    length -= incr;
        !          1451:                    ix += incr;
        !          1452:                    *pix += 2 + incr;
        !          1453:            }
        !          1454:            data_string_forget (&od, MDL);
        !          1455:        }
        !          1456: 
        !          1457:        if (option != NULL)
        !          1458:            option_dereference(&option, MDL);
        !          1459: 
        !          1460:        /* If we can overload, and we have, then PAD and END those spaces. */
        !          1461:        if (first_cutoff && six) {
        !          1462:            if ((first_cutoff + six + 1) < sbufend)
        !          1463:                memset (&buffer[first_cutoff + six + 1], DHO_PAD,
        !          1464:                        sbufend - (first_cutoff + six + 1));
        !          1465:            else if (first_cutoff + six >= sbufend)
        !          1466:                log_fatal("Second buffer overflow in overloaded options.");
        !          1467: 
        !          1468:            buffer[first_cutoff + six] = DHO_END;
        !          1469:            if (ocount != NULL)
        !          1470:                *ocount |= 1; /* So that caller knows there's data there. */
        !          1471:        }
        !          1472: 
        !          1473:        if (second_cutoff && tix) {
        !          1474:            if (second_cutoff + tix + 1 < buflen) {
        !          1475:                memset (&buffer[second_cutoff + tix + 1], DHO_PAD,
        !          1476:                        buflen - (second_cutoff + tix + 1));
        !          1477:            } else if (second_cutoff + tix >= buflen)
        !          1478:                log_fatal("Third buffer overflow in overloaded options.");
        !          1479: 
        !          1480:            buffer[second_cutoff + tix] = DHO_END;
        !          1481:            if (ocount != NULL)
        !          1482:                *ocount |= 2; /* So that caller knows there's data there. */
        !          1483:        }
        !          1484: 
        !          1485:        if ((six || tix) && (bufix + 3 > bufend))
        !          1486:            log_fatal("Not enough space for option overload option.");
        !          1487: 
        !          1488:        return bufix;
        !          1489: }
        !          1490: 
        !          1491: /* Return true if the format string has a variable length text option
        !          1492:  * ("t"), return false otherwise.
        !          1493:  */
        !          1494: 
        !          1495: int
        !          1496: format_has_text(format)
        !          1497:        const char *format;
        !          1498: {
        !          1499:        const char *p;
        !          1500: 
        !          1501:        p = format;
        !          1502:        while (*p != '\0') {
        !          1503:                switch (*p++) {
        !          1504:                    case 'd':
        !          1505:                    case 't':
        !          1506:                        return 1;
        !          1507: 
        !          1508:                        /* These symbols are arbitrary, not fixed or
        !          1509:                         * determinable length...text options with them is
        !          1510:                         * invalid (whatever the case, they are never NULL
        !          1511:                         * terminated).
        !          1512:                         */
        !          1513:                    case 'A':
        !          1514:                    case 'a':
        !          1515:                    case 'X':
        !          1516:                    case 'x':
        !          1517:                    case 'D':
        !          1518:                        return 0;
        !          1519: 
        !          1520:                    case 'c':
        !          1521:                        /* 'c' only follows 'D' atoms, and indicates that
        !          1522:                         * compression may be used.  If there was a 'D'
        !          1523:                         * atom already, we would have returned.  So this
        !          1524:                         * is an error, but continue looking for 't' anyway.
        !          1525:                         */
        !          1526:                        log_error("format_has_text(%s): 'c' atoms are illegal "
        !          1527:                                  "except after 'D' atoms.", format);
        !          1528:                        break;
        !          1529: 
        !          1530:                        /* 'E' is variable length, but not arbitrary...you
        !          1531:                         * can find its length if you can find an END option.
        !          1532:                         * N is (n)-byte in length but trails a name of a
        !          1533:                         * space defining the enumeration values.  So treat
        !          1534:                         * both the same - valid, fixed-length fields.
        !          1535:                         */
        !          1536:                    case 'E':
        !          1537:                    case 'N':
        !          1538:                        /* Consume the space name. */
        !          1539:                        while ((*p != '\0') && (*p++ != '.'))
        !          1540:                                ;
        !          1541:                        break;
        !          1542: 
        !          1543:                    default:
        !          1544:                        break;
        !          1545:                }
        !          1546:        }
        !          1547: 
        !          1548:        return 0;
        !          1549: }
        !          1550: 
        !          1551: /* Determine the minimum length of a DHCP option prior to any variable
        !          1552:  * or inconsistent length formats, according to its configured format
        !          1553:  * variable (and possibly from supplied option cache contents for variable
        !          1554:  * length format symbols).
        !          1555:  */
        !          1556: 
        !          1557: int
        !          1558: format_min_length(format, oc)
        !          1559:        const char *format;
        !          1560:        struct option_cache *oc;
        !          1561: {
        !          1562:        const char *p, *name;
        !          1563:        int min_len = 0;
        !          1564:        int last_size = 0;
        !          1565:        struct enumeration *espace;
        !          1566: 
        !          1567:        p = format;
        !          1568:        while (*p != '\0') {
        !          1569:                switch (*p++) {
        !          1570:                    case '6': /* IPv6 Address */
        !          1571:                        min_len += 16;
        !          1572:                        last_size = 16;
        !          1573:                        break;
        !          1574: 
        !          1575:                    case 'I': /* IPv4 Address */
        !          1576:                    case 'l': /* int32_t */
        !          1577:                    case 'L': /* uint32_t */
        !          1578:                    case 'T': /* Lease Time, uint32_t equivalent */
        !          1579:                        min_len += 4;
        !          1580:                        last_size = 4;
        !          1581:                        break;
        !          1582: 
        !          1583:                    case 's': /* int16_t */
        !          1584:                    case 'S': /* uint16_t */
        !          1585:                        min_len += 2;
        !          1586:                        last_size = 2;
        !          1587:                        break;
        !          1588: 
        !          1589:                    case 'N': /* Enumeration value. */
        !          1590:                        /* Consume space name. */
        !          1591:                        name = p;
        !          1592:                        p = strchr(p, '.');
        !          1593:                        if (p == NULL)
        !          1594:                                log_fatal("Corrupt format: %s", format);
        !          1595: 
        !          1596:                        espace = find_enumeration(name, p - name);
        !          1597:                        if (espace == NULL) {
        !          1598:                                log_error("Unknown enumeration: %s", format);
        !          1599:                                /* Max is safest value to return. */
        !          1600:                                return INT_MAX;
        !          1601:                        }
        !          1602: 
        !          1603:                        min_len += espace->width;
        !          1604:                        last_size = espace->width;
        !          1605:                        p++;
        !          1606: 
        !          1607:                        break;
        !          1608: 
        !          1609:                    case 'b': /* int8_t */
        !          1610:                    case 'B': /* uint8_t */
        !          1611:                    case 'F': /* Flag that is always true. */
        !          1612:                    case 'f': /* Flag */
        !          1613:                        min_len++;
        !          1614:                        last_size = 1;
        !          1615:                        break;
        !          1616: 
        !          1617:                    case 'o': /* Last argument is optional. */
        !          1618:                        min_len -= last_size;
        !          1619: 
        !          1620:                    /* XXX: It MAY be possible to sense the end of an
        !          1621:                     * encapsulated space, but right now this is too
        !          1622:                     * hard to support.  Return a safe value.
        !          1623:                     */
        !          1624:                    case 'e': /* Encapsulation hint (there is an 'E' later). */
        !          1625:                    case 'E': /* Encapsulated options. */
        !          1626:                        return min_len;
        !          1627: 
        !          1628:                    case 'd': /* "Domain name" */
        !          1629:                    case 'D': /* "rfc1035 formatted names" */
        !          1630:                    case 't': /* "ASCII Text" */
        !          1631:                    case 'X': /* "ASCII or Hex Conditional */
        !          1632:                    case 'x': /* "Hex" */
        !          1633:                    case 'A': /* Array of all that precedes. */
        !          1634:                    case 'a': /* Array of preceding symbol. */
        !          1635:                    case 'Z': /* nothing. */
        !          1636:                        return min_len;
        !          1637: 
        !          1638:                    case 'c': /* Compress flag for D atom. */
        !          1639:                        log_error("format_min_length(%s): 'c' atom is illegal "
        !          1640:                                  "except after 'D' atom.", format);
        !          1641:                        return INT_MAX;
        !          1642: 
        !          1643:                    default:
        !          1644:                        /* No safe value is known. */
        !          1645:                        log_error("format_min_length(%s): No safe value "
        !          1646:                                  "for unknown format symbols.", format);
        !          1647:                        return INT_MAX;
        !          1648:                }
        !          1649:        }
        !          1650: 
        !          1651:        return min_len;
        !          1652: }
        !          1653: 
        !          1654: 
        !          1655: /* Format the specified option so that a human can easily read it. */
        !          1656: 
        !          1657: const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
        !          1658:        struct option *option;
        !          1659:        const unsigned char *data;
        !          1660:        unsigned len;
        !          1661:        int emit_commas;
        !          1662:        int emit_quotes;
        !          1663: {
        !          1664:        static char optbuf [32768]; /* XXX */
        !          1665:        static char *endbuf = &optbuf[sizeof(optbuf)];
        !          1666:        int hunksize = 0;
        !          1667:        int opthunk = 0;
        !          1668:        int hunkinc = 0;
        !          1669:        int numhunk = -1;
        !          1670:        int numelem = 0;
        !          1671:        int count;
        !          1672:        int i, j, k, l;
        !          1673:        char fmtbuf[32] = "";
        !          1674:        struct iaddr iaddr;
        !          1675:        struct enumeration *enumbuf[32]; /* MUST be same as fmtbuf */
        !          1676:        char *op = optbuf;
        !          1677:        const unsigned char *dp = data;
        !          1678:        char comma;
        !          1679:        unsigned long tval;
        !          1680: 
        !          1681:        if (emit_commas)
        !          1682:                comma = ',';
        !          1683:        else
        !          1684:                comma = ' ';
        !          1685: 
        !          1686:        memset (enumbuf, 0, sizeof enumbuf);
        !          1687: 
        !          1688:        /* Figure out the size of the data. */
        !          1689:        for (l = i = 0; option -> format [i]; i++, l++) {
        !          1690:                if (l >= sizeof(fmtbuf) - 1)
        !          1691:                        log_fatal("Bounds failure on internal buffer at "
        !          1692:                                  "%s:%d", MDL);
        !          1693: 
        !          1694:                if (!numhunk) {
        !          1695:                        log_error ("%s: Extra codes in format string: %s",
        !          1696:                                   option -> name,
        !          1697:                                   &(option -> format [i]));
        !          1698:                        break;
        !          1699:                }
        !          1700:                numelem++;
        !          1701:                fmtbuf [l] = option -> format [i];
        !          1702:                switch (option -> format [i]) {
        !          1703:                      case 'a':
        !          1704:                      case 'A':
        !          1705:                        --numelem;
        !          1706:                        fmtbuf [l] = 0;
        !          1707:                        numhunk = 0;
        !          1708:                        break;
        !          1709:                      case 'E':
        !          1710:                        /* Skip the universe name. */
        !          1711:                        while (option -> format [i] &&
        !          1712:                               option -> format [i] != '.')
        !          1713:                                i++;
        !          1714:                        /* Fall Through! */
        !          1715:                      case 'X':
        !          1716:                        for (k = 0; k < len; k++) {
        !          1717:                                if (!isascii (data [k]) ||
        !          1718:                                    !isprint (data [k]))
        !          1719:                                        break;
        !          1720:                        }
        !          1721:                        /* If we found no bogus characters, or the bogus
        !          1722:                           character we found is a trailing NUL, it's
        !          1723:                           okay to print this option as text. */
        !          1724:                        if (k == len || (k + 1 == len && data [k] == 0)) {
        !          1725:                                fmtbuf [l] = 't';
        !          1726:                                numhunk = -2;
        !          1727:                        } else {
        !          1728:                                fmtbuf [l] = 'x';
        !          1729:                                hunksize++;
        !          1730:                                comma = ':';
        !          1731:                                numhunk = 0;
        !          1732:                        }
        !          1733:                        fmtbuf [l + 1] = 0;
        !          1734:                        break;
        !          1735:                      case 'c':
        !          1736:                        /* The 'c' atom is a 'D' modifier only. */
        !          1737:                        log_error("'c' atom not following D atom in format "
        !          1738:                                  "string: %s", option->format);
        !          1739:                        break;
        !          1740:                      case 'D':
        !          1741:                        /*
        !          1742:                         * Skip the 'c' atom, if present.  It does not affect
        !          1743:                         * how we convert wire->text format (if compression is
        !          1744:                         * present either way, we still process it).
        !          1745:                         */
        !          1746:                        if (option->format[i+1] == 'c')
        !          1747:                                i++;
        !          1748:                        fmtbuf[l + 1] = 0;
        !          1749:                        numhunk = -2;
        !          1750:                        break;
        !          1751:                      case 'd':
        !          1752:                        fmtbuf[l] = 't';
        !          1753:                        /* Fall Through ! */
        !          1754:                      case 't':
        !          1755:                        fmtbuf[l + 1] = 0;
        !          1756:                        numhunk = -2;
        !          1757:                        break;
        !          1758:                      case 'N':
        !          1759:                        k = i;
        !          1760:                        while (option -> format [i] &&
        !          1761:                               option -> format [i] != '.')
        !          1762:                                i++;
        !          1763:                        enumbuf [l] =
        !          1764:                                find_enumeration (&option -> format [k] + 1,
        !          1765:                                                  i - k - 1);
        !          1766:                        if (enumbuf[l] == NULL) {
        !          1767:                                hunksize += 1;
        !          1768:                                hunkinc = 1;
        !          1769:                        } else {
        !          1770:                                hunksize += enumbuf[l]->width;
        !          1771:                                hunkinc = enumbuf[l]->width;
        !          1772:                        }
        !          1773:                        break;
        !          1774:                      case '6':
        !          1775:                        hunksize += 16;
        !          1776:                        hunkinc = 16;
        !          1777:                        break;
        !          1778:                      case 'I':
        !          1779:                      case 'l':
        !          1780:                      case 'L':
        !          1781:                      case 'T':
        !          1782:                        hunksize += 4;
        !          1783:                        hunkinc = 4;
        !          1784:                        break;
        !          1785:                      case 's':
        !          1786:                      case 'S':
        !          1787:                        hunksize += 2;
        !          1788:                        hunkinc = 2;
        !          1789:                        break;
        !          1790:                      case 'b':
        !          1791:                      case 'B':
        !          1792:                      case 'f':
        !          1793:                      case 'F':
        !          1794:                        hunksize++;
        !          1795:                        hunkinc = 1;
        !          1796:                        break;
        !          1797:                      case 'e':
        !          1798:                      case 'Z':
        !          1799:                        break;
        !          1800:                      case 'o':
        !          1801:                        opthunk += hunkinc;
        !          1802:                        break;
        !          1803:                      default:
        !          1804:                        log_error ("%s: garbage in format string: %s",
        !          1805:                              option -> name,
        !          1806:                              &(option -> format [i]));
        !          1807:                        break;
        !          1808:                } 
        !          1809:        }
        !          1810: 
        !          1811:        /* Check for too few bytes... */
        !          1812:        if (hunksize - opthunk > len) {
        !          1813:                log_error ("%s: expecting at least %d bytes; got %d",
        !          1814:                      option -> name,
        !          1815:                      hunksize, len);
        !          1816:                return "<error>";
        !          1817:        }
        !          1818:        /* Check for too many bytes... */
        !          1819:        if (numhunk == -1 && hunksize < len)
        !          1820:                log_error ("%s: %d extra bytes",
        !          1821:                      option -> name,
        !          1822:                      len - hunksize);
        !          1823: 
        !          1824:        /* If this is an array, compute its size. */
        !          1825:        if (!numhunk)
        !          1826:                numhunk = len / hunksize;
        !          1827:        /* See if we got an exact number of hunks. */
        !          1828:        if (numhunk > 0 && numhunk * hunksize < len)
        !          1829:                log_error ("%s: %d extra bytes at end of array\n",
        !          1830:                      option -> name,
        !          1831:                      len - numhunk * hunksize);
        !          1832: 
        !          1833:        /* A one-hunk array prints the same as a single hunk. */
        !          1834:        if (numhunk < 0)
        !          1835:                numhunk = 1;
        !          1836: 
        !          1837:        /* Cycle through the array (or hunk) printing the data. */
        !          1838:        for (i = 0; i < numhunk; i++) {
        !          1839:                for (j = 0; j < numelem; j++) {
        !          1840:                        switch (fmtbuf [j]) {
        !          1841:                              case 't':
        !          1842:                                /* endbuf-1 leaves room for NULL. */
        !          1843:                                k = pretty_text(&op, endbuf - 1, &dp,
        !          1844:                                                data + len, emit_quotes);
        !          1845:                                if (k == -1) {
        !          1846:                                        log_error("Error printing text.");
        !          1847:                                        break;
        !          1848:                                }
        !          1849:                                *op = 0;
        !          1850:                                break;
        !          1851:                              case 'D': /* RFC1035 format name list */
        !          1852:                                for( ; dp < (data + len) ; dp += k) {
        !          1853:                                        unsigned char nbuff[NS_MAXCDNAME];
        !          1854:                                        const unsigned char *nbp, *nend;
        !          1855: 
        !          1856:                                        nend = &nbuff[sizeof(nbuff)];
        !          1857: 
        !          1858:                                        /* If this is for ISC DHCP consumption
        !          1859:                                         * (emit_quotes), lay it out as a list
        !          1860:                                         * of STRING tokens.  Otherwise, it is
        !          1861:                                         * a space-separated list of DNS-
        !          1862:                                         * escaped names as /etc/resolv.conf
        !          1863:                                         * might digest.
        !          1864:                                         */
        !          1865:                                        if (dp != data) {
        !          1866:                                                if (op + 2 > endbuf)
        !          1867:                                                        break;
        !          1868: 
        !          1869:                                                if (emit_quotes)
        !          1870:                                                        *op++ = ',';
        !          1871:                                                *op++ = ' ';
        !          1872:                                        }
        !          1873: 
        !          1874:                                        /* XXX: if fmtbuf[j+1] != 'c', we
        !          1875:                                         * should warn if the data was
        !          1876:                                         * compressed anyway.
        !          1877:                                         */
        !          1878:                                        k = MRns_name_unpack(data,
        !          1879:                                                             data + len,
        !          1880:                                                             dp, nbuff,
        !          1881:                                                             sizeof(nbuff));
        !          1882: 
        !          1883:                                        if (k == -1) {
        !          1884:                                                log_error("Invalid domain "
        !          1885:                                                          "list.");
        !          1886:                                                break;
        !          1887:                                        }
        !          1888: 
        !          1889:                                        /* If emit_quotes, then use ISC DHCP
        !          1890:                                         * escapes.  Otherwise, rely only on
        !          1891:                                         * ns_name_ntop().
        !          1892:                                         */
        !          1893:                                        if (emit_quotes) {
        !          1894:                                                nbp = nbuff;
        !          1895:                                                pretty_domain(&op, endbuf-1,
        !          1896:                                                              &nbp, nend);
        !          1897:                                        } else {
        !          1898:                                                /* ns_name_ntop() includes
        !          1899:                                                 * a trailing NUL in its
        !          1900:                                                 * count.
        !          1901:                                                 */
        !          1902:                                                count = MRns_name_ntop(
        !          1903:                                                                nbuff, op, 
        !          1904:                                                                (endbuf-op)-1);
        !          1905: 
        !          1906:                                                if (count <= 0) {
        !          1907:                                                        log_error("Invalid "
        !          1908:                                                                "domain name.");
        !          1909:                                                        break;
        !          1910:                                                }
        !          1911: 
        !          1912:                                                /* Consume all but the trailing
        !          1913:                                                 * NUL.
        !          1914:                                                 */
        !          1915:                                                op += count - 1;
        !          1916: 
        !          1917:                                                /* Replace the trailing NUL
        !          1918:                                                 * with the implicit root
        !          1919:                                                 * (in the unlikely event the
        !          1920:                                                 * domain name /is/ the root).
        !          1921:                                                 */
        !          1922:                                                *op++ = '.';
        !          1923:                                        }
        !          1924:                                }
        !          1925:                                *op = '\0';
        !          1926:                                break;
        !          1927:                                /* pretty-printing an array of enums is
        !          1928:                                   going to get ugly. */
        !          1929:                              case 'N':
        !          1930:                                if (!enumbuf [j]) {
        !          1931:                                        tval = *dp++;
        !          1932:                                        goto enum_as_num;
        !          1933:                                }
        !          1934: 
        !          1935:                                switch (enumbuf[j]->width) {
        !          1936:                                      case 1:
        !          1937:                                        tval = getUChar(dp);
        !          1938:                                        break;
        !          1939: 
        !          1940:                                     case 2:
        !          1941:                                        tval = getUShort(dp);
        !          1942:                                        break;
        !          1943: 
        !          1944:                                    case 4:
        !          1945:                                        tval = getULong(dp);
        !          1946:                                        break;
        !          1947: 
        !          1948:                                    default:
        !          1949:                                        log_fatal("Impossible case at %s:%d.",
        !          1950:                                                  MDL);
        !          1951:                                        return "<double impossible condition>";
        !          1952:                                }
        !          1953: 
        !          1954:                                for (i = 0; ;i++) {
        !          1955:                                        if (!enumbuf [j] -> values [i].name)
        !          1956:                                                goto enum_as_num;
        !          1957:                                        if (enumbuf [j] -> values [i].value ==
        !          1958:                                            tval)
        !          1959:                                                break;
        !          1960:                                }
        !          1961:                                strcpy (op, enumbuf [j] -> values [i].name);
        !          1962:                                dp += enumbuf[j]->width;
        !          1963:                                break;
        !          1964: 
        !          1965:                              enum_as_num:
        !          1966:                                sprintf(op, "%lu", tval);
        !          1967:                                break;
        !          1968: 
        !          1969:                              case 'I':
        !          1970:                                iaddr.len = 4;
        !          1971:                                memcpy(iaddr.iabuf, dp, 4);
        !          1972:                                strcpy(op, piaddr(iaddr));
        !          1973:                                dp += 4;
        !          1974:                                break;
        !          1975:                              case '6':
        !          1976:                                iaddr.len = 16;
        !          1977:                                memcpy(iaddr.iabuf, dp, 16);
        !          1978:                                strcpy(op, piaddr(iaddr));
        !          1979:                                dp += 16;
        !          1980:                                break;
        !          1981:                              case 'l':
        !          1982:                                sprintf (op, "%ld", (long)getLong (dp));
        !          1983:                                dp += 4;
        !          1984:                                break;
        !          1985:                              case 'T':
        !          1986:                                tval = getULong (dp);
        !          1987:                                if (tval == -1)
        !          1988:                                        sprintf (op, "%s", "infinite");
        !          1989:                                else
        !          1990:                                        sprintf(op, "%lu", tval);
        !          1991:                                break;
        !          1992:                              case 'L':
        !          1993:                                sprintf(op, "%lu",
        !          1994:                                        (unsigned long)getULong(dp));
        !          1995:                                dp += 4;
        !          1996:                                break;
        !          1997:                              case 's':
        !          1998:                                sprintf (op, "%d", (int)getShort (dp));
        !          1999:                                dp += 2;
        !          2000:                                break;
        !          2001:                              case 'S':
        !          2002:                                sprintf(op, "%u", (unsigned)getUShort(dp));
        !          2003:                                dp += 2;
        !          2004:                                break;
        !          2005:                              case 'b':
        !          2006:                                sprintf (op, "%d", *(const char *)dp++);
        !          2007:                                break;
        !          2008:                              case 'B':
        !          2009:                                sprintf (op, "%d", *dp++);
        !          2010:                                break;
        !          2011:                              case 'X':
        !          2012:                              case 'x':
        !          2013:                                sprintf (op, "%x", *dp++);
        !          2014:                                break;
        !          2015:                              case 'f':
        !          2016:                                strcpy (op, *dp++ ? "true" : "false");
        !          2017:                                break;
        !          2018:                              case 'F':
        !          2019:                                strcpy (op, "true");
        !          2020:                                break;
        !          2021:                              case 'e':
        !          2022:                              case 'Z':
        !          2023:                                *op = '\0';
        !          2024:                                break;
        !          2025:                              default:
        !          2026:                                log_error ("Unexpected format code %c",
        !          2027:                                           fmtbuf [j]);
        !          2028:                        }
        !          2029:                        op += strlen (op);
        !          2030:                        if (dp == data + len)
        !          2031:                                break;
        !          2032:                        if (j + 1 < numelem && comma != ':')
        !          2033:                                *op++ = ' ';
        !          2034:                }
        !          2035:                if (i + 1 < numhunk) {
        !          2036:                        *op++ = comma;
        !          2037:                }
        !          2038:                if (dp == data + len)
        !          2039:                        break;
        !          2040:        }
        !          2041:        return optbuf;
        !          2042: }
        !          2043: 
        !          2044: int get_option (result, universe, packet, lease, client_state,
        !          2045:                in_options, cfg_options, options, scope, code, file, line)
        !          2046:        struct data_string *result;
        !          2047:        struct universe *universe;
        !          2048:        struct packet *packet;
        !          2049:        struct lease *lease;
        !          2050:        struct client_state *client_state;
        !          2051:        struct option_state *in_options;
        !          2052:        struct option_state *cfg_options;
        !          2053:        struct option_state *options;
        !          2054:        struct binding_scope **scope;
        !          2055:        unsigned code;
        !          2056:        const char *file;
        !          2057:        int line;
        !          2058: {
        !          2059:        struct option_cache *oc;
        !          2060: 
        !          2061:        if (!universe -> lookup_func)
        !          2062:                return 0;
        !          2063:        oc = ((*universe -> lookup_func) (universe, options, code));
        !          2064:        if (!oc)
        !          2065:                return 0;
        !          2066:        if (!evaluate_option_cache (result, packet, lease, client_state,
        !          2067:                                    in_options, cfg_options, scope, oc,
        !          2068:                                    file, line))
        !          2069:                return 0;
        !          2070:        return 1;
        !          2071: }
        !          2072: 
        !          2073: void set_option (universe, options, option, op)
        !          2074:        struct universe *universe;
        !          2075:        struct option_state *options;
        !          2076:        struct option_cache *option;
        !          2077:        enum statement_op op;
        !          2078: {
        !          2079:        struct option_cache *oc, *noc;
        !          2080: 
        !          2081:        switch (op) {
        !          2082:              case if_statement:
        !          2083:              case add_statement:
        !          2084:              case eval_statement:
        !          2085:              case break_statement:
        !          2086:              default:
        !          2087:                log_error ("bogus statement type in set_option.");
        !          2088:                break;
        !          2089: 
        !          2090:              case default_option_statement:
        !          2091:                oc = lookup_option (universe, options,
        !          2092:                                    option -> option -> code);
        !          2093:                if (oc)
        !          2094:                        break;
        !          2095:                save_option (universe, options, option);
        !          2096:                break;
        !          2097: 
        !          2098:              case supersede_option_statement:
        !          2099:              case send_option_statement:
        !          2100:                /* Install the option, replacing any existing version. */
        !          2101:                save_option (universe, options, option);
        !          2102:                break;
        !          2103: 
        !          2104:              case append_option_statement:
        !          2105:              case prepend_option_statement:
        !          2106:                oc = lookup_option (universe, options,
        !          2107:                                    option -> option -> code);
        !          2108:                if (!oc) {
        !          2109:                        save_option (universe, options, option);
        !          2110:                        break;
        !          2111:                }
        !          2112:                /* If it's not an expression, make it into one. */
        !          2113:                if (!oc -> expression && oc -> data.len) {
        !          2114:                        if (!expression_allocate (&oc -> expression, MDL)) {
        !          2115:                                log_error ("Can't allocate const expression.");
        !          2116:                                break;
        !          2117:                        }
        !          2118:                        oc -> expression -> op = expr_const_data;
        !          2119:                        data_string_copy
        !          2120:                                (&oc -> expression -> data.const_data,
        !          2121:                                 &oc -> data, MDL);
        !          2122:                        data_string_forget (&oc -> data, MDL);
        !          2123:                }
        !          2124:                noc = (struct option_cache *)0;
        !          2125:                if (!option_cache_allocate (&noc, MDL))
        !          2126:                        break;
        !          2127:                if (op == append_option_statement) {
        !          2128:                        if (!make_concat (&noc -> expression,
        !          2129:                                          oc -> expression,
        !          2130:                                          option -> expression)) {
        !          2131:                                option_cache_dereference (&noc, MDL);
        !          2132:                                break;
        !          2133:                        }
        !          2134:                } else {
        !          2135:                        if (!make_concat (&noc -> expression,
        !          2136:                                          option -> expression,
        !          2137:                                          oc -> expression)) {
        !          2138:                                option_cache_dereference (&noc, MDL);
        !          2139:                                break;
        !          2140:                        }
        !          2141:                }
        !          2142:                option_reference(&(noc->option), oc->option, MDL);
        !          2143:                save_option (universe, options, noc);
        !          2144:                option_cache_dereference (&noc, MDL);
        !          2145:                break;
        !          2146:        }
        !          2147: }
        !          2148: 
        !          2149: struct option_cache *lookup_option (universe, options, code)
        !          2150:        struct universe *universe;
        !          2151:        struct option_state *options;
        !          2152:        unsigned code;
        !          2153: {
        !          2154:        if (!options)
        !          2155:                return (struct option_cache *)0;
        !          2156:        if (universe -> lookup_func)
        !          2157:                return (*universe -> lookup_func) (universe, options, code);
        !          2158:        else
        !          2159:                log_error ("can't look up options in %s space.",
        !          2160:                           universe -> name);
        !          2161:        return (struct option_cache *)0;
        !          2162: }
        !          2163: 
        !          2164: struct option_cache *lookup_hashed_option (universe, options, code)
        !          2165:        struct universe *universe;
        !          2166:        struct option_state *options;
        !          2167:        unsigned code;
        !          2168: {
        !          2169:        int hashix;
        !          2170:        pair bptr;
        !          2171:        pair *hash;
        !          2172: 
        !          2173:        /* Make sure there's a hash table. */
        !          2174:        if (universe -> index >= options -> universe_count ||
        !          2175:            !(options -> universes [universe -> index]))
        !          2176:                return (struct option_cache *)0;
        !          2177: 
        !          2178:        hash = options -> universes [universe -> index];
        !          2179: 
        !          2180:        hashix = compute_option_hash (code);
        !          2181:        for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
        !          2182:                if (((struct option_cache *)(bptr -> car)) -> option -> code ==
        !          2183:                    code)
        !          2184:                        return (struct option_cache *)(bptr -> car);
        !          2185:        }
        !          2186:        return (struct option_cache *)0;
        !          2187: }
        !          2188: 
        !          2189: /* Save a specified buffer into an option cache. */
        !          2190: int
        !          2191: save_option_buffer(struct universe *universe, struct option_state *options,
        !          2192:                   struct buffer *bp, unsigned char *buffer, unsigned length,
        !          2193:                   unsigned code, int terminatep)
        !          2194: {
        !          2195:        struct option_cache *op = NULL;
        !          2196:        int status = 1;
        !          2197: 
        !          2198:        status = prepare_option_buffer(universe, bp, buffer, length, code,
        !          2199:                                       terminatep, &op);
        !          2200: 
        !          2201:        if (status == 0)
        !          2202:                goto cleanup;
        !          2203: 
        !          2204:        save_option(universe, options, op);
        !          2205: 
        !          2206:     cleanup:
        !          2207:        if (op != NULL)
        !          2208:                option_cache_dereference(&op, MDL);
        !          2209: 
        !          2210:        return status;
        !          2211: }
        !          2212: 
        !          2213: /* Append a specified buffer onto the tail of an option cache. */
        !          2214: int
        !          2215: append_option_buffer(struct universe *universe, struct option_state *options,
        !          2216:                     struct buffer *bp, unsigned char *buffer, unsigned length,
        !          2217:                     unsigned code, int terminatep)
        !          2218: {
        !          2219:        struct option_cache *op = NULL;
        !          2220:        int status = 1;
        !          2221: 
        !          2222:        status = prepare_option_buffer(universe, bp, buffer, length, code,
        !          2223:                                       terminatep, &op);
        !          2224: 
        !          2225:        if (status == 0)
        !          2226:                goto cleanup;
        !          2227: 
        !          2228:        also_save_option(universe, options, op);
        !          2229: 
        !          2230:       cleanup:
        !          2231:        if (op != NULL)
        !          2232:                option_cache_dereference(&op, MDL);
        !          2233: 
        !          2234:        return status;
        !          2235: }
        !          2236: 
        !          2237: /* Create/copy a buffer into a new option cache. */
        !          2238: static int
        !          2239: prepare_option_buffer(struct universe *universe, struct buffer *bp,
        !          2240:                      unsigned char *buffer, unsigned length, unsigned code,
        !          2241:                      int terminatep, struct option_cache **opp)
        !          2242: {
        !          2243:        struct buffer *lbp = NULL;
        !          2244:        struct option *option = NULL;
        !          2245:        struct option_cache *op;
        !          2246:        int status = 1;
        !          2247: 
        !          2248:        /* Code sizes of 8, 16, and 32 bits are allowed. */
        !          2249:        switch(universe->tag_size) {
        !          2250:              case 1:
        !          2251:                if (code > 0xff)
        !          2252:                        return 0;
        !          2253:                break;
        !          2254:              case 2:
        !          2255:                if (code > 0xffff)
        !          2256:                        return 0;
        !          2257:                break;
        !          2258:              case 4:
        !          2259:                if (code > 0xffffffff)
        !          2260:                        return 0;
        !          2261:                break;
        !          2262: 
        !          2263:              default:
        !          2264:                log_fatal("Inconsistent universe tag size at %s:%d.", MDL);
        !          2265:        }
        !          2266: 
        !          2267:        option_code_hash_lookup(&option, universe->code_hash, &code, 0, MDL);
        !          2268: 
        !          2269:        /* If we created an option structure for each option a client
        !          2270:         * supplied, it's possible we may create > 2^32 option structures.
        !          2271:         * That's not feasible.  So by failing to enter these option
        !          2272:         * structures into the code and name hash tables, references will
        !          2273:         * never be more than 1 - when the option cache is destroyed, this
        !          2274:         * will be cleaned up.
        !          2275:         */
        !          2276:        if (!option) {
        !          2277:                char nbuf[sizeof("unknown-4294967295")];
        !          2278: 
        !          2279:                sprintf(nbuf, "unknown-%u", code);
        !          2280: 
        !          2281:                option = new_option(nbuf, MDL);
        !          2282: 
        !          2283:                if (!option)
        !          2284:                        return 0;
        !          2285: 
        !          2286:                option->format = default_option_format;
        !          2287:                option->universe = universe;
        !          2288:                option->code = code;
        !          2289: 
        !          2290:                /* new_option() doesn't set references, pretend. */
        !          2291:                option->refcnt = 1;
        !          2292:        }
        !          2293: 
        !          2294:        if (!option_cache_allocate (opp, MDL)) {
        !          2295:                log_error("No memory for option code %s.%s.",
        !          2296:                          universe->name, option->name);
        !          2297:                status = 0;
        !          2298:                goto cleanup;
        !          2299:        }
        !          2300: 
        !          2301:        /* Pointer rather than double pointer makes for less parens. */
        !          2302:        op = *opp;
        !          2303: 
        !          2304:        option_reference(&op->option, option, MDL);
        !          2305: 
        !          2306:        /* If we weren't passed a buffer in which the data are saved and
        !          2307:           refcounted, allocate one now. */
        !          2308:        if (!bp) {
        !          2309:                if (!buffer_allocate (&lbp, length + terminatep, MDL)) {
        !          2310:                        log_error ("no memory for option buffer.");
        !          2311: 
        !          2312:                        status = 0;
        !          2313:                        goto cleanup;
        !          2314:                }
        !          2315:                memcpy (lbp -> data, buffer, length + terminatep);
        !          2316:                bp = lbp;
        !          2317:                buffer = &bp -> data [0]; /* Refer to saved buffer. */
        !          2318:        }
        !          2319: 
        !          2320:        /* Reference buffer copy to option cache. */
        !          2321:        op -> data.buffer = (struct buffer *)0;
        !          2322:        buffer_reference (&op -> data.buffer, bp, MDL);
        !          2323: 
        !          2324:        /* Point option cache into buffer. */
        !          2325:        op -> data.data = buffer;
        !          2326:        op -> data.len = length;
        !          2327: 
        !          2328:        if (terminatep) {
        !          2329:                /* NUL terminate (we can get away with this because we (or
        !          2330:                   the caller!) allocated one more than the buffer size, and
        !          2331:                   because the byte following the end of an option is always
        !          2332:                   the code of the next option, which the caller is getting
        !          2333:                   out of the *original* buffer. */
        !          2334:                buffer [length] = 0;
        !          2335:                op -> data.terminated = 1;
        !          2336:        } else
        !          2337:                op -> data.terminated = 0;
        !          2338: 
        !          2339:        /* If this option is ultimately a text option, null determinate to
        !          2340:         * comply with RFC2132 section 2.  Mark a flag so this can be sensed
        !          2341:         * later to echo NULLs back to clients that supplied them (they
        !          2342:         * probably expect them).
        !          2343:         */
        !          2344:        if (format_has_text(option->format)) {
        !          2345:                int min_len = format_min_length(option->format, op);
        !          2346: 
        !          2347:                while ((op->data.len > min_len) &&
        !          2348:                       (op->data.data[op->data.len-1] == '\0')) {
        !          2349:                        op->data.len--;
        !          2350:                        op->flags |= OPTION_HAD_NULLS;
        !          2351:                }
        !          2352:        }
        !          2353: 
        !          2354:        /* And let go of our references. */
        !          2355:       cleanup:
        !          2356:        option_dereference(&option, MDL);
        !          2357: 
        !          2358:        return 1;
        !          2359: }
        !          2360: 
        !          2361: static void
        !          2362: count_options(struct option_cache *dummy_oc,
        !          2363:              struct packet *dummy_packet,
        !          2364:              struct lease *dummy_lease, 
        !          2365:              struct client_state *dummy_client_state,
        !          2366:              struct option_state *dummy_opt_state,
        !          2367:              struct option_state *opt_state,
        !          2368:              struct binding_scope **dummy_binding_scope,
        !          2369:              struct universe *dummy_universe, 
        !          2370:              void *void_accumulator) {
        !          2371:        int *accumulator = (int *)void_accumulator;
        !          2372: 
        !          2373:        *accumulator += 1;
        !          2374: }
        !          2375: 
        !          2376: static void
        !          2377: collect_oro(struct option_cache *oc,
        !          2378:            struct packet *dummy_packet,
        !          2379:            struct lease *dummy_lease, 
        !          2380:            struct client_state *dummy_client_state,
        !          2381:            struct option_state *dummy_opt_state,
        !          2382:            struct option_state *opt_state,
        !          2383:            struct binding_scope **dummy_binding_scope,
        !          2384:            struct universe *dummy_universe, 
        !          2385:            void *void_oro) {
        !          2386:        struct data_string *oro = (struct data_string *)void_oro;
        !          2387: 
        !          2388:        putUShort(oro->buffer->data + oro->len, oc->option->code);
        !          2389:        oro->len += 2;
        !          2390: }
        !          2391: 
        !          2392: /* build_server_oro() is presently unusued, but may be used at a future date
        !          2393:  * with support for Reconfigure messages (as a hint to the client about new
        !          2394:  * option value contents).
        !          2395:  */
        !          2396: void
        !          2397: build_server_oro(struct data_string *server_oro, 
        !          2398:                 struct option_state *options,
        !          2399:                 const char *file, int line) {
        !          2400:        int num_opts;
        !          2401:        int i;
        !          2402:        struct option *o;
        !          2403: 
        !          2404:        /*
        !          2405:         * Count the number of options, so we can allocate enough memory.
        !          2406:         * We want to mention sub-options too, so check all universes.
        !          2407:         */
        !          2408:        num_opts = 0;
        !          2409:        option_space_foreach(NULL, NULL, NULL, NULL, options,
        !          2410:                             NULL, &dhcpv6_universe, (void *)&num_opts,
        !          2411:                             count_options);
        !          2412:        for (i=0; i < options->universe_count; i++) {
        !          2413:                if (options->universes[i] != NULL) {
        !          2414:                        o = universes[i]->enc_opt;
        !          2415:                        while (o != NULL) {
        !          2416:                                if (o->universe == &dhcpv6_universe) {
        !          2417:                                        num_opts++;
        !          2418:                                        break;
        !          2419:                                }
        !          2420:                                o = o->universe->enc_opt;
        !          2421:                        }
        !          2422:                }
        !          2423:        }
        !          2424: 
        !          2425:        /*
        !          2426:         * Allocate space.
        !          2427:         */
        !          2428:        memset(server_oro, 0, sizeof(*server_oro));
        !          2429:        if (!buffer_allocate(&server_oro->buffer, num_opts * 2, MDL)) {
        !          2430:                log_fatal("no memory to build server ORO");
        !          2431:        }
        !          2432:        server_oro->data = server_oro->buffer->data;
        !          2433: 
        !          2434:        /*
        !          2435:         * Copy the data in.
        !          2436:         * We want to mention sub-options too, so check all universes.
        !          2437:         */
        !          2438:        server_oro->len = 0;    /* gets set in collect_oro */
        !          2439:        option_space_foreach(NULL, NULL, NULL, NULL, options,
        !          2440:                             NULL, &dhcpv6_universe, (void *)server_oro,
        !          2441:                             collect_oro);
        !          2442:        for (i=0; i < options->universe_count; i++) {
        !          2443:                if (options->universes[i] != NULL) {
        !          2444:                        o = universes[i]->enc_opt;
        !          2445:                        while (o != NULL) {
        !          2446:                                if (o->universe == &dhcpv6_universe) {
        !          2447:                                        unsigned char *tmp;
        !          2448:                                        tmp = server_oro->buffer->data;
        !          2449:                                        putUShort(tmp + server_oro->len,
        !          2450:                                                  o->code);
        !          2451:                                        server_oro->len += 2;
        !          2452:                                        break;
        !          2453:                                }
        !          2454:                                o = o->universe->enc_opt;
        !          2455:                        }
        !          2456:                }
        !          2457:        }
        !          2458: }
        !          2459: 
        !          2460: /* Wrapper function to put an option cache into an option state. */
        !          2461: void
        !          2462: save_option(struct universe *universe, struct option_state *options,
        !          2463:            struct option_cache *oc)
        !          2464: {
        !          2465:        if (universe->save_func)
        !          2466:                (*universe->save_func)(universe, options, oc, ISC_FALSE);
        !          2467:        else
        !          2468:                log_error("can't store options in %s space.", universe->name);
        !          2469: }
        !          2470: 
        !          2471: /* Wrapper function to append an option cache into an option state's list. */
        !          2472: void
        !          2473: also_save_option(struct universe *universe, struct option_state *options,
        !          2474:                 struct option_cache *oc)
        !          2475: {
        !          2476:        if (universe->save_func)
        !          2477:                (*universe->save_func)(universe, options, oc, ISC_TRUE);
        !          2478:        else
        !          2479:                log_error("can't store options in %s space.", universe->name);
        !          2480: }
        !          2481: 
        !          2482: void
        !          2483: save_hashed_option(struct universe *universe, struct option_state *options,
        !          2484:                   struct option_cache *oc, isc_boolean_t appendp)
        !          2485: {
        !          2486:        int hashix;
        !          2487:        pair bptr;
        !          2488:        pair *hash = options -> universes [universe -> index];
        !          2489:        struct option_cache **ocloc;
        !          2490: 
        !          2491:        if (oc -> refcnt == 0)
        !          2492:                abort ();
        !          2493: 
        !          2494:        /* Compute the hash. */
        !          2495:        hashix = compute_option_hash (oc -> option -> code);
        !          2496: 
        !          2497:        /* If there's no hash table, make one. */
        !          2498:        if (!hash) {
        !          2499:                hash = (pair *)dmalloc (OPTION_HASH_SIZE * sizeof *hash, MDL);
        !          2500:                if (!hash) {
        !          2501:                        log_error ("no memory to store %s.%s",
        !          2502:                                   universe -> name, oc -> option -> name);
        !          2503:                        return;
        !          2504:                }
        !          2505:                memset (hash, 0, OPTION_HASH_SIZE * sizeof *hash);
        !          2506:                options -> universes [universe -> index] = (void *)hash;
        !          2507:        } else {
        !          2508:                /* Try to find an existing option matching the new one. */
        !          2509:                for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
        !          2510:                        if (((struct option_cache *)
        !          2511:                             (bptr -> car)) -> option -> code ==
        !          2512:                            oc -> option -> code)
        !          2513:                                break;
        !          2514:                }
        !          2515: 
        !          2516:                /* Deal with collisions on the hash list. */
        !          2517:                if (bptr) {
        !          2518:                        ocloc = (struct option_cache **)&bptr->car;
        !          2519: 
        !          2520:                        /*
        !          2521:                         * If appendp is set, append it onto the tail of the
        !          2522:                         * ->next list.  If it is not set, rotate it into
        !          2523:                         * position at the head of the list.
        !          2524:                         */
        !          2525:                        if (appendp) {
        !          2526:                                do {
        !          2527:                                        ocloc = &(*ocloc)->next;
        !          2528:                                } while (*ocloc != NULL);
        !          2529:                        } else {
        !          2530:                                option_cache_dereference(ocloc, MDL);
        !          2531:                        }
        !          2532: 
        !          2533:                        option_cache_reference(ocloc, oc, MDL);
        !          2534:                        return;
        !          2535:                }
        !          2536:        }
        !          2537: 
        !          2538:        /* Otherwise, just put the new one at the head of the list. */
        !          2539:        bptr = new_pair (MDL);
        !          2540:        if (!bptr) {
        !          2541:                log_error ("No memory for option_cache reference.");
        !          2542:                return;
        !          2543:        }
        !          2544:        bptr -> cdr = hash [hashix];
        !          2545:        bptr -> car = 0;
        !          2546:        option_cache_reference ((struct option_cache **)&bptr -> car, oc, MDL);
        !          2547:        hash [hashix] = bptr;
        !          2548: }
        !          2549: 
        !          2550: void delete_option (universe, options, code)
        !          2551:        struct universe *universe;
        !          2552:        struct option_state *options;
        !          2553:        int code;
        !          2554: {
        !          2555:        if (universe -> delete_func)
        !          2556:                (*universe -> delete_func) (universe, options, code);
        !          2557:        else
        !          2558:                log_error ("can't delete options from %s space.",
        !          2559:                           universe -> name);
        !          2560: }
        !          2561: 
        !          2562: void delete_hashed_option (universe, options, code)
        !          2563:        struct universe *universe;
        !          2564:        struct option_state *options;
        !          2565:        int code;
        !          2566: {
        !          2567:        int hashix;
        !          2568:        pair bptr, prev = (pair)0;
        !          2569:        pair *hash = options -> universes [universe -> index];
        !          2570: 
        !          2571:        /* There may not be any options in this space. */
        !          2572:        if (!hash)
        !          2573:                return;
        !          2574: 
        !          2575:        /* Try to find an existing option matching the new one. */
        !          2576:        hashix = compute_option_hash (code);
        !          2577:        for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
        !          2578:                if (((struct option_cache *)(bptr -> car)) -> option -> code
        !          2579:                    == code)
        !          2580:                        break;
        !          2581:                prev = bptr;
        !          2582:        }
        !          2583:        /* If we found one, wipe it out... */
        !          2584:        if (bptr) {
        !          2585:                if (prev)
        !          2586:                        prev -> cdr = bptr -> cdr;
        !          2587:                else
        !          2588:                        hash [hashix] = bptr -> cdr;
        !          2589:                option_cache_dereference
        !          2590:                        ((struct option_cache **)(&bptr -> car), MDL);
        !          2591:                free_pair (bptr, MDL);
        !          2592:        }
        !          2593: }
        !          2594: 
        !          2595: extern struct option_cache *free_option_caches; /* XXX */
        !          2596: 
        !          2597: int option_cache_dereference (ptr, file, line)
        !          2598:        struct option_cache **ptr;
        !          2599:        const char *file;
        !          2600:        int line;
        !          2601: {
        !          2602:        if (!ptr || !*ptr) {
        !          2603:                log_error ("Null pointer in option_cache_dereference: %s(%d)",
        !          2604:                           file, line);
        !          2605: #if defined (POINTER_DEBUG)
        !          2606:                abort ();
        !          2607: #else
        !          2608:                return 0;
        !          2609: #endif
        !          2610:        }
        !          2611: 
        !          2612:        (*ptr) -> refcnt--;
        !          2613:        rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC);
        !          2614:        if (!(*ptr) -> refcnt) {
        !          2615:                if ((*ptr) -> data.buffer)
        !          2616:                        data_string_forget (&(*ptr) -> data, file, line);
        !          2617:                if ((*ptr)->option)
        !          2618:                        option_dereference(&(*ptr)->option, MDL);
        !          2619:                if ((*ptr) -> expression)
        !          2620:                        expression_dereference (&(*ptr) -> expression,
        !          2621:                                                file, line);
        !          2622:                if ((*ptr) -> next)
        !          2623:                        option_cache_dereference (&((*ptr) -> next),
        !          2624:                                                  file, line);
        !          2625:                /* Put it back on the free list... */
        !          2626:                (*ptr) -> expression = (struct expression *)free_option_caches;
        !          2627:                free_option_caches = *ptr;
        !          2628:                dmalloc_reuse (free_option_caches, (char *)0, 0, 0);
        !          2629:        }
        !          2630:        if ((*ptr) -> refcnt < 0) {
        !          2631:                log_error ("%s(%d): negative refcnt!", file, line);
        !          2632: #if defined (DEBUG_RC_HISTORY)
        !          2633:                dump_rc_history (*ptr);
        !          2634: #endif
        !          2635: #if defined (POINTER_DEBUG)
        !          2636:                abort ();
        !          2637: #else
        !          2638:                *ptr = (struct option_cache *)0;
        !          2639:                return 0;
        !          2640: #endif
        !          2641:        }
        !          2642:        *ptr = (struct option_cache *)0;
        !          2643:        return 1;
        !          2644: 
        !          2645: }
        !          2646: 
        !          2647: int hashed_option_state_dereference (universe, state, file, line)
        !          2648:        struct universe *universe;
        !          2649:        struct option_state *state;
        !          2650:        const char *file;
        !          2651:        int line;
        !          2652: {
        !          2653:        pair *heads;
        !          2654:        pair cp, next;
        !          2655:        int i;
        !          2656: 
        !          2657:        /* Get the pointer to the array of hash table bucket heads. */
        !          2658:        heads = (pair *)(state -> universes [universe -> index]);
        !          2659:        if (!heads)
        !          2660:                return 0;
        !          2661: 
        !          2662:        /* For each non-null head, loop through all the buckets dereferencing
        !          2663:           the attached option cache structures and freeing the buckets. */
        !          2664:        for (i = 0; i < OPTION_HASH_SIZE; i++) {
        !          2665:                for (cp = heads [i]; cp; cp = next) {
        !          2666:                        next = cp -> cdr;
        !          2667:                        option_cache_dereference
        !          2668:                                ((struct option_cache **)&cp -> car,
        !          2669:                                 file, line);
        !          2670:                        free_pair (cp, file, line);
        !          2671:                }
        !          2672:        }
        !          2673: 
        !          2674:        dfree (heads, file, line);
        !          2675:        state -> universes [universe -> index] = (void *)0;
        !          2676:        return 1;
        !          2677: }
        !          2678: 
        !          2679: /* The 'data_string' primitive doesn't have an appension mechanism.
        !          2680:  * This function must then append a new option onto an existing buffer
        !          2681:  * by first duplicating the original buffer and appending the desired
        !          2682:  * values, followed by coping the new value into place.
        !          2683:  */
        !          2684: int
        !          2685: append_option(struct data_string *dst, struct universe *universe,
        !          2686:              struct option *option, struct data_string *src)
        !          2687: {
        !          2688:        struct data_string tmp;
        !          2689: 
        !          2690:        if (src->len == 0 && option->format[0] != 'Z')
        !          2691:                return 0;
        !          2692: 
        !          2693:        memset(&tmp, 0, sizeof(tmp));
        !          2694: 
        !          2695:        /* Allocate a buffer to hold existing data, the current option's
        !          2696:         * tag and length, and the option's content.
        !          2697:         */
        !          2698:        if (!buffer_allocate(&tmp.buffer,
        !          2699:                             (dst->len + universe->length_size +
        !          2700:                              universe->tag_size + src->len), MDL)) {
        !          2701:                /* XXX: This kills all options presently stored in the
        !          2702:                 * destination buffer.  This is the way the original code
        !          2703:                 * worked, and assumes an 'all or nothing' approach to
        !          2704:                 * eg encapsulated option spaces.  It may or may not be
        !          2705:                 * desirable.
        !          2706:                 */
        !          2707:                data_string_forget(dst, MDL);
        !          2708:                return 0;
        !          2709:        }
        !          2710:        tmp.data = tmp.buffer->data;
        !          2711: 
        !          2712:        /* Copy the existing data off the destination. */
        !          2713:        if (dst->len != 0)
        !          2714:                memcpy(tmp.buffer->data, dst->data, dst->len);
        !          2715:        tmp.len = dst->len;
        !          2716: 
        !          2717:        /* Place the new option tag and length. */
        !          2718:        (*universe->store_tag)(tmp.buffer->data + tmp.len, option->code);
        !          2719:        tmp.len += universe->tag_size;
        !          2720:        (*universe->store_length)(tmp.buffer->data + tmp.len, src->len);
        !          2721:        tmp.len += universe->length_size;
        !          2722: 
        !          2723:        /* Copy the option contents onto the end. */
        !          2724:        memcpy(tmp.buffer->data + tmp.len, src->data, src->len);
        !          2725:        tmp.len += src->len;
        !          2726: 
        !          2727:        /* Play the shell game. */
        !          2728:        data_string_forget(dst, MDL);
        !          2729:        data_string_copy(dst, &tmp, MDL);
        !          2730:        data_string_forget(&tmp, MDL);
        !          2731:        return 1;
        !          2732: }
        !          2733: 
        !          2734: int
        !          2735: store_option(struct data_string *result, struct universe *universe,
        !          2736:             struct packet *packet, struct lease *lease,
        !          2737:             struct client_state *client_state,
        !          2738:             struct option_state *in_options, struct option_state *cfg_options,
        !          2739:             struct binding_scope **scope, struct option_cache *oc)
        !          2740: {
        !          2741:        struct data_string tmp;
        !          2742:        struct universe *subu=NULL;
        !          2743:        int status;
        !          2744:        char *start, *end;
        !          2745: 
        !          2746:        memset(&tmp, 0, sizeof(tmp));
        !          2747: 
        !          2748:        if (evaluate_option_cache(&tmp, packet, lease, client_state,
        !          2749:                                  in_options, cfg_options, scope, oc, MDL)) {
        !          2750:                /* If the option is an extended 'e'ncapsulation (not a
        !          2751:                 * direct 'E'ncapsulation), append the encapsulated space
        !          2752:                 * onto the currently prepared value.
        !          2753:                 */
        !          2754:                do {
        !          2755:                        if (oc->option->format &&
        !          2756:                            oc->option->format[0] == 'e') {
        !          2757:                                /* Skip forward to the universe name. */
        !          2758:                                start = strchr(oc->option->format, 'E');
        !          2759:                                if (start == NULL)
        !          2760:                                        break;
        !          2761: 
        !          2762:                                /* Locate the name-terminating '.'. */
        !          2763:                                end = strchr(++start, '.');
        !          2764: 
        !          2765:                                /* A zero-length name is not allowed in
        !          2766:                                 * these kinds of encapsulations.
        !          2767:                                 */
        !          2768:                                if (end == NULL || start == end)
        !          2769:                                        break;
        !          2770: 
        !          2771:                                universe_hash_lookup(&subu, universe_hash,
        !          2772:                                                     start, end - start, MDL);
        !          2773: 
        !          2774:                                if (subu == NULL) {
        !          2775:                                        log_error("store_option: option %d "
        !          2776:                                                  "refers to unknown "
        !          2777:                                                  "option space '%.*s'.",
        !          2778:                                                  oc->option->code,
        !          2779:                                                  (int)(end - start), start);
        !          2780:                                        break;
        !          2781:                                }
        !          2782: 
        !          2783:                                /* Append encapsulations, if any.  We
        !          2784:                                 * already have the prepended values, so
        !          2785:                                 * we send those even if there are no
        !          2786:                                 * encapsulated options (and ->encapsulate()
        !          2787:                                 * returns zero).
        !          2788:                                 */
        !          2789:                                subu->encapsulate(&tmp, packet, lease,
        !          2790:                                                  client_state, in_options,
        !          2791:                                                  cfg_options, scope, subu);
        !          2792:                                subu = NULL;
        !          2793:                        }
        !          2794:                } while (ISC_FALSE);
        !          2795: 
        !          2796:                status = append_option(result, universe, oc->option, &tmp);
        !          2797:                data_string_forget(&tmp, MDL);
        !          2798: 
        !          2799:                return status;
        !          2800:        }
        !          2801: 
        !          2802:        return 0;
        !          2803: }
        !          2804: 
        !          2805: int option_space_encapsulate (result, packet, lease, client_state,
        !          2806:                              in_options, cfg_options, scope, name)
        !          2807:        struct data_string *result;
        !          2808:        struct packet *packet;
        !          2809:        struct lease *lease;
        !          2810:        struct client_state *client_state;
        !          2811:        struct option_state *in_options;
        !          2812:        struct option_state *cfg_options;
        !          2813:        struct binding_scope **scope;
        !          2814:        struct data_string *name;
        !          2815: {
        !          2816:        struct universe *u = NULL;
        !          2817:        int status = 0;
        !          2818: 
        !          2819:        universe_hash_lookup(&u, universe_hash, 
        !          2820:                             (const char *)name->data, name->len, MDL);
        !          2821:        if (u == NULL) {
        !          2822:                log_error("option_space_encapsulate: option space '%.*s' does "
        !          2823:                          "not exist, but is configured.",
        !          2824:                          (int)name->len, name->data);
        !          2825:                return status;
        !          2826:        }
        !          2827: 
        !          2828:        if (u->encapsulate != NULL) {
        !          2829:                if (u->encapsulate(result, packet, lease, client_state,
        !          2830:                                   in_options, cfg_options, scope, u))
        !          2831:                        status = 1;
        !          2832:        } else
        !          2833:                log_error("encapsulation requested for '%s' with no support.",
        !          2834:                          name->data);
        !          2835: 
        !          2836:        return status;
        !          2837: }
        !          2838: 
        !          2839: /* Attempt to store any 'E'ncapsulated options that have not yet been
        !          2840:  * placed on the option buffer by the above (configuring a value in
        !          2841:  * the space over-rides any values in the child universe).
        !          2842:  *
        !          2843:  * Note that there are far fewer universes than there will ever be
        !          2844:  * options in any universe.  So it is faster to traverse the
        !          2845:  * configured universes, checking if each is encapsulated in the
        !          2846:  * current universe, and if so attempting to do so.
        !          2847:  *
        !          2848:  * For each configured universe for this configuration option space,
        !          2849:  * which is encapsulated within the current universe, can not be found
        !          2850:  * by the lookup function (the universe-specific encapsulation
        !          2851:  * functions would already have stored such a value), and encapsulates
        !          2852:  * at least one option, append it.
        !          2853:  */
        !          2854: static int
        !          2855: search_subencapsulation(struct data_string *result, struct packet *packet,
        !          2856:                        struct lease *lease, struct client_state *client_state,
        !          2857:                        struct option_state *in_options,
        !          2858:                        struct option_state *cfg_options,
        !          2859:                        struct binding_scope **scope,
        !          2860:                        struct universe *universe)
        !          2861: {
        !          2862:        struct data_string sub;
        !          2863:        struct universe *subu;
        !          2864:        int i, status = 0;
        !          2865: 
        !          2866:        memset(&sub, 0, sizeof(sub));
        !          2867:        for (i = 0 ; i < cfg_options->universe_count ; i++) {
        !          2868:                subu = universes[i];
        !          2869: 
        !          2870:                if (subu == NULL)
        !          2871:                        log_fatal("Impossible condition at %s:%d.", MDL);
        !          2872: 
        !          2873:                if (subu->enc_opt != NULL &&
        !          2874:                    subu->enc_opt->universe == universe &&
        !          2875:                    subu->enc_opt->format != NULL &&
        !          2876:                    subu->enc_opt->format[0] == 'E' &&
        !          2877:                    lookup_option(universe, cfg_options,
        !          2878:                                  subu->enc_opt->code) == NULL &&
        !          2879:                    subu->encapsulate(&sub, packet, lease, client_state,
        !          2880:                                      in_options, cfg_options,
        !          2881:                                      scope, subu)) {
        !          2882:                        if (append_option(result, universe,
        !          2883:                                          subu->enc_opt, &sub))
        !          2884:                                status = 1;
        !          2885: 
        !          2886:                        data_string_forget(&sub, MDL);
        !          2887:                }
        !          2888:        }
        !          2889: 
        !          2890:        return status;
        !          2891: }
        !          2892: 
        !          2893: int hashed_option_space_encapsulate (result, packet, lease, client_state,
        !          2894:                                     in_options, cfg_options, scope, universe)
        !          2895:        struct data_string *result;
        !          2896:        struct packet *packet;
        !          2897:        struct lease *lease;
        !          2898:        struct client_state *client_state;
        !          2899:        struct option_state *in_options;
        !          2900:        struct option_state *cfg_options;
        !          2901:        struct binding_scope **scope;
        !          2902:        struct universe *universe;
        !          2903: {
        !          2904:        pair p, *hash;
        !          2905:        int status;
        !          2906:        int i;
        !          2907: 
        !          2908:        if (universe -> index >= cfg_options -> universe_count)
        !          2909:                return 0;
        !          2910: 
        !          2911:        hash = cfg_options -> universes [universe -> index];
        !          2912:        if (!hash)
        !          2913:                return 0;
        !          2914: 
        !          2915:        /* For each hash bucket, and each configured option cache within
        !          2916:         * that bucket, append the option onto the buffer in encapsulated
        !          2917:         * format appropriate to the universe.
        !          2918:         */
        !          2919:        status = 0;
        !          2920:        for (i = 0; i < OPTION_HASH_SIZE; i++) {
        !          2921:                for (p = hash [i]; p; p = p -> cdr) {
        !          2922:                        if (store_option(result, universe, packet, lease,
        !          2923:                                         client_state, in_options, cfg_options,
        !          2924:                                         scope, (struct option_cache *)p->car))
        !          2925:                                status = 1;
        !          2926:                }
        !          2927:        }
        !          2928: 
        !          2929:        if (search_subencapsulation(result, packet, lease, client_state,
        !          2930:                                    in_options, cfg_options, scope, universe))
        !          2931:                status = 1;
        !          2932: 
        !          2933:        return status;
        !          2934: }
        !          2935: 
        !          2936: int nwip_option_space_encapsulate (result, packet, lease, client_state,
        !          2937:                                   in_options, cfg_options, scope, universe)
        !          2938:        struct data_string *result;
        !          2939:        struct packet *packet;
        !          2940:        struct lease *lease;
        !          2941:        struct client_state *client_state;
        !          2942:        struct option_state *in_options;
        !          2943:        struct option_state *cfg_options;
        !          2944:        struct binding_scope **scope;
        !          2945:        struct universe *universe;
        !          2946: {
        !          2947:        pair ocp;
        !          2948:        int status;
        !          2949:        static struct option_cache *no_nwip;
        !          2950:        struct data_string ds;
        !          2951:        struct option_chain_head *head;
        !          2952: 
        !          2953:        if (universe -> index >= cfg_options -> universe_count)
        !          2954:                return 0;
        !          2955:        head = ((struct option_chain_head *)
        !          2956:                cfg_options -> universes [nwip_universe.index]);
        !          2957:        if (!head)
        !          2958:                return 0;
        !          2959: 
        !          2960:        status = 0;
        !          2961:        for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
        !          2962:                if (store_option (result, universe, packet,
        !          2963:                                  lease, client_state, in_options,
        !          2964:                                  cfg_options, scope,
        !          2965:                                  (struct option_cache *)ocp -> car))
        !          2966:                        status = 1;
        !          2967:        }
        !          2968: 
        !          2969:        /* If there's no data, the nwip suboption is supposed to contain
        !          2970:           a suboption saying there's no data. */
        !          2971:        if (!status) {
        !          2972:                if (!no_nwip) {
        !          2973:                        unsigned one = 1;
        !          2974:                        static unsigned char nni [] = { 1, 0 };
        !          2975: 
        !          2976:                        memset (&ds, 0, sizeof ds);
        !          2977:                        ds.data = nni;
        !          2978:                        ds.len = 2;
        !          2979:                        if (option_cache_allocate (&no_nwip, MDL))
        !          2980:                                data_string_copy (&no_nwip -> data, &ds, MDL);
        !          2981:                        if (!option_code_hash_lookup(&no_nwip->option,
        !          2982:                                                     nwip_universe.code_hash,
        !          2983:                                                     &one, 0, MDL))
        !          2984:                                log_fatal("Nwip option hash does not contain "
        !          2985:                                          "1 (%s:%d).", MDL);
        !          2986:                }
        !          2987:                if (no_nwip) {
        !          2988:                        if (store_option (result, universe, packet, lease,
        !          2989:                                          client_state, in_options,
        !          2990:                                          cfg_options, scope, no_nwip))
        !          2991:                                status = 1;
        !          2992:                }
        !          2993:        } else {
        !          2994:                memset (&ds, 0, sizeof ds);
        !          2995: 
        !          2996:                /* If we have nwip options, the first one has to be the
        !          2997:                   nwip-exists-in-option-area option. */
        !          2998:                if (!buffer_allocate (&ds.buffer, result -> len + 2, MDL)) {
        !          2999:                        data_string_forget (result, MDL);
        !          3000:                        return 0;
        !          3001:                }
        !          3002:                ds.data = &ds.buffer -> data [0];
        !          3003:                ds.buffer -> data [0] = 2;
        !          3004:                ds.buffer -> data [1] = 0;
        !          3005:                memcpy (&ds.buffer -> data [2], result -> data, result -> len);
        !          3006:                data_string_forget (result, MDL);
        !          3007:                data_string_copy (result, &ds, MDL);
        !          3008:                data_string_forget (&ds, MDL);
        !          3009:        }
        !          3010: 
        !          3011:        return status;
        !          3012: }
        !          3013: 
        !          3014: /* We don't want to use ns_name_pton()...it doesn't tell us how many bytes
        !          3015:  * it has consumed, and it plays havoc with our escapes.
        !          3016:  *
        !          3017:  * So this function does DNS encoding, and returns either the number of
        !          3018:  * octects consumed (on success), or -1 on failure.
        !          3019:  */
        !          3020: static int
        !          3021: fqdn_encode(unsigned char *dst, int dstlen, const unsigned char *src,
        !          3022:            int srclen)
        !          3023: {
        !          3024:        unsigned char *out;
        !          3025:        int i, j, len, outlen=0;
        !          3026: 
        !          3027:        out = dst;
        !          3028:        for (i = 0, j = 0 ; i < srclen ; i = j) {
        !          3029:                while ((j < srclen) && (src[j] != '.') && (src[j] != '\0'))
        !          3030:                        j++;
        !          3031: 
        !          3032:                len = j - i;
        !          3033:                if ((outlen + 1 + len) > dstlen)
        !          3034:                        return -1;
        !          3035: 
        !          3036:                *out++ = len;
        !          3037:                outlen++;
        !          3038: 
        !          3039:                /* We only do one FQDN, ending in one root label. */
        !          3040:                if (len == 0)
        !          3041:                        return outlen;
        !          3042: 
        !          3043:                memcpy(out, src + i, len);
        !          3044:                out += len;
        !          3045:                outlen += len;
        !          3046: 
        !          3047:                /* Advance past the root label. */
        !          3048:                j++;
        !          3049:        }
        !          3050: 
        !          3051:        if ((outlen + 1) > dstlen)
        !          3052:                return -1;
        !          3053: 
        !          3054:        /* Place the root label. */
        !          3055:        *out++ = 0;
        !          3056:        outlen++;
        !          3057: 
        !          3058:        return outlen;
        !          3059: }
        !          3060: 
        !          3061: int fqdn_option_space_encapsulate (result, packet, lease, client_state,
        !          3062:                                   in_options, cfg_options, scope, universe)
        !          3063:        struct data_string *result;
        !          3064:        struct packet *packet;
        !          3065:        struct lease *lease;
        !          3066:        struct client_state *client_state;
        !          3067:        struct option_state *in_options;
        !          3068:        struct option_state *cfg_options;
        !          3069:        struct binding_scope **scope;
        !          3070:        struct universe *universe;
        !          3071: {
        !          3072:        pair ocp;
        !          3073:        struct data_string results [FQDN_SUBOPTION_COUNT + 1];
        !          3074:        int status = 1;
        !          3075:        int i;
        !          3076:        unsigned len;
        !          3077:        struct buffer *bp = (struct buffer *)0;
        !          3078:        struct option_chain_head *head;
        !          3079: 
        !          3080:        /* If there's no FQDN universe, don't encapsulate. */
        !          3081:        if (fqdn_universe.index >= cfg_options -> universe_count)
        !          3082:                return 0;
        !          3083:        head = ((struct option_chain_head *)
        !          3084:                cfg_options -> universes [fqdn_universe.index]);
        !          3085:        if (!head)
        !          3086:                return 0;
        !          3087: 
        !          3088:        /* Figure out the values of all the suboptions. */
        !          3089:        memset (results, 0, sizeof results);
        !          3090:        for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
        !          3091:                struct option_cache *oc = (struct option_cache *)(ocp -> car);
        !          3092:                if (oc -> option -> code > FQDN_SUBOPTION_COUNT)
        !          3093:                        continue;
        !          3094:                evaluate_option_cache (&results [oc -> option -> code],
        !          3095:                                       packet, lease, client_state, in_options,
        !          3096:                                       cfg_options, scope,  oc, MDL);
        !          3097:        }
        !          3098:        /* We add a byte for the flags field.
        !          3099:         * We add two bytes for the two RCODE fields.
        !          3100:         * We add a byte because we will prepend a label count.
        !          3101:         * We add a byte because the input len doesn't count null termination,
        !          3102:         * and we will add a root label.
        !          3103:         */
        !          3104:        len = 5 + results [FQDN_FQDN].len;
        !          3105:        /* Save the contents of the option in a buffer. */
        !          3106:        if (!buffer_allocate (&bp, len, MDL)) {
        !          3107:                log_error ("no memory for option buffer.");
        !          3108:                status = 0;
        !          3109:                goto exit;
        !          3110:        }
        !          3111:        buffer_reference (&result -> buffer, bp, MDL);
        !          3112:        result -> len = 3;
        !          3113:        result -> data = &bp -> data [0];
        !          3114: 
        !          3115:        memset (&bp -> data [0], 0, len);
        !          3116:        /* XXX: The server should set bit 4 (yes, 4, not 3) to 1 if it is
        !          3117:         * not going to perform any ddns updates.  The client should set the
        !          3118:         * bit if it doesn't want the server to perform any updates.
        !          3119:         * The problem is at this layer of abstraction we have no idea if
        !          3120:         * the caller is a client or server.
        !          3121:         *
        !          3122:         * See RFC4702, Section 3.1, 'The "N" bit'.
        !          3123:         *
        !          3124:         * if (?)
        !          3125:         *      bp->data[0] |= 8;
        !          3126:         */
        !          3127:        if (results [FQDN_NO_CLIENT_UPDATE].len &&
        !          3128:            results [FQDN_NO_CLIENT_UPDATE].data [0])
        !          3129:                bp -> data [0] |= 2;
        !          3130:        if (results [FQDN_SERVER_UPDATE].len &&
        !          3131:            results [FQDN_SERVER_UPDATE].data [0])
        !          3132:                bp -> data [0] |= 1;
        !          3133:        if (results [FQDN_RCODE1].len)
        !          3134:                bp -> data [1] = results [FQDN_RCODE1].data [0];
        !          3135:        if (results [FQDN_RCODE2].len)
        !          3136:                bp -> data [2] = results [FQDN_RCODE2].data [0];
        !          3137: 
        !          3138:        if (results [FQDN_ENCODED].len &&
        !          3139:            results [FQDN_ENCODED].data [0]) {
        !          3140:                bp->data[0] |= 4;
        !          3141:                if (results [FQDN_FQDN].len) {
        !          3142:                        i = fqdn_encode(&bp->data[3], len - 3,
        !          3143:                                        results[FQDN_FQDN].data,
        !          3144:                                        results[FQDN_FQDN].len);
        !          3145: 
        !          3146:                        if (i < 0) {
        !          3147:                                status = 0;
        !          3148:                                goto exit;
        !          3149:                        }
        !          3150: 
        !          3151:                        result->len += i;
        !          3152:                        result->terminated = 0;
        !          3153:                }
        !          3154:        } else {
        !          3155:                if (results [FQDN_FQDN].len) {
        !          3156:                        memcpy (&bp -> data [3], results [FQDN_FQDN].data,
        !          3157:                                results [FQDN_FQDN].len);
        !          3158:                        result -> len += results [FQDN_FQDN].len;
        !          3159:                        result -> terminated = 0;
        !          3160:                }
        !          3161:        }
        !          3162:       exit:
        !          3163:        for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) {
        !          3164:                if (results [i].len)
        !          3165:                        data_string_forget (&results [i], MDL);
        !          3166:        }
        !          3167:        buffer_dereference (&bp, MDL);
        !          3168:        if (!status)
        !          3169:                data_string_forget(result, MDL);
        !          3170:        return status;
        !          3171: }
        !          3172: 
        !          3173: /*
        !          3174:  * Trap invalid attempts to inspect FQND6 contents.
        !          3175:  */
        !          3176: struct option_cache *
        !          3177: lookup_fqdn6_option(struct universe *universe, struct option_state *options,
        !          3178:                    unsigned code)
        !          3179: {
        !          3180:        log_fatal("Impossible condition at %s:%d.", MDL);
        !          3181:        return NULL;
        !          3182: }
        !          3183: 
        !          3184: /*
        !          3185:  * Trap invalid attempts to save options directly to FQDN6 rather than FQDN.
        !          3186:  */
        !          3187: void
        !          3188: save_fqdn6_option(struct universe *universe, struct option_state *options,
        !          3189:                  struct option_cache *oc, isc_boolean_t appendp)
        !          3190: {
        !          3191:        log_fatal("Impossible condition at %s:%d.", MDL);
        !          3192: }
        !          3193: 
        !          3194: /*
        !          3195:  * Trap invalid attempts to delete an option out of the FQDN6 universe.
        !          3196:  */
        !          3197: void
        !          3198: delete_fqdn6_option(struct universe *universe, struct option_state *options,
        !          3199:                    int code)
        !          3200: {
        !          3201:        log_fatal("Impossible condition at %s:%d.", MDL);
        !          3202: }
        !          3203: 
        !          3204: /* Shill to the DHCPv4 fqdn option cache any attempts to traverse the
        !          3205:  * V6's option cache entry.
        !          3206:  *
        !          3207:  * This function is called speculatively by dhclient to setup
        !          3208:  * environment variables.  But it would have already called the
        !          3209:  * foreach on the normal fqdn universe, so this is superfluous.
        !          3210:  */
        !          3211: void
        !          3212: fqdn6_option_space_foreach(struct packet *packet, struct lease *lease,
        !          3213:                           struct client_state *client_state,
        !          3214:                           struct option_state *in_options,
        !          3215:                           struct option_state *cfg_options,
        !          3216:                           struct binding_scope **scope,
        !          3217:                           struct universe *u, void *stuff,
        !          3218:                           void (*func)(struct option_cache *,
        !          3219:                                        struct packet *,
        !          3220:                                        struct lease *,
        !          3221:                                        struct client_state *,
        !          3222:                                        struct option_state *,
        !          3223:                                        struct option_state *,
        !          3224:                                        struct binding_scope **,
        !          3225:                                        struct universe *, void *))
        !          3226: {
        !          3227:        /* Pretend it is empty. */
        !          3228:        return;
        !          3229: }
        !          3230: 
        !          3231: /* Turn the FQDN option space into a DHCPv6 FQDN option buffer.
        !          3232:  */
        !          3233: int
        !          3234: fqdn6_option_space_encapsulate(struct data_string *result,
        !          3235:                               struct packet *packet, struct lease *lease,
        !          3236:                               struct client_state *client_state,
        !          3237:                               struct option_state *in_options,
        !          3238:                               struct option_state *cfg_options,
        !          3239:                               struct binding_scope **scope,
        !          3240:                               struct universe *universe)
        !          3241: {
        !          3242:        pair ocp;
        !          3243:        struct option_chain_head *head;
        !          3244:        struct option_cache *oc;
        !          3245:        unsigned char *data;
        !          3246:        int i, len, rval = 0, count;
        !          3247:        struct data_string results[FQDN_SUBOPTION_COUNT + 1];
        !          3248: 
        !          3249:        if (fqdn_universe.index >= cfg_options->universe_count)
        !          3250:                return 0;
        !          3251:        head = ((struct option_chain_head *)
        !          3252:                cfg_options->universes[fqdn_universe.index]);
        !          3253:        if (head == NULL)
        !          3254:                return 0;
        !          3255: 
        !          3256:        memset(results, 0, sizeof(results));
        !          3257:        for (ocp = head->first ; ocp != NULL ; ocp = ocp->cdr) {
        !          3258:                oc = (struct option_cache *)(ocp->car);
        !          3259:                if (oc->option->code > FQDN_SUBOPTION_COUNT)
        !          3260:                        log_fatal("Impossible condition at %s:%d.", MDL);
        !          3261: 
        !          3262:                evaluate_option_cache(&results[oc->option->code], packet,
        !          3263:                                      lease, client_state, in_options,
        !          3264:                                      cfg_options, scope, oc, MDL);
        !          3265:        }
        !          3266: 
        !          3267:        /* We add a byte for the flags field at the start of the option.
        !          3268:         * We add a byte because we will prepend a label count.
        !          3269:         * We add a byte because the input length doesn't include a trailing
        !          3270:         * NULL, and we will add a root label.
        !          3271:         */
        !          3272:        len = results[FQDN_FQDN].len + 3;
        !          3273:        if (!buffer_allocate(&result->buffer, len, MDL)) {
        !          3274:                log_error("No memory for virtual option buffer.");
        !          3275:                goto exit;
        !          3276:        }
        !          3277:        data = result->buffer->data;
        !          3278:        result->data = data;
        !          3279: 
        !          3280:        /* The first byte is the flags field. */
        !          3281:        result->len = 1;
        !          3282:        data[0] = 0;
        !          3283:        /* XXX: The server should set bit 3 (yes, 3, not 4) to 1 if we
        !          3284:         * are not going to perform any DNS updates.  The problem is
        !          3285:         * that at this layer of abstraction, we do not know if the caller
        !          3286:         * is the client or the server.
        !          3287:         *
        !          3288:         * See RFC4704 Section 4.1, 'The "N" bit'.
        !          3289:         *
        !          3290:         * if (?)
        !          3291:         *      data[0] |= 4;
        !          3292:         */
        !          3293:        if (results[FQDN_NO_CLIENT_UPDATE].len &&
        !          3294:            results[FQDN_NO_CLIENT_UPDATE].data[0])
        !          3295:                data[0] |= 2;
        !          3296:        if (results[FQDN_SERVER_UPDATE].len &&
        !          3297:            results[FQDN_SERVER_UPDATE].data[0])
        !          3298:                data[0] |= 1;
        !          3299: 
        !          3300:        /* If there is no name, we're done. */
        !          3301:        if (results[FQDN_FQDN].len == 0) {
        !          3302:                rval = 1;
        !          3303:                goto exit;
        !          3304:        }
        !          3305: 
        !          3306:        /* Convert textual representation to DNS format. */
        !          3307:        count = fqdn_encode(data + 1, len - 1,
        !          3308:                            results[FQDN_FQDN].data, results[FQDN_FQDN].len);
        !          3309: 
        !          3310:        if (count < 0) {
        !          3311:                rval = 0;
        !          3312:                data_string_forget(result, MDL);
        !          3313:                goto exit;
        !          3314:        }
        !          3315: 
        !          3316:        result->len += count;
        !          3317:        result->terminated = 0;
        !          3318: 
        !          3319:        /* Success! */
        !          3320:        rval = 1;
        !          3321: 
        !          3322:       exit:
        !          3323:        for (i = 1 ; i <= FQDN_SUBOPTION_COUNT ; i++) {
        !          3324:                if (result[i].len)
        !          3325:                        data_string_forget(&results[i], MDL);
        !          3326:        }
        !          3327: 
        !          3328:        return rval;
        !          3329: }
        !          3330: 
        !          3331: /* Read the DHCPv6 FQDN option's contents into the FQDN virtual space.
        !          3332:  */
        !          3333: int
        !          3334: fqdn6_universe_decode(struct option_state *options,
        !          3335:                      const unsigned char *buffer, unsigned length,
        !          3336:                      struct universe *u)
        !          3337: {
        !          3338:        struct buffer *bp = NULL;
        !          3339:        unsigned char *first_dot;
        !          3340:        int len, hlen, dlen;
        !          3341: 
        !          3342:        /* The FQDN option has to be at least 1 byte long. */
        !          3343:        if (length < 1)
        !          3344:                return 0;
        !          3345: 
        !          3346:        /* Save the contents of the option in a buffer.  There are 3
        !          3347:         * one-byte values we record from the packet, so we go ahead
        !          3348:         * and allocate a bigger buffer to accommodate them.  But the
        !          3349:         * 'length' we got (because it is a DNS encoded string) is
        !          3350:         * one longer than we need...so we only add two extra octets.
        !          3351:         */
        !          3352:        if (!buffer_allocate(&bp, length + 2, MDL)) {
        !          3353:                log_error("No memory for dhcp6.fqdn option buffer.");
        !          3354:                return 0;
        !          3355:        }
        !          3356: 
        !          3357:        /* The v6 FQDN is always 'encoded' per DNS. */
        !          3358:        bp->data[0] = 1;
        !          3359:        if (!save_option_buffer(&fqdn_universe, options, bp,
        !          3360:                                bp->data, 1, FQDN_ENCODED, 0))
        !          3361:                goto error;
        !          3362: 
        !          3363:        /* XXX: We need to process 'The "N" bit'. */
        !          3364: 
        !          3365:        if (buffer[0] & 1) /* server-update. */
        !          3366:                bp->data[2] = 1;
        !          3367:        else
        !          3368:                bp->data[2] = 0;
        !          3369: 
        !          3370:        if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 2, 1,
        !          3371:                                FQDN_SERVER_UPDATE, 0))
        !          3372:                goto error;
        !          3373: 
        !          3374:        if (buffer[0] & 2) /* no-client-update. */
        !          3375:                bp->data[1] = 1;
        !          3376:        else
        !          3377:                bp->data[1] = 0;
        !          3378: 
        !          3379:        if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 1, 1,
        !          3380:                                FQDN_NO_CLIENT_UPDATE, 0))
        !          3381:                goto error;
        !          3382: 
        !          3383:        /* Convert the domain name to textual representation for config. */
        !          3384:        len = MRns_name_ntop(buffer + 1, (char *)bp->data + 3, length - 1);
        !          3385:        if (len == -1) {
        !          3386:                log_error("Unable to convert dhcp6.fqdn domain name to "
        !          3387:                          "printable form.");
        !          3388:                goto error;
        !          3389:        }
        !          3390: 
        !          3391:        /* Save the domain name. */
        !          3392:        if (len > 0) {
        !          3393:                unsigned char *fqdn_start = bp->data + 3;
        !          3394: 
        !          3395:                if (!save_option_buffer(&fqdn_universe, options, bp,
        !          3396:                                        fqdn_start, len, FQDN_FQDN, 1))
        !          3397:                        goto error;
        !          3398: 
        !          3399:                first_dot = (unsigned char *)strchr((char *)fqdn_start, '.');
        !          3400: 
        !          3401:                if (first_dot != NULL) {
        !          3402:                        hlen = first_dot - fqdn_start;
        !          3403:                        dlen = len - hlen;
        !          3404:                } else {
        !          3405:                        hlen = len;
        !          3406:                        dlen = 0;
        !          3407:                }
        !          3408: 
        !          3409:                if (!save_option_buffer(&fqdn_universe, options, bp,
        !          3410:                                        fqdn_start, len, FQDN_FQDN, 1) ||
        !          3411:                    ((hlen > 0) &&
        !          3412:                     !save_option_buffer(&fqdn_universe, options, bp,
        !          3413:                                         fqdn_start, hlen,
        !          3414:                                         FQDN_HOSTNAME, 0)) ||
        !          3415:                    ((dlen > 0) &&
        !          3416:                     !save_option_buffer(&fqdn_universe, options, bp,
        !          3417:                                         first_dot, dlen, FQDN_DOMAINNAME, 0)))
        !          3418:                                goto error;
        !          3419:        }
        !          3420: 
        !          3421:        buffer_dereference(&bp, MDL);
        !          3422:        return 1;
        !          3423: 
        !          3424:       error:
        !          3425:        buffer_dereference(&bp, MDL);
        !          3426:        return 0;
        !          3427: }
        !          3428: 
        !          3429: void option_space_foreach (struct packet *packet, struct lease *lease,
        !          3430:                           struct client_state *client_state,
        !          3431:                           struct option_state *in_options,
        !          3432:                           struct option_state *cfg_options,
        !          3433:                           struct binding_scope **scope,
        !          3434:                           struct universe *u, void *stuff,
        !          3435:                           void (*func) (struct option_cache *,
        !          3436:                                         struct packet *,
        !          3437:                                         struct lease *, struct client_state *,
        !          3438:                                         struct option_state *,
        !          3439:                                         struct option_state *,
        !          3440:                                         struct binding_scope **,
        !          3441:                                         struct universe *, void *))
        !          3442: {
        !          3443:        if (u -> foreach)
        !          3444:                (*u -> foreach) (packet, lease, client_state, in_options,
        !          3445:                                 cfg_options, scope, u, stuff, func);
        !          3446: }
        !          3447: 
        !          3448: void suboption_foreach (struct packet *packet, struct lease *lease,
        !          3449:                        struct client_state *client_state,
        !          3450:                        struct option_state *in_options,
        !          3451:                        struct option_state *cfg_options,
        !          3452:                        struct binding_scope **scope,
        !          3453:                        struct universe *u, void *stuff,
        !          3454:                        void (*func) (struct option_cache *,
        !          3455:                                      struct packet *,
        !          3456:                                      struct lease *, struct client_state *,
        !          3457:                                      struct option_state *,
        !          3458:                                      struct option_state *,
        !          3459:                                      struct binding_scope **,
        !          3460:                                      struct universe *, void *),
        !          3461:                        struct option_cache *oc,
        !          3462:                        const char *vsname)
        !          3463: {
        !          3464:        struct universe *universe = find_option_universe (oc -> option,
        !          3465:                                                          vsname);
        !          3466:        if (universe -> foreach)
        !          3467:                (*universe -> foreach) (packet, lease, client_state,
        !          3468:                                        in_options, cfg_options,
        !          3469:                                        scope, universe, stuff, func);
        !          3470: }
        !          3471: 
        !          3472: void hashed_option_space_foreach (struct packet *packet, struct lease *lease,
        !          3473:                                  struct client_state *client_state,
        !          3474:                                  struct option_state *in_options,
        !          3475:                                  struct option_state *cfg_options,
        !          3476:                                  struct binding_scope **scope,
        !          3477:                                  struct universe *u, void *stuff,
        !          3478:                                  void (*func) (struct option_cache *,
        !          3479:                                                struct packet *,
        !          3480:                                                struct lease *,
        !          3481:                                                struct client_state *,
        !          3482:                                                struct option_state *,
        !          3483:                                                struct option_state *,
        !          3484:                                                struct binding_scope **,
        !          3485:                                                struct universe *, void *))
        !          3486: {
        !          3487:        pair *hash;
        !          3488:        int i;
        !          3489:        struct option_cache *oc;
        !          3490: 
        !          3491:        if (cfg_options -> universe_count <= u -> index)
        !          3492:                return;
        !          3493: 
        !          3494:        hash = cfg_options -> universes [u -> index];
        !          3495:        if (!hash)
        !          3496:                return;
        !          3497:        for (i = 0; i < OPTION_HASH_SIZE; i++) {
        !          3498:                pair p;
        !          3499:                /* XXX save _all_ options! XXX */
        !          3500:                for (p = hash [i]; p; p = p -> cdr) {
        !          3501:                        oc = (struct option_cache *)p -> car;
        !          3502:                        (*func) (oc, packet, lease, client_state,
        !          3503:                                 in_options, cfg_options, scope, u, stuff);
        !          3504:                }
        !          3505:        }
        !          3506: }
        !          3507: 
        !          3508: void
        !          3509: save_linked_option(struct universe *universe, struct option_state *options,
        !          3510:                   struct option_cache *oc, isc_boolean_t appendp)
        !          3511: {
        !          3512:        pair *tail;
        !          3513:        struct option_chain_head *head;
        !          3514:        struct option_cache **ocloc;
        !          3515: 
        !          3516:        if (universe -> index >= options -> universe_count)
        !          3517:                return;
        !          3518:        head = ((struct option_chain_head *)
        !          3519:                options -> universes [universe -> index]);
        !          3520:        if (!head) {
        !          3521:                if (!option_chain_head_allocate (((struct option_chain_head **)
        !          3522:                                                  &options -> universes
        !          3523:                                                  [universe -> index]), MDL))
        !          3524:                        return;
        !          3525:                head = ((struct option_chain_head *)
        !          3526:                        options -> universes [universe -> index]);
        !          3527:        }
        !          3528: 
        !          3529:        /* Find the tail of the list. */
        !          3530:        for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
        !          3531:                ocloc = (struct option_cache **)&(*tail)->car;
        !          3532: 
        !          3533:                if (oc->option->code == (*ocloc)->option->code) {
        !          3534:                        if (appendp) {
        !          3535:                                do {
        !          3536:                                        ocloc = &(*ocloc)->next;
        !          3537:                                } while (*ocloc != NULL);
        !          3538:                        } else {
        !          3539:                                option_cache_dereference(ocloc, MDL);
        !          3540:                        }
        !          3541:                        option_cache_reference(ocloc, oc, MDL);
        !          3542:                        return;
        !          3543:                }
        !          3544:        }
        !          3545: 
        !          3546:        *tail = cons (0, 0);
        !          3547:        if (*tail) {
        !          3548:                option_cache_reference ((struct option_cache **)
        !          3549:                                        (&(*tail) -> car), oc, MDL);
        !          3550:        }
        !          3551: }
        !          3552: 
        !          3553: int linked_option_space_encapsulate (result, packet, lease, client_state,
        !          3554:                                    in_options, cfg_options, scope, universe)
        !          3555:        struct data_string *result;
        !          3556:        struct packet *packet;
        !          3557:        struct lease *lease;
        !          3558:        struct client_state *client_state;
        !          3559:        struct option_state *in_options;
        !          3560:        struct option_state *cfg_options;
        !          3561:        struct binding_scope **scope;
        !          3562:        struct universe *universe;
        !          3563: {
        !          3564:        int status = 0;
        !          3565:        pair oc;
        !          3566:        struct option_chain_head *head;
        !          3567: 
        !          3568:        if (universe -> index >= cfg_options -> universe_count)
        !          3569:                return status;
        !          3570:        head = ((struct option_chain_head *)
        !          3571:                cfg_options -> universes [universe -> index]);
        !          3572:        if (!head)
        !          3573:                return status;
        !          3574: 
        !          3575:        for (oc = head -> first; oc; oc = oc -> cdr) {
        !          3576:                if (store_option (result, universe, packet,
        !          3577:                                  lease, client_state, in_options, cfg_options,
        !          3578:                                  scope, (struct option_cache *)(oc -> car)))
        !          3579:                        status = 1;
        !          3580:        }
        !          3581: 
        !          3582:        if (search_subencapsulation(result, packet, lease, client_state,
        !          3583:                                    in_options, cfg_options, scope, universe))
        !          3584:                status = 1;
        !          3585: 
        !          3586:        return status;
        !          3587: }
        !          3588: 
        !          3589: void delete_linked_option (universe, options, code)
        !          3590:        struct universe *universe;
        !          3591:        struct option_state *options;
        !          3592:        int code;
        !          3593: {
        !          3594:        pair *tail, tmp = (pair)0;
        !          3595:        struct option_chain_head *head;
        !          3596: 
        !          3597:        if (universe -> index >= options -> universe_count)
        !          3598:                return;
        !          3599:        head = ((struct option_chain_head *)
        !          3600:                options -> universes [universe -> index]);
        !          3601:        if (!head)
        !          3602:                return;
        !          3603: 
        !          3604:        for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
        !          3605:                if (code ==
        !          3606:                    ((struct option_cache *)(*tail) -> car) -> option -> code)
        !          3607:                {
        !          3608:                        tmp = (*tail) -> cdr;
        !          3609:                        option_cache_dereference ((struct option_cache **)
        !          3610:                                                  (&(*tail) -> car), MDL);
        !          3611:                        dfree (*tail, MDL);
        !          3612:                        (*tail) = tmp;
        !          3613:                        break;
        !          3614:                }
        !          3615:        }
        !          3616: }
        !          3617: 
        !          3618: struct option_cache *lookup_linked_option (universe, options, code)
        !          3619:        struct universe *universe;
        !          3620:        struct option_state *options;
        !          3621:        unsigned code;
        !          3622: {
        !          3623:        pair oc;
        !          3624:        struct option_chain_head *head;
        !          3625: 
        !          3626:        if (universe -> index >= options -> universe_count)
        !          3627:                return 0;
        !          3628:        head = ((struct option_chain_head *)
        !          3629:                options -> universes [universe -> index]);
        !          3630:        if (!head)
        !          3631:                return 0;
        !          3632: 
        !          3633:        for (oc = head -> first; oc; oc = oc -> cdr) {
        !          3634:                if (code ==
        !          3635:                    ((struct option_cache *)(oc -> car)) -> option -> code) {
        !          3636:                        return (struct option_cache *)(oc -> car);
        !          3637:                }
        !          3638:        }
        !          3639: 
        !          3640:        return (struct option_cache *)0;
        !          3641: }
        !          3642: 
        !          3643: int linked_option_state_dereference (universe, state, file, line)
        !          3644:        struct universe *universe;
        !          3645:        struct option_state *state;
        !          3646:        const char *file;
        !          3647:        int line;
        !          3648: {
        !          3649:        return (option_chain_head_dereference
        !          3650:                ((struct option_chain_head **)
        !          3651:                 (&state -> universes [universe -> index]), MDL));
        !          3652: }
        !          3653: 
        !          3654: void linked_option_space_foreach (struct packet *packet, struct lease *lease,
        !          3655:                                  struct client_state *client_state,
        !          3656:                                  struct option_state *in_options,
        !          3657:                                  struct option_state *cfg_options,
        !          3658:                                  struct binding_scope **scope,
        !          3659:                                  struct universe *u, void *stuff,
        !          3660:                                  void (*func) (struct option_cache *,
        !          3661:                                                struct packet *,
        !          3662:                                                struct lease *,
        !          3663:                                                struct client_state *,
        !          3664:                                                struct option_state *,
        !          3665:                                                struct option_state *,
        !          3666:                                                struct binding_scope **,
        !          3667:                                                struct universe *, void *))
        !          3668: {
        !          3669:        pair car;
        !          3670:        struct option_chain_head *head;
        !          3671: 
        !          3672:        if (u -> index >= cfg_options -> universe_count)
        !          3673:                return;
        !          3674:        head = ((struct option_chain_head *)
        !          3675:                cfg_options -> universes [u -> index]);
        !          3676:        if (!head)
        !          3677:                return;
        !          3678:        for (car = head -> first; car; car = car -> cdr) {
        !          3679:                (*func) ((struct option_cache *)(car -> car),
        !          3680:                         packet, lease, client_state,
        !          3681:                         in_options, cfg_options, scope, u, stuff);
        !          3682:        }
        !          3683: }
        !          3684: 
        !          3685: void do_packet (interface, packet, len, from_port, from, hfrom)
        !          3686:        struct interface_info *interface;
        !          3687:        struct dhcp_packet *packet;
        !          3688:        unsigned len;
        !          3689:        unsigned int from_port;
        !          3690:        struct iaddr from;
        !          3691:        struct hardware *hfrom;
        !          3692: {
        !          3693:        struct option_cache *op;
        !          3694:        struct packet *decoded_packet;
        !          3695: #if defined (DEBUG_MEMORY_LEAKAGE)
        !          3696:        unsigned long previous_outstanding = dmalloc_outstanding;
        !          3697: #endif
        !          3698: 
        !          3699: #if defined (TRACING)
        !          3700:        trace_inpacket_stash (interface, packet, len, from_port, from, hfrom);
        !          3701: #endif
        !          3702: 
        !          3703:        decoded_packet = (struct packet *)0;
        !          3704:        if (!packet_allocate (&decoded_packet, MDL)) {
        !          3705:                log_error ("do_packet: no memory for incoming packet!");
        !          3706:                return;
        !          3707:        }
        !          3708:        decoded_packet -> raw = packet;
        !          3709:        decoded_packet -> packet_length = len;
        !          3710:        decoded_packet -> client_port = from_port;
        !          3711:        decoded_packet -> client_addr = from;
        !          3712:        interface_reference (&decoded_packet -> interface, interface, MDL);
        !          3713:        decoded_packet -> haddr = hfrom;
        !          3714: 
        !          3715:        if (packet -> hlen > sizeof packet -> chaddr) {
        !          3716:                packet_dereference (&decoded_packet, MDL);
        !          3717:                log_info ("Discarding packet with bogus hlen.");
        !          3718:                return;
        !          3719:        }
        !          3720: 
        !          3721:        /* If there's an option buffer, try to parse it. */
        !          3722:        if (decoded_packet -> packet_length >= DHCP_FIXED_NON_UDP + 4) {
        !          3723:                if (!parse_options (decoded_packet)) {
        !          3724:                        if (decoded_packet -> options)
        !          3725:                                option_state_dereference
        !          3726:                                        (&decoded_packet -> options, MDL);
        !          3727:                        packet_dereference (&decoded_packet, MDL);
        !          3728:                        return;
        !          3729:                }
        !          3730: 
        !          3731:                if (decoded_packet -> options_valid &&
        !          3732:                    (op = lookup_option (&dhcp_universe,
        !          3733:                                         decoded_packet -> options, 
        !          3734:                                         DHO_DHCP_MESSAGE_TYPE))) {
        !          3735:                        struct data_string dp;
        !          3736:                        memset (&dp, 0, sizeof dp);
        !          3737:                        evaluate_option_cache (&dp, decoded_packet,
        !          3738:                                               (struct lease *)0,
        !          3739:                                               (struct client_state *)0,
        !          3740:                                               decoded_packet -> options,
        !          3741:                                               (struct option_state *)0,
        !          3742:                                               (struct binding_scope **)0,
        !          3743:                                               op, MDL);
        !          3744:                        if (dp.len > 0)
        !          3745:                                decoded_packet -> packet_type = dp.data [0];
        !          3746:                        else
        !          3747:                                decoded_packet -> packet_type = 0;
        !          3748:                        data_string_forget (&dp, MDL);
        !          3749:                }
        !          3750:        }
        !          3751:                
        !          3752:        if (decoded_packet -> packet_type)
        !          3753:                dhcp (decoded_packet);
        !          3754:        else
        !          3755:                bootp (decoded_packet);
        !          3756: 
        !          3757:        /* If the caller kept the packet, they'll have upped the refcnt. */
        !          3758:        packet_dereference (&decoded_packet, MDL);
        !          3759: 
        !          3760: #if defined (DEBUG_MEMORY_LEAKAGE)
        !          3761:        log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term",
        !          3762:                  dmalloc_generation,
        !          3763:                  dmalloc_outstanding - previous_outstanding,
        !          3764:                  dmalloc_outstanding, dmalloc_longterm);
        !          3765: #endif
        !          3766: #if defined (DEBUG_MEMORY_LEAKAGE)
        !          3767:        dmalloc_dump_outstanding ();
        !          3768: #endif
        !          3769: #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
        !          3770:        dump_rc_history (0);
        !          3771: #endif
        !          3772: }
        !          3773: 
        !          3774: int
        !          3775: packet6_len_okay(const char *packet, int len) {
        !          3776:        if (len < 1) {
        !          3777:                return 0;
        !          3778:        }
        !          3779:        if ((packet[0] == DHCPV6_RELAY_FORW) || 
        !          3780:            (packet[0] == DHCPV6_RELAY_REPL)) {
        !          3781:                if (len >= offsetof(struct dhcpv6_relay_packet, options)) {
        !          3782:                        return 1;
        !          3783:                } else {
        !          3784:                        return 0;
        !          3785:                }
        !          3786:        } else {
        !          3787:                if (len >= offsetof(struct dhcpv6_packet, options)) {
        !          3788:                        return 1;
        !          3789:                } else {
        !          3790:                        return 0;
        !          3791:                }
        !          3792:        }
        !          3793: }
        !          3794: 
        !          3795: #ifdef DHCPv6
        !          3796: void 
        !          3797: do_packet6(struct interface_info *interface, const char *packet, 
        !          3798:           int len, int from_port, const struct iaddr *from, 
        !          3799:           isc_boolean_t was_unicast) {
        !          3800:        unsigned char msg_type;
        !          3801:        const struct dhcpv6_packet *msg;
        !          3802:        const struct dhcpv6_relay_packet *relay; 
        !          3803:        struct packet *decoded_packet;
        !          3804: 
        !          3805:        if (!packet6_len_okay(packet, len)) {
        !          3806:                log_info("do_packet6: "
        !          3807:                         "short packet from %s port %d, len %d, dropped",
        !          3808:                         piaddr(*from), from_port, len);
        !          3809:                return;
        !          3810:        }
        !          3811: 
        !          3812:        decoded_packet = NULL;
        !          3813:        if (!packet_allocate(&decoded_packet, MDL)) {
        !          3814:                log_error("do_packet6: no memory for incoming packet.");
        !          3815:                return;
        !          3816:        }
        !          3817: 
        !          3818:        if (!option_state_allocate(&decoded_packet->options, MDL)) {
        !          3819:                log_error("do_packet6: no memory for options.");
        !          3820:                packet_dereference(&decoded_packet, MDL);
        !          3821:                return;
        !          3822:        }
        !          3823: 
        !          3824:        /* IPv4 information, already set to 0 */
        !          3825:        /* decoded_packet->packet_type = 0; */
        !          3826:        /* memset(&decoded_packet->haddr, 0, sizeof(decoded_packet->haddr)); */
        !          3827:        /* decoded_packet->circuit_id = NULL; */
        !          3828:        /* decoded_packet->circuit_id_len = 0; */
        !          3829:        /* decoded_packet->remote_id = NULL; */
        !          3830:        /* decoded_packet->remote_id_len = 0; */
        !          3831:        decoded_packet->raw = (struct dhcp_packet *) packet;
        !          3832:        decoded_packet->packet_length = (unsigned) len;
        !          3833:        decoded_packet->client_port = from_port;
        !          3834:        decoded_packet->client_addr = *from;
        !          3835:        interface_reference(&decoded_packet->interface, interface, MDL);
        !          3836: 
        !          3837:        decoded_packet->unicast = was_unicast;
        !          3838: 
        !          3839:        msg_type = packet[0];
        !          3840:        if ((msg_type == DHCPV6_RELAY_FORW) || 
        !          3841:            (msg_type == DHCPV6_RELAY_REPL)) {
        !          3842:                relay = (const struct dhcpv6_relay_packet *)packet;
        !          3843:                decoded_packet->dhcpv6_msg_type = relay->msg_type;
        !          3844: 
        !          3845:                /* relay-specific data */
        !          3846:                decoded_packet->dhcpv6_hop_count = relay->hop_count;
        !          3847:                memcpy(&decoded_packet->dhcpv6_link_address,
        !          3848:                       relay->link_address, sizeof(relay->link_address));
        !          3849:                memcpy(&decoded_packet->dhcpv6_peer_address,
        !          3850:                       relay->peer_address, sizeof(relay->peer_address));
        !          3851: 
        !          3852:                if (!parse_option_buffer(decoded_packet->options, 
        !          3853:                                         relay->options, len-sizeof(*relay), 
        !          3854:                                         &dhcpv6_universe)) {
        !          3855:                        /* no logging here, as parse_option_buffer() logs all
        !          3856:                           cases where it fails */
        !          3857:                        packet_dereference(&decoded_packet, MDL);
        !          3858:                        return;
        !          3859:                }
        !          3860:        } else {
        !          3861:                msg = (const struct dhcpv6_packet *)packet;
        !          3862:                decoded_packet->dhcpv6_msg_type = msg->msg_type;
        !          3863: 
        !          3864:                /* message-specific data */
        !          3865:                memcpy(decoded_packet->dhcpv6_transaction_id, 
        !          3866:                       msg->transaction_id, 
        !          3867:                       sizeof(decoded_packet->dhcpv6_transaction_id));
        !          3868: 
        !          3869:                if (!parse_option_buffer(decoded_packet->options, 
        !          3870:                                         msg->options, len-sizeof(*msg), 
        !          3871:                                         &dhcpv6_universe)) {
        !          3872:                        /* no logging here, as parse_option_buffer() logs all
        !          3873:                           cases where it fails */
        !          3874:                        packet_dereference(&decoded_packet, MDL);
        !          3875:                        return;
        !          3876:                }
        !          3877:        }
        !          3878: 
        !          3879:        dhcpv6(decoded_packet);
        !          3880: 
        !          3881:        packet_dereference(&decoded_packet, MDL);
        !          3882: }
        !          3883: #endif /* DHCPv6 */
        !          3884: 
        !          3885: int
        !          3886: pretty_escape(char **dst, char *dend, const unsigned char **src,
        !          3887:              const unsigned char *send)
        !          3888: {
        !          3889:        int count = 0;
        !          3890: 
        !          3891:        /* If there aren't as many bytes left as there are in the source
        !          3892:         * buffer, don't even bother entering the loop.
        !          3893:         */
        !          3894:        if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
        !          3895:            *dst == NULL || *src == NULL || (*dst >= dend) || (*src > send) ||
        !          3896:            ((send - *src) > (dend - *dst)))
        !          3897:                return -1;
        !          3898: 
        !          3899:        for ( ; *src < send ; (*src)++) {
        !          3900:                if (!isascii (**src) || !isprint (**src)) {
        !          3901:                        /* Skip trailing NUL. */
        !          3902:                        if ((*src + 1) != send || **src != '\0') {
        !          3903:                                if (*dst + 4 > dend)
        !          3904:                                        return -1;
        !          3905: 
        !          3906:                                sprintf(*dst, "\\%03o",
        !          3907:                                        **src);
        !          3908:                                (*dst) += 4;
        !          3909:                                count += 4;
        !          3910:                        }
        !          3911:                } else if (**src == '"' || **src == '\'' || **src == '$' ||
        !          3912:                           **src == '`' || **src == '\\' || **src == '|' ||
        !          3913:                           **src == '&') {
        !          3914:                        if (*dst + 2 > dend)
        !          3915:                                return -1;
        !          3916: 
        !          3917:                        **dst = '\\';
        !          3918:                        (*dst)++;
        !          3919:                        **dst = **src;
        !          3920:                        (*dst)++;
        !          3921:                        count += 2;
        !          3922:                } else {
        !          3923:                        if (*dst + 1 > dend)
        !          3924:                                return -1;
        !          3925: 
        !          3926:                        **dst = **src;
        !          3927:                        (*dst)++;
        !          3928:                        count++;
        !          3929:                }
        !          3930:        }
        !          3931: 
        !          3932:        return count;
        !          3933: }
        !          3934: 
        !          3935: static int
        !          3936: pretty_text(char **dst, char *dend, const unsigned char **src,
        !          3937:            const unsigned char *send, int emit_quotes)
        !          3938: {
        !          3939:        int count;
        !          3940: 
        !          3941:        if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
        !          3942:            *dst == NULL || *src == NULL ||
        !          3943:            ((*dst + (emit_quotes ? 2 : 0)) > dend) || (*src > send))
        !          3944:                return -1;
        !          3945: 
        !          3946:        if (emit_quotes) {
        !          3947:                **dst = '"';
        !          3948:                (*dst)++;
        !          3949:        }
        !          3950: 
        !          3951:        /* dend-1 leaves 1 byte for the closing quote. */
        !          3952:        count = pretty_escape(dst, dend - (emit_quotes ? 1 : 0), src, send);
        !          3953: 
        !          3954:        if (count == -1)
        !          3955:                return -1;
        !          3956: 
        !          3957:        if (emit_quotes && (*dst < dend)) {
        !          3958:                **dst = '"';
        !          3959:                (*dst)++;
        !          3960: 
        !          3961:                /* Includes quote prior to pretty_escape(); */
        !          3962:                count += 2;
        !          3963:        }
        !          3964: 
        !          3965:        return count;
        !          3966: }
        !          3967: 
        !          3968: static int
        !          3969: pretty_domain(char **dst, char *dend, const unsigned char **src,
        !          3970:              const unsigned char *send)
        !          3971: {
        !          3972:        const unsigned char *tend;
        !          3973:        int count = 2;
        !          3974:        int tsiz, status;
        !          3975: 
        !          3976:        if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
        !          3977:            *dst == NULL || *src == NULL ||
        !          3978:            ((*dst + 2) > dend) || (*src >= send))
        !          3979:                return -1;
        !          3980: 
        !          3981:        **dst = '"';
        !          3982:        (*dst)++;
        !          3983: 
        !          3984:        do {
        !          3985:                /* Continue loop until end of src buffer. */
        !          3986:                if (*src >= send)
        !          3987:                        break;
        !          3988: 
        !          3989:                /* Consume tag size. */
        !          3990:                tsiz = **src;
        !          3991:                (*src)++;
        !          3992: 
        !          3993:                /* At root, finis. */
        !          3994:                if (tsiz == 0)
        !          3995:                        break;
        !          3996: 
        !          3997:                tend = (*src) + tsiz;
        !          3998: 
        !          3999:                /* If the tag exceeds the source buffer, it's illegal.
        !          4000:                 * This should also trap compression pointers (which should
        !          4001:                 * not be in these buffers).
        !          4002:                 */
        !          4003:                if (tend > send)
        !          4004:                        return -1;
        !          4005: 
        !          4006:                /* dend-2 leaves room for a trailing dot and quote. */
        !          4007:                status = pretty_escape(dst, dend-2, src, tend);
        !          4008: 
        !          4009:                if ((status == -1) || ((*dst + 2) > dend))
        !          4010:                        return -1;
        !          4011: 
        !          4012:                **dst = '.';
        !          4013:                (*dst)++;
        !          4014:                count += status + 1;
        !          4015:        }
        !          4016:        while(1);
        !          4017: 
        !          4018:        **dst = '"';
        !          4019:        (*dst)++;
        !          4020: 
        !          4021:        return count;
        !          4022: }
        !          4023: 
        !          4024: /*
        !          4025:  * Add the option identified with the option number and data to the
        !          4026:  * options state.
        !          4027:  */
        !          4028: int
        !          4029: add_option(struct option_state *options,
        !          4030:           unsigned int option_num,
        !          4031:           void *data,
        !          4032:           unsigned int data_len)
        !          4033: {
        !          4034:        struct option_cache *oc;
        !          4035:        struct option *option;
        !          4036: 
        !          4037:        /* INSIST(options != NULL); */
        !          4038:        /* INSIST(data != NULL); */
        !          4039: 
        !          4040:        option = NULL;
        !          4041:        if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, 
        !          4042:                                     &option_num, 0, MDL)) {
        !          4043:                log_error("Attempting to add unknown option %d.", option_num);
        !          4044:                return 0;
        !          4045:        }
        !          4046: 
        !          4047:        oc = NULL;
        !          4048:        if (!option_cache_allocate(&oc, MDL)) {
        !          4049:                log_error("No memory for option cache adding %s (option %d).",
        !          4050:                          option->name, option_num);
        !          4051:                return 0;
        !          4052:        }
        !          4053: 
        !          4054:        if (!make_const_data(&oc->expression, 
        !          4055:                             data, 
        !          4056:                             data_len,
        !          4057:                             0, 
        !          4058:                             0, 
        !          4059:                             MDL)) {
        !          4060:                log_error("No memory for constant data adding %s (option %d).",
        !          4061:                          option->name, option_num);
        !          4062:                option_cache_dereference(&oc, MDL);
        !          4063:                return 0;
        !          4064:        }
        !          4065: 
        !          4066:        option_reference(&(oc->option), option, MDL);
        !          4067:        save_option(&dhcp_universe, options, oc);
        !          4068:        option_cache_dereference(&oc, MDL);
        !          4069: 
        !          4070:        return 1;
        !          4071: }
        !          4072: 
        !          4073: 

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