1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * resource.rs - Types for converting between libcoap and Rust data structures.
4
 * This file is part of the libcoap-rs crate, see the README and LICENSE files for
5
 * more information and terms of use.
6
 * Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved.
7
 * See the README as well as the LICENSE file for more information.
8
 */
9

            
10
//! Types required for conversion between libcoap C library abstractions and Rust types.
11

            
12
use std::ffi::CString;
13
use std::fmt::{Display, Formatter};
14
use std::marker::PhantomPinned;
15
use std::pin::Pin;
16
use std::{
17
    fmt::Debug,
18
    mem::MaybeUninit,
19
    net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs},
20
    os::raw::c_int,
21
    str::FromStr,
22
};
23

            
24
use libc::{c_ushort, in6_addr, in_addr, sa_family_t, sockaddr_in, sockaddr_in6, socklen_t, AF_INET, AF_INET6};
25
use num_derive::FromPrimitive;
26
use num_traits::FromPrimitive;
27
#[cfg(feature = "url")]
28
use url::Url;
29

            
30
use libcoap_sys::coap_uri_scheme_t::{COAP_URI_SCHEME_COAPS_WS, COAP_URI_SCHEME_COAP_WS};
31
use libcoap_sys::{
32
    coap_address_t, coap_delete_optlist, coap_mid_t, coap_proto_t,
33
    coap_proto_t::{COAP_PROTO_DTLS, COAP_PROTO_NONE, COAP_PROTO_TCP, COAP_PROTO_TLS, COAP_PROTO_UDP},
34
    coap_split_proxy_uri, coap_split_uri, coap_str_const_t, coap_string_equal, coap_uri_into_options,
35
    coap_uri_scheme_t,
36
    coap_uri_scheme_t::{
37
        COAP_URI_SCHEME_COAP, COAP_URI_SCHEME_COAPS, COAP_URI_SCHEME_COAPS_TCP, COAP_URI_SCHEME_COAP_TCP,
38
        COAP_URI_SCHEME_HTTP, COAP_URI_SCHEME_HTTPS,
39
    },
40
    coap_uri_t, COAP_URI_SCHEME_SECURE_MASK,
41
};
42

            
43
use crate::context::ensure_coap_started;
44
use crate::error::UriParsingError;
45
use crate::message::CoapOption;
46
use crate::protocol::UriPort;
47

            
48
/// Interface index used internally by libcoap to refer to an endpoint.
49
pub type IfIndex = c_int;
50
/// Value for maximum retransmits.
51
pub type MaxRetransmit = c_ushort;
52
/// Identifier for a CoAP message.
53
pub type CoapMessageId = coap_mid_t;
54

            
55
/// Internal wrapper for the raw coap_address_t type, mainly used for conversion between types.
56
pub(crate) struct CoapAddress(coap_address_t);
57

            
58
impl CoapAddress {
59
    /// Returns a reference to the underlying raw [coap_address_t].
60
466
    pub(crate) fn as_raw_address(&self) -> &coap_address_t {
61
466
        &self.0
62
466
    }
63

            
64
    /// Returns a mutable reference to the underlying [coap_address_t].
65
    ///
66
    /// Because there are some invariants that must be kept with regards to the underlying
67
    /// [coap_address_t], this function is unsafe.
68
    /// If you want to get the coap_address_t safely, use [into_raw_address()](CoapAddress::into_raw_address()).
69
    ///
70
    /// # Safety
71
    /// The underlying [coap_address_t] must always refer to a valid instance of sockaddr_in or
72
    /// sockaddr_in6, and [coap_address_t::size] must always be the correct size of the sockaddr
73
    /// in the [coap_address_t::addr] field.
74
    // Kept for consistency
75
    #[allow(dead_code)]
76
    pub(crate) unsafe fn as_mut_raw_address(&mut self) -> &mut coap_address_t {
77
        &mut self.0
78
    }
79

            
80
    /// Converts this address into the corresponding raw [coap_address_t](libcoap_sys::coap_address_t)
81
    // Kept for consistency
82
    #[allow(dead_code)]
83
    pub(crate) fn into_raw_address(self) -> coap_address_t {
84
        self.0
85
    }
86
}
87

            
88
impl ToSocketAddrs for CoapAddress {
89
    type Iter = std::option::IntoIter<SocketAddr>;
90

            
91
    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
92
        // SAFETY: That the underlying value of addr is a valid sockaddr is an invariant, the only
93
        // way the value could be invalid is if as_mut_coap_address_t() (an unsafe function) is used
94
        // incorrectly.
95
        let socketaddr = match unsafe { self.0.addr.sa.as_ref().sa_family } as i32 {
96
            AF_INET => {
97
                // SAFETY: Validity of addr is an invariant, and we checked that the type of the
98
                // underlying sockaddr is actually sockaddr_in.
99
                let raw_addr = unsafe { self.0.addr.sin.as_ref() };
100
                SocketAddrV4::new(
101
                    Ipv4Addr::from(raw_addr.sin_addr.s_addr.to_ne_bytes()),
102
                    u16::from_be(raw_addr.sin_port),
103
                )
104
                .into()
105
            },
106
            AF_INET6 => {
107
                // SAFETY: Validity of addr is an invariant, and we checked that the type of the
108
                // underlying sockaddr is actually sockaddr_in6.
109
                let raw_addr = unsafe { self.0.addr.sin6.as_ref() };
110
                SocketAddrV6::new(
111
                    Ipv6Addr::from(raw_addr.sin6_addr.s6_addr),
112
                    u16::from_be(raw_addr.sin6_port),
113
                    raw_addr.sin6_flowinfo,
114
                    raw_addr.sin6_scope_id,
115
                )
116
                .into()
117
            },
118
            // This should not happen as long as the invariants are kept.
119
            _ => panic!("sa_family_t of underlying coap_address_t is invalid!"),
120
        };
121
        Ok(Some(socketaddr).into_iter())
122
    }
