]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Fix up all the option handling code to use evaluation instead of fixed data.
authorTed Lemon <source@isc.org>
Thu, 5 Nov 1998 18:43:23 +0000 (18:43 +0000)
committerTed Lemon <source@isc.org>
Thu, 5 Nov 1998 18:43:23 +0000 (18:43 +0000)
client/clparse.c
client/dhclient.c
common/parse.c

index 5b7003720b7f828563d066a77351ff881b5104a4..28f448210442a58599a2e1f00acf735f49431e24 100644 (file)
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: clparse.c,v 1.17 1998/10/22 04:52:23 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: clparse.c,v 1.18 1998/11/05 18:38:43 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -52,6 +52,17 @@ static TIME parsed_time;
 
 struct client_config top_level_config;
 
+u_int32_t default_requested_options [] = {
+       DHO_SUBNET_MASK,
+       DHO_BROADCAST_ADDRESS,
+       DHO_TIME_OFFSET,
+       DHO_ROUTERS,
+       DHO_DOMAIN_NAME,
+       DHO_DOMAIN_NAME_SERVERS,
+       DHO_HOST_NAME,
+       0
+};
+
 /* client-conf-file :== client-declarations EOF
    client-declarations :== <nil>
                         | client-declaration
@@ -84,32 +95,8 @@ int read_client_conf ()
        top_level_config.initial_interval = 10;
        top_level_config.bootp_policy = ACCEPT;
        top_level_config.script_name = "/etc/dhclient-script";
-       top_level_config.requested_options
-               [top_level_config.requested_option_count++] =
-                       DHO_SUBNET_MASK;
-       top_level_config.requested_options
-               [top_level_config.requested_option_count++] =
-                       DHO_BROADCAST_ADDRESS;
-       top_level_config.requested_options
-               [top_level_config.requested_option_count++] =
-                       DHO_TIME_OFFSET;
-       top_level_config.requested_options
-               [top_level_config.requested_option_count++] =
-                       DHO_ROUTERS;
-       top_level_config.requested_options
-               [top_level_config.requested_option_count++] =
-                       DHO_DOMAIN_NAME;
-       top_level_config.requested_options
-               [top_level_config.requested_option_count++] =
-                       DHO_DOMAIN_NAME_SERVERS;
-       top_level_config.requested_options
-               [top_level_config.requested_option_count++] =
-                       DHO_HOST_NAME;
-       top_level_config.requested_lease_time = 7200;
-       top_level_config.send_options.dhcp_options [DHO_DHCP_LEASE_TIME].data
-               = (unsigned char *)&top_level_config.requested_lease_time;
-       top_level_config.send_options.dhcp_options [DHO_DHCP_LEASE_TIME].len
-               = sizeof top_level_config.requested_lease_time;
+       top_level_config.requested_options = default_requested_options;
+       top_level_config.requested_lease = 7200;
 
        if ((cfile = fopen (path_dhclient_conf, "r")) != NULL) {
                do {
@@ -144,12 +131,6 @@ int read_client_conf ()
                                        error ("no memory for client config.");
                                memcpy (config, &top_level_config,
                                        sizeof top_level_config);
-                               i = DHO_DHCP_LEASE_TIME;
-                               config -> send_options.dhcp_options [i].data =
-                                       (unsigned char *)&config -> requested_lease_time;
-       top_level_config.send_options.dhcp_options [DHO_DHCP_LEASE_TIME].len
-               = sizeof top_level_config.requested_lease_time;
-
                        }
                        ip -> client -> config = config;
                }
@@ -215,19 +196,17 @@ void parse_client_statement (cfile, ip, config)
        char *val;
        struct option *option;
        struct executable_statement *stmt, **p;
-       enum statement op op;
+       enum statement_op op;
 
        switch (next_token (&val, cfile)) {
              case SEND:
-               p = &config -> on_tranmission;
+               p = &config -> on_transmission;
                op = send_option_statement;
              do_option:
                token = next_token (&val, cfile);
                option = parse_option_name (cfile);
-               if (!option) {
-                       *lose = 1;
-                       return (struct executable_statement *)0;
-               }
+               if (!option)
+                       return;
                stmt = parse_option_statement (cfile, 1, option,
                                               send_option_statement);
                for (; *p; p = &((*p) -> next))
@@ -271,14 +250,11 @@ void parse_client_statement (cfile, ip, config)
                return;
 
              case REQUEST:
-               config -> requested_option_count =
-                       parse_option_list (cfile, config -> requested_options);
+               parse_option_list (cfile, &config -> requested_options);
                return;
 
              case REQUIRE:
-               memset (config -> required_options, 0,
-                       sizeof config -> required_options);
-               parse_option_list (cfile, config -> required_options);
+               parse_option_list (cfile, &config -> required_options);
                return;
 
              case TIMEOUT:
@@ -390,13 +366,14 @@ int parse_X (cfile, buf, max)
 /* option-list :== option_name |
                   option_list COMMA option_name */
 
-int parse_option_list (cfile, list)
+void parse_option_list (cfile, list)
        FILE *cfile;
-       u_int8_t *list;
+       u_int32_t **list;
 {
        int ix, i;
        int token;
        char *val;
+       pair p = (pair)0, q, r;
 
        ix = 0;
        do {
@@ -404,7 +381,7 @@ int parse_option_list (cfile, list)
                if (!is_identifier (token)) {
                        parse_warn ("expected option name.");
                        skip_to_semi (cfile);
-                       return 0;
+                       return;
                }
                for (i = 0; i < 256; i++) {
                        if (!strcasecmp (dhcp_options [i].name, val))
@@ -413,22 +390,42 @@ int parse_option_list (cfile, list)
                if (i == 256) {
                        parse_warn ("%s: expected option name.");
                        skip_to_semi (cfile);
-                       return 0;
-               }
-               list [ix++] = i;
-               if (ix == 256) {
-                       parse_warn ("%s: too many options.", val);
-                       skip_to_semi (cfile);
-                       return 0;
+                       return;
                }
+               r = new_pair ("parse_option_list");
+               if (!r)
+                       error ("can't allocate pair for option code.");
+               r -> car = (caddr_t)i;
+               r -> cdr = (pair)0;
+               if (p)
+                       q -> cdr = r;
+               else
+                       p = r;
+               q = r;
+               ++ix;
                token = next_token (&val, cfile);
        } while (token == COMMA);
        if (token != SEMI) {
                parse_warn ("expecting semicolon.");
                skip_to_semi (cfile);
-               return 0;
+               return;
+       }
+       if (*list)
+               dfree (*list, "parse_option_list");
+       *list = dmalloc (ix * sizeof **list, "parse_option_list");
+       if (!*list)
+               warn ("no memory for option list.");
+       else {
+               ix = 0;
+               for (q = p; q; q = q -> cdr)
+                       (*list) [ix++] = (u_int32_t)q -> car;
+               (*list) [ix] = 0;
+       }
+       while (p) {
+               q = p -> cdr;
+               free_pair (p, "parse_option_list");
+               p = q;
        }
-       return ix;
 }
 
 /* interface-declaration :==
@@ -589,7 +586,7 @@ void parse_client_lease_statement (cfile, is_static)
        /* If the lease declaration didn't include an interface
           declaration that we recognized, it's of no use to us. */
        if (!ip) {
-               free_client_lease (lease);
+               destroy_client_lease (lease);
                return;
        }
 
@@ -616,7 +613,7 @@ void parse_client_lease_statement (cfile, is_static)
                                pl -> next = lp -> next;
                        else
                                ip -> client -> leases = lp -> next;
-                       free_client_lease (lp);
+                       destroy_client_lease (lp);
                        break;
                }
        }
@@ -642,13 +639,13 @@ void parse_client_lease_statement (cfile, is_static)
           still valid but no longer active. */
        if (ip -> client -> active) {
                if (ip -> client -> active -> expiry < cur_time)
-                       free_client_lease (ip -> client -> active);
+                       destroy_client_lease (ip -> client -> active);
                else if (ip -> client -> active -> address.len ==
                         lease -> address.len &&
                         !memcmp (ip -> client -> active -> address.iabuf,
                                  lease -> address.iabuf,
                                  lease -> address.len))
-                       free_client_lease (ip -> client -> active);
+                       destroy_client_lease (ip -> client -> active);
                else {
                        ip -> client -> active -> next =
                                ip -> client -> leases;
@@ -680,6 +677,7 @@ void parse_client_lease_declaration (cfile, lease, ipp)
        char *val;
        char *t, *n;
        struct interface_info *ip;
+       struct option_cache *oc;
 
        switch (next_token (&val, cfile)) {
              case BOOTP:
@@ -727,7 +725,14 @@ void parse_client_lease_declaration (cfile, lease, ipp)
                return;
 
              case OPTION:
-               parse_option_decl (cfile, lease -> options);
+               oc = (struct option_cache *)0;
+               if (parse_option_decl (&oc, cfile)) {
+                       /* XXX save_option here ought to account for the
+                          XXX correct option universe, but it doesn't. */
+                       save_option (lease -> options.dhcp_hash, oc);
+                       option_cache_dereference
+                               (&oc, "parse_client_lease_declaration");
+               }
                return;
 
              default:
@@ -742,9 +747,9 @@ void parse_client_lease_declaration (cfile, lease, ipp)
        }
 }
 
-struct option *parse_option_decl (cfile, options)
+int parse_option_decl (oc, cfile)
+       struct option_cache **oc;
        FILE *cfile;
-       struct option_data *options;
 {
        char *val;
        int token;
@@ -757,10 +762,11 @@ struct option *parse_option_decl (cfile, options)
        u_int8_t *dp;
        int len;
        int nul_term = 0;
+       struct buffer *bp;
 
        option = parse_option_name (cfile);
        if (!option)
-               return;
+               return 0;
 
        /* Parse the option data... */
        do {
@@ -784,14 +790,14 @@ struct option *parse_option_decl (cfile, options)
                                if (token != STRING) {
                                        parse_warn ("expecting string.");
                                        skip_to_semi (cfile);
-                                       return (struct option *)0;
+                                       return 0;
                                }
                                len = strlen (val);
                                if (hunkix + len + 1 > sizeof hunkbuf) {
                                        parse_warn ("option data buffer %s",
                                                    "overflow");
                                        skip_to_semi (cfile);
-                                       return (struct option *)0;
+                                       return 0;
                                }
                                memcpy (&hunkbuf [hunkix], val, len + 1);
                                nul_term = 1;
@@ -800,7 +806,7 @@ struct option *parse_option_decl (cfile, options)
 
                              case 'I': /* IP address. */
                                if (!parse_ip_addr (cfile, &ip_addr))
-                                       return (struct option *)0;
+                                       return 0;
                                len = ip_addr.len;
                                dp = ip_addr.iabuf;
 
@@ -809,7 +815,7 @@ struct option *parse_option_decl (cfile, options)
                                        parse_warn ("option data buffer %s",
                                                    "overflow");
                                        skip_to_semi (cfile);
-                                       return (struct option *)0;
+                                       return 0;
                                }
                                memcpy (&hunkbuf [hunkix], dp, len);
                                hunkix += len;
@@ -823,7 +829,7 @@ struct option *parse_option_decl (cfile, options)
                                        parse_warn ("expecting number.");
                                        if (token != SEMI)
                                                skip_to_semi (cfile);
-                                       return (struct option *)0;
+                                       return 0;
                                }
                                convert_num (buf, val, 0, 32);
                                len = 4;
@@ -857,7 +863,7 @@ struct option *parse_option_decl (cfile, options)
                                      bad_flag:
                                        if (token != SEMI)
                                                skip_to_semi (cfile);
-                                       return (struct option *)0;
+                                       return 0;
                                }
                                if (!strcasecmp (val, "true")
                                    || !strcasecmp (val, "on"))
