]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/dnssec.c
Start refactoring for correct handling of domain wire-format.
[people/ms/dnsmasq.git] / src / dnssec.c
CommitLineData
e292e93d
GB
1
2#include "dnsmasq.h"
d322de06 3#include "dnssec-crypto.h"
e292e93d
GB
4#include <assert.h>
5
6299ffbe
GB
6/* Maximum length in octects of a domain name, in wire format */
7#define MAXCDNAME 256
8
e292e93d
GB
9#define SERIAL_UNDEF -100
10#define SERIAL_EQ 0
11#define SERIAL_LT -1
12#define SERIAL_GT 1
13
e292e93d
GB
14/* Implement RFC1982 wrapped compare for 32-bit numbers */
15static int serial_compare_32(unsigned long s1, unsigned long s2)
16{
17 if (s1 == s2)
18 return SERIAL_EQ;
19
20 if ((s1 < s2 && (s2 - s1) < (1UL<<31)) ||
21 (s1 > s2 && (s1 - s2) > (1UL<<31)))
22 return SERIAL_LT;
23 if ((s1 < s2 && (s2 - s1) > (1UL<<31)) ||
24 (s1 > s2 && (s1 - s2) < (1UL<<31)))
25 return SERIAL_GT;
26 return SERIAL_UNDEF;
27}
28
29/* Extract a DNS name from wire format, without handling compression. This is
30 faster than extract_name() and does not require access to the full dns
31 packet. */
32static int extract_name_no_compression(unsigned char *rr, int maxlen, char *buf)
33{
34 unsigned char *start=rr, *end = rr+maxlen;
35 int count;
36
37 while (rr < end && *rr != 0)
38 {
39 count = *rr++;
6445c8ed 40 while (count-- > 0 && rr < end)
e292e93d
GB
41 {
42 *buf = *rr++;
b98f7715
GB
43 if (!isascii(*buf) || iscntrl(*buf) || *buf == '.')
44 return 0;
e292e93d
GB
45 if (*buf >= 'A' && *buf <= 'Z')
46 *buf += 'a' - 'A';
47 buf++;
79333a24 48 }
e292e93d
GB
49 *buf++ = '.';
50 }
dd090561 51 /* Remove trailing dot (if any) */
2ef843dd
GB
52 if (rr != start)
53 *(--buf) = 0;
e292e93d
GB
54 if (rr == end)
55 return 0;
dd090561 56 /* Trailing \0 in source data must be consumed */
79333a24 57 return rr-start+1;
e292e93d
GB
58}
59
6299ffbe
GB
60/* process_domain_name() - do operations with domain names in canonicalized wire format.
61 *
62 * Handling domain names in wire format can be done with buffers as large as MAXCDNAME (256),
63 * while the representation format (as created by, eg., extract_name) requires MAXDNAME (1024).
64 *
65 * With "canonicalized wire format", we mean the standard DNS wire format, eg:
66 *
67 * <3>www<7>example<3>org<0>
68 *
69 * with all ÅSCII letters converted to lowercase, and no wire-level compression.
70 *
71 * The function works with two different buffers:
72 * - Input buffer: 'rdata' is a pointer to the actual wire data, and 'rdlen' is
73 * the total length till the end of the rdata or DNS packet section. Both
74 * variables are updated after processing the domain name, so that rdata points
75 * after it, and rdlen is decreased by the amount of the processed octects.
76 * - Output buffer: 'out' points to it. In some cases, this buffer can be prefilled
77 * and used as additional input (see below).
78 *
79 * The argument "action" decides what to do with the submitted domain name:
80 *
81 * PDN_EXTRACT:
82 * Extract the domain name from input buffer into the output buffer, possibly uncompressing it.
83 * Return the length of the domain name in the output buffer in octects, or zero if error.
84 *
85 * PDN_COMPARE:
86 * Compare the domain name in the input buffer and the one in the output buffer (ignoring
87 * differences in compression). Returns 0 in case of error, a positive number
88 * if they are equal, or a negative number if they are different. This function always
89 * consumes the whole name in the input buffer (there is no early exit).
90 *
91 * PDN_ORDER:
92 * Order between the domain name in the input buffer and the domain name in the output buffer.
93 * Returns 0 if the names are equal, 1 if input > output, or -1 if input < output. This
94 * function early-exits when it finds a difference, so rdata might not be fully updated.
95 *
96 * Notice: because of compression, rdata/rdlen might be updated with a different quantity than
97 * the returned number of octects. For instance, if we extract a compressed domain name, rdata/rdlen
98 * might be updated only by 2 bytes (that is, rdata is incresed by 2, and rdlen decreased by 2),
99 * because it then reuses existing data elsewhere in the DNS packet, while the return value might be
100 * larger, reflecting the total number of octects composing the domain name.
101 *
102 */
103#define PDN_EXTRACT 0
104#define PDN_COMPARE 1
105#define PDN_ORDER 2
106static int process_domain_name(struct dns_header *header, size_t pktlen,
107 unsigned char** rdata, size_t* rdlen,
108 unsigned char *out, int action)
109{
110 int hops = 0, total = 0, i;
111 unsigned char label_type;
112 unsigned char *end = (unsigned char *)header + pktlen;
113 unsigned char count; unsigned char *p = *rdata;
114 int nonequal = 0;
115
116#define PROCESS(ch) \
117 do { \
118 if (action == PWN_EXTRACT) \
119 *out++ = ch; \
120 else if (action == PWN_COMPARE) \
121 { \
122 if (*out++ != ch) \
123 nonequal = 1; \
124 } \
125 else if (action == PWN_ORDER) \
126 { \
127 char _ch = *out++; \
128 if (ch < _ch) \
129 return -1; \
130 else if (_ch > ch) \
131 return 1; \
132 } \
133 } while (0)
134
135 while (1)
136 {
137 if (p >= end)
138 return 0;
139 if (!(count = *p++))
140 break;
141 label_type = count & 0xC0;
142 if (label_type == 0xC0)
143 {
144 if (p >= end)
145 return 0;
146 if (hops == 0)
147 {
148 if (p - *rdata > *rdlen)
149 return 0;
150 *rdlen -= p - *rdata;
151 *rdata = p;
152 }
153 if (++hops == 256)
154 return 0;
155 p = (unsigned char*)header + (count & 0x3F) * 256 + *p;
156 }
157 else if (label_type == 0x00)
158 {
159 if (p+count-1 >= end)
160 return 0;
161 total += count+1;
162 if (total >= MAXCDNAME)
163 return 0;
164 PROCESS(count);
165 for (i = 0; i < count; ++i)
166 {
167 unsigned char ch = *p++;
168 if (ch >= 'A' && ch <= 'Z')
169 ch += 'a' - 'A';
170 PROCESS(ch);
171 }
172 }
173 else
174 return 0; /* unsupported label_type */
175 }
176
177 if (hops == 0)
178 {
179 if (p - *rdata > *rdlen)
180 return 0;
181 *rdlen -= p - *rdata;
182 *rdata = p;
183 }
184 ++total;
185 if (total >= MAXCDNAME)
186 return 0;
187 PROCESS(0);
188
189 /* If we arrived here without early-exit, they're equal */
190 if (action == PWN_ORDER)
191 return 0;
192 return nonequal ? -total : total;
193
194 #undef PROCESS
195}
196
197
198/* RDATA meta-description.
199 *
200 * RFC4034 §6.2 introduces the concept of a "canonical form of a RR". This canonical
201 * form is used in two important points within the DNSSEC protocol/algorithm:
202 *
203 * 1) When computing the hash for verifying the RRSIG signature, we need to do it on
204 * the canonical form.
205 * 2) When ordering a RRset in canonical order (§6.3), we need to lexicographically sort
206 * the RRs in canonical form.
207 *
208 * The canonical form of a RR is specifically tricky because it also affects the RDATA,
209 * which is different for each RR type; in fact, RFC4034 says that "domain names in
210 * RDATA must be canonicalized" (= uncompressed and lower-cased).
211 *
212 * To handle this correctly, we then need a way to describe how the RDATA section is
213 * composed for each RR type; we don't need to describe every field, but just to specify
214 * where domain names are. The following array contains this description, and it is
215 * used by rrset_canonical_order() and verifyalg_add_rdata(), to adjust their behaviour
216 * for each RR type.
217 *
218 * The format of the description is very easy, for instance:
219 *
220 * { 12, RDESC_DOMAIN, RDESC_DOMAIN, 4, RDESC_DOMAIN, RDESC_END }
221 *
222 * This means that this (ficticious) RR type has a RDATA section containing 12 octects
223 * (we don't care what they contain), followed by 2 domain names, followed by 4 octects,
224 * followed by 1 domain name, and then followed by an unspecificied number of octects (0
225 * or more).
226 */
227
228#define RDESC_DOMAIN -1
229#define RDESC_END 0
230static const int rdata_description[][8] =
231{
232 /**/ { RDESC_END },
233 /* 1: A */ { RDESC_END },
234 /* 2: NS */ { RDESC_DOMAIN, RDESC_END },
235 /* 3: .. */ { RDESC_END },
236 /* 4: .. */ { RDESC_END },
237 /* 5: CNAME */ { RDESC_DOMAIN, RDESC_END },
238};
239
240
e292e93d
GB
241/* Check whether today/now is between date_start and date_end */
242static int check_date_range(unsigned long date_start, unsigned long date_end)
243{
244 /* TODO: double-check that time(0) is the correct time we are looking for */
245 /* TODO: dnssec requires correct timing; implement SNTP in dnsmasq? */
246 unsigned long curtime = time(0);
247
248 /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
41de7442
GB
249 return serial_compare_32(curtime, date_start) == SERIAL_GT
250 && serial_compare_32(curtime, date_end) == SERIAL_LT;
e292e93d
GB
251}
252
253/* Sort RRs within a RRset in canonical order, according to RFC4034, §6.3
254 Notice that the RRDATA sections have been already normalized, so a memcpy
255 is sufficient.
256 NOTE: r1/r2 point immediately after the owner name. */
257static int rrset_canonical_order(const void *r1, const void *r2)
258{
259 int r1len, r2len, res;
0decc869 260 const unsigned char *pr1=*(unsigned char**)r1, *pr2=*(unsigned char**)r2;
e292e93d
GB
261
262 pr1 += 8; pr2 += 8;
263 GETSHORT(r1len, pr1); GETSHORT(r2len, pr2);
264
265 /* Lexicographically compare RDATA (thus, if equal, smaller length wins) */
266 res = memcmp(pr1, pr2, MIN(r1len, r2len));
267 if (res == 0)
268 {
269 if (r1len < r2len)
270 return -1;
271 else
272 /* NOTE: RFC2181 says that an RRset is not allowed to contain duplicate
273 records. If it happens, it is a protocol error and anything goes. */
274 return 1;
275 }
276
277 return res;
278}
279
adca3e9c
GB
280typedef struct PendingRRSIGValidation
281{
282 VerifyAlgCtx *alg;
283 char *signer_name;
284 int keytag;
285} PendingRRSIGValidation;
286
7f0485cf
GB
287/* strchrnul - like strchr, but when character is not found, returns a pointer to the terminating \0.
288
289 This is an existing C GNU extension, but it's easier to reimplement it,
290 rather than tweaking with configure. */
291static char *strchrnul(char *str, char ch)
292{
293 while (*str && *str != ch)
294 str++;
295 return str;
296}
297
13e435eb
GB
298/* Pass a domain name through a verification hash function.
299
300 We must pass domain names in DNS wire format, but uncompressed.
301 This means that we cannot directly use raw data from the original
302 message since it might be compressed. */
303static void verifyalg_add_data_domain(VerifyAlgCtx *alg, char* name)
304{
305 unsigned char len; char *p;
306
7f0485cf 307 do
13e435eb 308 {
7f0485cf
GB
309 p = strchrnul(name, '.');
310 if ((len = p-name))
311 {
312 alg->vtbl->add_data(alg, &len, 1);
313 alg->vtbl->add_data(alg, name, len);
314 }
13e435eb
GB
315 name = p+1;
316 }
7f0485cf
GB
317 while (*p);
318
319 alg->vtbl->add_data(alg, "\0", 1);
13e435eb
GB
320}
321
0852d76b
GB
322/* Pass a DNS domain name in wire format through a hash function. Returns the
323 total number of bytes passed through the function or 0 in case of errors.
324 Updates the rdata pointer moving it further within the RR.
325
326 If alg is NULL, go in dry run mode (still correctly updates rdata and return
327 the correct total).
328
329 The function canonicalizes the domain name (RFC 4034, §6.2), which basically
330 means conversion to lower case, and uncompression. */
331static int verifyalg_add_data_wire_domain(VerifyAlgCtx *alg, struct dns_header *header, size_t pktlen,
332 unsigned char** rdata)
333{
334 int hops = 0, total = 0;
335 unsigned char label_type;
336 unsigned char *end = (unsigned char *)header + pktlen;
337 unsigned char count; unsigned char *p = *rdata;
338
339 while (1)
340 {
341 if (p >= end)
342 return 0;
343 if (!(count = *p++))
344 break;
345 label_type = count & 0xC0;
346 if (label_type == 0xC0)
347 {
348 if (p >= end)
349 return 0;
350 p = (unsigned char*)header + (count & 0x3F) * 256 + *p;
351 if (hops == 0)
352 *rdata = p;
353 if (++hops == 256)
354 return 0;
355 }
356 else if (label_type == 0x00)
357 {
358 if (p+count-1 >= end)
359 return 0;
360 if (alg)
361 {
362 alg->vtbl->add_data(alg, &count, 1);
363 /* TODO: missing conversion to lower-case and alphabet check */
364 alg->vtbl->add_data(alg, p, count);
365 }
366 total += count+1;
367 p += count;
368 }
369 else
370 return 0; /* unsupported label_type */
371 }
372
373 if (hops == 0)
374 *rdata = p;
375 if (alg)
376 alg->vtbl->add_data(alg, &count, 1);
377 return total+1;
378}
379
380/* Pass a resource record's rdata field through a verification hash function.
381
382 We must pass the record in DNS wire format, but if the record contains domain names,
383 they must be uncompressed. This makes things very tricky, because */
384static int verifyalg_add_rdata(VerifyAlgCtx *alg, int sigtype, struct dns_header *header, size_t pktlen,
385 unsigned char *rdata)
386{
387 unsigned char *p;
388 int res; unsigned short rdlen;
389
390 GETSHORT(rdlen, rdata);
391 p = rdata;
392
393 switch (sigtype)
394 {
395 /* TODO: missing lots of RR types, see RFC4034, §6.2 */
d1ca25ca 396 case T_NS:
0852d76b
GB
397 case T_CNAME:
398 if (!(res = verifyalg_add_data_wire_domain(NULL, header, pktlen, &p)))
399 return 0;
400 if (p - rdata > rdlen)
401 return 0;
402 rdlen = htons(res);
403 alg->vtbl->add_data(alg, &rdlen, 2);
404 verifyalg_add_data_wire_domain(alg, header, pktlen, &rdata);
405 break;
406
407 default:
408 alg->vtbl->add_data(alg, rdata-2, rdlen+2);
409 break;
410 }
411 return 1;
412}
413
414
adca3e9c
GB
415static int begin_rrsig_validation(struct dns_header *header, size_t pktlen,
416 unsigned char *reply, int count, char *owner,
417 int sigclass, int sigrdlen, unsigned char *sig,
418 PendingRRSIGValidation *out)
e292e93d
GB
419{
420 int i, res;
421 int sigtype, sigalg, siglbl;
422 unsigned char *sigrdata = sig;
423 unsigned long sigttl, date_end, date_start;
424 unsigned char* p = reply;
425 char* signer_name = daemon->namebuff;
13e435eb 426 int signer_name_rdlen;
e292e93d
GB
427 int keytag;
428 void *rrset[16]; /* TODO: max RRset size? */
429 int rrsetidx = 0;
430
431 if (sigrdlen < 18)
432 return 0;
433 GETSHORT(sigtype, sig);
434 sigalg = *sig++;
435 siglbl = *sig++;
436 GETLONG(sigttl, sig);
437 GETLONG(date_end, sig);
438 GETLONG(date_start, sig);
439 GETSHORT(keytag, sig);
440 sigrdlen -= 18;
441
adca3e9c 442 if (!verifyalg_supported(sigalg))
e292e93d
GB
443 {
444 printf("RRSIG algorithm not supported: %d\n", sigalg);
445 return 0;
446 }
447
d31d057a 448 if (!check_date_range(date_start, date_end))
e292e93d
GB
449 {
450 printf("RRSIG outside date range\n");
451 return 0;
452 }
453
454 /* Iterate within the answer and find the RRsets matching the current RRsig */
455 for (i = 0; i < count; ++i)
456 {
457 int qtype, qclass, rdlen;
458 if (!(res = extract_name(header, pktlen, &p, owner, 0, 10)))
459 return 0;
460 rrset[rrsetidx] = p;
461 GETSHORT(qtype, p);
462 GETSHORT(qclass, p);
463 p += 4; /* skip ttl */
464 GETSHORT(rdlen, p);
465 if (res == 1 && qtype == sigtype && qclass == sigclass)
466 {
467 ++rrsetidx;
382e38f4
GB
468 if (rrsetidx == countof(rrset))
469 {
470 /* Internal buffer too small */
471 printf("internal buffer too small for this RRset\n");
472 return 0;
473 }
e292e93d
GB
474 }
475 p += rdlen;
476 }
477
478 /* Sort RRset records in canonical order. */
479 qsort(rrset, rrsetidx, sizeof(void*), rrset_canonical_order);
480
50a96b62
GB
481 /* Skip through the signer name; we don't extract it right now because
482 we don't want to overwrite the single daemon->namebuff which contains
483 the owner name. We'll get to this later. */
484 if (!(p = skip_name(sig, header, pktlen, 0)))
e292e93d 485 return 0;
50a96b62
GB
486 signer_name_rdlen = p - sig;
487 sig = p; sigrdlen -= signer_name_rdlen;
13e435eb 488
e292e93d
GB
489 /* Now initialize the signature verification algorithm and process the whole
490 RRset */
adca3e9c
GB
491 VerifyAlgCtx *alg = verifyalg_alloc(sigalg);
492 if (!alg)
493 return 0;
494 if (!alg->vtbl->set_signature(alg, sig, sigrdlen))
e292e93d
GB
495 return 0;
496
4b0eecbb
GB
497 sigtype = htons(sigtype);
498 sigclass = htons(sigclass);
499 sigttl = htonl(sigttl);
500
adca3e9c 501 alg->vtbl->begin_data(alg);
13e435eb 502 alg->vtbl->add_data(alg, sigrdata, 18+signer_name_rdlen);
e292e93d
GB
503 for (i = 0; i < rrsetidx; ++i)
504 {
13e435eb
GB
505 p = (unsigned char*)(rrset[i]);
506
507 verifyalg_add_data_domain(alg, owner);
adca3e9c
GB
508 alg->vtbl->add_data(alg, &sigtype, 2);
509 alg->vtbl->add_data(alg, &sigclass, 2);
510 alg->vtbl->add_data(alg, &sigttl, 4);
e292e93d 511
e292e93d 512 p += 8;
0852d76b
GB
513 if (!verifyalg_add_rdata(alg, ntohs(sigtype), header, pktlen, p))
514 return 0;
e292e93d 515 }
adca3e9c 516 alg->vtbl->end_data(alg);
e292e93d 517
50a96b62
GB
518 /* We don't need the owner name anymore; now extract the signer name */
519 if (!extract_name_no_compression(sigrdata+18, signer_name_rdlen, signer_name))
520 return 0;
521
adca3e9c
GB
522 out->alg = alg;
523 out->keytag = keytag;
524 out->signer_name = signer_name;
525 return 1;
526}
527
528static int end_rrsig_validation(PendingRRSIGValidation *val, struct crec *crec_dnskey)
529{
a7338645 530 /* FIXME: keydata is non-contiguous */
708bcd2d 531 return val->alg->vtbl->verify(val->alg, crec_dnskey->addr.key.keydata, crec_dnskey->uid);
adca3e9c
GB
532}
533
534static void dnssec_parserrsig(struct dns_header *header, size_t pktlen,
535 unsigned char *reply, int count, char *owner,
536 int sigclass, int sigrdlen, unsigned char *sig)
537{
538 PendingRRSIGValidation val;
539
540 /* Initiate the RRSIG validation process. The pending state is returned into val. */
541 if (!begin_rrsig_validation(header, pktlen, reply, count, owner, sigclass, sigrdlen, sig, &val))
542 return;
543
544 printf("RRSIG: querying cache for DNSKEY %s (keytag: %d)\n", val.signer_name, val.keytag);
20bccd49
GB
545
546 /* Look in the cache for *all* the DNSKEYs with matching signer_name and keytag */
adca3e9c
GB
547 char onekey = 0;
548 struct crec *crecp = NULL;
a55ce08c 549 while ((crecp = cache_find_by_name(crecp, val.signer_name, time(0), F_DNSKEY))) /* TODO: time(0) */
adca3e9c
GB
550 {
551 onekey = 1;
552
20bccd49
GB
553 if (crecp->addr.key.keytag == val.keytag
554 && crecp->addr.key.algo == verifyalg_algonum(val.alg))
555 {
556 printf("RRSIG: found DNSKEY %d in cache, attempting validation\n", val.keytag);
adca3e9c 557
20bccd49
GB
558 if (end_rrsig_validation(&val, crecp))
559 printf("Validation OK\n");
560 else
561 printf("Validation FAILED\n");
562 }
adca3e9c
GB
563 }
564
565 if (!onekey)
566 {
ccca70cb 567 printf("DNSKEY not found, need to fetch it\n");
adca3e9c
GB
568 /* TODO: store PendingRRSIGValidation in routing table,
569 fetch key (and make it go through dnssec_parskey), then complete validation. */
570 }
e292e93d
GB
571}
572
3471f181
GB
573/* Compute keytag (checksum to quickly index a key). See RFC4034 */
574static int dnskey_keytag(unsigned char *rdata, int rdlen)
575{
576 unsigned long ac;
577 int i;
578
579 ac = 0;
580 for (i = 0; i < rdlen; ++i)
581 ac += (i & 1) ? rdata[i] : rdata[i] << 8;
582 ac += (ac >> 16) & 0xFFFF;
583 return ac & 0xFFFF;
584}
585
586int dnssec_parsekey(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
587 int rdlen, unsigned char *rdata)
588{
589 int flags, proto, alg;
590 struct keydata *key; struct crec *crecp;
3471f181
GB
591 unsigned char *ordata = rdata; int ordlen = rdlen;
592
593 CHECKED_GETSHORT(flags, rdata, rdlen);
594 CHECKED_GETCHAR(proto, rdata, rdlen);
595 CHECKED_GETCHAR(alg, rdata, rdlen);
596
597 if (proto != 3)
598 return 0;
0d829ebc
GB
599 /* Skip non-signing keys (as specified in RFC4034 */
600 if (!(flags & 0x100))
601 return 0;
3471f181 602
a55ce08c 603 key = keydata_alloc((char*)rdata, rdlen);
3471f181 604
3471f181
GB
605 /* TODO: time(0) is correct here? */
606 crecp = cache_insert(owner, NULL, time(0), ttl, F_FORWARD | F_DNSKEY);
607 if (crecp)
608 {
609 /* TODO: improve union not to name "uid" this field */
610 crecp->uid = rdlen;
611 crecp->addr.key.keydata = key;
612 crecp->addr.key.algo = alg;
613 crecp->addr.key.keytag = dnskey_keytag(ordata, ordlen);
614 printf("DNSKEY: storing key for %s (keytag: %d)\n", owner, crecp->addr.key.keytag);
615 }
616 else
617 {
618 keydata_free(key);
619 /* TODO: if insertion really might fail, verify we don't depend on cache
620 insertion success for validation workflow correctness */
621 printf("DNSKEY: cache insertion failure\n");
622 return 0;
623 }
3471f181
GB
624 return 1;
625}
626
627int dnssec_parseds(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
628 int rdlen, unsigned char *rdata)
629{
630 return 0;
631}
e292e93d
GB
632
633int dnssec_validate(struct dns_header *header, size_t pktlen)
634{
635 unsigned char *p, *reply;
636 char *owner = daemon->namebuff;
23c21766 637 int i, s, qtype, qclass, rdlen;
e292e93d 638 unsigned long ttl;
23c21766 639 int slen[3] = { ntohs(header->ancount), ntohs(header->nscount), ntohs(header->arcount) };
e292e93d 640
23c21766 641 if (slen[0] + slen[1] + slen[2] == 0)
e292e93d
GB
642 return 0;
643 if (!(reply = p = skip_questions(header, pktlen)))
644 return 0;
d0edff7d
GB
645
646 /* First, process DNSKEY/DS records and add them to the cache. */
647 cache_start_insert();
23c21766 648 for (i = 0; i < slen[0]; i++)
e292e93d
GB
649 {
650 if (!extract_name(header, pktlen, &p, owner, 1, 10))
651 return 0;
652 GETSHORT(qtype, p);
653 GETSHORT(qclass, p);
654 GETLONG(ttl, p);
655 GETSHORT(rdlen, p);
3471f181
GB
656 if (qtype == T_DS)
657 {
658 printf("DS found\n");
659 dnssec_parseds(header, pktlen, owner, ttl, rdlen, p);
660 }
661 else if (qtype == T_DNSKEY)
662 {
663 printf("DNSKEY found\n");
47f99dd2 664 dnssec_parsekey(header, pktlen, owner, ttl, rdlen, p);
3471f181 665 }
4137b84e
GB
666 p += rdlen;
667 }
d0edff7d 668 cache_end_insert();
4137b84e 669
d0edff7d 670 /* After we have cached DNSKEY/DS records, start looking for RRSIGs.
4137b84e
GB
671 We want to do this in a separate step because we want the cache
672 to be already populated with DNSKEYs before parsing signatures. */
673 p = reply;
23c21766 674 for (s = 0; s < 3; ++s)
4137b84e 675 {
23c21766
GB
676 reply = p;
677 for (i = 0; i < slen[s]; i++)
e292e93d 678 {
23c21766
GB
679 if (!extract_name(header, pktlen, &p, owner, 1, 10))
680 return 0;
681 GETSHORT(qtype, p);
682 GETSHORT(qclass, p);
683 GETLONG(ttl, p);
684 GETSHORT(rdlen, p);
685 if (qtype == T_RRSIG)
686 {
687 printf("RRSIG found (owner: %s)\n", owner);
688 /* TODO: missing logic. We should only validate RRSIGs for which we
689 have a valid DNSKEY that is referenced by a DS record upstream.
690 There is a memory vs CPU conflict here; should we validate everything
691 to save memory and thus waste CPU, or better first acquire all information
692 (wasting memory) and then doing the minimum CPU computations required? */
693 dnssec_parserrsig(header, pktlen, reply, slen[s], owner, qclass, rdlen, p);
694 }
695 p += rdlen;
4137b84e 696 }
e292e93d 697 }
4137b84e 698
e292e93d
GB
699 return 1;
700}