Skip to content

Commit c37b0dc

Browse files
committed
Add infrastructure for handling DNSSECError onion messages
lightning/blips#71 updated the DNSSEC resolution bLIP to include an explicit error message when DNS(SEC) resolution was attempted but failed, allowing for faster fallback to LN-Address (for clients that do) and faster payment failure. Here we simply add the new message framing and empty handlers. Largely writen by an LLM
1 parent 2981487 commit c37b0dc

5 files changed

Lines changed: 81 additions & 4 deletions

File tree

lightning-dns-resolver/src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use dnssec_prover::query::build_txt_proof_async;
1414
use lightning::blinded_path::message::DNSResolverContext;
1515
use lightning::ln::peer_handler::IgnoringMessageHandler;
1616
use lightning::onion_message::dns_resolution::{
17-
DNSResolverMessage, DNSResolverMessageHandler, DNSSECProof, DNSSECQuery,
17+
DNSResolverMessage, DNSResolverMessageHandler, DNSSECError, DNSSECProof, DNSSECQuery,
1818
};
1919
use lightning::onion_message::messenger::{
2020
MessageSendInstructions, Responder, ResponseInstruction,
@@ -103,6 +103,12 @@ impl<PH: DNSResolverMessageHandler> DNSResolverMessageHandler for OMDomainResolv
103103
}
104104
}
105105

106+
fn handle_dnssec_error(&self, error: DNSSECError, context: DNSResolverContext) {
107+
if let Some(proof_handler) = &self.proof_handler {
108+
proof_handler.handle_dnssec_error(error, context);
109+
}
110+
}
111+
106112
fn handle_dnssec_query(
107113
&self, q: DNSSECQuery, responder_opt: Option<Responder>,
108114
) -> Option<(DNSResolverMessage, ResponseInstruction)> {
@@ -229,6 +235,9 @@ mod test {
229235
core::mem::swap(&mut *self.resolved_uri.lock().unwrap(), &mut result);
230236
assert!(result.is_none());
231237
}
238+
fn handle_dnssec_error(&self, msg: DNSSECError, context: DNSResolverContext) {
239+
// TODO
240+
}
232241
fn release_pending_messages(&self) -> Vec<(DNSResolverMessage, MessageSendInstructions)> {
233242
core::mem::take(&mut *self.pending_messages.lock().unwrap())
234243
}

lightning/src/ln/peer_handler.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use crate::onion_message::async_payments::{
3535
ServeStaticInvoice, StaticInvoicePersisted,
3636
};
3737
use crate::onion_message::dns_resolution::{
38-
DNSResolverMessage, DNSResolverMessageHandler, DNSSECProof, DNSSECQuery,
38+
DNSResolverMessage, DNSResolverMessageHandler, DNSSECError, DNSSECProof, DNSSECQuery,
3939
};
4040
use crate::onion_message::messenger::{
4141
CustomOnionMessageHandler, MessageSendInstructions, Responder, ResponseInstruction,
@@ -273,6 +273,7 @@ impl DNSResolverMessageHandler for IgnoringMessageHandler {
273273
None
274274
}
275275
fn handle_dnssec_proof(&self, _message: DNSSECProof, _context: DNSResolverContext) {}
276+
fn handle_dnssec_error(&self, _message: DNSSECError, _context: DNSResolverContext) {}
276277
}
277278
impl CustomOnionMessageHandler for IgnoringMessageHandler {
278279
type CustomMessage = Infallible;

lightning/src/onion_message/dns_resolution.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ pub trait DNSResolverMessageHandler {
7777
/// [`OnionMessenger`]: crate::onion_message::messenger::OnionMessenger
7878
fn handle_dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext);
7979

80+
/// Handle a [`DNSSECError`] message (in response to a [`DNSSECQuery`] we presumably sent),
81+
/// indicating that the resolver was unable to resolve the requested name.
82+
///
83+
/// The provided [`DNSResolverContext`] was authenticated by the [`OnionMessenger`] as coming from
84+
/// a blinded path that we created.
85+
///
86+
/// Receiving this lets us avoid waiting for a [`DNSSECProof`] which will never come, failing the
87+
/// pending operation early instead (at least if the name is
88+
/// [definitely unresolvable](DNSSECError::definitely_unresolvable)).
89+
///
90+
/// [`OnionMessenger`]: crate::onion_message::messenger::OnionMessenger
91+
fn handle_dnssec_error(&self, message: DNSSECError, context: DNSResolverContext);
92+
8093
/// Gets the node feature flags which this handler itself supports. Useful for setting the
8194
/// `dns_resolver` flag if this handler supports returning [`DNSSECProof`] messages in response
8295
/// to [`DNSSECQuery`] messages.
@@ -99,6 +112,9 @@ impl<T: DNSResolverMessageHandler + ?Sized, D: Deref<Target = T>> DNSResolverMes
99112
fn handle_dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext) {
100113
self.deref().handle_dnssec_proof(message, context)
101114
}
115+
fn handle_dnssec_error(&self, message: DNSSECError, context: DNSResolverContext) {
116+
self.deref().handle_dnssec_error(message, context)
117+
}
102118
fn provided_node_features(&self) -> NodeFeatures {
103119
self.deref().provided_node_features()
104120
}
@@ -115,10 +131,14 @@ pub enum DNSResolverMessage {
115131
DNSSECQuery(DNSSECQuery),
116132
/// A response containing a DNSSEC proof
117133
DNSSECProof(DNSSECProof),
134+
/// An error in response to a [`DNSSECQuery`], indicating that the requested name could not be
135+
/// resolved into a [`DNSSECProof`].
136+
DNSSECError(DNSSECError),
118137
}
119138

120139
const DNSSEC_QUERY_TYPE: u64 = 65536;
121140
const DNSSEC_PROOF_TYPE: u64 = 65538;
141+
const DNSSEC_ERROR_TYPE: u64 = 65550;
122142

123143
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
124144
/// A message which is sent to a DNSSEC prover requesting a DNSSEC proof for the given name.
@@ -136,11 +156,30 @@ pub struct DNSSECProof {
136156
pub proof: Vec<u8>,
137157
}
138158

159+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
160+
/// A message which is sent in response to a [`DNSSECQuery`] when the resolver was unable to build a
161+
/// [`DNSSECProof`] for the requested name.
162+
///
163+
/// This lets the recipient stop waiting for a [`DNSSECProof`] which will not be forthcoming.
164+
pub struct DNSSECError {
165+
/// The name which the [`DNSSECQuery`] was for and which we were unable to resolve.
166+
pub name: Name,
167+
/// Whether the name is known to be permanently unresolvable, as opposed to having failed due to
168+
/// some transient error.
169+
///
170+
/// This is set if the requested name does not exist (i.e. the resolver received an NXDOMAIN
171+
/// response) or if the name is not in a DNSSEC-signed zone, in which case retrying or querying a
172+
/// different resolver will not help. It is not set for transient failures (e.g. a timeout
173+
/// communicating with an upstream DNS server), where a retry or a different resolver may yet
174+
/// succeed.
175+
pub definitely_unresolvable: bool,
176+
}
177+
139178
impl DNSResolverMessage {
140179
/// Returns whether `tlv_type` corresponds to a TLV record for DNS Resolvers.
141180
pub fn is_known_type(tlv_type: u64) -> bool {
142181
match tlv_type {
143-
DNSSEC_QUERY_TYPE | DNSSEC_PROOF_TYPE => true,
182+
DNSSEC_QUERY_TYPE | DNSSEC_PROOF_TYPE | DNSSEC_ERROR_TYPE => true,
144183
_ => false,
145184
}
146185
}
@@ -158,6 +197,11 @@ impl Writeable for DNSResolverMessage {
158197
w.write_all(&name.as_str().as_bytes())?;
159198
proof.write(w)
160199
},
200+
Self::DNSSECError(DNSSECError { name, definitely_unresolvable }) => {
201+
(name.as_str().len() as u8).write(w)?;
202+
w.write_all(&name.as_str().as_bytes())?;
203+
definitely_unresolvable.write(w)
204+
},
161205
}
162206
}
163207
}
@@ -176,6 +220,12 @@ impl ReadableArgs<u64> for DNSResolverMessage {
176220
let proof = Readable::read(r)?;
177221
Ok(DNSResolverMessage::DNSSECProof(DNSSECProof { name, proof }))
178222
},
223+
DNSSEC_ERROR_TYPE => {
224+
let s = Hostname::read(r)?;
225+
let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
226+
let definitely_unresolvable = Readable::read(r)?;
227+
Ok(DNSResolverMessage::DNSSECError(DNSSECError { name, definitely_unresolvable }))
228+
},
179229
_ => Err(DecodeError::InvalidValue),
180230
}
181231
}
@@ -187,19 +237,22 @@ impl OnionMessageContents for DNSResolverMessage {
187237
match self {
188238
DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query".to_string(),
189239
DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof".to_string(),
240+
DNSResolverMessage::DNSSECError(_) => "DNSSEC Error".to_string(),
190241
}
191242
}
192243
#[cfg(not(c_bindings))]
193244
fn msg_type(&self) -> &'static str {
194245
match self {
195246
DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query",
196247
DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof",
248+
DNSResolverMessage::DNSSECError(_) => "DNSSEC Error",
197249
}
198250
}
199251
fn tlv_type(&self) -> u64 {
200252
match self {
201253
DNSResolverMessage::DNSSECQuery(_) => DNSSEC_QUERY_TYPE,
202254
DNSResolverMessage::DNSSECProof(_) => DNSSEC_PROOF_TYPE,
255+
DNSResolverMessage::DNSSECError(_) => DNSSEC_ERROR_TYPE,
203256
}
204257
}
205258
}

