Skip to content

Commit cc7c19f

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 7e513f9 commit cc7c19f

File tree

3 files changed

+158
-0
lines changed

3 files changed

+158
-0
lines changed

lightning/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ lightning-invoice = { version = "0.32.0-rc1", path = "../lightning-invoice", def
4444
bech32 = { version = "0.9.1", default-features = false }
4545
bitcoin = { version = "0.32.2", default-features = false, features = ["secp-recovery"] }
4646

47+
dnssec-prover = { version = "0.6", default-features = false }
48+
4749
hashbrown = { version = "0.13", optional = true, default-features = false }
4850
possiblyrandom = { version = "0.2", path = "../possiblyrandom", optional = true, default-features = false }
4951
regex = { version = "1.5.6", optional = true }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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::{MessageSendInstructions, 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+
) -> Option<(DNSResolverMessage, ResponseInstruction)>;
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+
fn release_pending_messages(&self) -> Vec<(DNSResolverMessage, MessageSendInstructions)> {
46+
vec![]
47+
}
48+
}
49+
50+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
51+
/// An enum containing the possible onion messages which are used uses to request and recieve
52+
/// DNSSEC proofs.
53+
pub enum DNSResolverMessage {
54+
/// A query requesting a DNSSEC proof
55+
DNSSECQuery(DNSSECQuery),
56+
/// A response containing a DNSSEC proof
57+
DNSSECProof(DNSSECProof),
58+
}
59+
60+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
61+
/// A message which is sent to a DNSSEC prover requesting a DNSSEC proof for the given name.
62+
pub struct DNSSECQuery(pub Name);
63+
const DNSSEC_QUERY_TYPE: u64 = 65536;
64+
65+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
66+
/// A message which is sent in response to [`DNSSECQuery`] containing a DNSSEC proof.
67+
pub struct DNSSECProof {
68+
/// The name which the query was for. The proof may not contain a DNS RR for exactly this name
69+
/// if it contains a wildcard RR which contains this name instead.
70+
pub name: Name,
71+
/// The RFC 9102-formatted DNSSEC proof.
72+
pub proof: Vec<u8>,
73+
}
74+
const DNSSEC_PROOF_TYPE: u64 = 65538;
75+
76+
impl DNSResolverMessage {
77+
/// Returns whether `tlv_type` corresponds to a TLV record for DNS Resolvers.
78+
pub fn is_known_type(tlv_type: u64) -> bool {
79+
match tlv_type {
80+
DNSSEC_QUERY_TYPE | DNSSEC_PROOF_TYPE => true,
81+
_ => false,
82+
}
83+
}
84+
}
85+
86+
impl Writeable for DNSResolverMessage {
87+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
88+
match self {
89+
Self::DNSSECQuery(DNSSECQuery(q)) => {
90+
(q.as_str().len() as u8).write(w)?;
91+
w.write_all(&q.as_str().as_bytes())
92+
},
93+
Self::DNSSECProof(DNSSECProof { name, proof }) => {
94+
(name.as_str().len() as u8).write(w)?;
95+
w.write_all(&name.as_str().as_bytes())?;
96+
proof.write(w)
97+
},
98+
}
99+
}
100+
}
101+
102+
fn read_byte_len_ascii_string<R: io::Read>(buffer: &mut R) -> Result<String, DecodeError> {
103+
let len: u8 = Readable::read(buffer)?;
104+
let mut bytes = [0; 255];
105+
buffer.read_exact(&mut bytes[..len as usize])?;
106+
if bytes[..len as usize].iter().any(|b| *b < 0x20 || *b > 0x7e) {
107+
// If the bytes are not entirely in the printable ASCII range, fail
108+
return Err(DecodeError::InvalidValue);
109+
}
110+
let s =
111+
String::from_utf8(bytes[..len as usize].to_vec()).map_err(|_| DecodeError::InvalidValue)?;
112+
Ok(s)
113+
}
114+
115+
impl ReadableArgs<u64> for DNSResolverMessage {
116+
fn read<R: io::Read>(r: &mut R, message_type: u64) -> Result<Self, DecodeError> {
117+
match message_type {
118+
DNSSEC_QUERY_TYPE => {
119+
let s = read_byte_len_ascii_string(r)?;
120+
let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
121+
Ok(DNSResolverMessage::DNSSECQuery(DNSSECQuery(name)))
122+
},
123+
DNSSEC_PROOF_TYPE => {
124+
let s = read_byte_len_ascii_string(r)?;
125+
let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
126+
let proof = Readable::read(r)?;
127+
Ok(DNSResolverMessage::DNSSECProof(DNSSECProof { name, proof }))
128+
},
129+
_ => Err(DecodeError::InvalidValue),
130+
}
131+
}
132+
}
133+
134+
impl OnionMessageContents for DNSResolverMessage {
135+
#[cfg(c_bindings)]
136+
fn msg_type(&self) -> String {
137+
match self {
138+
DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query".to_string(),
139+
DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof".to_string(),
140+
}
141+
}
142+
#[cfg(not(c_bindings))]
143+
fn msg_type(&self) -> &'static str {
144+
match self {
145+
DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query",
146+
DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof",
147+
}
148+
}
149+
fn tlv_type(&self) -> u64 {
150+
match self {
151+
DNSResolverMessage::DNSSECQuery(_) => DNSSEC_QUERY_TYPE,
152+
DNSResolverMessage::DNSSECProof(_) => DNSSEC_PROOF_TYPE,
153+
}
154+
}
155+
}

lightning/src/onion_message/mod.rs

+1
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)