Skip to main content

esp_idf_svc/thread/
srp.rs

1use core::ffi::{c_void, CStr};
2use core::fmt::{self, Display};
3use core::marker::PhantomData;
4use core::net::{Ipv6Addr, SocketAddrV6};
5use core::ptr::addr_of_mut;
6
7use ::log::{debug, info, trace, warn};
8
9use crate::sys::{
10    esp, esp_openthread_get_instance, otDnsTxtEntry, otError, otError_OT_ERROR_DUPLICATED,
11    otError_OT_ERROR_INVALID_ARGS, otError_OT_ERROR_NONE, otError_OT_ERROR_NO_BUFS, otIp6Address,
12    otIp6Address__bindgen_ty_1, otSrpClientAddService, otSrpClientClearHostAndServices,
13    otSrpClientClearService, otSrpClientEnableAutoStartMode, otSrpClientGetHostInfo,
14    otSrpClientGetServerAddress, otSrpClientGetServices, otSrpClientHostInfo,
15    otSrpClientIsAutoStartModeEnabled, otSrpClientIsRunning, otSrpClientItemState,
16    otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_ADDING,
17    otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_REFRESHING,
18    otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_REGISTERED,
19    otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_REMOVED,
20    otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_REMOVING,
21    otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_TO_ADD,
22    otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_TO_REFRESH,
23    otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_TO_REMOVE, otSrpClientRemoveHostAndServices,
24    otSrpClientRemoveService, otSrpClientService, otSrpClientSetHostAddresses,
25    otSrpClientSetHostName, otSrpClientStart, otSrpClientStop, otThreadErrorToString, EspError,
26    ESP_ERR_INVALID_STATE,
27};
28
29#[cfg(not(esp_idf_version_major = "4"))]
30use crate::sys::{
31    otSrpClientEnableAutoHostAddress, otSrpClientGetKeyLeaseInterval, otSrpClientGetLeaseInterval,
32    otSrpClientGetTtl, otSrpClientSetKeyLeaseInterval, otSrpClientSetLeaseInterval,
33    otSrpClientSetTtl,
34};
35
36use crate::thread::{ot_esp, ot_esp_err, EspThread, Mode, NetifMode, ThreadDriver};
37
38/// The unique ID of a registered SRP service
39pub type SrpServiceSlot = usize;
40
41/// An enum describing the status of either a concrete SRP service, or the SRP host.
42#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
43pub enum SrpState {
44    /// The service/host is to be added/registered.
45    ToAdd,
46    /// The service/host is being added/registered.
47    Adding,
48    /// The service/host is to be refreshed (re-register to renew lease).
49    ToRefresh,
50    /// The service/host is being refreshed.
51    Refreshing,
52    /// The service/host is to be removed/unregistered.
53    ToRemove,
54    /// The service/host is being removed/unregistered.
55    Removing,
56    /// The service/host has been removed/unregistered.
57    Removed,
58    /// The service/host is registered.
59    Registered,
60    /// Any other state.
61    Other(otSrpClientItemState),
62}
63
64impl Display for SrpState {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        match self {
67            Self::ToAdd => write!(f, "To add"),
68            Self::Adding => write!(f, "Adding"),
69            Self::ToRefresh => write!(f, "To refresh"),
70            Self::Refreshing => write!(f, "Refreshing"),
71            Self::ToRemove => write!(f, "To remove"),
72            Self::Removing => write!(f, "Removing"),
73            Self::Removed => write!(f, "Removed"),
74            Self::Registered => write!(f, "Registered"),
75            Self::Other(state) => write!(f, "Other ({state})"),
76        }
77    }
78}
79
80#[allow(non_upper_case_globals)]
81#[allow(non_snake_case)]
82impl From<otSrpClientItemState> for SrpState {
83    fn from(value: otSrpClientItemState) -> Self {
84        match value {
85            otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_TO_ADD => Self::ToAdd,
86            otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_ADDING => Self::Adding,
87            otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_TO_REFRESH => Self::ToRefresh,
88            otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_REFRESHING => Self::Refreshing,
89            otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_TO_REMOVE => Self::ToRemove,
90            otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_REMOVING => Self::Removing,
91            otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_REMOVED => Self::Removed,
92            otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_REGISTERED => Self::Registered,
93            other => Self::Other(other),
94        }
95    }
96}
97
98/// The SRP configuration of the OpenThread stack.
99#[derive(Debug, Clone, Eq, PartialEq, Hash)]
100pub struct SrpConf<'a> {
101    /// SRP hostname
102    pub host_name: &'a str,
103    /// SRP host Ipv6 addresses.
104    /// If empty, the SRP implementation will automatically set the host addresses
105    /// by itself, using non-link-local addresses, once these become available.
106    pub host_addrs: &'a [Ipv6Addr],
107    /// SRP TTL (Time To Live) value.
108    pub ttl: u32,
109    /// Default lease time for SRP services if they specify 0 for their lease time.
110    /// Set to 0 to use the OpenThread default value.
111    pub default_lease_secs: u32,
112    /// Default key lease time for SRP services' keys if they specify 0 for their key lease time.
113    /// Set to 0 to use the OpenThread default value.
114    pub default_key_lease_secs: u32,
115}
116
117impl SrpConf<'_> {
118    /// Create a new `SrpConf` instance, wuth a host named "ot-device",
119    /// no explicit host addresses, a TTL of 60 seconds, and default lease times.
120    pub const fn new() -> Self {
121        Self {
122            host_name: "ot-device",
123            host_addrs: &[],
124            ttl: 60,
125            default_lease_secs: 0,
126            default_key_lease_secs: 0,
127        }
128    }
129
130    fn store(&self, ot_srp: &mut otSrpClientHostInfo, buf: &mut [u8]) -> Result<(), EspError> {
131        let (addrs, buf) = align_min::<otIp6Address>(buf, self.host_addrs.len())?;
132
133        ot_srp.mName = store_str(self.host_name, buf)?.0.as_ptr();
134
135        for (index, ip) in self.host_addrs.iter().enumerate() {
136            let addr = &mut addrs[index];
137            addr.mFields.m8 = ip.octets();
138        }
139
140        ot_srp.mAddresses = if addrs.is_empty() {
141            core::ptr::null_mut()
142        } else {
143            addrs.as_ptr()
144        };
145        ot_srp.mNumAddresses = addrs.len() as _;
146
147        #[cfg(not(esp_idf_version_major = "4"))]
148        {
149            ot_srp.mAutoAddress = addrs.is_empty();
150        }
151
152        Ok(())
153    }
154}
155
156impl Default for SrpConf<'_> {
157    fn default() -> Self {
158        Self::new()
159    }
160}
161
162/// An SRP service that can be registered with the OpenThread stack.
163#[derive(Debug, Clone, Eq, PartialEq, Hash)]
164pub struct SrpService<'a, SI, TI> {
165    /// The service name.
166    pub name: &'a str,
167    /// The instance name.
168    pub instance_name: &'a str,
169    /// The subtype labels.
170    pub subtype_labels: SI,
171    /// The TXT entries.
172    pub txt_entries: TI,
173    /// The service port.
174    pub port: u16,
175    /// The service priority.
176    pub priority: u16,
177    /// The service weight.
178    pub weight: u16,
179    /// The service lease time in seconds.
180    /// Set to 0 to use the default value as specified in `SrpConf`.
181    pub lease_secs: u32,
182    /// The service key lease time in seconds.
183    /// Set to 0 to use the default value as specified in `SrpConf`.
184    pub key_lease_secs: u32,
185}
186
187impl<'a, SI, TI> SrpService<'a, SI, TI>
188where
189    SI: Iterator<Item = &'a str> + Clone + 'a,
190    TI: Iterator<Item = (&'a str, &'a [u8])> + Clone + 'a,
191{
192    fn store(&self, ot_srp: &mut otSrpClientService, buf: &mut [u8]) -> Result<(), EspError> {
193        let subtype_labels_len = self.subtype_labels.clone().count();
194        let txt_entries_len = self.txt_entries.clone().count();
195
196        let (txt_entries, buf) = align_min::<otDnsTxtEntry>(buf, txt_entries_len)?;
197        let (subtype_labels, buf) = align_min::<*const char>(buf, subtype_labels_len + 1)?;
198
199        let (name, buf) = store_str(self.name, buf)?;
200        let (instance_name, buf) = store_str(self.instance_name, buf)?;
201
202        ot_srp.mName = name.as_ptr();
203        ot_srp.mInstanceName = instance_name.as_ptr();
204
205        let mut index = 0;
206        let mut buf = buf;
207
208        for subtype_label in self.subtype_labels.clone() {
209            let (subtype_label, rem_buf) = store_str(subtype_label, buf)?;
210
211            subtype_labels[index] = subtype_label.as_ptr() as *const _;
212
213            buf = rem_buf;
214            index += 1;
215        }
216
217        subtype_labels[index] = core::ptr::null();
218
219        index = 0;
220
221        for (key, value) in self.txt_entries.clone() {
222            let txt_entry = &mut txt_entries[index];
223
224            let (key, rem_buf) = store_str(key, buf)?;
225            let (value, rem_buf) = store_data(value, rem_buf)?;
226
227            txt_entry.mKey = key.as_ptr();
228            txt_entry.mValue = value.as_ptr();
229            txt_entry.mValueLength = value.len() as _;
230
231            buf = rem_buf;
232            index += 1;
233        }
234
235        ot_srp.mSubTypeLabels = subtype_labels.as_ptr() as *const _;
236        ot_srp.mTxtEntries = txt_entries.as_ptr();
237        ot_srp.mNumTxtEntries = txt_entries_len as _;
238        ot_srp.mPort = self.port;
239        ot_srp.mPriority = self.priority;
240        ot_srp.mWeight = self.weight;
241        #[cfg(not(esp_idf_version_major = "4"))]
242        {
243            ot_srp.mLease = self.lease_secs;
244            ot_srp.mKeyLease = self.key_lease_secs;
245        }
246        ot_srp.mState = 0;
247        ot_srp.mNext = core::ptr::null_mut();
248
249        Ok(())
250    }
251}
252
253impl<'a, SI, TI> Display for SrpService<'a, SI, TI>
254where
255    SI: Iterator<Item = &'a str> + Clone,
256    TI: Iterator<Item = (&'a str, &'a [u8])> + Clone,
257{
258    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259        write!(
260            f,
261            "SRP Service {{name: {}, instance: {}, port: {}, priority: {}, weight: {}, lease: {}, keylease: {}, labels: [",
262            self.name,
263            self.instance_name,
264            self.port,
265            self.priority,
266            self.weight,
267            self.lease_secs,
268            self.key_lease_secs
269        )?;
270
271        for (index, label) in self.subtype_labels.clone().enumerate() {
272            if index > 0 {
273                write!(f, ", {label}")?;
274            } else {
275                write!(f, "{label}")?;
276            }
277        }
278
279        write!(f, "], txt: [")?;
280
281        for (index, value) in self.txt_entries.clone().enumerate() {
282            if index > 0 {
283                write!(f, ", {}: {:?}", value.0, value.1)?;
284            } else {
285                write!(f, "{}: {:?}", value.0, value.1)?;
286            }
287        }
288
289        write!(f, "]}}")
290    }
291}
292
293/// Type alias for an SRP service as returned by
294/// `OpenThread::srp_services`.
295pub type OutSrpService<'a> = SrpService<'a, OutSrpSubtypeLabelsIter<'a>, OutSrpTxtEntriesIter<'a>>;
296
297/// An iterator over the subtype labels of an SRP service
298/// as returned by `OpenThread::srp_services`.
299#[derive(Clone)]
300pub struct OutSrpSubtypeLabelsIter<'a> {
301    ptr: *const *const u8,
302    index: usize,
303    _phantom: PhantomData<&'a ()>,
304}
305
306impl<'a> Iterator for OutSrpSubtypeLabelsIter<'a> {
307    type Item = &'a str;
308
309    fn next(&mut self) -> Option<Self::Item> {
310        if self.ptr.is_null() {
311            return None;
312        }
313
314        let label = unsafe { *self.ptr.add(self.index) };
315
316        if label.is_null() {
317            None
318        } else {
319            self.index += 1;
320            Some(unsafe { CStr::from_ptr(label as _) }.to_str().unwrap())
321        }
322    }
323}
324
325/// An iterator over the TXT entries of an SRP service
326/// as returned by `OpenThread::srp_services`.
327#[derive(Clone)]
328pub struct OutSrpTxtEntriesIter<'a> {
329    ptr: *const otDnsTxtEntry,
330    size: usize,
331    index: usize,
332    _phantom: PhantomData<&'a ()>,
333}
334
335impl<'a> Iterator for OutSrpTxtEntriesIter<'a> {
336    type Item = (&'a str, &'a [u8]);
337
338    fn next(&mut self) -> Option<Self::Item> {
339        if self.ptr.is_null() || self.index == self.size {
340            return None;
341        }
342
343        let entry = unsafe { self.ptr.add(self.index) };
344
345        self.index += 1;
346
347        let entry = unsafe { &*entry };
348
349        Some((
350            unsafe { CStr::from_ptr(entry.mKey) }.to_str().unwrap(),
351            unsafe { core::slice::from_raw_parts(entry.mValue, entry.mValueLength as _) },
352        ))
353    }
354}
355
356impl<'a> From<&'a otSrpClientService> for OutSrpService<'a> {
357    fn from(ot_srp: &'a otSrpClientService) -> Self {
358        #[allow(unused_mut)]
359        let mut this = Self {
360            name: if !ot_srp.mName.is_null() {
361                unsafe { CStr::from_ptr(ot_srp.mName) }.to_str().unwrap()
362            } else {
363                ""
364            },
365            instance_name: if !ot_srp.mInstanceName.is_null() {
366                unsafe { CStr::from_ptr(ot_srp.mInstanceName) }
367                    .to_str()
368                    .unwrap()
369            } else {
370                ""
371            },
372            subtype_labels: OutSrpSubtypeLabelsIter {
373                ptr: ot_srp.mSubTypeLabels as _,
374                index: 0,
375                _phantom: PhantomData,
376            },
377            txt_entries: OutSrpTxtEntriesIter {
378                ptr: ot_srp.mTxtEntries,
379                size: ot_srp.mNumTxtEntries as _,
380                index: 0,
381                _phantom: PhantomData,
382            },
383            port: ot_srp.mPort,
384            priority: ot_srp.mPriority,
385            weight: ot_srp.mWeight,
386            lease_secs: 0,
387            key_lease_secs: 0,
388        };
389
390        #[cfg(not(esp_idf_version_major = "4"))]
391        {
392            this.lease_secs = ot_srp.mLease;
393            this.key_lease_secs = ot_srp.mKeyLease;
394        }
395
396        this
397    }
398}
399
400impl<T> ThreadDriver<'_, T>
401where
402    T: Mode,
403{
404    /// Return the current SRP client configuration and SRP client host state to the provided closure.
405    ///
406    /// Arguments:
407    /// - `f`: A closure that takes the SRP configuration and SRP host state as arguments.
408    pub fn srp_conf<F, R>(&self, f: F) -> Result<R, EspError>
409    where
410        F: FnOnce(&SrpConf, SrpState, bool) -> Result<R, EspError>,
411    {
412        let inner = self.inner();
413
414        let instance = unsafe { esp_openthread_get_instance() };
415
416        let info = unsafe { otSrpClientGetHostInfo(instance).as_ref() }.unwrap();
417
418        #[allow(unused_mut)]
419        let mut conf = SrpConf {
420            host_name: if !info.mName.is_null() {
421                unsafe { CStr::from_ptr(info.mName) }.to_str().unwrap()
422            } else {
423                ""
424            },
425            host_addrs: if info.mNumAddresses > 0 && !info.mAddresses.is_null() {
426                unsafe {
427                    core::slice::from_raw_parts(
428                        info.mAddresses as *const _,
429                        info.mNumAddresses as _,
430                    )
431                }
432            } else {
433                &[]
434            },
435            ttl: 0,
436            default_lease_secs: 0,
437            default_key_lease_secs: 0,
438        };
439
440        #[cfg(not(esp_idf_version_major = "4"))]
441        {
442            unsafe {
443                conf.ttl = otSrpClientGetTtl(instance);
444                conf.default_lease_secs = otSrpClientGetLeaseInterval(instance);
445                conf.default_key_lease_secs = otSrpClientGetKeyLeaseInterval(instance);
446            }
447        }
448
449        f(&conf, info.mState.into(), !inner.srp.conf_taken)
450    }
451
452    /// Return `true` if there is neither host, nor any service currently registered with the SRP client.
453    pub fn srp_is_empty(&self) -> Result<bool, EspError> {
454        let inner = self.inner();
455
456        Ok(!inner.srp.conf_taken && inner.srp.services.iter().all(|service| !service.taken))
457    }
458
459    /// Set the SRP client configuration.
460    ///
461    /// Arguments:
462    /// - `conf`: The SRP configuration.
463    ///
464    /// Returns:
465    /// - `Ok(())` if the configuration was set successfully.
466    /// - `Err(OtError)` if the configuration could not be set. One reason why the configuration setting
467    ///   might fail is if the configuration had already been set and then not removed with `srp_remove_all`.
468    pub fn srp_set_conf(&self, conf: &SrpConf) -> Result<(), EspError> {
469        let mut inner = self.inner();
470
471        let instance = unsafe { esp_openthread_get_instance() };
472
473        if inner.srp.conf_taken {
474            esp!(ESP_ERR_INVALID_STATE)?;
475        }
476
477        #[cfg(not(esp_idf_version_major = "4"))]
478        unsafe {
479            otSrpClientSetLeaseInterval(instance, conf.default_lease_secs);
480            otSrpClientSetKeyLeaseInterval(instance, conf.default_key_lease_secs);
481            otSrpClientSetTtl(instance, conf.ttl);
482        }
483
484        let mut srp_conf = otSrpClientHostInfo {
485            mName: core::ptr::null(),
486            mAddresses: core::ptr::null(),
487            mNumAddresses: 0,
488            #[cfg(not(esp_idf_version_major = "4"))]
489            mAutoAddress: true,
490            mState: 0,
491        };
492
493        conf.store(&mut srp_conf, &mut inner.srp.conf_buf)?;
494        inner.srp.conf_taken = true;
495
496        ot_esp!(unsafe { otSrpClientSetHostName(instance, srp_conf.mName) })?;
497
498        if !conf.host_addrs.is_empty() {
499            ot_esp!(unsafe {
500                otSrpClientSetHostAddresses(instance, srp_conf.mAddresses, srp_conf.mNumAddresses)
501            })?;
502        } else {
503            #[cfg(not(esp_idf_version_major = "4"))]
504            {
505                ot_esp!(unsafe { otSrpClientEnableAutoHostAddress(instance) })?;
506            }
507        }
508
509        Ok(())
510    }
511
512    /// Return `true` if the SRP client is running, `false` otherwise.
513    pub fn srp_running(&self) -> Result<bool, EspError> {
514        let _lock = self.inner();
515
516        Ok(unsafe { otSrpClientIsRunning(esp_openthread_get_instance()) })
517    }
518
519    /// Return `true` if the SRP client is in auto-start mode, `false` otherwise.
520    pub fn srp_autostart_enabled(&self) -> Result<bool, EspError> {
521        let _lock = self.inner();
522
523        Ok(unsafe { otSrpClientIsAutoStartModeEnabled(esp_openthread_get_instance()) })
524    }
525
526    /// Auto-starts the SRP client.
527    pub fn srp_autostart(&self) -> Result<(), EspError> {
528        let mut inner = self.inner();
529
530        let instance = unsafe { esp_openthread_get_instance() };
531
532        let srp = &mut inner.srp;
533
534        unsafe {
535            otSrpClientEnableAutoStartMode(
536                instance,
537                Some(OtSrp::plat_c_srp_auto_start_callback),
538                srp as *mut _ as *mut _,
539            );
540        }
541
542        Ok(())
543    }
544
545    /// Start the SRP client for the given SRP server address.
546    ///
547    /// Arguments:
548    /// - `server_addr`: The SRP server address.
549    pub fn srp_start(&self, server_addr: SocketAddrV6) -> Result<(), EspError> {
550        let _lock = self.inner();
551
552        ot_esp!(unsafe {
553            otSrpClientStart(esp_openthread_get_instance(), &to_ot_addr(&server_addr))
554        })
555    }
556
557    /// Stop the SRP client.
558    pub fn srp_stop(&self) -> Result<(), EspError> {
559        let _lock = self.inner();
560
561        unsafe {
562            otSrpClientStop(esp_openthread_get_instance());
563        }
564
565        Ok(())
566    }
567
568    /// Return the SRP server address, if the SRP client is running and
569    /// had connected to a server.
570    pub fn srp_server_addr(&self) -> Result<Option<SocketAddrV6>, EspError> {
571        let _lock = self.inner();
572
573        let addr =
574            unsafe { otSrpClientGetServerAddress(esp_openthread_get_instance()).as_ref() }.unwrap();
575        let addr = to_sock_addr(&addr.mAddress, addr.mPort, 0);
576
577        // OT documentation notes that if the SRP client is not running
578        // this will return the unspecified addr (0.0.0.0.0.0.0.0)
579        Ok((!addr.ip().is_unspecified()).then_some(addr))
580    }
581
582    /// Iterate over the SRP services registered with the SRP client.
583    ///
584    /// Arguments:
585    /// - `f`: A closure that receives a tuple of the next SRP service, SRP service state, and SRP service ID.
586    ///   If there are no more SRP services, the closure will receive `None`.
587    pub fn srp_services<F>(&self, mut f: F) -> Result<(), EspError>
588    where
589        F: FnMut(Option<(&OutSrpService<'_>, SrpState, SrpServiceSlot)>),
590    {
591        let inner = self.inner();
592
593        let mut service_ptr: *const otSrpClientService =
594            unsafe { otSrpClientGetServices(esp_openthread_get_instance()) };
595
596        while !service_ptr.is_null() {
597            let service = unsafe { &*service_ptr };
598
599            let slot = inner
600                .srp
601                .services
602                .iter()
603                .position(|s| core::ptr::eq(&s.service, service))
604                .unwrap();
605
606            f(Some((&service.into(), service.mState.into(), slot)));
607
608            service_ptr = service.mNext;
609        }
610
611        f(None);
612
613        Ok(())
614    }
615
616    /// Add an SRP service to the SRP client.
617    ///
618    /// Arguments:
619    /// - `service`: The SRP service to add.
620    ///
621    /// Returns:
622    /// - The SRP service slot, if the service was added successfully.
623    /// - `Err(OtError)` if the service could not be added. One reason why the service addition
624    ///   might fail is if there are no more slots available for services. This can happen even if all services
625    ///   had been removed, as the slots are not freed until the SRP client propagates the removal info to the SRP server.
626    pub fn srp_add_service<'a, SI, TI>(
627        &self,
628        service: &'a SrpService<'a, SI, TI>,
629    ) -> Result<SrpServiceSlot, EspError>
630    where
631        SI: Iterator<Item = &'a str> + Clone + 'a,
632        TI: Iterator<Item = (&'a str, &'a [u8])> + Clone + 'a,
633    {
634        let mut inner = self.inner();
635
636        let slot = inner.srp.services.iter().position(|service| !service.taken);
637
638        let Some(slot) = slot else {
639            return Err(ot_esp_err(otError_OT_ERROR_NO_BUFS));
640        };
641
642        let our_service = &mut inner.srp.services[slot];
643
644        service.store(&mut our_service.service, &mut our_service.buf)?;
645
646        ot_esp!(unsafe {
647            otSrpClientAddService(esp_openthread_get_instance(), &mut our_service.service)
648        })?;
649
650        debug!("Service added");
651
652        our_service.taken = true;
653
654        Ok(slot)
655    }
656
657    /// Remove an SRP service from the SRP client.
658    ///
659    /// Arguments:
660    /// - `slot`: The SRP service to remove.
661    /// - `immediate`: If `true`, the service will be removed immediately, otherwise, the service will be removed gracefully
662    ///   by propagating the removal info to the SRP server.
663    pub fn srp_remove_service(
664        &self,
665        slot: SrpServiceSlot,
666        immediate: bool,
667    ) -> Result<(), EspError> {
668        let mut inner = self.inner();
669
670        if slot >= inner.srp.services.len() || !inner.srp.services[slot].taken {
671            ot_esp!(otError_OT_ERROR_INVALID_ARGS)?;
672        }
673
674        let service = &mut inner.srp.services[slot];
675
676        if immediate {
677            ot_esp!(unsafe {
678                otSrpClientClearService(esp_openthread_get_instance(), &mut service.service)
679            })?;
680            service.taken = false;
681            debug!("Service {slot} cleared immeidately");
682        } else {
683            ot_esp!(unsafe {
684                otSrpClientRemoveService(esp_openthread_get_instance(), &mut service.service)
685            })?;
686            debug!("Service {slot} scheduled for removal");
687        }
688
689        Ok(())
690    }
691
692    /// Remove the SRP hostname and all SRP services from the SRP client.
693    ///
694    /// Arguments:
695    /// - `immediate`: If `true`, the hostname and services will be removed immediately, otherwise,
696    ///   the hostname and services will be removed gracefully by propagating the removal info to the SRP server.
697    pub fn srp_remove_all(&self, immediate: bool) -> Result<(), EspError> {
698        let mut inner = self.inner();
699
700        let instance = unsafe { esp_openthread_get_instance() };
701
702        if immediate {
703            unsafe {
704                otSrpClientClearHostAndServices(instance);
705            }
706
707            inner.srp.conf_taken = false;
708            for service in &mut inner.srp.services {
709                service.taken = false;
710            }
711
712            debug!("Hostname and all services cleared immediately");
713        } else {
714            ot_esp!(unsafe { otSrpClientRemoveHostAndServices(instance, false, true) })?;
715            debug!("Hostname and all services scheduled for removal");
716        }
717
718        Ok(())
719    }
720
721    // /// Wait for the SRP state to change.
722    // ///
723    // /// This method will wait forever if `OpenThread` is not instantiated with SRP.
724    // ///
725    // /// NOTE:
726    // /// It is not advised to call this method concurrently from multiple async tasks
727    // /// because it uses a single waker registration. Thus, while the method will not panic,
728    // /// the tasks will fight with each other by each re-registering its own waker, thus keeping the CPU constantly busy.
729    // TODO
730    // pub async fn srp_wait_changed(&self) {
731    //     if self.activate().state().srp().is_ok() {
732    //         poll_fn(move |cx| {
733    //             self.activate().state().srp.as_mut().unwrap()
734    //                 .changes
735    //                 .poll_wait(cx)
736    //         })
737    //         .await;
738    //     } else {
739    //         core::future::pending::<()>().await;
740    //     }
741    // }
742}
743
744impl<T> EspThread<'_, T>
745where
746    T: NetifMode,
747{
748    /// Return the current SRP client configuration and SRP client host state to the provided closure.
749    ///
750    /// Arguments:
751    /// - `f`: A closure that takes the SRP configuration and SRP host state as arguments.
752    pub fn srp_conf<F, R>(&self, f: F) -> Result<R, EspError>
753    where
754        F: FnOnce(&SrpConf, SrpState, bool) -> Result<R, EspError>,
755    {
756        self.driver().srp_conf(f)
757    }
758
759    /// Return `true` if there is neither host, nor any service currently registered with the SRP client.
760    pub fn srp_is_empty(&self) -> Result<bool, EspError> {
761        self.driver().srp_is_empty()
762    }
763
764    /// Set the SRP client configuration.
765    ///
766    /// Arguments:
767    /// - `conf`: The SRP configuration.
768    ///
769    /// Returns:
770    /// - `Ok(())` if the configuration was set successfully.
771    /// - `Err(OtError)` if the configuration could not be set. One reason why the configuration setting
772    ///   might fail is if the configuration had already been set and then not removed with `srp_remove_all`.
773    pub fn srp_set_conf(&self, conf: &SrpConf) -> Result<(), EspError> {
774        self.driver().srp_set_conf(conf)
775    }
776
777    /// Return `true` if the SRP client is running, `false` otherwise.
778    pub fn srp_running(&self) -> Result<bool, EspError> {
779        self.driver().srp_running()
780    }
781
782    /// Return `true` if the SRP client is in auto-start mode, `false` otherwise.
783    pub fn srp_autostart_enabled(&self) -> Result<bool, EspError> {
784        self.driver().srp_autostart_enabled()
785    }
786
787    /// Auto-starts the SRP client.
788    pub fn srp_autostart(&self) -> Result<(), EspError> {
789        self.driver().srp_autostart()
790    }
791
792    /// Start the SRP client for the given SRP server address.
793    ///
794    /// Arguments:
795    /// - `server_addr`: The SRP server address.
796    pub fn srp_start(&self, server_addr: SocketAddrV6) -> Result<(), EspError> {
797        self.driver().srp_start(server_addr)
798    }
799
800    /// Stop the SRP client.
801    pub fn srp_stop(&self) -> Result<(), EspError> {
802        self.driver().srp_stop()
803    }
804
805    /// Return the SRP server address, if the SRP client is running and
806    /// had connected to a server.
807    pub fn srp_server_addr(&self) -> Result<Option<SocketAddrV6>, EspError> {
808        self.driver().srp_server_addr()
809    }
810
811    /// Iterate over the SRP services registered with the SRP client.
812    ///
813    /// Arguments:
814    /// - `f`: A closure that receives a tuple of the next SRP service, SRP service state, and SRP service ID.
815    ///   If there are no more SRP services, the closure will receive `None`.
816    pub fn srp_services<F>(&self, f: F) -> Result<(), EspError>
817    where
818        F: FnMut(Option<(&OutSrpService<'_>, SrpState, SrpServiceSlot)>),
819    {
820        self.driver().srp_services(f)
821    }
822
823    /// Add an SRP service to the SRP client.
824    ///
825    /// Arguments:
826    /// - `service`: The SRP service to add.
827    ///
828    /// Returns:
829    /// - The SRP service slot, if the service was added successfully.
830    /// - `Err(OtError)` if the service could not be added. One reason why the service addition
831    ///   might fail is if there are no more slots available for services. This can happen even if all services
832    ///   had been removed, as the slots are not freed until the SRP client propagates the removal info to the SRP server.
833    pub fn srp_add_service<'a, SI, TI>(
834        &self,
835        service: &'a SrpService<'a, SI, TI>,
836    ) -> Result<SrpServiceSlot, EspError>
837    where
838        SI: Iterator<Item = &'a str> + Clone + 'a,
839        TI: Iterator<Item = (&'a str, &'a [u8])> + Clone + 'a,
840    {
841        self.driver().srp_add_service(service)
842    }
843
844    /// Remove an SRP service from the SRP client.
845    ///
846    /// Arguments:
847    /// - `slot`: The SRP service to remove.
848    /// - `immediate`: If `true`, the service will be removed immediately, otherwise, the service will be removed gracefully
849    ///   by propagating the removal info to the SRP server.
850    pub fn srp_remove_service(
851        &self,
852        slot: SrpServiceSlot,
853        immediate: bool,
854    ) -> Result<(), EspError> {
855        self.driver().srp_remove_service(slot, immediate)
856    }
857
858    /// Remove the SRP hostname and all SRP services from the SRP client.
859    ///
860    /// Arguments:
861    /// - `immediate`: If `true`, the hostname and services will be removed immediately, otherwise,
862    ///   the hostname and services will be removed gracefully by propagating the removal info to the SRP server.
863    pub fn srp_remove_all(&self, immediate: bool) -> Result<(), EspError> {
864        self.driver().srp_remove_all(immediate)
865    }
866
867    // /// Wait for the SRP state to change.
868    // ///
869    // /// This method will wait forever if `OpenThread` is not instantiated with SRP.
870    // ///
871    // /// NOTE:
872    // /// It is not advised to call this method concurrently from multiple async tasks
873    // /// because it uses a single waker registration. Thus, while the method will not panic,
874    // /// the tasks will fight with each other by each re-registering its own waker, thus keeping the CPU constantly busy.
875    // TODO
876    // pub async fn srp_wait_changed(&self) {
877    //     self.driver().srp_wait_changed().await
878    // }
879}
880
881// TODO: Make these configurable with a feature
882//
883// NOTE: A slot stays taken until the SRP client has propagated the service removal to
884// the SRP server, so the pool needs headroom over the number of services concurrently
885// published (a Matter node publishes one operational record per commissioned fabric,
886// plus a commissionable one while its commissioning window is open).
887const SRP_SVCS: usize = 6;
888const SRP_SVC_BUF_SIZE: usize = 300;
889const SRP_HOST_BUF_SIZE: usize = 300;
890
891pub(crate) struct OtSrp {
892    conf_taken: bool,
893    conf_buf: [u8; SRP_HOST_BUF_SIZE],
894    services: [OtSrpService; SRP_SVCS],
895}
896
897impl OtSrp {
898    pub(crate) unsafe fn init(this: *mut Self) {
899        unsafe {
900            addr_of_mut!((*this).conf_taken).write(false);
901            addr_of_mut!((*this).conf_buf).write_bytes(0, 1);
902
903            for index in 0..SRP_SVCS {
904                let service = addr_of_mut!((*this).services[index]);
905                OtSrpService::init(service);
906            }
907        }
908    }
909
910    /// Reclaims the slots of the SRP host and services that are reported as removed
911    fn cleanup(
912        &mut self,
913        host_info: &otSrpClientHostInfo,
914        mut removed_services: Option<&otSrpClientService>,
915    ) {
916        if host_info.mState == otSrpClientItemState_OT_SRP_CLIENT_ITEM_STATE_REMOVED {
917            self.conf_taken = false;
918            info!("SRP host removed");
919        }
920
921        while let Some(service) = removed_services {
922            let (slot, our_service) = self
923                .services
924                .iter_mut()
925                .enumerate()
926                .find(|(_, s)| core::ptr::eq(&s.service, service))
927                .unwrap();
928
929            removed_services = unsafe { service.mNext.as_ref() };
930
931            our_service.taken = false;
932            info!("SRP service at slot {slot} removed");
933        }
934    }
935
936    fn plat_srp_changed(
937        &mut self,
938        error: otError,
939        host_info: &otSrpClientHostInfo,
940        _services: Option<&otSrpClientService>,
941        removed_services: Option<&otSrpClientService>,
942    ) {
943        trace!("Plat SRP changed callback");
944
945        if error != otError_OT_ERROR_NONE {
946            // The SRP client keeps retrying on its own, but without this the failure
947            // is completely invisible (i.e. services silently never get published)
948            let reason = unsafe { CStr::from_ptr(otThreadErrorToString(error)) }
949                .to_str()
950                .unwrap_or("Unknown");
951
952            warn!(
953                "SRP update failed: {reason} ({error}); host is {}",
954                SrpState::from(host_info.mState)
955            );
956
957            if error == otError_OT_ERROR_DUPLICATED {
958                // The SRP server maps this from a `YXDOMAIN` response, which it returns
959                // when the host name - or one of the service instance names - is already
960                // registered there under a *different* ECDSA key. The client cannot
961                // recover on its own: it will keep retrying until the stale registration
962                // reaches the end of its key lease (14 days by default) or the server is
963                // told to drop it.
964                warn!(
965                    "SRP name is registered on the server under a different key; \
966                     the SRP key of this device changed, or another device claimed the name"
967                );
968            }
969        }
970
971        self.cleanup(host_info, removed_services);
972    }
973
974    fn plat_srp_auto_started(&mut self) {
975        // TODO - in future, consider if we need to signal the changes here
976    }
977
978    pub(crate) unsafe extern "C" fn plat_c_srp_state_change_callback(
979        error: otError,
980        host_info: *const crate::sys::otSrpClientHostInfo,
981        services: *const crate::sys::otSrpClientService,
982        removed_services: *const crate::sys::otSrpClientService,
983        context: *mut c_void,
984    ) {
985        let srp = context as *mut OtSrp;
986        let srp = unsafe { srp.as_mut() }.unwrap();
987
988        srp.plat_srp_changed(
989            error,
990            unsafe { &*host_info },
991            unsafe { services.as_ref() },
992            unsafe { removed_services.as_ref() },
993        );
994    }
995
996    pub(crate) unsafe extern "C" fn plat_c_srp_auto_start_callback(
997        _server_sock_addr: *const crate::sys::otSockAddr,
998        context: *mut c_void,
999    ) {
1000        let srp = context as *mut OtSrp;
1001        let srp = unsafe { srp.as_mut() }.unwrap();
1002
1003        srp.plat_srp_auto_started();
1004    }
1005}
1006
1007struct OtSrpService {
1008    taken: bool,
1009    service: otSrpClientService,
1010    buf: [u8; SRP_SVC_BUF_SIZE],
1011}
1012
1013impl OtSrpService {
1014    pub(crate) unsafe fn init(this: *mut Self) {
1015        unsafe {
1016            addr_of_mut!((*this).taken).write(false);
1017            addr_of_mut!((*this).buf).write_bytes(0, 1);
1018            addr_of_mut!((*this).service).write_bytes(0, 1);
1019        }
1020    }
1021}
1022
1023fn align_min<T>(buf: &mut [u8], count: usize) -> Result<(&mut [T], &mut [u8]), EspError> {
1024    if count == 0 || core::mem::size_of::<T>() == 0 {
1025        return Ok((&mut [], buf));
1026    }
1027
1028    let (t_leading_buf0, t_buf, _) = unsafe { buf.align_to_mut::<T>() };
1029    if t_buf.len() < count {
1030        ot_esp!(otError_OT_ERROR_NO_BUFS)?;
1031    }
1032
1033    // Shrink `t_buf` to the number of requested items (count)
1034    let t_buf = &mut t_buf[..count];
1035    let t_leading_buf0_len = t_leading_buf0.len();
1036    let t_buf_size = core::mem::size_of_val(t_buf);
1037
1038    let (buf0, remaining_buf) = buf.split_at_mut(t_leading_buf0_len + t_buf_size);
1039
1040    let (t_leading_buf, t_buf, t_remaining_buf) = unsafe { buf0.align_to_mut::<T>() };
1041    assert_eq!(t_leading_buf0_len, t_leading_buf.len());
1042    assert_eq!(t_buf.len(), count);
1043    assert!(t_remaining_buf.is_empty());
1044
1045    Ok((t_buf, remaining_buf))
1046}
1047
1048fn store_str<'t>(str: &str, buf: &'t mut [u8]) -> Result<(&'t CStr, &'t mut [u8]), EspError> {
1049    let data_len = str.len() + 1;
1050
1051    if data_len > buf.len() {
1052        ot_esp!(otError_OT_ERROR_NO_BUFS)?;
1053    }
1054
1055    let (str_buf, rem_buf) = buf.split_at_mut(data_len);
1056
1057    str_buf[..str.len()].copy_from_slice(str.as_bytes());
1058    str_buf[str.len()] = 0;
1059
1060    Ok((
1061        CStr::from_bytes_with_nul(&str_buf[..data_len]).unwrap(),
1062        rem_buf,
1063    ))
1064}
1065
1066fn store_data<'t>(data: &[u8], buf: &'t mut [u8]) -> Result<(&'t [u8], &'t mut [u8]), EspError> {
1067    if data.len() > buf.len() {
1068        ot_esp!(otError_OT_ERROR_NO_BUFS)?;
1069    }
1070
1071    let (data_buf, rem_buf) = buf.split_at_mut(data.len());
1072
1073    data_buf[..data.len()].copy_from_slice(data);
1074
1075    Ok((data_buf, rem_buf))
1076}
1077
1078/// Convert an `otIp6Address`, port and network interface ID to a `SocketAddrV6`.
1079fn to_sock_addr(addr: &otIp6Address, port: u16, netif: u32) -> SocketAddrV6 {
1080    SocketAddrV6::new(Ipv6Addr::from(unsafe { addr.mFields.m8 }), port, 0, netif)
1081}
1082
1083/// Convert a `SocketAddrV6` to an `otSockAddr`.
1084fn to_ot_addr(addr: &SocketAddrV6) -> crate::sys::otSockAddr {
1085    crate::sys::otSockAddr {
1086        mAddress: otIp6Address {
1087            mFields: otIp6Address__bindgen_ty_1 {
1088                m8: addr.ip().octets(),
1089            },
1090        },
1091        mPort: addr.port(),
1092    }
1093}