From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id CF63E4FDD3 for ; Wed, 2 Jul 2025 16:59:30 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 2C0C268D452; Wed, 2 Jul 2025 19:57:29 +0300 (EEST) Received: from btbn.de (btbn.de [144.76.60.213]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id 9C08168D24A for ; Wed, 2 Jul 2025 19:57:11 +0300 (EEST) Received: from [authenticated] by btbn.de (Postfix) with ESMTPSA id 0D779281910B0; Wed, 02 Jul 2025 18:57:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rothenpieler.org; s=mail; t=1751475430; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=P69lru1hTAONSrSPOZkKaVAYXqqFbXAOHomJVeGzJuU=; b=QBQnDUwrmZJpBy3d6ZFVe3mBDD47cPS9YjxZ6MIX+snJ/A+/31u+d+Sbqjhl9SJgpkPF7E E6RV6mBsSLq7EpwFeSKFACLS5xfUs9gUrAu925kcmLzhRtS/SViVd6W3tz5IuVdyi+gRt8 Rwi6cKvnVqZo4ndxLhY+3Fp0ZLFzTjkYCA36ap/6B+WGz5yJyypXKVUOMP2WSxe+ONB5P9 XYdySljdu4XwHCYRAXbmanIBZnk5WsadbyOS5uVpnHhgJUpLjx8WFqBipKix/bqQuENXDH NDL6uYgLC2/eQ8xyQcOyMOeU4T44xWVMQLUWkOd+GkqTwnhyg3MZVTJRomJRiA== From: Timo Rothenpieler To: ffmpeg-devel@ffmpeg.org Date: Wed, 2 Jul 2025 18:56:46 +0200 Message-ID: <20250702165655.1325-19-timo@rothenpieler.org> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250702165655.1325-1-timo@rothenpieler.org> References: <20250702165655.1325-1-timo@rothenpieler.org> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 18/18] avformat/tls_schannel: add DTLS support X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Timo Rothenpieler Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: --- configure | 6 +- libavformat/tls_schannel.c | 853 ++++++++++++++++++++++++++++++++++--- 2 files changed, 801 insertions(+), 58 deletions(-) diff --git a/configure b/configure index 976a21b931..d11e583e4f 100755 --- a/configure +++ b/configure @@ -3854,7 +3854,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" +dtls_protocol_deps_any="openssl schannel" dtls_protocol_select="udp_protocol" udp_protocol_select="network" udplite_protocol_select="network" @@ -7266,8 +7266,10 @@ enabled securetransport && enabled schannel && check_func_headers "windows.h security.h" InitializeSecurityContext -DSECURITY_WIN32 -lsecur32 && + check_func_headers "windows.h ncrypt.h" NCryptOpenStorageProvider -DSECURITY_WIN32 -lncrypt && + check_func_headers "windows.h wincrypt.h" CertCreateSelfSignCertificate -DSECURITY_WIN32 -lcrypt32 && test_cpp_condition winerror.h "defined(SEC_I_CONTEXT_EXPIRED)" && - schannel_extralibs="-lsecur32" || + schannel_extralibs="-lsecur32 -lncrypt -lcrypt32" || disable schannel makeinfo --version > /dev/null 2>&1 && enable makeinfo || disable makeinfo diff --git a/libavformat/tls_schannel.c b/libavformat/tls_schannel.c index ae9a311d2a..3e4df7a372 100644 --- a/libavformat/tls_schannel.c +++ b/libavformat/tls_schannel.c @@ -32,6 +32,7 @@ #include #include #include +#include #define SCHANNEL_INITIAL_BUFFER_SIZE 4096 #define SCHANNEL_FREE_BUFFER_SIZE 1024 @@ -41,6 +42,508 @@ #define SECBUFFER_ALERT 17 #endif +#define FF_NCRYPT_TEMP_KEY_NAME L"FFMPEG_TEMP_TLS_KEY" + +static int der_to_pem(const char *data, size_t len, const char *header, char *buf, size_t bufsize) +{ + const int line_length = 64; + AVBPrint pem; + DWORD base64len = 0; + char *base64 = NULL; + int ret = 0; + + if (!CryptBinaryToStringA(data, len, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &base64len)) { + av_log(NULL, AV_LOG_ERROR, "CryptBinaryToString failed\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + + base64 = av_malloc(base64len); + + if (!CryptBinaryToStringA(data, len, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, base64, &base64len)) { + av_log(NULL, AV_LOG_ERROR, "CryptBinaryToString failed\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + + av_bprint_init_for_buffer(&pem, buf, bufsize); + av_bprintf(&pem, "-----BEGIN %s-----\n", header); + + for (DWORD i = 0; i < base64len; i += line_length) { + av_bprintf(&pem, "%.*s\n", line_length, base64 + i); + } + + av_bprintf(&pem, "-----END %s-----\n", header); + + if (!av_bprint_is_complete(&pem)) { + ret = AVERROR(ENOSPC); + goto end; + } + +end: + av_free(base64); + return ret; +} + +static int pem_to_der(const char *pem, char **buf, int *out_len) +{ + DWORD derlen = 0; + + if (!CryptStringToBinaryA(pem, 0, CRYPT_STRING_BASE64HEADER, NULL, &derlen, NULL, NULL)) { + av_log(NULL, AV_LOG_ERROR, "CryptStringToBinaryA failed\n"); + return AVERROR(EINVAL); + } + + *buf = av_malloc(derlen); + if (!*buf) + return AVERROR(ENOMEM); + + if (!CryptStringToBinaryA(pem, 0, CRYPT_STRING_BASE64HEADER, *buf, &derlen, NULL, NULL)) { + av_log(NULL, AV_LOG_ERROR, "CryptStringToBinaryA failed\n"); + return AVERROR(EINVAL); + } + + *out_len = derlen; + + return 0; +} + +static int der_to_fingerprint(const char *data, size_t len, char **fingerprint) +{ + AVBPrint buf; + unsigned char hash[32]; + DWORD hashsize = sizeof(hash); + + if (!CryptHashCertificate2(BCRYPT_SHA256_ALGORITHM, 0, NULL, data, len, hash, &hashsize)) + { + av_log(NULL, AV_LOG_ERROR, "CryptHashCertificate2 failed\n"); + return AVERROR_EXTERNAL; + } + + av_bprint_init(&buf, hashsize*3, hashsize*3); + + for (int i = 0; i < hashsize - 1; i++) + av_bprintf(&buf, "%02X:", hash[i]); + av_bprintf(&buf, "%02X", hash[hashsize - 1]); + + return av_bprint_finalize(&buf, fingerprint); +} + +static int tls_gen_self_signed(NCRYPT_KEY_HANDLE *key, PCCERT_CONTEXT *crtctx) +{ + NCRYPT_PROV_HANDLE provider = 0; + CERT_NAME_BLOB subject = { 0 }; + + DWORD export_props = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG; + DWORD usage_props = NCRYPT_ALLOW_ALL_USAGES; + LPCSTR ext_usages[] = { szOID_PKIX_KP_SERVER_AUTH }; + BYTE key_usage = CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DIGITAL_SIGNATURE_KEY_USAGE; + CRYPT_BIT_BLOB key_usage_blob = { 0 }; + CERT_ENHKEY_USAGE eku = { 0 }; + CERT_BASIC_CONSTRAINTS2_INFO basic_constraints = { 0 }; + CERT_ALT_NAME_ENTRY san_entry = { 0 }; + CERT_ALT_NAME_INFO san_info = { 0 }; + CERT_EXTENSION ext[4] = { 0 }; + CERT_EXTENSIONS exts = { 0 }; + CRYPT_ALGORITHM_IDENTIFIER sig_alg = { (LPSTR)szOID_ECDSA_SHA256 }; + CRYPT_KEY_PROV_INFO prov_info = { 0 }; + const char *subj_str = "CN=lavf"; + + SECURITY_STATUS sspi_ret; + int ret = 0; + + *crtctx = NULL; + + sspi_ret = NCryptOpenStorageProvider(&provider, MS_KEY_STORAGE_PROVIDER, 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptOpenStorageProvider failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + sspi_ret = NCryptCreatePersistedKey(provider, key, BCRYPT_ECDSA_P256_ALGORITHM, FF_NCRYPT_TEMP_KEY_NAME, 0, NCRYPT_OVERWRITE_KEY_FLAG); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptCreatePersistedKey failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + sspi_ret = NCryptSetProperty(*key, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)&export_props, sizeof(export_props), 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptSetProperty(NCRYPT_EXPORT_POLICY_PROPERTY) failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + sspi_ret = NCryptSetProperty(*key, NCRYPT_KEY_USAGE_PROPERTY, (PBYTE)&usage_props, sizeof(usage_props), 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptSetProperty(NCRYPT_KEY_USAGE_PROPERTY) failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + sspi_ret = NCryptFinalizeKey(*key, 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptFinalizeKey failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + if (!CertStrToNameA(X509_ASN_ENCODING, subj_str, 0, NULL, NULL, &subject.cbData, NULL)) + { + av_log(NULL, AV_LOG_ERROR, "Initial subj init failed\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + subject.pbData = av_malloc(subject.cbData); + if (!subject.pbData) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if (!CertStrToNameA(X509_ASN_ENCODING, subj_str, 0, NULL, subject.pbData, &subject.cbData, NULL)) + { + av_log(NULL, AV_LOG_ERROR, "Subj init failed\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + // Extended Key Usage extension + eku.cUsageIdentifier = 1; + eku.rgpszUsageIdentifier = (LPSTR*)ext_usages; + + if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, &eku, + CRYPT_ENCODE_ALLOC_FLAG, NULL, &ext[0].Value.pbData, &ext[0].Value.cbData)) { + av_log(NULL, AV_LOG_ERROR, "CryptEncodeObjectEx for EKU failed\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + ext[0].pszObjId = (LPSTR)szOID_ENHANCED_KEY_USAGE; + ext[0].fCritical = TRUE; + + // Key usage extension + key_usage_blob.cbData = sizeof(key_usage); + key_usage_blob.pbData = &key_usage; + + if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_BITS, &key_usage_blob, + CRYPT_ENCODE_ALLOC_FLAG, NULL, &ext[1].Value.pbData, &ext[1].Value.cbData)) { + av_log(NULL, AV_LOG_ERROR, "CryptEncodeObjectEx for KU failed\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + ext[1].pszObjId = (LPSTR)szOID_KEY_USAGE; + ext[1].fCritical = TRUE; + + // Cert Basic Constraints + basic_constraints.fCA = FALSE; + + if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_BASIC_CONSTRAINTS2, &basic_constraints, + CRYPT_ENCODE_ALLOC_FLAG, NULL, &ext[2].Value.pbData, &ext[2].Value.cbData)) { + av_log(NULL, AV_LOG_ERROR, "CryptEncodeObjectEx for KU failed\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + ext[2].pszObjId = (LPSTR)szOID_BASIC_CONSTRAINTS2; + ext[2].fCritical = TRUE; + + // Subject Alt Names + san_entry.dwAltNameChoice = CERT_ALT_NAME_DNS_NAME; + san_entry.pwszDNSName = (LPWSTR)L"localhost"; + + san_info.cAltEntry = 1; + san_info.rgAltEntry = &san_entry; + + if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME, &san_info, + CRYPT_ENCODE_ALLOC_FLAG, NULL, &ext[3].Value.pbData, &ext[3].Value.cbData)) { + av_log(NULL, AV_LOG_ERROR, "CryptEncodeObjectEx for KU failed\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + ext[3].pszObjId = (LPSTR)szOID_SUBJECT_ALT_NAME2; + ext[3].fCritical = TRUE; + + exts.cExtension = 4; + exts.rgExtension = ext; + + prov_info.pwszProvName = (LPWSTR)MS_KEY_STORAGE_PROVIDER; + prov_info.pwszContainerName = (LPWSTR)FF_NCRYPT_TEMP_KEY_NAME; + prov_info.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID; + + *crtctx = CertCreateSelfSignCertificate(*key, &subject, 0, &prov_info, &sig_alg, NULL, NULL, &exts); + if (!*crtctx) { + av_log(NULL, AV_LOG_ERROR, "CertCreateSelfSignCertificate failed: %lu\n", GetLastError()); + ret = AVERROR_EXTERNAL; + goto fail; + } + + NCryptFreeObject(provider); + av_free(subject.pbData); + for (int i = 0; i < FF_ARRAY_ELEMS(ext); i++) + LocalFree(ext[i].Value.pbData); + + return 0; + +fail: + if (*crtctx) + CertFreeCertificateContext(*crtctx); + if (*key) + if (NCryptDeleteKey(*key, NCRYPT_SILENT_FLAG) != ERROR_SUCCESS) + NCryptFreeObject(*key); + if (provider) + NCryptFreeObject(provider); + if (subject.pbData) + av_free(subject.pbData); + for (int i = 0; i < FF_ARRAY_ELEMS(ext); i++) + if (ext[i].Value.pbData) + LocalFree(ext[i].Value.pbData); + + *key = 0; + *crtctx = NULL; + + return ret; +} + +static int tls_export_key_cert(NCRYPT_KEY_HANDLE key, PCCERT_CONTEXT crtctx, + char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint) +{ + DWORD keysize = 0; + char *keybuf = NULL; + + SECURITY_STATUS sspi_ret; + int ret = 0; + + sspi_ret = NCryptExportKey(key, 0, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, NULL, NULL, 0, &keysize, 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "Initial NCryptExportKey failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto end; + } + + keybuf = av_malloc(keysize); + if (!keybuf) { + ret = AVERROR(ENOMEM); + goto end; + } + + sspi_ret = NCryptExportKey(key, 0, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, NULL, keybuf, keysize, &keysize, 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "Initial NCryptExportKey failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto end; + } + + ret = der_to_pem(keybuf, keysize, "PRIVATE KEY", key_buf, key_sz); + if (ret < 0) + goto end; + + ret = der_to_pem(crtctx->pbCertEncoded, crtctx->cbCertEncoded, "CERTIFICATE", cert_buf, cert_sz); + if (ret < 0) + goto end; + + ret = der_to_fingerprint(crtctx->pbCertEncoded, crtctx->cbCertEncoded, fingerprint); + if (ret < 0) + goto end; + +end: + av_free(keybuf); + return ret; +} + +int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint) +{ + NCRYPT_KEY_HANDLE key = 0; + PCCERT_CONTEXT crtctx = NULL; + + int ret = tls_gen_self_signed(&key, &crtctx); + if (ret < 0) + goto end; + + ret = tls_export_key_cert(key, crtctx, key_buf, key_sz, cert_buf, cert_sz, fingerprint); + if (ret < 0) + goto end; + +end: + if (key) + if (NCryptDeleteKey(key, NCRYPT_SILENT_FLAG) != ERROR_SUCCESS) + NCryptFreeObject(key); + if (crtctx) + CertFreeCertificateContext(crtctx); + + return ret; +} + +static int tls_import_key_cert(char *key_buf, char *cert_buf, NCRYPT_KEY_HANDLE *key, PCCERT_CONTEXT *crtctx) +{ + NCRYPT_PROV_HANDLE provider = 0; + + DWORD export_props = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG; + DWORD usage_props = NCRYPT_ALLOW_ALL_USAGES; + NCryptBufferDesc buffer_desc = { 0 }; + NCryptBuffer buffer = { 0 }; + CRYPT_KEY_PROV_INFO prov_info = { 0 }; + + int key_der_len = 0, cert_der_len = 0; + char *key_der = NULL, *cert_der = NULL; + + SECURITY_STATUS sspi_ret; + int ret = 0; + + ret = pem_to_der(key_buf, &key_der, &key_der_len); + if (ret < 0) + goto fail; + + ret = pem_to_der(cert_buf, &cert_der, &cert_der_len); + if (ret < 0) + goto fail; + + sspi_ret = NCryptOpenStorageProvider(&provider, MS_KEY_STORAGE_PROVIDER, 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptOpenStorageProvider failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + buffer_desc.ulVersion = NCRYPTBUFFER_VERSION; + buffer_desc.cBuffers = 1; + buffer_desc.pBuffers = &buffer; + + buffer.BufferType = NCRYPTBUFFER_PKCS_KEY_NAME; + buffer.pvBuffer = (LPWSTR)FF_NCRYPT_TEMP_KEY_NAME; + buffer.cbBuffer = sizeof(FF_NCRYPT_TEMP_KEY_NAME); + + sspi_ret = NCryptImportKey(provider, 0, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, &buffer_desc, key, key_der, key_der_len, NCRYPT_DO_NOT_FINALIZE_FLAG | NCRYPT_OVERWRITE_KEY_FLAG); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptImportKey failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + sspi_ret = NCryptSetProperty(*key, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)&export_props, sizeof(export_props), 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptSetProperty(NCRYPT_EXPORT_POLICY_PROPERTY) failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + sspi_ret = NCryptSetProperty(*key, NCRYPT_KEY_USAGE_PROPERTY, (PBYTE)&usage_props, sizeof(usage_props), 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptSetProperty(NCRYPT_KEY_USAGE_PROPERTY) failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + sspi_ret = NCryptFinalizeKey(*key, 0); + if (sspi_ret != ERROR_SUCCESS) { + av_log(NULL, AV_LOG_ERROR, "NCryptFinalizeKey failed(0x%lx)\n", sspi_ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + *crtctx = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert_der, cert_der_len); + if (!*crtctx) { + av_log(NULL, AV_LOG_ERROR, "CertCreateCertificateContext failed: %lu\n", GetLastError()); + ret = AVERROR_EXTERNAL; + goto fail; + } + + if (!CertSetCertificateContextProperty(*crtctx, CERT_NCRYPT_KEY_HANDLE_PROP_ID, 0, key)) { + av_log(NULL, AV_LOG_ERROR, "CertSetCertificateContextProperty(CERT_NCRYPT_KEY_HANDLE_PROP_ID) failed: %lu\n", GetLastError()); + ret = AVERROR_EXTERNAL; + goto fail; + } + + prov_info.pwszProvName = (LPWSTR)MS_KEY_STORAGE_PROVIDER; + prov_info.pwszContainerName = (LPWSTR)FF_NCRYPT_TEMP_KEY_NAME; + prov_info.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID; + + if (!CertSetCertificateContextProperty(*crtctx, CERT_KEY_PROV_INFO_PROP_ID, 0, &prov_info)) { + av_log(NULL, AV_LOG_ERROR, "CertSetCertificateContextProperty(CERT_KEY_PROV_INFO_PROP_ID) failed: %lu\n", GetLastError()); + ret = AVERROR_EXTERNAL; + goto fail; + } + + goto end; + +fail: + if (*key) + if (NCryptDeleteKey(*key, NCRYPT_SILENT_FLAG) != ERROR_SUCCESS) + NCryptFreeObject(*key); + if (*crtctx) + CertFreeCertificateContext(*crtctx); + + *key = 0; + *crtctx = NULL; + +end: + if (key_der) + av_free(key_der); + if (cert_der) + av_free(cert_der); + if (provider) + NCryptFreeObject(provider); + return ret; +} + +static int tls_load_key_cert(char *key_url, char *cert_url, NCRYPT_KEY_HANDLE *key, PCCERT_CONTEXT *crtctx) +{ + AVBPrint key_bp, cert_bp; + int ret = 0; + + av_bprint_init(&key_bp, 1, MAX_CERTIFICATE_SIZE); + av_bprint_init(&cert_bp, 1, MAX_CERTIFICATE_SIZE); + + /* Read key file. */ + ret = ff_url_read_all(key_url, &key_bp); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "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, "Failed to open cert file %s\n", cert_url); + goto end; + } + + ret = tls_import_key_cert(key_bp.str, cert_bp.str, key, crtctx); + if (ret < 0) + goto end; + +end: + av_bprint_finalize(&key_bp, NULL); + av_bprint_finalize(&cert_bp, NULL); + + return ret; +} + +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) +{ + NCRYPT_KEY_HANDLE key = 0; + PCCERT_CONTEXT crtctx = NULL; + + int ret = tls_load_key_cert(key_url, cert_url, &key, &crtctx); + if (ret < 0) + goto end; + + ret = tls_export_key_cert(key, crtctx, key_buf, key_sz, cert_buf, cert_sz, fingerprint); + if (ret < 0) + goto end; + +end: + if (key) + if (NCryptDeleteKey(key, NCRYPT_SILENT_FLAG) != ERROR_SUCCESS) + NCryptFreeObject(key); + if (crtctx) + CertFreeCertificateContext(crtctx); + + return ret; +} + typedef struct TLSContext { const AVClass *class; TLSShared tls_shared; @@ -49,6 +552,7 @@ typedef struct TLSContext { TimeStamp cred_timestamp; CtxtHandle ctxt_handle; + int have_context; TimeStamp ctxt_timestamp; ULONG request_flags; @@ -69,6 +573,67 @@ typedef struct TLSContext { int sspi_close_notify; } TLSContext; +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) +{ + TLSContext *c = h->priv_data; + + SecPkgContext_KeyingMaterialInfo keying_info = { 0 }; + SecPkgContext_KeyingMaterial keying_material = { 0 }; + + const char* dst = "EXTRACTOR-dtls_srtp"; + SECURITY_STATUS sspi_ret; + + if (!c->have_context) + return AVERROR(EINVAL); + + keying_info.cbLabel = strlen(dst) + 1; + keying_info.pszLabel = (LPSTR)dst; + keying_info.cbContextValue = 0; + keying_info.pbContextValue = NULL; + keying_info.cbKeyingMaterial = materials_sz; + + sspi_ret = SetContextAttributes(&c->ctxt_handle, SECPKG_ATTR_KEYING_MATERIAL_INFO, &keying_info, sizeof(keying_info)); + if (sspi_ret != SEC_E_OK) { + av_log(h, AV_LOG_ERROR, "Setting keying material info failed: %lx\n", sspi_ret); + return AVERROR_EXTERNAL; + } + + sspi_ret = QueryContextAttributes(&c->ctxt_handle, SECPKG_ATTR_KEYING_MATERIAL, &keying_material); + if (sspi_ret != SEC_E_OK) { + av_log(h, AV_LOG_ERROR, "Querying keying material failed: %lx\n", sspi_ret); + return AVERROR_EXTERNAL; + } + + memcpy(dtls_srtp_materials, keying_material.pbKeyingMaterial, FFMIN(materials_sz, keying_material.cbKeyingMaterial)); + FreeContextBuffer(keying_material.pbKeyingMaterial); + + if (keying_material.cbKeyingMaterial > materials_sz) { + av_log(h, AV_LOG_WARNING, "Keying material size mismatch: %ld > %zu\n", keying_material.cbKeyingMaterial, materials_sz); + return AVERROR(ENOSPC); + } + + return 0; +} + +int ff_dtls_state(URLContext *h) +{ + TLSContext *c = h->priv_data; + return c->tls_shared.state; +} + static void init_sec_buffer(SecBuffer *buffer, unsigned long type, void *data, unsigned long size) { @@ -89,6 +654,7 @@ static int tls_shutdown_client(URLContext *h) { TLSContext *c = h->priv_data; TLSShared *s = &c->tls_shared; + URLContext *uc = s->is_dtls ? s->udp : s->tcp; int ret; if (c->connected) { @@ -109,12 +675,17 @@ static int tls_shutdown_client(URLContext *h) init_sec_buffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); init_sec_buffer_desc(&outbuf_desc, &outbuf, 1); - sspi_ret = InitializeSecurityContext(&c->cred_handle, &c->ctxt_handle, s->host, - c->request_flags, 0, 0, NULL, 0, &c->ctxt_handle, - &outbuf_desc, &c->context_flags, &c->ctxt_timestamp); + if (s->listen) + sspi_ret = AcceptSecurityContext(&c->cred_handle, &c->ctxt_handle, NULL, c->request_flags, 0, + &c->ctxt_handle, &outbuf_desc, &c->context_flags, + &c->ctxt_timestamp); + else + sspi_ret = InitializeSecurityContext(&c->cred_handle, &c->ctxt_handle, s->host, + c->request_flags, 0, 0, NULL, 0, &c->ctxt_handle, + &outbuf_desc, &c->context_flags, &c->ctxt_timestamp); if (sspi_ret == SEC_E_OK || sspi_ret == SEC_I_CONTEXT_EXPIRED) { - s->tcp->flags &= ~AVIO_FLAG_NONBLOCK; - ret = ffurl_write(s->tcp, outbuf.pvBuffer, outbuf.cbBuffer); + uc->flags &= ~AVIO_FLAG_NONBLOCK; + ret = ffurl_write(uc, outbuf.pvBuffer, outbuf.cbBuffer); FreeContextBuffer(outbuf.pvBuffer); if (ret < 0 || ret != outbuf.cbBuffer) av_log(h, AV_LOG_ERROR, "Failed to send close message\n"); @@ -128,6 +699,7 @@ static int tls_shutdown_client(URLContext *h) static int tls_close(URLContext *h) { TLSContext *c = h->priv_data; + TLSShared *s = &c->tls_shared; tls_shutdown_client(h); @@ -140,19 +712,27 @@ static int tls_close(URLContext *h) av_freep(&c->dec_buf); c->dec_buf_size = c->dec_buf_offset = 0; - ffurl_closep(&c->tls_shared.tcp); + if (s->is_dtls) { + if (!s->external_sock) + ffurl_closep(&c->tls_shared.udp); + } else { + ffurl_closep(&c->tls_shared.tcp); + } + return 0; } -static int tls_client_handshake_loop(URLContext *h, int initial) +static int tls_handshake_loop(URLContext *h, int initial) { TLSContext *c = h->priv_data; TLSShared *s = &c->tls_shared; + URLContext *uc = s->is_dtls ? s->udp : s->tcp; SECURITY_STATUS sspi_ret; SecBuffer outbuf[3] = { 0 }; SecBufferDesc outbuf_desc; - SecBuffer inbuf[2]; + SecBuffer inbuf[3]; SecBufferDesc inbuf_desc; + struct sockaddr_storage recv_addr = { 0 }; int i, ret = 0, read_data = initial; if (c->enc_buf == NULL) { @@ -182,19 +762,35 @@ static int tls_client_handshake_loop(URLContext *h, int initial) } if (read_data) { - ret = ffurl_read(c->tls_shared.tcp, c->enc_buf + c->enc_buf_offset, - c->enc_buf_size - c->enc_buf_offset); + ret = ffurl_read(uc, c->enc_buf + c->enc_buf_offset, c->enc_buf_size - c->enc_buf_offset); if (ret < 0) { av_log(h, AV_LOG_ERROR, "Failed to read handshake response\n"); goto fail; } c->enc_buf_offset += ret; + if (s->is_dtls && !recv_addr.ss_family) { + ff_udp_get_last_recv_addr(uc, &recv_addr); + + if (s->listen) { + ret = ff_udp_set_remote_addr(uc, (struct sockaddr *)&recv_addr, sizeof(recv_addr), 1); + if (ret < 0) { + av_log(h, AV_LOG_ERROR, "Failed connecting udp context\n"); + goto fail; + } + av_log(h, AV_LOG_TRACE, "Set UDP remote addr on UDP socket, now 'connected'\n"); + } + } } /* input buffers */ init_sec_buffer(&inbuf[0], SECBUFFER_TOKEN, av_malloc(c->enc_buf_offset), c->enc_buf_offset); init_sec_buffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); - init_sec_buffer_desc(&inbuf_desc, inbuf, 2); + if (s->listen && s->is_dtls) { + init_sec_buffer(&inbuf[2], SECBUFFER_EXTRA, &recv_addr, sizeof(recv_addr)); + init_sec_buffer_desc(&inbuf_desc, inbuf, 3); + } else { + init_sec_buffer_desc(&inbuf_desc, inbuf, 2); + } if (inbuf[0].pvBuffer == NULL) { av_log(h, AV_LOG_ERROR, "Failed to allocate input buffer\n"); @@ -210,17 +806,26 @@ static int tls_client_handshake_loop(URLContext *h, int initial) init_sec_buffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0); init_sec_buffer_desc(&outbuf_desc, outbuf, 3); - sspi_ret = InitializeSecurityContext(&c->cred_handle, &c->ctxt_handle, s->host, c->request_flags, - 0, 0, &inbuf_desc, 0, NULL, &outbuf_desc, &c->context_flags, - &c->ctxt_timestamp); + if (s->listen) + sspi_ret = AcceptSecurityContext(&c->cred_handle, c->have_context ? &c->ctxt_handle : NULL, &inbuf_desc, + c->request_flags, 0, &c->ctxt_handle, &outbuf_desc, + &c->context_flags, &c->ctxt_timestamp); + else + sspi_ret = InitializeSecurityContext(&c->cred_handle, c->have_context ? &c->ctxt_handle : NULL, + s->host, c->request_flags, 0, 0, &inbuf_desc, 0, &c->ctxt_handle, + &outbuf_desc, &c->context_flags, &c->ctxt_timestamp); av_freep(&inbuf[0].pvBuffer); + av_log(h, AV_LOG_TRACE, "Handshake res with %d bytes of data: 0x%lx\n", c->enc_buf_offset, sspi_ret); + if (sspi_ret == SEC_E_INCOMPLETE_MESSAGE) { - av_log(h, AV_LOG_DEBUG, "Received incomplete handshake, need more data\n"); + av_log(h, AV_LOG_TRACE, "Received incomplete handshake, need more data\n"); read_data = 1; continue; } + c->have_context = 1; + /* remote requests a client certificate - attempt to continue without one anyway */ if (sspi_ret == SEC_I_INCOMPLETE_CREDENTIALS && !(c->request_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { @@ -231,10 +836,10 @@ static int tls_client_handshake_loop(URLContext *h, int initial) } /* continue handshake */ - if (sspi_ret == SEC_I_CONTINUE_NEEDED || sspi_ret == SEC_E_OK) { + if (sspi_ret == SEC_I_CONTINUE_NEEDED || sspi_ret == SEC_I_MESSAGE_FRAGMENT || sspi_ret == SEC_E_OK) { for (i = 0; i < 3; i++) { if (outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { - ret = ffurl_write(c->tls_shared.tcp, outbuf[i].pvBuffer, outbuf[i].cbBuffer); + ret = ffurl_write(uc, outbuf[i].pvBuffer, outbuf[i].cbBuffer); if (ret < 0 || ret != outbuf[i].cbBuffer) { av_log(h, AV_LOG_VERBOSE, "Failed to send handshake data\n"); ret = AVERROR(EIO); @@ -256,12 +861,19 @@ static int tls_client_handshake_loop(URLContext *h, int initial) goto fail; } + if (sspi_ret == SEC_I_MESSAGE_FRAGMENT) { + av_log(h, AV_LOG_TRACE, "Writing fragmented output message part\n"); + read_data = 0; + continue; + } + if (inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { if (c->enc_buf_offset > inbuf[1].cbBuffer) { memmove(c->enc_buf, (c->enc_buf + c->enc_buf_offset) - inbuf[1].cbBuffer, inbuf[1].cbBuffer); c->enc_buf_offset = inbuf[1].cbBuffer; if (sspi_ret == SEC_I_CONTINUE_NEEDED) { + av_log(h, AV_LOG_TRACE, "Sent reply, handshake continues. %d extra bytes\n", (int)inbuf[1].cbBuffer); read_data = 0; continue; } @@ -271,6 +883,7 @@ static int tls_client_handshake_loop(URLContext *h, int initial) } if (sspi_ret == SEC_I_CONTINUE_NEEDED) { + av_log(h, AV_LOG_TRACE, "Handshake continues\n"); read_data = 1; continue; } @@ -278,6 +891,8 @@ static int tls_client_handshake_loop(URLContext *h, int initial) break; } + av_log(h, AV_LOG_TRACE, "Handshake completed\n"); + return 0; fail: @@ -289,6 +904,8 @@ fail: } } + av_log(h, AV_LOG_TRACE, "Handshake failed\n"); + return ret; } @@ -296,6 +913,7 @@ static int tls_client_handshake(URLContext *h) { TLSContext *c = h->priv_data; TLSShared *s = &c->tls_shared; + URLContext *uc = s->is_dtls ? s->udp : s->tcp; SecBuffer outbuf; SecBufferDesc outbuf_desc; SECURITY_STATUS sspi_ret; @@ -305,8 +923,11 @@ static int tls_client_handshake(URLContext *h) init_sec_buffer_desc(&outbuf_desc, &outbuf, 1); c->request_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_STREAM; + ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY; + if (s->is_dtls) + c->request_flags |= ISC_REQ_DATAGRAM; + else + c->request_flags |= ISC_REQ_STREAM; sspi_ret = InitializeSecurityContext(&c->cred_handle, NULL, s->host, c->request_flags, 0, 0, NULL, 0, &c->ctxt_handle, &outbuf_desc, &c->context_flags, @@ -317,8 +938,10 @@ static int tls_client_handshake(URLContext *h) goto fail; } - s->tcp->flags &= ~AVIO_FLAG_NONBLOCK; - ret = ffurl_write(s->tcp, outbuf.pvBuffer, outbuf.cbBuffer); + c->have_context = 1; + + uc->flags &= ~AVIO_FLAG_NONBLOCK; + ret = ffurl_write(uc, outbuf.pvBuffer, outbuf.cbBuffer); FreeContextBuffer(outbuf.pvBuffer); if (ret < 0 || ret != outbuf.cbBuffer) { av_log(h, AV_LOG_ERROR, "Failed to send initial handshake data\n"); @@ -326,44 +949,121 @@ static int tls_client_handshake(URLContext *h) goto fail; } - return tls_client_handshake_loop(h, 1); + return tls_handshake_loop(h, 1); fail: DeleteSecurityContext(&c->ctxt_handle); return ret; } -static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options) +static int tls_server_handshake(URLContext *h) +{ + TLSContext *c = h->priv_data; + TLSShared *s = &c->tls_shared; + + c->request_flags = ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | + ASC_REQ_CONFIDENTIALITY | ASC_REQ_ALLOCATE_MEMORY; + if (s->is_dtls) + c->request_flags |= ASC_REQ_DATAGRAM; + else + c->request_flags |= ASC_REQ_STREAM; + + c->have_context = 0; + + return tls_handshake_loop(h, 1); +} + +static int tls_handshake(URLContext *h) { TLSContext *c = h->priv_data; TLSShared *s = &c->tls_shared; SECURITY_STATUS sspi_ret; - SCHANNEL_CRED schannel_cred = { 0 }; - int ret; + int ret = 0; - if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0) - goto fail; + if (s->listen) + ret = tls_server_handshake(h); + else + ret = tls_client_handshake(h); - if (s->listen) { - av_log(h, AV_LOG_ERROR, "TLS Listen Sockets with SChannel is not implemented.\n"); - ret = AVERROR(EINVAL); + if (ret < 0) goto fail; + + if (s->is_dtls && s->mtu > 0) { + ULONG mtu = s->mtu; + sspi_ret = SetContextAttributes(&c->ctxt_handle, SECPKG_ATTR_DTLS_MTU, &mtu, sizeof(mtu)); + if (sspi_ret != SEC_E_OK) { + av_log(h, AV_LOG_ERROR, "Failed setting DTLS MTU to %d.\n", s->mtu); + ret = AVERROR(EINVAL); + goto fail; + } + av_log(h, AV_LOG_VERBOSE, "Set DTLS MTU to %d\n", s->mtu); + } + + c->connected = 1; + s->state = DTLS_STATE_FINISHED; + +fail: + return ret; +} + +static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options) +{ + TLSContext *c = h->priv_data; + TLSShared *s = &c->tls_shared; + SECURITY_STATUS sspi_ret; + SCHANNEL_CRED schannel_cred = { 0 }; + PCCERT_CONTEXT crtctx = NULL; + NCRYPT_KEY_HANDLE key = 0; + int ret = 0; + + if (!s->external_sock) { + if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0) + goto fail; } /* SChannel Options */ schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; - if (s->verify) - schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | - SCH_CRED_REVOCATION_CHECK_CHAIN; - else - schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | - SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; + if (s->listen) { + if (s->key_buf && s->cert_buf) { + ret = tls_import_key_cert(s->key_buf, s->cert_buf, &key, &crtctx); + if (ret < 0) + goto fail; + } else if (s->key_file && s->cert_file) { + ret = tls_load_key_cert(s->key_file, s->cert_file, &key, &crtctx); + if (ret < 0) + goto fail; + } else { + av_log(h, AV_LOG_VERBOSE, "No server certificate provided, using self-signed\n"); + ret = tls_gen_self_signed(&key, &crtctx); + if (ret < 0) + goto fail; + } + + schannel_cred.cCreds = 1; + schannel_cred.paCred = &crtctx; + + schannel_cred.dwFlags = SCH_CRED_NO_SYSTEM_MAPPER | SCH_CRED_MANUAL_CRED_VALIDATION; + + if (s->is_dtls) + schannel_cred.grbitEnabledProtocols = SP_PROT_DTLS1_X_SERVER; + } else { + if (s->verify) + schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | + SCH_CRED_REVOCATION_CHECK_CHAIN; + else + schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | + SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE; + + if (s->is_dtls) + schannel_cred.grbitEnabledProtocols = SP_PROT_DTLS1_X_CLIENT; + } /* Get credential handle */ - sspi_ret = AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, SECPKG_CRED_OUTBOUND, - NULL, &schannel_cred, NULL, NULL, &c->cred_handle, + sspi_ret = AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, + s->listen ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, + NULL, &schannel_cred, NULL, NULL, &c->cred_handle, &c->cred_timestamp); if (sspi_ret != SEC_E_OK) { av_log(h, AV_LOG_ERROR, "Unable to acquire security credentials (0x%lx)\n", sspi_ret); @@ -371,23 +1071,42 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op goto fail; } - ret = tls_client_handshake(h); - if (ret < 0) - goto fail; - - c->connected = 1; + if (!s->external_sock) { + ret = tls_handshake(h); + if (ret < 0) + goto fail; + } - return 0; + goto end; fail: tls_close(h); + +end: + if (crtctx) + CertFreeCertificateContext(crtctx); + if (key) + if (NCryptDeleteKey(key, NCRYPT_SILENT_FLAG) != ERROR_SUCCESS) + NCryptFreeObject(key); + 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 len) { TLSContext *c = h->priv_data; TLSShared *s = &c->tls_shared; + URLContext *uc = s->is_dtls ? s->udp : s->tcp; SECURITY_STATUS sspi_ret = SEC_E_OK; SecBuffer inbuf[4]; SecBufferDesc inbuf_desc; @@ -418,10 +1137,10 @@ static int tls_read(URLContext *h, uint8_t *buf, int len) } } - s->tcp->flags &= ~AVIO_FLAG_NONBLOCK; - s->tcp->flags |= h->flags & AVIO_FLAG_NONBLOCK; + uc->flags &= ~AVIO_FLAG_NONBLOCK; + uc->flags |= h->flags & AVIO_FLAG_NONBLOCK; - ret = ffurl_read(s->tcp, c->enc_buf + c->enc_buf_offset, + ret = ffurl_read(uc, c->enc_buf + c->enc_buf_offset, c->enc_buf_size - c->enc_buf_offset); if (ret == AVERROR_EOF) { c->connection_closed = 1; @@ -489,7 +1208,7 @@ static int tls_read(URLContext *h, uint8_t *buf, int len) } av_log(h, AV_LOG_VERBOSE, "Re-negotiating security context\n"); - ret = tls_client_handshake_loop(h, 0); + ret = tls_handshake_loop(h, 0); if (ret < 0) { goto cleanup; } @@ -536,6 +1255,7 @@ static int tls_write(URLContext *h, const uint8_t *buf, int len) { TLSContext *c = h->priv_data; TLSShared *s = &c->tls_shared; + URLContext *uc = s->is_dtls ? s->udp : s->tcp; SECURITY_STATUS sspi_ret; int ret = 0, data_size; uint8_t *data = NULL; @@ -549,7 +1269,7 @@ static int tls_write(URLContext *h, const uint8_t *buf, int len) } /* limit how much data we can consume */ - len = FFMIN(len, c->sizes.cbMaximumMessage); + len = FFMIN(len, c->sizes.cbMaximumMessage - c->sizes.cbHeader - c->sizes.cbTrailer); data_size = c->sizes.cbHeader + len + c->sizes.cbTrailer; data = av_malloc(data_size); @@ -572,10 +1292,10 @@ static int tls_write(URLContext *h, const uint8_t *buf, int len) if (sspi_ret == SEC_E_OK) { len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; - s->tcp->flags &= ~AVIO_FLAG_NONBLOCK; - s->tcp->flags |= h->flags & AVIO_FLAG_NONBLOCK; + uc->flags &= ~AVIO_FLAG_NONBLOCK; + uc->flags |= h->flags & AVIO_FLAG_NONBLOCK; - ret = ffurl_write(s->tcp, data, len); + ret = ffurl_write(uc, data, len); if (ret == AVERROR(EAGAIN)) { goto done; } else if (ret < 0 || ret != len) { @@ -600,13 +1320,15 @@ done: static int tls_get_file_handle(URLContext *h) { TLSContext *c = h->priv_data; - return ffurl_get_file_handle(c->tls_shared.tcp); + TLSShared *s = &c->tls_shared; + return ffurl_get_file_handle(s->is_dtls ? c->tls_shared.udp : c->tls_shared.tcp); } static int tls_get_short_seek(URLContext *h) { - TLSContext *s = h->priv_data; - return ffurl_get_short_seek(s->tls_shared.tcp); + TLSContext *c = h->priv_data; + TLSShared *s = &c->tls_shared; + return ffurl_get_short_seek(s->is_dtls ? c->tls_shared.udp : c->tls_shared.tcp); } static const AVOption options[] = { @@ -633,3 +1355,22 @@ 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 = tls_handshake, + .url_close = tls_close, + .url_read = tls_read, + .url_write = tls_write, + .priv_data_size = sizeof(TLSContext), + .flags = URL_PROTOCOL_FLAG_NETWORK, + .priv_data_class = &dtls_class, +}; -- 2.49.0 _______________________________________________ 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".