+ min_ttl = cmp::min(min_ttl, ttl);
+ if let RR::RRSig(rrsig) = rr { rrsig_key_names.push(rrsig.key_name); }
+ }
+ Ok(min_ttl)
+}
+
+#[cfg(fuzzing)]
+/// Read a stream of responses and handle them it as if they came from a server, for fuzzing.
+pub fn fuzz_proof_builder(mut response_stream: &[u8]) {
+ let (mut builder, _) = ProofBuilder::new(&"example.com.".try_into().unwrap(), Txt::TYPE);
+ while builder.awaiting_responses() {
+ let len = if let Ok(len) = read_u16(&mut response_stream) { len } else { return };
+ let mut buf = QueryBuf::new_zeroed(len);
+ if response_stream.len() < len as usize { return; }
+ buf.copy_from_slice(&response_stream[..len as usize]);
+ response_stream = &response_stream[len as usize..];
+ let _ = builder.process_response(&buf);
+ }
+ let _ = builder.finish_proof();
+}
+
+const MAX_REQUESTS: usize = 10;
+/// A simple state machine which will generate a series of queries and process the responses until
+/// it has built a DNSSEC proof.
+///
+/// A [`ProofBuilder`] driver starts with [`ProofBuilder::new`], fetching the state machine and
+/// initial query. As long as [`ProofBuilder::awaiting_responses`] returns true, responses should
+/// be read from the resolver. For each query response read from the DNS resolver,
+/// [`ProofBuilder::process_response`] should be called, and each fresh query returned should be
+/// sent to the resolver. Once [`ProofBuilder::awaiting_responses`] returns false,
+/// [`ProofBuilder::finish_proof`] should be called to fetch the resulting proof.
+///
+/// To build a DNSSEC proof using a DoH server, take each [`QueryBuf`], encode it as base64url, and
+/// make a query to `https://doh-server/endpoint?dns=base64url_encoded_query` with an `Accept`
+/// header of `application/dns-message`. Each response, in raw binary, can be fed directly into
+/// [`ProofBuilder::process_response`].
+pub struct ProofBuilder {
+ proof: Vec<u8>,
+ min_ttl: u32,
+ dnskeys_requested: Vec<Name>,
+ pending_queries: usize,
+ queries_made: usize,
+}
+
+impl ProofBuilder {
+ /// Constructs a new [`ProofBuilder`] and an initial query to send to the recursive resolver to
+ /// begin the proof building process.
+ ///
+ /// Given a correctly-functioning resolver the proof will ultimately be able to prove the
+ /// contents of any records with the given `ty`pe at the given `name` (as long as the given
+ /// `ty`pe is supported by this library).
+ ///
+ /// You can find constants for supported standard types in the [`crate::rr`] module.
+ pub fn new(name: &Name, ty: u16) -> (ProofBuilder, QueryBuf) {
+ let initial_query = build_query(name, ty);
+ (ProofBuilder {
+ proof: Vec::new(),
+ min_ttl: u32::MAX,
+ dnskeys_requested: Vec::with_capacity(MAX_REQUESTS),
+ pending_queries: 1,
+ queries_made: 1,
+ }, initial_query)