diff options
Diffstat (limited to 'contrib/isc-dhcp/common/parse.c')
| -rw-r--r-- | contrib/isc-dhcp/common/parse.c | 643 | 
1 files changed, 643 insertions, 0 deletions
diff --git a/contrib/isc-dhcp/common/parse.c b/contrib/isc-dhcp/common/parse.c new file mode 100644 index 000000000000..97bfa877b17e --- /dev/null +++ b/contrib/isc-dhcp/common/parse.c @@ -0,0 +1,643 @@ +/* parse.c + +   Common parser code for dhcpd and dhclient. */ + +/* + * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + *    of its contributors may be used to endorse or promote products derived + *    from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie + * Enterprises.  To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''.  To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: parse.c,v 1.2.2.1 1998/06/25 21:11:31 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.  All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include "dhctoken.h" + +/* Skip to the semicolon ending the current statement.   If we encounter +   braces, the matching closing brace terminates the statement.   If we +   encounter a right brace but haven't encountered a left brace, return +   leaving the brace in the token buffer for the caller.   If we see a +   semicolon and haven't seen a left brace, return.   This lets us skip +   over: + +   	statement; +	statement foo bar { } +	statement foo bar { statement { } } +	statement} +  +	...et cetera. */ + +void skip_to_semi (cfile) +	FILE *cfile; +{ +	int token; +	char *val; +	int brace_count = 0; + +	do { +		token = peek_token (&val, cfile); +		if (token == RBRACE) { +			if (brace_count) { +				token = next_token (&val, cfile); +				if (!--brace_count) +					return; +			} else +				return; +		} else if (token == LBRACE) { +			brace_count++; +		} else if (token == SEMI && !brace_count) { +			token = next_token (&val, cfile); +			return; +		} else if (token == EOL) { +			/* EOL only happens when parsing /etc/resolv.conf, +			   and we treat it like a semicolon because the +			   resolv.conf file is line-oriented. */ +			token = next_token (&val, cfile); +			return; +		} +		token = next_token (&val, cfile); +	} while (token != EOF); +} + +int parse_semi (cfile) +	FILE *cfile; +{ +	int token; +	char *val; + +	token = next_token (&val, cfile); +	if (token != SEMI) { +		parse_warn ("semicolon expected."); +		skip_to_semi (cfile); +		return 0; +	} +	return 1; +} + +/* string-parameter :== STRING SEMI */ + +char *parse_string (cfile) +	FILE *cfile; +{ +	char *val; +	int token; +	char *s; + +	token = next_token (&val, cfile); +	if (token != STRING) { +		parse_warn ("filename must be a string"); +		skip_to_semi (cfile); +		return (char *)0; +	} +	s = (char *)malloc (strlen (val) + 1); +	if (!s) +		error ("no memory for string %s.", val); +	strcpy (s, val); + +	if (!parse_semi (cfile)) +		return (char *)0; +	return s; +} + +/* hostname :== identifier | hostname DOT identifier */ + +char *parse_host_name (cfile) +	FILE *cfile; +{ +	char *val; +	int token; +	int len = 0; +	char *s; +	char *t; +	pair c = (pair)0; +	 +	/* Read a dotted hostname... */ +	do { +		/* Read a token, which should be an identifier. */ +		token = next_token (&val, cfile); +		if (!is_identifier (token) && token != NUMBER) { +			parse_warn ("expecting an identifier in hostname"); +			skip_to_semi (cfile); +			return (char *)0; +		} +		/* Store this identifier... */ +		if (!(s = (char *)malloc (strlen (val) + 1))) +			error ("can't allocate temp space for hostname."); +		strcpy (s, val); +		c = cons ((caddr_t)s, c); +		len += strlen (s) + 1; +		/* Look for a dot; if it's there, keep going, otherwise +		   we're done. */ +		token = peek_token (&val, cfile); +		if (token == DOT) +			token = next_token (&val, cfile); +	} while (token == DOT); + +	/* Assemble the hostname together into a string. */ +	if (!(s = (char *)malloc (len))) +		error ("can't allocate space for hostname."); +	t = s + len; +	*--t = 0; +	while (c) { +		pair cdr = c -> cdr; +		int l = strlen ((char *)(c -> car)); +		t -= l; +		memcpy (t, (char *)(c -> car), l); +		/* Free up temp space. */ +		free (c -> car); +		free (c); +		c = cdr; +		if (t != s) +			*--t = '.'; +	} +	return s; +} + +int parse_ip_addr (cfile, addr) +	FILE *cfile; +	struct iaddr *addr; +{ +	char *val; +	int token; + +	addr -> len = 4; +	if (parse_numeric_aggregate (cfile, addr -> iabuf, +				     &addr -> len, DOT, 10, 8)) +		return 1; +	return 0; +}	 + +/* hardware-parameter :== HARDWARE ETHERNET csns SEMI +   csns :== NUMBER | csns COLON NUMBER */ + +void parse_hardware_param (cfile, hardware) +	FILE *cfile; +	struct hardware *hardware; +{ +	char *val; +	int token; +	int hlen; +	unsigned char *t; + +	token = next_token (&val, cfile); +	switch (token) { +	      case ETHERNET: +		hardware -> htype = HTYPE_ETHER; +		break; +	      case TOKEN_RING: +		hardware -> htype = HTYPE_IEEE802; +		break; +	      default: +		parse_warn ("expecting a network hardware type"); +		skip_to_semi (cfile); +		return; +	} + +	/* Parse the hardware address information.   Technically, +	   it would make a lot of sense to restrict the length of the +	   data we'll accept here to the length of a particular hardware +	   address type.   Unfortunately, there are some broken clients +	   out there that put bogus data in the chaddr buffer, and we accept +	   that data in the lease file rather than simply failing on such +	   clients.   Yuck. */ +	hlen = 0; +	t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen, +				     COLON, 16, 8); +	if (!t) +		return; +	if (hlen > sizeof hardware -> haddr) { +		free (t); +		parse_warn ("hardware address too long"); +	} else { +		hardware -> hlen = hlen; +		memcpy ((unsigned char *)&hardware -> haddr [0], +			t, hardware -> hlen); +		free (t); +	} +	 +	token = next_token (&val, cfile); +	if (token != SEMI) { +		parse_warn ("expecting semicolon."); +		skip_to_semi (cfile); +	} +} + +/* lease-time :== NUMBER SEMI */ + +void parse_lease_time (cfile, timep) +	FILE *cfile; +	TIME *timep; +{ +	char *val; +	int token; + +	token = next_token (&val, cfile); +	if (token != NUMBER) { +		parse_warn ("Expecting numeric lease time"); +		skip_to_semi (cfile); +		return; +	} +	convert_num ((unsigned char *)timep, val, 10, 32); +	/* Unswap the number - convert_num returns stuff in NBO. */ +	*timep = ntohl (*timep); /* XXX */ + +	parse_semi (cfile); +} + +/* No BNF for numeric aggregates - that's defined by the caller.  What +   this function does is to parse a sequence of numbers seperated by +   the token specified in seperator.  If max is zero, any number of +   numbers will be parsed; otherwise, exactly max numbers are +   expected.  Base and size tell us how to internalize the numbers +   once they've been tokenized. */ + +unsigned char *parse_numeric_aggregate (cfile, buf, +					max, seperator, base, size) +	FILE *cfile; +	unsigned char *buf; +	int *max; +	int seperator; +	int base; +	int size; +{ +	char *val; +	int token; +	unsigned char *bufp = buf, *s; +	char *t; +	int count = 0; +	pair c = (pair)0; + +	if (!bufp && *max) { +		bufp = (unsigned char *)malloc (*max * size / 8); +		if (!bufp) +			error ("can't allocate space for numeric aggregate"); +	} else +		s = bufp; + +	do { +		if (count) { +			token = peek_token (&val, cfile); +			if (token != seperator) { +				if (!*max) +					break; +				if (token != RBRACE && token != LBRACE) +					token = next_token (&val, cfile); +				parse_warn ("too few numbers."); +				if (token != SEMI) +					skip_to_semi (cfile); +				return (unsigned char *)0; +			} +			token = next_token (&val, cfile); +		} +		token = next_token (&val, cfile); + +		if (token == EOF) { +			parse_warn ("unexpected end of file"); +			break; +		} + +		/* Allow NUMBER_OR_NAME if base is 16. */ +		if (token != NUMBER && +		    (base != 16 || token != NUMBER_OR_NAME)) { +			parse_warn ("expecting numeric value."); +			skip_to_semi (cfile); +			return (unsigned char *)0; +		} +		/* If we can, convert the number now; otherwise, build +		   a linked list of all the numbers. */ +		if (s) { +			convert_num (s, val, base, size); +			s += size / 8; +		} else { +			t = (char *)malloc (strlen (val) + 1); +			if (!t) +				error ("no temp space for number."); +			strcpy (t, val); +			c = cons (t, c); +		} +	} while (++count != *max); + +	/* If we had to cons up a list, convert it now. */ +	if (c) { +		bufp = (unsigned char *)malloc (count * size / 8); +		if (!bufp) +			error ("can't allocate space for numeric aggregate."); +		s = bufp + count - size / 8; +		*max = count; +	} +	while (c) { +		pair cdr = c -> cdr; +		convert_num (s, (char *)(c -> car), base, size); +		s -= size / 8; +		/* Free up temp space. */ +		free (c -> car); +		free (c); +		c = cdr; +	} +	return bufp; +} + +void convert_num (buf, str, base, size) +	unsigned char *buf; +	char *str; +	int base; +	int size; +{ +	char *ptr = str; +	int negative = 0; +	u_int32_t val = 0; +	int tval; +	int max; + +	if (*ptr == '-') { +		negative = 1; +		++ptr; +	} + +	/* If base wasn't specified, figure it out from the data. */ +	if (!base) { +		if (ptr [0] == '0') { +			if (ptr [1] == 'x') { +				base = 16; +				ptr += 2; +			} else if (isascii (ptr [1]) && isdigit (ptr [1])) { +				base = 8; +				ptr += 1; +			} else { +				base = 10; +			} +		} else { +			base = 10; +		} +	} + +	do { +		tval = *ptr++; +		/* XXX assumes ASCII... */ +		if (tval >= 'a') +			tval = tval - 'a' + 10; +		else if (tval >= 'A') +			tval = tval - 'A' + 10; +		else if (tval >= '0') +			tval -= '0'; +		else { +			warn ("Bogus number: %s.", str); +			break; +		} +		if (tval >= base) { +			warn ("Bogus number: %s: digit %d not in base %d\n", +			      str, tval, base); +			break; +		} +		val = val * base + tval; +	} while (*ptr); + +	if (negative) +		max = (1 << (size - 1)); +	else +		max = (1 << (size - 1)) + ((1 << (size - 1)) - 1); +	if (val > max) { +		switch (base) { +		      case 8: +			warn ("value %s%o exceeds max (%d) for precision.", +			      negative ? "-" : "", val, max); +			break; +		      case 16: +			warn ("value %s%x exceeds max (%d) for precision.", +			      negative ? "-" : "", val, max); +			break; +		      default: +			warn ("value %s%u exceeds max (%d) for precision.", +			      negative ? "-" : "", val, max); +			break; +		} +	} + +	if (negative) { +		switch (size) { +		      case 8: +			*buf = -(unsigned long)val; +			break; +		      case 16: +			putShort (buf, -(unsigned long)val); +			break; +		      case 32: +			putLong (buf, -(unsigned long)val); +			break; +		      default: +			warn ("Unexpected integer size: %d\n", size); +			break; +		} +	} else { +		switch (size) { +		      case 8: +			*buf = (u_int8_t)val; +			break; +		      case 16: +			putUShort (buf, (u_int16_t)val); +			break; +		      case 32: +			putULong (buf, val); +			break; +		      default: +			warn ("Unexpected integer size: %d\n", size); +			break; +		} +	} +} + +/* date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER  +   		NUMBER COLON NUMBER COLON NUMBER SEMI + +   Dates are always in GMT; first number is day of week; next is +   year/month/day; next is hours:minutes:seconds on a 24-hour +   clock. */ + +TIME parse_date (cfile) +	FILE *cfile; +{ +	struct tm tm; +	int guess; +	char *val; +	int token; +	static int months [11] = { 31, 59, 90, 120, 151, 181, +					  212, 243, 273, 304, 334 }; + +	/* Day of week... */ +	token = next_token (&val, cfile); +	if (token != NUMBER) { +		parse_warn ("numeric day of week expected."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} +	tm.tm_wday = atoi (val); + +	/* Year... */ +	token = next_token (&val, cfile); +	if (token != NUMBER) { +		parse_warn ("numeric year expected."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} +	tm.tm_year = atoi (val); +	if (tm.tm_year > 1900) +		tm.tm_year -= 1900; + +	/* Slash seperating year from month... */ +	token = next_token (&val, cfile); +	if (token != SLASH) { +		parse_warn ("expected slash seperating year from month."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} + +	/* Month... */ +	token = next_token (&val, cfile); +	if (token != NUMBER) { +		parse_warn ("numeric month expected."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} +	tm.tm_mon = atoi (val) - 1; + +	/* Slash seperating month from day... */ +	token = next_token (&val, cfile); +	if (token != SLASH) { +		parse_warn ("expected slash seperating month from day."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} + +	/* Month... */ +	token = next_token (&val, cfile); +	if (token != NUMBER) { +		parse_warn ("numeric day of month expected."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} +	tm.tm_mday = atoi (val); + +	/* Hour... */ +	token = next_token (&val, cfile); +	if (token != NUMBER) { +		parse_warn ("numeric hour expected."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} +	tm.tm_hour = atoi (val); + +	/* Colon seperating hour from minute... */ +	token = next_token (&val, cfile); +	if (token != COLON) { +		parse_warn ("expected colon seperating hour from minute."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} + +	/* Minute... */ +	token = next_token (&val, cfile); +	if (token != NUMBER) { +		parse_warn ("numeric minute expected."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} +	tm.tm_min = atoi (val); + +	/* Colon seperating minute from second... */ +	token = next_token (&val, cfile); +	if (token != COLON) { +		parse_warn ("expected colon seperating hour from minute."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} + +	/* Minute... */ +	token = next_token (&val, cfile); +	if (token != NUMBER) { +		parse_warn ("numeric minute expected."); +		if (token != SEMI) +			skip_to_semi (cfile); +		return (TIME)0; +	} +	tm.tm_sec = atoi (val); +	tm.tm_isdst = 0; + +	/* XXX */ /* We assume that mktime does not use tm_yday. */ +	tm.tm_yday = 0; + +	/* Make sure the date ends in a semicolon... */ +	token = next_token (&val, cfile); +	if (token != SEMI) { +		parse_warn ("semicolon expected."); +		skip_to_semi (cfile); +		return 0; +	} + +	/* Guess the time value... */ +	guess = ((((((365 * (tm.tm_year - 70) +	/* Days in years since '70 */ +		      (tm.tm_year - 69) / 4 +	/* Leap days since '70 */ +		      (tm.tm_mon		/* Days in months this year */ +		       ? months [tm.tm_mon - 1] +		       : 0) + +		      (tm.tm_mon > 1 &&		/* Leap day this year */ +		       !((tm.tm_year - 72) & 3)) + +		      tm.tm_mday - 1) * 24) +	/* Day of month */ +		    tm.tm_hour) * 60) + +		  tm.tm_min) * 60) + tm.tm_sec; + +	/* This guess could be wrong because of leap seconds or other +	   weirdness we don't know about that the system does.   For +	   now, we're just going to accept the guess, but at some point +	   it might be nice to do a successive approximation here to +	   get an exact value.   Even if the error is small, if the +	   server is restarted frequently (and thus the lease database +	   is reread), the error could accumulate into something +	   significant. */ + +	return guess; +}  | 
