1/*****************************************************************
2**
3**	@(#) ncparse.c -- A very simple named.conf parser
4**
5**	Copyright (c) Apr 2005 - Nov 2007, 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 <ctype.h>
40# include <assert.h>
41# include "debug.h"
42# include "misc.h"
43# include "log.h"
44#define extern
45# include "ncparse.h"
46#undef extern
47
48# define	TOK_STRING	257
49# define	TOK_DIR		258
50# define	TOK_INCLUDE	259
51
52# define	TOK_ZONE	260
53# define	TOK_TYPE	261
54# define	TOK_MASTER	262
55# define	TOK_SLAVE	263
56# define	TOK_STUB	264
57# define	TOK_HINT	265
58# define	TOK_FORWARD	266
59# define	TOK_DELEGATION	267
60# define	TOK_VIEW	268
61
62# define	TOK_FILE	270
63
64# define	TOK_UNKNOWN	511
65
66/* list of "named.conf" keywords we are interested in */
67static struct KeyWords {
68	char	*name;
69	int	tok;
70} kw[] = {
71	{ "STRING",	TOK_STRING },
72	{ "include",	TOK_INCLUDE },
73	{ "directory",	TOK_DIR },
74	{ "file",	TOK_FILE },
75	{ "zone",	TOK_ZONE },
76#if 0	/* we don't need the type keyword; master, slave etc. is sufficient */
77	{ "type",	TOK_TYPE },
78#endif
79	{ "master",	TOK_MASTER },
80	{ "slave",	TOK_SLAVE },
81	{ "stub",	TOK_STUB },
82	{ "hint",	TOK_HINT },
83	{ "forward",	TOK_FORWARD },
84	{ "delegation-only", TOK_DELEGATION },
85	{ "view",	TOK_VIEW },
86	{ NULL,		TOK_UNKNOWN },
87};
88
89#ifdef DBG
90static	const char	*tok2str (int  tok)
91{
92	int	i;
93
94	i = 0;
95	while ( kw[i].name && kw[i].tok != tok )
96		i++;
97
98	return kw[i].name;
99}
100#endif
101
102static	int	searchkw (const char *keyword)
103{
104	int	i;
105
106	dbg_val ("ncparse: searchkw (%s)\n", keyword);
107	i = 0;
108	while ( kw[i].name && strcmp (kw[i].name, keyword) != 0 )
109		i++;
110
111	return kw[i].tok;
112}
113
114static	int	gettok (FILE *fp, char *val, size_t valsize)
115{
116	int	lastc;
117	int	c;
118	char	buf[255+1];
119	char	*p;
120	char	*bufend;
121
122	*val = '\0';
123	do {
124		while ( (c = getc (fp)) != EOF && isspace (c) )
125			;
126
127		if ( c == '#' )		/* single line comment ? */
128		{
129			while ( (c = getc (fp)) != EOF && c != '\n' )
130				;
131			continue;
132		}
133
134		if ( c == EOF )
135			return EOF;
136
137		if ( c == '{' || c == '}' || c == ';' )
138			continue;
139
140		if ( c == '/' )		/* begin of C comment ? */
141		{
142			if ( (c = getc (fp)) == '*' )	/* yes! */
143			{
144				lastc = EOF;		/* read until end of c comment */
145				while ( (c = getc (fp)) != EOF && !(lastc == '*' && c == '/') )
146					lastc = c;
147			}
148			else if ( c == '/' )	/* is it a C single line comment ? */
149			{
150				while ( (c = getc (fp)) != EOF && c != '\n' )
151					;
152			}
153			else		/* no ! */
154				ungetc (c, fp);
155			continue;
156		}
157
158		if ( c == '\"' )
159		{
160			p = val;
161			bufend = val + valsize - 1;
162			while ( (c = getc (fp)) != EOF && p < bufend && c != '\"' )
163				*p++ = c;
164			*p = '\0';
165			/* if string buffer is too small, eat up rest of string */
166			while ( c != EOF && c != '\"' )
167				c = getc (fp);
168
169			return TOK_STRING;
170		}
171
172		p = buf;
173		bufend = buf + sizeof (buf) - 1;
174		do
175			*p++ = tolower (c);
176		while ( (c = getc (fp)) != EOF && p < bufend && (isalpha (c) || c == '-') );
177		*p = '\0';
178		ungetc (c, fp);
179
180		if ( (c = searchkw (buf)) != TOK_UNKNOWN )
181			return c;
182	}  while ( c != EOF );
183
184	return EOF;
185}
186
187/*****************************************************************
188**
189**	parse_namedconf (const char *filename, chroot_dir, dir, dirsize, int (*func) ())
190**
191**	Very dumb named.conf parser.
192**	- In a zone declaration the _first_ keyword MUST be "type"
193**	- For every master zone "func (directory, zone, filename)" will be called
194**
195*****************************************************************/
196int	parse_namedconf (const char *filename, const char *chroot_dir, char *dir, size_t dirsize, int (*func) ())
197{
198	FILE	*fp;
199	int	tok;
200	char	path[511+1];
201#if 1	/* this is potentialy too small for key data, but we don't need the keys... */
202	char	strval[255+1];
203#else
204	char	strval[4095+1];
205#endif
206	char	view[255+1];
207	char	zone[255+1];
208	char	zonefile[255+1];
209
210	dbg_val ("parse_namedconf: parsing file \"%s\" \n", filename);
211
212	assert (filename != NULL);
213	assert (dir != NULL && dirsize != 0);
214	assert (func != NULL);
215
216	view[0] = '\0';
217	if ( (fp = fopen (filename, "r")) == NULL )
218		return 0;
219
220	while ( (tok = gettok (fp, strval, sizeof strval)) != EOF )
221	{
222		if ( tok > 0 && tok < 256 )
223		{
224			error ("parse_namedconf: token found with value %-10d: %c\n", tok, tok);
225			lg_mesg (LG_ERROR, "parse_namedconf: token found with value %-10d: %c", tok, tok);
226		}
227		else if ( tok == TOK_DIR )
228		{
229			if ( gettok (fp, strval, sizeof (strval)) == TOK_STRING )
230			{
231				dbg_val2 ("parse_namedconf: directory found \"%s\" (dir is %s)\n",
232										 strval, dir);
233				if ( *strval != '/' &&  *dir )
234					snprintf (path, sizeof (path), "%s/%s", dir, strval);
235				else
236					snprintf (path, sizeof (path), "%s", strval);
237
238				/* prepend chroot directory (do it only once) */
239				if ( chroot_dir && *chroot_dir )
240				{
241					snprintf (dir, dirsize, "%s%s%s", chroot_dir, *path == '/' ? "": "/", path);
242					chroot_dir = NULL;
243				}
244				else
245					snprintf (dir, dirsize, "%s", path);
246				dbg_val ("parse_namedconf: new dir \"%s\" \n", dir);
247			}
248		}
249		else if ( tok == TOK_INCLUDE )
250		{
251			if ( gettok (fp, strval, sizeof (strval)) == TOK_STRING )
252			{
253				if ( *strval != '/' && *dir )
254					snprintf (path, sizeof (path), "%s/%s", dir, strval);
255				else
256					snprintf (path, sizeof (path), "%s", strval);
257				if ( !parse_namedconf (path, chroot_dir, dir, dirsize, func) )
258					return 0;
259			}
260			else
261			{
262				error ("parse_namedconf: need a filename after \"include\"!\n");
263				lg_mesg (LG_ERROR, "parse_namedconf: need a filename after \"include\"!");
264			}
265		}
266		else if ( tok == TOK_VIEW )
267		{
268			if ( gettok (fp, strval, sizeof (strval)) != TOK_STRING )
269				continue;
270			snprintf (view, sizeof view, "%s", strval);	/* store the name of the view */
271		}
272		else if ( tok == TOK_ZONE )
273		{
274			if ( gettok (fp, strval, sizeof (strval)) != TOK_STRING )
275				continue;
276			snprintf (zone, sizeof zone, "%s", strval);	/* store the name of the zone */
277
278			if ( gettok (fp, strval, sizeof (strval)) != TOK_MASTER )
279				continue;
280			if ( gettok (fp, strval, sizeof (strval)) != TOK_FILE )
281				continue;
282			if ( gettok (fp, strval, sizeof (strval)) != TOK_STRING )
283				continue;
284			snprintf (zonefile, sizeof zonefile, "%s", strval);	/* this is the filename */
285
286			dbg_val4 ("dir %s view %s zone %s file %s\n", dir, view, zone, zonefile);
287			(*func) (dir, view, zone, zonefile);
288		}
289		else
290			dbg_val3 ("%-10s(%d): %s\n", tok2str(tok), tok, strval);
291	}
292	fclose (fp);
293
294	return 1;
295}
296
297#ifdef TEST_NCPARSE
298int	printzone (const char *dir, const char *view, const char *zone, const char *file)
299{
300	printf ("printzone ");
301	printf ("view \"%s\" " , view);
302	printf ("zone \"%s\" " , zone);
303	printf ("file ");
304	if ( dir && *dir )
305		printf ("%s/", dir, file);
306	printf ("%s", file);
307	putchar ('\n');
308	return 1;
309}
310
311char	*progname;
312
313main (int argc, char *argv[])
314{
315	char	directory[255+1];
316
317	progname = argv[0];
318
319	directory[0] = '\0';
320	if ( --argc == 0 )
321		parse_namedconf ("/var/named/named.conf", NULL, directory, sizeof (directory), printzone);
322	else
323		parse_namedconf (argv[1], NULL, directory, sizeof (directory), printzone);
324}
325#endif
326