123
}
124

            
125
impl From<SocketAddr> for CoapAddress {
126
466
    fn from(addr: SocketAddr) -> Self {
127
466
        match addr {
128
            SocketAddr::V4(addr) => {
129
                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
130
                // to use a pointer instead.
131
                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
132
                unsafe {
133
                    let mut coap_addr = coap_address_t {
134
                        size: std::mem::size_of::<sockaddr_in>() as socklen_t,
135
                        addr: std::mem::zeroed(),
136
                    };
137

            
138
                    *coap_addr.addr.sin.as_mut() = sockaddr_in {
139
                        #[cfg(any(
140
                            target_os = "freebsd",
141
                            target_os = "dragonfly",
142
                            target_os = "openbsd",
143
                            target_os = "netbsd",
144
                            target_os = "aix",
145
                            target_os = "haiku",
146
                            target_os = "hurd",
147
                            target_os = "espidf",
148
                        ))]
149
                        sin_len: (std::mem::size_of::<sockaddr_in>() as u8),
150
                        sin_family: AF_INET as sa_family_t,
151
                        sin_port: addr.port().to_be(),
152
                        sin_addr: in_addr {
153
                            s_addr: u32::from_ne_bytes(addr.ip().octets()),
154
                        },
155
                        sin_zero: Default::default(),
156
                    };
157
                    CoapAddress(coap_addr)
158
                }
159
            },
160
466
            SocketAddr::V6(addr) => {
161
466
                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
162
466
                // to use a pointer instead.
163
466
                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
164
466
                unsafe {
165
466
                    let mut coap_addr = coap_address_t {
166
466
                        size: std::mem::size_of::<sockaddr_in6>() as socklen_t,
167
466
                        addr: std::mem::zeroed(),
168
466
                    };
169
466

            
170
466
                    *coap_addr.addr.sin6.as_mut() = sockaddr_in6 {
171
466
                        #[cfg(any(
172
466
                            target_os = "freebsd",
173
466
                            target_os = "dragonfly",
174
466
                            target_os = "openbsd",
175
466
                            target_os = "netbsd",
176
466
                            target_os = "aix",
177
466
                            target_os = "haiku",
178
466
                            target_os = "hurd",
179
466
                            target_os = "espidf",
180
466
                        ))]
181
466
                        sin6_len: (std::mem::size_of::<sockaddr_in6>() as u8),
182
466
                        sin6_family: AF_INET6 as sa_family_t,
183
466
                        sin6_port: addr.port().to_be(),
184
466
                        sin6_addr: in6_addr {
185
466
                            s6_addr: addr.ip().octets(),
186
466
                        },
187
466
                        sin6_flowinfo: addr.flowinfo(),
188
466
                        sin6_scope_id: addr.scope_id(),
189
466
                    };
190
466

            
191
466
                    CoapAddress(coap_addr)
192
                }
193
            },
194
        }
195
466
    }
196
}
197

            
198
#[doc(hidden)]
199
impl From<coap_address_t> for CoapAddress {
200
    fn from(raw_addr: coap_address_t) -> Self {
201
        CoapAddress(raw_addr)
202
    }
203
}
204

            
205
#[doc(hidden)]
206
impl From<&coap_address_t> for CoapAddress {
207
    fn from(raw_addr: &coap_address_t) -> Self {
208
        let mut new_addr = MaybeUninit::zeroed();
209
        unsafe {
210
            std::ptr::copy_nonoverlapping(raw_addr, new_addr.as_mut_ptr(), 1);
211
            CoapAddress(new_addr.assume_init())
212
        }
213
    }
214
}
215

            
216
/// Representation for a URI scheme that can be used in CoAP (proxy) requests.
217
#[repr(u32)]
218
175
#[derive(Copy, Clone, FromPrimitive, Debug, PartialEq, Eq, Hash)]
219
pub enum CoapUriScheme {
220
    Coap = COAP_URI_SCHEME_COAP as u32,
221
    Coaps = COAP_URI_SCHEME_COAPS as u32,
222
    CoapTcp = COAP_URI_SCHEME_COAP_TCP as u32,
223
    CoapsTcp = COAP_URI_SCHEME_COAPS_TCP as u32,
224
    Http = COAP_URI_SCHEME_HTTP as u32,
225
    Https = COAP_URI_SCHEME_HTTPS as u32,
226
    CoapWs = COAP_URI_SCHEME_COAP_WS as u32,
227
    CoapsWs = COAP_URI_SCHEME_COAPS_WS as u32,
228
}
229

            
230
impl CoapUriScheme {
231
    pub fn is_secure(self) -> bool {
232
        COAP_URI_SCHEME_SECURE_MASK & (self as u32) > 0
233
    }
234

            
235
175
    pub fn from_raw_scheme(scheme: coap_uri_scheme_t) -> CoapUriScheme {
236
175
        FromPrimitive::from_u32(scheme as u32).expect("unknown scheme")
237
175
    }
238
}
239

            
240
impl FromStr for CoapUriScheme {
241
    type Err = UriParsingError;
242

            
243
    fn from_str(s: &str) -> Result<Self, Self::Err> {
244
        match s {
245
            "coap" => Ok(CoapUriScheme::Coap),
246
            "coaps" => Ok(CoapUriScheme::Coaps),
247
            "coap+tcp" => Ok(CoapUriScheme::CoapTcp),
248
            "coaps+tcp" => Ok(CoapUriScheme::CoapsTcp),
249
            "http" => Ok(CoapUriScheme::Http),
250
            "https" => Ok(CoapUriScheme::Https),
251
            "coap+ws" => Ok(CoapUriScheme::CoapWs),
252
            "coaps+ws" => Ok(CoapUriScheme::CoapsWs),
253
            _ => Err(UriParsingError::NotACoapScheme(s.to_string())),
254
        }
255
    }
256
}
257

            
258
impl Display for CoapUriScheme {
259
70
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
260
70
        f.write_str(match self {
261
70
            CoapUriScheme::Coap => "coap",
262
            CoapUriScheme::Coaps => "coaps",
263
            CoapUriScheme::CoapTcp => "coap+tcp",
264
            CoapUriScheme::CoapsTcp => "coaps+tcp",
265
            CoapUriScheme::Http => "http",
266
            CoapUriScheme::Https => "https",
267
            CoapUriScheme::CoapWs => "coap+ws",
268
            CoapUriScheme::CoapsWs => "coaps+ws",
269
        })
270
70
    }
