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 <ldns/config.h>
11#include <ldns/ldns.h>
12
13#include <limits.h>
14#include <strings.h>
15
16ldns_lookup_table ldns_directive_types[] = {
17        { LDNS_DIR_TTL, "$TTL" },
18        { LDNS_DIR_ORIGIN, "$ORIGIN" },
19        { LDNS_DIR_INCLUDE, "$INCLUDE" },
20        { 0, NULL }
21};
22
23/* add max_limit here? */
24ssize_t
25ldns_fget_token(FILE *f, char *token, const char *delim, size_t limit)
26{
27	return ldns_fget_token_l(f, token, delim, limit, NULL);
28}
29
30enum file_type2parse {
31	zone_file_type, resolv_conf_file_type
32};
33
34static ldns_status
35ldns_fget_token_l_st_file_type(FILE *f, char **token, size_t *limit,
36		bool fixed, const char *delim, int *line_nr,
37		enum file_type2parse file_type)
38{
39	int c, prev_c;
40	int p; /* 0 -> no parentheses seen, >0 nr of ( seen */
41	int com, quoted;
42	char *t, *old_token;
43	size_t i;
44	const char *d;
45	const char *del;
46
47	/* standard delimiters */
48	if (!delim) {
49		/* from isspace(3) */
50		del = LDNS_PARSE_NORMAL;
51	} else {
52		del = delim;
53	}
54	if (!token || !limit)
55		return LDNS_STATUS_NULL;
56
57	if (fixed) {
58		if (*token == NULL || *limit == 0)
59			return LDNS_STATUS_NULL;
60
61	} else if (*token == NULL) {
62		*limit = LDNS_MAX_LINELEN;
63		if (!(*token = LDNS_XMALLOC(char, *limit + 1)))
64			return LDNS_STATUS_MEM_ERR;
65
66	} else if (*limit == 0)
67		return LDNS_STATUS_ERR;
68	p = 0;
69	i = 0;
70	com = 0;
71	quoted = 0;
72	prev_c = 0;
73	t = *token;
74	if (del[0] == '"') {
75		quoted = 1;
76	}
77	while ((c = getc(f)) != EOF) {
78		if (c == '\r') /* carriage return */
79			c = ' ';
80		if (c == '(' && prev_c != '\\' && !quoted) {
81			/* this only counts for non-comments */
82			if (com == 0) {
83				p++;
84			}
85			prev_c = c;
86			continue;
87		}
88
89		if (c == ')' && prev_c != '\\' && !quoted) {
90			/* this only counts for non-comments */
91			if (com == 0) {
92				p--;
93			}
94			prev_c = c;
95			continue;
96		}
97
98		if (p < 0) {
99			/* more ) then ( - close off the string */
100			*t = '\0';
101			return i == 0 ? LDNS_STATUS_SYNTAX_EMPTY
102			              : LDNS_STATUS_OK;
103		}
104
105		/* do something with comments ; */
106		if ((c == ';'
107		||  (c == '#' && file_type == resolv_conf_file_type))
108				&& quoted == 0) {
109			if (prev_c != '\\') {
110				com = 1;
111			}
112		}
113		if (c == '\"' && com == 0 && prev_c != '\\') {
114			quoted = 1 - quoted;
115		}
116
117		if (c == '\n' && com != 0) {
118			/* comments */
119			com = 0;
120			*t = ' ';
121			if (line_nr) {
122				*line_nr = *line_nr + 1;
123			}
124			if (p == 0 && i > 0) {
125				goto tokenread;
126			} else {
127				prev_c = c;
128				continue;
129			}
130		}
131
132		if (com == 1) {
133			*t = ' ';
134			prev_c = c;
135			continue;
136		}
137
138		if (c == '\n' && p != 0 && t > *token) {
139			/* in parentheses */
140			if (line_nr) {
141				*line_nr = *line_nr + 1;
142			}
143			if (*limit > 0
144			&&  (i >= *limit || (size_t)(t - *token) >= *limit)) {
145				if (fixed) {
146					*t = '\0';
147					return LDNS_STATUS_SYNTAX_ERR;
148				}
149				old_token = *token;
150				*limit *= 2;
151				*token = LDNS_XREALLOC(*token, char, *limit + 1);
152				if (*token == NULL) {
153					*token = old_token;
154					*t = '\0';
155					return LDNS_STATUS_MEM_ERR;
156				}
157				if (*token != old_token)
158					t = *token + (t - old_token);
159			}
160			*t++ = ' ';
161			prev_c = c;
162			continue;
163		}
164
165		/* check if we hit the delim */
166		for (d = del; *d; d++) {
167			if (c == *d && i > 0 && prev_c != '\\' && p == 0) {
168				if (c == '\n' && line_nr) {
169					*line_nr = *line_nr + 1;
170				}
171				goto tokenread;
172			}
173		}
174		if (c != '\0' && c != '\n') {
175			i++;
176		}
177		if (*limit > 0
178		&&  (i >= *limit || (size_t)(t - *token) >= *limit)) {
179			if (fixed) {
180				*t = '\0';
181				return LDNS_STATUS_SYNTAX_ERR;
182			}
183			old_token = *token;
184			*limit *= 2;
185			*token = LDNS_XREALLOC(*token, char, *limit + 1);
186			if (*token == NULL) {
187				*token = old_token;
188				*t = '\0';
189				return LDNS_STATUS_MEM_ERR;
190			}
191			if (*token != old_token)
192				t = *token + (t - old_token);
193		}
194		if (c != '\0' && c != '\n') {
195			*t++ = c;
196		}
197		if (c == '\n' && line_nr) {
198			*line_nr = *line_nr + 1;
199		}
200		if (c == '\\' && prev_c == '\\')
201			prev_c = 0;
202		else	prev_c = c;
203	}
204	*t = '\0';
205	if (c == EOF) {
206		return i == 0 ? LDNS_STATUS_SYNTAX_EMPTY : LDNS_STATUS_OK;
207	}
208
209	if (p != 0) {
210		return LDNS_STATUS_SYNTAX_ERR;
211	}
212	return i == 0 ? LDNS_STATUS_SYNTAX_EMPTY : LDNS_STATUS_OK;
213
214tokenread:
215	if(*del == '"') /* do not skip over quotes, they are significant */
216		ldns_fskipcs_l(f, del+1, line_nr);
217	else	ldns_fskipcs_l(f, del, line_nr);
218	*t = '\0';
219	if (p != 0) {
220		return LDNS_STATUS_SYNTAX_ERR;
221	}
222	return i == 0 ? LDNS_STATUS_SYNTAX_EMPTY : LDNS_STATUS_OK;
223}
224
225ldns_status
226ldns_fget_token_l_st(FILE *f, char **token, size_t *limit, bool fixed
227                    , const char *delim, int *line_nr)
228{
229	return ldns_fget_token_l_st_file_type(
230		f, token, limit, fixed, delim, line_nr, zone_file_type);
231}
232
233ssize_t
234ldns_fget_token_l_resolv_conf(FILE *f, char *token, const char *delim,
235		size_t limit, int *line_nr)
236{
237	if (limit == 0)
238		limit = LDNS_MAX_LINELEN;
239	if (ldns_fget_token_l_st_file_type(f, &token, &limit, true, delim,
240				line_nr, resolv_conf_file_type))
241		return -1;
242	else
243		return (ssize_t)strlen(token);
244}
245
246ssize_t
247ldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *line_nr)
248{
249	if (limit == 0)
250		limit = LDNS_MAX_LINELEN;
251	if (ldns_fget_token_l_st(f, &token, &limit, true, delim, line_nr))
252		return -1;
253	else
254		return (ssize_t)strlen(token);
255}
256
257ssize_t
258ldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, char *data,
259               const char *d_del, size_t data_limit)
260{
261       return ldns_fget_keyword_data_l(f, keyword, k_del, data, d_del,
262		       data_limit, NULL);
263}
264
265ssize_t
266ldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_del, char *data,
267               const char *d_del, size_t data_limit, int *line_nr)
268{
269       /* we assume: keyword|sep|data */
270       char *fkeyword;
271       ssize_t i;
272
273       if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
274               return -1;
275       fkeyword = LDNS_XMALLOC(char, LDNS_MAX_KEYWORDLEN);
276       if(!fkeyword)
277               return -1;
278
279       i = ldns_fget_token(f, fkeyword, k_del, LDNS_MAX_KEYWORDLEN);
280       if(i==0 || i==-1) {
281               LDNS_FREE(fkeyword);
282               return -1;
283       }
284
285       /* case??? i instead of strlen? */
286       if (strncmp(fkeyword, keyword, LDNS_MAX_KEYWORDLEN - 1) == 0) {
287               /* whee! */
288               /* printf("%s\n%s\n", "Matching keyword", fkeyword); */
289               i = ldns_fget_token_l(f, data, d_del, data_limit, line_nr);
290               LDNS_FREE(fkeyword);
291               return i;
292       } else {
293               /*printf("no match for %s (read: %s)\n", keyword, fkeyword);*/
294               LDNS_FREE(fkeyword);
295               return -1;
296       }
297}
298
299
300ssize_t
301ldns_bget_token(ldns_buffer *b, char *token, const char *delim, size_t limit)
302{
303	int c, lc;
304	int p; /* 0 -> no parentheses seen, >0 nr of ( seen */
305	int com, quoted;
306	char *t;
307	size_t i;
308	const char *d;
309	const char *del;
310
311	/* standard delimiters */
312	if (!delim) {
313		/* from isspace(3) */
314		del = LDNS_PARSE_NORMAL;
315	} else {
316		del = delim;
317	}
318
319	p = 0;
320	i = 0;
321	com = 0;
322	quoted = 0;
323	t = token;
324	lc = 0;
325	if (del[0] == '"') {
326		quoted = 1;
327	}
328
329	while ((c = ldns_bgetc(b)) != EOF) {
330		if (c == '\r') /* carriage return */
331			c = ' ';
332		if (c == '(' && lc != '\\' && !quoted) {
333			/* this only counts for non-comments */
334			if (com == 0) {
335				p++;
336			}
337			lc = c;
338			continue;
339		}
340
341		if (c == ')' && lc != '\\' && !quoted) {
342			/* this only counts for non-comments */
343			if (com == 0) {
344				p--;
345			}
346			lc = c;
347			continue;
348		}
349
350		if (p < 0) {
351			/* more ) then ( */
352			*t = '\0';
353			return 0;
354		}
355
356		/* do something with comments ; */
357		if (c == ';' && quoted == 0) {
358			if (lc != '\\') {
359				com = 1;
360			}
361		}
362		if (c == '"' && com == 0 && lc != '\\') {
363			quoted = 1 - quoted;
364		}
365
366		if (c == '\n' && com != 0) {
367			/* comments */
368			com = 0;
369			*t = ' ';
370			lc = c;
371			continue;
372		}
373
374		if (com == 1) {
375			*t = ' ';
376			lc = c;
377			continue;
378		}
379
380		if (c == '\n' && p != 0) {
381			/* in parentheses */
382			*t++ = ' ';
383			lc = c;
384			continue;
385		}
386
387		/* check if we hit the delim */
388		for (d = del; *d; d++) {
389                        if (c == *d && lc != '\\' && p == 0) {
390				goto tokenread;
391                        }
392		}
393
394		i++;
395		if (limit > 0 && (i >= limit || (size_t)(t-token) >= limit)) {
396			*t = '\0';
397			return -1;
398		}
399		*t++ = c;
400
401		if (c == '\\' && lc == '\\') {
402			lc = 0;
403		} else {
404			lc = c;
405		}
406	}
407	*t = '\0';
408	if (i == 0) {
409		/* nothing read */
410		return -1;
411	}
412	if (p != 0) {
413		return -1;
414	}
415	return (ssize_t)i;
416
417tokenread:
418	if(*del == '"') /* do not skip over quotes, they are significant */
419		ldns_bskipcs(b, del+1);
420	else	ldns_bskipcs(b, del);
421	*t = '\0';
422
423	if (p != 0) {
424		return -1;
425	}
426	return (ssize_t)i;
427}
428
429
430void
431ldns_bskipcs(ldns_buffer *buffer, const char *s)
432{
433        bool found;
434        char c;
435        const char *d;
436
437        while(ldns_buffer_available_at(buffer, buffer->_position, sizeof(char))) {
438                c = (char) ldns_buffer_read_u8_at(buffer, buffer->_position);
439                found = false;
440                for (d = s; *d; d++) {
441                        if (*d == c) {
442                                found = true;
443                        }
444                }
445                if (found && buffer->_limit > buffer->_position) {
446                        buffer->_position += sizeof(char);
447                } else {
448                        return;
449                }
450        }
451}
452
453void
454ldns_fskipcs(FILE *fp, const char *s)
455{
456	ldns_fskipcs_l(fp, s, NULL);
457}
458
459void
460ldns_fskipcs_l(FILE *fp, const char *s, int *line_nr)
461{
462        bool found;
463        int c;
464        const char *d;
465
466	while ((c = fgetc(fp)) != EOF) {
467		if (line_nr && c == '\n') {
468			*line_nr = *line_nr + 1;
469		}
470                found = false;
471                for (d = s; *d; d++) {
472                        if (*d == c) {
473                                found = true;
474                        }
475                }
476		if (!found) {
477			/* with getc, we've read too far */
478			ungetc(c, fp);
479			return;
480		}
481	}
482}
483
484ssize_t
485ldns_bget_keyword_data(ldns_buffer *b, const char *keyword, const char *k_del, char
486*data, const char *d_del, size_t data_limit)
487{
488       /* we assume: keyword|sep|data */
489       char *fkeyword;
490       ssize_t i;
491
492       if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
493               return -1;
494       fkeyword = LDNS_XMALLOC(char, LDNS_MAX_KEYWORDLEN);
495       if(!fkeyword)
496               return -1; /* out of memory */
497
498       i = ldns_bget_token(b, fkeyword, k_del, data_limit);
499       if(i==0 || i==-1) {
500               LDNS_FREE(fkeyword);
501               return -1; /* nothing read */
502       }
503
504       /* case??? */
505       if (strncmp(fkeyword, keyword, strlen(keyword)) == 0) {
506               LDNS_FREE(fkeyword);
507               /* whee, the match! */
508               /* retrieve it's data */
509               i = ldns_bget_token(b, data, d_del, 0);
510               return i;
511       } else {
512               LDNS_FREE(fkeyword);
513               return -1;
514       }
515}
516
517