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 #[derive(Default)]
31 pub struct QueryRecordFlags: ffi::DNSServiceFlags {
32 const LONG_LIVED_QUERY = ffi::FLAGS_LONG_LIVED_QUERY;
36 }
37}
38
39bitflags::bitflags! {
40 #[derive(Default)]
42 pub struct QueriedRecordFlags: ffi::DNSServiceFlags {
43 const MORE_COMING = ffi::FLAGS_MORE_COMING;
48
49 const ADD = ffi::FLAGS_ADD;
54 }
55}
56
57#[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#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
76pub struct QueryRecordResult {
77 pub flags: QueriedRecordFlags,
79 pub interface: Interface,
81 pub fullname: String,
83 pub rr_type: Type,
85 pub rr_class: Class,
87 pub rdata: Vec<u8>,
89 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#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
136pub struct QueryRecordData {
137 pub flags: QueryRecordFlags,
139 pub interface: Interface,
141 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#[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#[doc(alias = "DNSServiceQueryRecord")]
202pub fn query_record(fullname: &str, rr_type: Type) -> QueryRecord {
203 query_record_extended(fullname, rr_type, QueryRecordData::default())
204}