Coverage Report

Created: 2025-07-11 06:08

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