Home | History | Annotate | Line # | Download | only in sldns
      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 
     18 sldns_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? */
     26 ssize_t
     27 sldns_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 
     32 ssize_t
     33 sldns_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 parentheses seen, >0 nr of ( seen */
     37 	int com, quoted, only_blank;
     38 	char *t;
     39 	size_t i;
     40 	const char *d;
     41 	const char *del;
     42 
     43 	/* standard delimiters */
     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 	only_blank = 1;	/* Assume we got only <blank> until now */
     57 	t = token;
     58 	if (del[0] == '"') {
     59 		quoted = 1;
     60 	}
     61 	while ((c = getc(f)) != EOF) {
     62 		if (c == '\r') /* carriage return */
     63 			c = ' ';
     64 		if (c == '(' && prev_c != '\\' && !quoted) {
     65 			/* this only counts for non-comments */
     66 			if (com == 0) {
     67 				p++;
     68 			}
     69 			prev_c = c;
     70 			continue;
     71 		}
     72 
     73 		if (c == ')' && prev_c != '\\' && !quoted) {
     74 			/* this only counts for non-comments */
     75 			if (com == 0) {
     76 				p--;
     77 			}
     78 			prev_c = c;
     79 			continue;
     80 		}
     81 
     82 		if (p < 0) {
     83 			/* more ) then ( - close off the string */
     84 			*t = '\0';
     85 			return 0;
     86 		}
     87 
     88 		/* do something with comments ; */
     89 		if (c == ';' && quoted == 0) {
     90 			if (prev_c != '\\') {
     91 				com = 1;
     92 			}
     93 		}
     94 		if (c == '\"' && com == 0 && prev_c != '\\') {
     95 			quoted = 1 - quoted;
     96 		}
     97 
     98 		if (c == '\n' && com != 0) {
     99 			/* comments */
    100 			com = 0;
    101 			*t = ' ';
    102 			if (line_nr) {
    103 				*line_nr = *line_nr + 1;
    104 			}
    105 			if (only_blank && i > 0) {
    106 				/* Got only <blank> so far. Reset and try
    107 				 * again with the next line.
    108 				 */
    109 				i = 0;
    110 				t = token;
    111 			}
    112 			if (p == 0) {
    113 				/* If p != 0 then the next line is a continuation. So
    114 				 * we assume that the next line starts with a blank only
    115 				 * if it is actually a new line.
    116 				 */
    117 				only_blank = 1;	/* Assume next line starts with
    118 						 * <blank>.
    119 						 */
    120 			}
    121 			if (p == 0 && i > 0) {
    122 				goto tokenread;
    123 			} else {
    124 				prev_c = c;
    125 				continue;
    126 			}
    127 		}
    128 
    129 		if (com == 1) {
    130 			*t = ' ';
    131 			prev_c = c;
    132 			continue;
    133 		}
    134 
    135 		if (c == '\n' && p != 0 && t > token) {
    136 			/* in parentheses */
    137 			if (line_nr) {
    138 				*line_nr = *line_nr + 1;
    139 			}
    140 			if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) {
    141 				*t = '\0';
    142 				return -1;
    143 			}
    144 			*t++ = ' ';
    145 			prev_c = c;
    146 			continue;
    147 		}
    148 
    149 		/* check if we hit the delim */
    150 		for (d = del; *d; d++) {
    151 			if (c == *d)
    152 				break;
    153 		}
    154 
    155 		if (c == *d && i > 0 && prev_c != '\\' && p == 0) {
    156 			if (c == '\n' && line_nr) {
    157 				*line_nr = *line_nr + 1;
    158 			}
    159 			if (only_blank) {
    160 				/* Got only <blank> so far. Reset and
    161 				 * try again with the next line.
    162 				 */
    163 				i = 0;
    164 				t = token;
    165 				only_blank = 1;
    166 				prev_c = c;
    167 				continue;
    168 			}
    169 			goto tokenread;
    170 		}
    171 		if (c != ' ' && c != '\t') {
    172 			/* Found something that is not <blank> */
    173 			only_blank= 0;
    174 		}
    175 		if (c != '\0' && c != '\n') {
    176 			i++;
    177 		}
    178 		/* is there space for the character and the zero after it */
    179 		if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) {
    180 			*t = '\0';
    181 			return -1;
    182 		}
    183 		if (c != '\0' && c != '\n') {
    184 			*t++ = c;
    185 		}
    186 		if (c == '\n') {
    187 			if (line_nr) {
    188 				*line_nr = *line_nr + 1;
    189 			}
    190 			only_blank = 1;	/* Assume next line starts with
    191 					 * <blank>.
    192 					 */
    193 		}
    194 		if (c == '\\' && prev_c == '\\')
    195 			prev_c = 0;
    196 		else	prev_c = c;
    197 	}
    198 	*t = '\0';
    199 	if (c == EOF) {
    200 		return (ssize_t)i;
    201 	}
    202 
    203 	if (i == 0) {
    204 		/* nothing read */
    205 		return -1;
    206 	}
    207 	if (p != 0) {
    208 		return -1;
    209 	}
    210 	return (ssize_t)i;
    211 
    212 tokenread:
    213 	if(*del == '"')
    214 		/* do not skip over quotes after the string, they are part
    215 		 * of the next string.  But skip over whitespace (if needed)*/
    216 		sldns_fskipcs_l(f, del+1, line_nr);
    217 	else	sldns_fskipcs_l(f, del, line_nr);
    218 	*t = '\0';
    219 	if (p != 0) {
    220 		return -1;
    221 	}
    222 
    223 	return (ssize_t)i;
    224 }
    225 
    226 ssize_t
    227 sldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, char *data,
    228                const char *d_del, size_t data_limit)
    229 {
    230        return sldns_fget_keyword_data_l(f, keyword, k_del, data, d_del,
    231 		       data_limit, NULL);
    232 }
    233 
    234 ssize_t
    235 sldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_del, char *data,
    236                const char *d_del, size_t data_limit, int *line_nr)
    237 {
    238        /* we assume: keyword|sep|data */
    239        char *fkeyword;
    240        ssize_t i;
    241 
    242        if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
    243                return -1;
    244        fkeyword = (char*)malloc(LDNS_MAX_KEYWORDLEN);
    245        if(!fkeyword)
    246                return -1;
    247 
    248        i = sldns_fget_token(f, fkeyword, k_del, LDNS_MAX_KEYWORDLEN);
    249        if(i==0 || i==-1) {
    250                free(fkeyword);
    251                return -1;
    252        }
    253 
    254        /* case??? i instead of strlen? */
    255        if (strncmp(fkeyword, keyword, LDNS_MAX_KEYWORDLEN - 1) == 0) {
    256                /* whee! */
    257                /* printf("%s\n%s\n", "Matching keyword", fkeyword); */
    258                i = sldns_fget_token_l(f, data, d_del, data_limit, line_nr);
    259                free(fkeyword);
    260                return i;
    261        } else {
    262                /*printf("no match for %s (read: %s)\n", keyword, fkeyword);*/
    263                free(fkeyword);
    264                return -1;
    265        }
    266 }
    267 
    268 int
    269 sldns_bgetc(sldns_buffer *buffer)
    270 {
    271 	if (!sldns_buffer_available_at(buffer, buffer->_position, sizeof(uint8_t))) {
    272 		sldns_buffer_set_position(buffer, sldns_buffer_limit(buffer));
    273 		/* sldns_buffer_rewind(buffer);*/
    274 		return EOF;
    275 	}
    276 	return (int)sldns_buffer_read_u8(buffer);
    277 }
    278 
    279 ssize_t
    280 sldns_bget_token(sldns_buffer *b, char *token, const char *delim, size_t limit)
    281 {
    282 	return sldns_bget_token_par(b, token, delim, limit, NULL, NULL);
    283 }
    284 
    285 ssize_t
    286 sldns_bget_token_par(sldns_buffer *b, char *token, const char *delim,
    287 	size_t limit, int* par, const char* skipw)
    288 {
    289 	int c, lc;
    290 	int p; /* 0 -> no parentheses seen, >0 nr of ( seen */
    291 	int com, quoted;
    292 	char *t;
    293 	size_t i;
    294 	const char *d;
    295 	const char *del;
    296 
    297 	/* standard delimiters */
    298 	if (!delim) {
    299 		/* from isspace(3) */
    300 		del = LDNS_PARSE_NORMAL;
    301 	} else {
    302 		del = delim;
    303 	}
    304 
    305 	p = (par?*par:0);
    306 	i = 0;
    307 	com = 0;
    308 	quoted = 0;
    309 	t = token;
    310 	lc = 0;
    311 	if (del[0] == '"') {
    312 		quoted = 1;
    313 	}
    314 
    315 	while ((c = sldns_bgetc(b)) != EOF) {
    316 		if (c == '\r') /* carriage return */
    317 			c = ' ';
    318 		if (c == '(' && lc != '\\' && !quoted) {
    319 			/* this only counts for non-comments */
    320 			if (com == 0) {
    321 				if(par) (*par)++;
    322 				p++;
    323 			}
    324 			lc = c;
    325 			continue;
    326 		}
    327 
    328 		if (c == ')' && lc != '\\' && !quoted) {
    329 			/* this only counts for non-comments */
    330 			if (com == 0) {
    331 				if(par) (*par)--;
    332 				p--;
    333 			}
    334 			lc = c;
    335 			continue;
    336 		}
    337 
    338 		if (p < 0) {
    339 			/* more ) then ( */
    340 			*t = '\0';
    341 			return 0;
    342 		}
    343 
    344 		/* do something with comments ; */
    345 		if (c == ';' && quoted == 0) {
    346 			if (lc != '\\') {
    347 				com = 1;
    348 			}
    349 		}
    350 		if (c == '"' && com == 0 && lc != '\\') {
    351 			quoted = 1 - quoted;
    352 		}
    353 
    354 		if (c == '\n' && com != 0) {
    355 			/* comments */
    356 			com = 0;
    357 			*t = ' ';
    358 			lc = c;
    359 			continue;
    360 		}
    361 
    362 		if (com == 1) {
    363 			*t = ' ';
    364 			lc = c;
    365 			continue;
    366 		}
    367 
    368 		if (c == '\n' && p != 0) {
    369 			/* in parentheses */
    370 			/* do not write ' ' if we want to skip spaces */
    371 			if(!(skipw && (strchr(skipw, c)||strchr(skipw, ' ')))) {
    372 				/* check for space for the space character and a zero delimiter after that. */
    373 				if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) {
    374 					*t = '\0';
    375 					return -1;
    376 				}
    377 				*t++ = ' ';
    378 			}
    379 			lc = c;
    380 			continue;
    381 		}
    382 
    383 		/* check to skip whitespace at start, but also after ( */
    384 		if(skipw && i==0 && !com && !quoted && lc != '\\') {
    385 			if(strchr(skipw, c)) {
    386 				lc = c;
    387 				continue;
    388 			}
    389 		}
    390 
    391 		/* check if we hit the delim */
    392 		for (d = del; *d; d++) {
    393 			/* we can only exit if no parens or user tracks them */
    394                         if (c == *d && lc != '\\' && (p == 0 || par)) {
    395 				goto tokenread;
    396                         }
    397 		}
    398 
    399 		i++;
    400 		if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) {
    401 			*t = '\0';
    402 			return -1;
    403 		}
    404 		*t++ = c;
    405 
    406 		if (c == '\\' && lc == '\\') {
    407 			lc = 0;
    408 		} else {
    409 			lc = c;
    410 		}
    411 	}
    412 	*t = '\0';
    413 	if (i == 0) {
    414 		/* nothing read */
    415 		return -1;
    416 	}
    417 	if (!par && p != 0) {
    418 		return -1;
    419 	}
    420 	return (ssize_t)i;
    421 
    422 tokenread:
    423 	if(*del == '"')
    424 		/* do not skip over quotes after the string, they are part
    425 		 * of the next string.  But skip over whitespace (if needed)*/
    426 		sldns_bskipcs(b, del+1);
    427 	else 	sldns_bskipcs(b, del);
    428 	*t = '\0';
    429 
    430 	if (!par && p != 0) {
    431 		return -1;
    432 	}
    433 	return (ssize_t)i;
    434 }
    435 
    436 
    437 void
    438 sldns_bskipcs(sldns_buffer *buffer, const char *s)
    439 {
    440         int found;
    441         char c;
    442         const char *d;
    443 
    444         while(sldns_buffer_available_at(buffer, buffer->_position, sizeof(char))) {
    445                 c = (char) sldns_buffer_read_u8_at(buffer, buffer->_position);
    446                 found = 0;
    447                 for (d = s; *d; d++) {
    448                         if (*d == c) {
    449                                 found = 1;
    450                         }
    451                 }
    452                 if (found && buffer->_limit > buffer->_position) {
    453                         buffer->_position += sizeof(char);
    454                 } else {
    455                         return;
    456                 }
    457         }
    458 }
    459 
    460 void
    461 sldns_fskipcs(FILE *fp, const char *s)
    462 {
    463 	sldns_fskipcs_l(fp, s, NULL);
    464 }
    465 
    466 void
    467 sldns_fskipcs_l(FILE *fp, const char *s, int *line_nr)
    468 {
    469         int found;
    470         int c;
    471         const char *d;
    472 
    473 	while ((c = fgetc(fp)) != EOF) {
    474 		if (line_nr && c == '\n') {
    475 			*line_nr = *line_nr + 1;
    476 		}
    477                 found = 0;
    478                 for (d = s; *d; d++) {
    479                         if (*d == c) {
    480                                 found = 1;
    481                         }
    482                 }
    483 		if (!found) {
    484 			/* with getc, we've read too far */
    485 			ungetc(c, fp);
    486 			return;
    487 		}
    488 	}
    489 }
    490 
    491 ssize_t
    492 sldns_bget_keyword_data(sldns_buffer *b, const char *keyword, const char *k_del, char
    493 *data, const char *d_del, size_t data_limit)
    494 {
    495        /* we assume: keyword|sep|data */
    496        char *fkeyword;
    497        ssize_t i;
    498 
    499        if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
    500                return -1;
    501        fkeyword = (char*)malloc(LDNS_MAX_KEYWORDLEN);
    502        if(!fkeyword)
    503                return -1; /* out of memory */
    504 
    505        i = sldns_bget_token(b, fkeyword, k_del, data_limit);
    506        if(i==0 || i==-1) {
    507                free(fkeyword);
    508                return -1; /* nothing read */
    509        }
    510 
    511        /* case??? */
    512        if (strncmp(fkeyword, keyword, strlen(keyword)) == 0) {
    513                free(fkeyword);
    514                /* whee, the match! */
    515                /* retrieve it's data */
    516                i = sldns_bget_token(b, data, d_del, 0);
    517                return i;
    518        } else {
    519                free(fkeyword);
    520                return -1;
    521        }
    522 }
    523 
    524