1/*****************************************************************
2**
3**	@(#) zfparse.c -- A zone file parser
4**
5**	Copyright (c) Jan 2010 - Jan 2010, Holger Zuleger HZnet. All rights reserved.
6**
7**	This software is open source.
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**	Redistributions of source code must retain the above copyright notice,
14**	this list of conditions and the following disclaimer.
15**
16**	Redistributions in binary form must reproduce the above copyright notice,
17**	this list of conditions and the following disclaimer in the documentation
18**	and/or other materials provided with the distribution.
19**
20**	Neither the name of Holger Zuleger HZnet nor the names of its contributors may
21**	be used to endorse or promote products derived from this software without
22**	specific prior written permission.
23**
24**	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25**	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26**	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27**	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
28**	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29**	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30**	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31**	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32**	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33**	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34**	POSSIBILITY OF SUCH DAMAGE.
35**
36*****************************************************************/
37# include <stdio.h>
38# include <string.h>
39# include <stdlib.h>
40# include <unistd.h>	/* for link(), unlink() */
41# include <ctype.h>
42# include <assert.h>
43#if 0
44# include <sys/types.h>
45# include <sys/stat.h>
46# include <time.h>
47# include <utime.h>
48# include <errno.h>
49# include <fcntl.h>
50#endif
51#ifdef HAVE_CONFIG_H
52# include <config.h>
53#endif
54# include "config_zkt.h"
55# include "zconf.h"
56# include "log.h"
57# include "debug.h"
58#define extern
59# include "zfparse.h"
60#undef extern
61
62
63extern	const	char	*progname;
64
65/*****************************************************************
66**	is_multiline_rr (const char *s)
67*****************************************************************/
68static	const	char	*is_multiline_rr (int *multi_line_rr, const char *p)
69{
70	while ( *p && *p != ';' )
71	{
72		if ( *p == '\"' )
73			do
74				p++;
75			while ( *p && *p != '\"' );
76
77		if ( *p == '(' )
78			*multi_line_rr = 1;
79		if ( *p == ')' )
80			*multi_line_rr = 0;
81		p++;
82	}
83	return p;
84}
85
86/*****************************************************************
87**	skipws (const char *s)
88*****************************************************************/
89static	const	char	*skipws (const char *s)
90{
91	while ( *s && (*s == ' ' || *s == '\t' || *s == '\n') )
92		s++;
93	return s;
94}
95
96/*****************************************************************
97**	skiplabel (const char *s)
98*****************************************************************/
99static	const	char	*skiplabel (const char *s)
100{
101	while ( *s && *s != ';' && *s != ' ' && *s != '\t' && *s != '\n' )
102		s++;
103	return s;
104}
105
106/*****************************************************************
107**	setminmax ()
108*****************************************************************/
109static	void	setminmax (long *pmin, long val, long *pmax)
110{
111	if ( val < *pmin )
112		*pmin = val;
113	if ( val > *pmax )
114		*pmax = val;
115}
116
117/*****************************************************************
118**	get_ttl ()
119*****************************************************************/
120static	long	get_ttl (const char *s)
121{
122	char	quantity;
123	long	lval;
124
125	quantity = 'd';
126	sscanf (s, "%ld%c", &lval, &quantity);
127	quantity = tolower (quantity);
128	if  ( quantity == 'm' )
129		lval *= MINSEC;
130	else if  ( quantity == 'h' )
131		lval *= HOURSEC;
132	else if  ( quantity == 'd' )
133		lval *= DAYSEC;
134	else if  ( quantity == 'w' )
135		lval *= WEEKSEC;
136	else if  ( quantity == 'y' )
137		lval *= YEARSEC;
138
139	return lval;
140}
141
142/*****************************************************************
143**	addkeydb ()
144*****************************************************************/
145int	addkeydb (const char *file, const char *keydbfile)
146{
147	FILE	*fp;
148
149	if ( (fp = fopen (file, "a")) == NULL )
150		return -1;
151
152	fprintf (fp, "\n");
153	fprintf (fp, "$INCLUDE %s\t; this is the database of public DNSKEY RR\n", keydbfile);
154
155	fclose (fp);
156
157	return 0;
158}
159
160/*****************************************************************
161**	parsezonefile ()
162**	parse the BIND zone file 'file' and store the minimum and
163**	maximum ttl value in the corresponding parameter.
164**	if keydbfile is set, check if this file is already include.
165**	return 0 if keydbfile is not included
166**	return 1 if keydbfile is included
167**	return -1 on error
168*****************************************************************/
169int	parsezonefile (const char *file, long *pminttl, long *pmaxttl, const char *keydbfile)
170{
171	FILE	*infp;
172	int	len;
173	int	lnr;
174	long	ttl;
175	int	multi_line_rr;
176	int	keydbfilefound;
177	char	buf[1024];
178	const	char	*p;
179
180	assert (file != NULL);
181	assert (pminttl != NULL);
182	assert (pmaxttl != NULL);
183
184	dbg_val4 ("parsezonefile (\"%s\", %ld, %ld, \"%s\")\n", file, *pminttl, *pmaxttl, keydbfile);
185
186	if ( (infp = fopen (file, "r")) == NULL )
187		return -1;
188
189	lnr = 0;
190	keydbfilefound = 0;
191	multi_line_rr = 0;
192	while ( fgets (buf, sizeof buf, infp) != NULL )
193	{
194		len = strlen (buf);
195		if ( buf[len-1] != '\n' )	/* line too long ? */
196			fprintf (stderr, "line too long\n");
197		lnr++;
198
199		p = buf;
200		if ( multi_line_rr )	/* skip line if it's part of a multiline rr */
201		{
202			is_multiline_rr (&multi_line_rr, p);
203			continue;
204		}
205
206		if ( *p == '$' )	/* special directive ? */
207		{
208			if ( strncmp (p+1, "TTL", 3) == 0 )	/* $TTL ? */
209			{
210				ttl = get_ttl (p+4);
211				dbg_val3 ("%s:%d:ttl %ld\n", file, lnr, ttl);
212				setminmax (pminttl, ttl, pmaxttl);
213			}
214			else if ( strncmp (p+1, "INCLUDE", 7) == 0 )	/* $INCLUDE ? */
215			{
216				char	fname[30+1];
217
218				sscanf (p+9, "%30s", fname);
219				dbg_val ("$INCLUDE directive for file \"%s\" found\n", fname);
220				if ( keydbfile && strcmp (fname, keydbfile) == 0 )
221					keydbfilefound = 1;
222				else
223					keydbfilefound = parsezonefile (fname, pminttl, pmaxttl, keydbfile);
224			}
225		}
226		else if ( !isspace (*p) )	/* label ? */
227			p = skiplabel (p);
228
229		p = skipws (p);
230		if ( *p == ';' )	/* skip line if it's  a comment line */
231			continue;
232
233			/* skip class (hesiod is not supported now) */
234		if ( (toupper (*p) == 'I' && toupper (p[1]) == 'N') ||
235		     (toupper (*p) == 'C' && toupper (p[1]) == 'H') )
236			p += 2;
237		p = skipws (p);
238
239		if ( isdigit (*p) )	/* ttl ? */
240		{
241			ttl = get_ttl (p);
242			dbg_val3 ("%s:%d:ttl %ld\n", file, lnr, ttl);
243			setminmax (pminttl, ttl, pmaxttl);
244		}
245
246		/* check the rest of the line if it's the beginning of a multi_line_rr */
247		is_multiline_rr (&multi_line_rr, p);
248	}
249
250	if ( file )
251		fclose (infp);
252
253	dbg_val5 ("parsezonefile (\"%s\", %ld, %ld, \"%s\") ==> %d\n",
254			file, *pminttl, *pmaxttl, keydbfile, keydbfilefound);
255	return keydbfilefound;
256}
257
258
259#ifdef TEST
260const char *progname;
261int	main (int argc, char *argv[])
262{
263	long	minttl;
264	long	maxttl;
265	int	keydbfound;
266	char	*dnskeydb;
267
268	progname = *argv;
269	dnskeydb = NULL;
270	dnskeydb = "dnskey.db";
271
272	minttl = 0x7FFFFFFF;
273	maxttl = 0;
274	keydbfound = parsezonefile (argv[1], &minttl, &maxttl, dnskeydb);
275	if ( keydbfound < 0 )
276		error ("can't parse zone file %s\n", argv[1]);
277
278	if ( dnskeydb && !keydbfound )
279	{
280		printf ("$INCLUDE %s directive added \n", dnskeydb);
281		addkeydb (argv[1], dnskeydb);
282	}
283
284	printf ("minttl = %ld\n", minttl);
285	printf ("maxttl = %ld\n", maxttl);
286
287	return 0;
288}
289#endif
290