async_dnssd/service/
connection.rs

1use futures_util::FutureExt;
2use std::{
3	future::Future,
4	io,
5	os::raw::c_void,
6	pin::Pin,
7	task::{
8		Context,
9		Poll,
10	},
11};
12
13use crate::{
14	cstr,
15	dns_consts::{
16		Class,
17		Type,
18	},
19	ffi,
20	inner,
21	interface::Interface,
22};
23
24type CallbackFuture = crate::future::ServiceFuture<inner::SharedService, RegisterRecordResult>;
25
26/// Connection to register records with
27pub struct Connection(inner::SharedService);
28
29/// Create [`Connection`](struct.Connection.html) to register records
30/// with
31///
32/// See [`DNSServiceCreateConnection`](https://developer.apple.com/documentation/dnssd/1804724-dnsservicecreateconnection).
33#[doc(alias = "DNSServiceCreateConnection")]
34pub fn connect() -> io::Result<Connection> {
35	crate::init();
36
37	Ok(Connection(inner::SharedService::create_connection()?))
38}
39
40bitflags::bitflags! {
41	/// Flags used to register a record
42	#[derive(Default)]
43	pub struct RegisterRecordFlags: ffi::DNSServiceFlags {
44		/// Indicates there might me multiple records with the given name, type and class.
45		///
46		/// See [`kDNSServiceFlagsShared`](https://developer.apple.com/documentation/dnssd/1823436-anonymous/kdnsserviceflagsshared).
47		const SHARED = ffi::FLAGS_SHARED;
48
49		/// Indicates the records with the given name, type and class is unique.
50		///
51		/// See [`kDNSServiceFlagsUnique`](https://developer.apple.com/documentation/dnssd/1823436-anonymous/kdnsserviceflagsunique).
52		const UNIQUE = ffi::FLAGS_UNIQUE;
53	}
54}
55
56/// Pending record registration
57///
58/// Becomes invalid when the future completes; use the returned
59/// [`Record`](struct.Record.html) instead.
60// the future gets canceled by dropping the record; must
61// not drop the future without dropping the record.
62#[must_use = "futures do nothing unless polled"]
63pub struct RegisterRecord {
64	future: CallbackFuture,
65	record: Option<crate::Record>,
66}
67
68impl RegisterRecord {
69	pin_utils::unsafe_pinned!(future: CallbackFuture);
70
71	pin_utils::unsafe_unpinned!(record: Option<crate::Record>);
72}
73
74impl Future for RegisterRecord {
75	type Output = io::Result<crate::Record>;
76
77	fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
78		futures_core::ready!(self.as_mut().future().poll(cx))?;
79		Poll::Ready(Ok(self.record().take().unwrap()))
80	}
81}
82
83#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
84struct RegisterRecordResult;
85
86unsafe extern "C" fn register_record_callback(
87	_sd_ref: ffi::DNSServiceRef,
88	_record_ref: ffi::DNSRecordRef,
89	_flags: ffi::DNSServiceFlags,
90	error_code: ffi::DNSServiceErrorType,
91	context: *mut c_void,
92) {
93	CallbackFuture::run_callback(context, error_code, || Ok(RegisterRecordResult));
94}
95
96/// Optional data when registering a record; either use its default
97/// value or customize it like:
98///
99/// ```
100/// # use async_dnssd::RegisterRecordData;
101/// RegisterRecordData {
102///     ttl: 60,
103///     ..Default::default()
104/// };
105/// ```
106#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
107pub struct RegisterRecordData {
108	/// flags for registration
109	pub flags: RegisterRecordFlags,
110	/// interface to register record on
111	pub interface: Interface,
112	/// class of the resource record (default: `IN`)
113	pub rr_class: Class,
114	/// time to live of the resource record in seconds (passing 0 will
115	/// select a sensible default)
116	pub ttl: u32,
117	#[doc(hidden)]
118	pub _non_exhaustive: crate::non_exhaustive_struct::NonExhaustiveMarker,
119}
120
121impl Default for RegisterRecordData {
122	fn default() -> Self {
123		Self {
124			flags: RegisterRecordFlags::default(),
125			interface: Interface::default(),
126			rr_class: Class::IN,
127			ttl: 0,
128			_non_exhaustive: crate::non_exhaustive_struct::NonExhaustiveMarker,
129		}
130	}
131}
132
133impl Connection {
134	/// Register record on interface with given name, type, class, rdata
135	/// and ttl
136	///
137	/// See [`DNSServiceRegisterRecord`](https://developer.apple.com/documentation/dnssd/1804727-dnsserviceregisterrecord).
138	#[doc(alias = "DNSServiceRegisterRecord")]
139	pub fn register_record_extended(
140		&self,
141		fullname: &str,
142		rr_type: Type,
143		rdata: &[u8],
144		data: RegisterRecordData,
145	) -> io::Result<RegisterRecord> {
146		let fullname = cstr::CStr::from(&fullname)?;
147
148		let (future, record) = CallbackFuture::new_with(self.0.clone(), move |sender| {
149			self.0.clone().register_record(
150				data.flags.bits(),
151				data.interface.into_raw(),
152				&fullname,
153				rr_type,
154				data.rr_class,
155				rdata,
156				data.ttl,
157				Some(register_record_callback),
158				sender,
159			)
160		})?;
161
162		Ok(RegisterRecord {
163			future,
164			record: Some(record.into()),
165		})
166	}
167
168	/// Register record on interface with given name, type, class, rdata
169	/// and ttl
170	///
171	/// Uses [`register_record_extended`] with default [`RegisterRecordData`].
172	///
173	/// See [`DNSServiceRegisterRecord`](https://developer.apple.com/documentation/dnssd/1804727-dnsserviceregisterrecord).
174	///
175	/// [`register_record_extended`]: fn.register_record_extended.html
176	/// [`RegisterRecordData`]: struct.RegisterRecordData.html
177	#[doc(alias = "DNSServiceRegisterRecord")]
178	pub fn register_record(
179		&self,
180		fullname: &str,
181		rr_type: Type,
182		rdata: &[u8],
183	) -> io::Result<RegisterRecord> {
184		self.register_record_extended(fullname, rr_type, rdata, RegisterRecordData::default())
185	}
186}
187
188impl RegisterRecord {
189	fn inner_record(&self) -> &crate::Record {
190		self.record.as_ref().expect("RegisterRecord future is done")
191	}
192
193	/// Type of the record
194	///
195	/// # Panics
196	///
197	/// Panics after the future completed.  Use the returned
198	/// [`Record`](struct.Record.html) instead.
199	pub fn rr_type(&self) -> Type {
200		self.inner_record().rr_type()
201	}
202
203	/// Update record
204	///
205	/// Cannot change type or class of record.
206	///
207	/// # Panics
208	///
209	/// Panics after the future completed.  Use the returned
210	/// [`Record`](struct.Record.html) instead.
211	///
212	/// See [`DNSServiceUpdateRecord`](https://developer.apple.com/documentation/dnssd/1804739-dnsserviceupdaterecord).
213	#[doc(alias = "DNSServiceUpdateRecord")]
214	pub fn update_record(&self, rdata: &[u8], ttl: u32) -> io::Result<()> {
215		self.inner_record().update_record(rdata, ttl)
216	}
217
218	/// Keep record for as long as the underlying connection lives.
219	///
220	/// Keep the a handle to the underlying connection (either the
221	/// [`Connection`](struct.Connection.html) or some other record from
222	/// the same `Connection`) alive.
223	///
224	/// Due to some implementation detail the underlying connection
225	/// might live until this future successfully completes.
226	///
227	/// # Panics
228	///
229	/// Panics after the future completed.  Use the returned
230	/// [`Record`](struct.Record.html) instead.
231	// - implementation detail: this drives the future to continuation,
232	//   it is not possible to drop the (shared) underlying service
233	//   before. instead we could store the callback context with the
234	//   underyling service, and drop it either when dropping the
235	//   service or the callback was called.
236	pub fn keep(self) {
237		let (fut, rec) = (
238			self.future,
239			self.record.expect("RegisterRecord future is done"),
240		);
241		// drive future to continuation, ignore errors
242		tokio::spawn(fut.map(|_| ()));
243		rec.keep();
244	}
245}