GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/dhclient/parse.c Lines: 0 153 0.0 %
Date: 2017-11-07 Branches: 0 98 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parse.c,v 1.63 2017/09/17 22:14:53 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/queue.h>
44
#include <sys/socket.h>
45
46
#include <net/if.h>
47
48
#include <netinet/in.h>
49
#include <netinet/if_ether.h>
50
51
#include <errno.h>
52
#include <limits.h>
53
#include <signal.h>
54
#include <stdio.h>
55
#include <stdlib.h>
56
#include <stdint.h>
57
#include <string.h>
58
#include <syslog.h>
59
#include <unistd.h>
60
#include <vis.h>
61
62
#include "dhcp.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
	int		 brace_count = 0;
87
88
	do {
89
		token = peek_token(NULL, cfile);
90
		if (token == '}') {
91
			if (brace_count > 0) {
92
				if (--brace_count == 0) {
93
					token = next_token(NULL, cfile);
94
					return;
95
				}
96
			} else
97
				return;
98
		} else if (token == '{') {
99
			brace_count++;
100
		} else if (token == ';' && brace_count == 0) {
101
			token = next_token(NULL, 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(NULL, cfile);
111
			return;
112
		}
113
		token = next_token(NULL, cfile);
114
	} while (token != EOF);
115
}
116
117
int
118
parse_semi(FILE *cfile)
119
{
120
	int token;
121
122
	token = next_token(NULL, cfile);
123
	if (token != ';') {
124
		parse_warn("expecting semicolon.");
125
		skip_to_semi(cfile);
126
		return 0;
127
	}
128
	return 1;
129
}
130
131
char *
132
parse_string(FILE *cfile, unsigned int *len)
133
{
134
	static char	 unvisbuf[1500];
135
	char		*val, *s;
136
	int		 i, token;
137
138
	token = next_token(&val, cfile);
139
	if (token != TOK_STRING) {
140
		parse_warn("expecting string.");
141
		if (token != ';')
142
			skip_to_semi(cfile);
143
		return NULL;
144
	}
145
146
	i = strnunvis(unvisbuf, val, sizeof(unvisbuf));
147
	if (i == -1) {
148
		parse_warn("could not unvis string");
149
		return NULL;
150
	}
151
	s = malloc(i+1);
152
	if (s == NULL)
153
		fatal("unvis string %s", val);
154
	memcpy(s, unvisbuf, i+1);	/* Copy the terminating NUL. */
155
	if (len != NULL)
156
		*len = i;
157
158
	return s;
159
}
160
161
/*
162
 * cidr :== ip-address "/" bit-count
163
 * ip-address :== NUMBER [ DOT NUMBER [ DOT NUMBER [ DOT NUMBER ] ] ]
164
 * bit-count :== 0..32
165
 */
166
int
167
parse_cidr(FILE *cfile, unsigned char *cidr)
168
{
169
	struct in_addr	 addr;
170
	int		 token;
171
	int		 len;
172
173
	token = '.';
174
	len = 0;
175
	for (token = '.'; token == '.'; token = next_token(NULL, cfile)) {
176
		if (parse_decimal(cfile, cidr + 1 + len, 'B') == 0)
177
			break;
178
		if (++len == sizeof(addr)) {
179
			token = next_token(NULL, cfile);
180
			break;
181
		}
182
	}
183
184
	if (len == 0) {
185
		parse_warn("expecting decimal value.");
186
		skip_to_semi(cfile);
187
		return 0;
188
	} else if (token != '/') {
189
		parse_warn("expecting '/'.");
190
		skip_to_semi(cfile);
191
		return 0;
192
	} else if (parse_decimal(cfile, cidr, 'B') == 0 || *cidr > 32) {
193
		parse_warn("expecting decimal value <= 32.");
194
		skip_to_semi(cfile);
195
		return 0;
196
	}
197
198
	return 1;
199
}
200
201
int
202
parse_ip_addr(FILE *cfile, struct in_addr *addr)
203
{
204
	struct in_addr	 buf;
205
	int		 len, token;
206
207
	token = '.';
208
	len = 0;
209
	for (token = '.'; token == '.'; token = next_token(NULL, cfile)) {
210
		if (parse_decimal(cfile, (unsigned char *)&buf + len, 'B') == 0)
211
			break;
212
		if (++len == sizeof(buf))
213
			break;
214
	}
215
216
	if (len == 4) {
217
		memcpy(addr, &buf, sizeof(*addr));
218
		return 1;
219
	} else if (token != '.') {
220
		parse_warn("expecting '.'.");
221
		skip_to_semi(cfile);
222
		return 0;
223
	} else {
224
		parse_warn("expecting decimal value.");
225
		skip_to_semi(cfile);
226
		return 0;
227
	}
228
}
229
230
/*
231
 * lease-time :== NUMBER SEMI
232
 */
