1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * lib.rs - Main library entry point for raw libcoap bindings.
4
 * This file is part of the libcoap-sys 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
//! Auto-generated unsafe bindings to [libcoap](https://github.com/obgm/libcoap), generated using
11
//! [bindgen](https://crates.io/crates/bindgen).
12
//!
13
//! This crate allows direct (but unsafe) usage of the libcoap C library from Rust. The declarations
14
//! made in this library are generated automatically using bindgen, for further documentation on how
15
//! to use them, refer to the [libcoap documentation](https://libcoap.net/documentation.html).
16
//!
17
//! In most cases you probably want to use the safe wrapper provided by the libcoap crate (or
18
//! another coap library written in pure rust such as [coap-rs](https://github.com/covertness/coap-rs))
19
//! instead.
20
//!
21
//! Cargo Features
22
//! --------------
23
//! We currently define a number of features that affect the functionality provided by this wrapper
24
//! and required by the linked libcoap library.
25
//!
26
//! Features affecting functionality:
27
//! - `dtls`: Enable usage of DTLS for transport security. Supports a number of different backends.
28
//!
29
//!   Note that while not specified here due to limitations in Cargo's syntax, the DTLS feature
30
//!   depends on one of the DTLS backends being enabled, and failing to enable a DTLS backend will
31
//!   result in a build failure.
32
//!   
33
//!   If you are developing a library based on libcoap-sys and do not care about the DTLS backend,
34
//!   enable the dtls feature and let the user decide on the backend to use, either by
35
//!   re-exporting these features (see [the Cargo Book](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features))
36
//!   or by assuming that the user will use libcoap-sys as a dependency and enable the
37
//!   corresponding backend feature themselves, relying on Cargo's feature unification to enable
38
//!   it for your crate as well.
39
//!   
40
//!   Also note that the backends are **mutually exclusive** due to the C library having these
41
//!   backends as mutually exclusive features. If multiple backends are enabled (e.g. because
42
//!   multiple dependencies use libcoap-sys and use different backends), we select one based on
43
//!   the auto-detection order specified in [the libcoap configure script](https://github.com/obgm/libcoap/blob/develop/configure.ac#L494)
44
//!   (gnutls > openssl > mbedtls > tinydtls).
45
//!   - `dtls_backend_(openssl|gnutls|mbedtls|tinydtls)`: Enable the corresponding DTLS backend.
46
//!      
47
//!      Note that enabling the OpenSSL, GnuTLS, TinyDTLS or MbedTLS backend will also require the
48
//!      appropriate library to be available (hence the dependency on the corresponding sys-crate).
49
//!      The TinyDTLS backend is built using a vendored (and statically linked) version of TinyDTLS
50
//!      by default, see the tinydtls-sys crate for more info.
51
//!      Choosing a DTLS backend also means that the license terms of these libraries may apply to
52
//!      you. See the relevant parts of the [libcoap license file](https://github.com/obgm/libcoap/blob/develop/LICENSE)
53
//!      for more information.
54
//! - `tcp` (default): Enable CoAP over TCP support
55
//! - `async` (default): Enable async functionality.
56
//!   
57
//!   Note that this async functionality is not translated to Rust's async language functionality,
58
//!   but instead adds functionality to the underlying C library to allow for making asynchronous
59
//!   requests (i.e. function calls that return before the response has arrived).
60
//!
61
//!   Integrating libcoap into Rusts async language features is out of scope for this crate, but
62
//!   might be implemented later on in the libcoap (safe abstraction) crate.
63
//! - `server` (default): Enable code related to server functionality
64
//! - `client` (default): Enable code related to client functionality
65
//! - `epoll` (default): Allow the underlying C library to perform IO operations using epoll.
66
//!
67
//! Other features:
68
//! - `vendored` (default): Use a vendored version of libcoap instead of the system-provided one.
69
//!   Note that `vendored` implies `static`.
70
//! - `static` (default): Perform static linking to the libcoap C library.
71
//!
72
//! ### Note on features affecting functionality
73
//! The features that add or remove functionality do not change the generated bindings as libcoap's
74
//! headers (unlike the source files themselves) are not affected by the corresponding `#define`s.
75
//!
76
//! For library users that link to a shared version of libcoap, this means that the feature flags
77
//! do not have any effect and the supported features will correspond directly to the features
78
//! enabled during the build of the shared libcoap instance (using the configure-script).
79
//!
80
//! For users of the vendored version of libcoap (see the `vendored` feature), the supported
81
//! features of the vendored libcoap will be set to match the cargo features during build.
82

            
83
// Bindgen translates the C headers, clippy's and rustfmt's recommendations are not applicable here.
84
#![allow(clippy::all)]
85
#![allow(non_camel_case_types)]
86
#![allow(deref_nullptr)]
87
#![allow(non_snake_case)]
88

            
89
use std::ffi::c_void;
90

            
91
#[allow(unused_imports)]
92
use libc::{fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t};
93
#[allow(unused_imports)]
94
#[cfg(not(target_os = "espidf"))]
95
use libc::epoll_event;
96
// use dtls backend libraries in cases where they set our linker flags, otherwise cargo will
97
// optimize them out.
98
#[allow(unused_imports)]
99
#[cfg(feature = "dtls_backend_mbedtls_vendored")]
100
use mbedtls_sys as _;
101
#[allow(unused_imports)]
102
#[cfg(feature = "dtls_backend_openssl")]
103
use openssl_sys as _;
104
#[allow(unused_imports)]
105
#[cfg(feature = "dtls_backend_tinydtls")]
106
use tinydtls_sys as _;
107

            
108
#[cfg(target_family = "windows")]
109
include!(concat!(env!("OUT_DIR"), "\\bindings.rs"));
110
#[cfg(not(target_family = "windows"))]
111
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
112

            
113
#[inline]
114
#[cfg(not(non_inlined_coap_send_rst))]
115
pub unsafe fn coap_send_rst(session: *mut coap_session_t, request: *const coap_pdu_t) -> coap_mid_t {
116
    coap_send_message_type(session, request, crate::coap_pdu_type_t::COAP_MESSAGE_RST)
117
}
118

            
119
/// Compares instances of coap_str_const_t and/or coap_string_t.
120
///
121
/// This macro is a reimplementation of the macro defined in coap_str.h, see
122
/// <https://libcoap.net/doc/reference/develop/group__string.html#ga7f43c10b486dc6d45c37fcaf987d711b>.
123
#[macro_export]
124
macro_rules! coap_string_equal {
125
    ( $string1:expr, $string2:expr ) => {{
126
        use libcoap_sys::coap_string_equal_internal;
127
        let s1 = $string1;
128
        let s2 = $string2;
129
        coap_string_equal_internal((*s1).s, (*s1).length, (*s2).s, (*s2).length)
130
    }};
131
}
132

            
133
/// Internal only function for CoAP string comparisons.
134
///
135
/// *DO NOT USE THIS FUNCTION DIRECTLY.* It is only public because it is used by the
136
/// [coap_string_equal] macro, which is the function you probably wanted to call instead.
137
///
138
/// # Safety
139
///
140
/// This function should not be called directly, use [coap_string_equal] instead.
141
pub unsafe fn coap_string_equal_internal(
142
    str1_ptr: *const u8,
143
    str1_len: usize,
144
    str2_ptr: *const u8,
145
    str2_len: usize,
146
) -> bool {
147
    str1_len == str2_len
148
        && (str1_len == 0
149
            || !str1_ptr.is_null()
150
                && !str2_ptr.is_null()
151
                && memcmp(str1_ptr as *const c_void, str2_ptr as *const c_void, str1_len) == 0)
152
}
153

            
154
/// Initialize the CoAP library and additionally perform runtime checks to ensure that required
155
/// features (as enabled in `Cargo.toml`) are available.
156
///
157
/// You *should* prefer using this function over [coap_startup], as without calling this function
158
/// some of the features enabled using the Cargo features may not actually be available.
159
/// (NOTE: However, see the below section on safety).
160
///
161
/// Either this function or [coap_startup] must be run once before any libcoap function is called.
162
///
163
/// If you are absolutely 100% certain that all features you require are always available (or are
164
/// prepared to deal with error return values/different behavior on your own if they aren't), you
165
/// may use [coap_startup] instead.
166
///
167
/// # SAFETY WARNING
168
/// In order to preserve backwards compatibility, this method may (for now) skip the feature checks
169
/// altogether if they aren't provided by libcoap (which may be the case for libcoap < 4.3.5rc3).
170
///
171
/// If you want to be absolutely certain that a given feature is available, you *must* also check
172
/// the libcoap version to ensure that it is at least 4.3.5rc3.
173
///
174
/// This behavior will be changed as soon as libcoap 4.3.5 is released, after which libcoap 4.3.5
175
/// will become the minimum supported version and these checks will be mandatory.
176
746
pub fn coap_startup_with_feature_checks() {
177
746
    // only compile checks if they are available for the given libcoap version.
178
746
    #[cfg(feature_checks_available)]
179
746
    {
180
746
        #[cfg(feature = "af-unix")]
181
746
        // SAFETY: Function is always safe to call.
182
746
        if unsafe { coap_af_unix_is_supported() != 1 } {
183
746
            panic!("Required feature \"af-unix\" is not supported by libcoap")
184
746
        }
185
746
        #[cfg(feature = "async")]
186
746
        // SAFETY: Function is always safe to call.
187
746
        if unsafe { coap_async_is_supported() != 1 } {
188
746
            panic!("Required feature \"async\" is not supported by libcoap")
189
746
        }
190
746
        #[cfg(feature = "client")]
191
746
        // SAFETY: Function is always safe to call.
192
746
        if unsafe { coap_client_is_supported() != 1 } {
193
            panic!("Required feature \"ipv4\" is not supported by libcoap")
194
746
        }
195
746
        #[cfg(feature = "dtls")]
196
746
        // SAFETY: Function is always safe to call.
197
746
        if unsafe { coap_dtls_is_supported() != 1 } {
198
            panic!("Required feature \"dtls\" is not supported by libcoap")
199
746
        }
200
746
        #[cfg(feature = "dtls-cid")]
201
746
        // SAFETY: Function is always safe to call.
202
746
        if unsafe { coap_dtls_cid_is_supported() != 1 } {
203
746
            panic!("Required feature \"dtls-cid\" is not supported by libcoap")
204
746
        }
205
746
        #[cfg(feature = "dtls-psk")]
206
746
        // SAFETY: Function is always safe to call.
207
746
        if unsafe { coap_dtls_psk_is_supported() != 1 } {
208
            panic!("Required feature \"dtls-psk\" is not supported by libcoap")
209
746
        }
210
746
        #[cfg(feature = "dtls-pki")]
211
746
        // SAFETY: Function is always safe to call.
212
746
        if unsafe { coap_dtls_pki_is_supported() != 1 } {
213
64
            panic!("Required feature \"dtls-pki\" is not supported by libcoap")
214
746
        }
215
746
        #[cfg(feature = "dtls-pkcs11")]
216
746
        // SAFETY: Function is always safe to call.
217
746
        if !unsafe { coap_dtls_pkcs11_is_supported() == 1 } {
218
746
            panic!("Required feature \"dtls-pkcs11\" is not supported by libcoap")
219
746
        }
220
746
        #[cfg(feature = "dtls-rpk")]
221
746
        // SAFETY: Function is always safe to call.
222
746
        if unsafe { coap_dtls_rpk_is_supported() != 1 } {
223
484
            panic!("Required feature \"dtls-rpk\" is not supported by libcoap")
224
746
        }
225
746
        #[cfg(feature = "epoll")]
226
746
        // SAFETY: Function is always safe to call.
227
746
        if unsafe { coap_epoll_is_supported() != 1 } {
228
746
            panic!("Required feature \"epoll\" is not supported by libcoap")
229
746
        }
230
746
        #[cfg(feature = "ipv4")]
231
746
        // SAFETY: Function is always safe to call.
232
746
        if !unsafe { coap_ipv4_is_supported() == 1 } {
233
746
            panic!("Required feature \"ipv4\" is not supported by libcoap")
234
746
        }
235
746
        #[cfg(feature = "ipv6")]
236
746
        // SAFETY: Function is always safe to call.
237
746
        if !unsafe { coap_ipv6_is_supported() == 1 } {
238
746
            panic!("Required feature \"ipv6\" is not supported by libcoap")
239
746
        }
240
746
        #[cfg(feature = "observe-persist")]
241
746
        // SAFETY: Function is always safe to call.
242
746
        if unsafe { coap_observe_persist_is_supported() != 1 } {
243
746
            panic!("Required feature \"ipv4\" is not supported by libcoap")
244
746
        }
245
746
        #[cfg(feature = "oscore")]
246
746
        // SAFETY: Function is always safe to call.
247
746
        if unsafe { coap_oscore_is_supported() != 1 } {
248
746
            panic!("Required feature \"oscore\" is not supported by libcoap")
249
746
        }
250
746
        #[cfg(feature = "q-block")]
251
746
        // SAFETY: Function is always safe to call.
252
746
        if unsafe { coap_q_block_is_supported() != 1 } {
253
746
            panic!("Required feature \"q-block\" is not supported by libcoap")
254
746
        }
255
746
        #[cfg(feature = "server")]
256
746
        // SAFETY: Function is always safe to call.
257
746
        if unsafe { coap_server_is_supported() != 1 } {
258
            panic!("Required feature \"ipv4\" is not supported by libcoap")
259
746
        }
260
746
        #[cfg(feature = "tcp")]
261
746
        // SAFETY: Function is always safe to call.
262
746
        if unsafe { coap_tcp_is_supported() != 1 } {
263
            panic!("Required feature \"tcp\" is not supported by libcoap")
264
746
        }
265
746
        #[cfg(feature = "thread-safe")]
266
746
        // SAFETY: Function is always safe to call.
267
746
        if unsafe { coap_threadsafe_is_supported() != 1 } {
268
746
            panic!("Required feature \"thread-safe\" is not supported by libcoap")
269
746
        }
270
746
        #[cfg(feature = "tls")]
271
746
        // SAFETY: Function is always safe to call.
272
746
        if unsafe { coap_tls_is_supported() != 1 } {
273
746
            panic!("Required feature \"tls\" is not supported by libcoap")
274
746
        }
275
746
        #[cfg(feature = "websockets")]
276
746
        // SAFETY: Function is always safe to call.
277
746
        if unsafe { coap_ws_is_supported() != 1 } {
278
746
            panic!("Required feature \"websockets\" is not supported by libcoap")
279
746
        }
280
746
        #[cfg(feature = "secure-websockets")]
281
746
        // SAFETY: Function is always safe to call.
282
746
        if unsafe { coap_wss_is_supported() != 1 } {
283
746
            panic!("Required feature \"websockets\" is not supported by libcoap")
284
746
        }
285
746
    }
286
746
    #[cfg(not(feature_checks_available))]
287
746
    {
288
746
        println!("WARNING: coap_startup_with_feature_checks() could not assert the availability of features because the linked version of libcoap is too old (< 4.3.5rc3)!")
289
746
    }
290
746
    // SAFETY: Function is always safe to call.
291
746
    unsafe { coap_startup() }
292
746
}
293

            
294
#[cfg(all(test, not(target_os = "espidf")))]
295
mod tests {
296
    use std::{
297
        ffi::c_void,
298
        net::{SocketAddr, UdpSocket},
299
        os::raw::c_int,
300
        sync::{Arc, Barrier},
301
    };
302

            
303
    use libc::{AF_INET, AF_INET6, in6_addr, in_addr, sa_family_t, size_t};
304

            
305
    use crate::{
306
        coap_pdu_code_t::{COAP_REQUEST_CODE_GET, COAP_RESPONSE_CODE_CONTENT},
307
        coap_proto_t::COAP_PROTO_UDP,
308
        coap_request_t::COAP_REQUEST_GET,
309
        coap_response_t::COAP_RESPONSE_OK,
310
    };
311

            
312
    use super::*;
313

            
314
    const COAP_TEST_RESOURCE_URI: &str = "test";
315
    const COAP_TEST_RESOURCE_RESPONSE: &str = "Hello World!";
316

            
317
    /// Creates a coap_address_t from a &SocketAddr.
318
8
    fn coap_address_from_socketaddr(addr: &SocketAddr) -> coap_address_t {
319
8
        match addr {
320
            SocketAddr::V4(addr) => {
321
                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
322
                // to use a pointer instead.
323
                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
324
                unsafe {
325
                    let mut coap_addr = coap_address_t {
326
                        size: std::mem::size_of::<sockaddr_in>() as socklen_t,
327
                        addr: std::mem::zeroed(),
328
                    };
329
                    *coap_addr.addr.sin.as_mut() = sockaddr_in {
330
                        sin_family: AF_INET as sa_family_t,
331
                        sin_port: addr.port().to_be(),
332
                        sin_addr: in_addr {
333
                            s_addr: u32::from_ne_bytes(addr.ip().octets()),
334
                        },
335
                        sin_zero: Default::default(),
336
                    };
337
                    coap_addr
338
                }
339
            },
340
8
            SocketAddr::V6(addr) => {
341
8
                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
342
8
                // to use a pointer instead.
343
8
                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
344
8
                unsafe {
345
8
                    let mut coap_addr = coap_address_t {
346
8
                        size: std::mem::size_of::<sockaddr_in6>() as socklen_t,
347
8
                        addr: std::mem::zeroed(),
348
8
                    };
349
8
                    *coap_addr.addr.sin6.as_mut() = sockaddr_in6 {
350
8
                        sin6_family: AF_INET6 as sa_family_t,
351
8
                        sin6_port: addr.port().to_be(),
352
8
                        sin6_addr: in6_addr {
353
8
                            s6_addr: addr.ip().octets(),
354
8
                        },
355
8
                        sin6_flowinfo: addr.flowinfo(),
356
8
                        sin6_scope_id: addr.scope_id(),
357
8
                    };
358
8
                    coap_addr
359
                }
360
            },
361
        }
362
8
    }
363

            
364
    /// Response handler for the CoAP client/server test (client-side)
365
    /// # Safety
366
    /// Assumes all pointers to be valid and pointing to data structures containing valid data
367
    /// according to the specification of the method handler function provided in libcoap.
368
    /// Assumes that the application data in the CoAP context associated with the session is a
369
    /// pointer to a boolean specifying whether a successful request was received (will be set to
370
    /// true by this function).
371
4
    unsafe extern "C" fn test_resource_handler(
372
4
        _resource: *mut coap_resource_t,
373
4
        session: *mut coap_session_t,
374
4
        _incoming_pdu: *const coap_pdu_t,
375
4
        _query: *const coap_string_t,
376
4
        response_pdu: *mut coap_pdu_t,
377
4
    ) {
378
4
        let mut buf: [u8; 3] = [0; 3];
379
4
        coap_add_option(
380
4
            response_pdu,
381
4
            COAP_OPTION_CONTENT_TYPE as coap_option_num_t,
382
4
            coap_encode_var_safe(buf.as_mut_ptr(), buf.len(), COAP_MEDIATYPE_TEXT_PLAIN) as usize,
383
4
            buf.as_ptr(),
384
4
        );
385
4
        coap_add_data(
386
4
            response_pdu,
387
4
            COAP_TEST_RESOURCE_RESPONSE.len(),
388
4
            COAP_TEST_RESOURCE_RESPONSE.as_ptr(),
389
4
        );
390
4
        coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void);
391
4
        coap_pdu_set_code(response_pdu, COAP_RESPONSE_CODE_CONTENT);
392
4
    }
