summaryrefslogtreecommitdiffstats
path: root/PICA_media.h
diff options
context:
space:
mode:
Diffstat (limited to 'PICA_media.h')
-rw-r--r--PICA_media.h177
1 files changed, 177 insertions, 0 deletions
diff --git a/PICA_media.h b/PICA_media.h
new file mode 100644
index 0000000..971b918
--- /dev/null
+++ b/PICA_media.h
@@ -0,0 +1,177 @@
+/*
+ (c) Copyright 2012 - 2026 Anton Sviridenko
+ https://picapica.im
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 3.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/* mediac2c connections - call media over UDP, protected by DTLS.
+ *
+ * A mediac2c connection exists only while a call is in progress and only
+ * between two clients that already have an established directc2c connection.
+ * It reuses that connection's address and port number with UDP instead of
+ * TCP, so nothing has to be announced or negotiated over the c2c connection:
+ * the client that made the outgoing directc2c connection is the DTLS client,
+ * the one that accepted it is the DTLS server.
+ *
+ * Media packets are sent over this connection whenever it is established and
+ * over the c2c/directc2c connection otherwise; the receiving side accepts
+ * them over either transport at any time, so there is no moment where the
+ * media stops while the transport changes.
+ */
+
+#ifndef PICA_MEDIA_H
+#define PICA_MEDIA_H
+
+#include "PICA_client.h"
+
+/* Link MTU assumed for a mediac2c connection until path MTU discovery
+ * (RFC 8899) is implemented. 1200 octets is the value QUIC uses as the size
+ * every path is expected to carry.
+ */
+#define PICA_MEDIA_LINK_MTU 1200
+/* IPv4 + UDP headers */
+#define PICA_MEDIA_IPUDP_OVERHEAD 28
+
+/* Amount of media data that certainly fits into one datagram of a mediac2c
+ * connection: the assumed link MTU less a generous allowance for the IP, UDP,
+ * DTLS record and protocol message headers, whose exact sizes depend on the
+ * ciphersuite that ends up being negotiated.
+ * It lets the media be produced in datagram sized pieces from the start of a
+ * call, before it is known whether a media connection will come up at all,
+ * rather than having to change size in the middle of one.
+ */
+#define PICA_MEDIA_SAFE_PAYLOAD (PICA_MEDIA_LINK_MTU - 100)
+
+/* How long a call keeps trying to bring the media connection up before
+ * giving up and carrying the media over the c2c connection for the rest of
+ * the call, in seconds.
+ */
+#define PICA_MEDIA_HANDSHAKE_TIMEOUT 10
+
+/* An established media connection with no datagram received for this many
+ * seconds is considered broken; media falls back to the c2c connection.
+ */
+#define PICA_MEDIA_SILENCE_TIMEOUT 10
+
+/* Ping is sent over an established media connection after this many seconds
+ * without anything being sent, to hold NAT and firewall state open.
+ */
+#define PICA_MEDIA_KEEPALIVE_INTERVAL 4
+
+#define PICA_MEDIA_READBUFSIZE 4096
+
+/* transport of the media packets of a call, reported by call_media_transport_cb */
+#define PICA_CALL_TRANSPORT_C2C 0
+#define PICA_CALL_TRANSPORT_MEDIAC2C 1
+
+enum PICA_media_state
+{
+ PICA_MEDIA_STATE_NEW = 0,
+ /* DTLS server role, socket bound, waiting for the first datagram */
+ PICA_MEDIA_STATE_LISTENING,
+ PICA_MEDIA_STATE_HANDSHAKE,
+ PICA_MEDIA_STATE_ACTIVE,
+ PICA_MEDIA_STATE_FAILED
+};
+
+struct PICA_media_listener
+{
+ const struct PICA_acc *acc;
+ SOCKET sck;
+ int public_port;
+ int local_port;
+
+ /* The media connection currently using this listener's socket, if any.
+ * Only one call can be in progress at a time, so there is at most one.
+ */
+ struct PICA_media_channel *channel;
+};
+
+struct PICA_media_channel
+{
+ struct PICA_c2c *c2c;
+ /* set when this side has the DTLS server role */
+ struct PICA_media_listener *listener;
+
+ SOCKET sck;
+ /* 0 when sck belongs to the listener and must not be closed with the
+ * channel
+ */
+ int owns_sck;
+ struct sockaddr_in peer_addr;
+
+ SSL *ssl;
+ X509 *peer_cert;
+ int is_dtls_client;
+
+ enum PICA_media_state state;
+ time_t started;
+ time_t last_rx;
+ time_t last_tx;
+
+ unsigned int max_payload;
+
+ unsigned char read_buf[PICA_MEDIA_READBUFSIZE];
+ unsigned int read_pos;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* implemented in PICA_media.c */
+
+int PICA_media_listener_new(const struct PICA_acc *acc, int public_port, int local_port,
+ struct PICA_media_listener **ml);
+void PICA_media_listener_close(struct PICA_media_listener *ml);
+
+/**
+ * Brings up a media connection for a call that has just become active.
+ * Does nothing if the preconditions (an established directc2c connection,
+ * and a media listener on the accepting side) are not met - the media then
+ * stays on the c2c connection.
+ */
+void PICA_media_start(struct PICA_c2c *c2c);
+void PICA_media_close(struct PICA_c2c *c2c);
+
+int PICA_media_is_active(const struct PICA_c2c *c2c);
+/**
+ * Largest amount of data one media packet can carry over this c2c connection,
+ * i.e. what has to fit into one datagram once a media connection is up.
+ */
+unsigned int PICA_media_max_payload(const struct PICA_c2c *c2c);
+
+/**
+ * Sends one already formatted protocol message over the media connection.
+ * The message is dropped, and PICA_OK returned, if the send would block -
+ * for real time media that is the wanted behaviour.
+ */
+int PICA_media_send(struct PICA_c2c *c2c, const unsigned char *buf, unsigned int len);
+
+void PICA_media_fdset(struct PICA_c2n *c2n, fd_set *rfds, int *nfds);
+void PICA_media_process(struct PICA_c2n *c2n, fd_set *rfds);
+
+/* implemented in PICA_client.c, used by PICA_media.c */
+
+int PICA_verify_callback(int preverify_ok, X509_STORE_CTX *ctx);
+int PICA_verify_peer_cert_common(X509 **peer_cert, SSL *ssl, const unsigned char *peer_id);
+unsigned int PICA_media_process_messages(unsigned char *buf, unsigned int *read_pos, struct PICA_c2c *c2c);
+void PICA_media_transport_changed(struct PICA_c2c *c2c, int transport, const char *ciphersuitename);
+int PICA_media_send_ping(struct PICA_c2c *c2c, int reply);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif