GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dhcpd/parse.c Lines: 0 253 0.0 %
Date: 2017-11-07 Branches: 0 145 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parse.c,v 1.26 2017/02/16 00:24:43 krw Exp $	*/
2
3
/* Common parser code for dhcpd and dhclient. */
4
5
/*
6
 * Copyright (c) 1995, 1996, 1997, 1998 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/types.h>
44
#include <sys/socket.h>
45
46
#include <net/if.h>
47
48
#include <netinet/in.h>
49
50
#include <ctype.h>
51
#include <errno.h>
52
#include <stdarg.h>
53
#include <stdint.h>
54
#include <stdio.h>
55
#include <stdlib.h>
56
#include <string.h>
57
#include <syslog.h>
58
#include <time.h>
59
#include <unistd.h>
60
61
#include "dhcp.h"
62
#include "tree.h"
63
#include "dhcpd.h"
64
#include "dhctoken.h"
65
#include "log.h"
66
67
/*
68
 * Skip to the semicolon ending the current statement.   If we encounter
69
 * braces, the matching closing brace terminates the statement.   If we
70
 * encounter a right brace but haven't encountered a left brace, return
71
 * leaving the brace in the token buffer for the caller.   If we see a
72
 * semicolon and haven't seen a left brace, return.   This lets us skip
73
 * over:
74
 *
75
 *	statement;
76
 *	statement foo bar { }
77
 *	statement foo bar { statement { } }
78
 *	statement}
79
 *
80
 *	...et cetera.
81
 */
82
void
83
skip_to_semi(FILE *cfile)
84
{
85
	int		 token;
86
	char		*val;
87
	int		 brace_count = 0;
88
89
	do {
90
		token = peek_token(&val, cfile);
91
		if (token == '}') {
92
			if (brace_count) {
93
				token = next_token(&val, cfile);
94
				if (!--brace_count)
95
					return;
96
			} else
97
				return;
98
		} else if (token == '{') {
99
			brace_count++;
100
		} else if (token == ';' && !brace_count) {
101
			token = next_token(&val, cfile);
102
			return;
103
		} else if (token == '\n') {
104
			/*
105
			 * EOL only happens when parsing
106
			 * /etc/resolv.conf, and we treat it like a
107
			 * semicolon because the resolv.conf file is
108
			 * line-oriented.
109
			 */
110
			token = next_token(&val, cfile);
111
			return;
112
		}
113
		token = next_token(&val, cfile);
114
	} while (token != EOF);
115
}
116
117
int
118
parse_semi(FILE *cfile)
119
{
120
	int token;
121
	char *val;
122
123
	token = next_token(&val, cfile);
124
	if (token != ';') {
125
		parse_warn("semicolon expected.");
126
		skip_to_semi(cfile);
127
		return (0);
128
	}
129
	return (1);
130
}
131
132
/*
133
 * string-parameter :== STRING SEMI
134
 */
135
char *
136
parse_string(FILE *cfile)
137
{
138
	char *val, *s;
139
	int token;
140
141
	token = next_token(&val, cfile);
142
	if (token != TOK_STRING) {
143
		parse_warn("filename must be a string");
144
		skip_to_semi(cfile);
145
		return (NULL);
146
	}
147
	s = strdup(val);
148
	if (s == NULL)
149
		fatalx("no memory for string %s.", val);
150
151
	if (!parse_semi(cfile)) {
152
		free(s);
153
		return (NULL);
154
	}
155
	return (s);
156
}
157
158
/*
159
 * hostname :== identifier | hostname DOT identifier
160
 */