393

            
394
    /// Response handler for the CoAP client/server test (client-side)
395
    /// # Safety
396
    /// Assumes all pointers to be valid and pointing to data structures containing valid data
397
    /// according to the specification of the response handler function provided in libcoap.
398
    /// Assumes that the application data in the CoAP context associated with the session is a
399
    /// pointer to a boolean specifying whether a successful response was received (will be set to
400
    /// true by this function).
401
4
    unsafe extern "C" fn test_response_handler(
402
4
        session: *mut coap_session_t,
403
4
        _sent: *const coap_pdu_t,
404
4
        received: *const coap_pdu_t,
405
4
        _mid: coap_mid_t,
406
4
    ) -> coap_response_t {
407
4
        assert_eq!(coap_pdu_get_code(received), COAP_RESPONSE_CODE_CONTENT);
408
4
        let mut len: size_t = 0;
409
4
        let mut data: *const u8 = std::ptr::null();
410
4
        assert_ne!(coap_get_data(received, &mut len, &mut data), 0);
411
4
        let data = std::slice::from_raw_parts(data, len);
412
4

            
413
4
        assert_eq!(data, COAP_TEST_RESOURCE_RESPONSE.as_bytes());
414
4
        coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void);
415
4
        return COAP_RESPONSE_OK;
