GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/dhclient/parse.c Lines: 0 160 0.0 %
Date: 2016-12-06 Branches: 0 108 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parse.c,v 1.40 2016/02/06 19:30:52 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
59
#include "dhcp.h"
60
#include "dhcpd.h"
61
#include "dhctoken.h"
62
63
/*
64
 * Skip to the semicolon ending the current statement.   If we encounter
65
 * braces, the matching closing brace terminates the statement.   If we
66
 * encounter a right brace but haven't encountered a left brace, return
67
 * leaving the brace in the token buffer for the caller.   If we see a
68
 * semicolon and haven't seen a left brace, return.   This lets us skip
69
 * over:
70
 *
71
 *	statement;
72
 *	statement foo bar { }
73
 *	statement foo bar { statement { } }
74
 *	statement}
75
 *
76
 *	...et cetera.
77
 */
78
void
79
skip_to_semi(FILE *cfile)
80
{
81
	int		 token;
82
	int		 brace_count = 0;
83
84
	do {
85
		token = peek_token(NULL, cfile);
86
		if (token == '}') {
87
			if (brace_count) {
88
				if (!--brace_count) {
89
					token = next_token(NULL, cfile);
90
					return;
91
				}
92
			} else
93
				return;
94
		} else if (token == '{') {
95
			brace_count++;
96
		} else if (token == ';' && !brace_count) {
97
			token = next_token(NULL, cfile);
98
			return;
99
		} else if (token == '\n') {
100
			/*
101
			 * EOL only happens when parsing
102
			 * /etc/resolv.conf, and we treat it like a
103
			 * semicolon because the resolv.conf file is
104
			 * line-oriented.
105
			 */
106
			token = next_token(NULL, cfile);
107
			return;
108
		}
109
		token = next_token(NULL, cfile);
110
	} while (token != EOF);
111
}
112
113
int
114
parse_semi(FILE *cfile)
115
{
116
	int token;
117
118
	token = next_token(NULL, cfile);
119
	if (token != ';') {
120
		parse_warn("expecting semicolon.");
121
		skip_to_semi(cfile);
122
		return (0);
123
	}
124
	return (1);
125
}
126
127
/*
128
 * string-parameter :== STRING SEMI
129
 */
130
char *
131
parse_string(FILE *cfile)
132
{
133
	char *val, *s;
134
	int token;
135
136
	token = next_token(&val, cfile);
137
	if (token != TOK_STRING) {
138
		parse_warn("filename must be a string");
139
		if (token != ';')
140
			skip_to_semi(cfile);
141
		return (NULL);
142
	}
143
	s = strdup(val);
144
	if (!s)
145
		error("no memory for string %s.", val);
146
147
	if (!parse_semi(cfile)) {
148
		free(s);
149
		return (NULL);
150
	}
151
	return (s);
152
}
153
154
/* cidr :== ip-address "/" bit-count
155
 * ip-address :== NUMBER [ DOT NUMBER [ DOT NUMBER [ DOT NUMBER ] ] ]
156
 * bit-count :== 0..32
157
 */
158
int
159
parse_cidr(FILE *cfile, unsigned char *cidr)
160
{
161
	struct in_addr addr;
162
	int token;
163
	int len;
164
165
	token = '.';
166
	len = 0;
167
	for (token = '.'; token == '.'; token = next_token(NULL, cfile)) {
168
		if (!parse_decimal(cfile, cidr + 1 + len, 'B'))
169
			break;
170
		if (++len == sizeof(addr)) {
171
			token = next_token(NULL, cfile);
172
			break;
173
		}
174
	}
175
176
	if (!len) {
177
		parse_warn("expecting CIDR subnet.");
178
		skip_to_semi(cfile);
179
		return (0);
180
	} else if (token != '/') {
181
		parse_warn("expecting '/'.");
182
		skip_to_semi(cfile);
183
		return (0);
184
	} else if (!parse_decimal(cfile, cidr, 'B') || *cidr > 32) {
185
		parse_warn("Expecting CIDR prefix length.");
186
		skip_to_semi(cfile);
187
		return (0);
188
	}
189
190
	return (1);
191
}
192
193
int
194
parse_ip_addr(FILE *cfile, struct in_addr *addr)
195
{
196
	struct in_addr buf;
197
	int len, token;
198
199
	token = '.';
200
	len = 0;
201
	for (token = '.'; token == '.'; token = next_token(NULL, cfile)) {
202
		if (!parse_decimal(cfile, (unsigned char *)&buf + len, 'B'))
203
			break;
204
		if (++len == sizeof(buf))
205
			break;
206
	}
207
208
	if (len == 4) {
209
		memcpy(addr, &buf, sizeof(*addr));
210
		return (1);
211
	} else if (token != '.') {
212
		parse_warn("expecting '.'.");
213
		skip_to_semi(cfile);
214
		return (0);
215
	} else {
216
		parse_warn("expecting decimal octet.");
217
		skip_to_semi(cfile);
218
		return (0);
219
	}
220
}
221
222
/*
223
 * ETHERNET :== 'ethernet' NUMBER:NUMBER:NUMBER:NUMBER:NUMBER:NUMBER
224
 */
