1use std::fmt::{Display, Formatter};
13use std::{
14 convert::Infallible,
15 fmt::Debug,
16 mem::MaybeUninit,
17 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs},
18 os::raw::c_int,
19 slice::Iter,
20 str::FromStr,
21 vec::Drain,
22};
23
24use libc::{c_ushort, in6_addr, in_addr, sa_family_t, sockaddr_in, sockaddr_in6, socklen_t, AF_INET, AF_INET6};
25use num_derive::FromPrimitive;
26use num_traits::FromPrimitive;
27use url::{Host, Url};
28
29use libcoap_sys::{
30 coap_address_t, coap_mid_t, coap_proto_t,
31 coap_proto_t::{COAP_PROTO_DTLS, COAP_PROTO_NONE, COAP_PROTO_TCP, COAP_PROTO_TLS, COAP_PROTO_UDP},
32 coap_uri_scheme_t,
33 coap_uri_scheme_t::{
34 COAP_URI_SCHEME_COAP, COAP_URI_SCHEME_COAPS, COAP_URI_SCHEME_COAPS_TCP, COAP_URI_SCHEME_COAP_TCP,
35 COAP_URI_SCHEME_HTTP, COAP_URI_SCHEME_HTTPS,
36 },
37 COAP_URI_SCHEME_SECURE_MASK,
38};
39
40use crate::error::UriParsingError;
41
42pub type IfIndex = c_int;
44pub type MaxRetransmit = c_ushort;
46pub type CoapMessageId = coap_mid_t;
48
49pub(crate) struct CoapAddress(coap_address_t);
51
52impl CoapAddress {
53 pub(crate) fn as_raw_address(&self) -> &coap_address_t {
55 &self.0
56 }
57
58 #[allow(dead_code)]
70 pub(crate) unsafe fn as_mut_raw_address(&mut self) -> &mut coap_address_t {
71 &mut self.0
72 }
73
74 #[allow(dead_code)]
77 pub(crate) fn into_raw_address(self) -> coap_address_t {
78 self.0
79 }
80}
81
82impl ToSocketAddrs for CoapAddress {
83 type Iter = std::option::IntoIter<SocketAddr>;
84
85 fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
86 let socketaddr = match unsafe { self.0.addr.sa.as_ref().sa_family } as i32 {
90 AF_INET => {
91 let raw_addr = unsafe { self.0.addr.sin.as_ref() };
94 SocketAddrV4::new(
95 Ipv4Addr::from(raw_addr.sin_addr.s_addr.to_ne_bytes()),
96 u16::from_be(raw_addr.sin_port),
97 )
98 .into()
99 },
100 AF_INET6 => {
101 let raw_addr = unsafe { self.0.addr.sin6.as_ref() };
104 SocketAddrV6::new(
105 Ipv6Addr::from(raw_addr.sin6_addr.s6_addr),
106 u16::from_be(raw_addr.sin6_port),
107 raw_addr.sin6_flowinfo,
108 raw_addr.sin6_scope_id,
109 )
110 .into()
111 },
112 _ => panic!("sa_family_t of underlying coap_address_t is invalid!"),
114 };
115 Ok(Some(socketaddr).into_iter())
116 }
117}
118
119impl From<SocketAddr> for CoapAddress {
120 fn from(addr: SocketAddr) -> Self {
121 match addr {
122 SocketAddr::V4(addr) => {
123 unsafe {
127 let mut coap_addr = coap_address_t {
128 size: std::mem::size_of::<sockaddr_in>() as socklen_t,
129 addr: std::mem::zeroed(),
130 };
131 *coap_addr.addr.sin.as_mut() = sockaddr_in {
132 sin_family: AF_INET as sa_family_t,
133 sin_port: addr.port().to_be(),
134 sin_addr: in_addr {
135 s_addr: u32::from_ne_bytes(addr.ip().octets()),
136 },
137 sin_zero: Default::default(),
138 };
139 CoapAddress(coap_addr)
140 }
141 },
142 SocketAddr::V6(addr) => {
143 unsafe {
147 let mut coap_addr = coap_address_t {
148 size: std::mem::size_of::<sockaddr_in6>() as socklen_t,
149 addr: std::mem::zeroed(),
150 };
151 *coap_addr.addr.sin6.as_mut() = sockaddr_in6 {
152 sin6_family: AF_INET6 as sa_family_t,
153 sin6_port: addr.port().to_be(),
154 sin6_addr: in6_addr {
155 s6_addr: addr.ip().octets(),
156 },
157 sin6_flowinfo: addr.flowinfo(),
158 sin6_scope_id: addr.scope_id(),
159 };
160 CoapAddress(coap_addr)
161 }
162 },
163 }
164 }
165}
166
167#[doc(hidden)]
168impl From<coap_address_t> for CoapAddress {
169 fn from(raw_addr: coap_address_t) -> Self {
170 CoapAddress(raw_addr)
171 }
172}
173
174#[doc(hidden)]
175impl From<&coap_address_t> for CoapAddress {
176 fn from(raw_addr: &coap_address_t) -> Self {
177 let mut new_addr = MaybeUninit::zeroed();
178 unsafe {
179 std::ptr::copy_nonoverlapping(raw_addr, new_addr.as_mut_ptr(), 1);
180 CoapAddress(new_addr.assume_init())
181 }
182 }
183}
184
185#[repr(u32)]
187#[derive(Copy, Clone, FromPrimitive, Debug, PartialEq, Eq, Hash)]
188pub enum CoapUriScheme {
189 Coap = COAP_URI_SCHEME_COAP as u32,
190 Coaps = COAP_URI_SCHEME_COAPS as u32,
191 CoapTcp = COAP_URI_SCHEME_COAP_TCP as u32,
192 CoapsTcp = COAP_URI_SCHEME_COAPS_TCP as u32,
193 Http = COAP_URI_SCHEME_HTTP as u32,
194 Https = COAP_URI_SCHEME_HTTPS as u32,
195}
196
197impl CoapUriScheme {
198 pub fn is_secure(self) -> bool {
199 COAP_URI_SCHEME_SECURE_MASK & (self as u32) > 0
200 }
201
202 pub fn from_raw_scheme(scheme: coap_uri_scheme_t) -> CoapUriScheme {
203 num_traits::FromPrimitive::from_u32(scheme as u32).expect("unknown scheme")
204 }
205}
206
207impl FromStr for CoapUriScheme {
208 type Err = UriParsingError;
209
210 fn from_str(s: &str) -> Result<Self, Self::Err> {
211 match s {
212 "coap" => Ok(CoapUriScheme::Coap),
213 "coaps" => Ok(CoapUriScheme::Coaps),
214 "coap+tcp" => Ok(CoapUriScheme::CoapTcp),
215 "coaps+tcp" => Ok(CoapUriScheme::CoapsTcp),
216 "http" => Ok(CoapUriScheme::Http),
217 "https" => Ok(CoapUriScheme::Https),
218 _ => Err(UriParsingError::NotACoapScheme),
219 }
220 }
221}
222
223impl Display for CoapUriScheme {
224 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
225 f.write_str(match self {
226 CoapUriScheme::Coap => "coap",
227 CoapUriScheme::Coaps => "coaps",
228 CoapUriScheme::CoapTcp => "coap+tcp",
229 CoapUriScheme::CoapsTcp => "coaps+tcp",
230 CoapUriScheme::Http => "http",
231 CoapUriScheme::Https => "https",
232 })
233 }
234}
235
236impl From<coap_uri_scheme_t> for CoapUriScheme {
237 fn from(scheme: coap_uri_scheme_t) -> Self {
238 CoapUriScheme::from_raw_scheme(scheme)
239 }
240}
241
242#[derive(Clone, Debug, PartialEq, Eq, Hash)]
244pub enum CoapUriHost {
245 IpLiteral(IpAddr),
246 Name(String),
247}
248
249impl<T: ToString> From<url::Host<T>> for CoapUriHost {
250 fn from(host: Host<T>) -> Self {
251 match host {
252 Host::Domain(d) => CoapUriHost::Name(d.to_string()),
253 Host::Ipv4(addr) => CoapUriHost::IpLiteral(IpAddr::V4(addr)),
254 Host::Ipv6(addr) => CoapUriHost::IpLiteral(IpAddr::V6(addr)),
255 }
256 }
257}
258
259impl FromStr for CoapUriHost {
260 type Err = Infallible;
261
262 fn from_str(s: &str) -> Result<Self, Self::Err> {
263 Ok(IpAddr::from_str(s).map_or_else(|_| CoapUriHost::Name(s.to_string()), CoapUriHost::IpLiteral))
264 }
265}
266
267impl Display for CoapUriHost {
268 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
269 f.write_str(
270 match self {
271 CoapUriHost::IpLiteral(addr) => addr.to_string(),
272 CoapUriHost::Name(host) => host.clone(),
273 }
274 .as_str(),
275 )
276 }
277}
278
279#[derive(Clone, Debug, PartialEq, Eq, Hash)]
281pub struct CoapUri {
282 scheme: Option<CoapUriScheme>,
283 host: Option<CoapUriHost>,
284 port: Option<u16>,
285 path: Option<Vec<String>>,
286 query: Option<Vec<String>>,
287}
288
289impl CoapUri {
290 pub fn new(
292 scheme: Option<CoapUriScheme>,
293 host: Option<CoapUriHost>,
294 port: Option<u16>,
295 path: Option<Vec<String>>,
296 query: Option<Vec<String>>,
297 ) -> CoapUri {
298 CoapUri {
299 scheme,
300 host,
301 port,
302 path,
303 query,
304 }
305 }
306
307 pub fn try_from_url(url: Url) -> Result<CoapUri, UriParsingError> {
312 let path: Vec<String> = url
313 .path()
314 .split('/')
315 .map(String::from)
316 .filter(|v| !v.is_empty())
317 .collect();
318 let path = if path.is_empty() { None } else { Some(path) };
319
320 let query: Vec<String> = url.query_pairs().map(|(k, v)| format!("{}={}", k, v)).collect();
321 let query = if query.is_empty() { None } else { Some(query) };
322 Ok(CoapUri {
323 scheme: Some(CoapUriScheme::from_str(url.scheme())?),
324 host: url.host().map(|h| h.into()),
325 port: url.port(),
326 path,
327 query,
328 })
329 }
330
331 pub fn scheme(&self) -> Option<&CoapUriScheme> {
333 self.scheme.as_ref()
334 }
335
336 pub fn host(&self) -> Option<&CoapUriHost> {
338 self.host.as_ref()
339 }
340
341 pub fn port(&self) -> Option<u16> {
343 self.port
344 }
345
346 pub(crate) fn drain_path_iter(&mut self) -> Option<Drain<String>> {
348 self.path.as_mut().map(|p| p.drain(..))
349 }
350
351 pub fn path_iter(&self) -> Option<Iter<'_, String>> {
353 self.path.as_ref().map(|p| p.iter())
354 }
355
356 pub fn drain_query_iter(&mut self) -> Option<Drain<String>> {
358 self.query.as_mut().map(|p| p.drain(..))
359 }
360
361 pub fn query_iter(&self) -> Option<Iter<String>> {
363 self.query.as_ref().map(|p| p.iter())
364 }
365}
366
367impl TryFrom<Url> for CoapUri {
368 type Error = UriParsingError;
369
370 fn try_from(value: Url) -> Result<Self, Self::Error> {
371 CoapUri::try_from_url(value)
372 }
373}
374
375impl Display for CoapUri {
376 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
377 f.write_fmt(format_args!(
378 "{}{}{}{}{}",
379 self.scheme.map_or_else(String::new, |v| format!("{}://", v)),
380 self.host.as_ref().map_or_else(String::new, |v| v.to_string()),
381 self.port.map_or_else(String::new, |v| format!(":{}", v)),
382 self.path
383 .as_ref()
384 .map_or_else(String::new, |v| format!("/{}", v.join("/"))),
385 self.query
386 .as_ref()
387 .map_or_else(String::new, |v| format!("?{}", v.join("&"))),
388 ))
389 }
390}
391
392#[repr(u32)]
394#[non_exhaustive]
395#[derive(Copy, Clone, FromPrimitive, PartialEq, Eq, Hash)]
396pub enum CoapProtocol {
397 None = COAP_PROTO_NONE as u32,
398 Udp = COAP_PROTO_UDP as u32,
399 Dtls = COAP_PROTO_DTLS as u32,
400 Tcp = COAP_PROTO_TCP as u32,
401 Tls = COAP_PROTO_TLS as u32,
402}
403
404#[doc(hidden)]
405impl From<coap_proto_t> for CoapProtocol {
406 fn from(raw_proto: coap_proto_t) -> Self {
407 <CoapProtocol as FromPrimitive>::from_u32(raw_proto as u32).expect("unknown protocol")
408 }
409}
410
411impl Display for CoapProtocol {
412 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
413 f.write_str(match self {
414 CoapProtocol::None => "none",
415 CoapProtocol::Udp => "udp",
416 CoapProtocol::Dtls => "dtls",
417 CoapProtocol::Tcp => "tcp",
418 CoapProtocol::Tls => "tls",
419 })
420 }
421}
422
423fn convert_to_fixed_size_slice(n: usize, val: &[u8]) -> Box<[u8]> {
424 if val.len() > n {
425 panic!("supplied slice too short");
426 }
427 let mut buffer: Vec<u8> = vec![0; n];
428 let (_, target_buffer) = buffer.split_at_mut(n - val.len());
429 target_buffer.copy_from_slice(val);
430 buffer.truncate(n);
431 buffer.into_boxed_slice()
432}
433
434pub(crate) fn decode_var_len_u32(val: &[u8]) -> u32 {
436 u32::from_be_bytes(
437 convert_to_fixed_size_slice(4, val)[..4]
438 .try_into()
439 .expect("could not convert from variable sized value to fixed size number as the lengths don't match"),
440 )
441}
442
443pub(crate) fn encode_var_len_u32(val: u32) -> Box<[u8]> {
444 let bytes_to_discard = val.leading_zeros() / 8;
446 let mut ret_val = Vec::from(val.to_be_bytes());
447 ret_val.drain(..bytes_to_discard as usize);
448 ret_val.into_boxed_slice()
449}
450
451#[allow(unused)]
453pub(crate) fn decode_var_len_u64(val: &[u8]) -> u64 {
454 u64::from_be_bytes(
455 convert_to_fixed_size_slice(8, val)[..8]
456 .try_into()
457 .expect("could not convert from variable sized value to fixed size number as the lengths don't match"),
458 )
459}
460
461#[allow(unused)]
463pub(crate) fn encode_var_len_u64(val: u64) -> Box<[u8]> {
464 let bytes_to_discard = val.leading_zeros() / 8;
466 let mut ret_val = Vec::from(val.to_be_bytes());
467 ret_val.drain(..bytes_to_discard as usize);
468 ret_val.into_boxed_slice()
469}
470
471pub(crate) fn decode_var_len_u16(val: &[u8]) -> u16 {
472 u16::from_be_bytes(
473 convert_to_fixed_size_slice(2, val)[..2]
474 .try_into()
475 .expect("could not convert from variable sized value to fixed size number as the lengths don't match"),
476 )
477}
478
479pub(crate) fn encode_var_len_u16(val: u16) -> Box<[u8]> {
480 let bytes_to_discard = val.leading_zeros() / 8;
482 let mut ret_val = Vec::from(val.to_be_bytes());
483 ret_val.drain(..bytes_to_discard as usize);
484 ret_val.into_boxed_slice()
485}
486
487#[allow(unused)]
489pub(crate) fn decode_var_len_u8(val: &[u8]) -> u16 {
490 u16::from_be_bytes(
491 convert_to_fixed_size_slice(1, val)[..1]
492 .try_into()
493 .expect("could not convert from variable sized value to fixed size number as the lengths don't match"),
494 )
495}
496
497pub(crate) fn encode_var_len_u8(val: u8) -> Box<[u8]> {
498 Vec::from([val]).into_boxed_slice()
499}