Upgrade libsrt to v1.5.3. v5.0.183 v6.0.81 (#3808)

fix https://github.com/ossrs/srs/issues/3155
Build srt-1-fit fails with `standard attributes in middle of
decl-specifiers` on GCC 12,Arch Linux.

See https://github.com/Haivision/srt/releases/tag/v1.5.3
pull/3806/head
Haibo Chen 1 year ago committed by GitHub
parent f9bba0a9b0
commit c5e067fb0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,8 +9,8 @@ nginx-1.5.7.zip
* for srs to support hls streaming.
srt-1-fit
srt-1.4.1.tar.gz
* https://github.com/Haivision/srt/releases/tag/v1.4.1
srt-1.5.3.tar.gz
* https://github.com/Haivision/srt/releases/tag/v1.5.3
* https://ossrs.net/lts/zh-cn/license#srt
openssl-1.1-fit

@ -8,7 +8,7 @@
#
cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR)
set (SRT_VERSION 1.5.1)
set (SRT_VERSION 1.5.3)
set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts")
include(haiUtil) # needed for set_version_variables
@ -117,6 +117,9 @@ if (ENABLE_DEBUG)
set(ENABLE_HEAVY_LOGGING_DEFAULT ON)
endif()
# Note that the IP_PKTINFO has a limited portability and may not work on some platforms
# (Windows, FreeBSD, ...).
set (ENABLE_PKTINFO_DEFAULT OFF)
set(ENABLE_STDCXX_SYNC_DEFAULT OFF)
set(ENABLE_MONOTONIC_CLOCK_DEFAULT OFF)
@ -144,10 +147,13 @@ option(ENABLE_HEAVY_LOGGING "Should heavy debug logging be enabled" ${ENABLE_HEA
option(ENABLE_HAICRYPT_LOGGING "Should logging in haicrypt be enabled" 0)
option(ENABLE_SHARED "Should libsrt be built as a shared library" ON)
option(ENABLE_STATIC "Should libsrt be built as a static library" ON)
option(ENABLE_PKTINFO "Enable using IP_PKTINFO to allow the listener extracting the target IP address from incoming packets" ${ENABLE_PKTINFO_DEFAULT})
option(ENABLE_RELATIVE_LIBPATH "Should application contain relative library paths, like ../lib" OFF)
option(ENABLE_GETNAMEINFO "In-logs sockaddr-to-string should do rev-dns" OFF)
option(ENABLE_UNITTESTS "Enable unit tests" OFF)
option(ENABLE_ENCRYPTION "Enable encryption in SRT" ON)
option(ENABLE_AEAD_API_PREVIEW "Enable AEAD API preview in SRT" Off)
option(ENABLE_MAXREXMITBW "Enable SRTO_MAXREXMITBW (v1.6.0 API preview)" Off)
option(ENABLE_CXX_DEPS "Extra library dependencies in srt.pc for the CXX libraries useful with C language" ON)
option(USE_STATIC_LIBSTDCXX "Should use static rather than shared libstdc++" OFF)
option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON)
@ -160,7 +166,6 @@ option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potenti
option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF)
option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON)
option(ENABLE_SHOW_PROJECT_CONFIG "Enable show Project Configuration" OFF)
option(ENABLE_NEW_RCVBUFFER "Enable new receiver buffer implementation" ON)
option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF)
@ -200,6 +205,16 @@ else()
message(STATUS "USE_BUSY_WAITING: OFF (default)")
endif()
# Reduce the frequency of some frequent logs, milliseconds
set(SRT_LOG_SLOWDOWN_FREQ_MS_DEFAULT 1000) # 1s
if (NOT DEFINED SRT_LOG_SLOWDOWN_FREQ_MS)
if (ENABLE_HEAVY_LOGGING)
set(SRT_LOG_SLOWDOWN_FREQ_MS 0) # Just show every log message.
else()
set(SRT_LOG_SLOWDOWN_FREQ_MS ${SRT_LOG_SLOWDOWN_FREQ_MS_DEFAULT})
endif()
endif()
if ( CYGWIN AND NOT CYGWIN_USE_POSIX )
set(WIN32 1)
set(CMAKE_LEGACY_CYGWIN_WIN32 1)
@ -285,7 +300,13 @@ if(WIN32)
if(ENABLE_INET_PTON)
set(CMAKE_REQUIRED_LIBRARIES ws2_32)
check_function_exists(inet_pton HAVE_INET_PTON)
add_definitions(-D_WIN32_WINNT=0x0600)
try_compile(AT_LEAST_VISTA
${CMAKE_BINARY_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/test_vista.c")
if(NOT AT_LEAST_VISTA)
# force targeting Vista
add_definitions(-D_WIN32_WINNT=0x0600)
endif()
else()
add_definitions(-D_WIN32_WINNT=0x0501)
endif()
@ -422,8 +443,21 @@ if (ENABLE_ENCRYPTION)
add_definitions(-DSRT_ENABLE_ENCRYPTION)
message(STATUS "ENCRYPTION: ENABLED, using: ${SSL_REQUIRED_MODULES}")
message (STATUS "SSL libraries: ${SSL_LIBRARIES}")
if (ENABLE_AEAD_API_PREVIEW)
if ("${USE_ENCLIB}" STREQUAL "openssl-evp")
add_definitions(-DENABLE_AEAD_API_PREVIEW)
message(STATUS "ENCRYPTION AEAD API: ENABLED")
else()
message(FATAL_ERROR "ENABLE_AEAD_API_PREVIEW is only available with USE_ENCLIB=openssl-evp!")
endif()
else()
message(STATUS "ENCRYPTION AEAD API: DISABLED")
endif()
else()
message(STATUS "ENCRYPTION: DISABLED")
message(STATUS "ENCRYPTION AEAD API: N/A")
endif()
if (USE_GNUSTL)
@ -433,8 +467,15 @@ if (USE_GNUSTL)
set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS})
endif()
if (ENABLE_MAXREXMITBW)
add_definitions(-DENABLE_MAXREXMITBW)
message(STATUS "MAXREXMITBW API: ENABLED")
else()
message(STATUS "MAXREXMITBW API: DISABLED")
endif()
if (USING_DEFAULT_COMPILER_PREFIX)
# Detect if the compiler is GNU compatable for flags
# Detect if the compiler is GNU compatible for flags
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Intel|Clang|AppleClang")
message(STATUS "COMPILER: ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER}) - GNU compat")
set(HAVE_COMPILER_GNU_COMPAT 1)
@ -478,6 +519,15 @@ CheckGCCAtomicIntrinsics()
include(CheckCXXAtomic)
CheckCXXAtomic()
# Check for std::put_time():
# Sets:
# HAVE_CXX_STD_PUT_TIME
include(CheckCXXStdPutTime)
CheckCXXStdPutTime()
if (HAVE_CXX_STD_PUT_TIME)
add_definitions(-DHAVE_CXX_STD_PUT_TIME=1)
endif()
if (DISABLE_CXX11)
set (ENABLE_CXX11 0)
elseif( DEFINED ENABLE_CXX11 )
@ -533,14 +583,6 @@ if (ENABLE_SOCK_CLOEXEC)
add_definitions(-DENABLE_SOCK_CLOEXEC=1)
endif()
if (ENABLE_NEW_RCVBUFFER)
add_definitions(-DENABLE_NEW_RCVBUFFER=1)
message(STATUS "RECEIVER_BUFFER: NEW")
else()
remove_definitions(-DENABLE_NEW_RCVBUFFER)
message(STATUS "RECEIVER_BUFFER: OLD")
endif()
if (CMAKE_MAJOR_VERSION LESS 3)
set (FORCE_CXX_STANDARD_GNUONLY 1)
endif()
@ -614,6 +656,9 @@ endif()
# add extra warning flags for gccish compilers
if (HAVE_COMPILER_GNU_COMPAT)
set (SRT_GCC_WARN "-Wall -Wextra")
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set (SRT_GCC_WARN "${SRT_GCC_WARN} -Wshadow=local")
endif()
else()
# cpp debugging on Windows :D
#set (SRT_GCC_WARN "/showIncludes")
@ -720,6 +765,15 @@ if (ENABLE_GETNAMEINFO)
list(APPEND SRT_EXTRA_CFLAGS "-DENABLE_GETNAMEINFO=1")
endif()
if (ENABLE_PKTINFO)
if (WIN32 OR BSD)
message(FATAL_ERROR "PKTINFO is not implemented on Windows or *BSD.")
endif()
list(APPEND SRT_EXTRA_CFLAGS "-DSRT_ENABLE_PKTINFO=1")
endif()
# ENABLE_EXPERIMENTAL_BONDING is deprecated. Use ENABLE_BONDING. ENABLE_EXPERIMENTAL_BONDING is be removed in v1.6.0.
if (ENABLE_EXPERIMENTAL_BONDING)
message(DEPRECATION "ENABLE_EXPERIMENTAL_BONDING is deprecated. Please use ENABLE_BONDING instead.")
@ -1040,6 +1094,8 @@ if (ENABLE_SHARED)
target_compile_definitions(srt_virtual PUBLIC -DSRT_DYNAMIC)
endif()
target_compile_definitions(srt_virtual PRIVATE -DSRT_LOG_SLOWDOWN_FREQ_MS=${SRT_LOG_SLOWDOWN_FREQ_MS})
if (srt_libspec_shared)
if (MICROSOFT)
target_link_libraries(${TARGET_srt}_shared PUBLIC Ws2_32.lib)
@ -1064,6 +1120,11 @@ elseif (HAVE_LIBATOMIC AND HAVE_LIBATOMIC_COMPILES_STATIC)
if (srt_libspec_static)
target_link_libraries(${TARGET_srt}_static PUBLIC atomic)
endif()
elseif (LINUX AND HAVE_LIBATOMIC AND HAVE_LIBATOMIC_COMPILES)
# This is a workaround for some older Linux Toolchains.
if (srt_libspec_static)
target_link_libraries(${TARGET_srt}_static PUBLIC atomic)
endif()
endif()
# Cygwin installs the *.dll libraries in bin directory and uses PATH.
@ -1122,7 +1183,7 @@ endif()
# obtained by `pkg-config --libs`.
if(ENABLE_CXX_DEPS)
foreach(LIB ${CMAKE_CXX_IMPLICIT_LINK_LIBRARIES})
if(IS_ABSOLUTE ${LIB} AND EXISTS ${LIB})
if((IS_ABSOLUTE ${LIB} AND EXISTS ${LIB}) OR (${LIB} MATCHES "^-l"))
set(SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ${LIB})
else()
set(SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} "-l${LIB}")
@ -1334,6 +1395,10 @@ if (ENABLE_EXAMPLES)
srt_add_example(recvfile.cpp)
srt_add_example(sendmsg.cpp)
srt_add_example(recvmsg.cpp)
srt_add_example(test-c-client.c)
srt_add_example(example-client-nonblock.c)
@ -1363,7 +1428,13 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
find_package(GTest 1.8)
# Version ranges are only supported with CMake 3.19 or later.
# Need GTest v1.10 or higher to support GTEST_SKIP.
if (${CMAKE_VERSION} VERSION_LESS "3.19.0")
find_package(GTest 1.10)
else()
find_package(GTest 1.10...1.12)
endif()
if (NOT GTEST_FOUND)
message(STATUS "GTEST not found! Fetching from git.")
include(googletest)
@ -1395,12 +1466,13 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11)
NAME test-srt
COMMAND ${CMAKE_BINARY_DIR}/test-srt
)
#set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE)
else()
gtest_add_tests(
test-srt
""
AUTO
TEST_LIST tests_srt
TARGET test-srt
)
set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE)
endif()
enable_testing()

@ -43,6 +43,7 @@ set cmake_options {
enable-logging "Should logging be enabled (default: ON)"
enable-heavy-logging "Should heavy debug logging be enabled (default: OFF)"
enable-haicrypt-logging "Should logging in haicrypt be enabled (default: OFF)"
enable-pktinfo "Should pktinfo reading and using be enabled (POSIX only) (default: OFF)"
enable-shared "Should libsrt be built as a shared library (default: ON)"
enable-static "Should libsrt be built as a static library (default: ON)"
enable-relative-libpath "Should applications contain relative library paths, like ../lib (default: OFF)"

@ -16,7 +16,7 @@ written by
2022-05-19 (jdube)
CRYSPR2 adaptation
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
MBedTLS CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#include "hcrypt.h"
@ -32,7 +32,7 @@ written by
static mbedtls_ctr_drbg_context crysprMbedtls_ctr_drbg;
static mbedtls_entropy_context crysprMbedtls_entropy;
typedef struct tag_crysprGnuTLS_AES_cb {
typedef struct tag_crysprMBedTLS_AES_cb {
CRYSPR_cb ccb; /* CRYSPR control block */
/* Add other cryptolib specific data here */
#ifdef CRYSPR2
@ -75,9 +75,9 @@ int crysprMbedtls_AES_SetKey(
// kstr_len is in "bytes" convention (16, 24, 32).
if (bEncrypt) { /* Encrypt key */
ret = mbedtls_aes_setkey_enc(aes_key, kstr, kstr_len*8);
ret = mbedtls_aes_setkey_enc(aes_key, kstr, (unsigned int)kstr_len*8);
} else { /* Decrypt key */
ret = mbedtls_aes_setkey_dec(aes_key, kstr, kstr_len*8);
ret = mbedtls_aes_setkey_dec(aes_key, kstr, (unsigned int)kstr_len*8);
}
return ret == 0 ? 0 : -1;
@ -91,8 +91,8 @@ int crysprMbedtls_AES_EcbCipher( /* AES Electronic Codebook cipher*/
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen) /* dst len */
{
int nblk = inlen/CRYSPR_AESBLKSZ;
int nmore = inlen%CRYSPR_AESBLKSZ;
int nblk = (int)(inlen/CRYSPR_AESBLKSZ);
int nmore = (int)(inlen%CRYSPR_AESBLKSZ);
int i;
if (bEncrypt) {
@ -160,7 +160,6 @@ int crysprMbedtls_AES_CtrCipher( /* AES-CTR128 Encryption */
static CRYSPR_cb *crysprMbedtls_Open(CRYSPR_methods *cryspr, size_t max_len)
{
crysprMbedtls_cb *aes_data;
CRYSPR_cb *cryspr_cb;
aes_data = (crysprMbedtls_cb *)crysprHelper_Open(cryspr, sizeof(crysprMbedtls_cb), max_len);
if (NULL == aes_data) {
@ -216,7 +215,7 @@ int crysprMbedtls_KmPbkdf2(
ret = mbedtls_pkcs5_pbkdf2_hmac(&mdctx,
(unsigned char*)passwd, passwd_len, salt, salt_len,
itr, key_len, out);
itr, (uint32_t)key_len, out);
mbedtls_md_free(&mdctx);

@ -13,12 +13,12 @@ written by
Haivision Systems Inc.
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
MBedTLS CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#ifndef CRYSPR_GNUTLS_H
#define CRYSPR_GNUTLS_H
#ifndef CRYSPR_MBEDTLS_H
#define CRYSPR_MBEDTLS_H
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/aes.h>
@ -59,5 +59,5 @@ typedef mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key
struct tag_CRYSPR_methods *crysprMbedtls(void);
#endif /* CRYSPR_GNUTLS_H */
#endif /* CRYSPR_MBEDTLS_H */

@ -34,9 +34,11 @@ int crysprOpenSSL_EVP_Prng(unsigned char* rn, int len)
const EVP_CIPHER* (*Xcipher_fnptr)(void) = EVP_aes_128_ecb;
const EVP_CIPHER* (*_crysprOpenSSL_EVP_cipher_fnptr[][3])(void) = {
{NULL, NULL, NULL},
{EVP_aes_128_ecb, EVP_aes_192_ecb, EVP_aes_256_ecb},
{EVP_aes_128_ctr, EVP_aes_192_ctr, EVP_aes_256_ctr},
{NULL, NULL, NULL}, // HCRYPT_CTX_MODE_CLRTXT
{EVP_aes_128_ecb, EVP_aes_192_ecb, EVP_aes_256_ecb}, // HCRYPT_CTX_MODE_AESECB
{EVP_aes_128_ctr, EVP_aes_192_ctr, EVP_aes_256_ctr}, // HCRYPT_CTX_MODE_AESCTR
{NULL, NULL, NULL}, // HCRYPT_CTX_MODE_AESCBC
{EVP_aes_128_gcm, EVP_aes_192_gcm, EVP_aes_256_gcm}, // HCRYPT_CTX_MODE_AESGCM
};
int crysprOpenSSL_EVP_AES_SetKey(
@ -47,7 +49,7 @@ int crysprOpenSSL_EVP_AES_SetKey(
CRYSPR_AESCTX* aes_key) /* CRYpto Service PRovider AES Key context */
{
const EVP_CIPHER* cipher = NULL;
int idxKlen = (kstr_len / 8) - 2; /* key_len index in cipher_fnptr array in [0,1,2] range */
int idxKlen = (int)((kstr_len / 8) - 2); /* key_len index in cipher_fnptr array in [0,1,2] range */
switch (cipher_type)
{
@ -61,6 +63,8 @@ int crysprOpenSSL_EVP_AES_SetKey(
cipher_type = HCRYPT_CTX_MODE_AESECB;
#endif
break;
case HCRYPT_CTX_MODE_AESGCM:
break;
default:
HCRYPT_LOG(LOG_ERR,
"invalid cipher type (%d). Expected: [%d..%d]\n",
@ -139,7 +143,7 @@ int crysprOpenSSL_EVP_AES_EcbCipher(bool bEncrypt, /* true:encry
size_t* outlen_p) /* in/out dst len */
{
int nmore = inlen % CRYSPR_AESBLKSZ; /* bytes in last incomplete block */
int nblk = inlen / CRYSPR_AESBLKSZ + (nmore ? 1 : 0); /* blocks including incomplete */
int nblk = (int)(inlen / CRYSPR_AESBLKSZ + (nmore ? 1 : 0)); /* blocks including incomplete */
size_t outsiz = (outlen_p ? *outlen_p : 0);
int c_len = 0, f_len = 0;
@ -156,7 +160,7 @@ int crysprOpenSSL_EVP_AES_EcbCipher(bool bEncrypt, /* true:encry
return (-1); /* output buf size must have room for PKCS7 padding */
}
/* allows reusing of 'e' for multiple encryption cycles */
if (!EVP_CipherInit_ex(aes_key, NULL, NULL, NULL, NULL, -1))
if (!EVP_CipherInit_ex(aes_key, NULL, NULL, NULL, NULL, bEncrypt))
{
HCRYPT_LOG(LOG_ERR, "EVP_CipherInit_ex(%p,NULL,...,-1) failed\n", aes_key);
return -1;
@ -170,7 +174,7 @@ int crysprOpenSSL_EVP_AES_EcbCipher(bool bEncrypt, /* true:encry
/* update ciphertext, c_len is filled with the length of ciphertext generated,
* cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes
*/
if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, inlen))
if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, (int)inlen))
{
HCRYPT_LOG(LOG_ERR, "EVP_CipherUpdate(%p, out, %d, in, %d) failed\n", aes_key, c_len, inlen);
return -1;
@ -223,7 +227,7 @@ int crysprOpenSSL_EVP_AES_CtrCipher(bool bEncrypt, /* true:encry
/* update ciphertext, c_len is filled with the length of ciphertext generated,
* cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes
*/
if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, inlen))
if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, (int)inlen))
{
HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherUpdate() failed");
return -1;
@ -247,6 +251,83 @@ int crysprOpenSSL_EVP_AES_CtrCipher(bool bEncrypt, /* true:encry
return 0;
}
int crysprOpenSSL_EVP_AES_GCMCipher(bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX* aes_key, /* CRYpto Service PRovider AES Key context */
unsigned char* iv, /* iv */
const unsigned char* aad, /* associated data */
size_t aadlen,
const unsigned char* indata, /* src */
size_t inlen, /* length */
unsigned char* out_txt,
unsigned char* out_tag) /* auth tag */
{
int c_len, f_len;
/* allows reusing of 'e' for multiple encryption cycles */
if (!EVP_CipherInit_ex(aes_key, NULL, NULL, NULL, iv, -1))
{
HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherInit_ex() failed");
return -1;
}
if (!EVP_CIPHER_CTX_set_padding(aes_key, 0))
{
HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CIPHER_CTX_set_padding() failed");
return -1;
}
/*
* Provide any AAD data. This can be called zero or more times as
* required
*/
if (1 != EVP_CipherUpdate(aes_key, NULL, &c_len, aad, (int) aadlen))
{
ERR_print_errors_fp(stderr);
HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_EncryptUpdate failed");
return -1;
}
/* update ciphertext, c_len is filled with the length of ciphertext generated,
* cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes
*/
if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, (int) inlen))
{
HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherUpdate() failed");
return -1;
}
if (!bEncrypt && !EVP_CIPHER_CTX_ctrl(aes_key, EVP_CTRL_GCM_SET_TAG, HAICRYPT_AUTHTAG_MAX, out_tag)) {
ERR_print_errors_fp(stderr);
HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_EncryptUpdate failed");
return -1;
}
/* update ciphertext with the final remaining bytes */
/* Useless with pre-padding */
f_len = 0;
if (0 == EVP_CipherFinal_ex(aes_key, &out_txt[c_len], &f_len))
{
#if ENABLE_HAICRYPT_LOGGING
char szErrBuf[256];
HCRYPT_LOG(LOG_ERR,
"EVP_CipherFinal_ex(ctx,&out[%d],%d)) failed: %s\n",
c_len,
f_len,
ERR_error_string(ERR_get_error(), szErrBuf));
#endif /*ENABLE_HAICRYPT_LOGGING*/
return -1;
}
/* Get the tag if we are encrypting */
if (bEncrypt && !EVP_CIPHER_CTX_ctrl(aes_key, EVP_CTRL_GCM_GET_TAG, HAICRYPT_AUTHTAG_MAX, out_tag))
{
ERR_print_errors_fp(stderr);
HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_GET_TAG) failed");
return -1;
}
return 0;
}
/*
* Password-based Key Derivation Function
*/
@ -260,7 +341,7 @@ int crysprOpenSSL_EVP_KmPbkdf2(CRYSPR_cb* cryspr_cb,
unsigned char* out) /* derived key */
{
(void)cryspr_cb;
int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd, passwd_len, salt, salt_len, itr, key_len, out);
int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd, (int)passwd_len, salt, (int)salt_len, itr, (int)key_len, out);
return (rc == 1 ? 0 : -1);
}
@ -300,6 +381,7 @@ CRYSPR_methods* crysprOpenSSL_EVP(void)
#if CRYSPR_HAS_AESCTR
crysprOpenSSL_EVP_methods.aes_ctr_cipher = crysprOpenSSL_EVP_AES_CtrCipher;
#endif
crysprOpenSSL_EVP_methods.aes_gcm_cipher = crysprOpenSSL_EVP_AES_GCMCipher;
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
/* AES-ECB only required if cryspr has no AES-CTR and no AES KeyWrap */
/* OpenSSL has both AESCTR and AESKWRP and the AESECB wrapper is only used

@ -34,6 +34,10 @@ written by
*/
#define CRYSPR_HAS_AESCTR 1
/* Define CRYSPR_HAS_AESGCM to 1 if this CRYSPR has AES GCM cipher mode. OpenSSL EVP supports GCM.
*/
#define CRYSPR_HAS_AESGCM 1
/* Define CRYSPR_HAS_AESKWRAP to 1 if this CRYSPR has AES Key Wrap
if not set to 0 to enable default/fallback crysprFallback_AES_WrapKey/crysprFallback_AES_UnwrapKey methods
and provide the aes_ecb_cipher method .

@ -48,12 +48,12 @@ int crysprOpenSSL_AES_SetKey(
(void)cipher_type;
if (bEncrypt) { /* Encrypt key */
if (AES_set_encrypt_key(kstr, kstr_len * 8, aes_key)) {
if (AES_set_encrypt_key(kstr, (int)(kstr_len * 8), aes_key)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) failed\n");
return(-1);
}
} else { /* Decrypt key */
if (AES_set_decrypt_key(kstr, kstr_len * 8, aes_key)) {
if (AES_set_decrypt_key(kstr, (int)(kstr_len * 8), aes_key)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_decrypt_key(kek) failed\n");
return(-1);
}
@ -163,7 +163,7 @@ int crysprOpenSSL_KmPbkdf2(
unsigned char *out) /* derived key */
{
(void)cryspr_cb;
int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd,passwd_len,salt,salt_len,itr,key_len,out);
int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd,(int)passwd_len,salt,(int)salt_len,itr,(int)key_len,out);
return(rc == 1? 0 : -1);
}

@ -14,7 +14,7 @@ written by
Haivision Systems Inc.
2019-06-28 (jdube)
CRYSPR/4SRT Initial implementation.
CRYSPR/4SRT Initial implementation.
*****************************************************************************/
#include "hcrypt.h"
@ -25,111 +25,135 @@ written by
int crysprStub_Prng(unsigned char *rn, int len)
{
(void)rn;
(void)len;
return(0);
(void)rn;
(void)len;
return(0);
}
int crysprStub_AES_SetKey(
int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR|AESGDM] */
bool bEncrypt, /* true Enxcrypt key, false: decrypt */
const unsigned char *kstr, /* key sttring*/
size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR|AESGDM] */
bool bEncrypt, /* true Enxcrypt key, false: decrypt */
const unsigned char *kstr, /* key sttring*/
size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
{
(void)cipher_type;
(void)bEncrypt;
(void)kstr;
(void)kstr_len;
(void)aes_key;
(void)cipher_type;
(void)bEncrypt;
(void)kstr;
(void)kstr_len;
(void)aes_key;
return(0);
return(0);
}
int crysprStub_AES_EcbCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* AES context */
const unsigned char *indata,/* src (clear text)*/
size_t inlen, /* length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen) /* dst len */
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* AES context */
const unsigned char *indata,/* src (clear text)*/
size_t inlen, /* length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen) /* dst len */
{
(void)bEncrypt;
(void)aes_key;
(void)indata;
(void)inlen;
(void)out_txt;
(void)outlen;
return -1;
(void)bEncrypt;
(void)aes_key;
(void)indata;
(void)inlen;
(void)out_txt;
(void)outlen;
return -1;
}
int crysprStub_AES_CtrCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* AES context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* length */
unsigned char *out_txt) /* dest */
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* AES context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* length */
unsigned char *out_txt) /* dest */
{
(void)bEncrypt;
(void)aes_key;
(void)iv;
(void)indata;
(void)inlen;
(void)out_txt;
return(-1);
}
int crysprStub_AES_GCMCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* AES context */
unsigned char *iv, /* iv */
const unsigned char *aad, /* associated data */
size_t aadlen,
const unsigned char * indata,
size_t inlen,
unsigned char *out_txt,
unsigned char* out_tag)
{
(void)bEncrypt;
(void)aes_key;
(void)iv;
(void)indata;
(void)inlen;
(void)out_txt;
return(-1);
(void)bEncrypt;
(void)aes_key;
(void)iv;
(void)aad;
(void)aadlen;
(void)indata;
(void)inlen;
(void)out_txt;
(void)out_tag;
return(-1);
}
unsigned char *crysprStub_SHA1_MsgDigest(
const unsigned char *m, /* in: message */
size_t m_len, /* message length */
unsigned char *md) /* out: message digest buffer *160 bytes */
const unsigned char *m, /* in: message */
size_t m_len, /* message length */
unsigned char *md) /* out: message digest buffer *160 bytes */
{
(void)m;
(void)m_len;
(void)md;
(void)m;
(void)m_len;
(void)md;
return(NULL);//return md;
return(NULL);//return md;
}
/*
* Password-based Key Derivation Function
*/
int crysprStub_KmPbkdf2(
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key */
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key */
{
(void)cryspr_cb;
(void)passwd;
(void)passwd_len;
(void)salt;
(void)salt_len;
(void)itr;
(void)key_len;
(void)out;
/* >>Todo:
* develop PBKDF2 using SHA1 primitive cryspr_cb->cryspr->sha1_msg_digest() for cryptolibs not providing it
*/
return(-1);
(void)cryspr_cb;
(void)passwd;
(void)passwd_len;
(void)salt;
(void)salt_len;
(void)itr;
(void)key_len;
(void)out;
/* >>Todo:
* develop PBKDF2 using SHA1 primitive cryspr_cb->cryspr->sha1_msg_digest() for cryptolibs not providing it
*/
return(-1);
}
static int crysprFallback_KmSetKey(CRYSPR_cb *cryspr_cb, bool bWrap, const unsigned char *kek, size_t kek_len)
{
CRYSPR_AESCTX *aes_kek = CRYSPR_GETKEK(cryspr_cb);
CRYSPR_AESCTX *aes_kek = CRYSPR_GETKEK(cryspr_cb);
if (cryspr_cb->cryspr->aes_set_key(HCRYPT_CTX_MODE_AESECB, bWrap, kek, kek_len, aes_kek)) {
HCRYPT_LOG(LOG_ERR, "aes_set_%s_key(kek) failed\n", bWrap? "encrypt": "decrypt");
return(-1);
}
if (cryspr_cb->cryspr->aes_set_key(HCRYPT_CTX_MODE_AESECB, bWrap, kek, kek_len, aes_kek)) {
HCRYPT_LOG(LOG_ERR, "aes_set_%s_key(kek) failed\n", bWrap? "encrypt": "decrypt");
return(-1);
}
return(0);
}
@ -144,7 +168,7 @@ static const unsigned char default_iv[] = {
int crysprFallback_AES_WrapKey(CRYSPR_cb *cryspr_cb,
unsigned char *out,
const unsigned char *in,
unsigned int inlen)
unsigned int inlen)
{
unsigned char *A, B[16], *R;
const unsigned char *iv = default_iv;
@ -180,13 +204,13 @@ int crysprFallback_AES_WrapKey(CRYSPR_cb *cryspr_cb,
}
}
memcpy(out, A, 8);
return 0;
return 0;
}
int crysprFallback_AES_UnwrapKey(CRYSPR_cb *cryspr_cb,
unsigned char *out,
const unsigned char *in,
unsigned int inlen)
unsigned int inlen)
{
unsigned char *A, B[16], *R;
const unsigned char *iv = default_iv;
@ -225,9 +249,9 @@ int crysprFallback_AES_UnwrapKey(CRYSPR_cb *cryspr_cb,
if (memcmp(A, iv, 8))
{
memset(out, 0, inlen);
return -1;
return -1;
}
return 0;
return 0;
}
static unsigned char *_crysprFallback_GetOutbuf(CRYSPR_cb *cryspr_cb, size_t pfx_len, size_t out_len)
@ -249,12 +273,12 @@ CRYSPR_cb *crysprHelper_Open(CRYSPR_methods *cryspr, size_t cb_len, size_t max_l
unsigned char *membuf;
size_t memsiz, padded_len = hcryptMsg_PaddedLen(max_len, 128/8);
if(cb_len < sizeof(*cryspr_cb)) {
HCRYPT_LOG(LOG_ERR, "crysprHelper_Open() cb_len too small (%zd < %zd)n",
cb_len, sizeof(*cryspr_cb));
return(NULL);
}
memsiz = cb_len + (CRYSPR_OUTMSGMAX * padded_len);
if(cb_len < sizeof(*cryspr_cb)) {
HCRYPT_LOG(LOG_ERR, "crysprHelper_Open() cb_len too small (%zd < %zd)n",
cb_len, sizeof(*cryspr_cb));
return(NULL);
}
memsiz = cb_len + (CRYSPR_OUTMSGMAX * padded_len);
#if !CRYSPR_HAS_AESCTR
memsiz += HCRYPT_CTR_STREAM_SZ;
#endif /* !CRYSPR_HAS_AESCTR */
@ -267,8 +291,8 @@ CRYSPR_cb *crysprHelper_Open(CRYSPR_methods *cryspr, size_t cb_len, size_t max_l
membuf = (unsigned char *)cryspr_cb;
membuf += sizeof(*cryspr_cb);
/*reserve cryspr's private data that caller will initialize */
membuf += (cb_len-sizeof(CRYSPR_cb));
/*reserve cryspr's private data that caller will initialize */
membuf += (cb_len-sizeof(CRYSPR_cb));
#if !CRYSPR_HAS_AESCTR
cryspr_cb->ctr_stream = membuf;
@ -289,28 +313,33 @@ CRYSPR_cb *crysprHelper_Open(CRYSPR_methods *cryspr, size_t cb_len, size_t max_l
int crysprHelper_Close(CRYSPR_cb *cryspr_cb)
{
free(cryspr_cb);
return(0);
free(cryspr_cb);
return(0);
}
static CRYSPR_cb *crysprFallback_Open(CRYSPR_methods *cryspr, size_t max_len)
{
CRYSPR_cb *cryspr_cb;
CRYSPR_cb *cryspr_cb;
cryspr_cb = crysprHelper_Open(cryspr, sizeof(CRYSPR_cb), max_len);
return(cryspr_cb);
cryspr_cb = crysprHelper_Open(cryspr, sizeof(CRYSPR_cb), max_len);
return(cryspr_cb);
}
static int crysprFallback_Close(CRYSPR_cb *cryspr_cb)
{
return(crysprHelper_Close(cryspr_cb));
return(crysprHelper_Close(cryspr_cb));
}
static int crysprFallback_MsSetKey(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, const unsigned char *key, size_t key_len)
{
CRYSPR_AESCTX *aes_sek = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx)); /* Ctx tells if it's for odd or even key */
if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) /* Encrypt key */
if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) { /* AES GCM mode */
if (cryspr_cb->cryspr->aes_set_key(HCRYPT_CTX_MODE_AESGCM, (ctx->flags & HCRYPT_CTX_F_ENCRYPT) != 0, key, key_len, aes_sek)) {
HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_encrypt_key(sek) failed\n");
return(-1);
}
} else if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) /* Encrypt key */
|| (ctx->mode == HCRYPT_CTX_MODE_AESCTR)) { /* CTR mode decrypts using encryption methods */
if (cryspr_cb->cryspr->aes_set_key(HCRYPT_CTX_MODE_AESCTR, true, key, key_len, aes_sek)) {
HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_encrypt_key(sek) failed\n");
@ -395,10 +424,17 @@ static int crysprFallback_MsEncrypt(
* to reserve room for unencrypted message header in output buffer
*/
pfx_len = ctx->msg_info->pfx_len;
/* Extra 16 bytes are needed for an authentication tag in GCM. */
const int aux_len = (ctx->mode == HCRYPT_CTX_MODE_AESGCM) ? HAICRYPT_AUTHTAG_MAX : 0;
/* Get buffer room from the internal circular output buffer */
out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, in_data[0].len);
/* Auth tag produced by AES GCM. */
unsigned char tag[HAICRYPT_AUTHTAG_MAX];
/*
* Get buffer room from the internal circular output buffer.
* Reserve additional 16 bytes for auth tag in AES GCM mode when needed.
*/
out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, in_data[0].len + aux_len);
if (NULL == out_msg) {
/* input data too big */
return(-1);
@ -406,6 +442,7 @@ static int crysprFallback_MsEncrypt(
switch(ctx->mode) {
case HCRYPT_CTX_MODE_AESCTR: /* Counter mode */
case HCRYPT_CTX_MODE_AESGCM:
{
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx)); /* Ctx tells if it's for odd or even key */
@ -416,48 +453,64 @@ static int crysprFallback_MsEncrypt(
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
if (ctx->mode == HCRYPT_CTX_MODE_AESGCM)
{
const int iret = cryspr_cb->cryspr->aes_gcm_cipher(true, aes_key, iv, in_data[0].pfx, pfx_len, in_data[0].payload, in_data[0].len,
&out_msg[pfx_len], tag);
if (iret) {
return(iret);
}
}
else {
#if CRYSPR_HAS_AESCTR
cryspr_cb->cryspr->aes_ctr_cipher(true, aes_key, iv, in_data[0].payload, in_data[0].len,
&out_msg[pfx_len]);
cryspr_cb->cryspr->aes_ctr_cipher(true, aes_key, iv, in_data[0].payload, in_data[0].len,
&out_msg[pfx_len]);
#else /*CRYSPR_HAS_AESCTR*/
/* Create CtrStream. May be longer than in_len (next cryspr block size boundary) */
int iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv);
if (iret) {
return(iret);
}
/* Reserve output buffer for cryspr */
out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, cryspr_cb->ctr_stream_len);
/* Create KeyStream (encrypt CtrStream) */
iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key,
cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len,
&out_msg[pfx_len], &out_len);
if (iret) {
HCRYPT_LOG(LOG_ERR, "%s", "hcOpenSSL_AES_ecb_cipher(encrypt, failed\n");
return(iret);
}
/* Create CtrStream. May be longer than in_len (next cryspr block size boundary) */
int iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv);
if (iret) {
return(iret);
}
/* Reserve output buffer for cryspr */
out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, cryspr_cb->ctr_stream_len);
/* Create KeyStream (encrypt CtrStream) */
iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key,
cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len,
&out_msg[pfx_len], &out_len);
if (iret) {
HCRYPT_LOG(LOG_ERR, "%s", "hcOpenSSL_AES_ecb_cipher(encrypt, failed\n");
return(iret);
}
#endif/*CRYSPR_HAS_AESCTR*/
}
/* Prepend packet prefix (clear text) in output buffer */
memcpy(out_msg, in_data[0].pfx, pfx_len);
/* CTR mode output length is same as input, no padding */
out_len = in_data[0].len;
if (ctx->mode == HCRYPT_CTX_MODE_AESGCM)
{
memcpy(out_msg + pfx_len + out_len, tag, sizeof(tag));
out_len += sizeof(tag);
}
break;
}
case HCRYPT_CTX_MODE_CLRTXT: /* Clear text mode (transparent mode for tests) */
@ -497,8 +550,12 @@ static int crysprFallback_MsEncrypt(
memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
}
#else /* CRYSPR_HAS_AESCTR */
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) {
// Encoding produced more payload (auth tag).
return (int)out_len;
}
#endif /* CRYSPR_HAS_AESCTR */
} else {
/* Copy header in output buffer if needed */
@ -533,8 +590,8 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx,
if (NULL != out_txt) {
switch(ctx->mode) {
case HCRYPT_CTX_MODE_AESCTR:
case HCRYPT_CTX_MODE_AESGCM:
{
#if CRYSPR_HAS_AESCTR
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx));
unsigned char iv[CRYSPR_AESBLKSZ];
@ -560,54 +617,63 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx,
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
cryspr_cb->cryspr->aes_ctr_cipher(false, aes_key, iv, in_data[0].payload, in_data[0].len,
if (ctx->mode == HCRYPT_CTX_MODE_AESGCM)
{
unsigned char* tag = in_data[0].payload + in_data[0].len - HAICRYPT_AUTHTAG_MAX;
int liret = cryspr_cb->cryspr->aes_gcm_cipher(false, aes_key, iv, in_data[0].pfx, ctx->msg_info->pfx_len, in_data[0].payload, in_data[0].len - HAICRYPT_AUTHTAG_MAX,
out_txt, tag);
if (liret) {
return(liret);
}
out_len = in_data[0].len - HAICRYPT_AUTHTAG_MAX;
}
else {
#if CRYSPR_HAS_AESCTR
cryspr_cb->cryspr->aes_ctr_cipher(false, aes_key, iv, in_data[0].payload, in_data[0].len,
out_txt);
out_len = in_data[0].len;
out_len = in_data[0].len;
#else /*CRYSPR_HAS_AESCTR*/
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx));
unsigned char iv[CRYSPR_AESBLKSZ];
int iret = 0;
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
/* Create CtrStream. May be longer than in_len (next cipher block size boundary) */
iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv);
if (iret) {
return(iret);
}
/* Reserve output buffer for cryspr */
out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, cryspr_cb->ctr_stream_len);
/* Create KeyStream (encrypt CtrStream) */
iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key,
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char*)&pki, ctx->salt, iv);
/* Create CtrStream. May be longer than in_len (next cipher block size boundary) */
int liret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv);
if (liret) {
return(liret);
}
/* Reserve output buffer for cryspr */
out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, cryspr_cb->ctr_stream_len);
/* Create KeyStream (encrypt CtrStream) */
liret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key,
cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len,
out_txt, &out_len);
if (iret) {
HCRYPT_LOG(LOG_ERR, "%s", "crysprNatural_AES_ecb_cipher(encrypt failed\n");
return(iret);
}
if (liret) {
HCRYPT_LOG(LOG_ERR, "%s", "crysprNatural_AES_ecb_cipher(encrypt failed\n");
return(liret);
}
#endif /*CRYSPR_HAS_AESCTR*/
}
break;
}
case HCRYPT_CTX_MODE_CLRTXT:
@ -638,6 +704,7 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx,
#else /* CRYSPR_HAS_AESCTR */
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, out_txt, out_len);
in_data->len = out_len;
#endif /* CRYSPR_HAS_AESCTR */
} else {
/* Copy header in output buffer if needed */
@ -683,9 +750,9 @@ CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr)
cryspr->aes_set_key = crysprStub_AES_SetKey;
cryspr->aes_ecb_cipher = crysprStub_AES_EcbCipher;
cryspr->aes_ctr_cipher = crysprStub_AES_CtrCipher;
cryspr->aes_gcm_cipher = crysprStub_AES_GCMCipher;
cryspr->sha1_msg_digest = crysprStub_SHA1_MsgDigest;
/* Crypto Session API */
cryspr->open = crysprFallback_Open;
cryspr->close = crysprFallback_Close;
@ -704,5 +771,5 @@ CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr)
HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance(void)
{
return((HaiCrypt_Cryspr)cryspr4SRT());
return((HaiCrypt_Cryspr)cryspr4SRT());
}

@ -40,32 +40,32 @@ extern "C" {
typedef struct tag_CRYSPR_cb {
#ifdef CRYSPR2
CRYSPR_AESCTX *aes_kek; /* Key Encrypting Key (KEK) */
CRYSPR_AESCTX *aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */
CRYSPR_AESCTX *aes_kek; /* Key Encrypting Key (KEK) */
CRYSPR_AESCTX *aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */
#define CRYSPR_GETKEK(cb) ((cb)->aes_kek)
#define CRYSPR_GETSEK(cb,kk) ((cb)->aes_sek[kk])
#else /*CRYSPR2*/
CRYSPR_AESCTX aes_kek; /* Key Encrypting Key (KEK) */
CRYSPR_AESCTX aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */
CRYSPR_AESCTX aes_kek; /* Key Encrypting Key (KEK) */
CRYSPR_AESCTX aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */
#define CRYSPR_GETKEK(cb) (&((cb)->aes_kek))
#define CRYSPR_GETSEK(cb,kk) (&((cb)->aes_sek[kk]))
#endif /*CRYSPR2*/
struct tag_CRYSPR_methods *cryspr;
struct tag_CRYSPR_methods *cryspr;
#if !CRYSPR_HAS_AESCTR
/* Reserve room to build the counter stream ourself */
#define HCRYPT_CTR_BLK_SZ CRYSPR_AESBLKSZ
#define HCRYPT_CTR_STREAM_SZ 2048
unsigned char * ctr_stream;
size_t ctr_stream_len; /* Content size */
size_t ctr_stream_siz; /* Allocated length */
unsigned char * ctr_stream;
size_t ctr_stream_len; /* Content size */
size_t ctr_stream_siz; /* Allocated length */
#endif /* !CRYSPR_HAS_AESCTR */
#define CRYSPR_OUTMSGMAX 6
uint8_t * outbuf; /* output circle buffer */
size_t outbuf_ofs; /* write offset in circle buffer */
size_t outbuf_siz; /* circle buffer size */
uint8_t * outbuf; /* output circle buffer */
size_t outbuf_ofs; /* write offset in circle buffer */
size_t outbuf_siz; /* circle buffer size */
} CRYSPR_cb;
typedef struct tag_CRYSPR_methods {
@ -100,6 +100,17 @@ typedef struct tag_CRYSPR_methods {
size_t inlen, /* src length */
unsigned char *out_txt);/* dest */
int (*aes_gcm_cipher)(
bool bEncrypt, /* true:encrypt false:decrypt (don't care with CTR) */
CRYSPR_AESCTX* aes_key, /* ctx */
unsigned char* iv, /* iv */
const unsigned char* aad, /* associated data */
size_t aadlen,
const unsigned char* indata, /* src (clear text) */
size_t inlen, /* src length */
unsigned char* out_txt, /* dest */
unsigned char* out_tag);
unsigned char *(*sha1_msg_digest)(
const unsigned char *m, /* in: message */
size_t m_len, /* message length */

@ -36,7 +36,7 @@ HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance (void); /* Return a default crys
#define HAICRYPT_PWD_MAX_SZ 80 /* MAX password (for Password-based Key Derivation) */
#define HAICRYPT_KEY_MAX_SZ 32 /* MAX key */
#define HAICRYPT_SECRET_MAX_SZ (HAICRYPT_PWD_MAX_SZ > HAICRYPT_KEY_MAX_SZ ? HAICRYPT_PWD_MAX_SZ : HAICRYPT_KEY_MAX_SZ)
#define HAICRYPT_AUTHTAG_MAX 16 /* maximum length of the auth tag (e.g. GCM) */
#define HAICRYPT_SALT_SZ 16
@ -60,6 +60,7 @@ typedef struct {
#define HAICRYPT_CFG_F_TX 0x01 /* !TX -> RX */
#define HAICRYPT_CFG_F_CRYPTO 0x02 /* Perform crypto Tx:Encrypt Rx:Decrypt */
#define HAICRYPT_CFG_F_FEC 0x04 /* Do Forward Error Correction */
#define HAICRYPT_CFG_F_GCM 0x08 /* Use AES-GCM */
unsigned flags;
HaiCrypt_Secret secret; /* Security Association */
@ -108,10 +109,15 @@ int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p
int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
/// @brief Check if the crypto service provider supports AES GCM.
/// @return returns 1 if AES GCM is supported, 0 otherwise.
int HaiCrypt_IsAESGCM_Supported(void);
/* Status values */
#define HAICRYPT_ERROR -1
#define HAICRYPT_ERROR_WRONG_SECRET -2
#define HAICRYPT_ERROR_CIPHER -3
#define HAICRYPT_OK 0

@ -44,10 +44,10 @@ int HaiCrypt_SetLogLevel(int level, int logfa)
#define HAICRYPT_DEFINE_LOG_DISPATCHER(LOGLEVEL, dispatcher) \
int HaiCrypt_LogF_##LOGLEVEL ( const char* file, int line, const char* function, const char* format, ...) \
{ \
va_list ap; \
va_start(ap, format); \
srt_logging::LogDispatcher& lg = hclog.dispatcher; \
if (!lg.CheckEnabled()) return -1; \
va_list ap; \
va_start(ap, format); \
lg().setloc(file, line, function).vform(format, ap); \
va_end(ap); \
return 0; \

@ -21,7 +21,7 @@ HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_EMERG);
#define HCRYPT_LOG_INIT()
#define HCRYPT_LOG_EXIT()
#define HCRYPT_LOG(lvl, fmt, ...) HaiCrypt_LogF_##lvl (__FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__)
#define HCRYPT_LOG(lvl, ...) HaiCrypt_LogF_##lvl (__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#if ENABLE_HAICRYPT_LOGGING == 2
#define HCRYPT_DEV 1

@ -156,7 +156,7 @@ int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc)
|| hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[1], cfg)) {
free(crypto);
return(-1);
}
}
/* Generate keys for first (default) context */
if (hcryptCtx_Tx_Rekey(crypto, &crypto->ctx_pair[0])) {
free(crypto);
@ -196,6 +196,9 @@ int HaiCrypt_ExtractConfig(HaiCrypt_Handle hhcSrc, HaiCrypt_Cfg* pcfg)
pcfg->flags = HAICRYPT_CFG_F_CRYPTO;
if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) == HCRYPT_CTX_F_ENCRYPT)
pcfg->flags |= HAICRYPT_CFG_F_TX;
if (ctx->mode == HCRYPT_CTX_MODE_AESGCM)
pcfg->flags |= HAICRYPT_CFG_F_GCM;
/* Set this explicitly - this use of this library is SRT only. */
pcfg->xport = HAICRYPT_XPT_SRT;
@ -237,7 +240,8 @@ int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handl
if (tx) {
HaiCrypt_Cfg crypto_config;
HaiCrypt_ExtractConfig(hhcSrc, &crypto_config);
if (-1 == HaiCrypt_ExtractConfig(hhcSrc, &crypto_config))
return -1;
/*
* Just invert the direction written in flags and use the
@ -303,8 +307,7 @@ int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handl
return(-1);
}
/* Configure contexts */
/* Configure contexts. Note that GCM mode has been already copied from the source context. */
if (hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[0], NULL)
|| hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[1], NULL)) {
free(cryptoClone);
@ -336,3 +339,12 @@ int HaiCrypt_Close(HaiCrypt_Handle hhc)
HCRYPT_LOG_EXIT();
return rc;
}
int HaiCrypt_IsAESGCM_Supported(void)
{
#if CRYSPR_HAS_AESGCM
return 1;
#else
return 0;
#endif
}

@ -32,10 +32,6 @@ written by
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#if defined(_MSC_VER)
#pragma warning(disable:4267)
#pragma warning(disable:4018)
#endif
#else
#include <sys/time.h>
#endif
@ -163,7 +159,18 @@ int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *a
int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto);
int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto, void *out_p[], size_t out_len_p[], int maxout);
/// @brief Initialize receiving crypto context.
/// @param crypto library instance handle.
/// @param ctx additional crypto context.
/// @param cfg crypto configuration.
/// @return -1 on error, 0 otherwise.
int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg);
/// @brief Parse an incoming message related to cryptography module.
/// @param crypto library instance handle.
/// @param msg a message to parse.
/// @param msg_len length of the message in bytes.
/// @return 0 on success; -3 on cipher mode mismatch; -2 on unmatched shared secret; -1 on other failures.
int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *msg, size_t msg_len);
#endif /* HCRYPT_H */

@ -45,7 +45,7 @@ typedef struct {
typedef struct tag_hcrypt_Ctx {
struct tag_hcrypt_Ctx * alt; /* Alternative ctx (even/odd) */
#define HCRYPT_CTX_F_MSG 0x00FF /* Aligned wiht message header flags */
#define HCRYPT_CTX_F_MSG 0x00FF /* Aligned with message header flags */
#define HCRYPT_CTX_F_eSEK HCRYPT_MSG_F_eSEK
#define HCRYPT_CTX_F_oSEK HCRYPT_MSG_F_oSEK
#define HCRYPT_CTX_F_xSEK HCRYPT_MSG_F_xSEK
@ -68,6 +68,7 @@ typedef struct tag_hcrypt_Ctx {
#define HCRYPT_CTX_MODE_AESECB 1 /* Electronic Code Book mode */
#define HCRYPT_CTX_MODE_AESCTR 2 /* Counter mode */
#define HCRYPT_CTX_MODE_AESCBC 3 /* Cipher-block chaining mode */
#define HCRYPT_CTX_MODE_AESGCM 4 /* AES GCM authenticated encryption */
unsigned mode;
struct {

@ -14,9 +14,9 @@ written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memcpy */
@ -24,16 +24,18 @@ written by
int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg)
{
ctx->mode = HCRYPT_CTX_MODE_AESCTR;
ctx->status = HCRYPT_CTX_S_INIT;
if (cfg) {
ctx->mode = (cfg->flags & HAICRYPT_CFG_F_GCM) ? HCRYPT_CTX_MODE_AESGCM : HCRYPT_CTX_MODE_AESCTR;
}
ctx->status = HCRYPT_CTX_S_INIT;
ctx->msg_info = crypto->msg_info;
ctx->msg_info = crypto->msg_info;
if (cfg && hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) {
return(-1);
}
ctx->status = HCRYPT_CTX_S_SARDY;
return(0);
if (cfg && hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) {
return(-1);
}
ctx->status = HCRYPT_CTX_S_SARDY;
return(0);
}
int hcryptCtx_Rx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *sek, size_t sek_len)
@ -98,9 +100,31 @@ int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t m
}
/* Check options support */
if ((HCRYPT_CIPHER_AES_CTR != km_msg[HCRYPT_MSG_KM_OFS_CIPHER])
|| (HCRYPT_AUTH_NONE != km_msg[HCRYPT_MSG_KM_OFS_AUTH])) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported option\n");
if (HCRYPT_CIPHER_AES_CTR != km_msg[HCRYPT_MSG_KM_OFS_CIPHER]
&& HCRYPT_CIPHER_AES_GCM != km_msg[HCRYPT_MSG_KM_OFS_CIPHER])
{
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported cipher\n");
return(-1);
}
#if !CRYSPR_HAS_AESGCM
/* Only OpenSSL EVP crypto provider allows the use of GCM.Add this condition. Reject if GCM is not supported by the CRYSPR. */
if (HCRYPT_CIPHER_AES_GCM == km_msg[HCRYPT_MSG_KM_OFS_CIPHER])
{
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported GCM cipher\n");
return(-1);
}
#endif
if (HCRYPT_CIPHER_AES_GCM == km_msg[HCRYPT_MSG_KM_OFS_CIPHER]
&& HCRYPT_AUTH_AES_GCM != km_msg[HCRYPT_MSG_KM_OFS_AUTH]) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg GCM auth method was expected.\n");
return(-1);
}
if (HCRYPT_CIPHER_AES_CTR == km_msg[HCRYPT_MSG_KM_OFS_CIPHER]
&& HCRYPT_AUTH_NONE != km_msg[HCRYPT_MSG_KM_OFS_AUTH]) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported auth method\n");
return(-1);
}
@ -144,6 +168,13 @@ int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t m
do_pbkdf = 1; /* Impact on password derived kek */
}
/* Check cipher mode */
if (ctx->mode != km_msg[HCRYPT_MSG_KM_OFS_CIPHER])
{
HCRYPT_LOG(LOG_WARNING, "%s", "cipher mode mismatch\n");
return(-3);
}
/*
* Regenerate KEK if it is password derived
* and Salt or SEK length changed
@ -159,7 +190,7 @@ int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t m
/* Unwrap SEK(s) and set in context */
if (0 > crypto->cryspr->km_unwrap(crypto->cryspr_cb, seks,
&km_msg[HCRYPT_MSG_KM_OFS_SALT + salt_len],
(sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) {
(unsigned int)((sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ))) {
HCRYPT_LOG(LOG_WARNING, "%s", "unwrap key failed\n");
return(-2); //Report unmatched shared secret
}
@ -184,7 +215,6 @@ int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t m
alt->salt_len = salt_len;
if (kek_len) { /* New or changed KEK */
// memcpy(&alt->aes_kek, &ctx->aes_kek, sizeof(alt->aes_kek));
alt->status = HCRYPT_CTX_S_SARDY;
}

@ -14,18 +14,18 @@ written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memcpy */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <win/wintime.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <win/wintime.h>
#else
#include <sys/time.h>
#include <sys/time.h>
#endif
#include "hcrypt.h"
@ -33,7 +33,7 @@ int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cf
{
ctx->cfg.key_len = cfg->key_len;
ctx->mode = HCRYPT_CTX_MODE_AESCTR;
ctx->mode = (cfg->flags & HAICRYPT_CFG_F_GCM) ? HCRYPT_CTX_MODE_AESGCM : HCRYPT_CTX_MODE_AESCTR;
ctx->status = HCRYPT_CTX_S_INIT;
ctx->msg_info = crypto->msg_info;
@ -52,14 +52,14 @@ int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx)
/* Generate Salt */
ctx->salt_len = HAICRYPT_SALT_SZ;
if (0 > (iret = crypto->cryspr->prng(ctx->salt, ctx->salt_len))) {
if (0 > (iret = crypto->cryspr->prng(ctx->salt, (int)ctx->salt_len))) {
HCRYPT_LOG(LOG_ERR, "PRNG(salt[%zd]) failed\n", ctx->salt_len);
return(iret);
}
/* Generate SEK */
ctx->sek_len = ctx->cfg.key_len;
if (0 > (iret = crypto->cryspr->prng(ctx->sek, ctx->sek_len))) {
if (0 > (iret = crypto->cryspr->prng(ctx->sek, (int)ctx->sek_len))) {
HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", ctx->sek_len);
return(iret);
}
@ -195,7 +195,7 @@ int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto)
HCRYPT_LOG(LOG_DEBUG, "refresh/generate SEK. salt_len=%d sek_len=%d\n", (int)new_ctx->salt_len, (int)new_ctx->sek_len);
if (0 > crypto->cryspr->prng(new_ctx->sek, new_ctx->sek_len)) {
if (0 > crypto->cryspr->prng(new_ctx->sek, (int)new_ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", new_ctx->sek_len);
return(-1);
}
@ -299,9 +299,9 @@ int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *a
2 == sek_cnt ? HCRYPT_MSG_F_xSEK : (ctx->flags & HCRYPT_MSG_F_xSEK));
/* crypto->KMmsg_cache[4..7]: KEKI=0 */
km_msg[HCRYPT_MSG_KM_OFS_CIPHER] = HCRYPT_CIPHER_AES_CTR;
km_msg[HCRYPT_MSG_KM_OFS_AUTH] = HCRYPT_AUTH_NONE;
km_msg[HCRYPT_MSG_KM_OFS_SE] = crypto->se;
km_msg[HCRYPT_MSG_KM_OFS_CIPHER] = (ctx->mode == HCRYPT_CTX_MODE_AESGCM) ? HCRYPT_CIPHER_AES_GCM : HCRYPT_CIPHER_AES_CTR;
km_msg[HCRYPT_MSG_KM_OFS_AUTH] = (ctx->mode == HCRYPT_CTX_MODE_AESGCM) ? HCRYPT_AUTH_AES_GCM : HCRYPT_AUTH_NONE;
km_msg[HCRYPT_MSG_KM_OFS_SE] = (char) crypto->se;
hcryptMsg_KM_SetSaltLen(km_msg, ctx->salt_len);
hcryptMsg_KM_SetSekLen(km_msg, ctx->sek_len);
@ -322,7 +322,7 @@ int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *a
}
if (0 > crypto->cryspr->km_wrap(crypto->cryspr_cb,
&km_msg[HCRYPT_MSG_KM_OFS_SALT + ctx->salt_len],
seks, sek_cnt * ctx->sek_len)) {
seks, (unsigned int)(sek_cnt * ctx->sek_len))) {
HCRYPT_LOG(LOG_ERR, "%s", "wrap key failed\n");
return(-1);
@ -360,7 +360,7 @@ int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto)
* prepare next SEK for announcement
*/
hcryptCtx_Tx_Refresh(crypto);
HCRYPT_LOG(LOG_INFO, "KM[%d] Pre-announced\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);

@ -122,8 +122,10 @@ typedef struct {
#define HCRYPT_CIPHER_AES_ECB 1
#define HCRYPT_CIPHER_AES_CTR 2
#define HCRYPT_CIPHER_AES_CBC 3
#define HCRYPT_CIPHER_AES_GCM 4
#define HCRYPT_AUTH_NONE 0
#define HCRYPT_AUTH_AES_GCM 1
#define HCRYPT_SE_TSUDP 1
hcrypt_MsgInfo * hcryptMsg_STA_MsgInfo(void);
@ -148,8 +150,8 @@ typedef struct {
#define hcryptMsg_KM_GetSaltLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_SLEN] * 4)
#define hcryptMsg_KM_GetSekLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_KLEN] * 4)
#define hcryptMsg_KM_SetSaltLen(msg,len)do {(msg)[HCRYPT_MSG_KM_OFS_SLEN] = (len)/4;} while(0)
#define hcryptMsg_KM_SetSekLen(msg,len) do {(msg)[HCRYPT_MSG_KM_OFS_KLEN] = (len)/4;} while(0)
#define hcryptMsg_KM_SetSaltLen(msg,len)do {(msg)[HCRYPT_MSG_KM_OFS_SLEN] = (unsigned char)(len)/4;} while(0)
#define hcryptMsg_KM_SetSekLen(msg,len) do {(msg)[HCRYPT_MSG_KM_OFS_KLEN] = (unsigned char)(len)/4;} while(0)
#endif /* HCRYPT_MSG_H */

@ -53,7 +53,7 @@ int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc,
if (0 > (nb = crypto->cryspr->ms_decrypt(crypto->cryspr_cb, ctx, &indata, 1, NULL, NULL, NULL))) {
HCRYPT_LOG(LOG_ERR, "%s", "ms_decrypt failed\n");
} else {
nb = indata.len;
nb = (int)indata.len;
}
} else { /* No key received yet */
nb = 0;
@ -124,9 +124,6 @@ int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc,
|| (0 != memcmp(ctx->KMmsg_cache, in_msg, in_len))) { /* or different */
nbout = hcryptCtx_Rx_ParseKM(crypto, in_msg, in_len);
//-2: unmatched shared secret
//-1: other failures
//0: success
} else {
nbout = 0;
}

@ -14,20 +14,20 @@ written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
Adaptation for SRT.
*****************************************************************************/
#include <sys/types.h>
#include <stdlib.h> /* NULL */
#include <string.h> /* memcpy */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdint.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdint.h>
#else
#include <arpa/inet.h> /* htonl */
#include <arpa/inet.h> /* htonl */
#endif
#include "hcrypt.h"
@ -52,29 +52,28 @@ int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_
return(crypto->msg_info->pfx_len);
}
int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout)
int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = crypto->ctx;
int nbout = 0;
if ((NULL == crypto)
|| (NULL == ctx)
|| (NULL == crypto->ctx)
|| (NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "ManageKeys: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
HCRYPT_LOG(LOG_ERR, "ManageKeys: invalid params: crypto=%p out_p=%p out_len_p=%p\n",
crypto, out_p, out_len_p);
return(-1);
}
/* Manage Key Material (refresh, announce, decommission) */
hcryptCtx_Tx_ManageKM(crypto);
ctx = crypto->ctx;
if (NULL == ctx) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
if (NULL == crypto->ctx) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context NULL after ManageKM call\n");
return(-1);
}
ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE);
ASSERT(crypto->ctx->status == HCRYPT_CTX_S_ACTIVE);
nbout = hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout);
return(nbout);
@ -83,30 +82,35 @@ int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[
int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = crypto->ctx;
hcrypt_Ctx *ctx = NULL;
if ((NULL == crypto)
|| (NULL == ctx)){
|| (NULL == (ctx = crypto->ctx))) {
HCRYPT_LOG(LOG_ERR, "GetKeyFlags: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
return(hcryptCtx_GetKeyFlags(ctx));
return(hcryptCtx_GetKeyFlags(crypto->ctx));
}
int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc,
unsigned char *in_pfx, unsigned char *in_data, size_t in_len)
int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc,
unsigned char *in_pfx, unsigned char *in_data, size_t in_len)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = crypto->ctx;
hcrypt_Ctx *ctx = NULL;
int nbout = 0;
if ((NULL == crypto)
|| (NULL == ctx)){
|| (NULL == (ctx = crypto->ctx))) {
HCRYPT_LOG(LOG_ERR, "Tx_Data: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
/* Get/Set packet index */
ctx->msg_info->indexMsg(in_pfx, ctx->MSpfx_cache);
ctx->msg_info->indexMsg(in_pfx, ctx->MSpfx_cache);
if (hcryptMsg_GetKeyIndex(ctx->msg_info, in_pfx) != hcryptCtx_GetKeyIndex(ctx))
{
HCRYPT_LOG(LOG_ERR, "Tx_Data: Key mismatch!");
}
/* Encrypt */
{
@ -125,16 +129,16 @@ int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc,
return(nbout);
}
int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc,
unsigned char *in_msg, size_t in_len,
int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc,
unsigned char *in_msg, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = crypto->ctx;
hcrypt_Ctx *ctx = NULL;
int nb, nbout = 0;
if ((NULL == crypto)
|| (NULL == ctx)
|| (NULL == (ctx = crypto->ctx))
|| (NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "Tx_Process: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
@ -144,11 +148,6 @@ int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc,
/* Manage Key Material (refresh, announce, decommission) */
hcryptCtx_Tx_ManageKM(crypto);
ctx = crypto->ctx;
if (NULL == ctx) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
return(-1);
}
ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE);
nbout += hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout);

@ -0,0 +1,57 @@
#
# SRT - Secure, Reliable, Transport Copyright (c) 2022 Haivision Systems Inc.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at http://mozilla.org/MPL/2.0/.
#
# Check for C++11 std::put_time().
#
# Sets:
# HAVE_CXX_STD_PUT_TIME
include(CheckCSourceCompiles)
function(CheckCXXStdPutTime)
unset(HAVE_CXX_STD_PUT_TIME CACHE)
set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6
unset(CMAKE_REQUIRED_FLAGS)
unset(CMAKE_REQUIRED_LIBRARIES)
unset(CMAKE_REQUIRED_LINK_OPTIONS)
set(CheckCXXStdPutTime_CODE
"
#include <iostream>
#include <iomanip>
#include <ctime>
int main(void)
{
const int result = 0;
std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
std::cout
<< std::put_time(&tm, \"%FT%T\")
<< std::setfill('0')
<< std::setw(6)
<< std::endl;
return result;
}
"
)
# NOTE: Should we set -std or use the current compiler configuration.
# It seems that the top level build does not track the compiler
# in a consistent manner. So Maybe we need this?
set(CMAKE_REQUIRED_FLAGS "-std=c++11")
# Check that the compiler can build the std::put_time() example:
message(STATUS "Checking for C++ 'std::put_time()':")
check_cxx_source_compiles(
"${CheckCXXStdPutTime_CODE}"
HAVE_CXX_STD_PUT_TIME)
endfunction(CheckCXXStdPutTime)

@ -36,7 +36,7 @@ function(CheckGCCAtomicIntrinsics)
unset(CMAKE_REQUIRED_LIBRARIES)
unset(CMAKE_REQUIRED_LINK_OPTIONS)
# Check for existance of libatomic and whether this symbol is present.
# Check for existence of libatomic and whether this symbol is present.
check_library_exists(atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC)
set(CheckLibAtomicCompiles_CODE

@ -141,6 +141,7 @@ function(ShowProjectConfig)
" HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC: ${HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC}\n"
" HAVE_CXX_ATOMIC: ${HAVE_CXX_ATOMIC}\n"
" HAVE_CXX_ATOMIC_STATIC: ${HAVE_CXX_ATOMIC_STATIC}\n"
" HAVE_CXX_STD_PUT_TIME: ${HAVE_CXX_STD_PUT_TIME}\n"
" Project Configuration:\n"
" ENABLE_DEBUG: ${ENABLE_DEBUG}\n"
" ENABLE_CXX11: ${ENABLE_CXX11}\n"

@ -0,0 +1,3 @@
## Scripts for building SRT for Android
See [Building SRT for Android](../../docs/build/build-android.md) for the instructions.

@ -0,0 +1,111 @@
#!/bin/sh
echo_help()
{
echo "Usage: $0 [options...]"
echo " -n NDK root path for the build"
echo " -a Target API level"
echo " -t Space-separated list of target architectures"
echo " Android supports the following architectures: armeabi-v7a arm64-v8a x86 x86_64"
echo " -e Encryption library to be used. Possible options: openssl (default) mbedtls"
echo " -o OpenSSL version. E.g. 1.1.1l"
echo " -m Mbed TLS version. E.g. v2.26.0"
echo
echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/23.0.7599858 -a 28 -t \"arm64-v8a x86_64\""
echo
}
# Init optional command line vars
NDK_ROOT=""
API_LEVEL=28
BUILD_TARGETS="armeabi-v7a arm64-v8a x86 x86_64"
OPENSSL_VERSION=1.1.1l
ENC_LIB=openssl
MBEDTLS_VERSION=v2.26.0
while getopts n:a:t:o:s:e:m: option
do
case "${option}"
in
n) NDK_ROOT=${OPTARG};;
a) API_LEVEL=${OPTARG};;
t) BUILD_TARGETS=${OPTARG};;
o) OPENSSL_VERSION=${OPTARG};;
s) SRT_VERSION=${OPTARG};;
e) ENC_LIB=${OPTARG};;
m) MBEDTLS_VERSION=${OPTARG};;
*) twentytwo=${OPTARG};;
esac
done
echo_help
if [ -z "$NDK_ROOT" ] ; then
echo "NDK directory not set."
exit 128
else
if [ ! -d "$NDK_ROOT" ]; then
echo "NDK directory does not exist: $NDK_ROOT"
exit 128
fi
fi
SCRIPT_DIR=$(pwd)
HOST_TAG='unknown'
unamestr=$(uname -s)
if [ "$unamestr" = 'Linux' ]; then
HOST_TAG='linux-x86_64'
elif [ "$unamestr" = 'Darwin' ]; then
if [ $(uname -p) = 'arm' ]; then
echo "NDK does not currently support ARM64"
exit 128
else
HOST_TAG='darwin-x86_64'
fi
fi
# Write files relative to current location
BASE_DIR=$(pwd)
case "${BASE_DIR}" in
*\ * )
echo "Your path contains whitespaces, which is not supported by 'make install'."
exit 128
;;
esac
cd "${BASE_DIR}"
if [ $ENC_LIB = 'openssl' ]; then
echo "Building OpenSSL $OPENSSL_VERSION"
$SCRIPT_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION -d $BASE_DIR -h $HOST_TAG
elif [ $ENC_LIB = 'mbedtls' ]; then
if [ ! -d $BASE_DIR/mbedtls ]; then
git clone https://github.com/ARMmbed/mbedtls mbedtls
if [ ! -z "$MBEDTLS_VERSION" ]; then
git -C $BASE_DIR/mbedtls checkout $MBEDTLS_VERSION
fi
fi
else
echo "Unknown encryption library. Possible options: openssl mbedtls"
exit 128
fi
# Build working copy of srt repository
REPO_DIR="../.."
for build_target in $BUILD_TARGETS; do
LIB_DIR=$BASE_DIR/$build_target/lib
JNI_DIR=$BASE_DIR/prebuilt/$build_target
mkdir -p $JNI_DIR
if [ $ENC_LIB = 'mbedtls' ]; then
$SCRIPT_DIR/mkmbedtls -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/mbedtls -i $BASE_DIR/$build_target
cp $LIB_DIR/libmbedcrypto.so $JNI_DIR/libmbedcrypto.so
cp $LIB_DIR/libmbedtls.so $JNI_DIR/libmbedtls.so
cp $LIB_DIR/libmbedx509.so $JNI_DIR/libmbedx509.so
fi
git -C $REPO_DIR clean -fd -e scripts
$SCRIPT_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $REPO_DIR -i $BASE_DIR/$build_target
cp $LIB_DIR/libsrt.so $JNI_DIR/libsrt.so
done

@ -0,0 +1,27 @@
#!/bin/sh
while getopts s:i:t:n:a: option
do
case "${option}"
in
s) SRC_DIR=${OPTARG};;
i) INSTALL_DIR=${OPTARG};;
t) ARCH_ABI=${OPTARG};;
n) NDK_ROOT=${OPTARG};;
a) API_LEVEL=${OPTARG};;
*) twentytwo=${OPTARG};;
esac
done
BUILD_DIR=/tmp/mbedtls_android_build
rm -rf $BUILD_DIR
mkdir $BUILD_DIR
cd $BUILD_DIR
cmake -DENABLE_TESTING=Off -DUSE_SHARED_MBEDTLS_LIBRARY=On \
-DCMAKE_PREFIX_PATH=$INSTALL_DIR -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR -DCMAKE_ANDROID_NDK=$NDK_ROOT \
-DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=$API_LEVEL -DCMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \
-DCMAKE_C_FLAGS="-fPIC" -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo $SRC_DIR
cmake --build .
cmake --install .

@ -0,0 +1,32 @@
#!/bin/sh
while getopts s:i:t:n:a:e: option
do
case "${option}"
in
s) SRC_DIR=${OPTARG};;
i) INSTALL_DIR=${OPTARG};;
t) ARCH_ABI=${OPTARG};;
n) NDK_ROOT=${OPTARG};;
a) API_LEVEL=${OPTARG};;
e) ENC_LIB=${OPTARG};;
*) twentytwo=${OPTARG};;
esac
done
cd $SRC_DIR
./configure --use-enclib=$ENC_LIB \
--use-openssl-pc=OFF \
--OPENSSL_INCLUDE_DIR=$INSTALL_DIR/include \
--OPENSSL_CRYPTO_LIBRARY=$INSTALL_DIR/lib/libcrypto.a --OPENSSL_SSL_LIBRARY=$INSTALL_DIR/lib/libssl.a \
--STATIC_MBEDTLS=FALSE \
--MBEDTLS_INCLUDE_DIR=$INSTALL_DIR/include --MBEDTLS_INCLUDE_DIRS=$INSTALL_DIR/include \
--MBEDTLS_LIBRARIES=$INSTALL_DIR/lib/libmbedtls.so \
--CMAKE_PREFIX_PATH=$INSTALL_DIR --CMAKE_INSTALL_PREFIX=$INSTALL_DIR --CMAKE_ANDROID_NDK=$NDK_ROOT \
--CMAKE_SYSTEM_NAME=Android --CMAKE_SYSTEM_VERSION=$API_LEVEL --CMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \
--CMAKE_C_FLAGS="-fPIC" --CMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \
--enable-c++11 --enable-stdcxx-sync \
--enable-debug=2 --enable-logging=0 --enable-heavy-logging=0 --enable-apps=0
make
make install

@ -0,0 +1,85 @@
#!/bin/sh
while getopts n:o:a:t:d:h: option
do
case "${option}"
in
n) ANDROID_NDK=${OPTARG};;
o) OPENSSL_VERSION=${OPTARG};;
a) API_LEVEL=${OPTARG};;
t) BUILD_TARGETS=${OPTARG};;
d) OUT_DIR=${OPTARG};;
h) HOST_TAG=${OPTARG};;
*) twentytwo=${OPTARG};;
esac
done
BUILD_DIR=/tmp/openssl_android_build
if [ ! -d openssl-${OPENSSL_VERSION} ]
then
if [ ! -f openssl-${OPENSSL_VERSION}.tar.gz ]
then
wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz || exit 128
fi
tar xzf openssl-${OPENSSL_VERSION}.tar.gz || exit 128
fi
cd openssl-${OPENSSL_VERSION} || exit 128
##### export ndk directory. Required by openssl-build-scripts #####
case ${OPENSSL_VERSION} in
1.1.1*)
export ANDROID_NDK_HOME=$ANDROID_NDK
;;
*)
export ANDROID_NDK_ROOT=$ANDROID_NDK
;;
esac
export PATH=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG/bin:$PATH
##### build-function #####
build_the_thing() {
make clean
./Configure $SSL_TARGET -D__ANDROID_API__=$API_LEVEL && \
make SHLIB_EXT=.so && \
make install SHLIB_EXT=.so DESTDIR=$DESTDIR || exit 128
}
##### set variables according to build-tagret #####
for build_target in $BUILD_TARGETS
do
case $build_target in
armeabi-v7a)
DESTDIR="$BUILD_DIR/armeabi-v7a"
SSL_TARGET="android-arm"
;;
x86)
DESTDIR="$BUILD_DIR/x86"
SSL_TARGET="android-x86"
;;
x86_64)
DESTDIR="$BUILD_DIR/x86_64"
SSL_TARGET="android-x86_64"
;;
arm64-v8a)
DESTDIR="$BUILD_DIR/arm64-v8a"
SSL_TARGET="android-arm64"
;;
esac
rm -rf $DESTDIR
build_the_thing
#### copy libraries and includes to output-directory #####
mkdir -p $OUT_DIR/$build_target/include
cp -R $DESTDIR/usr/local/include/* $OUT_DIR/$build_target/include
cp -R $DESTDIR/usr/local/ssl/* $OUT_DIR/$build_target/
mkdir -p $OUT_DIR/$build_target/lib
cp -R $DESTDIR/usr/local/lib/*.so $OUT_DIR/$build_target/lib
cp -R $DESTDIR/usr/local/lib/*.a $OUT_DIR/$build_target/lib
done
echo Success

@ -101,7 +101,7 @@ if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) {
# get pthreads from nuget if CXX11 is not enabled
if ( $CXX11 -eq "OFF" ) {
# get pthreads (this is legacy, and is only availble in nuget for VS2015 and VS2013)
# get pthreads (this is legacy, and is only available in nuget for VS2015 and VS2013)
if ( $VS_VERSION -gt 2015 ) {
Write-Output "Pthreads is not recommended for use beyond VS2015 and is not supported by this build script - aborting build"
throw

@ -114,7 +114,7 @@ set errortypes {
SIDINVAL "Invalid socket ID"
ISUNBOUND "Cannot do this operation on an UNBOUND socket"
NOLISTEN "Socket is not in listening state"
ISRENDEZVOUS "Listen/accept is not supported in rendezous connection setup"
ISRENDEZVOUS "Listen/accept is not supported in rendezvous connection setup"
ISRENDUNBOUND "Cannot call connect on UNBOUND socket in rendezvous connection setup"
INVALMSGAPI "Incorrect use of Message API (sendmsg/recvmsg)."
INVALBUFFERAPI "Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile)."

@ -11,7 +11,7 @@ ExternalProject_Add(
BINARY_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-build"
GIT_REPOSITORY
https://github.com/google/googletest.git
GIT_TAG release-1.8.1
GIT_TAG release-1.10.0
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

@ -4,15 +4,24 @@ Script designed to generate release notes template with main sections, contribut
In order to obtain the git log file since the previous release (e.g., v1.4.0), use the following command:
```shell
git log --pretty=format:"%h|%s|%an|%ae" v1.4.0...HEAD > commits.csv
```
git log --pretty=format:"%h|%s|%an|%ae" v1.4.0...HEAD^ > commits.csv
Use the produced `commits.csv` file as an input to the script:
```shell
python scripts/release-notes/generate-release-notes.py commits.csv
```
The script produces `release-notes.md` as an output.
## Requirements
* Python 3.6+
To install Python libraries use:
```
```shell
pip install -r requirements.txt
```

@ -115,7 +115,7 @@ fields.tsbpd_delay = ProtoField.uint16("srt_dev.tsbpd_delay", "TsbPd Delay", bas
fields.rcv_tsbpd_delay = ProtoField.uint16("srt_dev.rcv_tsbpd_delay", "Receiver TsbPd Delay", base.DEC)
fields.snd_tsbpd_delay = ProtoField.uint16("srt_dev.snd_tsbpd_delay", "Sender TsbPd Delay", base.DEC)
-- V adn PT status flag
-- V and PT status flag
local V_state_select = {
[1] = "Initial version"
}
@ -252,7 +252,7 @@ function srt_dev.dissector (tvb, pinfo, tree)
offset = offset + 4
if UDT_version == 4 then
-- UDT version is 4, packet is diffrent from UDT version 5
-- UDT version is 4, packet is different from UDT version 5
-- Handle sock type
local sock_type = tvb(offset, 4):uint()
if sock_type == 1 then

@ -0,0 +1,10 @@
/* Copyright © 2023 Steve Lhomme */
/* SPDX-License-Identifier: ISC */
#include <windows.h>
#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600 /* _WIN32_WINNT_VISTA */
#error NOPE
#endif
int main(void)
{
return 0;
}

@ -125,9 +125,11 @@ Section "Install"
; Header files.
CreateDirectory "$INSTDIR\include\srt"
SetOutPath "$INSTDIR\include\srt"
File "${RepoDir}\srtcore\access_control.h"
File "${RepoDir}\srtcore\logging_api.h"
File "${RepoDir}\srtcore\platform_sys.h"
File "${RepoDir}\srtcore\srt.h"
File "${RepoDir}\srtcore\udt.h"
File "${Build64Dir}\version.h"
CreateDirectory "$INSTDIR\include\win"

@ -41,7 +41,7 @@ written by
// CODE NOT IN USE 408: unused: no timeout predicted for listener callback
#define SRT_REJX_CONFLICT 1409 // The resource being accessed is already locked for modification. This is in case of m=publish and the specified resource is currently read-only.
// CODE NOT IN USE 410: unused: treated as a specific case of 404
// CODE NOT IN USE 411: unused: no reason to include lenght in the protocol
// CODE NOT IN USE 411: unused: no reason to include length in the protocol
// CODE NOT IN USE 412: unused: preconditions not predicted in AC
// CODE NOT IN USE 413: unused: AC size is already defined as 512
// CODE NOT IN USE 414: unused: AC size is already defined as 512

@ -326,6 +326,7 @@ SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group)
// We have a rollover on the socket value, so
// definitely we haven't made the Columbus mistake yet.
m_SocketIDGenerator = MAX_SOCKET_VAL;
sockval = MAX_SOCKET_VAL;
}
// Check all sockets if any of them has this value.
@ -459,10 +460,8 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps)
// failure and rollback
delete ns;
ns = NULL;
}
if (!ns)
throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
}
if (pps)
*pps = ns;
@ -563,7 +562,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen,
}
catch (const CUDTException&)
{
LOGF(cnlog.Fatal, "newConnection: IPE: all sockets occupied? Last gen=%d", m_SocketIDGenerator);
LOGC(cnlog.Fatal, log << "newConnection: IPE: all sockets occupied? Last gen=" << m_SocketIDGenerator);
// generateSocketID throws exception, which can be naturally handled
// when the call is derived from the API call, but here it's called
// internally in response to receiving a handshake. It must be handled
@ -600,7 +599,8 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen,
// this call causes sending the SRT Handshake through this socket.
// Without this mapping the socket cannot be found and therefore
// the SRT Handshake message would fail.
HLOGF(cnlog.Debug, "newConnection: incoming %s, mapping socket %d", peer.str().c_str(), ns->m_SocketID);
HLOGC(cnlog.Debug, log <<
"newConnection: incoming " << peer.str() << ", mapping socket " << ns->m_SocketID);
{
ScopedLock cg(m_GlobControlLock);
m_Sockets[ns->m_SocketID] = ns;
@ -619,7 +619,14 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen,
// bind to the same addr of listening socket
ns->core().open();
updateListenerMux(ns, ls);
if (!updateListenerMux(ns, ls))
{
// This is highly unlikely if not impossible, but there's
// a theoretical runtime chance of failure so it should be
// handled
ns->core().m_RejectReason = SRT_REJ_IPE;
throw false; // let it jump directly into the omni exception handler
}
ns->core().acceptAndRespond(ls->m_SelfAddr, peer, hspkt, (w_hs));
}
@ -647,7 +654,8 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen,
ScopedLock glock(m_GlobControlLock);
try
{
HLOGF(cnlog.Debug, "newConnection: mapping peer %d to that socket (%d)\n", ns->m_PeerID, ns->m_SocketID);
HLOGC(cnlog.Debug, log << "newConnection: mapping peer " << ns->m_PeerID
<< " to that socket (" << ns->m_SocketID << ")");
m_PeerRec[ns->getPeerSpec()].insert(ns->m_SocketID);
}
catch (...)
@ -666,7 +674,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen,
// XXX this might require another check of group type.
// For redundancy group, at least, update the status in the group
CUDTGroup* g = ns->m_GroupOf;
ScopedLock glock(g->m_GroupLock);
ScopedLock grlock(g->m_GroupLock);
if (g->m_bClosing)
{
error = 1; // "INTERNAL REJECTION"
@ -911,6 +919,15 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name)
if (s->m_Status != SRTS_INIT)
throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
if (s->core().m_config.iIpV6Only == -1 && name.family() == AF_INET6 && name.isany())
{
// V6ONLY option must be set explicitly if you want to bind to a wildcard address in IPv6
HLOGP(smlog.Error,
"bind: when binding to :: (IPv6 wildcard), SRTO_IPV6ONLY option must be set explicitly to 0 or 1");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
}
s->core().open();
updateMux(s, name);
s->m_Status = SRTS_OPENED;
@ -1078,7 +1095,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int
SRTSOCKET u = CUDT::INVALID_SOCK;
bool accepted = false;
// !!only one conection can be set up each time!!
// !!only one connection can be set up each time!!
while (!accepted)
{
UniqueLock accept_lock(ls->m_AcceptLock);
@ -1179,7 +1196,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int
int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen)
{
// Here both srcname and tarname must be specified
if (!srcname || !tarname || size_t(namelen) < sizeof(sockaddr_in))
if (!srcname || !tarname || namelen < int(sizeof(sockaddr_in)))
{
LOGC(aclog.Error,
log << "connect(with source): invalid call: srcname=" << srcname << " tarname=" << tarname
@ -1224,6 +1241,12 @@ int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockadd
int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn)
{
if (!name || namelen < int(sizeof(sockaddr_in)))
{
LOGC(aclog.Error, log << "connect(): invalid call: name=" << name << " namelen=" << namelen);
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
sockaddr_any target_addr(name, namelen);
if (target_addr.len == 0)
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
@ -1458,7 +1481,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i
ns->m_GroupMemberData = f;
ns->m_GroupOf = &g;
f->weight = targets[tii].weight;
LOGC(aclog.Note, log << "srt_connect_group: socket @" << sid << " added to group $" << g.m_GroupID);
HLOGC(aclog.Debug, log << "srt_connect_group: socket @" << sid << " added to group $" << g.m_GroupID);
}
else
{
@ -1474,21 +1497,13 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i
// XXX This should be reenabled later, this should
// be probably still in use to exchange information about
// packets assymetrically lost. But for no other purpose.
// packets asymmetrically lost. But for no other purpose.
/*
ns->core().m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival);
*/
int isn = g.currentSchedSequence();
// Don't synchronize ISN in case of synch on msgno. Every link
// may send their own payloads independently.
if (g.synconmsgno())
{
HLOGC(aclog.Debug, log << "groupConnect: NOT synchronizing sequence numbers: will sync on msgno");
isn = -1;
}
// Set it the groupconnect option, as all in-group sockets should have.
ns->core().m_config.iGroupConnect = 1;
@ -1867,6 +1882,8 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i
*/
try
{
// record peer address
s->m_PeerAddr = target_addr;
s->core().startConnect(target_addr, forced_isn);
}
catch (const CUDTException&) // Interceptor, just to change the state.
@ -1946,9 +1963,9 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g)
int srt::CUDTUnited::close(CUDTSocket* s)
{
HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSE. Acquiring control lock");
HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSE. Acquiring control lock");
ScopedLock socket_cg(s->m_ControlLock);
HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSING (removing from listening, closing CUDT)");
HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSING (removing from listening, closing CUDT)");
const bool synch_close_snd = s->core().m_config.bSynSending;
@ -1971,14 +1988,16 @@ int srt::CUDTUnited::close(CUDTSocket* s)
// be unable to bind to this port that the about-to-delete listener
// is currently occupying (due to blocked slot in the RcvQueue).
HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSING (removing listener immediately)");
HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSING (removing listener immediately)");
s->core().notListening();
s->m_Status = SRTS_CLOSING;
// broadcast all "accept" waiting
CSync::lock_notify_all(s->m_AcceptCond, s->m_AcceptLock);
}
else
{
s->m_Status = SRTS_CLOSING;
// Note: this call may be done on a socket that hasn't finished
// sending all packets scheduled for sending, which means, this call
// may block INDEFINITELY. As long as it's acceptable to block the
@ -1989,7 +2008,7 @@ int srt::CUDTUnited::close(CUDTSocket* s)
// synchronize with garbage collection.
HLOGC(smlog.Debug,
log << "@" << u << "U::close done. GLOBAL CLOSE: " << s->core().CONID()
<< ". Acquiring GLOBAL control lock");
<< "Acquiring GLOBAL control lock");
ScopedLock manager_cg(m_GlobControlLock);
// since "s" is located before m_GlobControlLock, locate it again in case
// it became invalid
@ -2101,6 +2120,7 @@ int srt::CUDTUnited::close(CUDTSocket* s)
...
}
*/
CSync::notify_one_relaxed(m_GCStopCond);
return 0;
}
@ -2597,11 +2617,7 @@ void srt::CUDTUnited::checkBrokenSockets()
// NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when
// this function is called (isRcvDataReady also checks if the
// available data is "ready to play").
#if ENABLE_NEW_RCVBUFFER
&& s->core().m_pRcvBuffer->hasAvailablePackets())
#else
&& s->core().m_pRcvBuffer->isRcvDataAvailable())
#endif
{
const int bc = s->core().m_iBrokenCounter.load();
if (bc > 0)
@ -2615,7 +2631,7 @@ void srt::CUDTUnited::checkBrokenSockets()
#if ENABLE_BONDING
if (s->m_GroupOf)
{
LOGC(smlog.Note,
HLOGC(smlog.Debug,
log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " - REMOVING FROM GROUP");
s->removeFromGroup(true);
}
@ -2644,7 +2660,7 @@ void srt::CUDTUnited::checkBrokenSockets()
for (sockets_t::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++j)
{
// HLOGF(smlog.Debug, "checking CLOSED socket: %d\n", j->first);
// HLOGC(smlog.Debug, log << "checking CLOSED socket: " << j->first);
if (!is_zero(j->second->core().m_tsLingerExpiration))
{
// asynchronous close:
@ -2671,7 +2687,7 @@ void srt::CUDTUnited::checkBrokenSockets()
log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed "
<< FormatDuration(closed_ago) << " ago and removed from RcvQ - will remove");
// HLOGF(smlog.Debug, "will unref socket: %d\n", j->first);
// HLOGC(smlog.Debug, log << "will unref socket: " << j->first);
tbr.push_back(j->first);
}
}
@ -2826,12 +2842,41 @@ uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm)
return sa.hport();
}
bool srt::CUDTUnited::inet6SettingsCompat(const sockaddr_any& muxaddr, const CSrtMuxerConfig& cfgMuxer,
const sockaddr_any& reqaddr, const CSrtMuxerConfig& cfgSocket)
{
if (muxaddr.family() != AF_INET6)
return true; // Don't check - the family has been checked already
if (reqaddr.isany())
{
if (cfgSocket.iIpV6Only == -1) // Treat as "adaptive"
return true;
// If set explicitly, then it must be equal to the one of found muxer.
return cfgSocket.iIpV6Only == cfgMuxer.iIpV6Only;
}
// If binding to the certain IPv6 address, then this setting doesn't matter.
return true;
}
bool srt::CUDTUnited::channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket)
{
return cfgMuxer.bReuseAddr && cfgMuxer == cfgSocket;
if (!cfgMuxer.bReuseAddr)
{
HLOGP(smlog.Debug, "channelSettingsMatch: fail: the multiplexer is not reusable");
return false;
}
if (cfgMuxer.isCompatWith(cfgSocket))
return true;
HLOGP(smlog.Debug, "channelSettingsMatch: fail: some options have different values");
return false;
}
void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/)
void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, const UDPSOCKET* udpsock /*[[nullable]]*/)
{
ScopedLock cg(m_GlobControlLock);
@ -2844,9 +2889,23 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U
{
// If not, we need to see if there exist already a multiplexer bound
// to the same endpoint.
const int port = addr.hport();
const int port = reqaddr.hport();
const CSrtConfig& cfgSocket = s->core().m_config;
// This loop is going to check the attempted binding of
// address:port and socket settings against every existing
// multiplexer. Possible results of the check are:
// 1. MATCH: identical address - reuse it and quit.
// 2. CONFLICT: report error: the binding partially overlaps
// so it neither can be reused nor is free to bind.
// 3. PASS: different and not overlapping - continue searching.
// In this function the convention is:
// MATCH: do nothing and proceed with binding reusage, THEN break.
// CONFLICT: throw an exception.
// PASS: use 'continue' to pass to the next element.
bool reuse_attempt = false;
for (map<int, CMultiplexer>::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++i)
{
@ -2862,74 +2921,166 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U
}
// If this is bound to the wildcard address, it can be reused if:
// - addr is also a wildcard
// - reqaddr is also a wildcard
// - channel settings match
// Otherwise it's a conflict.
sockaddr_any sa;
m.m_pChannel->getSockAddr((sa));
sockaddr_any mux_addr;
m.m_pChannel->getSockAddr((mux_addr));
HLOGC(smlog.Debug,
log << "bind: Found existing muxer @" << m.m_iID << " : " << sa.str() << " - check against "
<< addr.str());
log << "bind: Found existing muxer @" << m.m_iID << " : " << mux_addr.str() << " - check against "
<< reqaddr.str());
if (sa.isany())
if (mux_addr.isany())
{
if (!addr.isany())
if (mux_addr.family() == AF_INET6)
{
LOGC(smlog.Error,
log << "bind: Address: " << addr.str()
<< " conflicts with existing wildcard binding: " << sa.str());
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
// With IPv6 we need to research two possibilities:
// iIpV6Only == 1 -> This means that it binds only :: wildcard, but not 0.0.0.0
// iIpV6Only == 0 -> This means that it binds both :: and 0.0.0.0.
// iIpV6Only == -1 -> Hard to say what to do, but treat it as a potential conflict in any doubtful case.
if (m.m_mcfg.iIpV6Only == 1)
{
// PASS IF: candidate is IPv4, no matter the address
// MATCH IF: candidate is IPv6 with only=1
// CONFLICT IF: candidate is IPv6 with only != 1 or IPv6 non-wildcard.
if (reqaddr.family() == AF_INET)
{
HLOGC(smlog.Debug, log << "bind: muxer @" << m.m_iID
<< " is :: v6only - requested IPv4 ANY is NOT IN THE WAY. Searching on.");
continue;
}
// Candidate is AF_INET6
if (cfgSocket.iIpV6Only != 1 || !reqaddr.isany())
{
// CONFLICT:
// 1. attempting to make a wildcard IPv4 + IPv6
// while the multiplexer for wildcard IPv6 exists.
// 2. If binding to a given address, it conflicts with the wildcard
LOGC(smlog.Error,
log << "bind: Address: " << reqaddr.str()
<< " conflicts with existing IPv6 wildcard binding: " << mux_addr.str());
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
}
// Otherwise, MATCH.
}
else if (m.m_mcfg.iIpV6Only == 0)
{
// Muxer's address is a wildcard for :: and 0.0.0.0 at once.
// This way only IPv6 wildcard with v6only=0 is a perfect match and everything
// else is a conflict.
if (reqaddr.family() == AF_INET6 && reqaddr.isany() && cfgSocket.iIpV6Only == 0)
{
// MATCH
}
else
{
// CONFLICT: attempting to make a wildcard IPv4 + IPv6 while
// the multiplexer for wildcard IPv6 exists.
LOGC(smlog.Error,
log << "bind: Address: " << reqaddr.str() << " v6only=" << cfgSocket.iIpV6Only
<< " conflicts with existing IPv6 + IPv4 wildcard binding: " << mux_addr.str());
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
}
}
else // Case -1, by unknown reason. Accept only with -1 setting, others are conflict.
{
if (reqaddr.family() == AF_INET6 && reqaddr.isany() && cfgSocket.iIpV6Only == -1)
{
// MATCH
}
else
{
LOGC(smlog.Error,
log << "bind: Address: " << reqaddr.str() << " v6only=" << cfgSocket.iIpV6Only
<< " conflicts with existing IPv6 v6only=unknown wildcard binding: " << mux_addr.str());
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
}
}
}
// Still, for ANY you need either the same family, or open
// for families.
if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != cfgSocket.iIpV6Only)
else // muxer is IPv4 wildcard
{
LOGC(smlog.Error,
log << "bind: Address: " << addr.str()
<< " conflicts with existing IPv6 wildcard binding: " << sa.str());
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
// Then only IPv4 wildcard is a match and:
// - IPv6 with only=true is PASS (not a conflict)
// - IPv6 with only=false is CONFLICT
// - IPv6 with only=undefined is CONFLICT
// REASON: we need to make a potential conflict a conflict as there will be
// no bind() call to check if this wouldn't be a conflict in result. If you want
// to have a binding to IPv6 that should avoid conflict with IPv4 wildcard binding,
// then SRTO_IPV6ONLY option must be explicitly set before binding.
// Also:
if (reqaddr.family() == AF_INET)
{
if (reqaddr.isany())
{
// MATCH
}
else
{
LOGC(smlog.Error,
log << "bind: Address: " << reqaddr.str()
<< " conflicts with existing IPv4 wildcard binding: " << mux_addr.str());
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
}
}
else // AF_INET6
{
if (cfgSocket.iIpV6Only == 1 || !reqaddr.isany())
{
// PASS
HLOGC(smlog.Debug, log << "bind: muxer @" << m.m_iID
<< " is IPv4 wildcard - requested " << reqaddr.str() << " v6only=" << cfgSocket.iIpV6Only
<< " is NOT IN THE WAY. Searching on.");
continue;
}
else
{
LOGC(smlog.Error,
log << "bind: Address: " << reqaddr.str() << " v6only=" << cfgSocket.iIpV6Only
<< " conflicts with existing IPv4 wildcard binding: " << mux_addr.str());
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
}
}
}
if ((m.m_mcfg.iIpV6Only == 0 || cfgSocket.iIpV6Only == 0) && m.m_iIPversion != addr.family())
{
LOGC(smlog.Error,
log << "bind: Address: " << addr.str() << " conflicts with IPv6 wildcard binding: " << sa.str()
<< " : family " << (m.m_iIPversion == AF_INET ? "IPv4" : "IPv6") << " vs. "
<< (addr.family() == AF_INET ? "IPv4" : "IPv6"));
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
}
reuse_attempt = true;
HLOGC(smlog.Debug, log << "bind: wildcard address - multiplexer reusable");
}
else if (addr.isany() && addr.family() == sa.family())
// Muxer address is NOT a wildcard, so conflicts only with WILDCARD of the same type
else if (reqaddr.isany() && reqaddr.family() == mux_addr.family())
{
LOGC(smlog.Error,
log << "bind: Wildcard address: " << addr.str()
<< " conflicts with existting IP binding: " << sa.str());
log << "bind: Wildcard address: " << reqaddr.str()
<< " conflicts with existting IP binding: " << mux_addr.str());
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
}
// If this is bound to a certain address, AND:
else if (sa.equal_address(addr))
else if (mux_addr.equal_address(reqaddr))
{
// - the address is the same as addr
// - the address is the same as reqaddr
reuse_attempt = true;
HLOGC(smlog.Debug, log << "bind: same IP address - multiplexer reusable");
}
else
{
HLOGC(smlog.Debug, log << "bind: IP addresses differ - ALLOWED to create a new multiplexer");
continue;
}
// Otherwise:
// - the address is different than addr
// - the address is different than reqaddr
// - the address can't be reused, but this can go on with new one.
// If this is a reusage attempt:
if (reuse_attempt)
{
// - if the channel settings match, it can be reused
if (channelSettingsMatch(m.m_mcfg, cfgSocket))
if (channelSettingsMatch(m.m_mcfg, cfgSocket) && inet6SettingsCompat(mux_addr, m.m_mcfg, reqaddr, cfgSocket))
{
HLOGC(smlog.Debug, log << "bind: reusing multiplexer for port " << port);
// reuse the existing multiplexer
@ -2941,7 +3092,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U
{
// - if not, it's a conflict
LOGC(smlog.Error,
log << "bind: Address: " << addr.str() << " conflicts with binding: " << sa.str()
log << "bind: Address: " << reqaddr.str() << " conflicts with binding: " << mux_addr.str()
<< " due to channel settings");
throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0);
}
@ -2950,13 +3101,15 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U
// candidates, proceed with creating a new multiplexer.
// Note that a binding to a different IP address is not treated
// as a candidate for either reuseage or conflict.
// as a candidate for either reusage or conflict.
LOGC(smlog.Fatal, log << "SHOULD NOT GET HERE!!!");
SRT_ASSERT(false);
}
}
// a new multiplexer is needed
CMultiplexer m;
configureMuxer((m), s, addr.family());
configureMuxer((m), s, reqaddr.family());
try
{
@ -2965,25 +3118,39 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U
if (udpsock)
{
// In this case, addr contains the address
// In this case, reqaddr contains the address
// that has been extracted already from the
// given socket
m.m_pChannel->attach(*udpsock, addr);
m.m_pChannel->attach(*udpsock, reqaddr);
}
else if (addr.empty())
else if (reqaddr.empty())
{
// The case of previously used case of a NULL address.
// This here is used to pass family only, in this case
// just automatically bind to the "0" address to autoselect
// everything.
m.m_pChannel->open(addr.family());
m.m_pChannel->open(reqaddr.family());
}
else
{
// If at least the IP address is specified, then bind to that
// address, but still possibly autoselect the outgoing port, if the
// port was specified as 0.
m.m_pChannel->open(addr);
m.m_pChannel->open(reqaddr);
}
// AFTER OPENING, check the matter of IPV6_V6ONLY option,
// as it decides about the fact that the occupied binding address
// in case of wildcard is both :: and 0.0.0.0, or only ::.
if (reqaddr.family() == AF_INET6 && m.m_mcfg.iIpV6Only == -1)
{
// XXX We don't know how probable it is to get the error here
// and resulting -1 value. As a fallback for that case, the value -1
// is honored here, just all side-bindings for other sockes will be
// rejected as a potential conflict, even if binding would be accepted
// in these circumstances. Only a perfect match in case of potential
// overlapping will be accepted on the same port.
m.m_mcfg.iIpV6Only = m.m_pChannel->sockopt(IPPROTO_IPV6, IPV6_V6ONLY, -1);
}
m.m_pTimer = new CTimer;
@ -3032,8 +3199,14 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls)
CMultiplexer* mux = map_getp(m_mMultiplexer, ls->m_iMuxID);
// NOTE:
// THIS BELOW CODE is only for a highly unlikely, and probably buggy,
// situation when the Multiplexer wasn't found by ID recorded in the listener.
// THIS BELOW CODE is only for a highly unlikely situation when the listener
// socket has been closed in the meantime when the accepted socket is being
// processed. This procedure is different than updateMux because this time we
// only want to have a multiplexer socket to be assigned to the accepted socket.
// It is also unlikely that the listener socket is garbage-collected so fast, so
// this procedure will most likely find the multiplexer of the zombie listener socket,
// which no longer accepts new connections (the listener is withdrawn immediately from
// the port) that wasn't yet completely deleted.
CMultiplexer* fallback = NULL;
if (!mux)
{
@ -3060,8 +3233,9 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls)
mux = &m; // best match
break;
}
else
else if (m.m_iIPversion == AF_INET6)
{
// Allowed fallback case when we only need an accepted socket.
fallback = &m;
}
}
@ -4369,7 +4543,7 @@ int epoll_wait2(int eid,
int* lwnum)
{
// This API is an alternative format for epoll_wait, created for
// compatability with other languages. Users need to pass in an array
// compatibility with other languages. Users need to pass in an array
// for holding the returned sockets, with the maximum array length
// stored in *rnum, etc., which will be updated with returned number
// of sockets.

@ -193,6 +193,15 @@ public:
/// to finish sending the data that were scheduled for sending so far.
void setClosed();
// This is necessary to be called from the group before the group clears
// the connection with the socket. As for managed groups (and there are
// currently no other group types), a socket disconnected from the group
// is no longer usable.
void setClosing()
{
core().m_bClosing = true;
}
/// This does the same as setClosed, plus sets the m_bBroken to true.
/// Such a socket can still be read from so that remaining data from
/// the receiver buffer can be read, but no longer sends anything.
@ -447,6 +456,8 @@ private:
/// @param cfgSocket socket configuration.
/// @return tru if configurations match, false otherwise.
static bool channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket);
static bool inet6SettingsCompat(const sockaddr_any& muxaddr, const CSrtMuxerConfig& cfgMuxer,
const sockaddr_any& reqaddr, const CSrtMuxerConfig& cfgSocket);
private:
std::map<int, CMultiplexer> m_mMultiplexer; // UDP multiplexer

File diff suppressed because it is too large Load Diff

@ -1,611 +0,0 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 05/05/2009
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_BUFFER_H
#define INC_SRT_BUFFER_H
#include "udt.h"
#include "list.h"
#include "queue.h"
#include "tsbpd_time.h"
#include "utilities.h"
// The notation used for "circular numbers" in comments:
// The "cicrular numbers" are numbers that when increased up to the
// maximum become zero, and similarly, when the zero value is decreased,
// it turns into the maximum value minus one. This wrapping works the
// same for adding and subtracting. Circular numbers cannot be multiplied.
// Operations done on these numbers are marked with additional % character:
// a %> b : a is later than b
// a ++% (++%a) : shift a by 1 forward
// a +% b : shift a by b
// a == b : equality is same as for just numbers
namespace srt {
/// The AvgBufSize class is used to calculate moving average of the buffer (RCV or SND)
class AvgBufSize
{
typedef sync::steady_clock::time_point time_point;
public:
AvgBufSize()
: m_dBytesCountMAvg(0.0)
, m_dCountMAvg(0.0)
, m_dTimespanMAvg(0.0)
{
}
public:
bool isTimeToUpdate(const time_point& now) const;
void update(const time_point& now, int pkts, int bytes, int timespan_ms);
public:
inline double pkts() const { return m_dCountMAvg; }
inline double timespan_ms() const { return m_dTimespanMAvg; }
inline double bytes() const { return m_dBytesCountMAvg; }
private:
time_point m_tsLastSamplingTime;
double m_dBytesCountMAvg;
double m_dCountMAvg;
double m_dTimespanMAvg;
};
/// The class to estimate source bitrate based on samples submitted to the buffer.
/// Is currently only used by the CSndBuffer.
class CRateEstimator
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
public:
CRateEstimator();
public:
uint64_t getInRatePeriod() const { return m_InRatePeriod; }
/// Retrieve input bitrate in bytes per second
int getInputRate() const { return m_iInRateBps; }
void setInputRateSmpPeriod(int period);
/// Update input rate calculation.
/// @param [in] time current time in microseconds
/// @param [in] pkts number of packets newly added to the buffer
/// @param [in] bytes number of payload bytes in those newly added packets
///
/// @return Current size of the data in the sending list.
void updateInputRate(const time_point& time, int pkts = 0, int bytes = 0);
void resetInputRateSmpPeriod(bool disable = false) { setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US); }
private: // Constants
static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms
static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms
static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload
static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE;
private:
int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime
int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime
time_point m_tsInRateStartTime;
uint64_t m_InRatePeriod; // usec
int m_iInRateBps; // Input Rate in Bytes/sec
};
class CSndBuffer
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
public:
// XXX There's currently no way to access the socket ID set for
// whatever the buffer is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
/// @brief CSndBuffer constructor.
/// @param size initial number of blocks (each block to store one packet payload).
/// @param maxpld maximum packet payload.
CSndBuffer(int size = 32, int maxpld = 1500);
~CSndBuffer();
public:
/// Insert a user buffer into the sending list.
/// For @a w_mctrl the following fields are used:
/// INPUT:
/// - msgttl: timeout for retransmitting the message, if lost
/// - inorder: request to deliver the message in order of sending
/// - srctime: local time as a base for packet's timestamp (0 if unused)
/// - pktseq: sequence number to be stamped on the packet (-1 if unused)
/// - msgno: message number to be stamped on the packet (-1 if unused)
/// OUTPUT:
/// - srctime: local time stamped on the packet (same as input, if input wasn't 0)
/// - pktseq: sequence number to be stamped on the next packet
/// - msgno: message number stamped on the packet
/// @param [in] data pointer to the user data block.
/// @param [in] len size of the block.
/// @param [inout] w_mctrl Message control data
SRT_ATTR_EXCLUDES(m_BufLock)
void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl);
/// Read a block of data from file and insert it into the sending list.
/// @param [in] ifs input file stream.
/// @param [in] len size of the block.
/// @return actual size of data added from the file.
SRT_ATTR_EXCLUDES(m_BufLock)
int addBufferFromFile(std::fstream& ifs, int len);
/// Find data position to pack a DATA packet from the furthest reading point.
/// @param [out] packet the packet to read.
/// @param [out] origintime origin time stamp of the message
/// @param [in] kflags Odd|Even crypto key flag
/// @param [out] seqnoinc the number of packets skipped due to TTL, so that seqno should be incremented.
/// @return Actual length of data read.
SRT_ATTR_EXCLUDES(m_BufLock)
int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc);
/// Peek an information on the next original data packet to send.
/// @return origin time stamp of the next packet; epoch start time otherwise.
SRT_ATTR_EXCLUDES(m_BufLock)
time_point peekNextOriginal() const;
/// Find data position to pack a DATA packet for a retransmission.
/// @param [in] offset offset from the last ACK point (backward sequence number difference)
/// @param [out] packet the packet to read.
/// @param [out] origintime origin time stamp of the message
/// @param [out] msglen length of the message
/// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded).
SRT_ATTR_EXCLUDES(m_BufLock)
int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen);
/// Get the time of the last retransmission (if any) of the DATA packet.
/// @param [in] offset offset from the last ACK point (backward sequence number difference)
///
/// @return Last time of the last retransmission event for the corresponding DATA packet.
SRT_ATTR_EXCLUDES(m_BufLock)
time_point getPacketRexmitTime(const int offset);
/// Update the ACK point and may release/unmap/return the user data according to the flag.
/// @param [in] offset number of packets acknowledged.
int32_t getMsgNoAt(const int offset);
void ackData(int offset);
/// Read size of data still in the sending list.
/// @return Current size of the data in the sending list.
int getCurrBufSize() const;
SRT_ATTR_EXCLUDES(m_BufLock)
int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time);
void updAvgBufSize(const time_point& time);
int getAvgBufSize(int& bytes, int& timespan);
int getCurrBufSize(int& bytes, int& timespan);
/// @brief Get the buffering delay of the oldest message in the buffer.
/// @return the delay value.
SRT_ATTR_EXCLUDES(m_BufLock)
duration getBufferingDelay(const time_point& tnow) const;
uint64_t getInRatePeriod() const { return m_rateEstimator.getInRatePeriod(); }
/// Retrieve input bitrate in bytes per second
int getInputRate() const { return m_rateEstimator.getInputRate(); }
void resetInputRateSmpPeriod(bool disable = false) { m_rateEstimator.resetInputRateSmpPeriod(disable); }
const CRateEstimator& getRateEstimator() const { return m_rateEstimator; }
void setRateEstimator(const CRateEstimator& other) { m_rateEstimator = other; }
private:
void increase();
private:
mutable sync::Mutex m_BufLock; // used to synchronize buffer operation
struct Block
{
char* m_pcData; // pointer to the data block
int m_iLength; // payload length of the block.
int32_t m_iMsgNoBitset; // message number
int32_t m_iSeqNo; // sequence number for scheduling
time_point m_tsOriginTime; // block origin time (either provided from above or equals the time a message was submitted for sending.
time_point m_tsRexmitTime; // packet retransmission time
int m_iTTL; // time to live (milliseconds)
Block* m_pNext; // next block
int32_t getMsgSeq()
{
// NOTE: this extracts message ID with regard to REXMIT flag.
// This is valid only for message ID that IS GENERATED in this instance,
// not provided by the peer. This can be otherwise sent to the peer - it doesn't matter
// for the peer that it uses LESS bits to represent the message.
return m_iMsgNoBitset & MSGNO_SEQ::mask;
}
} * m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock;
// m_pBlock: The head pointer
// m_pFirstBlock: The first block
// m_pCurrBlock: The current block
// m_pLastBlock: The last block (if first == last, buffer is empty)
struct Buffer
{
char* m_pcData; // buffer
int m_iSize; // size
Buffer* m_pNext; // next buffer
} * m_pBuffer; // physical buffer
int32_t m_iNextMsgNo; // next message number
int m_iSize; // buffer size (number of packets)
const int m_iBlockLen; // maximum length of a block holding packet payload (excluding packet header).
int m_iCount; // number of used blocks
int m_iBytesCount; // number of payload bytes in queue
time_point m_tsLastOriginTime;
AvgBufSize m_mavg;
CRateEstimator m_rateEstimator;
private:
CSndBuffer(const CSndBuffer&);
CSndBuffer& operator=(const CSndBuffer&);
};
////////////////////////////////////////////////////////////////////////////////
#if (!ENABLE_NEW_RCVBUFFER)
class CRcvBuffer
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
public:
// XXX There's currently no way to access the socket ID set for
// whatever the queue is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
static const int DEFAULT_SIZE = 65536;
/// Construct the buffer.
/// @param [in] queue CUnitQueue that actually holds the units (packets)
/// @param [in] bufsize_pkts in units (packets)
CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE);
~CRcvBuffer();
public:
/// Write data into the buffer.
/// @param [in] unit pointer to a data unit containing new packet
/// @param [in] offset offset from last ACK point.
/// @return 0 is success, -1 if data is repeated.
int addData(CUnit* unit, int offset);
/// Read data into a user buffer.
/// @param [in] data pointer to user buffer.
/// @param [in] len length of user buffer.
/// @return size of data read.
int readBuffer(char* data, int len);
/// Read data directly into file.
/// @param [in] file C++ file stream.
/// @param [in] len expected length of data to write into the file.
/// @return size of data read.
int readBufferToFile(std::fstream& ofs, int len);
/// Update the ACK point of the buffer.
/// @param [in] len number of units to be acknowledged.
/// @return 1 if a user buffer is fulfilled, otherwise 0.
int ackData(int len);
/// Query how many buffer space left for data receiving.
/// Actually only acknowledged packets, that are still in the buffer,
/// are considered to take buffer space.
///
/// @return size of available buffer space (including user buffer) for data receiving.
/// Not counting unacknowledged packets.
int getAvailBufSize() const;
/// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now).
/// @return size of valid (continous) data for reading.
int getRcvDataSize() const;
/// Query how many data was received and acknowledged.
/// @param [out] bytes bytes
/// @param [out] spantime spantime
/// @return size in pkts of acked data.
int getRcvDataSize(int& bytes, int& spantime);
/// Query a 1 sec moving average of how many data was received and acknowledged.
/// @param [out] bytes bytes
/// @param [out] spantime spantime
/// @return size in pkts of acked data.
int getRcvAvgDataSize(int& bytes, int& spantime);
/// Query how many data of the receive buffer is acknowledged.
/// @param [in] now current time in us.
/// @return none.
void updRcvAvgDataSize(const time_point& now);
/// Query the received average payload size.
/// @return size (bytes) of payload size
unsigned getRcvAvgPayloadSize() const;
struct ReadingState
{
time_point tsStart;
time_point tsLastAck;
time_point tsEnd;
int iNumAcknowledged;
int iNumUnacknowledged;
};
ReadingState debugGetReadingState() const;
/// Form a string of the current buffer fullness state.
/// number of packets acknowledged, TSBPD readiness, etc.
std::string strFullnessState(const time_point& tsNow) const;
/// Mark the message to be dropped from the message list.
/// @param [in] msgno message number.
/// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the
/// msgno value)
void dropMsg(int32_t msgno, bool using_rexmit_flag);
/// read a message.
/// @param [out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @return actuall size of data read.
int readMsg(char* data, int len);
#if ENABLE_HEAVY_LOGGING
void readMsgHeavyLogging(int p);
#endif
/// read a message.
/// @param [out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
/// @return actuall size of data read.
int readMsg(char* data, int len, SRT_MSGCTRL& w_mctrl, int upto);
/// Query if data is ready to read (tsbpdtime <= now if TsbPD is active).
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
/// of next packet in recv buffer, ready or not.
/// @param [out] curpktseq Sequence number of the packet if there is one ready to play
/// @return true if ready to play, false otherwise (tsbpdtime may be !0 in
/// both cases).
bool isRcvDataReady(time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance);
#ifdef SRT_DEBUG_TSBPD_OUTJITTER
void debugTraceJitter(time_point t);
#else
void debugTraceJitter(time_point) {}
#endif /* SRT_DEBUG_TSBPD_OUTJITTER */
bool isRcvDataReady();
bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; }
CPacket* getRcvReadyPacket(int32_t seqdistance);
/// Set TimeStamp-Based Packet Delivery Rx Mode
/// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay
/// @param [in] delay aggreed TsbPD delay
void setRcvTsbPdMode(const time_point& timebase, const duration& delay);
/// Add packet timestamp for drift caclculation and compensation
/// @param [in] timestamp packet time stamp
/// @param [in] tsPktArrival arrival time of the packet used to extract the drift sample.
/// @param [in] rtt RTT sample
bool addRcvTsbPdDriftSample(uint32_t timestamp, const time_point& tsPktArrival, int rtt);
#ifdef SRT_DEBUG_TSBPD_DRIFT
void printDriftHistogram(int64_t iDrift);
void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg);
#endif
/// Get information on the 1st message in queue.
// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] w_tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0
/// if none
/// @param [out] w_passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app)
/// @param [out] w_skipseqno SRT_SEQNO_NONE or seq number of 1st unacknowledged pkt ready to play preceeded by
/// missing packets.
/// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base if exist packet ready-to-play
/// and larger than base
/// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true
/// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE:
/// IF skipseqno != SRT_SEQNO_NONE, packet ready to play preceeded by missing packets.;
/// IF skipseqno == SRT_SEQNO_NONE, no missing packet but 1st not ready to play.
bool getRcvFirstMsg(time_point& w_tsbpdtime,
bool& w_passack,
int32_t& w_skipseqno,
int32_t& w_curpktseq,
int32_t base_seq = SRT_SEQNO_NONE);
/// Update the ACK point of the buffer.
/// @param [in] len size of data to be skip & acknowledged.
void skipData(int len);
#if ENABLE_HEAVY_LOGGING
void reportBufferStats() const; // Heavy logging Debug only
#endif
bool empty() const
{
// This will not always return the intended value,
// that is, it may return false when the buffer really is
// empty - but it will return true then in one of next calls.
// This function will be always called again at some point
// if it returned false, and on true the connection
// is going to be broken - so this behavior is acceptable.
return m_iStartPos == m_iLastAckPos;
}
bool full() const { return m_iStartPos == (m_iLastAckPos + 1) % m_iSize; }
int capacity() const { return m_iSize; }
private:
/// This gives up unit at index p. The unit is given back to the
/// free unit storage for further assignment for the new incoming
/// data.
size_t freeUnitAt(size_t p)
{
CUnit* u = m_pUnit[p];
m_pUnit[p] = NULL;
size_t rmbytes = u->m_Packet.getLength();
m_pUnitQueue->makeUnitFree(u);
return rmbytes;
}
/// Adjust receive queue to 1st ready to play message (tsbpdtime < now).
/// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if
/// none
/// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base
/// @retval true 1st packet ready to play without discontinuity (no hole)
/// @retval false tsbpdtime = 0: no packet ready to play
bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto, int base_seq = SRT_SEQNO_NONE);
public:
/// @brief Get clock drift in microseconds.
int64_t getDrift() const { return m_tsbpd.drift(); }
public:
int32_t getTopMsgno() const;
void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift);
void applyGroupTime(const time_point& timebase, bool wrapcheck, uint32_t delay, const duration& udrift);
void applyGroupDrift(const time_point& timebase, bool wrapcheck, const duration& udrift);
time_point getPktTsbPdTime(uint32_t timestamp);
int debugGetSize() const;
time_point debugGetDeliveryTime(int offset);
size_t dropData(int len);
private:
int extractData(char* data, int len, int p, int q, bool passack);
bool accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playtime, int upto);
/// Describes the state of the first N packets
std::string debugTimeState(size_t first_n_pkts) const;
/// thread safe bytes counter of the Recv & Ack buffer
/// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true)
/// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer.
/// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer
void countBytes(int pkts, int bytes, bool acked = false);
private:
bool scanMsg(int& w_start, int& w_end, bool& w_passack);
int shift(int basepos, int shift) const { return (basepos + shift) % m_iSize; }
/// Simplified versions with ++ and --; avoid using division instruction
int shiftFwd(int basepos) const
{
if (++basepos == m_iSize)
return 0;
return basepos;
}
int shiftBack(int basepos) const
{
if (basepos == 0)
return m_iSize - 1;
return --basepos;
}
private:
CUnit** m_pUnit; // Array of pointed units collected in the buffer
const int m_iSize; // Size of the internal array of CUnit* items
CUnitQueue* m_pUnitQueue; // the shared unit queue
int m_iStartPos; // HEAD: first packet available for reading
int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable
// EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1
int m_iMaxPos; // delta between acked-TAIL and reception-TAIL
int m_iNotch; // the starting read point of the first unit
// (this is required for stream reading mode; it's
// the position in the first unit in the list
// up to which data are already retrieved;
// in message reading mode it's unused and always 0)
sync::Mutex m_BytesCountLock; // used to protect counters operations
int m_iBytesCount; // Number of payload bytes in the buffer
int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer
int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer
unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation
CTsbpdTime m_tsbpd;
AvgBufSize m_mavg;
private:
CRcvBuffer();
CRcvBuffer(const CRcvBuffer&);
CRcvBuffer& operator=(const CRcvBuffer&);
};
#endif // !ENABLE_NEW_RCVBUFFER
} // namespace srt
#endif

@ -1,4 +1,48 @@
#if ENABLE_NEW_RCVBUFFER
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <cmath>
#include <limits>
#include "buffer_rcv.h"
@ -31,15 +75,15 @@ namespace {
#define IF_RCVBUF_DEBUG(instr) (void)0
// Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosInc) % iSize].
// Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize].
// The right edge is included because we expect iFirstNonreadPos to be
// right after the last valid packet position if all packets are available.
bool isInRange(int iStartPos, int iMaxPosInc, size_t iSize, int iFirstNonreadPos)
bool isInRange(int iStartPos, int iMaxPosOff, size_t iSize, int iFirstNonreadPos)
{
if (iFirstNonreadPos == iStartPos)
return true;
const int iLastPos = (iStartPos + iMaxPosInc) % iSize;
const int iLastPos = (iStartPos + iMaxPosOff) % iSize;
const bool isOverrun = iLastPos < iStartPos;
if (isOverrun)
@ -54,7 +98,7 @@ namespace {
* RcvBufferNew (circular buffer):
*
* |<------------------- m_iSize ----------------------------->|
* | |<----------- m_iMaxPosInc ------------>| |
* | |<----------- m_iMaxPosOff ------------>| |
* | | | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+
* | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[]
@ -68,17 +112,17 @@ namespace {
* thread safety:
* m_iStartPos: CUDT::m_RecvLock
* m_iLastAckPos: CUDT::m_AckLock
* m_iMaxPosInc: none? (modified on add and ack
* m_iMaxPosOff: none? (modified on add and ack
*/
CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI)
CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI)
: m_entries(size)
, m_szSize(size) // TODO: maybe just use m_entries.size()
, m_pUnitQueue(unitqueue)
, m_iStartSeqNo(initSeqNo)
, m_iStartPos(0)
, m_iFirstNonreadPos(0)
, m_iMaxPosInc(0)
, m_iMaxPosOff(0)
, m_iNotch(0)
, m_numOutOfOrderPackets(0)
, m_iFirstReadableOutOfOrder(-1)
@ -91,9 +135,9 @@ CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue,
SRT_ASSERT(size < size_t(std::numeric_limits<int>::max())); // All position pointers are integers
}
CRcvBufferNew::~CRcvBufferNew()
CRcvBuffer::~CRcvBuffer()
{
// Can be optimized by only iterating m_iMaxPosInc from m_iStartPos.
// Can be optimized by only iterating m_iMaxPosOff from m_iStartPos.
for (FixedArray<Entry>::iterator it = m_entries.begin(); it != m_entries.end(); ++it)
{
if (!it->pUnit)
@ -104,14 +148,14 @@ CRcvBufferNew::~CRcvBufferNew()
}
}
int CRcvBufferNew::insert(CUnit* unit)
int CRcvBuffer::insert(CUnit* unit)
{
SRT_ASSERT(unit != NULL);
const int32_t seqno = unit->m_Packet.getSeqNo();
const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno);
IF_RCVBUF_DEBUG(ScopedLog scoped_log);
IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::insert: seqno " << seqno);
IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::insert: seqno " << seqno);
IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag));
IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset);
@ -132,8 +176,8 @@ int CRcvBufferNew::insert(CUnit* unit)
SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2);
const int pos = (m_iStartPos + offset) % m_szSize;
if (offset >= m_iMaxPosInc)
m_iMaxPosInc = offset + 1;
if (offset >= m_iMaxPosOff)
m_iMaxPosOff = offset + 1;
// Packet already exists
SRT_ASSERT(pos >= 0 && pos < int(m_szSize));
@ -144,7 +188,7 @@ int CRcvBufferNew::insert(CUnit* unit)
}
SRT_ASSERT(m_entries[pos].pUnit == NULL);
m_pUnitQueue->makeUnitGood(unit);
m_pUnitQueue->makeUnitTaken(unit);
m_entries[pos].pUnit = unit;
m_entries[pos].status = EntryState_Avail;
countBytes(1, (int)unit->m_Packet.getLength());
@ -162,10 +206,10 @@ int CRcvBufferNew::insert(CUnit* unit)
return 0;
}
int CRcvBufferNew::dropUpTo(int32_t seqno)
int CRcvBuffer::dropUpTo(int32_t seqno)
{
IF_RCVBUF_DEBUG(ScopedLog scoped_log);
IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo);
IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo);
int len = CSeqNo::seqoff(m_iStartSeqNo, seqno);
if (len <= 0)
@ -174,9 +218,9 @@ int CRcvBufferNew::dropUpTo(int32_t seqno)
return 0;
}
m_iMaxPosInc -= len;
if (m_iMaxPosInc < 0)
m_iMaxPosInc = 0;
m_iMaxPosOff -= len;
if (m_iMaxPosOff < 0)
m_iMaxPosOff = 0;
const int iDropCnt = len;
while (len > 0)
@ -192,91 +236,87 @@ int CRcvBufferNew::dropUpTo(int32_t seqno)
m_iStartSeqNo = seqno;
// Move forward if there are "read/drop" entries.
releaseNextFillerEntries();
// Set nonread position to the starting position before updating,
// because start position was increased, and preceeding packets are invalid.
m_iFirstNonreadPos = m_iStartPos;
updateNonreadPos();
// If the nonread position is now behind the starting position, set it to the starting position and update.
// Preceding packets were likely missing, and the non read position can probably be moved further now.
if (CSeqNo::seqcmp(m_iFirstNonreadPos, m_iStartPos) < 0)
{
m_iFirstNonreadPos = m_iStartPos;
updateNonreadPos();
}
if (!m_tsbpd.isEnabled() && m_bMessageAPI)
updateFirstReadableOutOfOrder();
return iDropCnt;
}
int CRcvBufferNew::dropAll()
int CRcvBuffer::dropAll()
{
if (empty())
return 0;
const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosInc);
const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff);
return dropUpTo(end_seqno);
}
int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno)
int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, DropActionIfExists actionOnExisting)
{
IF_RCVBUF_DEBUG(ScopedLog scoped_log);
IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo);
// TODO: count bytes as removed?
const int end_pos = incPos(m_iStartPos, m_iMaxPosInc);
if (msgno != 0)
{
IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno);
int minDroppedOffset = -1;
int iDropCnt = 0;
for (int i = m_iStartPos; i != end_pos; i = incPos(i))
{
// TODO: Maybe check status?
if (!m_entries[i].pUnit)
continue;
// TODO: Break the loop if a massege has been found. No need to search further.
const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag);
if (msgseq == msgno)
{
++iDropCnt;
dropUnitInPos(i);
m_entries[i].status = EntryState_Drop;
if (minDroppedOffset == -1)
minDroppedOffset = offPos(m_iStartPos, i);
}
}
IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt);
// Check if units before m_iFirstNonreadPos are dropped.
bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize());
releaseNextFillerEntries();
if (needUpdateNonreadPos)
{
m_iFirstNonreadPos = m_iStartPos;
updateNonreadPos();
}
if (!m_tsbpd.isEnabled() && m_bMessageAPI)
{
if (!checkFirstReadableOutOfOrder())
m_iFirstReadableOutOfOrder = -1;
updateFirstReadableOutOfOrder();
}
return iDropCnt;
}
IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropMessage(): %(" << seqnolo << " - " << seqnohi << ")"
<< " #" << msgno << " actionOnExisting=" << actionOnExisting << " m_iStartSeqNo=%"
<< m_iStartSeqNo);
// Drop by packet seqno range.
// Drop by packet seqno range to also wipe those packets that do not exist in the buffer.
const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo);
const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi);
if (offset_b < 0)
{
LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; "
<< seqnohi << "]. Buffer start " << m_iStartSeqNo << ".");
LOGC(rbuflog.Debug, log << "CRcvBuffer.dropMessage(): nothing to drop. Requested [" << seqnolo << "; "
<< seqnohi << "]. Buffer start " << m_iStartSeqNo << ".");
return 0;
}
const int start_off = max(0, offset_a);
const int last_pos = incPos(m_iStartPos, offset_b);
const bool bKeepExisting = (actionOnExisting == KEEP_EXISTING);
int minDroppedOffset = -1;
int iDropCnt = 0;
for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i))
const int start_off = max(0, offset_a);
const int start_pos = incPos(m_iStartPos, start_off);
const int end_off = min((int) m_szSize - 1, offset_b + 1);
const int end_pos = incPos(m_iStartPos, end_off);
bool bDropByMsgNo = msgno > SRT_MSGNO_CONTROL; // Excluding both SRT_MSGNO_NONE (-1) and SRT_MSGNO_CONTROL (0).
for (int i = start_pos; i != end_pos; i = incPos(i))
{
// Don't drop messages, if all its packets are already in the buffer.
// TODO: Don't drop a several-packet message if all packets are in the buffer.
if (m_entries[i].pUnit && m_entries[i].pUnit->m_Packet.getMsgBoundary() == PB_SOLO)
// Check if the unit was already dropped earlier.
if (m_entries[i].status == EntryState_Drop)
continue;
if (m_entries[i].pUnit)
{
const PacketBoundary bnd = packetAt(i).getMsgBoundary();
// Don't drop messages, if all its packets are already in the buffer.
// TODO: Don't drop a several-packet message if all packets are in the buffer.
if (bKeepExisting && bnd == PB_SOLO)
{
bDropByMsgNo = false; // Solo packet, don't search for the rest of the message.
LOGC(rbuflog.Debug,
log << "CRcvBuffer::dropMessage(): Skipped dropping an existing SOLO packet %"
<< packetAt(i).getSeqNo() << ".");
continue;
}
const int32_t msgseq = packetAt(i).getMsgSeq(m_bPeerRexmitFlag);
if (msgno > SRT_MSGNO_CONTROL && msgseq != msgno)
{
LOGC(rbuflog.Warn, log << "CRcvBuffer.dropMessage(): Packet seqno %" << packetAt(i).getSeqNo() << " has msgno " << msgseq << " differs from requested " << msgno);
}
if (bDropByMsgNo && bnd == PB_FIRST)
{
// First packet of the message is about to be dropped. That was the only reason to search for msgno.
bDropByMsgNo = false;
}
}
dropUnitInPos(i);
++iDropCnt;
m_entries[i].status = EntryState_Drop;
@ -284,11 +324,48 @@ int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno)
minDroppedOffset = offPos(m_iStartPos, i);
}
LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): [" << seqnolo << "; "
<< seqnohi << "].");
if (bDropByMsgNo)
{
// If msgno is specified, potentially not the whole message was dropped using seqno range.
// The sender might have removed the first packets of the message, and thus @a seqnolo may point to a packet in the middle.
// The sender should have the last packet of the message it is requesting to be dropped.
// Therefore we don't search forward, but need to check earlier packets in the RCV buffer.
// Try to drop by the message number in case the message starts earlier than @a seqnolo.
const int stop_pos = decPos(m_iStartPos);
for (int i = start_pos; i != stop_pos; i = decPos(i))
{
// Can't drop if message number is not known.
if (!m_entries[i].pUnit) // also dropped earlier.
continue;
const PacketBoundary bnd = packetAt(i).getMsgBoundary();
const int32_t msgseq = packetAt(i).getMsgSeq(m_bPeerRexmitFlag);
if (msgseq != msgno)
break;
if (bKeepExisting && bnd == PB_SOLO)
{
LOGC(rbuflog.Debug,
log << "CRcvBuffer::dropMessage(): Skipped dropping an existing SOLO message packet %"
<< packetAt(i).getSeqNo() << ".");
break;
}
++iDropCnt;
dropUnitInPos(i);
m_entries[i].status = EntryState_Drop;
// As the search goes backward, i is always earlier than minDroppedOffset.
minDroppedOffset = offPos(m_iStartPos, i);
// Break the loop if the start of the message has been found. No need to search further.
if (bnd == PB_FIRST)
break;
}
IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt);
}
// Check if units before m_iFirstNonreadPos are dropped.
bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize());
const bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize());
releaseNextFillerEntries();
if (needUpdateNonreadPos)
{
@ -305,19 +382,19 @@ int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno)
return iDropCnt;
}
int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl)
int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl)
{
const bool canReadInOrder = hasReadableInorderPkts();
if (!canReadInOrder && m_iFirstReadableOutOfOrder < 0)
{
LOGC(rbuflog.Warn, log << "CRcvBufferNew.readMessage(): nothing to read. Ignored isRcvDataReady() result?");
LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?");
return 0;
}
const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder;
IF_RCVBUF_DEBUG(ScopedLog scoped_log);
IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos);
IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos);
size_t remain = len;
char* dst = data;
@ -329,11 +406,11 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl)
SRT_ASSERT(m_entries[i].pUnit);
if (!m_entries[i].pUnit)
{
LOGC(rbuflog.Error, log << "CRcvBufferNew::readMessage(): null packet encountered.");
LOGC(rbuflog.Error, log << "CRcvBuffer::readMessage(): null packet encountered.");
break;
}
const CPacket& packet = m_entries[i].pUnit->m_Packet;
const CPacket& packet = packetAt(i);
const size_t pktsize = packet.getLength();
const int32_t pktseqno = packet.getSeqNo();
@ -368,8 +445,8 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl)
if (updateStartPos)
{
m_iStartPos = incPos(i);
--m_iMaxPosInc;
SRT_ASSERT(m_iMaxPosInc >= 0);
--m_iMaxPosOff;
SRT_ASSERT(m_iMaxPosOff >= 0);
m_iStartSeqNo = CSeqNo::incseq(pktseqno);
}
else
@ -390,7 +467,7 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl)
releaseNextFillerEntries();
if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos))
if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos))
{
m_iFirstNonreadPos = m_iStartPos;
//updateNonreadPos();
@ -440,7 +517,7 @@ namespace {
}
}
int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg)
int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg)
{
int p = m_iStartPos;
const int end_pos = m_iFirstNonreadPos;
@ -458,7 +535,7 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg)
return -1;
}
const srt::CPacket& pkt = m_entries[p].pUnit->m_Packet;
const srt::CPacket& pkt = packetAt(p);
if (bTsbPdEnabled)
{
@ -486,8 +563,8 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg)
m_iNotch = 0;
m_iStartPos = p;
--m_iMaxPosInc;
SRT_ASSERT(m_iMaxPosInc >= 0);
--m_iMaxPosOff;
SRT_ASSERT(m_iMaxPosOff >= 0);
m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo);
}
else
@ -502,8 +579,8 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg)
// Update positions
// Set nonread position to the starting position before updating,
// because start position was increased, and preceeding packets are invalid.
if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos))
// because start position was increased, and preceding packets are invalid.
if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos))
{
m_iFirstNonreadPos = m_iStartPos;
}
@ -516,22 +593,22 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg)
return iBytesRead;
}
int CRcvBufferNew::readBuffer(char* dst, int len)
int CRcvBuffer::readBuffer(char* dst, int len)
{
return readBufferTo(len, copyBytesToBuf, reinterpret_cast<void*>(dst));
}
int CRcvBufferNew::readBufferToFile(fstream& ofs, int len)
int CRcvBuffer::readBufferToFile(fstream& ofs, int len)
{
return readBufferTo(len, writeBytesToFile, reinterpret_cast<void*>(&ofs));
}
bool CRcvBufferNew::hasAvailablePackets() const
bool CRcvBuffer::hasAvailablePackets() const
{
return hasReadableInorderPkts() || (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1);
}
int CRcvBufferNew::getRcvDataSize() const
int CRcvBuffer::getRcvDataSize() const
{
if (m_iFirstNonreadPos >= m_iStartPos)
return m_iFirstNonreadPos - m_iStartPos;
@ -539,36 +616,40 @@ int CRcvBufferNew::getRcvDataSize() const
return int(m_szSize + m_iFirstNonreadPos - m_iStartPos);
}
int CRcvBufferNew::getTimespan_ms() const
int CRcvBuffer::getTimespan_ms() const
{
if (!m_tsbpd.isEnabled())
return 0;
if (m_iMaxPosInc == 0)
if (m_iMaxPosOff == 0)
return 0;
const int lastpos = incPos(m_iStartPos, m_iMaxPosInc - 1);
int startpos = m_iStartPos;
while (m_entries[startpos].pUnit == NULL)
int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1);
// Normally the last position should always be non empty
// if TSBPD is enabled (reading out of order is not allowed).
// However if decryption of the last packet fails, it may be dropped
// from the buffer (AES-GCM), and the position will be empty.
SRT_ASSERT(m_entries[lastpos].pUnit != NULL || m_entries[lastpos].status == EntryState_Drop);
while (m_entries[lastpos].pUnit == NULL && lastpos != m_iStartPos)
{
if (startpos == lastpos)
break;
lastpos = decPos(lastpos);
}
if (m_entries[lastpos].pUnit == NULL)
return 0;
int startpos = m_iStartPos;
while (m_entries[startpos].pUnit == NULL && startpos != lastpos)
{
startpos = incPos(startpos);
}
if (m_entries[startpos].pUnit == NULL)
return 0;
// Should not happen
SRT_ASSERT(m_entries[lastpos].pUnit != NULL);
if (m_entries[lastpos].pUnit == NULL)
return 0;
const steady_clock::time_point startstamp =
getPktTsbPdTime(m_entries[startpos].pUnit->m_Packet.getMsgTimeStamp());
const steady_clock::time_point endstamp = getPktTsbPdTime(m_entries[lastpos].pUnit->m_Packet.getMsgTimeStamp());
getPktTsbPdTime(packetAt(startpos).getMsgTimeStamp());
const steady_clock::time_point endstamp = getPktTsbPdTime(packetAt(lastpos).getMsgTimeStamp());
if (endstamp < startstamp)
return 0;
@ -577,7 +658,7 @@ int CRcvBufferNew::getTimespan_ms() const
return static_cast<int>(count_milliseconds(endstamp - startstamp) + 1);
}
int CRcvBufferNew::getRcvDataSize(int& bytes, int& timespan) const
int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) const
{
ScopedLock lck(m_BytesCountLock);
bytes = m_iBytesCount;
@ -585,16 +666,16 @@ int CRcvBufferNew::getRcvDataSize(int& bytes, int& timespan) const
return m_iPktsCount;
}
CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstValidPacketInfo() const
CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const
{
const int end_pos = incPos(m_iStartPos, m_iMaxPosInc);
const int end_pos = incPos(m_iStartPos, m_iMaxPosOff);
for (int i = m_iStartPos; i != end_pos; i = incPos(i))
{
// TODO: Maybe check status?
if (!m_entries[i].pUnit)
continue;
const CPacket& packet = m_entries[i].pUnit->m_Packet;
const CPacket& packet = packetAt(i);
const PacketInfo info = { packet.getSeqNo(), i != m_iStartPos, getPktTsbPdTime(packet.getMsgTimeStamp()) };
return info;
}
@ -603,20 +684,20 @@ CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstValidPacketInfo() const
return info;
}
std::pair<int, int> CRcvBufferNew::getAvailablePacketsRange() const
std::pair<int, int> CRcvBuffer::getAvailablePacketsRange() const
{
const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, (int) countReadable());
return std::pair<int, int>(m_iStartSeqNo, seqno_last);
}
size_t CRcvBufferNew::countReadable() const
size_t CRcvBuffer::countReadable() const
{
if (m_iFirstNonreadPos >= m_iStartPos)
return m_iFirstNonreadPos - m_iStartPos;
return m_szSize + m_iFirstNonreadPos - m_iStartPos;
}
bool CRcvBufferNew::isRcvDataReady(time_point time_now) const
bool CRcvBuffer::isRcvDataReady(time_point time_now) const
{
const bool haveInorderPackets = hasReadableInorderPkts();
if (!m_tsbpd.isEnabled())
@ -636,7 +717,7 @@ bool CRcvBufferNew::isRcvDataReady(time_point time_now) const
return info.tsbpd_time <= time_now;
}
CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point time_now) const
CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_now) const
{
const PacketInfo unreadableInfo = {SRT_SEQNO_NONE, false, time_point()};
const bool hasInorderPackets = hasReadableInorderPkts();
@ -645,7 +726,7 @@ CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point t
{
if (hasInorderPackets)
{
const CPacket& packet = m_entries[m_iStartPos].pUnit->m_Packet;
const CPacket& packet = packetAt(m_iStartPos);
const PacketInfo info = {packet.getSeqNo(), false, time_point()};
return info;
}
@ -653,7 +734,7 @@ CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point t
if (m_iFirstReadableOutOfOrder >= 0)
{
SRT_ASSERT(m_numOutOfOrderPackets > 0);
const CPacket& packet = m_entries[m_iFirstReadableOutOfOrder].pUnit->m_Packet;
const CPacket& packet = packetAt(m_iFirstReadableOutOfOrder);
const PacketInfo info = {packet.getSeqNo(), true, time_point()};
return info;
}
@ -671,7 +752,7 @@ CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point t
return unreadableInfo;
}
void CRcvBufferNew::countBytes(int pkts, int bytes)
void CRcvBuffer::countBytes(int pkts, int bytes)
{
ScopedLock lock(m_BytesCountLock);
m_iBytesCount += bytes; // added or removed bytes from rcv buffer
@ -680,7 +761,7 @@ void CRcvBufferNew::countBytes(int pkts, int bytes)
m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes);
}
void CRcvBufferNew::releaseUnitInPos(int pos)
void CRcvBuffer::releaseUnitInPos(int pos)
{
CUnit* tmp = m_entries[pos].pUnit;
m_entries[pos] = Entry(); // pUnit = NULL; status = Empty
@ -688,15 +769,15 @@ void CRcvBufferNew::releaseUnitInPos(int pos)
m_pUnitQueue->makeUnitFree(tmp);
}
bool CRcvBufferNew::dropUnitInPos(int pos)
bool CRcvBuffer::dropUnitInPos(int pos)
{
if (!m_entries[pos].pUnit)
return false;
if (m_tsbpd.isEnabled())
{
updateTsbPdTimeBase(m_entries[pos].pUnit->m_Packet.getMsgTimeStamp());
updateTsbPdTimeBase(packetAt(pos).getMsgTimeStamp());
}
else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag())
else if (m_bMessageAPI && !packetAt(pos).getMsgOrderFlag())
{
--m_numOutOfOrderPackets;
if (pos == m_iFirstReadableOutOfOrder)
@ -706,7 +787,7 @@ bool CRcvBufferNew::dropUnitInPos(int pos)
return true;
}
void CRcvBufferNew::releaseNextFillerEntries()
void CRcvBuffer::releaseNextFillerEntries()
{
int pos = m_iStartPos;
while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop)
@ -715,24 +796,24 @@ void CRcvBufferNew::releaseNextFillerEntries()
releaseUnitInPos(pos);
pos = incPos(pos);
m_iStartPos = pos;
--m_iMaxPosInc;
if (m_iMaxPosInc < 0)
m_iMaxPosInc = 0;
--m_iMaxPosOff;
if (m_iMaxPosOff < 0)
m_iMaxPosOff = 0;
}
}
// TODO: Is this function complete? There are some comments left inside.
void CRcvBufferNew::updateNonreadPos()
void CRcvBuffer::updateNonreadPos()
{
if (m_iMaxPosInc == 0)
if (m_iMaxPosOff == 0)
return;
const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); // The empty position right after the last valid entry.
const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry.
int pos = m_iFirstNonreadPos;
while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail)
{
if (m_bMessageAPI && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST) == 0)
if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0)
break;
for (int i = pos; i != end_pos; i = incPos(i))
@ -743,7 +824,7 @@ void CRcvBufferNew::updateNonreadPos()
}
// Check PB_LAST only in message mode.
if (!m_bMessageAPI || m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST)
if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST)
{
m_iFirstNonreadPos = incPos(i);
break;
@ -757,13 +838,13 @@ void CRcvBufferNew::updateNonreadPos()
}
}
int CRcvBufferNew::findLastMessagePkt()
int CRcvBuffer::findLastMessagePkt()
{
for (int i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i))
{
SRT_ASSERT(m_entries[i].pUnit);
if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST)
if (packetAt(i).getMsgBoundary() & PB_LAST)
{
return i;
}
@ -772,7 +853,7 @@ int CRcvBufferNew::findLastMessagePkt()
return -1;
}
void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos)
void CRcvBuffer::onInsertNotInOrderPacket(int insertPos)
{
if (m_numOutOfOrderPackets == 0)
return;
@ -788,9 +869,9 @@ void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos)
// Just a sanity check. This function is called when a new packet is added.
// So the should be unacknowledged packets.
SRT_ASSERT(m_iMaxPosInc > 0);
SRT_ASSERT(m_iMaxPosOff > 0);
SRT_ASSERT(m_entries[insertPos].pUnit);
const CPacket& pkt = m_entries[insertPos].pUnit->m_Packet;
const CPacket& pkt = packetAt(insertPos);
const PacketBoundary boundary = pkt.getMsgBoundary();
//if ((boundary & PB_FIRST) && (boundary & PB_LAST))
@ -816,19 +897,19 @@ void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos)
return;
}
bool CRcvBufferNew::checkFirstReadableOutOfOrder()
bool CRcvBuffer::checkFirstReadableOutOfOrder()
{
if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosInc == 0)
if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosOff == 0)
return false;
const int endPos = incPos(m_iStartPos, m_iMaxPosInc);
const int endPos = incPos(m_iStartPos, m_iMaxPosOff);
int msgno = -1;
for (int pos = m_iFirstReadableOutOfOrder; pos != endPos; pos = incPos(pos))
{
if (!m_entries[pos].pUnit)
return false;
const CPacket& pkt = m_entries[pos].pUnit->m_Packet;
const CPacket& pkt = packetAt(pos);
if (pkt.getMsgOrderFlag())
return false;
@ -844,12 +925,12 @@ bool CRcvBufferNew::checkFirstReadableOutOfOrder()
return false;
}
void CRcvBufferNew::updateFirstReadableOutOfOrder()
void CRcvBuffer::updateFirstReadableOutOfOrder()
{
if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0)
return;
if (m_iMaxPosInc == 0)
if (m_iMaxPosOff == 0)
return;
// TODO: unused variable outOfOrderPktsRemain?
@ -857,7 +938,7 @@ void CRcvBufferNew::updateFirstReadableOutOfOrder()
// Search further packets to the right.
// First check if there are packets to the right.
const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize;
const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize;
int posFirst = -1;
int posLast = -1;
@ -871,7 +952,7 @@ void CRcvBufferNew::updateFirstReadableOutOfOrder()
continue;
}
const CPacket& pkt = m_entries[pos].pUnit->m_Packet;
const CPacket& pkt = packetAt(pos);
if (pkt.getMsgOrderFlag()) // Skip in order packet
{
@ -907,11 +988,11 @@ void CRcvBufferNew::updateFirstReadableOutOfOrder()
return;
}
int CRcvBufferNew::scanNotInOrderMessageRight(const int startPos, int msgNo) const
int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const
{
// Search further packets to the right.
// First check if there are packets to the right.
const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize;
const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize;
if (startPos == lastPos)
return -1;
@ -922,7 +1003,7 @@ int CRcvBufferNew::scanNotInOrderMessageRight(const int startPos, int msgNo) con
if (!m_entries[pos].pUnit)
break;
const CPacket& pkt = m_entries[pos].pUnit->m_Packet;
const CPacket& pkt = packetAt(pos);
if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo)
{
@ -938,9 +1019,9 @@ int CRcvBufferNew::scanNotInOrderMessageRight(const int startPos, int msgNo) con
return -1;
}
int CRcvBufferNew::scanNotInOrderMessageLeft(const int startPos, int msgNo) const
int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const
{
// Search preceeding packets to the left.
// Search preceding packets to the left.
// First check if there are packets to the left.
if (startPos == m_iStartPos)
return -1;
@ -953,7 +1034,7 @@ int CRcvBufferNew::scanNotInOrderMessageLeft(const int startPos, int msgNo) cons
if (!m_entries[pos].pUnit)
return -1;
const CPacket& pkt = m_entries[pos].pUnit->m_Packet;
const CPacket& pkt = packetAt(pos);
if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo)
{
@ -969,17 +1050,17 @@ int CRcvBufferNew::scanNotInOrderMessageLeft(const int startPos, int msgNo) cons
return -1;
}
bool CRcvBufferNew::addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample)
bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample)
{
return m_tsbpd.addDriftSample(usTimestamp, tsPktArrival, usRTTSample);
}
void CRcvBufferNew::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay)
void CRcvBuffer::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay)
{
m_tsbpd.setTsbPdMode(timebase, wrap, delay);
}
void CRcvBufferNew::applyGroupTime(const steady_clock::time_point& timebase,
void CRcvBuffer::applyGroupTime(const steady_clock::time_point& timebase,
bool wrp,
uint32_t delay,
const steady_clock::duration& udrift)
@ -987,41 +1068,44 @@ void CRcvBufferNew::applyGroupTime(const steady_clock::time_point& timebase,
m_tsbpd.applyGroupTime(timebase, wrp, delay, udrift);
}
void CRcvBufferNew::applyGroupDrift(const steady_clock::time_point& timebase,
void CRcvBuffer::applyGroupDrift(const steady_clock::time_point& timebase,
bool wrp,
const steady_clock::duration& udrift)
{
m_tsbpd.applyGroupDrift(timebase, wrp, udrift);
}
CRcvBufferNew::time_point CRcvBufferNew::getTsbPdTimeBase(uint32_t usPktTimestamp) const
CRcvBuffer::time_point CRcvBuffer::getTsbPdTimeBase(uint32_t usPktTimestamp) const
{
return m_tsbpd.getTsbPdTimeBase(usPktTimestamp);
}
void CRcvBufferNew::updateTsbPdTimeBase(uint32_t usPktTimestamp)
void CRcvBuffer::updateTsbPdTimeBase(uint32_t usPktTimestamp)
{
m_tsbpd.updateTsbPdTimeBase(usPktTimestamp);
}
string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const
string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const
{
stringstream ss;
ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize;
ss << " pkts. ";
if (m_tsbpd.isEnabled() && m_iMaxPosInc > 0)
ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo
<< " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". ";
ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. ";
if (m_tsbpd.isEnabled() && m_iMaxPosOff > 0)
{
const PacketInfo nextValidPkt = getFirstValidPacketInfo();
ss << "(TSBPD ready in ";
if (!is_zero(nextValidPkt.tsbpd_time))
{
ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms";
const int iLastPos = incPos(m_iStartPos, m_iMaxPosInc - 1);
const int iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1);
if (m_entries[iLastPos].pUnit)
{
ss << ", timespan ";
const uint32_t usPktTimestamp = m_entries[iLastPos].pUnit->m_Packet.getMsgTimeStamp();
const uint32_t usPktTimestamp = packetAt(iLastPos).getMsgTimeStamp();
ss << count_milliseconds(m_tsbpd.getPktTsbPdTime(usPktTimestamp) - nextValidPkt.tsbpd_time);
ss << " ms";
}
@ -1030,7 +1114,6 @@ string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& t
{
ss << "n/a";
}
ss << "). ";
}
@ -1038,13 +1121,13 @@ string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& t
return ss.str();
}
CRcvBufferNew::time_point CRcvBufferNew::getPktTsbPdTime(uint32_t usPktTimestamp) const
CRcvBuffer::time_point CRcvBuffer::getPktTsbPdTime(uint32_t usPktTimestamp) const
{
return m_tsbpd.getPktTsbPdTime(usPktTimestamp);
}
/* Return moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */
int CRcvBufferNew::getRcvAvgDataSize(int& bytes, int& timespan)
int CRcvBuffer::getRcvAvgDataSize(int& bytes, int& timespan)
{
// Average number of packets and timespan could be small,
// so rounding is beneficial, while for the number of
@ -1056,7 +1139,7 @@ int CRcvBufferNew::getRcvAvgDataSize(int& bytes, int& timespan)
}
/* Update moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */
void CRcvBufferNew::updRcvAvgDataSize(const steady_clock::time_point& now)
void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now)
{
if (!m_mavg.isTimeToUpdate(now))
return;
@ -1068,5 +1151,3 @@ void CRcvBufferNew::updRcvAvgDataSize(const steady_clock::time_point& now)
}
} // namespace srt
#endif // ENABLE_NEW_RCVBUFFER

@ -11,12 +11,9 @@
#ifndef INC_SRT_BUFFER_RCV_H
#define INC_SRT_BUFFER_RCV_H
#if ENABLE_NEW_RCVBUFFER
#include "buffer.h" // AvgBufSize
#include "buffer_tools.h" // AvgBufSize
#include "common.h"
#include "queue.h"
#include "sync.h"
#include "tsbpd_time.h"
namespace srt
@ -26,7 +23,7 @@ namespace srt
* Circular receiver buffer.
*
* |<------------------- m_szSize ---------------------------->|
* | |<------------ m_iMaxPosInc ----------->| |
* | |<------------ m_iMaxPosOff ----------->| |
* | | | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+
* | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[]
@ -45,15 +42,15 @@ namespace srt
* first_nonread_pos_:
*/
class CRcvBufferNew
class CRcvBuffer
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
public:
CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI);
CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI);
~CRcvBufferNew();
~CRcvBuffer();
public:
/// Insert a unit into the buffer.
@ -77,14 +74,29 @@ public:
/// @return the number of dropped packets.
int dropAll();
/// @brief Drop the whole message from the buffer.
/// If message number is 0, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi].
/// When one packet of the message is in the range of dropping, the whole message is to be dropped.
enum DropActionIfExists {
DROP_EXISTING = 0,
KEEP_EXISTING = 1
};
/// @brief Drop a sequence of packets from the buffer.
/// If @a msgno is valid, sender has requested to drop the whole message by TTL. In this case it has to also provide a pkt seqno range.
/// However, if a message has been partially acknowledged and already removed from the SND buffer,
/// the @a seqnolo might specify some position in the middle of the message, not the very first packet.
/// If those packets have been acknowledged, they must exist in the receiver buffer unless already read.
/// In this case the @a msgno should be used to determine starting packets of the message.
/// Some packets of the message can be missing on the receiver, therefore the actual drop should still be performed by pkt seqno range.
/// If message number is 0 or SRT_MSGNO_NONE, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi].
/// A SOLO message packet can be kept depending on @a actionOnExisting value.
/// TODO: A message in general can be kept if all of its packets are in the buffer, depending on @a actionOnExisting value.
/// This is done to avoid dropping existing packet when the sender was asked to re-transmit a packet from an outdated loss report,
/// which is already not available in the SND buffer.
/// @param seqnolo sequence number of the first packet in the dropping range.
/// @param seqnohi sequence number of the last packet in the dropping range.
/// @param msgno message number to drop (0 if unknown)
/// @param actionOnExisting Should an exising SOLO packet be dropped from the buffer or preserved?
/// @return the number of packets actually dropped.
int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno);
int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, DropActionIfExists actionOnExisting);
/// Read the whole message from one or several packets.
///
@ -130,9 +142,8 @@ public:
const int iRBufSeqNo = getStartSeqNo();
if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo
{
// Full capacity is available, still don't want to encourage extra packets to come.
// Note: CSeqNo::seqlen(n, n) returns 1.
return capacity() - CSeqNo::seqlen(iFirstUnackSeqNo, iRBufSeqNo) + 1;
// Full capacity is available.
return capacity();
}
// Note: CSeqNo::seqlen(n, n) returns 1.
@ -140,13 +151,14 @@ public:
}
/// @brief Checks if the buffer has packets available for reading regardless of the TSBPD.
/// A message is available for reading only if all of its packets are present in the buffer.
/// @return true if there are packets available for reading, false otherwise.
bool hasAvailablePackets() const;
/// Query how many data has been continuously received (for reading) and available for reading out
/// regardless of the TSBPD.
/// TODO: Rename to countAvailablePackets().
/// @return size of valid (continous) data for reading.
/// @return size of valid (continuous) data for reading.
int getRcvDataSize() const;
/// Get the number of packets, bytes and buffer timespan.
@ -165,11 +177,11 @@ public:
/// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if
/// none
/// @param [out] passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app)
/// @param [out] skipseqno -1 or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets.
/// @param [out] passack true if 1st ready packet is not yet acknowledged (allowed to be delivered to the app)
/// @param [out] skipseqno -1 or sequence number of 1st unacknowledged packet (after one or more missing packets) that is ready to play.
/// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true
/// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE:
/// IF skipseqno != -1, packet ready to play preceeded by missing packets.;
/// IF skipseqno != -1, packet ready to play preceded by missing packets.;
/// IF skipseqno == -1, no missing packet but 1st not ready to play.
PacketInfo getFirstValidPacketInfo() const;
@ -185,7 +197,7 @@ public:
bool empty() const
{
return (m_iMaxPosInc == 0);
return (m_iMaxPosOff == 0);
}
/// Return buffer capacity.
@ -227,6 +239,18 @@ private:
inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; }
inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); }
inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); }
inline int cmpPos(int pos2, int pos1) const
{
// XXX maybe not the best implementation, but this keeps up to the rule
const int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + (int)m_szSize - m_iStartPos;
const int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + (int)m_szSize - m_iStartPos;
return off2 - off1;
}
// NOTE: Assumes that pUnit != NULL
CPacket& packetAt(int pos) { return m_entries[pos].pUnit->m_Packet; }
const CPacket& packetAt(int pos) const { return m_entries[pos].pUnit->m_Packet; }
private:
void countBytes(int pkts, int bytes);
@ -268,7 +292,7 @@ private:
int getTimespan_ms() const;
private:
// TODO: Call makeUnitGood upon assignment, and makeUnitFree upon clearing.
// TODO: Call makeUnitTaken upon assignment, and makeUnitFree upon clearing.
// TODO: CUnitPtr is not in use at the moment, but may be a smart pointer.
// class CUnitPtr
// {
@ -313,7 +337,7 @@ private:
int m_iStartSeqNo;
int m_iStartPos; // the head position for I/O (inclusive)
int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos)
int m_iMaxPosInc; // the furthest data position
int m_iMaxPosOff; // the furthest data position
int m_iNotch; // the starting read point of the first unit
size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false
@ -326,7 +350,7 @@ public: // TSBPD public functions
/// Set TimeStamp-Based Packet Delivery Rx Mode
/// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay
/// @param [in] wrap Is in wrapping period
/// @param [in] delay aggreed TsbPD delay
/// @param [in] delay agreed TsbPD delay
///
/// @return 0
void setTsbPdMode(const time_point& timebase, bool wrap, duration delay);
@ -344,6 +368,8 @@ public: // TSBPD public functions
time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const;
void updateTsbPdTimeBase(uint32_t usPktTimestamp);
bool isTsbPd() const { return m_tsbpd.isEnabled(); }
/// Form a string of the current buffer fullness state.
/// number of packets acknowledged, TSBPD readiness, etc.
std::string strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const;
@ -363,5 +389,4 @@ private: // Statistics
} // namespace srt
#endif // ENABLE_NEW_RCVBUFFER
#endif // INC_SRT_BUFFER_RCV_H

@ -0,0 +1,730 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 03/12/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include <cmath>
#include "buffer_snd.h"
#include "packet.h"
#include "core.h" // provides some constants
#include "logging.h"
namespace srt {
using namespace std;
using namespace srt_logging;
using namespace sync;
CSndBuffer::CSndBuffer(int size, int maxpld, int authtag)
: m_BufLock()
, m_pBlock(NULL)
, m_pFirstBlock(NULL)
, m_pCurrBlock(NULL)
, m_pLastBlock(NULL)
, m_pBuffer(NULL)
, m_iNextMsgNo(1)
, m_iSize(size)
, m_iBlockLen(maxpld)
, m_iAuthTagSize(authtag)
, m_iCount(0)
, m_iBytesCount(0)
{
// initial physical buffer of "size"
m_pBuffer = new Buffer;
m_pBuffer->m_pcData = new char[m_iSize * m_iBlockLen];
m_pBuffer->m_iSize = m_iSize;
m_pBuffer->m_pNext = NULL;
// circular linked list for out bound packets
m_pBlock = new Block;
Block* pb = m_pBlock;
char* pc = m_pBuffer->m_pcData;
for (int i = 0; i < m_iSize; ++i)
{
pb->m_iMsgNoBitset = 0;
pb->m_pcData = pc;
pc += m_iBlockLen;
if (i < m_iSize - 1)
{
pb->m_pNext = new Block;
pb = pb->m_pNext;
}
}
pb->m_pNext = m_pBlock;
m_pFirstBlock = m_pCurrBlock = m_pLastBlock = m_pBlock;
setupMutex(m_BufLock, "Buf");
}
CSndBuffer::~CSndBuffer()
{
Block* pb = m_pBlock->m_pNext;
while (pb != m_pBlock)
{
Block* temp = pb;
pb = pb->m_pNext;
delete temp;
}
delete m_pBlock;
while (m_pBuffer != NULL)
{
Buffer* temp = m_pBuffer;
m_pBuffer = m_pBuffer->m_pNext;
delete[] temp->m_pcData;
delete temp;
}
releaseMutex(m_BufLock);
}
void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl)
{
int32_t& w_msgno = w_mctrl.msgno;
int32_t& w_seqno = w_mctrl.pktseq;
int64_t& w_srctime = w_mctrl.srctime;
const int& ttl = w_mctrl.msgttl;
const int iPktLen = getMaxPacketLen();
const int iNumBlocks = countNumPacketsRequired(len, iPktLen);
HLOGC(bslog.Debug,
log << "addBuffer: needs=" << iNumBlocks << " buffers for " << len << " bytes. Taken=" << m_iCount << "/" << m_iSize);
// Retrieve current time before locking the mutex to be closer to packet submission event.
const steady_clock::time_point tnow = steady_clock::now();
ScopedLock bufferguard(m_BufLock);
// Dynamically increase sender buffer if there is not enough room.
while (iNumBlocks + m_iCount >= m_iSize)
{
HLOGC(bslog.Debug, log << "addBuffer: ... still lacking " << (iNumBlocks + m_iCount - m_iSize) << " buffers...");
increase();
}
const int32_t inorder = w_mctrl.inorder ? MSGNO_PACKET_INORDER::mask : 0;
HLOGC(bslog.Debug,
log << CONID() << "addBuffer: adding " << iNumBlocks << " packets (" << len << " bytes) to send, msgno="
<< (w_msgno > 0 ? w_msgno : m_iNextMsgNo) << (inorder ? "" : " NOT") << " in order");
// Calculate origin time (same for all blocks of the message).
m_tsLastOriginTime = w_srctime ? time_point() + microseconds_from(w_srctime) : tnow;
// Rewrite back the actual value, even if it stays the same, so that the calling facilities can reuse it.
// May also be a subject to conversion error, thus the actual value is signalled back.
w_srctime = count_microseconds(m_tsLastOriginTime.time_since_epoch());
// The sequence number passed to this function is the sequence number
// that the very first packet from the packet series should get here.
// If there's more than one packet, this function must increase it by itself
// and then return the accordingly modified sequence number in the reference.
Block* s = m_pLastBlock;
if (w_msgno == SRT_MSGNO_NONE) // DEFAULT-UNCHANGED msgno supplied
{
HLOGC(bslog.Debug, log << "addBuffer: using internally managed msgno=" << m_iNextMsgNo);
w_msgno = m_iNextMsgNo;
}
else
{
HLOGC(bslog.Debug, log << "addBuffer: OVERWRITTEN by msgno supplied by caller: msgno=" << w_msgno);
m_iNextMsgNo = w_msgno;
}
for (int i = 0; i < iNumBlocks; ++i)
{
int pktlen = len - i * iPktLen;
if (pktlen > iPktLen)
pktlen = iPktLen;
HLOGC(bslog.Debug,
log << "addBuffer: %" << w_seqno << " #" << w_msgno << " offset=" << (i * iPktLen)
<< " size=" << pktlen << " TO BUFFER:" << (void*)s->m_pcData);
memcpy((s->m_pcData), data + i * iPktLen, pktlen);
s->m_iLength = pktlen;
s->m_iSeqNo = w_seqno;
w_seqno = CSeqNo::incseq(w_seqno);
s->m_iMsgNoBitset = m_iNextMsgNo | inorder;
if (i == 0)
s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST);
if (i == iNumBlocks - 1)
s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST);
// NOTE: if i is neither 0 nor size-1, it resuls with PB_SUBSEQUENT.
// if i == 0 == size-1, it results with PB_SOLO.
// Packets assigned to one message can be:
// [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] - 4 packets per message
// [PB_FIRST] [PB_LAST] - 2 packets per message
// [PB_SOLO] - 1 packet per message
s->m_iTTL = ttl;
s->m_tsRexmitTime = time_point();
s->m_tsOriginTime = m_tsLastOriginTime;
// Should never happen, as the call to increase() should ensure enough buffers.
SRT_ASSERT(s->m_pNext);
s = s->m_pNext;
}
m_pLastBlock = s;
m_iCount += iNumBlocks;
m_iBytesCount += len;
m_rateEstimator.updateInputRate(m_tsLastOriginTime, iNumBlocks, len);
updAvgBufSize(m_tsLastOriginTime);
// MSGNO_SEQ::mask has a form: 00000011111111...
// At least it's known that it's from some index inside til the end (to bit 0).
// If this value has been reached in a step of incrementation, it means that the
// maximum value has been reached. Casting to int32_t to ensure the same sign
// in comparison, although it's far from reaching the sign bit.
const int nextmsgno = ++MsgNo(m_iNextMsgNo);
HLOGC(bslog.Debug, log << "CSndBuffer::addBuffer: updating msgno: #" << m_iNextMsgNo << " -> #" << nextmsgno);
m_iNextMsgNo = nextmsgno;
}
int CSndBuffer::addBufferFromFile(fstream& ifs, int len)
{
const int iPktLen = getMaxPacketLen();
const int iNumBlocks = countNumPacketsRequired(len, iPktLen);
HLOGC(bslog.Debug,
log << "addBufferFromFile: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << iPktLen
<< " buffers for " << len << " bytes");
// dynamically increase sender buffer
while (iNumBlocks + m_iCount >= m_iSize)
{
HLOGC(bslog.Debug,
log << "addBufferFromFile: ... still lacking " << (iNumBlocks + m_iCount - m_iSize) << " buffers...");
increase();
}
HLOGC(bslog.Debug,
log << CONID() << "addBufferFromFile: adding " << iPktLen << " packets (" << len
<< " bytes) to send, msgno=" << m_iNextMsgNo);
Block* s = m_pLastBlock;
int total = 0;
for (int i = 0; i < iNumBlocks; ++i)
{
if (ifs.bad() || ifs.fail() || ifs.eof())
break;
int pktlen = len - i * iPktLen;
if (pktlen > iPktLen)
pktlen = iPktLen;
HLOGC(bslog.Debug,
log << "addBufferFromFile: reading from=" << (i * iPktLen) << " size=" << pktlen
<< " TO BUFFER:" << (void*)s->m_pcData);
ifs.read(s->m_pcData, pktlen);
if ((pktlen = int(ifs.gcount())) <= 0)
break;
// currently file transfer is only available in streaming mode, message is always in order, ttl = infinite
s->m_iMsgNoBitset = m_iNextMsgNo | MSGNO_PACKET_INORDER::mask;
if (i == 0)
s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST);
if (i == iNumBlocks - 1)
s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST);
// NOTE: PB_FIRST | PB_LAST == PB_SOLO.
// none of PB_FIRST & PB_LAST == PB_SUBSEQUENT.
s->m_iLength = pktlen;
s->m_iTTL = SRT_MSGTTL_INF;
s = s->m_pNext;
total += pktlen;
}
m_pLastBlock = s;
enterCS(m_BufLock);
m_iCount += iNumBlocks;
m_iBytesCount += total;
leaveCS(m_BufLock);
m_iNextMsgNo++;
if (m_iNextMsgNo == int32_t(MSGNO_SEQ::mask))
m_iNextMsgNo = 1;
return total;
}
int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs, int& w_seqnoinc)
{
int readlen = 0;
w_seqnoinc = 0;
ScopedLock bufferguard(m_BufLock);
while (m_pCurrBlock != m_pLastBlock)
{
// Make the packet REFLECT the data stored in the buffer.
w_packet.m_pcData = m_pCurrBlock->m_pcData;
readlen = m_pCurrBlock->m_iLength;
w_packet.setLength(readlen, m_iBlockLen);
w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo;
// 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0).
// 2. The readData() is called to get the original (unique) payload not ever sent yet.
// The payload must be encrypted for the first time if the encryption
// is enabled (arg kflgs != EK_NOENC). The KK encryption flag of the data packet
// header must be set and remembered accordingly (see EncryptionKeySpec).
// 3. The next time this packet is read (only for retransmission), the payload is already
// encrypted, and the proper flag value is already stored.
// TODO: Alternatively, encryption could happen before the packet is submitted to the buffer
// (before the addBuffer() call), and corresponding flags could be set accordingly.
// This may also put an encryption burden on the application thread, rather than the sending thread,
// which could be more efficient. Note that packet sequence number must be properly set in that case,
// as it is used as a counter for the AES encryption.
if (kflgs == -1)
{
HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING.");
readlen = 0;
}
else
{
m_pCurrBlock->m_iMsgNoBitset |= MSGNO_ENCKEYSPEC::wrap(kflgs);
}
Block* p = m_pCurrBlock;
w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset;
w_srctime = m_pCurrBlock->m_tsOriginTime;
m_pCurrBlock = m_pCurrBlock->m_pNext;
if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - w_srctime) > p->m_iTTL))
{
LOGC(bslog.Warn, log << CONID() << "CSndBuffer: skipping packet %" << p->m_iSeqNo << " #" << p->getMsgSeq() << " with TTL=" << p->m_iTTL);
// Skip this packet due to TTL expiry.
readlen = 0;
++w_seqnoinc;
continue;
}
HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send");
break;
}
return readlen;
}
CSndBuffer::time_point CSndBuffer::peekNextOriginal() const
{
ScopedLock bufferguard(m_BufLock);
if (m_pCurrBlock == m_pLastBlock)
return time_point();
return m_pCurrBlock->m_tsOriginTime;
}
int32_t CSndBuffer::getMsgNoAt(const int offset)
{
ScopedLock bufferguard(m_BufLock);
Block* p = m_pFirstBlock;
if (p)
{
HLOGC(bslog.Debug,
log << "CSndBuffer::getMsgNoAt: FIRST MSG: size=" << p->m_iLength << " %" << p->m_iSeqNo << " #"
<< p->getMsgSeq() << " !" << BufferStamp(p->m_pcData, p->m_iLength));
}
if (offset >= m_iCount)
{
// Prevent accessing the last "marker" block
LOGC(bslog.Error,
log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, max offset=" << m_iCount);
return SRT_MSGNO_CONTROL;
}
// XXX Suboptimal procedure to keep the blocks identifiable
// by sequence number. Consider using some circular buffer.
int i;
Block* ee SRT_ATR_UNUSED = 0;
for (i = 0; i < offset && p; ++i)
{
ee = p;
p = p->m_pNext;
}
if (!p)
{
LOGC(bslog.Error,
log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, stopped at " << i << " with #"
<< (ee ? ee->getMsgSeq() : SRT_MSGNO_NONE));
return SRT_MSGNO_CONTROL;
}
HLOGC(bslog.Debug,
log << "CSndBuffer::getMsgNoAt: offset=" << offset << " found, size=" << p->m_iLength << " %" << p->m_iSeqNo
<< " #" << p->getMsgSeq() << " !" << BufferStamp(p->m_pcData, p->m_iLength));
return p->getMsgSeq();
}
int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen)
{
int32_t& msgno_bitset = w_packet.m_iMsgNo;
ScopedLock bufferguard(m_BufLock);
Block* p = m_pFirstBlock;
// XXX Suboptimal procedure to keep the blocks identifiable
// by sequence number. Consider using some circular buffer.
for (int i = 0; i < offset && p != m_pLastBlock; ++i)
{
p = p->m_pNext;
}
if (p == m_pLastBlock)
{
LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!");
return 0;
}
#if ENABLE_HEAVY_LOGGING
const int32_t first_seq = p->m_iSeqNo;
int32_t last_seq = p->m_iSeqNo;
#endif
// Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale.
// If so, then inform the caller that it should first take care of the whole
// message (all blocks with that message id). Shift the m_pCurrBlock pointer
// to the position past the last of them. Then return -1 and set the
// msgno_bitset return reference to the message id that should be dropped as
// a whole.
// After taking care of that, the caller should immediately call this function again,
// this time possibly in order to find the real data to be sent.
// if found block is stale
// (This is for messages that have declared TTL - messages that fail to be sent
// before the TTL defined time comes, will be dropped).
if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL))
{
int32_t msgno = p->getMsgSeq();
w_msglen = 1;
p = p->m_pNext;
bool move = false;
while (p != m_pLastBlock && msgno == p->getMsgSeq())
{
#if ENABLE_HEAVY_LOGGING
last_seq = p->m_iSeqNo;
#endif
if (p == m_pCurrBlock)
move = true;
p = p->m_pNext;
if (move)
m_pCurrBlock = p;
w_msglen++;
}
HLOGC(qslog.Debug,
log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", "
<< w_msglen << " packets to drop, msgno=" << msgno);
// If readData returns -1, then msgno_bitset is understood as a Message ID to drop.
// This means that in this case it should be written by the message sequence value only
// (not the whole 4-byte bitset written at PH_MSGNO).
msgno_bitset = msgno;
return -1;
}
w_packet.m_pcData = p->m_pcData;
const int readlen = p->m_iLength;
w_packet.setLength(readlen, m_iBlockLen);
// XXX Here the value predicted to be applied to PH_MSGNO field is extracted.
// As this function is predicted to extract the data to send as a rexmited packet,
// the packet must be in the form ready to send - so, in case of encryption,
// encrypted, and with all ENC flags already set. So, the first call to send
// the packet originally (the other overload of this function) must set these
// flags.
w_packet.m_iMsgNo = p->m_iMsgNoBitset;
w_srctime = p->m_tsOriginTime;
// This function is called when packet retransmission is triggered.
// Therefore we are setting the rexmit time.
p->m_tsRexmitTime = steady_clock::now();
HLOGC(qslog.Debug,
log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo
<< " size=" << readlen << " to send [REXMIT]");
return readlen;
}
sync::steady_clock::time_point CSndBuffer::getPacketRexmitTime(const int offset)
{
ScopedLock bufferguard(m_BufLock);
const Block* p = m_pFirstBlock;
// XXX Suboptimal procedure to keep the blocks identifiable
// by sequence number. Consider using some circular buffer.
for (int i = 0; i < offset; ++i)
{
SRT_ASSERT(p);
p = p->m_pNext;
}
SRT_ASSERT(p);
return p->m_tsRexmitTime;
}
void CSndBuffer::ackData(int offset)
{
ScopedLock bufferguard(m_BufLock);
bool move = false;
for (int i = 0; i < offset; ++i)
{
m_iBytesCount -= m_pFirstBlock->m_iLength;
if (m_pFirstBlock == m_pCurrBlock)
move = true;
m_pFirstBlock = m_pFirstBlock->m_pNext;
}
if (move)
m_pCurrBlock = m_pFirstBlock;
m_iCount -= offset;
updAvgBufSize(steady_clock::now());
}
int CSndBuffer::getCurrBufSize() const
{
return m_iCount;
}
int CSndBuffer::getMaxPacketLen() const
{
return m_iBlockLen - m_iAuthTagSize;
}
int CSndBuffer::countNumPacketsRequired(int iPldLen) const
{
const int iPktLen = getMaxPacketLen();
return countNumPacketsRequired(iPldLen, iPktLen);
}
int CSndBuffer::countNumPacketsRequired(int iPldLen, int iPktLen) const
{
return (iPldLen + iPktLen - 1) / iPktLen;
}
namespace {
int round_val(double val)
{
return static_cast<int>(round(val));
}
}
int CSndBuffer::getAvgBufSize(int& w_bytes, int& w_tsp)
{
ScopedLock bufferguard(m_BufLock); /* Consistency of pkts vs. bytes vs. spantime */
/* update stats in case there was no add/ack activity lately */
updAvgBufSize(steady_clock::now());
// Average number of packets and timespan could be small,
// so rounding is beneficial, while for the number of
// bytes in the buffer is a higher value, so rounding can be omitted,
// but probably better to round all three values.
w_bytes = round_val(m_mavg.bytes());
w_tsp = round_val(m_mavg.timespan_ms());
return round_val(m_mavg.pkts());
}
void CSndBuffer::updAvgBufSize(const steady_clock::time_point& now)
{
if (!m_mavg.isTimeToUpdate(now))
return;
int bytes = 0;
int timespan_ms = 0;
const int pkts = getCurrBufSize((bytes), (timespan_ms));
m_mavg.update(now, pkts, bytes, timespan_ms);
}
int CSndBuffer::getCurrBufSize(int& w_bytes, int& w_timespan) const
{
w_bytes = m_iBytesCount;
/*
* Timespan can be less then 1000 us (1 ms) if few packets.
* Also, if there is only one pkt in buffer, the time difference will be 0.
* Therefore, always add 1 ms if not empty.
*/
w_timespan = 0 < m_iCount ? (int) count_milliseconds(m_tsLastOriginTime - m_pFirstBlock->m_tsOriginTime) + 1 : 0;
return m_iCount;
}
CSndBuffer::duration CSndBuffer::getBufferingDelay(const time_point& tnow) const
{
ScopedLock lck(m_BufLock);
SRT_ASSERT(m_pFirstBlock);
if (m_iCount == 0)
return duration(0);
return tnow - m_pFirstBlock->m_tsOriginTime;
}
int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_clock::time_point& too_late_time)
{
int dpkts = 0;
int dbytes = 0;
bool move = false;
int32_t msgno = 0;
ScopedLock bufferguard(m_BufLock);
for (int i = 0; i < m_iCount && m_pFirstBlock->m_tsOriginTime < too_late_time; ++i)
{
dpkts++;
dbytes += m_pFirstBlock->m_iLength;
msgno = m_pFirstBlock->getMsgSeq();
if (m_pFirstBlock == m_pCurrBlock)
move = true;
m_pFirstBlock = m_pFirstBlock->m_pNext;
}
if (move)
{
m_pCurrBlock = m_pFirstBlock;
}
m_iCount -= dpkts;
m_iBytesCount -= dbytes;
w_bytes = dbytes;
// We report the increased number towards the last ever seen
// by the loop, as this last one is the last received. So remained
// (even if "should remain") is the first after the last removed one.
w_first_msgno = ++MsgNo(msgno);
updAvgBufSize(steady_clock::now());
return (dpkts);
}
void CSndBuffer::increase()
{
int unitsize = m_pBuffer->m_iSize;
// new physical buffer
Buffer* nbuf = NULL;
try
{
nbuf = new Buffer;
nbuf->m_pcData = new char[unitsize * m_iBlockLen];
}
catch (...)
{
delete nbuf;
throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
}
nbuf->m_iSize = unitsize;
nbuf->m_pNext = NULL;
// insert the buffer at the end of the buffer list
Buffer* p = m_pBuffer;
while (p->m_pNext != NULL)
p = p->m_pNext;
p->m_pNext = nbuf;
// new packet blocks
Block* nblk = NULL;
try
{
nblk = new Block;
}
catch (...)
{
delete nblk;
throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
}
Block* pb = nblk;
for (int i = 1; i < unitsize; ++i)
{
pb->m_pNext = new Block;
pb = pb->m_pNext;
}
// insert the new blocks onto the existing one
pb->m_pNext = m_pLastBlock->m_pNext;
m_pLastBlock->m_pNext = nblk;
pb = nblk;
char* pc = nbuf->m_pcData;
for (int i = 0; i < unitsize; ++i)
{
pb->m_pcData = pc;
pb = pb->m_pNext;
pc += m_iBlockLen;
}
m_iSize += unitsize;
HLOGC(bslog.Debug,
log << "CSndBuffer: BUFFER FULL - adding " << (unitsize * m_iBlockLen) << " bytes spread to " << unitsize
<< " blocks"
<< " (total size: " << m_iSize << " bytes)");
}
} // namespace srt

@ -0,0 +1,259 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 05/05/2009
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_BUFFER_SND_H
#define INC_SRT_BUFFER_SND_H
#include "srt.h"
#include "packet.h"
#include "buffer_tools.h"
// The notation used for "circular numbers" in comments:
// The "cicrular numbers" are numbers that when increased up to the
// maximum become zero, and similarly, when the zero value is decreased,
// it turns into the maximum value minus one. This wrapping works the
// same for adding and subtracting. Circular numbers cannot be multiplied.
// Operations done on these numbers are marked with additional % character:
// a %> b : a is later than b
// a ++% (++%a) : shift a by 1 forward
// a +% b : shift a by b
// a == b : equality is same as for just numbers
namespace srt {
class CSndBuffer
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
public:
// XXX There's currently no way to access the socket ID set for
// whatever the buffer is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
/// @brief CSndBuffer constructor.
/// @param size initial number of blocks (each block to store one packet payload).
/// @param maxpld maximum packet payload (including auth tag).
/// @param authtag auth tag length in bytes (16 for GCM, 0 otherwise).
CSndBuffer(int size = 32, int maxpld = 1500, int authtag = 0);
~CSndBuffer();
public:
/// Insert a user buffer into the sending list.
/// For @a w_mctrl the following fields are used:
/// INPUT:
/// - msgttl: timeout for retransmitting the message, if lost
/// - inorder: request to deliver the message in order of sending
/// - srctime: local time as a base for packet's timestamp (0 if unused)
/// - pktseq: sequence number to be stamped on the packet (-1 if unused)
/// - msgno: message number to be stamped on the packet (-1 if unused)
/// OUTPUT:
/// - srctime: local time stamped on the packet (same as input, if input wasn't 0)
/// - pktseq: sequence number to be stamped on the next packet
/// - msgno: message number stamped on the packet
/// @param [in] data pointer to the user data block.
/// @param [in] len size of the block.
/// @param [inout] w_mctrl Message control data
SRT_ATTR_EXCLUDES(m_BufLock)
void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl);
/// Read a block of data from file and insert it into the sending list.
/// @param [in] ifs input file stream.
/// @param [in] len size of the block.
/// @return actual size of data added from the file.
SRT_ATTR_EXCLUDES(m_BufLock)
int addBufferFromFile(std::fstream& ifs, int len);
/// Find data position to pack a DATA packet from the furthest reading point.
/// @param [out] packet the packet to read.
/// @param [out] origintime origin time stamp of the message
/// @param [in] kflags Odd|Even crypto key flag
/// @param [out] seqnoinc the number of packets skipped due to TTL, so that seqno should be incremented.
/// @return Actual length of data read.
SRT_ATTR_EXCLUDES(m_BufLock)
int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc);
/// Peek an information on the next original data packet to send.
/// @return origin time stamp of the next packet; epoch start time otherwise.
SRT_ATTR_EXCLUDES(m_BufLock)
time_point peekNextOriginal() const;
/// Find data position to pack a DATA packet for a retransmission.
/// @param [in] offset offset from the last ACK point (backward sequence number difference)
/// @param [out] packet the packet to read.
/// @param [out] origintime origin time stamp of the message
/// @param [out] msglen length of the message
/// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded).
SRT_ATTR_EXCLUDES(m_BufLock)
int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen);
/// Get the time of the last retransmission (if any) of the DATA packet.
/// @param [in] offset offset from the last ACK point (backward sequence number difference)
///
/// @return Last time of the last retransmission event for the corresponding DATA packet.
SRT_ATTR_EXCLUDES(m_BufLock)
time_point getPacketRexmitTime(const int offset);
/// Update the ACK point and may release/unmap/return the user data according to the flag.
/// @param [in] offset number of packets acknowledged.
int32_t getMsgNoAt(const int offset);
void ackData(int offset);
/// Read size of data still in the sending list.
/// @return Current size of the data in the sending list.
int getCurrBufSize() const;
SRT_ATTR_EXCLUDES(m_BufLock)
int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time);
void updAvgBufSize(const time_point& time);
int getAvgBufSize(int& bytes, int& timespan);
int getCurrBufSize(int& bytes, int& timespan) const;
/// Het maximum payload length per packet.
int getMaxPacketLen() const;
/// @brief Count the number of required packets to store the payload (message).
/// @param iPldLen the length of the payload to check.
/// @return the number of required data packets.
int countNumPacketsRequired(int iPldLen) const;
/// @brief Count the number of required packets to store the payload (message).
/// @param iPldLen the length of the payload to check.
/// @param iMaxPktLen the maximum payload length of the packet (the value returned from getMaxPacketLen()).
/// @return the number of required data packets.
int countNumPacketsRequired(int iPldLen, int iMaxPktLen) const;
/// @brief Get the buffering delay of the oldest message in the buffer.
/// @return the delay value.
SRT_ATTR_EXCLUDES(m_BufLock)
duration getBufferingDelay(const time_point& tnow) const;
uint64_t getInRatePeriod() const { return m_rateEstimator.getInRatePeriod(); }
/// Retrieve input bitrate in bytes per second
int getInputRate() const { return m_rateEstimator.getInputRate(); }
void resetInputRateSmpPeriod(bool disable = false) { m_rateEstimator.resetInputRateSmpPeriod(disable); }
const CRateEstimator& getRateEstimator() const { return m_rateEstimator; }
void setRateEstimator(const CRateEstimator& other) { m_rateEstimator = other; }
private:
void increase();
private:
mutable sync::Mutex m_BufLock; // used to synchronize buffer operation
struct Block
{
char* m_pcData; // pointer to the data block
int m_iLength; // payload length of the block (excluding auth tag).
int32_t m_iMsgNoBitset; // message number
int32_t m_iSeqNo; // sequence number for scheduling
time_point m_tsOriginTime; // block origin time (either provided from above or equals the time a message was submitted for sending.
time_point m_tsRexmitTime; // packet retransmission time
int m_iTTL; // time to live (milliseconds)
Block* m_pNext; // next block
int32_t getMsgSeq()
{
// NOTE: this extracts message ID with regard to REXMIT flag.
// This is valid only for message ID that IS GENERATED in this instance,
// not provided by the peer. This can be otherwise sent to the peer - it doesn't matter
// for the peer that it uses LESS bits to represent the message.
return m_iMsgNoBitset & MSGNO_SEQ::mask;
}
} * m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock;
// m_pBlock: The head pointer
// m_pFirstBlock: The first block
// m_pCurrBlock: The current block
// m_pLastBlock: The last block (if first == last, buffer is empty)
struct Buffer
{
char* m_pcData; // buffer
int m_iSize; // size
Buffer* m_pNext; // next buffer
} * m_pBuffer; // physical buffer
int32_t m_iNextMsgNo; // next message number
int m_iSize; // buffer size (number of packets)
const int m_iBlockLen; // maximum length of a block holding packet payload and AUTH tag (excluding packet header).
const int m_iAuthTagSize; // Authentication tag size (if GCM is enabled).
int m_iCount; // number of used blocks
int m_iBytesCount; // number of payload bytes in queue
time_point m_tsLastOriginTime;
AvgBufSize m_mavg;
CRateEstimator m_rateEstimator;
private:
CSndBuffer(const CSndBuffer&);
CSndBuffer& operator=(const CSndBuffer&);
};
} // namespace srt
#endif

@ -0,0 +1,275 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 03/12/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include "buffer_tools.h"
#include "packet.h"
#include "logger_defs.h"
#include "utilities.h"
namespace srt {
using namespace std;
using namespace srt_logging;
using namespace sync;
// You can change this value at build config by using "ENFORCE" options.
#if !defined(SRT_MAVG_SAMPLING_RATE)
#define SRT_MAVG_SAMPLING_RATE 40
#endif
bool AvgBufSize::isTimeToUpdate(const time_point& now) const
{
const int usMAvgBasePeriod = 1000000; // 1s in microseconds
const int us2ms = 1000;
const int msMAvgPeriod = (usMAvgBasePeriod / SRT_MAVG_SAMPLING_RATE) / us2ms;
const uint64_t elapsed_ms = count_milliseconds(now - m_tsLastSamplingTime); // ms since last sampling
return (elapsed_ms >= msMAvgPeriod);
}
void AvgBufSize::update(const steady_clock::time_point& now, int pkts, int bytes, int timespan_ms)
{
const uint64_t elapsed_ms = count_milliseconds(now - m_tsLastSamplingTime); // ms since last sampling
m_tsLastSamplingTime = now;
const uint64_t one_second_in_ms = 1000;
if (elapsed_ms > one_second_in_ms)
{
// No sampling in last 1 sec, initialize average
m_dCountMAvg = pkts;
m_dBytesCountMAvg = bytes;
m_dTimespanMAvg = timespan_ms;
return;
}
//
// weight last average value between -1 sec and last sampling time (LST)
// and new value between last sampling time and now
// |elapsed_ms|
// +----------------------------------+-------+
// -1 LST 0(now)
//
m_dCountMAvg = avg_iir_w<1000, double>(m_dCountMAvg, pkts, elapsed_ms);
m_dBytesCountMAvg = avg_iir_w<1000, double>(m_dBytesCountMAvg, bytes, elapsed_ms);
m_dTimespanMAvg = avg_iir_w<1000, double>(m_dTimespanMAvg, timespan_ms, elapsed_ms);
}
CRateEstimator::CRateEstimator()
: m_iInRatePktsCount(0)
, m_iInRateBytesCount(0)
, m_InRatePeriod(INPUTRATE_FAST_START_US) // 0.5 sec (fast start)
, m_iInRateBps(INPUTRATE_INITIAL_BYTESPS)
{}
void CRateEstimator::setInputRateSmpPeriod(int period)
{
m_InRatePeriod = (uint64_t)period; //(usec) 0=no input rate calculation
}
void CRateEstimator::updateInputRate(const time_point& time, int pkts, int bytes)
{
// no input rate calculation
if (m_InRatePeriod == 0)
return;
if (is_zero(m_tsInRateStartTime))
{
m_tsInRateStartTime = time;
return;
}
else if (time < m_tsInRateStartTime)
{
// Old packets are being submitted for estimation, e.g. during the backup link activation.
return;
}
m_iInRatePktsCount += pkts;
m_iInRateBytesCount += bytes;
// Trigger early update in fast start mode
const bool early_update = (m_InRatePeriod < INPUTRATE_RUNNING_US) && (m_iInRatePktsCount > INPUTRATE_MAX_PACKETS);
const uint64_t period_us = count_microseconds(time - m_tsInRateStartTime);
if (!early_update && period_us <= m_InRatePeriod)
return;
// Required Byte/sec rate (payload + headers)
m_iInRateBytesCount += (m_iInRatePktsCount * CPacket::SRT_DATA_HDR_SIZE);
m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us);
HLOGC(bslog.Debug,
log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount
<< " rate=" << (m_iInRateBps * 8) / 1000 << "kbps interval=" << period_us);
m_iInRatePktsCount = 0;
m_iInRateBytesCount = 0;
m_tsInRateStartTime = time;
setInputRateSmpPeriod(INPUTRATE_RUNNING_US);
}
CSndRateEstimator::CSndRateEstimator(const time_point& tsNow)
: m_tsFirstSampleTime(tsNow)
, m_iFirstSampleIdx(0)
, m_iCurSampleIdx(0)
, m_iRateBps(0)
{
}
void CSndRateEstimator::addSample(const time_point& ts, int pkts, size_t bytes)
{
const int iSampleDeltaIdx = (int) count_milliseconds(ts - m_tsFirstSampleTime) / SAMPLE_DURATION_MS;
const int delta = NUM_PERIODS - iSampleDeltaIdx;
// TODO: -delta <= NUM_PERIODS, then just reset the state on the estimator.
if (iSampleDeltaIdx >= 2 * NUM_PERIODS)
{
// Just reset the estimator and start like if new.
for (int i = 0; i < NUM_PERIODS; ++i)
{
const int idx = incSampleIdx(m_iFirstSampleIdx, i);
m_Samples[idx].reset();
if (idx == m_iCurSampleIdx)
break;
}
m_iFirstSampleIdx = 0;
m_iCurSampleIdx = 0;
m_iRateBps = 0;
m_tsFirstSampleTime += milliseconds_from(iSampleDeltaIdx * SAMPLE_DURATION_MS);
}
else if (iSampleDeltaIdx > NUM_PERIODS)
{
// In run-time a constant flow of samples is expected. Once all periods are filled (after 1 second of sampling),
// the iSampleDeltaIdx should be either (NUM_PERIODS - 1),
// or NUM_PERIODS. In the later case it means the start of a new sampling period.
int d = delta;
while (d < 0)
{
m_Samples[m_iFirstSampleIdx].reset();
m_iFirstSampleIdx = incSampleIdx(m_iFirstSampleIdx);
m_tsFirstSampleTime += milliseconds_from(SAMPLE_DURATION_MS);
m_iCurSampleIdx = incSampleIdx(m_iCurSampleIdx);
++d;
}
}
// Check if the new sample period has started.
const int iNewDeltaIdx = (int) count_milliseconds(ts - m_tsFirstSampleTime) / SAMPLE_DURATION_MS;
if (incSampleIdx(m_iFirstSampleIdx, iNewDeltaIdx) != m_iCurSampleIdx)
{
// Now there should be some periods (at most last NUM_PERIODS) ready to be summed,
// rate estimation updated, after which all the new entry should be added.
Sample sum;
int iNumPeriods = 0;
bool bMetNonEmpty = false;
for (int i = 0; i < NUM_PERIODS; ++i)
{
const int idx = incSampleIdx(m_iFirstSampleIdx, i);
const Sample& s = m_Samples[idx];
sum += s;
if (bMetNonEmpty || !s.empty())
{
++iNumPeriods;
bMetNonEmpty = true;
}
if (idx == m_iCurSampleIdx)
break;
}
if (iNumPeriods == 0)
{
m_iRateBps = 0;
}
else
{
m_iRateBps = sum.m_iBytesCount * 1000 / (iNumPeriods * SAMPLE_DURATION_MS);
}
HLOGC(bslog.Note,
log << "CSndRateEstimator: new rate estimation :" << (m_iRateBps * 8) / 1000 << " kbps. Based on "
<< iNumPeriods << " periods, " << sum.m_iPktsCount << " packets, " << sum.m_iBytesCount << " bytes.");
// Shift one sampling period to start collecting the new one.
m_iCurSampleIdx = incSampleIdx(m_iCurSampleIdx);
m_Samples[m_iCurSampleIdx].reset();
// If all NUM_SAMPLES are recorded, the first position has to be shifted as well.
if (delta <= 0)
{
m_iFirstSampleIdx = incSampleIdx(m_iFirstSampleIdx);
m_tsFirstSampleTime += milliseconds_from(SAMPLE_DURATION_MS);
}
}
m_Samples[m_iCurSampleIdx].m_iBytesCount += bytes;
m_Samples[m_iCurSampleIdx].m_iPktsCount += pkts;
}
int CSndRateEstimator::getCurrentRate() const
{
SRT_ASSERT(m_iCurSampleIdx >= 0 && m_iCurSampleIdx < NUM_PERIODS);
return (int) avg_iir<16, unsigned long long>(m_iRateBps, m_Samples[m_iCurSampleIdx].m_iBytesCount * 1000 / SAMPLE_DURATION_MS);
}
int CSndRateEstimator::incSampleIdx(int val, int inc) const
{
SRT_ASSERT(inc >= 0 && inc <= NUM_PERIODS);
val += inc;
while (val >= NUM_PERIODS)
val -= NUM_PERIODS;
return val;
}
}

@ -0,0 +1,201 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 05/05/2009
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_BUFFER_TOOLS_H
#define INC_SRT_BUFFER_TOOLS_H
#include "common.h"
namespace srt
{
/// The AvgBufSize class is used to calculate moving average of the buffer (RCV or SND)
class AvgBufSize
{
typedef sync::steady_clock::time_point time_point;
public:
AvgBufSize()
: m_dBytesCountMAvg(0.0)
, m_dCountMAvg(0.0)
, m_dTimespanMAvg(0.0)
{
}
public:
bool isTimeToUpdate(const time_point& now) const;
void update(const time_point& now, int pkts, int bytes, int timespan_ms);
public:
inline double pkts() const { return m_dCountMAvg; }
inline double timespan_ms() const { return m_dTimespanMAvg; }
inline double bytes() const { return m_dBytesCountMAvg; }
private:
time_point m_tsLastSamplingTime;
double m_dBytesCountMAvg;
double m_dCountMAvg;
double m_dTimespanMAvg;
};
/// The class to estimate source bitrate based on samples submitted to the buffer.
/// Is currently only used by the CSndBuffer.
class CRateEstimator
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
public:
CRateEstimator();
public:
uint64_t getInRatePeriod() const { return m_InRatePeriod; }
/// Retrieve input bitrate in bytes per second
int getInputRate() const { return m_iInRateBps; }
void setInputRateSmpPeriod(int period);
/// Update input rate calculation.
/// @param [in] time current time
/// @param [in] pkts number of packets newly added to the buffer
/// @param [in] bytes number of payload bytes in those newly added packets
void updateInputRate(const time_point& time, int pkts = 0, int bytes = 0);
void resetInputRateSmpPeriod(bool disable = false) { setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US); }
private: // Constants
static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms
static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms
static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload
static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE;
private:
int m_iInRatePktsCount; // number of payload packets added since InRateStartTime.
int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime.
time_point m_tsInRateStartTime;
uint64_t m_InRatePeriod; // usec
int m_iInRateBps; // Input Rate in Bytes/sec
};
class CSndRateEstimator
{
typedef sync::steady_clock::time_point time_point;
public:
CSndRateEstimator(const time_point& tsNow);
/// Add sample.
/// @param [in] time sample (sending) time.
/// @param [in] pkts number of packets in the sample.
/// @param [in] bytes number of payload bytes in the sample.
void addSample(const time_point& time, int pkts = 0, size_t bytes = 0);
/// Retrieve estimated bitrate in bytes per second
int getRate() const { return m_iRateBps; }
/// Retrieve estimated bitrate in bytes per second inluding the current sampling interval.
int getCurrentRate() const;
private:
static const int NUM_PERIODS = 10;
static const int SAMPLE_DURATION_MS = 100; // 100 ms
struct Sample
{
int m_iPktsCount; // number of payload packets
int m_iBytesCount; // number of payload bytes
void reset()
{
m_iPktsCount = 0;
m_iBytesCount = 0;
}
Sample()
: m_iPktsCount(0)
, m_iBytesCount(0)
{
}
Sample(int iPkts, int iBytes)
: m_iPktsCount(iPkts)
, m_iBytesCount(iBytes)
{
}
Sample operator+(const Sample& other)
{
return Sample(m_iPktsCount + other.m_iPktsCount, m_iBytesCount + other.m_iBytesCount);
}
Sample& operator+=(const Sample& other)
{
*this = *this + other;
return *this;
}
bool empty() const { return m_iPktsCount == 0; }
};
int incSampleIdx(int val, int inc = 1) const;
Sample m_Samples[NUM_PERIODS];
time_point m_tsFirstSampleTime; //< Start time of the first sameple.
int m_iFirstSampleIdx; //< Index of the first sample.
int m_iCurSampleIdx; //< Index of the current sample being collected.
int m_iRateBps; // Input Rate in Bytes/sec
};
} // namespace srt
#endif

@ -62,7 +62,7 @@ srt::CInfoBlock& srt::CInfoBlock::copyFrom(const CInfoBlock& obj)
return *this;
}
bool srt::CInfoBlock::operator==(const CInfoBlock& obj)
bool srt::CInfoBlock::operator==(const CInfoBlock& obj) const
{
if (m_iIPversion != obj.m_iIPversion)
return false;

@ -253,7 +253,7 @@ public:
CInfoBlock& copyFrom(const CInfoBlock& obj);
CInfoBlock(const CInfoBlock& src) { copyFrom(src); }
CInfoBlock& operator=(const CInfoBlock& src) { return copyFrom(src); }
bool operator==(const CInfoBlock& obj);
bool operator==(const CInfoBlock& obj) const;
CInfoBlock* clone();
int getKey();
void release() {}

@ -139,7 +139,23 @@ static int set_cloexec(int fd, int set)
srt::CChannel::CChannel()
: m_iSocket(INVALID_SOCKET)
#ifdef SRT_ENABLE_PKTINFO
, m_bBindMasked(true)
#endif
{
#ifdef SRT_ENABLE_PKTINFO
// Do the check for ancillary data buffer size, kinda assertion
static const size_t CMSG_MAX_SPACE = sizeof (CMSGNodeIPv4) + sizeof (CMSGNodeIPv6);
if (CMSG_MAX_SPACE < CMSG_SPACE(sizeof(in_pktinfo)) + CMSG_SPACE(sizeof(in6_pktinfo)))
{
LOGC(kmlog.Fatal, log << "Size of CMSG_MAX_SPACE="
<< CMSG_MAX_SPACE << " too short for cmsg "
<< CMSG_SPACE(sizeof(in_pktinfo)) << ", "
<< CMSG_SPACE(sizeof(in6_pktinfo)) << " - PLEASE FIX");
throw CUDTException(MJ_SETUP, MN_NONE, 0);
}
#endif
}
srt::CChannel::~CChannel() {}
@ -207,6 +223,9 @@ void srt::CChannel::open(const sockaddr_any& addr)
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
m_BindAddr = addr;
#ifdef SRT_ENABLE_PKTINFO
m_bBindMasked = m_BindAddr.isany();
#endif
LOGC(kmlog.Debug, log << "CHANNEL: Bound to local address: " << m_BindAddr.str());
setUDPSockOpt();
@ -247,6 +266,12 @@ void srt::CChannel::open(int family)
}
m_BindAddr = sockaddr_any(res->ai_addr, (sockaddr_any::len_t)res->ai_addrlen);
#ifdef SRT_ENABLE_PKTINFO
// We know that this is intentionally bound now to "any",
// so the requester-destination address must be remembered and passed.
m_bBindMasked = true;
#endif
::freeaddrinfo(res);
HLOGC(kmlog.Debug, log << "CHANNEL: Bound to local address: " << m_BindAddr.str());
@ -472,6 +497,27 @@ void srt::CChannel::setUDPSockOpt()
if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(timeval)))
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
#endif
#ifdef SRT_ENABLE_PKTINFO
if (m_bBindMasked)
{
HLOGP(kmlog.Debug, "Socket bound to ANY - setting PKTINFO for address retrieval");
const int on = 1, off SRT_ATR_UNUSED = 0;
if (m_BindAddr.family() == AF_INET || m_mcfg.iIpV6Only == 0)
{
::setsockopt(m_iSocket, IPPROTO_IP, IP_PKTINFO, (char*)&on, sizeof(on));
}
if (m_BindAddr.family() == AF_INET6)
{
::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
}
// XXX Unknown why this has to be off. RETEST.
//::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
}
#endif
}
void srt::CChannel::close() const
@ -502,6 +548,11 @@ void srt::CChannel::setConfig(const CSrtMuxerConfig& config)
m_mcfg = config;
}
void srt::CChannel::getSocketOption(int level, int option, char* pw_dataptr, socklen_t& w_len, int& w_status)
{
w_status = ::getsockopt(m_iSocket, level, option, (pw_dataptr), (&w_len));
}
int srt::CChannel::getIpTTL() const
{
if (m_iSocket == INVALID_SOCKET)
@ -610,11 +661,19 @@ void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const
w_addr.len = namelen;
}
int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const
int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const sockaddr_any& source_addr SRT_ATR_UNUSED) const
{
HLOGC(kslog.Debug,
log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID
<< " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp << " " << packet.Info());
#if ENABLE_HEAVY_LOGGING
ostringstream dsrc;
#ifdef SRT_ENABLE_PKTINFO
dsrc << " sourceIP=" << (m_bBindMasked && !source_addr.isany() ? source_addr.str() : "default");
#endif
LOGC(kslog.Debug,
log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID
<< " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp
<< dsrc.str() << " " << packet.Info());
#endif
#ifdef SRT_TEST_FAKE_LOSS
@ -683,16 +742,51 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const
mh.msg_namelen = addr.size();
mh.msg_iov = (iovec*)packet.m_PacketVector;
mh.msg_iovlen = 2;
mh.msg_control = NULL;
mh.msg_controllen = 0;
bool have_set_src = false;
#ifdef SRT_ENABLE_PKTINFO
// Note that even if PKTINFO is desired, the first caller's packet will be sent
// without ancillary info anyway because there's no "peer" yet to know where to send it.
if (m_bBindMasked && source_addr.family() != AF_UNSPEC && !source_addr.isany())
{
if (!setSourceAddress(mh, source_addr))
{
LOGC(kslog.Error, log << "CChannel::setSourceAddress: source address invalid family #" << source_addr.family() << ", NOT setting.");
}
else
{
HLOGC(kslog.Debug, log << "CChannel::setSourceAddress: setting as " << source_addr.str());
have_set_src = true;
}
}
#endif
if (!have_set_src)
{
mh.msg_control = NULL;
mh.msg_controllen = 0;
}
mh.msg_flags = 0;
const int res = ::sendmsg(m_iSocket, &mh, 0);
const int res = (int)::sendmsg(m_iSocket, &mh, 0);
#else
DWORD size = (DWORD)(CPacket::HDR_SIZE + packet.getLength());
int addrsize = addr.size();
int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, NULL, NULL);
res = (0 == res) ? size : -1;
WSAOVERLAPPED overlapped;
SecureZeroMemory((PVOID)&overlapped, sizeof(WSAOVERLAPPED));
int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &overlapped, NULL);
if (res == SOCKET_ERROR && NET_ERROR == WSA_IO_PENDING)
{
DWORD dwFlags = 0;
const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &overlapped, &size, true, &dwFlags);
WSACloseEvent(overlapped.hEvent);
res = bCompleted ? 0 : -1;
}
res = (0 == res) ? size : -1;
#endif
packet.toHL();
@ -725,18 +819,36 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet
}
#ifndef _WIN32
msghdr mh; // will not be used on failure
if (select_ret > 0)
{
msghdr mh;
mh.msg_name = (w_addr.get());
mh.msg_namelen = w_addr.size();
mh.msg_iov = (w_packet.m_PacketVector);
mh.msg_iovlen = 2;
// Default
mh.msg_control = NULL;
mh.msg_controllen = 0;
#ifdef SRT_ENABLE_PKTINFO
// Without m_bBindMasked, we don't need ancillary data - the source
// address will always be the bound address.
if (m_bBindMasked)
{
// Extract the destination IP address from the ancillary
// data. This might be interesting for the connection to
// know to which address the packet should be sent back during
// the handshake and then addressed when sending during connection.
mh.msg_control = (m_acCmsgRecvBuffer);
mh.msg_controllen = sizeof m_acCmsgRecvBuffer;
}
#endif
mh.msg_flags = 0;
recv_size = ::recvmsg(m_iSocket, (&mh), 0);
recv_size = (int)::recvmsg(m_iSocket, (&mh), 0);
msg_flags = mh.msg_flags;
}
@ -779,6 +891,17 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet
goto Return_error;
}
#ifdef SRT_ENABLE_PKTINFO
if (m_bBindMasked)
{
// Extract the address. Set it explicitly; if this returns address that isany(),
// it will simply set this on the packet so that it behaves as if nothing was
// extracted (it will "fail the old way").
w_packet.m_DestAddr = getTargetAddress(mh);
HLOGC(krlog.Debug, log << CONID() << "(sys)recvmsg: ANY BOUND, retrieved DEST ADDR: " << w_packet.m_DestAddr.str());
}
#endif
#else
// XXX REFACTORING NEEDED!
// This procedure uses the WSARecvFrom function that just reads
@ -876,9 +999,30 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet
// packet was received, so the packet will be then retransmitted.
if (msg_flags != 0)
{
#if ENABLE_HEAVY_LOGGING
std::ostringstream flg;
#if !defined(_WIN32)
static const pair<int, const char* const> errmsgflg [] = {
make_pair<int>(MSG_OOB, "OOB"),
make_pair<int>(MSG_EOR, "EOR"),
make_pair<int>(MSG_TRUNC, "TRUNC"),
make_pair<int>(MSG_CTRUNC, "CTRUNC")
};
for (size_t i = 0; i < Size(errmsgflg); ++i)
if ((msg_flags & errmsgflg[i].first) != 0)
flg << " " << errmsgflg[i].second;
// This doesn't work the same way on Windows, so on Windows just skip it.
#endif
HLOGC(krlog.Debug,
log << CONID() << "NET ERROR: packet size=" << recv_size << " msg_flags=0x" << hex << msg_flags
<< ", possibly MSG_TRUNC)");
<< ", detected flags:" << flg.str());
#endif
status = RST_AGAIN;
goto Return_error;
}

@ -115,9 +115,10 @@ public:
/// Send a packet to the given address.
/// @param [in] addr pointer to the destination address.
/// @param [in] packet reference to a CPacket entity.
/// @param [in] src source address to sent on an outgoing packet (if not ANY)
/// @return Actual size of data sent.
int sendto(const sockaddr_any& addr, srt::CPacket& packet) const;
int sendto(const sockaddr_any& addr, srt::CPacket& packet, const sockaddr_any& src) const;
/// Receive a packet from the channel and record the source address.
/// @param [in] addr pointer to the source address.
@ -128,6 +129,21 @@ public:
void setConfig(const CSrtMuxerConfig& config);
void getSocketOption(int level, int sockoptname, char* pw_dataptr, socklen_t& w_len, int& w_status);
template<class Type>
Type sockopt(int level, int sockoptname, Type deflt)
{
Type retval;
socklen_t socklen = sizeof retval;
int status;
getSocketOption(level, sockoptname, ((char*)&retval), (socklen), (status));
if (status == -1)
return deflt;
return retval;
}
/// Get the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return TTL.
@ -160,6 +176,121 @@ private:
// although the object itself isn't considered modified.
mutable CSrtMuxerConfig m_mcfg; // Note: ReuseAddr is unused and ineffective.
sockaddr_any m_BindAddr;
// This feature is not enabled on Windows, for now.
// This is also turned off in case of MinGW
#ifdef SRT_ENABLE_PKTINFO
bool m_bBindMasked; // True if m_BindAddr is INADDR_ANY. Need for quick check.
// Calculating the required space is extremely tricky, and whereas on most
// platforms it's possible to define it this way:
//
// size_t s = max( CMSG_SPACE(sizeof(in_pktinfo)), CMSG_SPACE(sizeof(in6_pktinfo)) )
//
// ...on some platforms however CMSG_SPACE macro can't be resolved as constexpr.
//
// This structure is exclusively used to determine the required size for
// CMSG buffer so that it can be allocated in a solid block with CChannel.
// NOT TO BE USED to access any data inside the CMSG message.
struct CMSGNodeIPv4
{
in_pktinfo in4;
size_t extrafill;
cmsghdr hdr;
};
struct CMSGNodeIPv6
{
in6_pktinfo in6;
size_t extrafill;
cmsghdr hdr;
};
// This is 'mutable' because it's a utility buffer defined here
// to avoid unnecessary re-allocations.
mutable char m_acCmsgRecvBuffer [sizeof (CMSGNodeIPv4) + sizeof (CMSGNodeIPv6)]; // Reserved space for ancillary data with pktinfo
mutable char m_acCmsgSendBuffer [sizeof (CMSGNodeIPv4) + sizeof (CMSGNodeIPv6)]; // Reserved space for ancillary data with pktinfo
// IMPORTANT!!! This function shall be called EXCLUSIVELY just after
// calling ::recvmsg function. It uses a static buffer to supply data
// for the call, and it's stated that only one thread is trying to
// use a CChannel object in receiving mode.
sockaddr_any getTargetAddress(const msghdr& msg) const
{
// Loop through IP header messages
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR(((msghdr*)&msg), cmsg))
{
// This should be safe - this packet contains always either
// IPv4 headers or IPv6 headers.
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
{
in_pktinfo *dest_ip_ptr = (in_pktinfo*)CMSG_DATA(cmsg);
return sockaddr_any(dest_ip_ptr->ipi_addr, 0);
}
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO)
{
in6_pktinfo* dest_ip_ptr = (in6_pktinfo*)CMSG_DATA(cmsg);
return sockaddr_any(dest_ip_ptr->ipi6_addr, 0);
}
}
// Fallback for an error
return sockaddr_any(m_BindAddr.family());
}
// IMPORTANT!!! This function shall be called EXCLUSIVELY just before
// calling ::sendmsg function. It uses a static buffer to supply data
// for the call, and it's stated that only one thread is trying to
// use a CChannel object in sending mode.
bool setSourceAddress(msghdr& mh, const sockaddr_any& adr) const
{
// In contrast to an advice followed on the net, there's no case of putting
// both IPv4 and IPv6 ancillary data, case we could have them. Only one
// IP version is used and it's the version as found in @a adr, which should
// be the version used for binding.
if (adr.family() == AF_INET)
{
mh.msg_control = m_acCmsgSendBuffer;
mh.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo));
cmsghdr* cmsg_send = CMSG_FIRSTHDR(&mh);
// after initializing msghdr & control data to CMSG_SPACE(sizeof(struct in_pktinfo))
cmsg_send->cmsg_level = IPPROTO_IP;
cmsg_send->cmsg_type = IP_PKTINFO;
cmsg_send->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
in_pktinfo* pktinfo = (in_pktinfo*) CMSG_DATA(cmsg_send);
pktinfo->ipi_ifindex = 0;
pktinfo->ipi_spec_dst = adr.sin.sin_addr;
return true;
}
if (adr.family() == AF_INET6)
{
mh.msg_control = m_acCmsgSendBuffer;
mh.msg_controllen = CMSG_SPACE(sizeof(in6_pktinfo));
cmsghdr* cmsg_send = CMSG_FIRSTHDR(&mh);
cmsg_send->cmsg_level = IPPROTO_IPV6;
cmsg_send->cmsg_type = IPV6_PKTINFO;
cmsg_send->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
in6_pktinfo* pktinfo = (in6_pktinfo*) CMSG_DATA(cmsg_send);
pktinfo->ipi6_ifindex = 0;
pktinfo->ipi6_addr = adr.sin6.sin6_addr;
return true;
}
return false;
}
#endif // SRT_ENABLE_PKTINFO
};
} // namespace srt

@ -168,11 +168,7 @@ void srt::CIPAddress::ntop(const sockaddr_any& addr, uint32_t ip[4])
}
else
{
const sockaddr_in6* a = &addr.sin6;
ip[3] = (a->sin6_addr.s6_addr[15] << 24) + (a->sin6_addr.s6_addr[14] << 16) + (a->sin6_addr.s6_addr[13] << 8) + a->sin6_addr.s6_addr[12];
ip[2] = (a->sin6_addr.s6_addr[11] << 24) + (a->sin6_addr.s6_addr[10] << 16) + (a->sin6_addr.s6_addr[9] << 8) + a->sin6_addr.s6_addr[8];
ip[1] = (a->sin6_addr.s6_addr[7] << 24) + (a->sin6_addr.s6_addr[6] << 16) + (a->sin6_addr.s6_addr[5] << 8) + a->sin6_addr.s6_addr[4];
ip[0] = (a->sin6_addr.s6_addr[3] << 24) + (a->sin6_addr.s6_addr[2] << 16) + (a->sin6_addr.s6_addr[1] << 8) + a->sin6_addr.s6_addr[0];
std::memcpy(ip, addr.sin6.sin6_addr.s6_addr, 16);
}
}
@ -223,18 +219,7 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc
// Here both agent and peer use IPv6, in which case
// `ip` contains the full IPv6 address, so just copy
// it as is.
// XXX Possibly, a simple
// memcpy( (a->sin6_addr.s6_addr), ip, 16);
// would do the same thing, and faster. The address in `ip`,
// even though coded here as uint32_t, is still big endian.
for (int i = 0; i < 4; ++ i)
{
a->sin6_addr.s6_addr[i * 4 + 0] = ip[i] & 0xFF;
a->sin6_addr.s6_addr[i * 4 + 1] = (unsigned char)((ip[i] & 0xFF00) >> 8);
a->sin6_addr.s6_addr[i * 4 + 2] = (unsigned char)((ip[i] & 0xFF0000) >> 16);
a->sin6_addr.s6_addr[i * 4 + 3] = (unsigned char)((ip[i] & 0xFF000000) >> 24);
}
std::memcpy(a->sin6_addr.s6_addr, ip, 16);
return; // The address is written, nothing left to do.
}
@ -457,7 +442,7 @@ std::string TransmissionEventStr(ETransmissionEvent ev)
return vals[ev];
}
bool SrtParseConfig(string s, SrtConfig& w_config)
bool SrtParseConfig(const string& s, SrtConfig& w_config)
{
using namespace std;

@ -53,7 +53,6 @@ modified by
#ifndef INC_SRT_COMMON_H
#define INC_SRT_COMMON_H
#define _CRT_SECURE_NO_WARNINGS 1 // silences windows complaints for sscanf
#include <memory>
#include <cstdlib>
#include <cstdio>
@ -1393,8 +1392,11 @@ inline ATR_CONSTEXPR uint32_t SrtVersion(int major, int minor, int patch)
inline int32_t SrtParseVersion(const char* v)
{
int major, minor, patch;
#if defined(_MSC_VER)
int result = sscanf_s(v, "%d.%d.%d", &major, &minor, &patch);
#else
int result = sscanf(v, "%d.%d.%d", &major, &minor, &patch);
#endif
if (result != 3)
{
return 0;
@ -1409,12 +1411,16 @@ inline std::string SrtVersionString(int version)
int minor = (version/0x100)%0x100;
int major = version/0x10000;
char buf[20];
sprintf(buf, "%d.%d.%d", major, minor, patch);
char buf[22];
#if defined(_MSC_VER) && _MSC_VER < 1900
_snprintf(buf, sizeof(buf) - 1, "%d.%d.%d", major, minor, patch);
#else
snprintf(buf, sizeof(buf), "%d.%d.%d", major, minor, patch);
#endif
return buf;
}
bool SrtParseConfig(std::string s, SrtConfig& w_config);
bool SrtParseConfig(const std::string& s, SrtConfig& w_config);
} // namespace srt

@ -230,7 +230,7 @@ private:
* For realtime Transport Stream content, pkts/sec is not a good indication of time to transmit
* since packets are not filled to m_iMSS and packet size average is lower than (7*188)
* for low bit rates.
* If NAK report is lost, another cycle (RTT) is requred which is bad for low latency so we
* If NAK report is lost, another cycle (RTT) is required which is bad for low latency so we
* accelerate the NAK Reports frequency, at the cost of possible duplicate resend.
* Finally, the UDT4 native minimum NAK interval (m_ullMinNakInt_tk) is 300 ms which is too high
* (~10 i30 video frames) to maintain low latency.

@ -52,8 +52,8 @@ public:
struct IsName
{
std::string n;
IsName(std::string nn): n(nn) {}
const std::string n;
IsName(const std::string& nn): n(nn) {}
bool operator()(NamePtr np) { return n == np.first; }
};

File diff suppressed because it is too large Load Diff

@ -59,7 +59,7 @@ modified by
#include "srt.h"
#include "common.h"
#include "list.h"
#include "buffer.h"
#include "buffer_snd.h"
#include "buffer_rcv.h"
#include "window.h"
#include "packet.h"
@ -288,7 +288,7 @@ public: // internal API
{
#if ENABLE_LOGGING
std::ostringstream os;
os << "@" << m_SocketID << ":";
os << "@" << m_SocketID << ": ";
return os.str();
#else
return "";
@ -312,8 +312,10 @@ public: // internal API
int32_t schedSeqNo() const { return m_iSndNextSeqNo; }
bool overrideSndSeqNo(int32_t seq);
#if ENABLE_BONDING
sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime.load(); }
sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; }
#endif
int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; }
int flowWindowSize() const { return m_iFlowWindowSize; }
@ -371,27 +373,34 @@ public: // internal API
int minSndSize(int len = 0) const
{
const int ps = (int) maxPayloadSize();
if (len == 0) // wierd, can't use non-static data member as default argument!
if (len == 0) // weird, can't use non-static data member as default argument!
len = ps;
return m_config.bMessageAPI ? (len+ps-1)/ps : 1;
}
int32_t makeTS(const time_point& from_time) const
static int32_t makeTS(const time_point& from_time, const time_point& tsStartTime)
{
// NOTE:
// - This calculates first the time difference towards start time.
// - This difference value is also CUT OFF THE SEGMENT information
// (a multiple of MAX_TIMESTAMP+1)
// So, this can be simply defined as: TS = (RTS - STS) % (MAX_TIMESTAMP+1)
// XXX Would be nice to check if local_time > m_tsStartTime,
// otherwise it may go unnoticed with clock skew.
return (int32_t) sync::count_microseconds(from_time - m_stats.tsStartTime);
SRT_ASSERT(from_time >= tsStartTime);
return (int32_t) sync::count_microseconds(from_time - tsStartTime);
}
void setPacketTS(CPacket& p, const time_point& local_time)
{
p.m_iTimeStamp = makeTS(local_time);
}
/// @brief Set the timestamp field of the packet using the provided value (no check)
/// @param p the packet structure to set the timestamp on.
/// @param ts timestamp to use as a source for packet timestamp.
SRT_ATTR_EXCLUDES(m_StatsLock)
void setPacketTS(CPacket& p, const time_point& ts);
/// @brief Set the timestamp field of the packet according the TSBPD mode.
/// Also checks the connection start time (m_tsStartTime).
/// @param p the packet structure to set the timestamp on.
/// @param ts timestamp to use as a source for packet timestamp. Ignored if m_bPeerTsbPd is false.
SRT_ATTR_EXCLUDES(m_StatsLock)
void setDataPacketTS(CPacket& p, const time_point& ts);
// Utility used for closing a listening socket
// immediately to free the socket
@ -416,11 +425,7 @@ public: // internal API
SRTU_PROPERTY_RO(SRTSOCKET, id, m_SocketID);
SRTU_PROPERTY_RO(bool, isClosing, m_bClosing);
#if ENABLE_NEW_RCVBUFFER
SRTU_PROPERTY_RO(srt::CRcvBufferNew*, rcvBuffer, m_pRcvBuffer);
#else
SRTU_PROPERTY_RO(CRcvBuffer*, rcvBuffer, m_pRcvBuffer);
#endif
SRTU_PROPERTY_RO(srt::CRcvBuffer*, rcvBuffer, m_pRcvBuffer);
SRTU_PROPERTY_RO(bool, isTLPktDrop, m_bTLPktDrop);
SRTU_PROPERTY_RO(bool, isSynReceiving, m_config.bSynRecving);
SRTU_PROPERTY_RR(sync::Condition*, recvDataCond, &m_RecvDataCond);
@ -441,16 +446,13 @@ public: // internal API
private:
/// initialize a UDT entity and bind to a local address.
void open();
/// Start listening to any connection request.
void setListenState();
/// Connect to a UDT entity listening at address "peer".
/// @param peer [in] The address of the listening UDT entity.
void startConnect(const sockaddr_any& peer, int32_t forced_isn);
/// Process the response handshake packet. Failure reasons can be:
@ -461,7 +463,6 @@ private:
/// @retval 0 Connection successful
/// @retval 1 Connection in progress (m_ConnReq turned into RESPONSE)
/// @retval -1 Connection failed
SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock)
EConnectStatus processConnectResponse(const CPacket& pkt, CUDTException* eout) ATR_NOEXCEPT;
@ -484,15 +485,20 @@ private:
/// @param rst Current read status to know if the HS packet was freshly received from the peer, or this is only a periodic update (RST_AGAIN)
SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock)
EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt);
void sendRendezvousRejection(const sockaddr_any& serv_addr, CPacket& request);
/// Create the CryptoControl object based on the HS packet.
SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock)
bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException* eout);
/// Create the CryptoControl object based on the HS packet. Allocates sender and receiver buffers and loss lists.
/// Allocates sender and receiver buffers and loss lists.
SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock)
bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout);
bool prepareBuffers(CUDTException* eout);
SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock)
EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT;
SRT_ATR_NODISCARD bool applyResponseSettings() ATR_NOEXCEPT;
SRT_ATR_NODISCARD bool applyResponseSettings(const CPacket* hspkt /*[[nullable]]*/) ATR_NOEXCEPT;
SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT;
SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket* response, const sockaddr_any& serv_addr);
SRT_ATR_NODISCARD EConnectStatus craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize);
@ -662,8 +668,14 @@ private:
/// the receiver fresh loss list.
void unlose(const CPacket& oldpacket);
void dropFromLossLists(int32_t from, int32_t to);
bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason);
SRT_ATTR_EXCLUDES(m_ConnectionLock)
void checkSndTimers();
/// @brief Check and perform KM refresh if needed.
void checkSndKMRefresh();
void checkSndTimers(Whether2RegenKm regen = DONT_REGEN_KM);
void handshakeDone()
{
m_iSndHsRetryCnt = 0;
@ -708,18 +720,17 @@ private:
SRT_ATTR_EXCLUDES(m_RcvBufferLock)
bool isRcvBufferReady() const;
SRT_ATTR_REQUIRES(m_RcvBufferLock)
bool isRcvBufferReadyNoLock() const;
// TSBPD thread main function.
static void* tsbpd(void* param);
#if ENABLE_NEW_RCVBUFFER
/// Drop too late packets (receiver side). Updaet loss lists and ACK positions.
/// Drop too late packets (receiver side). Update loss lists and ACK positions.
/// The @a seqno packet itself is not dropped.
/// @param seqno [in] The sequence number of the first packets following those to be dropped.
/// @return The number of packets dropped.
int rcvDropTooLateUpTo(int seqno);
#endif
void updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno);
static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt);
static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt);
@ -811,6 +822,9 @@ private: // Sending related data
CSndBuffer* m_pSndBuffer; // Sender buffer
CSndLossList* m_pSndLossList; // Sender loss list
CPktTimeWindow<16, 16> m_SndTimeWindow; // Packet sending time window
#ifdef ENABLE_MAXREXMITBW
CSndRateEstimator m_SndRexmitRate; // Retransmission rate estimation.
#endif
atomic_duration m_tdSendInterval; // Inter-packet time, in CPU clock cycles
@ -826,6 +840,7 @@ private: // Timers
duration m_tdACKInterval; // ACK interval
duration m_tdNAKInterval; // NAK interval
SRT_ATTR_GUARDED_BY(m_RecvAckLock)
atomic_time_point m_tsLastRspTime; // Timestamp of last response from the peer
time_point m_tsLastRspAckTime; // (SND) Timestamp of last ACK from the peer
@ -851,7 +866,7 @@ private: // Timers
// and this is the sequence number that refers to the block at position [0]. Upon acknowledgement,
// this value is shifted to the acknowledged position, and the blocks are removed from the
// m_pSndBuffer buffer up to excluding this sequence number.
// XXX CONSIDER removing this field and give up the maintenance of this sequence number
// XXX CONSIDER removing this field and giving up the maintenance of this sequence number
// to the sending buffer. This way, extraction of an old packet for retransmission should
// require only the lost sequence number, and how to find the packet with this sequence
// will be up to the sending buffer.
@ -894,14 +909,22 @@ private: // Timers
SRT_ATTR_GUARDED_BY(m_RecvAckLock)
int32_t m_iReXmitCount; // Re-Transmit Count since last ACK
time_point m_tsLogSlowDown; // The last time a log message from the "slow down" group was shown.
// The "slow down" group of logs are those that can be printed too often otherwise, but can't be turned off (warnings and errors).
// Currently only used by decryption failure message, therefore no mutex protection needed.
/// @brief Check if a frequent log can be shown.
/// @param tnow current time
/// @return true if it is ok to print a frequent log message.
bool frequentLogAllowed(const time_point& tnow) const;
private: // Receiving related data
#if ENABLE_NEW_RCVBUFFER
CRcvBufferNew* m_pRcvBuffer; //< Receiver buffer
#else
CRcvBuffer* m_pRcvBuffer; //< Receiver buffer
#endif
SRT_ATTR_GUARDED_BY(m_RcvLossLock)
CRcvLossList* m_pRcvLossList; //< Receiver loss list
SRT_ATTR_GUARDED_BY(m_RcvLossLock)
std::deque<CRcvFreshLoss> m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for.
int m_iReorderTolerance; //< Current value of dynamic reorder tolerance
int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came <TTL-2 time, resets with every increased reorder tolerance
int m_iConsecOrderedDelivery; //< Increases with every packet coming in order or retransmitted, resets with every out-of-order packet
@ -913,12 +936,11 @@ private: // Receiving related data
#ifdef ENABLE_LOGGING
int32_t m_iDebugPrevLastAck;
#endif
int32_t m_iRcvLastSkipAck; // Last dropped sequence ACK
int32_t m_iRcvLastAckAck; // (RCV) Latest packet seqno in a sent ACK acknowledged by ACKACK. RcvQTh (sendCtrlAck {r}, processCtrlAckAck {r}, processCtrlAck {r}, connection {w}).
int32_t m_iAckSeqNo; // Last ACK sequence number
sync::atomic<int32_t> m_iRcvCurrSeqNo; // (RCV) Largest received sequence number. RcvQTh, TSBPDTh.
int32_t m_iRcvCurrPhySeqNo; // Same as m_iRcvCurrSeqNo, but physical only (disregarding a filter)
bool m_bBufferWasFull; // Indicate that RX buffer was full last time a ack was sent
int32_t m_iPeerISN; // Initial Sequence Number of the peer side
uint32_t m_uPeerSrtVersion;
@ -959,7 +981,7 @@ private: // synchronization: mutexes and conditions
mutable sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer
// Protects access to m_iSndCurrSeqNo, m_iSndLastAck
sync::Mutex m_RecvAckLock; // Protects the state changes while processing incomming ACK (SRT_EPOLL_OUT)
sync::Mutex m_RecvAckLock; // Protects the state changes while processing incoming ACK (SRT_EPOLL_OUT)
sync::Condition m_RecvDataCond; // used to block "srt_recv*" when there is no data. Use together with m_RecvLock
sync::Mutex m_RecvLock; // used to synchronize "srt_recv*" call, protects TSBPD drift updates (CRcvBuffer::isRcvDataReady())
@ -1036,32 +1058,50 @@ private: // Generation and processing of packets
void updateSndLossListOnACK(int32_t ackdata_seqno);
/// Pack a packet from a list of lost packets.
///
/// @param packet [in, out] a packet structure to fill
/// @param origintime [in, out] origin timestamp of the packet
///
/// @return payload size on success, <=0 on failure
int packLostData(CPacket &packet, time_point &origintime);
int packLostData(CPacket &packet);
/// Pack a unique data packet (never sent so far) in CPacket for sending.
///
/// @param packet [in, out] a CPacket structure to fill.
/// @param origintime [in, out] origin timestamp of the packet.
///
/// @return true if a packet has been packets; false otherwise.
bool packUniqueData(CPacket& packet, time_point& origintime);
bool packUniqueData(CPacket& packet);
/// Pack in CPacket the next data to be send.
///
/// @param packet [in, out] a CPacket structure to fill
/// @param packet [out] a CPacket structure to fill
/// @param nexttime [out] Time when this socket should be next time picked up for processing.
/// @param src_addr [out] Source address to pass to channel's sendto
///
/// @return A pair of values is returned (is_payload_valid, timestamp).
/// If is_payload_valid is false, there was nothing packed for sending,
/// and the timestamp value should be ignored.
/// The timestamp is the full source/origin timestamp of the data.
std::pair<bool, time_point> packData(CPacket& packet);
/// @retval true A packet was extracted for sending, the socket should be rechecked at @a nexttime
/// @retval false Nothing was extracted for sending, @a nexttime should be ignored
bool packData(CPacket& packet, time_point& nexttime, sockaddr_any& src_addr);
int processData(CUnit* unit);
/// This function passes the incoming packet to the initial processing
/// (like packet filter) and is about to store it effectively to the
/// receiver buffer and do some postprocessing (decryption) if necessary
/// and report the status thereof.
///
/// @param incoming [in] The packet coming from the network medium
/// @param w_new_inserted [out] Set false, if the packet already exists, otherwise true (packet added)
/// @param w_was_sent_in_order [out] Set false, if the packet was belated, but had no R flag set.
/// @param w_srt_loss_seqs [out] Gets inserted a loss, if this function has detected it.
///
/// @return 0 The call was successful (regardless if the packet was accepted or not).
/// @return -1 The call has failed: no space left in the buffer.
/// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy).
int handleSocketPacketReception(const std::vector<CUnit*>& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs);
/// Get the packet's TSBPD time.
/// The @a grp passed by void* is not used yet
/// and shall not be used when ENABLE_BONDING=0.
time_point getPktTsbPdTime(void* grp, const CPacket& packet);
/// Checks and spawns the TSBPD thread if required.
int checkLazySpawnTsbPdThread();
void processClose();
/// Process the request after receiving the handshake from caller.
@ -1075,12 +1115,7 @@ private: // Generation and processing of packets
static void addLossRecord(std::vector<int32_t>& lossrecord, int32_t lo, int32_t hi);
int32_t bake(const sockaddr_any& addr, int32_t previous_cookie = 0, int correction = 0);
/// @brief Acknowledge reading position up to the @p seq.
/// Updates m_iRcvLastAck and m_iRcvLastSkipAck to @p seq.
/// @param seq first unacknowledged packet sequence number.
void ackDataUpTo(int32_t seq);
#if ENABLE_BONDING && ENABLE_NEW_RCVBUFFER
#if ENABLE_BONDING
/// @brief Drop packets in the recv buffer behind group_recv_base.
/// Updates m_iRcvLastSkipAck if it's behind group_recv_base.
void dropToGroupRecvBase();
@ -1088,9 +1123,6 @@ private: // Generation and processing of packets
void processKeepalive(const CPacket& ctrlpkt, const time_point& tsArrival);
/// Locks m_RcvBufferLock and retrieves the available size of the receiver buffer.
SRT_ATTR_EXCLUDES(m_RcvBufferLock)
size_t getAvailRcvBufferSizeLock() const;
/// Retrieves the available size of the receiver buffer.
/// Expects that m_RcvBufferLock is locked.
@ -1120,10 +1152,12 @@ public:
static const int PACKETPAIR_MASK = 0xF;
private: // Timers functions
#if ENABLE_BONDING
time_point m_tsFreshActivation; // GROUPS: time of fresh activation of the link, or 0 if past the activation phase or idle
time_point m_tsUnstableSince; // GROUPS: time since unexpected ACK delay experienced, or 0 if link seems healthy
time_point m_tsWarySince; // GROUPS: time since an unstable link has first some response
#endif
static const int BECAUSE_NO_REASON = 0, // NO BITS
BECAUSE_ACK = 1 << 0,
BECAUSE_LITEACK = 1 << 1,
@ -1142,6 +1176,7 @@ private: // for UDP multiplexer
CSndQueue* m_pSndQueue; // packet sending queue
CRcvQueue* m_pRcvQueue; // packet receiving queue
sockaddr_any m_PeerAddr; // peer address
sockaddr_any m_SourceAddr; // override UDP source address with this one when sending
uint32_t m_piSelfIP[4]; // local UDP IP address
CSNode* m_pSNode; // node information for UDT list used in snd queue
CRNode* m_pRNode; // node information for UDT list used in rcv queue

@ -57,11 +57,18 @@ std::string KmStateStr(SRT_KM_STATE state)
TAKE(SECURING);
TAKE(NOSECRET);
TAKE(BADSECRET);
#ifdef ENABLE_AEAD_API_PREVIEW
TAKE(BADCRYPTOMODE);
#endif
#undef TAKE
default:
{
char buf[256];
sprintf(buf, "??? (%d)", state);
#if defined(_MSC_VER) && _MSC_VER < 1900
_snprintf(buf, sizeof(buf) - 1, "??? (%d)", state);
#else
snprintf(buf, sizeof(buf), "??? (%d)", state);
#endif
return buf;
}
}
@ -79,6 +86,15 @@ void srt::CCryptoControl::globalInit()
#endif
}
bool srt::CCryptoControl::isAESGCMSupported()
{
#ifdef SRT_ENABLE_ENCRYPTION
return HaiCrypt_IsAESGCM_Supported() != 0;
#else
return false;
#endif
}
#if ENABLE_LOGGING
std::string srt::CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srtlen)
{
@ -112,7 +128,7 @@ void srt::CCryptoControl::createFakeSndContext()
if (!m_iSndKmKeyLen)
m_iSndKmKeyLen = 16;
if (!createCryptoCtx(m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, (m_hSndCrypto)))
if (!createCryptoCtx((m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, false))
{
HLOGC(cnlog.Debug, log << "Error: Can't create fake crypto context for sending - sending will return ERROR!");
m_hSndCrypto = 0;
@ -139,7 +155,7 @@ int srt::CCryptoControl::processSrtMsg_KMREQ(
// what has called this function. The HSv5 handshake only enforces bidirectional
// connection.
bool bidirectional = hsv > CUDT::HS_VERSION_UDT4;
const bool bidirectional = hsv > CUDT::HS_VERSION_UDT4;
// Local macro to return rejection appropriately.
// CHANGED. The first version made HSv5 reject the connection.
@ -152,6 +168,10 @@ int srt::CCryptoControl::processSrtMsg_KMREQ(
bool wasb4 SRT_ATR_UNUSED = false;
size_t sek_len = 0;
const bool bUseGCM =
(m_iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO && kmdata[HCRYPT_MSG_KM_OFS_CIPHER] == HCRYPT_CIPHER_AES_GCM) ||
(m_iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM);
// What we have to do:
// If encryption is on (we know that by having m_KmSecret nonempty), create
// the crypto context (if bidirectional, create for both sending and receiving).
@ -199,13 +219,16 @@ int srt::CCryptoControl::processSrtMsg_KMREQ(
}
wasb4 = m_hRcvCrypto;
if (!createCryptoCtx(m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX, (m_hRcvCrypto)))
if (!createCryptoCtx((m_hRcvCrypto), m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX, bUseGCM))
{
LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: Can't create RCV CRYPTO CTX - must reject...");
m_RcvKmState = SRT_KM_S_NOSECRET;
KMREQ_RESULT_REJECTION();
}
// Deduce resulting mode.
m_iCryptoMode = bUseGCM ? CSrtConfig::CIPHER_MODE_AES_GCM : CSrtConfig::CIPHER_MODE_AES_CTR;
if (!wasb4)
{
HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: created RX ENC with KeyLen=" << m_iRcvKmKeyLen);
@ -230,6 +253,15 @@ int srt::CCryptoControl::processSrtMsg_KMREQ(
w_srtlen = 1;
LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure - BADSECRET");
break;
case HAICRYPT_ERROR_CIPHER:
#ifdef ENABLE_AEAD_API_PREVIEW
m_RcvKmState = m_SndKmState = SRT_KM_S_BADCRYPTOMODE;
#else
m_RcvKmState = m_SndKmState = SRT_KM_S_BADSECRET; // Use "bad secret" as a fallback.
#endif
w_srtlen = 1;
LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure - BADCRYPTOMODE");
break;
case HAICRYPT_ERROR: //Other errors
default:
m_RcvKmState = m_SndKmState = SRT_KM_S_NOSECRET;
@ -244,7 +276,6 @@ int srt::CCryptoControl::processSrtMsg_KMREQ(
// until the next KMREQ is received as a key regeneration.
m_bErrorReported = false;
if (w_srtlen == 1)
goto HSv4_ErrorReport;
@ -379,6 +410,14 @@ int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len
m_SndKmState = SRT_KM_S_UNSECURED;
retstatus = 0;
break;
#ifdef ENABLE_AEAD_API_PREVIEW
case SRT_KM_S_BADCRYPTOMODE:
// The peer expects to use a different cryptographic mode (e.g. AES-GCM, not AES-CTR).
m_RcvKmState = SRT_KM_S_BADCRYPTOMODE;
m_SndKmState = SRT_KM_S_BADCRYPTOMODE;
retstatus = -1;
break;
#endif
default:
LOGC(cnlog.Fatal, log << "processSrtMsg_KMRSP: IPE: unknown peer error state: "
@ -427,8 +466,9 @@ int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len
return retstatus;
}
void srt::CCryptoControl::sendKeysToPeer(CUDT* sock SRT_ATR_UNUSED, int iSRTT SRT_ATR_UNUSED, Whether2RegenKm regen SRT_ATR_UNUSED)
void srt::CCryptoControl::sendKeysToPeer(CUDT* sock SRT_ATR_UNUSED, int iSRTT SRT_ATR_UNUSED)
{
sync::ScopedLock lck(m_mtxLock);
if (!m_hSndCrypto || m_SndKmState == SRT_KM_S_UNSECURED)
{
HLOGC(cnlog.Debug, log << "sendKeysToPeer: NOT sending/regenerating keys: "
@ -461,21 +501,13 @@ void srt::CCryptoControl::sendKeysToPeer(CUDT* sock SRT_ATR_UNUSED, int iSRTT SR
}
}
}
if (regen)
{
regenCryptoKm(
sock, // send UMSG_EXT + SRT_CMD_KMREQ to the peer using this socket
false // Do not apply the regenerated key to the to the receiver context
); // regenerate and send
}
#endif
}
#ifdef SRT_ENABLE_ENCRYPTION
void srt::CCryptoControl::regenCryptoKm(CUDT* sock, bool bidirectional)
void srt::CCryptoControl::regenCryptoKm(CUDT* sock SRT_ATR_UNUSED, bool bidirectional SRT_ATR_UNUSED)
{
#ifdef SRT_ENABLE_ENCRYPTION
sync::ScopedLock lck(m_mtxLock);
if (!m_hSndCrypto)
return;
@ -549,8 +581,8 @@ void srt::CCryptoControl::regenCryptoKm(CUDT* sock, bool bidirectional)
if (sent)
m_SndKmLastTime = srt::sync::steady_clock::now();
}
#endif
}
srt::CCryptoControl::CCryptoControl(SRTSOCKET id)
: m_SocketID(id)
@ -560,9 +592,9 @@ srt::CCryptoControl::CCryptoControl(SRTSOCKET id)
, m_RcvKmState(SRT_KM_S_UNSECURED)
, m_KmRefreshRatePkt(0)
, m_KmPreAnnouncePkt(0)
, m_iCryptoMode(CSrtConfig::CIPHER_MODE_AUTO)
, m_bErrorReported(false)
{
m_KmSecret.len = 0;
//send
m_SndKmMsg[0].MsgLen = 0;
@ -587,6 +619,19 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b
// Set UNSECURED state as default
m_RcvKmState = SRT_KM_S_UNSECURED;
m_iCryptoMode = cfg.iCryptoMode;
#ifdef SRT_ENABLE_ENCRYPTION
if (!cfg.bTSBPD && m_iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO)
m_iCryptoMode = CSrtConfig::CIPHER_MODE_AES_CTR;
const bool bUseGCM = m_iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM;
if (bUseGCM && !isAESGCMSupported())
{
LOGC(cnlog.Warn, log << "CCryptoControl: AES GCM is not supported by the crypto service provider.");
return false;
}
#endif
// Set security-pending state, if a password was set.
m_SndKmState = hasPassphrase() ? SRT_KM_S_SECURING : SRT_KM_S_UNSECURED;
@ -594,7 +639,7 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b
m_KmPreAnnouncePkt = cfg.uKmPreAnnouncePkt;
m_KmRefreshRatePkt = cfg.uKmRefreshRatePkt;
if ( side == HSD_INITIATOR )
if (side == HSD_INITIATOR)
{
if (hasPassphrase())
{
@ -605,13 +650,13 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b
m_iSndKmKeyLen = 16;
}
bool ok = createCryptoCtx(m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, (m_hSndCrypto));
bool ok = createCryptoCtx((m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, bUseGCM);
HLOGC(cnlog.Debug, log << "CCryptoControl::init: creating SND crypto context: " << ok);
if (ok && bidirectional)
{
m_iRcvKmKeyLen = m_iSndKmKeyLen;
int st = HaiCrypt_Clone(m_hSndCrypto, HAICRYPT_CRYPTO_DIR_RX, &m_hRcvCrypto);
const int st = HaiCrypt_Clone(m_hSndCrypto, HAICRYPT_CRYPTO_DIR_RX, &m_hRcvCrypto);
HLOGC(cnlog.Debug, log << "CCryptoControl::init: creating CLONED RCV crypto context: status=" << st);
ok = st == 0;
}
@ -630,12 +675,14 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b
NULL, // Do not send the key (the KM msg will be attached to the HSv5 handshake)
bidirectional // replicate the key to the receiver context, if bidirectional
);
m_iCryptoMode = bUseGCM ? CSrtConfig::CIPHER_MODE_AES_GCM : CSrtConfig::CIPHER_MODE_AES_CTR;
#else
// This error would be a consequence of setting the passphrase, while encryption
// is turned off at compile time. Setting the password itself should be not allowed
// so this could only happen as a consequence of an IPE.
LOGC(cnlog.Error, log << "CCryptoControl::init: IPE: encryption not supported");
return true;
return false;
#endif
}
else
@ -654,6 +701,7 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b
void srt::CCryptoControl::close()
{
/* Wipeout secrets */
sync::ScopedLock lck(m_mtxLock);
memset(&m_KmSecret, 0, sizeof(m_KmSecret));
}
@ -691,9 +739,8 @@ static std::string CryptoFlags(int flg)
} // namespace srt
#endif // ENABLE_HEAVY_LOGGING
bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir, HaiCrypt_Handle& w_hCrypto)
bool srt::CCryptoControl::createCryptoCtx(HaiCrypt_Handle& w_hCrypto, size_t keylen, HaiCrypt_CryptoDir cdir, bool bAESGCM)
{
if (w_hCrypto)
{
// XXX You can check here if the existing handle represents
@ -715,7 +762,7 @@ bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir
m_KmRefreshRatePkt = 2000;
m_KmPreAnnouncePkt = 500;
#endif
crypto_cfg.flags = HAICRYPT_CFG_F_CRYPTO | (cdir == HAICRYPT_CRYPTO_DIR_TX ? HAICRYPT_CFG_F_TX : 0);
crypto_cfg.flags = HAICRYPT_CFG_F_CRYPTO | (cdir == HAICRYPT_CRYPTO_DIR_TX ? HAICRYPT_CFG_F_TX : 0) | (bAESGCM ? HAICRYPT_CFG_F_GCM : 0);
crypto_cfg.xport = HAICRYPT_XPT_SRT;
crypto_cfg.cryspr = HaiCryptCryspr_Get_Instance();
crypto_cfg.key_len = (size_t)keylen;
@ -724,7 +771,6 @@ bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir
crypto_cfg.km_refresh_rate_pkt = m_KmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : m_KmRefreshRatePkt;
crypto_cfg.km_pre_announce_pkt = m_KmPreAnnouncePkt == 0 ? SRT_CRYPT_KM_PRE_ANNOUNCE : m_KmPreAnnouncePkt;
crypto_cfg.secret = m_KmSecret;
//memcpy(&crypto_cfg.secret, &m_KmSecret, sizeof(crypto_cfg.secret));
HLOGC(cnlog.Debug, log << "CRYPTO CFG: flags=" << CryptoFlags(crypto_cfg.flags) << " xport=" << crypto_cfg.xport << " cryspr=" << crypto_cfg.cryspr
<< " keylen=" << crypto_cfg.key_len << " passphrase_length=" << crypto_cfg.secret.len);
@ -740,7 +786,7 @@ bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir
return true;
}
#else
bool srt::CCryptoControl::createCryptoCtx(size_t, HaiCrypt_CryptoDir, HaiCrypt_Handle&)
bool srt::CCryptoControl::createCryptoCtx(HaiCrypt_Handle&, size_t, HaiCrypt_CryptoDir, bool)
{
return false;
}
@ -754,6 +800,8 @@ srt::EncryptionStatus srt::CCryptoControl::encrypt(CPacket& w_packet SRT_ATR_UNU
if ( getSndCryptoFlags() == EK_NOENC )
return ENCS_CLEAR;
// Note that in case of GCM the header has to zero Retransmitted Packet Flag (R).
// If TSBPD is disabled, timestamp also has to be zeroed.
int rc = HaiCrypt_Tx_Data(m_hSndCrypto, ((uint8_t*)w_packet.getHeader()), ((uint8_t*)w_packet.m_pcData), w_packet.getLength());
if (rc < 0)
{
@ -826,10 +874,10 @@ srt::EncryptionStatus srt::CCryptoControl::decrypt(CPacket& w_packet SRT_ATR_UNU
return ENCS_FAILED;
}
int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, ((uint8_t *)w_packet.getHeader()), ((uint8_t *)w_packet.m_pcData), w_packet.getLength());
if ( rc <= 0 )
const int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, ((uint8_t *)w_packet.getHeader()), ((uint8_t *)w_packet.m_pcData), w_packet.getLength());
if (rc <= 0)
{
LOGC(cnlog.Error, log << "decrypt ERROR (IPE): HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption");
LOGC(cnlog.Note, log << "decrypt ERROR: HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption");
// -1: decryption failure
// 0: key not received yet
return ENCS_FAILED;

@ -50,8 +50,6 @@ const size_t SRT_KMR_KMSTATE = 0;
#define SRT_CMD_MAXSZ HCRYPT_MSG_KM_MAX_SZ /* Maximum SRT custom messages payload size (bytes) */
const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(uint32_t);
enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1};
class CCryptoControl
{
SRTSOCKET m_SocketID;
@ -69,10 +67,12 @@ private:
// putting the whole HaiCrypt_Cfg object here.
int m_KmRefreshRatePkt;
int m_KmPreAnnouncePkt;
int m_iCryptoMode;
HaiCrypt_Secret m_KmSecret; //Key material shared secret
// Sender
sync::steady_clock::time_point m_SndKmLastTime;
sync::Mutex m_mtxLock; // A mutex to protect concurrent access to CCryptoControl.
struct {
unsigned char Msg[HCRYPT_MSG_KM_MAX_SZ];
size_t MsgLen;
@ -87,6 +87,8 @@ private:
public:
static void globalInit();
static bool isAESGCMSupported();
bool sendingAllowed()
{
// This function is called to state as to whether the
@ -109,15 +111,16 @@ public:
return m_KmSecret.len > 0;
}
private:
#ifdef SRT_ENABLE_ENCRYPTION
/// Regenerate cryptographic key material.
int getCryptoMode() const
{
return m_iCryptoMode;
}
/// Regenerate cryptographic key material if needed.
/// @param[in] sock If not null, the socket will be used to send the KM message to the peer (e.g. KM refresh).
/// @param[in] bidirectional If true, the key material will be regenerated for both directions (receiver and sender).
SRT_ATTR_EXCLUDES(m_mtxLock)
void regenCryptoKm(CUDT* sock, bool bidirectional);
#endif
public:
size_t KeyLen() { return m_iSndKmKeyLen; }
@ -205,13 +208,17 @@ public:
std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen);
bool init(HandshakeSide, const CSrtConfig&, bool);
SRT_ATTR_EXCLUDES(m_mtxLock)
void close();
/// @return True if the handshake is in progress.
/// (Re)send KM request to a peer on timeout.
/// This function is used in:
/// - HSv4 (initial key material exchange - in HSv5 it's attached to handshake)
/// - case of key regeneration, which should be then exchanged again.
void sendKeysToPeer(CUDT* sock, int iSRTT, Whether2RegenKm regen);
/// - HSv4 (initial key material exchange - in HSv5 it's attached to handshake).
/// - The case of key regeneration (KM refresh), when a new key has to be sent again.
/// In this case the first sending happens in regenCryptoKm(..). This function
/// retransmits the KM request by timeout if not KM response has been received.
SRT_ATTR_EXCLUDES(m_mtxLock)
void sendKeysToPeer(CUDT* sock, int iSRTT);
void setCryptoSecret(const HaiCrypt_Secret& secret)
{
@ -224,7 +231,7 @@ public:
m_iRcvKmKeyLen = keylen;
}
bool createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir tx, HaiCrypt_Handle& rh);
bool createCryptoCtx(HaiCrypt_Handle& rh, size_t keylen, HaiCrypt_CryptoDir tx, bool bAESGCM);
int getSndCryptoFlags() const
{

@ -66,6 +66,7 @@ modified by
#include "epoll.h"
#include "logging.h"
#include "udt.h"
#include "utilities.h"
using namespace std;
using namespace srt::sync;
@ -214,8 +215,8 @@ void srt::CEPoll::clear_ready_usocks(CEPollDesc& d, int direction)
}
}
for (size_t i = 0; i < cleared.size(); ++i)
d.removeSubscription(cleared[i]);
for (size_t j = 0; j < cleared.size(); ++j)
d.removeSubscription(cleared[j]);
}
int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
@ -639,8 +640,8 @@ int srt::CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* wr
#ifdef LINUX
const int max_events = ed.m_sLocals.size();
SRT_ASSERT(max_events > 0);
epoll_event ev[max_events];
int nfds = ::epoll_wait(ed.m_iLocalID, ev, max_events, 0);
srt::FixedArray<epoll_event> ev(max_events);
int nfds = ::epoll_wait(ed.m_iLocalID, ev.data(), ev.size(), 0);
IF_HEAVY_LOGGING(const int prev_total = total);
for (int i = 0; i < nfds; ++ i)
@ -660,23 +661,23 @@ int srt::CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* wr
#elif defined(BSD) || TARGET_OS_MAC
struct timespec tmout = {0, 0};
const int max_events = ed.m_sLocals.size();
const int max_events = (int)ed.m_sLocals.size();
SRT_ASSERT(max_events > 0);
struct kevent ke[max_events];
srt::FixedArray<struct kevent> ke(max_events);
int nfds = kevent(ed.m_iLocalID, NULL, 0, ke, max_events, &tmout);
int nfds = kevent(ed.m_iLocalID, NULL, 0, ke.data(), (int)ke.size(), &tmout);
IF_HEAVY_LOGGING(const int prev_total = total);
for (int i = 0; i < nfds; ++ i)
{
if ((NULL != lrfds) && (ke[i].filter == EVFILT_READ))
{
lrfds->insert(ke[i].ident);
lrfds->insert((int)ke[i].ident);
++ total;
}
if ((NULL != lwfds) && (ke[i].filter == EVFILT_WRITE))
{
lwfds->insert(ke[i].ident);
lwfds->insert((int)ke[i].ident);
++ total;
}
}
@ -702,7 +703,7 @@ int srt::CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* wr
if (lwfds)
FD_SET(*i, &rqwritefds);
if ((int)*i > max_fd)
max_fd = *i;
max_fd = (int)*i;
}
IF_HEAVY_LOGGING(const int prev_total = total);

@ -69,8 +69,11 @@ class CUDTGroup;
class CEPollDesc
{
#ifdef __GNUG__
const int m_iID; // epoll ID
#else
const int m_iID SRT_ATR_UNUSED; // epoll ID
#endif
struct Wait;
struct Notice: public SRT_EPOLL_EVENT

@ -345,13 +345,13 @@ void FECFilterBuiltin::ConfigureColumns(Container& which, int32_t isn)
{
offset = col + 1; // +1 because we want it for the next column
HLOGC(pflog.Debug, log << "ConfigureColumns: [" << (col+1) << "]... (resetting to row 0: +"
<< offset << " %" << CSeqNo::incseq(isn, offset) << ")");
<< offset << " %" << CSeqNo::incseq(isn, (int32_t)offset) << ")");
}
else
{
offset += 1 + sizeRow();
HLOGC(pflog.Debug, log << "ConfigureColumns: [" << (col+1) << "] ... (continue +"
<< offset << " %" << CSeqNo::incseq(isn, offset) << ")");
<< offset << " %" << CSeqNo::incseq(isn, (int32_t)offset) << ")");
}
}
}
@ -453,7 +453,9 @@ void FECFilterBuiltin::feedSource(CPacket& packet)
HLOGC(pflog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() << " rowoff=" << baseoff
<< " column=" << vert_gx << " .base=%" << vert_base << " coloff=" << vert_off);
if (vert_off >= 0 && sizeCol() > 1)
// [[assert sizeCol() >= 2]]; // see the condition above.
if (vert_off >= 0)
{
// BEWARE! X % Y with different signedness upgrades int to unsigned!
@ -468,7 +470,7 @@ void FECFilterBuiltin::feedSource(CPacket& packet)
return;
}
SRT_ASSERT(vert_off >= 0);
// [[assert vert_off >= 0]]; // this condition branch
int vert_pos = vert_off / int(sizeRow());
HLOGC(pflog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo()
@ -496,7 +498,6 @@ void FECFilterBuiltin::feedSource(CPacket& packet)
}
else
{
HLOGC(pflog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo()
<< " B:%" << baseoff << " H:*[" << horiz_pos << "] V(B=%" << vert_base
<< ")[col=" << vert_gx << "]<NO-COLUMN>"
@ -604,8 +605,8 @@ void FECFilterBuiltin::ClipData(Group& g, uint16_t length_net, uint8_t kflg,
}
// Fill the rest with zeros. When this packet is going to be
// recovered, the payload extraced from this process will have
// the maximum lenght, but it will be cut to the right length
// recovered, the payload extracted from this process will have
// the maximum length, but it will be cut to the right length
// and these padding 0s taken out.
for (size_t i = payload_size; i < payloadSize(); ++i)
g.payload_clip[i] = g.payload_clip[i] ^ 0;
@ -1128,10 +1129,10 @@ static void DebugPrintCells(int32_t base, const std::deque<bool>& cells, size_t
for ( ; i < cells.size(); i += row_size )
{
std::ostringstream os;
os << "cell[" << i << "-" << (i+row_size-1) << "] %" << CSeqNo::incseq(base, i) << ":";
os << "cell[" << i << "-" << (i+row_size-1) << "] %" << CSeqNo::incseq(base, (int32_t)i) << ":";
for (size_t y = 0; y < row_size; ++y)
{
os << " " << CellMark(cells, i+y);
os << " " << CellMark(cells, (int)(i+y));
}
LOGP(pflog.Debug, os.str());
}
@ -1568,11 +1569,10 @@ size_t FECFilterBuiltin::ExtendRows(size_t rowx)
const size_t size_in_packets = rowx * numberCols();
const int n_series = int(rowx / numberRows());
if (size_in_packets > rcvBufferSize() && n_series > 2)
if (CheckEmergencyShrink(n_series, size_in_packets))
{
HLOGC(pflog.Debug, log << "FEC: Emergency resize, rowx=" << rowx << " series=" << n_series
HLOGC(pflog.Debug, log << "FEC: DONE Emergency resize, rowx=" << rowx << " series=" << n_series
<< "npackets=" << size_in_packets << " exceeds buf=" << rcvBufferSize());
EmergencyShrink(n_series);
}
// Create and configure next groups.
@ -1700,8 +1700,27 @@ bool FECFilterBuiltin::IsLost(int32_t seq) const
return rcv.cells[offset];
}
void FECFilterBuiltin::EmergencyShrink(size_t n_series)
bool FECFilterBuiltin::CheckEmergencyShrink(size_t n_series, size_t size_in_packets)
{
// The minimum required size of the covered sequence range must be such
// that groups for packets from the previous range must be still reachable.
// It's then "this and previous" series in case of even arrangement.
//
// For staircase arrangement the potential range for a single column series
// (number of columns equal to a row size) spans for 2 matrices (rows * cols)
// minus one row. As dismissal is only allowed to be done by one full series
// of rows and columns, the history must keep as many groups as needed to reach
// out for this very packet of this group and all packets in the same row.
// Hence we need two series of columns to cover a similar range as two row, twice.
const size_t min_series_history = m_arrangement_staircase ? 4 : 2;
if (n_series <= min_series_history)
return false;
if (size_in_packets < rcvBufferSize() && n_series < SRT_FEC_MAX_RCV_HISTORY)
return false;
// Shrink is required in order to prepare place for
// either vertical or horizontal group in series `n_series`.
@ -1782,7 +1801,7 @@ void FECFilterBuiltin::EmergencyShrink(size_t n_series)
else
{
HLOGC(pflog.Debug, log << "FEC: Shifting rcv row %" << oldbase << " -> %" << newbase);
rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.end() + shift_rows);
rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + shift_rows);
}
const size_t shift_cols = shift_series * numberCols();
@ -1816,6 +1835,8 @@ void FECFilterBuiltin::EmergencyShrink(size_t n_series)
rcv.cells.push_back(false);
}
rcv.cell_base = newbase;
return true;
}
FECFilterBuiltin::EHangStatus FECFilterBuiltin::HangVertical(const CPacket& rpkt, signed char fec_col, loss_seqs_t& irrecover)
@ -1932,7 +1953,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t
{
HLOGC(pflog.Debug, log << "FEC/V: ... [" << i << "] base=%"
<< pg.base << " TOO EARLY (last=%"
<< CSeqNo::incseq(pg.base, (sizeCol()-1)*sizeRow())
<< CSeqNo::incseq(pg.base, (int32_t)((sizeCol()-1)*sizeRow()))
<< ")");
continue;
}
@ -1943,7 +1964,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t
HLOGC(pflog.Debug, log << "FEC/V: ... [" << i << "] base=%"
<< pg.base << " - PAST last=%"
<< CSeqNo::incseq(pg.base, (sizeCol()-1)*sizeRow())
<< CSeqNo::incseq(pg.base, (int32_t)((sizeCol()-1)*sizeRow()))
<< " - collecting losses.");
pg.dismissed = true; // mark irrecover already collected
@ -2489,11 +2510,11 @@ size_t FECFilterBuiltin::ExtendColumns(size_t colgx)
// of packets as many as the number of rows, so simply multiply this.
const size_t size_in_packets = colgx * numberRows();
const size_t n_series = colgx / numberCols();
if (size_in_packets > rcvBufferSize()/2 || n_series > SRT_FEC_MAX_RCV_HISTORY)
if (CheckEmergencyShrink(n_series, size_in_packets))
{
HLOGC(pflog.Debug, log << "FEC: Emergency resize, colgx=" << colgx << " series=" << n_series
HLOGC(pflog.Debug, log << "FEC: DONE Emergency resize, colgx=" << colgx << " series=" << n_series
<< "npackets=" << size_in_packets << " exceeds buf=" << rcvBufferSize());
EmergencyShrink(n_series);
}
else
{

@ -47,7 +47,7 @@ public:
size_t drop; //< by how much the sequence should increase to get to the next series
size_t collected; //< how many packets were taken to collect the clip
Group(): base(CSeqNo::m_iMaxSeqNo), step(0), drop(0), collected(0)
Group(): base(SRT_SEQNO_NONE), step(0), drop(0), collected(0)
{
}
@ -87,7 +87,7 @@ public:
#if ENABLE_HEAVY_LOGGING
std::string DisplayStats()
{
if (base == CSeqNo::m_iMaxSeqNo)
if (base == SRT_SEQNO_NONE)
return "UNINITIALIZED!!!";
std::ostringstream os;
@ -222,7 +222,7 @@ private:
void RcvRebuild(Group& g, int32_t seqno, Group::Type tp);
int32_t RcvGetLossSeqHoriz(Group& g);
int32_t RcvGetLossSeqVert(Group& g);
void EmergencyShrink(size_t n_series);
bool CheckEmergencyShrink(size_t n_series, size_t size_in_packets);
static void TranslateLossRecords(const std::set<int32_t>& loss, loss_seqs_t& irrecover);
void RcvCheckDismissColumn(int32_t seqno, int colgx, loss_seqs_t& irrecover);

@ -2,8 +2,9 @@
SOURCES
api.cpp
buffer.cpp
buffer_snd.cpp
buffer_rcv.cpp
buffer_tools.cpp
cache.cpp
channel.cpp
common.cpp
@ -53,8 +54,9 @@ udt.h
PRIVATE HEADERS
api.h
buffer.h
buffer_snd.h
buffer_rcv.h
buffer_tools.h
cache.h
channel.h
common.h

@ -226,12 +226,12 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data)
data.sndstate = SRT_GST_PENDING;
data.rcvstate = SRT_GST_PENDING;
HLOGC(gmlog.Debug, log << "CUDTGroup::add: adding new member @" << data.id);
LOGC(gmlog.Note, log << "group/add: adding member @" << data.id << " into group $" << id());
m_Group.push_back(data);
gli_t end = m_Group.end();
if (m_iMaxPayloadSize == -1)
{
int plsize = data.ps->core().OPT_PayloadSize();
int plsize = (int)data.ps->core().OPT_PayloadSize();
HLOGC(gmlog.Debug,
log << "CUDTGroup::add: taking MAX payload size from socket @" << data.ps->m_SocketID << ": " << plsize
<< " " << (plsize ? "(explicit)" : "(unspecified = fallback to 1456)"));
@ -251,7 +251,6 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype)
: m_Global(CUDT::uglobal())
, m_GroupID(-1)
, m_PeerGroupID(-1)
, m_bSyncOnMsgNo(false)
, m_type(gtype)
, m_listener()
, m_iBusy()
@ -336,6 +335,7 @@ void CUDTGroup::GroupContainer::erase(CUDTGroup::gli_t it)
}
}
m_List.erase(it);
--m_SizeCache;
}
void CUDTGroup::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen)
@ -394,12 +394,6 @@ void CUDTGroup::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen)
break;
case SRTO_CONGESTION:
// Currently no socket groups allow any other
// congestion control mode other than live.
LOGP(gmlog.Error, "group option: SRTO_CONGESTION is only allowed as 'live' and cannot be changed");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
default:
break;
}
@ -554,7 +548,7 @@ void CUDTGroup::deriveSettings(CUDT* u)
if (u->m_config.CryptoSecret.len)
{
string password((const char*)u->m_config.CryptoSecret.str, u->m_config.CryptoSecret.len);
m_config.push_back(ConfigItem(SRTO_PASSPHRASE, password.c_str(), password.size()));
m_config.push_back(ConfigItem(SRTO_PASSPHRASE, password.c_str(), (int)password.size()));
}
IM(SRTO_KMREFRESHRATE, uKmRefreshRatePkt);
@ -563,7 +557,7 @@ void CUDTGroup::deriveSettings(CUDT* u)
string cc = u->m_CongCtl.selected_name();
if (cc != "live")
{
m_config.push_back(ConfigItem(SRTO_CONGESTION, cc.c_str(), cc.size()));
m_config.push_back(ConfigItem(SRTO_CONGESTION, cc.c_str(), (int)cc.size()));
}
// NOTE: This is based on information extracted from the "semi-copy-constructor" of CUDT class.
@ -893,6 +887,16 @@ void CUDTGroup::close()
HLOGC(smlog.Debug, log << "group/close: IPE(NF): group member @" << ig->id << " already deleted");
continue;
}
// Make the socket closing BEFORE withdrawing its group membership
// because a socket created as a group member cannot be valid
// without the group.
// This is not true in case of non-managed groups, which
// only collect sockets, but also non-managed groups should not
// use common group buffering and tsbpd. Also currently there are
// no other groups than managed one.
s->setClosing();
s->m_GroupOf = NULL;
s->m_GroupMemberData = NULL;
HLOGC(smlog.Debug, log << "group/close: CUTTING OFF @" << ig->id << " (found as @" << s->m_SocketID << ") from the group");
@ -1219,12 +1223,10 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc)
// and therefore take over the leading role in setting the ISN. If the
// second one fails, too, then the only remaining idle link will simply
// go with its own original sequence.
//
// On the opposite side the reader should know that the link is inactive
// so the first received payload activates it. Activation of an idle link
// means that the very first packet arriving is TAKEN AS A GOOD DEAL, that is,
// no LOSSREPORT is sent even if the sequence looks like a "jumped over".
// Only for activated links is the LOSSREPORT sent upon seqhole detection.
// On the opposite side, if the first packet arriving looks like a jump over,
// the corresponding LOSSREPORT is sent. For packets that are truly lost,
// the sender retransmits them, for packets that before ISN, DROPREQ is sent.
// Now we can go to the idle links and attempt to send the payload
// also over them.
@ -1714,7 +1716,7 @@ int CUDTGroup::getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize)
copyGroupData(*d, (pdata[i]));
}
return m_Group.size();
return (int)m_Group.size();
}
// [[using locked(this->m_GroupLock)]]
@ -1888,7 +1890,7 @@ void CUDTGroup::recv_CollectAliveAndBroken(vector<CUDTSocket*>& alive, set<CUDTS
// Don't skip packets that are ahead because if we have a situation
// that all links are either "elephants" (do not report read readiness)
// and "kangaroos" (have already delivered an ahead packet) then
// omiting kangaroos will result in only elephants to be polled for
// omitting kangaroos will result in only elephants to be polled for
// reading. Due to the strict timing requirements and ensurance that
// TSBPD on every link will result in exactly the same delivery time
// for a packet of given sequence, having an elephant and kangaroo in
@ -2134,7 +2136,6 @@ static bool isValidSeqno(int32_t iBaseSeqno, int32_t iPktSeqno)
return false;
}
#ifdef ENABLE_NEW_RCVBUFFER
int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc)
{
// First, acquire GlobControlLock to make sure all member sockets still exist
@ -2197,7 +2198,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc)
// Find the first readable packet among all member sockets.
CUDTSocket* socketToRead = NULL;
CRcvBufferNew::PacketInfo infoToRead = {-1, false, time_point()};
CRcvBuffer::PacketInfo infoToRead = {-1, false, time_point()};
for (vector<CUDTSocket*>::const_iterator si = readySockets.begin(); si != readySockets.end(); ++si)
{
CUDTSocket* ps = *si;
@ -2215,7 +2216,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc)
}
}
const CRcvBufferNew::PacketInfo info =
const CRcvBuffer::PacketInfo info =
ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now());
if (info.seqno == SRT_SEQNO_NONE)
{
@ -2292,13 +2293,14 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc)
m_stats.recv.count(res);
updateAvgPayloadSize(res);
bool canReadFurther = false;
for (vector<CUDTSocket*>::const_iterator si = aliveMembers.begin(); si != aliveMembers.end(); ++si)
{
CUDTSocket* ps = *si;
ScopedLock lg(ps->core().m_RcvBufferLock);
if (m_RcvBaseSeqNo != SRT_SEQNO_NONE)
{
int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo));
const int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo));
if (cnt > 0)
{
HLOGC(grlog.Debug,
@ -2306,596 +2308,22 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc)
<< " packets after reading: m_RcvBaseSeqNo=" << m_RcvBaseSeqNo);
}
}
}
for (vector<CUDTSocket*>::const_iterator si = aliveMembers.begin(); si != aliveMembers.end(); ++si)
{
CUDTSocket* ps = *si;
if (!ps->core().isRcvBufferReady())
if (!ps->core().isRcvBufferReadyNoLock())
m_Global.m_EPoll.update_events(ps->m_SocketID, ps->core().m_sPollID, SRT_EPOLL_IN, false);
else
canReadFurther = true;
}
if (!canReadFurther)
m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false);
return res;
}
LOGC(grlog.Error, log << "grp/recv: UNEXPECTED RUN PATH, ABANDONING.");
m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false);
throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0);
}
#else
// The "app reader" version of the reading function.
// This reads the packets from every socket treating them as independent
// and prepared to work with the application. Then packets are sorted out
// by getting the sequence number.
int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc)
{
typedef map<SRTSOCKET, ReadPos>::iterator pit_t;
// Later iteration over it might be less efficient than
// by vector, but we'll also often try to check a single id
// if it was ever seen broken, so that it's skipped.
set<CUDTSocket*> broken;
size_t output_size = 0;
// First, acquire GlobControlLock to make sure all member sockets still exist
enterCS(m_Global.m_GlobControlLock);
ScopedLock guard(m_GroupLock);
if (m_bClosing)
{
// The group could be set closing in the meantime, but if
// this is only about to be set by another thread, this thread
// must fist wait for being able to acquire this lock.
// The group will not be deleted now because it is added usage counter
// by this call, but will be released once it exits.
leaveCS(m_Global.m_GlobControlLock);
throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
}
// Now, still under lock, check if all sockets still can be dispatched
send_CheckValidSockets();
leaveCS(m_Global.m_GlobControlLock);
if (m_bClosing)
throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
for (;;)
{
if (!m_bOpened || !m_bConnected)
{
LOGC(grlog.Error,
log << boolalpha << "group/recv: ERROR opened=" << m_bOpened << " connected=" << m_bConnected);
throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
}
// Check first the ahead packets if you have any to deliver.
if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && !m_Positions.empty())
{
// This function also updates the group sequence pointer.
ReadPos* pos = checkPacketAhead();
if (pos)
{
if (size_t(len) < pos->packet.size())
throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0);
HLOGC(grlog.Debug,
log << "group/recv: delivering AHEAD packet %" << pos->mctrl.pktseq << " #" << pos->mctrl.msgno
<< ": " << BufferStamp(&pos->packet[0], pos->packet.size()));
memcpy(buf, &pos->packet[0], pos->packet.size());
fillGroupData((w_mc), pos->mctrl);
m_RcvBaseSeqNo = pos->mctrl.pktseq;
len = pos->packet.size();
pos->packet.clear();
// Update stats as per delivery
m_stats.recv.count(len);
updateAvgPayloadSize(len);
// We predict to have only one packet ahead, others are pending to be reported by tsbpd.
// This will be "re-enabled" if the later check puts any new packet into ahead.
m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false);
return len;
}
}
// LINK QUALIFICATION NAMES:
//
// HORSE: Correct link, which delivers the very next sequence.
// Not necessarily this link is currently active.
//
// KANGAROO: Got some packets dropped and the sequence number
// of the packet jumps over the very next sequence and delivers
// an ahead packet.
//
// ELEPHANT: Is not ready to read, while others are, or reading
// up to the current latest delivery sequence number does not
// reach this sequence and the link becomes non-readable earlier.
// The above condition has ruled out one kangaroo and turned it
// into a horse.
// Below there's a loop that will try to extract packets. Kangaroos
// will be among the polled ones because skipping them risks that
// the elephants will take over the reading. Links already known as
// elephants will be also polled in an attempt to revitalize the
// connection that experienced just a short living choking.
//
// After polling we attempt to read from every link that reported
// read-readiness and read at most up to the sequence equal to the
// current delivery sequence.
// Links that deliver a packet below that sequence will be retried
// until they deliver no more packets or deliver the packet of
// expected sequence. Links that don't have a record in m_Positions
// and report readiness will be always read, at least to know what
// sequence they currently stand on.
//
// Links that are already known as kangaroos will be polled, but
// no reading attempt will be done. If after the reading series
// it will turn out that we have no more horses, the slowest kangaroo
// will be "upgraded to a horse" (the ahead link with a sequence
// closest to the current delivery sequence will get its sequence
// set as current delivered and its recorded ahead packet returned
// as the read packet).
// If we find at least one horse, the packet read from that link
// will be delivered. All other link will be just ensured update
// up to this sequence number, or at worst all available packets
// will be read. In this case all kangaroos remain kangaroos,
// until the current delivery sequence m_RcvBaseSeqNo will be lifted
// to the sequence recorded for these links in m_Positions,
// during the next time ahead check, after which they will become
// horses.
const size_t size = m_Group.size();
// Prepare first the list of sockets to be added as connect-pending
// and as read-ready, then unlock the group, and then add them to epoll.
vector<CUDTSocket*> aliveMembers;
recv_CollectAliveAndBroken(aliveMembers, broken);
const vector<CUDTSocket*> ready_sockets = recv_WaitForReadReady(aliveMembers, broken);
// m_GlobControlLock lifted, m_GroupLock still locked.
// Now we can safely do this scoped way.
if (!m_bSynRecving && ready_sockets.empty())
{
HLOGC(grlog.Debug,
log << "group/rcv $" << m_GroupID << ": Not available AT THIS TIME, NOT READ-READY now.");
m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false);
throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0);
}
// Ok, now we need to have some extra qualifications:
// 1. If a socket has no registry yet, we read anyway, just
// to notify the current position. We read ONLY ONE PACKET this time,
// we'll worry later about adjusting it to the current group sequence
// position.
// 2. If a socket is already position ahead, DO NOT read from it, even
// if it is ready.
// The state of things whether we were able to extract the very next
// sequence will be simply defined by the fact that `output` is nonempty.
int32_t next_seq = m_RcvBaseSeqNo;
if (m_bClosing)
{
HLOGC(gslog.Debug, log << "grp/sendBroadcast: GROUP CLOSED, ABANDONING");
throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
}
//
// NOTE: Although m_GlobControlLock is lifted here so potentially sockets
// colected in ready_sockets could be closed at any time, all of them are member
// sockets of this group. Therefore the first socket attempted to be closed will
// have to remove the socket from the group, and this will require lock on GroupLock,
// which is still applied here. So this will have to wait for this function to finish
// (or block on swait, in which case the lock is lifted) anyway.
for (vector<CUDTSocket*>::const_iterator si = ready_sockets.begin(); si != ready_sockets.end(); ++si)
{
CUDTSocket* ps = *si;
SRTSOCKET id = ps->m_SocketID;
ReadPos* p = NULL;
pit_t pe = m_Positions.find(id);
if (pe != m_Positions.end())
{
p = &pe->second;
// Possible results of comparison:
// x < 0: the sequence is in the past, the socket should be adjusted FIRST
// x = 0: the socket should be ready to get the exactly next packet
// x = 1: the case is already handled by GroupCheckPacketAhead.
// x > 1: AHEAD. DO NOT READ.
const int seqdiff = CSeqNo::seqcmp(p->mctrl.pktseq, m_RcvBaseSeqNo);
if (seqdiff > 1)
{
HLOGC(grlog.Debug,
log << "group/recv: EPOLL: @" << id << " %" << p->mctrl.pktseq << " AHEAD %" << m_RcvBaseSeqNo
<< ", not reading.");
continue;
}
}
else
{
// The position is not known, so get the position on which
// the socket is currently standing.
pair<pit_t, bool> ee = m_Positions.insert(make_pair(id, ReadPos(ps->core().m_iRcvLastSkipAck)));
p = &(ee.first->second);
HLOGC(grlog.Debug,
log << "group/recv: EPOLL: @" << id << " %" << p->mctrl.pktseq << " NEW SOCKET INSERTED");
}
// Read from this socket stubbornly, until:
// - reading is no longer possible (AGAIN)
// - the sequence difference is >= 1
for (;;)
{
SRT_MSGCTRL mctrl = srt_msgctrl_default;
// Read the data into the user's buffer. This is an optimistic
// prediction that we'll read the right data. This will be overwritten
// by "more correct data" if found more appropriate later. But we have to
// copy these data anyway anywhere, even if they need to fall on the floor later.
int stat;
char extrabuf[SRT_LIVE_MAX_PLSIZE];
char* msgbuf = NULL;
if (output_size)
{
// We already have the target data in `buf`. Now reading extra data potentially redundant (to be ignored)
// or AHEAD (to be buffered internally by the group)
msgbuf = extrabuf;
stat = ps->core().receiveMessage((extrabuf), SRT_LIVE_MAX_PLSIZE, (mctrl), CUDTUnited::ERH_RETURN);
HLOGC(grlog.Debug,
log << "group/recv: @" << id << " EXTRACTED EXTRA data with %" << mctrl.pktseq
<< " #" << mctrl.msgno << ": " << (stat <= 0 ? "(NOTHING)" : BufferStamp(extrabuf, stat))
<< (CSeqNo::seqcmp(mctrl.pktseq, m_RcvBaseSeqNo) > 1 ? " - TO STORE" : " - TO IGNORE"));
}
else
{
msgbuf = buf;
stat = ps->core().receiveMessage((buf), len, (mctrl), CUDTUnited::ERH_RETURN);
HLOGC(grlog.Debug,
log << "group/recv: @" << id << " EXTRACTED data with %" << mctrl.pktseq << " #"
<< mctrl.msgno << ": " << (stat <= 0 ? "(NOTHING)" : BufferStamp(buf, stat)));
}
if (stat == 0)
{
HLOGC(grlog.Debug, log << "group/recv @" << id << ": SPURIOUS epoll, ignoring");
// This is returned in case of "again". In case of errors, we have SRT_ERROR.
// Do not treat this as spurious, just stop reading.
break;
}
if (stat == SRT_ERROR)
{
HLOGC(grlog.Debug, log << "group/recv: @" << id << ": " << srt_getlasterror_str());
broken.insert(ps);
break;
}
// NOTE: checks against m_RcvBaseSeqNo and decisions based on it
// must NOT be done if m_RcvBaseSeqNo is SRT_SEQNO_NONE, which
// means that we are about to deliver the very first packet and we
// take its sequence number as a good deal.
// The order must be:
// - check discrepancy
// - record the sequence
// - check ordering.
// The second one must be done always, but failed discrepancy
// check should exclude the socket from any further checks.
// That's why the common check for m_RcvBaseSeqNo != SRT_SEQNO_NONE can't
// embrace everything below.
// We need to first qualify the sequence, just for a case
if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && !isValidSeqno(m_RcvBaseSeqNo, mctrl.pktseq))
{
// This error should be returned if the link turns out
// to be the only one, or set to the group data.
// err = SRT_ESECFAIL;
LOGC(grlog.Error,
log << "group/recv: @" << id << ": SEQUENCE DISCREPANCY: base=%" << m_RcvBaseSeqNo
<< " vs pkt=%" << mctrl.pktseq << ", setting ESECFAIL");
broken.insert(ps);
break;
}
// Rewrite it to the state for a case when next reading
// would not succeed. Do not insert the buffer here because
// this is only required when the sequence is ahead; for that
// it will be fixed later.
p->mctrl.pktseq = mctrl.pktseq;
if (m_RcvBaseSeqNo != SRT_SEQNO_NONE)
{
// Now we can safely check it.
const int seqdiff = CSeqNo::seqcmp(mctrl.pktseq, m_RcvBaseSeqNo);
if (seqdiff <= 0)
{
HLOGC(grlog.Debug,
log << "group/recv: @" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno
<< " BEHIND base=%" << m_RcvBaseSeqNo << " - discarding");
// The sequence is recorded, the packet has to be discarded.
m_stats.recvDiscard.count(stat);
continue;
}
// Now we have only two possibilities:
// seqdiff == 1: The very next sequence, we want to read and return the packet.
// seqdiff > 1: The packet is ahead - record the ahead packet, but continue with the others.
if (seqdiff > 1)
{
HLOGC(grlog.Debug,
log << "@" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " AHEAD base=%"
<< m_RcvBaseSeqNo);
p->packet.assign(msgbuf, msgbuf + stat);
p->mctrl = mctrl;
break; // Don't read from that socket anymore.
}
}
// We have seqdiff = 1, or we simply have the very first packet
// which's sequence is taken as a good deal. Update the sequence
// and record output.
if (output_size)
{
HLOGC(grlog.Debug,
log << "group/recv: @" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " REDUNDANT");
break;
}
HLOGC(grlog.Debug,
log << "group/recv: @" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " DELIVERING");
output_size = stat;
fillGroupData((w_mc), mctrl);
// Update stats as per delivery
m_stats.recv.count(output_size);
updateAvgPayloadSize(output_size);
// Record, but do not update yet, until all sockets are handled.
next_seq = mctrl.pktseq;
break;
}
}
#if ENABLE_HEAVY_LOGGING
if (!broken.empty())
{
std::ostringstream brks;
for (set<CUDTSocket*>::iterator b = broken.begin(); b != broken.end(); ++b)
brks << "@" << (*b)->m_SocketID << " ";
LOGC(grlog.Debug, log << "group/recv: REMOVING BROKEN: " << brks.str());
}
#endif
vector<SRTSOCKET> brokenid;
// Now remove all broken sockets from aheads, if any.
// Even if they have already delivered a packet.
for (set<CUDTSocket*>::iterator di = broken.begin(); di != broken.end(); ++di)
{
CUDTSocket* ps = *di;
m_Positions.erase(ps->m_SocketID);
//ps->setBrokenClosed();
}
// Force closing
{
InvertedLock ung (m_GroupLock);
for (set<CUDTSocket*>::iterator b = broken.begin(); b != broken.end(); ++b)
{
CUDT::uglobal().close(*b);
}
}
if (broken.size() >= size) // This > is for sanity check
{
// All broken
HLOGC(grlog.Debug, log << "group/recv: All sockets broken");
m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true);
throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
}
// May be required to be re-read.
broken.clear();
if (output_size)
{
// We have extracted something, meaning that we have the sequence shift.
// Update it now and don't do anything else with the sockets.
// Sanity check
if (next_seq == SRT_SEQNO_NONE)
{
LOGP(grlog.Error, "IPE: next_seq not set after output extracted!");
// This should never happen, but the only way to keep the code
// safe an recoverable is to use the incremented sequence. By
// leaving the sequence as is there's a risk of hangup.
// Not doing it in case of SRT_SEQNO_NONE as it would make a valid %0.
if (m_RcvBaseSeqNo != SRT_SEQNO_NONE)
m_RcvBaseSeqNo = CSeqNo::incseq(m_RcvBaseSeqNo);
}
else
{
m_RcvBaseSeqNo = next_seq;
}
const ReadPos* pos = checkPacketAhead();
if (!pos)
{
// Don't clear the read-readinsess state if you have a packet ahead because
// if you have, the next read call will return it.
m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false);
}
HLOGC(grlog.Debug,
log << "group/recv: successfully extracted packet size=" << output_size << " - returning");
return output_size;
}
HLOGC(grlog.Debug, log << "group/recv: NOT extracted anything - checking for a need to kick kangaroos");
// Check if we have any sockets left :D
// Here we surely don't have any more HORSES,
// only ELEPHANTS and KANGAROOS. Qualify them and
// attempt to at least take advantage of KANGAROOS.
// In this position all links are either:
// - updated to the current position
// - updated to the newest possible possition available
// - not yet ready for extraction (not present in the group)
// If we haven't extracted the very next sequence position,
// it means that we might only have the ahead packets read,
// that is, the next sequence has been dropped by all links.
if (!m_Positions.empty())
{
// This might notify both lingering links, which didn't
// deliver the required sequence yet, and links that have
// the sequence ahead. Review them, and if you find at
// least one packet behind, just wait for it to be ready.
// Use again the waiting function because we don't want
// the general waiting procedure to skip others.
set<SRTSOCKET> elephants;
// const because it's `typename decltype(m_Positions)::value_type`
pair<const SRTSOCKET, ReadPos>* slowest_kangaroo = 0;
for (pit_t rp = m_Positions.begin(); rp != m_Positions.end(); ++rp)
{
// NOTE that m_RcvBaseSeqNo in this place wasn't updated
// because we haven't successfully extracted anything.
int seqdiff = CSeqNo::seqcmp(rp->second.mctrl.pktseq, m_RcvBaseSeqNo);
if (seqdiff < 0)
{
elephants.insert(rp->first);
}
// If seqdiff == 0, we have a socket ON TRACK.
else if (seqdiff > 0)
{
// If there's already a slowest_kangaroo, seqdiff decides if this one is slower.
// Otherwise it is always slower by having no competition.
seqdiff = slowest_kangaroo
? CSeqNo::seqcmp(slowest_kangaroo->second.mctrl.pktseq, rp->second.mctrl.pktseq)
: 1;
if (seqdiff > 0)
{
slowest_kangaroo = &*rp;
}
}
}
// Note that if no "slowest_kangaroo" was found, it means
// that we don't have kangaroos.
if (slowest_kangaroo)
{
// We have a slowest kangaroo. Elephants must be ignored.
// Best case, they will get revived, worst case they will be
// soon broken.
//
// As we already have the packet delivered by the slowest
// kangaroo, we can simply return it.
// Check how many were skipped and add them to the stats
const int32_t jump = (CSeqNo(slowest_kangaroo->second.mctrl.pktseq) - CSeqNo(m_RcvBaseSeqNo)) - 1;
if (jump > 0)
{
m_stats.recvDrop.count(stats::BytesPackets(jump * static_cast<uint64_t>(avgRcvPacketSize()), jump));
LOGC(grlog.Warn,
log << "@" << m_GroupID << " GROUP RCV-DROPPED " << jump << " packet(s): seqno %"
<< m_RcvBaseSeqNo << " to %" << slowest_kangaroo->second.mctrl.pktseq);
}
m_RcvBaseSeqNo = slowest_kangaroo->second.mctrl.pktseq;
vector<char>& pkt = slowest_kangaroo->second.packet;
if (size_t(len) < pkt.size())
throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0);
HLOGC(grlog.Debug,
log << "@" << slowest_kangaroo->first << " KANGAROO->HORSE %"
<< slowest_kangaroo->second.mctrl.pktseq << " #" << slowest_kangaroo->second.mctrl.msgno
<< ": " << BufferStamp(&pkt[0], pkt.size()));
memcpy(buf, &pkt[0], pkt.size());
fillGroupData((w_mc), slowest_kangaroo->second.mctrl);
len = pkt.size();
pkt.clear();
// Update stats as per delivery
m_stats.recv.count(len);
updateAvgPayloadSize(len);
// It is unlikely to have a packet ahead because usually having one packet jumped-ahead
// clears the possibility of having aheads at all.
// XXX Research if this is possible at all; if it isn't, then don't waste time on
// looking for it.
const ReadPos* pos = checkPacketAhead();
if (!pos)
{
// Don't clear the read-readinsess state if you have a packet ahead because
// if you have, the next read call will return it.
m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false);
}
return len;
}
HLOGC(grlog.Debug,
log << "group/recv: "
<< (elephants.empty() ? "NO LINKS REPORTED ANY FRESHER PACKET." : "ALL LINKS ELEPHANTS.")
<< " Re-polling.");
}
else
{
HLOGC(grlog.Debug, log << "group/recv: POSITIONS EMPTY - Re-polling.");
}
}
}
#endif
// [[using locked(m_GroupLock)]]
CUDTGroup::ReadPos* CUDTGroup::checkPacketAhead()
{
typedef map<SRTSOCKET, ReadPos>::iterator pit_t;
ReadPos* out = 0;
// This map no longer maps only ahead links.
// Here are all links, and whether ahead, it's defined by the sequence.
for (pit_t i = m_Positions.begin(); i != m_Positions.end(); ++i)
{
// i->first: socket ID
// i->second: ReadPos { sequence, packet }
// We are not interested with the socket ID because we
// aren't going to read from it - we have the packet already.
ReadPos& a = i->second;
const int seqdiff = CSeqNo::seqcmp(a.mctrl.pktseq, m_RcvBaseSeqNo);
if (seqdiff == 1)
{
// The very next packet. Return it.
HLOGC(grlog.Debug,
log << "group/recv: Base %" << m_RcvBaseSeqNo << " ahead delivery POSSIBLE %" << a.mctrl.pktseq
<< " #" << a.mctrl.msgno << " from @" << i->first << ")");
out = &a;
}
else if (seqdiff < 1 && !a.packet.empty())
{
HLOGC(grlog.Debug,
log << "group/recv: @" << i->first << " dropping collected ahead %" << a.mctrl.pktseq << "#"
<< a.mctrl.msgno << " with base %" << m_RcvBaseSeqNo);
a.packet.clear();
}
// In case when it's >1, keep it in ahead
}
return out;
}
const char* CUDTGroup::StateStr(CUDTGroup::GroupState st)
{
@ -3684,7 +3112,7 @@ void CUDTGroup::sendBackup_CheckUnstableSockets(SendBackupCtx& w_sendBackupCtx,
<< " is qualified as unstable, but does not have the 'unstable since' timestamp. Still marking for closure.");
}
const int unstable_for_ms = count_milliseconds(currtime - sock.m_tsUnstableSince);
const int unstable_for_ms = (int)count_milliseconds(currtime - sock.m_tsUnstableSince);
if (unstable_for_ms < sock.peerIdleTimeout_ms())
continue;
@ -4399,7 +3827,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc)
{
// NOTE: an exception from here will interrupt the loop
// and will be caught in the upper level.
stat = core.sendmsg2(i->data, i->size, (i->mc));
stat = core.sendmsg2(i->data, (int)i->size, (i->mc));
if (stat == -1)
{
// Stop sending if one sending ended up with error
@ -4529,7 +3957,7 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s)
HLOGC(grlog.Debug,
log << "updateLatestRcv: BACKUP group, updating from active link @" << s->m_SocketID << " with %"
<< s->core().m_iRcvLastSkipAck);
<< s->core().m_iRcvLastAck);
CUDT* source = &s->core();
vector<CUDT*> targets;

@ -155,7 +155,7 @@ public:
srt::sync::ScopedLock g(m_GroupLock);
bool empty = false;
HLOGC(gmlog.Debug, log << "group/remove: going to remove @" << id << " from $" << m_GroupID);
LOGC(gmlog.Note, log << "group/remove: removing member @" << id << " from group $" << m_GroupID);
gli_t f = std::find_if(m_Group.begin(), m_Group.end(), HaveID(id));
if (f != m_Group.end())
@ -194,9 +194,6 @@ public:
m_bConnected = false;
}
// XXX BUGFIX
m_Positions.erase(id);
return !empty;
}
@ -265,7 +262,7 @@ private:
/// @param[in] pktseq Packet sequence number currently tried to be sent
/// @param[out] w_u CUDT unit of the current member (to allow calling overrideSndSeqNo)
/// @param[out] w_curseq Group's current sequence number (either -1 or the value used already for other links)
/// @param[out] w_final_stat w_final_stat = send_status if sending succeded.
/// @param[out] w_final_stat w_final_stat = send_status if sending succeeded.
///
/// @returns true if the sending operation result (submitted in stat) is a success, false otherwise.
bool sendBackup_CheckSendStatus(const time_point& currtime,
@ -406,7 +403,9 @@ private:
SRTSOCKET m_PeerGroupID;
struct GroupContainer
{
std::list<SocketData> m_List;
private:
std::list<SocketData> m_List;
sync::atomic<size_t> m_SizeCache;
/// This field is used only by some types of groups that need
/// to keep track as to which link was lately used. Note that
@ -414,8 +413,11 @@ private:
/// must be appropriately reset.
gli_t m_LastActiveLink;
public:
GroupContainer()
: m_LastActiveLink(m_List.end())
: m_SizeCache(0)
, m_LastActiveLink(m_List.end())
{
}
@ -425,18 +427,18 @@ private:
gli_t begin() { return m_List.begin(); }
gli_t end() { return m_List.end(); }
bool empty() { return m_List.empty(); }
void push_back(const SocketData& data) { m_List.push_back(data); }
void push_back(const SocketData& data) { m_List.push_back(data); ++m_SizeCache; }
void clear()
{
m_LastActiveLink = end();
m_List.clear();
m_SizeCache = 0;
}
size_t size() { return m_List.size(); }
size_t size() { return m_SizeCache; }
void erase(gli_t it);
};
GroupContainer m_Group;
const bool m_bSyncOnMsgNo; // It goes into a dedicated HS field. Could be true for balancing groups (not implemented).
SRT_GROUP_TYPE m_type;
CUDTSocket* m_listener; // A "group" can only have one listener.
srt::sync::atomic<int> m_iBusy;
@ -641,20 +643,6 @@ private:
time_point m_tsStartTime;
time_point m_tsRcvPeerStartTime;
struct ReadPos
{
std::vector<char> packet;
SRT_MSGCTRL mctrl;
ReadPos(int32_t s)
: mctrl(srt_msgctrl_default)
{
mctrl.pktseq = s;
}
};
std::map<SRTSOCKET, ReadPos> m_Positions;
ReadPos* checkPacketAhead();
void recv_CollectAliveAndBroken(std::vector<srt::CUDTSocket*>& w_alive, std::set<srt::CUDTSocket*>& w_broken);
/// The function polls alive member sockets and retrieves a list of read-ready.
@ -813,7 +801,6 @@ public:
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, int32_t, currentSchedSequence, m_iLastSchedSeqNo);
SRTU_PROPERTY_RRW(std::set<int>&, epollset, m_sPollID);
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, int64_t, latency, m_iTsbPdDelay_us);
SRTU_PROPERTY_RO(bool, synconmsgno, m_bSyncOnMsgNo);
SRTU_PROPERTY_RO(bool, closing, m_bClosing);
};

@ -140,7 +140,8 @@ const char* srt_rejectreason_name [] = {
"CONGESTION",
"FILTER",
"GROUP",
"TIMEOUT"
"TIMEOUT",
"CRYPTO"
};
}

@ -61,10 +61,12 @@ namespace srt_logging
{
extern Logger qrlog;
extern Logger qslog;
extern Logger tslog;
}
using srt_logging::qrlog;
using srt_logging::qslog;
using srt_logging::tslog;
using namespace srt::sync;
@ -504,7 +506,7 @@ srt::CRcvLossList::~CRcvLossList()
delete[] m_caSeq;
}
void srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2)
int srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2)
{
// Data to be inserted must be larger than all those in the list
if (m_iLargestSeq != SRT_SEQNO_NONE && CSeqNo::seqcmp(seqno1, m_iLargestSeq) <= 0)
@ -522,7 +524,7 @@ void srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2)
log << "RCV-LOSS/insert: (" << seqno1 << "," << seqno2
<< ") to be inserted is too small: m_iLargestSeq=" << m_iLargestSeq << ", m_iLength=" << m_iLength
<< ", m_iHead=" << m_iHead << ", m_iTail=" << m_iTail << " -- REJECTING");
return;
return 0;
}
}
m_iLargestSeq = seqno2;
@ -538,19 +540,19 @@ void srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2)
m_caSeq[m_iHead].inext = -1;
m_caSeq[m_iHead].iprior = -1;
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
return;
const int n = CSeqNo::seqlen(seqno1, seqno2);
m_iLength += n;
return n;
}
// otherwise searching for the position where the node should be
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno1);
const int offset = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno1);
if (offset < 0)
{
LOGC(qrlog.Error,
log << "RCV-LOSS/insert: IPE: new LOSS %(" << seqno1 << "-" << seqno2 << ") PREDATES HEAD %"
<< m_caSeq[m_iHead].seqstart << " -- REJECTING");
return;
return -1;
}
int loc = (m_iHead + offset) % m_iSize;
@ -575,7 +577,9 @@ void srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2)
m_iTail = loc;
}
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
const int n = CSeqNo::seqlen(seqno1, seqno2);
m_iLength += n;
return n;
}
bool srt::CRcvLossList::remove(int32_t seqno)
@ -715,22 +719,45 @@ bool srt::CRcvLossList::remove(int32_t seqno)
bool srt::CRcvLossList::remove(int32_t seqno1, int32_t seqno2)
{
if (seqno1 <= seqno2)
if (CSeqNo::seqcmp(seqno1, seqno2) > 0)
{
for (int32_t i = seqno1; i <= seqno2; ++i)
remove(i);
return false;
}
else
for (int32_t i = seqno1; CSeqNo::seqcmp(i, seqno2) <= 0; i = CSeqNo::incseq(i))
{
for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++j)
remove(j);
for (int32_t k = 0; k <= seqno2; ++k)
remove(k);
remove(i);
}
return true;
}
int32_t srt::CRcvLossList::removeUpTo(int32_t seqno_last)
{
int32_t first = getFirstLostSeq();
if (first == SRT_SEQNO_NONE)
{
//HLOGC(tslog.Debug, log << "rcv-loss: DROP to %" << seqno_last << " - empty list");
return first; // empty, so nothing to remove
}
if (CSeqNo::seqcmp(seqno_last, first) < 0)
{
//HLOGC(tslog.Debug, log << "rcv-loss: DROP to %" << seqno_last << " - first %" << first << " is newer, exitting");
return first; // seqno_last older than first - nothing to remove
}
HLOGC(tslog.Debug, log << "rcv-loss: DROP to %" << seqno_last << " ...");
// NOTE: seqno_last is past-the-end here. Removed are only seqs
// that are earlier than this.
for (int32_t i = first; CSeqNo::seqcmp(i, seqno_last) <= 0; i = CSeqNo::incseq(i))
{
//HLOGC(tslog.Debug, log << "... removing %" << i);
remove(i);
}
return first;
}
bool srt::CRcvLossList::find(int32_t seqno1, int32_t seqno2) const
{
if (0 == m_iLength)
@ -839,8 +866,10 @@ srt::CRcvFreshLoss::Emod srt::CRcvFreshLoss::revoke(int32_t lo, int32_t hi)
// ITEM: <lo, hi> <--- delete
// If the sequence range is older than the range to be revoked,
// delete it anyway.
if (CSeqNo::seqcmp(lo, seq[1]) > 0)
if (lo != SRT_SEQNO_NONE && CSeqNo::seqcmp(lo, seq[1]) > 0)
return DELETE;
// IF <lo> is NONE, then rely simply on that item.hi <% arg.hi,
// which is a condition at the end.
// LOHI: <lo, hi>
// ITEM: <lo, hi> <-- NOTFOUND
@ -868,3 +897,53 @@ srt::CRcvFreshLoss::Emod srt::CRcvFreshLoss::revoke(int32_t lo, int32_t hi)
return DELETE;
}
bool srt::CRcvFreshLoss::removeOne(std::deque<CRcvFreshLoss>& w_container, int32_t sequence, int* pw_had_ttl)
{
for (size_t i = 0; i < w_container.size(); ++i)
{
const int had_ttl = w_container[i].ttl;
Emod wh = w_container[i].revoke(sequence);
if (wh == NONE)
continue; // Not found. Search again.
if (wh == DELETE) // ... oo ... x ... o ... => ... oo ... o ...
{
// Removed the only element in the record - remove the record.
w_container.erase(w_container.begin() + i);
}
else if (wh == SPLIT) // ... ooxooo ... => ... oo ... ooo ...
{
// Create a new element that will hold the upper part of the range,
// and the found one modify to be the lower part of the range.
// Keep the current end-of-sequence value for the second element
int32_t next_end = w_container[i].seq[1];
// seq-1 set to the end of this element
w_container[i].seq[1] = CSeqNo::decseq(sequence);
// seq+1 set to the begin of the next element
int32_t next_begin = CSeqNo::incseq(sequence);
// Use position of the NEXT element because insertion happens BEFORE pointed element.
// Use the same TTL (will stay the same in the other one).
w_container.insert(w_container.begin() + i + 1,
CRcvFreshLoss(next_begin, next_end, w_container[i].ttl));
}
// For STRIPPED: ... xooo ... => ... ooo ...
// i.e. there's nothing to do.
// Every loss is unique. We're done here.
if (pw_had_ttl)
*pw_had_ttl = had_ttl;
return true;
}
if (pw_had_ttl)
*pw_had_ttl = 0;
return false;
}

@ -53,6 +53,8 @@ modified by
#ifndef INC_SRT_LIST_H
#define INC_SRT_LIST_H
#include <deque>
#include "udt.h"
#include "common.h"
@ -84,6 +86,12 @@ public:
void traceState() const;
// Debug/unittest support.
int head() const { return m_iHead; }
int next(int loc) const { return m_caSeq[loc].inext; }
int last() const { return m_iLastInsertPos; }
private:
struct Seq
{
@ -118,6 +126,8 @@ private:
/// @param seqno2 last sequence number in range (SRT_SEQNO_NONE if no range)
bool updateElement(int pos, int32_t seqno1, int32_t seqno2);
static const int LOC_NONE = -1;
private:
CSndLossList(const CSndLossList&);
CSndLossList& operator=(const CSndLossList&);
@ -134,8 +144,8 @@ public:
/// Insert a series of loss seq. no. between "seqno1" and "seqno2" into the receiver's loss list.
/// @param [in] seqno1 sequence number starts.
/// @param [in] seqno2 seqeunce number ends.
void insert(int32_t seqno1, int32_t seqno2);
/// @return length of the loss record inserted (seqlen(seqno1, seqno2)), -1 on error.
int insert(int32_t seqno1, int32_t seqno2);
/// Remove a loss seq. no. from the receiver's loss list.
/// @param [in] seqno sequence number.
@ -150,6 +160,12 @@ public:
bool remove(int32_t seqno1, int32_t seqno2);
/// Remove all numbers that precede the given sequence number.
/// @param [in] seqno sequence number.
/// @return the first removed sequence number
int32_t removeUpTo(int32_t seqno);
/// Find if there is any lost packets whose sequence number falling seqno1 and seqno2.
/// @param [in] seqno1 start sequence number.
/// @param [in] seqno2 end sequence number.
@ -264,6 +280,8 @@ struct CRcvFreshLoss
Emod revoke(int32_t sequence);
Emod revoke(int32_t lo, int32_t hi);
static bool removeOne(std::deque<CRcvFreshLoss>& w_container, int32_t sequence, int* had_ttl = NULL);
};
} // namespace srt

@ -60,6 +60,7 @@ written by
// LOGF uses printf-like style formatting.
// Usage: LOGF(gglog.Debug, "%s: %d", param1.c_str(), int(param2));
// NOTE: LOGF is deprecated and should not be used
#define LOGF(logdes, ...) if (logdes.CheckEnabled()) logdes().setloc(__FILE__, __LINE__, __FUNCTION__).form(__VA_ARGS__)
// LOGP is C++11 only OR with only one string argument.
@ -165,14 +166,24 @@ public:
// See Logger::Logger; we know this has normally 2 characters,
// except !!FATAL!!, which has 9. Still less than 32.
strcpy(prefix, your_pfx);
// If the size of the FA name together with severity exceeds the size,
// just skip the former.
if (logger_pfx && strlen(prefix) + strlen(logger_pfx) + 1 < MAX_PREFIX_SIZE)
{
strcat(prefix, ":");
strcat(prefix, logger_pfx);
#if defined(_MSC_VER) && _MSC_VER < 1900
_snprintf(prefix, MAX_PREFIX_SIZE, "%s:%s", your_pfx, logger_pfx);
#else
snprintf(prefix, MAX_PREFIX_SIZE + 1, "%s:%s", your_pfx, logger_pfx);
#endif
}
else
{
#ifdef _MSC_VER
strncpy_s(prefix, MAX_PREFIX_SIZE + 1, your_pfx, _TRUNCATE);
#else
strncpy(prefix, your_pfx, MAX_PREFIX_SIZE);
prefix[MAX_PREFIX_SIZE] = '\0';
#endif
}
}
@ -242,7 +253,9 @@ public:
return *this;
}
DummyProxy& form(const char*, ...)
// DEPRECATED: DO NOT use LOGF/HLOGF macros anymore.
// Use iostream-style formatting with LOGC or a direct argument with LOGP.
SRT_ATR_DEPRECATED_PX DummyProxy& form(const char*, ...) SRT_ATR_DEPRECATED
{
return *this;
}
@ -356,7 +369,11 @@ struct LogDispatcher::Proxy
{
char buf[512];
vsprintf(buf, fmts, ap);
#if defined(_MSC_VER) && _MSC_VER < 1900
_vsnprintf(buf, sizeof(buf) - 1, fmts, ap);
#else
vsnprintf(buf, sizeof(buf), fmts, ap);
#endif
size_t len = strlen(buf);
if ( buf[len-1] == '\n' )
{

@ -27,7 +27,7 @@
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
@ -38,165 +38,166 @@
that follows (in reverse chronological order):
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
either statically or dynamically; added missing #include <string.h>
in library.
either statically or dynamically; added missing #include <string.h>
in library.
2002-03-11 lpd Corrected argument list for main(), and added int return
type, in test program and T value program.
type, in test program and T value program.
2002-02-21 lpd Added missing #include <stdio.h> in test program.
2000-07-03 lpd Patched to eliminate warnings about "constant is
unsigned in ANSI C, signed in traditional"; made test program
self-checking.
unsigned in ANSI C, signed in traditional"; made test program
self-checking.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1999-05-03 lpd Original version.
*/
#include "md5.h"
#include <stdint.h>
#include <string.h>
/*
* All symbols have been put under the srt namespace
* to avoid potential linkage conflicts.
*/
namespace srt {
namespace srt
{
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#ifdef ARCH_IS_BIG_ENDIAN
# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
#else
# define BYTE_ORDER 0
#define BYTE_ORDER 0
#endif
#define T_MASK ((md5_word_t)~0)
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
#define T3 0x242070db
#define T3 0x242070db
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
#define T6 0x4787c62a
#define T6 0x4787c62a
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
#define T9 0x698098d8
#define T9 0x698098d8
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
#define T13 0x6b901122
#define T13 0x6b901122
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
#define T16 0x49b40821
#define T16 0x49b40821
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
#define T19 0x265e5a51
#define T19 0x265e5a51
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
#define T22 0x02441453
#define T22 0x02441453
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
#define T25 0x21e1cde6
#define T25 0x21e1cde6
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
#define T28 0x455a14ed
#define T28 0x455a14ed
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
#define T31 0x676f02d9
#define T31 0x676f02d9
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
#define T35 0x6d9d6122
#define T35 0x6d9d6122
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
#define T38 0x4bdecfa9
#define T38 0x4bdecfa9
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
#define T41 0x289b7ec6
#define T41 0x289b7ec6
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
#define T44 0x04881d05
#define T44 0x04881d05
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
#define T47 0x1fa27cf8
#define T47 0x1fa27cf8
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
#define T50 0x432aff97
#define T50 0x432aff97
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
#define T53 0x655b59c3
#define T53 0x655b59c3
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
#define T57 0x6fa87e4f
#define T57 0x6fa87e4f
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
#define T60 0x4e0811a1
#define T60 0x4e0811a1
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
#define T63 0x2ad7d2bb
#define T63 0x2ad7d2bb
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
static void
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
static void md5_process(md5_state_t* pms, const md5_byte_t* data /*[64]*/)
{
md5_word_t
a = pms->abcd[0], b = pms->abcd[1],
c = pms->abcd[2], d = pms->abcd[3];
md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3];
md5_word_t t;
#if BYTE_ORDER > 0
/* Define storage only for big-endian CPUs. */
md5_word_t X[16];
#else
/* Define storage for little-endian or both types of CPUs. */
md5_word_t xbuf[16];
const md5_word_t *X;
md5_word_t xbuf[16];
const md5_word_t* X;
#endif
{
#if BYTE_ORDER == 0
/*
* Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient
* algorithm on the latter.
*/
static const int w = 1;
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
/*
* Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient
* algorithm on the latter.
*/
static const int w = 1;
if (*((const md5_byte_t*)&w)) /* dynamic little-endian */
#endif
#if BYTE_ORDER <= 0 /* little-endian */
{
/*
* On little-endian machines, we can process properly aligned
* data without copying it.
*/
if (!(uintptr_t(data) & 3))
{
/* data are properly aligned */
X = (const md5_word_t*)data;
}
else
{
/* not aligned */
memcpy((xbuf), data, 64);
X = xbuf;
}
}
#endif
#if BYTE_ORDER <= 0 /* little-endian */
{
/*
* On little-endian machines, we can process properly aligned
* data without copying it.
*/
if (!((data - (const md5_byte_t *)0) & 3)) {
/* data are properly aligned */
X = (const md5_word_t *)data;
} else {
/* not aligned */
memcpy((xbuf), data, 64);
X = xbuf;
}
}
#if BYTE_ORDER == 0
else /* dynamic big-endian */
#endif
#if BYTE_ORDER >= 0 /* big-endian */
{
/*
* On big-endian machines, we must arrange the bytes in the
* right order.
*/
const md5_byte_t* xp = data;
int i;
#if BYTE_ORDER == 0
else /* dynamic big-endian */
X = xbuf; /* (dynamic only) */
#else
#define xbuf X /* (static only) */
#endif
#if BYTE_ORDER >= 0 /* big-endian */
{
/*
* On big-endian machines, we must arrange the bytes in the
* right order.
*/
const md5_byte_t *xp = data;
int i;
# if BYTE_ORDER == 0
X = xbuf; /* (dynamic only) */
# else
# define xbuf X /* (static only) */
# endif
for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
}
for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
}
#endif
}
@ -206,184 +207,179 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
/* Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + F(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
#define SET(a, b, c, d, k, s, Ti) \
t = a + F(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(c, d, a, b, 10, 17, T11);
SET(b, c, d, a, 11, 22, T12);
SET(a, b, c, d, 12, 7, T13);
SET(a, b, c, d, 12, 7, T13);
SET(d, a, b, c, 13, 12, T14);
SET(c, d, a, b, 14, 17, T15);
SET(b, c, d, a, 15, 22, T16);
#undef SET
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + G(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
#define SET(a, b, c, d, k, s, Ti) \
t = a + G(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(c, d, a, b, 15, 14, T23);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 12, 20, T32);
#undef SET
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti)\
t = a + H(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
#define SET(a, b, c, d, k, s, Ti) \
t = a + H(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35);
SET(b, c, d, a, 14, 23, T36);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(b, c, d, a, 10, 23, T40);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(d, a, b, c, 12, 11, T46);
SET(c, d, a, b, 15, 16, T47);
SET(b, c, d, a, 2, 23, T48);
SET(b, c, d, a, 2, 23, T48);
#undef SET
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + I(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
#define SET(a, b, c, d, k, s, Ti) \
t = a + I(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(c, d, a, b, 10, 15, T55);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(d, a, b, c, 15, 10, T58);
SET(c, d, a, b, 6, 15, T59);
SET(c, d, a, b, 6, 15, T59);
SET(b, c, d, a, 13, 21, T60);
SET(a, b, c, d, 4, 6, T61);
SET(a, b, c, d, 4, 6, T61);
SET(d, a, b, c, 11, 10, T62);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
#undef SET
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
pms->abcd[0] += a;
pms->abcd[1] += b;
pms->abcd[2] += c;
pms->abcd[3] += d;
}
void
md5_init(md5_state_t *pms)
void md5_init(md5_state_t* pms)
{
pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
pms->abcd[3] = 0x10325476;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
pms->abcd[3] = 0x10325476;
}
void
md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
void md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes)
{
const md5_byte_t *p = data;
int left = nbytes;
int offset = (pms->count[0] >> 3) & 63;
md5_word_t nbits = (md5_word_t)(nbytes << 3);
const md5_byte_t* p = data;
int left = nbytes;
int offset = (pms->count[0] >> 3) & 63;
md5_word_t nbits = (md5_word_t)(nbytes << 3);
if (nbytes <= 0)
return;
return;
/* Update the message length. */
pms->count[1] += nbytes >> 29;
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
pms->count[1]++;
/* Process an initial partial block. */
if (offset) {
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy((pms->buf + offset), p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
if (offset)
{
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy((pms->buf + offset), p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
/* Process full blocks. */
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
md5_process(pms, p);
/* Process a final partial block. */
if (left)
memcpy((pms->buf), p, left);
memcpy((pms->buf), p, left);
}
void
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
void md5_finish(md5_state_t* pms, md5_byte_t digest[16])
{
static const md5_byte_t pad[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
md5_byte_t data[8];
int i;
static const md5_byte_t pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
md5_byte_t data[8];
int i;
/* Save the length before padding. */
for (i = 0; i < 8; ++i)
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */
md5_append(pms, data, 8);
for (i = 0; i < 16; ++i)
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}
} // namespace srt

@ -27,7 +27,7 @@
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
@ -38,23 +38,24 @@
that follows (in reverse chronological order):
2002-04-13 lpd Removed support for non-ANSI compilers; removed
references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically.
references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>.
added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>.
1999-05-03 lpd Original version.
*/
#ifndef md5_INCLUDED
# define md5_INCLUDED
#define md5_INCLUDED
/*
* All symbols have been put under the srt namespace
* to avoid potential linkage conflicts.
*/
namespace srt {
namespace srt
{
/*
* This package supports both compile-time and run-time determination of CPU
@ -67,23 +68,24 @@ namespace srt {
*/
typedef unsigned char md5_byte_t; /* 8-bit byte */
typedef unsigned int md5_word_t; /* 32-bit word */
typedef unsigned int md5_word_t; /* 32-bit word */
/* Define the state of the MD5 Algorithm. */
typedef struct md5_state_s {
md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */
typedef struct md5_state_s
{
md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */
} md5_state_t;
/* Initialize the algorithm. */
void md5_init(md5_state_t *pms);
void md5_init(md5_state_t* pms);
/* Append a string to the message. */
void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
void md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes);
/* Finish the message and return the digest. */
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
void md5_finish(md5_state_t* pms, md5_byte_t digest[16]);
} // namespace srt

@ -151,11 +151,11 @@ modified by
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Loss List Field Coding:
// For any consectutive lost seqeunce numbers that the differnece between
// For any consecutive lost seqeunce numbers that the differnece between
// the last and first is more than 1, only record the first (a) and the
// the last (b) sequence numbers in the loss list field, and modify the
// the first bit of a to 1.
// For any single loss or consectutive loss less than 2 packets, use
// For any single loss or consecutive loss less than 2 packets, use
// the original sequence numbers in the field.
#include "platform_sys.h"
@ -172,9 +172,12 @@ extern Logger inlog;
}
using namespace srt_logging;
namespace srt {
// Set up the aliases in the constructure
srt::CPacket::CPacket()
: m_extra_pad()
CPacket::CPacket()
: m_nHeader() // Silences GCC 12 warning "used uninitialized".
, m_extra_pad()
, m_data_owned(false)
, m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO]))
, m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO]))
@ -194,12 +197,12 @@ srt::CPacket::CPacket()
m_PacketVector[PV_DATA].set(NULL, 0);
}
char* srt::CPacket::getData()
char* CPacket::getData()
{
return (char*)m_PacketVector[PV_DATA].dataRef();
}
void srt::CPacket::allocate(size_t alloc_buffer_size)
void CPacket::allocate(size_t alloc_buffer_size)
{
if (m_data_owned)
{
@ -213,14 +216,15 @@ void srt::CPacket::allocate(size_t alloc_buffer_size)
m_data_owned = true;
}
void srt::CPacket::deallocate()
void CPacket::deallocate()
{
if (m_data_owned)
delete[](char*) m_PacketVector[PV_DATA].data();
m_PacketVector[PV_DATA].set(NULL, 0);
m_data_owned = false;
}
char* srt::CPacket::release()
char* CPacket::release()
{
// When not owned, release returns NULL.
char* buffer = NULL;
@ -234,31 +238,99 @@ char* srt::CPacket::release()
return buffer;
}
srt::CPacket::~CPacket()
CPacket::~CPacket()
{
// PV_HEADER is always owned, PV_DATA may use a "borrowed" buffer.
// Delete the internal buffer only if it was declared as owned.
if (m_data_owned)
delete[](char*) m_PacketVector[PV_DATA].data();
deallocate();
}
size_t srt::CPacket::getLength() const
size_t CPacket::getLength() const
{
return m_PacketVector[PV_DATA].size();
}
void srt::CPacket::setLength(size_t len)
void CPacket::setLength(size_t len)
{
m_PacketVector[PV_DATA].setLength(len);
}
void srt::CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size)
void CPacket::setLength(size_t len, size_t cap)
{
SRT_ASSERT(len <= cap);
setLength(len);
m_zCapacity = cap;
}
#if ENABLE_HEAVY_LOGGING
// Debug only
static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, void* rparam, const size_t size)
{
// This may be changed over time, so use special interpretation
// only for certain types, and still display all data, no matter
// if it is expected to provide anything or not.
std::ostringstream out;
out << "ARG=";
if (lparam)
out << *lparam;
else
out << "none";
if (size == 0)
{
out << " [no data]";
return out.str();
}
else if (!rparam)
{
out << " [ {" << size << "} ]";
return out.str();
}
bool interp_as_seq = (pkttype == UMSG_LOSSREPORT || pkttype == UMSG_DROPREQ);
bool display_dec = (pkttype == UMSG_ACK || pkttype == UMSG_ACKACK || pkttype == UMSG_DROPREQ);
out << " [ ";
// Will be effective only for hex/oct.
out << std::showbase;
const size_t size32 = size/4;
for (size_t i = 0; i < size32; ++i)
{
int32_t val = ((int32_t*)rparam)[i];
if (interp_as_seq)
{
if (val & LOSSDATA_SEQNO_RANGE_FIRST)
out << "<" << (val & (~LOSSDATA_SEQNO_RANGE_FIRST)) << ">";
else
out << val;
}
else
{
if (!display_dec)
{
out << std::hex;
out << val << "/";
out << std::dec;
}
out << val;
}
out << " ";
}
out << "]";
return out.str();
}
#endif
void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size)
{
// Set (bit-0 = 1) and (bit-1~15 = type)
setControl(pkttype);
HLOGC(inlog.Debug,
log << "pack: type=" << MessageTypeStr(pkttype) << " ARG=" << (lparam ? Sprint(*lparam) : std::string("NULL"))
<< " [ " << (rparam ? Sprint(*(int32_t*)rparam) : std::string()) << " ]");
HLOGC(inlog.Debug, log << "pack: type=" << MessageTypeStr(pkttype) << " " << FormatNumbers(pkttype, lparam, rparam, size));
// Set additional information and control information field
switch (pkttype)
@ -364,7 +436,7 @@ void srt::CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rpa
}
}
void srt::CPacket::toNL()
void CPacket::toNL()
{
// XXX USE HtoNLA!
if (isControl())
@ -382,7 +454,7 @@ void srt::CPacket::toNL()
}
}
void srt::CPacket::toHL()
void CPacket::toHL()
{
// convert back into local host order
uint32_t* p = m_nHeader;
@ -399,22 +471,22 @@ void srt::CPacket::toHL()
}
}
srt::IOVector* srt::CPacket::getPacketVector()
IOVector* CPacket::getPacketVector()
{
return m_PacketVector;
}
srt::UDTMessageType srt::CPacket::getType() const
UDTMessageType CPacket::getType() const
{
return UDTMessageType(SEQNO_MSGTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]));
}
int srt::CPacket::getExtendedType() const
int CPacket::getExtendedType() const
{
return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
}
int32_t srt::CPacket::getAckSeqNo() const
int32_t CPacket::getAckSeqNo() const
{
// read additional information field
// This field is used only in UMSG_ACK and UMSG_ACKACK,
@ -423,7 +495,7 @@ int32_t srt::CPacket::getAckSeqNo() const
return m_nHeader[SRT_PH_MSGNO];
}
uint16_t srt::CPacket::getControlFlags() const
uint16_t CPacket::getControlFlags() const
{
// This returns exactly the "extended type" value,
// which is not used at all in case when the standard
@ -432,17 +504,17 @@ uint16_t srt::CPacket::getControlFlags() const
return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
}
srt::PacketBoundary srt::CPacket::getMsgBoundary() const
PacketBoundary CPacket::getMsgBoundary() const
{
return PacketBoundary(MSGNO_PACKET_BOUNDARY::unwrap(m_nHeader[SRT_PH_MSGNO]));
}
bool srt::CPacket::getMsgOrderFlag() const
bool CPacket::getMsgOrderFlag() const
{
return 0 != MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]);
}
int32_t srt::CPacket::getMsgSeq(bool has_rexmit) const
int32_t CPacket::getMsgSeq(bool has_rexmit) const
{
if (has_rexmit)
{
@ -454,13 +526,18 @@ int32_t srt::CPacket::getMsgSeq(bool has_rexmit) const
}
}
bool srt::CPacket::getRexmitFlag() const
bool CPacket::getRexmitFlag() const
{
// return false; //
return 0 != MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]);
}
srt::EncryptionKeySpec srt::CPacket::getMsgCryptoFlags() const
void CPacket::setRexmitFlag(bool bRexmit)
{
const int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_REXMIT::mask;
m_nHeader[SRT_PH_MSGNO] = clr_msgno | MSGNO_REXMIT::wrap(bRexmit? 1 : 0);
}
EncryptionKeySpec CPacket::getMsgCryptoFlags() const
{
return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(m_nHeader[SRT_PH_MSGNO]));
}
@ -468,32 +545,30 @@ srt::EncryptionKeySpec srt::CPacket::getMsgCryptoFlags() const
// This is required as the encryption/decryption happens in place.
// This is required to clear off the flags after decryption or set
// crypto flags after encrypting a packet.
void srt::CPacket::setMsgCryptoFlags(EncryptionKeySpec spec)
void CPacket::setMsgCryptoFlags(EncryptionKeySpec spec)
{
int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask;
m_nHeader[SRT_PH_MSGNO] = clr_msgno | EncryptionKeyBits(spec);
}
uint32_t srt::CPacket::getMsgTimeStamp() const
uint32_t CPacket::getMsgTimeStamp() const
{
// SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests
// SRT_DEBUG_TSBPD_WRAP used to enable smaller timestamps for faster testing of how wraparounds are handled
return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK;
}
srt::CPacket* srt::CPacket::clone() const
CPacket* CPacket::clone() const
{
CPacket* pkt = new CPacket;
memcpy((pkt->m_nHeader), m_nHeader, HDR_SIZE);
pkt->m_pcData = new char[m_PacketVector[PV_DATA].size()];
memcpy((pkt->m_pcData), m_pcData, m_PacketVector[PV_DATA].size());
pkt->m_PacketVector[PV_DATA].setLength(m_PacketVector[PV_DATA].size());
pkt->allocate(this->getLength());
SRT_ASSERT(this->getLength() == pkt->getLength());
memcpy((pkt->m_pcData), m_pcData, this->getLength());
pkt->m_DestAddr = m_DestAddr;
return pkt;
}
namespace srt
{
// Useful for debugging
std::string PacketMessageFlagStr(uint32_t msgno_field)
{
@ -522,10 +597,8 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val)
os << val;
}
} // namespace srt
#if ENABLE_LOGGING
std::string srt::CPacket::Info()
std::string CPacket::Info()
{
std::ostringstream os;
os << "TARGET=@" << m_iID << " ";
@ -580,3 +653,5 @@ std::string srt::CPacket::Info()
return os.str();
}
#endif
} // end namespace srt

@ -150,7 +150,7 @@ const int32_t LOSSDATA_SEQNO_RANGE_LAST = 0, LOSSDATA_SEQNO_SOLO = 0;
inline int32_t CreateControlSeqNo(UDTMessageType type)
{
return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(size_t(type));
return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(uint32_t(type));
}
inline int32_t CreateControlExtSeqNo(int exttype)
@ -236,6 +236,11 @@ public:
/// @param len [in] the payload or the control information field length.
void setLength(size_t len);
/// Set the payload or the control information field length.
/// @param len [in] the payload or the control information field length.
/// @param cap [in] capacity (if known).
void setLength(size_t len, size_t cap);
/// Pack a Control packet.
/// @param pkttype [in] packet type filed.
/// @param lparam [in] pointer to the first data structure, explained by the packet type.
@ -286,6 +291,8 @@ public:
/// (because the peer will understand this bit as a part of MSGNO field).
bool getRexmitFlag() const;
void setRexmitFlag(bool bRexmit);
/// Read the message sequence number.
/// @return packet header field [1]
int32_t getMsgSeq(bool has_rexmit = true) const;
@ -300,6 +307,8 @@ public:
/// @return packet header field [2] (bit 0~31, bit 0-26 if SRT_DEBUG_TSBPD_WRAP).
uint32_t getMsgTimeStamp() const;
sockaddr_any udpDestAddr() const { return m_DestAddr; }
#ifdef SRT_DEBUG_TSBPD_WRAP // Receiver
static const uint32_t MAX_TIMESTAMP = 0x07FFFFFF; // 27 bit fast wraparound for tests (~2m15s)
#else
@ -335,6 +344,8 @@ protected:
int32_t m_extra_pad;
bool m_data_owned;
sockaddr_any m_DestAddr;
size_t m_zCapacity;
protected:
CPacket& operator=(const CPacket&);
@ -368,6 +379,8 @@ public:
char* data() { return m_pcData; }
const char* data() const { return m_pcData; }
size_t size() const { return getLength(); }
size_t capacity() const { return m_zCapacity; }
void setCapacity(size_t cap) { m_zCapacity = cap; }
uint32_t header(SrtPktHeaderFields field) const { return m_nHeader[field]; }
#if ENABLE_LOGGING

@ -26,7 +26,7 @@ using namespace std;
using namespace srt_logging;
using namespace srt::sync;
bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf)
bool srt::ParseFilterConfig(const string& s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf)
{
if (!SrtParseConfig(s, (w_config)))
return false;
@ -43,7 +43,7 @@ bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config, PacketFilter::F
return true;
}
bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config)
bool srt::ParseFilterConfig(const string& s, SrtFilterConfig& w_config)
{
return ParseFilterConfig(s, (w_config), NULL);
}
@ -130,7 +130,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector<CUnit*>& w_incoming, lo
{
// For the sake of rebuilding MARK THIS UNIT GOOD, otherwise the
// unit factory will supply it from getNextAvailUnit() as if it were not in use.
unit->m_iFlag = CUnit::GOOD;
unit->m_bTaken = true;
HLOGC(pflog.Debug, log << "FILTER: PASSTHRU current packet %" << unit->m_Packet.getSeqNo());
w_incoming.push_back(unit);
}
@ -169,7 +169,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector<CUnit*>& w_incoming, lo
InsertRebuilt(w_incoming, m_unitq);
ScopedLock lg(m_parent->m_StatsLock);
m_parent->m_stats.rcvr.suppliedByFilter.count(nsupply);
m_parent->m_stats.rcvr.suppliedByFilter.count((uint32_t)nsupply);
}
// Now that all units have been filled as they should be,
@ -178,11 +178,11 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector<CUnit*>& w_incoming, lo
// Wanted units will be set GOOD flag, unwanted will remain
// with FREE and therefore will be returned at the next
// call to getNextAvailUnit().
unit->m_iFlag = CUnit::FREE;
unit->m_bTaken = false;
for (vector<CUnit*>::iterator i = w_incoming.begin(); i != w_incoming.end(); ++i)
{
CUnit* u = *i;
u->m_iFlag = CUnit::FREE;
u->m_bTaken = false;
}
// Packets must be sorted by sequence number, ascending, in order
@ -251,9 +251,9 @@ void srt::PacketFilter::InsertRebuilt(vector<CUnit*>& incoming, CUnitQueue* uq)
break;
}
// LOCK the unit as GOOD because otherwise the next
// LOCK the unit as taken because otherwise the next
// call to getNextAvailUnit will return THE SAME UNIT.
u->m_iFlag = CUnit::GOOD;
u->m_bTaken = true;
// After returning from this function, all units will be
// set back to FREE so that the buffer can decide whether
// it wants them or not.

@ -51,7 +51,7 @@ public:
virtual ~Factory();
};
private:
friend bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf);
friend bool ParseFilterConfig(const std::string& s, SrtFilterConfig& out, PacketFilter::Factory** ppf);
template <class Target>
class Creator: public Factory
@ -212,7 +212,7 @@ bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer);
inline void PacketFilter::feedSource(CPacket& w_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource((w_packet)); }
inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_filter->arqLevel(); }
bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf);
bool ParseFilterConfig(const std::string& s, SrtFilterConfig& out, PacketFilter::Factory** ppf);
} // namespace srt

@ -81,7 +81,7 @@ struct SrtPacket
};
bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config);
bool ParseFilterConfig(const std::string& s, SrtFilterConfig& w_config);
class SrtPacketFilterBase

@ -24,7 +24,6 @@
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS 1 // silences windows complaints for sscanf
#include <winsock2.h>
#include <ws2tcpip.h>
#include <ws2ipdef.h>
@ -40,9 +39,6 @@
#include <stdint.h>
#include <inttypes.h>
#if defined(_MSC_VER)
#pragma warning(disable: 4251 26812)
#endif
#else
#if defined(__APPLE__) && __APPLE__

@ -125,7 +125,7 @@ srt::CUnitQueue::CQEntry* srt::CUnitQueue::allocateEntry(const int iNumUnits, co
for (int i = 0; i < iNumUnits; ++i)
{
tempu[i].m_iFlag = CUnit::FREE;
tempu[i].m_bTaken = false;
tempu[i].m_Packet.m_pcData = tempb + i * mss;
}
@ -172,7 +172,7 @@ srt::CUnit* srt::CUnitQueue::getNextAvailUnit()
const CUnit* end = m_pCurrQueue->m_pUnit + m_pCurrQueue->m_iSize;
for (; m_pAvailUnit != end; ++m_pAvailUnit, ++units_checked)
{
if (m_pAvailUnit->m_iFlag == CUnit::FREE)
if (!m_pAvailUnit->m_bTaken)
{
return m_pAvailUnit;
}
@ -188,19 +188,19 @@ srt::CUnit* srt::CUnitQueue::getNextAvailUnit()
void srt::CUnitQueue::makeUnitFree(CUnit* unit)
{
SRT_ASSERT(unit != NULL);
SRT_ASSERT(unit->m_iFlag != CUnit::FREE);
unit->m_iFlag.store(CUnit::FREE);
SRT_ASSERT(unit->m_bTaken);
unit->m_bTaken.store(false);
--m_iNumTaken;
}
void srt::CUnitQueue::makeUnitGood(CUnit* unit)
void srt::CUnitQueue::makeUnitTaken(CUnit* unit)
{
++m_iNumTaken;
SRT_ASSERT(unit != NULL);
SRT_ASSERT(unit->m_iFlag == CUnit::FREE);
unit->m_iFlag.store(CUnit::GOOD);
SRT_ASSERT(!unit->m_bTaken);
unit->m_bTaken.store(true);
}
srt::CSndUList::CSndUList(sync::CTimer* pTimer)
@ -481,6 +481,25 @@ bool srt::CSndQueue::getBind(char* dst, size_t len) const
}
#endif
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
static void CSndQueueDebugHighratePrint(const srt::CSndQueue* self, const steady_clock::time_point currtime)
{
if (self->m_DbgTime <= currtime)
{
fprintf(stdout,
"SndQueue %lu slt:%lu nrp:%lu snt:%lu nrt:%lu ctw:%lu\n",
self->m_WorkerStats.lIteration,
self->m_WorkerStats.lSleepTo,
self->m_WorkerStats.lNotReadyPop,
self->m_WorkerStats.lSendTo,
self->m_WorkerStats.lNotReadyTs,
self->m_WorkerStats.lCondWait);
memset(&self->m_WorkerStats, 0, sizeof(self->m_WorkerStats));
self->m_DbgTime = currtime + self->m_DbgPeriod;
}
}
#endif
void* srt::CSndQueue::worker(void* param)
{
CSndQueue* self = (CSndQueue*)param;
@ -492,34 +511,32 @@ void* srt::CSndQueue::worker(void* param)
#endif
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
CTimer::rdtsc(self->m_ullDbgTime);
self->m_ullDbgPeriod = uint64_t(5000000) * CTimer::getCPUFrequency();
self->m_ullDbgTime += self->m_ullDbgPeriod;
#define IF_DEBUG_HIGHRATE(statement) statement
self->m_DbgTime = sync::steady_clock::now();
self->m_DbgPeriod = sync::microseconds_from(5000000);
self->m_DbgTime += self->m_DbgPeriod;
#else
#define IF_DEBUG_HIGHRATE(statement) (void)0
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
while (!self->m_bClosing)
{
const steady_clock::time_point next_time = self->m_pSndUList->getNextProcTime();
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
self->m_WorkerStats.lIteration++;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
INCREMENT_THREAD_ITERATIONS();
IF_DEBUG_HIGHRATE(self->m_WorkerStats.lIteration++);
if (is_zero(next_time))
{
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
self->m_WorkerStats.lNotReadyTs++;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
IF_DEBUG_HIGHRATE(self->m_WorkerStats.lNotReadyTs++);
// wait here if there is no sockets with data to be sent
THREAD_PAUSED();
if (!self->m_bClosing)
{
self->m_pSndUList->waitNonEmpty();
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
self->m_WorkerStats.lCondWait++;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
IF_DEBUG_HIGHRATE(self->m_WorkerStats.lCondWait++);
}
THREAD_RESUMED();
@ -529,43 +546,23 @@ void* srt::CSndQueue::worker(void* param)
// wait until next processing time of the first socket on the list
const steady_clock::time_point currtime = steady_clock::now();
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
if (self->m_ullDbgTime <= currtime)
{
fprintf(stdout,
"SndQueue %lu slt:%lu nrp:%lu snt:%lu nrt:%lu ctw:%lu\n",
self->m_WorkerStats.lIteration,
self->m_WorkerStats.lSleepTo,
self->m_WorkerStats.lNotReadyPop,
self->m_WorkerStats.lSendTo,
self->m_WorkerStats.lNotReadyTs,
self->m_WorkerStats.lCondWait);
memset(&self->m_WorkerStats, 0, sizeof(self->m_WorkerStats));
self->m_ullDbgTime = currtime + self->m_ullDbgPeriod;
}
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
THREAD_PAUSED();
IF_DEBUG_HIGHRATE(CSndQueueDebugHighratePrint(self, currtime));
if (currtime < next_time)
{
THREAD_PAUSED();
self->m_pTimer->sleep_until(next_time);
#if defined(HAI_DEBUG_SNDQ_HIGHRATE)
self->m_WorkerStats.lSleepTo++;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
THREAD_RESUMED();
IF_DEBUG_HIGHRATE(self->m_WorkerStats.lSleepTo++);
}
THREAD_RESUMED();
// Get a socket with a send request if any.
CUDT* u = self->m_pSndUList->pop();
if (u == NULL)
{
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
self->m_WorkerStats.lNotReadyPop++;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
IF_DEBUG_HIGHRATE(self->m_WorkerStats.lNotReadyPop++);
continue;
}
#define UST(field) ((u->m_b##field) ? "+" : "-") << #field << " "
HLOGC(qslog.Debug,
log << "CSndQueue: requesting packet from @" << u->socketID() << " STATUS: " << UST(Listening)
@ -575,46 +572,44 @@ void* srt::CSndQueue::worker(void* param)
if (!u->m_bConnected || u->m_bBroken)
{
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
self->m_WorkerStats.lNotReadyPop++;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
IF_DEBUG_HIGHRATE(self->m_WorkerStats.lNotReadyPop++);
continue;
}
// pack a packet from the socket
CPacket pkt;
const std::pair<bool, steady_clock::time_point> res_time = u->packData((pkt));
steady_clock::time_point next_send_time;
sockaddr_any source_addr;
const bool res = u->packData((pkt), (next_send_time), (source_addr));
// Check if payload size is invalid.
if (res_time.first == false)
// Check if extracted anything to send
if (res == false)
{
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
self->m_WorkerStats.lNotReadyPop++;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
IF_DEBUG_HIGHRATE(self->m_WorkerStats.lNotReadyPop++);
continue;
}
const sockaddr_any addr = u->m_PeerAddr;
const steady_clock::time_point next_send_time = res_time.second;
if (!is_zero(next_send_time))
self->m_pSndUList->update(u, CSndUList::DO_RESCHEDULE, next_send_time);
HLOGC(qslog.Debug, log << self->CONID() << "chn:SENDING: " << pkt.Info());
self->m_pChannel->sendto(addr, pkt);
self->m_pChannel->sendto(addr, pkt, source_addr);
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)
self->m_WorkerStats.lSendTo++;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
IF_DEBUG_HIGHRATE(self->m_WorkerStats.lSendTo++);
}
THREAD_EXIT();
return NULL;
}
int srt::CSndQueue::sendto(const sockaddr_any& w_addr, CPacket& w_packet)
int srt::CSndQueue::sendto(const sockaddr_any& addr, CPacket& w_packet, const sockaddr_any& src)
{
// send out the packet immediately (high priority), this is a control packet
m_pChannel->sendto(w_addr, w_packet);
// NOTE: w_packet is passed by mutable reference because this function will do
// a modification in place and then it will revert it. After returning this object
// should look unmodified, hence it is here passed without a reference marker.
m_pChannel->sendto(addr, w_packet, src);
return (int)w_packet.getLength();
}
@ -842,14 +837,42 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET&
{
ScopedLock vg(m_RIDListLock);
IF_HEAVY_LOGGING(const char* const id_type = w_id ? "THIS ID" : "A NEW CONNECTION");
// TODO: optimize search
for (list<CRL>::const_iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i)
{
if (i->m_PeerAddr == addr && ((w_id == 0) || (w_id == i->m_iID)))
{
// This procedure doesn't exactly respond to the original UDT idea.
// As the "rendezvous queue" is used for both handling rendezvous and
// the caller sockets in the non-blocking mode (for blocking mode the
// entire handshake procedure is handled in a loop-style in CUDT::startConnect),
// the RID list should give up a socket entity in the following cases:
// 1. For THE SAME id as passed in w_id, respond always, as per a caller
// socket that is currently trying to connect and is managed with
// HS roundtrips in an event-style. Same for rendezvous.
// 2. For the "connection request" ID=0 the found socket should be given up
// ONLY IF it is rendezvous. Normally ID=0 is only for listener as a
// connection request. But if there was a listener, then this function
// wouldn't even be called, as this case would be handled before trying
// to call this function.
//
// This means: if an incoming ID is 0, then this search should succeed ONLY
// IF THE FOUND SOCKET WAS RENDEZVOUS.
if (!w_id && !i->m_pUDT->m_config.bRendezvous)
{
HLOGC(cnlog.Debug,
log << "RID: found id @" << i->m_iID << " while looking for "
<< id_type << " FROM " << i->m_PeerAddr.str()
<< ", but it's NOT RENDEZVOUS, skipping");
continue;
}
HLOGC(cnlog.Debug,
log << "RID: found id @" << i->m_iID << " while looking for "
<< (w_id ? "THIS ID FROM " : "A NEW CONNECTION FROM ") << i->m_PeerAddr.str());
log << "RID: found id @" << i->m_iID << " while looking for "
<< id_type << " FROM " << i->m_PeerAddr.str());
w_id = i->m_iID;
return i->m_pUDT;
}
@ -908,10 +931,26 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst
EReadStatus read_st = rst;
EConnectStatus conn_st = cst;
if (i->id != dest_id)
if (cst != CONN_RENDEZVOUS && dest_id != 0)
{
read_st = RST_AGAIN;
conn_st = CONN_AGAIN;
if (i->id != dest_id)
{
HLOGC(cnlog.Debug, log << "updateConnStatus: cst=" << ConnectStatusStr(cst) << " but for RID @" << i->id
<< " dest_id=@" << dest_id << " - resetting to AGAIN");
read_st = RST_AGAIN;
conn_st = CONN_AGAIN;
}
else
{
HLOGC(cnlog.Debug, log << "updateConnStatus: cst=" << ConnectStatusStr(cst) << " for @"
<< i->id);
}
}
else
{
HLOGC(cnlog.Debug, log << "updateConnStatus: cst=" << ConnectStatusStr(cst) << " and dest_id=@" << dest_id
<< " - NOT checking against RID @" << i->id);
}
HLOGC(cnlog.Debug,
@ -1123,7 +1162,6 @@ srt::CRcvQueue::~CRcvQueue()
while (!i->second.empty())
{
CPacket* pkt = i->second.front();
delete[] pkt->m_pcData;
delete pkt;
i->second.pop();
}
@ -1182,6 +1220,8 @@ void* srt::CRcvQueue::worker(void* param)
{
bool have_received = false;
EReadStatus rst = self->worker_RetrieveUnit((id), (unit), (sa));
INCREMENT_THREAD_ITERATIONS();
if (rst == RST_OK)
{
if (id < 0)
@ -1324,14 +1364,12 @@ srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_un
{
// no space, skip this packet
CPacket temp;
temp.m_pcData = new char[m_szPayloadSize];
temp.setLength(m_szPayloadSize);
temp.allocate(m_szPayloadSize);
THREAD_PAUSED();
EReadStatus rst = m_pChannel->recvfrom((w_addr), (temp));
THREAD_RESUMED();
// Note: this will print nothing about the packet details unless heavy logging is on.
LOGC(qrlog.Error, log << CONID() << "LOCAL STORAGE DEPLETED. Dropping 1 packet: " << temp.Info());
delete[] temp.m_pcData;
// Be transparent for RST_ERROR, but ignore the correct
// data read and fake that the packet was dropped.
@ -1500,7 +1538,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUni
if (cst == CONN_CONFUSED)
{
LOGC(cnlog.Warn, log << "AsyncOrRND: PACKET NOT HANDSHAKE - re-requesting handshake from peer");
storePkt(id, unit->m_Packet.clone());
storePktClone(id, unit->m_Packet);
if (!u->processAsyncConnectRequest(RST_AGAIN, CONN_CONTINUE, &unit->m_Packet, u->m_PeerAddr))
{
// Reuse previous behavior to reject a packet
@ -1575,7 +1613,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUni
log << "AsyncOrRND: packet RESOLVED TO ID=" << id << " -- continuing through CENTRAL PACKET QUEUE");
// This is where also the packets for rendezvous connection will be landing,
// in case of a synchronous connection.
storePkt(id, unit->m_Packet.clone());
storePktClone(id, unit->m_Packet);
return CONN_CONTINUE;
}
@ -1637,8 +1675,8 @@ int srt::CRcvQueue::recvfrom(int32_t id, CPacket& w_packet)
memcpy((w_packet.m_nHeader), newpkt->m_nHeader, CPacket::HDR_SIZE);
memcpy((w_packet.m_pcData), newpkt->m_pcData, newpkt->getLength());
w_packet.setLength(newpkt->getLength());
w_packet.m_DestAddr = newpkt->m_DestAddr;
delete[] newpkt->m_pcData;
delete newpkt;
// remove this message from queue,
@ -1693,7 +1731,6 @@ void srt::CRcvQueue::removeConnector(const SRTSOCKET& id)
log << "removeConnector: ... and its packet queue with " << i->second.size() << " packets collected");
while (!i->second.empty())
{
delete[] i->second.front()->m_pcData;
delete i->second.front();
i->second.pop();
}
@ -1726,7 +1763,7 @@ srt::CUDT* srt::CRcvQueue::getNewEntry()
return u;
}
void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt)
void srt::CRcvQueue::storePktClone(int32_t id, const CPacket& pkt)
{
CUniqueSync passcond(m_BufferLock, m_BufferCond);
@ -1734,22 +1771,22 @@ void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt)
if (i == m_mBuffer.end())
{
m_mBuffer[id].push(pkt);
m_mBuffer[id].push(pkt.clone());
passcond.notify_one();
}
else
{
// avoid storing too many packets, in case of malfunction or attack
// Avoid storing too many packets, in case of malfunction or attack.
if (i->second.size() > 16)
return;
i->second.push(pkt);
i->second.push(pkt.clone());
}
}
void srt::CMultiplexer::destroy()
{
// Reverse order of the assigned
// Reverse order of the assigned.
delete m_pRcvQueue;
delete m_pSndQueue;
delete m_pTimer;

@ -71,16 +71,7 @@ class CUDT;
struct CUnit
{
CPacket m_Packet; // packet
enum Flag
{
FREE = 0,
GOOD = 1,
PASSACK = 2,
DROPPED = 3
};
// TODO: The new RcvBuffer allows to use atomic_bool here.
sync::atomic<Flag> m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped
sync::atomic<bool> m_bTaken; // true if the unit is is use (can be stored in the RCV buffer).
};
class CUnitQueue
@ -106,7 +97,7 @@ public:
void makeUnitFree(CUnit* unit);
void makeUnitGood(CUnit* unit);
void makeUnitTaken(CUnit* unit);
private:
struct CQEntry
@ -203,7 +194,7 @@ private:
void insert_(const sync::steady_clock::time_point& ts, const CUDT* u);
/// Insert a new UDT instance into the list without realloc.
/// Should be called if there is a gauranteed space for the element.
/// Should be called if there is a guaranteed space for the element.
///
/// @param [in] ts time stamp: next processing time
/// @param [in] u pointer to the UDT instance
@ -419,25 +410,25 @@ public:
/// Initialize the sending queue.
/// @param [in] c UDP channel to be associated to the queue
/// @param [in] t Timer
void init(CChannel* c, sync::CTimer* t);
/// Send out a packet to a given address.
/// Send out a packet to a given address. The @a src parameter is
/// blindly passed by the caller down the call with intention to
/// be received eventually by CChannel::sendto, and used only if
/// appropriate conditions state so.
/// @param [in] addr destination address
/// @param [in] packet packet to be sent out
/// @param [in,ref] packet packet to be sent out
/// @param [in] src The source IP address (details above)
/// @return Size of data sent out.
int sendto(const sockaddr_any& addr, CPacket& packet);
int sendto(const sockaddr_any& addr, CPacket& packet, const sockaddr_any& src);
/// Get the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return TTL.
int getIpTTL() const;
/// Get the IP Type of Service.
/// @return ToS.
int getIpToS() const;
#ifdef SRT_ENABLE_BINDTODEVICE
@ -460,9 +451,10 @@ private:
sync::atomic<bool> m_bClosing; // closing the worker
public:
#if defined(SRT_DEBUG_SNDQ_HIGHRATE) //>>debug high freq worker
uint64_t m_ullDbgPeriod;
uint64_t m_ullDbgTime;
sync::steady_clock::duration m_DbgPeriod;
mutable sync::steady_clock::time_point m_DbgTime;
struct
{
unsigned long lIteration; //
@ -471,14 +463,15 @@ private:
unsigned long lSendTo;
unsigned long lNotReadyTs;
unsigned long lCondWait; // block on m_WindowCond
} m_WorkerStats;
} mutable m_WorkerStats;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
private:
#if ENABLE_LOGGING
static int m_counter;
#endif
private:
CSndQueue(const CSndQueue&);
CSndQueue& operator=(const CSndQueue&);
};
@ -533,7 +526,7 @@ private:
CUnitQueue* m_pUnitQueue; // The received packet queue
CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue
CHash* m_pHash; // Hash table for UDT socket looking up
CChannel* m_pChannel; // UDP channel for receving packets
CChannel* m_pChannel; // UDP channel for receiving packets
sync::CTimer* m_pTimer; // shared timer with the snd queue
int m_iIPversion; // IP version
@ -558,7 +551,7 @@ private:
bool ifNewEntry();
CUDT* getNewEntry();
void storePkt(int32_t id, CPacket* pkt);
void storePktClone(int32_t id, const CPacket& pkt);
private:
sync::Mutex m_LSLock;

@ -69,7 +69,7 @@ struct CSrtConfigSetter<SRTO_MSS>
{
static void set(CSrtConfig& co, const void* optval, int optlen)
{
int ival = cast_optval<int>(optval, optlen);
const int ival = cast_optval<int>(optval, optlen);
if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize))
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
@ -236,6 +236,21 @@ struct CSrtConfigSetter<SRTO_MAXBW>
}
};
#ifdef ENABLE_MAXREXMITBW
template<>
struct CSrtConfigSetter<SRTO_MAXREXMITBW>
{
static void set(CSrtConfig& co, const void* optval, int optlen)
{
const int64_t val = cast_optval<int64_t>(optval, optlen);
if (val < -1)
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
co.llMaxRexmitBW = val;
}
};
#endif
template<>
struct CSrtConfigSetter<SRTO_IPTTL>
{
@ -333,7 +348,17 @@ struct CSrtConfigSetter<SRTO_TSBPDMODE>
{
static void set(CSrtConfig& co, const void* optval, int optlen)
{
co.bTSBPD = cast_optval<bool>(optval, optlen);
const bool val = cast_optval<bool>(optval, optlen);
#ifdef SRT_ENABLE_ENCRYPTION
if (val == false && co.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM)
{
using namespace srt_logging;
LOGC(aclog.Error, log << "Can't disable TSBPD as long as AES GCM is enabled.");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
}
#endif
co.bTSBPD = val;
}
};
template<>
@ -502,7 +527,7 @@ struct CSrtConfigSetter<SRTO_CONNTIMEO>
if (val < 0)
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
using namespace sync;
using namespace srt::sync;
co.tdConnTimeOut = milliseconds_from(val);
}
};
@ -601,7 +626,7 @@ struct CSrtConfigSetter<SRTO_PAYLOADSIZE>
if (val > SRT_LIVE_MAX_PLSIZE)
{
LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU.");
LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << ", maximum payload per MTU.");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
}
@ -622,12 +647,22 @@ struct CSrtConfigSetter<SRTO_PAYLOADSIZE>
if (size_t(val) > efc_max_payload_size)
{
LOGC(aclog.Error,
log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size
log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << " bytes decreased by " << fc.extra_size
<< " required for packet filter header");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
}
}
// Not checking AUTO to allow defaul 1456 bytes.
if ((co.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM)
&& (val > (SRT_LIVE_MAX_PLSIZE - HAICRYPT_AUTHTAG_MAX)))
{
LOGC(aclog.Error,
log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX
<< " required for AES-GCM.");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
}
co.zExpPayloadSize = val;
}
};
@ -883,6 +918,40 @@ struct CSrtConfigSetter<SRTO_RETRANSMITALGO>
}
};
#ifdef ENABLE_AEAD_API_PREVIEW
template<>
struct CSrtConfigSetter<SRTO_CRYPTOMODE>
{
static void set(CSrtConfig& co, const void* optval, int optlen)
{
using namespace srt_logging;
const int val = cast_optval<int>(optval, optlen);
#ifdef SRT_ENABLE_ENCRYPTION
if (val < CSrtConfig::CIPHER_MODE_AUTO || val > CSrtConfig::CIPHER_MODE_AES_GCM)
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
if (val == CSrtConfig::CIPHER_MODE_AES_GCM && !HaiCrypt_IsAESGCM_Supported())
{
LOGC(aclog.Error, log << "AES GCM is not supported by the crypto provider.");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
}
if (val == CSrtConfig::CIPHER_MODE_AES_GCM && !co.bTSBPD)
{
LOGC(aclog.Error, log << "Enable TSBPD to use AES GCM.");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
}
co.iCryptoMode = val;
#else
LOGC(aclog.Error, log << "SRT was built without crypto module.");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
#endif
}
};
#endif
int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int optlen)
{
switch (optName)
@ -940,6 +1009,12 @@ int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int opt
DISPATCH(SRTO_IPV6ONLY);
DISPATCH(SRTO_PACKETFILTER);
DISPATCH(SRTO_RETRANSMITALGO);
#ifdef ENABLE_AEAD_API_PREVIEW
DISPATCH(SRTO_CRYPTOMODE);
#endif
#ifdef ENABLE_MAXREXMITBW
DISPATCH(SRTO_MAXREXMITBW);
#endif
#undef DISPATCH
default:
@ -987,7 +1062,7 @@ bool SRT_SocketOptionObject::add(SRT_SOCKOPT optname, const void* optval, size_t
case SRTO_PEERIDLETIMEO:
case SRTO_RCVBUF:
//SRTO_RCVSYN - must be always false in groups
//SRTO_RCVTIMEO - must be alwyas -1 in groups
//SRTO_RCVTIMEO - must be always -1 in groups
case SRTO_SNDBUF:
case SRTO_SNDDROPDELAY:
//SRTO_TLPKTDROP - per transmission setting

@ -91,19 +91,27 @@ struct CSrtMuxerConfig
int iUDPSndBufSize; // UDP sending buffer size
int iUDPRcvBufSize; // UDP receiving buffer size
bool operator==(const CSrtMuxerConfig& other) const
// NOTE: this operator is not reversable. The syntax must use:
// muxer_entry == socket_entry
bool isCompatWith(const CSrtMuxerConfig& other) const
{
#define CEQUAL(field) (field == other.field)
return CEQUAL(iIpTTL)
&& CEQUAL(iIpToS)
&& CEQUAL(iIpV6Only)
&& CEQUAL(bReuseAddr)
#ifdef SRT_ENABLE_BINDTODEVICE
&& CEQUAL(sBindToDevice)
#endif
&& CEQUAL(iUDPSndBufSize)
&& CEQUAL(iUDPRcvBufSize);
&& CEQUAL(iUDPRcvBufSize)
&& (other.iIpV6Only == -1 || CEQUAL(iIpV6Only))
// NOTE: iIpV6Only is not regarded because
// this matches only in case of IPv6 with "any" address.
// And this aspect must be checked separately because here
// this procedure has no access to neither the address,
// nor the IP version (family).
#undef CEQUAL
&& true;
}
CSrtMuxerConfig()
@ -150,6 +158,16 @@ public:
return set(s.c_str(), s.size());
}
size_t copy(char* s, size_t length) const
{
if (!s)
return 0;
size_t copy_len = std::min((size_t)len, length);
memcpy(s, stor, copy_len);
return copy_len;
}
std::string str() const
{
return len == 0 ? std::string() : std::string(stor);
@ -176,6 +194,13 @@ struct CSrtConfig: CSrtMuxerConfig
DEF_LINGER_S = 3*60, // 3 minutes
DEF_CONNTIMEO_S = 3; // 3 seconds
enum
{
CIPHER_MODE_AUTO = 0,
CIPHER_MODE_AES_CTR = 1,
CIPHER_MODE_AES_GCM = 2
};
static const int COMM_RESPONSE_TIMEOUT_MS = 5 * 1000; // 5 seconds
static const uint32_t COMM_DEF_MIN_STABILITY_TIMEOUT_MS = 60; // 60 ms
@ -189,8 +214,8 @@ struct CSrtConfig: CSrtMuxerConfig
size_t zExpPayloadSize; // Expected average payload size (user option)
// Options
bool bSynSending; // Sending syncronization mode
bool bSynRecving; // Receiving syncronization mode
bool bSynSending; // Sending synchronization mode
bool bSynRecving; // Receiving synchronization mode
int iFlightFlagSize; // Maximum number of packets in flight from the peer side
int iSndBufSize; // Maximum UDT sender buffer size
int iRcvBufSize; // Maximum UDT receiver buffer size
@ -202,6 +227,9 @@ struct CSrtConfig: CSrtMuxerConfig
int iSndTimeOut; // sending timeout in milliseconds
int iRcvTimeOut; // receiving timeout in milliseconds
int64_t llMaxBW; // maximum data transfer rate (threshold)
#ifdef ENABLE_MAXREXMITBW
int64_t llMaxRexmitBW; // maximum bandwidth limit for retransmissions (Bytes/s).
#endif
// These fields keep the options for encryption
// (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is
@ -224,6 +252,7 @@ struct CSrtConfig: CSrtMuxerConfig
int iPeerIdleTimeout_ms; // Timeout for hearing anything from the peer (ms).
uint32_t uMinStabilityTimeout_ms;
int iRetransmitAlgo;
int iCryptoMode; // SRTO_CRYPTOMODE
int64_t llInputBW; // Input stream rate (bytes/sec). 0: use internally estimated input bandwidth
int64_t llMinInputBW; // Minimum input stream rate estimate (bytes/sec)
@ -263,6 +292,9 @@ struct CSrtConfig: CSrtMuxerConfig
, iSndTimeOut(-1)
, iRcvTimeOut(-1)
, llMaxBW(-1)
#ifdef ENABLE_MAXREXMITBW
, llMaxRexmitBW(-1)
#endif
, bDataSender(false)
, bMessageAPI(true)
, bTSBPD(true)
@ -275,6 +307,7 @@ struct CSrtConfig: CSrtMuxerConfig
, iPeerIdleTimeout_ms(COMM_RESPONSE_TIMEOUT_MS)
, uMinStabilityTimeout_ms(COMM_DEF_MIN_STABILITY_TIMEOUT_MS)
, iRetransmitAlgo(1)
, iCryptoMode(CIPHER_MODE_AUTO)
, llInputBW(0)
, llMinInputBW(0)
, iOverheadBW(25)

@ -72,7 +72,7 @@ written by
#endif
// Stadnard attributes
// Standard attributes
// When compiling in C++17 mode, use the standard C++17 attributes
// (out of these, only [[deprecated]] is supported in C++14, so
@ -238,7 +238,13 @@ typedef enum SRT_SOCKOPT {
SRTO_GROUPMINSTABLETIMEO, // Minimum Link Stability timeout (backup mode) in milliseconds (ENABLE_BONDING)
SRTO_GROUPTYPE, // Group type to which an accepted socket is about to be added, available in the handshake (ENABLE_BONDING)
SRTO_PACKETFILTER = 60, // Add and configure a packet filter
SRTO_RETRANSMITALGO = 61, // An option to select packet retransmission algorithm
SRTO_RETRANSMITALGO = 61, // An option to select packet retransmission algorithm
#ifdef ENABLE_AEAD_API_PREVIEW
SRTO_CRYPTOMODE = 62, // Encryption cipher mode (AES-CTR, AES-GCM, ...).
#endif
#ifdef ENABLE_MAXREXMITBW
SRTO_MAXREXMITBW = 63, // Maximum bandwidth limit for retransmision (Bytes/s)
#endif
SRTO_E_SIZE // Always last element, not a valid option.
} SRT_SOCKOPT;
@ -553,6 +559,9 @@ enum SRT_REJECT_REASON
SRT_REJ_FILTER, // incompatible packet filter
SRT_REJ_GROUP, // incompatible group
SRT_REJ_TIMEOUT, // connection timeout
#ifdef ENABLE_AEAD_API_PREVIEW
SRT_REJ_CRYPTO, // conflicting cryptographic configurations
#endif
SRT_REJ_E_SIZE,
};
@ -634,11 +643,14 @@ enum SRT_REJECT_REASON
enum SRT_KM_STATE
{
SRT_KM_S_UNSECURED = 0, //No encryption
SRT_KM_S_SECURING = 1, //Stream encrypted, exchanging Keying Material
SRT_KM_S_SECURED = 2, //Stream encrypted, keying Material exchanged, decrypting ok.
SRT_KM_S_NOSECRET = 3, //Stream encrypted and no secret to decrypt Keying Material
SRT_KM_S_BADSECRET = 4 //Stream encrypted and wrong secret, cannot decrypt Keying Material
SRT_KM_S_UNSECURED = 0, // No encryption
SRT_KM_S_SECURING = 1, // Stream encrypted, exchanging Keying Material
SRT_KM_S_SECURED = 2, // Stream encrypted, keying Material exchanged, decrypting ok.
SRT_KM_S_NOSECRET = 3, // Stream encrypted and no secret to decrypt Keying Material
SRT_KM_S_BADSECRET = 4 // Stream encrypted and wrong secret is used, cannot decrypt Keying Material
#ifdef ENABLE_AEAD_API_PREVIEW
,SRT_KM_S_BADCRYPTOMODE = 5 // Stream encrypted but wrong cryptographic mode is used, cannot decrypt. Since v1.5.2.
#endif
};
enum SRT_EPOLL_OPT

@ -89,7 +89,7 @@ used by SRT library internally.
// - Other compilers: none.
///////////////////////////////////////////////////////////////////////////////
#if _MSC_VER >= 1920
// In case of MSVC these attributes have to preceed the attributed objects (variable, function).
// In case of MSVC these attributes have to precede the attributed objects (variable, function).
// E.g. SRT_ATTR_GUARDED_BY(mtx) int object;
// It is tricky to annotate e.g. the following function, as clang complaints it does not know 'm'.
// SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m)

@ -439,6 +439,9 @@ const char* const srt_rejection_reason_msg [] = {
"Packet Filter settings error",
"Group settings collision",
"Connection timeout"
#ifdef ENABLE_AEAD_API_PREVIEW
,"Crypto mode"
#endif
};
// Deprecated, available in SRT API.
@ -460,6 +463,9 @@ extern const char* const srt_rejectreason_msg[] = {
srt_rejection_reason_msg[14],
srt_rejection_reason_msg[15],
srt_rejection_reason_msg[16]
#ifdef ENABLE_AEAD_API_PREVIEW
, srt_rejection_reason_msg[17]
#endif
};
const char* srt_rejectreason_str(int id)

@ -70,7 +70,7 @@ extern const char * SysStrError(int errnum, char * buf, size_t buflen)
// your compilation fails when you use wide characters.
// The problem is that when TCHAR != char, then the buffer written this way
// would have to be converted to ASCII, not just copied by strncpy.
FormatMessage(0
FormatMessageA(0
| FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
@ -87,8 +87,12 @@ extern const char * SysStrError(int errnum, char * buf, size_t buflen)
if (lpMsgBuf)
{
#ifdef _MSC_VER
strncpy_s(buf, buflen, lpMsgBuf, _TRUNCATE);
#else
strncpy(buf, lpMsgBuf, buflen-1);
buf[buflen-1] = 0;
#endif
LocalFree((HLOCAL)lpMsgBuf);
}
else

@ -71,12 +71,12 @@ const char* strerror_msgs_notsup [] = {
"Operation not supported: Invalid socket ID", // MN_SIDINVAL = 4
"Operation not supported: Cannot do this operation on an UNBOUND socket", // MN_ISUNBOUND = 5
"Operation not supported: Socket is not in listening state", // MN_NOLISTEN = 6
"Operation not supported: Listen/accept is not supported in rendezous connection setup", // MN_ISRENDEZVOUS = 7
"Operation not supported: Listen/accept is not supported in rendezvous connection setup", // MN_ISRENDEZVOUS = 7
"Operation not supported: Cannot call connect on UNBOUND socket in rendezvous connection setup", // MN_ISRENDUNBOUND = 8
"Operation not supported: Incorrect use of Message API (sendmsg/recvmsg).", // MN_INVALMSGAPI = 9
"Operation not supported: Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile).", // MN_INVALBUFFERAPI = 10
"Operation not supported: Incorrect use of Message API (sendmsg/recvmsg)", // MN_INVALMSGAPI = 9
"Operation not supported: Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile)", // MN_INVALBUFFERAPI = 10
"Operation not supported: Another socket is already listening on the same port", // MN_BUSY = 11
"Operation not supported: Message is too large to send (it must be less than the SRT send buffer size)", // MN_XSIZE = 12
"Operation not supported: Message is too large to send", // MN_XSIZE = 12
"Operation not supported: Invalid epoll ID", // MN_EIDINVAL = 13
"Operation not supported: All sockets removed from epoll, waiting would deadlock", // MN_EEMPTY = 14
"Operation not supported: Another socket is bound to that port and is not reusable for requested settings", // MN_BUSYPORT = 15

@ -66,7 +66,7 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp)
const steady_clock::time_point now_timestamp = steady_clock::now();
const int64_t delta_us = count_microseconds(timestamp - now_timestamp);
const int64_t delta_s =
floor((static_cast<int64_t>(count_microseconds(now_timestamp.time_since_epoch()) % 1000000) + delta_us) / 1000000.0);
static_cast<int64_t>(floor((static_cast<double>(count_microseconds(now_timestamp.time_since_epoch()) % 1000000) + delta_us) / 1000000.0));
const time_t tt = now_s + delta_s;
struct tm tm = SysLocalTime(tt); // in seconds
char tmp_buf[512];
@ -94,7 +94,11 @@ bool StartThread(CThread& th, void* (*f) (void*), void* args, const string& name
th.create_thread(f, args);
#endif
}
#if ENABLE_HEAVY_LOGGING
catch (const CThreadException& e)
#else
catch (const CThreadException&)
#endif
{
HLOGC(inlog.Debug, log << name << ": failed to start thread. " << e.what());
return false;

@ -11,6 +11,8 @@
#ifndef INC_SRT_SYNC_H
#define INC_SRT_SYNC_H
#include "platform_sys.h"
#include <cstdlib>
#include <limits>
#ifdef ENABLE_STDCXX_SYNC
@ -233,7 +235,7 @@ inline Duration<steady_clock> operator*(const int& lhs, const Duration<steady_cl
#endif // ENABLE_STDCXX_SYNC
// NOTE: Moved the following class definitons to "atomic_clock.h"
// NOTE: Moved the following class definitions to "atomic_clock.h"
// template <class Clock>
// class AtomicDuration;
// template <class Clock>
@ -602,7 +604,7 @@ public:
/// Causes the current thread to block until
/// a specific time is reached.
///
/// @return true if condition occured or spuriously woken up
/// @return true if condition occurred or spuriously woken up
/// false on timeout
bool lock_wait_until(const steady_clock::time_point& tp);
@ -613,7 +615,7 @@ public:
/// It may also be unblocked spuriously.
/// Uses internal mutex to lock.
///
/// @return true if condition occured or spuriously woken up
/// @return true if condition occurred or spuriously woken up
/// false on timeout
bool lock_wait_for(const steady_clock::duration& rel_time);
@ -624,7 +626,7 @@ public:
/// It may also be unblocked spuriously.
/// When unblocked, regardless of the reason, lock is reacquiredand wait_for() exits.
///
/// @return true if condition occured or spuriously woken up
/// @return true if condition occurred or spuriously woken up
/// false on timeout
bool wait_for(UniqueLock& lk, const steady_clock::duration& rel_time);

@ -52,7 +52,7 @@ static void rdtsc(uint64_t& x)
asm("mov %0=ar.itc" : "=r"(x)::"memory");
#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_AMD64_RDTSC
uint32_t lval, hval;
asm("rdtsc" : "=a"(lval), "=d"(hval));
asm volatile("rdtsc" : "=a"(lval), "=d"(hval));
x = hval;
x = (x << 32) | lval;
#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_WINQPC

@ -93,7 +93,7 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3
r_ack = r_aSeq[i].iACK;
// Calculate RTT estimate
const int rtt = count_microseconds(currtime - r_aSeq[i].tsTimeStamp);
const int rtt = (int)count_microseconds(currtime - r_aSeq[i].tsTimeStamp);
if (i + 1 == r_iHead)
{
@ -112,7 +112,7 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3
}
// Head has exceeded the physical window boundary, so it is behind tail
for (int j = r_iTail, n = r_iHead + size; j < n; ++ j)
for (int j = r_iTail, n = r_iHead + (int)size; j < n; ++ j)
{
// Looking for an identical ACK Seq. No.
if (seq == r_aSeq[j % size].iACKSeqNo)
@ -122,7 +122,7 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3
r_ack = r_aSeq[j].iACK;
// Calculate RTT estimate
const int rtt = count_microseconds(currtime - r_aSeq[j].tsTimeStamp);
const int rtt = (int)count_microseconds(currtime - r_aSeq[j].tsTimeStamp);
if (j == r_iHead)
{
@ -176,7 +176,7 @@ int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica,
const int* bp = abytes;
// median filtering
const int* p = window;
for (int i = 0, n = asize; i < n; ++ i)
for (int i = 0, n = (int)asize; i < n; ++ i)
{
if ((*p < upper) && (*p > lower))
{
@ -192,7 +192,7 @@ int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica,
if (count > (asize >> 1))
{
bytes += (srt::CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received
bytesps = (unsigned long)ceil(1000000.0 / (double(sum) / double(bytes)));
bytesps = (int)ceil(1000000.0 / (double(sum) / double(bytes)));
return (int)ceil(1000000.0 / (sum / count));
}
else
@ -240,7 +240,7 @@ int srt::CPktTimeWindowTools::getBandwidth_in(const int* window, int* replica, s
// median filtering
const int* p = window;
for (int i = 0, n = psize; i < n; ++ i)
for (int i = 0, n = (int)psize; i < n; ++ i)
{
if ((*p < upper) && (*p > lower))
{

@ -7,6 +7,7 @@ The changelog for SRS.
<a name="v6-changes"></a>
## SRS 6.0 Changelog
* v6.0, 2023-09-21, Merge [#3808](https://github.com/ossrs/srs/pull/3808): Upgrade libsrt to v1.5.3. v6.0.81 (#3808)
* v6.0, 2023-09-21, Merge [#3404](https://github.com/ossrs/srs/pull/3404): WebRTC: Support WHEP for play. v6.0.80 (#3404)
* v6.0, 2023-09-21, Merge [#3807](https://github.com/ossrs/srs/pull/3807): Prevent the output of srt logs in utest. v6.0.79 (#3807)
* v6.0, 2023-09-21, Merge [#3696](https://github.com/ossrs/srs/pull/3696): SRT: modify log level from error to debug when no socket to accept. v6.0.78 (#3696)
@ -92,6 +93,7 @@ The changelog for SRS.
<a name="v5-changes"></a>
## SRS 5.0 Changelog
* v5.0, 2023-09-21, Merge [#3808](https://github.com/ossrs/srs/pull/3808): Upgrade libsrt to v1.5.3. v5.0.183 (#3808)
* v5.0, 2023-09-21, Merge [#3404](https://github.com/ossrs/srs/pull/3404): WebRTC: Support WHEP for play. v5.0.182 (#3404)
* v5.0, 2023-09-21, Merge [#3807](https://github.com/ossrs/srs/pull/3807): Prevent the output of srt logs in utest. v5.0.181 (#3807)
* v5.0, 2023-09-21, Merge [#3696](https://github.com/ossrs/srs/pull/3696): SRT: modify log level from error to debug when no socket to accept. v5.0.180 (#3696)

@ -9,6 +9,6 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 0
#define VERSION_REVISION 182
#define VERSION_REVISION 183
#endif

@ -9,6 +9,6 @@
#define VERSION_MAJOR 6
#define VERSION_MINOR 0
#define VERSION_REVISION 80
#define VERSION_REVISION 81
#endif

Loading…
Cancel
Save