lightning_dns_resolver/
lib.rs

1//! A simple crate which uses [`dnssec_prover`] to create DNSSEC Proofs in response to bLIP 32
2//! Onion Message DNSSEC Proof Queries.
3
4#![deny(missing_docs)]
5#![deny(rustdoc::broken_intra_doc_links)]
6#![deny(rustdoc::private_intra_doc_links)]
7
8use std::net::SocketAddr;
9use std::ops::Deref;
10use std::sync::atomic::{AtomicUsize, Ordering};
11use std::sync::{Arc, Mutex};
12
13use dnssec_prover::query::build_txt_proof_async;
14
15use lightning::blinded_path::message::DNSResolverContext;
16use lightning::ln::peer_handler::IgnoringMessageHandler;
17use lightning::onion_message::dns_resolution::{
18	DNSResolverMessage, DNSResolverMessageHandler, DNSSECProof, DNSSECQuery,
19};
20use lightning::onion_message::messenger::{
21	MessageSendInstructions, Responder, ResponseInstruction,
22};
23
24use lightning_types::features::NodeFeatures;
25
26use tokio::runtime::Handle;
27
28#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
29const WE_REQUIRE_32_OR_64_BIT_USIZE: u8 = 424242;
30
31/// A resolver which implements [`DNSResolverMessageHandler`] and replies to [`DNSSECQuery`]
32/// messages with with [`DNSSECProof`]s.
33pub struct OMDomainResolver<PH: Deref>
34where
35	PH::Target: DNSResolverMessageHandler,
36{
37	state: Arc<OMResolverState>,
38	proof_handler: Option<PH>,
39	runtime_handle: Mutex<Option<Handle>>,
40}
41
42const MAX_PENDING_RESPONSES: usize = 1024;
43struct OMResolverState {
44	resolver: SocketAddr,
45	pending_replies: Mutex<Vec<(DNSResolverMessage, MessageSendInstructions)>>,
46	pending_query_count: AtomicUsize,
47}
48
49impl OMDomainResolver<IgnoringMessageHandler> {
50	/// Creates a new [`OMDomainResolver`] given the [`SocketAddr`] of a DNS resolver listening on
51	/// TCP (e.g. 8.8.8.8:53, 1.1.1.1:53 or your local DNS resolver).
52	///
53	/// Ignores any incoming [`DNSSECProof`] messages.
54	pub fn ignoring_incoming_proofs(resolver: SocketAddr) -> Self {
55		Self::new(resolver, None)
56	}
57}
58
59impl<PH: Deref> OMDomainResolver<PH>
60where
61	PH::Target: DNSResolverMessageHandler,
62{
63	/// Creates a new [`OMDomainResolver`] given the [`SocketAddr`] of a DNS resolver listening on
64	/// TCP (e.g. 8.8.8.8:53, 1.1.1.1:53 or your local DNS resolver).
65	///
66	/// Uses `tokio`'s [`Handle::current`] to fetch the async runtime on which futures will be
67	/// spawned.
68	///
69	/// The optional `proof_handler` can be provided to pass proofs coming back to us to the
70	/// underlying handler. This is useful when this resolver is handling incoming resolution
71	/// requests but some other handler is making proof requests of remote nodes and wants to get
72	/// results.
73	pub fn new(resolver: SocketAddr, proof_handler: Option<PH>) -> Self {
74		Self::with_runtime(resolver, proof_handler, Some(Handle::current()))
75	}
76
77	/// Creates a new [`OMDomainResolver`] given the [`SocketAddr`] of a DNS resolver listening on
78	/// TCP (e.g. 8.8.8.8:53, 1.1.1.1:53 or your local DNS resolver) and a `tokio` runtime
79	/// [`Handle`] on which futures will be spawned. If no runtime is provided, `set_runtime` must
80	/// be called before any queries will be handled.
81	///
82	/// The optional `proof_handler` can be provided to pass proofs coming back to us to the
83	/// underlying handler. This is useful when this resolver is handling incoming resolution
84	/// requests but some other handler is making proof requests of remote nodes and wants to get
85	/// results.
86	pub fn with_runtime(
87		resolver: SocketAddr, proof_handler: Option<PH>, runtime_handle: Option<Handle>,
88	) -> Self {
89		Self {
90			state: Arc::new(OMResolverState {
91				resolver,
92				pending_replies: Mutex::new(Vec::new()),
93				pending_query_count: AtomicUsize::new(0),
94			}),
95			proof_handler,
96			runtime_handle: Mutex::new(runtime_handle),
97		}
98	}
99
100	/// Sets the runtime on which futures will be spawned.
101	pub fn set_runtime(&self, runtime_handle: Handle) {
102		*self.runtime_handle.lock().unwrap() = Some(runtime_handle);
103	}
104}
105
106impl<PH: Deref> DNSResolverMessageHandler for OMDomainResolver<PH>
107where
108	PH::Target: DNSResolverMessageHandler,
109{
110	fn handle_dnssec_proof(&self, proof: DNSSECProof, context: DNSResolverContext) {
111		if let Some(proof_handler) = &self.proof_handler {
112			proof_handler.handle_dnssec_proof(proof, context);
113		}
114	}
115
116	fn handle_dnssec_query(
117		&self, q: DNSSECQuery, responder_opt: Option<Responder>,
118	) -> Option<(DNSResolverMessage, ResponseInstruction)> {
119		let responder = match responder_opt {
120			Some(responder) => responder,
121			None => return None,
122		};
123		let runtime = if let Some(runtime) = self.runtime_handle.lock().unwrap().clone() {
124			runtime
125		} else {
126			return None;
127		};
128		if self.state.pending_query_count.fetch_add(1, Ordering::Relaxed) > MAX_PENDING_RESPONSES {
129			self.state.pending_query_count.fetch_sub(1, Ordering::Relaxed);
130			return None;
131		}
132		let us = Arc::clone(&self.state);
133		runtime.spawn(async move {
134			if let Ok((proof, _ttl)) = build_txt_proof_async(us.resolver, &q.0).await {
135				let contents = DNSResolverMessage::DNSSECProof(DNSSECProof { name: q.0, proof });
136				let instructions = responder.respond().into_instructions();
137				us.pending_replies.lock().unwrap().push((contents, instructions));
138				us.pending_query_count.fetch_sub(1, Ordering::Relaxed);
139			}
140		});
141		None
142	}
143
144	fn provided_node_features(&self) -> NodeFeatures {
145		let mut features = NodeFeatures::empty();
146		features.set_dns_resolution_optional();
147		features
148	}
149
150	fn release_pending_messages(&self) -> Vec<(DNSResolverMessage, MessageSendInstructions)> {
151		core::mem::take(&mut *self.state.pending_replies.lock().unwrap())
152	}
153}
154
155#[cfg(test)]
156mod test {
157	use super::*;
158
159	use bitcoin::secp256k1::{self, PublicKey, Secp256k1};
160	use bitcoin::Block;
161
162	use lightning::blinded_path::message::{
163		BlindedMessagePath, MessageContext, MessageForwardNode,
164	};
165	use lightning::blinded_path::NodeIdLookUp;
166	use lightning::events::{Event, PaymentPurpose};
167	use lightning::ln::channelmanager::{OptionalOfferPaymentParams, PaymentId};
168	use lightning::ln::functional_test_utils::*;
169	use lightning::ln::msgs::{
170		BaseMessageHandler, ChannelMessageHandler, Init, OnionMessageHandler,
171	};
172	use lightning::ln::peer_handler::IgnoringMessageHandler;
173	use lightning::offers::offer::Offer;
174	use lightning::onion_message::dns_resolution::{HumanReadableName, OMNameResolver};
175	use lightning::onion_message::messenger::{
176		AOnionMessenger, Destination, MessageRouter, OnionMessagePath, OnionMessenger,
177	};
178	use lightning::sign::{KeysManager, NodeSigner, ReceiveAuthKey, Recipient};
179	use lightning::types::features::InitFeatures;
180	use lightning::types::payment::PaymentHash;
181	use lightning::util::logger::Logger;
182
183	use lightning::{commitment_signed_dance, expect_payment_claimed, get_htlc_update_msgs};
184	use lightning_types::string::UntrustedString;
185
186	use std::ops::Deref;
187	use std::sync::Mutex;
188	use std::time::{Duration, Instant, SystemTime};
189
190	struct TestLogger {
191		node: &'static str,
192	}
193	impl Logger for TestLogger {
194		fn log(&self, record: lightning::util::logger::Record) {
195			eprintln!("{}: {}", self.node, record.args);
196		}
197	}
198	impl Deref for TestLogger {
199		type Target = TestLogger;
200		fn deref(&self) -> &TestLogger {
201			self
202		}
203	}
204
205	struct DummyNodeLookup {}
206	impl NodeIdLookUp for DummyNodeLookup {
207		fn next_node_id(&self, _: u64) -> Option<PublicKey> {
208			None
209		}
210	}
211	impl Deref for DummyNodeLookup {
212		type Target = DummyNodeLookup;
213		fn deref(&self) -> &DummyNodeLookup {
214			self
215		}
216	}
217
218	struct DirectlyConnectedRouter {}
219	impl MessageRouter for DirectlyConnectedRouter {
220		fn find_path(
221			&self, _sender: PublicKey, _peers: Vec<PublicKey>, destination: Destination,
222		) -> Result<OnionMessagePath, ()> {
223			Ok(OnionMessagePath {
224				destination,
225				first_node_addresses: Vec::new(),
226				intermediate_nodes: Vec::new(),
227			})
228		}
229
230		fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
231			&self, recipient: PublicKey, local_node_receive_key: ReceiveAuthKey,
232			context: MessageContext, _peers: Vec<MessageForwardNode>, secp_ctx: &Secp256k1<T>,
233		) -> Result<Vec<BlindedMessagePath>, ()> {
234			let keys = KeysManager::new(&[0; 32], 42, 43, true);
235			Ok(vec![BlindedMessagePath::one_hop(
236				recipient,
237				local_node_receive_key,
238				context,
239				&keys,
240				secp_ctx,
241			)])
242		}
243	}
244	impl Deref for DirectlyConnectedRouter {
245		type Target = DirectlyConnectedRouter;
246		fn deref(&self) -> &DirectlyConnectedRouter {
247			self
248		}
249	}
250
251	struct URIResolver {
252		resolved_uri: Mutex<Option<(HumanReadableName, PaymentId, String)>>,
253		resolver: OMNameResolver,
254		pending_messages: Mutex<Vec<(DNSResolverMessage, MessageSendInstructions)>>,
255	}
256	impl DNSResolverMessageHandler for URIResolver {
257		fn handle_dnssec_query(
258			&self, _: DNSSECQuery, _: Option<Responder>,
259		) -> Option<(DNSResolverMessage, ResponseInstruction)> {
260			panic!();
261		}
262
263		fn handle_dnssec_proof(&self, msg: DNSSECProof, context: DNSResolverContext) {
264			let mut proof = self.resolver.handle_dnssec_proof_for_uri(msg, context).unwrap();
265			assert_eq!(proof.0.len(), 1);
266			let payment = proof.0.pop().unwrap();
267			let mut result = Some((payment.0, payment.1, proof.1));
268			core::mem::swap(&mut *self.resolved_uri.lock().unwrap(), &mut result);
269			assert!(result.is_none());
270		}
271		fn release_pending_messages(&self) -> Vec<(DNSResolverMessage, MessageSendInstructions)> {
272			core::mem::take(&mut *self.pending_messages.lock().unwrap())
273		}
274	}
275
276	fn create_resolver() -> (impl AOnionMessenger, PublicKey) {
277		let resolver_keys = Arc::new(KeysManager::new(&[99; 32], 42, 43, true));
278		let resolver_logger = TestLogger { node: "resolver" };
279		let resolver = OMDomainResolver::ignoring_incoming_proofs("8.8.8.8:53".parse().unwrap());
280		let resolver = Arc::new(resolver);
281		(
282			OnionMessenger::new(
283				Arc::clone(&resolver_keys),
284				Arc::clone(&resolver_keys),
285				resolver_logger,
286				DummyNodeLookup {},
287				DirectlyConnectedRouter {},
288				IgnoringMessageHandler {},
289				IgnoringMessageHandler {},
290				Arc::clone(&resolver),
291				IgnoringMessageHandler {},
292			),
293			resolver_keys.get_node_id(Recipient::Node).unwrap(),
294		)
295	}
296
297	fn get_om_init() -> Init {
298		let mut init_msg =
299			Init { features: InitFeatures::empty(), networks: None, remote_network_address: None };
300		init_msg.features.set_onion_messages_optional();
301		init_msg
302	}
303
304	#[tokio::test]
305	async fn resolution_test() {
306		let secp_ctx = Secp256k1::new();
307
308		let (resolver_messenger, resolver_id) = create_resolver();
309
310		let resolver_dest = Destination::Node(resolver_id);
311		let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
312
313		let payment_id = PaymentId([42; 32]);
314		let name = HumanReadableName::from_encoded("matt@mattcorallo.com").unwrap();
315
316		let payer_keys = Arc::new(KeysManager::new(&[2; 32], 42, 43, true));
317		let payer_logger = TestLogger { node: "payer" };
318		let payer_id = payer_keys.get_node_id(Recipient::Node).unwrap();
319		let payer = Arc::new(URIResolver {
320			resolved_uri: Mutex::new(None),
321			resolver: OMNameResolver::new(now as u32, 1),
322			pending_messages: Mutex::new(Vec::new()),
323		});
324		let payer_messenger = Arc::new(OnionMessenger::new(
325			Arc::clone(&payer_keys),
326			Arc::clone(&payer_keys),
327			payer_logger,
328			DummyNodeLookup {},
329			DirectlyConnectedRouter {},
330			IgnoringMessageHandler {},
331			IgnoringMessageHandler {},
332			Arc::clone(&payer),
333			IgnoringMessageHandler {},
334		));
335
336		let init_msg = get_om_init();
337		payer_messenger.peer_connected(resolver_id, &init_msg, true).unwrap();
338		resolver_messenger.get_om().peer_connected(payer_id, &init_msg, false).unwrap();
339
340		let (msg, context) =
341			payer.resolver.resolve_name(payment_id, name.clone(), &*payer_keys).unwrap();
342		let query_context = MessageContext::DNSResolver(context);
343		let receive_key = payer_keys.get_receive_auth_key();
344		let reply_path = BlindedMessagePath::one_hop(
345			payer_id,
346			receive_key,
347			query_context,
348			&*payer_keys,
349			&secp_ctx,
350		);
351		payer.pending_messages.lock().unwrap().push((
352			DNSResolverMessage::DNSSECQuery(msg),
353			MessageSendInstructions::WithSpecifiedReplyPath {
354				destination: resolver_dest,
355				reply_path,
356			},
357		));
358
359		let query = payer_messenger.next_onion_message_for_peer(resolver_id).unwrap();
360		resolver_messenger.get_om().handle_onion_message(payer_id, &query);
361
362		assert!(resolver_messenger.get_om().next_onion_message_for_peer(payer_id).is_none());
363		let start = Instant::now();
364		let response = loop {
365			tokio::time::sleep(Duration::from_millis(10)).await;
366			if let Some(msg) = resolver_messenger.get_om().next_onion_message_for_peer(payer_id) {
367				break msg;
368			}
369			assert!(start.elapsed() < Duration::from_secs(10), "Resolution took too long");
370		};
371
372		payer_messenger.handle_onion_message(resolver_id, &response);
373		let resolution = payer.resolved_uri.lock().unwrap().take().unwrap();
374		assert_eq!(resolution.0, name);
375		assert_eq!(resolution.1, payment_id);
376		assert!(resolution.2[.."bitcoin:".len()].eq_ignore_ascii_case("bitcoin:"));
377	}
378
379	async fn pay_offer_flow<'a, 'b, 'c>(
380		nodes: &[Node<'a, 'b, 'c>], resolver_messenger: &impl AOnionMessenger,
381		resolver_id: PublicKey, payer_id: PublicKey, payee_id: PublicKey, offer: Offer,
382		name: HumanReadableName, payment_id: PaymentId, payer_note: Option<String>,
383		resolvers: Vec<Destination>,
384	) {
385		// Override contents to offer provided
386		let proof_override = &nodes[0].node.testing_dnssec_proof_offer_resolution_override;
387		proof_override.lock().unwrap().insert(name.clone(), offer);
388		let amt = 42_000;
389		let mut opts = OptionalOfferPaymentParams::default();
390		opts.payer_note = payer_note.clone();
391		#[allow(deprecated)]
392		nodes[0]
393			.node
394			.pay_for_offer_from_human_readable_name(name, amt, payment_id, opts, resolvers)
395			.unwrap();
396
397		let query = nodes[0].onion_messenger.next_onion_message_for_peer(resolver_id).unwrap();
398		resolver_messenger.get_om().handle_onion_message(payer_id, &query);
399
400		assert!(resolver_messenger.get_om().next_onion_message_for_peer(payer_id).is_none());
401		let start = Instant::now();
402		let response = loop {
403			tokio::time::sleep(Duration::from_millis(10)).await;
404			if let Some(msg) = resolver_messenger.get_om().next_onion_message_for_peer(payer_id) {
405				break msg;
406			}
407			assert!(start.elapsed() < Duration::from_secs(10), "Resolution took too long");
408		};
409
410		nodes[0].onion_messenger.handle_onion_message(resolver_id, &response);
411
412		let invreq = nodes[0].onion_messenger.next_onion_message_for_peer(payee_id).unwrap();
413		nodes[1].onion_messenger.handle_onion_message(payer_id, &invreq);
414
415		let inv = nodes[1].onion_messenger.next_onion_message_for_peer(payer_id).unwrap();
416		nodes[0].onion_messenger.handle_onion_message(payee_id, &inv);
417
418		check_added_monitors(&nodes[0], 1);
419		let updates = get_htlc_update_msgs!(nodes[0], payee_id);
420		nodes[1].node.handle_update_add_htlc(payer_id, &updates.update_add_htlcs[0]);
421		commitment_signed_dance!(nodes[1], nodes[0], updates.commitment_signed, false);
422		expect_and_process_pending_htlcs(&nodes[1], false);
423
424		let claimable_events = nodes[1].node.get_and_clear_pending_events();
425		assert_eq!(claimable_events.len(), 1);
426		let our_payment_preimage;
427		if let Event::PaymentClaimable { purpose, amount_msat, .. } = &claimable_events[0] {
428			assert_eq!(*amount_msat, amt);
429			if let PaymentPurpose::Bolt12OfferPayment {
430				payment_preimage, payment_context, ..
431			} = purpose
432			{
433				our_payment_preimage = payment_preimage.unwrap();
434				nodes[1].node.claim_funds(our_payment_preimage);
435				let payment_hash: PaymentHash = our_payment_preimage.into();
436				expect_payment_claimed!(nodes[1], payment_hash, amt);
437				if let Some(note) = payer_note {
438					assert_eq!(
439						payment_context.invoice_request.payer_note_truncated,
440						Some(UntrustedString(note.into()))
441					);
442				} else {
443					assert_eq!(payment_context.invoice_request.payer_note_truncated, None);
444				}
445			} else {
446				panic!();
447			}
448		} else {
449			panic!();
450		}
451
452		check_added_monitors(&nodes[1], 1);
453		let mut updates = get_htlc_update_msgs!(nodes[1], payer_id);
454		nodes[0].node.handle_update_fulfill_htlc(payee_id, updates.update_fulfill_htlcs.remove(0));
455		commitment_signed_dance!(nodes[0], nodes[1], updates.commitment_signed, false);
456
457		expect_payment_sent(&nodes[0], our_payment_preimage, None, true, true);
458	}
459
460	#[tokio::test]
461	async fn end_to_end_test() {
462		let chanmon_cfgs = create_chanmon_cfgs(2);
463		let node_cfgs = create_node_cfgs_with_node_id_message_router(2, &chanmon_cfgs);
464		let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
465		let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
466
467		create_announced_chan_between_nodes(&nodes, 0, 1);
468
469		// The DNSSEC validation will only work with the current time, so set the time on the
470		// resolver.
471		let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
472		let block = Block {
473			header: create_dummy_header(nodes[0].best_block_hash(), now as u32),
474			txdata: Vec::new(),
475		};
476		connect_block(&nodes[0], &block);
477		connect_block(&nodes[1], &block);
478
479		let payer_id = nodes[0].node.get_our_node_id();
480		let payee_id = nodes[1].node.get_our_node_id();
481
482		let (resolver_messenger, resolver_id) = create_resolver();
483		let init_msg = get_om_init();
484		nodes[0].onion_messenger.peer_connected(resolver_id, &init_msg, true).unwrap();
485		resolver_messenger.get_om().peer_connected(payer_id, &init_msg, false).unwrap();
486
487		let name = HumanReadableName::from_encoded("matt@mattcorallo.com").unwrap();
488
489		let bs_offer = nodes[1].node.create_offer_builder().unwrap().build().unwrap();
490		let resolvers = vec![Destination::Node(resolver_id)];
491
492		pay_offer_flow(
493			&nodes,
494			&resolver_messenger,
495			resolver_id,
496			payer_id,
497			payee_id,
498			bs_offer.clone(),
499			name.clone(),
500			PaymentId([42; 32]),
501			None,
502			resolvers.clone(),
503		)
504		.await;
505
506		// Pay offer with payer_note
507		pay_offer_flow(
508			&nodes,
509			&resolver_messenger,
510			resolver_id,
511			payer_id,
512			payee_id,
513			bs_offer,
514			name,
515			PaymentId([21; 32]),
516			Some("foo".into()),
517			resolvers,
518		)
519		.await;
520	}
521}