1238104Sdes/*
2238104Sdes * a generic (simple) parser. Use to parse rr's, private key
3238104Sdes * information and /etc/resolv.conf files
4238104Sdes *
5238104Sdes * a Net::DNS like library for C
6238104Sdes * LibDNS Team @ NLnet Labs
7238104Sdes * (c) NLnet Labs, 2005-2006
8238104Sdes * See the file LICENSE for the license
9238104Sdes */
10238104Sdes#include <ldns/config.h>
11238104Sdes#include <ldns/ldns.h>
12238104Sdes
13238104Sdes#include <limits.h>
14238104Sdes#include <strings.h>
15238104Sdes
16238104Sdesldns_lookup_table ldns_directive_types[] = {
17238104Sdes        { LDNS_DIR_TTL, "$TTL" },
18238104Sdes        { LDNS_DIR_ORIGIN, "$ORIGIN" },
19238104Sdes        { LDNS_DIR_INCLUDE, "$INCLUDE" },
20238104Sdes        { 0, NULL }
21238104Sdes};
22238104Sdes
23238104Sdes/* add max_limit here? */
24238104Sdesssize_t
25238104Sdesldns_fget_token(FILE *f, char *token, const char *delim, size_t limit)
26238104Sdes{
27238104Sdes	return ldns_fget_token_l(f, token, delim, limit, NULL);
28238104Sdes}
29238104Sdes
30238104Sdesssize_t
31238104Sdesldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *line_nr)
32238104Sdes{
33238104Sdes	int c, prev_c;
34238104Sdes	int p; /* 0 -> no parenthese seen, >0 nr of ( seen */
35238104Sdes	int com, quoted;
36238104Sdes	char *t;
37238104Sdes	size_t i;
38238104Sdes	const char *d;
39238104Sdes	const char *del;
40238104Sdes
41238104Sdes	/* standard delimeters */
42238104Sdes	if (!delim) {
43238104Sdes		/* from isspace(3) */
44238104Sdes		del = LDNS_PARSE_NORMAL;
45238104Sdes	} else {
46238104Sdes		del = delim;
47238104Sdes	}
48238104Sdes
49238104Sdes	p = 0;
50238104Sdes	i = 0;
51238104Sdes	com = 0;
52238104Sdes	quoted = 0;
53238104Sdes	prev_c = 0;
54238104Sdes	t = token;
55238104Sdes	if (del[0] == '"') {
56238104Sdes		quoted = 1;
57238104Sdes	}
58238104Sdes	while ((c = getc(f)) != EOF) {
59238104Sdes		if (c == '\r') /* carriage return */
60238104Sdes			c = ' ';
61238104Sdes		if (c == '(' && prev_c != '\\' && !quoted) {
62238104Sdes			/* this only counts for non-comments */
63238104Sdes			if (com == 0) {
64238104Sdes				p++;
65238104Sdes			}
66238104Sdes			prev_c = c;
67238104Sdes			continue;
68238104Sdes		}
69238104Sdes
70238104Sdes		if (c == ')' && prev_c != '\\' && !quoted) {
71238104Sdes			/* this only counts for non-comments */
72238104Sdes			if (com == 0) {
73238104Sdes				p--;
74238104Sdes			}
75238104Sdes			prev_c = c;
76238104Sdes			continue;
77238104Sdes		}
78238104Sdes
79238104Sdes		if (p < 0) {
80238104Sdes			/* more ) then ( - close off the string */
81238104Sdes			*t = '\0';
82238104Sdes			return 0;
83238104Sdes		}
84238104Sdes
85238104Sdes		/* do something with comments ; */
86238104Sdes		if (c == ';' && quoted == 0) {
87238104Sdes			if (prev_c != '\\') {
88238104Sdes				com = 1;
89238104Sdes			}
90238104Sdes		}
91238104Sdes		if (c == '\"' && com == 0 && prev_c != '\\') {
92238104Sdes			quoted = 1 - quoted;
93238104Sdes		}
94238104Sdes
95238104Sdes		if (c == '\n' && com != 0) {
96238104Sdes			/* comments */
97238104Sdes			com = 0;
98238104Sdes			*t = ' ';
99238104Sdes			if (line_nr) {
100238104Sdes				*line_nr = *line_nr + 1;
101238104Sdes			}
102238104Sdes			if (p == 0 && i > 0) {
103238104Sdes				goto tokenread;
104238104Sdes			} else {
105238104Sdes				prev_c = c;
106238104Sdes				continue;
107238104Sdes			}
108238104Sdes		}
109238104Sdes
110238104Sdes		if (com == 1) {
111238104Sdes			*t = ' ';
112238104Sdes			prev_c = c;
113238104Sdes			continue;
114238104Sdes		}
115238104Sdes
116238104Sdes		if (c == '\n' && p != 0 && t > token) {
117238104Sdes			/* in parentheses */
118238104Sdes			if (line_nr) {
119238104Sdes				*line_nr = *line_nr + 1;
120238104Sdes			}
121238104Sdes			*t++ = ' ';
122238104Sdes			prev_c = c;
123238104Sdes			continue;
124238104Sdes		}
125238104Sdes
126238104Sdes		/* check if we hit the delim */
127238104Sdes		for (d = del; *d; d++) {
128238104Sdes			if (c == *d && i > 0 && prev_c != '\\' && p == 0) {
129238104Sdes				if (c == '\n' && line_nr) {
130238104Sdes					*line_nr = *line_nr + 1;
131238104Sdes				}
132238104Sdes				goto tokenread;
133238104Sdes			}
134238104Sdes		}
135238104Sdes		if (c != '\0' && c != '\n') {
136238104Sdes			i++;
137238104Sdes		}
138266114Sdes		if (limit > 0 && (i >= limit || (size_t)(t-token) >= limit)) {
139238104Sdes			*t = '\0';
140238104Sdes			return -1;
141238104Sdes		}
142238104Sdes		if (c != '\0' && c != '\n') {
143238104Sdes			*t++ = c;
144238104Sdes		}
145238104Sdes		if (c == '\\' && prev_c == '\\')
146238104Sdes			prev_c = 0;
147238104Sdes		else	prev_c = c;
148238104Sdes	}
149238104Sdes	*t = '\0';
150238104Sdes	if (c == EOF) {
151238104Sdes		return (ssize_t)i;
152238104Sdes	}
153238104Sdes
154238104Sdes	if (i == 0) {
155238104Sdes		/* nothing read */
156238104Sdes		return -1;
157238104Sdes	}
158238104Sdes	if (p != 0) {
159238104Sdes		return -1;
160238104Sdes	}
161238104Sdes	return (ssize_t)i;
162238104Sdes
163238104Sdestokenread:
164246854Sdes	ldns_fskipcs_l(f, del, line_nr);
165238104Sdes	*t = '\0';
166238104Sdes	if (p != 0) {
167238104Sdes		return -1;
168238104Sdes	}
169238104Sdes
170238104Sdes	return (ssize_t)i;
171238104Sdes}
172238104Sdes
173238104Sdesssize_t
174238104Sdesldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, char *data,
175238104Sdes               const char *d_del, size_t data_limit)
176238104Sdes{
177238104Sdes       return ldns_fget_keyword_data_l(f, keyword, k_del, data, d_del,
178238104Sdes		       data_limit, NULL);
179238104Sdes}
180238104Sdes
181238104Sdesssize_t
182238104Sdesldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_del, char *data,
183238104Sdes               const char *d_del, size_t data_limit, int *line_nr)
184238104Sdes{
185238104Sdes       /* we assume: keyword|sep|data */
186238104Sdes       char *fkeyword;
187238104Sdes       ssize_t i;
188238104Sdes
189238104Sdes       if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
190238104Sdes               return -1;
191238104Sdes       fkeyword = LDNS_XMALLOC(char, LDNS_MAX_KEYWORDLEN);
192238104Sdes       if(!fkeyword)
193238104Sdes               return -1;
194238104Sdes
195238104Sdes       i = ldns_fget_token(f, fkeyword, k_del, LDNS_MAX_KEYWORDLEN);
196238104Sdes       if(i==0 || i==-1) {
197238104Sdes               LDNS_FREE(fkeyword);
198238104Sdes               return -1;
199238104Sdes       }
200238104Sdes
201238104Sdes       /* case??? i instead of strlen? */
202238104Sdes       if (strncmp(fkeyword, keyword, LDNS_MAX_KEYWORDLEN - 1) == 0) {
203238104Sdes               /* whee! */
204238104Sdes               /* printf("%s\n%s\n", "Matching keyword", fkeyword); */
205238104Sdes               i = ldns_fget_token_l(f, data, d_del, data_limit, line_nr);
206238104Sdes               LDNS_FREE(fkeyword);
207238104Sdes               return i;
208238104Sdes       } else {
209238104Sdes               /*printf("no match for %s (read: %s)\n", keyword, fkeyword);*/
210238104Sdes               LDNS_FREE(fkeyword);
211238104Sdes               return -1;
212238104Sdes       }
213238104Sdes}
214238104Sdes
215238104Sdes
216238104Sdesssize_t
217238104Sdesldns_bget_token(ldns_buffer *b, char *token, const char *delim, size_t limit)
218238104Sdes{
219238104Sdes	int c, lc;
220238104Sdes	int p; /* 0 -> no parenthese seen, >0 nr of ( seen */
221238104Sdes	int com, quoted;
222238104Sdes	char *t;
223238104Sdes	size_t i;
224238104Sdes	const char *d;
225238104Sdes	const char *del;
226238104Sdes
227238104Sdes	/* standard delimiters */
228238104Sdes	if (!delim) {
229238104Sdes		/* from isspace(3) */
230238104Sdes		del = LDNS_PARSE_NORMAL;
231238104Sdes	} else {
232238104Sdes		del = delim;
233238104Sdes	}
234238104Sdes
235238104Sdes	p = 0;
236238104Sdes	i = 0;
237238104Sdes	com = 0;
238238104Sdes	quoted = 0;
239238104Sdes	t = token;
240238104Sdes	lc = 0;
241238104Sdes	if (del[0] == '"') {
242238104Sdes		quoted = 1;
243238104Sdes	}
244238104Sdes
245238104Sdes	while ((c = ldns_bgetc(b)) != EOF) {
246238104Sdes		if (c == '\r') /* carriage return */
247238104Sdes			c = ' ';
248238104Sdes		if (c == '(' && lc != '\\' && !quoted) {
249238104Sdes			/* this only counts for non-comments */
250238104Sdes			if (com == 0) {
251238104Sdes				p++;
252238104Sdes			}
253238104Sdes			lc = c;
254238104Sdes			continue;
255238104Sdes		}
256238104Sdes
257238104Sdes		if (c == ')' && lc != '\\' && !quoted) {
258238104Sdes			/* this only counts for non-comments */
259238104Sdes			if (com == 0) {
260238104Sdes				p--;
261238104Sdes			}
262238104Sdes			lc = c;
263238104Sdes			continue;
264238104Sdes		}
265238104Sdes
266238104Sdes		if (p < 0) {
267238104Sdes			/* more ) then ( */
268238104Sdes			*t = '\0';
269238104Sdes			return 0;
270238104Sdes		}
271238104Sdes
272238104Sdes		/* do something with comments ; */
273238104Sdes		if (c == ';' && quoted == 0) {
274238104Sdes			if (lc != '\\') {
275238104Sdes				com = 1;
276238104Sdes			}
277238104Sdes		}
278238104Sdes		if (c == '"' && com == 0 && lc != '\\') {
279238104Sdes			quoted = 1 - quoted;
280238104Sdes		}
281238104Sdes
282238104Sdes		if (c == '\n' && com != 0) {
283238104Sdes			/* comments */
284238104Sdes			com = 0;
285238104Sdes			*t = ' ';
286238104Sdes			lc = c;
287238104Sdes			continue;
288238104Sdes		}
289238104Sdes
290238104Sdes		if (com == 1) {
291238104Sdes			*t = ' ';
292238104Sdes			lc = c;
293238104Sdes			continue;
294238104Sdes		}
295238104Sdes
296238104Sdes		if (c == '\n' && p != 0) {
297238104Sdes			/* in parentheses */
298238104Sdes			*t++ = ' ';
299238104Sdes			lc = c;
300238104Sdes			continue;
301238104Sdes		}
302238104Sdes
303238104Sdes		/* check if we hit the delim */
304238104Sdes		for (d = del; *d; d++) {
305238104Sdes                        if (c == *d && lc != '\\' && p == 0) {
306238104Sdes				goto tokenread;
307238104Sdes                        }
308238104Sdes		}
309238104Sdes
310238104Sdes		i++;
311266114Sdes		if (limit > 0 && (i >= limit || (size_t)(t-token) >= limit)) {
312238104Sdes			*t = '\0';
313238104Sdes			return -1;
314238104Sdes		}
315238104Sdes		*t++ = c;
316238104Sdes
317238104Sdes		if (c == '\\' && lc == '\\') {
318238104Sdes			lc = 0;
319238104Sdes		} else {
320238104Sdes			lc = c;
321238104Sdes		}
322238104Sdes	}
323238104Sdes	*t = '\0';
324238104Sdes	if (i == 0) {
325238104Sdes		/* nothing read */
326238104Sdes		return -1;
327238104Sdes	}
328238104Sdes	if (p != 0) {
329238104Sdes		return -1;
330238104Sdes	}
331238104Sdes	return (ssize_t)i;
332238104Sdes
333238104Sdestokenread:
334246854Sdes	ldns_bskipcs(b, del);
335238104Sdes	*t = '\0';
336238104Sdes
337238104Sdes	if (p != 0) {
338238104Sdes		return -1;
339238104Sdes	}
340238104Sdes	return (ssize_t)i;
341238104Sdes}
342238104Sdes
343238104Sdes
344238104Sdesvoid
345238104Sdesldns_bskipcs(ldns_buffer *buffer, const char *s)
346238104Sdes{
347238104Sdes        bool found;
348238104Sdes        char c;
349238104Sdes        const char *d;
350238104Sdes
351238104Sdes        while(ldns_buffer_available_at(buffer, buffer->_position, sizeof(char))) {
352238104Sdes                c = (char) ldns_buffer_read_u8_at(buffer, buffer->_position);
353238104Sdes                found = false;
354238104Sdes                for (d = s; *d; d++) {
355238104Sdes                        if (*d == c) {
356238104Sdes                                found = true;
357238104Sdes                        }
358238104Sdes                }
359238104Sdes                if (found && buffer->_limit > buffer->_position) {
360238104Sdes                        buffer->_position += sizeof(char);
361238104Sdes                } else {
362238104Sdes                        return;
363238104Sdes                }
364238104Sdes        }
365238104Sdes}
366238104Sdes
367238104Sdesvoid
368238104Sdesldns_fskipcs(FILE *fp, const char *s)
369238104Sdes{
370238104Sdes	ldns_fskipcs_l(fp, s, NULL);
371238104Sdes}
372238104Sdes
373238104Sdesvoid
374238104Sdesldns_fskipcs_l(FILE *fp, const char *s, int *line_nr)
375238104Sdes{
376238104Sdes        bool found;
377238104Sdes        int c;
378238104Sdes        const char *d;
379238104Sdes
380238104Sdes	while ((c = fgetc(fp)) != EOF) {
381238104Sdes		if (line_nr && c == '\n') {
382238104Sdes			*line_nr = *line_nr + 1;
383238104Sdes		}
384238104Sdes                found = false;
385238104Sdes                for (d = s; *d; d++) {
386238104Sdes                        if (*d == c) {
387238104Sdes                                found = true;
388238104Sdes                        }
389238104Sdes                }
390238104Sdes		if (!found) {
391238104Sdes			/* with getc, we've read too far */
392238104Sdes			ungetc(c, fp);
393238104Sdes			return;
394238104Sdes		}
395238104Sdes	}
396238104Sdes}
397238104Sdes
398238104Sdesssize_t
399238104Sdesldns_bget_keyword_data(ldns_buffer *b, const char *keyword, const char *k_del, char
400238104Sdes*data, const char *d_del, size_t data_limit)
401238104Sdes{
402238104Sdes       /* we assume: keyword|sep|data */
403238104Sdes       char *fkeyword;
404238104Sdes       ssize_t i;
405238104Sdes
406238104Sdes       if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
407238104Sdes               return -1;
408238104Sdes       fkeyword = LDNS_XMALLOC(char, LDNS_MAX_KEYWORDLEN);
409238104Sdes       if(!fkeyword)
410238104Sdes               return -1; /* out of memory */
411238104Sdes
412238104Sdes       i = ldns_bget_token(b, fkeyword, k_del, data_limit);
413238104Sdes       if(i==0 || i==-1) {
414238104Sdes               LDNS_FREE(fkeyword);
415238104Sdes               return -1; /* nothing read */
416238104Sdes       }
417238104Sdes
418238104Sdes       /* case??? */
419238104Sdes       if (strncmp(fkeyword, keyword, strlen(keyword)) == 0) {
420238104Sdes               LDNS_FREE(fkeyword);
421238104Sdes               /* whee, the match! */
422238104Sdes               /* retrieve it's data */
423238104Sdes               i = ldns_bget_token(b, data, d_del, 0);
424238104Sdes               return i;
425238104Sdes       } else {
426238104Sdes               LDNS_FREE(fkeyword);
427238104Sdes               return -1;
428238104Sdes       }
429238104Sdes}
430238104Sdes
431