161
char *
162
parse_host_name(FILE *cfile)
163
{
164
	char *val, *s, *t;
165
	int token, len = 0;
166
	pair c = NULL;
167
168
	/* Read a dotted hostname... */
169
	do {
170
		/* Read a token, which should be an identifier. */
171
		token = next_token(&val, cfile);
172
		if (!is_identifier(token)) {
173
			parse_warn("expecting an identifier in hostname");
174
			skip_to_semi(cfile);
175
			return (NULL);
176
		}
177
		/* Store this identifier... */
178
		s = strdup(val);
179
		if (s == NULL)
180
			fatalx("can't allocate temp space for hostname.");
181
		c = cons((caddr_t) s, c);
182
		len += strlen(s) + 1;
183
		/*
184
		 * Look for a dot; if it's there, keep going, otherwise
185
		 * we're done.
186
		 */
187
		token = peek_token(&val, cfile);
188
		if (token == '.')
189
			token = next_token(&val, cfile);
190
	} while (token == '.');
191
192
	/* Assemble the hostname together into a string. */
193
	if (!(s = malloc(len)))
194
		fatalx("can't allocate space for hostname.");
195
	t = s + len;
196
	*--t = '\0';
197
	while (c) {
198
		pair cdr = c->cdr;
199
		int l = strlen((char *)c->car);
200
201
		t -= l;
202
		memcpy(t, (char *)c->car, l);
203
		/* Free up temp space. */
204
		free(c->car);
205
		free(c);
206
		c = cdr;
207
		if (t != s)
208
			*--t = '.';
209
	}
210
	return (s);
211
}
212
213
/*
214
 * hardware-parameter :== HARDWARE ETHERNET csns SEMI
215
 * csns :== NUMBER | csns COLON NUMBER
216
 */
217
void
218
parse_hardware_param(FILE *cfile, struct hardware *hardware)
219
{
220
	char *val;
221
	int token, hlen;
222
	unsigned char *t;
223
224
	token = next_token(&val, cfile);
225
	switch (token) {
226
	case TOK_ETHERNET:
227
		hardware->htype = HTYPE_ETHER;
228
		break;
229
	case TOK_IPSEC_TUNNEL:
230
		hardware->htype = HTYPE_IPSEC_TUNNEL;
231
		break;
232
	default:
233
		parse_warn("expecting a network hardware type");
234
		skip_to_semi(cfile);
235
		return;
236
	}
237
238
	/*
239
	 * Parse the hardware address information.   Technically, it
240
	 * would make a lot of sense to restrict the length of the data
241
	 * we'll accept here to the length of a particular hardware
242
	 * address type.   Unfortunately, there are some broken clients
243
	 * out there that put bogus data in the chaddr buffer, and we
244
	 * accept that data in the lease file rather than simply failing
245
	 * on such clients.   Yuck.
246
	 */
247
	hlen = 0;
248
	t = parse_numeric_aggregate(cfile, NULL, &hlen, ':', 16, 8);
249
	if (!t)
250
		return;
251
	if (hlen > sizeof(hardware->haddr)) {
252
		free(t);
253
		parse_warn("hardware address too long");
254
	} else {
255
		hardware->hlen = hlen;
256
		memcpy((unsigned char *)&hardware->haddr[0], t,
257
		    hardware->hlen);
258
		if (hlen < sizeof(hardware->haddr))
259
			memset(&hardware->haddr[hlen], 0,
260
			    sizeof(hardware->haddr) - hlen);
261
		free(t);
262
	}
263
264
	token = next_token(&val, cfile);
265
	if (token != ';') {
266
		parse_warn("expecting semicolon.");
267
		skip_to_semi(cfile);
268
	}
269
}
270
271
/*
272
 * lease-time :== NUMBER SEMI
273
 */
274
void
275
parse_lease_time(FILE *cfile, time_t *timep)
276
{
277
	const char *errstr;
278
	char *val;
279
	uint32_t value;
280
	int token;
281
282
	token = next_token(&val, cfile);
283
284
	value = strtonum(val, 0, UINT32_MAX, &errstr);
285
	if (errstr) {
286
		parse_warn("lease time is %s: %s", errstr, val);
287
		skip_to_semi(cfile);
288
		return;
289
	}
290
291
	*timep = value;
292
293
	parse_semi(cfile);
294
}
295
296
/*
297
 * No BNF for numeric aggregates - that's defined by the caller.  What
298
 * this function does is to parse a sequence of numbers separated by the
299
 * token specified in separator.  If max is zero, any number of numbers
300
 * will be parsed; otherwise, exactly max numbers are expected.  Base
301
 * and size tell us how to internalize the numbers once they've been
302
 * tokenized.
303
 */