271
}
272

            
273
impl From<coap_uri_scheme_t> for CoapUriScheme {
274
    fn from(scheme: coap_uri_scheme_t) -> Self {
275
        CoapUriScheme::from_raw_scheme(scheme)
276
    }
277
}
278

            
279
impl From<CoapProtocol> for CoapUriScheme {
280
233
    fn from(value: CoapProtocol) -> Self {
281
233
        match value {
282
35
            CoapProtocol::None | CoapProtocol::Udp => CoapUriScheme::Coap,
283
163
            CoapProtocol::Dtls => CoapUriScheme::Coaps,
284
35
            CoapProtocol::Tcp => CoapUriScheme::CoapTcp,
285
            CoapProtocol::Tls => CoapUriScheme::CoapsTcp,
286
        }
287
233
    }
288
}
289

            
290
/// Representation of a URI for CoAP requests, responses or proxy URIs.
291
///
292
/// See https://datatracker.ietf.org/doc/html/rfc7252#section-6 for a description of how a URI
293
/// should look like.
294
///
295
/// # Examples
296
/// The easiest way to instantiate a request or location CoAP URI is by parsing a string (either
297
/// using the [FromStr] implementation or using [CoapUri::try_from_str]):
298
/// ```
299
4
/// use libcoap_rs::error::UriParsingError;
300
/// use libcoap_rs::types::{CoapUri, CoapUriScheme};
301
///
302
/// let uri: CoapUri = "coap://example.com:4711/foo/bar?answer=42".parse()?;
303
1
///
304
/// assert_eq!(uri.scheme(), Some(CoapUriScheme::Coap));
305
1
/// assert_eq!(uri.host(), Some("example.com".as_bytes()));
306
1
/// assert_eq!(uri.port(), Some(4711));
307
1
/// assert_eq!(uri.path(), Some("foo/bar".as_bytes()));
308
1
/// assert_eq!(uri.query(), Some("answer=42".as_bytes()));
309
1
/// assert!(!uri.is_proxy());
310
1
///
311
/// # Result::<(), UriParsingError>::Ok(())
312
1
/// ```
313
4
///
314
/// Alternatively, a [CoapUri] may be constructed from its parts using [CoapUri::new] or
315
/// [CoapUri::new_relative] or from a [Url] (requires the `url` feature), refer to the method level
316
/// documentation for more information.
317
///
318
/// If you want to create a proxy URI, refer to the method-level documentation [CoapUri::new_proxy],
319
/// [CoapUri::try_from_str_proxy] or [CoapUri::try_from_url_proxy].
320
///
321
/// # Note on URI Length Limits
322
///
323
/// Due to [the specified limits](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10)
324
/// of CoAP option lengths, the URI path components, query components, and hostnames for a URI must not
325
/// exceed 255 bytes each, i.e. a full path with more than 255 bytes is fine, but each individual
326
/// path segment must be smaller than 255 bytes.
327
///
328
/// For proxy URIs, there is a length limit of 255 bytes for the scheme.
329
/// As we use the Uri-* options for encoding proxy URIs instead of the Proxy-Uri option (as
330
/// specified in [RFC 7252, section 5.10.2](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.2)),
331
/// the above limits regarding path and query components also apply here.
332
#[derive(Debug)]
333
pub struct CoapUri {
334
    is_proxy: bool,
335
    raw_uri: coap_uri_t,
336
    uri_str: Pin<Box<CoapUriInner>>,
337
}
338

            
339
#[derive(Debug)]
340
struct CoapUriInner(CString, PhantomPinned);
341

            
342
impl CoapUri {
343
    /// Creates a new [CoapUri] for use as a request or location URI from its constituent parts.
344
    ///
345
    /// # Errors
346
    /// May fail if the provided fields do not represent a valid relative URI or if the arguments
347
    /// exceed maximum lengths (see the struct level documentation).
348
    ///
349
    /// # Examples
350
    /// ```
351
4
    /// use libcoap_rs::error::UriParsingError;
352
    /// use libcoap_rs::types::{CoapUri, CoapUriScheme};
353
    ///
354
    /// let uri: CoapUri = CoapUri::new(
355
1
    ///     CoapUriScheme::Coap,
356
1
    ///     "example.com".as_bytes(),
357
1
    ///     4711,
358
1
    ///     Some("/foo/bar".as_bytes()),
359
1
    ///     Some("?answer=42".as_bytes())
360
1
    /// )?;
361
1
    ///
362
    /// assert_eq!(uri.scheme(), Some(CoapUriScheme::Coap));
363
1
    /// assert_eq!(uri.host(), Some("example.com".as_bytes()));
364
1
    /// assert_eq!(uri.port(), Some(4711));
365
1
    /// assert_eq!(uri.path(), Some("foo/bar".as_bytes()));
366
1
    /// assert_eq!(uri.query(), Some("answer=42".as_bytes()));
367
1
    /// assert!(!uri.is_proxy());
368
1
    ///
369
    /// # Result::<(), UriParsingError>::Ok(())
370
1
    /// ```
371
303
    pub fn new(
372
303
        scheme: CoapUriScheme,
373
303
        host: &[u8],
374
303
        port: u16,
375
303
        path: Option<&[u8]>,
376
303
        query: Option<&[u8]>,
377
303
    ) -> Result<CoapUri, UriParsingError> {
378
303
        let (uri_str, _, _, _) =
379
303
            Self::construct_uri_string_from_parts(scheme, host, port, path.unwrap_or(&[b'/']), query.unwrap_or(&[]))?;
380
        // SAFETY: coap_split_uri is one of the allowed functions.
381
303
        unsafe { CoapUri::create_parsed_uri(uri_str, coap_split_uri, false) }
382
303
    }
383

            
384
    /// Creates a new [CoapUri] for use as a proxy URI from its constituent parts.
385
    ///
386
    /// # Errors
387
    /// May fail if the provided fields do not represent a valid relative URI or if the arguments
388
    /// exceed maximum lengths (see the struct level documentation).
389
    /// # Examples
390
    /// ```
391
4
    /// use libcoap_rs::error::UriParsingError;
392
    /// use libcoap_rs::types::{CoapUri, CoapUriScheme};
393
    ///
394
    /// let uri: CoapUri = CoapUri::new_proxy(
395
1
    ///     CoapUriScheme::Coap,
396
1
    ///     "example.com".as_bytes(),
397
1
    ///     4711,
398
1
    ///     Some("/foo/bar".as_bytes()),
399
1
    ///     Some("?answer=42".as_bytes())
400
1
    /// )?;
401
1
    ///
402
    /// assert_eq!(uri.scheme(), Some(CoapUriScheme::Coap));
403
1
    /// assert_eq!(uri.host(), Some("example.com".as_bytes()));
404
1
    /// assert_eq!(uri.port(), Some(4711));
405
1
    /// assert_eq!(uri.path(), Some("foo/bar".as_bytes()));
406
1
    /// assert_eq!(uri.query(), Some("answer=42".as_bytes()));
407
1
    /// assert!(uri.is_proxy());
408
1
    ///
409
    /// # Result::<(), UriParsingError>::Ok(())
410
1
    /// ```
411
38
    pub fn new_proxy(
412
35
        scheme: CoapUriScheme,
413
35
        host: &[u8],
414
35
        port: u16,
415
35
        path: Option<&[u8]>,
416
35
        query: Option<&[u8]>,
417
35
    ) -> Result<CoapUri, UriParsingError> {
418
35
        let (uri_str, _, _, _) =
419
35
            Self::construct_uri_string_from_parts(scheme, host, port, path.unwrap_or(&[b'/']), query.unwrap_or(&[]))?;
420
        // SAFETY: coap_split_proxy_uri is one of the allowed functions.
421
35
        unsafe { CoapUri::create_parsed_uri(uri_str, coap_split_proxy_uri, true) }
422
35
    }
423

            
424
    /// Attempts to convert the provided `path` and `query` into a relative [CoapUri] suitable as a
425
    /// request/location URI.
426
    ///
427
    /// # Errors
428
    /// May fail if the provided `path` and `query` do not represent a valid relative URI or if the
429
    /// arguments exceed maximum lengths (see the struct level documentation).
430
    ///
431
    /// # Examples
432
    /// ```
433
4
    /// use libcoap_rs::error::UriParsingError;
434
    /// use libcoap_rs::types::{CoapUri, CoapUriScheme};
435
    ///
436
    /// let uri: CoapUri = CoapUri::new_relative(
437
1
    ///     Some("/foo/bar".as_bytes()),
438
1
    ///     Some("?answer=42".as_bytes())
439
1
    /// )?;
440
1
    ///
441
    /// assert_eq!(uri.scheme(), None);
442
1
    /// assert_eq!(uri.host(), None);
443
1
    /// assert_eq!(uri.port(), Some(5683));
444
1
    /// assert_eq!(uri.path(), Some("foo/bar".as_bytes()));
445
1
    /// assert_eq!(uri.query(), Some("answer=42".as_bytes()));
446
1
    /// assert!(!uri.is_proxy());
447
1
    ///
448
    /// # Result::<(), UriParsingError>::Ok(())
449
1
    /// ```
450
38
    pub fn new_relative(path: Option<&[u8]>, query: Option<&[u8]>) -> Result<CoapUri, UriParsingError> {
451
35
        CoapUri::new(CoapUriScheme::Coap, &[], 0, path, query)
452
35
    }
453

            
454
    /// Attempts to convert the provided `uri_str` into a [CoapUri] suitable as a request/location
455
    /// URI.
456
    ///
457
    /// # Errors
458
    /// May fail if the provided `uri_str` is not a valid URI or if the URI components exceed
459
    /// maximum lengths (see the struct level documentation).
460
    ///
461
    /// # Examples
462
    /// ```
463
4
    /// use libcoap_rs::error::UriParsingError;
464
    /// use libcoap_rs::types::{CoapUri, CoapUriScheme};
465
    ///
466
    /// let uri: CoapUri = CoapUri::try_from_str("coap://example.com:4711/foo/bar?answer=42")?;
467
1
    ///
468
    /// assert_eq!(uri.scheme(), Some(CoapUriScheme::Coap));
469
1
    /// assert_eq!(uri.host(), Some("example.com".as_bytes()));
470
1
    /// assert_eq!(uri.port(), Some(4711));
471
1
    /// assert_eq!(uri.path(), Some("foo/bar".as_bytes()));
472
1
    /// assert_eq!(uri.query(), Some("answer=42".as_bytes()));
473
1
    /// assert!(!uri.is_proxy());
474
1
    ///
475
    /// # Result::<(), UriParsingError>::Ok(())
476
1
    /// ```
477
303
    pub fn try_from_str(uri_str: &str) -> Result<CoapUri, UriParsingError> {
478
303
        // SAFETY: coap_split_uri is one of the allowed functions.
479
303
        unsafe { CoapUri::create_parsed_uri(CString::new(uri_str)?, coap_split_uri, false) }
480
303
    }
481

            
482
    /// Attempts to convert the provided `uri_str` into a [CoapUri] suitable as a proxy URI.
483
    ///
484
    /// # Errors
485
    /// May fail if the provided `uri_str` is not a valid proxy URI or if the URI components exceed
486
    /// maximum lengths (see the struct level documentation).
487
    ///
488
    /// # Examples
489
    /// ```
490
4
    /// use libcoap_rs::error::UriParsingError;
491
    /// use libcoap_rs::types::{CoapUri, CoapUriScheme};
492
    ///
493
    /// let uri: CoapUri = CoapUri::try_from_str_proxy("coap://example.com:4711/foo/bar?answer=42")?;
494
1
    ///
495
    /// assert_eq!(uri.scheme(), Some(CoapUriScheme::Coap));
496
1
    /// assert_eq!(uri.host(), Some("example.com".as_bytes()));
497
1
    /// assert_eq!(uri.port(), Some(4711));
498
1
    /// assert_eq!(uri.path(), Some("foo/bar".as_bytes()));
499
1
    /// assert_eq!(uri.query(), Some("answer=42".as_bytes()));
500
1
    /// assert!(uri.is_proxy());
501
1
    ///
502
    /// # Result::<(), UriParsingError>::Ok(())
503
1
    /// ```
504
38
    pub fn try_from_str_proxy(uri_str: &str) -> Result<CoapUri, UriParsingError> {
505
35
        // SAFETY: coap_split_proxy_uri is one of the allowed functions.
506
35
        unsafe { CoapUri::create_parsed_uri(CString::new(uri_str)?, coap_split_proxy_uri, true) }
507
35
    }
508

            
509
    /// Attempts to convert a [Url] into a [CoapUri].
510
    ///
511
    /// # Errors
512
    /// May fail if the provided Url is not a valid URI supported by libcoap or if the URI
513
    /// components exceed maximum lengths (see the struct level documentation).
514
    #[cfg(feature = "url")]
515
    pub fn try_from_url(url: &Url) -> Result<CoapUri, UriParsingError> {
516
        Self::try_from_str(url.as_str())
517
    }
518

            
519
    /// Attempts to convert a [Url] into a proxy [CoapUri].
520
    ///
521
    /// # Errors
522
    /// May fail if the provided Url is not a valid proxy URI supported by libcoap or if the URI
523
    /// components exceed maximum lengths (see the struct level documentation).
524
    #[cfg(feature = "url")]
525
    pub fn try_from_url_proxy(url: &Url) -> Result<CoapUri, UriParsingError> {
526
        Self::try_from_str_proxy(url.as_str())
527
    }
528

            
529
    /// Returns the scheme part of this URI.
530
210
    pub fn scheme(&self) -> Option<CoapUriScheme> {
531
210
        // URIs can either be absolute or relative. If they are relative, the scheme is also not
532
210
        // set (but defaults to CoAP as the default enum value is 0).
533
210
        self.host()?;
534
175
        Some(CoapUriScheme::from_raw_scheme(self.raw_uri.scheme))
535
210
    }
536

            
537
    /// Returns the host part of this URI.
538
420
    pub fn host(&self) -> Option<&[u8]> {
539
420
        let raw_str = self.raw_uri.host;
540
420
        if raw_str.length == 0 {
541
70
            return None;
542
350
        }
543
350
        // SAFETY: After construction the fields of self.raw_uri always reference the corresponding
544
350
        //         parts of the underlying string, which is pinned. Therefore, the pointer and
545
350
        //         length are valid for the lifetime of this struct.
546
350
        Some(unsafe { std::slice::from_raw_parts(raw_str.s, raw_str.length) })
547
420
    }
548

            
549
    /// Returns the port of this URI (if provided).
550
210
    pub fn port(&self) -> Option<UriPort> {
551
210
        match self.raw_uri.port {
552
            0 => None,
553
210
            v => Some(v),
554
        }
555
210
    }
556

            
557
    /// Returns the URI path part of this URI.
558
210
    pub fn path(&self) -> Option<&[u8]> {
559
210
        let raw_str = self.raw_uri.path;
560
210
        if raw_str.s.is_null() {
561
            return None;
562
210
        }
563
210
        // SAFETY: After construction the fields of self.raw_uri always reference the corresponding
564
210
        //         parts of the underlying string, which is pinned. Therefore, the pointer and
565
210
        //         length are valid for the lifetime of this struct.
566
210
        Some(unsafe { std::slice::from_raw_parts(raw_str.s, raw_str.length) })
567
210
    }
568

            
569
    /// Returns the host part of this URI.
570
210
    pub fn query(&self) -> Option<&[u8]> {
571
210
        let raw_str = self.raw_uri.query;
572
210
        if raw_str.s.is_null() {
573
            return None;
574
210
        }
575
210
        // SAFETY: After construction the fields of self.raw_uri always reference the corresponding
576
210
        //         parts of the underlying string, which is pinned. Therefore, the pointer and
577
210
        //         length are valid for the lifetime of this struct.
578
210
        Some(unsafe { std::slice::from_raw_parts(raw_str.s, raw_str.length) })
579
210
    }
580

            
581
    /// Returns whether this URI is a proxy URI.
582
443
    pub fn is_proxy(&self) -> bool {
583
443
        self.is_proxy
584
443
    }
585

            
586
    /// Converts the given URI into a `Vec` of [CoapOption]s that can be added to a
587
    /// [crate::message::CoapMessage].
588
233
    pub fn into_options(self) -> Vec<CoapOption> {
589
233
        // TODO this is a lot of copying around, however, fixing that would require an entire
590
233
        //      rewrite of the option handling code, so it's better kept for a separate PR.
591
233

            
592
233
        // Set size of temporary buffer for option storage.
593
233
        // TODO remove when updating minimum libcoap version to 4.3.5, as this buffer is no longer
594
233
        //      used there.
595
233
        #[cfg(coap_uri_buf_unused)]
596
233
        let mut buf = [];
597
233
        #[cfg(not(coap_uri_buf_unused))]
598
233
        let mut buf = {
599
233
            let buf_len =
600
233
                // Length of UriHost option (length of host + max. 5 bytes of option header)
601
233
                self.host().map(|v| v.len()+5).unwrap_or(0)
602
233
                // Length of UriPort option (max. 2 bytes for the port number + max. 5 bytes of
603
233
                // option header)
604
233
                + self.port().map(|v| 7).unwrap_or(0)
605
233
                // Length of path segment
606
233
                + self.path().map(|v| v.len()).unwrap_or(0)
607
233
                // Length of option headers for path segments.
608
233
                // Each path segment has its own header, which can be up to 5 bytes in size.
609
233
                + self.path().map(|v| (v.iter().filter(|c| **c as char == '/').count()+1)*5).unwrap_or(0)
610
233
                // Length of query segment
611
233
                + self.query().map(|v| v.len()).unwrap_or(0)
612
233
                // Length of option headers for query segments.
613
233
                // Each query segment has its own header, which can be up to 5 bytes in size.
614
233
                + self.query().map(|v| (v.iter().filter(|c| **c as char == '?' || **c as char == '&').count()+1)*5).unwrap_or(0);
615
233
            vec![0u8; buf_len]
616
233
        };
617
233

            
618
233
        let mut optlist = std::ptr::null_mut();
619
233
        // SAFETY: self.raw_uri is always valid after construction. The destination may be a null
620
233
        //         pointer, optlist may be a null pointer at the start (it will be set to a valid
621
233
        //         pointer by this call). Buf and create_port_host_opt are set according to the
622
233
        //         libcoap documentation.
623
233
        if unsafe {
624
233
            coap_uri_into_options(
625
233
                &self.raw_uri,
626
233
                std::ptr::null(),
627
233
                &mut optlist,
628
233
                1,
629
233
                buf.as_mut_ptr(),
630
233
                buf.len(),
631
233
            )
632
233
        } < 0
633
        {
634
            // We have already parsed this URI. If converting it into options fails, something went
635
            // terribly wrong.
636
            panic!("could not convert valid coap URI into options");
637
233
        }
638
233
        let mut out_opts = Vec::new();
639
466
        while !optlist.is_null() {
640
233
            // SAFETY: coap_uri_into_options should have ensured that optlist is either null or a
641
233
            //         valid coap option list. In the former case, we wouldn't be in this loop, in
642
233
            //         the latter case calling from_optlist_entry is fine.
643
233
            out_opts.push(unsafe {
644
233
                CoapOption::from_optlist_entry(optlist.as_ref().expect("self-generated options should always be valid"))
645
233
                    .expect("self-generated options should always be valid")
646
233
            });
647
233
            optlist = unsafe { *optlist }.next;
648
233
        }
649
        // SAFETY: optlist has been set by coap_uri_into_options, which has not returned an error.
650
233
        unsafe {
651
233
            coap_delete_optlist(optlist);
652
233
        }
653
233
        drop(self);
654
233
        out_opts
655
233
    }
656

            
657
    /// Provides a reference to the raw [coap_uri_t] struct represented by this [CoapUri].
658
    ///
659
    /// Note that while obtaining this struct and reading the fields is safe (which is why this
660
    /// method is safe), modifying the referenced URI parts by (unsafely) dereferencing and mutating
661
    /// the `const` pointers inside is not.
662
    pub fn as_raw_uri(&self) -> &coap_uri_t {
663
        &self.raw_uri
664
    }
665

            
666
    /// Converts the given `raw_uri` to a new [CoapUri] instance.
667
    ///
668
    /// This method will create a copy of the provided URI, i.e. `raw_uri` will remain valid and not
669
    /// be owned by the created [CoapUri] instance.
670
    ///
671
    /// # Safety
672
    ///
673
    /// The provided `raw_uri` must point to a valid instance of [coap_uri_t].
674
    /// In particular, the provided pointers for the URI components must also be valid.
675
    ///
676
    /// # Panics
677
    ///
678
    /// Panics if the provided `raw_uri` is null or the provided URI contains a null byte.
679
    pub unsafe fn from_raw_uri(raw_uri: *const coap_uri_t, is_proxy: bool) -> CoapUri {
680
        // Loosely based on coap_clone_uri.
681
        assert!(!raw_uri.is_null());
682
        let host_slice = (*raw_uri)
683
            .host
684
            .s
685
            .is_null()
686
            .then_some(&[] as &[u8])
687
            .unwrap_or_else(|| std::slice::from_raw_parts((*raw_uri).host.s, (*raw_uri).host.length));
688
        let path_slice = (*raw_uri)
689
            .path
690
            .s
691
            .is_null()
692
            .then_some(&[] as &[u8])
693
            .unwrap_or_else(|| std::slice::from_raw_parts((*raw_uri).path.s, (*raw_uri).path.length));
694
        let query_slice = (*raw_uri)
695
            .query
696
            .s
697
            .is_null()
698
            .then_some(&[] as &[u8])
699
            .unwrap_or_else(|| std::slice::from_raw_parts((*raw_uri).query.s, (*raw_uri).query.length));
700
        // Clone the actual URI string.
701
        let (uri_str_copy, host_pos, path_pos, query_pos) = Self::construct_uri_string_from_parts(
702
            CoapUriScheme::from_raw_scheme((*raw_uri).scheme),
703
            host_slice,
704
            (*raw_uri).port,
705
            path_slice,
706
            query_slice,
707
        )
708
        .expect("provided raw URI is invalid");
709

            
710
        let mut result = CoapUri::create_unparsed_uri(
711
            CString::new(uri_str_copy).expect("provided raw_uri contains null bytes!"),
712
            is_proxy,
713
        );
714
        result.raw_uri.port = (*raw_uri).port;
715
        result.raw_uri.scheme = (*raw_uri).scheme;
716
        // Now, _after_ the uri_str is pinned, we can set the new object's raw_uri string fields.
717
        result.raw_uri.host = coap_str_const_t {
718
            length: (*raw_uri).host.length,
719
            s: result.uri_str.0.as_bytes_with_nul()[host_pos..host_pos + 1].as_ptr(),
720
        };
721
        result.raw_uri.path = coap_str_const_t {
722
            length: (*raw_uri).path.length,
723
            s: result.uri_str.0.as_bytes_with_nul()[path_pos..path_pos + 1].as_ptr(),
724
        };
725
        result.raw_uri.query = coap_str_const_t {
726
            length: (*raw_uri).query.length,
727
            s: result.uri_str.0.as_bytes_with_nul()[query_pos..query_pos + 1].as_ptr(),
728
        };
729

            
730
        result
731
    }
732

            
733
    /// Create an instance of [CoapUri] with the given `uri_str`, but don't parse the value, i.e.
734
    /// the resulting `raw_uri` is not set correctly.
735
676
    fn create_unparsed_uri(uri_str: CString, is_proxy: bool) -> Self {
736
676
        let uri_str = Box::pin(CoapUriInner(uri_str, PhantomPinned));
737
676
        CoapUri {
738
676
            raw_uri: coap_uri_t {
739
676
                host: coap_str_const_t {
740
676
                    length: 0,
741
676
                    s: std::ptr::null(),
742
676
                },
743
676
                port: 0,
744
676
                path: coap_str_const_t {
745
676
                    length: 0,
746
676
                    s: std::ptr::null(),
747
676
                },
748
676
                query: coap_str_const_t {
749
676
                    length: 0,
750
676
                    s: std::ptr::null(),
751
676
                },
752
676
                scheme: coap_uri_scheme_t::COAP_URI_SCHEME_COAP,
753
676
            },
754
676
            uri_str,
755
676
            is_proxy,
756
676
        }
757
676
    }
758

            
759
    /// Create and parse a URI from a CString.
760
    ///
761
    /// # Safety
762
    ///
763
    /// parsing_fn must be either coap_split_uri or coap_split_proxy_uri.
764
676
    unsafe fn create_parsed_uri(
765
676
        uri_str: CString,
766
676
        parsing_fn: unsafe extern "C" fn(*const u8, usize, *mut coap_uri_t) -> c_int,
767
676
        is_proxy: bool,
768
676
    ) -> Result<CoapUri, UriParsingError> {
769
676
        ensure_coap_started();
770
676
        let mut uri = Self::create_unparsed_uri(uri_str, is_proxy);
771
676

            
772
676
        // SAFETY: The provided pointers to raw_uri and uri_str are valid.
773
676
        // Because uri_str is pinned (and its type is not Unpin), the pointer locations are always
774
676
        // valid while this object lives, therefore the resulting coap_uri_t remains valid for the
775
676
        // entire lifetime of this object too.
776
676
        if unsafe {
777
676
            parsing_fn(
778
676
                uri.uri_str.0.as_ptr() as *const u8,
779
676
                libc::strlen(uri.uri_str.0.as_ptr()),
780
676
                std::ptr::from_mut(&mut uri.raw_uri),
781
676
            )
782
676
        } < 0
783
        {
784
            return Err(UriParsingError::Unknown);
785
676
        }
786
676
        Ok(uri)
787
676
    }
788

            
789
    /// Constructs a CString representing the given URI parts in a form parsable by libcoap.
790
338
    fn construct_uri_string_from_parts(
791
338
        scheme: CoapUriScheme,
792
338
        host: &[u8],
793
338
        port: u16,
794
338
        path: &[u8],
795
338
        query: &[u8],
796
338
    ) -> Result<(CString, usize, usize, usize), UriParsingError> {
797
        // Reconstruct string for scheme.
798
338
        let scheme = if !host.is_empty() {
799
70
            format!("{}://", scheme)
800
        } else {
801
268
            String::new()
802
        };
803
338
        let port = if port != 0 { format!(":{}", port) } else { String::new() };
804
338
        let parts = [scheme.as_bytes(), host, port.as_bytes(), path, query];
805
1690
        let uri_str_len = parts.iter().map(|v| v.len()).sum::<usize>();
806
338

            
807
338
        let mut uri_str_copy = vec![0u8; uri_str_len];
808
338
        let mut cur;
809
338
        let mut rest = uri_str_copy.as_mut_slice();
810
1690
        for part in parts.iter() {
811
1690
            (cur, rest) = rest.split_at_mut(part.len());
812
1690
            cur.clone_from_slice(part)
813
        }
814

            
815
        // The host is index 1 in the parts list
816
338
        let host_pos = parts[..1].iter().map(|v| v.len()).sum();
817
338
        // The path is index 3 in the parts list
818
1014
        let path_pos = parts[..3].iter().map(|v| v.len()).sum();
819
338
        // The query is index 4 in the parts list
820
1352
        let query_pos = parts[..4].iter().map(|v| v.len()).sum();
821
338

            
822
338
        CString::new(uri_str_copy)
823
338
            .map(|v| (v, host_pos, path_pos, query_pos))
824
338
            .map_err(UriParsingError::from)
825
338
    }
826
}
827

            
828
impl PartialEq for CoapUri {
829
    fn eq(&self, other: &Self) -> bool {
830
        self.raw_uri.port == other.raw_uri.port
831
            && self.raw_uri.scheme == other.raw_uri.scheme
832
            // SAFETY: After construction the fields of self.raw_uri always reference the
833
            //         corresponding parts of the underlying string, which is pinned. Therefore, the
834
            //         pointer and length are valid for the lifetime of this struct.
835
            && unsafe {
836
                coap_string_equal!(&self.raw_uri.host, &other.raw_uri.host)
837
                    && coap_string_equal!(&self.raw_uri.path, &other.raw_uri.path)
838
                    && coap_string_equal!(&self.raw_uri.query, &other.raw_uri.query)
839
            }
840
    }
841
}
842

            
843
impl Eq for CoapUri {}
844

            
845
impl Clone for CoapUri {
846
    fn clone(&self) -> Self {
847
        // SAFETY: raw_uri is a valid pointer to a coap_uri_t (by construction of this type and
848
        // contract of from_raw_uri)
849
        unsafe { CoapUri::from_raw_uri(&self.raw_uri, self.is_proxy) }
850
    }
851
}
852

            
853
#[cfg(feature = "url")]
854
impl TryFrom<&Url> for CoapUri {
855
    type Error = UriParsingError;
856

            
857
    fn try_from(value: &Url) -> Result<Self, Self::Error> {
858
        CoapUri::try_from_url(value)
859
    }
860
}
861

            
862
impl FromStr for CoapUri {
863
    type Err = UriParsingError;
864

            
865
268
    fn from_str(s: &str) -> Result<Self, Self::Err> {
866
268
        Self::try_from_str(s)
867
268
    }
868
}
869

            
870
impl Display for CoapUri {
871
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
872
        self.uri_str.fmt(f)
873
    }
