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