1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * lib.rs - Main library entry point for safe libcoap bindings.
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-2024 The NAMIB Project Developers, all rights reserved.
7
 * See the README as well as the LICENSE file for more information.
8
 */
9

            
10
//! A safe wrapper around the libcoap C library.
11
//!
12
//! This wrapper allows for safe and idiomatic usage of the libcoap C library in Rust.
13
//!
14
//! # Feature support
15
//! libcoap-rs currently supports the following subset of the libcoap feature set:
16
//! - [x] Basic CoAP client
17
//! - [x] Basic CoAP server
18
//! - [ ] Transports:
19
//!     - [x] UDP
20
//!     - [x] DTLS
21
//!         - [x] DTLS using PSK
22
//!         - [x] DTLS using PKI/RPK
23
//!     - [ ] TCP
24
//!     - [ ] TLS
25
//!     - [ ] OSCORE
26
//!     - [ ] WebSockets
27
//! - [ ] Blockwise Transfer
28
//!     - [x] Receiving large messages
29
//!         - Note: Handled in libcoap by setting `COAP_BLOCK_USE_LIBCOAP|COAP_BLOCK_SINGLE_BODY`.
30
//!                 Manually constructing and managing blockwise transfers is currently not in scope
31
//!                 for this library.
32
//!     - [x] sending client-side large messages
33
//!     - [ ] sending server-side large messages
34
//! - [ ] Resource observation
35
//!     - [ ] Observing resources as a client
36
//!     - [x] Notifying observers as a server
37
//!
38
//! # Building
39
//! libcoap-rs can be linked to either an included version of libcoap or to a version provided by
40
//! the environment.
41
//! By default, it will use the vendored version, which can be disabled by disabling the default
42
//! feature `vendored`.
43
//!
44
//! In order to use DTLS, a DTLS library must be chosen, see the later section on using
45
//! cryptography for more information.
46
//!
47
//! Some (but not all) of the available DTLS libraries may also be vendored using the
48
//! `dtls_[LIBRARY]_vendored` feature.
49
//!
50
//! ## Building on the ESP32
51
//!
52
//! libcoap-rs and libcoap-sys support building for the ESP32.
53
//! This is done by using the version of libcoap provided by the ESP-IDF as a managed component
54
//! and generating bindings for it.
55
//!
56
//! In order to build for the ESP, ensure that the following preconditions are met:
57
//!
58
//! - The version of `esp-idf-sys` used by your crate matches the one used by `libcoap-sys`.
59
//! - Ensure that your `sdkconfig.defaults` enables the features required by your chosen
60
//!   feature set of `libcoap-rs`
61
//! - Ensure that the ESP-IDF version you link against is supported. `libcoap-rs` _should_
62
//!   compile on at least ESP-IDF 5.1.3 and 5.3.
63
//!   If it does not (or you require support for newer versions of ESP-IDF), please open an issue
64
//!   in the [`libcoap-rs` issue tracker](https://github.com/namib-project/libcoap-rs/issues).
65
//!
66
//! An example for a typical excerpt from `sdkconfig.defaults` can be found here:
67
//! ```ini
68
//! # libcoap base functionality (client and server)
69
//! CONFIG_COAP_SERVER_SUPPORT=y
70
//! CONFIG_COAP_CLIENT_SUPPORT=y
71
//!
72
//! # enable DTLS in libcoap
73
//! CONFIG_COAP_MBEDTLS_PSK=y
74
//! CONFIG_COAP_MBEDTLS_PKI=y
75
//! CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
76
//! CONFIG_MBEDTLS_PSK_MODES=y
77
//! CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
78
//! ```
79
//!
80
//! # Using cryptography
81
//! If you wish to use CoAP over DTLS, you have to provide credential and key information to
82
//! libcoap. See the documentation of the [`crypto`] module for more information and examples.
83
//!
84
//! libcoap requires a DTLS library to be selected for DTLS functionality. By default, libcoap-rs
85
//! will use `openssl` for this purpose. If you wish to use one of the other supported DTLS
86
//! libraries (GnuTLS, MbedTLS, tinydtls), disable the `dtls_openssl` feature and replace it with
87
//! the feature for the library of your choice.
88
//!
89
//! Note that enabling multiple backends is not possible and doing so will result in a single
90
//! backend being chosen based on the priority order (gnutls > openssl > mbedtls > tinydtls).
91
//!
92
//! # Examples
93
//!
94
//! ## Client
95
//! This example runs a simple CoAP client which makes a request to `coap://[::1]:5683/hello_world`
96
//! and checks whether the result has the code 2.00 (Content) and the payload `Hello World!`.
97
//!
98
//! ```no_run
99
//! use std::{
100
//!     net::{SocketAddr, UdpSocket},
101
//!     time::Duration,
102
//! };
103
//!
104
//! use libcoap_rs::{
105
//!     CoapContext,
106
//!     message::{CoapMessageCommon, CoapResponse, CoapRequest},
107
//!     protocol::{CoapRequestCode, CoapResponseCode, CoapMessageCode, CoapMessageType},
108
//!     CoapRequestHandler, CoapResource,
109
//!     session::{CoapSessionCommon, CoapClientSession},
110
//!     types::{CoapUriScheme, CoapUri}
111
//! };
112
//!
113
//! let server_address : SocketAddr = "[::1]:5683".parse().unwrap();
114
//!
115
//! // Create a new context.
116
//! let mut context = CoapContext::new().expect("Failed to create CoAP context");
117
//!
118
//! // Connect to the server at the specified address over UDP (no encryption)
119
//! let session = CoapClientSession::connect_udp(&mut context, server_address)
120
//!                 .expect("Failed to create client-side session");
121
//!
122
//! // Create a new CoAP URI to request from.
123
//! let uri = "coap://[::1]:5683/hello_world".parse().unwrap();
124
//!
125
//! // Create a new request of type get with the specified URI.
126
//! let mut request = CoapRequest::new(CoapMessageType::Con, CoapRequestCode::Get, uri).unwrap();
127
//!
128
//! // Send the request and wait for a response.
129
//! let req_handle = session.send_request(request).expect("Unable to send request");
130
//! loop {
131
//!     context.do_io(Some(Duration::from_secs(10))).expect("error during IO");
132
//!     // Poll for responses to a request using the request handle.
133
//!     for response in session.poll_handle(&req_handle) {
134
//!         assert_eq!(response.code(), CoapMessageCode::Response(CoapResponseCode::Content));
135
//!         assert_eq!(response.data().unwrap().as_ref(), "Hello World!".as_bytes());
136
//!         return;
137
//!     }
138
//! }
139
//! ```
140
//!
141
//! ## Server
142
//! This example runs a simple CoAP server that provides a resource under the URI path
143
//! `/hello_world` with `Hello World!` as the response payload.
144
//!
145
//! ```no_run
146
//! use std::{
147
//!     net::{SocketAddr, UdpSocket},
148
//!     time::Duration,
149
//! };
150
//!
151
//! use libcoap_rs::{
152
//!     CoapContext,
153
//!     message::{CoapMessageCommon, CoapResponse, CoapRequest},
154
//!     protocol::{CoapRequestCode, CoapResponseCode},
155
//!     CoapRequestHandler, CoapResource,
156
//!     session::{CoapSessionCommon, CoapServerSession},
157
//! };
158
//!
159
//! // a new CoAP context and bind to the generated SocketAddr.
160
//! let mut context = CoapContext::new().expect("Failed to create CoAP context");
161
//! context.add_endpoint_udp("[::1]:5683".parse().unwrap()).expect("Unable to add/bind to endpoint");
162
//!
163
//! // Create a new resource that is available at the URI path `hello_world`
164
//! // The second argument can be used to provide any kind of user-specific data, which will
165
//! // then be passed to the handler function.
166
//! let resource = CoapResource::new("hello_world", (), false);
167
//! // Set a method handler for the GET method.
168
//! resource.set_method_handler(
169
//!     CoapRequestCode::Get,
170
//!     Some(CoapRequestHandler::new(
171
//!         // The handler can be a lambda or some other kind of function.
172
//!         // Using methods is also possible by setting the resource's user data to an instance
173
//!         // of the struct, as the first argument will then be a mutable reference to the
174
//!         // user data. Methods will then use this user data as the `&mut self` reference.
175
//!         //
176
//!         // The provided CoapResponse is already filled with the correct token to be
177
//!         // interpreted as a response to the correct request by the client.
178
//!         |completed: &mut (), session: &mut CoapServerSession, request: &CoapRequest, mut response: CoapResponse| {
179
//!             // Set content of the response message to "Hello World!"
180
//!             let data = Vec::<u8>::from("Hello World!".as_bytes());
181
//!             response.set_data(Some(data));
182
//!             // Set the response code to 2.00 "Content"
183
//!             response.set_code(CoapResponseCode::Content);
184
//!             // Send the response message.
185
//!             session.send(response).expect("Unable to send response");
186
//!         },
187
//!     )),
188
//! );
189
//!
190
//! // Add the resource to the context.
191
//! context.add_resource(resource);
192
//! loop {
193
//!     // process IO in a loop...
194
//!     if let Err(e) = context.do_io(Some(Duration::from_secs(1))) {
195
//!         break;
196
//!     }
197
//!     // ...until we want to shut down.
198
//! }
199
//! // Properly shut down, completing outstanding IO requests and properly closing sessions.
200
//! context.shutdown(Some(Duration::from_secs(0))).unwrap();
201
//! ```
202

            
203
extern crate core;
204

            
205
pub use context::CoapContext;
206
pub use event::CoapEventHandler;
207
pub use resource::{CoapRequestHandler, CoapResource};
208

            
209
mod context;
210
#[cfg(dtls)]
211
pub mod crypto;
212
pub mod error;
213
mod event;
214
mod mem;
215
pub mod message;
216
pub mod prng;
217
pub mod protocol;
218
mod resource;
219
pub mod session;
220
pub mod transport;
221
pub mod types;