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