233
void
234
parse_lease_time(FILE *cfile, time_t *timep)
235
{
236
	uint32_t	 value;
237
238
	if (parse_decimal(cfile, (char *)&value, 'L') == 0) {
239
		parse_warn("expecting unsigned 32-bit decimal value.");
240
		skip_to_semi(cfile);
241
		return;
242
	}
243
244
	*timep = betoh32(value);
245
246
	parse_semi(cfile);
247
}
248
249
int
250
parse_boolean(FILE *cfile, unsigned char *buf)
251
{
252
	char	*val;
253
	int	 token;
254
255
	token = next_token(&val, cfile);
256
	if (is_identifier(token) != 0) {
257
		if (strcasecmp(val, "true") == 0 ||
258
		    strcasecmp(val, "on") == 0) {
259
			buf[0] = 1;
260
			return 1;
261
		}
262
		if (strcasecmp(val, "false") == 0 ||
263
		    strcasecmp(val, "off") == 0) {
264
			buf[0] = 0;
265
			return 1;
266
		}
267
	}
268
269
	return 0;
270
}
271
272
int
273
parse_decimal(FILE *cfile, unsigned char *buf, char fmt)
274
{
275
	const char	*errstr;
276
	char		*val;
277
	int		 bytes, token;
278
	long long	 numval, low, high;
279
280
	token = next_token(&val, cfile);
281
282
	switch (fmt) {
283
	case 'l':	/* Signed 32-bit integer. */
284
		low = INT32_MIN;
285
		high = INT32_MAX;
286
		bytes = 4;
287
		break;
288
	case 'L':	/* Unsigned 32-bit integer. */
289
		low = 0;
290
		high = UINT32_MAX;
291
		bytes = 4;
292
		break;
293
	case 'S':	/* Unsigned 16-bit integer. */
294
		low = 0;
295
		high = UINT16_MAX;
296
		bytes = 2;
297
		break;
298
	case 'B':	/* Unsigned 8-bit integer. */
299
		low = 0;
300
		high = UINT8_MAX;
301
		bytes = 1;
302
		break;
303
	default:
304
		return 0;
305
	}
306
307
	numval = strtonum(val, low, high, &errstr);
308
	if (errstr != NULL)
309
		return 0;
310
311
	numval = htobe64(numval);
312
	memcpy(buf, (char *)&numval + (sizeof(numval) - bytes), bytes);
313
314
	return 1;
315
}
316
317
int
318
parse_hex(FILE *cfile, unsigned char *buf)
319
{
320
	char		*val, *ep;
321
	unsigned long	 ulval;
322
	int		 token;
323
324
	token = next_token(&val, cfile);
325
326
	errno = 0;
327
	ulval = strtoul(val, &ep, 16);
328
	if ((val[0] == '\0' || *ep != '\0') ||
329
	    (errno == ERANGE && ulval == ULONG_MAX) ||
330
	    (ulval > UINT8_MAX))
331
		return 0;
332
333
	buf[0] = ulval;
334
335
	return 1;
336
}
337
338
/*
339
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
340
 *		NUMBER COLON NUMBER COLON NUMBER UTC SEMI
341
 *
342
 * Dates are always in UTC; first number is day of week; next is
343
 * year/month/day; next is hours:minutes:seconds on a 24-hour
344
 * clock.
345
 */
346
time_t
347
parse_date(FILE *cfile)
348
{
349
	char timestr[26]; /* "w yyyy/mm/dd hh:mm:ss UTC" */
350
	struct tm	 tm;
351
	char		*val, *p;
352
	size_t		 n;
353
	time_t		 guess;
354
	int		 token;
355
356
	memset(timestr, 0, sizeof(timestr));
357
358
	do {
359
		token = peek_token(NULL, cfile);
360
		switch (token) {
361
		case TOK_NAME:
362
		case TOK_NUMBER:
363
		case TOK_NUMBER_OR_NAME:
364
		case '/':
365
		case ':':
366
			token = next_token(&val, cfile);
367
			n = strlcat(timestr, val, sizeof(timestr));
368
			if (n >= sizeof(timestr)) {
369
				/* XXX Will break after year 9999! */
370
				parse_warn("time string too long");
371
				skip_to_semi(cfile);
372
				return 0;
373
			}
374
			break;
375
		case';':
376
			break;
377
		default:
378
			parse_warn("invalid time string");
379
			skip_to_semi(cfile);
380
			return 0;
381
		}
382
	} while (token != ';');
383
384
	parse_semi(cfile);
385
386
	memset(&tm, 0, sizeof(tm));	/* 'cuz strptime ignores tm_isdt. */
387
	p = strptime(timestr, DB_TIMEFMT, &tm);
388
	if (p == NULL || *p != '\0') {
389
		parse_warn("unparseable time string");
390
		return 0;
391
	}
392
393
	guess = timegm(&tm);
394
	if (guess == -1) {
395
		parse_warn("time could not be represented");
396
		return 0;
397
	}
398
399
	return guess;
400
}
401
402
void
403
parse_warn(char *msg)
404
{
405
	static char	 spaces[81];
406
	unsigned int	 i;
407
408
	log_warnx("%s: %s line %d: %s", log_procname, tlname, lexline, msg);
409
	log_warnx("%s: %s", log_procname, token_line);
410
	if ((unsigned int)lexchar < sizeof(spaces)) {
411
		memset(spaces, 0, sizeof(spaces));
412
		for (i = 0; (int)i < lexchar - 1; i++) {
413
			if (token_line[i] == '\t')
414
				spaces[i] = '\t';
415
			else
416
				spaces[i] = ' ';
417
		}
418
	}
419
	log_warnx("%s: %s^", log_procname, spaces);
420
}