304
unsigned char *
305
parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max,
306
    int separator, int base, int size)
307
{
308
	char *val, *t;
309
	int token, count = 0;
310
	unsigned char *bufp = buf, *s = NULL;
311
	pair c = NULL;
312
313
	if (!bufp && *max) {
314
		bufp = malloc(*max * size / 8);
315
		if (!bufp)
316
			fatalx("can't allocate space for numeric aggregate");
317
	} else
318
		s = bufp;
319
320
	do {
321
		if (count) {
322
			token = peek_token(&val, cfile);
323
			if (token != separator) {
324
				if (!*max)
325
					break;
326
				if (token != '{' && token != '}')
327
					token = next_token(&val, cfile);
328
				parse_warn("too few numbers.");
329
				if (token != ';')
330
					skip_to_semi(cfile);
331
				return (NULL);
332
			}
333
			token = next_token(&val, cfile);
334
		}
335
		token = next_token(&val, cfile);
336
337
		if (token == EOF) {
338
			parse_warn("unexpected end of file");
339
			break;
340
		}
341
		if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
342
			parse_warn("expecting numeric value.");
343
			skip_to_semi(cfile);
344
			return (NULL);
345
		}
346
		/*
347
		 * If we can, convert the number now; otherwise, build a
348
		 * linked list of all the numbers.
349
		 */
350
		if (s) {
351
			convert_num(s, val, base, size);
352
			s += size / 8;
353
		} else {
354
			t = strdup(val);
355
			if (t == NULL)
356
				fatalx("no temp space for number.");
357
			c = cons(t, c);
358
		}
359
	} while (++count != *max);
360
361
	/* If we had to cons up a list, convert it now. */
362
	if (c) {
363
		bufp = malloc(count * size / 8);
364
		if (!bufp)
365
			fatalx("can't allocate space for numeric aggregate.");
366
		s = bufp + count - size / 8;
367
		*max = count;
368
	}
369
	while (c) {
370
		pair		cdr = c->cdr;
371
		convert_num(s, (char *)c->car, base, size);
372
		s -= size / 8;
373
		/* Free up temp space. */
374
		free(c->car);
375
		free(c);
376
		c = cdr;
377
	}
378
	return (bufp);
379
}
380
381
void
382
convert_num(unsigned char *buf, char *str, int base, int size)
383
{
384
	int negative = 0, tval, max;
385
	u_int32_t val = 0;
386
	char *ptr = str;
387
388
	if (*ptr == '-') {
389
		negative = 1;
390
		ptr++;
391
	}
392
393
	/* If base wasn't specified, figure it out from the data. */
394
	if (!base) {
395
		if (ptr[0] == '0') {
396
			if (ptr[1] == 'x') {
397
				base = 16;
398
				ptr += 2;
399
			} else if (isascii((unsigned char)ptr[1]) &&
400
			    isdigit((unsigned char)ptr[1])) {
401
				base = 8;
402
				ptr += 1;
403
			} else
404
				base = 10;
405
		} else
406
			base = 10;
407
	}
408
409
	do {
410
		tval = *ptr++;
411
		/* XXX assumes ASCII... */
412
		if (tval >= 'a')
413
			tval = tval - 'a' + 10;
414
		else if (tval >= 'A')
415
			tval = tval - 'A' + 10;
416
		else if (tval >= '0')
417
			tval -= '0';
418
		else {
419
			log_warnx("Bogus number: %s.", str);
420
			break;
421
		}
422
		if (tval >= base) {
423
			log_warnx("Bogus number: %s: digit %d not in base %d",
424
			    str, tval, base);
425
			break;
426
		}
427
		val = val * base + tval;
428
	} while (*ptr);
429
430
	if (negative)
431
		max = (1 << (size - 1));
432
	else
433
		max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
434
	if (val > max) {
435
		switch (base) {
436
		case 8:
437
			log_warnx("value %s%o exceeds max (%d) for precision.",
438
			    negative ? "-" : "", val, max);
439
			break;
440
		case 16:
441
			log_warnx("value %s%x exceeds max (%d) for precision.",
442
			    negative ? "-" : "", val, max);
443
			break;
444
		default:
445
			log_warnx("value %s%u exceeds max (%d) for precision.",
446
			    negative ? "-" : "", val, max);
447
			break;
448
		}
449
	}
450
451
	if (negative) {
452
		switch (size) {
453
		case 8:
454
			*buf = -(unsigned long)val;
455
			break;
456
		case 16:
457
			putShort(buf, -(unsigned long)val);
458
			break;
459
		case 32:
460
			putLong(buf, -(unsigned long)val);
461
			break;
462
		default:
463
			log_warnx("Unexpected integer size: %d", size);
464
			break;
465
		}
466
	} else {
467
		switch (size) {
468
		case 8:
469
			*buf = (u_int8_t)val;
470
			break;
471
		case 16:
472
			putUShort(buf, (u_int16_t)val);
473
			break;
474
		case 32:
475
			putULong(buf, val);
476
			break;
477
		default:
478
			log_warnx("Unexpected integer size: %d", size);
479
			break;
480
		}
481
	}
482
}
483
484
/*
485
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
486
 *		NUMBER COLON NUMBER COLON NUMBER UTC SEMI
487
 *
488
 * Dates are always in UTC; first number is day of week; next is
489
 * year/month/day; next is hours:minutes:seconds on a 24-hour
490
 * clock.
491
 */