416
4
    }
417

            
418
    /// Creates a CoAP server that provides a single resource under COAP_TEST_RESOURCE_URI over the
419
    /// supplied socket
420
4
    fn run_coap_test_server(addr: &SocketAddr, barrier: Arc<Barrier>) {
421
4
        // SAFETY: Null pointer is a valid parameter here.
422
4
        let context = unsafe { coap_new_context(std::ptr::null()) };
423
4
        assert!(!context.is_null());
424

            
425
4
        let address: coap_address_t = coap_address_from_socketaddr(addr);
426
4

            
427
4
        // SAFETY: We asserted that context != null, listen_addr is a reference and can therefore not be null.
428
4
        let endpoint = unsafe { coap_new_endpoint(context, &address, coap_proto_t::COAP_PROTO_UDP) };
429
4
        assert!(!endpoint.is_null());
430

            
431
        // SAFETY: Since we use a string constant here, the arguments to the function are all valid.
432
4
        let uri = unsafe { coap_new_str_const(COAP_TEST_RESOURCE_URI.as_ptr(), COAP_TEST_RESOURCE_URI.len()) };
433
4
        assert!(!uri.is_null());
434

            
435
        // SAFETY: We just asserted that uri is valid, COAP_RESOURCE_FLAGS_RELEASE_URI is valid because we will not free the uri ourselves.
436
4
        let test_resource = unsafe { coap_resource_init(uri, COAP_RESOURCE_FLAGS_RELEASE_URI as c_int) };
437
4
        assert!(!test_resource.is_null());
438

            
439
        // SAFETY: We asserted that test_resource and context are valid, other pointers are always valid.
440
        // The fact that we cast a constant to a mutable pointer is not problematic, because neither we
441
        // nor libcoap ever mutate the pointer. This is necessary, because the underlying libcoap
442
        // struct allows for mutable pointers to be set there, so that applications can use this to
443
        // modify some application specific state.
444
4
        unsafe {
445
4
            coap_register_request_handler(test_resource, COAP_REQUEST_GET, Some(test_resource_handler));
446
4
            coap_add_resource(context, test_resource);
447
4
            coap_set_app_data(context, (&false) as *const bool as *mut c_void);
448
4
        }
449
4

            
450
4
        barrier.wait();
451
        loop {
452
4
            let time_spent_millis = unsafe { coap_io_process(context, 10000) };
453
4
            // SAFETY: We are the only ones setting or accessing this value, so we know it is a
454
4
            // const bool pointer (we have also set it to *false before and only ever set it to
455
4
            // *true afterwards).
456
4
            if unsafe { *(coap_get_app_data(context) as *const bool).as_ref().unwrap() } {
457
4
                break;
458
            }
459
            if time_spent_millis == -1 || time_spent_millis >= 10000 {
460
                panic!("Test timeout exceeded");
461
            }
462
        }
463
        // SAFETY: Context is not referenced outside this function, and handlers (which get the
464
        // context from libcoap) will not be called after the context is freed.
465
        // This also seems to free all resources.
466
4
        unsafe {
467
4
            coap_free_context(context);
468
4
        }
469
4
    }
