From: Jack Lau via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> To: ffmpeg-devel@ffmpeg.org Cc: Jack Lau <code@ffmpeg.org> Subject: [FFmpeg-devel] [PATCH] avformat/tls_gnutls: add dtls support (PR #20254) Date: Sat, 16 Aug 2025 13:45:42 +0300 (EEST) Message-ID: <20250816104542.2DF4468D312@ffbox0-bg.ffmpeg.org> (raw) 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".
reply other threads:[~2025-08-16 10:45 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20250816104542.2DF4468D312@ffbox0-bg.ffmpeg.org \ --to=ffmpeg-devel@ffmpeg.org \ --cc=code@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
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