@@ -877,7 +883,7 @@ struct option *parse_option_decl (cfile, options)
                                warn ("Bad format %c in parse_option_param.",
                                      *fmt);
                                skip_to_semi (cfile);
-                               return (struct option *)0;
+                               return 0;
                        }
                }
                token = next_token (&val, cfile);
@@ -886,16 +892,25 @@ struct option *parse_option_decl (cfile, options)
        if (token != SEMI) {
                parse_warn ("semicolon expected.");
                skip_to_semi (cfile);
-               return (struct option *)0;
+               return 0;
        }
 
-       options [option -> code].data =
-               (unsigned char *)malloc (hunkix + nul_term);
-       if (!options [option -> code].data)
+       bp = (struct buffer *)0;
+       if (!buffer_allocate (&bp, hunkix + nul_term, "parse_option_decl"))
+               error ("no memory to store option declaration.");
+       if (!bp -> data)
                error ("out of memory allocating option data.");
-       memcpy (options [option -> code].data, hunkbuf, hunkix + nul_term);
-       options [option -> code].len = hunkix;
-       return option;
+       memcpy (bp -> data, hunkbuf, hunkix + nul_term);
+       
+       if (!option_cache_allocate (oc, "parse_option_decl"))
+               error ("out of memory allocating option cache.");
+
+       (*oc) -> data.buffer = bp;
+       (*oc) -> data.data = &bp -> data [0];
+       (*oc) -> data.terminated = nul_term;
+       (*oc) -> data.len = hunkix;
+       (*oc) -> option = option;
+       return 1;
 }
 
 void parse_string_list (cfile, lp, multiple)
index 785c9148ee40564e67fc748488be3d6b6e550dc6..0a9893cc76ff4eeb15a3eae131143cc1198a1a67 100644 (file)
@@ -56,7 +56,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: dhclient.c,v 1.49 1998/04/09 04:26:24 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: dhclient.c,v 1.50 1998/11/05 18:39:04 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -259,6 +259,25 @@ void cleanup ()
 {
 }
 
+struct class *find_class (s)
+       char *s;
+{
+       return (struct class *)0;
+}
+
+int check_collection (packet, collection)
+       struct packet *packet;
+       struct collection *collection;
+{
+       return 0;
+}
+
+void classify (packet, class)
+       struct packet *packet;
+       struct class *class;
+{
+}
+
 /* Individual States:
  * 
  * Each routine is called from the dhclient_state_machine() in one of
@@ -389,7 +408,7 @@ void state_selecting (ipp)
                        picked -> next = (struct client_lease *)0;
                } else {
                      freeit:
-                       free_client_lease (lp);
+                       destroy_client_lease (lp);
                }
        }
        ip -> client -> offered_leases = (struct client_lease *)0;
@@ -403,7 +422,7 @@ void state_selecting (ipp)
        }
 
        /* If it was a BOOTREPLY, we can just take the address right now. */
