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