nrelease - fix/improve livecd
[dragonfly.git] / sbin / dhclient / clparse.c
CommitLineData
dafcba85 1/* $OpenBSD: src/sbin/dhclient/clparse.c,v 1.41 2012/10/27 23:08:53 krw Exp $ */
846204b6
HT
2
3/* Parser for dhclient config and lease files... */
4
5/*
6 * Copyright (c) 1997 The Internet Software Consortium.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
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.
21 *
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
34 * SUCH DAMAGE.
35 *
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''.
41 */
42
43#include "dhcpd.h"
44#include "dhctoken.h"
45
46/*
47 * client-conf-file :== client-declarations EOF
48 * client-declarations :== <nil>
49 * | client-declaration
50 * | client-declarations client-declaration
51 */
52int
53read_client_conf(void)
54{
55 FILE *cfile;
846204b6
HT
56 int token;
57
58 new_parse(path_dhclient_conf);
59
60 /* Set some defaults... */
852231a5 61 config->link_timeout = 30;
846204b6
HT
62 config->timeout = 60;
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;
6bdc8b43
MD
76 config->requested_options
77 [config->requested_option_count++] = DHO_CLASSLESS_ROUTES;
846204b6
HT
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;
86
87 if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
88 do {
54dfa84a 89 token = peek_token(NULL, cfile);
846204b6
HT
90 if (token == EOF)
91 break;
92 parse_client_statement(cfile);
93 } while (1);
54dfa84a 94 token = next_token(NULL, cfile); /* Clear the peek buffer */
846204b6
HT
95 fclose(cfile);
96 }
97
98 return (!warnings_occurred);
99}
100
101/*
102 * lease-file :== client-lease-statements EOF
103 * client-lease-statements :== <nil>
104 * | client-lease-statements LEASE client-lease-statement
105 */
106void
107read_client_leases(void)
108{
109 FILE *cfile;
846204b6
HT
110 int token;
111
112 new_parse(path_dhclient_db);
113
114 /* Open the lease file. If we can't open it, just return -
115 we can safely trust the server to remember our state. */
116 if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
117 return;
118 do {
54dfa84a 119 token = next_token(NULL, cfile);
846204b6
HT
120 if (token == EOF)
121 break;
122 if (token != TOK_LEASE) {
123 warning("Corrupt lease file - possible data loss!");
124 skip_to_semi(cfile);
125 break;
126 } else
127 parse_client_lease_statement(cfile, 0);
128
129 } while (1);
130 fclose(cfile);
131}
132
133/*
134 * client-declaration :==
135 * TOK_SEND option-decl |
136 * TOK_DEFAULT option-decl |
137 * TOK_SUPERSEDE option-decl |
138 * TOK_APPEND option-decl |
139 * TOK_PREPEND option-decl |
140 * TOK_MEDIA string-list |
141 * hardware-declaration |
142 * TOK_REQUEST option-list |
143 * TOK_REQUIRE option-list |
144 * TOK_TIMEOUT number |
145 * TOK_RETRY number |
146 * TOK_SELECT_TIMEOUT number |
147 * TOK_REBOOT number |
148 * TOK_BACKOFF_CUTOFF number |
149 * TOK_INITIAL_INTERVAL number |
150 * TOK_SCRIPT string |
151 * interface-declaration |
152 * TOK_LEASE client-lease-statement |
153 * TOK_ALIAS client-lease-statement |
154 * TOK_REJECT reject-statement
155 */
156void
157parse_client_statement(FILE *cfile)
158{
dafcba85
AHJ
159 u_int8_t ignorelist[256];
160 int token, code, count, i;
846204b6 161
54dfa84a 162 switch (next_token(NULL, cfile)) {
846204b6
HT
163 case TOK_SEND:
164 parse_option_decl(cfile, &config->send_options[0]);
165 return;
166 case TOK_DEFAULT:
167 code = parse_option_decl(cfile, &config->defaults[0]);
168 if (code != -1)
169 config->default_actions[code] = ACTION_DEFAULT;
170 return;
171 case TOK_SUPERSEDE:
172 code = parse_option_decl(cfile, &config->defaults[0]);
173 if (code != -1)
174 config->default_actions[code] = ACTION_SUPERSEDE;
175 return;
04e4af02 176 case TOK_IGNORE:
dafcba85
AHJ
177 count = parse_option_list(cfile, ignorelist);
178 for (i = 0; i < count; i++)
179 config->default_actions[ignorelist[i]] = ACTION_IGNORE;
04e4af02 180 return;
846204b6
HT
181 case TOK_APPEND:
182 code = parse_option_decl(cfile, &config->defaults[0]);
183 if (code != -1)
184 config->default_actions[code] = ACTION_APPEND;
185 return;
186 case TOK_PREPEND:
187 code = parse_option_decl(cfile, &config->defaults[0]);
188 if (code != -1)
189 config->default_actions[code] = ACTION_PREPEND;
190 return;
191 case TOK_MEDIA:
7cbe3601 192 skip_to_semi(cfile);
846204b6
HT
193 return;
194 case TOK_HARDWARE:
195 parse_hardware_param(cfile, &ifi->hw_address);
196 return;
197 case TOK_REQUEST:
198 config->requested_option_count =
199 parse_option_list(cfile, config->requested_options);
200 return;
201 case TOK_REQUIRE:
202 memset(config->required_options, 0,
203 sizeof(config->required_options));
204 parse_option_list(cfile, config->required_options);
205 return;
206 case TOK_LINK_TIMEOUT:
207 parse_lease_time(cfile, &config->link_timeout);
208 return;
209 case TOK_TIMEOUT:
210 parse_lease_time(cfile, &config->timeout);
211 return;
212 case TOK_RETRY:
213 parse_lease_time(cfile, &config->retry_interval);
214 return;
215 case TOK_SELECT_TIMEOUT:
216 parse_lease_time(cfile, &config->select_interval);
217 return;
218 case TOK_REBOOT:
219 parse_lease_time(cfile, &config->reboot_timeout);
220 return;
221 case TOK_BACKOFF_CUTOFF:
222 parse_lease_time(cfile, &config->backoff_cutoff);
223 return;
224 case TOK_INITIAL_INTERVAL:
225 parse_lease_time(cfile, &config->initial_interval);
226 return;
227 case TOK_SCRIPT:
228 config->script_name = parse_string(cfile);
229 return;
230 case TOK_INTERFACE:
231 parse_interface_declaration(cfile);
232 return;
233 case TOK_LEASE:
234 parse_client_lease_statement(cfile, 1);
235 return;
236 case TOK_ALIAS:
7cbe3601 237 skip_to_semi(cfile);
846204b6
HT
238 return;
239 case TOK_REJECT:
240 parse_reject_statement(cfile);
241 return;
242 default:
243 parse_warn("expecting a statement.");
244 skip_to_semi(cfile);
245 break;
246 }
54dfa84a 247 token = next_token(NULL, cfile);
846204b6
HT
248 if (token != ';') {
249 parse_warn("semicolon expected.");
250 skip_to_semi(cfile);
251 }
252}
253
254int
255parse_X(FILE *cfile, u_int8_t *buf, int max)
256{
257 int token;
258 char *val;
259 int len;
260
261 token = peek_token(&val, cfile);
262 if (token == TOK_NUMBER_OR_NAME || token == TOK_NUMBER) {
263 len = 0;
264 do {
265 token = next_token(&val, cfile);
266 if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
267 parse_warn("expecting hexadecimal constant.");
268 skip_to_semi(cfile);
269 return (0);
270 }
271 convert_num(&buf[len], val, 16, 8);
272 if (len++ > max) {
273 parse_warn("hexadecimal constant too long.");
274 skip_to_semi(cfile);
275 return (0);
276 }
277 token = peek_token(&val, cfile);
278 if (token == ':')
279 token = next_token(&val, cfile);
280 } while (token == ':');
281 val = (char *)buf;
282 } else if (token == TOK_STRING) {
283 token = next_token(&val, cfile);
284 len = strlen(val);
285 if (len + 1 > max) {
286 parse_warn("string constant too long.");
287 skip_to_semi(cfile);
288 return (0);
289 }
290 memcpy(buf, val, len + 1);
291 } else {
292 parse_warn("expecting string or hexadecimal data");
293 skip_to_semi(cfile);
294 return (0);
295 }
296 return (len);
297}
298
299/*
300 * option-list :== option_name |
301 * option_list COMMA option_name
302 */
303int
304parse_option_list(FILE *cfile, u_int8_t *list)
305{
306 int ix, i;
307 int token;
308 char *val;
309
310 ix = 0;
311 do {
312 token = next_token(&val, cfile);
313 if (!is_identifier(token)) {
314 parse_warn("expected option name.");
315 skip_to_semi(cfile);
316 return (0);
317 }
318 for (i = 0; i < 256; i++)
319 if (!strcasecmp(dhcp_options[i].name, val))
320 break;
321
322 if (i == 256) {
323 parse_warn("%s: unexpected option name.", val);
324 skip_to_semi(cfile);
325 return (0);
326 }
327 list[ix++] = i;
328 if (ix == 256) {
329 parse_warn("%s: too many options.", val);
330 skip_to_semi(cfile);
331 return (0);
332 }
333 token = next_token(&val, cfile);
334 } while (token == ',');
335 if (token != ';') {
336 parse_warn("expecting semicolon.");
337 skip_to_semi(cfile);
338 return (0);
339 }
340 return (ix);
341}
342
343/*
344 * interface-declaration :==
345 * INTERFACE string LBRACE client-declarations RBRACE
346 */
347void
348parse_interface_declaration(FILE *cfile)
349{
350 char *val;
351 int token;
352
353 token = next_token(&val, cfile);
354 if (token != TOK_STRING) {
355 parse_warn("expecting interface name (in quotes).");
356 skip_to_semi(cfile);
357 return;
358 }
359
360 if (strcmp(ifi->name, val) != 0) {
361 skip_to_semi(cfile);
362 return;
363 }
364
365 token = next_token(&val, cfile);
366 if (token != '{') {
367 parse_warn("expecting left brace.");
368 skip_to_semi(cfile);
369 return;
370 }
371
372 do {
373 token = peek_token(&val, cfile);
374 if (token == EOF) {
375 parse_warn("unterminated interface declaration.");
376 return;
377 }
378 if (token == '}')
379 break;
380 parse_client_statement(cfile);
381 } while (1);
382 token = next_token(&val, cfile);
383}
384
385/*
386 * client-lease-statement :==
387 * RBRACE client-lease-declarations LBRACE
388 *
389 * client-lease-declarations :==
390 * <nil> |
391 * client-lease-declaration |
392 * client-lease-declarations client-lease-declaration
393 */
394void
395parse_client_lease_statement(FILE *cfile, int is_static)
396{
397 struct client_lease *lease, *lp, *pl;
398 int token;
846204b6 399
54dfa84a 400 token = next_token(NULL, cfile);
846204b6
HT
401 if (token != '{') {
402 parse_warn("expecting left brace.");
403 skip_to_semi(cfile);
404 return;
405 }
406
407 lease = malloc(sizeof(struct client_lease));
408 if (!lease)
409 error("no memory for lease.");
410 memset(lease, 0, sizeof(*lease));
411 lease->is_static = is_static;
412
413 do {
54dfa84a 414 token = peek_token(NULL, cfile);
846204b6
HT
415 if (token == EOF) {
416 parse_warn("unterminated lease declaration.");
417 return;
418 }
419 if (token == '}')
420 break;
421 parse_client_lease_declaration(cfile, lease);
422 } while (1);
54dfa84a 423 token = next_token(NULL, cfile);
846204b6
HT
424
425 /* If the lease declaration didn't include an interface
426 * declaration that we recognized, it's of no use to us.
427 */
428 if (!ifi) {
429 free_client_lease(lease);
430 return;
431 }
432
846204b6
HT
433 /*
434 * The new lease may supersede a lease that's not the active
435 * lease but is still on the lease list, so scan the lease list
436 * looking for a lease with the same address, and if we find it,
437 * toss it.
438 */
439 pl = NULL;
440 for (lp = client->leases; lp; lp = lp->next) {
c3b31e60 441 if (addr_eq(lp->address, lease->address)) {
846204b6
HT
442 if (pl)
443 pl->next = lp->next;
444 else
445 client->leases = lp->next;
446 free_client_lease(lp);
447 break;
44967127
AHJ
448 } else
449 pl = lp;
846204b6
HT
450 }
451
452 /*
453 * If this is a preloaded lease, just put it on the list of
454 * recorded leases - don't make it the active lease.
455 */
456 if (is_static) {
457 lease->next = client->leases;
458 client->leases = lease;
459 return;
460 }
461
462 /*
463 * The last lease in the lease file on a particular interface is
464 * the active lease for that interface. Of course, we don't
465 * know what the last lease in the file is until we've parsed
466 * the whole file, so at this point, we assume that the lease we
467 * just parsed is the active lease for its interface. If
468 * there's already an active lease for the interface, and this
469 * lease is for the same ip address, then we just toss the old
470 * active lease and replace it with this one. If this lease is
471 * for a different address, then if the old active lease has
472 * expired, we dump it; if not, we put it on the list of leases
473 * for this interface which are still valid but no longer
474 * active.
475 */
476 if (client->active) {
3c279f6e 477 if (client->active->expiry < time(NULL))
846204b6 478 free_client_lease(client->active);
c3b31e60 479 else if (addr_eq(client->active->address, lease->address))
846204b6
HT
480 free_client_lease(client->active);
481 else {
482 client->active->next = client->leases;
483 client->leases = client->active;
484 }
485 }
486 client->active = lease;
487
488 /* Phew. */
489}
490
491/*
492 * client-lease-declaration :==
493 * BOOTP |
494 * INTERFACE string |
495 * FIXED_ADDR ip_address |
496 * FILENAME string |
497 * SERVER_NAME string |
498 * OPTION option-decl |
499 * RENEW time-decl |
500 * REBIND time-decl |
501 * EXPIRE time-decl
502 */
503void
504parse_client_lease_declaration(FILE *cfile, struct client_lease *lease)
505{
506 char *val;
507 int token;
508
509 switch (next_token(&val, cfile)) {
510 case TOK_BOOTP:
511 lease->is_bootp = 1;
512 break;
513 case TOK_INTERFACE:
514 token = next_token(&val, cfile);
515 if (token != TOK_STRING) {
516 parse_warn("expecting interface name (in quotes).");
517 skip_to_semi(cfile);
518 break;
519 }
520 if (strcmp(ifi->name, val) != 0) {
521 parse_warn("wrong interface name. Expecting '%s'.",
522 ifi->name);
523 skip_to_semi(cfile);
524 break;
525 }
526 break;
527 case TOK_FIXED_ADDR:
528 if (!parse_ip_addr(cfile, &lease->address))
529 return;
530 break;
531 case TOK_MEDIUM:
7cbe3601 532 skip_to_semi(cfile);
846204b6
HT
533 return;
534 case TOK_FILENAME:
535 lease->filename = parse_string(cfile);
536 return;
537 case TOK_SERVER_NAME:
538 lease->server_name = parse_string(cfile);
539 return;
540 case TOK_RENEW:
541 lease->renewal = parse_date(cfile);
542 return;
543 case TOK_REBIND:
544 lease->rebind = parse_date(cfile);
545 return;
546 case TOK_EXPIRE:
547 lease->expiry = parse_date(cfile);
548 return;
549 case TOK_OPTION:
550 parse_option_decl(cfile, lease->options);
551 return;
552 default:
553 parse_warn("expecting lease declaration.");
554 skip_to_semi(cfile);
555 break;
556 }
557 token = next_token(&val, cfile);
558 if (token != ';') {
559 parse_warn("expecting semicolon.");
560 skip_to_semi(cfile);
561 }
562}
563
564int
565parse_option_decl(FILE *cfile, struct option_data *options)
566{
567 char *val;
568 int token;
569 u_int8_t buf[4];
570 u_int8_t hunkbuf[1024];
571 int hunkix = 0;
572 char *fmt;
573 struct iaddr ip_addr;
574 u_int8_t *dp;
575 int len, code;
576 int nul_term = 0;
577
578 token = next_token(&val, cfile);
579 if (!is_identifier(token)) {
580 parse_warn("expecting identifier after option keyword.");
581 if (token != ';')
582 skip_to_semi(cfile);
583 return (-1);
584 }
585
586 /* Look up the actual option info. */
587 fmt = NULL;
588 for (code = 0; code < 256; code++)
589 if (strcmp(dhcp_options[code].name, val) == 0)
590 break;
591
592 if (code > 255) {
593 parse_warn("no option named %s", val);
594 skip_to_semi(cfile);
595 return (-1);
596 }
597
598 /* Parse the option data... */
599 do {
600 for (fmt = dhcp_options[code].format; *fmt; fmt++) {
601 if (*fmt == 'A')
602 break;
603 switch (*fmt) {
604 case 'X':
605 len = parse_X(cfile, &hunkbuf[hunkix],
606 sizeof(hunkbuf) - hunkix);
607 hunkix += len;
608 break;
609 case 't': /* Text string... */
610 token = next_token(&val, cfile);
611 if (token != TOK_STRING) {
612 parse_warn("expecting string.");
613 skip_to_semi(cfile);
614 return (-1);
615 }
616 len = strlen(val);
617 if (hunkix + len + 1 > sizeof(hunkbuf)) {
618 parse_warn("option data buffer %s",
619 "overflow");
620 skip_to_semi(cfile);
621 return (-1);
622 }
623 memcpy(&hunkbuf[hunkix], val, len + 1);
624 nul_term = 1;
625 hunkix += len;
626 break;
627 case 'I': /* IP address. */
628 if (!parse_ip_addr(cfile, &ip_addr))
629 return (-1);
630 len = ip_addr.len;
631 dp = ip_addr.iabuf;
632alloc:
633 if (hunkix + len > sizeof(hunkbuf)) {
634 parse_warn("option data buffer "
635 "overflow");
636 skip_to_semi(cfile);
637 return (-1);
638 }
639 memcpy(&hunkbuf[hunkix], dp, len);
640 hunkix += len;
641 break;
642 case 'L': /* Unsigned 32-bit integer... */
643 case 'l': /* Signed 32-bit integer... */
644 token = next_token(&val, cfile);
645 if (token != TOK_NUMBER) {
646need_number:
647 parse_warn("expecting number.");
648 if (token != ';')
649 skip_to_semi(cfile);
650 return (-1);
651 }
652 convert_num(buf, val, 0, 32);
653 len = 4;
654 dp = buf;
655 goto alloc;
656 case 's': /* Signed 16-bit integer. */
657 case 'S': /* Unsigned 16-bit integer. */
658 token = next_token(&val, cfile);
659 if (token != TOK_NUMBER)
660 goto need_number;
661 convert_num(buf, val, 0, 16);
662 len = 2;
663 dp = buf;
664 goto alloc;
665 case 'b': /* Signed 8-bit integer. */
666 case 'B': /* Unsigned 8-bit integer. */
667 token = next_token(&val, cfile);
668 if (token != TOK_NUMBER)
669 goto need_number;
670 convert_num(buf, val, 0, 8);
671 len = 1;
672 dp = buf;
673 goto alloc;
674 case 'f': /* Boolean flag. */
675 token = next_token(&val, cfile);
676 if (!is_identifier(token)) {
677 parse_warn("expecting identifier.");
678bad_flag:
679 if (token != ';')
680 skip_to_semi(cfile);
681 return (-1);
682 }
683 if (!strcasecmp(val, "true") ||
684 !strcasecmp(val, "on"))
685 buf[0] = 1;
686 else if (!strcasecmp(val, "false") ||
687 !strcasecmp(val, "off"))
688 buf[0] = 0;
689 else {
690 parse_warn("expecting boolean.");
691 goto bad_flag;
692 }
693 len = 1;
694 dp = buf;
695 goto alloc;
696 default:
697 warning("Bad format %c in parse_option_param.",
698 *fmt);
699 skip_to_semi(cfile);
700 return (-1);
701 }
702 }
703 token = next_token(&val, cfile);
704 } while (*fmt == 'A' && token == ',');
705
706 if (token != ';') {
707 parse_warn("semicolon expected.");
708 skip_to_semi(cfile);
709 return (-1);
710 }
711
712 options[code].data = malloc(hunkix + nul_term);
713 if (!options[code].data)
714 error("out of memory allocating option data.");
715 memcpy(options[code].data, hunkbuf, hunkix + nul_term);
716 options[code].len = hunkix;
717 return (code);
718}
719
846204b6
HT
720void
721parse_reject_statement(FILE *cfile)
722{
723 struct iaddrlist *list;
724 struct iaddr addr;
846204b6
HT
725 int token;
726
727 do {
728 if (!parse_ip_addr(cfile, &addr)) {
729 parse_warn("expecting IP address.");
730 skip_to_semi(cfile);
731 return;
732 }
733
734 list = malloc(sizeof(struct iaddrlist));
735 if (!list)
736 error("no memory for reject list!");
737
738 list->addr = addr;
739 list->next = config->reject_list;
740 config->reject_list = list;
741
54dfa84a 742 token = next_token(NULL, cfile);
846204b6
HT
743 } while (token == ',');
744
745 if (token != ';') {
746 parse_warn("expecting semicolon.");
747 skip_to_semi(cfile);
748 }
749}