874
}
875

            
876
/// Transport protocols that can be used with libcoap.
877
#[repr(u32)]
878
#[non_exhaustive]
879
233
#[derive(Copy, Clone, FromPrimitive, PartialEq, Eq, Hash)]
880
pub enum CoapProtocol {
881
    None = COAP_PROTO_NONE as u32,
882
    Udp = COAP_PROTO_UDP as u32,
883
    Dtls = COAP_PROTO_DTLS as u32,
884
    Tcp = COAP_PROTO_TCP as u32,
885
    Tls = COAP_PROTO_TLS as u32,
886
}
887

            
888
impl CoapProtocol {
889
    pub fn is_secure(&self) -> bool {
890
        match self {
891
            CoapProtocol::None | CoapProtocol::Udp | CoapProtocol::Tcp => false,
892
            CoapProtocol::Dtls | CoapProtocol::Tls => true,
893
        }
894
    }
895
}
896

            
897
#[doc(hidden)]
898
impl From<coap_proto_t> for CoapProtocol {
899
233
    fn from(raw_proto: coap_proto_t) -> Self {
900
233
        <CoapProtocol as FromPrimitive>::from_u32(raw_proto as u32).expect("unknown protocol")
901
233
    }
902
}
903

            
904
impl Display for CoapProtocol {
905
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
906
        f.write_str(match self {
907
            CoapProtocol::None => "none",
908
            CoapProtocol::Udp => "udp",
909
            CoapProtocol::Dtls => "dtls",
910
            CoapProtocol::Tcp => "tcp",
911
            CoapProtocol::Tls => "tls",
912
        })
913
    }
