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; 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 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 if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) { 124 *t = '\0'; 125 return -1; 126 } 127 *t++ = ' '; 128 prev_c = c; 129 continue; 130 } 131 132 /* check if we hit the delim */ 133 for (d = del; *d; d++) { 134 if (c == *d && i > 0 && prev_c != '\\' && p == 0) { 135 if (c == '\n' && line_nr) { 136 *line_nr = *line_nr + 1; 137 } 138 goto tokenread; 139 } 140 } 141 if (c != '\0' && c != '\n') { 142 i++; 143 } 144 /* is there space for the character and the zero after it */ 145 if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) { 146 *t = '\0'; 147 return -1; 148 } 149 if (c != '\0' && c != '\n') { 150 *t++ = c; 151 } 152 if (c == '\\' && prev_c == '\\') 153 prev_c = 0; 154 else prev_c = c; 155 } 156 *t = '\0'; 157 if (c == EOF) { 158 return (ssize_t)i; 159 } 160 161 if (i == 0) { 162 /* nothing read */ 163 return -1; 164 } 165 if (p != 0) { 166 return -1; 167 } 168 return (ssize_t)i; 169 170 tokenread: 171 if(*del == '"') 172 /* do not skip over quotes after the string, they are part 173 * of the next string. But skip over whitespace (if needed)*/ 174 sldns_fskipcs_l(f, del+1, line_nr); 175 else sldns_fskipcs_l(f, del, line_nr); 176 *t = '\0'; 177 if (p != 0) { 178 return -1; 179 } 180 181 return (ssize_t)i; 182 } 183 184 ssize_t 185 sldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, char *data, 186 const char *d_del, size_t data_limit) 187 { 188 return sldns_fget_keyword_data_l(f, keyword, k_del, data, d_del, 189 data_limit, NULL); 190 } 191 192 ssize_t 193 sldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_del, char *data, 194 const char *d_del, size_t data_limit, int *line_nr) 195 { 196 /* we assume: keyword|sep|data */ 197 char *fkeyword; 198 ssize_t i; 199 200 if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN) 201 return -1; 202 fkeyword = (char*)malloc(LDNS_MAX_KEYWORDLEN); 203 if(!fkeyword) 204 return -1; 205 206 i = sldns_fget_token(f, fkeyword, k_del, LDNS_MAX_KEYWORDLEN); 207 if(i==0 || i==-1) { 208 free(fkeyword); 209 return -1; 210 } 211 212 /* case??? i instead of strlen? */ 213 if (strncmp(fkeyword, keyword, LDNS_MAX_KEYWORDLEN - 1) == 0) { 214 /* whee! */ 215 /* printf("%s\n%s\n", "Matching keyword", fkeyword); */ 216 i = sldns_fget_token_l(f, data, d_del, data_limit, line_nr); 217 free(fkeyword); 218 return i; 219 } else { 220 /*printf("no match for %s (read: %s)\n", keyword, fkeyword);*/ 221 free(fkeyword); 222 return -1; 223 } 224 } 225 226 int 227 sldns_bgetc(sldns_buffer *buffer) 228 { 229 if (!sldns_buffer_available_at(buffer, buffer->_position, sizeof(uint8_t))) { 230 sldns_buffer_set_position(buffer, sldns_buffer_limit(buffer)); 231 /* sldns_buffer_rewind(buffer);*/ 232 return EOF; 233 } 234 return (int)sldns_buffer_read_u8(buffer); 235 } 236 237 ssize_t 238 sldns_bget_token(sldns_buffer *b, char *token, const char *delim, size_t limit) 239 { 240 return sldns_bget_token_par(b, token, delim, limit, NULL, NULL); 241 } 242 243 ssize_t 244 sldns_bget_token_par(sldns_buffer *b, char *token, const char *delim, 245 size_t limit, int* par, const char* skipw) 246 { 247 int c, lc; 248 int p; /* 0 -> no parentheses seen, >0 nr of ( seen */ 249 int com, quoted; 250 char *t; 251 size_t i; 252 const char *d; 253 const char *del; 254 255 /* standard delimiters */ 256 if (!delim) { 257 /* from isspace(3) */ 258 del = LDNS_PARSE_NORMAL; 259 } else { 260 del = delim; 261 } 262 263 p = (par?*par:0); 264 i = 0; 265 com = 0; 266 quoted = 0; 267 t = token; 268 lc = 0; 269 if (del[0] == '"') { 270 quoted = 1; 271 } 272 273 while ((c = sldns_bgetc(b)) != EOF) { 274 if (c == '\r') /* carriage return */ 275 c = ' '; 276 if (c == '(' && lc != '\\' && !quoted) { 277 /* this only counts for non-comments */ 278 if (com == 0) { 279 if(par) (*par)++; 280 p++; 281 } 282 lc = c; 283 continue; 284 } 285 286 if (c == ')' && lc != '\\' && !quoted) { 287 /* this only counts for non-comments */ 288 if (com == 0) { 289 if(par) (*par)--; 290 p--; 291 } 292 lc = c; 293 continue; 294 } 295 296 if (p < 0) { 297 /* more ) then ( */ 298 *t = '\0'; 299 return 0; 300 } 301 302 /* do something with comments ; */ 303 if (c == ';' && quoted == 0) { 304 if (lc != '\\') { 305 com = 1; 306 } 307 } 308 if (c == '"' && com == 0 && lc != '\\') { 309 quoted = 1 - quoted; 310 } 311 312 if (c == '\n' && com != 0) { 313 /* comments */ 314 com = 0; 315 *t = ' '; 316 lc = c; 317 continue; 318 } 319 320 if (com == 1) { 321 *t = ' '; 322 lc = c; 323 continue; 324 } 325 326 if (c == '\n' && p != 0) { 327 /* in parentheses */ 328 /* do not write ' ' if we want to skip spaces */ 329 if(!(skipw && (strchr(skipw, c)||strchr(skipw, ' ')))) { 330 /* check for space for the space character and a zero delimiter after that. */ 331 if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) { 332 *t = '\0'; 333 return -1; 334 } 335 *t++ = ' '; 336 } 337 lc = c; 338 continue; 339 } 340 341 /* check to skip whitespace at start, but also after ( */ 342 if(skipw && i==0 && !com && !quoted && lc != '\\') { 343 if(strchr(skipw, c)) { 344 lc = c; 345 continue; 346 } 347 } 348 349 /* check if we hit the delim */ 350 for (d = del; *d; d++) { 351 /* we can only exit if no parens or user tracks them */ 352 if (c == *d && lc != '\\' && (p == 0 || par)) { 353 goto tokenread; 354 } 355 } 356 357 i++; 358 if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) { 359 *t = '\0'; 360 return -1; 361 } 362 *t++ = c; 363 364 if (c == '\\' && lc == '\\') { 365 lc = 0; 366 } else { 367 lc = c; 368 } 369 } 370 *t = '\0'; 371 if (i == 0) { 372 /* nothing read */ 373 return -1; 374 } 375 if (!par && p != 0) { 376 return -1; 377 } 378 return (ssize_t)i; 379 380 tokenread: 381 if(*del == '"') 382 /* do not skip over quotes after the string, they are part 383 * of the next string. But skip over whitespace (if needed)*/ 384 sldns_bskipcs(b, del+1); 385 else sldns_bskipcs(b, del); 386 *t = '\0'; 387 388 if (!par && p != 0) { 389 return -1; 390 } 391 return (ssize_t)i; 392 } 393 394 395 void 396 sldns_bskipcs(sldns_buffer *buffer, const char *s) 397 { 398 int found; 399 char c; 400 const char *d; 401 402 while(sldns_buffer_available_at(buffer, buffer->_position, sizeof(char))) { 403 c = (char) sldns_buffer_read_u8_at(buffer, buffer->_position); 404 found = 0; 405 for (d = s; *d; d++) { 406 if (*d == c) { 407 found = 1; 408 } 409 } 410 if (found && buffer->_limit > buffer->_position) { 411 buffer->_position += sizeof(char); 412 } else { 413 return; 414 } 415 } 416 } 417 418 void 419 sldns_fskipcs(FILE *fp, const char *s) 420 { 421 sldns_fskipcs_l(fp, s, NULL); 422 } 423 424 void 425 sldns_fskipcs_l(FILE *fp, const char *s, int *line_nr) 426 { 427 int found; 428 int c; 429 const char *d; 430 431 while ((c = fgetc(fp)) != EOF) { 432 if (line_nr && c == '\n') { 433 *line_nr = *line_nr + 1; 434 } 435 found = 0; 436 for (d = s; *d; d++) { 437 if (*d == c) { 438 found = 1; 439 } 440 } 441 if (!found) { 442 /* with getc, we've read too far */ 443 ungetc(c, fp); 444 return; 445 } 446 } 447 } 448 449 ssize_t 450 sldns_bget_keyword_data(sldns_buffer *b, const char *keyword, const char *k_del, char 451 *data, const char *d_del, size_t data_limit) 452 { 453 /* we assume: keyword|sep|data */ 454 char *fkeyword; 455 ssize_t i; 456 457 if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN) 458 return -1; 459 fkeyword = (char*)malloc(LDNS_MAX_KEYWORDLEN); 460 if(!fkeyword) 461 return -1; /* out of memory */ 462 463 i = sldns_bget_token(b, fkeyword, k_del, data_limit); 464 if(i==0 || i==-1) { 465 free(fkeyword); 466 return -1; /* nothing read */ 467 } 468 469 /* case??? */ 470 if (strncmp(fkeyword, keyword, strlen(keyword)) == 0) { 471 free(fkeyword); 472 /* whee, the match! */ 473 /* retrieve it's data */ 474 i = sldns_bget_token(b, data, d_del, 0); 475 return i; 476 } else { 477 free(fkeyword); 478 return -1; 479 } 480 } 481 482