1#[cfg(feature = "dnssec")]
24use core::str::FromStr;
25#[cfg(feature = "dnssec")]
26use core::sync::atomic::{AtomicUsize, Ordering};
27
28#[cfg(feature = "dnssec")]
29use dnssec_prover::rr::RR;
30#[cfg(feature = "dnssec")]
31use dnssec_prover::ser::parse_rr_stream;
32#[cfg(feature = "dnssec")]
33use dnssec_prover::validation::verify_rr_stream;
34
35use dnssec_prover::rr::Name;
36
37use lightning_types::features::NodeFeatures;
38
39use crate::blinded_path::message::DNSResolverContext;
40use crate::io;
41#[cfg(feature = "dnssec")]
42use crate::ln::channelmanager::PaymentId;
43use crate::ln::msgs::DecodeError;
44#[cfg(feature = "dnssec")]
45use crate::offers::offer::Offer;
46use crate::onion_message::messenger::{MessageSendInstructions, Responder, ResponseInstruction};
47use crate::onion_message::packet::OnionMessageContents;
48use crate::prelude::*;
49#[cfg(feature = "dnssec")]
50use crate::sign::EntropySource;
51#[cfg(feature = "dnssec")]
52use crate::sync::Mutex;
53use crate::util::ser::{Hostname, Readable, ReadableArgs, Writeable, Writer};
54
55pub trait DNSResolverMessageHandler {
59 fn handle_dnssec_query(
64 &self, message: DNSSECQuery, responder: Option<Responder>,
65 ) -> Option<(DNSResolverMessage, ResponseInstruction)>;
66
67 fn handle_dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext);
71
72 fn provided_node_features(&self) -> NodeFeatures {
76 NodeFeatures::empty()
77 }
78
79 fn release_pending_messages(&self) -> Vec<(DNSResolverMessage, MessageSendInstructions)> {
81 vec![]
82 }
83}
84
85#[derive(Clone, Debug, Hash, PartialEq, Eq)]
86pub enum DNSResolverMessage {
89 DNSSECQuery(DNSSECQuery),
91 DNSSECProof(DNSSECProof),
93}
94
95const DNSSEC_QUERY_TYPE: u64 = 65536;
96const DNSSEC_PROOF_TYPE: u64 = 65538;
97
98#[derive(Clone, Debug, Hash, PartialEq, Eq)]
99pub struct DNSSECQuery(pub Name);
101
102#[derive(Clone, Debug, Hash, PartialEq, Eq)]
103pub struct DNSSECProof {
105 pub name: Name,
108 pub proof: Vec<u8>,
112}
113
114impl DNSResolverMessage {
115 pub fn is_known_type(tlv_type: u64) -> bool {
117 match tlv_type {
118 DNSSEC_QUERY_TYPE | DNSSEC_PROOF_TYPE => true,
119 _ => false,
120 }
121 }
122}
123
124impl Writeable for DNSResolverMessage {
125 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
126 match self {
127 Self::DNSSECQuery(DNSSECQuery(q)) => {
128 (q.as_str().len() as u8).write(w)?;
129 w.write_all(&q.as_str().as_bytes())
130 },
131 Self::DNSSECProof(DNSSECProof { name, proof }) => {
132 (name.as_str().len() as u8).write(w)?;
133 w.write_all(&name.as_str().as_bytes())?;
134 proof.write(w)
135 },
136 }
137 }
138}
139
140impl ReadableArgs<u64> for DNSResolverMessage {
141 fn read<R: io::Read>(r: &mut R, message_type: u64) -> Result<Self, DecodeError> {
142 match message_type {
143 DNSSEC_QUERY_TYPE => {
144 let s = Hostname::read(r)?;
145 let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
146 Ok(DNSResolverMessage::DNSSECQuery(DNSSECQuery(name)))
147 },
148 DNSSEC_PROOF_TYPE => {
149 let s = Hostname::read(r)?;
150 let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
151 let proof = Readable::read(r)?;
152 Ok(DNSResolverMessage::DNSSECProof(DNSSECProof { name, proof }))
153 },
154 _ => Err(DecodeError::InvalidValue),
155 }
156 }
157}
158
159impl OnionMessageContents for DNSResolverMessage {
160 #[cfg(c_bindings)]
161 fn msg_type(&self) -> String {
162 match self {
163 DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query".to_string(),
164 DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof".to_string(),
165 }
166 }
167 #[cfg(not(c_bindings))]
168 fn msg_type(&self) -> &'static str {
169 match self {
170 DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query",
171 DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof",
172 }
173 }
174 fn tlv_type(&self) -> u64 {
175 match self {
176 DNSResolverMessage::DNSSECQuery(_) => DNSSEC_QUERY_TYPE,
177 DNSResolverMessage::DNSSECProof(_) => DNSSEC_PROOF_TYPE,
178 }
179 }
180}
181
182#[derive(Clone, Debug, Hash, PartialEq, Eq)]
192pub struct HumanReadableName {
193 user: String,
195 domain: String,
196}
197
198impl HumanReadableName {
199 pub fn new(user: String, mut domain: String) -> Result<HumanReadableName, ()> {
202 if domain.ends_with(".") {
204 domain.pop();
205 }
206 const REQUIRED_EXTRA_LEN: usize = ".user._bitcoin-payment.".len() + 1;
208 if user.len() + domain.len() + REQUIRED_EXTRA_LEN > 255 {
209 return Err(());
210 }
211 if user.is_empty() || domain.is_empty() {
212 return Err(());
213 }
214 if !Hostname::str_is_valid_hostname(&user) || !Hostname::str_is_valid_hostname(&domain) {
215 return Err(());
216 }
217 Ok(HumanReadableName { user, domain })
218 }
219
220 pub fn from_encoded(encoded: &str) -> Result<HumanReadableName, ()> {
225 if let Some((user, domain)) = encoded.strip_prefix('₿').unwrap_or(encoded).split_once("@")
226 {
227 Self::new(user.to_string(), domain.to_string())
228 } else {
229 Err(())
230 }
231 }
232
233 pub fn user(&self) -> &str {
235 &self.user
236 }
237
238 pub fn domain(&self) -> &str {
240 &self.domain
241 }
242}
243
244impl Writeable for HumanReadableName {
246 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
247 (self.user.len() as u8).write(writer)?;
248 writer.write_all(&self.user.as_bytes())?;
249 (self.domain.len() as u8).write(writer)?;
250 writer.write_all(&self.domain.as_bytes())
251 }
252}
253
254impl Readable for HumanReadableName {
255 fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
256 let mut read_bytes = [0; 255];
257
258 let user_len: u8 = Readable::read(reader)?;
259 reader.read_exact(&mut read_bytes[..user_len as usize])?;
260 let user_bytes: Vec<u8> = read_bytes[..user_len as usize].into();
261 let user = match String::from_utf8(user_bytes) {
262 Ok(user) => user,
263 Err(_) => return Err(DecodeError::InvalidValue),
264 };
265
266 let domain_len: u8 = Readable::read(reader)?;
267 reader.read_exact(&mut read_bytes[..domain_len as usize])?;
268 let domain_bytes: Vec<u8> = read_bytes[..domain_len as usize].into();
269 let domain = match String::from_utf8(domain_bytes) {
270 Ok(domain) => domain,
271 Err(_) => return Err(DecodeError::InvalidValue),
272 };
273
274 HumanReadableName::new(user, domain).map_err(|()| DecodeError::InvalidValue)
275 }
276}
277
278#[cfg(feature = "dnssec")]
279struct PendingResolution {
280 start_height: u32,
281 context: DNSResolverContext,
282 name: HumanReadableName,
283 payment_id: PaymentId,
284}
285
286#[cfg(feature = "dnssec")]
295pub struct OMNameResolver {
296 pending_resolves: Mutex<HashMap<Name, Vec<PendingResolution>>>,
297 latest_block_time: AtomicUsize,
298 latest_block_height: AtomicUsize,
299}
300
301#[cfg(feature = "dnssec")]
302impl OMNameResolver {
303 pub fn new(latest_block_time: u32, latest_block_height: u32) -> Self {
305 Self {
306 pending_resolves: Mutex::new(new_hash_map()),
307 latest_block_time: AtomicUsize::new(latest_block_time as usize),
308 latest_block_height: AtomicUsize::new(latest_block_height as usize),
309 }
310 }
311
312 pub fn new_best_block(&self, height: u32, time: u32) {
317 self.latest_block_time.store(time as usize, Ordering::Release);
318 self.latest_block_height.store(height as usize, Ordering::Release);
319 let mut resolves = self.pending_resolves.lock().unwrap();
320 resolves.retain(|_, queries| {
321 queries.retain(|query| query.start_height >= height - 1);
322 !queries.is_empty()
323 });
324 }
325
326 pub fn resolve_name<ES: EntropySource + ?Sized>(
331 &self, payment_id: PaymentId, name: HumanReadableName, entropy_source: &ES,
332 ) -> Result<(DNSSECQuery, DNSResolverContext), ()> {
333 let dns_name =
334 Name::try_from(format!("{}.user._bitcoin-payment.{}.", name.user, name.domain));
335 debug_assert!(
336 dns_name.is_ok(),
337 "The HumanReadableName constructor shouldn't allow names which are too long"
338 );
339 let mut context = DNSResolverContext { nonce: [0; 16] };
340 context.nonce.copy_from_slice(&entropy_source.get_secure_random_bytes()[..16]);
341 if let Ok(dns_name) = dns_name {
342 let start_height = self.latest_block_height.load(Ordering::Acquire) as u32;
343 let mut pending_resolves = self.pending_resolves.lock().unwrap();
344 let context_ret = context.clone();
345 let resolution = PendingResolution { start_height, context, name, payment_id };
346 pending_resolves.entry(dns_name.clone()).or_insert_with(Vec::new).push(resolution);
347 Ok((DNSSECQuery(dns_name), context_ret))
348 } else {
349 Err(())
350 }
351 }
352
353 pub fn handle_dnssec_proof_for_offer(
365 &self, msg: DNSSECProof, context: DNSResolverContext,
366 ) -> Option<(Vec<(HumanReadableName, PaymentId)>, Offer)> {
367 let (completed_requests, uri) = self.handle_dnssec_proof_for_uri(msg, context)?;
368 if let Some((_onchain, params)) = uri.split_once("?") {
369 for param in params.split("&") {
370 let (k, v) = if let Some(split) = param.split_once("=") {
371 split
372 } else {
373 continue;
374 };
375 if k.eq_ignore_ascii_case("lno") {
376 if let Ok(offer) = Offer::from_str(v) {
377 return Some((completed_requests, offer));
378 }
379 return None;
380 }
381 }
382 }
383 None
384 }
385
386 pub fn handle_dnssec_proof_for_uri(
398 &self, msg: DNSSECProof, context: DNSResolverContext,
399 ) -> Option<(Vec<(HumanReadableName, PaymentId)>, String)> {
400 let DNSSECProof { name: answer_name, proof } = msg;
401 let mut pending_resolves = self.pending_resolves.lock().unwrap();
402 if let hash_map::Entry::Occupied(entry) = pending_resolves.entry(answer_name) {
403 if !entry.get().iter().any(|query| query.context == context) {
404 return None;
413 }
414 let parsed_rrs = parse_rr_stream(&proof);
415 let validated_rrs =
416 parsed_rrs.as_ref().and_then(|rrs| verify_rr_stream(rrs).map_err(|_| &()));
417 if let Ok(validated_rrs) = validated_rrs {
418 let block_time = self.latest_block_time.load(Ordering::Acquire) as u64;
419 if validated_rrs.valid_from > block_time + 60 * 2 {
424 return None;
425 }
426 if validated_rrs.expires < block_time - 60 * 2 {
427 return None;
428 }
429 let resolved_rrs = validated_rrs.resolve_name(&entry.key());
430 if resolved_rrs.is_empty() {
431 return None;
432 }
433
434 let (_, requests) = entry.remove_entry();
435
436 const URI_PREFIX: &str = "bitcoin:";
437 let mut candidate_records = resolved_rrs
438 .iter()
439 .filter_map(
440 |rr| if let RR::Txt(txt) = rr { Some(txt.data.as_vec()) } else { None },
441 )
442 .filter_map(
443 |data| if let Ok(s) = String::from_utf8(data) { Some(s) } else { None },
444 )
445 .filter(|data_string| data_string.len() > URI_PREFIX.len())
446 .filter(|data_string| {
447 data_string[..URI_PREFIX.len()].eq_ignore_ascii_case(URI_PREFIX)
448 });
449 match (candidate_records.next(), candidate_records.next()) {
452 (Some(txt), None) => {
453 let completed_requests =
454 requests.into_iter().map(|r| (r.name, r.payment_id)).collect();
455 return Some((completed_requests, txt));
456 },
457 _ => {},
458 }
459 }
460 }
461 None
462 }
463}