* [FFmpeg-devel] [PATCH] avformat/tls_gnutls: add dtls support (PR #20254)
@ 2025-08-16 10:45 Jack Lau via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: Jack Lau via ffmpeg-devel @ 2025-08-16 10:45 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Jack Lau
PR #20254 opened by Jack Lau (JackLau)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20254
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20254.patch
From a303893bdfb1ca740a36ba517d5780ec828ad850 Mon Sep 17 00:00:00 2001
From: Jack Lau <jacklau1222@qq.com>
Date: Wed, 13 Aug 2025 10:04:33 +0800
Subject: [PATCH 1/2] avformat/tls_gnutls: cleanup the pointer name
Pure cleanup, no functional changes
Unify local pointer names to `TLSContext *c` and `TLSShared *s` to
reduce confusion from inconsistent names (e.g. p, s, c)
Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
libavformat/tls_gnutls.c | 58 ++++++++++++++++++++--------------------
1 file changed, 29 insertions(+), 29 deletions(-)
diff --git a/libavformat/tls_gnutls.c b/libavformat/tls_gnutls.c
index df251ad79c..e894765d7c 100644
--- a/libavformat/tls_gnutls.c
+++ b/libavformat/tls_gnutls.c
@@ -149,66 +149,66 @@ static ssize_t gnutls_url_push(gnutls_transport_ptr_t transport,
static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
{
- TLSContext *p = h->priv_data;
- TLSShared *c = &p->tls_shared;
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
int ret;
ff_gnutls_init();
- if ((ret = ff_tls_open_underlying(c, h, uri, options)) < 0)
+ if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
goto fail;
- gnutls_init(&p->session, c->listen ? GNUTLS_SERVER : GNUTLS_CLIENT);
- if (!c->listen && !c->numerichost)
- gnutls_server_name_set(p->session, GNUTLS_NAME_DNS, c->host, strlen(c->host));
- gnutls_certificate_allocate_credentials(&p->cred);
- if (c->ca_file) {
- ret = gnutls_certificate_set_x509_trust_file(p->cred, c->ca_file, GNUTLS_X509_FMT_PEM);
+ gnutls_init(&c->session, s->listen ? GNUTLS_SERVER : GNUTLS_CLIENT);
+ if (!s->listen && !s->numerichost)
+ gnutls_server_name_set(c->session, GNUTLS_NAME_DNS, s->host, strlen(s->host));
+ gnutls_certificate_allocate_credentials(&c->cred);
+ if (s->ca_file) {
+ ret = gnutls_certificate_set_x509_trust_file(c->cred, s->ca_file, GNUTLS_X509_FMT_PEM);
if (ret < 0)
av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret));
}
#if GNUTLS_VERSION_NUMBER >= 0x030020
else
- gnutls_certificate_set_x509_system_trust(p->cred);
+ gnutls_certificate_set_x509_system_trust(c->cred);
#endif
- gnutls_certificate_set_verify_flags(p->cred, c->verify ?
+ gnutls_certificate_set_verify_flags(c->cred, s->verify ?
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT : 0);
- if (c->cert_file && c->key_file) {
- ret = gnutls_certificate_set_x509_key_file(p->cred,
- c->cert_file, c->key_file,
+ if (s->cert_file && s->key_file) {
+ ret = gnutls_certificate_set_x509_key_file(c->cred,
+ s->cert_file, s->key_file,
GNUTLS_X509_FMT_PEM);
if (ret < 0) {
av_log(h, AV_LOG_ERROR,
"Unable to set cert/key files %s and %s: %s\n",
- c->cert_file, c->key_file, gnutls_strerror(ret));
+ s->cert_file, s->key_file, gnutls_strerror(ret));
ret = AVERROR(EIO);
goto fail;
}
- } else if (c->cert_file || c->key_file)
+ } else if (s->cert_file || s->key_file)
av_log(h, AV_LOG_ERROR, "cert and key required\n");
- gnutls_credentials_set(p->session, GNUTLS_CRD_CERTIFICATE, p->cred);
- gnutls_transport_set_pull_function(p->session, gnutls_url_pull);
- gnutls_transport_set_push_function(p->session, gnutls_url_push);
- gnutls_transport_set_ptr(p->session, p);
- gnutls_set_default_priority(p->session);
+ gnutls_credentials_set(c->session, GNUTLS_CRD_CERTIFICATE, c->cred);
+ gnutls_transport_set_pull_function(c->session, gnutls_url_pull);
+ gnutls_transport_set_push_function(c->session, gnutls_url_push);
+ gnutls_transport_set_ptr(c->session, c);
+ gnutls_set_default_priority(c->session);
do {
if (ff_check_interrupt(&h->interrupt_callback)) {
ret = AVERROR_EXIT;
goto fail;
}
- ret = gnutls_handshake(p->session);
+ ret = gnutls_handshake(c->session);
if (gnutls_error_is_fatal(ret)) {
ret = print_tls_error(h, ret);
goto fail;
}
} while (ret);
- p->need_shutdown = 1;
- if (c->verify) {
+ c->need_shutdown = 1;
+ if (s->verify) {
unsigned int status, cert_list_size;
gnutls_x509_crt_t cert;
const gnutls_datum_t *cert_list;
- if ((ret = gnutls_certificate_verify_peers2(p->session, &status)) < 0) {
+ if ((ret = gnutls_certificate_verify_peers2(c->session, &status)) < 0) {
av_log(h, AV_LOG_ERROR, "Unable to verify peer certificate: %s\n",
gnutls_strerror(ret));
ret = AVERROR(EIO);
@@ -219,19 +219,19 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
ret = AVERROR(EIO);
goto fail;
}
- if (gnutls_certificate_type_get(p->session) != GNUTLS_CRT_X509) {
+ if (gnutls_certificate_type_get(c->session) != GNUTLS_CRT_X509) {
av_log(h, AV_LOG_ERROR, "Unsupported certificate type\n");
ret = AVERROR(EIO);
goto fail;
}
gnutls_x509_crt_init(&cert);
- cert_list = gnutls_certificate_get_peers(p->session, &cert_list_size);
+ cert_list = gnutls_certificate_get_peers(c->session, &cert_list_size);
gnutls_x509_crt_import(cert, cert_list, GNUTLS_X509_FMT_DER);
- ret = gnutls_x509_crt_check_hostname(cert, c->host);
+ ret = gnutls_x509_crt_check_hostname(cert, s->host);
gnutls_x509_crt_deinit(cert);
if (!ret) {
av_log(h, AV_LOG_ERROR,
- "The certificate's owner does not match hostname %s\n", c->host);
+ "The certificate's owner does not match hostname %s\n", s->host);
ret = AVERROR(EIO);
goto fail;
}
--
2.49.1
From 1bfba8ae15c53001fd10c812a11bf8338697592a Mon Sep 17 00:00:00 2001
From: Jack Lau <jacklau1222@qq.com>
Date: Tue, 12 Aug 2025 15:35:01 +0800
Subject: [PATCH 2/2] avformat/tls_gnutls: add dtls support
Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
configure | 2 +-
libavformat/tls_gnutls.c | 498 +++++++++++++++++++++++++++++++++++++--
2 files changed, 484 insertions(+), 16 deletions(-)
diff --git a/configure b/configure
index e1809a3e58..7f14576390 100755
--- a/configure
+++ b/configure
@@ -3884,7 +3884,7 @@ tcp_protocol_select="network"
tls_protocol_deps_any="gnutls openssl schannel securetransport libtls mbedtls"
tls_protocol_select="tcp_protocol"
# TODO: Support libtls, mbedtls, and gnutls.
-dtls_protocol_deps_any="openssl schannel"
+dtls_protocol_deps_any="openssl schannel gnutls"
dtls_protocol_select="udp_protocol"
udp_protocol_select="network"
udplite_protocol_select="network"
diff --git a/libavformat/tls_gnutls.c b/libavformat/tls_gnutls.c
index e894765d7c..cfd81ec575 100644
--- a/libavformat/tls_gnutls.c
+++ b/libavformat/tls_gnutls.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
#include <gnutls/x509.h>
#include "avformat.h"
@@ -29,8 +30,11 @@
#include "os_support.h"
#include "url.h"
#include "tls.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/thread.h"
+#include "libavutil/random_seed.h"
#ifndef GNUTLS_VERSION_NUMBER
#define GNUTLS_VERSION_NUMBER LIBGNUTLS_VERSION_NUMBER
@@ -41,6 +45,293 @@
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
+static int pkey_to_pem_string(gnutls_x509_privkey_t key, char *out, size_t out_sz)
+{
+ size_t size = 0;
+ int ret = 0;
+
+ ret = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, NULL, &size);
+ if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_privkey_export size: %s\n", gnutls_strerror(ret));
+ return AVERROR(EINVAL);
+ }
+
+ if (size > out_sz) {
+ av_log(NULL, AV_LOG_ERROR, "Private key PEM string size %zu is large than %zu\n", size, out_sz);
+ return AVERROR(EINVAL);
+ }
+
+ ret = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, out, &size);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_privkey_export: %s\n", gnutls_strerror(ret));
+ return AVERROR(EINVAL);
+ }
+ out[size] = '\0';
+ return ret;
+}
+
+static int crt_to_pem_string(gnutls_x509_crt_t crt, char *out, size_t out_sz)
+{
+ size_t size = 0;
+ int ret = 0;
+
+ ret = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, NULL, &size);
+ if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_export size: %s\n", gnutls_strerror(ret));
+ return AVERROR(EINVAL);
+ }
+
+ if (size > out_sz) {
+ av_log(NULL, AV_LOG_ERROR, "Certificate PEM string size %zu is large than %zu\n", size, out_sz);
+ return AVERROR(EINVAL);
+ }
+
+ ret = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, out, &size);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_export: %s\n", gnutls_strerror(ret));
+ return AVERROR(EINVAL);
+ }
+ out[size] = '\0';
+ return ret;
+}
+
+static int gnutls_x509_fingerprint(gnutls_x509_crt_t cert, char **fingerprint)
+{
+ unsigned char *buf = NULL;
+ size_t buf_size = 0;
+ AVBPrint pbuf;
+ int ret;
+
+ ret = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, NULL, &buf_size);
+ if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ return AVERROR(EINVAL);
+ }
+ buf = av_malloc(buf_size);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, buf, &buf_size)) {
+ av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint, %s\n",
+ gnutls_strerror(ret));
+ return AVERROR(ENOMEM);
+ }
+
+ av_bprint_init(&pbuf, buf_size*3, buf_size*3);
+
+ for (int i = 0; i < buf_size - 1; i++)
+ av_bprintf(&pbuf, "%02X:", buf[i]);
+ av_bprintf(&pbuf, "%02X", buf[buf_size - 1]);
+
+ return av_bprint_finalize(&pbuf, fingerprint);
+}
+
+int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint)
+{
+ int ret = 0;
+ AVBPrint key_bp, cert_bp;
+ gnutls_x509_crt_t crt = NULL;
+ gnutls_x509_privkey_t key = NULL;
+ gnutls_datum_t temp;
+
+ av_bprint_init(&key_bp, 1, MAX_CERTIFICATE_SIZE);
+ av_bprint_init(&cert_bp, 1, MAX_CERTIFICATE_SIZE);
+
+ ret = ff_url_read_all(key_url, &key_bp);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "TLS: Failed to open key file %s\n", key_url);
+ goto end;
+ }
+
+ ret = ff_url_read_all(cert_url, &cert_bp);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "TLS: Failed to open cert file %s\n", cert_url);
+ goto end;
+ }
+
+ ret = gnutls_x509_privkey_init(&key);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_privkey_init: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_init: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ temp.data = key_bp.str;
+ temp.size = key_bp.len;
+ ret = gnutls_x509_privkey_import(key, &temp, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_privkey_import: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ temp.data = cert_bp.str;
+ temp.size = cert_bp.len;
+ ret = gnutls_x509_crt_import(crt, &temp, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_import: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = pkey_to_pem_string(key, key_buf, key_sz);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Failed to convert private key to PEM string\n");
+ goto end;
+ }
+
+ ret = crt_to_pem_string(crt, cert_buf, cert_sz);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Failed to convert certificate to PEM string\n");
+ goto end;
+ }
+
+ ret = gnutls_x509_fingerprint(crt, fingerprint);
+ if (ret < 0)
+ av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint\n");
+end:
+ av_bprint_finalize(&key_bp, NULL);
+ av_bprint_finalize(&cert_bp, NULL);
+ if (crt)
+ gnutls_x509_crt_deinit(crt);
+ if (key)
+ gnutls_x509_privkey_deinit(key);
+ return ret;
+}
+
+static int gnutls_gen_private_key(gnutls_x509_privkey_t *key)
+{
+ int ret = 0;
+
+ ret = gnutls_x509_privkey_init(key);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_privkey_init: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_privkey_generate(*key, GNUTLS_PK_ECDSA,
+ gnutls_sec_param_to_pk_bits(GNUTLS_PK_ECDSA, GNUTLS_SEC_PARAM_MEDIUM), 0);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_privkey_generate: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+end:
+ return ret;
+}
+
+static int gnutls_gen_certificate(gnutls_x509_privkey_t key, gnutls_x509_crt_t *crt, char **fingerprint)
+{
+ int ret = 0;
+ uint64_t serial;
+ unsigned char buf[8];
+ const char *dn = "CN=lavf";
+
+ ret = gnutls_x509_crt_init(crt);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_init: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_crt_set_version(*crt, 3);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_set_version: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ /**
+ * See https://gnutls.org/manual/gnutls.html#gnutls_005fx509_005fcrt_005fset_005fserial-1
+ * The provided serial should be a big-endian positive number (i.e. its leftmost bit should be zero).
+ */
+ serial = av_get_random_seed();
+ AV_WB64(buf, serial);
+ buf[0] &= 0x7F;
+ ret = gnutls_x509_crt_set_serial(*crt, buf, sizeof(buf));
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_set_serial: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_crt_set_activation_time(*crt, time(NULL));
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_set_activation_time: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_crt_set_expiration_time(*crt, time(NULL) + 365 * 24 * 60 * 60);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_set_expiration_time: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_crt_set_dn(*crt, dn, NULL);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_set_dn: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_crt_set_issuer_dn(*crt, dn, NULL);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_set_issuer_dn: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_crt_set_key(*crt, key);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_set_key: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_crt_sign2(*crt, *crt, key, GNUTLS_DIG_SHA256, 0);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_x509_crt_sign2: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_x509_fingerprint(*crt, fingerprint);
+ if (ret < 0)
+ av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint\n");
+end:
+ return ret;
+}
+
+int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint)
+{
+ int ret;
+ gnutls_x509_crt_t crt = NULL;
+ gnutls_x509_privkey_t key = NULL;
+
+ ret = gnutls_gen_private_key(&key);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_gen_private_key: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = gnutls_gen_certificate(key, &crt, fingerprint);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "gnutls_gen_certificate: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ ret = pkey_to_pem_string(key, key_buf, key_sz);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Failed to convert private key to PEM string\n");
+ goto end;
+ }
+
+ ret = crt_to_pem_string(crt, cert_buf, cert_sz);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Failed to convert certificate to PEM string\n");
+ goto end;
+ }
+end:
+ if (crt)
+ gnutls_x509_crt_deinit(crt);
+ if (key)
+ gnutls_x509_privkey_deinit(key);
+ return ret;
+}
+
typedef struct TLSContext {
const AVClass *class;
TLSShared tls_shared;
@@ -48,6 +339,8 @@ typedef struct TLSContext {
gnutls_certificate_credentials_t cred;
int need_shutdown;
int io_err;
+ struct sockaddr_storage dest_addr;
+ socklen_t dest_addr_len;
} TLSContext;
static AVMutex gnutls_mutex = AV_MUTEX_INITIALIZER;
@@ -97,16 +390,44 @@ static int print_tls_error(URLContext *h, int ret)
return AVERROR(EIO);
}
+int ff_tls_set_external_socket(URLContext *h, URLContext *sock)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+
+ if (s->is_dtls)
+ c->tls_shared.udp = sock;
+ else
+ c->tls_shared.tcp = sock;
+
+ return 0;
+}
+
+int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t materials_sz)
+{
+ int ret = 0;
+ TLSContext *c = h->priv_data;
+
+ ret = gnutls_srtp_get_keys(c->session, dtls_srtp_materials, materials_sz, NULL, NULL, NULL, NULL);
+ if (ret < 0) {
+ av_log(c, AV_LOG_ERROR, "Failed to export SRTP material, %s\n", gnutls_strerror(ret));
+ return -1;
+ }
+ return 0;
+}
+
static int tls_close(URLContext *h)
{
TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
if (c->need_shutdown)
gnutls_bye(c->session, GNUTLS_SHUT_WR);
if (c->session)
gnutls_deinit(c->session);
if (c->cred)
gnutls_certificate_free_credentials(c->cred);
- ffurl_closep(&c->tls_shared.tcp);
+ if (!s->external_sock)
+ ffurl_closep(s->is_dtls ? &s->udp : &s->tcp);
ff_gnutls_deinit();
return 0;
}
@@ -115,7 +436,7 @@ static ssize_t gnutls_url_pull(gnutls_transport_ptr_t transport,
void *buf, size_t len)
{
TLSContext *c = (TLSContext*) transport;
- int ret = ffurl_read(c->tls_shared.tcp, buf, len);
+ int ret = ffurl_read(c->tls_shared.is_dtls ? c->tls_shared.udp : c->tls_shared.tcp, buf, len);
if (ret >= 0)
return ret;
if (ret == AVERROR_EXIT)
@@ -133,7 +454,7 @@ static ssize_t gnutls_url_push(gnutls_transport_ptr_t transport,
const void *buf, size_t len)
{
TLSContext *c = (TLSContext*) transport;
- int ret = ffurl_write(c->tls_shared.tcp, buf, len);
+ int ret = ffurl_write(c->tls_shared.is_dtls ? c->tls_shared.udp : c->tls_shared.tcp, buf, len);
if (ret >= 0)
return ret;
if (ret == AVERROR_EXIT)
@@ -147,18 +468,88 @@ static ssize_t gnutls_url_push(gnutls_transport_ptr_t transport,
return -1;
}
-static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
+static int gnutls_pull_timeout(gnutls_transport_ptr_t ptr, unsigned int ms) {
+ TLSContext *c = (TLSContext*)ptr;
+ TLSShared *s = &c->tls_shared;
+ int ret;
+ fd_set read_fds;
+ struct timeval timeout;
+ int sockfd = ffurl_get_file_handle(s->udp);
+ if (sockfd < 0)
+ return 0;
+
+ FD_ZERO(&read_fds);
+ FD_SET(sockfd, &read_fds);
+
+ timeout.tv_sec = ms / 1000;
+ timeout.tv_usec = (ms % 1000) * 1000;
+
+ ret = select(sockfd + 1, &read_fds, NULL, NULL, &timeout);
+ if (ret > 0)
+ return 1;
+ return 0;
+}
+
+static int dtls_handshake(URLContext *h)
{
TLSContext *c = h->priv_data;
TLSShared *s = &c->tls_shared;
int ret;
+ s->udp->flags &= ~AVIO_FLAG_NONBLOCK;
+
+ if (s->listen && !c->dest_addr_len) {
+ char buf[1500];
+ while(1) {
+ ret = ffurl_read(s->udp, buf, sizeof(buf));
+ if (ret == AVERROR(EAGAIN))
+ continue;
+ if (ret > 0)
+ break;
+ if (ret < 0) {
+ c->io_err = ret;
+ return ret;
+ }
+ }
+
+ ff_udp_get_last_recv_addr(s->udp, &c->dest_addr, &c->dest_addr_len);
+ ret = ff_udp_set_remote_addr(s->udp, (struct sockaddr *)&c->dest_addr, c->dest_addr_len, 1);
+ if (ret < 0) {
+ av_log(c, AV_LOG_ERROR, "Failed connecting udp context\n");
+ return ret;
+ }
+ av_log(c, AV_LOG_TRACE, "Set UDP remote addr on UDP socket, now 'connected'\n");
+ }
+
+ ret = gnutls_handshake(c->session);
+ if (ret < 0) {
+ av_log(h, AV_LOG_ERROR, "Handshake failed: %s\n", gnutls_strerror(ret));
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+ int ret;
+ unsigned int gnutls_flags = s->is_dtls ? GNUTLS_DATAGRAM : 0;
+
ff_gnutls_init();
- if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
- goto fail;
+ if (!s->external_sock) {
+ if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
+ goto fail;
+ }
- gnutls_init(&c->session, s->listen ? GNUTLS_SERVER : GNUTLS_CLIENT);
+ if (s->listen)
+ gnutls_flags |= GNUTLS_SERVER;
+ else
+ gnutls_flags |= GNUTLS_CLIENT;
+
+ gnutls_init(&c->session, gnutls_flags);
if (!s->listen && !s->numerichost)
gnutls_server_name_set(c->session, GNUTLS_NAME_DNS, s->host, strlen(s->host));
gnutls_certificate_allocate_credentials(&c->cred);
@@ -184,20 +575,64 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
ret = AVERROR(EIO);
goto fail;
}
- } else if (s->cert_file || s->key_file)
- av_log(h, AV_LOG_ERROR, "cert and key required\n");
+ } else if (s->cert_buf && s->key_buf) {
+ gnutls_datum_t crt_data, key_data;
+ crt_data.data = s->cert_buf;
+ crt_data.size = strlen(s->cert_buf);
+ key_data.data = s->key_buf;
+ key_data.size = strlen(s->key_buf);
+ ret = gnutls_certificate_set_x509_key_mem(c->cred, &crt_data, &key_data, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ av_log(h, AV_LOG_ERROR, "Unable to set cert/key memory: %s\n", gnutls_strerror(ret));
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+ } else if (s->listen && !s->cert_buf && !s->key_buf && !s->cert_file && !s->key_file) {
+ gnutls_x509_crt_t crt = NULL;
+ gnutls_x509_privkey_t key = NULL;
+
+ av_log(h, AV_LOG_VERBOSE, "No server certificate provided, using self-signed\n");
+ ret = gnutls_gen_private_key(&key);
+ if (ret < 0)
+ goto fail;
+
+ ret = gnutls_gen_certificate(key, &crt, NULL);
+ if (ret < 0)
+ goto fail;
+ ret = gnutls_certificate_set_x509_key(c->cred, &crt, 1, key);
+ if (ret < 0) {
+ av_log(h, AV_LOG_ERROR, "Unable to set certificate and key: %s\n", gnutls_strerror(ret));
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ }
gnutls_credentials_set(c->session, GNUTLS_CRD_CERTIFICATE, c->cred);
gnutls_transport_set_pull_function(c->session, gnutls_url_pull);
gnutls_transport_set_push_function(c->session, gnutls_url_push);
gnutls_transport_set_ptr(c->session, c);
+ if (s->is_dtls) {
+ gnutls_transport_set_pull_timeout_function(c->session, gnutls_pull_timeout);
+ if (s->mtu)
+ gnutls_dtls_set_mtu(c->session, s->mtu);
+ }
gnutls_set_default_priority(c->session);
+ if (s->use_srtp) {
+ int ret = gnutls_srtp_set_profile(c->session, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80);
+ if (ret < 0) {
+ av_log(c, AV_LOG_ERROR, "Unable to set SRTP profile: %s\n", gnutls_strerror(ret));
+ }
+ }
do {
+ if (s->external_sock)
+ break;
if (ff_check_interrupt(&h->interrupt_callback)) {
ret = AVERROR_EXIT;
goto fail;
}
-
- ret = gnutls_handshake(c->session);
+ if (s->is_dtls)
+ ret = dtls_handshake(h);
+ else
+ ret = gnutls_handshake(c->session);
if (gnutls_error_is_fatal(ret)) {
ret = print_tls_error(h, ret);
goto fail;
@@ -243,13 +678,23 @@ fail:
return ret;
}
+static int dtls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+ s->is_dtls = 1;
+ return tls_open(h, uri, flags, options);
+}
+
static int tls_read(URLContext *h, uint8_t *buf, int size)
{
TLSContext *c = h->priv_data;
+ URLContext *uc = c->tls_shared.is_dtls ? c->tls_shared.udp
+ : c->tls_shared.tcp;
int ret;
// Set or clear the AVIO_FLAG_NONBLOCK on c->tls_shared.tcp
- c->tls_shared.tcp->flags &= ~AVIO_FLAG_NONBLOCK;
- c->tls_shared.tcp->flags |= h->flags & AVIO_FLAG_NONBLOCK;
+ uc->flags &= ~AVIO_FLAG_NONBLOCK;
+ uc->flags |= h->flags & AVIO_FLAG_NONBLOCK;
ret = gnutls_record_recv(c->session, buf, size);
if (ret > 0)
return ret;
@@ -261,10 +706,12 @@ static int tls_read(URLContext *h, uint8_t *buf, int size)
static int tls_write(URLContext *h, const uint8_t *buf, int size)
{
TLSContext *c = h->priv_data;
+ URLContext *uc = c->tls_shared.is_dtls ? c->tls_shared.udp
+ : c->tls_shared.tcp;
int ret;
// Set or clear the AVIO_FLAG_NONBLOCK on c->tls_shared.tcp
- c->tls_shared.tcp->flags &= ~AVIO_FLAG_NONBLOCK;
- c->tls_shared.tcp->flags |= h->flags & AVIO_FLAG_NONBLOCK;
+ uc->flags &= ~AVIO_FLAG_NONBLOCK;
+ uc->flags |= h->flags & AVIO_FLAG_NONBLOCK;
ret = gnutls_record_send(c->session, buf, size);
if (ret > 0)
return ret;
@@ -309,3 +756,24 @@ const URLProtocol ff_tls_protocol = {
.flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class = &tls_class,
};
+
+static const AVClass dtls_class = {
+ .class_name = "dtls",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_dtls_protocol = {
+ .name = "dtls",
+ .url_open2 = dtls_open,
+ .url_handshake = dtls_handshake,
+ .url_read = tls_read,
+ .url_write = tls_write,
+ .url_close = tls_close,
+ .url_get_file_handle = tls_get_file_handle,
+ .url_get_short_seek = tls_get_short_seek,
+ .priv_data_size = sizeof(TLSContext),
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+ .priv_data_class = &dtls_class,
+};
--
2.49.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-08-16 10:45 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-08-16 10:45 [FFmpeg-devel] [PATCH] avformat/tls_gnutls: add dtls support (PR #20254) Jack Lau via ffmpeg-devel
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git