async_dnssd/service/
resolve.rs

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