38#define STUN_RTO_INITIAL_MS 300
39#define STUN_RTO_MAX_MS 3000
40#define STUN_MAX_ATTEMPTS 7
41#define PARK_TIMEOUT_MS 30000
42#define HANDSHAKE_TIMEOUT_MS 30000
43#define LIVENESS_TIMEOUT_MS 30000
44#define SCTP_TICK_ACTIVE_MS 10
45#define SCTP_TICK_IDLE_MS 250
46#define SCTP_ACTIVE_WINDOW_MS 1000
52#define UDP_SOCKBUF (4 << 20)
64static void conn_timer_cb(
void *arg);
101 return tl_ctx == ctx;
124 if (
n == 0 || ctx->
udp_fd < 0)
return;
135 msgs[
i].msg_hdr.msg_iovlen = 1;
170 if (conn->
dirty)
return;
240 if (
c->remote.sin_addr.s_addr ==
remote->sin_addr.s_addr &&
241 c->remote.sin_port ==
remote->sin_port)
300 if (!conn)
return NULL;
347 table_remove(
ctx, conn);
360 conn_mark_dirty(conn);
372 conn_rearm_timer(conn);
384 conn_rearm_timer(conn);
394 conn_on_dtls_up(conn);
398 conn_rearm_timer(conn);
417 conn_mark_dirty(conn);
429 ctx_tx_queue(conn->
ctx,
msg, (
size_t)len, &conn->
remote);
440 switch (conn->
state) {
474static void conn_timer_cb(
void *arg) {
480 switch (conn->
state) {
488 conn_send_stun_request(conn);
507 conn_mark_dirty(conn);
509 conn_rearm_timer(conn);
510 ctx_leave(
ctx, prev);
530static void ctx_sctp_timer_cb(
void *arg) {
535 ctx_leave(
ctx, prev);
541 const uint8_t *data,
size_t len,
int is_string) {
555 ctx_tx_queue(conn->
ctx, data, len, &conn->
remote);
567static void sctp_out_post_cb(
void *arg) {
568 sctp_out_post *
p = arg;
572 ctx_leave(conn->
ctx, prev);
590 p->post.cb = sctp_out_post_cb;
609 if (
rlen < 0)
return;
629 table_remove(
ctx, conn);
631 conn = conn_new(
ctx,
NULL,
true);
636 table_insert(
ctx, conn);
647 conn_start_dtls(conn);
654 if (len == 0)
return;
659 if (buf[0] < 20 || buf[0] > 63)
return;
663 conn_dtls_input(conn, buf, len);
666static void udp_readable(
int fd,
void *payload);
689static void udp_readable(
int fd,
void *payload) {
710 msgs[
i].msg_hdr.msg_namelen =
sizeof(
from[
i]);
712 msgs[
i].msg_hdr.msg_iovlen = 1;
717 for (
int i = 0;
i <
n;
i++) {
738 if (
handled > 0) ctx_note_activity(ctx);
739 ctx_leave(ctx, prev);
742 ctx_arm_udp_slot(ctx);
750static void detect_host_ip(
char *out,
size_t cap) {
755 if (!
ifa->ifa_addr ||
ifa->ifa_addr->sa_family !=
AF_INET)
continue;
758 if (((
a >> 24) & 0xFF) == 127)
continue;
759 snprintf(out,
cap,
"%u.%u.%u.%u", (
a >> 24) & 0xFF, (
a >> 16) & 0xFF, (
a >> 8) & 0xFF,
771 ctx_release_resources(ctx);
809 ctx_leave(ctx, prev);
816 if (!ctx)
return NULL;
873static void *ctx_thread_main(
void *arg) {
881 if (!reactor)
return NULL;
897 if (!ctx)
return NULL;
898 if (!ctx_arm_udp_slot(ctx)) {
913 ctx_teardown_owner(ctx);
931static void ctx_free_post_cb(
void *arg) {
933 ctx_teardown_on_reactor(ctx);
938static void ctx_stop_post_cb(
void *arg) {
940 ctx_teardown_owner(ctx);
961 ctx_teardown_owner(ctx);
974 ctx_teardown_on_reactor(ctx);
1026static void park_post_cb(
void *arg) {
1038 conn_rearm_timer(conn);
1039 ctx_leave(
ctx, prev);
1053 if (!conn)
return -1;
1064static void connect_post_cb(
void *arg) {
1072 table_insert(
ctx, conn);
1073 conn_send_stun_request(conn);
1074 conn_rearm_timer(conn);
1075 ctx_leave(
ctx, prev);
1081 if (!conn)
return NULL;
1112static void kick_post_cb(
void *arg) {
1133 ctx_note_activity(conn->
ctx);
1136 conn_enqueue(conn,
rev);
1139 conn_mark_dirty(conn);
1140 ctx_leave(conn->
ctx, prev);
1165 ctx_note_activity(conn->
ctx);
1168 if (
rc == 0)
return 0;
1169 if (
rc < 0)
return -1;
1171 if (!conn_reserve(conn, len))
return -1;
1181 if (len)
memcpy(
m->data, data, len);
1182 conn_enqueue(conn,
m);
1183 conn_mark_dirty(conn);
1186 if (!conn_reserve(conn, len))
return -1;
1196 if (len)
memcpy(
m->data, data, len);
1215static void close_post_cb(
void *arg) {
1220 conn_mark_dirty(conn);
1221 ctx_leave(conn->
ctx, prev);
1233 conn_mark_dirty(conn);
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_malloc(size_t size)
Allocate zeroed memory via the CWIST owner context.
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
int cwist_dtls_fingerprint(X509 *cert, char *out, size_t cap)
Compute the SHA-256 fingerprint of a certificate in SDP format.
int cwist_dtls_generate_cert(X509 **cert_out, EVP_PKEY **pkey_out)
Generate an ephemeral self-signed ECDSA certificate for DTLS.
SSL_CTX * cwist_dtls_ctx_new(int is_server, X509 *cert, EVP_PKEY *pkey)
Create a DTLS SSL_CTX for the client or server role.
unsigned int cwist_dtls_link_mtu(void)
Return the DTLS link MTU in bytes.
BIO * cwist_dtls_bio_new(struct cwist_webrtc_conn *conn)
Datagram BIO for conn: reads return conn->dtls_in once, writes go to cwist_webrtc_conn_dtls_out() one...
int cwist_ice_stun_parse_response(const uint8_t *buf, size_t len, const uint8_t txid[12], const char *pwd, struct sockaddr_in *mapped)
Validate a received STUN binding response.
int cwist_ice_stun_is_message(const uint8_t *buf, size_t len)
Check whether a datagram looks like a STUN message.
bool cwist_ice_stun_has_use_candidate(const uint8_t *buf, size_t len)
Check whether a STUN message carries the USE-CANDIDATE attribute.
int cwist_ice_stun_sign_request(uint8_t *msg, size_t cap, size_t msg_len, const char *pwd)
Public wrapper around stun_sign() for signing a built binding request.
int cwist_ice_stun_validate_request(const uint8_t *buf, size_t len, const char *pwd)
Validate the MESSAGE-INTEGRITY of a STUN message against a password.
int cwist_ice_stun_build_request(uint8_t *out, size_t cap, const uint8_t txid[12], const char *username)
Build an unsigned ICE-controlling STUN binding request.
int cwist_ice_stun_get_remote_ufrag(const uint8_t *buf, size_t len, char *out, size_t cap)
Extract the sender's ufrag from the USERNAME attribute of a STUN message.
bool cwist_ice_stun_is_binding_request(const uint8_t *buf, size_t len)
Check whether a datagram is a STUN binding request.
void cwist_ice_random_creds(char *ufrag, size_t ufrag_cap, char *pwd, size_t pwd_cap)
Generate random ICE ufrag/pwd credentials.
int cwist_ice_stun_build_response(uint8_t *out, size_t cap, const uint8_t *req, size_t req_len, const struct sockaddr_in *mapped, const char *pwd)
Build a STUN binding response for a received binding request.
mbedTLS TLS backend: RAND_bytes over mbedTLS CTR-DRBG.
struct cwist_reactor cwist_reactor_t
bool cwist_reactor_timer_arm(cwist_reactor_t *reactor, cwist_reactor_timer_t *timer, uint64_t delay_us)
bool cwist_reactor_add(cwist_reactor_t *reactor, int fd, cwist_reactor_cb_t cb, const void *payload, size_t payload_size)
void cwist_reactor_timer_init(cwist_reactor_timer_t *timer, void(*cb)(void *ctx), void *ctx)
bool cwist_reactor_del(cwist_reactor_t *reactor, int fd)
void cwist_reactor_destroy(cwist_reactor_t *reactor)
void cwist_reactor_stop(cwist_reactor_t *reactor)
cwist_reactor_t * cwist_reactor_create(void)
void cwist_reactor_run(cwist_reactor_t *reactor)
void cwist_reactor_timer_cancel(cwist_reactor_t *reactor, cwist_reactor_timer_t *timer)
int cwist_sctp_conn_open(struct cwist_webrtc_conn *conn)
Create, bind and listen/connect the conn's SCTP socket.
int cwist_sctp_global_init(void)
One-time usrsctp initialisation (no threads, AF_CONN only).
int cwist_sctp_conn_drain(struct cwist_webrtc_conn *conn)
Accept (server role), then deliver every complete inbound message.
void cwist_sctp_tick(void)
Advance usrsctp's process-wide timer clock to now. Any thread.
bool cwist_sctp_assoc_established(struct cwist_webrtc_conn *conn)
true once the SCTP association is up.
int cwist_sctp_send(struct cwist_webrtc_conn *conn, uint16_t channel, const uint8_t *data, size_t len, int is_string)
Send one message, announcing the channel with DCEP OPEN first if we are opening it.
void cwist_sctp_conn_close(struct cwist_webrtc_conn *conn)
Abort the association and release SCTP state. Idempotent.
void cwist_sctp_conn_input(struct cwist_webrtc_conn *conn, const uint8_t *data, size_t len)
Feed one decrypted SCTP packet to usrsctp.
int cwist_sdp_write_answer(char *out, size_t cap, const char *fingerprint, const char *ufrag, const char *pwd, const char *mid, const char *host, uint16_t port)
Generate an SDP answer for an ICE-lite DataChannel endpoint.
int cwist_sdp_parse(const char *sdp, size_t len, cwist_sdp_info *info)
Parse an SDP blob into a cwist_sdp_info structure.
#define SSL_ERROR_SYSCALL
#define SSL_ERROR_ZERO_RETURN
struct cwist_mtls_ctx SSL_CTX
Fields the ICE-lite agent needs from an SDP description.
One peer: ICE state, DTLS session, SCTP association.
cwist_reactor_post_t reg_post
struct socket * sctp_sock
struct cwist_webrtc_conn * hnext
struct sockaddr_in remote
struct cwist_webrtc_conn * dirty_next
cwist_webrtc_msg * pending_head
cwist_webrtc_msg * pending_tail
cwist_reactor_post_t kick_post
cwist_reactor_post_t close_post
cwist_reactor_timer_t timer
A UDP endpoint bound to one reactor, serving many conns.
uint64_t last_activity_ns
cwist_webrtc_close_cb close_cb
cwist_reactor_t * reactor
cwist_webrtc_message_cb msg_cb
cwist_reactor_post_t free_post
struct sockaddr_in tx_addr[CWIST_WEBRTC_TX_BATCH]
cwist_webrtc_conn ** buckets
cwist_webrtc_conn * dirty
cwist_reactor_timer_t sctp_timer
cwist_webrtc_channel_cb ch_cb
atomic_int ice_lite_server
size_t tx_len[CWIST_WEBRTC_TX_BATCH]
cwist_webrtc_conn * parked
A DataChannel message on its way into SCTP.
struct cwist_webrtc_msg * next
void cwist_webrtc_conn_on_channel_open(struct cwist_webrtc_conn *conn, uint16_t channel, const char *label)
Report a channel the peer opened to the channel handler.
#define STUN_RTO_INITIAL_MS
void cwist_webrtc_conn_close(cwist_webrtc_conn *conn)
Close a connection (SCTP abort, DTLS teardown). Safe from any thread; the close handler runs on the r...
const char * cwist_webrtc_ctx_fingerprint(const cwist_webrtc_ctx *ctx)
Local certificate fingerprint (SHA-256, colon-separated hex).
void cwist_webrtc_ctx_set_close_handler(cwist_webrtc_ctx *ctx, cwist_webrtc_close_cb cb, void *user)
Set the callback for closed connections.
void cwist_webrtc_conn_sctp_out(struct cwist_webrtc_conn *conn, const void *buffer, size_t len)
usrsctp output for conn; marshalled to the owner thread when needed.
#define SCTP_TICK_IDLE_MS
size_t cwist_webrtc_conn_buffered_amount(const cwist_webrtc_conn *conn)
Bytes accepted by cwist_webrtc_conn_send() and not yet handed to SCTP (like RTCDataChannel....
cwist_webrtc_ctx * cwist_webrtc_ctx_new(uint16_t port)
Create a WebRTC context bound to a UDP port (0 picks an ephemeral port), running on its own reactor t...
cwist_webrtc_ctx * cwist_webrtc_ctx_new_on(cwist_reactor_t *reactor, uint16_t port)
Create a WebRTC context on a reactor the caller runs.
int cwist_webrtc_ctx_connection_count(const cwist_webrtc_ctx *ctx)
Number of connections whose SCTP association is up.
cwist_webrtc_conn * cwist_webrtc_connect(cwist_webrtc_ctx *ctx, const struct sockaddr_in *remote, const char *peer_ufrag, const char *peer_pwd)
Offering-side dialer used by the loopback tests: registers a client conn to remote and starts ICE.
void cwist_webrtc_ctx_free(cwist_webrtc_ctx *ctx)
Close every connection and free the context.
void cwist_webrtc_conn_release(cwist_webrtc_conn *conn)
Drop a reference taken with cwist_webrtc_conn_retain().
bool cwist_webrtc_on_owner(const cwist_webrtc_ctx *ctx)
true when the calling thread is servicing ctx right now.
void cwist_webrtc_conn_dtls_out(struct cwist_webrtc_conn *conn, const uint8_t *data, size_t len)
Queue one datagram to the conn's peer (owner thread).
#define SCTP_ACTIVE_WINDOW_MS
#define LIVENESS_TIMEOUT_MS
#define HANDSHAKE_TIMEOUT_MS
#define STUN_MAX_ATTEMPTS
#define SCTP_TICK_ACTIVE_MS
void cwist_webrtc_ctx_set_message_handler(cwist_webrtc_ctx *ctx, cwist_webrtc_message_cb cb, void *user)
Set the callback for incoming DataChannel messages. Set handlers before answering the first offer.
void cwist_webrtc_conn_retain(cwist_webrtc_conn *conn)
Take a reference on conn so it stays valid outside a callback.
uint16_t cwist_webrtc_ctx_port(const cwist_webrtc_ctx *ctx)
UDP port the context is bound to.
int cwist_webrtc_conn_send(cwist_webrtc_conn *conn, uint16_t channel_id, const uint8_t *data, size_t len, cwist_webrtc_data_type type)
Send a message on a DataChannel.
int cwist_webrtc_handle_offer(cwist_webrtc_ctx *ctx, const char *offer, char *answer_out, size_t out_len)
Answer a browser SDP offer (ICE-lite, setup:passive).
void cwist_webrtc_conn_on_message(struct cwist_webrtc_conn *conn, uint16_t channel, const uint8_t *data, size_t len, int is_string)
Deliver a complete inbound message to the message handler.
void cwist_webrtc_ctx_set_channel_handler(cwist_webrtc_ctx *ctx, cwist_webrtc_channel_cb cb, void *user)
Set the callback for incoming DataChannel open (DCEP) events.
void(* cwist_webrtc_channel_cb)(cwist_webrtc_conn *conn, uint16_t channel_id, const char *label, void *user)
Optional DataChannel open notification (remote peer opened a channel).
@ CWIST_WEBRTC_DATA_STRING
@ CWIST_WEBRTC_DATA_BINARY
void(* cwist_webrtc_close_cb)(cwist_webrtc_conn *conn, void *user)
Optional connection-closed notification. Runs once per connection whose SCTP association came up; aft...
void(* cwist_webrtc_message_cb)(cwist_webrtc_conn *conn, uint16_t channel_id, const uint8_t *data, size_t len, cwist_webrtc_data_type type, void *user)
DataChannel message callback, invoked on the ctx's reactor thread.
Internal types and cross-file helpers for the WebRTC DataChannel module (ICE-lite + DTLS + SCTP over ...
#define CWIST_WEBRTC_DGRAM_MAX
#define CWIST_WEBRTC_RX_BATCH
#define CWIST_WEBRTC_TX_BATCH
#define CWIST_WEBRTC_MAX_MESSAGE
#define CWIST_WEBRTC_MAX_BUFFERED