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