225
void
226
parse_ethernet(FILE *cfile, struct ether_addr *hardware)
227
{
228
	struct ether_addr buf;
229
	int len, token;
230
231
	token = next_token(NULL, cfile);
232
	if (token != TOK_ETHERNET) {
233
		parse_warn("expecting 'ethernet'.");
234
		if (token != ';')
235
			skip_to_semi(cfile);
236
		return;
237
	}
238
239
	len = 0;
240
	for (token = ':'; token == ':'; token = next_token(NULL, cfile)) {
241
		if (!parse_hex(cfile, &buf.ether_addr_octet[len]))
242
			break;
243
		if (++len == sizeof(buf.ether_addr_octet))
244
			break;
245
	}
246
247
	if (len == 6) {
248
		if (parse_semi(cfile))
249
		    memcpy(hardware, &buf, sizeof(*hardware));
250
	} else if (token != ':') {
251
		parse_warn("expecting ':'.");
252
		skip_to_semi(cfile);
253
	} else {
254
		parse_warn("expecting hex octet.");
255
		skip_to_semi(cfile);
256
	}
257
}
258
259
/*
260
 * lease-time :== NUMBER SEMI
261
 */
262
void
263
parse_lease_time(FILE *cfile, time_t *timep)
264
{
265
	u_int32_t value;
266
267
	if (!parse_decimal(cfile, (char *)&value, 'L')) {
268
		parse_warn("expecting unsigned 32-bit decimal value.");
269
		skip_to_semi(cfile);
270
		return;
271
	}
272
273
	*timep = betoh32(value);
274
275
	parse_semi(cfile);
276
}
277
278
int
279
parse_decimal(FILE *cfile, unsigned char *buf, char fmt)
280
{
281
	char *val;
282
	const char *errstr;
283
	int bytes, token;
284
	long long numval, low, high;
285
286
	token = next_token(&val, cfile);
287
288
	switch (fmt) {
289
	case 'l':	/* Signed 32-bit integer. */
290
		low = INT32_MIN;
291
		high = INT32_MAX;
292
		bytes = 4;
293
		break;
294
	case 'L':	/* Unsigned 32-bit integer. */
295
		low = 0;
296
		high = UINT32_MAX;
297
		bytes = 4;
298
		break;
299
	case 'S':	/* Unsigned 16-bit integer. */
300
		low = 0;
301
		high = UINT16_MAX;
302
		bytes = 2;
303
		break;
304
	case 'B':	/* Unsigned 8-bit integer. */
305
		low = 0;
306
		high = UINT8_MAX;
307
		bytes = 1;
308
		break;
309
	default:
310
		return (0);
311
	}
312
313
	numval = strtonum(val, low, high, &errstr);
314
	if (errstr)
315
		return (0);
316
317
	numval = htobe64(numval);
318
	memcpy(buf, (char *)&numval + (sizeof(numval) - bytes), bytes);
319
320
	return (1);
321
}
322
323
int
324
parse_hex(FILE *cfile, unsigned char *buf)
325
{
326
	char *val, *ep;
327
	int token;
328
	unsigned long ulval;
329
330
	token = next_token(&val, cfile);
331
332
	errno = 0;
333
	ulval = strtoul(val, &ep, 16);
334
	if ((val[0] == '\0' || *ep != '\0') ||
335
	    (errno == ERANGE && ulval == ULONG_MAX) ||
336
	    (ulval > UINT8_MAX))
337
		return (0);
338
339
	buf[0] = ulval;
340
341
	return (1);
342
}
343
344
/*
345
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
346
 *		NUMBER COLON NUMBER COLON NUMBER UTC SEMI
347
 *
348
 * Dates are always in UTC; first number is day of week; next is
349
 * year/month/day; next is hours:minutes:seconds on a 24-hour
350
 * clock.
351
 */
352
time_t
353
parse_date(FILE *cfile)
354
{
355
	struct tm tm;
356
	char timestr[26]; /* "w yyyy/mm/dd hh:mm:ss UTC" */
357
	char *val, *p;
358
	size_t n;
359
	time_t guess;
360
	int token;
361
362
	memset(timestr, 0, sizeof(timestr));
363
364
	do {
365
		token = peek_token(NULL, cfile);
366
		switch (token) {
367
		case TOK_NAME:
368
		case TOK_NUMBER:
369
		case TOK_NUMBER_OR_NAME:
370
		case '/':
371
		case ':':
372
			token = next_token(&val, cfile);
373
			n = strlcat(timestr, val, sizeof(timestr));
374
			if (n >= sizeof(timestr)) {
375
				/* XXX Will break after year 9999! */
376
				parse_warn("time string too long");
377
				skip_to_semi(cfile);
378
				return (0);
379
			}
380
			break;
381
		case';':
382
			break;
383
		default:
384
			parse_warn("invalid time string");
385
			skip_to_semi(cfile);
386
			return (0);
387
		}
388
	} while (token != ';');
389
390
	parse_semi(cfile);
391
392
	memset(&tm, 0, sizeof(tm));	/* 'cuz strptime ignores tm_isdt. */
393
	p = strptime(timestr, DB_TIMEFMT, &tm);
394
	if (p == NULL || *p != '\0') {
395
		p = strptime(timestr, OLD_DB_TIMEFMT, &tm);
396
		if (p == NULL || *p != '\0') {
397
			p = strptime(timestr, BAD_DB_TIMEFMT, &tm);
398
			if (p == NULL || *p != '\0') {
399
				parse_warn("unparseable time string");
400
				return (0);
401
			}
402
		}
403
	}
404
405
	guess = timegm(&tm);
406
	if (guess == -1) {
407
		parse_warn("time could not be represented");
408
		return (0);
409
	}
410
411
	return (guess);
412
}