492
time_t
493
parse_date(FILE *cfile)
494
{
495
	struct tm tm;
496
	char timestr[26]; /* "w yyyy/mm/dd hh:mm:ss UTC" */
497
	char *val, *p;
498
	size_t n;
499
	time_t guess;
500
	int token;
501
502
	memset(timestr, 0, sizeof(timestr));
503
504
	do {
505
		token = peek_token(NULL, cfile);
506
		switch (token) {
507
		case TOK_NAME:
508
		case TOK_NUMBER:
509
		case TOK_NUMBER_OR_NAME:
510
		case '/':
511
		case ':':
512
			token = next_token(&val, cfile);
513
			n = strlcat(timestr, val, sizeof(timestr));
514
			if (n >= sizeof(timestr)) {
515
				/* XXX Will break after year 9999! */
516
				parse_warn("time string too long");
517
				skip_to_semi(cfile);
518
				return (0);
519
			}
520
			break;
521
		case';':
522
			break;
523
		default:
524
			parse_warn("invalid time string");
525
			skip_to_semi(cfile);
526
			return (0);
527
		}
528
	} while (token != ';');
529
530
	parse_semi(cfile);
531
532
	memset(&tm, 0, sizeof(tm));	/* 'cuz strptime ignores tm_isdt. */
533
	p = strptime(timestr, DB_TIMEFMT, &tm);
534
	if (p == NULL || *p != '\0') {
535
		p = strptime(timestr, OLD_DB_TIMEFMT, &tm);
536
		if (p == NULL || *p != '\0') {
537
			parse_warn("unparseable time string");
538
			return (0);
539
		}
540
	}
541
542
	guess = timegm(&tm);
543
	if (guess == -1) {
544
		parse_warn("time could not be represented");
545
		return (0);
546
	}
547
548
	return (guess);
549
}
550
551
int warnings_occurred;
552
553
int
554
parse_warn(char *fmt, ...)
555
{
556
	static char fbuf[1024];
557
	static char mbuf[1024];
558
	static char spaces[81];
559
	va_list list;
560
	int i;
561
562
	snprintf(fbuf, sizeof(fbuf), "%s line %d: %s", tlname, lexline, mbuf);
563
	va_start(list, fmt);
564
	vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
565
	va_end(list);
566
567
	log_warnx("%s", mbuf);
568
	log_warnx("%s", token_line);
569
	if (lexchar < sizeof(spaces)) {
570
		memset(spaces, 0, sizeof(spaces));
571
		for (i = 0; i < lexchar - 1; i++) {
572
			if (token_line[i] == '\t')
573
				spaces[i] = '\t';
574
			else
575
				spaces[i] = ' ';
576
		}
577
	}
578
	log_warnx("%s^", spaces);
579
580
	warnings_occurred = 1;
581
582
	return (0);
583
}