lightning/src/onion_message/functional_tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use super::async_payments::{
1414
ServeStaticInvoice, StaticInvoicePersisted,
1515
};
1616
use super::dns_resolution::{
17-
DNSResolverMessage, DNSResolverMessageHandler, DNSSECProof, DNSSECQuery,
17+
DNSResolverMessage, DNSResolverMessageHandler, DNSSECError, DNSSECProof, DNSSECQuery,
1818
};
1919
use super::messenger::{
2020
CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageSendInstructions,
@@ -155,6 +155,7 @@ impl DNSResolverMessageHandler for TestDNSResolverMessageHandler {
155155
None
156156
}
157157
fn handle_dnssec_proof(&self, _message: DNSSECProof, _context: DNSResolverContext) {}
158+
fn handle_dnssec_error(&self, _message: DNSSECError, _context: DNSResolverContext) {}
158159
}
159160

160161
#[derive(Clone, Debug, PartialEq)]

lightning/src/onion_message/messenger.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2309,6 +2309,19 @@ impl<
23092309
};
23102310
self.dns_resolver_handler.handle_dnssec_proof(msg, context);
23112311
},
2312+
DNSResolverMessage::DNSSECError(msg) => {
2313+
let context = match context {
2314+
Some(ctx) => ctx,
2315+
None => {
2316+
log_trace!(
2317+
logger,
2318+
"Ignoring DNSSECError onion message due to missing context"
2319+
);
2320+
return;
2321+
},
2322+
};
2323+
self.dns_resolver_handler.handle_dnssec_error(msg, context);
2324+
},
23122325
}
23132326
},
23142327
Ok(PeeledOnion::Custom(message, context, reply_path)) => {

0 commit comments

Comments
 (0)