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 core::fmt;
40
41use crate::blinded_path::message::DNSResolverContext;
42use crate::io;
43#[cfg(feature = "dnssec")]
44use crate::ln::channelmanager::PaymentId;
45use crate::ln::msgs::DecodeError;
46#[cfg(feature = "dnssec")]
47use crate::offers::offer::Offer;
48use crate::onion_message::messenger::{MessageSendInstructions, Responder, ResponseInstruction};
49use crate::onion_message::packet::OnionMessageContents;
50use crate::prelude::*;
51#[cfg(feature = "dnssec")]
52use crate::sign::EntropySource;
53#[cfg(feature = "dnssec")]
54use crate::sync::Mutex;
55use crate::util::ser::{Hostname, Readable, ReadableArgs, Writeable, Writer};
56
57pub trait DNSResolverMessageHandler {
61 fn handle_dnssec_query(
66 &self, message: DNSSECQuery, responder: Option<Responder>,
67 ) -> Option<(DNSResolverMessage, ResponseInstruction)>;
68
69 fn handle_dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext);
78
79 fn provided_node_features(&self) -> NodeFeatures {
83 NodeFeatures::empty()
84 }
85
86 fn release_pending_messages(&self) -> Vec<(DNSResolverMessage, MessageSendInstructions)> {
88 vec![]
89 }
90}
91
92#[derive(Clone, Debug, Hash, PartialEq, Eq)]
93pub enum DNSResolverMessage {
96 DNSSECQuery(DNSSECQuery),
98 DNSSECProof(DNSSECProof),
100}
101
102const DNSSEC_QUERY_TYPE: u64 = 65536;
103const DNSSEC_PROOF_TYPE: u64 = 65538;
104
105#[derive(Clone, Debug, Hash, PartialEq, Eq)]
106pub struct DNSSECQuery(pub Name);
108
109#[derive(Clone, Debug, Hash, PartialEq, Eq)]
110pub struct DNSSECProof {
112 pub name: Name,
115 pub proof: Vec<u8>,
119}
120
121impl DNSResolverMessage {
122 pub fn is_known_type(tlv_type: u64) -> bool {
124 match tlv_type {
125 DNSSEC_QUERY_TYPE | DNSSEC_PROOF_TYPE => true,
126 _ => false,
127 }
128 }
129}
130
131impl Writeable for DNSResolverMessage {
132 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
133 match self {
134 Self::DNSSECQuery(DNSSECQuery(q)) => {
135 (q.as_str().len() as u8).write(w)?;
136 w.write_all(&q.as_str().as_bytes())
137 },
138 Self::DNSSECProof(DNSSECProof { name, proof }) => {
139 (name.as_str().len() as u8).write(w)?;
140 w.write_all(&name.as_str().as_bytes())?;
141 proof.write(w)
142 },
143 }
144 }
145}
146
147impl ReadableArgs<u64> for DNSResolverMessage {
148 fn read<R: io::Read>(r: &mut R, message_type: u64) -> Result<Self, DecodeError> {
149 match message_type {
150 DNSSEC_QUERY_TYPE => {
151 let s = Hostname::read(r)?;
152 let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
153 Ok(DNSResolverMessage::DNSSECQuery(DNSSECQuery(name)))
154 },
155 DNSSEC_PROOF_TYPE => {
156 let s = Hostname::read(r)?;
157 let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
158 let proof = Readable::read(r)?;
159 Ok(DNSResolverMessage::DNSSECProof(DNSSECProof { name, proof }))
160 },
161 _ => Err(DecodeError::InvalidValue),
162 }
163 }
164}
165
166impl OnionMessageContents for DNSResolverMessage {
167 #[cfg(c_bindings)]
168 fn msg_type(&self) -> String {
169 match self {
170 DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query".to_string(),
171 DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof".to_string(),
172 }
173 }
174 #[cfg(not(c_bindings))]
175 fn msg_type(&self) -> &'static str {
176 match self {
177 DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query",
178 DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof",
179 }
180 }
181 fn tlv_type(&self) -> u64 {
182 match self {
183 DNSResolverMessage::DNSSECQuery(_) => DNSSEC_QUERY_TYPE,
184 DNSResolverMessage::DNSSECProof(_) => DNSSEC_PROOF_TYPE,
185 }
186 }
187}
188
189const REQUIRED_EXTRA_LEN: usize = ".user._bitcoin-payment.".len() + 1;
191
192#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
205pub struct HumanReadableName {
206 contents: [u8; 255 - REQUIRED_EXTRA_LEN],
207 user_len: u8,
208 domain_len: u8,
209}
210
211impl HumanReadableName {
212 pub fn new(user: &str, mut domain: &str) -> Result<HumanReadableName, ()> {
215 if domain.ends_with('.') {
217 domain = &domain[..domain.len() - 1];
218 }
219 if user.len() + domain.len() + REQUIRED_EXTRA_LEN > 255 {
220 return Err(());
221 }
222 if user.is_empty() || domain.is_empty() {
223 return Err(());
224 }
225 if !Hostname::str_is_valid_hostname(&user) || !Hostname::str_is_valid_hostname(&domain) {
226 return Err(());
227 }
228 let mut contents = [0; 255 - REQUIRED_EXTRA_LEN];
229 contents[..user.len()].copy_from_slice(user.as_bytes());
230 contents[user.len()..user.len() + domain.len()].copy_from_slice(domain.as_bytes());
231 Ok(HumanReadableName {
232 contents,
233 user_len: user.len() as u8,
234 domain_len: domain.len() as u8,
235 })
236 }
237
238 pub fn from_encoded(encoded: &str) -> Result<HumanReadableName, ()> {
243 if let Some((user, domain)) = encoded.strip_prefix('₿').unwrap_or(encoded).split_once("@")
244 {
245 Self::new(user, domain)
246 } else {
247 Err(())
248 }
249 }
250
251 pub fn user(&self) -> &str {
253 let bytes = &self.contents[..self.user_len as usize];
254 core::str::from_utf8(bytes).expect("Checked in constructor")
255 }
256
257 pub fn domain(&self) -> &str {
259 let user_len = self.user_len as usize;
260 let bytes = &self.contents[user_len..user_len + self.domain_len as usize];
261 core::str::from_utf8(bytes).expect("Checked in constructor")
262 }
263}
264
265impl Writeable for HumanReadableName {
267 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
268 (self.user().len() as u8).write(writer)?;
269 writer.write_all(&self.user().as_bytes())?;
270 (self.domain().len() as u8).write(writer)?;
271 writer.write_all(&self.domain().as_bytes())
272 }
273}
274
275impl Readable for HumanReadableName {
276 fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
277 let mut user_bytes = [0; 255];
278 let user_len: u8 = Readable::read(reader)?;
279 reader.read_exact(&mut user_bytes[..user_len as usize])?;
280 let user = match core::str::from_utf8(&user_bytes[..user_len as usize]) {
281 Ok(user) => user,
282 Err(_) => return Err(DecodeError::InvalidValue),
283 };
284
285 let mut domain_bytes = [0; 255];
286 let domain_len: u8 = Readable::read(reader)?;
287 reader.read_exact(&mut domain_bytes[..domain_len as usize])?;
288 let domain = match core::str::from_utf8(&domain_bytes[..domain_len as usize]) {
289 Ok(domain) => domain,
290 Err(_) => return Err(DecodeError::InvalidValue),
291 };
292
293 HumanReadableName::new(user, domain).map_err(|()| DecodeError::InvalidValue)
294 }
295}
296
297impl fmt::Display for HumanReadableName {
298 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
299 write!(f, "₿{}@{}", self.user(), self.domain())
300 }
301}
302
303#[cfg(feature = "dnssec")]
304struct PendingResolution {
305 start_height: u32,
306 context: DNSResolverContext,
307 name: HumanReadableName,
308 payment_id: PaymentId,
309}
310
311#[cfg(feature = "dnssec")]
320pub struct OMNameResolver {
321 pending_resolves: Mutex<HashMap<Name, Vec<PendingResolution>>>,
322 latest_block_time: AtomicUsize,
323 latest_block_height: AtomicUsize,
324}
325
326#[cfg(feature = "dnssec")]
327impl OMNameResolver {
328 pub fn new(latest_block_time: u32, latest_block_height: u32) -> Self {
330 Self {
331 pending_resolves: Mutex::new(new_hash_map()),
332 latest_block_time: AtomicUsize::new(latest_block_time as usize),
333 latest_block_height: AtomicUsize::new(latest_block_height as usize),
334 }
335 }
336
337 pub fn new_without_no_std_expiry_validation() -> Self {
348 Self {
349 pending_resolves: Mutex::new(new_hash_map()),
350 latest_block_time: AtomicUsize::new(0),
351 latest_block_height: AtomicUsize::new(0),
352 }
353 }
354
355 pub fn new_best_block(&self, height: u32, time: u32) {
361 self.latest_block_time.store(time as usize, Ordering::Release);
362 self.latest_block_height.store(height as usize, Ordering::Release);
363 let mut resolves = self.pending_resolves.lock().unwrap();
364 resolves.retain(|_, queries| {
365 queries.retain(|query| query.start_height >= height - 1);
366 !queries.is_empty()
367 });
368 }
369
370 pub fn expire_pending_resolution(&self, name: &HumanReadableName, payment_id: PaymentId) {
376 let dns_name =
377 Name::try_from(format!("{}.user._bitcoin-payment.{}.", name.user(), name.domain()));
378 debug_assert!(
379 dns_name.is_ok(),
380 "The HumanReadableName constructor shouldn't allow names which are too long"
381 );
382 if let Ok(name) = dns_name {
383 let mut pending_resolves = self.pending_resolves.lock().unwrap();
384 if let hash_map::Entry::Occupied(mut entry) = pending_resolves.entry(name) {
385 let resolutions = entry.get_mut();
386 resolutions.retain(|resolution| resolution.payment_id != payment_id);
387 if resolutions.is_empty() {
388 entry.remove();
389 }
390 }
391 }
392 }
393
394 pub fn resolve_name<ES: EntropySource + ?Sized>(
399 &self, payment_id: PaymentId, name: HumanReadableName, entropy_source: &ES,
400 ) -> Result<(DNSSECQuery, DNSResolverContext), ()> {
401 let dns_name =
402 Name::try_from(format!("{}.user._bitcoin-payment.{}.", name.user(), name.domain()));
403 debug_assert!(
404 dns_name.is_ok(),
405 "The HumanReadableName constructor shouldn't allow names which are too long"
406 );
407 let mut context = DNSResolverContext { nonce: [0; 16] };
408 context.nonce.copy_from_slice(&entropy_source.get_secure_random_bytes()[..16]);
409 if let Ok(dns_name) = dns_name {
410 let start_height = self.latest_block_height.load(Ordering::Acquire) as u32;
411 let mut pending_resolves = self.pending_resolves.lock().unwrap();
412 let context_ret = context.clone();
413 let resolution = PendingResolution { start_height, context, name, payment_id };
414 pending_resolves.entry(dns_name.clone()).or_insert_with(Vec::new).push(resolution);
415 Ok((DNSSECQuery(dns_name), context_ret))
416 } else {
417 Err(())
418 }
419 }
420
421 pub fn handle_dnssec_proof_for_offer(
433 &self, msg: DNSSECProof, context: DNSResolverContext,
434 ) -> Option<(Vec<(HumanReadableName, PaymentId)>, Offer)> {
435 let (completed_requests, uri) = self.handle_dnssec_proof_for_uri(msg, context)?;
436 if let Some((_onchain, params)) = uri.split_once("?") {
437 for param in params.split("&") {
438 let (k, v) = if let Some(split) = param.split_once("=") {
439 split
440 } else {
441 continue;
442 };
443 if k.eq_ignore_ascii_case("lno") {
444 if let Ok(offer) = Offer::from_str(v) {
445 return Some((completed_requests, offer));
446 }
447 return None;
448 }
449 }
450 }
451 None
452 }
453
454 pub fn handle_dnssec_proof_for_uri(
466 &self, msg: DNSSECProof, context: DNSResolverContext,
467 ) -> Option<(Vec<(HumanReadableName, PaymentId)>, String)> {
468 let DNSSECProof { name: answer_name, proof } = msg;
469 let mut pending_resolves = self.pending_resolves.lock().unwrap();
470 if let hash_map::Entry::Occupied(entry) = pending_resolves.entry(answer_name) {
471 if !entry.get().iter().any(|query| query.context == context) {
472 return None;
481 }
482 let parsed_rrs = parse_rr_stream(&proof);
483 let validated_rrs =
484 parsed_rrs.as_ref().and_then(|rrs| verify_rr_stream(rrs).map_err(|_| &()));
485 if let Ok(validated_rrs) = validated_rrs {
486 #[allow(unused_assignments, unused_mut)]
487 let mut time = self.latest_block_time.load(Ordering::Acquire) as u64;
488 #[cfg(feature = "std")]
489 {
490 use std::time::{SystemTime, UNIX_EPOCH};
491 let now = SystemTime::now().duration_since(UNIX_EPOCH);
492 time = now.expect("Time must be > 1970").as_secs();
493 }
494 if time != 0 {
495 let max_time_offset = if cfg!(feature = "std") { 0 } else { 60 * 2 };
500 if validated_rrs.valid_from > time + max_time_offset {
501 return None;
502 }
503 if validated_rrs.expires < time - max_time_offset {
504 return None;
505 }
506 }
507 let resolved_rrs = validated_rrs.resolve_name(&entry.key());
508 if resolved_rrs.is_empty() {
509 return None;
510 }
511
512 let (_, requests) = entry.remove_entry();
513
514 const URI_PREFIX: &str = "bitcoin:";
515 let mut candidate_records = resolved_rrs
516 .iter()
517 .filter_map(
518 |rr| if let RR::Txt(txt) = rr { Some(txt.data.as_vec()) } else { None },
519 )
520 .filter_map(|data| String::from_utf8(data).ok())
521 .filter(|data_string| data_string.len() > URI_PREFIX.len())
522 .filter(|data_string| {
523 data_string[..URI_PREFIX.len()].eq_ignore_ascii_case(URI_PREFIX)
524 });
525 match (candidate_records.next(), candidate_records.next()) {
528 (Some(txt), None) => {
529 let completed_requests =
530 requests.into_iter().map(|r| (r.name, r.payment_id)).collect();
531 return Some((completed_requests, txt));
532 },
533 _ => {},
534 }
535 }
536 }
537 None
538 }
539}
540
541#[cfg(test)]
542mod tests {
543 use super::*;
544
545 #[test]
546 fn test_hrn_display_format() {
547 let user = "user";
548 let domain = "example.com";
549 let hrn = HumanReadableName::new(user, domain)
550 .expect("Failed to create HumanReadableName for user");
551
552 let expected_display = format!("₿{}@{}", user, domain);
554 assert_eq!(
555 format!("{}", hrn),
556 expected_display,
557 "HumanReadableName display format mismatch"
558 );
559 }
560
561 #[test]
562 #[cfg(feature = "dnssec")]
563 fn test_expiry() {
564 let keys = crate::sign::KeysManager::new(&[33; 32], 0, 0, true);
565 let resolver = OMNameResolver::new(42, 42);
566 let name = HumanReadableName::new("user", "example.com").unwrap();
567
568 resolver.resolve_name(PaymentId([0; 32]), name.clone(), &keys).unwrap();
570 assert_eq!(resolver.pending_resolves.lock().unwrap().len(), 1);
571 resolver.new_best_block(44, 42);
573 assert_eq!(resolver.pending_resolves.lock().unwrap().len(), 0);
574
575 resolver.resolve_name(PaymentId([1; 32]), name.clone(), &keys).unwrap();
577 assert_eq!(resolver.pending_resolves.lock().unwrap().len(), 1);
578 resolver.new_best_block(45, 42);
580 assert_eq!(resolver.pending_resolves.lock().unwrap().len(), 1);
581 assert_eq!(resolver.pending_resolves.lock().unwrap().iter().next().unwrap().1.len(), 1);
582 resolver.resolve_name(PaymentId([2; 32]), name.clone(), &keys).unwrap();
584 resolver.resolve_name(PaymentId([3; 32]), name.clone(), &keys).unwrap();
585 assert_eq!(resolver.pending_resolves.lock().unwrap().len(), 1);
586 assert_eq!(resolver.pending_resolves.lock().unwrap().iter().next().unwrap().1.len(), 3);
587 resolver.new_best_block(46, 42);
589 assert_eq!(resolver.pending_resolves.lock().unwrap().len(), 1);
590 assert_eq!(resolver.pending_resolves.lock().unwrap().iter().next().unwrap().1.len(), 2);
591 resolver.expire_pending_resolution(&name, PaymentId([3; 32]));
593 assert_eq!(resolver.pending_resolves.lock().unwrap().len(), 1);
594 assert_eq!(resolver.pending_resolves.lock().unwrap().iter().next().unwrap().1.len(), 1);
595 resolver.new_best_block(47, 42);
597 assert_eq!(resolver.pending_resolves.lock().unwrap().len(), 0);
598 }
599}