1 /*	$NetBSD: zfparse.c,v 1.1.1.1 2015/07/08 15:37:48 christos Exp $	*/
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 #ifdef HAVE_CONFIG_H
46 # include <config.h>
47 #endif
48 # include "config_zkt.h"
49 # include "zconf.h"
50 # include "misc.h"
51 # include "log.h"
52 # include "debug.h"
53 #define extern
54 # include "zfparse.h"
55 #undef extern
56 
57 
58 extern	const	char	*progname;
59 
60 /*****************************************************************
61 **	is_multiline_rr (const char *s)
62 *****************************************************************/
63 static	const	char	*is_multiline_rr (int *multi_line_rr, const char *p)
64 {
65 	while ( *p && *p != ';' )
66 	{
67 		if ( *p == '\"' )
68 			do
69 				p++;
70 			while ( *p && *p != '\"' );
71 
72 		if ( *p == '(' )
73 			*multi_line_rr = 1;
74 		if ( *p == ')' )
75 			*multi_line_rr = 0;
76 		p++;
77 	}
78 	return p;
79 }
80 
81 /*****************************************************************
82 **	skipws (const char *s)
83 *****************************************************************/
84 static	const	char	*skipws (const char *s)
85 {
86 	while ( *s && (*s == ' ' || *s == '\t' || *s == '\n') )
87 		s++;
88 	return s;
89 }
90 
91 /*****************************************************************
92 **	skiplabel (const char *s)
93 *****************************************************************/
94 static	const	char	*skiplabel (const char *s)
95 {
96 	while ( *s && *s != ';' && *s != ' ' && *s != '\t' && *s != '\n' )
97 		s++;
98 	return s;
99 }
100 
101 /*****************************************************************
102 **	setminmax ()
103 *****************************************************************/
104 static	void	setminmax (long *pmin, long val, long *pmax)
105 {
106 	if ( val < *pmin )
107 		*pmin = val;
108 	if ( val > *pmax )
109 		*pmax = val;
110 }
111 
112 /*****************************************************************
113 **	get_ttl ()
114 *****************************************************************/
115 static	long	get_ttl (const char *s)
116 {
117 	char	quantity;
118 	long	lval;
119 
120 	quantity = 'd';
121 	sscanf (s, "%ld%c", &lval, &quantity);
122 	quantity = tolower (quantity);
123 	if  ( quantity == 'm' )
124 		lval *= MINSEC;
125 	else if  ( quantity == 'h' )
126 		lval *= HOURSEC;
127 	else if  ( quantity == 'd' )
128 		lval *= DAYSEC;
129 	else if  ( quantity == 'w' )
130 		lval *= WEEKSEC;
131 	else if  ( quantity == 'y' )
132 		lval *= YEARSEC;
133 
134 	return lval;
135 }
136 
137 /*****************************************************************
138 **	addkeydb ()
139 *****************************************************************/
140 int	addkeydb (const char *file, const char *keydbfile)
141 {
142 	FILE	*fp;
143 
144 	if ( (fp = fopen (file, "a")) == NULL )
145 		return -1;
146 
147 	fprintf (fp, "\n");
148 	fprintf (fp, "$INCLUDE %s\t; this is the database of public DNSKEY RR\n", keydbfile);
149 
150 	fclose (fp);
151 
152 	return 0;
153 }
154 
155 /*****************************************************************
156 **	parsezonefile ()
157 **	parse the BIND zone file 'file' and store the minimum and
158 **	maximum ttl value in the corresponding parameter.
159 **	if keydbfile is set, check if this file is already include.
160 **	if inclfiles is not NULL store a list of included files names
161 **	in it.
162 **	return 0 if keydbfile is not included
163 **	return 1 if keydbfile is included
164 **	return -1 on error
165 *****************************************************************/
166 int	parsezonefile (const char *file, long *pminttl, long *pmaxttl, const char *keydbfile, char *inclfiles, size_t *plen)
167 {
168 	FILE	*infp;
169 	int	len;
170 	int	lnr;
171 	long	ttl;
172 	int	multi_line_rr;
173 	int	keydbfilefound;
174 	char	buf[1024];
175 	const	char	*p;
176 
177 	assert (file != NULL);
178 	assert (pminttl != NULL);
179 	assert (pmaxttl != NULL);
180 
181 	dbg_val4 ("parsezonefile (\"%s\", %ld, %ld, \"%s\")\n", file, *pminttl, *pmaxttl, keydbfile);
182 
183 	if ( (infp = fopen (file, "r")) == NULL )
184 	{
185 		error ("parsezonefile: couldn't open file \"%s\" for input\n", file);
186 		return -1;
187 	}
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 ( strcmp (fname, keydbfile) == 0 )
221 					keydbfilefound = 1;
222 				else
223 				{
224 					if ( inclfiles && plen )
225 					{
226 						len = snprintf (inclfiles, *plen, ",%s", fname);
227 						if ( *plen <= len )	/* no space left in include file string */
228 							return keydbfilefound;
229 						inclfiles += len;
230 						*plen -= len;
231 					}
232 					int	ret = parsezonefile (fname, pminttl, pmaxttl, keydbfile, inclfiles, plen);
233 					if ( ret )	/* keydb found or read error ? */
234 						keydbfilefound = ret;
235 				}
236 			}
237 		}
238 		else if ( !isspace (*p) )	/* label ? */
239 			p = skiplabel (p);
240 
241 		p = skipws (p);
242 		if ( *p == ';' )	/* skip line if it's  a comment line */
243 			continue;
244 
245 			/* skip class (hesiod is not supported now) */
246 		if ( (toupper (*p) == 'I' && toupper (p[1]) == 'N') ||
247 		     (toupper (*p) == 'C' && toupper (p[1]) == 'H') )
248 			p += 2;
249 		p = skipws (p);
250 
251 		if ( isdigit (*p) )	/* ttl ? */
252 		{
253 			ttl = get_ttl (p);
254 			dbg_val3 ("%s:%d:ttl %ld\n", file, lnr, ttl);
255 			setminmax (pminttl, ttl, pmaxttl);
256 		}
257 
258 		/* check the rest of the line if it's the beginning of a multi_line_rr */
259 		is_multiline_rr (&multi_line_rr, p);
260 	}
261 
262 	if ( file )
263 		fclose (infp);
264 
265 	dbg_val5 ("parsezonefile (\"%s\", %ld, %ld, \"%s\") ==> %d\n",
266 			file, *pminttl, *pmaxttl, keydbfile, keydbfilefound);
267 	return keydbfilefound;
268 }
269 
270 
271 #ifdef TEST
272 const char *progname;
273 int	main (int argc, char *argv[])
274 {
275 	long	minttl;
276 	long	maxttl;
277 	int	keydbfound;
278 	char	*dnskeydb;
279 
280 	progname = *argv;
281 	dnskeydb = NULL;
282 	dnskeydb = "dnskey.db";
283 
284 	minttl = 0x7FFFFFFF;
285 	maxttl = 0;
286 	keydbfound = parsezonefile (argv[1], &minttl, &maxttl, dnskeydb);
287 	if ( keydbfound < 0 )
288 		error ("can't parse zone file %s\n", argv[1]);
289 
290 	if ( dnskeydb && !keydbfound )
291 	{
292 		printf ("$INCLUDE %s directive added \n", dnskeydb);
293 		addkeydb (argv[1], dnskeydb);
294 	}
295 
296 	printf ("minttl = %ld\n", minttl);
297 	printf ("maxttl = %ld\n", maxttl);
298 
299 	return 0;
300 }
301 #endif
302