1 /* $OpenBSD: clparse.c,v 1.34 2007/02/14 23:19:26 deraadt Exp $ */
2 /* $DragonFly: src/sbin/dhclient/clparse.c,v 1.1 2008/08/30 16:07:58 hasso Exp $ */
4 /* Parser for dhclient config and lease files... */
7 * Copyright (c) 1997 The Internet Software Consortium.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of The Internet Software Consortium nor the names
20 * of its contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
24 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
31 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
32 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * This software has been written for the Internet Software Consortium
38 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
39 * Enterprises. To learn more about the Internet Software Consortium,
40 * see ``http://www.vix.com/isc''. To learn more about Vixie
41 * Enterprises, see ``http://www.vix.com''.
48 * client-conf-file :== client-declarations EOF
49 * client-declarations :== <nil>
50 * | client-declaration
51 * | client-declarations client-declaration
54 read_client_conf(void)
60 new_parse(path_dhclient_conf
);
62 /* Set some defaults... */
63 config
->link_timeout
= 10;
65 config
->select_interval
= 0;
66 config
->reboot_timeout
= 10;
67 config
->retry_interval
= 300;
68 config
->backoff_cutoff
= 15;
69 config
->initial_interval
= 3;
70 config
->bootp_policy
= ACCEPT
;
71 config
->script_name
= _PATH_DHCLIENT_SCRIPT
;
72 config
->requested_options
73 [config
->requested_option_count
++] = DHO_SUBNET_MASK
;
74 config
->requested_options
75 [config
->requested_option_count
++] = DHO_BROADCAST_ADDRESS
;
76 config
->requested_options
77 [config
->requested_option_count
++] = DHO_TIME_OFFSET
;
78 config
->requested_options
79 [config
->requested_option_count
++] = DHO_ROUTERS
;
80 config
->requested_options
81 [config
->requested_option_count
++] = DHO_DOMAIN_NAME
;
82 config
->requested_options
83 [config
->requested_option_count
++] = DHO_DOMAIN_NAME_SERVERS
;
84 config
->requested_options
85 [config
->requested_option_count
++] = DHO_HOST_NAME
;
87 if ((cfile
= fopen(path_dhclient_conf
, "r")) != NULL
) {
89 token
= peek_token(&val
, cfile
);
92 parse_client_statement(cfile
);
94 token
= next_token(&val
, cfile
); /* Clear the peek buffer */
98 return (!warnings_occurred
);
102 * lease-file :== client-lease-statements EOF
103 * client-lease-statements :== <nil>
104 * | client-lease-statements LEASE client-lease-statement
107 read_client_leases(void)
113 new_parse(path_dhclient_db
);
115 /* Open the lease file. If we can't open it, just return -
116 we can safely trust the server to remember our state. */
117 if ((cfile
= fopen(path_dhclient_db
, "r")) == NULL
)
120 token
= next_token(&val
, cfile
);
123 if (token
!= TOK_LEASE
) {
124 warning("Corrupt lease file - possible data loss!");
128 parse_client_lease_statement(cfile
, 0);
135 * client-declaration :==
136 * TOK_SEND option-decl |
137 * TOK_DEFAULT option-decl |
138 * TOK_SUPERSEDE option-decl |
139 * TOK_APPEND option-decl |
140 * TOK_PREPEND option-decl |
141 * TOK_MEDIA string-list |
142 * hardware-declaration |
143 * TOK_REQUEST option-list |
144 * TOK_REQUIRE option-list |
145 * TOK_TIMEOUT number |
147 * TOK_SELECT_TIMEOUT number |
148 * TOK_REBOOT number |
149 * TOK_BACKOFF_CUTOFF number |
150 * TOK_INITIAL_INTERVAL number |
151 * TOK_SCRIPT string |
152 * interface-declaration |
153 * TOK_LEASE client-lease-statement |
154 * TOK_ALIAS client-lease-statement |
155 * TOK_REJECT reject-statement
158 parse_client_statement(FILE *cfile
)
163 switch (next_token(&val
, cfile
)) {
165 parse_option_decl(cfile
, &config
->send_options
[0]);
168 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
170 config
->default_actions
[code
] = ACTION_DEFAULT
;
173 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
175 config
->default_actions
[code
] = ACTION_SUPERSEDE
;
178 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
180 config
->default_actions
[code
] = ACTION_APPEND
;
183 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
185 config
->default_actions
[code
] = ACTION_PREPEND
;
188 parse_string_list(cfile
, &config
->media
, 1);
191 parse_hardware_param(cfile
, &ifi
->hw_address
);
194 config
->requested_option_count
=
195 parse_option_list(cfile
, config
->requested_options
);
198 memset(config
->required_options
, 0,
199 sizeof(config
->required_options
));
200 parse_option_list(cfile
, config
->required_options
);
202 case TOK_LINK_TIMEOUT
:
203 parse_lease_time(cfile
, &config
->link_timeout
);
206 parse_lease_time(cfile
, &config
->timeout
);
209 parse_lease_time(cfile
, &config
->retry_interval
);
211 case TOK_SELECT_TIMEOUT
:
212 parse_lease_time(cfile
, &config
->select_interval
);
215 parse_lease_time(cfile
, &config
->reboot_timeout
);
217 case TOK_BACKOFF_CUTOFF
:
218 parse_lease_time(cfile
, &config
->backoff_cutoff
);
220 case TOK_INITIAL_INTERVAL
:
221 parse_lease_time(cfile
, &config
->initial_interval
);
224 config
->script_name
= parse_string(cfile
);
227 parse_interface_declaration(cfile
);
230 parse_client_lease_statement(cfile
, 1);
233 parse_client_lease_statement(cfile
, 2);
236 parse_reject_statement(cfile
);
239 parse_warn("expecting a statement.");
243 token
= next_token(&val
, cfile
);
245 parse_warn("semicolon expected.");
251 parse_X(FILE *cfile
, u_int8_t
*buf
, int max
)
257 token
= peek_token(&val
, cfile
);
258 if (token
== TOK_NUMBER_OR_NAME
|| token
== TOK_NUMBER
) {
261 token
= next_token(&val
, cfile
);
262 if (token
!= TOK_NUMBER
&& token
!= TOK_NUMBER_OR_NAME
) {
263 parse_warn("expecting hexadecimal constant.");
267 convert_num(&buf
[len
], val
, 16, 8);
269 parse_warn("hexadecimal constant too long.");
273 token
= peek_token(&val
, cfile
);
275 token
= next_token(&val
, cfile
);
276 } while (token
== ':');
278 } else if (token
== TOK_STRING
) {
279 token
= next_token(&val
, cfile
);
282 parse_warn("string constant too long.");
286 memcpy(buf
, val
, len
+ 1);
288 parse_warn("expecting string or hexadecimal data");
296 * option-list :== option_name |
297 * option_list COMMA option_name
300 parse_option_list(FILE *cfile
, u_int8_t
*list
)
308 token
= next_token(&val
, cfile
);
309 if (!is_identifier(token
)) {
310 parse_warn("expected option name.");
314 for (i
= 0; i
< 256; i
++)
315 if (!strcasecmp(dhcp_options
[i
].name
, val
))
319 parse_warn("%s: unexpected option name.", val
);
325 parse_warn("%s: too many options.", val
);
329 token
= next_token(&val
, cfile
);
330 } while (token
== ',');
332 parse_warn("expecting semicolon.");
340 * interface-declaration :==
341 * INTERFACE string LBRACE client-declarations RBRACE
344 parse_interface_declaration(FILE *cfile
)
349 token
= next_token(&val
, cfile
);
350 if (token
!= TOK_STRING
) {
351 parse_warn("expecting interface name (in quotes).");
356 if (strcmp(ifi
->name
, val
) != 0) {
361 token
= next_token(&val
, cfile
);
363 parse_warn("expecting left brace.");
369 token
= peek_token(&val
, cfile
);
371 parse_warn("unterminated interface declaration.");
376 parse_client_statement(cfile
);
378 token
= next_token(&val
, cfile
);
382 * client-lease-statement :==
383 * RBRACE client-lease-declarations LBRACE
385 * client-lease-declarations :==
387 * client-lease-declaration |
388 * client-lease-declarations client-lease-declaration
391 parse_client_lease_statement(FILE *cfile
, int is_static
)
393 struct client_lease
*lease
, *lp
, *pl
;
397 token
= next_token(&val
, cfile
);
399 parse_warn("expecting left brace.");
404 lease
= malloc(sizeof(struct client_lease
));
406 error("no memory for lease.");
407 memset(lease
, 0, sizeof(*lease
));
408 lease
->is_static
= is_static
;
411 token
= peek_token(&val
, cfile
);
413 parse_warn("unterminated lease declaration.");
418 parse_client_lease_declaration(cfile
, lease
);
420 token
= next_token(&val
, cfile
);
422 /* If the lease declaration didn't include an interface
423 * declaration that we recognized, it's of no use to us.
426 free_client_lease(lease
);
430 /* If this is an alias lease, it doesn't need to be sorted in. */
431 if (is_static
== 2) {
432 client
->alias
= lease
;
437 * The new lease may supersede a lease that's not the active
438 * lease but is still on the lease list, so scan the lease list
439 * looking for a lease with the same address, and if we find it,
443 for (lp
= client
->leases
; lp
; lp
= lp
->next
) {
444 if (lp
->address
.len
== lease
->address
.len
&&
445 !memcmp(lp
->address
.iabuf
, lease
->address
.iabuf
,
446 lease
->address
.len
)) {
450 client
->leases
= lp
->next
;
451 free_client_lease(lp
);
457 * If this is a preloaded lease, just put it on the list of
458 * recorded leases - don't make it the active lease.
461 lease
->next
= client
->leases
;
462 client
->leases
= lease
;
467 * The last lease in the lease file on a particular interface is
468 * the active lease for that interface. Of course, we don't
469 * know what the last lease in the file is until we've parsed
470 * the whole file, so at this point, we assume that the lease we
471 * just parsed is the active lease for its interface. If
472 * there's already an active lease for the interface, and this
473 * lease is for the same ip address, then we just toss the old
474 * active lease and replace it with this one. If this lease is
475 * for a different address, then if the old active lease has
476 * expired, we dump it; if not, we put it on the list of leases
477 * for this interface which are still valid but no longer
480 if (client
->active
) {
481 if (client
->active
->expiry
< cur_time
)
482 free_client_lease(client
->active
);
483 else if (client
->active
->address
.len
==
484 lease
->address
.len
&&
485 !memcmp(client
->active
->address
.iabuf
,
486 lease
->address
.iabuf
, lease
->address
.len
))
487 free_client_lease(client
->active
);
489 client
->active
->next
= client
->leases
;
490 client
->leases
= client
->active
;
493 client
->active
= lease
;
499 * client-lease-declaration :==
502 * FIXED_ADDR ip_address |
504 * SERVER_NAME string |
505 * OPTION option-decl |
511 parse_client_lease_declaration(FILE *cfile
, struct client_lease
*lease
)
516 switch (next_token(&val
, cfile
)) {
521 token
= next_token(&val
, cfile
);
522 if (token
!= TOK_STRING
) {
523 parse_warn("expecting interface name (in quotes).");
527 if (strcmp(ifi
->name
, val
) != 0) {
528 parse_warn("wrong interface name. Expecting '%s'.",
535 if (!parse_ip_addr(cfile
, &lease
->address
))
539 parse_string_list(cfile
, &lease
->medium
, 0);
542 lease
->filename
= parse_string(cfile
);
544 case TOK_SERVER_NAME
:
545 lease
->server_name
= parse_string(cfile
);
548 lease
->renewal
= parse_date(cfile
);
551 lease
->rebind
= parse_date(cfile
);
554 lease
->expiry
= parse_date(cfile
);
557 parse_option_decl(cfile
, lease
->options
);
560 parse_warn("expecting lease declaration.");
564 token
= next_token(&val
, cfile
);
566 parse_warn("expecting semicolon.");
572 parse_option_decl(FILE *cfile
, struct option_data
*options
)
577 u_int8_t hunkbuf
[1024];
580 struct iaddr ip_addr
;
585 token
= next_token(&val
, cfile
);
586 if (!is_identifier(token
)) {
587 parse_warn("expecting identifier after option keyword.");
593 /* Look up the actual option info. */
595 for (code
= 0; code
< 256; code
++)
596 if (strcmp(dhcp_options
[code
].name
, val
) == 0)
600 parse_warn("no option named %s", val
);
605 /* Parse the option data... */
607 for (fmt
= dhcp_options
[code
].format
; *fmt
; fmt
++) {
612 len
= parse_X(cfile
, &hunkbuf
[hunkix
],
613 sizeof(hunkbuf
) - hunkix
);
616 case 't': /* Text string... */
617 token
= next_token(&val
, cfile
);
618 if (token
!= TOK_STRING
) {
619 parse_warn("expecting string.");
624 if (hunkix
+ len
+ 1 > sizeof(hunkbuf
)) {
625 parse_warn("option data buffer %s",
630 memcpy(&hunkbuf
[hunkix
], val
, len
+ 1);
634 case 'I': /* IP address. */
635 if (!parse_ip_addr(cfile
, &ip_addr
))
640 if (hunkix
+ len
> sizeof(hunkbuf
)) {
641 parse_warn("option data buffer "
646 memcpy(&hunkbuf
[hunkix
], dp
, len
);
649 case 'L': /* Unsigned 32-bit integer... */
650 case 'l': /* Signed 32-bit integer... */
651 token
= next_token(&val
, cfile
);
652 if (token
!= TOK_NUMBER
) {
654 parse_warn("expecting number.");
659 convert_num(buf
, val
, 0, 32);
663 case 's': /* Signed 16-bit integer. */
664 case 'S': /* Unsigned 16-bit integer. */
665 token
= next_token(&val
, cfile
);
666 if (token
!= TOK_NUMBER
)
668 convert_num(buf
, val
, 0, 16);
672 case 'b': /* Signed 8-bit integer. */
673 case 'B': /* Unsigned 8-bit integer. */
674 token
= next_token(&val
, cfile
);
675 if (token
!= TOK_NUMBER
)
677 convert_num(buf
, val
, 0, 8);
681 case 'f': /* Boolean flag. */
682 token
= next_token(&val
, cfile
);
683 if (!is_identifier(token
)) {
684 parse_warn("expecting identifier.");
690 if (!strcasecmp(val
, "true") ||
691 !strcasecmp(val
, "on"))
693 else if (!strcasecmp(val
, "false") ||
694 !strcasecmp(val
, "off"))
697 parse_warn("expecting boolean.");
704 warning("Bad format %c in parse_option_param.",
710 token
= next_token(&val
, cfile
);
711 } while (*fmt
== 'A' && token
== ',');
714 parse_warn("semicolon expected.");
719 options
[code
].data
= malloc(hunkix
+ nul_term
);
720 if (!options
[code
].data
)
721 error("out of memory allocating option data.");
722 memcpy(options
[code
].data
, hunkbuf
, hunkix
+ nul_term
);
723 options
[code
].len
= hunkix
;
728 parse_string_list(FILE *cfile
, struct string_list
**lp
, int multiple
)
732 struct string_list
*cur
, *tmp
;
734 /* Find the last medium in the media list. */
736 for (cur
= *lp
; cur
->next
; cur
= cur
->next
)
742 token
= next_token(&val
, cfile
);
743 if (token
!= TOK_STRING
) {
744 parse_warn("Expecting media options.");
749 tmp
= malloc(sizeof(struct string_list
) + strlen(val
));
751 error("no memory for string list entry.");
752 strlcpy(tmp
->string
, val
, strlen(val
) + 1);
755 /* Store this medium at the end of the media list. */
762 token
= next_token(&val
, cfile
);
763 } while (multiple
&& token
== ',');
766 parse_warn("expecting semicolon.");
772 parse_reject_statement(FILE *cfile
)
774 struct iaddrlist
*list
;
780 if (!parse_ip_addr(cfile
, &addr
)) {
781 parse_warn("expecting IP address.");
786 list
= malloc(sizeof(struct iaddrlist
));
788 error("no memory for reject list!");
791 list
->next
= config
->reject_list
;
792 config
->reject_list
= list
;
794 token
= next_token(&val
, cfile
);
795 } while (token
== ',');
798 parse_warn("expecting semicolon.");