470

            
471
    /// Test case that creates a basic coap server and makes a request to it from a separate context
472
    #[test]
473
4
    fn test_coap_client_server_basic() {
474
4
        // This will give us a SocketAddress with a port in the local port range automatically
475
4
        // assigned by the operating system.
476
4
        // Because the UdpSocket goes out of scope, the Port will be free for usage by libcoap.
477
4
        // This seems to be the only portable way to get a port number assigned from the operating
478
4
        // system, which is necessary here because of potential parallelisation (we can't just use
479
4
        // the default CoAP port if multiple tests are run in parallel).
480
4
        // It is assumed here that after unbinding the temporary socket, the OS will not reassign
481
4
        // this port until we bind it again. This should work in most cases (unless we run on a
482
4
        // system with very few free ports), because at least Linux will not reuse port numbers
483
4
        // unless necessary, see https://unix.stackexchange.com/a/132524.
484
4
        let server_address = UdpSocket::bind("localhost:0")
485
4
            .expect("Failed to bind server socket")
486
4
            .local_addr()
487
4
            .expect("Failed to get server socket address");
488
4

            
489
4
        let preparation_barrier = Arc::new(Barrier::new(2));
490
4

            
491
4
        let server_address_clone = server_address.clone();
492
4
        let preparation_barrier_clone = preparation_barrier.clone();
493
4
        let server_thread_handle =
494
4
            std::thread::spawn(move || run_coap_test_server(&server_address_clone, preparation_barrier_clone));
495
4

            
496
4
        // SAFETY: Null pointer is a valid parameter here.
497
4
        let context = unsafe { coap_new_context(std::ptr::null()) };
498
4
        assert!(!context.is_null());
499

            
500
4
        preparation_barrier.wait();
501
4

            
502
4
        let server_address = coap_address_from_socketaddr(&server_address);
503
4

            
504
4
        // SAFETY: null pointer is valid argument for local_if, server_address is guaranteed to be
505
4
        // a correct value (conversion cannot fail), validity of context was asserted before.
506
4
        let client_session =
507
4
            unsafe { coap_new_client_session(context, std::ptr::null(), &server_address, COAP_PROTO_UDP) };
508
4

            
509
4
        // SAFETY: context and client_session were asserted to be valid.
510
4
        // Casting *const to *mut is fine because we don't mutate the value pointed to and the
511
4
        // pointer is not used by libcoap (the application data pointer is intended to be used by
512
4
        // client applications to store and/or modify their own data, which is why it is a mutable
513
4
        // pointer in the first place).
514
4
        // coap_request_pdu is asserted to be valid before it is used.
515
4
        unsafe {
516
4
            coap_register_response_handler(context, Some(test_response_handler));
517
4
            coap_set_app_data(context, (&false) as *const bool as *mut c_void);
518
4
            let coap_request_pdu =
519
4
                coap_new_pdu(coap_pdu_type_t::COAP_MESSAGE_NON, COAP_REQUEST_CODE_GET, client_session);
520
4
            assert!(!coap_request_pdu.is_null());
521
4
            assert_ne!(
522
4
                coap_add_option(
523
4
                    coap_request_pdu,
524
4
                    COAP_OPTION_URI_PATH as coap_option_num_t,
525
4
                    COAP_TEST_RESOURCE_URI.len(),
526
4
                    COAP_TEST_RESOURCE_URI.as_ptr(),
527
4
                ),
528
4
                0
529
4
            );
530
4
            assert_ne!(coap_send(client_session, coap_request_pdu), COAP_INVALID_MID);
531
        }
532

            
533
        loop {
534
            // SAFETY: context is asserted to be valid, no known side effects that would violate any guarantees
535
4
            let time_spent_millis = unsafe { coap_io_process(context, 10000) };
536
4
            // SAFETY: We are the only ones setting or accessing this value, so we know it is a
537
4
            // const bool pointer (we have also set it to *false before and only ever set it to
538
4
            // *true afterwards).
539
4
            if unsafe { *(coap_get_app_data(context) as *const bool).as_ref().unwrap() } {
540
4
                break;
541
            }
542
            if time_spent_millis == -1 || time_spent_millis >= 10000 {
543
                panic!("Test timeout exceeded");
544
            }
545
        }
546
        // SAFETY: Context is not references outside this function, and handlers (which get the
547
        // context from libcoap) will not be called after the context is freed.
548
        // This also seems to free all resources.
549
4
        unsafe {
550
4
            coap_free_context(context);
551
4
        }
552
4
        server_thread_handle.join().expect("Error waiting for server thread");
553
4
    }
554
}