1 /* $OpenBSD: src/sbin/dhclient/clparse.c,v 1.41 2012/10/27 23:08:53 krw Exp $ */
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''.
47 * client-conf-file :== client-declarations EOF
48 * client-declarations :== <nil>
49 * | client-declaration
50 * | client-declarations client-declaration
53 read_client_conf(void)
58 new_parse(path_dhclient_conf
);
60 /* Set some defaults... */
61 config
->link_timeout
= 30;
63 config
->select_interval
= 0;
64 config
->reboot_timeout
= 10;
65 config
->retry_interval
= 300;
66 config
->backoff_cutoff
= 15;
67 config
->initial_interval
= 3;
68 config
->bootp_policy
= ACCEPT
;
69 config
->script_name
= _PATH_DHCLIENT_SCRIPT
;
70 config
->requested_options
71 [config
->requested_option_count
++] = DHO_SUBNET_MASK
;
72 config
->requested_options
73 [config
->requested_option_count
++] = DHO_BROADCAST_ADDRESS
;
74 config
->requested_options
75 [config
->requested_option_count
++] = DHO_TIME_OFFSET
;
76 config
->requested_options
77 [config
->requested_option_count
++] = DHO_ROUTERS
;
78 config
->requested_options
79 [config
->requested_option_count
++] = DHO_DOMAIN_NAME
;
80 config
->requested_options
81 [config
->requested_option_count
++] = DHO_DOMAIN_NAME_SERVERS
;
82 config
->requested_options
83 [config
->requested_option_count
++] = DHO_HOST_NAME
;
85 if ((cfile
= fopen(path_dhclient_conf
, "r")) != NULL
) {
87 token
= peek_token(NULL
, cfile
);
90 parse_client_statement(cfile
);
92 token
= next_token(NULL
, cfile
); /* Clear the peek buffer */
96 return (!warnings_occurred
);
100 * lease-file :== client-lease-statements EOF
101 * client-lease-statements :== <nil>
102 * | client-lease-statements LEASE client-lease-statement
105 read_client_leases(void)
110 new_parse(path_dhclient_db
);
112 /* Open the lease file. If we can't open it, just return -
113 we can safely trust the server to remember our state. */
114 if ((cfile
= fopen(path_dhclient_db
, "r")) == NULL
)
117 token
= next_token(NULL
, cfile
);
120 if (token
!= TOK_LEASE
) {
121 warning("Corrupt lease file - possible data loss!");
125 parse_client_lease_statement(cfile
, 0);
132 * client-declaration :==
133 * TOK_SEND option-decl |
134 * TOK_DEFAULT option-decl |
135 * TOK_SUPERSEDE option-decl |
136 * TOK_APPEND option-decl |
137 * TOK_PREPEND option-decl |
138 * TOK_MEDIA string-list |
139 * hardware-declaration |
140 * TOK_REQUEST option-list |
141 * TOK_REQUIRE option-list |
142 * TOK_TIMEOUT number |
144 * TOK_SELECT_TIMEOUT number |
145 * TOK_REBOOT number |
146 * TOK_BACKOFF_CUTOFF number |
147 * TOK_INITIAL_INTERVAL number |
148 * TOK_SCRIPT string |
149 * interface-declaration |
150 * TOK_LEASE client-lease-statement |
151 * TOK_ALIAS client-lease-statement |
152 * TOK_REJECT reject-statement
155 parse_client_statement(FILE *cfile
)
157 u_int8_t ignorelist
[256];
158 int token
, code
, count
, i
;
160 switch (next_token(NULL
, cfile
)) {
162 parse_option_decl(cfile
, &config
->send_options
[0]);
165 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
167 config
->default_actions
[code
] = ACTION_DEFAULT
;
170 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
172 config
->default_actions
[code
] = ACTION_SUPERSEDE
;
175 count
= parse_option_list(cfile
, ignorelist
);
176 for (i
= 0; i
< count
; i
++)
177 config
->default_actions
[ignorelist
[i
]] = ACTION_IGNORE
;
180 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
182 config
->default_actions
[code
] = ACTION_APPEND
;
185 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
187 config
->default_actions
[code
] = ACTION_PREPEND
;
193 parse_hardware_param(cfile
, &ifi
->hw_address
);
196 config
->requested_option_count
=
197 parse_option_list(cfile
, config
->requested_options
);
200 memset(config
->required_options
, 0,
201 sizeof(config
->required_options
));
202 parse_option_list(cfile
, config
->required_options
);
204 case TOK_LINK_TIMEOUT
:
205 parse_lease_time(cfile
, &config
->link_timeout
);
208 parse_lease_time(cfile
, &config
->timeout
);
211 parse_lease_time(cfile
, &config
->retry_interval
);
213 case TOK_SELECT_TIMEOUT
:
214 parse_lease_time(cfile
, &config
->select_interval
);
217 parse_lease_time(cfile
, &config
->reboot_timeout
);
219 case TOK_BACKOFF_CUTOFF
:
220 parse_lease_time(cfile
, &config
->backoff_cutoff
);
222 case TOK_INITIAL_INTERVAL
:
223 parse_lease_time(cfile
, &config
->initial_interval
);
226 config
->script_name
= parse_string(cfile
);
229 parse_interface_declaration(cfile
);
232 parse_client_lease_statement(cfile
, 1);
238 parse_reject_statement(cfile
);
241 parse_warn("expecting a statement.");
245 token
= next_token(NULL
, cfile
);
247 parse_warn("semicolon expected.");
253 parse_X(FILE *cfile
, u_int8_t
*buf
, int max
)
259 token
= peek_token(&val
, cfile
);
260 if (token
== TOK_NUMBER_OR_NAME
|| token
== TOK_NUMBER
) {
263 token
= next_token(&val
, cfile
);
264 if (token
!= TOK_NUMBER
&& token
!= TOK_NUMBER_OR_NAME
) {
265 parse_warn("expecting hexadecimal constant.");
269 convert_num(&buf
[len
], val
, 16, 8);
271 parse_warn("hexadecimal constant too long.");
275 token
= peek_token(&val
, cfile
);
277 token
= next_token(&val
, cfile
);
278 } while (token
== ':');
280 } else if (token
== TOK_STRING
) {
281 token
= next_token(&val
, cfile
);
284 parse_warn("string constant too long.");
288 memcpy(buf
, val
, len
+ 1);
290 parse_warn("expecting string or hexadecimal data");
298 * option-list :== option_name |
299 * option_list COMMA option_name
302 parse_option_list(FILE *cfile
, u_int8_t
*list
)
310 token
= next_token(&val
, cfile
);
311 if (!is_identifier(token
)) {
312 parse_warn("expected option name.");
316 for (i
= 0; i
< 256; i
++)
317 if (!strcasecmp(dhcp_options
[i
].name
, val
))
321 parse_warn("%s: unexpected option name.", val
);
327 parse_warn("%s: too many options.", val
);
331 token
= next_token(&val
, cfile
);
332 } while (token
== ',');
334 parse_warn("expecting semicolon.");
342 * interface-declaration :==
343 * INTERFACE string LBRACE client-declarations RBRACE
346 parse_interface_declaration(FILE *cfile
)
351 token
= next_token(&val
, cfile
);
352 if (token
!= TOK_STRING
) {
353 parse_warn("expecting interface name (in quotes).");
358 if (strcmp(ifi
->name
, val
) != 0) {
363 token
= next_token(&val
, cfile
);
365 parse_warn("expecting left brace.");
371 token
= peek_token(&val
, cfile
);
373 parse_warn("unterminated interface declaration.");
378 parse_client_statement(cfile
);
380 token
= next_token(&val
, cfile
);
384 * client-lease-statement :==
385 * RBRACE client-lease-declarations LBRACE
387 * client-lease-declarations :==
389 * client-lease-declaration |
390 * client-lease-declarations client-lease-declaration
393 parse_client_lease_statement(FILE *cfile
, int is_static
)
395 struct client_lease
*lease
, *lp
, *pl
;
398 token
= next_token(NULL
, cfile
);
400 parse_warn("expecting left brace.");
405 lease
= malloc(sizeof(struct client_lease
));
407 error("no memory for lease.");
408 memset(lease
, 0, sizeof(*lease
));
409 lease
->is_static
= is_static
;
412 token
= peek_token(NULL
, cfile
);
414 parse_warn("unterminated lease declaration.");
419 parse_client_lease_declaration(cfile
, lease
);
421 token
= next_token(NULL
, cfile
);
423 /* If the lease declaration didn't include an interface
424 * declaration that we recognized, it's of no use to us.
427 free_client_lease(lease
);
432 * The new lease may supersede a lease that's not the active
433 * lease but is still on the lease list, so scan the lease list
434 * looking for a lease with the same address, and if we find it,
438 for (lp
= client
->leases
; lp
; lp
= lp
->next
) {
439 if (addr_eq(lp
->address
, lease
->address
)) {
443 client
->leases
= lp
->next
;
444 free_client_lease(lp
);
451 * If this is a preloaded lease, just put it on the list of
452 * recorded leases - don't make it the active lease.
455 lease
->next
= client
->leases
;
456 client
->leases
= lease
;
461 * The last lease in the lease file on a particular interface is
462 * the active lease for that interface. Of course, we don't
463 * know what the last lease in the file is until we've parsed
464 * the whole file, so at this point, we assume that the lease we
465 * just parsed is the active lease for its interface. If
466 * there's already an active lease for the interface, and this
467 * lease is for the same ip address, then we just toss the old
468 * active lease and replace it with this one. If this lease is
469 * for a different address, then if the old active lease has
470 * expired, we dump it; if not, we put it on the list of leases
471 * for this interface which are still valid but no longer
474 if (client
->active
) {
475 if (client
->active
->expiry
< time(NULL
))
476 free_client_lease(client
->active
);
477 else if (addr_eq(client
->active
->address
, lease
->address
))
478 free_client_lease(client
->active
);
480 client
->active
->next
= client
->leases
;
481 client
->leases
= client
->active
;
484 client
->active
= lease
;
490 * client-lease-declaration :==
493 * FIXED_ADDR ip_address |
495 * SERVER_NAME string |
496 * OPTION option-decl |
502 parse_client_lease_declaration(FILE *cfile
, struct client_lease
*lease
)
507 switch (next_token(&val
, cfile
)) {
512 token
= next_token(&val
, cfile
);
513 if (token
!= TOK_STRING
) {
514 parse_warn("expecting interface name (in quotes).");
518 if (strcmp(ifi
->name
, val
) != 0) {
519 parse_warn("wrong interface name. Expecting '%s'.",
526 if (!parse_ip_addr(cfile
, &lease
->address
))
533 lease
->filename
= parse_string(cfile
);
535 case TOK_SERVER_NAME
:
536 lease
->server_name
= parse_string(cfile
);
539 lease
->renewal
= parse_date(cfile
);
542 lease
->rebind
= parse_date(cfile
);
545 lease
->expiry
= parse_date(cfile
);
548 parse_option_decl(cfile
, lease
->options
);
551 parse_warn("expecting lease declaration.");
555 token
= next_token(&val
, cfile
);
557 parse_warn("expecting semicolon.");
563 parse_option_decl(FILE *cfile
, struct option_data
*options
)
568 u_int8_t hunkbuf
[1024];
571 struct iaddr ip_addr
;
576 token
= next_token(&val
, cfile
);
577 if (!is_identifier(token
)) {
578 parse_warn("expecting identifier after option keyword.");
584 /* Look up the actual option info. */
586 for (code
= 0; code
< 256; code
++)
587 if (strcmp(dhcp_options
[code
].name
, val
) == 0)
591 parse_warn("no option named %s", val
);
596 /* Parse the option data... */
598 for (fmt
= dhcp_options
[code
].format
; *fmt
; fmt
++) {
603 len
= parse_X(cfile
, &hunkbuf
[hunkix
],
604 sizeof(hunkbuf
) - hunkix
);
607 case 't': /* Text string... */
608 token
= next_token(&val
, cfile
);
609 if (token
!= TOK_STRING
) {
610 parse_warn("expecting string.");
615 if (hunkix
+ len
+ 1 > sizeof(hunkbuf
)) {
616 parse_warn("option data buffer %s",
621 memcpy(&hunkbuf
[hunkix
], val
, len
+ 1);
625 case 'I': /* IP address. */
626 if (!parse_ip_addr(cfile
, &ip_addr
))
631 if (hunkix
+ len
> sizeof(hunkbuf
)) {
632 parse_warn("option data buffer "
637 memcpy(&hunkbuf
[hunkix
], dp
, len
);
640 case 'L': /* Unsigned 32-bit integer... */
641 case 'l': /* Signed 32-bit integer... */
642 token
= next_token(&val
, cfile
);
643 if (token
!= TOK_NUMBER
) {
645 parse_warn("expecting number.");
650 convert_num(buf
, val
, 0, 32);
654 case 's': /* Signed 16-bit integer. */
655 case 'S': /* Unsigned 16-bit integer. */
656 token
= next_token(&val
, cfile
);
657 if (token
!= TOK_NUMBER
)
659 convert_num(buf
, val
, 0, 16);
663 case 'b': /* Signed 8-bit integer. */
664 case 'B': /* Unsigned 8-bit integer. */
665 token
= next_token(&val
, cfile
);
666 if (token
!= TOK_NUMBER
)
668 convert_num(buf
, val
, 0, 8);
672 case 'f': /* Boolean flag. */
673 token
= next_token(&val
, cfile
);
674 if (!is_identifier(token
)) {
675 parse_warn("expecting identifier.");
681 if (!strcasecmp(val
, "true") ||
682 !strcasecmp(val
, "on"))
684 else if (!strcasecmp(val
, "false") ||
685 !strcasecmp(val
, "off"))
688 parse_warn("expecting boolean.");
695 warning("Bad format %c in parse_option_param.",
701 token
= next_token(&val
, cfile
);
702 } while (*fmt
== 'A' && token
== ',');
705 parse_warn("semicolon expected.");
710 options
[code
].data
= malloc(hunkix
+ nul_term
);
711 if (!options
[code
].data
)
712 error("out of memory allocating option data.");
713 memcpy(options
[code
].data
, hunkbuf
, hunkix
+ nul_term
);
714 options
[code
].len
= hunkix
;
719 parse_reject_statement(FILE *cfile
)
721 struct iaddrlist
*list
;
726 if (!parse_ip_addr(cfile
, &addr
)) {
727 parse_warn("expecting IP address.");
732 list
= malloc(sizeof(struct iaddrlist
));
734 error("no memory for reject list!");
737 list
->next
= config
->reject_list
;
738 config
->reject_list
= list
;
740 token
= next_token(NULL
, cfile
);
741 } while (token
== ',');
744 parse_warn("expecting semicolon.");