Skip to content

Commit cc49792

Browse files
committed
merge: pull request #12 from petardimitrijevic/coap_tcp
2 parents 600470b + 4b0e3c1 commit cc49792

File tree

6 files changed

+189
-11
lines changed

6 files changed

+189
-11
lines changed

libcoap/src/context.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use crate::{
4141
error::{ContextCreationError, EndpointCreationError, IoProcessError},
4242
resource::{CoapResource, UntypedCoapResource},
4343
session::session_response_handler,
44-
transport::{CoapEndpoint, CoapUdpEndpoint},
44+
transport::{CoapEndpoint, CoapUdpEndpoint, CoapTcpEndpoint},
4545
};
4646

4747
#[derive(Debug)]
@@ -239,10 +239,16 @@ impl CoapContext<'_> {
239239
Ok(())
240240
}
241241

242-
/// TODO
242+
/// Creates a new TCP endpoint that is bound to the given address.
243243
#[cfg(feature = "tcp")]
244-
pub fn add_endpoint_tcp(&mut self, _addr: SocketAddr) -> Result<(), EndpointCreationError> {
245-
todo!()
244+
pub fn add_endpoint_tcp(&mut self, addr: SocketAddr) -> Result<(), EndpointCreationError> {
245+
// SAFETY: Because we never return an owned reference to the endpoint, it cannot outlive the
246+
// context it is bound to (i.e. this one).
247+
let endpoint = unsafe { CoapTcpEndpoint::new(self, addr)? }.into();
248+
let mut inner_ref = self.inner.borrow_mut();
249+
inner_ref.endpoints.push(endpoint);
250+
// Cannot fail, we just pushed to the Vec.
251+
Ok(())
246252
}
247253

248254
/// Creates a new DTLS endpoint that is bound to the given address.
@@ -607,7 +613,7 @@ impl CoapContext<'_> {
607613
/// (will cause an abort on drop)
608614
/// - Calling `coap_free_context()` on this context (for obvious reasons, this will probably
609615
/// cause a segfault if you don't immediately [std::mem::forget()] the CoapContext and never
610-
/// use anything related to the context again, but why would you do that?)
616+
/// use anything related to the context again, but why would you do that?)
611617
// Kept here for consistency, even though it is unused.
612618
#[allow(unused)]
613619
pub(crate) unsafe fn as_raw_context(&self) -> &coap_context_t {
@@ -630,7 +636,7 @@ impl CoapContext<'_> {
630636
/// (will cause an abort on drop)
631637
/// - Calling `coap_free_context()` on this context (for obvious reasons, this will probably
632638
/// cause a segfault if you don't immediately [std::mem::forget()] the CoapContext and never
633-
/// use anything related to the context again, but why would you do that?)
639+
/// use anything related to the context again, but why would you do that?)
634640
pub(crate) unsafe fn as_mut_raw_context(&mut self) -> &mut coap_context_t {
635641
// SAFETY: raw_context is checked to be a valid pointer on struct instantiation, cannot be
636642
// freed by anything outside of here (assuming the contract of this function is kept), and

libcoap/src/event.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use std::fmt::Debug;
1313

1414
use libcoap_sys::{coap_event_t, coap_session_get_context, coap_session_t};
15+
use libcoap_sys::{coap_session_get_type, coap_session_type_t};
1516

1617
use crate::context::CoapContext;
1718
use crate::session::CoapSession;
@@ -110,11 +111,17 @@ pub trait CoapEventHandler: Debug {
110111
// This should be fine as we don't provide this type to a FFI function, we only read from it.
111112
#[allow(improper_ctypes_definitions)]
112113
pub(crate) unsafe extern "C" fn event_handler_callback(raw_session: *mut coap_session_t, event: coap_event_t) -> i32 {
113-
let session: CoapSession = if event == coap_event_t::COAP_EVENT_SERVER_SESSION_NEW {
114+
let raw_session_type = coap_session_get_type(raw_session);
115+
116+
let session: CoapSession = if event == coap_event_t::COAP_EVENT_SERVER_SESSION_NEW
117+
|| (event == coap_event_t::COAP_EVENT_TCP_CONNECTED
118+
&& raw_session_type == coap_session_type_t::COAP_SESSION_TYPE_SERVER)
119+
{
114120
CoapServerSession::initialize_raw(raw_session).into()
115121
} else {
116122
CoapSession::from_raw(raw_session)
117123
};
124+
118125
// SAFETY: Pointer is always valid as long as there is no bug in libcoap.
119126
let context = CoapContext::from_raw(coap_session_get_context(raw_session));
120127
context.handle_event(session, event);

libcoap/src/session/client.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,41 @@ impl CoapClientSession<'_> {
146146
})
147147
}
148148

149+
150+
/// Create a new unencrypted session with the given peer over TCP.
151+
///
152+
/// # Errors
153+
/// Will return a [SessionCreationError] if libcoap was unable to create a session (most likely
154+
/// because it was not possible to bind to a port).
155+
pub fn connect_tcp<'a>(
156+
ctx: &mut CoapContext<'a>,
157+
addr: SocketAddr,
158+
) -> Result<CoapClientSession<'a>, SessionCreationError> {
159+
// SAFETY: self.raw_context is guaranteed to be valid, local_if can be null.
160+
let session = unsafe {
161+
coap_new_client_session(
162+
ctx.as_mut_raw_context(),
163+
std::ptr::null(),
164+
CoapAddress::from(addr).as_raw_address(),
165+
coap_proto_t::COAP_PROTO_TCP,
166+
)
167+
};
168+
if session.is_null() {
169+
return Err(SessionCreationError::Unknown);
170+
}
171+
// SAFETY: Session was just checked for validity, no crypto info was provided to
172+
// coap_new_client_session().
173+
Ok(unsafe {
174+
CoapClientSession::new(
175+
session as *mut coap_session_t,
176+
#[cfg(feature = "dtls")]
177+
None,
178+
#[cfg(feature = "dtls")]
179+
None,
180+
)
181+
})
182+
}
183+
149184
/// Initializes a new CoapClientSession from its raw counterpart with the provided initial
150185
/// information.
151186
///

libcoap/src/transport/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ use libcoap_sys::{coap_endpoint_set_default_mtu, coap_endpoint_t};
1515
pub use dtls::CoapDtlsEndpoint;
1616
pub use udp::CoapUdpEndpoint;
1717

18+
#[cfg(feature = "tcp")]
19+
pub use tcp::CoapTcpEndpoint;
20+
1821
#[cfg(feature = "dtls")]
1922
mod dtls;
2023
#[cfg(feature = "tcp")]
@@ -63,6 +66,8 @@ pub trait EndpointCommon {
6366
#[derive(Debug)]
6467
pub enum CoapEndpoint {
6568
Udp(CoapUdpEndpoint),
69+
#[cfg(feature = "tcp")]
70+
Tcp(CoapTcpEndpoint),
6671
#[cfg(feature = "dtls")]
6772
Dtls(CoapDtlsEndpoint),
6873
}
@@ -73,6 +78,13 @@ impl From<CoapUdpEndpoint> for CoapEndpoint {
7378
}
7479
}
7580

81+
#[cfg(feature = "tcp")]
82+
impl From<CoapTcpEndpoint> for CoapEndpoint {
83+
fn from(ep: CoapTcpEndpoint) -> Self {
84+
CoapEndpoint::Tcp(ep)
85+
}
86+
}
87+
7688
#[cfg(feature = "dtls")]
7789
impl From<CoapDtlsEndpoint> for CoapEndpoint {
7890
fn from(ep: CoapDtlsEndpoint) -> Self {
@@ -84,6 +96,8 @@ impl EndpointCommon for CoapEndpoint {
8496
unsafe fn as_raw_endpoint(&self) -> &coap_endpoint_t {
8597
match self {
8698
CoapEndpoint::Udp(ep) => ep.as_raw_endpoint(),
99+
#[cfg(feature = "tcp")]
100+
CoapEndpoint::Tcp(ep) => ep.as_raw_endpoint(),
87101
#[cfg(feature = "dtls")]
88102
CoapEndpoint::Dtls(ep) => ep.as_raw_endpoint(),
89103
}
@@ -92,6 +106,8 @@ impl EndpointCommon for CoapEndpoint {
92106
unsafe fn as_mut_raw_endpoint(&mut self) -> &mut coap_endpoint_t {
93107
match self {
94108
CoapEndpoint::Udp(ep) => ep.as_mut_raw_endpoint(),
109+
#[cfg(feature = "tcp")]
110+
CoapEndpoint::Tcp(ep) => ep.as_mut_raw_endpoint(),
95111
#[cfg(feature = "dtls")]
96112
CoapEndpoint::Dtls(ep) => ep.as_mut_raw_endpoint(),
97113
}

libcoap/src/transport/tcp.rs

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,86 @@
11
// SPDX-License-Identifier: BSD-2-Clause
22
/*
3-
* transport/dtls.rs - transport-specific code for TCP.
3+
* transport/tcp.rs - transport-specific code for TCP.
44
* This file is part of the libcoap-rs crate, see the README and LICENSE files for
55
* more information and terms of use.
66
* Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved.
77
* See the README as well as the LICENSE file for more information.
88
*/
99

10-
/// TODO
11-
#[allow(dead_code)]
10+
use libcoap_sys::{coap_endpoint_t, coap_free_endpoint, coap_new_endpoint, coap_proto_t::COAP_PROTO_TCP};
11+
12+
use crate::{context::CoapContext, error::EndpointCreationError, transport::EndpointCommon, types::CoapAddress};
13+
14+
use std::net::SocketAddr;
15+
1216
#[cfg(feature = "tcp")]
13-
pub struct CoapTcpEndpoint {}
17+
#[derive(Debug)]
18+
pub struct CoapTcpEndpoint {
19+
raw_endpoint: *mut coap_endpoint_t,
20+
}
21+
22+
impl CoapTcpEndpoint {
23+
/// Creates a new CoapTcpEndpoint and binds it to the supplied SocketAddr.
24+
///
25+
/// This is an unsafe function (see #Safety for an explanation of why) used internally by
26+
/// libcoap-rs to instantiate new endpoints. You should most likely not use this function, and
27+
/// use one of the following alternatives instead:
28+
/// - If you just want to add an endpoint to the coap context, use [CoapContext::add_endpoint_tcp()].
29+
/// - If you need to modify the underlying [coap_endpoint_t] directly (in an unsafe manner), use
30+
/// [CoapContext::add_endpoint_tcp()] to instantiate the endpoint and then [as_mut_raw_endpoint()]
31+
/// to access the underlying struct.
32+
///
33+
/// # Safety
34+
/// All endpoint types defined in this crate contain a [coap_endpoint_t] instance,
35+
/// which is the representation of endpoints used by the underlying libcoap C library.
36+
///
37+
/// On instantiation, these [coap_endpoint_t] instances are bound to a context, which includes
38+
/// adding them to a list maintained by the [CoapContext] (or – to be more specific – the
39+
/// underlying [libcoap_sys::coap_context_t].
40+
///
41+
/// When the context that this endpoint is bound to is dropped, the context calls [libcoap_sys::coap_free_context()],
42+
/// which will not only free the context, but also all [coap_endpoint_t] instances associated
43+
/// with it, including the one this struct points to.
44+
///
45+
/// Therefore, if you decide to use this function anyway, you have to ensure that the
46+
/// CoapContext lives at least as long as this struct does.
47+
/// Also note that unlike [CoapContext::add_endpoint_tcp()], this function does not add the
48+
/// endpoint to the [CoapContext::endpoints] vector, while the underlying [coap_endpoint_t] is
49+
/// added to the underlying [libcoap_sys::coap_context_t]
50+
pub(crate) unsafe fn new(context: &mut CoapContext, addr: SocketAddr) -> Result<Self, EndpointCreationError> {
51+
let endpoint = coap_new_endpoint(
52+
context.as_mut_raw_context(),
53+
CoapAddress::from(addr).as_raw_address(),
54+
COAP_PROTO_TCP,
55+
);
56+
if endpoint.is_null() {
57+
return Err(EndpointCreationError::Unknown);
58+
}
59+
Ok(Self { raw_endpoint: endpoint })
60+
}
61+
}
62+
63+
impl EndpointCommon for CoapTcpEndpoint {
64+
unsafe fn as_raw_endpoint(&self) -> &coap_endpoint_t {
65+
// SAFETY: raw_endpoint is checked to be a valid pointer on struct instantiation, cannot be
66+
// freed by anything outside of here (assuming the contract of this function is kept), and
67+
// the default (elided) lifetimes are correct (the pointer is valid as long as the endpoint
68+
// is).
69+
&*self.raw_endpoint
70+
}
71+
72+
unsafe fn as_mut_raw_endpoint(&mut self) -> &mut coap_endpoint_t {
73+
// SAFETY: raw_endpoint is checked to be a valid pointer on struct instantiation, is not
74+
// freed by anything outside of here (assuming the contract of this function is kept), and
75+
// the default (elided) lifetimes are correct (the pointer is valid as long as the endpoint
76+
// is).
77+
&mut *self.raw_endpoint
78+
}
79+
}
80+
81+
impl Drop for CoapTcpEndpoint {
82+
fn drop(&mut self) {
83+
// SAFETY: Raw endpoint is guaranteed to exist for as long as the container exists.
84+
unsafe { coap_free_endpoint(self.raw_endpoint) }
85+
}
86+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: BSD-2-Clause
2+
/*
3+
* tcp_client_server_test.rs - Tests for TCP clients+servers.
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+
use libcoap_rs::session::CoapClientSession;
11+
use libcoap_rs::{
12+
message::CoapMessageCommon,
13+
protocol::{CoapMessageCode, CoapResponseCode},
14+
session::CoapSessionCommon,
15+
CoapContext,
16+
};
17+
use std::time::Duration;
18+
19+
mod common;
20+
21+
#[test]
22+
pub fn basic_client_server_request() {
23+
let server_address = common::get_unused_server_addr();
24+
25+
let server_handle = common::spawn_test_server(move |context| context.add_endpoint_tcp(server_address).unwrap());
26+
27+
let mut context = CoapContext::new().unwrap();
28+
let session = CoapClientSession::connect_tcp(&mut context, server_address).unwrap();
29+
30+
let request = common::gen_test_request(server_address);
31+
let req_handle = session.send_request(request).unwrap();
32+
loop {
33+
assert!(context.do_io(Some(Duration::from_secs(10))).expect("error during IO") <= Duration::from_secs(10));
34+
for response in session.poll_handle(&req_handle) {
35+
assert_eq!(response.code(), CoapMessageCode::Response(CoapResponseCode::Content));
36+
assert_eq!(response.data().unwrap().as_ref(), "Hello World!".as_bytes());
37+
server_handle.join().unwrap();
38+
return;
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)