Skip to content

Commit e2cd095

Browse files
committed
Add DNS(SEC) query and proof messages and onion message handler
This creates the initial DNSSEC proof and query messages in a new module in `onion_message`, as well as a new message handler to handle them. In the coming commits, a default implementation will be added which verifies DNSSEC proofs which can be used to resolve BIP 353 URIs without relying on anything outside of the lightning network.
1 parent 4bd4f19 commit e2cd095

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

lightning/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ default = ["std", "grind_signatures"]
4343
bech32 = { version = "0.9.1", default-features = false }
4444
bitcoin = { version = "0.31.2", default-features = false, features = ["secp-recovery"] }
4545

46+
dnssec-prover = { version = "0.6", default-features = false }
47+
4648
hashbrown = { version = "0.13", optional = true, default-features = false }
4749
possiblyrandom = { version = "0.2", optional = true, default-features = false }
4850
hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! This module defines message handling for DNSSEC proof fetching using [bLIP 32].
11+
//!
12+
//! It contains [`DNSResolverMessage`]s as well as a [`DNSResolverMessageHandler`] trait to handle
13+
//! such messages using an [`OnionMessenger`].
14+
//!
15+
//! [bLIP 32]: https://github.com/lightning/blips/blob/master/blip-0032.md
16+
//! [`OnionMessenger`]: super::messenger::OnionMessenger
17+
18+
use dnssec_prover::rr::Name;
19+
20+
use crate::io;
21+
use crate::ln::msgs::DecodeError;
22+
use crate::onion_message::messenger::{PendingOnionMessage, Responder, ResponseInstruction};
23+
use crate::onion_message::packet::OnionMessageContents;
24+
use crate::prelude::*;
25+
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
26+
27+
/// A handler for an [`OnionMessage`] containing a DNS(SEC) query or a DNSSEC proof
28+
///
29+
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
30+
pub trait DNSResolverMessageHandler {
31+
/// Handle a [`DNSSECQuery`] message.
32+
///
33+
/// If we provide DNS resolution services to third parties, we should respond with a
34+
/// [`DNSSECProof`] message.
35+
fn dnssec_query(
36+
&self, message: DNSSECQuery, responder: Option<Responder>,
37+
) -> ResponseInstruction<DNSResolverMessage>;
38+
39+
/// Handle a [`DNSSECProof`] message (in response to a [`DNSSECQuery`] we presumably sent).
40+
///
41+
/// With this, we should be able to validate the DNS record we requested.
42+
fn dnssec_proof(&self, message: DNSSECProof);
43+
44+
/// Release any [`DNSResolverMessage`]s that need to be sent.
45+
#[cfg(not(c_bindings))]
46+
fn release_pending_messages(&self) -> Vec<PendingOnionMessage<DNSResolverMessage>> {
47+
vec![]
48+
}
49+
50+
/// Release any [`DNSResolverMessage`]s that need to be sent.
51+
#[cfg(c_bindings)]
52+
fn release_pending_messages(
53+
&self,
54+
) -> Vec<(
55+
DNSResolverMessage,
56+
crate::onion_message::messenger::Destination,
57+
Option<crate::blinded_path::BlindedPath>,
58+
)> {
59+
vec![]
60+
}
61+
}
62+
63+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
64+
/// An enum containing the possible onion messages which are used uses to request and recieve
65+
/// DNSSEC proofs.
66+
pub enum DNSResolverMessage {
67+
/// A query requesting a DNSSEC proof
68+
DNSSECQuery(DNSSECQuery),
69+
/// A response containing a DNSSEC proof
70+
DNSSECProof(DNSSECProof),
71+
}
72+
73+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
74+
/// A message which is sent to a DNSSEC prover requesting a DNSSEC proof for the given name.
75+
pub struct DNSSECQuery(pub Name);
76+
const DNSSEC_QUERY_TYPE: u64 = 65536;
77+
78+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
79+
/// A message which is sent in response to [`DNSSECQuery`] containing a DNSSEC proof.
80+
pub struct DNSSECProof {
81+
/// The name which the query was for. The proof may not contain a DNS RR for exactly this name
82+
/// if it contains a wildcard RR which contains this name instead.
83+
pub name: Name,
84+
/// The RFC 9102-formatted DNSSEC proof.
85+
pub proof: Vec<u8>,
86+
}
87+
const DNSSEC_PROOF_TYPE: u64 = 65538;
88+
89+
impl DNSResolverMessage {
90+
/// Returns whether `tlv_type` corresponds to a TLV record for DNS Resolvers.
91+
pub fn is_known_type(tlv_type: u64) -> bool {
92+
match tlv_type {
93+
DNSSEC_QUERY_TYPE | DNSSEC_PROOF_TYPE => true,
94+
_ => false,
95+
}
96+
}
97+
}
98+
99+
impl Writeable for DNSResolverMessage {
100+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
101+
match self {
102+
Self::DNSSECQuery(DNSSECQuery(q)) => {
103+
(q.as_str().len() as u8).write(w)?;
104+
w.write_all(&q.as_str().as_bytes())
105+
},
106+
Self::DNSSECProof(DNSSECProof { name, proof }) => {
107+
(name.as_str().len() as u8).write(w)?;
108+
w.write_all(&name.as_str().as_bytes())?;
109+
proof.write(w)
110+
},
111+
}
112+
}
113+
}
114+
115+
fn read_byte_len_ascii_string<R: io::Read>(buffer: &mut R) -> Result<String, DecodeError> {
116+
let len: u8 = Readable::read(buffer)?;
117+
let mut bytes = [0; 255];
118+
buffer.read_exact(&mut bytes[..len as usize])?;
119+
if bytes[..len as usize].iter().any(|b| *b < 0x20 || *b > 0x7e) {
120+
// If the bytes are not entirely in the printable ASCII range, fail
121+
return Err(DecodeError::InvalidValue);
122+
}
123+
let s =
124+
String::from_utf8(bytes[..len as usize].to_vec()).map_err(|_| DecodeError::InvalidValue)?;
125+
Ok(s)
126+
}
127+
128+
impl ReadableArgs<u64> for DNSResolverMessage {
129+
fn read<R: io::Read>(r: &mut R, message_type: u64) -> Result<Self, DecodeError> {
130+
match message_type {
131+
DNSSEC_QUERY_TYPE => {
132+
let s = read_byte_len_ascii_string(r)?;
133+
let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
134+
Ok(DNSResolverMessage::DNSSECQuery(DNSSECQuery(name)))
135+
},
136+
DNSSEC_PROOF_TYPE => {
137+
let s = read_byte_len_ascii_string(r)?;
138+
let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
139+
let proof = Readable::read(r)?;
140+
Ok(DNSResolverMessage::DNSSECProof(DNSSECProof { name, proof }))
141+
},
142+
_ => Err(DecodeError::InvalidValue),
143+
}
144+
}
145+
}
146+
147+
impl OnionMessageContents for DNSResolverMessage {
148+
fn msg_type(&self) -> &'static str {
149+
match self {
150+
DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query",
151+
DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof",
152+
}
153+
}
154+
fn tlv_type(&self) -> u64 {
155+
match self {
156+
DNSResolverMessage::DNSSECQuery(_) => DNSSEC_QUERY_TYPE,
157+
DNSResolverMessage::DNSSECProof(_) => DNSSEC_PROOF_TYPE,
158+
}
159+
}
160+
}

lightning/src/onion_message/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
//! [`OnionMessenger`]: self::messenger::OnionMessenger
2323
2424
pub mod async_payments;
25+
pub mod dns_resolution;
2526
pub mod messenger;
2627
pub mod offers;
2728
pub mod packet;

0 commit comments

Comments
 (0)