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