1/*
2 * a generic (simple) parser. Use to parse rr's, private key
3 * information and /etc/resolv.conf files
4 *
5 * a Net::DNS like library for C
6 * LibDNS Team @ NLnet Labs
7 * (c) NLnet Labs, 2005-2006
8 * See the file LICENSE for the license
9 */
10#include "config.h"
11#include "sldns/parse.h"
12#include "sldns/parseutil.h"
13#include "sldns/sbuffer.h"
14
15#include <limits.h>
16#include <strings.h>
17
18sldns_lookup_table sldns_directive_types[] = {
19        { LDNS_DIR_TTL, "$TTL" },
20        { LDNS_DIR_ORIGIN, "$ORIGIN" },
21        { LDNS_DIR_INCLUDE, "$INCLUDE" },
22        { 0, NULL }
23};
24
25/* add max_limit here? */
26ssize_t
27sldns_fget_token(FILE *f, char *token, const char *delim, size_t limit)
28{
29	return sldns_fget_token_l(f, token, delim, limit, NULL);
30}
31
32ssize_t
33sldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *line_nr)
34{
35	int c, prev_c;
36	int p; /* 0 -> no parenthese seen, >0 nr of ( seen */
37	int com, quoted;
38	char *t;
39	size_t i;
40	const char *d;
41	const char *del;
42
43	/* standard delimeters */
44	if (!delim) {
45		/* from isspace(3) */
46		del = LDNS_PARSE_NORMAL;
47	} else {
48		del = delim;
49	}
50
51	p = 0;
52	i = 0;
53	com = 0;
54	quoted = 0;
55	prev_c = 0;
56	t = token;
57	if (del[0] == '"') {
58		quoted = 1;
59	}
60	while ((c = getc(f)) != EOF) {
61		if (c == '\r') /* carriage return */
62			c = ' ';
63		if (c == '(' && prev_c != '\\' && !quoted) {
64			/* this only counts for non-comments */
65			if (com == 0) {
66				p++;
67			}
68			prev_c = c;
69			continue;
70		}
71
72		if (c == ')' && prev_c != '\\' && !quoted) {
73			/* this only counts for non-comments */
74			if (com == 0) {
75				p--;
76			}
77			prev_c = c;
78			continue;
79		}
80
81		if (p < 0) {
82			/* more ) then ( - close off the string */
83			*t = '\0';
84			return 0;
85		}
86
87		/* do something with comments ; */
88		if (c == ';' && quoted == 0) {
89			if (prev_c != '\\') {
90				com = 1;
91			}
92		}
93		if (c == '\"' && com == 0 && prev_c != '\\') {
94			quoted = 1 - quoted;
95		}
96
97		if (c == '\n' && com != 0) {
98			/* comments */
99			com = 0;
100			*t = ' ';
101			if (line_nr) {
102				*line_nr = *line_nr + 1;
103			}
104			if (p == 0 && i > 0) {
105				goto tokenread;
106			} else {
107				prev_c = c;
108				continue;
109			}
110		}
111
112		if (com == 1) {
113			*t = ' ';
114			prev_c = c;
115			continue;
116		}
117
118		if (c == '\n' && p != 0 && t > token) {
119			/* in parentheses */
120			if (line_nr) {
121				*line_nr = *line_nr + 1;
122			}
123			*t++ = ' ';
124			prev_c = c;
125			continue;
126		}
127
128		/* check if we hit the delim */
129		for (d = del; *d; d++) {
130			if (c == *d && i > 0 && prev_c != '\\' && p == 0) {
131				if (c == '\n' && line_nr) {
132					*line_nr = *line_nr + 1;
133				}
134				goto tokenread;
135			}
136		}
137		if (c != '\0' && c != '\n') {
138			i++;
139		}
140		if (limit > 0 && (i >= limit || (size_t)(t-token) >= limit)) {
141			*t = '\0';
142			return -1;
143		}
144		if (c != '\0' && c != '\n') {
145			*t++ = c;
146		}
147		if (c == '\\' && prev_c == '\\')
148			prev_c = 0;
149		else	prev_c = c;
150	}
151	*t = '\0';
152	if (c == EOF) {
153		return (ssize_t)i;
154	}
155
156	if (i == 0) {
157		/* nothing read */
158		return -1;
159	}
160	if (p != 0) {
161		return -1;
162	}
163	return (ssize_t)i;
164
165tokenread:
166	if(*del == '"')
167		/* do not skip over quotes after the string, they are part
168		 * of the next string.  But skip over whitespace (if needed)*/
169		sldns_fskipcs_l(f, del+1, line_nr);
170	else	sldns_fskipcs_l(f, del, line_nr);
171	*t = '\0';
172	if (p != 0) {
173		return -1;
174	}
175
176	return (ssize_t)i;
177}
178
179ssize_t
180sldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, char *data,
181               const char *d_del, size_t data_limit)
182{
183       return sldns_fget_keyword_data_l(f, keyword, k_del, data, d_del,
184		       data_limit, NULL);
185}
186
187ssize_t
188sldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_del, char *data,
189               const char *d_del, size_t data_limit, int *line_nr)
190{
191       /* we assume: keyword|sep|data */
192       char *fkeyword;
193       ssize_t i;
194
195       if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
196               return -1;
197       fkeyword = (char*)malloc(LDNS_MAX_KEYWORDLEN);
198       if(!fkeyword)
199               return -1;
200
201       i = sldns_fget_token(f, fkeyword, k_del, LDNS_MAX_KEYWORDLEN);
202       if(i==0 || i==-1) {
203               free(fkeyword);
204               return -1;
205       }
206
207       /* case??? i instead of strlen? */
208       if (strncmp(fkeyword, keyword, LDNS_MAX_KEYWORDLEN - 1) == 0) {
209               /* whee! */
210               /* printf("%s\n%s\n", "Matching keyword", fkeyword); */
211               i = sldns_fget_token_l(f, data, d_del, data_limit, line_nr);
212               free(fkeyword);
213               return i;
214       } else {
215               /*printf("no match for %s (read: %s)\n", keyword, fkeyword);*/
216               free(fkeyword);
217               return -1;
218       }
219}
220
221int
222sldns_bgetc(sldns_buffer *buffer)
223{
224	if (!sldns_buffer_available_at(buffer, buffer->_position, sizeof(uint8_t))) {
225		sldns_buffer_set_position(buffer, sldns_buffer_limit(buffer));
226		/* sldns_buffer_rewind(buffer);*/
227		return EOF;
228	}
229	return (int)sldns_buffer_read_u8(buffer);
230}
231
232ssize_t
233sldns_bget_token(sldns_buffer *b, char *token, const char *delim, size_t limit)
234{
235	return sldns_bget_token_par(b, token, delim, limit, NULL, NULL);
236}
237
238ssize_t
239sldns_bget_token_par(sldns_buffer *b, char *token, const char *delim,
240	size_t limit, int* par, const char* skipw)
241{
242	int c, lc;
243	int p; /* 0 -> no parenthese seen, >0 nr of ( seen */
244	int com, quoted;
245	char *t;
246	size_t i;
247	const char *d;
248	const char *del;
249
250	/* standard delimiters */
251	if (!delim) {
252		/* from isspace(3) */
253		del = LDNS_PARSE_NORMAL;
254	} else {
255		del = delim;
256	}
257
258	p = (par?*par:0);
259	i = 0;
260	com = 0;
261	quoted = 0;
262	t = token;
263	lc = 0;
264	if (del[0] == '"') {
265		quoted = 1;
266	}
267
268	while ((c = sldns_bgetc(b)) != EOF) {
269		if (c == '\r') /* carriage return */
270			c = ' ';
271		if (c == '(' && lc != '\\' && !quoted) {
272			/* this only counts for non-comments */
273			if (com == 0) {
274				if(par) (*par)++;
275				p++;
276			}
277			lc = c;
278			continue;
279		}
280
281		if (c == ')' && lc != '\\' && !quoted) {
282			/* this only counts for non-comments */
283			if (com == 0) {
284				if(par) (*par)--;
285				p--;
286			}
287			lc = c;
288			continue;
289		}
290
291		if (p < 0) {
292			/* more ) then ( */
293			*t = '\0';
294			return 0;
295		}
296
297		/* do something with comments ; */
298		if (c == ';' && quoted == 0) {
299			if (lc != '\\') {
300				com = 1;
301			}
302		}
303		if (c == '"' && com == 0 && lc != '\\') {
304			quoted = 1 - quoted;
305		}
306
307		if (c == '\n' && com != 0) {
308			/* comments */
309			com = 0;
310			*t = ' ';
311			lc = c;
312			continue;
313		}
314
315		if (com == 1) {
316			*t = ' ';
317			lc = c;
318			continue;
319		}
320
321		if (c == '\n' && p != 0) {
322			/* in parentheses */
323			/* do not write ' ' if we want to skip spaces */
324			if(!(skipw && (strchr(skipw, c)||strchr(skipw, ' '))))
325				*t++ = ' ';
326			lc = c;
327			continue;
328		}
329
330		/* check to skip whitespace at start, but also after ( */
331		if(skipw && i==0 && !com && !quoted && lc != '\\') {
332			if(strchr(skipw, c)) {
333				lc = c;
334				continue;
335			}
336		}
337
338		/* check if we hit the delim */
339		for (d = del; *d; d++) {
340			/* we can only exit if no parens or user tracks them */
341                        if (c == *d && lc != '\\' && (p == 0 || par)) {
342				goto tokenread;
343                        }
344		}
345
346		i++;
347		if (limit > 0 && (i >= limit || (size_t)(t-token) >= limit)) {
348			*t = '\0';
349			return -1;
350		}
351		*t++ = c;
352
353		if (c == '\\' && lc == '\\') {
354			lc = 0;
355		} else {
356			lc = c;
357		}
358	}
359	*t = '\0';
360	if (i == 0) {
361		/* nothing read */
362		return -1;
363	}
364	if (!par && p != 0) {
365		return -1;
366	}
367	return (ssize_t)i;
368
369tokenread:
370	if(*del == '"')
371		/* do not skip over quotes after the string, they are part
372		 * of the next string.  But skip over whitespace (if needed)*/
373		sldns_bskipcs(b, del+1);
374	else 	sldns_bskipcs(b, del);
375	*t = '\0';
376
377	if (!par && p != 0) {
378		return -1;
379	}
380	return (ssize_t)i;
381}
382
383
384void
385sldns_bskipcs(sldns_buffer *buffer, const char *s)
386{
387        int found;
388        char c;
389        const char *d;
390
391        while(sldns_buffer_available_at(buffer, buffer->_position, sizeof(char))) {
392                c = (char) sldns_buffer_read_u8_at(buffer, buffer->_position);
393                found = 0;
394                for (d = s; *d; d++) {
395                        if (*d == c) {
396                                found = 1;
397                        }
398                }
399                if (found && buffer->_limit > buffer->_position) {
400                        buffer->_position += sizeof(char);
401                } else {
402                        return;
403                }
404        }
405}
406
407void
408sldns_fskipcs(FILE *fp, const char *s)
409{
410	sldns_fskipcs_l(fp, s, NULL);
411}
412
413void
414sldns_fskipcs_l(FILE *fp, const char *s, int *line_nr)
415{
416        int found;
417        int c;
418        const char *d;
419
420	while ((c = fgetc(fp)) != EOF) {
421		if (line_nr && c == '\n') {
422			*line_nr = *line_nr + 1;
423		}
424                found = 0;
425                for (d = s; *d; d++) {
426                        if (*d == c) {
427                                found = 1;
428                        }
429                }
430		if (!found) {
431			/* with getc, we've read too far */
432			ungetc(c, fp);
433			return;
434		}
435	}
436}
437
438ssize_t
439sldns_bget_keyword_data(sldns_buffer *b, const char *keyword, const char *k_del, char
440*data, const char *d_del, size_t data_limit)
441{
442       /* we assume: keyword|sep|data */
443       char *fkeyword;
444       ssize_t i;
445
446       if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
447               return -1;
448       fkeyword = (char*)malloc(LDNS_MAX_KEYWORDLEN);
449       if(!fkeyword)
450               return -1; /* out of memory */
451
452       i = sldns_bget_token(b, fkeyword, k_del, data_limit);
453       if(i==0 || i==-1) {
454               free(fkeyword);
455               return -1; /* nothing read */
456       }
457
458       /* case??? */
459       if (strncmp(fkeyword, keyword, strlen(keyword)) == 0) {
460               free(fkeyword);
461               /* whee, the match! */
462               /* retrieve it's data */
463               i = sldns_bget_token(b, data, d_del, 0);
464               return i;
465       } else {
466               free(fkeyword);
467               return -1;
468       }
469}
470
471