async_dnssd/service/
resolve.rs

1use std::{
2	io,
3	os::raw::{
4		c_char,
5		c_void,
6	},
7	pin::Pin,
8	task::{
9		Context,
10		Poll,
11	},
12};
13
14use crate::{
15	cstr,
16	ffi,
17	inner,
18	interface::Interface,
19	service::{
20		resolve_host_extended,
21		ResolveHost,
22		ResolveHostData,
23	},
24};
25
26type CallbackStream = crate::stream::ServiceStream<inner::OwnedService, ResolveResult>;
27
28bitflags::bitflags! {
29	/// Flags for [`ResolveResult`](struct.ResolveResult.html)
30	#[derive(Default)]
31	pub struct ResolvedFlags: ffi::DNSServiceFlags {
32		/// Indicates at least one more result is pending in the queue.  If
33		/// not set there still might be more results coming in the future.
34		///
35		/// See [`kDNSServiceFlagsMoreComing`](https://developer.apple.com/documentation/dnssd/1823436-anonymous/kdnsserviceflagsmorecoming).
36		const MORE_COMING = ffi::FLAGS_MORE_COMING;
37	}
38}
39
40/// Pending resolve request
41#[must_use = "streams do nothing unless polled"]
42pub struct Resolve {
43	stream: crate::fused_err_stream::FusedErrorStream<CallbackStream>,
44}
45
46impl Resolve {
47	pin_utils::unsafe_pinned!(stream: crate::fused_err_stream::FusedErrorStream<CallbackStream>);
48}
49
50impl futures_core::Stream for Resolve {
51	type Item = io::Result<ResolveResult>;
52
53	fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
54		self.stream().poll_next(cx)
55	}
56}
57
58/// Resolve result
59///
60/// See [`DNSServiceResolveReply`](https://developer.apple.com/documentation/dnssd/dnsserviceresolvereply).
61#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
62pub struct ResolveResult {
63	/// flags
64	pub flags: ResolvedFlags,
65	/// interface service was resolved on
66	pub interface: Interface,
67	/// full name of service
68	pub fullname: String,
69	/// hostname the service is provided on
70	pub host_target: String,
71	/// port the service is provided on (native endian)
72	pub port: u16,
73	/// TXT RDATA describing service parameters
74	pub txt: Vec<u8>,
75}
76
77impl ResolveResult {
78	/// Lookup socket addresses for resolved service
79	pub fn resolve_socket_address(&self) -> ResolveHost {
80		let rhdata = ResolveHostData {
81			interface: self.interface,
82			..Default::default()
83		};
84		resolve_host_extended(&self.host_target, self.port, rhdata)
85	}
86}
87
88unsafe extern "C" fn resolve_callback(
89	_sd_ref: ffi::DNSServiceRef,
90	flags: ffi::DNSServiceFlags,
91	interface_index: u32,
92	error_code: ffi::DNSServiceErrorType,
93	fullname: *const c_char,
94	host_target: *const c_char,
95	port: u16,
96	txt_len: u16,
97	txt_record: *const u8,
98	context: *mut c_void,
99) {
100	CallbackStream::run_callback(context, error_code, || {
101		let fullname = cstr::from_cstr(fullname)?;
102		let host_target = cstr::from_cstr(host_target)?;
103		let txt = ::std::slice::from_raw_parts(txt_record, txt_len as usize);
104
105		Ok(ResolveResult {
106			flags: ResolvedFlags::from_bits_truncate(flags),
107			interface: Interface::from_raw(interface_index),
108			fullname: fullname.to_string(),
109			host_target: host_target.to_string(),
110			port: u16::from_be(port),
111			txt: txt.into(),
112		})
113	});
114}
115
116fn _resolve(interface: Interface, name: &str, reg_type: &str, domain: &str) -> io::Result<Resolve> {
117	crate::init();
118
119	let name = cstr::CStr::from(&name)?;
120	let reg_type = cstr::CStr::from(&reg_type)?;
121	let domain = cstr::CStr::from(&domain)?;
122
123	let stream = CallbackStream::new(move |sender| {
124		inner::OwnedService::resolve(
125			0, // no flags
126			interface.into_raw(),
127			&name,
128			&reg_type,
129			&domain,
130			Some(resolve_callback),
131			sender,
132		)
133	})
134	.into();
135
136	Ok(Resolve { stream })
137}
138
139/// Find hostname and port (and more) for a service
140///
141/// You probably want to use [`BrowseResult::resolve`] instead.
142///
143/// See [`DNSServiceResolve`](https://developer.apple.com/documentation/dnssd/1804744-dnsserviceresolve).
144///
145/// [`BrowseResult::resolve`]: struct.BrowseResult.html#method.resolve
146#[doc(alias = "DNSServiceResolve")]
147pub fn resolve(interface: Interface, name: &str, reg_type: &str, domain: &str) -> Resolve {
148	match _resolve(interface, name, reg_type, domain) {
149		Ok(r) => r,
150		Err(e) => Resolve {
151			stream: Err(e).into(),
152		},
153	}
154}