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