async_dnssd/service/
query_record.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	dns_consts::{
18		Class,
19		Type,
20	},
21	ffi,
22	inner,
23	interface::Interface,
24};
25
26type CallbackStream = crate::stream::ServiceStream<inner::OwnedService, QueryRecordResult>;
27
28bitflags::bitflags! {
29	/// Flags used to query for a record
30	#[derive(Default)]
31	pub struct QueryRecordFlags: ffi::DNSServiceFlags {
32		/// long-lived unicast query
33		///
34		/// See [`kDNSServiceFlagsLongLivedQuery`](https://developer.apple.com/documentation/dnssd/1823436-anonymous/kdnsserviceflagslonglivedquery).
35		const LONG_LIVED_QUERY = ffi::FLAGS_LONG_LIVED_QUERY;
36	}
37}
38
39bitflags::bitflags! {
40	/// Flags for [`QueryRecordResult`]
41	#[derive(Default)]
42	pub struct QueriedRecordFlags: ffi::DNSServiceFlags {
43		/// Indicates at least one more result is pending in the queue.  If
44		/// not set there still might be more results coming in the future.
45		///
46		/// See [`kDNSServiceFlagsMoreComing`](https://developer.apple.com/documentation/dnssd/1823436-anonymous/kdnsserviceflagsmorecoming).
47		const MORE_COMING = ffi::FLAGS_MORE_COMING;
48
49		/// Indicates the result is new.  If not set indicates the result
50		/// was removed.
51		///
52		/// See [`kDNSServiceFlagsAdd`](https://developer.apple.com/documentation/dnssd/1823436-anonymous/kdnsserviceflagsadd).
53		const ADD = ffi::FLAGS_ADD;
54	}
55}
56
57/// Pending query
58#[must_use = "streams do nothing unless polled"]
59pub struct QueryRecord {
60	stream: crate::fused_err_stream::FusedErrorStream<CallbackStream>,
61}
62
63impl futures_core::Stream for QueryRecord {
64	type Item = io::Result<QueryRecordResult>;
65
66	fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
67		let this = self.get_mut();
68		this.stream.poll_next_unpin(cx)
69	}
70}
71
72/// Query result
73///
74/// See [`DNSServiceQueryRecordReply`](https://developer.apple.com/documentation/dnssd/dnsservicequeryrecordreply).
75#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
76pub struct QueryRecordResult {
77	/// flags
78	pub flags: QueriedRecordFlags,
79	/// interface the record was found on
80	pub interface: Interface,
81	/// name of record
82	pub fullname: String,
83	/// type of record
84	pub rr_type: Type,
85	/// class of record
86	pub rr_class: Class,
87	/// wire RDATA of record
88	pub rdata: Vec<u8>,
89	/// TTL (time to live) of record
90	pub ttl: u32,
91}
92
93unsafe extern "C" fn query_record_callback(
94	_sd_ref: ffi::DNSServiceRef,
95	flags: ffi::DNSServiceFlags,
96	interface_index: u32,
97	error_code: ffi::DNSServiceErrorType,
98	fullname: *const c_char,
99	rr_type: u16,
100	rr_class: u16,
101	rd_len: u16,
102	rdata: *const u8,
103	ttl: u32,
104	context: *mut c_void,
105) {
106	unsafe {
107		CallbackStream::run_callback(context, error_code, || {
108			let fullname = cstr::from_cstr(fullname)?;
109			let rdata = ::std::slice::from_raw_parts(rdata, rd_len as usize);
110
111			Ok(QueryRecordResult {
112				flags: QueriedRecordFlags::from_bits_truncate(flags),
113				interface: Interface::from_raw(interface_index),
114				fullname: fullname.to_string(),
115				rr_type: Type(rr_type),
116				rr_class: Class(rr_class),
117				rdata: rdata.into(),
118				ttl,
119			})
120		});
121	}
122}
123
124/// Optional data when querying for a record; either use its default
125/// value or customize it like:
126///
127/// ```
128/// # use async_dnssd::QueryRecordData;
129/// # use async_dnssd::QueryRecordFlags;
130/// QueryRecordData {
131///     flags: QueryRecordFlags::LONG_LIVED_QUERY,
132///     ..Default::default()
133/// };
134/// ```
135#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
136pub struct QueryRecordData {
137	/// flags for query
138	pub flags: QueryRecordFlags,
139	/// interface to query records on
140	pub interface: Interface,
141	/// class of the resource record (default: `IN`)
142	pub rr_class: Class,
143	#[doc(hidden)]
144	pub _non_exhaustive: crate::non_exhaustive_struct::NonExhaustiveMarker,
145}
146
147impl Default for QueryRecordData {
148	fn default() -> Self {
149		Self {
150			flags: QueryRecordFlags::default(),
151			interface: Interface::default(),
152			rr_class: Class::IN,
153			_non_exhaustive: crate::non_exhaustive_struct::NonExhaustiveMarker,
154		}
155	}
156}
157
158fn _query_record_extended(
159	fullname: &str,
160	rr_type: Type,
161	data: QueryRecordData,
162) -> io::Result<QueryRecord> {
163	crate::init();
164
165	let fullname = cstr::CStr::from(&fullname)?;
166
167	let stream = CallbackStream::new(move |sender| {
168		inner::OwnedService::query_record(
169			data.flags.bits(),
170			data.interface.into_raw(),
171			&fullname,
172			rr_type,
173			data.rr_class,
174			Some(query_record_callback),
175			sender,
176		)
177	})
178	.into();
179
180	Ok(QueryRecord { stream })
181}
182
183/// Query for an arbitrary DNS record
184///
185/// See [`DNSServiceQueryRecord`](https://developer.apple.com/documentation/dnssd/1804747-dnsservicequeryrecord).
186#[doc(alias = "DNSServiceQueryRecord")]
187pub fn query_record_extended(fullname: &str, rr_type: Type, data: QueryRecordData) -> QueryRecord {
188	match _query_record_extended(fullname, rr_type, data) {
189		Ok(qr) => qr,
190		Err(e) => QueryRecord {
191			stream: Err(e).into(),
192		},
193	}
194}
195
196/// Query for an arbitrary DNS record
197///
198/// Uses [`query_record_extended`] with default [`QueryRecordData`].
199///
200/// See [`DNSServiceQueryRecord`](https://developer.apple.com/documentation/dnssd/1804747-dnsservicequeryrecord).
201#[doc(alias = "DNSServiceQueryRecord")]
202pub fn query_record(fullname: &str, rr_type: Type) -> QueryRecord {
203	query_record_extended(fullname, rr_type, QueryRecordData::default())
204}