914
}
915

            
916
fn convert_to_fixed_size_slice(n: usize, val: &[u8]) -> Box<[u8]> {
917
    if val.len() > n {
918
        panic!("supplied slice too short");
919
    }
920
    let mut buffer: Vec<u8> = vec![0; n];
921
    let (_, target_buffer) = buffer.split_at_mut(n - val.len());
922
    target_buffer.copy_from_slice(val);
923
    buffer.truncate(n);
924
    buffer.into_boxed_slice()
925
}
926

            
927
// TODO the following functions should probably return a result and use generics.
928
pub(crate) fn decode_var_len_u32(val: &[u8]) -> u32 {
929
    u32::from_be_bytes(
930
        convert_to_fixed_size_slice(4, val)[..4]
931
            .try_into()
932
            .expect("could not convert from variable sized value to fixed size number as the lengths don't match"),
933
    )
934
}
935

            
936
pub(crate) fn encode_var_len_u32(val: u32) -> Box<[u8]> {
937
    // I really hope that rust accounts for endianness here.
938
    let bytes_to_discard = val.leading_zeros() / 8;
939
    let mut ret_val = Vec::from(val.to_be_bytes());
940
    ret_val.drain(..bytes_to_discard as usize);
941
    ret_val.into_boxed_slice()
942
}
943

            
944
// Kept for consistency
945
#[allow(unused)]
946
pub(crate) fn decode_var_len_u64(val: &[u8]) -> u64 {
947
    u64::from_be_bytes(
948
        convert_to_fixed_size_slice(8, val)[..8]
949
            .try_into()
950
            .expect("could not convert from variable sized value to fixed size number as the lengths don't match"),
951
    )
952
}
953

            
954
// Kept for consistency
955
#[allow(unused)]
956
pub(crate) fn encode_var_len_u64(val: u64) -> Box<[u8]> {
957
    // I really hope that rust accounts for endianness here.
958
    let bytes_to_discard = val.leading_zeros() / 8;
959
    let mut ret_val = Vec::from(val.to_be_bytes());
960
    ret_val.drain(..bytes_to_discard as usize);
961
    ret_val.into_boxed_slice()
962
}
963

            
964
pub(crate) fn decode_var_len_u16(val: &[u8]) -> u16 {
965
    u16::from_be_bytes(
966
        convert_to_fixed_size_slice(2, val)[..2]
967
            .try_into()
968
            .expect("could not convert from variable sized value to fixed size number as the lengths don't match"),
969
    )
970
}
971

            
972
pub(crate) fn encode_var_len_u16(val: u16) -> Box<[u8]> {
973
    // I really hope that rust accounts for endianness here.
974
    let bytes_to_discard = val.leading_zeros() / 8;
975
    let mut ret_val = Vec::from(val.to_be_bytes());
976
    ret_val.drain(..bytes_to_discard as usize);
977
    ret_val.into_boxed_slice()
978
}
979

            
980
pub(crate) fn decode_var_len_u8(val: &[u8]) -> u16 {
981
    u16::from_be_bytes(
982
        convert_to_fixed_size_slice(1, val)[..1]
983
            .try_into()
984
            .expect("could not convert from variable sized value to fixed size number as the lengths don't match"),
985
    )
986
}
987

            
988
pub(crate) fn encode_var_len_u8(val: u8) -> Box<[u8]> {
989
    Vec::from([val]).into_boxed_slice()
990
}