xref: /openbsd/sbin/dhclient/clparse.c (revision 2b7f747f)
1 /*	$OpenBSD: clparse.c,v 1.203 2021/02/27 17:44:58 krw Exp $	*/
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 <sys/queue.h>
44 #include <sys/socket.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 
48 #include <net/if.h>
49 #include <net/if_arp.h>
50 
51 #include <netinet/in.h>
52 #include <netinet/if_ether.h>
53 
54 #include <err.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <limits.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdint.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64 
65 #include "dhcp.h"
66 #include "dhcpd.h"
67 #include "dhctoken.h"
68 #include "log.h"
69 
70 void	parse_conf_decl(FILE *, char *);
71 int	parse_hex_octets(FILE *, unsigned int *, uint8_t **);
72 int	parse_domain_list(FILE *, int *, char **);
73 int	parse_option_list(FILE *, int *, uint8_t *);
74 int	parse_interface(FILE *, char *);
75 int	parse_lease(FILE *, struct client_lease **);
76 void	parse_lease_decl(FILE *, struct client_lease *);
77 int	parse_option(FILE *, int *, struct option_data *);
78 int	parse_reject_statement(FILE *);
79 
80 void	apply_actions(uint8_t *);
81 void	set_default_client_identifier(struct ether_addr *);
82 void	set_default_hostname(void);
83 
84 void
init_config(void)85 init_config(void)
86 {
87 	struct option_data	*option;
88 	uint32_t		 expiry;
89 
90 	config = calloc(1, sizeof(*config));
91 	if (config == NULL)
92 		fatal("config");
93 
94 	TAILQ_INIT(&config->reject_list);
95 
96 	/* Set some defaults. */
97 	config->link_interval = 10;	/* secs before going daemon w/o lease */
98 	config->select_interval = 0;	/* secs to wait for other OFFERs */
99 	config->retry_interval = 1;	/* secs before asking for OFFER */
100 #ifdef SMALL
101 	config->backoff_cutoff = 2;	/* max secs between packet retries */
102 	config->reboot_interval = 5;	/* secs before giving up on reboot */
103 	config->offer_interval = 10;		/* secs to wait for an OFFER */
104 #else
105 	config->backoff_cutoff = 10;	/* max secs between packet retries */
106 	config->reboot_interval = 1;	/* secs before giving up on reboot */
107 	config->offer_interval = 30;		/* secs to wait for an OFFER */
108 #endif
109 	config->initial_interval = 1;	/* secs before 1st retry */
110 
111 	/* All leases must supply a subnet mask. Classful defaults are dead, Jim. */
112 	config->required_options[config->required_option_count++] = DHO_SUBNET_MASK;
113 
114 	/*
115 	 * Set default lease length, which will determine default renewal
116 	 * and rebind times.
117 	 *
118 	 * XXX Thus applies to both BOOTP and DHCP leases.
119 	 *
120 	 * DHO_DHCP_LEASE_TIME (12 hours == 43200 seconds),
121 	 */
122 	option = &config->defaults[DHO_DHCP_LEASE_TIME];
123 	option->data = malloc(4);
124 	if (option->data == NULL)
125 		fatal("default lease length");
126 
127 	config->default_actions[DHO_DHCP_LEASE_TIME] = ACTION_DEFAULT;
128 	option->len = 4;
129 	expiry = htonl(43200);
130 	memcpy(option->data, &expiry, 4);
131 
132 	config->requested_options
133 	    [config->requested_option_count++] = DHO_SUBNET_MASK;
134 	config->requested_options
135 	    [config->requested_option_count++] = DHO_BROADCAST_ADDRESS;
136 	config->requested_options
137 	    [config->requested_option_count++] = DHO_TIME_OFFSET;
138 	/* RFC 3442 says CLASSLESS_STATIC_ROUTES must be before ROUTERS! */
139 	config->requested_options
140 	    [config->requested_option_count++] = DHO_CLASSLESS_STATIC_ROUTES;
141 	config->requested_options
142 	    [config->requested_option_count++] = DHO_ROUTERS;
143 	config->requested_options
144 	    [config->requested_option_count++] = DHO_DOMAIN_NAME;
145 	config->requested_options
146 	    [config->requested_option_count++] = DHO_DOMAIN_SEARCH;
147 	config->requested_options
148 	    [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
149 	config->requested_options
150 	    [config->requested_option_count++] = DHO_HOST_NAME;
151 	config->requested_options
152 	    [config->requested_option_count++] = DHO_BOOTFILE_NAME;
153 	config->requested_options
154 	    [config->requested_option_count++] = DHO_TFTP_SERVER;
155 }
156 
157 /*
158  * conf-decls :==
159  *	  <nil>
160  *	| conf-decl
161  *	| conf-decls conf-decl
162  */
163 void
read_conf(char * name,uint8_t * actions,struct ether_addr * hwaddr)164 read_conf(char *name, uint8_t *actions, struct ether_addr *hwaddr)
165 {
166 	FILE			*cfile;
167 	int			 token;
168 
169 	init_config();
170 
171 	if (path_dhclient_conf != NULL) {
172 		cfile = fopen(path_dhclient_conf, "r");
173 		if (cfile == NULL)
174 			fatal("fopen(%s)", path_dhclient_conf);
175 		new_parse(path_dhclient_conf);
176 		for (;;) {
177 			token = peek_token(NULL, cfile);
178 			if (token == EOF)
179 				break;
180 			parse_conf_decl(cfile, name);
181 		}
182 		fclose(cfile);
183 	}
184 
185 	set_default_client_identifier(hwaddr);
186 	set_default_hostname();
187 	apply_actions(actions);
188 }
189 
190 /*
191  * leases :==
192  *	  <nil>
193  *	| lease
194  *	| leases lease
195  */
196 void
read_lease_db(struct client_lease_tq * lease_db)197 read_lease_db(struct client_lease_tq *lease_db)
198 {
199 	struct client_lease	*lease, *lp, *nlp;
200 	FILE			*cfile;
201 	int			 i;
202 
203 	TAILQ_INIT(lease_db);
204 
205 	if ((cfile = fopen(path_lease_db, "r")) == NULL)
206 		return;
207 
208 	new_parse(path_lease_db);
209 
210 	i = DHO_DHCP_CLIENT_IDENTIFIER;
211 	while (feof(cfile) == 0) {
212 		if (parse_lease(cfile, &lease) == 0)
213 			continue;
214 
215 		/*
216 		 * The new lease will supersede a lease with the same
217 		 * ssid AND the same Client Identifier AND the same
218 		 * IP address.
219 		 */
220 		TAILQ_FOREACH_SAFE(lp, lease_db, next, nlp) {
221 			if (lp->ssid_len != lease->ssid_len)
222 				continue;
223 			if (memcmp(lp->ssid, lease->ssid, lp->ssid_len) != 0)
224 				continue;
225 			if ((lease->options[i].len != 0) &&
226 			    ((lp->options[i].len != lease->options[i].len) ||
227 			    memcmp(lp->options[i].data, lease->options[i].data,
228 			    lp->options[i].len) != 0))
229 				continue;
230 			if (lp->address.s_addr != lease->address.s_addr)
231 				continue;
232 
233 			TAILQ_REMOVE(lease_db, lp, next);
234 			free_client_lease(lp);
235 		}
236 
237 		if (lease->epoch == 0)
238 			time(&lease->epoch);
239 		TAILQ_INSERT_TAIL(lease_db, lease, next);
240 	}
241 
242 	fclose(cfile);
243 }
244 
245 /*
246  * conf-decl :==
247  *	  APPEND		option SEMI
248  *	| BACKOFF_CUTOFF	number SEMI
249  *	| DEFAULT		option SEMI
250  *	| FILENAME		string SEMI
251  *	| FIXED_ADDR		ip-address SEMI
252  *	| IGNORE		option-name-list SEMI
253  *	| INITIAL_INTERVAL	number SEMI
254  *	| INTERFACE		interface
255  *	| LINK_TIMEOUT		number SEMI
256  *	| NEXT_SERVER		string SEMI
257  *	| PREPEND		option SEMI
258  *	| REBOOT		number SEMI
259  *	| REJECT		ip-address SEMI
260  *	| REQUEST		option-name-list SEMI
261  *	| REQUIRE		option-name-list SEMI
262  *	| RETRY			number SEMI
263  *	| SELECT_TIMEOUT	number SEMI
264  *	| SEND			option SEMI
265  *	| SERVER_NAME		string SEMI
266  *	| SUPERSEDE		option SEMI
267  *	| TIMEOUT		number SEMI
268  */
269 void
parse_conf_decl(FILE * cfile,char * name)270 parse_conf_decl(FILE *cfile, char *name)
271 {
272 	uint8_t			 list[DHO_COUNT];
273 	char			*val;
274 	enum actions 		*p;
275 	int			 action, count, i, token;
276 
277 	token = next_token(NULL, cfile);
278 
279 	switch (token) {
280 	case TOK_APPEND:
281 		if (parse_option(cfile, &i, config->defaults) == 0)
282 			return;
283 		action = code_to_action(i, ACTION_APPEND);
284 		if (action == ACTION_DEFAULT)
285 			parse_warn("'append' treated as 'default'");
286 		config->default_actions[i] = action;
287 		break;
288 	case TOK_BACKOFF_CUTOFF:
289 		if (parse_number(cfile, &config->backoff_cutoff, 0, INT32_MAX)
290 		    == 0)
291 			return;
292 		break;
293 	case TOK_DEFAULT:
294 		if (parse_option(cfile, &i, config->defaults) == 0)
295 			return;
296 		config->default_actions[i] = ACTION_DEFAULT;
297 		break;
298 	case TOK_FILENAME:
299 		if (parse_string(cfile, &val) == 0)
300 			return;
301 		free(config->filename);
302 		config->filename = val;
303 		break;
304 	case TOK_FIXED_ADDR:
305 		if (parse_ip_addr(cfile, &config->address) == 0)
306 			return;
307 		break;
308 	case TOK_IGNORE:
309 		memset(list, 0, sizeof(list));
310 		count = 0;
311 		if (parse_option_list(cfile, &count, list) == 0)
312 			return;
313 		p = config->default_actions;
314 		if (count == 0) {
315 			for (i = 0; i < DHO_COUNT; i++)
316 				if (p[i] == ACTION_IGNORE)
317 					p[i] = ACTION_USELEASE;
318 		} else {
319 			for (i = 0; i < count; i++)
320 				p[list[i]] = ACTION_IGNORE;
321 		}
322 		break;
323 	case TOK_INITIAL_INTERVAL:
324 		if (parse_number(cfile, &config->initial_interval, 0, INT32_MAX)
325 		    == 0)
326 			return;
327 		break;
328 	case TOK_INTERFACE:
329 		parse_interface(cfile, name);
330 		return;
331 	case TOK_LINK_TIMEOUT:
332 		if (parse_number(cfile, &config->link_interval, 0, INT32_MAX)
333 		    == 0)
334 			return;
335 		break;
336 	case TOK_NEXT_SERVER:
337 		if (parse_ip_addr(cfile, &config->next_server) == 0)
338 			return;
339 		break;
340 	case TOK_PREPEND:
341 		if (parse_option(cfile, &i, config->defaults) == 0)
342 			return;
343 		action = code_to_action(i, ACTION_PREPEND);
344 		if (action == ACTION_SUPERSEDE)
345 			parse_warn("'prepend' treated as 'supersede'");
346 		config->default_actions[i] = action;
347 		break;
348 	case TOK_REBOOT:
349 		if (parse_number(cfile, &config->reboot_interval, 0, INT32_MAX)
350 		    == 0)
351 			return;
352 		break;
353 	case TOK_REJECT:
354 		if (parse_reject_statement(cfile) == 0)
355 			return;
356 		break;
357 	case TOK_REQUEST:
358 		if (parse_option_list(cfile, &config->requested_option_count,
359 		    config->requested_options) == 0)
360 			return;
361 		break;
362 	case TOK_REQUIRE:
363 		if (parse_option_list(cfile, &config->required_option_count,
364 		    config->required_options) == 0)
365 			return;
366 		break;
367 	case TOK_RETRY:
368 		if (parse_number(cfile, &config->retry_interval, 0, INT32_MAX)
369 		    == 0)
370 			return;
371 		break;
372 	case TOK_SELECT_TIMEOUT:
373 		if (parse_number(cfile, &config->select_interval, 0, INT32_MAX)
374 		    == 0)
375 			return;
376 		break;
377 	case TOK_SEND:
378 		if (parse_option(cfile, &i, config->send_options) == 0)
379 			return;
380 		break;
381 	case TOK_SERVER_NAME:
382 		if (parse_string(cfile, &val) == 0)
383 			return;
384 		free(config->server_name);
385 		config->server_name = val;
386 		break;
387 	case TOK_SUPERSEDE:
388 		if (parse_option(cfile, &i, config->defaults) == 0)
389 			return;
390 		config->default_actions[i] = ACTION_SUPERSEDE;
391 		break;
392 	case TOK_TIMEOUT:
393 		if (parse_number(cfile, &config->offer_interval, 0, INT32_MAX)
394 		    == 0)
395 			return;
396 		break;
397 	case TOK_USELEASE:
398 		memset(list, 0, sizeof(list));
399 		count = 0;
400 		if (parse_option_list(cfile, &count, list) == 0)
401 			return;
402 		p = config->default_actions;
403 		if (count == 0) {
404 			for (i = 0; i < DHO_COUNT; i++) {
405 				free(config->defaults[i].data);
406 				config->defaults[i].data = NULL;
407 				config->defaults[i].len = 0;
408 				p[i] = ACTION_USELEASE;
409 			}
410 		} else {
411 			for (i = 0; i < count; i++) {
412 				free(config->defaults[list[i]].data);
413 				config->defaults[list[i]].data = NULL;
414 				config->defaults[list[i]].len = 0;
415 				p[list[i]] = ACTION_USELEASE;
416 			}
417 		}
418 		break;
419 	default:
420 		parse_warn("expecting statement.");
421 		skip_to_semi(cfile);
422 		return;
423 	}
424 
425 	parse_semi(cfile);
426 }
427 
428 int
parse_hex_octets(FILE * cfile,unsigned int * len,uint8_t ** buf)429 parse_hex_octets(FILE *cfile, unsigned int *len, uint8_t **buf)
430 {
431 	static uint8_t	 	 octets[1500];
432 	char			*val, *ep;
433 	unsigned long		 ulval;
434 	unsigned int		 i;
435 	int			 token;
436 
437 	i = 0;
438 	do {
439 		token = next_token(&val, cfile);
440 
441 		errno = 0;
442 		ulval = strtoul(val, &ep, 16);
443 		if ((val[0] == '\0' || *ep != '\0') ||
444 		    (errno == ERANGE && ulval == ULONG_MAX) ||
445 		    (ulval > UINT8_MAX))
446 			break;
447 		octets[i++] = ulval;
448 
449 		if (peek_token(NULL, cfile) == ';') {
450 			*buf = malloc(i);
451 			if (*buf == NULL)
452 				break;
453 			memcpy(*buf, octets, i);
454 			*len = i;
455 			return 1;
456 		}
457 		if (i == sizeof(octets))
458 			break;
459 		token = next_token(NULL, cfile);
460 	} while (token == ':');
461 
462 	parse_warn("expecting colon delimited list of hex octets.");
463 
464 	if (token != ';')
465 		skip_to_semi(cfile);
466 
467 	return 0;
468 }
469 
470 int
parse_domain_list(FILE * cfile,int * len,char ** dp)471 parse_domain_list(FILE *cfile, int *len, char **dp)
472 {
473 	uint8_t		 buf[DHCP_DOMAIN_SEARCH_LEN];
474 	char		*domain;
475 	int		 count, token;
476 
477 	memset(buf, 0, sizeof(buf));
478 	count = 0;
479 
480 	do {
481 		if (parse_string(cfile, &domain) == 0)
482 			return 0;
483 
484 		count++;
485 		if (count > DHCP_DOMAIN_SEARCH_CNT) {
486 			parse_warn("more than 6 search domains");
487 			break;
488 		}
489 
490 		if (count > 1)
491 			strlcat(buf, " ", sizeof(buf));
492 		if (strlcat(buf, domain, sizeof(buf)) >= sizeof(buf)) {
493 			parse_warn("domain list too long");
494 			break;
495 		}
496 
497 		token = peek_token(NULL, cfile);
498 		if (token == ';') {
499 			*dp = strdup(buf);
500 			if (*dp == NULL)
501 				fatal("domain name list");
502 			*len = strlen(*dp);
503 			return 1;
504 		}
505 		token = next_token(NULL, cfile);
506 		if (token != ',')
507 			parse_warn("';' or ',' expected");
508 	} while (token == ',');
509 
510 	if (token != ';')
511 		skip_to_semi(cfile);
512 
513 	return 0;
514 }
515 
516 /*
517  * option-list :==
518  *	  <nil>
519  *	| option-name
520  *	| option-list COMMA option-name
521  */
522 int
parse_option_list(FILE * cfile,int * count,uint8_t * optlist)523 parse_option_list(FILE *cfile, int *count, uint8_t *optlist)
524 {
525 	uint8_t		 list[DHO_COUNT];
526 	unsigned int	 ix, j;
527 	int		 i;
528 	int		 token;
529 	char		*val;
530 
531 	/* Empty list of option names is allowed, to re-init optlist. */
532 	if (peek_token(NULL, cfile) == ';') {
533 		memset(optlist, DHO_PAD, sizeof(list));
534 		*count = 0;
535 		return 1;
536 	}
537 
538 	memset(list, 0, sizeof(list));
539 	memcpy(list, optlist, *count);
540 	ix = *count;
541 	do {
542 		/* Next token must be an option name. */
543 		token = next_token(&val, cfile);
544 		i = name_to_code(val);
545 		if (i == DHO_END)
546 			break;
547 
548 		/* Avoid storing duplicate options in the list. */
549 		for (j = 0; j < ix && list[j] != i; j++)
550 			;
551 		if (j == ix)
552 			list[ix++] = i;
553 
554 		if (peek_token(NULL, cfile) == ';') {
555 			memcpy(optlist, list, sizeof(list));
556 			*count = ix;
557 			return 1;
558 		}
559 		token = next_token(NULL, cfile);
560 	} while (token == ',');
561 
562 	parse_warn("expecting comma delimited list of option names.");
563 
564 	if (token != ';')
565 		skip_to_semi(cfile);
566 
567 	return 0;
568 }
569 
570 /*
571  * interface :==
572  *	string LBRACE conf-decls RBRACE
573  */
574 int
parse_interface(FILE * cfile,char * name)575 parse_interface(FILE *cfile, char *name)
576 {
577 	char	*val;
578 	int	 token;
579 
580 	token = next_token(&val, cfile);
581 	if (token != TOK_STRING) {
582 		parse_warn("expecting string.");
583 		if (token != ';')
584 			skip_to_semi(cfile);
585 		return 0;
586 	}
587 
588 	if (strcmp(name, val) != 0) {
589 		skip_to_semi(cfile);
590 		return 1;
591 	}
592 
593 	token = next_token(&val, cfile);
594 	if (token != '{') {
595 		parse_warn("expecting '{'.");
596 		if (token != ';')
597 			skip_to_semi(cfile);
598 		return 0;
599 	}
600 
601 	for (;;) {
602 		token = peek_token(&val, cfile);
603 		if (token == EOF) {
604 			parse_warn("unterminated interface declaration.");
605 			return 0;
606 		}
607 		if (token == '}') {
608 			token = next_token(NULL, cfile);
609 			return 1;
610 		}
611 		parse_conf_decl(cfile, name);
612 	}
613 
614 	return 0;
615 }
616 
617 /*
618  * lease :== LEASE RBRACE lease-decls LBRACE
619  *
620  * lease-decls :==
621  *	  <nil>
622  *	| lease-decl
623  *	| lease-decls lease-decl
624  */
625 int
parse_lease(FILE * cfile,struct client_lease ** lp)626 parse_lease(FILE *cfile, struct client_lease **lp)
627 {
628 	struct client_lease	*lease;
629 	int			 token;
630 
631 	token = next_token(NULL, cfile);
632 	if (token == EOF)
633 		return 0;
634 	if (token != TOK_LEASE) {
635 		parse_warn("expecting lease");
636 		if (token != ';')
637 			skip_to_semi(cfile);
638 		return 0;
639 	}
640 
641 	token = next_token(NULL, cfile);
642 	if (token != '{') {
643 		parse_warn("expecting '{'.");
644 		if (token != ';')
645 			skip_to_semi(cfile);
646 		return 0;
647 	}
648 
649 	lease = calloc(1, sizeof(*lease));
650 	if (lease == NULL)
651 		fatal("lease");
652 
653 	for (;;) {
654 		token = peek_token(NULL, cfile);
655 		if (token == EOF) {
656 			parse_warn("unterminated lease.");
657 			free_client_lease(lease);
658 			break;
659 		}
660 		if (token == '}') {
661 			token = next_token(NULL, cfile);
662 			*lp = lease;
663 			return 1;
664 		}
665 		parse_lease_decl(cfile, lease);
666 	}
667 
668 	return 0;
669 }
670 
671 /*
672  * lease-decl :==
673  *	  BOOTP		SEMI
674  *	| EPOCH		number SEMI
675  *	| EXPIRE	<skip to semi> SEMI
676  *	| FILENAME	string SEMI
677  *	| FIXED_ADDR	ip_address SEMI
678  *	| INTERFACE	string SEMI
679  *	| NEXT_SERVER	string SEMI
680  *	| OPTION	option SEMI
681  *	| REBIND	<skip to semi> SEMI
682  *	| RENEW		<skip to semi> SEMI
683  *	| SERVER_NAME	string SEMI
684  *	| SSID		string SEMI
685  */
686 void
parse_lease_decl(FILE * cfile,struct client_lease * lease)687 parse_lease_decl(FILE *cfile, struct client_lease *lease)
688 {
689 	char		*val;
690 	unsigned int	 len;
691 	int		 i, token;
692 
693 	token = next_token(&val, cfile);
694 
695 	switch (token) {
696 	case TOK_BOOTP:
697 		/* 'bootp' is just a comment. See BOOTP_LEASE(). */
698 		break;
699 	case TOK_EPOCH:
700 		if (parse_number(cfile, &lease->epoch, INT64_MIN, INT64_MAX)
701 		    == 0)
702 			return;
703 		break;
704 	case TOK_EXPIRE:
705 		/* 'expire' is just a comment. See 'epoch'. */
706 		skip_to_semi(cfile);
707 		return;
708 	case TOK_FILENAME:
709 		if (parse_string(cfile, &val) == 0)
710 			return;
711 		free(lease->filename);
712 		lease->filename = val;
713 		break;
714 	case TOK_FIXED_ADDR:
715 		if (parse_ip_addr(cfile, &lease->address) == 0)
716 			return;
717 		break;
718 	case TOK_INTERFACE:
719 		/* 'interface' is just a comment. */
720 		skip_to_semi(cfile);
721 		return;
722 	case TOK_NEXT_SERVER:
723 		if (parse_ip_addr(cfile, &lease->next_server) == 0)
724 			return;
725 		break;
726 	case TOK_OPTION:
727 		if (parse_option(cfile, &i, lease->options) == 0)
728 			return;
729 		break;
730 	case TOK_REBIND:
731 	case TOK_RENEW:
732 		/* 'rebind' & 'renew' are just comments. See 'epoch'. */
733 		skip_to_semi(cfile);
734 		return;
735 	case TOK_SERVER_NAME:
736 		if (parse_string(cfile, &val) == 0)
737 			return;
738 		free(lease->server_name);
739 		lease->server_name = val;
740 		break;
741 	case TOK_SSID:
742 		if (parse_string(cfile, &val) == 0)
743 			return;
744 		len = strlen(val);
745 		if (len > sizeof(lease->ssid)) {
746 			free(val);
747 			parse_warn("ssid > 32 bytes");
748 			skip_to_semi(cfile);
749 			return;
750 		}
751 		memset(lease->ssid, 0, sizeof(lease->ssid));
752 		memcpy(lease->ssid, val, len);
753 		free(val);
754 		lease->ssid_len = len;
755 		break;
756 	default:
757 		parse_warn("expecting lease declaration.");
758 		skip_to_semi(cfile);
759 		return;
760 	}
761 
762 	parse_semi(cfile);
763 }
764 
765 /*
766  * option :==
767  *	option-name option-value
768  *
769  * option-value :==
770  *	  text
771  *	| hex-octets
772  *	| signed-32
773  *	| unsigned-32
774  *	| unsigned-16
775  *	| unsigned-8
776  *	| flag
777  *	| ip-address
778  *	| ip-address-array
779  *	| ip-address-pair-array
780  *	| uint16-array
781  *	| cidr-ip-address-array
782  */
783 int
parse_option(FILE * cfile,int * code,struct option_data * options)784 parse_option(FILE *cfile, int *code, struct option_data *options)
785 {
786 	uint8_t			 hunkbuf[1024], cidr[5], buf[4];
787 	struct in_addr		 ip_addr;
788 	uint8_t			*dp;
789 	char			*fmt, *val;
790 	long long		 number;
791 	unsigned int		 hunkix = 0;
792 	int			 i, freedp, len, token;
793 
794 	token = next_token(&val, cfile);
795 	i = name_to_code(val);
796 	if (i == DHO_END) {
797 		parse_warn("expecting option name.");
798 		skip_to_semi(cfile);
799 		return 0;
800 	}
801 
802 	/* Parse the option data. */
803 	do {
804 		for (fmt = code_to_format(i); *fmt != '\0'; fmt++) {
805 			if (*fmt == 'A')
806 				break;
807 			freedp = 0;
808 			switch (*fmt) {
809 			case 'X':
810 				if (peek_token(NULL, cfile) == TOK_STRING) {
811 					if (parse_string(cfile, (char **)&dp)
812 					    == 0)
813 						return 0;
814 					len = strlen(dp);
815 				} else if (parse_hex_octets(cfile, &len, &dp)
816 				    == 0)
817 					return 0;
818 				freedp = 1;
819 				break;
820 			case 't': /* Text string. */
821 				if (parse_string(cfile, (char **)&dp) == 0)
822 					return 0;
823 				len = strlen(dp);
824 				freedp = 1;
825 				break;
826 			case 'I': /* IP address. */
827 				if (parse_ip_addr(cfile, &ip_addr) == 0)
828 					return 0;
829 				len = sizeof(ip_addr);
830 				dp = (uint8_t *)&ip_addr;
831 				break;
832 			case 'l':	/* Signed 32-bit integer. */
833 				if (parse_number(cfile, &number, INT32_MIN,
834 				    INT32_MAX) == 0)
835 					return 0;
836 				number = htobe64(number);
837 				len = sizeof(int32_t);
838 				memcpy(buf, (char *)&number + (sizeof(number) - len), len);
839 				dp = buf;
840 				break;
841 			case 'L':	/* Unsigned 32-bit integer. */
842 				if (parse_number(cfile, &number, 0, UINT32_MAX) == 0)
843 					return 0;
844 				number = htobe64(number);
845 				len = sizeof(uint32_t);
846 				memcpy(buf, (char *)&number + (sizeof(number) - len), len);
847 				dp = buf;
848 				break;
849 			case 'S':	/* Unsigned 16-bit integer. */
850 				if (parse_number(cfile, &number, 0, UINT16_MAX) == 0)
851 					return 0;
852 				number = htobe64(number);
853 				len = sizeof(uint16_t);
854 				memcpy(buf, (char *)&number + (sizeof(number) - len), len);
855 				dp = buf;
856 				break;
857 			case 'B':	/* Unsigned 8-bit integer. */
858 				if (parse_number(cfile, &number, 0, UINT8_MAX) == 0)
859 					return 0;
860 				buf[0] = number;
861 				len = 1;
862 				dp = buf;
863 				break;
864 			case 'f': /* Boolean flag. */
865 				if (parse_boolean(cfile, buf) == 0)
866 					return 0;
867 				len = 1;
868 				dp = buf;
869 				break;
870 			case 'C':
871 				if (parse_cidr(cfile, cidr) == 0)
872 					return 0;
873 				len = 1 + (cidr[0] + 7) / 8;
874 				dp = cidr;
875 				break;
876 			case 'D':
877 				if (peek_token(NULL, cfile) == TOK_STRING) {
878 					if (parse_domain_list(cfile, &len,
879 					    (char **)&dp) == 0)
880 						return 0;
881 				} else {
882 					if (parse_hex_octets(cfile, &len, &dp)
883 					    == 0)
884 						return 0;
885 					val = rfc1035_as_string(dp, len);
886 					free(dp);
887 					dp = strdup(val);
888 					if (dp == NULL)
889 						fatal("RFC1035 hex octets");
890 					len = strlen(dp);
891 				}
892 				freedp = 1;
893 				break;
894 			default:
895 				log_warnx("%s: bad format %c in "
896 				    "parse_option_param", log_procname, *fmt);
897 				skip_to_semi(cfile);
898 				return 0;
899 			}
900 			if (dp != NULL && len > 0) {
901 				if (hunkix + len > sizeof(hunkbuf)) {
902 					if (freedp == 1)
903 						free(dp);
904 					parse_warn("option data buffer "
905 					    "overflow");
906 					skip_to_semi(cfile);
907 					return 0;
908 				}
909 				memcpy(&hunkbuf[hunkix], dp, len);
910 				hunkix += len;
911 				if (freedp == 1)
912 					free(dp);
913 			}
914 		}
915 		token = peek_token(NULL, cfile);
916 		if (*fmt == 'A' && token == ',')
917 			token = next_token(NULL, cfile);
918 	} while (*fmt == 'A' && token == ',');
919 
920 	free(options[i].data);
921 	options[i].data = malloc(hunkix);
922 	if (options[i].data == NULL)
923 		fatal("option data");
924 	memcpy(options[i].data, hunkbuf, hunkix);
925 	options[i].len = hunkix;
926 
927 	*code = i;
928 
929 	return 1;
930 }
931 
932 int
parse_reject_statement(FILE * cfile)933 parse_reject_statement(FILE *cfile)
934 {
935 	struct in_addr		 addr;
936 	struct reject_elem	*elem;
937 
938 	if (parse_ip_addr(cfile, &addr) == 0)
939 		return 0;
940 
941 	TAILQ_FOREACH(elem, &config->reject_list, next) {
942 		if (elem->addr.s_addr == addr.s_addr)
943 			return 1;
944 	}
945 
946 	elem = malloc(sizeof(*elem));
947 	if (elem == NULL)
948 		fatal("reject address");
949 	elem->addr = addr;
950 	TAILQ_INSERT_TAIL(&config->reject_list, elem, next);
951 
952 	return 1;
953 }
954 
955 void
apply_actions(uint8_t * actions)956 apply_actions(uint8_t *actions)
957 {
958 	int		 i;
959 
960 	for (i = 0; i < DHO_END; i++) {
961 		switch (actions[i]) {
962 		case ACTION_IGNORE:
963 			config->default_actions[i] = ACTION_IGNORE;
964 			free(config->defaults[i].data);
965 			config->defaults[i].data = NULL;
966 			config->defaults[i].len = 0;
967 			break;
968 		default:
969 			break;
970 		}
971 	}
972 }
973 
974 void
set_default_client_identifier(struct ether_addr * hwaddr)975 set_default_client_identifier(struct ether_addr *hwaddr)
976 {
977 	struct option_data	*opt;
978 
979 	/*
980 	 * Check both len && data so
981 	 *
982 	 *     send dhcp-client-identifier "";
983 	 *
984 	 * can be used to suppress sending the default client
985 	 * identifier.
986 	 */
987 	opt = &config->send_options[DHO_DHCP_CLIENT_IDENTIFIER];
988 	if (opt->len == 0 && opt->data == NULL) {
989 		opt->data = calloc(1, ETHER_ADDR_LEN + 1);
990 		if (opt->data == NULL)
991 			fatal("default client identifier");
992 		opt->data[0] = HTYPE_ETHER;
993 		memcpy(&opt->data[1], hwaddr->ether_addr_octet,
994 		    ETHER_ADDR_LEN);
995 		opt->len = ETHER_ADDR_LEN + 1;
996 	}
997 }
998 
999 void
set_default_hostname(void)1000 set_default_hostname(void)
1001 {
1002 	char			 hn[HOST_NAME_MAX + 1], *p;
1003 	struct option_data	*opt;
1004 	int			 rslt;
1005 
1006 	/*
1007 	 * Check both len && data so
1008 	 *
1009 	 *     send host-name "";
1010 	 *
1011 	 * can be used to suppress sending the default host
1012 	 * name.
1013 	 */
1014 	opt = &config->send_options[DHO_HOST_NAME];
1015 	if (opt->len == 0 && opt->data == NULL) {
1016 		rslt = gethostname(hn, sizeof(hn));
1017 		if (rslt == -1) {
1018 			log_warn("host-name");
1019 			return;
1020 		}
1021 		p = strchr(hn, '.');
1022 		if (p != NULL)
1023 			*p = '\0';
1024 		opt->data = strdup(hn);
1025 		if (opt->data == NULL)
1026 			fatal("default host-name");
1027 		opt->len = strlen(opt->data);
1028 	}
1029 }
1030