1 /* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "dnssec-crypto.h"
21 /* Maximum length in octects of a domain name, in wire format */
24 #define SERIAL_UNDEF -100
29 /* Implement RFC1982 wrapped compare for 32-bit numbers */
30 static int serial_compare_32(unsigned long s1
, unsigned long s2
)
35 if ((s1
< s2
&& (s2
- s1
) < (1UL<<31)) ||
36 (s1
> s2
&& (s1
- s2
) > (1UL<<31)))
38 if ((s1
< s2
&& (s2
- s1
) > (1UL<<31)) ||
39 (s1
> s2
&& (s1
- s2
) < (1UL<<31)))
44 /* Extract a DNS name from wire format, without handling compression. This is
45 faster than extract_name() and does not require access to the full dns
47 static int extract_name_no_compression(unsigned char *rr
, int maxlen
, char *buf
)
49 unsigned char *start
=rr
, *end
= rr
+maxlen
;
52 while (rr
< end
&& *rr
!= 0)
55 while (count
-- > 0 && rr
< end
)
58 if (!isascii(*buf
) || iscntrl(*buf
) || *buf
== '.')
60 if (*buf
>= 'A' && *buf
<= 'Z')
66 /* Remove trailing dot (if any) */
71 /* Trailing \0 in source data must be consumed */
75 /* process_domain_name() - do operations with domain names in canonicalized wire format.
77 * Handling domain names in wire format can be done with buffers as large as MAXCDNAME (256),
78 * while the representation format (as created by, eg., extract_name) requires MAXDNAME (1024).
80 * With "canonicalized wire format", we mean the standard DNS wire format, eg:
82 * <3>www<7>example<3>org<0>
84 * with all ÅSCII letters converted to lowercase, and no wire-level compression.
86 * The function works with two different buffers:
87 * - Input buffer: 'rdata' is a pointer to the actual wire data, and 'rdlen' is
88 * the total length till the end of the rdata or DNS packet section. Both
89 * variables are updated after processing the domain name, so that rdata points
90 * after it, and rdlen is decreased by the amount of the processed octects.
91 * - Output buffer: 'out' points to it. In some cases, this buffer can be prefilled
92 * and used as additional input (see below).
94 * The argument "action" decides what to do with the submitted domain name:
97 * Extract the domain name from input buffer into the output buffer, possibly uncompressing it.
98 * Return the length of the domain name in the output buffer in octects, or zero if error.
101 * Compare the domain name in the input buffer and the one in the output buffer (ignoring
102 * differences in compression). Returns 0 in case of error, a positive number
103 * if they are equal, or a negative number if they are different. This function always
104 * consumes the whole name in the input buffer (there is no early exit).
107 * Order between the domain name in the input buffer and the domain name in the output buffer.
108 * Returns 0 if the names are equal, 1 if input > output, or -1 if input < output. This
109 * function early-exits when it finds a difference, so rdata might not be fully updated.
111 * Notice: because of compression, rdata/rdlen might be updated with a different quantity than
112 * the returned number of octects. For instance, if we extract a compressed domain name, rdata/rdlen
113 * might be updated only by 2 bytes (that is, rdata is incresed by 2, and rdlen decreased by 2),
114 * because it then reuses existing data elsewhere in the DNS packet, while the return value might be
115 * larger, reflecting the total number of octects composing the domain name.
118 #define PWN_EXTRACT 0
119 #define PWN_COMPARE 1
121 static int process_domain_name(struct dns_header
*header
, size_t pktlen
,
122 unsigned char** rdata
, size_t* rdlen
,
123 unsigned char *out
, int action
)
125 int hops
= 0, total
= 0, i
;
126 unsigned char label_type
;
127 unsigned char *end
= (unsigned char *)header
+ pktlen
;
128 unsigned char count
; unsigned char *p
= *rdata
;
131 #define PROCESS(ch) \
133 if (action == PWN_EXTRACT) \
135 else if (action == PWN_COMPARE) \
140 else if (action == PWN_ORDER) \
156 label_type
= count
& 0xC0;
157 if (label_type
== 0xC0)
165 if (p
- *rdata
> *rdlen
)
167 *rdlen
-= p
- *rdata
;
172 p
= (unsigned char*)header
+ (count
& 0x3F) * 256 + l2
;
174 else if (label_type
== 0x00)
176 if (p
+count
-1 >= end
)
179 if (total
>= MAXCDNAME
)
182 for (i
= 0; i
< count
; ++i
)
184 unsigned char ch
= *p
++;
185 if (ch
>= 'A' && ch
<= 'Z')
191 return 0; /* unsupported label_type */
196 if (p
- *rdata
> *rdlen
)
198 *rdlen
-= p
- *rdata
;
202 if (total
>= MAXCDNAME
)
206 /* If we arrived here without early-exit, they're equal */
207 if (action
== PWN_ORDER
)
209 return nonequal
? -total
: total
;
215 /* RDATA meta-description.
217 * RFC4034 §6.2 introduces the concept of a "canonical form of a RR". This canonical
218 * form is used in two important points within the DNSSEC protocol/algorithm:
220 * 1) When computing the hash for verifying the RRSIG signature, we need to do it on
221 * the canonical form.
222 * 2) When ordering a RRset in canonical order (§6.3), we need to lexicographically sort
223 * the RRs in canonical form.
225 * The canonical form of a RR is specifically tricky because it also affects the RDATA,
226 * which is different for each RR type; in fact, RFC4034 says that "domain names in
227 * RDATA must be canonicalized" (= uncompressed and lower-cased).
229 * To handle this correctly, we then need a way to describe how the RDATA section is
230 * composed for each RR type; we don't need to describe every field, but just to specify
231 * where domain names are. The following array contains this description, and it is
232 * used by rrset_canonical_order() and verifyalg_add_rdata(), to adjust their behaviour
235 * The format of the description is very easy, for instance:
237 * { 12, RDESC_DOMAIN, RDESC_DOMAIN, 4, RDESC_DOMAIN, RDESC_END }
239 * This means that this (ficticious) RR type has a RDATA section containing 12 octects
240 * (we don't care what they contain), followed by 2 domain names, followed by 4 octects,
241 * followed by 1 domain name, and then followed by an unspecificied number of octects (0
245 #define RDESC_DOMAIN -1
247 static const int rdata_description
[][8] =
250 /* 1: A */ { RDESC_END
},
251 /* 2: NS */ { RDESC_DOMAIN
, RDESC_END
},
252 /* 3: .. */ { RDESC_END
},
253 /* 4: .. */ { RDESC_END
},
254 /* 5: CNAME */ { RDESC_DOMAIN
, RDESC_END
},
255 /* 6: SOA */ { RDESC_DOMAIN
, RDESC_DOMAIN
, RDESC_END
},
256 /* 7: */ { RDESC_END
},
257 /* 8: */ { RDESC_END
},
258 /* 9: */ { RDESC_END
},
259 /* 10: */ { RDESC_END
},
260 /* 11: */ { RDESC_END
},
261 /* 12: */ { RDESC_END
},
262 /* 13: */ { RDESC_END
},
263 /* 14: */ { RDESC_END
},
264 /* 15: MX */ { 2, RDESC_DOMAIN
, RDESC_END
},
268 /* On-the-fly rdata canonicalization
270 * This set of functions allow the user to iterate over the rdata section of a RR
271 * while canonicalizing it on-the-fly. This is a great memory saving since the user
272 * doesn't need to allocate memory for a copy of the whole rdata section.
279 * header, pktlen, // dns_header
280 * rdata, // pointer to rdata section
282 * tmpbuf); // temporary buf (MAXCDNAME)
284 * while ((p = rdata_cfrom_next(&cf, &len))
286 * // Process p[0..len]
289 * if (rdata_cfrom_error(&cf))
290 * // error occurred while parsing
295 struct dns_header
*header
;
297 unsigned char *rdata
;
298 unsigned char *tmpbuf
;
304 static void rdata_cform_init(RDataCForm
*ctx
, struct dns_header
*header
, size_t pktlen
,
305 unsigned char *rdata
, int rrtype
, unsigned char *tmpbuf
)
307 if (rrtype
>= countof(rdata_description
))
309 ctx
->header
= header
;
310 ctx
->pktlen
= pktlen
;
312 ctx
->rrtype
= rrtype
;
313 ctx
->tmpbuf
= tmpbuf
;
315 GETSHORT(ctx
->rdlen
, ctx
->rdata
);
318 static int rdata_cform_error(RDataCForm
*ctx
)
320 return ctx
->cnt
== -2;
323 static unsigned char *rdata_cform_next(RDataCForm
*ctx
, size_t *len
)
325 if (ctx
->cnt
!= -1 && rdata_description
[ctx
->rrtype
][ctx
->cnt
] == RDESC_END
)
328 int d
= rdata_description
[ctx
->rrtype
][++ctx
->cnt
];
329 if (d
== RDESC_DOMAIN
)
331 *len
= process_domain_name(ctx
->header
, ctx
->pktlen
, &ctx
->rdata
, &ctx
->rdlen
, ctx
->tmpbuf
, PWN_EXTRACT
);
339 else if (d
== RDESC_END
)
346 unsigned char *ret
= ctx
->rdata
;
355 /* Check whether today/now is between date_start and date_end */
356 static int check_date_range(unsigned long date_start
, unsigned long date_end
)
358 /* TODO: double-check that time(0) is the correct time we are looking for */
359 /* TODO: dnssec requires correct timing; implement SNTP in dnsmasq? */
360 unsigned long curtime
= time(0);
362 /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
363 return serial_compare_32(curtime
, date_start
) == SERIAL_GT
364 && serial_compare_32(curtime
, date_end
) == SERIAL_LT
;
368 /* Sort RRs within a RRset in canonical order, according to RFC4034, §6.3
369 Notice that the RRDATA sections have been already normalized, so a memcpy
371 NOTE: r1/r2 point immediately after the owner name. */
374 struct dns_header
*header
;
376 } rrset_canonical_order_ctx
;
378 static int rrset_canonical_order(const void *r1
, const void *r2
)
382 unsigned char *pr1
=*(unsigned char**)r1
, *pr2
=*(unsigned char**)r2
;
383 unsigned char tmp1
[MAXCDNAME
], tmp2
[MAXCDNAME
]; /* TODO: use part of daemon->namebuff */
385 GETSHORT(rrtype
, pr1
);
389 rdata_cform_init(&cf1
, rrset_canonical_order_ctx
.header
, rrset_canonical_order_ctx
.pktlen
,
391 rdata_cform_init(&cf2
, rrset_canonical_order_ctx
.header
, rrset_canonical_order_ctx
.pktlen
,
393 while ((pr1
= rdata_cform_next(&cf1
, &r1len
)) &&
394 (pr2
= rdata_cform_next(&cf2
, &r2len
)))
396 int res
= memcmp(pr1
, pr2
, MIN(r1len
,r2len
));
405 /* If we reached this point, the two RRs are identical (or an error occurred).
406 RFC2181 says that an RRset is not allowed to contain duplicate
407 records. If it happens, it is a protocol error and anything goes. */
411 typedef struct PendingRRSIGValidation
416 } PendingRRSIGValidation
;
418 /* strchrnul - like strchr, but when character is not found, returns a pointer to the terminating \0.
420 This is an existing C GNU extension, but it's easier to reimplement it,
421 rather than tweaking with configure. */
422 static char *my_strchrnul(char *str
, char ch
)
424 while (*str
&& *str
!= ch
)
429 /* Convert a domain name to wire format */
430 static int convert_domain_to_wire(char *name
, unsigned char* out
)
433 unsigned char *start
= out
;
438 p
= my_strchrnul(name
, '.');
445 /* TODO: this will not be required anymore once we
446 remove all usages of extract_name() from DNSSEC code */
447 if (ch
>= 'A' && ch
<= 'Z')
461 /* Pass a resource record's rdata field through the currently-initailized digest algorithm.
463 We must pass the record in DNS wire format, but if the record contains domain names,
464 they must be uncompressed. This makes things very tricky, because */
465 static int digestalg_add_rdata(int sigtype
, struct dns_header
*header
, size_t pktlen
,
466 unsigned char *rdata
)
470 unsigned short total
;
471 unsigned char tmpbuf
[MAXDNAME
]; /* TODO: reuse part of daemon->namebuff */
474 /* Initialize two iterations over the canonical form*/
475 rdata_cform_init(&cf1
, header
, pktlen
, rdata
, sigtype
, tmpbuf
);
478 /* Iteration 1: go through the canonical record and count the total octects.
479 This number might be different from the non-canonical rdata length
480 because of domain names compression. */
482 while ((p
= rdata_cform_next(&cf1
, &len
)))
484 if (rdata_cform_error(&cf1
))
487 /* Iteration 2: process the canonical record through the hash function */
488 total
= htons(total
);
489 digestalg_add_data(&total
, 2);
491 while ((p
= rdata_cform_next(&cf2
, &len
)))
492 digestalg_add_data(p
, len
);
498 static int begin_rrsig_validation(struct dns_header
*header
, size_t pktlen
,
499 unsigned char *reply
, int count
, char *owner
,
500 int sigclass
, int sigrdlen
, unsigned char *sig
,
501 PendingRRSIGValidation
*out
)
504 int sigtype
, sigalg
, siglbl
;
505 unsigned char *sigrdata
= sig
;
506 unsigned long sigttl
, date_end
, date_start
;
507 unsigned char* p
= reply
;
508 char* signer_name
= daemon
->namebuff
;
509 int signer_name_rdlen
;
511 void *rrset
[16]; /* TODO: max RRset size? */
516 GETSHORT(sigtype
, sig
);
519 GETLONG(sigttl
, sig
);
520 GETLONG(date_end
, sig
);
521 GETLONG(date_start
, sig
);
522 GETSHORT(keytag
, sig
);
525 if (!verifyalg_supported(sigalg
))
527 printf("ERROR: RRSIG algorithm not supported: %d\n", sigalg
);
531 if (!check_date_range(date_start
, date_end
))
533 printf("ERROR: RRSIG outside date range\n");
537 /* Iterate within the answer and find the RRsets matching the current RRsig */
538 for (i
= 0; i
< count
; ++i
)
540 int qtype
, qclass
, rdlen
;
541 if (!(res
= extract_name(header
, pktlen
, &p
, owner
, 0, 10)))
546 p
+= 4; /* skip ttl */
548 if (res
== 1 && qtype
== sigtype
&& qclass
== sigclass
)
551 if (rrsetidx
== countof(rrset
))
553 /* Internal buffer too small */
554 printf("internal buffer too small for this RRset\n");
561 /* Sort RRset records in canonical order. */
562 rrset_canonical_order_ctx
.header
= header
;
563 rrset_canonical_order_ctx
.pktlen
= pktlen
;
564 qsort(rrset
, rrsetidx
, sizeof(void*), rrset_canonical_order
);
566 /* Skip through the signer name; we don't extract it right now because
567 we don't want to overwrite the single daemon->namebuff which contains
568 the owner name. We'll get to this later. */
569 if (!(p
= skip_name(sig
, header
, pktlen
, 0)))
571 signer_name_rdlen
= p
- sig
;
572 sig
= p
; sigrdlen
-= signer_name_rdlen
;
574 /* Now initialize the signature verification algorithm and process the whole
576 VerifyAlgCtx
*alg
= verifyalg_alloc(sigalg
);
580 alg
->siglen
= sigrdlen
;
582 sigtype
= htons(sigtype
);
583 sigclass
= htons(sigclass
);
584 sigttl
= htonl(sigttl
);
586 /* TODO: we shouldn't need to convert this to wire here. Best solution would be:
587 - Use process_name() instead of extract_name() everywhere in dnssec code
588 - Convert from wire format to representation format only for querying/storing cache
590 unsigned char owner_wire
[MAXCDNAME
];
591 int owner_wire_len
= convert_domain_to_wire(owner
, owner_wire
);
593 digestalg_begin(alg
->vtbl
->digest_algo
);
594 digestalg_add_data(sigrdata
, 18+signer_name_rdlen
);
595 for (i
= 0; i
< rrsetidx
; ++i
)
597 p
= (unsigned char*)(rrset
[i
]);
599 digestalg_add_data(owner_wire
, owner_wire_len
);
600 digestalg_add_data(&sigtype
, 2);
601 digestalg_add_data(&sigclass
, 2);
602 digestalg_add_data(&sigttl
, 4);
605 if (!digestalg_add_rdata(ntohs(sigtype
), header
, pktlen
, p
))
608 int digest_len
= digestalg_len();
609 memcpy(alg
->digest
, digestalg_final(), digest_len
);
611 /* We don't need the owner name anymore; now extract the signer name */
612 if (!extract_name_no_compression(sigrdata
+18, signer_name_rdlen
, signer_name
))
616 out
->keytag
= keytag
;
617 out
->signer_name
= signer_name
;
621 static int end_rrsig_validation(PendingRRSIGValidation
*val
, struct crec
*crec_dnskey
)
623 /* FIXME: keydata is non-contiguous */
624 return val
->alg
->vtbl
->verify(val
->alg
, crec_dnskey
->addr
.key
.keydata
, crec_dnskey
->uid
);
627 static void dnssec_parserrsig(struct dns_header
*header
, size_t pktlen
,
628 unsigned char *reply
, int count
, char *owner
,
629 int sigclass
, int sigrdlen
, unsigned char *sig
)
631 PendingRRSIGValidation val
;
633 /* Initiate the RRSIG validation process. The pending state is returned into val. */
634 if (!begin_rrsig_validation(header
, pktlen
, reply
, count
, owner
, sigclass
, sigrdlen
, sig
, &val
))
637 printf("RRSIG: querying cache for DNSKEY %s (keytag: %d)\n", val
.signer_name
, val
.keytag
);
639 /* Look in the cache for *all* the DNSKEYs with matching signer_name and keytag */
641 struct crec
*crecp
= NULL
;
642 while ((crecp
= cache_find_by_name(crecp
, val
.signer_name
, time(0), F_DNSKEY
))) /* TODO: time(0) */
646 if (crecp
->addr
.key
.keytag
== val
.keytag
647 && crecp
->addr
.key
.algo
== verifyalg_algonum(val
.alg
))
649 printf("RRSIG: found DNSKEY %d in cache, attempting validation\n", val
.keytag
);
651 if (end_rrsig_validation(&val
, crecp
))
652 printf("Validation OK\n");
654 printf("ERROR: Validation FAILED (%s, keytag:%d, algo:%d)\n", owner
, val
.keytag
, verifyalg_algonum(val
.alg
));
660 printf("DNSKEY not found, need to fetch it\n");
661 /* TODO: store PendingRRSIGValidation in routing table,
662 fetch key (and make it go through dnssec_parskey), then complete validation. */
666 /* Compute keytag (checksum to quickly index a key). See RFC4034 */
667 static int dnskey_keytag(int alg
, unsigned char *rdata
, int rdlen
)
671 /* Algorithm 1 (RSAMD5) has a different (older) keytag calculation algorithm.
672 See RFC4034, Appendix B.1 */
673 return rdata
[rdlen
-3] * 256 + rdata
[rdlen
-2];
681 for (i
= 0; i
< rdlen
; ++i
)
682 ac
+= (i
& 1) ? rdata
[i
] : rdata
[i
] << 8;
683 ac
+= (ac
>> 16) & 0xFFFF;
688 /* Check if the DS record (from cache) points to the DNSKEY record (from cache) */
689 static int dnskey_ds_match(struct crec
*dnskey
, struct crec
*ds
)
691 if (dnskey
->addr
.key
.keytag
!= ds
->addr
.key
.keytag
)
693 if (dnskey
->addr
.key
.algo
!= ds
->addr
.key
.algo
)
696 unsigned char owner
[MAXCDNAME
]; /* TODO: user part of daemon->namebuff */
697 int owner_len
= convert_domain_to_wire(cache_get_name(ds
), owner
);
698 size_t keylen
= dnskey
->uid
;
702 if (!digestalg_begin(dig
))
704 digsize
= digestalg_len();
705 digestalg_add_data(owner
, owner_len
);
706 digestalg_add_data("\x01\x01\x03", 3);
707 digestalg_add_data(&ds
->addr
.key
.algo
, 1);
708 digestalg_add_keydata(dnskey
->addr
.key
.keydata
, keylen
);
709 return (memcmp(digestalg_final(), ds
->addr
.key
.keydata
->key
, digsize
) == 0);
712 int dnssec_parsekey(struct dns_header
*header
, size_t pktlen
, char *owner
, unsigned long ttl
,
713 int rdlen
, unsigned char *rdata
)
715 int flags
, proto
, alg
;
716 struct keydata
*key
; struct crec
*crecp
;
717 unsigned char *ordata
= rdata
; int ordlen
= rdlen
;
719 CHECKED_GETSHORT(flags
, rdata
, rdlen
);
720 CHECKED_GETCHAR(proto
, rdata
, rdlen
);
721 CHECKED_GETCHAR(alg
, rdata
, rdlen
);
725 /* Skip non-signing keys (as specified in RFC4034 */
726 if (!(flags
& 0x100))
729 key
= keydata_alloc((char*)rdata
, rdlen
);
731 /* TODO: time(0) is correct here? */
732 crecp
= cache_insert(owner
, NULL
, time(0), ttl
, F_FORWARD
| F_DNSKEY
);
735 /* TODO: improve union not to name "uid" this field */
737 crecp
->addr
.key
.keydata
= key
;
738 crecp
->addr
.key
.algo
= alg
;
739 crecp
->addr
.key
.keytag
= dnskey_keytag(alg
, ordata
, ordlen
);
740 printf("DNSKEY: storing key for %s (keytag: %d)\n", owner
, crecp
->addr
.key
.keytag
);
745 /* TODO: if insertion really might fail, verify we don't depend on cache
746 insertion success for validation workflow correctness */
747 printf("DNSKEY: cache insertion failure\n");
753 int dnssec_parseds(struct dns_header
*header
, size_t pktlen
, char *owner
, unsigned long ttl
,
754 int rdlen
, unsigned char *rdata
)
756 int keytag
, algo
, dig
;
757 struct keydata
*key
; struct crec
*crec_ds
, *crec_key
;
759 CHECKED_GETSHORT(keytag
, rdata
, rdlen
);
760 CHECKED_GETCHAR(algo
, rdata
, rdlen
);
761 CHECKED_GETCHAR(dig
, rdata
, rdlen
);
763 if (!digestalg_supported(dig
))
766 key
= keydata_alloc((char*)rdata
, rdlen
);
768 /* TODO: time(0) is correct here? */
769 crec_ds
= cache_insert(owner
, NULL
, time(0), ttl
, F_FORWARD
| F_DS
);
773 /* TODO: if insertion really might fail, verify we don't depend on cache
774 insertion success for validation workflow correctness */
775 printf("DS: cache insertion failure\n");
779 /* TODO: improve union not to name "uid" this field */
781 crec_ds
->addr
.key
.keydata
= key
;
782 crec_ds
->addr
.key
.algo
= algo
;
783 crec_ds
->addr
.key
.keytag
= keytag
;
784 printf("DS: storing key for %s (digest: %d)\n", owner
, dig
);
786 /* Now try to find a DNSKEY which matches this DS digest. */
787 printf("Looking for a DNSKEY matching DS %d...\n", keytag
);
789 while ((crec_key
= cache_find_by_name(crec_key
, owner
, time(0), F_DNSKEY
))) /* TODO: time(0) */
791 if (dnskey_ds_match(crec_key
, crec_ds
))
793 /* TODO: create a link within the cache: ds => dnskey */
794 printf("MATCH FOUND for keytag %d\n", keytag
);
799 printf("ERROR: match not found for DS %d (owner: %s)\n", keytag
, owner
);
803 int dnssec_validate(struct dns_header
*header
, size_t pktlen
)
805 unsigned char *p
, *reply
;
806 char *owner
= daemon
->namebuff
;
807 int i
, s
, qtype
, qclass
, rdlen
;
809 int slen
[3] = { ntohs(header
->ancount
), ntohs(header
->nscount
), ntohs(header
->arcount
) };
811 if (slen
[0] + slen
[1] + slen
[2] == 0)
813 if (!(reply
= p
= skip_questions(header
, pktlen
)))
816 /* First, process DNSKEY/DS records and add them to the cache. */
817 cache_start_insert();
818 for (i
= 0; i
< slen
[0]; i
++)
820 if (!extract_name(header
, pktlen
, &p
, owner
, 1, 10))
828 printf("DS found\n");
829 dnssec_parseds(header
, pktlen
, owner
, ttl
, rdlen
, p
);
831 else if (qtype
== T_DNSKEY
)
833 printf("DNSKEY found\n");
834 dnssec_parsekey(header
, pktlen
, owner
, ttl
, rdlen
, p
);
840 /* After we have cached DNSKEY/DS records, start looking for RRSIGs.
841 We want to do this in a separate step because we want the cache
842 to be already populated with DNSKEYs before parsing signatures. */
844 for (s
= 0; s
< 3; ++s
)
847 for (i
= 0; i
< slen
[s
]; i
++)
849 if (!extract_name(header
, pktlen
, &p
, owner
, 1, 10))
855 if (qtype
== T_RRSIG
)
857 printf("RRSIG found (owner: %s)\n", owner
);
858 /* TODO: missing logic. We should only validate RRSIGs for which we
859 have a valid DNSKEY that is referenced by a DS record upstream.
860 There is a memory vs CPU conflict here; should we validate everything
861 to save memory and thus waste CPU, or better first acquire all information
862 (wasting memory) and then doing the minimum CPU computations required? */
863 dnssec_parserrsig(header
, pktlen
, reply
, slen
[s
], owner
, qclass
, rdlen
, p
);