-       if (!picked -> options [DHO_DHCP_MESSAGE_TYPE].len) {
+       if (!picked -> is_bootp) {
                ip -> client -> new = picked;
 
                /* Make up some lease expiry times
@@ -430,7 +449,7 @@ void state_selecting (ipp)
        ip -> client -> xid = ip -> client -> packet.xid;
 
        /* Toss the lease we picked - we'll get it back in a DHCPACK. */
-       free_client_lease (picked);
+       destroy_client_lease (picked);
 
        /* Add an immediate timeout to send the first DHCPREQUEST packet. */
        send_request (ip);
@@ -444,6 +463,8 @@ void dhcpack (packet)
 {
        struct interface_info *ip = packet -> interface;
        struct client_lease *lease;
+       struct option_cache *oc;
+       struct data_string ds;
        int i;
        
        /* If we're not receptive to an offer right now, or if the offer
@@ -479,26 +500,63 @@ void dhcpack (packet)
        cancel_timeout (send_request, ip);
 
        /* Figure out the lease time. */
-       ip -> client -> new -> expiry =
-               getULong (ip -> client ->
-                         new -> options [DHO_DHCP_LEASE_TIME].data);
+       oc = lookup_option (ip -> client -> new -> options.dhcp_hash,
+                           DHO_DHCP_LEASE_TIME);
+       memset (&ds, 0, sizeof ds);
+       if (oc &&
+           evaluate_option_cache (&ds, packet,
+                                  &ip -> client -> new -> options, oc)) {
+               if (ds.len > 3)
+                       ip -> client -> new -> expiry = getULong (ds.data);
+               else
+                       ip -> client -> new -> expiry = 0;
+               data_string_forget (&ds, "dhcpack");
+       } else
+                       ip -> client -> new -> expiry = 0;
+
+       if (!ip -> client -> new -> expiry) {
+               warn ("no expiry time on offered lease.");
+               /* XXX this is going to be bad - if this _does_
+                  XXX happen, we should probably dynamically 
+                  XXX disqualify the DHCP server that gave us the
+                  XXX bad packet from future selections and
+                  XXX then go back into the init state. */
+               state_init (ip);
+               return;
+       }
 
-       /* Take the server-provided renewal time if there is one;
-          otherwise figure it out according to the spec. */
-       if (ip -> client -> new -> options [DHO_DHCP_RENEWAL_TIME].len)
-               ip -> client -> new -> renewal =
-                       getULong (ip -> client ->
-                                 new -> options [DHO_DHCP_RENEWAL_TIME].data);
-       else
+       /* Take the server-provided renewal time if there is one. */
+       oc = lookup_option (ip -> client -> new -> options.dhcp_hash,
+                           DHO_DHCP_RENEWAL_TIME);
+       if (oc &&
+           evaluate_option_cache (&ds, packet,
+                                  &ip -> client -> new -> options, oc)) {
+               if (ds.len > 3)
+                       ip -> client -> new -> renewal = getULong (ds.data);
+               else
+                       ip -> client -> new -> renewal = 0;
+       } else
+                       ip -> client -> new -> renewal = 0;
+
+       /* If it wasn't specified by the server, calculate it. */
+       if (!ip -> client -> new -> renewal)
                ip -> client -> new -> renewal =
                        ip -> client -> new -> expiry / 2;
 
        /* Same deal with the rebind time. */
-       if (ip -> client -> new -> options [DHO_DHCP_REBINDING_TIME].len)
-               ip -> client -> new -> rebind =
-                       getULong (ip -> client -> new ->
-                                 options [DHO_DHCP_REBINDING_TIME].data);
-       else
+       oc = lookup_option (ip -> client -> new -> options.dhcp_hash,
+                           DHO_DHCP_REBINDING_TIME);
+       if (oc &&
+           evaluate_option_cache (&ds, packet,
+                                  &ip -> client -> new -> options, oc)) {
+               if (ds.len > 3)
+                       ip -> client -> new -> rebind = getULong (ds.data);
+               else
+                       ip -> client -> new -> rebind = 0;
+       } else
+                       ip -> client -> new -> rebind = 0;
+
+       if (!ip -> client -> new -> rebind)
                ip -> client -> new -> rebind =
                        ip -> client -> new -> renewal +
                                ip -> client -> new -> renewal / 2 +
@@ -537,7 +595,7 @@ void bind_lease (ip)
 
        /* Replace the old active lease with the new one. */
        if (ip -> client -> active)
-               free_client_lease (ip -> client -> active);
+               destroy_client_lease (ip -> client -> active);
        ip -> client -> active = ip -> client -> new;
        ip -> client -> new = (struct client_lease *)0;
 
@@ -562,6 +620,9 @@ void state_bound (ipp)
        void *ipp;
 {
        struct interface_info *ip = ipp;
+       int i;
+       struct option_cache *oc;
+       struct data_string ds;
 
        ASSERT_STATE(state, S_BOUND);
 
@@ -569,12 +630,17 @@ void state_bound (ipp)
        make_request (ip, ip -> client -> active);
        ip -> client -> xid = ip -> client -> packet.xid;
 
-       if (ip -> client -> active ->
-           options [DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
-               memcpy (ip -> client -> destination.iabuf,
-                       ip -> client -> active ->
-                       options [DHO_DHCP_SERVER_IDENTIFIER].data, 4);
-               ip -> client -> destination.len = 4;
+       memset (&ds, 0, sizeof ds);
+       oc = lookup_option (ip -> client -> active -> options.dhcp_hash,
+                           DHO_DHCP_SERVER_IDENTIFIER);
+       if (oc &&
+           evaluate_option_cache (&ds, (struct packet *)0,
+                                  &ip -> client -> active -> options, oc)) {
+               if (ds.len > 3) {
+                       memcpy (ip -> client -> destination.iabuf, ds.data, 4);
+                       ip -> client -> destination.len = 4;
+               } else
+                       ip -> client -> destination = iaddr_broadcast;
        } else
                ip -> client -> destination = iaddr_broadcast;
 
@@ -671,9 +737,9 @@ void dhcpoffer (packet)
        struct client_lease *lease, *lp;
        int i;
        int arp_timeout_needed, stop_selecting;
-       char *name = (packet -> options [DHO_DHCP_MESSAGE_TYPE].len
-                     ? "DHCPOFFER" : "BOOTREPLY");
+       char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY";
        struct iaddrlist *ap;
+       struct option_cache *oc;
        
 #ifdef DEBUG_PACKET
        dump_packet (packet);
@@ -696,11 +762,15 @@ void dhcpoffer (packet)
 
        /* If this lease doesn't supply the minimum required parameters,
           blow it off. */
-       for (i = 0; ip -> client -> config -> required_options [i]; i++) {
-               if (!packet -> options [ip -> client -> config ->
-                                       required_options [i]].len) {
-                       note ("%s isn't satisfactory.", name);
-                       return;
+       if (ip -> client -> config -> required_options) {
+               for (i = 0;
+                    ip -> client -> config -> required_options [i]; i++) {
+                       if (!lookup_option
+                           (packet -> options.dhcp_hash,
+                            ip -> client -> config -> required_options [i])) {
+                               note ("%s isn't satisfactory.", name);
+                               return;
+                       }
                }
        }
 
@@ -723,7 +793,7 @@ void dhcpoffer (packet)
 
        /* If this lease was acquired through a BOOTREPLY, record that
           fact. */
-       if (!packet -> options [DHO_DHCP_MESSAGE_TYPE].len)
+       if (!packet -> options_valid || !packet -> packet_type)
                lease -> is_bootp = 1;
 
        /* Record the medium under which this lease was offered. */
@@ -797,8 +867,10 @@ struct client_lease *packet_to_lease (packet)
 {
        struct client_lease *lease;
        int i;
+       struct option_cache *oc;
+       struct data_string data;
 
-       lease = (struct client_lease *)malloc (sizeof (struct client_lease));
+       lease = (struct client_lease *)new_client_lease ("packet_to_lease");
 
        if (!lease) {
                warn ("dhcpoffer: no memory to record lease.\n");
@@ -808,45 +880,38 @@ struct client_lease *packet_to_lease (packet)
        memset (lease, 0, sizeof *lease);
 
        /* Copy the lease options. */
-       for (i = 0; i < 256; i++) {
-               if (packet -> options [i].len) {
-                       lease -> options [i].data =
-                               (unsigned char *)
-                                       malloc (packet -> options [i].len + 1);
-                       if (!lease -> options [i].data) {
-                               warn ("dhcpoffer: no memory for option %d\n",
-                                     i);
-                               free_client_lease (lease);
-                               return (struct client_lease *)0;
-                       } else {
-                               memcpy (lease -> options [i].data,
-                                       packet -> options [i].data,
-                                       packet -> options [i].len);
-                               lease -> options [i].len =
-                                       packet -> options [i].len;
-                               lease -> options [i].data
-                                       [lease -> options [i].len] = 0;
-                       }
-               }
-       }
+       lease -> options = packet -> options;
+       memset (&packet -> options, 0, sizeof packet -> options);
 
        lease -> address.len = sizeof (packet -> raw -> yiaddr);
        memcpy (lease -> address.iabuf, &packet -> raw -> yiaddr,
                lease -> address.len);
 
+       /* Figure out the overload flag. */
+       oc = lookup_option (lease -> options.dhcp_hash,
+                           DHO_DHCP_OPTION_OVERLOAD);
+       memset (&data, 0, sizeof data);
+       if (oc &&
+           evaluate_option_cache (&data, packet, &lease -> options, oc)) {
+               if (data.len > 0)
+                       i = data.data [0];
+               else
+                       i = 0;
+               data_string_forget (&data, "packet_to_lease");
+       } else
+               i = 0;
+
        /* If the server name was filled out, copy it. */
-       if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len ||
-            !(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)) &&
-           packet -> raw -> sname [0]) {
+       if (!(i & 2) && packet -> raw -> sname [0]) {
                int len;
                /* Don't count on the NUL terminator. */
                for (len = 0; len < 64; len++)
                        if (!packet -> raw -> sname [len])
                                break;
-               lease -> server_name = malloc (len + 1);
+               lease -> server_name = dmalloc (len + 1, "packet_to_lease");
                if (!lease -> server_name) {
                        warn ("dhcpoffer: no memory for filename.\n");
-                       free_client_lease (lease);
+                       destroy_client_lease (lease);
                        return (struct client_lease *)0;
                } else {
                        memcpy (lease -> server_name,
@@ -856,18 +921,16 @@ struct client_lease *packet_to_lease (packet)
        }
 
        /* Ditto for the filename. */
-       if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len ||
-            !(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)) &&
-           packet -> raw -> file [0]) {
+       if ((i & 1) && packet -> raw -> file [0]) {
                int len;
                /* Don't count on the NUL terminator. */
                for (len = 0; len < 64; len++)
                        if (!packet -> raw -> file [len])
                                break;
-               lease -> filename = malloc (len + 1);
+               lease -> filename = dmalloc (len + 1, "packet_to_lease");
                if (!lease -> filename) {
                        warn ("dhcpoffer: no memory for filename.\n");
-                       free_client_lease (lease);
+                       destroy_client_lease (lease);
                        return (struct client_lease *)0;
                } else {
                        memcpy (lease -> filename,
@@ -909,7 +972,7 @@ void dhcpnak (packet)
                return;
        }
 
-       free_client_lease (ip -> client -> active);
+       destroy_client_lease (ip -> client -> active);
        ip -> client -> active = (struct client_lease *)0;
 
        /* Stop sending DHCPREQUEST packets... */
@@ -1317,74 +1380,124 @@ void send_release (ipp)
                warn ("send_packet: %m");
 }
 
-void make_discover (ip, lease)
+void make_client_options (ip, lease, type, sid, rip, prl, statements,
+                         options)
        struct interface_info *ip;
        struct client_lease *lease;
+       u_int8_t *type;
+       struct option_cache *sid;
+       struct iaddr *rip;
+       u_int32_t *prl;
+       struct executable_statement *statements;
+       struct option_state *options;
 {
-       struct dhcp_packet *raw;
-       unsigned char discover = DHCPDISCOVER;
        int i;
-
-       struct tree_cache *options [256];
-       struct tree_cache option_elements [256];
-
-       memset (option_elements, 0, sizeof option_elements);
-       memset (options, 0, sizeof options);
-       memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet));
-
-       /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
-       i = DHO_DHCP_MESSAGE_TYPE;
-       options [i] = &option_elements [i];
-       options [i] -> value = &discover;
-       options [i] -> len = sizeof discover;
-       options [i] -> buf_size = sizeof discover;
-       options [i] -> timeout = 0xFFFFFFFF;
-       options [i] -> tree = (struct tree *)0;
-
-       /* Request the options we want */
-       i  = DHO_DHCP_PARAMETER_REQUEST_LIST;
-       options [i] = &option_elements [i];
-       options [i] -> value = ip -> client -> config -> requested_options;
-       options [i] -> len = ip -> client -> config -> requested_option_count;
-       options [i] -> buf_size =
-               ip -> client -> config -> requested_option_count;
-       options [i] -> timeout = 0xFFFFFFFF;
-       options [i] -> tree = (struct tree *)0;
-
-       /* If we had an address, try to get it again. */
-       if (lease) {
-               ip -> client -> requested_address = lease -> address;
-               i = DHO_DHCP_REQUESTED_ADDRESS;
-               options [i] = &option_elements [i];
-               options [i] -> value = lease -> address.iabuf;
-               options [i] -> len = lease -> address.len;
-               options [i] -> buf_size = lease -> address.len;
-               options [i] -> timeout = 0xFFFFFFFF;
-               options [i] -> tree = (struct tree *)0;
+       struct option_cache *oc;
+       struct buffer *bp = (struct buffer *)0;
+
+       memset (options, 0, sizeof *options);
+
+       /* Send the server identifier if provided. */
+       if (sid)
+               save_option (options -> dhcp_hash, sid);
+
+       /* Send the requested address if provided. */
+       if (rip) {
+               ip -> client -> requested_address = *rip;
+               if (!(make_const_option_cache
+                     (&oc, (struct buffer **)0,
+                      rip -> iabuf, rip -> len,
+                      &dhcp_options [DHO_DHCP_REQUESTED_ADDRESS],
+                      "make_client_options")))
+                       warn ("can't make requested address option cache.");
+               else {
+                       save_option (options -> dhcp_hash, oc);
+                       option_cache_dereference (&oc, "make_client_options");
+               }
        } else {
                ip -> client -> requested_address.len = 0;
        }
 
-       /* Send any options requested in the config file. */
-       for (i = 0; i < 256; i++) {
-               if (!options [i] &&
-                   ip -> client -> config -> send_options [i].data) {
-                       options [i] = &option_elements [i];
-                       options [i] -> value = ip -> client -> config ->
-                               send_options [i].data;
-                       options [i] -> len = ip -> client -> config ->
-                               send_options [i].len;
-                       options [i] -> buf_size = ip -> client -> config ->
-                               send_options [i].len;
-                       options [i] -> timeout = 0xFFFFFFFF;
-                       options [i] -> tree = (struct tree *)0;
+       if (!(make_const_option_cache
+             (&oc, (struct buffer **)0,
+              type, 1, &dhcp_options [DHO_DHCP_MESSAGE_TYPE],
+              "make_client_options")))
+               warn ("can't make message type.");
+       else {
+               save_option (options -> dhcp_hash, oc);
+               option_cache_dereference (&oc, "make_client_options");
+       }
+
+       if (prl) {
+               /* Figure out how many parameters were requested. */
+               for (i = 0; prl [i]; i++)
+                       ;
+               if (!buffer_allocate (&bp, i, "make_client_options"))
+                       warn ("can't make buffer for parameter request list.");
+               else {
+                       for (i = 0; prl [i]; i++)
+                               bp -> data [i] = prl [i];
+                       if (!(make_const_option_cache
+                             (&oc, &bp, (u_int8_t *)0, i,
+                              &dhcp_options [DHO_DHCP_PARAMETER_REQUEST_LIST],
+                              "make_client_options")))
+                               warn ("can't make option cache");
+                       else {
+                               save_option (options -> dhcp_hash, oc);
+                               option_cache_dereference
+                                       (&oc, "make_client_options");
+                       }
                }
        }
 
+       if (!(oc = lookup_option (options -> dhcp_hash,
+                                 DHO_DHCP_LEASE_TIME))) {
+               if (!buffer_allocate (&bp, sizeof (u_int32_t),
+                                     "make_client_options"))
+                       warn ("can't make buffer for requested lease time.");
+               else {
+                       putULong (bp -> data,
+                                 ip -> client -> config -> requested_lease);
+                       if (!(make_const_option_cache
+                             (&oc, &bp, (u_int8_t *)0, sizeof (u_int32_t),
+                              &dhcp_options [DHO_DHCP_LEASE_TIME],
+                              "make_client_options")))
+                               warn ("can't make option cache");
+                       else {
+                               save_option (options -> dhcp_hash, oc);
+                               option_cache_dereference
+                                       (&oc, "make_client_options");
+                       }
+               }
+       }               
+
+       /* Run statements that need to be run on transmission. */
+       if (statements)
+               execute_statements ((struct packet *)0,
+                                   &lease -> options, options, statements);
+}
+
+void make_discover (ip, lease)
+       struct interface_info *ip;
+       struct client_lease *lease;
+{
+       struct dhcp_packet *raw;
+       unsigned char discover = DHCPDISCOVER;
+       int i;
+       struct option_state options;
+
+       memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet));
+
+       make_client_options (ip, lease, &discover, (struct option_cache *)0,
+                            lease ? &lease -> address : (struct iaddr *)0,
+                            ip -> client -> config -> requested_options,
+                            ip -> client -> config -> on_transmission,
+                            &options);
+
        /* Set up the option buffer... */
        ip -> client -> packet_length =
                cons_options ((struct packet *)0, &ip -> client -> packet, 0,
-                             options, (struct agent_options *)0, 0, 0, 0);
+                             &options, 0, 0, 0);
        if (ip -> client -> packet_length < BOOTP_MIN_LEN)
                ip -> client -> packet_length = BOOTP_MIN_LEN;
 
@@ -1420,78 +1533,30 @@ void make_request (ip, lease)
 {
        unsigned char request = DHCPREQUEST;
        int i;
+       struct option_state options;
+       struct option_cache *oc;
 
-       struct tree_cache *options [256];
-       struct tree_cache option_elements [256];
-
-       memset (options, 0, sizeof options);
        memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet));
 
-       /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
-       i = DHO_DHCP_MESSAGE_TYPE;
-       options [i] = &option_elements [i];
-       options [i] -> value = &request;
-       options [i] -> len = sizeof request;
-       options [i] -> buf_size = sizeof request;
-       options [i] -> timeout = 0xFFFFFFFF;
-       options [i] -> tree = (struct tree *)0;
-
-       /* Request the options we want */
-       i = DHO_DHCP_PARAMETER_REQUEST_LIST;
-       options [i] = &option_elements [i];
-       options [i] -> value = ip -> client -> config -> requested_options;
-       options [i] -> len = ip -> client -> config -> requested_option_count;
-       options [i] -> buf_size =
-               ip -> client -> config -> requested_option_count;
-       options [i] -> timeout = 0xFFFFFFFF;
-       options [i] -> tree = (struct tree *)0;
-
-       /* If we are requesting an address that hasn't yet been assigned
-          to us, use the DHCP Requested Address option. */
-       if (ip -> client -> state == S_REQUESTING) {
-               /* Send back the server identifier... */
-               i = DHO_DHCP_SERVER_IDENTIFIER;
-               options [i] = &option_elements [i];
-               options [i] -> value = lease -> options [i].data;
-               options [i] -> len = lease -> options [i].len;
-               options [i] -> buf_size = lease -> options [i].len;
-               options [i] -> timeout = 0xFFFFFFFF;
-               options [i] -> tree = (struct tree *)0;
-       }
-       if (ip -> client -> state == S_REQUESTING ||
-           ip -> client -> state == S_REBOOTING) {
-               ip -> client -> requested_address = lease -> address;
-               i = DHO_DHCP_REQUESTED_ADDRESS;
-               options [i] = &option_elements [i];
-               options [i] -> value = lease -> address.iabuf;
-               options [i] -> len = lease -> address.len;
-               options [i] -> buf_size = lease -> address.len;
-               options [i] -> timeout = 0xFFFFFFFF;
-               options [i] -> tree = (struct tree *)0;
-       } else {
-               ip -> client -> requested_address.len = 0;
-       }
+       if (ip -> client -> state == S_REQUESTING)
+               oc = lookup_option (lease -> options.dhcp_hash,
+                                   DHO_DHCP_SERVER_IDENTIFIER);
+       else
+               oc = (struct option_cache *)0;
 
-       /* Send any options requested in the config file. */
-       for (i = 0; i < 256; i++) {
-               if (!options [i] &&
-                   ip -> client -> config -> send_options [i].data) {
-                       options [i] = &option_elements [i];
-                       options [i] -> value = ip -> client -> config ->
-                               send_options [i].data;
-                       options [i] -> len = ip -> client -> config ->
-                               send_options [i].len;
-                       options [i] -> buf_size = ip -> client -> config ->
-                               send_options [i].len;
-                       options [i] -> timeout = 0xFFFFFFFF;
-                       options [i] -> tree = (struct tree *)0;
-               }
-       }
+       make_client_options (ip, lease, &request, oc,
+                            ((ip -> client -> state == S_REQUESTING ||
+                              ip -> client -> state == S_REBOOTING)
+                             ? &lease -> address
+                             : (struct iaddr *)0),
+                            ip -> client -> config -> requested_options,
+                            ip -> client -> config -> on_transmission,
+                            &options);
 
        /* Set up the option buffer... */
        ip -> client -> packet_length =
                cons_options ((struct packet *)0, &ip -> client -> packet, 0,
-                             options, (struct agent_options *)0, 0, 0, 0);
+                             &options, 0, 0, 0);
        if (ip -> client -> packet_length < BOOTP_MIN_LEN)
                ip -> client -> packet_length = BOOTP_MIN_LEN;
 
@@ -1535,62 +1600,22 @@ void make_decline (ip, lease)
 {
        unsigned char decline = DHCPDECLINE;
        int i;
+       struct option_cache *oc;
 
-       struct tree_cache *options [256];
-       struct tree_cache message_type_tree;
-       struct tree_cache requested_address_tree;
-       struct tree_cache server_id_tree;
-       struct tree_cache client_id_tree;
+       struct option_state options;
 
-       memset (options, 0, sizeof options);
        memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet));
 
-       /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */
-       i = DHO_DHCP_MESSAGE_TYPE;
-       options [i] = &message_type_tree;
-       options [i] -> value = &decline;
-       options [i] -> len = sizeof decline;
-       options [i] -> buf_size = sizeof decline;
-       options [i] -> timeout = 0xFFFFFFFF;
-       options [i] -> tree = (struct tree *)0;
-
-       /* Send back the server identifier... */
-       i = DHO_DHCP_SERVER_IDENTIFIER;
-        options [i] = &server_id_tree;
-        options [i] -> value = lease -> options [i].data;
-        options [i] -> len = lease -> options [i].len;
-        options [i] -> buf_size = lease -> options [i].len;
-        options [i] -> timeout = 0xFFFFFFFF;
-        options [i] -> tree = (struct tree *)0;
-
-       /* Send back the address we're declining. */
-       i = DHO_DHCP_REQUESTED_ADDRESS;
-       options [i] = &requested_address_tree;
-       options [i] -> value = lease -> address.iabuf;
-       options [i] -> len = lease -> address.len;
-       options [i] -> buf_size = lease -> address.len;
-       options [i] -> timeout = 0xFFFFFFFF;
-       options [i] -> tree = (struct tree *)0;
-
-       /* Send the uid if the user supplied one. */
-       i = DHO_DHCP_CLIENT_IDENTIFIER;
-       if (ip -> client -> config -> send_options [i].len) {
-               options [i] = &client_id_tree;
-               options [i] -> value = ip -> client -> config ->
-                       send_options [i].data;
-               options [i] -> len = ip -> client -> config ->
-                       send_options [i].len;
-               options [i] -> buf_size = ip -> client -> config ->
-                       send_options [i].len;
-               options [i] -> timeout = 0xFFFFFFFF;
-               options [i] -> tree = (struct tree *)0;
-       }
-
+       oc = lookup_option (lease -> options.dhcp_hash,
+                           DHO_DHCP_SERVER_IDENTIFIER);
+       make_client_options (ip, lease, &decline, oc,
+                            &lease -> address, (u_int32_t *)0,
+                            (struct executable_statement *)0, &options);
 
        /* Set up the option buffer... */
        ip -> client -> packet_length =
                cons_options ((struct packet *)0, &ip -> client -> packet, 0,
-                             options, (struct agent_options *)0, 0, 0, 0);
+                             &options, 0, 0, 0);
        if (ip -> client -> packet_length < BOOTP_MIN_LEN)
                ip -> client -> packet_length = BOOTP_MIN_LEN;
 
@@ -1626,37 +1651,22 @@ void make_release (ip, lease)
 {
        unsigned char request = DHCPRELEASE;
        int i;
+       struct option_cache *oc;
 
-       struct tree_cache *options [256];
-       struct tree_cache message_type_tree;
-       struct tree_cache requested_address_tree;
-       struct tree_cache server_id_tree;
+       struct option_state options;
 
-       memset (options, 0, sizeof options);
        memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet));
 
-       /* Set DHCP_MESSAGE_TYPE to DHCPRELEASE */
-       i = DHO_DHCP_MESSAGE_TYPE;
-       options [i] = &message_type_tree;
-       options [i] -> value = &request;
-       options [i] -> len = sizeof request;
-       options [i] -> buf_size = sizeof request;
-       options [i] -> timeout = 0xFFFFFFFF;
-       options [i] -> tree = (struct tree *)0;
-
-       /* Send back the server identifier... */
-       i = DHO_DHCP_SERVER_IDENTIFIER;
-        options [i] = &server_id_tree;
-        options [i] -> value = lease -> options [i].data;
-        options [i] -> len = lease -> options [i].len;
-        options [i] -> buf_size = lease -> options [i].len;
-        options [i] -> timeout = 0xFFFFFFFF;
-        options [i] -> tree = (struct tree *)0;
+       oc = lookup_option (lease -> options.dhcp_hash,
+                           DHO_DHCP_SERVER_IDENTIFIER);
+       make_client_options (ip, lease, &request, oc,
+                            &lease -> address, (u_int32_t *)0,
+                            (struct executable_statement *)0, &options);
 
        /* Set up the option buffer... */
        ip -> client -> packet_length =
                cons_options ((struct packet *)0, &ip -> client -> packet, 0,
-                             options, (struct agent_options *)0, 0, 0, 0);
+                             &options, 0, 0, 0);
        if (ip -> client -> packet_length < BOOTP_MIN_LEN)
                ip -> client -> packet_length = BOOTP_MIN_LEN;
 
@@ -1685,20 +1695,17 @@ void make_release (ip, lease)
 #endif
 }
 
-void free_client_lease (lease)
+void destroy_client_lease (lease)
        struct client_lease *lease;
 {
        int i;
 
        if (lease -> server_name)
-               free (lease -> server_name);
+               dfree (lease -> server_name, "destroy_client_lease");
        if (lease -> filename)
-               free (lease -> filename);
-       for (i = 0; i < 256; i++) {
-               if (lease -> options [i].len)
-                       free (lease -> options [i].data);
-       }
-       free (lease);
+               dfree (lease -> filename, "destroy_client_lease");
+       option_state_dereference (&lease -> options);
+       free_client_lease (lease, "destroy_client_lease");
 }
 
 FILE *leaseFile;
@@ -1744,6 +1751,8 @@ void write_client_lease (ip, lease, rewrite)
        int i;
        struct tm *t;
        static int leases_written;
+       struct option_cache *oc;
+       struct data_string ds;
 
        if (!rewrite) {
                if (leases_written++ > 20) {
@@ -1778,14 +1787,21 @@ void write_client_lease (ip, lease, rewrite)
        if (lease -> medium)
                fprintf (leaseFile, "  medium \"%s\";\n",
                         lease -> medium -> string);
-       for (i = 0; i < 256; i++) {
-               if (lease -> options [i].len) {
-                       fprintf (leaseFile,
-                                "  option %s %s;\n",
-                                dhcp_options [i].name,
-                                pretty_print_option
-                                (i, lease -> options [i].data,
-                                 lease -> options [i].len, 1, 1));
+       for (i = 0; i < OPTION_HASH_SIZE; i++) {
+               pair p;
+               for (p = lease -> options.dhcp_hash [i]; p; p = p -> cdr) {
+                       memset (&ds, 0, sizeof ds);
+                       if (evaluate_option_cache (&ds, (struct packet *)0,
+                                                  &lease -> options, oc)) {
+                               fprintf (leaseFile,
+                                        "  option %s %s;\n",
+                                        dhcp_options [i].name,
+                                        pretty_print_option
+                                        (i, ds.data, ds.len,
+                                         1, 1));
+                               data_string_forget (&ds,
+                                                   "write_client_lease");
+                       }
                }
        }
 
@@ -1863,8 +1879,8 @@ void script_write_params (ip, prefix, lease)
        struct client_lease *lease;
 {
        int i;
-       u_int8_t dbuf [1500];
-       int len;
+       struct data_string data;
+       struct option_cache *oc;
 
        fprintf (scriptFile, "%sip_address=\"%s\"\n",
                 prefix, piaddr (lease -> address));
@@ -1877,33 +1893,45 @@ void script_write_params (ip, prefix, lease)
           broadcast address, not the host address all zeroes
           broadcast address). */
 
-       if (lease -> options [DHO_SUBNET_MASK].len &&
-           (lease -> options [DHO_SUBNET_MASK].len <
-            sizeof lease -> address.iabuf)) {
-               struct iaddr netmask, subnet, broadcast;
-
-               memcpy (netmask.iabuf,
-                       lease -> options [DHO_SUBNET_MASK].data,
-                       lease -> options [DHO_SUBNET_MASK].len);
-               netmask.len = lease -> options [DHO_SUBNET_MASK].len;
-
-               subnet = subnet_number (lease -> address, netmask);
-               if (subnet.len) {
-                       fprintf (scriptFile, "%snetwork_number=\"%s\";\n",
-                                prefix, piaddr (subnet));
-                       fprintf (scriptFile, "export %snetwork_number\n",
-                                prefix);
-
-                       if (!lease -> options [DHO_BROADCAST_ADDRESS].len) {
-                               broadcast = broadcast_addr (subnet, netmask);
-                               if (broadcast.len) {
-                                       fprintf (scriptFile,
-                                                "%s%s=\"%s\";\n", prefix,
-                                                "broadcast_address",
-                                                piaddr (broadcast));
-                                       fprintf (scriptFile,
-                                                "export %s%s\n", prefix,
-                                                "broadcast_address");
+       memset (&data, 0, sizeof data);
+       oc = lookup_option (lease -> options.dhcp_hash, DHO_SUBNET_MASK);
+       if (oc && evaluate_option_cache (&data, (struct packet *)0,
+                                        &lease -> options, oc)) {
+               if (data.len > 3) {
+                       struct iaddr netmask, subnet, broadcast;
+
+                       memcpy (netmask.iabuf, data.data, data.len);
+                       netmask.len = data.len;
+                       data_string_forget (&data, "script_write_params");
+
+                       subnet = subnet_number (lease -> address, netmask);
+                       if (subnet.len) {
+                               fprintf (scriptFile,
+                                        "%snetwork_number=\"%s\";\n",
+                                        prefix, piaddr (subnet));
+                               fprintf (scriptFile,
+                                        "export %snetwork_number\n", prefix);
+
+                               oc = lookup_option (lease -> options.dhcp_hash,
+                                                   DHO_BROADCAST_ADDRESS);
+                               if (!oc ||
+                                   !evaluate_option_cache (&data,
+                                                           (struct packet *)0,
+                                                           &lease -> options,
+                                                           oc)) {
+                                       broadcast = broadcast_addr (subnet,
+                                                                   netmask);
+                                       if (broadcast.len) {
+                                               fprintf (scriptFile,
+                                                        "%s%s=\"%s\";\n",
+                                                        prefix,
+                                                        "broadcast_address",
+                                                        piaddr (broadcast));
+                                               fprintf (scriptFile,
+                                                        "export %s%s\n",
+                                                        prefix,
+                                                        "broadcast_address");
+                                       }
                                }
                        }
                }
@@ -1919,84 +1947,33 @@ void script_write_params (ip, prefix, lease)
                         prefix, lease -> server_name);
                fprintf (scriptFile, "export %sserver_name\n", prefix);
        }
-       for (i = 0; i < 256; i++) {
-               u_int8_t *dp;
-
-               if (ip -> client -> config -> defaults [i].len) {
-                       if (lease -> options [i].len) {
-                               switch (ip -> client ->
-                                       config -> default_actions [i]) {
-                                     case ACTION_DEFAULT:
-                                       dp = lease -> options [i].data;
-                                       len = lease -> options [i].len;
-                                       break;
-                                     case ACTION_SUPERSEDE:
-                                     supersede:
-                                       dp = ip -> client ->
-                                               config -> defaults [i].data;
-                                       len = ip -> client ->
-                                               config -> defaults [i].len;
-                                       break;
-                                     case ACTION_PREPEND:
-                                       len = (ip -> client ->
-                                              config -> defaults [i].len +
-                                              lease -> options [i].len);
-                                       if (len > sizeof dbuf) {
-                                               warn ("no space to %s %s",
-                                                     "prepend option",
-                                                     dhcp_options [i].name);
-                                               goto supersede;
-                                       }
-                                       dp = dbuf;
-                                       memcpy (dp,
-                                               ip -> client -> 
-                                               config -> defaults [i].data,
-                                               ip -> client -> 
-                                               config -> defaults [i].len);
-                                       memcpy (dp + ip -> client -> 
-                                               config -> defaults [i].len,
-                                               lease -> options [i].data,
-                                               lease -> options [i].len);
-                                       break;
-                                     case ACTION_APPEND:
-                                       len = (ip -> client ->
-                                              config -> defaults [i].len +
-                                              lease -> options [i].len);
-                                       if (len > sizeof dbuf) {
-                                               warn ("no space to %s %s",
-                                                     "prepend option",
-                                                     dhcp_options [i].name);
-                                               goto supersede;
-                                       }
-                                       dp = dbuf;
-                                       memcpy (dp,
-                                               ip -> client -> 
-                                               config -> defaults [i].data,
-                                               ip -> client -> 
-                                               config -> defaults [i].len);
-                                       memcpy (dp + ip -> client -> 
-                                               config -> defaults [i].len,
-                                               lease -> options [i].data,
-                                               lease -> options [i].len);
+
+       execute_statements ((struct packet *)0, &lease -> options,
+                           &lease -> options,
+                           ip -> client -> config -> on_transmission);
+
+       for (i = 0; i < OPTION_HASH_SIZE; i++) {
+               pair hp;
+
+               for (hp = lease -> options.dhcp_hash [i]; hp; hp = hp -> cdr) {
+                       oc = (struct option_cache *)hp -> car;
+
+                       if (evaluate_option_cache (&data, (struct packet *)0,
+                                                  &lease -> options, oc)) {
+
+                               if (data.len) {
+                                       char *s = (dhcp_option_ev_name
+                                                  (oc -> option));
+                               
+                                       fprintf (scriptFile,
+                                                "%s%s=\"%s\"\n", prefix, s,
+                                                (pretty_print_option
+                                                 (i, data.data, data.len,
+                                                  0, 0)));
+                                       fprintf (scriptFile,
+                                                "export %s%s\n", prefix, s);
                                }
-                       } else {
-                               dp = ip -> client ->
-                                       config -> defaults [i].data;
-                               len = ip -> client ->
-                                       config -> defaults [i].len;
                        }
-               } else if (lease -> options [i].len) {
-                       len = lease -> options [i].len;
-                       dp = lease -> options [i].data;
-               } else {
-                       len = 0;
-               }
-               if (len) {
-                       char *s = dhcp_option_ev_name (&dhcp_options [i]);
-                               
-                       fprintf (scriptFile, "%s%s=\"%s\"\n", prefix, s,
-                                pretty_print_option (i, dp, len, 0, 0));
-                       fprintf (scriptFile, "export %s%s\n", prefix, s);
                }
        }
        fprintf (scriptFile, "%sexpiry=\"%d\"\n",
index ece7e70024b0f262f02b6049d156a78856b2b4ad..251eb0095c33ebb8badd8e6d20434246e0c8c5b1 100644 (file)
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: parse.c,v 1.7 1998/06/25 03:07:51 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: parse.c,v 1.8 1998/11/05 18:43:23 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -65,7 +65,7 @@ static char copyright[] =
 void skip_to_semi (cfile)
        FILE *cfile;
 {
-       int token;
+       enum dhcp_token token;
        char *val;
        int brace_count = 0;
 
@@ -97,7 +97,7 @@ void skip_to_semi (cfile)
 int parse_semi (cfile)
        FILE *cfile;
 {
-       int token;
+       enum dhcp_token token;
        char *val;
 
        token = next_token (&val, cfile);
@@ -115,7 +115,7 @@ char *parse_string (cfile)
        FILE *cfile;
 {
        char *val;
-       int token;
+       enum dhcp_token token;
        char *s;
 
        token = next_token (&val, cfile);
@@ -144,7 +144,7 @@ char *parse_host_name (cfile)
        FILE *cfile;
 {
        char *val;
-       int token;
+       enum dhcp_token token;
        int len = 0;
        char *s;
        char *t;
@@ -198,29 +198,36 @@ char *parse_host_name (cfile)
    an expr_substring node to limit hostnames that evaluate to more
    than one IP address. */
 
-struct expression *parse_ip_addr_or_hostname (cfile, uniform)
+int parse_ip_addr_or_hostname (expr, cfile, uniform)
+       struct expression **expr;
        FILE *cfile;
        int uniform;
 {
        char *val;
-       int token;
+       enum dhcp_token token;
        unsigned char addr [4];
        int len = sizeof addr;
        char *name;
-       struct expression *rv;
+       struct expression *x = (struct expression *)0;
 
        token = peek_token (&val, cfile);
        if (is_identifier (token)) {
                name = parse_host_name (cfile);
                if (!name)
-                       return (struct expression *)0;
-               rv = make_host_lookup (name);
-               if (!uniform)
-                       rv = make_limit (rv, 4);
+                       return 0;
+               if (!make_host_lookup (expr, name))
+                       return 0;
+               if (!uniform) {
+                       if (!make_limit (&x, *expr, 4))
+                               return 0;
+                       expression_dereference (expr,
+                                               "parse_ip_addr_or_hostname");
+                       *expr = x;
+               }
        } else if (token == NUMBER) {
                if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
-                       return (struct expression *)0;
-               rv = make_const_data (addr, len, 0, 0);
+                       return 0;
+               return make_const_data (expr, addr, len, 0, 1);
        } else {
                if (token != RBRACE && token != LBRACE)
                        token = next_token (&val, cfile);
@@ -228,10 +235,10 @@ struct expression *parse_ip_addr_or_hostname (cfile, uniform)
                            val, token);
                if (token != SEMI)
                        skip_to_semi (cfile);
-               return (struct expression *)0;
+               return 0;
        }
 
-       return rv;
+       return 1;
 }      
        
 /*
@@ -243,7 +250,7 @@ int parse_ip_addr (cfile, addr)
        struct iaddr *addr;
 {
        char *val;
-       int token;
+       enum dhcp_token token;
 
        addr -> len = 4;
        if (parse_numeric_aggregate (cfile, addr -> iabuf,
@@ -262,7 +269,7 @@ void parse_hardware_param (cfile, hardware)
        struct hardware *hardware;
 {
        char *val;
-       int token;
+       enum dhcp_token token;
        int hlen;
        unsigned char *t;
 
@@ -316,7 +323,7 @@ void parse_lease_time (cfile, timep)
        TIME *timep;
 {
        char *val;
-       int token;
+       enum dhcp_token token;
 
        token = next_token (&val, cfile);
        if (token != NUMBER) {
@@ -348,7 +355,7 @@ unsigned char *parse_numeric_aggregate (cfile, buf,
        int size;
 {
        char *val;
-       int token;
+       enum dhcp_token token;
        unsigned char *bufp = buf, *s, *t;
        int count = 0;
        pair c = (pair)0;
@@ -547,7 +554,7 @@ TIME parse_date (cfile)
        struct tm tm;
        int guess;
        char *val;
-       int token;
+       enum dhcp_token token;
        static int months [11] = { 31, 59, 90, 120, 151, 181,
                                          212, 243, 273, 304, 334 };
 
@@ -709,7 +716,7 @@ struct option *parse_option_name (cfile)
        FILE *cfile;
 {
        char *val;
-       int token;
+       enum dhcp_token token;
        char *vendor;
        struct universe *universe;
        struct option *option;
@@ -783,18 +790,18 @@ struct option *parse_option_name (cfile)
  *                             NUMBER COLON colon-seperated-hex-list
  */
 
-unsigned char *parse_cshl (cfile, plen)
+int parse_cshl (data, cfile)
+       struct data_string *data;
        FILE *cfile;
-       int *plen;
 {
        char ibuf [128];
        int ilen = 0;
        int tlen = 0;
        struct option_tag *sl = (struct option_tag *)0;
        struct option_tag *next, **last = &sl;
-       int token;
+       enum dhcp_token token;
        char *val;
-       unsigned char *rv, *rvp;
+       unsigned char *rvp;
 
        do {
                token = next_token (&val, cfile);
@@ -805,7 +812,7 @@ unsigned char *parse_cshl (cfile, plen)
                                next = sl -> next;
                                dfree (sl, "parse_cshl");
                        }
-                       return (unsigned char *)0;
+                       return 0;
                }
                if (ilen == sizeof ibuf) {
                        next = (struct option_tag *)
@@ -828,10 +835,13 @@ unsigned char *parse_cshl (cfile, plen)
                token = next_token (&val, cfile);
        } while (1);
 
-       rv = dmalloc (tlen + ilen, "parse_cshl");
-       if (!rv)
+       if (!buffer_allocate (&data -> buffer, tlen + ilen, "parse_cshl"))
                error ("no memory to store octet data.");
-       rvp = rv;
+       data -> data = &data -> buffer -> data [0];
+       data -> len = tlen + ilen;
+       data -> terminated = 0;
+
+       rvp = &data -> data [0];
        while (sl) {
                next = sl -> next;
                memcpy (rvp, sl -> data, sizeof ibuf);
@@ -841,8 +851,7 @@ unsigned char *parse_cshl (cfile, plen)
        }
        
        memcpy (rvp, ibuf, ilen);
-       *plen = ilen + tlen;
-       return rv;
+       return 1;
 }
 
 /*
@@ -868,7 +877,7 @@ struct executable_statement *parse_executable_statements (cfile, lose)
        next = &head;
        while ((*next = parse_executable_statement (cfile, lose)))
                next = &((*next) -> next);
-       if (!lose)
+       if (!*lose)
                return head;
        return (struct executable_statement *)0;
 }
@@ -877,7 +886,7 @@ struct executable_statement *parse_executable_statement (cfile, lose)
        FILE *cfile;
        int *lose;
 {
-       int token;
+       enum dhcp_token token;
        char *val;
        struct executable_statement *stmt, base;
        struct class *cta;
@@ -989,19 +998,22 @@ struct executable_statement *parse_if_statement (cfile, lose)
        FILE *cfile;
        int *lose;
 {
-       int token;
+       enum dhcp_token token;
        char *val;
        struct executable_statement *stmt;
        struct expression *if_condition;
        struct executable_statement *true, *false;
 
        token = next_token (&val, cfile);
-       if_condition = parse_boolean_expression (cfile, lose);
-       if (!if_condition) {
+       if_condition = (struct expression *)0;
+       if (!parse_boolean_expression (&if_condition, cfile, lose)) {
                if (!*lose)
                        parse_warn ("boolean expression expected.");
                return (struct executable_statement *)0;
        }
+#if defined (DEBUG_EXPRESSION_PARSE)
+       print_expression ("if condition", if_condition);
+#endif
        token = next_token (&val, cfile);
        if (token != LBRACE) {
                parse_warn ("left brace expected.");
@@ -1066,18 +1078,95 @@ struct executable_statement *parse_if_statement (cfile, lose)
  *                       data-expression EQUAL data-expression |
  *                       boolean-expression AND boolean-expression |
  *                       boolean-expression OR boolean-expression
+ *                       EXISTS OPTION-NAME
  */
                          
+int parse_boolean_expression (expr, cfile, lose)
+       struct expression **expr;
+       FILE *cfile;
+       int *lose;
+{
+       /* Parse an expression... */
+       if (!parse_expression (expr, cfile, lose, context_boolean,
+                              (struct expression **)0, expr_none))
+               return 0;
+
+       if (!is_boolean_expression (*expr)) {
+               parse_warn ("Expecting a boolean expression.");
+               *lose = 1;
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * data_expression :== SUBSTRING LPAREN data-expression COMMA
+ *                                     numeric-expression COMMA
+ *                                     numeric-expression RPAREN |
+ *                    SUFFIX LPAREN data_expression COMMA
+ *                                  numeric-expression |
+ *                    OPTION option_name |
+ *                    HARDWARE |
+ *                    PACKET LPAREN numeric-expression COMMA
+ *                                  numeric-expression RPAREN |
+ *                    STRING |
+ *                    colon_seperated_hex_list
+ */
+
+int parse_data_expression (expr, cfile, lose)
+       struct expression **expr;
+       FILE *cfile;
+       int *lose;
+{
+       /* Parse an expression... */
+       if (!parse_expression (expr, cfile, lose, context_data,
+                              (struct expression **)0, expr_none))
+               return 0;
+
+       if (!is_data_expression (*expr)) {
+               parse_warn ("Expecting a data expression.");
+               *lose = 1;
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * numeric-expression :== EXTRACT_INT LPAREN data-expression
+ *                                          COMMA number RPAREN |
+ *                       NUMBER
+ */
+
+int parse_numeric_expression (expr, cfile, lose)
+       struct expression **expr;
+       FILE *cfile;
+       int *lose;
+{
+       /* Parse an expression... */
+       if (!parse_expression (expr, cfile, lose, context_numeric,
+                              (struct expression **)0, expr_none))
+               return 0;
+
+       if (!is_numeric_expression (*expr)) {
+               parse_warn ("Expecting a numeric expression.");
+               *lose = 1;
+               return 0;
+       }
+       return 1;
+}
+
+/* Parse a subexpression that does not contain a binary operator. */
 
-struct expression *parse_boolean_expression (cfile, lose)
+int parse_non_binary (expr, cfile, lose, context)
+       struct expression **expr;
        FILE *cfile;
        int *lose;
+       enum expression_context context;
 {
-       int token;
+       enum dhcp_token token;
        char *val;
        struct collection *col;
-       struct expression buf, *rv;
-       struct expression *left, *right;
+       struct option *option;
 
        token = peek_token (&val, cfile);
 
@@ -1090,7 +1179,7 @@ struct expression *parse_boolean_expression (cfile, lose)
                        parse_warn ("string expected.");
                        skip_to_semi (cfile);
                        *lose = 1;
-                       return (struct expression *)0;
+                       return 0;
                }
                for (col = collections; col; col = col -> next)
                        if (!strcmp (col -> name, val))
@@ -1098,150 +1187,102 @@ struct expression *parse_boolean_expression (cfile, lose)
                if (!col) {
                        parse_warn ("unknown collection.");
                        *lose = 1;
-                       return (struct expression *)0;
+                       return 0;
                }
-               buf.op = expr_check;
-               buf.data.check = col;
-               goto have_expr;
+               if (!expression_allocate (expr, "parse_expression: CHECK"))
+                       error ("can't allocate expression");
+               (*expr) -> op = expr_check;
+               (*expr) -> data.check = col;
+               break;
 
              case NOT:
                token = next_token (&val, cfile);
-               buf.op = expr_not;
-               buf.data.not = parse_boolean_expression (cfile, lose);
-               if (!buf.data.not) {
+               if (!expression_allocate (expr, "parse_expression: NOT"))
+                       error ("can't allocate expression");
+               (*expr) -> op = expr_not;
+               if (!parse_non_binary (&(*expr) -> data.not,
+                                      cfile, lose, context)) {
                        if (!*lose) {
-                               parse_warn ("match expression expected");
+                               parse_warn ("expression expected");
                                skip_to_semi (cfile);
                        }
                        *lose = 1;
-                       return (struct expression *)0;
+                       expression_dereference (expr, "parse_expression: NOT");
+                       return 0;
                }
-               goto have_expr;
-       }
-
-       /* If we're going to find an expression at this point, it must
-          involve a binary operator seperating two subexpressions. */
-       left = parse_data_expression (cfile, lose);
-       if (!left)
-               return left;
-       token = peek_token (&val, cfile);
-       switch (token) {
-             case EQUAL:
-               buf.op = expr_equal;
-               break;
-             case AND:
-               buf.op = expr_and;
                break;
-             case OR:
-               buf.op = expr_or;
-               break;
-             default:
-               parse_warn ("Expecting a boolean expression.");
-               skip_to_semi (cfile);
-               *lose = 1;
-               return (struct expression *)0;
-       }
-       token = next_token (&val, cfile);
 
-       /* Now find the RHS of the expression. */
-       right = parse_data_expression (cfile, lose);
-       if (!right) {
-               if (!*lose) {
-                       if (buf.op == expr_equal)
-                               parse_warn ("Expecting a data expression.");
-                       else
-                               parse_warn ("Expecting a boolean expression.");
-                       skip_to_semi (cfile);
+             case EXISTS:
+               token = next_token (&val, cfile);
+               if (!expression_allocate (expr, "parse_expression: EXISTS"))
+                       error ("can't allocate expression");
+               (*expr) -> op = expr_exists;
+               (*expr) -> data.option = parse_option_name (cfile);
+               if (!(*expr) -> data.option) {
+                       *lose = 1;
+                       expression_dereference (expr,
+                                               "parse_expression: EXISTS");
+                       return 0;
                }
-               return right;
-       }
-
-       /* Store the LHS and RHS. */
-       buf.data.equal [0] = left;
-       buf.data.equal [1] = right;
-
-      have_expr:
-       rv = new_expression ("parse_boolean_expression");
-       if (!rv)
-               error ("No memory for boolean expression.");
-       *rv = buf;
-       return rv;
-}      
-
-/*
- * data_expression :== SUBSTRING LPAREN data-expression COMMA
- *                                     numeric-expression COMMA
- *                                     numeric-expression RPAREN |
- *                    SUFFIX LPAREN data_expression COMMA
- *                                  numeric-expression |
- *                    OPTION option_name |
- *                    HARDWARE |
- *                    PACKET LPAREN numeric-expression COMMA
- *                                  numeric-expression RPAREN |
- *                    STRING |
- *                    colon_seperated_hex_list
- */
-
-struct expression *parse_data_expression (cfile, lose)
-       FILE *cfile;
-       int *lose;
-{
-       int token;
-       char *val;
-       struct collection *col;
-       struct expression buf, *rv;
-       struct expression *left, *right;
-       struct option *option;
-
-       token = peek_token (&val, cfile);
+               break;
 
-       switch (token) {
              case SUBSTRING:
                token = next_token (&val, cfile);
-               buf.op = expr_substring;
+               if (!expression_allocate (expr, "parse_expression: SUBSTRING"))
+                       error ("can't allocate expression");
+               (*expr) -> op = expr_substring;
 
                token = next_token (&val, cfile);
                if (token != LPAREN) {
                      nolparen:
+                       expression_dereference (expr,
+                                               "parse_expression: nolparen");
                        parse_warn ("left parenthesis expected.");
                        *lose = 1;
-                       return (struct expression *)0;
+                       return 0;
                }
 
-               rv = parse_data_expression (cfile, lose);
-               if (!rv) {
+               if (!parse_data_expression (&(*expr) -> data.substring.expr,
+                                           cfile, lose)) {
                      nodata:
+                       expression_dereference (expr,
+                                               "parse_expression: nodata");
                        parse_warn ("expecting data expression.");
                        skip_to_semi (cfile);
                        *lose = 1;
-                       return (struct expression *)0;
+                       return 0;
                }
 
                token = next_token (&val, cfile);
                if (token != COMMA) {
                      nocomma:
+                       expression_dereference (expr,
+                                               "parse_expression: nocomma1");
                        parse_warn ("comma expected.");
                        *lose = 1;
-                       return (struct expression *)0;
+
+                       return 0;
                }
 
-               left = parse_numeric_expression (cfile, lose);
-               if (!left) {
+               if (!parse_numeric_expression
+                   (&(*expr) -> data.substring.offset,cfile, lose)) {
                      nonum:
                        if (!*lose) {
                                parse_warn ("expecting numeric expression.");
                                skip_to_semi (cfile);
                                *lose = 1;
                        }
-                       return (struct expression *)0;
+                       expression_dereference (expr,
+                                               "parse_expression: nonum");
+                       return 0;
                }
 
                token = next_token (&val, cfile);
                if (token != COMMA)
                        goto nocomma;
 
-               right = parse_numeric_expression (cfile, lose);
-               if (!right)
+               if (!parse_numeric_expression
+                   (&(*expr) -> data.substring.len, cfile, lose))
                        goto nonum;
 
                token = next_token (&val, cfile);
@@ -1249,201 +1290,331 @@ struct expression *parse_data_expression (cfile, lose)
                      norparen:
                        parse_warn ("right parenthesis expected.");
                        *lose = 1;
-                       return (struct expression *)0;
+                       expression_dereference (expr,
+                                               "parse_expression: norparen");
+                       return 0;
                }
-               return make_substring (rv, left, right);
+               break;
 
              case SUFFIX:
                token = next_token (&val, cfile);
-               buf.op = expr_suffix;
+               if (!expression_allocate (expr, "parse_expression: SUFFIX"))
+                       error ("can't allocate expression");
+               (*expr) -> op = expr_suffix;
 
                token = next_token (&val, cfile);
                if (token != LPAREN)
                        goto nolparen;
 
-               buf.data.suffix.expr = parse_data_expression (cfile, lose);
-               if (!buf.data.suffix.expr)
+               if (!parse_data_expression (&(*expr) -> data.suffix.expr,
+                                           cfile, lose))
                        goto nodata;
 
                token = next_token (&val, cfile);
                if (token != COMMA)
                        goto nocomma;
 
-               buf.data.suffix.len = parse_numeric_expression (cfile, lose);
-               if (!buf.data.suffix.len)
+               if (!parse_data_expression (&(*expr) -> data.suffix.len,
+                                           cfile, lose))
                        goto nonum;
 
                token = next_token (&val, cfile);
                if (token != RPAREN)
                        goto norparen;
-               goto have_expr;
+               break;
 
              case OPTION:
                token = next_token (&val, cfile);
-               buf.op = expr_option;
-               buf.data.option = parse_option_name (cfile);
-               if (!buf.data.option) {
+               if (!expression_allocate (expr, "parse_expression: OPTION"))
+                       error ("can't allocate expression");
+               (*expr) -> op = expr_option;
+               (*expr) -> data.option = parse_option_name (cfile);
+               if (!(*expr) -> data.option) {
                        *lose = 1;
-                       return (struct expression *)0;
+                       expression_dereference (expr,
+                                               "parse_expression: OPTION");
+                       return 0;
                }
-               goto have_expr;
+               break;
 
              case HARDWARE:
                token = next_token (&val, cfile);
-               buf.op = expr_hardware;
-               goto have_expr;
+               if (!expression_allocate (expr, "parse_expression: HARDWARE"))
+                       error ("can't allocate expression");
+               (*expr) -> op = expr_hardware;
+               break;
 
              case PACKET:
                token = next_token (&val, cfile);
-               buf.op = expr_packet;
+               if (!expression_allocate (expr, "parse_expression: PACKET"))
+                       error ("can't allocate expression");
+               (*expr) -> op = expr_packet;
 
                token = next_token (&val, cfile);
                if (token != LPAREN)
                        goto nolparen;
 
-               buf.data.packet.offset =
-                       parse_numeric_expression (cfile, lose);
-               if (!buf.data.packet.offset)
+               if (!parse_numeric_expression (&(*expr) -> data.packet.offset,
+                                              cfile, lose))
                        goto nonum;
 
                token = next_token (&val, cfile);
                if (token != COMMA)
                        goto nocomma;
 
-               buf.data.packet.len =
-                       parse_numeric_expression (cfile, lose);
-               if (!buf.data.substring.len)
+               if (!parse_numeric_expression (&(*expr) -> data.packet.len,
+                                              cfile, lose))
                        goto nonum;
 
                token = next_token (&val, cfile);
                if (token != RPAREN)
                        goto norparen;
-               goto have_expr;
+               break;
                
              case STRING:
                token = next_token (&val, cfile);
-               return make_const_data (val, strlen (val), 1, 1);
-
-             case NUMBER:
-             case NUMBER_OR_NAME:
-               buf.op = expr_const_data;
-               memset (&buf.data, 0, sizeof buf.data);
-               buf.data.const_data.data =
-                       parse_cshl (cfile, &buf.data.const_data.len);
-               goto have_expr;
-
-             default:
-               return (struct expression *)0;
-       }
-
-      have_expr:
-       rv = (struct expression *)dmalloc (sizeof (struct expression),
-                                          "parse_boolean_expression");
-       if (!rv)
-               error ("No memory for boolean expression.");
-       *rv = buf;
-       return rv;
-}
-
-/*
- * numeric-expression :== EXTRACT_INT LPAREN data-expression
- *                                          COMMA number RPAREN |
- *                       NUMBER
- */
-
-struct expression *parse_numeric_expression (cfile, lose)
-       FILE *cfile;
-       int *lose;
-{
-       int token;
-       char *val;
-       struct collection *col;
-       struct expression buf, *rv;
-       struct expression *left, *right;
-       struct option *option;
-
-       token = peek_token (&val, cfile);
+               if (!make_const_data (expr, val, strlen (val), 1, 1))
+                       error ("can't make constant string expression.");
+               break;
 
-       switch (token) {
              case EXTRACT_INT:
                token = next_token (&val, cfile);       
-
                token = next_token (&val, cfile);
                if (token != LPAREN) {
                        parse_warn ("left parenthesis expected.");
                        *lose = 1;
-                       return (struct expression *)0;
+                       return 0;
                }
 
-               buf.data.extract_int.expr =
-                       parse_data_expression (cfile, lose);
-               if (!buf.data.extract_int.expr) {
+               if (!expression_allocate (expr,
+                                         "parse_expression: EXTRACT_INT"))
+                       error ("can't allocate expression");
+
+               if (!parse_data_expression (&(*expr) -> data.extract_int,
+                                           cfile, lose)) {
                        parse_warn ("expecting data expression.");
                        skip_to_semi (cfile);
                        *lose = 1;
-                       return (struct expression *)0;
+                       expression_dereference
+                               (expr, "parse_expression: EXTRACT_INT");
+                       return 0;
                }
 
                token = next_token (&val, cfile);
                if (token != COMMA) {
                        parse_warn ("comma expected.");
                        *lose = 1;
-                       return (struct expression *)0;
+                       return 0;
                }
 
                token = next_token (&val, cfile);
                if (token != NUMBER) {
                        parse_warn ("number expected.");
                        *lose = 1;
-                       return (struct expression *)0;
+                       return 0;
                }
-               buf.data.extract_int.width = (struct expression *)0;
                switch (atoi (val)) {
                      case 8:
-                       buf.op = expr_extract_int8;
+                       (*expr) -> op = expr_extract_int8;
                        break;
 
                      case 16:
-                       buf.op = expr_extract_int16;
+                       (*expr) -> op = expr_extract_int16;
                        break;
 
                      case 32:
-                       buf.op = expr_extract_int32;
+                       (*expr) -> op = expr_extract_int32;
                        break;
 
                      default:
                        parse_warn ("unsupported integer size %d", atoi (val));
                        *lose = 1;
                        skip_to_semi (cfile);
-                       return (struct expression *)0;
+                       expression_dereference
+                               (expr, "parse_expression: EXTRACT_INT");
+                       return 0;
                }
 
                token = next_token (&val, cfile);
                if (token != RPAREN) {
                        parse_warn ("right parenthesis expected.");
                        *lose = 1;
-                       return (struct expression *)0;
+                       return 0;
                }
-               goto have_expr;
+               break;
        
              case NUMBER:
-               buf.op = expr_const_int;
-               buf.data.const_int = atoi (val);
-               goto have_expr;
+               if (!expression_allocate (expr,
+                                         "parse_expression: NUMBER"))
+                       error ("can't allocate expression");
+
+               /* If we're in a numeric context, this should just be a
+                  number, by itself. */
+               if (context == context_numeric) {
+                       next_token (&val, cfile);       /* Eat the number. */
+                       (*expr) -> op = expr_const_int;
+                       (*expr) -> data.const_int = atoi (val);
+                       break;
+               }
 
+             case NUMBER_OR_NAME:
+               (*expr) -> op = expr_const_data;
+               if (!parse_cshl (&(*expr) -> data.const_data, cfile)) {
+                       expression_dereference (expr,
+                                               "parse_expression: cshl");
+                       return 0;
+               }
+               break;
+
+               /* Not a valid start to an expression... */
              default:
-               return (struct expression *)0;
+               return 0;
        }
-
-      have_expr:
-       rv = (struct expression *)dmalloc (sizeof (struct expression),
-                                          "parse_boolean_expression");
-       if (!rv)
-               error ("No memory for boolean expression.");
-       *rv = buf;
-       return rv;
+       return 1;
 }
 
+/* Parse an expression. */
+
+int parse_expression (expr, cfile, lose, context, plhs, binop)
+       struct expression **expr;
+       FILE *cfile;
+       int *lose;
+       enum expression_context context;
+       struct expression **plhs;
+       enum expr_op binop;
+{
+       enum dhcp_token token;
+       char *val;
+       struct expression *rhs = (struct expression *)0, *tmp;
+       struct expression *lhs;
+       enum expr_op next_op;
+
+       /* Consume the left hand side we were passed. */
+       if (plhs) {
+               lhs = *plhs;
+               *plhs = (struct expression *)0;
+       } else
+               lhs = (struct expression *)0;
+
+      new_rhs:
+       if (!parse_non_binary (&rhs, cfile, lose, context)) {
+               /* If we already have a left-hand side, then it's not
+                  okay for there not to be a right-hand side here, so
+                  we need to flag it as an error. */
+               if (lhs) {
+                       if (!*lose) {
+                               parse_warn ("expecting right-hand side.");
+                               *lose = 1;
+                               skip_to_semi (cfile);
+                       }
+                       expression_dereference (&lhs, "parse_expression");
+               }
+               return 0;
+       }
+
+       /* At this point, rhs contains either an entire subexpression,
+          or at least a left-hand-side.   If we do not see a binary token
+          as the next token, we're done with the expression. */
+
+       token = peek_token (&val, cfile);
+       switch (token) {
+             case EQUAL:
+               next_op = expr_equal;
+               break;
+
+             case AND:
+               next_op = expr_and;
+               break;
+
+             case OR:
+               next_op = expr_or;
+               break;
+
+             default:
+               next_op = expr_none;
+       }
+
+       /* If we have no lhs yet, we just parsed it. */
+       if (!lhs) {
+               /* If there was no operator following what we just parsed,
+                  then we're done - return it. */
+               if (next_op == expr_none) {
+                       *expr = rhs;
+                       return 1;
+               }
+               lhs = rhs;
+               rhs = (struct expression *)0;
+               binop = next_op;
+               next_token (&val, cfile);       /* Consume the operator. */
+               goto new_rhs;
+       }
+
+       /* Now, if we didn't find a binary operator, we're done parsing
+          this subexpression, so combine it with the preceding binary
+          operator and return the result. */
+       if (next_op == expr_none) {
+               if (!expression_allocate (expr,
+                                         "parse_expression: COMBINE"))
+                       error ("Can't allocate expression!");
+
+               (*expr) -> op = binop;
+               /* All the binary operators' data union members
+                  are the same, so we'll cheat and use the member
+                  for the equals operator. */
+               (*expr) -> data.equal [0] = lhs;
+               (*expr) -> data.equal [1] = rhs;
+               return 1;
+       }
+
+       /* Eat the operator token - we now know it was a binary operator... */
+       token = next_token (&val, cfile);
+
+       /* If the binary operator we saw previously has a lower precedence
+          than the next operator, then the rhs we just parsed for that
+          operator is actually the lhs of the operator with the higher
+          precedence - to get the real rhs, we need to recurse on the
+          new operator. */
+       if (binop != expr_none &&
+           op_precedence (binop, next_op) < 0) {
+               tmp = rhs;
+               rhs = (struct expression *)0;
+               if (!parse_expression (&rhs, cfile, lose, op_context (next_op),
+                                      &tmp, next_op)) {
+                       if (!*lose) {
+                               parse_warn ("expecting a subexpression");
+                               *lose = 1;
+                       }
+                       return 0;
+               }
+               next_op = expr_none;
+       }
+
+       /* Now combine the LHS and the RHS using binop. */
+       tmp = (struct expression *)0;
+       if (!expression_allocate (&tmp, "parse_expression: COMBINE2"))
+               error ("No memory for equal precedence combination.");
+       
+       /* Store the LHS and RHS. */
+       tmp -> data.equal [0] = lhs;
+       tmp -> data.equal [1] = rhs;
+       tmp -> op = binop;
+       
+       lhs = tmp;
+       tmp = (struct expression *)0;
+       rhs = (struct expression *)0;
+
+       /* Recursions don't return until we have parsed the end of the
+          expression, so if we recursed earlier, we can now return what
+          we got. */
+       if (next_op == expr_none) {
+               *expr = lhs;
+               return 1;
+       }
+
+       binop = next_op;
+       goto new_rhs;
+}      
+
 /* option-statement :== identifier DOT identifier <syntax> SEMI
                      | identifier <syntax> SEMI
 
@@ -1459,32 +1630,21 @@ struct executable_statement *parse_option_statement (cfile, lookups,
        enum statement_op op;
 {
        char *val;
-       int token;
+       enum dhcp_token token;
        char *fmt;
        struct expression *expr = (struct expression *)0;
+       struct expression *tmp;
        int lose;
        struct executable_statement *stmt;
+       int ftt = 1;
 
        token = peek_token (&val, cfile);
        if (token == SEMI) {
                /* Eat the semicolon... */
                token = next_token (&val, cfile);
-               expr = make_const_data (0, 0, 0, 0);
                goto done;
        }
 
-       /* See if there's a data expression, and if so, use it rather than
-          the standard format. */
-       expr = ((struct expression *)parse_data_expression (cfile, &lose));
-
-       /* Found a data expression, but it was bogus? */
-       if (lose)
-               return (struct executable_statement *)0;
-               
-       /* We found one. */
-       if (expr)
-               goto done;
-
        /* Parse the option data... */
        do {
                /* Set a flag if this is an array of a simple type (i.e.,
@@ -1495,8 +1655,16 @@ struct executable_statement *parse_option_statement (cfile, lookups,
                for (fmt = option -> format; *fmt; fmt++) {
                        if (*fmt == 'A')
                                break;
-                       expr = parse_option_token (cfile, fmt,
-                                                  expr, uniform, lookups);
+                       tmp = expr;
+                       if (!parse_option_token (&expr, cfile, fmt,
+                                                tmp, uniform, lookups)) {
+                               expression_dereference
+                                       (&tmp, "parse_option_statement");
+                               return (struct executable_statement *)0;
+                       }
+                       if (tmp)
+                               expression_dereference
+                                       (&tmp, "parse_option_statement");
                }
                if (*fmt == 'A') {
                        token = peek_token (&val, cfile);
@@ -1508,6 +1676,19 @@ struct executable_statement *parse_option_statement (cfile, lookups,
                }
        } while (*fmt == 'A');
 
+#if 0
+       goto done;
+
+      try_expr:
+       /* See if there's a data expression, and if so, use it rather than
+          the standard format. */
+       expr = parse_data_expression (cfile, &lose);
+
+       /* Found a data expression, but it was bogus? */
+       if (lose)
+               return (struct executable_statement *)0;
+               
+#endif /* 0 */
       done:
        token = next_token (&val, cfile);
        if (token != SEMI) {
@@ -1517,12 +1698,16 @@ struct executable_statement *parse_option_statement (cfile, lookups,
        }
        stmt = ((struct executable_statement *)
                dmalloc (sizeof *stmt, "parse_option_statement"));
+       memset (stmt, 0, sizeof *stmt);
        stmt -> op = op;
-       stmt -> data.option = option_cache (expr, option);
+       if (expr && !option_cache (&stmt -> data.option,
+                                  (struct data_string *)0, expr, option))
+               error ("no memory for option cache in parse_option_statement");
        return stmt;
 }
 
-struct expression *parse_option_token (cfile, fmt, expr, uniform, lookups)
+int parse_option_token (rv, cfile, fmt, expr, uniform, lookups)
+       struct expression **rv;
        FILE *cfile;
        char *fmt;
        struct expression *expr;
@@ -1530,8 +1715,8 @@ struct expression *parse_option_token (cfile, fmt, expr, uniform, lookups)
        int lookups;
 {
        char *val;
-       int token;
-       struct expression *t;
+       enum dhcp_token token;
+       struct expression *t = (struct expression *)0;
        unsigned char buf [4];
        int len;
        unsigned char *ob;
@@ -1541,21 +1726,20 @@ struct expression *parse_option_token (cfile, fmt, expr, uniform, lookups)
              case 'X':
                token = peek_token (&val, cfile);
                if (token == NUMBER_OR_NAME || token == NUMBER) {
-                       ob = parse_cshl (cfile, &len);
-                       return make_concat (expr,
-                                           make_const_data (ob, len, 0, 0));
+                       if (!expression_allocate (&t, "parse_option_token"))
+                               return 0;
+                       if (!parse_cshl (&t -> data.const_data, cfile))
+                               return 0;
                } else if (token == STRING) {
                        token = next_token (&val, cfile);
-                       return make_concat (expr,
-                                           make_const_data ((unsigned char *)
-                                                            val,
-                                                            strlen (val),
-                                                            1, 1));
+                       if (!make_const_data (&t, (unsigned char *) val,
+                                             strlen (val), 1, 1))
+                               error ("No memory for concatenation");
                } else {
                        parse_warn ("expecting string %s.",
                                    "or hexadecimal data");
                        skip_to_semi (cfile);
-                       return (struct expression *)0;
+                       return 0;
                }
                break;
                
@@ -1565,24 +1749,23 @@ struct expression *parse_option_token (cfile, fmt, expr, uniform, lookups)
                        parse_warn ("expecting string.");
                        if (token != SEMI)
                                skip_to_semi (cfile);
-                       return (struct expression *)0;
+                       return 0;
                }
-               return make_concat (expr,
-                                   make_const_data ((unsigned char *)
-                                                    val, strlen (val), 1, 1));
+               if (!make_const_data (&t, (unsigned char *)val,
+                                     strlen (val), 1, 1))
+                       error ("No memory for concatenation");
                break;
                
              case 'I': /* IP address or hostname. */
-               if (lookups)
-                       t = parse_ip_addr_or_hostname (cfile, uniform);
-               else {
+               if (lookups) {
+                       if (!parse_ip_addr_or_hostname (&t, cfile, uniform))
+                               return 0;
+               } else {
                        if (!parse_ip_addr (cfile, &addr))
-                               return (struct expression *)0;
-                       t = make_const_data (addr.iabuf, addr.len, 0, 1);
+                               return 0;
+                       if (!make_const_data (&t, addr.iabuf, addr.len, 0, 1))
+                               return 0;
                }
-               if (!t)
-                       return (struct expression *)0;
-               return make_concat (expr, t);
                break;
                
              case 'L': /* Unsigned 32-bit integer... */
@@ -1593,27 +1776,33 @@ struct expression *parse_option_token (cfile, fmt, expr, uniform, lookups)
                        parse_warn ("expecting number.");
                        if (token != SEMI)
                                skip_to_semi (cfile);
-                       return (struct expression *)0;
+                       return 0;
                }
                convert_num (buf, val, 0, 32);
-               return make_concat (expr, make_const_data (buf, 4, 0, 1));
+               if (!make_const_data (&t, buf, 4, 0, 1))
+                       return 0;
                break;
+
              case 's': /* Signed 16-bit integer. */
              case 'S': /* Unsigned 16-bit integer. */
                token = next_token (&val, cfile);
                if (token != NUMBER)
                        goto need_number;
                convert_num (buf, val, 0, 16);
-               return make_concat (expr, make_const_data (buf, 2, 0, 1));
+               if (!make_const_data (&t, buf, 2, 0, 1))
+                       return 0;
                break;
+
              case 'b': /* Signed 8-bit integer. */
              case 'B': /* Unsigned 8-bit integer. */
                token = next_token (&val, cfile);
                if (token != NUMBER)
                        goto need_number;
                convert_num (buf, val, 0, 8);
-               return make_concat (expr, make_const_data (buf, 1, 0, 1));
+               if (!make_const_data (&t, buf, 1, 0, 1))
+                       return 0;
                break;
+
              case 'f': /* Boolean flag. */
                token = next_token (&val, cfile);
                if (!is_identifier (token)) {
@@ -1621,7 +1810,7 @@ struct expression *parse_option_token (cfile, fmt, expr, uniform, lookups)
                      bad_flag:
                        if (token != SEMI)
                                skip_to_semi (cfile);
-                       return (struct expression *)0;
+                       return 0;
                }
                if (!strcasecmp (val, "true")
                    || !strcasecmp (val, "on"))
@@ -1633,12 +1822,21 @@ struct expression *parse_option_token (cfile, fmt, expr, uniform, lookups)
                        parse_warn ("expecting boolean.");
                        goto bad_flag;
                }
-               return make_concat (expr, make_const_data (buf, 1, 0, 1));
+               if (!make_const_data (&t, buf, 1, 0, 1))
+                       return 0;
                break;
+
              default:
                warn ("Bad format %c in parse_option_param.",
                      *fmt);
                skip_to_semi (cfile);
-               return (struct expression *)0;
+               return 0;
        }
+       if (expr) {
+               if (!make_concat (rv, expr, t))
+                       return 0;
+               expression_dereference (&t, "parse_option_token");
+       } else
+               *rv = t;
+       return 1;
 }