3 Parser for dhclient config and lease files... */
6 * Copyright (c) 1997 The Internet Software Consortium.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38 * Enterprises. To learn more about the Internet Software Consortium,
39 * see ``http://www.vix.com/isc''. To learn more about Vixie
40 * Enterprises, see ``http://www.vix.com''.
44 static char copyright[] =
45 "$Id: clparse.c,v 1.13.2.4 1999/03/29 21:21:37 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium. All rights reserved.\n";
51 struct client_config top_level_config;
53 /* client-conf-file :== client-declarations EOF
54 client-declarations :== <nil>
56 | client-declarations client-declaration */
58 int read_client_conf ()
63 struct client_config *config;
64 struct interface_info *ip;
66 new_parse (path_dhclient_conf);
68 /* Set up the initial dhcp option universe. */
69 initialize_universes ();
71 /* Initialize the top level client configuration. */
72 memset (&top_level_config, 0, sizeof top_level_config);
74 /* Set some defaults... */
75 top_level_config.timeout = 60;
76 top_level_config.select_interval = 0;
77 top_level_config.reboot_timeout = 10;
78 top_level_config.retry_interval = 300;
79 top_level_config.backoff_cutoff = 15;
80 top_level_config.initial_interval = 3;
81 top_level_config.bootp_policy = ACCEPT;
82 top_level_config.script_name = "/sbin/dhclient-script";
83 top_level_config.requested_options
84 [top_level_config.requested_option_count++] =
86 top_level_config.requested_options
87 [top_level_config.requested_option_count++] =
88 DHO_BROADCAST_ADDRESS;
89 top_level_config.requested_options
90 [top_level_config.requested_option_count++] =
92 top_level_config.requested_options
93 [top_level_config.requested_option_count++] =
95 top_level_config.requested_options
96 [top_level_config.requested_option_count++] =
98 top_level_config.requested_options
99 [top_level_config.requested_option_count++] =
100 DHO_DOMAIN_NAME_SERVERS;
101 top_level_config.requested_options
102 [top_level_config.requested_option_count++] =
105 if ((cfile = fopen (path_dhclient_conf, "r")) != NULL) {
107 token = peek_token (&val, cfile);
110 parse_client_statement (cfile,
111 (struct interface_info *)0,
114 token = next_token (&val, cfile); /* Clear the peek buffer */
118 /* Set up state and config structures for clients that don't
119 have per-interface configuration declarations. */
120 config = (struct client_config *)0;
121 for (ip = interfaces; ip; ip = ip -> next) {
123 ip -> client = (struct client_state *)
124 malloc (sizeof (struct client_state));
126 error ("no memory for client state.");
127 memset (ip -> client, 0, sizeof *(ip -> client));
129 if (!ip -> client -> config) {
131 config = (struct client_config *)
132 malloc (sizeof (struct client_config));
134 error ("no memory for client config.");
135 memcpy (config, &top_level_config,
136 sizeof top_level_config);
138 ip -> client -> config = config;
142 return !warnings_occurred;
145 /* lease-file :== client-lease-statements EOF
146 client-lease-statements :== <nil>
147 | client-lease-statements LEASE client-lease-statement */
149 void read_client_leases ()
155 new_parse (path_dhclient_db);
157 /* Open the lease file. If we can't open it, just return -
158 we can safely trust the server to remember our state. */
159 if ((cfile = fopen (path_dhclient_db, "r")) == NULL)
162 token = next_token (&val, cfile);
165 if (token != LEASE) {
166 warn ("Corrupt lease file - possible data loss!");
167 skip_to_semi (cfile);
170 parse_client_lease_statement (cfile, 0);
175 /* client-declaration :==
177 DEFAULT option-decl |
178 SUPERSEDE option-decl |
179 PREPEND option-decl |
181 hardware-declaration |
182 REQUEST option-list |
183 REQUIRE option-list |
187 SELECT_TIMEOUT number |
189 interface-declaration |
190 LEASE client-lease-statement |
191 ALIAS client-lease-statement */
193 void parse_client_statement (cfile, ip, config)
195 struct interface_info *ip;
196 struct client_config *config;
200 struct option *option;
202 switch (next_token (&val, cfile)) {
204 parse_option_decl (cfile, &config -> send_options [0]);
208 option = parse_option_decl (cfile, &config -> defaults [0]);
210 config -> default_actions [option -> code] =
215 option = parse_option_decl (cfile, &config -> defaults [0]);
217 config -> default_actions [option -> code] =
222 option = parse_option_decl (cfile, &config -> defaults [0]);
224 config -> default_actions [option -> code] =
229 option = parse_option_decl (cfile, &config -> defaults [0]);
231 config -> default_actions [option -> code] =
236 parse_string_list (cfile, &config -> media, 1);
241 parse_hardware_param (cfile, &ip -> hw_address);
243 parse_warn ("hardware address parameter %s",
244 "not allowed here.");
245 skip_to_semi (cfile);
250 config -> requested_option_count =
251 parse_option_list (cfile, config -> requested_options);
255 memset (config -> required_options, 0,
256 sizeof config -> required_options);
257 parse_option_list (cfile, config -> required_options);
261 parse_lease_time (cfile, &config -> timeout);
265 parse_lease_time (cfile, &config -> retry_interval);
269 parse_lease_time (cfile, &config -> select_interval);
273 parse_lease_time (cfile, &config -> reboot_timeout);
277 parse_lease_time (cfile, &config -> backoff_cutoff);
280 case INITIAL_INTERVAL:
281 parse_lease_time (cfile, &config -> initial_interval);
285 config -> script_name = parse_string (cfile);
290 parse_warn ("nested interface declaration.");
291 parse_interface_declaration (cfile, config);
295 parse_client_lease_statement (cfile, 1);
299 parse_client_lease_statement (cfile, 2);
303 parse_reject_statement (cfile, config);
307 parse_warn ("expecting a statement.");
308 skip_to_semi (cfile);
311 token = next_token (&val, cfile);
313 parse_warn ("semicolon expected.");
314 skip_to_semi (cfile);
318 int parse_X (cfile, buf, max)
327 token = peek_token (&val, cfile);
328 if (token == NUMBER_OR_NAME || token == NUMBER) {
331 token = next_token (&val, cfile);
332 if (token != NUMBER && token != NUMBER_OR_NAME) {
333 parse_warn ("expecting hexadecimal constant.");
334 skip_to_semi (cfile);
337 convert_num (&buf [len], val, 16, 8);
339 parse_warn ("hexadecimal constant too long.");
340 skip_to_semi (cfile);
343 token = peek_token (&val, cfile);
345 token = next_token (&val, cfile);
346 } while (token == COLON);
348 } else if (token == STRING) {
349 token = next_token (&val, cfile);
352 parse_warn ("string constant too long.");
353 skip_to_semi (cfile);
356 memcpy (buf, val, len + 1);
358 parse_warn ("expecting string or hexadecimal data");
359 skip_to_semi (cfile);
365 /* option-list :== option_name |
366 option_list COMMA option_name */
368 int parse_option_list (cfile, list)
378 token = next_token (&val, cfile);
379 if (!is_identifier (token)) {
380 parse_warn ("expected option name.");
381 skip_to_semi (cfile);
384 for (i = 0; i < 256; i++) {
385 if (!strcasecmp (dhcp_options [i].name, val))
389 parse_warn ("%s: expected option name.");
390 skip_to_semi (cfile);
395 parse_warn ("%s: too many options.", val);
396 skip_to_semi (cfile);
399 token = next_token (&val, cfile);
400 } while (token == COMMA);
402 parse_warn ("expecting semicolon.");
403 skip_to_semi (cfile);
409 /* interface-declaration :==
410 INTERFACE string LBRACE client-declarations RBRACE */
412 void parse_interface_declaration (cfile, outer_config)
414 struct client_config *outer_config;
419 struct interface_info *ip;
421 token = next_token (&val, cfile);
422 if (token != STRING) {
423 parse_warn ("expecting interface name (in quotes).");
424 skip_to_semi (cfile);
428 ip = interface_or_dummy (val);
431 make_client_state (ip);
433 if (!ip -> client -> config)
434 make_client_config (ip, outer_config);
436 ip -> flags &= ~INTERFACE_AUTOMATIC;
437 interfaces_requested = 1;
439 token = next_token (&val, cfile);
440 if (token != LBRACE) {
441 parse_warn ("expecting left brace.");
442 skip_to_semi (cfile);
447 token = peek_token (&val, cfile);
449 parse_warn ("unterminated interface declaration.");
454 parse_client_statement (cfile, ip, ip -> client -> config);
456 token = next_token (&val, cfile);
459 struct interface_info *interface_or_dummy (name)
462 struct interface_info *ip;
464 /* Find the interface (if any) that matches the name. */
465 for (ip = interfaces; ip; ip = ip -> next) {
466 if (!strcmp (ip -> name, name))
470 /* If it's not a real interface, see if it's on the dummy list. */
472 for (ip = dummy_interfaces; ip; ip = ip -> next) {
473 if (!strcmp (ip -> name, name))
478 /* If we didn't find an interface, make a dummy interface as
481 ip = ((struct interface_info *)malloc (sizeof *ip));
483 error ("Insufficient memory to record interface %s",
485 memset (ip, 0, sizeof *ip);
486 strcpy (ip -> name, name);
487 ip -> next = dummy_interfaces;
488 dummy_interfaces = ip;
493 void make_client_state (ip)
494 struct interface_info *ip;
497 ((struct client_state *)malloc (sizeof *(ip -> client)));
499 error ("no memory for state on %s\n", ip -> name);
500 memset (ip -> client, 0, sizeof *(ip -> client));
503 void make_client_config (ip, config)
504 struct interface_info *ip;
505 struct client_config *config;
507 ip -> client -> config =
508 ((struct client_config *)
509 malloc (sizeof (struct client_config)));
510 if (!ip -> client -> config)
511 error ("no memory for config for %s\n", ip -> name);
512 memset (ip -> client -> config, 0,
513 sizeof *(ip -> client -> config));
514 memcpy (ip -> client -> config, config, sizeof *config);
517 /* client-lease-statement :==
518 RBRACE client-lease-declarations LBRACE
520 client-lease-declarations :==
522 client-lease-declaration |
523 client-lease-declarations client-lease-declaration */
526 void parse_client_lease_statement (cfile, is_static)
530 struct client_lease *lease, *lp, *pl;
531 struct interface_info *ip;
535 token = next_token (&val, cfile);
536 if (token != LBRACE) {
537 parse_warn ("expecting left brace.");
538 skip_to_semi (cfile);
542 lease = (struct client_lease *)malloc (sizeof (struct client_lease));
544 error ("no memory for lease.\n");
545 memset (lease, 0, sizeof *lease);
546 lease -> is_static = is_static;
548 ip = (struct interface_info *)0;
551 token = peek_token (&val, cfile);
553 parse_warn ("unterminated lease declaration.");
558 parse_client_lease_declaration (cfile, lease, &ip);
560 token = next_token (&val, cfile);
562 /* If the lease declaration didn't include an interface
563 declaration that we recognized, it's of no use to us. */
565 free_client_lease (lease);
569 /* Make sure there's a client state structure... */
571 make_client_state (ip);
573 /* If this is an alias lease, it doesn't need to be sorted in. */
574 if (is_static == 2) {
575 ip -> client -> alias = lease;
579 /* The new lease may supersede a lease that's not the
580 active lease but is still on the lease list, so scan the
581 lease list looking for a lease with the same address, and
582 if we find it, toss it. */
583 pl = (struct client_lease *)0;
584 for (lp = ip -> client -> leases; lp; lp = lp -> next) {
585 if (lp -> address.len == lease -> address.len &&
586 !memcmp (lp -> address.iabuf, lease -> address.iabuf,
587 lease -> address.len)) {
589 pl -> next = lp -> next;
591 ip -> client -> leases = lp -> next;
592 free_client_lease (lp);
597 /* If this is a preloaded lease, just put it on the list of recorded
598 leases - don't make it the active lease. */
600 lease -> next = ip -> client -> leases;
601 ip -> client -> leases = lease;
605 /* The last lease in the lease file on a particular interface is
606 the active lease for that interface. Of course, we don't know
607 what the last lease in the file is until we've parsed the whole
608 file, so at this point, we assume that the lease we just parsed
609 is the active lease for its interface. If there's already
610 an active lease for the interface, and this lease is for the same
611 ip address, then we just toss the old active lease and replace
612 it with this one. If this lease is for a different address,
613 then if the old active lease has expired, we dump it; if not,
614 we put it on the list of leases for this interface which are
615 still valid but no longer active. */
616 if (ip -> client -> active) {
617 if (ip -> client -> active -> expiry < cur_time)
618 free_client_lease (ip -> client -> active);
619 else if (ip -> client -> active -> address.len ==
620 lease -> address.len &&
621 !memcmp (ip -> client -> active -> address.iabuf,
622 lease -> address.iabuf,
623 lease -> address.len))
624 free_client_lease (ip -> client -> active);
626 ip -> client -> active -> next =
627 ip -> client -> leases;
628 ip -> client -> leases = ip -> client -> active;
631 ip -> client -> active = lease;
636 /* client-lease-declaration :==
639 FIXED_ADDR ip_address |
647 void parse_client_lease_declaration (cfile, lease, ipp)
649 struct client_lease *lease;
650 struct interface_info **ipp;
654 struct interface_info *ip;
656 switch (next_token (&val, cfile)) {
658 lease -> is_bootp = 1;
662 token = next_token (&val, cfile);
663 if (token != STRING) {
664 parse_warn ("expecting interface name (in quotes).");
665 skip_to_semi (cfile);
668 ip = interface_or_dummy (val);
673 if (!parse_ip_addr (cfile, &lease -> address))
678 parse_string_list (cfile, &lease -> medium, 0);
682 lease -> filename = parse_string (cfile);
686 lease -> server_name = parse_string (cfile);
690 lease -> renewal = parse_date (cfile);
694 lease -> rebind = parse_date (cfile);
698 lease -> expiry = parse_date (cfile);
702 parse_option_decl (cfile, lease -> options);
706 parse_warn ("expecting lease declaration.");
707 skip_to_semi (cfile);
710 token = next_token (&val, cfile);
712 parse_warn ("expecting semicolon.");
713 skip_to_semi (cfile);
717 struct option *parse_option_decl (cfile, options)
719 struct option_data *options;
724 u_int8_t hunkbuf [1024];
728 struct universe *universe;
729 struct option *option;
730 struct iaddr ip_addr;
735 token = next_token (&val, cfile);
736 if (!is_identifier (token)) {
737 parse_warn ("expecting identifier after option keyword.");
739 skip_to_semi (cfile);
740 return (struct option *)0;
742 vendor = malloc (strlen (val) + 1);
744 error ("no memory for vendor information.");
745 strcpy (vendor, val);
746 token = peek_token (&val, cfile);
748 /* Go ahead and take the DOT token... */
749 token = next_token (&val, cfile);
751 /* The next token should be an identifier... */
752 token = next_token (&val, cfile);
753 if (!is_identifier (token)) {
754 parse_warn ("expecting identifier after '.'");
756 skip_to_semi (cfile);
757 return (struct option *)0;
760 /* Look up the option name hash table for the specified
762 universe = ((struct universe *)
763 hash_lookup (&universe_hash,
764 (unsigned char *)vendor, 0));
765 /* If it's not there, we can't parse the rest of the
768 parse_warn ("no vendor named %s.", vendor);
769 skip_to_semi (cfile);
770 return (struct option *)0;
773 /* Use the default hash table, which contains all the
774 standard dhcp option names. */
776 universe = &dhcp_universe;
779 /* Look up the actual option info... */
780 option = (struct option *)hash_lookup (universe -> hash,
781 (unsigned char *)val, 0);
783 /* If we didn't get an option structure, it's an undefined option. */
786 parse_warn ("no option named %s", val);
788 parse_warn ("no option named %s for vendor %s",
790 skip_to_semi (cfile);
791 return (struct option *)0;
794 /* Free the initial identifier token. */
797 /* Parse the option data... */
799 for (fmt = option -> format; *fmt; fmt++) {
804 len = parse_X (cfile, &hunkbuf [hunkix],
805 sizeof hunkbuf - hunkix);
809 case 't': /* Text string... */
810 token = next_token (&val, cfile);
811 if (token != STRING) {
812 parse_warn ("expecting string.");
813 skip_to_semi (cfile);
814 return (struct option *)0;
817 if (hunkix + len + 1 > sizeof hunkbuf) {
818 parse_warn ("option data buffer %s",
820 skip_to_semi (cfile);
821 return (struct option *)0;
823 memcpy (&hunkbuf [hunkix], val, len + 1);
828 case 'I': /* IP address. */
829 if (!parse_ip_addr (cfile, &ip_addr))
830 return (struct option *)0;
835 if (hunkix + len > sizeof hunkbuf) {
836 parse_warn ("option data buffer %s",
838 skip_to_semi (cfile);
839 return (struct option *)0;
841 memcpy (&hunkbuf [hunkix], dp, len);
845 case 'L': /* Unsigned 32-bit integer... */
846 case 'l': /* Signed 32-bit integer... */
847 token = next_token (&val, cfile);
848 if (token != NUMBER) {
850 parse_warn ("expecting number.");
852 skip_to_semi (cfile);
853 return (struct option *)0;
855 convert_num (buf, val, 0, 32);
860 case 's': /* Signed 16-bit integer. */
861 case 'S': /* Unsigned 16-bit integer. */
862 token = next_token (&val, cfile);
865 convert_num (buf, val, 0, 16);
870 case 'b': /* Signed 8-bit integer. */
871 case 'B': /* Unsigned 8-bit integer. */
872 token = next_token (&val, cfile);
875 convert_num (buf, val, 0, 8);
880 case 'f': /* Boolean flag. */
881 token = next_token (&val, cfile);
882 if (!is_identifier (token)) {
883 parse_warn ("expecting identifier.");
886 skip_to_semi (cfile);
887 return (struct option *)0;
889 if (!strcasecmp (val, "true")
890 || !strcasecmp (val, "on"))
892 else if (!strcasecmp (val, "false")
893 || !strcasecmp (val, "off"))
896 parse_warn ("expecting boolean.");
904 warn ("Bad format %c in parse_option_param.",
906 skip_to_semi (cfile);
907 return (struct option *)0;
910 token = next_token (&val, cfile);
911 } while (*fmt == 'A' && token == COMMA);
914 parse_warn ("semicolon expected.");
915 skip_to_semi (cfile);
916 return (struct option *)0;
919 options [option -> code].data =
920 (unsigned char *)malloc (hunkix + nul_term);
921 if (!options [option -> code].data)
922 error ("out of memory allocating option data.");
923 memcpy (options [option -> code].data, hunkbuf, hunkix + nul_term);
924 options [option -> code].len = hunkix;
928 void parse_string_list (cfile, lp, multiple)
930 struct string_list **lp;
935 struct string_list *cur, *tmp;
937 /* Find the last medium in the media list. */
939 for (cur = *lp; cur -> next; cur = cur -> next)
942 cur = (struct string_list *)0;
946 token = next_token (&val, cfile);
947 if (token != STRING) {
948 parse_warn ("Expecting media options.");
949 skip_to_semi (cfile);
953 tmp = (struct string_list *)malloc (strlen (val) + 1 +
955 (struct string_list *));
957 error ("no memory for string list entry.");
959 strcpy (tmp -> string, val);
960 tmp -> next = (struct string_list *)0;
962 /* Store this medium at the end of the media list. */
969 token = next_token (&val, cfile);
970 } while (multiple && token == COMMA);
973 parse_warn ("expecting semicolon.");
974 skip_to_semi (cfile);
978 void parse_reject_statement (cfile, config)
980 struct client_config *config;
985 struct iaddrlist *list;
988 if (!parse_ip_addr (cfile, &addr)) {
989 parse_warn ("expecting IP address.");
990 skip_to_semi (cfile);
994 list = (struct iaddrlist *)malloc (sizeof (struct iaddrlist));
996 error ("no memory for reject list!");
999 list -> next = config -> reject_list;
1000 config -> reject_list = list;
1002 token = next_token (&val, cfile);
1003 } while (token == COMMA);
1005 if (token != SEMI) {
1006 parse_warn ("expecting semicolon.");
1007 skip_to_semi (cfile);