1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

SRT: Upgrade libsrt from 1.4.1 to 1.5.1. v6.0.12 (#3362)

Co-authored-by: winlin <winlin@vip.126.com>
This commit is contained in:
john 2023-01-04 19:56:33 +08:00 committed by GitHub
parent 7a56208f2f
commit fe086dfc31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
143 changed files with 38185 additions and 15108 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,63 +0,0 @@
#ifndef _WINPORTING_H_
#define _WINPORTING_H_
// NOTE: This file has been borrowed from LCM project
// http://lcm-proj.github.io/
#if !defined(__MINGW32__)
#define strtoll _strtoi64
#define strdup _strdup
#define mode_t int
#define snprintf _snprintf
//#define PATH_MAX MAX_PATH
#define fseeko _fseeki64
#define ftello _ftelli64
//#define socklen_t int
#define in_addr_t in_addr
#define SHUT_RDWR SD_BOTH
#define HUGE HUGE_VAL
#define O_NONBLOCK 0x4000
#define F_GETFL 3
#define F_SETFL 4
#endif
#include <direct.h>
#include <winsock2.h>
#ifdef __cplusplus
extern "C" {
#endif
// Microsoft implementation of these structures has the
// pointer and length in reversed positions.
typedef struct iovec
{
ULONG iov_len;
char *iov_base;
} iovec;
typedef struct msghdr
{
struct sockaddr *msg_name;
int msg_namelen;
struct iovec *msg_iov;
ULONG msg_iovlen;
int msg_controllen;
char *msg_control;
ULONG msg_flags;
} msghdr;
//typedef long int ssize_t;
//int inet_aton(const char *cp, struct in_addr *inp);
int fcntl (int fd, int flag1, ...);
size_t recvmsg ( SOCKET s, struct msghdr *msg, int flags );
size_t sendmsg ( SOCKET s, const struct msghdr *msg, int flags );
#ifdef __cplusplus
}
#endif
#endif // _WINPORTING_H_

View file

@ -1,5 +1,5 @@
#ifndef INC__WINDOWS_SYSLOG_DEFS_H
#define INC__WINDOWS_SYSLOG_DEFS_H
#ifndef INC_SRT_WINDOWS_SYSLOG_DEFS_H
#define INC_SRT_WINDOWS_SYSLOG_DEFS_H
#define LOG_EMERG 0
#define LOG_ALERT 1

View file

@ -1,5 +1,5 @@
#ifndef INC__WIN_WINTIME
#define INC__WIN_WINTIME
#ifndef INC_SRT_WIN_WINTIME
#define INC_SRT_WIN_WINTIME
#include <winsock2.h>
#include <windows.h>
@ -7,7 +7,6 @@
// where pthread.h, which defines _POSIX_THREAD_SAFE_FUNCTIONS,
// has to be included before time.h so that time.h defines
// localtime_r correctly
#include <pthread.h>
#include <time.h>
#ifdef __cplusplus
@ -53,4 +52,4 @@ SRTCOMPAT_WINTIME_STATIC_INLINE_DECL int gettimeofday(
}
#endif
#endif // INC__WIN_WINTIME
#endif // INC_SRT_WIN_WINTIME

View file

@ -27,39 +27,11 @@ void SRTCompat_timeradd(struct timeval *a, struct timeval *b, struct timeval *re
}
}
int SRTCompat_gettimeofday(struct timeval* tp, struct timezone* tz)
int SRTCompat_gettimeofday(struct timeval* tp, struct timezone*)
{
static LARGE_INTEGER tickFrequency, epochOffset;
// For our first call, use "ftime()", so that we get a time with a proper epoch.
// For subsequent calls, use "QueryPerformanceCount()", because it's more fine-grain.
static int isFirstCall = 1;
LARGE_INTEGER tickNow;
QueryPerformanceCounter(&tickNow);
if (isFirstCall)
{
struct timeb tb;
ftime(&tb);
tp->tv_sec = (long)tb.time;
tp->tv_usec = 1000*tb.millitm;
// Also get our counter frequency:
QueryPerformanceFrequency(&tickFrequency);
// And compute an offset to add to subsequent counter times, so we get a proper epoch:
epochOffset.QuadPart = tb.time*tickFrequency.QuadPart + (tb.millitm*tickFrequency.QuadPart)/1000 - tickNow.QuadPart;
isFirstCall = 0; // for next time
}
else
{
// Adjust our counter time so that we get a proper epoch:
tickNow.QuadPart += epochOffset.QuadPart;
tp->tv_sec = (long) (tickNow.QuadPart / tickFrequency.QuadPart);
tp->tv_usec = (long) (((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart);
}
struct timeb tb;
ftime(&tb);
tp->tv_sec = (long)tb.time;
tp->tv_usec = 1000*tb.millitm;
return 0;
}

View file

@ -102,7 +102,7 @@ foreach {o desc} $options {
}
if { $argv == "--help" } {
if { $argv == "--help" || $argv == "-h" } {
puts stderr "Usage: ./configure \[options\]"
puts stderr "OPTIONS:"
foreach o [lsort [array names opt]] {
@ -153,8 +153,10 @@ foreach a $argv {
set type ""
if { [string first = $a] != -1 } {
lassign [split $a =] a val
if { [set a1 [string first = $a]] != -1 } {
# Do not split. Options may include =.
set val [string range $a $a1+1 end]
set a [string range $a 0 $a1-1]
}
if { [dict exists $::alias $a] } {
@ -194,6 +196,23 @@ if { $saveopt != "" } {
error "Extra unhandled argument: $saveopt"
}
# Save the original call into config-status.sh
set ofd [open config-status.sh w]
puts $ofd "#!/bin/bash"
puts -nonewline $ofd "$argv0 "
foreach a $argv {
set len 1
if {[catch {llength $a} len] || $len > 1 } {
puts -nonewline $ofd "'$a' "
} else {
puts -nonewline $ofd "$a "
}
}
puts $ofd ""
close $ofd
file attributes config-status.sh -permissions +x
set cmakeopt ""
resolve_disablers

View file

@ -25,47 +25,55 @@
# Options processed here internally, not passed to cmake
set internal_options {
with-compiler-prefix=<prefix> "set C/C++ toolchains <prefix>gcc and <prefix>g++"
with-compiler-type=<name> "compiler type: gcc(default), cc, others simply add ++ for C++"
with-srt-name=<name> "Override srt library name"
with-haicrypt-name=<name> "Override haicrypt library name (if compiled separately)"
with-compiler-prefix=<prefix> "set C/C++ toolchains <prefix>gcc and <prefix>g++"
with-compiler-type=<name> "compiler type: gcc(default), cc, others simply add ++ for C++"
with-srt-name=<name> "Override srt library name"
with-haicrypt-name=<name> "Override haicrypt library name (if compiled separately)"
with-atomic=<spec> "Select implementation for atomics (compiler-intrinsics or sync-mutex)"
}
# Options that refer directly to variables used in CMakeLists.txt
set cmake_options {
cygwin-use-posix "Should the POSIX API be used for cygwin. Ignored if the system isn't cygwin. (default: OFF)"
enable-encryption "Should encryption features be enabled (default: ON)"
enable-c++11 "Should the c++11 parts (srt-live-transmit) be enabled (default: ON)"
enable-c++11 "Should the c++11 parts (srt-live-transmit) be enabled (default: ON, with gcc < 4.7 OFF)"
enable-apps "Should the Support Applications be Built? (default: ON)"
enable-bonding "Enable 'bonding' SRT feature (default: OFF)"
enable-testing "Should developer testing applications be built (default: OFF)"
enable-c++-deps "Extra library dependencies in srt.pc for C language (default: OFF)"
enable-heavy-logging "Should heavy debug logging be enabled (default: OFF)"
enable-profile "Should instrument the code for profiling. Ignored for non-GNU compiler. (default: OFF)"
enable-logging "Should logging be enabled (default: ON)"
enable-debug=<0,1,2> "Enable debug mode (0=disabled, 1=debug, 2=rel-with-debug)"
enable-heavy-logging "Should heavy debug logging be enabled (default: OFF)"
enable-haicrypt-logging "Should logging in haicrypt be enabled (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)"
enable-getnameinfo "In-logs sockaddr-to-string should do rev-dns (default: OFF)"
enable-unittests "Enable Unit Tests (will download Google UT) (default: OFF)"
enable-encryption "Should encryption features be enabled (default: ON)"
enable-c++-deps "Extra library dependencies in srt.pc for C language (default: ON)"
use-static-libstdc++ "Should use static rather than shared libstdc++ (default: OFF)"
enable-inet-pton "Set to OFF to prevent usage of inet_pton when building against modern SDKs (default: ON)"
enable-code-coverage "Enable code coverage reporting (default: OFF)"
enable-monotonic-clock "Enforced clock_gettime with monotonic clock on GC CV /temporary fix for #729/ (default: OFF)"
enable-profile "Should instrument the code for profiling. Ignored for non-GNU compiler. (default: OFF)"
enable-relative-libpath "Should applications contain relative library paths, like ../lib (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-suflip "Should suflip tool be built (default: OFF)"
enable-getnameinfo "In-logs sockaddr-to-string should do rev-dns (default: OFF)"
enable-unittests "Enable unit tests (default: OFF)"
enable-thread-check "Enable #include <threadcheck.h> that implements THREAD_* macros"
openssl-crypto-library=<filepath> "Path to a library."
openssl-include-dir=<path> "Path to a file."
openssl-ssl-library=<filepath> "Path to a library."
pkg-config-executable=<filepath> "pkg-config executable"
pthread-include-dir=<path> "Path to a file."
pthread-library=<filepath> "Path to a library."
enable-stdc++-sync "Use standard C++11 chrono/threads instead of pthread wrapper (default: OFF, on Windows: ON)"
use-openssl-pc "Use pkg-config to find OpenSSL libraries (default: ON)"
openssl-use-static-libs "Link OpenSSL statically (default: OFF)."
use-busy-waiting "Enable more accurate sending times at a cost of potentially higher CPU load (default: OFF)"
use-gnustl "Get c++ library/headers from the gnustl.pc"
enable-sock-cloexec "Enable setting SOCK_CLOEXEC on a socket (default: ON)"
enable-show-project-config "Enables use of ShowProjectConfig() in cmake (default: OFF)"
enable-new-rcvbuffer "Enables the new receiver buffer implementation (default: ON)"
enable-clang-tsa "Enable Clang's Thread-Safety-Analysis (default: OFF)"
atomic-use-srt-sync-mutex "Use mutex to implement atomics (alias: --with-atomic=sync-mutex) (default: OFF)"
use-enclib "Encryption library to be used: openssl(default), gnutls, mbedtls"
use-gnutls "DEPRECATED. Use USE_ENCLIB=openssl|gnutls|mbedtls instead"
use-openssl-pc "Use pkg-config to find OpenSSL libraries (default: ON)"
use-static-libstdc++ "Should use static rather than shared libstdc++ (default: OFF)"
enable-debug=<0,1,2> "Enable debug mode (0=disabled, 1=debug, 2=rel-with-debug)"
pkg-config-executable=<filepath> "pkg-config executable"
openssl-crypto-library=<filepath> "OpenSSL: Path to a libcrypto library."
openssl-include-dir=<path> "OpenSSL: Path to includes."
openssl-ssl-library=<filepath> "OpenSSL: Path to a libssl library."
pthread-include-dir=<path> "PThread: Path to includes"
pthread-library=<filepath> "PThread: Path to the pthread library."
}
set options $internal_options$cmake_options
@ -162,6 +170,24 @@ proc preprocess {} {
set ::haicrypt_name $::optval(--with-haicrypt-name)
unset ::optval(--with-haicrypt-name)
}
if { "--with-atomic" in $::optkeys } {
switch -- $::optval(--with-atomic) {
compiler-intrinsics {
}
sync-mutex {
set ::optval(--atomic-use-srt-sync-mutex) 1
}
default {
puts "ERROR: --with-atomic option accepts two values: compiler-intrinsics (default) or sync-mutex"
exit 1
}
}
unset ::optval(--with-atomic)
}
}
proc GetCompilerCommand {} {
@ -170,9 +196,16 @@ proc GetCompilerCommand {} {
# --cmake-c[++]-compiler
# (cmake-toolchain-file will set things up without the need to check things here)
set compiler gcc
if { [info exists ::optval(--with-compiler-type)] } {
set compiler $::optval(--with-compiler-type)
}
if { [info exists ::optval(--with-compiler-prefix)] } {
set prefix $::optval(--with-compiler-prefix)
return ${prefix}gcc
return ${prefix}$compiler
} else {
return $compiler
}
if { [info exists ::optval(--cmake-c-compiler)] } {
@ -201,6 +234,7 @@ proc postprocess {} {
set toolchain_changed no
foreach changer {
--with-compiler-prefix
--with-compiler-type
--cmake-c-compiler
--cmake-c++-compiler
--cmake-cxx-compiler
@ -223,6 +257,7 @@ proc postprocess {} {
# Check characteristics of the compiler - in particular, whether the target is different
# than the current target.
set compiler_path ""
set target_platform ""
set cmd [GetCompilerCommand]
if { $cmd != "" } {
set gcc_version [exec $cmd -v 2>@1]
@ -237,7 +272,7 @@ proc postprocess {} {
}
if { $target_platform == "" } {
puts "NOTE: can't obtain target from gcc -v: $l"
puts "NOTE: can't obtain target from '[file tail $cmd] -v': $l - ASSUMING HOST compiler"
} else {
if { $target_platform != $::tcl_platform(machine) } {
puts "NOTE: foreign target type detected ($target)" ;# - setting CROSSCOMPILING flag"
@ -245,6 +280,8 @@ proc postprocess {} {
set iscross 1
}
}
} else {
puts "CONFIGURE: default compiler used"
}
}
@ -332,8 +369,19 @@ proc postprocess {} {
# Otherwise don't set PKG_CONFIG_PATH and we'll see.
}
if { $::HAVE_DARWIN && !$toolchain_changed} {
set use_brew 0
if { $::HAVE_DARWIN && !$toolchain_changed } {
set use_brew 1
}
if { $use_brew } {
foreach item $::cmakeopt {
if { [string first "Android" $item] != -1 } {
set use_brew 0
break
}
}
}
if { $use_brew } {
if { $have_gnutls } {
# Use gnutls explicitly, as found in brew
set er [catch {exec brew info gnutls} res]

View file

@ -1,5 +1,5 @@
#ifndef INC__CRYSPR_CONFIG_H
#define INC__CRYSPR_CONFIG_H
#ifndef INC_SRT_CRYSPR_CONFIG_H
#define INC_SRT_CRYSPR_CONFIG_H
// Size of the single block for encryption.
// This might need tweaking for particular implementation library.
@ -8,14 +8,22 @@
#if defined(USE_OPENSSL)
#include "cryspr-openssl.h"
#define cryspr4SRT() crysprOpenSSL()
#define CRYSPR_IMPL_DESC "OpenSSL-AES"
#elif defined(USE_OPENSSL_EVP)
#include "cryspr-openssl-evp.h"
#define cryspr4SRT() crysprOpenSSL_EVP()
#define CRYSPR_IMPL_DESC "OpenSSL-EVP"
#elif defined(USE_GNUTLS)
#include "cryspr-gnutls.h"
#define cryspr4SRT() crysprGnuTLS()
#define CRYSPR_IMPL_DESC "GnuTLS"
#elif defined(USE_MBEDTLS)
#include "cryspr-mbedtls.h"
#define cryspr4SRT() crysprMbedtls()
#define CRYSPR_IMPL_DESC "MbedTLS"
#else
#error Cryspr implementation not selected. Please define USE_* + OPENSSL/GNUTLS/MBEDTLS.
#define CRYSPR_IMPL_DESC "No Cipher"
#endif

View file

@ -13,6 +13,8 @@
written by
Haivision Systems Inc.
2022-05-19 (jdube)
CRYSPR2 adaptation
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
@ -24,6 +26,10 @@ written by
typedef struct tag_crysprGnuTLS_AES_cb {
CRYSPR_cb ccb; /* CRYSPR control block */
/* Add other cryptolib specific data here */
#ifdef CRYSPR2
CRYSPR_AESCTX aes_kek_buf; /* Key Encrypting Key (KEK) */
CRYSPR_AESCTX aes_sek_buf[2]; /* even/odd Stream Encrypting Key (SEK) */
#endif
} crysprGnuTLS_cb;
@ -33,11 +39,14 @@ int crysprGnuTLS_Prng(unsigned char *rn, int len)
}
int crysprGnuTLS_AES_SetKey(
int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR] */
bool bEncrypt, /* true:encrypt key, false:decrypt key*/
const unsigned char *kstr, /* key string */
size_t kstr_len, /* kstr length in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
{
(void)cipher_type;
if (bEncrypt) { /* Encrypt key */
if (!(kstr_len == 16 || kstr_len == 24 || kstr_len == 32)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) bad length\n");
@ -114,6 +123,31 @@ int crysprGnuTLS_AES_CtrCipher( /* AES-CTR128 Encryption */
return 0;
}
#ifdef CRYSPR2
static CRYSPR_cb *crysprGnuTLS_Open(CRYSPR_methods *cryspr, size_t max_len)
{
crysprGnuTLS_cb *aes_data;
CRYSPR_cb *cryspr_cb;
aes_data = (crysprGnuTLS_cb *)crysprHelper_Open(cryspr, sizeof(crysprGnuTLS_cb), max_len);
if (NULL == aes_data) {
HCRYPT_LOG(LOG_ERR, "crysprHelper_Open(%p, %zd, %zd) failed\n", cryspr, sizeof(crysprGnuTLS_cb), max_len);
return(NULL);
}
aes_data->ccb.aes_kek = &aes_data->aes_kek_buf; //key encrypting key
aes_data->ccb.aes_sek[0] = &aes_data->aes_sek_buf[0]; //stream encrypting key
aes_data->ccb.aes_sek[1] = &aes_data->aes_sek_buf[1]; //stream encrypting key
return(&aes_data->ccb);
}
static int crysprGnuTLS_Close(CRYSPR_cb *cryspr_cb)
{
return(crysprHelper_Close(cryspr_cb));
}
#endif /* CRYSPR2 */
#ifdef CRYSPR_HAS_PBKDF2
/*
* Password-based Key Derivation Function
@ -157,8 +191,13 @@ CRYSPR_methods *crysprGnuTLS(void)
#endif
//--Crypto Session (Top API)
#ifdef CRYSPR2
crysprGnuTLS_methods.open = crysprGnuTLS_Open;
crysprGnuTLS_methods.close = crysprGnuTLS_Close;
#else /* CRYSPR2 */
// crysprGnuTLS_methods.open =
// crysprGnuTLS_methods.close =
#endif /* CRYSPR2 */
//--Keying material (km) encryption
#if CRYSPR_HAS_PBKDF2
crysprGnuTLS_methods.km_pbkdf2 = crysprGnuTLS_KmPbkdf2;

View file

@ -13,6 +13,8 @@
written by
Haivision Systems Inc.
2022-05-19 (jdube)
CRYSPR2 adaptation
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
@ -29,11 +31,14 @@ written by
// Static members of cryspr::mbedtls class.
static mbedtls_ctr_drbg_context crysprMbedtls_ctr_drbg;
static mbedtls_entropy_context crysprMbedtls_entropy;
static mbedtls_md_context_t crysprMbedtls_mdctx;
typedef struct tag_crysprGnuTLS_AES_cb {
CRYSPR_cb ccb; /* CRYSPR control block */
/* Add other cryptolib specific data here */
#ifdef CRYSPR2
CRYSPR_AESCTX aes_kek_buf; /* Key Encrypting Key (KEK) */
CRYSPR_AESCTX aes_sek_buf[2]; /* even/odd Stream Encrypting Key (SEK) */
#endif
} crysprMbedtls_cb;
@ -49,18 +54,23 @@ int crysprMbedtls_Prng(unsigned char *rn, int len)
}
int crysprMbedtls_AES_SetKey(
int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR] */
bool bEncrypt, /* true:encrypt key, false:decrypt key*/
const unsigned char *kstr, /* key string */
size_t kstr_len, /* kstr length in bytes (16, 24, or 32 bytes, for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
{
(void)cipher_type;
if (!(kstr_len == 16 || kstr_len == 24 || kstr_len == 32)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) bad length\n");
return -1;
}
int ret;
#ifdef CRYSPR2
(void)cipher_type;
#endif
// mbedtls uses the "bits" convention (128, 192, 254), just like openssl.
// kstr_len is in "bytes" convention (16, 24, 32).
@ -146,6 +156,31 @@ int crysprMbedtls_AES_CtrCipher( /* AES-CTR128 Encryption */
return 0;
}
#ifdef CRYSPR2
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) {
HCRYPT_LOG(LOG_ERR, "crysprHelper_Open(%p, %zd, %zd) failed\n", cryspr, sizeof(crysprMbedtls_cb), max_len);
return(NULL);
}
aes_data->ccb.aes_kek = &aes_data->aes_kek_buf; //key encrypting key
aes_data->ccb.aes_sek[0] = &aes_data->aes_sek_buf[0]; //stream encrypting key
aes_data->ccb.aes_sek[1] = &aes_data->aes_sek_buf[1]; //stream encrypting key
return(&aes_data->ccb);
}
static int crysprMbedtls_Close(CRYSPR_cb *cryspr_cb)
{
return(crysprHelper_Close(cryspr_cb));
}
#endif /* CRYSPR2 */
/*
* Password-based Key Derivation Function
*/
@ -161,10 +196,30 @@ int crysprMbedtls_KmPbkdf2(
{
(void)cryspr_cb;
int ret = mbedtls_pkcs5_pbkdf2_hmac(&crysprMbedtls_mdctx,
const mbedtls_md_info_t* ifo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
if ( ifo == NULL ) {
// XXX report error, log?
return -1;
}
mbedtls_md_context_t mdctx;
mbedtls_md_init(&mdctx);
const int yes_use_hmac = 1;
int ret;
if ( (ret = mbedtls_md_setup(&mdctx, ifo, yes_use_hmac)) != 0 ) {
mbedtls_md_free(&mdctx);
// XXX report error, log?
return ret;
}
ret = mbedtls_pkcs5_pbkdf2_hmac(&mdctx,
(unsigned char*)passwd, passwd_len, salt, salt_len,
itr, key_len, out);
mbedtls_md_free(&mdctx);
if (ret == 0)
return 0;
@ -196,8 +251,13 @@ CRYSPR_methods *crysprMbedtls(void)
#endif
//--Crypto Session (Top API)
#ifdef CRYSPR2
crysprMbedtls_methods.open = crysprMbedtls_Open;
crysprMbedtls_methods.close = crysprMbedtls_Close;
#else
// crysprMbedtls_methods.open =
// crysprMbedtls_methods.close =
#endif
//--Keying material (km) encryption
crysprMbedtls_methods.km_pbkdf2 = crysprMbedtls_KmPbkdf2;
// crysprMbedtls_methods.km_setkey =
@ -220,14 +280,6 @@ CRYSPR_methods *crysprMbedtls(void)
return NULL;
}
// Ok, mbedtls with all flexibility you couldn't make it more complicated.
mbedtls_md_init(&crysprMbedtls_mdctx);
const mbedtls_md_info_t* ifo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
const int yes_use_hmac = 1;
mbedtls_md_setup(&crysprMbedtls_mdctx, ifo, yes_use_hmac);
return(&crysprMbedtls_methods);
}

View file

@ -52,10 +52,10 @@ written by
/*
#define CRYSPR_AESCTX to the CRYSPR specifix AES key context object.
This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK
It is set from hte keystring through CRYSPR_methods.aes_set_key and passed
It is set from the keystring through CRYSPR_methods.aes_set_key and passed
to CRYSPR_methods.aes_XXX.
*/
typedef struct mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */
typedef mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */
struct tag_CRYSPR_methods *crysprMbedtls(void);

View file

@ -0,0 +1,335 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 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/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2022-05-19 (jdube)
OpenSSL EVP CRYSPR/4SRT (CRYypto Service PRovider for SRT).
*****************************************************************************/
#include "hcrypt.h"
#include <string.h>
typedef struct tag_crysprOpenSSL_EVP_cb
{
CRYSPR_cb ccb;
/* Add cryptolib specific data here */
} crysprOpenSSL_EVP_cb;
int crysprOpenSSL_EVP_Prng(unsigned char* rn, int len)
{
return (RAND_bytes(rn, len) <= 0 ? -1 : 0);
}
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},
};
int crysprOpenSSL_EVP_AES_SetKey(
int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR] */
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) /* 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 */
switch (cipher_type)
{
case HCRYPT_CTX_MODE_CLRTXT:
return 0;
case HCRYPT_CTX_MODE_AESECB:
break;
case HCRYPT_CTX_MODE_AESCTR:
#if !CRYSPR_HAS_AESCTR
/* internal implementation of AES-CTR using crypto lib's AES-ECB */
cipher_type = HCRYPT_CTX_MODE_AESECB;
#endif
break;
default:
HCRYPT_LOG(LOG_ERR,
"invalid cipher type (%d). Expected: [%d..%d]\n",
cipher_type,
HCRYPT_CTX_MODE_AESECB,
HCRYPT_CTX_MODE_AESCTR);
return (-1);
}
switch (kstr_len)
{
case 128 / 8:
case 192 / 8:
case 256 / 8:
break;
default:
HCRYPT_LOG(LOG_ERR, "invalid key length (%d). Expected: 16, 24, 32\n", (int)kstr_len);
return -1;
}
cipher = _crysprOpenSSL_EVP_cipher_fnptr[cipher_type][idxKlen]();
if (bEncrypt)
{ /* Encrypt key */
if (!EVP_EncryptInit_ex(aes_key, cipher, NULL, kstr, NULL))
{
HCRYPT_LOG(LOG_ERR, "%s", "EVP_CipherInit_ex(kek) failed\n");
return (-1);
}
}
else
{ /* Decrypt key */
if (!EVP_DecryptInit_ex(aes_key, cipher, NULL, kstr, NULL))
{
HCRYPT_LOG(LOG_ERR, "%s", "EVP_CipherInit_ex(kek) failed\n");
return (-1);
}
}
return (0);
}
static CRYSPR_cb* crysprOpenSSL_EVP_Open(CRYSPR_methods* cryspr, size_t max_len)
{
CRYSPR_cb* cryspr_cb = crysprHelper_Open(cryspr, sizeof(*cryspr_cb), max_len);
if (NULL == cryspr_cb)
{
HCRYPT_LOG(LOG_ERR, "crysprFallback_Open(%p, %zd) failed\n", cryspr, max_len);
return (NULL);
}
cryspr_cb->aes_kek = EVP_CIPHER_CTX_new();
cryspr_cb->aes_sek[0] = EVP_CIPHER_CTX_new();
cryspr_cb->aes_sek[1] = EVP_CIPHER_CTX_new();
return (cryspr_cb);
}
static int crysprOpenSSL_EVP_Close(CRYSPR_cb* cryspr_cb)
{
if (NULL != cryspr_cb)
{
EVP_CIPHER_CTX_free(cryspr_cb->aes_sek[0]);
EVP_CIPHER_CTX_free(cryspr_cb->aes_sek[1]);
EVP_CIPHER_CTX_free(cryspr_cb->aes_kek);
}
return (crysprHelper_Close(cryspr_cb));
}
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
int crysprOpenSSL_EVP_AES_EcbCipher(bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX* aes_key, /* CRYpto Service PRovider AES Key context */
const unsigned char* indata, /* src (clear text if encrypt, cipher text otherwise)*/
size_t inlen, /* indata length */
unsigned char* out_txt, /* dst (cipher text if encrypt, clear text otherwise) */
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 */
size_t outsiz = (outlen_p ? *outlen_p : 0);
int c_len = 0, f_len = 0;
(void)bEncrypt; // not needed, alreadydefined in context
if (outsiz % CRYSPR_AESBLKSZ)
{
HCRYPT_LOG(LOG_ERR, "%s\n", "EcbCipher() no room for PKCS7 padding");
return (-1); /* output buf size must be a multiple of AES block size (16) */
}
if ((outsiz > 16) && ((int)outsiz < (nblk * CRYSPR_AESBLKSZ)))
{
HCRYPT_LOG(LOG_ERR, "%s\n", "EcbCipher() no room for PKCS7 padding");
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))
{
HCRYPT_LOG(LOG_ERR, "EVP_CipherInit_ex(%p,NULL,...,-1) failed\n", aes_key);
return -1;
}
if (!EVP_CIPHER_CTX_set_padding(aes_key, 0))
{
HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CIPHER_CTX_set_padding(%p) failed", aes_key);
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, inlen))
{
HCRYPT_LOG(LOG_ERR, "EVP_CipherUpdate(%p, out, %d, in, %d) failed\n", aes_key, c_len, inlen);
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;
}
if (outlen_p != NULL) *outlen_p = nblk * CRYSPR_AESBLKSZ;
return 0;
}
#endif /* !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP) */
int crysprOpenSSL_EVP_AES_CtrCipher(bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX* aes_key, /* CRYpto Service PRovider AES Key context */
unsigned char* iv, /* iv */
const unsigned char* indata, /* src */
size_t inlen, /* length */
unsigned char* out_txt) /* dest */
{
int c_len, f_len;
(void)bEncrypt;
/* 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;
}
/* 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))
{
HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherUpdate() 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;
}
return 0;
}
/*
* Password-based Key Derivation Function
*/
int crysprOpenSSL_EVP_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 */
{
(void)cryspr_cb;
int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd, passwd_len, salt, salt_len, itr, key_len, out);
return (rc == 1 ? 0 : -1);
}
#if CRYSPR_HAS_AESKWRAP
int crysprOpenSSL_EVP_KmWrap(CRYSPR_cb* cryspr_cb, unsigned char* wrap, const unsigned char* sek, unsigned int seklen)
{
crysprOpenSSL_EVP_cb* aes_data = (crysprOpenSSL_EVP_cb*)cryspr_cb;
EVP_CIPHER_CTX* kek = CRYSPR_GETKEK(cryspr_cb); // key encrypting key
return (((seklen + HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_wrap_key(kek, NULL, wrap, sek, seklen)) ? 0 : -1);
}
int crysprOpenSSL_EVP_KmUnwrap(CRYSPR_cb* cryspr_cb,
unsigned char* sek, // Stream encrypting key
const unsigned char* wrap,
unsigned int wraplen)
{
crysprOpenSSL_EVP_cb* aes_data = (crysprOpenSSL_EVP_cb*)cryspr_cb;
EVP_CIPHER_CTX* kek = CRYSPR_GETKEK(cryspr_cb); // key encrypting key
return (((wraplen - HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_unwrap_key(kek, NULL, sek, wrap, wraplen)) ? 0
: -1);
}
#endif /*CRYSPR_HAS_AESKWRAP*/
static CRYSPR_methods crysprOpenSSL_EVP_methods;
CRYSPR_methods* crysprOpenSSL_EVP(void)
{
if (NULL == crysprOpenSSL_EVP_methods.open)
{
crysprInit(&crysprOpenSSL_EVP_methods); // Default/fallback methods
crysprOpenSSL_EVP_methods.prng = crysprOpenSSL_EVP_Prng;
//--CryptoLib Primitive API-----------------------------------------------
crysprOpenSSL_EVP_methods.aes_set_key = crysprOpenSSL_EVP_AES_SetKey;
#if CRYSPR_HAS_AESCTR
crysprOpenSSL_EVP_methods.aes_ctr_cipher = crysprOpenSSL_EVP_AES_CtrCipher;
#endif
#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
to test the falback methods */
crysprOpenSSL_EVP_methods.aes_ecb_cipher = crysprOpenSSL_EVP_AES_EcbCipher;
#endif
#if !CRYSPR_HAS_PBKDF2
crysprOpenSSL_EVP_methods.sha1_msg_digest = NULL; // Required to use eventual default/fallback KmPbkdf2
#endif
//--Crypto Session API-----------------------------------------
crysprOpenSSL_EVP_methods.open = crysprOpenSSL_EVP_Open;
crysprOpenSSL_EVP_methods.close = crysprOpenSSL_EVP_Close;
//--Keying material (km) encryption
#if CRYSPR_HAS_PBKDF2
crysprOpenSSL_EVP_methods.km_pbkdf2 = crysprOpenSSL_EVP_KmPbkdf2;
#else
#error There is no default/fallback method for PBKDF2
#endif
// crysprOpenSSL_EVP_methods.km_setkey =
#if CRYSPR_HAS_AESKWRAP
crysprOpenSSL_EVP_methods.km_wrap = crysprOpenSSL_EVP_KmWrap;
crysprOpenSSL_EVP_methods.km_unwrap = crysprOpenSSL_EVP_KmUnwrap;
#endif
//--Media stream (ms) encryption
// crysprOpenSSL_EVP_methods.ms_setkey =
// crysprOpenSSL_EVP_methods.ms_encrypt =
// crysprOpenSSL_EVP_methods.ms_decrypt =
}
return (&crysprOpenSSL_EVP_methods);
}

View file

@ -0,0 +1,63 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 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/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2022-05-19 (jdube)
OpenSSL EVP AES CRYSPR/4SRT (CRYypto Service PRovider for SRT).
*****************************************************************************/
#ifndef CRYSPR_OPENSSL_H
#define CRYSPR_OPENSSL_H
#include <openssl/evp.h> /* PKCS5_xxx() */
#include <openssl/aes.h> /* AES_xxx() */
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL))
#include <openssl/modes.h> /* CRYPTO_xxx() */
#endif
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */
/* Define CRYSPR_HAS_AESCTR to 1 if this CRYSPR has AESCTR cipher mode
if not set it 0 to use enable CTR cipher mode implementation using ECB cipher mode
and provide the aes_ecb_cipher method.
*/
#define CRYSPR_HAS_AESCTR 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 .
*/
#if 1 // Force internal AES-WRAP (using AES-ECB) until implemented with EVP (OPENSSL_VERSION_NUMBER < 0x00xxxxxxL)
#define CRYSPR_HAS_AESKWRAP 0
#else
#define CRYSPR_HAS_AESKWRAP 1
#endif
/* Define CRYSPR_HAS_PBKDF2 to 1 if this CRYSPR has SHA1-HMAC Password-based Key Derivaion Function 2
if not set to 0 to enable not-yet-implemented/fallback crysprFallback.km_pbkdf2 method
and provide the sha1_msg_digest method.
*/
#define CRYSPR_HAS_PBKDF2 1 /* Define to 1 if CRYSPR has Password-based Key Derivaion Function 2 */
/*
#define CRYSPR_AESCTX to the CRYSPR specifix AES key context object.
This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK
It is set from hte keystring through CRYSPR_methods.aes_set_key and passed
to CRYSPR_methods.aes_*.
*/
typedef EVP_CIPHER_CTX CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */
struct tag_CRYSPR_methods* crysprOpenSSL_EVP(void);
#endif /* CRYSPR_OPENSSL_H */

View file

@ -13,18 +13,23 @@
written by
Haivision Systems Inc.
2022-05-19 (jdube)
CRYSPR2 adaptation
2019-06-26 (jdube)
OpenSSL CRYSPR/4SRT (CRYypto Service PRovider for SRT).
OpenSSL Direct AES CRYSPR/4SRT (CRYypto Service PRovider for SRT).
*****************************************************************************/
#include "hcrypt.h"
#include <string.h>
typedef struct tag_crysprOpenSSL_AES_cb {
CRYSPR_cb ccb;
/* Add cryptolib specific data here */
CRYSPR_cb ccb;
/* Add cryptolib specific data here */
#ifdef CRYSPR2
CRYSPR_AESCTX aes_kek_buf; /* Key Encrypting Key (KEK) */
CRYSPR_AESCTX aes_sek_buf[2]; /* even/odd Stream Encrypting Key (SEK) */
#endif
} crysprOpenSSL_cb;
@ -34,11 +39,14 @@ int crysprOpenSSL_Prng(unsigned char *rn, int len)
}
int crysprOpenSSL_AES_SetKey(
int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR] */
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) /* CRYpto Service PRovider AES Key context */
{
(void)cipher_type;
if (bEncrypt) { /* Encrypt key */
if (AES_set_encrypt_key(kstr, kstr_len * 8, aes_key)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) failed\n");
@ -123,7 +131,24 @@ int crysprOpenSSL_AES_CtrCipher(
#endif
return 0;
}
#ifdef CRYSPR2
static CRYSPR_cb *crysprOpenSSL_Open(CRYSPR_methods *cryspr, size_t max_len)
{
crysprOpenSSL_cb *aes_data;
aes_data = (crysprOpenSSL_cb *)crysprHelper_Open(cryspr, sizeof(crysprOpenSSL_cb), max_len);
if (NULL == aes_data) {
HCRYPT_LOG(LOG_ERR, "crysprHelper_Open(%p, %zd, %zd) failed\n", cryspr, sizeof(crysprOpenSSL_cb), max_len);
return(NULL);
}
aes_data->ccb.aes_kek = &aes_data->aes_kek_buf; //key encrypting key
aes_data->ccb.aes_sek[0] = &aes_data->aes_sek_buf[0]; //stream encrypting key
aes_data->ccb.aes_sek[1] = &aes_data->aes_sek_buf[1]; //stream encrypting key
return(&aes_data->ccb);
}
#endif /* CRYSPR2 */
/*
* Password-based Key Derivation Function
*/
@ -148,8 +173,7 @@ int crysprOpenSSL_KmWrap(CRYSPR_cb *cryspr_cb,
const unsigned char *sek,
unsigned int seklen)
{
crysprOpenSSL_cb *aes_data = (crysprOpenSSL_cb *)cryspr_cb;
AES_KEY *kek = &aes_data->ccb.aes_kek; //key encrypting key
AES_KEY *kek = CRYSPR_GETKEK(cryspr_cb); //key encrypting key
return(((seklen + HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_wrap_key(kek, NULL, wrap, sek, seklen)) ? 0 : -1);
}
@ -160,8 +184,7 @@ int crysprOpenSSL_KmUnwrap(
const unsigned char *wrap,
unsigned int wraplen)
{
crysprOpenSSL_cb *aes_data = (crysprOpenSSL_cb *)cryspr_cb;
AES_KEY *kek = &aes_data->ccb.aes_kek; //key encrypting key
AES_KEY *kek = CRYSPR_GETKEK(cryspr_cb); //key encrypting key
return(((wraplen - HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_unwrap_key(kek, NULL, sek, wrap, wraplen)) ? 0 : -1);
}
@ -192,7 +215,11 @@ CRYSPR_methods *crysprOpenSSL(void)
#endif
//--Crypto Session API-----------------------------------------
#ifdef CRYSPR2
crysprOpenSSL_methods.open = crysprOpenSSL_Open;
#else
// crysprOpenSSL_methods.open =
#endif
// crysprOpenSSL_methods.close =
//--Keying material (km) encryption

View file

@ -31,11 +31,13 @@ int crysprStub_Prng(unsigned char *rn, int len)
}
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 */
{
(void)cipher_type;
(void)bEncrypt;
(void)kstr;
(void)kstr_len;
@ -122,10 +124,10 @@ int crysprStub_KmPbkdf2(
static int crysprFallback_KmSetKey(CRYSPR_cb *cryspr_cb, bool bWrap, const unsigned char *kek, size_t kek_len)
{
CRYSPR_AESCTX *aes_kek = &cryspr_cb->aes_kek;
CRYSPR_AESCTX *aes_kek = CRYSPR_GETKEK(cryspr_cb);
if (cryspr_cb->cryspr->aes_set_key(bWrap, kek, kek_len, aes_kek)) {
HCRYPT_LOG(LOG_ERR, "AES_set_%s_key(kek) failed\n", bWrap? "encrypt": "decrypt");
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);
@ -163,7 +165,9 @@ int crysprFallback_AES_WrapKey(CRYSPR_cb *cryspr_cb,
memcpy(B + 8, R, 8);
{
size_t outlen = 16;
cryspr_cb->cryspr->aes_ecb_cipher(true, &cryspr_cb->aes_kek, B, 16, B, &outlen);
CRYSPR_AESCTX *aes_kek = CRYSPR_GETKEK(cryspr_cb);
cryspr_cb->cryspr->aes_ecb_cipher(true, aes_kek, B, 16, B, &outlen);
}
A[7] ^= (unsigned char)(t & 0xff);
if (t > 0xff)
@ -211,7 +215,9 @@ int crysprFallback_AES_UnwrapKey(CRYSPR_cb *cryspr_cb,
memcpy(B + 8, R, 8);
{
size_t outlen = 16;
cryspr_cb->cryspr->aes_ecb_cipher(false, &cryspr_cb->aes_kek, B, 16, B, &outlen);
CRYSPR_AESCTX *aes_kek = CRYSPR_GETKEK(cryspr_cb);
cryspr_cb->cryspr->aes_ecb_cipher(false, aes_kek, B, 16, B, &outlen);
}
memcpy(R, B + 8, 8);
}
@ -237,20 +243,23 @@ static unsigned char *_crysprFallback_GetOutbuf(CRYSPR_cb *cryspr_cb, size_t pfx
return(out_buf);
}
static CRYSPR_cb *crysprFallback_Open(CRYSPR_methods *cryspr, size_t max_len)
CRYSPR_cb *crysprHelper_Open(CRYSPR_methods *cryspr, size_t cb_len, size_t max_len)
{
CRYSPR_cb *cryspr_cb;
unsigned char *membuf;
size_t memsiz, padded_len = hcryptMsg_PaddedLen(max_len, 128/8);
HCRYPT_LOG(LOG_DEBUG, "%s", "Using OpenSSL AES\n");
memsiz = sizeof(*cryspr_cb) + (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 */
cryspr_cb = malloc(memsiz);
cryspr_cb = calloc(1, memsiz);
if (NULL == cryspr_cb) {
HCRYPT_LOG(LOG_ERR, "malloc(%zd) failed\n", memsiz);
return(NULL);
@ -258,6 +267,9 @@ static CRYSPR_cb *crysprFallback_Open(CRYSPR_methods *cryspr, size_t max_len)
membuf = (unsigned char *)cryspr_cb;
membuf += 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;
membuf += HCRYPT_CTR_STREAM_SZ;
@ -275,26 +287,37 @@ static CRYSPR_cb *crysprFallback_Open(CRYSPR_methods *cryspr, size_t max_len)
return(cryspr_cb);
}
int crysprHelper_Close(CRYSPR_cb *cryspr_cb)
{
free(cryspr_cb);
return(0);
}
static CRYSPR_cb *crysprFallback_Open(CRYSPR_methods *cryspr, size_t max_len)
{
CRYSPR_cb *cryspr_cb;
cryspr_cb = crysprHelper_Open(cryspr, sizeof(CRYSPR_cb), max_len);
return(cryspr_cb);
}
static int crysprFallback_Close(CRYSPR_cb *cryspr_cb)
{
if (NULL != cryspr_cb) {
free(cryspr_cb);
}
return(0);
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_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)]; /* Ctx tells if it's for odd or even key */
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 */
|| (ctx->mode == HCRYPT_CTX_MODE_AESCTR)) { /* CTR mode decrypts using encryption methods */
if (cryspr_cb->cryspr->aes_set_key(true, key, key_len, aes_sek)) {
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");
return(-1);
}
} else { /* Decrypt key */
if (cryspr_cb->cryspr->aes_set_key(false, key, key_len, aes_sek)) {
if (cryspr_cb->cryspr->aes_set_key(HCRYPT_CTX_MODE_AESCTR, false, key, key_len, aes_sek)) {
HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_decrypt_key(sek) failed\n");
return(-1);
}
@ -376,135 +399,78 @@ static int crysprFallback_MsEncrypt(
/* Get buffer room from the internal circular output buffer */
out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, in_data[0].len);
if (NULL != out_msg) {
switch(ctx->mode) {
case HCRYPT_CTX_MODE_AESCTR: /* Counter mode */
{
#if CRYSPR_HAS_AESCTR
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
unsigned char iv[CRYSPR_AESBLKSZ];
/* 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);
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*/
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[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 cryspr 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_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;
break;
}
case HCRYPT_CTX_MODE_CLRTXT: /* Clear text mode (transparent mode for tests) */
memcpy(&out_msg[pfx_len], in_data[0].payload, in_data[0].len);
memcpy(out_msg, in_data[0].pfx, pfx_len);
out_len = in_data[0].len;
break;
default:
/* Unsupported cipher mode */
return(-1);
}
} else {
if (NULL == out_msg) {
/* input data too big */
return(-1);
}
if (out_len > 0) {
/* Encrypted messages have been produced */
if (NULL == out_p) {
switch(ctx->mode) {
case HCRYPT_CTX_MODE_AESCTR: /* Counter mode */
{
/* 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 */
unsigned char iv[CRYSPR_AESBLKSZ];
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Application did not provided output buffer,
* so copy encrypted message back in input buffer
*/
memcpy(in_data[0].pfx, out_msg, pfx_len);
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
/* XOR KeyStream with input text directly in input buffer */
hcrypt_XorStream(in_data[0].payload, &out_msg[pfx_len], out_len);
}else{
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
* 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 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]);
#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);
}
#else /* CRYSPR_HAS_AESCTR */
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
#endif /* CRYSPR_HAS_AESCTR */
} else {
/* Copy header in output buffer if needed */
if (pfx_len > 0) memcpy(out_msg, in_data[0].pfx, pfx_len);
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
hcrypt_XorStream(&out_msg[pfx_len], in_data[0].payload, out_len);
/* 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 */
out_p[0] = out_msg;
out_len_p[0] = pfx_len + out_len;
*nbout_p = 1;
#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;
break;
}
} else {
case HCRYPT_CTX_MODE_CLRTXT: /* Clear text mode (transparent mode for tests) */
memcpy(&out_msg[pfx_len], in_data[0].payload, in_data[0].len);
memcpy(out_msg, in_data[0].pfx, pfx_len);
out_len = in_data[0].len;
break;
default:
/* Unsupported cipher mode */
return(-1);
}
if (out_len <= 0) {
/*
* Nothing out
* This is not an error for implementations using deferred/async processing
@ -514,6 +480,39 @@ static int crysprFallback_MsEncrypt(
if (nbout_p != NULL) *nbout_p = 0;
return(-1);
}
/* Encrypted messages have been produced */
if (NULL == out_p) {
/*
* Application did not provided output buffer,
* so copy encrypted message back in input buffer
*/
memcpy(in_data[0].pfx, out_msg, pfx_len);
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
/* XOR KeyStream with input text directly in input buffer */
hcrypt_XorStream(in_data[0].payload, &out_msg[pfx_len], out_len);
}else{
/* Copy output data back in input buffer */
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);
#endif /* CRYSPR_HAS_AESCTR */
} else {
/* Copy header in output buffer if needed */
if (pfx_len > 0) memcpy(out_msg, in_data[0].pfx, pfx_len);
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
hcrypt_XorStream(&out_msg[pfx_len], in_data[0].payload, out_len);
}
#endif /* CRYSPR_HAS_AESCTR */
out_p[0] = out_msg;
out_len_p[0] = pfx_len + out_len;
*nbout_p = 1;
}
return(0);
}
@ -537,7 +536,7 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx,
{
#if CRYSPR_HAS_AESCTR
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
CRYSPR_AESCTX *aes_key = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx));
unsigned char iv[CRYSPR_AESBLKSZ];
/* Get input packet index (in network order) */
@ -566,7 +565,7 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx,
out_len = in_data[0].len;
#else /*CRYSPR_HAS_AESCTR*/
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
CRYSPR_AESCTX *aes_key = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx));
unsigned char iv[CRYSPR_AESBLKSZ];
int iret = 0;

View file

@ -39,8 +39,17 @@ extern "C" {
#include "cryspr-config.h"
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) */
#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) */
#define CRYSPR_GETKEK(cb) (&((cb)->aes_kek))
#define CRYSPR_GETSEK(cb,kk) (&((cb)->aes_sek[kk]))
#endif /*CRYSPR2*/
struct tag_CRYSPR_methods *cryspr;
@ -69,6 +78,7 @@ typedef struct tag_CRYSPR_methods {
int rn_len);
int (*aes_set_key)(
int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR|AESGDM] */
bool bEncrypt, /* true Enxcrypt key, false: decrypt */
const unsigned char *kstr,/* key string*/
size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
@ -194,6 +204,9 @@ typedef struct tag_CRYSPR_methods {
} CRYSPR_methods;
CRYSPR_cb *crysprHelper_Open(CRYSPR_methods *cryspr, size_t cb_len, size_t max_len);
int crysprHelper_Close(CRYSPR_cb *cryspr_cb);
CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr);
#ifdef __cplusplus

View file

@ -23,5 +23,4 @@ hcrypt_rx.c
hcrypt_sa.c
hcrypt_tx.c
hcrypt_xpt_srt.c
hcrypt_xpt_sta.c
haicrypt_log.cpp

View file

@ -21,5 +21,4 @@ hcrypt_rx.c
hcrypt_sa.c
hcrypt_tx.c
hcrypt_xpt_srt.c
hcrypt_xpt_sta.c
haicrypt_log.cpp

View file

@ -0,0 +1,24 @@
# HaiCrypt library contents
PUBLIC HEADERS
haicrypt.h
hcrypt_ctx.h
hcrypt_msg.h
PRIVATE HEADERS
hcrypt.h
cryspr.h
cryspr-openssl-evp.h
haicrypt_log.h
SOURCES
cryspr.c
cryspr-openssl-evp.c
hcrypt.c
hcrypt_ctx_rx.c
hcrypt_ctx_tx.c
hcrypt_rx.c
hcrypt_sa.c
hcrypt_tx.c
hcrypt_xpt_srt.c
haicrypt_log.cpp

View file

@ -21,5 +21,4 @@ hcrypt_rx.c
hcrypt_sa.c
hcrypt_tx.c
hcrypt_xpt_srt.c
hcrypt_xpt_sta.c
haicrypt_log.cpp

View file

@ -27,25 +27,9 @@ written by
#ifdef __cplusplus
extern "C" {
#endif
// setup exports
#if defined _WIN32 && !defined __MINGW__
#ifdef HAICRYPT_DYNAMIC
#ifdef HAICRYPT_EXPORTS
#define HAICRYPT_API __declspec(dllexport)
#else
#define HAICRYPT_API __declspec(dllimport)
#endif
#else
#define HAICRYPT_API
#endif
#else
#define HAICRYPT_API
#endif
typedef void *HaiCrypt_Cryspr;
HAICRYPT_API HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance (void); /* Return a default cryspr instance */
HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance (void); /* Return a default cryspr instance */
#define HAICRYPT_CIPHER_BLK_SZ 16 /* AES Block Size */
@ -108,21 +92,21 @@ typedef struct hcrypt_Session_str* HaiCrypt_Handle;
HAICRYPT_API int HaiCrypt_SetLogLevel(int level, int logfa);
int HaiCrypt_SetLogLevel(int level, int logfa);
HAICRYPT_API int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc);
HAICRYPT_API int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handle *phhc);
HAICRYPT_API int HaiCrypt_Close(HaiCrypt_Handle hhc);
HAICRYPT_API int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_p);
HAICRYPT_API int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout);
HAICRYPT_API int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout);
int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc);
int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handle *phhc);
int HaiCrypt_Close(HaiCrypt_Handle hhc);
int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_p);
int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout);
int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout);
HAICRYPT_API int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc);
HAICRYPT_API int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout);
HAICRYPT_API int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
HAICRYPT_API int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc);
int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout);
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);
/* Status values */

View file

@ -10,6 +10,8 @@
#if ENABLE_HAICRYPT_LOGGING
#include "haicrypt_log.h"
#include "hcrypt.h"
#include "haicrypt.h"
#include "../srtcore/srt.h"
@ -18,7 +20,7 @@
extern srt_logging::LogConfig srt_logger_config;
// LOGFA symbol defined in srt.h
srt_logging::Logger hclog(SRT_LOGFA_HAICRYPT, srt_logger_config, "SRT.k");
srt_logging::Logger hclog(SRT_LOGFA_HAICRYPT, srt_logger_config, "SRT.hc");
extern "C" {
@ -97,12 +99,7 @@ void HaiCrypt_DumpConfig(const HaiCrypt_Cfg* cfg)
LOGC(hclog.Debug, log << "CFG DUMP: flags=" << cfg_flags.str()
<< " xport=" << (cfg->xport == HAICRYPT_XPT_SRT ? "SRT" : "INVALID")
<< " cipher="
<< (cfg->cipher == HaiCryptCipher_OpenSSL_EVP_CTR() ? "OSSL-EVP-CTR":
cfg->cipher == HaiCryptCipher_OpenSSL_AES() ? "OSSL-AES":
// This below is used as the only one when Nettle is used. When OpenSSL
// is used, one of the above will trigger, and the one below will then never trigger.
cfg->cipher == HaiCryptCipher_Get_Instance() ? "Nettle-AES":
"UNKNOWN")
<< CRYSPR_IMPL_DESC
<< " key_len=" << cfg->key_len << " data_max_len=" << cfg->data_max_len);

View file

@ -1,5 +1,5 @@
#ifndef IMC__HAICRYPT_LOG_H
#define IMC__HAICRYPT_LOG_H
#ifndef INC_SRT_HAICRYPT_LOG_H
#define INC_SRT_HAICRYPT_LOG_H
#ifdef __cplusplus
extern "C" {

View file

@ -74,10 +74,6 @@ static hcrypt_Session* sHaiCrypt_PrepareHandle(const HaiCrypt_Cfg* cfg, HaiCrypt
/* Setup transport packet info */
switch (cfg->xport) {
case HAICRYPT_XPT_STANDALONE:
crypto->se = HCRYPT_SE_TSUDP;
crypto->msg_info = hcryptMsg_STA_MsgInfo();
break;
case HAICRYPT_XPT_SRT:
crypto->se = HCRYPT_SE_TSSRT;
crypto->msg_info = hcryptMsg_SRT_MsgInfo();

View file

@ -24,8 +24,8 @@ written by
MINGW-W64 Build.
*****************************************************************************/
#ifndef HCRYPT_H
#define HCRYPT_H
#ifndef INC_SRT_HCRYPT_H
#define INC_SRT_HCRYPT_H
#include <sys/types.h>

View file

@ -74,9 +74,10 @@ int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx)
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
/* Regenerate KEK if Password-based (uses newly generated salt and sek_len) */
if ((0 < ctx->cfg.pwd_len)
&& (0 > (iret = hcryptCtx_GenSecret(crypto, ctx)))) {
return(iret);
if (0 < ctx->cfg.pwd_len) {
iret = hcryptCtx_GenSecret(crypto, ctx);
if (iret < 0)
return(iret);
}
/* Assemble the new Keying Material message */
@ -106,22 +107,22 @@ int hcryptCtx_Tx_CloneKey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const hcrypt_
ASSERT(HCRYPT_CTX_S_SARDY <= ctx->status);
const hcrypt_Ctx* ctxSrc = cryptoSrc->ctx;
if (!ctxSrc)
{
/* Probbly the context is not yet completely initialized, so
* use blindly the first context from the pair
*/
ctxSrc = &cryptoSrc->ctx_pair[0];
}
const hcrypt_Ctx* ctxSrc = cryptoSrc->ctx;
if (!ctxSrc)
{
/* Probbly the context is not yet completely initialized, so
* use blindly the first context from the pair
*/
ctxSrc = &cryptoSrc->ctx_pair[0];
}
/* Copy SALT (instead of generating) */
ctx->salt_len = ctxSrc->salt_len;
memcpy(ctx->salt, ctxSrc->salt, ctx->salt_len);
/* Copy SALT (instead of generating) */
ctx->salt_len = ctxSrc->salt_len;
memcpy(ctx->salt, ctxSrc->salt, ctx->salt_len);
/* Copy SEK */
ctx->sek_len = ctxSrc->sek_len;
memcpy(ctx->sek, ctxSrc->sek, ctx->sek_len);
/* Copy SEK */
ctx->sek_len = ctxSrc->sek_len;
memcpy(ctx->sek, ctxSrc->sek, ctx->sek_len);
/* Set SEK in cryspr */
if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, ctx->sek, ctx->sek_len)) {
@ -133,11 +134,12 @@ int hcryptCtx_Tx_CloneKey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const hcrypt_
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
/* Regenerate KEK if Password-based (uses newly generated salt and sek_len) */
/* (note for CloneKey imp: it's expected that the same passphrase-salt pair
shall generate the same KEK. GenSecret also prints the KEK */
if ((0 < ctx->cfg.pwd_len)
&& (0 > (iret = hcryptCtx_GenSecret(crypto, ctx)))) {
return(iret);
/* (note for CloneKey imp: it's expected that the same passphrase-salt pair
shall generate the same KEK. GenSecret also prints the KEK */
if (0 < ctx->cfg.pwd_len) {
iret = hcryptCtx_GenSecret(crypto, ctx);
if (iret < 0)
return(iret);
}
/* Assemble the new Keying Material message */
@ -336,8 +338,8 @@ int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto)
ASSERT(NULL != ctx);
HCRYPT_LOG(LOG_DEBUG, "KM[%d] KEY STATUS: pkt_cnt=%u against ref.rate=%u and pre.announce=%u\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2,
ctx->pkt_cnt, crypto->km.refresh_rate, crypto->km.pre_announce);
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2,
ctx->pkt_cnt, crypto->km.refresh_rate, crypto->km.pre_announce);
if ((ctx->pkt_cnt > crypto->km.refresh_rate)
|| (ctx->pkt_cnt == 0)) { //rolled over

View file

@ -37,7 +37,7 @@ int hcryptCtx_SetSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_
ctx->cfg.pwd_len = 0;
/* KEK: Key Encrypting Key */
if (0 > (iret = crypto->cryspr->km_setkey(crypto->cryspr_cb,
(HCRYPT_CTX_F_ENCRYPT & ctx->flags ? true : false),
((HCRYPT_CTX_F_ENCRYPT & ctx->flags) ? true : false),
secret->str, secret->len))) {
HCRYPT_LOG(LOG_ERR, "km_setkey(pdkek[%zd]) failed (rc=%d)\n", secret->len, iret);
return(-1);
@ -87,7 +87,7 @@ int hcryptCtx_GenSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx)
HCRYPT_PRINTKEY(kek, kek_len, "kek");
/* KEK: Key Encrypting Key */
if (0 > (iret = crypto->cryspr->km_setkey(crypto->cryspr_cb, (HCRYPT_CTX_F_ENCRYPT & ctx->flags ? true : false), kek, kek_len))) {
if (0 > (iret = crypto->cryspr->km_setkey(crypto->cryspr_cb, ((HCRYPT_CTX_F_ENCRYPT & ctx->flags) ? true : false), kek, kek_len))) {
HCRYPT_LOG(LOG_ERR, "km_setkey(pdkek[%zd]) failed (rc=%d)\n", kek_len, iret);
return(-1);
}

View file

@ -55,11 +55,11 @@ int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_
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 = NULL;
hcrypt_Ctx *ctx = crypto->ctx;
int nbout = 0;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))
|| (NULL == ctx)
|| (NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "ManageKeys: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
@ -69,7 +69,8 @@ int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[
/* Manage Key Material (refresh, announce, decommission) */
hcryptCtx_Tx_ManageKM(crypto);
if (NULL == (ctx = crypto->ctx)) {
ctx = crypto->ctx;
if (NULL == ctx) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
return(-1);
}
@ -82,10 +83,10 @@ 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 = NULL;
hcrypt_Ctx *ctx = crypto->ctx;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))){
|| (NULL == ctx)){
HCRYPT_LOG(LOG_ERR, "GetKeyFlags: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
@ -96,11 +97,11 @@ 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 = NULL;
hcrypt_Ctx *ctx = crypto->ctx;
int nbout = 0;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))){
|| (NULL == ctx)){
HCRYPT_LOG(LOG_ERR, "Tx_Data: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
@ -129,11 +130,11 @@ int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc,
void *out_p[], size_t out_len_p[], int maxout)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = NULL;
hcrypt_Ctx *ctx = crypto->ctx;
int nb, nbout = 0;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))
|| (NULL == ctx)
|| (NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "Tx_Process: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
@ -143,7 +144,8 @@ int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc,
/* Manage Key Material (refresh, announce, decommission) */
hcryptCtx_Tx_ManageKM(crypto);
if (NULL == (ctx = crypto->ctx)) {
ctx = crypto->ctx;
if (NULL == ctx) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
return(-1);
}

View file

@ -1,180 +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/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memset, memcpy */
#include <time.h> /* time() */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h> /* htonl, ntohl */
#endif
#include "hcrypt.h"
/*
* HaiCrypt Standalone Transport Media Stream (MS) Data Msg Prefix:
* Cache maintained in network order
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x00 |0|Vers | PT | Sign | resv |KF |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x04 | pki |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* | payload... |
*/
/*
* HaiCrypt Standalone Transport Keying Material (KM) Msg (no prefix, use KM Msg directly):
* Cache maintained in network order
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x00 |0|Vers | PT | Sign | resv |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* ... .
*/
#define HCRYPT_MSG_STA_HDR_SZ 4
#define HCRYPT_MSG_STA_PKI_SZ 4
#define HCRYPT_MSG_STA_PFX_SZ (HCRYPT_MSG_STA_HDR_SZ + HCRYPT_MSG_STA_PKI_SZ)
#define HCRYPT_MSG_STA_OFS_VERSION HCRYPT_MSG_KM_OFS_VERSION
#define HCRYPT_MSG_STA_OFS_PT HCRYPT_MSG_KM_OFS_PT
#define HCRYPT_MSG_STA_OFS_SIGN HCRYPT_MSG_KM_OFS_SIGN
#define HCRYPT_MSG_STA_OFS_KFLGS HCRYPT_MSG_KM_OFS_KFLGS
#define HCRYPT_MSG_STA_OFS_PKI HCRYPT_MSG_STA_HDR_SZ
#define hcryptMsg_STA_GetVersion(msg) (((msg)[HCRYPT_MSG_STA_OFS_VERSION]>>4)& 0xF)
#define hcryptMsg_STA_GetPktType(msg) (((msg)[HCRYPT_MSG_STA_OFS_PT]) & 0xF)
#define hcryptMsg_STA_GetSign(msg) (((msg)[HCRYPT_MSG_STA_OFS_SIGN]<<8) | (msg)[HCRYPT_MSG_STA_OFS_SIGN+1])
static hcrypt_MsgInfo _hcMsg_STA_MsgInfo;
static unsigned hcryptMsg_STA_GetKeyFlags(unsigned char *msg)
{
return((unsigned)(msg[HCRYPT_MSG_STA_OFS_KFLGS] & HCRYPT_MSG_F_xSEK));
}
static hcrypt_Pki hcryptMsg_STA_GetPki(unsigned char *msg, int nwkorder)
{
hcrypt_Pki pki;
memcpy(&pki, &msg[HCRYPT_MSG_STA_OFS_PKI], sizeof(pki)); //header is in host order
return (nwkorder ? pki : ntohl(pki));
}
static void hcryptMsg_STA_SetPki(unsigned char *msg, hcrypt_Pki pki)
{
hcrypt_Pki nwk_pki = htonl(pki);
memcpy(&msg[HCRYPT_MSG_STA_OFS_PKI], &nwk_pki, sizeof(nwk_pki)); //header is in host order
}
static void hcryptMsg_STA_ResetCache(unsigned char *pfx_cache, unsigned pkt_type, unsigned kflgs)
{
pfx_cache[HCRYPT_MSG_STA_OFS_VERSION] = (unsigned char)((HCRYPT_MSG_VERSION << 4) | pkt_type); // version || PT
pfx_cache[HCRYPT_MSG_STA_OFS_SIGN] = (unsigned char)((HCRYPT_MSG_SIGN >> 8) & 0xFF); // Haivision PnP Mfr ID
pfx_cache[HCRYPT_MSG_STA_OFS_SIGN+1] = (unsigned char)(HCRYPT_MSG_SIGN & 0xFF);
switch(pkt_type) {
case HCRYPT_MSG_PT_MS:
pfx_cache[HCRYPT_MSG_STA_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx
hcryptMsg_STA_SetPki(pfx_cache, 0);
break;
case HCRYPT_MSG_PT_KM:
pfx_cache[HCRYPT_MSG_KM_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx
break;
default:
break;
}
}
static void hcryptMsg_STA_IndexMsg(unsigned char *msg, unsigned char *pfx_cache)
{
hcrypt_Pki pki = hcryptMsg_STA_GetPki(pfx_cache, 0); //Get in host order
memcpy(msg, pfx_cache, HCRYPT_MSG_STA_PFX_SZ);
hcryptMsg_SetPki(&_hcMsg_STA_MsgInfo, pfx_cache, ++pki);
}
static time_t _tLastLogTime = 0;
static int hcryptMsg_STA_ParseMsg(unsigned char *msg)
{
int rc;
if ((HCRYPT_MSG_VERSION != hcryptMsg_STA_GetVersion(msg)) /* Version 1 */
|| (HCRYPT_MSG_SIGN != hcryptMsg_STA_GetSign(msg))) { /* 'HAI' PnP Mfr ID */
time_t tCurrentTime = time(NULL);
// invalid data
if ((tCurrentTime - _tLastLogTime) >= 2 || (0 == _tLastLogTime))
{
_tLastLogTime = tCurrentTime;
HCRYPT_LOG(LOG_ERR, "invalid msg hdr: 0x%02x %02x%02x %02x\n",
msg[0], msg[1], msg[2], msg[3]);
}
return(-1); /* Invalid packet */
}
rc = hcryptMsg_STA_GetPktType(msg);
switch(rc) {
case HCRYPT_MSG_PT_MS:
if (hcryptMsg_HasNoSek(&_hcMsg_STA_MsgInfo, msg)
|| hcryptMsg_HasBothSek(&_hcMsg_STA_MsgInfo, msg)) {
HCRYPT_LOG(LOG_ERR, "invalid MS msg flgs: %02x\n",
hcryptMsg_GetKeyIndex(&_hcMsg_STA_MsgInfo, msg));
return(-1);
}
break;
case HCRYPT_MSG_PT_KM:
if (HCRYPT_SE_TSUDP != hcryptMsg_KM_GetSE(msg)) {
HCRYPT_LOG(LOG_ERR, "invalid KM msg SE: %d\n",
hcryptMsg_KM_GetSE(msg));
} else if (hcryptMsg_KM_HasNoSek(msg)) {
HCRYPT_LOG(LOG_ERR, "invalid KM msg flgs: %02x\n",
hcryptMsg_KM_GetKeyIndex(msg));
return(-1);
}
break;
default:
HCRYPT_LOG(LOG_ERR, "invalid pkt type: %d\n", rc);
rc = 0; /* unknown packet type */
break;
}
return(rc); /* -1: error, 0: unknown: >0: PT */
}
static hcrypt_MsgInfo _hcMsg_STA_MsgInfo;
hcrypt_MsgInfo *hcryptMsg_STA_MsgInfo(void)
{
_hcMsg_STA_MsgInfo.hdr_len = HCRYPT_MSG_STA_HDR_SZ;
_hcMsg_STA_MsgInfo.pfx_len = HCRYPT_MSG_STA_PFX_SZ;
_hcMsg_STA_MsgInfo.getKeyFlags = hcryptMsg_STA_GetKeyFlags;
_hcMsg_STA_MsgInfo.getPki = hcryptMsg_STA_GetPki;
_hcMsg_STA_MsgInfo.setPki = hcryptMsg_STA_SetPki;
_hcMsg_STA_MsgInfo.resetCache = hcryptMsg_STA_ResetCache;
_hcMsg_STA_MsgInfo.indexMsg = hcryptMsg_STA_IndexMsg;
_hcMsg_STA_MsgInfo.parseMsg = hcryptMsg_STA_ParseMsg;
return(&_hcMsg_STA_MsgInfo);
}

View file

@ -0,0 +1,62 @@
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2021 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::atomic.
#
# Sets:
# HAVE_CXX_ATOMIC
# HAVE_CXX_ATOMIC_STATIC
include(CheckCXXSourceCompiles)
include(CheckLibraryExists)
function(CheckCXXAtomic)
unset(HAVE_CXX_ATOMIC CACHE)
unset(HAVE_CXX_ATOMIC_STATIC CACHE)
unset(CMAKE_REQUIRED_FLAGS)
unset(CMAKE_REQUIRED_LIBRARIES)
unset(CMAKE_REQUIRED_LINK_OPTIONS)
set(CheckCXXAtomic_CODE
"
#include<cstdint>
#include<atomic>
int main(void)
{
std::atomic<std::ptrdiff_t> x(0);
std::atomic<std::intmax_t> y(0);
return x + y;
}
")
set(CMAKE_REQUIRED_FLAGS "-std=c++11")
check_cxx_source_compiles(
"${CheckCXXAtomic_CODE}"
HAVE_CXX_ATOMIC)
if(HAVE_CXX_ATOMIC)
# CMAKE_REQUIRED_LINK_OPTIONS was introduced in CMake 3.14.
if(CMAKE_VERSION VERSION_LESS "3.14")
set(CMAKE_REQUIRED_LINK_OPTIONS "-static")
else()
set(CMAKE_REQUIRED_FLAGS "-std=c++11 -static")
endif()
check_cxx_source_compiles(
"${CheckCXXAtomic_CODE}"
HAVE_CXX_ATOMIC_STATIC)
endif()
unset(CMAKE_REQUIRED_FLAGS)
unset(CMAKE_REQUIRED_LIBRARIES)
unset(CMAKE_REQUIRED_LINK_OPTIONS)
endfunction(CheckCXXAtomic)

View file

@ -0,0 +1,113 @@
#
# SRT - Secure, Reliable, Transport Copyright (c) 2021 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 GCC Atomic Intrinsics and whether libatomic is required.
#
# Sets:
# HAVE_LIBATOMIC
# HAVE_LIBATOMIC_COMPILES
# HAVE_LIBATOMIC_COMPILES_STATIC
# HAVE_GCCATOMIC_INTRINSICS
# HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC
#
# See
# https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
# https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
include(CheckCSourceCompiles)
include(CheckLibraryExists)
function(CheckGCCAtomicIntrinsics)
unset(HAVE_LIBATOMIC CACHE)
unset(HAVE_LIBATOMIC_COMPILES CACHE)
unset(HAVE_LIBATOMIC_COMPILES_STATIC CACHE)
unset(HAVE_GCCATOMIC_INTRINSICS CACHE)
unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE)
set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6
unset(CMAKE_REQUIRED_FLAGS)
unset(CMAKE_REQUIRED_LIBRARIES)
unset(CMAKE_REQUIRED_LINK_OPTIONS)
# Check for existance of libatomic and whether this symbol is present.
check_library_exists(atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC)
set(CheckLibAtomicCompiles_CODE
"
int main(void)
{
const int result = 0;
return result;
}
")
set(CMAKE_REQUIRED_LIBRARIES "atomic")
# Check that the compiler can build a simple application and link with
# libatomic.
check_c_source_compiles("${CheckLibAtomicCompiles_CODE}"
HAVE_LIBATOMIC_COMPILES)
if(NOT HAVE_LIBATOMIC_COMPILES)
set(HAVE_LIBATOMIC
0
CACHE INTERNAL "" FORCE)
endif()
if(HAVE_LIBATOMIC AND HAVE_LIBATOMIC_COMPILES)
# CMAKE_REQUIRED_LINK_OPTIONS was introduced in CMake 3.14.
if(CMAKE_VERSION VERSION_LESS "3.14")
set(CMAKE_REQUIRED_LINK_OPTIONS "-static")
else()
set(CMAKE_REQUIRED_FLAGS "-static")
endif()
# Check that the compiler can build a simple application and statically link
# with libatomic.
check_c_source_compiles("${CheckLibAtomicCompiles_CODE}"
HAVE_LIBATOMIC_COMPILES_STATIC)
else()
set(HAVE_LIBATOMIC_COMPILES_STATIC
0
CACHE INTERNAL "" FORCE)
endif()
unset(CMAKE_REQUIRED_FLAGS)
unset(CMAKE_REQUIRED_LIBRARIES)
unset(CMAKE_REQUIRED_LINK_OPTIONS)
set(CheckGCCAtomicIntrinsics_CODE
"
#include<stddef.h>
#include<stdint.h>
int main(void)
{
ptrdiff_t x = 0;
intmax_t y = 0;
__atomic_add_fetch(&x, 1, __ATOMIC_SEQ_CST);
__atomic_add_fetch(&y, 1, __ATOMIC_SEQ_CST);
return __atomic_sub_fetch(&x, 1, __ATOMIC_SEQ_CST)
+ __atomic_sub_fetch(&y, 1, __ATOMIC_SEQ_CST);
}
")
set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6
check_c_source_compiles("${CheckGCCAtomicIntrinsics_CODE}"
HAVE_GCCATOMIC_INTRINSICS)
if(NOT HAVE_GCCATOMIC_INTRINSICS AND HAVE_LIBATOMIC)
set(CMAKE_REQUIRED_LIBRARIES "atomic")
check_c_source_compiles("${CheckGCCAtomicIntrinsics_CODE}"
HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC)
if(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC)
set(HAVE_GCCATOMIC_INTRINSICS
1
CACHE INTERNAL "" FORCE)
endif()
endif()
endfunction(CheckGCCAtomicIntrinsics)

View file

@ -111,5 +111,5 @@ endif()
# Now we've accounted for the 3-vs-1 library case:
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Libmbedtls DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS)
find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS)
mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS)

View file

@ -0,0 +1,73 @@
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2021 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 pthread_getname_np(3) and pthread_setname_np(3)
# used in srtcore/threadname.h.
#
# Some BSD distros need to include <pthread_np.h> for pthread_getname_np().
#
# TODO: Some BSD distros have pthread_get_name_np() and pthread_set_name_np()
# instead of pthread_getname_np() and pthread_setname_np().
#
# Sets:
# HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H
# HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H
# HAVE_PTHREAD_GETNAME_NP
# HAVE_PTHREAD_SETNAME_NP
# Sets as appropriate:
# add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1)
# add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1)
# add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1)
# add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1)
include(CheckSymbolExists)
function(FindPThreadGetSetName)
unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H CACHE)
unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H CACHE)
unset(HAVE_PTHREAD_GETNAME_NP CACHE)
unset(HAVE_PTHREAD_SETNAME_NP CACHE)
set(CMAKE_REQUIRED_DEFINITIONS
-D_GNU_SOURCE -D_DARWIN_C_SOURCE -D_POSIX_SOURCE=1)
set(CMAKE_REQUIRED_FLAGS "-pthread")
message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread_np.h':")
check_symbol_exists(
pthread_getname_np "pthread_np.h" HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H)
if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H)
add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1)
endif()
check_symbol_exists(
pthread_setname_np "pthread_np.h" HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H)
if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H)
add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1)
endif()
message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread.h':")
check_symbol_exists(pthread_getname_np "pthread.h" HAVE_PTHREAD_GETNAME_NP)
if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H)
set(HAVE_PTHREAD_GETNAME_NP 1 CACHE INTERNAL "" FORCE)
endif()
check_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP)
if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H)
set(HAVE_PTHREAD_SETNAME_NP 1 CACHE INTERNAL "" FORCE)
endif()
if (HAVE_PTHREAD_GETNAME_NP)
add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1)
endif()
if (HAVE_PTHREAD_SETNAME_NP)
add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1)
endif()
unset(CMAKE_REQUIRED_DEFINITIONS)
unset(CMAKE_REQUIRED_FLAGS)
endfunction(FindPThreadGetSetName)

View file

@ -0,0 +1,191 @@
#
# SRT - Secure, Reliable, Transport Copyright (c) 2021 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/.
#
function(ShowProjectConfig)
set(__ssl_configuration)
if (SSL_FOUND OR SSL_LIBRARIES)
set(__ssl_configuration
" SSL Configuration:
SSL_FOUND=${SSL_FOUND}
SSL_INCLUDE_DIRS=${SSL_INCLUDE_DIRS}
SSL_LIBRARIES=${SSL_LIBRARIES}
SSL_VERSION=${SSL_VERSION}\n")
endif()
set(static_property_link_libraries)
if (srt_libspec_static)
get_target_property(
static_property_link_libraries
${TARGET_srt}_static
LINK_LIBRARIES)
endif()
set(shared_property_link_libraries)
if (srt_libspec_shared)
get_target_property(
shared_property_link_libraries
${TARGET_srt}_shared
LINK_LIBRARIES)
endif()
# See https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#id13
set(__more_tc1_config)
if (CMAKE_CROSSCOMPILING)
set(__more_tc1_config
" CMAKE_SYSROOT: ${CMAKE_SYSROOT}\n")
endif()
# See https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#id13
set(__more_tc2_config)
if (APPLE)
set(__more_tc2_config
" CMAKE_INSTALL_NAME_TOOL: ${CMAKE_INSTALL_NAME_TOOL}
CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT}
CMAKE_OSX_ARCHITECTURES: ${CMAKE_OSX_ARCHITECTURES}
CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET}
CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT}\n")
elseif (ANDROID)
set(__more_tc2_config
" CMAKE_ANDROID_NDK: ${CMAKE_ANDROID_NDK}
CMAKE_ANDROID_STANDALONE_TOOLCHAIN: ${CMAKE_ANDROID_STANDALONE_TOOLCHAIN}
CMAKE_ANDROID_API: ${CMAKE_ANDROID_API}
CMAKE_ANDROID_ARCH_ABI: ${CMAKE_ANDROID_ARCH_ABI}
CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION: ${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}
CMAKE_ANDROID_STL_TYPE: ${CMAKE_ANDROID_STL_TYPE}\n")
endif()
message(STATUS
"\n"
"========================================================================\n"
"= Project Configuration:\n"
"========================================================================\n"
" SRT Version:\n"
" SRT_VERSION: ${SRT_VERSION}\n"
" SRT_VERSION_BUILD: ${SRT_VERSION_BUILD}\n"
" CMake Configuration:\n"
" CMAKE_VERSION: ${CMAKE_VERSION}\n"
" CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}\n"
" CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\n"
" Target Configuration:\n"
" CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}\n"
" CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}\n"
" CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}\n"
" CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}\n"
" DARWIN: ${DARWIN}\n"
" LINUX: ${LINUX}\n"
" BSD: ${BSD}\n"
" MICROSOFT: ${MICROSOFT}\n"
" GNU: ${GNU}\n"
" ANDROID: ${ANDROID}\n"
" SUNOS: ${SUNOS}\n"
" POSIX: ${POSIX}\n"
" SYMLINKABLE: ${SYMLINKABLE}\n"
" APPLE: ${APPLE}\n"
" UNIX: ${UNIX}\n"
" WIN32: ${WIN32}\n"
" MINGW: ${MINGW}\n"
" CYGWIN: ${CYGWIN}\n"
" CYGWIN_USE_POSIX: ${CYGWIN_USE_POSIX}\n"
" Toolchain Configuration:\n"
" CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}\n"
" CMAKE_CROSSCOMPILING: ${CMAKE_CROSSCOMPILING}\n"
"${__more_tc1_config}"
" CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}\n"
" CMAKE_C_COMPILER_VERSION: ${CMAKE_C_COMPILER_VERSION}\n"
" CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}\n"
" CMAKE_C_FLAGS: '${CMAKE_C_FLAGS}'\n"
" CMAKE_C_COMPILE_FEATURES: ${CMAKE_C_COMPILE_FEATURES}\n"
" CMAKE_C_STANDARD: ${CMAKE_CXX_STANDARD}\n"
" CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}\n"
" CMAKE_CXX_COMPILER_VERSION: ${CMAKE_CXX_COMPILER_VERSION}\n"
" CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}\n"
" CMAKE_CXX_FLAGS: '${CMAKE_CXX_FLAGS}'\n"
" CMAKE_CXX_COMPILE_FEATURES: ${CMAKE_CXX_COMPILE_FEATURES}\n"
" CMAKE_CXX_STANDARD: ${CMAKE_CXX_STANDARD}\n"
" CMAKE_LINKER: ${CMAKE_LINKER}\n"
#" CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}\n"
#" CMAKE_EXE_LINKER_FLAGS_INIT: ${CMAKE_EXE_LINKER_FLAGS_INIT}\n"
#" CMAKE_MODULE_LINKER_FLAGS: ${CMAKE_MODULE_LINKER_FLAGS}\n"
#" CMAKE_MODULE_LINKER_FLAGS_INIT: ${CMAKE_MODULE_LINKER_FLAGS_INIT}\n"
#" CMAKE_SHARED_LINKER_FLAGS: ${CMAKE_SHARED_LINKER_FLAGS}\n"
#" CMAKE_SHARED_LINKER_FLAGS_INIT: ${CMAKE_SHARED_LINKER_FLAGS_INIT}\n"
#" CMAKE_STATIC_LINKER_FLAGS: ${CMAKE_STATIC_LINKER_FLAGS}\n"
#" CMAKE_STATIC_LINKER_FLAGS_INIT: ${CMAKE_STATIC_LINKER_FLAGS_INIT}\n"
" CMAKE_NM: ${CMAKE_NM}\n"
" CMAKE_AR: ${CMAKE_AR}\n"
" CMAKE_RANLIB: ${CMAKE_RANLIB}\n"
"${__more_tc2_config}"
" HAVE_COMPILER_GNU_COMPAT: ${HAVE_COMPILER_GNU_COMPAT}\n"
" CMAKE_THREAD_LIBS: ${CMAKE_THREAD_LIBS}\n"
" CMAKE_THREAD_LIBS_INIT: ${CMAKE_THREAD_LIBS_INIT}\n"
" ENABLE_THREAD_CHECK: ${ENABLE_THREAD_CHECK}\n"
" USE_CXX_STD_APP: ${USE_CXX_STD_APP}\n"
" USE_CXX_STD_LIB: ${USE_CXX_STD_LIB}\n"
" STDCXX: ${STDCXX}\n"
" USE_CXX_STD: ${USE_CXX_STD}\n"
" HAVE_CLOCK_GETTIME_IN: ${HAVE_CLOCK_GETTIME_IN}\n"
" HAVE_CLOCK_GETTIME_LIBRT: ${HAVE_CLOCK_GETTIME_LIBRT}\n"
" HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H: ${HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H}\n"
" HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H: ${HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H}\n"
" HAVE_PTHREAD_GETNAME_NP: ${HAVE_PTHREAD_GETNAME_NP}\n"
" HAVE_PTHREAD_SETNAME_NP: ${HAVE_PTHREAD_SETNAME_NP}\n"
" HAVE_LIBATOMIC: ${HAVE_LIBATOMIC}\n"
" HAVE_LIBATOMIC_COMPILES: ${HAVE_LIBATOMIC_COMPILES}\n"
" HAVE_LIBATOMIC_COMPILES_STATIC: ${HAVE_LIBATOMIC_COMPILES_STATIC}\n"
" HAVE_GCCATOMIC_INTRINSICS: ${HAVE_GCCATOMIC_INTRINSICS}\n"
" 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"
" Project Configuration:\n"
" ENABLE_DEBUG: ${ENABLE_DEBUG}\n"
" ENABLE_CXX11: ${ENABLE_CXX11}\n"
" ENABLE_APPS: ${ENABLE_APPS}\n"
" ENABLE_EXAMPLES: ${ENABLE_EXAMPLES}\n"
" ENABLE_BONDING: ${ENABLE_BONDING}\n"
" ENABLE_TESTING: ${ENABLE_TESTING}\n"
" ENABLE_PROFILE: ${ENABLE_PROFILE}\n"
" ENABLE_LOGGING: ${ENABLE_LOGGING}\n"
" ENABLE_HEAVY_LOGGING: ${ENABLE_HEAVY_LOGGING}\n"
" ENABLE_HAICRYPT_LOGGING: ${ENABLE_HAICRYPT_LOGGING}\n"
" ENABLE_SHARED: ${ENABLE_SHARED}\n"
" ENABLE_STATIC: ${ENABLE_STATIC}\n"
" ENABLE_RELATIVE_LIBPATH: ${ENABLE_RELATIVE_LIBPATH}\n"
" ENABLE_GETNAMEINFO: ${ENABLE_GETNAMEINFO}\n"
" ENABLE_UNITTESTS: ${ENABLE_UNITTESTS}\n"
" ENABLE_ENCRYPTION: ${ENABLE_ENCRYPTION}\n"
" ENABLE_CXX_DEPS: ${ENABLE_CXX_DEPS}\n"
" USE_STATIC_LIBSTDCXX: ${USE_STATIC_LIBSTDCXX}\n"
" ENABLE_INET_PTON: ${ENABLE_INET_PTON}\n"
" ENABLE_CODE_COVERAGE: ${ENABLE_CODE_COVERAGE}\n"
" ENABLE_MONOTONIC_CLOCK: ${ENABLE_MONOTONIC_CLOCK}\n"
" ENABLE_STDCXX_SYNC: ${ENABLE_STDCXX_SYNC}\n"
" USE_OPENSSL_PC: ${USE_OPENSSL_PC}\n"
" OPENSSL_USE_STATIC_LIBS: ${OPENSSL_USE_STATIC_LIBS}\n"
" USE_BUSY_WAITING: ${USE_BUSY_WAITING}\n"
" USE_GNUSTL: ${USE_GNUSTL}\n"
" ENABLE_SOCK_CLOEXEC: ${ENABLE_SOCK_CLOEXEC}\n"
" ENABLE_SHOW_PROJECT_CONFIG: ${ENABLE_SHOW_PROJECT_CONFIG}\n"
" ENABLE_CLANG_TSA: ${ENABLE_CLANG_TSA}\n"
" ATOMIC_USE_SRT_SYNC_MUTEX: ${ATOMIC_USE_SRT_SYNC_MUTEX}\n"
" Constructed Configuration:\n"
" DISABLE_CXX11: ${DISABLE_CXX11}\n"
" HAVE_INET_PTON: ${HAVE_INET_PTON}\n"
" PTHREAD_LIBRARY: ${PTHREAD_LIBRARY}\n"
" USE_ENCLIB: ${USE_ENCLIB}\n"
"${__ssl_configuration}"
" TARGET_srt: ${TARGET_srt}\n"
" srt_libspec_static: ${srt_libspec_static}\n"
" srt_libspec_shared: ${srt_libspec_shared}\n"
" SRT_LIBS_PRIVATE: ${SRT_LIBS_PRIVATE}\n"
" Target Link Libraries:\n"
" Static: ${static_property_link_libraries}\n"
" Shared: ${shared_property_link_libraries}\n"
"========================================================================\n"
)
endfunction(ShowProjectConfig)

View file

@ -0,0 +1,3 @@
@ECHO OFF
%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\PowerShell.exe -Command "& '%~dpn0.ps1'"
pause

View file

@ -0,0 +1,238 @@
################################################################################
# Windows SRT Build Script
#============================
# Usable on a Windows PC with Powershell and Visual studio,
# or called by CI systems like AppVeyor
#
# By default produces a VS2019 64-bit Release binary using C++11 threads, without
# encryption or unit tests enabled, but including test apps.
# Before enabling any encryption options, install OpenSSL or set VCKPG flag to build
################################################################################
param (
[Parameter()][String]$VS_VERSION = "2019",
[Parameter()][String]$CONFIGURATION = "Release",
[Parameter()][String]$DEVENV_PLATFORM = "x64",
[Parameter()][String]$ENABLE_ENCRYPTION = "OFF",
[Parameter()][String]$STATIC_LINK_SSL = "OFF",
[Parameter()][String]$CXX11 = "ON",
[Parameter()][String]$BUILD_APPS = "ON",
[Parameter()][String]$UNIT_TESTS = "OFF",
[Parameter()][String]$BUILD_DIR = "_build",
[Parameter()][String]$VCPKG_OPENSSL = "OFF",
[Parameter()][String]$BONDING = "OFF"
)
# cmake can be optionally installed (useful when running interactively on a developer station).
# The URL for automatic download is defined later in the script, but it should be possible to just vary the
# specific version set below and the URL should be stable enough to still work - you have been warned.
$cmakeVersion = "3.23.2"
# make all errors trigger a script stop, rather than just carry on
$ErrorActionPreference = "Stop"
$projectRoot = Join-Path $PSScriptRoot "/.." -Resolve
# if running within AppVeyor, use environment variables to set params instead of passed-in values
if ( $Env:APPVEYOR ) {
if ( $Env:PLATFORM -eq 'x86' ) { $DEVENV_PLATFORM = 'Win32' } else { $DEVENV_PLATFORM = 'x64' }
if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2019' ) { $VS_VERSION='2019' }
if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2015' ) { $VS_VERSION='2015' }
if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2013' ) { $VS_VERSION='2013' }
#if not statically linking OpenSSL, set flag to gather the specific openssl package from the build server into package
if ( $STATIC_LINK_SSL -eq 'OFF' ) { $Env:GATHER_SSL_INTO_PACKAGE = $true }
#if unit tests are on, set flag to actually execute ctest step
if ( $UNIT_TESTS -eq 'ON' ) { $Env:RUN_UNIT_TESTS = $true }
$CONFIGURATION = $Env:CONFIGURATION
#appveyor has many openssl installations - place the latest one in the default location unless VS2013
if( $VS_VERSION -ne '2013' ) {
Remove-Item -Path "C:\OpenSSL-Win32" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
Remove-Item -Path "C:\OpenSSL-Win64" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
Copy-Item -Path "C:\OpenSSL-v111-Win32" "C:\OpenSSL-Win32" -Recurse | Out-Null
Copy-Item -Path "C:\OpenSSL-v111-Win64" "C:\OpenSSL-Win64" -Recurse | Out-Null
}
}
# persist VS_VERSION so it can be used in an artifact name later
$Env:VS_VERSION = $VS_VERSION
# select the appropriate cmake generator string given the environment
if ( $VS_VERSION -eq '2019' ) { $CMAKE_GENERATOR = 'Visual Studio 16 2019'; $MSBUILDVER = "16.0"; }
if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015'; $MSBUILDVER = "14.0"; }
if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015 Win64'; $MSBUILDVER = "14.0"; }
if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013'; $MSBUILDVER = "12.0"; }
if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013 Win64'; $MSBUILDVER = "12.0"; }
# clear any previous build and create & enter the build directory
$buildDir = Join-Path "$projectRoot" "$BUILD_DIR"
Write-Output "Creating (or cleaning if already existing) the folder $buildDir for project files and outputs"
Remove-Item -Path $buildDir -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
New-Item -ItemType Directory -Path $buildDir -ErrorAction SilentlyContinue | Out-Null
Push-Location $buildDir
# check cmake is installed
if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) {
$installCmake = Read-Host "Unable to find cmake in your PATH - would you like to download and install automatically? [yes/no]"
if ( $installCmake -eq "y" -or $installCmake -eq "yes" ) {
# download cmake and run MSI for user
$client = New-Object System.Net.WebClient
$tempDownloadFile = New-TemporaryFile
$cmakeUrl = "https://github.com/Kitware/CMake/releases/download/v$cmakeVersion/cmake-$cmakeVersion-win64-x64.msi"
$cmakeMsiFile = "$tempDownloadFile.cmake-$cmakeVersion-win64-x64.msi"
Write-Output "Downloading cmake from $cmakeUrl (temporary file location $cmakeMsiFile)"
Write-Output "Note: select the option to add cmake to path for this script to operate"
$client.DownloadFile("$cmakeUrl", "$cmakeMsiFile")
Start-Process $cmakeMsiFile -Wait
Remove-Item $cmakeMsiFile
Write-Output "Cmake should have installed, this script will now exit because of path updates - please now re-run this script"
throw
}
else{
Write-Output "Quitting because cmake is required"
throw
}
}
# 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)
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
}
if ( $DEVENV_PLATFORM -eq 'Win32' ) {
nuget install cinegy.pthreads-win32-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages
}
else {
nuget install cinegy.pthreads-win64-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages
}
}
# check to see if static SSL linking was requested, and enable encryption if not already ON
if ( $STATIC_LINK_SSL -eq "ON" ) {
if ( $ENABLE_ENCRYPTION -eq "OFF" ) {
# requesting a static link implicitly requires encryption support
Write-Output "Static linking to OpenSSL requested, will force encryption feature ON"
$ENABLE_ENCRYPTION = "ON"
}
}
# check to see if VCPKG is marked to provide OpenSSL, and enable encryption if not already ON
if ( $VCPKG_OPENSSL -eq "ON" ) {
if ( $ENABLE_ENCRYPTION -eq "OFF" ) {
# requesting VCPKG to provide OpenSSL requires encryption support
Write-Output "VCPKG compilation of OpenSSL requested, will force encryption feature ON"
$ENABLE_ENCRYPTION = "ON"
}
}
# build the cmake command flags from arguments
$cmakeFlags = "-DCMAKE_BUILD_TYPE=$CONFIGURATION " +
"-DENABLE_STDCXX_SYNC=$CXX11 " +
"-DENABLE_APPS=$BUILD_APPS " +
"-DENABLE_ENCRYPTION=$ENABLE_ENCRYPTION " +
"-DENABLE_BONDING=$BONDING " +
"-DENABLE_UNITTESTS=$UNIT_TESTS"
# if VCPKG is flagged to provide OpenSSL, checkout VCPKG and install package
if ( $VCPKG_OPENSSL -eq 'ON' ) {
Push-Location $projectRoot
Write-Output "Cloning VCPKG into: $(Get-Location)"
if (Test-Path -Path ".\vcpkg") {
Set-Location .\vcpkg
git pull
} else {
git clone https://github.com/microsoft/vcpkg
Set-Location .\vcpkg
}
.\bootstrap-vcpkg.bat
if($DEVENV_PLATFORM -EQ "x64"){
if($STATIC_LINK_SSL -EQ "ON"){
.\vcpkg install openssl:x64-windows-static
$cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x64-windows-static"
}
else{
.\vcpkg install openssl:x64-windows
}
}
else{
if($STATIC_LINK_SSL -EQ "ON"){
.\vcpkg install openssl:x86-windows-static
$cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x86-windows-static"
}
else{
.\vcpkg install openssl:x86-windows
}
}
.\vcpkg integrate install
Pop-Location
$cmakeFlags += " -DCMAKE_TOOLCHAIN_FILE=$projectRoot\vcpkg\scripts\buildsystems\vcpkg.cmake"
}
else {
$cmakeFlags += " -DOPENSSL_USE_STATIC_LIBS=$STATIC_LINK_SSL "
}
# cmake uses a flag for architecture from vs2019, so add that as a suffix
if ( $VS_VERSION -eq '2019' ) {
$cmakeFlags += " -A `"$DEVENV_PLATFORM`""
}
# fire cmake to build project files
$execVar = "cmake ../ -G`"$CMAKE_GENERATOR`" $cmakeFlags"
Write-Output $execVar
# Reset reaction to Continue for cmake as it sometimes tends to print
# things on stderr, which is understood by PowerShell as error. The
# exit code from cmake will be checked anyway.
$ErrorActionPreference = "Continue"
Invoke-Expression "& $execVar"
# check build ran OK, exit if cmake failed
if( $LASTEXITCODE -ne 0 ) {
Write-Output "Non-zero exit code from cmake: $LASTEXITCODE"
throw
}
$ErrorActionPreference = "Stop"
# run the set-version-metadata script to inject build numbers into appveyors console and the resulting DLL
. $PSScriptRoot/set-version-metadata.ps1
# look for msbuild
$msBuildPath = Get-Command "msbuild.exe" -ErrorAction SilentlyContinue
if ( $null -eq $msBuildPath ) {
# no mbsuild in the path, so try to locate with 'vswhere'
$vsWherePath = Get-Command "vswhere.exe" -ErrorAction SilentlyContinue
if ( $null -eq $vsWherePath ) {
# no vswhere in the path, so check the Microsoft published location (true since VS2017 Update 2)
$vsWherePath = Get-Command "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -ErrorAction SilentlyContinue
if ( $null -eq $vsWherePath ) {
Write-Output "Cannot find vswhere (used to locate msbuild). Please install VS2017 update 2 (or later) or add vswhere to your path and try again"
throw
}
}
$msBuildPath = & $vsWherePath -products * -version $MSBUILDVER -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1
if ( $null -eq $msBuildPath ) {
Write-Output "vswhere.exe cannot find msbuild for the specified Visual Studio version - please check the installation"
throw
}
}
& $msBuildPath SRT.sln -m /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM
# return to the directory previously occupied before running the script
Pop-Location
# if msbuild returned non-zero, throw to cause failure in CI
if( $LASTEXITCODE -ne 0 ) {
throw
}

View file

@ -0,0 +1,7 @@
#!/bin/bash
shopt -s globstar
gcov_data_dir="."
for x in ./**/*.o; do
echo "x: $x"
gcov "$gcov_data_dir/$x"
done

View file

@ -15,7 +15,9 @@ md %APPVEYOR_BUILD_FOLDER%\package\include
md %APPVEYOR_BUILD_FOLDER%\package\include\win
md %APPVEYOR_BUILD_FOLDER%\package\bin
md %APPVEYOR_BUILD_FOLDER%\package\lib
md %APPVEYOR_BUILD_FOLDER%\package\openssl-win%FOLDER_PLATFORM%
IF "%GATHER_SSL_INTO_PACKAGE%"=="True" (
md %APPVEYOR_BUILD_FOLDER%\package\openssl-win%FOLDER_PLATFORM%
)
rem Gather SRT includes, binaries and libs
copy %APPVEYOR_BUILD_FOLDER%\version.h %APPVEYOR_BUILD_FOLDER%\package\include\
@ -23,13 +25,13 @@ copy %APPVEYOR_BUILD_FOLDER%\srtcore\*.h %APPVEYOR_BUILD_FOLDER%\package\include
copy %APPVEYOR_BUILD_FOLDER%\haicrypt\*.h %APPVEYOR_BUILD_FOLDER%\package\include\
copy %APPVEYOR_BUILD_FOLDER%\common\*.h %APPVEYOR_BUILD_FOLDER%\package\include\
copy %APPVEYOR_BUILD_FOLDER%\common\win\*.h %APPVEYOR_BUILD_FOLDER%\package\include\win\
copy %APPVEYOR_BUILD_FOLDER%\%CONFIGURATION%\*.exe %APPVEYOR_BUILD_FOLDER%\package\bin\
copy %APPVEYOR_BUILD_FOLDER%\%CONFIGURATION%\*.dll %APPVEYOR_BUILD_FOLDER%\package\bin\
copy %APPVEYOR_BUILD_FOLDER%\%CONFIGURATION%\*.lib %APPVEYOR_BUILD_FOLDER%\package\lib\
IF "%CONFIGURATION%"=="Debug" (
copy %APPVEYOR_BUILD_FOLDER%\%CONFIGURATION%\*.pdb %APPVEYOR_BUILD_FOLDER%\package\bin\
)
copy %APPVEYOR_BUILD_FOLDER%\_build\%CONFIGURATION%\*.exe %APPVEYOR_BUILD_FOLDER%\package\bin\
copy %APPVEYOR_BUILD_FOLDER%\_build\%CONFIGURATION%\*.dll %APPVEYOR_BUILD_FOLDER%\package\bin\
copy %APPVEYOR_BUILD_FOLDER%\_build\%CONFIGURATION%\*.lib %APPVEYOR_BUILD_FOLDER%\package\lib\
copy %APPVEYOR_BUILD_FOLDER%\_build\%CONFIGURATION%\*.pdb %APPVEYOR_BUILD_FOLDER%\package\bin\
rem gather 3rd party openssl elements
(robocopy c:\openssl-win%FOLDER_PLATFORM%\ %APPVEYOR_BUILD_FOLDER%\package\openssl-win%FOLDER_PLATFORM% /s /e /np) ^& IF %ERRORLEVEL% GTR 1 exit %ERRORLEVEL%
rem Gather 3rd party openssl elements
IF "%GATHER_SSL_INTO_PACKAGE%"=="True" (
(robocopy c:\openssl-win%FOLDER_PLATFORM%\ %APPVEYOR_BUILD_FOLDER%\package\openssl-win%FOLDER_PLATFORM% /s /e /np) ^& IF %ERRORLEVEL% GTR 1 exit %ERRORLEVEL%
)
exit 0

View file

@ -0,0 +1,251 @@
#!/usr/bin/tclsh
#*
#* SRT - Secure, Reliable, Transport
#* Copyright (c) 2020 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/.
#*
#*/
#
#*****************************************************************************
#written by
# Haivision Systems Inc.
#*****************************************************************************
set code_major {
UNKNOWN -1
SUCCESS 0
SETUP 1
CONNECTION 2
SYSTEMRES 3
FILESYSTEM 4
NOTSUP 5
AGAIN 6
PEERERROR 7
}
set code_minor {
NONE 0
TIMEOUT 1
REJECTED 2
NORES 3
SECURITY 4
CLOSED 5
CONNLOST 1
NOCONN 2
THREAD 1
MEMORY 2
OBJECT 3
SEEKGFAIL 1
READFAIL 2
SEEKPFAIL 3
WRITEFAIL 4
ISBOUND 1
ISCONNECTED 2
INVAL 3
SIDINVAL 4
ISUNBOUND 5
NOLISTEN 6
ISRENDEZVOUS 7
ISRENDUNBOUND 8
INVALMSGAPI 9
INVALBUFFERAPI 10
BUSY 11
XSIZE 12
EIDINVAL 13
EEMPTY 14
BUSYPORT 15
WRAVAIL 1
RDAVAIL 2
XMTIMEOUT 3
CONGESTION 4
}
set errortypes {
SUCCESS "Success" {
NONE ""
}
SETUP "Connection setup failure" {
NONE ""
TIMEOUT "connection timed out"
REJECTED "connection rejected"
NORES "unable to create/configure SRT socket"
SECURITY "aborted for security reasons"
CLOSED "socket closed during operation"
}
CONNECTION "" {
NONE ""
CONNLOST "Connection was broken"
NOCONN "Connection does not exist"
}
SYSTEMRES "System resource failure" {
NONE ""
THREAD "unable to create new threads"
MEMORY "unable to allocate buffers"
OBJECT "unable to allocate a system object"
}
FILESYSTEM "File system failure" {
NONE ""
SEEKGFAIL "cannot seek read position"
READFAIL "failure in read"
SEEKPFAIL "cannot seek write position"
WRITEFAIL "failure in write"
}
NOTSUP "Operation not supported" {
NONE ""
ISBOUND "Cannot do this operation on a BOUND socket"
ISCONNECTED "Cannot do this operation on a CONNECTED socket"
INVAL "Bad parameters"
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"
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)."
BUSY "Another socket is already listening on the same port"
XSIZE "Message is too large to send (it must be less than the SRT send buffer size)"
EIDINVAL "Invalid epoll ID"
EEMPTY "All sockets removed from epoll, waiting would deadlock"
BUSYPORT "Another socket is bound to that port and is not reusable for requested settings"
}
AGAIN "Non-blocking call failure" {
NONE ""
WRAVAIL "no buffer available for sending"
RDAVAIL "no data available for reading"
XMTIMEOUT "transmission timed out"
CONGESTION "early congestion notification"
}
PEERERROR "The peer side has signaled an error" {
NONE ""
}
}
set main_array_item {
const char** strerror_array_major [] = {
$minor_array_list
};
}
set major_size_item {
const size_t strerror_array_sizes [] = {
$minor_array_sizes
};
}
set minor_array_item {
const char* strerror_msgs_$majorlc [] = {
$minor_message_items
};
}
set strerror_function {
const char* strerror_get_message(size_t major, size_t minor)
{
static const char* const undefined = "UNDEFINED ERROR";
// Extract the major array
if (major >= sizeof(strerror_array_major)/sizeof(const char**))
{
return undefined;
}
const char** array = strerror_array_major[major];
size_t size = strerror_array_sizes[major];
if (minor >= size)
{
return undefined;
}
return array[minor];
}
}
set globalheader {
/*
WARNING: Generated from ../scripts/generate-error-types.tcl
DO NOT MODIFY.
Copyright applies as per the generator script.
*/
#include <cstddef>
}
proc Generate:imp {} {
puts $::globalheader
puts "namespace srt\n\{"
# Generate major array
set majitem 0
set minor_array_sizes ""
foreach {mt mm cont} $::errortypes {
puts "// MJ_$mt '$mm'"
# Generate minor array
set majorlc [string tolower $mt]
set minor_message_items ""
set minitem 0
foreach {mnt mnm} $cont {
if {$mm == ""} {
set msg $mnm
} elseif {$mnm == ""} {
set msg $mm
} else {
set msg "$mm: $mnm"
}
append minor_message_items " \"$msg\", // MN_$mnt = $minitem\n"
incr minitem
}
append minor_message_items " \"\""
puts [subst -nobackslashes -nocommands $::minor_array_item]
append minor_array_list " strerror_msgs_$majorlc, // MJ_$mt = $majitem\n"
#append minor_array_sizes " [expr {$minitem}],\n"
append minor_array_sizes " SRT_ARRAY_SIZE(strerror_msgs_$majorlc) - 1,\n"
incr majitem
}
append minor_array_list " NULL"
append minor_array_sizes " 0"
puts [subst -nobackslashes -nocommands $::main_array_item]
puts {#define SRT_ARRAY_SIZE(ARR) sizeof(ARR) / sizeof(ARR[0])}
puts [subst -nobackslashes -nocommands $::major_size_item]
puts $::strerror_function
puts "\} // namespace srt"
}
set defmode imp
if {[lindex $argv 0] != ""} {
set defmode [lindex $argv 0]
}
Generate:$defmode

View file

@ -0,0 +1,454 @@
#!/usr/bin/tclsh
#*
#* SRT - Secure, Reliable, Transport
#* Copyright (c) 2020 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/.
#*
#*/
#
#*****************************************************************************
#written by
# Haivision Systems Inc.
#*****************************************************************************
# What fields are there in every entry
set model {
longname
shortname
id
description
}
# Logger definitions.
# Comments here allowed, just only for the whole line.
# Use values greater than 0. Value 0 is reserved for LOGFA_GENERAL,
# which is considered always enabled.
set loggers {
GENERAL gg 0 "General uncategorized log, for serious issues only"
SOCKMGMT sm 1 "Socket create/open/close/configure activities"
CONN cn 2 "Connection establishment and handshake"
XTIMER xt 3 "The checkTimer and around activities"
TSBPD ts 4 "The TsBPD thread"
RSRC rs 5 "System resource allocation and management"
CONGEST cc 7 "Congestion control module"
PFILTER pf 8 "Packet filter module"
API_CTRL ac 11 "API part for socket and library managmenet"
QUE_CTRL qc 13 "Queue control activities"
EPOLL_UPD ei 16 "EPoll, internal update activities"
API_RECV ar 21 "API part for receiving"
BUF_RECV br 22 "Buffer, receiving side"
QUE_RECV qr 23 "Queue, receiving side"
CHN_RECV kr 24 "CChannel, receiving side"
GRP_RECV gr 25 "Group, receiving side"
API_SEND as 31 "API part for sending"
BUF_SEND bs 32 "Buffer, sending side"
QUE_SEND qs 33 "Queue, sending side"
CHN_SEND ks 34 "CChannel, sending side"
GRP_SEND gs 35 "Group, sending side"
INTERNAL in 41 "Internal activities not connected directly to a socket"
QUE_MGMT qm 43 "Queue, management part"
CHN_MGMT km 44 "CChannel, management part"
GRP_MGMT gm 45 "Group, management part"
EPOLL_API ea 46 "EPoll, API part"
}
set hidden_loggers {
# Haicrypt logging - usually off.
HAICRYPT hc 6 "Haicrypt module area"
# defined in apps, this is only a stub to lock the value
APPLOG ap 10 "Applications"
}
set globalheader {
/*
WARNING: Generated from ../scripts/generate-logging-defs.tcl
DO NOT MODIFY.
Copyright applies as per the generator script.
*/
}
# This defines, what kind of definition will be generated
# for a given file out of the log FA entry list.
# Fields:
# - prefix/postfix model
# - logger_format
# - hidden_logger_format
# COMMENTS NOT ALLOWED HERE! Only as C++ comments inside C++ model code.
set special {
srtcore/logger_default.cpp {
if {"$longname" == "HAICRYPT"} {
puts $od "
#if ENABLE_HAICRYPT_LOGGING
allfa.set(SRT_LOGFA_HAICRYPT, true);
#endif"
}
}
}
proc GenerateModelForSrtH {} {
# `path` will be set to the git top path
global path
set fd [open [file join $path srtcore/srt.h] r]
set contents ""
set state read
set pass looking
while { [gets $fd line] != -1 } {
if { $state == "read" } {
if { $pass != "passed" } {
set re [regexp {SRT_LOGFA BEGIN GENERATED SECTION} $line]
if {$re} {
set state skip
set pass found
}
}
append contents "$line\n"
continue
}
if {$state == "skip"} {
if { [string trim $line] == "" } {
# Empty line, continue skipping
continue
}
set re [regexp {SRT_LOGFA END GENERATED SECTION} $line]
if {!$re} {
# Still SRT_LOGFA definitions
continue
}
# End of generated section. Switch back to pass-thru.
# First fill the gap
append contents "\n\$entries\n\n"
append contents "$line\n"
set state read
set pass passed
}
}
close $fd
# Sanity check
if {$pass != "passed"} {
error "Invalid contents of `srt.h` file, can't find '#define SRT_LOGFA_' phrase"
}
return $contents
}
# COMMENTS NOT ALLOWED HERE! Only as C++ comments inside C++ model code.
# (NOTE: Tcl syntax highlighter will likely falsely highlight # as comment here)
#
# Model: TARGET-NAME { format-model logger-pattern hidden-logger-pattern }
#
# Special syntax:
#
# %<command> : a high-level command execution. This declares a command that
# must be executed to GENERATE the model. Then, [subst] is executed
# on the results.
#
# = : when placed as the hidden-logger-pattern, it's equal to logger-pattern.
#
set generation {
srtcore/srt.h {
{%GenerateModelForSrtH}
{#define [format "%-20s %-3d" SRT_LOGFA_${longname} $id] // ${shortname}log: $description}
=
}
srtcore/logger_default.cpp {
{
$globalheader
#include "srt.h"
#include "logging.h"
#include "logger_defs.h"
namespace srt_logging
{
AllFaOn::AllFaOn()
{
$entries
}
} // namespace srt_logging
}
{
allfa.set(SRT_LOGFA_${longname}, true);
}
}
srtcore/logger_defs.cpp {
{
$globalheader
#include "srt.h"
#include "logging.h"
#include "logger_defs.h"
namespace srt_logging { AllFaOn logger_fa_all; }
// We need it outside the namespace to preserve the global name.
// It's a part of "hidden API" (used by applications)
SRT_API srt_logging::LogConfig srt_logger_config(srt_logging::logger_fa_all.allfa);
namespace srt_logging
{
$entries
} // namespace srt_logging
}
{
Logger ${shortname}log(SRT_LOGFA_${longname}, srt_logger_config, "SRT.${shortname}");
}
}
srtcore/logger_defs.h {
{
$globalheader
#ifndef INC_SRT_LOGGER_DEFS_H
#define INC_SRT_LOGGER_DEFS_H
#include "srt.h"
#include "logging.h"
namespace srt_logging
{
struct AllFaOn
{
LogConfig::fa_bitset_t allfa;
AllFaOn();
};
$entries
} // namespace srt_logging
#endif
}
{
extern Logger ${shortname}log;
}
}
apps/logsupport_appdefs.cpp {
{
$globalheader
#include "logsupport.hpp"
LogFANames::LogFANames()
{
$entries
}
}
{
Install("$longname", SRT_LOGFA_${longname});
}
{
Install("$longname", SRT_LOGFA_${longname});
}
}
}
# EXECUTION
set here [file dirname [file normalize $argv0]]
if {[lindex [file split $here] end] != "scripts"} {
puts stderr "The script is in weird location."
exit 1
}
set path [file join {*}[lrange [file split $here] 0 end-1]]
# Utility. Allows to put line-oriented comments and have empty lines
proc no_comments {input} {
set output ""
foreach line [split $input \n] {
set nn [string trim $line]
if { $nn == "" || [string index $nn 0] == "#" } {
continue
}
append output $line\n
}
return $output
}
proc generate_file {od target} {
global globalheader
lassign [dict get $::generation $target] format_model pattern hpattern
set ptabprefix ""
if {[string index $format_model 0] == "%"} {
set command [string range $format_model 1 end]
set format_model [eval $command]
}
if {$format_model != ""} {
set beginindex 0
while { [string index $format_model $beginindex] == "\n" } {
incr beginindex
}
set endindex $beginindex
while { [string is space [string index $format_model $endindex]] } {
incr endindex
}
set tabprefix [string range $pattern $beginindex $endindex-1]
set newformat ""
foreach line [split $format_model \n] {
if {[string trim $line] == ""} {
append newformat "\n"
continue
}
if {[string first $tabprefix $line] == 0} {
set line [string range $line [string length $tabprefix] end]
}
append newformat $line\n
set ie [string first {$} $line]
if {$ie != -1} {
if {[string range $line $ie end] == {$entries}} {
set ptabprefix "[string range $line 0 $ie-1]"
}
}
}
set format_model $newformat
unset newformat
}
set entries ""
if {[string trim $pattern] != "" } {
set prevval 0
set pattern [string trim $pattern]
# The first "$::model" will expand into variable names
# as defined there.
foreach [list {*}$::model] [no_comments $::loggers] {
if {$prevval + 1 != $id} {
append entries "\n"
}
append entries "${ptabprefix}[subst -nobackslashes $pattern]\n"
set prevval $id
}
}
if {$hpattern != ""} {
if {$hpattern == "="} {
set hpattern $pattern
} else {
set hpattern [string trim $hpattern]
}
# Extra line to separate from the normal entries
append entries "\n"
foreach [list {*}$::model] [no_comments $::hidden_loggers] {
append entries "${ptabprefix}[subst -nobackslashes $hpattern]\n"
}
}
if { [dict exists $::special $target] } {
set code [subst [dict get $::special $target]]
# The code should contain "append entries" !
eval $code
}
set entries [string trim $entries]
if {$format_model == ""} {
set format_model $entries
}
# For any case, cut external spaces
puts $od [string trim [subst -nocommands -nobackslashes $format_model]]
}
proc debug_vars {list} {
set output ""
foreach name $list {
upvar $name _${name}
lappend output "${name}=[set _${name}]"
}
return $output
}
# MAIN
set entryfiles $argv
if {$entryfiles == ""} {
set entryfiles [dict keys $generation]
} else {
foreach ef $entryfiles {
if { $ef ni [dict keys $generation] } {
error "Unknown generation target: $entryfiles"
}
}
}
foreach f $entryfiles {
# Set simple relative path, if the file isn't defined as path.
if { [llength [file split $f]] == 1 } {
set filepath $f
} else {
set filepath [file join $path $f]
}
puts stderr "Generating '$filepath'"
set od [open $filepath.tmp w]
generate_file $od $f
close $od
if { [file exists $filepath] } {
puts "WARNING: will overwrite exiting '$f'. Hit ENTER to confirm, or Control-C to stop"
gets stdin
}
file rename -force $filepath.tmp $filepath
}
puts stderr Done.

View file

@ -12,11 +12,11 @@ include(CheckCXXSourceCompiles)
# Useful for combinging paths
function(adddirname prefix lst out_lst)
set(output)
foreach(item ${lst})
list(APPEND output "${prefix}/${item}")
endforeach()
set(${out_lst} ${${out_lst}} ${output} PARENT_SCOPE)
set(output)
foreach(item ${lst})
list(APPEND output "${prefix}/${item}")
endforeach()
set(${out_lst} ${${out_lst}} ${output} PARENT_SCOPE)
endfunction()
# Splits a version formed as "major.minor.patch" recorded in variable 'prefix'
@ -32,11 +32,11 @@ ENDMACRO(set_version_variables)
# Sets given variable to 1, if the condition that follows it is satisfied.
# Otherwise set it to 0.
MACRO(set_if varname)
IF(${ARGN})
SET(${varname} 1)
ELSE(${ARGN})
SET(${varname} 0)
ENDIF(${ARGN})
IF(${ARGN})
SET(${varname} 1)
ELSE(${ARGN})
SET(${varname} 0)
ENDIF(${ARGN})
ENDMACRO(set_if)
FUNCTION(join_arguments outvar)
@ -49,83 +49,8 @@ FUNCTION(join_arguments outvar)
set (${outvar} ${output} PARENT_SCOPE)
ENDFUNCTION()
# LEGACY. PLEASE DON'T USE ANYMORE.
MACRO(MafRead maffile)
message(WARNING "MafRead is deprecated. Please use MafReadDir instead")
# ARGN contains the extra "section-variable" pairs
# If empty, return nothing
set (MAFREAD_TAGS
SOURCES # source files
PUBLIC_HEADERS # installable headers for include
PROTECTED_HEADERS # installable headers used by other headers
PRIVATE_HEADERS # non-installable headers
)
cmake_parse_arguments(MAFREAD_VAR "" "${MAFREAD_TAGS}" "" ${ARGN})
# Arguments for these tags are variables to be filled
# with the contents of particular section.
# While reading the file, extract the section.
# Section is recognized by either first uppercase character or space.
# @c http://cmake.org/pipermail/cmake/2007-May/014222.html
FILE(READ ${maffile} MAFREAD_CONTENTS)
STRING(REGEX REPLACE ";" "\\\\;" MAFREAD_CONTENTS "${MAFREAD_CONTENTS}")
STRING(REGEX REPLACE "\n" ";" MAFREAD_CONTENTS "${MAFREAD_CONTENTS}")
#message("DEBUG: MAF FILE CONTENTS: ${MAFREAD_CONTENTS}")
#message("DEBUG: PASSED VARIABLES:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# message("DEBUG: ${DEBUG_VAR}=${MAFREAD_VAR_${DEBUG_VAR}}")
#endforeach()
# The unnamed section becomes SOURCES
set (MAFREAD_VARIABLE ${MAFREAD_VAR_SOURCES})
set (MAFREAD_UNASSIGNED "")
FOREACH(MAFREAD_LINE ${MAFREAD_CONTENTS})
# Test what this line is
string(STRIP ${MAFREAD_LINE} MAFREAD_OLINE)
string(SUBSTRING ${MAFREAD_OLINE} 0 1 MAFREAD_FIRST)
#message("DEBUG: LINE='${MAFREAD_LINE}' FIRST='${MAFREAD_FIRST}'")
# The 'continue' command is cmake 3.2 - very late discovery
if (MAFREAD_FIRST STREQUAL "")
#message("DEBUG: ... skipped: empty")
elseif (MAFREAD_FIRST STREQUAL "#")
#message("DEBUG: ... skipped: comment")
else()
# Will be skipped if the line was a comment/empty
string(REGEX MATCH "[ A-Z]" MAFREAD_SECMARK ${MAFREAD_FIRST})
if (MAFREAD_SECMARK STREQUAL "")
# This isn't a section, it's a list element.
#message("DEBUG: ITEM: ${MAFREAD_OLINE} --> ${MAFREAD_VARIABLE}")
LIST(APPEND ${MAFREAD_VARIABLE} ${MAFREAD_OLINE})
else()
# It's a section - change the running variable
# Make it section name
STRING(REPLACE " " "_" MAFREAD_SECNAME ${MAFREAD_OLINE})
set(MAFREAD_VARIABLE ${MAFREAD_VAR_${MAFREAD_SECNAME}})
if (MAFREAD_VARIABLE STREQUAL "")
set(MAFREAD_VARIABLE MAFREAD_UNASSIGNED)
endif()
#message("DEBUG: NEW SECTION: '${MAFREAD_SECNAME}' --> VARIABLE: '${MAFREAD_VARIABLE}'")
endif()
endif()
ENDFOREACH()
# Final debug report
#set (ALL_VARS "")
#message("DEBUG: extracted variables:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# list(APPEND ALL_VARS ${MAFREAD_VAR_${DEBUG_VAR}})
#endforeach()
#list(REMOVE_DUPLICATES ALL_VARS)
#foreach(DEBUG_VAR ${ALL_VARS})
# message("DEBUG: --> ${DEBUG_VAR} = ${${DEBUG_VAR}}")
#endforeach()
ENDMACRO(MafRead)
# New version of MafRead macro, which automatically adds directory
# prefix. This should also resolve each relative path.
# The directory specifies the location of maffile and
# all files specified in the list.
MACRO(MafReadDir directory maffile)
# ARGN contains the extra "section-variable" pairs
# If empty, return nothing
@ -155,11 +80,11 @@ MACRO(MafReadDir directory maffile)
configure_file(${directory}/${maffile} dummy_${maffile}.cmake.out)
file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/dummy_${maffile}.cmake.out)
#message("DEBUG: MAF FILE CONTENTS: ${MAFREAD_CONTENTS}")
#message("DEBUG: PASSED VARIABLES:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# message("DEBUG: ${DEBUG_VAR}=${MAFREAD_VAR_${DEBUG_VAR}}")
#endforeach()
#message("DEBUG: MAF FILE CONTENTS: ${MAFREAD_CONTENTS}")
#message("DEBUG: PASSED VARIABLES:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# message("DEBUG: ${DEBUG_VAR}=${MAFREAD_VAR_${DEBUG_VAR}}")
#endforeach()
# The unnamed section becomes SOURCES
set (MAFREAD_VARIABLE ${MAFREAD_VAR_SOURCES})
@ -188,11 +113,60 @@ MACRO(MafReadDir directory maffile)
if (${MAFREAD_SECTION_TYPE} STREQUAL file)
get_filename_component(MAFREAD_OLINE ${directory}/${MAFREAD_OLINE} ABSOLUTE)
endif()
LIST(APPEND ${MAFREAD_VARIABLE} ${MAFREAD_OLINE})
set (MAFREAD_CONDITION_OK 1)
if (DEFINED MAFREAD_CONDITION_LIST)
FOREACH(MFITEM IN ITEMS ${MAFREAD_CONDITION_LIST})
separate_arguments(MFITEM)
FOREACH(MFVAR IN ITEMS ${MFITEM})
STRING(SUBSTRING ${MFVAR} 0 1 MFPREFIX)
if (MFPREFIX STREQUAL "!")
STRING(SUBSTRING ${MFVAR} 1 -1 MFVAR)
if (${MFVAR})
set (MFCONDITION_RESULT 0)
else()
set (MFCONDITION_RESULT 1)
endif()
else()
if (${MFVAR})
set (MFCONDITION_RESULT 1)
else()
set (MFCONDITION_RESULT 0)
endif()
endif()
#message("CONDITION: ${MFPREFIX} ${MFVAR} -> ${MFCONDITION_RESULT}")
MATH(EXPR MAFREAD_CONDITION_OK "${MAFREAD_CONDITION_OK} & (${MFCONDITION_RESULT})")
ENDFOREACH()
ENDFOREACH()
endif()
if (MAFREAD_CONDITION_OK)
LIST(APPEND ${MAFREAD_VARIABLE} ${MAFREAD_OLINE})
else()
#message("... NOT ADDED ITEM: ${MAFREAD_OLINE}")
endif()
else()
# It's a section - change the running variable
# It's a section
# Check for conditionals (clear current conditions first)
unset(MAFREAD_CONDITION_LIST)
STRING(FIND ${MAFREAD_OLINE} " -" MAFREAD_HAVE_CONDITION)
if (NOT MAFREAD_HAVE_CONDITION EQUAL -1)
# Cut off conditional specification, and
# grab the section name and condition list
STRING(REPLACE " -" ";" MAFREAD_CONDITION_LIST ${MAFREAD_OLINE})
#message("CONDITION READ: ${MAFREAD_CONDITION_LIST}")
LIST(GET MAFREAD_CONDITION_LIST 0 MAFREAD_OLINE)
LIST(REMOVE_AT MAFREAD_CONDITION_LIST 0)
#message("EXTRACTING SECTION=${MAFREAD_OLINE} CONDITIONS=${MAFREAD_CONDITION_LIST}")
endif()
# change the running variable
# Make it section name
STRING(REPLACE " " "_" MAFREAD_SECNAME ${MAFREAD_OLINE})
#message("MAF SECTION: ${MAFREAD_SECNAME}")
# The cmake's version of 'if (MAFREAD_SECNAME[0] == '-')' - sigh...
string(SUBSTRING ${MAFREAD_SECNAME} 0 1 MAFREAD_SECNAME0)
@ -212,15 +186,15 @@ MACRO(MafReadDir directory maffile)
ENDFOREACH()
# Final debug report
#set (ALL_VARS "")
#message("DEBUG: extracted variables:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# list(APPEND ALL_VARS ${MAFREAD_VAR_${DEBUG_VAR}})
#endforeach()
#list(REMOVE_DUPLICATES ALL_VARS)
#foreach(DEBUG_VAR ${ALL_VARS})
# message("DEBUG: --> ${DEBUG_VAR} = ${${DEBUG_VAR}}")
#endforeach()
#set (ALL_VARS "")
#message("DEBUG: extracted variables:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# list(APPEND ALL_VARS ${MAFREAD_VAR_${DEBUG_VAR}})
#endforeach()
#list(REMOVE_DUPLICATES ALL_VARS)
#foreach(DEBUG_VAR ${ALL_VARS})
# message("DEBUG: --> ${DEBUG_VAR} = ${${DEBUG_VAR}}")
#endforeach()
ENDMACRO(MafReadDir)
# NOTE: This is historical only. Not in use.
@ -240,9 +214,9 @@ MACRO(GetMafHeaders directory outvar)
ENDMACRO(GetMafHeaders)
function (getVarsWith _prefix _varResult)
get_cmake_property(_vars VARIABLES)
string (REGEX MATCHALL "(^|;)${_prefix}[A-Za-z0-9_]*" _matchedVars "${_vars}")
set (${_varResult} ${_matchedVars} PARENT_SCOPE)
get_cmake_property(_vars VARIABLES)
string (REGEX MATCHALL "(^|;)${_prefix}[A-Za-z0-9_]*" _matchedVars "${_vars}")
set (${_varResult} ${_matchedVars} PARENT_SCOPE)
endfunction()
function (check_testcode_compiles testcode libraries _successful)
@ -254,15 +228,18 @@ function (check_testcode_compiles testcode libraries _successful)
set (CMAKE_REQUIRED_LIBRARIES ${save_required_libraries})
endfunction()
function (test_requires_clock_gettime _result)
function (test_requires_clock_gettime _enable _linklib)
# This function tests if clock_gettime can be used
# - at all
# - with or without librt
# Result will be:
# rt (if librt required)
# "" (if no extra libraries required)
# -- killed by FATAL_ERROR if clock_gettime is not available
# - CLOCK_MONOTONIC is available, link with librt:
# _enable = ON; _linklib = "-lrt".
# - CLOCK_MONOTONIC is available, link without librt:
# _enable = ON; _linklib = "".
# - CLOCK_MONOTONIC is not available:
# _enable = OFF; _linklib = "-".
set (code "
#include <time.h>
@ -275,17 +252,39 @@ function (test_requires_clock_gettime _result)
check_testcode_compiles(${code} "" HAVE_CLOCK_GETTIME_IN)
if (HAVE_CLOCK_GETTIME_IN)
message(STATUS "Checked clock_gettime(): no extra libs needed")
set (${_result} "" PARENT_SCOPE)
message(STATUS "CLOCK_MONOTONIC: available, no extra libs needed")
set (${_enable} ON PARENT_SCOPE)
set (${_linklib} "" PARENT_SCOPE)
return()
endif()
check_testcode_compiles(${code} "rt" HAVE_CLOCK_GETTIME_LIBRT)
if (HAVE_CLOCK_GETTIME_LIBRT)
message(STATUS "Checked clock_gettime(): requires -lrt")
set (${_result} "-lrt" PARENT_SCOPE)
message(STATUS "CLOCK_MONOTONIC: available, requires -lrt")
set (${_enable} ON PARENT_SCOPE)
set (${_linklib} "-lrt" PARENT_SCOPE)
return()
endif()
message(FATAL_ERROR "clock_gettime() is not available on this system")
set (${_enable} OFF PARENT_SCOPE)
set (${_linklib} "-" PARENT_SCOPE)
message(STATUS "CLOCK_MONOTONIC: not available on this system")
endfunction()
function (parse_compiler_type wct _type _suffix)
if (wct STREQUAL "")
set(${_type} "" PARENT_SCOPE)
set(${_suffix} "" PARENT_SCOPE)
else()
string(REPLACE "-" ";" OUTLIST ${wct})
list(LENGTH OUTLIST OUTLEN)
list(GET OUTLIST 0 ITEM)
set(${_type} ${ITEM} PARENT_SCOPE)
if (OUTLEN LESS 2)
set(_suffix "" PARENT_SCOPE)
else()
list(GET OUTLIST 1 ITEM)
set(${_suffix} "-${ITEM}" PARENT_SCOPE)
endif()
endif()
endfunction()

View file

@ -151,10 +151,10 @@ if (NOT DEFINED IOS_ARCH)
set (IOS_ARCH x86_64)
endif (${IOS_PLATFORM} STREQUAL OS)
endif(NOT DEFINED IOS_ARCH)
set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS")
set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS")
# Set the find root to the iOS developer roots and to user defined paths
set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root")
set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "iOS find search path root")
# default to searching for frameworks first
set (CMAKE_FIND_FRAMEWORK FIRST)

View file

@ -0,0 +1,38 @@
"
" SRT - Secure, Reliable, Transport
" Copyright (c) 2020 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/.
"
" This file describes MAF ("manifest") file syntax used by
" SRT project.
"
if exists("b:current_syntax")
finish
endif
" conditionals
syn match mafCondition contained " - [!A-Za-z].*"hs=s+2
" section
syn match mafSection "^[A-Z][0-9A-Za-z_].*$" contains=mafCondition
syn match mafsection "^ .*$" contains=mafCondition
" comments
syn match mafComment "^\s*\zs#.*$"
syn match mafComment "\s\zs#.*$"
syn match mafComment contained "#.*$"
" hilites
hi def link mafComment Comment
hi def link mafSection Statement
hi def link mafCondition Number
let b:current_syntax = "maf"

View file

@ -0,0 +1,18 @@
# Script Description
Script designed to generate release notes template with main sections, contributors list, and detailed changelog out of `.csv` SRT git log file. The output `release-notes.md` file is generated in the root folder.
In order to obtain the git log file since the previous release (e.g., v1.4.0), use the following command:
```
git log --pretty=format:"%h|%s|%an|%ae" v1.4.0...HEAD^ > commits.csv
```
## Requirements
* Python 3.6+
To install Python libraries use:
```
pip install -r requirements.txt
```

View file

@ -0,0 +1,120 @@
import enum
import click
import numpy as np
import pandas as pd
@enum.unique
class Area(enum.Enum):
core = 'core'
tests = 'tests'
build = 'build'
apps = 'apps'
docs = 'docs'
def define_area(msg):
areas = [e.value for e in Area]
for area in areas:
if msg.startswith(f'[{area}] '):
return area
return np.NaN
def delete_prefix(msg):
prefixes = [f'[{e.value}] ' for e in Area]
for prefix in prefixes:
if msg.startswith(prefix):
return msg[len(prefix):]
return msg[:]
def write_into_changelog(df, f):
f.write('\n')
for _, row in df.iterrows():
f.write(f"\n{row['commit']} {row['message']}")
f.write('\n')
@click.command()
@click.argument(
'git_log',
type=click.Path(exists=True)
)
def main(git_log):
"""
Script designed to generate release notes template with main sections,
contributors list, and detailed changelog out of .csv SRT git log file.
"""
df = pd.read_csv(git_log, sep = '|', names = ['commit', 'message', 'author', 'email'])
df['area'] = df['message'].apply(define_area)
df['message'] = df['message'].apply(delete_prefix)
# Split commits by areas
core = df[df['area']==Area.core.value]
tests = df[df['area']==Area.tests.value]
build = df[df['area']==Area.build.value]
apps = df[df['area']==Area.apps.value]
docs = df[df['area']==Area.docs.value]
other = df[df['area'].isna()]
# Define individual contributors
contributors = df.groupby(['author', 'email'])
contributors = list(contributors.groups.keys())
with open('release-notes.md', 'w') as f:
f.write('# Release Notes\n')
f.write('\n## API / ABI / Integration Changes\n')
f.write('\n**API/ABI version: 1.x.**\n')
f.write('\n## New Features and Improvements\n')
f.write('\n## Important Bug Fixes\n')
f.write('\n## Build\n')
f.write('\n## Documentation\n')
f.write('\n## Contributors\n')
for name, email in contributors:
f.write(f'\n{name} <{email}>')
f.write('\n')
f.write('\n## Changelog\n')
f.write('\n<details><summary>Click to expand/collapse</summary>')
f.write('\n<p>')
f.write('\n')
if not core.empty:
f.write('\n### Core Functionality')
write_into_changelog(core, f)
if not tests.empty:
f.write('\n### Unit Tests')
write_into_changelog(tests, f)
if not build.empty:
f.write('\n### Build Scripts (CMake, etc.)')
write_into_changelog(build, f)
if not apps.empty:
f.write('\n### Sample Applications')
write_into_changelog(apps, f)
if not docs.empty:
f.write('\n### Documentation')
write_into_changelog(docs, f)
if not other.empty:
f.write('\n### Other')
write_into_changelog(other, f)
f.write('\n</p>')
f.write('\n</details>')
if __name__ == '__main__':
main()

View file

@ -0,0 +1,3 @@
click>=7.1.2
numpy>=1.19.1
pandas>=0.25.3

View file

@ -27,13 +27,22 @@ if($Env:APPVEYOR){
Update-AppveyorBuild -Version "$majorVer.$minorVer.$patchVer.$buildNum"
$FileDescriptionBranchCommitValue = "$Env:APPVEYOR_REPO_NAME - $($Env:APPVEYOR_REPO_BRANCH) ($($Env:APPVEYOR_REPO_COMMIT.substring(0,8)))"
}
if($Env:TEAMCITY_VERSION){
#make TeamCity update with this new version number
Write-Output "##teamcity[buildNumber '$majorVer.$minorVer.$patchVer.$buildNum']"
Write-Output "##teamcity[setParameter name='MajorVersion' value='$majorVer']"
Write-Output "##teamcity[setParameter name='MinorVersion' value='$minorVer']"
Write-Output "##teamcity[setParameter name='PatchVersion' value='$patchVer']"
Write-Output "##teamcity[setParameter name='BuildVersion' value='$buildNum']"
$FileDescriptionBranchCommitValue = "$majorVer.$minorVer.$patchVer.$buildNum - ($($Env:BUILD_VCS_NUMBER.substring(0,8)))"
}
#find C++ resource files and update file description with branch / commit details
$FileDescriptionStringRegex = '(\bVALUE\s+\"FileDescription\"\s*\,\s*\")([^\"]*\\\")*[^\"]*(\")'
Get-ChildItem -Path "./srtcore/srt_shared.rc" | ForEach-Object {
Get-ChildItem -Path "../srtcore/srt_shared.rc" | ForEach-Object {
$fileName = $_
Write-Host "Processing metadata changes for file: $fileName"
Write-Output "Processing metadata changes for file: $fileName"
$FileLines = Get-Content -path $fileName

View file

@ -0,0 +1,938 @@
-- @brief srt-dev Protocol dissector plugin
-- create a new dissector
local NAME = "SRT-dev"
local srt_dev = Proto(NAME, "SRT-dev Protocol")
-- create a preference of a Protocol
srt_dev.prefs["srt_udp_port"] = Pref.uint("SRT UDP Port", 1935, "SRT UDP Port")
-- create fields of srt_dev
-- Base.HEX, Base.DEC, Base.OCT, Base.UNIT_STRING, Base.NONE
local fields = srt_dev.fields
-- General field
local pack_type_select = {
[0] = "Data Packet",
[1] = "Control Packet"
}
fields.pack_type_tree = ProtoField.uint32(NAME .. ".pack_type_tree", "Packet Type", base.HEX)
fields.pack_type = ProtoField.uint16("srt_dev.pack_type", "Packet Type", base.HEX, pack_type_select, 0x8000)
fields.reserve = ProtoField.uint16("srt_dev.reserve", "Reserve", base.DEC)
fields.additional_info = ProtoField.uint32("srt_dev.additional_info", "Additional Information", base.DEC)
fields.time_stamp = ProtoField.uint32("srt_dev.time_stamp", "Time Stamp", base.DEC)
fields.dst_sock = ProtoField.uint32("srt_dev.dst_sock", "Destination Socket ID", base.DEC)
fields.none = ProtoField.none("srt_dev.none", "none", base.NONE)
-- Data packet fields
fields.data_flag_info_tree = ProtoField.uint8("srt_dev.data_flag_info_tree", "Data Flag Info", base.HEX)
local FF_state_select = {
[0] = "[Middle packet]",
[1] = "[Last packet]",
[2] = "[First packet]",
[3] = "[Single packet]"
}
fields.FF_state = ProtoField.uint8("srt_dev.FF_state", "FF state", base.HEX, FF_state_select, 0xC0)
local O_state_select = {
[0] = "[ORD_RELAX]",
[1] = "[ORD_REQUIRED]"
}
fields.O_state = ProtoField.uint8("srt_dev.O_state", "O state", base.HEX, O_state_select, 0x20)
local KK_state_select = {
[0] = "[Not encrypted]",
[1] = "[Data encrypted with even key]",
[2] = "[Data encrypted with odd key]"
}
fields.KK_state = ProtoField.uint8("srt_dev.KK_state", "KK state", base.HEX, KK_state_select, 0x18)
local R_state_select = {
[0] = "[ORIGINAL]",
[1] = "[RETRANSMITTED]"
}
fields.R_state = ProtoField.uint8("srt_dev.R_state", "R state", base.HEX, R_state_select, 0x04)
fields.seq_num = ProtoField.uint32("srt_dev.seq_num", "Sequence Number", base.DEC)
fields.msg_num = ProtoField.uint32("srt_dev.msg_num", "Message Number", base.DEC)--, nil, 0x3FFFFFF)
-- control packet fields
local msg_type_select = {
[0] = "[HANDSHAKE]",
[1] = "[KEEPALIVE]",
[2] = "[ACK]",
[3] = "[NAK(Loss Report)]",
[4] = "[Congestion Warning]",
[5] = "[Shutdown]",
[6] = "[ACKACK]",
[7] = "[Drop Request]",
[8] = "[Peer Error]",
[0x7FFF] = "[Message Extension Type]"
}
fields.msg_type = ProtoField.uint16("srt_dev.msg_type", "Message Type", base.HEX, msg_type_select, 0x7FFF)
fields.msg_ext_type = ProtoField.uint16("srt_dev.msg_ext_type", "Message Extented Type", base.DEC)
local flag_state_select = {
[0] = "Unset",
[1] = "Set"
}
-- Handshake packet fields
fields.UDT_version = ProtoField.uint32("srt_dev.UDT_version", "UDT Version", base.DEC)
fields.sock_type = ProtoField.uint32("srt_dev.sock_type", "Socket Type", base.DEC)
fields.ency_fld = ProtoField.uint16("srt_dev.ency_fld", "Encryption Field", base.DEC)
fields.ext_fld = ProtoField.uint16("srt_dev.ext_fld", "Extension Fields", base.HEX)
fields.ext_fld_tree = ProtoField.uint16("srt_dev.ext_fld_tree", "Extension Fields Tree", base.HEX)
fields.hsreq = ProtoField.uint16("srt_dev.hsreq", "HS_EXT_HSREQ", base.HEX, flag_state_select, 0x1)
fields.kmreq = ProtoField.uint16("srt_dev.kmreq", "HS_EXT_KMREQ", base.HEX, flag_state_select, 0x2)
fields.config = ProtoField.uint16("srt_dev.config", "HS_EXT_CONFIG", base.HEX, flag_state_select, 0x4)
fields.isn = ProtoField.uint32("srt_dev.isn", "Initial packet sequence number", base.DEC)
fields.mss = ProtoField.uint32("srt_dev.mss", "Max Packet Size", base.DEC)
fields.fc = ProtoField.uint32("srt_dev.fc", "Maximum Flow Window Size", base.DEC)
fields.conn_type = ProtoField.int32("srt_dev.conn_type", "Connection Type", base.DEC)
fields.sock_id = ProtoField.uint32("srt_dev.sock_id", "Socket ID", base.DEC)
fields.syn_cookie = ProtoField.uint32("srt_dev.syn_cookie", "SYN cookie", base.DEC)
fields.peer_ipaddr = ProtoField.none("srt_dev.peer_ipaddr", "Peer IP address", base.NONE)
fields.peer_ipaddr_4 = ProtoField.ipv4("srt_dev.peer_ipaddr", "Peer IP address")
fields.peer_ipaddr_6 = ProtoField.ipv6("srt_dev.peer_ipaddr", "Peer IP address")
local ext_type_select = {
[-1] = "SRT_CMD_NONE",
[0] = "SRT_CMD_REJECT",
[1] = "SRT_CMD_HSREQ",
[2] = "SRT_CMD_HSRSP",
[3] = "SRT_CMD_KMREQ",
[4] = "SRT_CMD_KMRSP",
[5] = "SRT_CMD_SID",
[6] = "SRT_CMD_CONGESTION",
[7] = "SRT_CMD_FILTER",
[8] = "SRT_CMD_GROUP"
}
fields.ext_type_msg_tree = ProtoField.none("srt_dev.ext_type", "Extension Type Message", base.NONE)
fields.ext_type = ProtoField.uint16("srt_dev.ext_type", "Extension Type", base.HEX, ext_type_select, 0xF)
fields.ext_size = ProtoField.uint16("srt_dev.ext_size", "Extension Size", base.DEC)
-- Handshake packet, ext type == SRT_CMD_HSREQ or SRT_CMD_HSRSP field
fields.srt_version = ProtoField.uint32("srt_dev.srt_version", "SRT Version", base.HEX)
fields.srt_flags = ProtoField.uint32("srt_dev.srt_flags", "SRT Flags", base.HEX)
fields.tsbpb_resv = ProtoField.uint16("srt_dev.tsbpb_resv", "TsbPb Receive", base.DEC)
fields.tsbpb_delay = ProtoField.uint16("srt_dev.tsbpb_delay", "TsbPb Delay", base.DEC)
fields.tsbpd_delay = ProtoField.uint16("srt_dev.tsbpd_delay", "TsbPd Delay", base.DEC)
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
local V_state_select = {
[1] = "Initial version"
}
fields.V_state = ProtoField.uint8("srt_dev.V_state", "V", base.HEX, V_state_select, 0x70)
local PT_state_select = {
[0] = "Reserved",
[1] = "MSmsg",
[2] = "KMmsg",
[7] = "Reserved to discriminate MPEG-TS packet(0x47=sync byte)"
}
fields.PT_state = ProtoField.uint8("srt_dev.PT_state", "PT", base.HEX, state_table, 0xF)
fields.sign = ProtoField.uint16("srt_dev.sign", "Signature", base.HEX)
local resv_select = {
[0] = "Reserved for flag extension or other usage"
}
fields.resv = ProtoField.uint8("srt_dev.resv", "Resv", base.DEC, state_table, 0xFC)
fields.ext_KK_state = ProtoField.uint8("srt_dev.ext_KK_state", "KK_state", base.HEX, KK_state_select, 0x3)
fields.KEKI = ProtoField.uint32("srt_dev.KEKI", "KEKI", base.DEC)
fields.cipher = ProtoField.uint8("srt_dev.cipher", "Cipher", base.DEC)
fields.auth = ProtoField.uint8("srt_dev.auth", "auth", base.DEC)
fields.SE = ProtoField.uint8("srt_dev.SE", "SE", base.DEC)
fields.resv1 = ProtoField.uint8("srt_dev.resv1", "resv1", base.DEC)
fields.resv2 = ProtoField.uint16("srt_dev.resv2", "resv2", base.DEC)
fields.slen = ProtoField.uint8("srt_dev.slen", "Salt length(bytes)/4", base.DEC)
fields.klen = ProtoField.uint8("srt_dev.klen", "SEK length(bytes)/4", base.DEC)
fields.salt = ProtoField.uint32("srt_dev.salt", "Salt key", base.DEC)
fields.wrap = ProtoField.none("srt_dev.wrap", "Wrap key(s)", base.NONE)
-- Wrap Field
fields.ICV = ProtoField.uint64("srt_dev.ICV", "Integerity Check Vector", base.HEX)
fields.odd_key = ProtoField.stringz("srt_dev.odd_key", "Odd key", base.ASCII)
fields.even_key = ProtoField.stringz("srt_dev.even_key", "Even key", base.ASCII)
-- ext_type == SRT_CMD_SID field
fields.sid = ProtoField.string("srt_dev.sid", "Stream ID", base.ASCII)
-- ext_type == SRT_CMD_CONGESTION field
fields.congestion = ProtoField.string("srt_dev.congestion", "Congestion Controller", base.ASCII)
-- ext_type == SRT_CMD_FILTER field
fields.filter = ProtoField.string("srt_dev.filter", "Filter", base.ASCII)
-- ext_type == SRT_CMD_GROUP field
fields.group = ProtoField.string("srt_dev.group", "Group Data", base.ASCII)
-- SRT flags
fields.srt_opt_tsbpdsnd = ProtoField.uint32("srt_dev.srt_opt_tsbpdsnd", "SRT_OPT_TSBPDSND", base.HEX, flag_state_select, 0x1)
fields.srt_opt_tsbpdrcv = ProtoField.uint32("srt_dev.srt_opt_tsbpdrcv", "SRT_OPT_TSBPDRCV", base.HEX, flag_state_select, 0x2)
fields.srt_opt_haicrypt = ProtoField.uint32("srt_dev.srt_opt_haicrypt", "SRT_OPT_HAICRYPT", base.HEX, flag_state_select, 0x4)
fields.srt_opt_tlpktdrop = ProtoField.uint32("srt_dev.srt_opt_tlpktdrop", "SRT_OPT_TLPKTDROP", base.HEX, flag_state_select, 0x8)
fields.srt_opt_nakreport = ProtoField.uint32("srt_dev.srt_opt_nakreport", "SRT_OPT_NAKREPORT", base.HEX, flag_state_select, 0x10)
fields.srt_opt_rexmitflg = ProtoField.uint32("srt_dev.srt_opt_rexmitflg", "SRT_OPT_REXMITFLG", base.HEX, flag_state_select, 0x20)
fields.srt_opt_stream = ProtoField.uint32("srt_dev.srt_opt_stream", "SRT_OPT_STREAM", base.HEX, flag_state_select, 0x40)
-- ACK fields
fields.last_ack_pack = ProtoField.uint32("srt_dev.last_ack_pack", "Last ACK Packet Sequence Number", base.DEC)
fields.rtt = ProtoField.int32("srt_dev.rtt", "Round Trip Time", base.DEC)
fields.rtt_variance = ProtoField.int32("srt_dev.rtt_variance", "Round Trip Time Variance", base.DEC)
fields.buf_size = ProtoField.uint32("srt_dev.buf_size", "Available Buffer Size", base.DEC)
fields.pack_rcv_rate = ProtoField.uint32("srt_dev.pack_rcv_rate", "Packet Receiving Rate", base.DEC)
fields.est_link_capacity = ProtoField.uint32("srt_dev.est_link_capacity", "Estimated Link Capacity", base.DEC)
fields.rcv_rate = ProtoField.uint32("srt_dev.rcv_rate", "Receiving Rate", base.DEC)
-- ACKACK fields
fields.ack_num = ProtoField.uint32("srt_dev.ack_num", "ACK number", base.DEC)
fields.ctl_info = ProtoField.uint32("srt_dev.ctl_info", "Control Information", base.DEC)
-- KMRSP fields
local srt_km_state_select = {
[0] = "[SRT_KM_UNSECURED]",
[1] = "[SRT_KM_SECURING]",
[2] = "[SRT_KM_SECURED]",
[3] = "[SRT_KM_NOSECRET]",
[4] = "[SRT_KM_BADSECRET]"
}
fields.km_err = ProtoField.uint32("srt_dev.km_err", "Key Message Error", base.HEX, srt_km_state_select, 0xF)
-- NAK Control Packet fields
fields.lost_list_tree = ProtoField.none("srt_dev.lost_list_tree", "Lost Packet List", base.NONE)
fields.lost_pack_seq = ProtoField.uint32("srt_dev.lost_pack_seq", "Lost Packet Sequence Number", base.DEC)
fields.lost_pack_range_tree = ProtoField.none("srt_dev.lost_pack_range_tree", "Lost Packet Range", base.NONE)
fields.lost_start = ProtoField.uint32("srt_dev.lost_start", "Lost Starting Sequence", base.DEC)
fields.lost_up_to = ProtoField.uint32("srt_dev.lost_up_to", "Lost Up To(including)", base.DEC)
-- Dissect packet
function srt_dev.dissector (tvb, pinfo, tree)
-- Packet is based on UDP, so the data can be processed directly after UDP
local subtree = tree:add(srt_dev, tvb())
local offset = 0
-- Changes the protocol name
pinfo.cols.protocol = srt_dev.name
-- Take out the first bit of package
-- 0 -> Data Packet
-- 1 -> Control Packet
local typebit = bit.rshift(tvb(offset, 1):uint(), 7)
pack_type_tree = subtree:add(fields.pack_type_tree, tvb(offset, 4))
if typebit == 1 then
-- Handle Control Packet
pack_type_tree:add(fields.pack_type, tvb(offset, 2))
local msg_type = tvb(offset, 2):uint()
if msg_type ~= 0xFFFF then
-- If type field isn't '0x7FFF',it means packet is normal data packet, then handle type field
msg_type = bit.band(msg_type, 0x7FFF)
function parse_three_param()
-- Ignore Additional Info (this field is not defined in this packet type)
subtree:add(fields.additional_info, tvb(offset, 4)):append_text(" [undefined]")
offset = offset + 4
-- Handle Time Stamp
subtree:add(fields.time_stamp, tvb(offset, 4)):append_text(" μs")
offset = offset + 4
-- Handle Destination Socket
subtree:add(fields.dst_sock, tvb(offset, 4))
offset = offset + 4
end
local switch = {
[0] = function()
pinfo.cols.info:append(" [HANDSHAKE]")
pack_type_tree:append_text(" [HANDSHAKE]")
pack_type_tree:add(fields.msg_type, tvb(offset, 2))
pack_type_tree:add(fields.reserve, tvb(offset + 2, 2)):append_text(" [Undefined]")
offset = offset + 4
-- Handle Additional Info, Timestamp and Destination Socket
parse_three_param()
-- Handle UDT version field
local UDT_version = tvb(offset, 4):uint()
subtree:add(fields.UDT_version, tvb(offset, 4))
offset = offset + 4
if UDT_version == 4 then
-- UDT version is 4, packet is diffrent from UDT version 5
-- Handle sock type
local sock_type = tvb(offset, 4):uint()
if sock_type == 1 then
subtree:add(fields.sock_type, tvb(offset, 4)):append_text(" [SRT_STREAM]")
elseif sock_type == 2 then
subtree:add(fields.sock_type, tvb(offset, 4)):append_text(" [SRT_DRAGAM]")
end
offset = offset + 4
elseif UDT_version == 5 then
-- Handle Encryption Field
local encr_fld = tvb(offset, 2):int()
if encr_fld == 0 then
subtree:add(fields.ency_fld, tvb(offset, 2)):append_text(" (PBKEYLEN not advertised)")
elseif encr_fld == 2 then
subtree:add(fields.ency_fld, tvb(offset, 2)):append_text(" (AES-128)")
elseif encr_fld == 3 then
subtree:add(fields.ency_fld, tvb(offset, 2)):append_text(" (AES-192)")
else
subtree:add(fields.ency_fld, tvb(offset, 2)):append_text(" (AES-256)")
end
offset = offset + 2
-- Handle Extension Field
local ext_fld = tvb(offset, 2):int()
if ext_fld == 0x4A17 then
subtree:add(fields.ext_fld, tvb(offset, 2)):append_text(" [HSv5 MAGIC]")
else
-- Extension Field is HS_EXT_prefix
-- The define is in fiel handshake.h
local ext_fld_tree = subtree:add(fields.ext_fld_tree, tvb(offset, 2))
local str_table = { " [" }
ext_fld_tree:add(fields.hsreq, tvb(offset, 2))
if bit.band(tvb(offset, 2):uint(), 0x1) == 1 then
table.insert(str_table, "HS_EXT_HSREQ")
table.insert(str_table, " | ")
end
ext_fld_tree:add(fields.kmreq, tvb(offset, 2)):append_text(" [HS_EXT_KMREQ]")
if bit.band(tvb(offset, 2):uint(), 0x2) == 2 then
table.insert(str_table, "HS_EXT_KMREQ")
table.insert(str_table, " | ")
end
ext_fld_tree:add(fields.config, tvb(offset, 2)):append_text(" [HS_EXT_CONFIG]")
if bit.band(tvb(offset, 2):uint(), 0x4) == 4 then
table.insert(str_table, "HS_EXT_CONFIG")
table.insert(str_table, " | ")
end
table.remove(str_table)
table.insert(str_table, "]")
if ext_fld ~= 0 then
ext_fld_tree:append_text(table.concat(str_table))
end
end
offset = offset + 2
end
-- Handle Initial packet sequence number
subtree:add(fields.isn, tvb(offset, 4))
offset = offset + 4
-- Handle Maximum Packet Size
subtree:add(fields.mss, tvb(offset, 4))
offset = offset + 4
-- Handle Maximum Flow Window Size
subtree:add(fields.fc, tvb(offset, 4))
offset = offset + 4
-- Handle Connection Type
local conn_type = tvb(offset, 4):int()
local conn_type_tree = subtree:add(fields.conn_type, tvb(offset, 4))
if conn_type == 0 then
conn_type_tree:append_text(" [WAVEAHAND] (Rendezvous Mode)")
pinfo.cols.info:append(" [WAVEAHAND] (Rendezvous Mode)")
elseif conn_type == 1 then
conn_type_tree:append_text(" [INDUCTION]")
elseif conn_type == -1 then
conn_type_tree:append_text(" [CONCLUSION]")
elseif conn_type == -2 then
conn_type_tree:append_text(" [AGREEMENT] (Rendezvous Mode)")
pinfo.cols.info:append(" [AGREEMENT] (Rendezvous Mode)")
end
offset = offset + 4
-- Handle Socket ID
subtree:add(fields.sock_id, tvb(offset, 4))
offset = offset + 4
-- Handle SYN cookie
local syn_cookie = tvb(offset, 4):int()
subtree:add(fields.syn_cookie, tvb(offset, 4))
if syn_cookie == 0 then
conn_type_tree:append_text(" (Caller to Listener)")
pinfo.cols.info:append(" (Caller to Listener)")
else
if conn_type == 1 then
-- reports cookie from listener
conn_type_tree:append_text(" (Listener to Caller)")
pinfo.cols.info:append(" (Listener to Caller)")
end
end
offset = offset + 4
-- Handle Peer IP address
-- Note the network byte order
local the_last_96_bits = 0
the_last_96_bits = the_last_96_bits + math.floor(tvb(offset + 4, 4):int() * (2 ^ 16))
the_last_96_bits = the_last_96_bits + math.floor(tvb(offset + 8, 4):int() * (2 ^ 8))
the_last_96_bits = the_last_96_bits + tvb(offset + 12, 4):int()
if the_last_96_bits == 0 then
subtree:add_le(fields.peer_ipaddr_4, tvb(offset, 4))
else
subtree:add_le(fields.peer_ipaddr, tvb(offset, 16))
end
offset = offset + 16
-- UDT version is 4, packet handle finish
if UDT_version == 4 or offset == tvb:len() then
return
end
function process_ext_type()
-- Handle Ext Type, processing by type
local ext_type = tvb(offset, 2):int()
if ext_type == 1 or ext_type == 2 then
local ext_type_msg_tree = subtree:add(fields.ext_type_msg_tree, tvb(offset, 16))
if ext_type == 1 then
ext_type_msg_tree:append_text(" [SRT_CMD_HSREQ]")
ext_type_msg_tree:add(fields.ext_type, tvb(offset, 2))
conn_type_tree:append_text(" (Caller to Listener)")
pinfo.cols.info:append(" (Caller to Listener)")
else
ext_type_msg_tree:append_text(" [SRT_CMD_HSRSP]")
ext_type_msg_tree:add(fields.ext_type, tvb(offset, 2))
conn_type_tree:append_text(" (Listener to Caller)")
pinfo.cols.info:append(" (Listener to Caller)")
end
offset = offset + 2
-- Handle Ext Size
ext_type_msg_tree:add(fields.ext_size, tvb(offset, 2))
offset = offset + 2
-- Handle SRT Version
ext_type_msg_tree:add(fields.srt_version, tvb(offset, 4))
offset = offset + 4
-- Handle SRT Flags
local SRT_flags_tree = ext_type_msg_tree:add(fields.srt_flags, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_tsbpdsnd, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_tsbpdrcv, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_haicrypt, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_tlpktdrop, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_nakreport, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_rexmitflg, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_stream, tvb(offset, 4))
offset = offset + 4
-- Handle Recv TsbPd Delay and Snd TsbPd Delay
if UDT_version == 4 then
ext_type_msg_tree:add(fields.tsbpd_delay, tvb(offset, 2)):append_text(" [Unused in HSv4]")
offset = offset + 2
ext_type_msg_tree:add(fields.tsbpb_delay, tvb(offset, 2))
offset = offset + 2
else
ext_type_msg_tree:add(fields.rcv_tsbpd_delay, tvb(offset, 2))
offset = offset + 2
ext_type_msg_tree:add(fields.snd_tsbpd_delay, tvb(offset, 2))
offset = offset + 2
end
elseif ext_type == 3 or ext_type == 4 then
local ext_type_msg_tree = subtree:add(fields.ext_type_msg_tree, tvb(offset, 16))
if ext_type == 3 then
ext_type_msg_tree:append_text(" [SRT_CMD_KMREQ]")
ext_type_msg_tree:add(fields.ext_type, tvb(offset, 2))
conn_type_tree:append_text(" (Listener to Caller)")
else
ext_type_msg_tree:append_text(" [SRT_CMD_KMRSP]")
ext_type_msg_tree:add(fields.ext_type, tvb(offset, 2))
end
offset = offset + 2
-- Handle Ext Size
local km_len = tvb(offset, 2):uint()
ext_type_msg_tree:add(fields.ext_size, tvb(offset, 2)):append_text(" (byte/4)")
offset = offset + 2
-- Handle SRT_CMD_KMREQ message
-- V and PT status flag
ext_type_msg_tree:add(fields.V_state, tvb(offset, 1))
ext_type_msg_tree:add(fields.PT_state, tvb(offset, 1))
offset = offset + 1
-- Handle sign
ext_type_msg_tree:add(fields.sign, tvb(offset, 2)):append_text(" (/'HAI/' PnP Vendor ID in big endian order)")
offset = offset + 2
-- Handle resv
ext_type_msg_tree:add(fields.resv, tvb(offset, 1))
-- Handle KK flag
local KK = tvb(offset, 1):uint()
ext_type_msg_tree:add(fields.ext_KK_state, tvb(offset, 1))
offset = offset + 1
-- Handle KEKI
if tvb(offset, 4):uint() == 0 then
ext_type_msg_tree:add(fields.KEKI, tvb(offset, 4)):append_text(" (Default stream associated key(stream/system default))")
else
ext_type_msg_tree:add(fields.KEKI, tvb(offset, 4)):append_text(" (Reserved for manually indexed keys)")
end
offset = offset + 4
-- Handle Cipher
local cipher_node = ext_type_msg_tree:add(fields.cipher, tvb(offset, 1))
local cipher = tvb(offset, 1):uint()
if cipher == 0 then
elseif cipher == 1 then
cipher_node:append_text(" (AES-ECB(potentially for VF 2.0 compatible message))")
elseif cipher == 2 then
cipher_node:append_text(" (AES-CTR[FP800-38A])")
else
cipher_node:append_text(" (AES-CCM or AES-GCM)")
end
offset = offset + 1
-- Handle Auth
if tvb(offset, 1):uint() == 0 then
ext_type_msg_tree:add(fields.auth, tvb(offset, 1)):append_text(" (None or KEKI indexed crypto context)")
else
ext_type_msg_tree:add(fields.auth, tvb(offset, 1))
end
offset = offset + 1
-- Handle SE
local SE_node = ext_type_msg_tree:add(fields.SE, tvb(offset, 1))
local SE = tvb(offset, 1):uint()
if SE == 0 then
SE_node:append_text( " (Unspecified or KEKI indexed crypto context)")
elseif SE == 1 then
SE_node:append_text( " (MPEG-TS/UDP)")
elseif SE == 2 then
SE_node:append_text( " (MPEG-TS/SRT)")
end
offset = offset + 1
-- Handle resv1
ext_type_msg_tree:add(fields.resv1, tvb(offset, 1))
offset = offset + 1
-- Handle resv2
ext_type_msg_tree:add(fields.resv2, tvb(offset, 2))
offset = offset + 2
-- Handle slen
ext_type_msg_tree:add(fields.slen, tvb(offset, 1))
offset = offset + 1
-- Handle klen
local klen = tvb(offset, 1):uint()
ext_type_msg_tree:add(fields.klen, tvb(offset, 1))
offset = offset + 1
-- Handle salt key
ext_type_msg_tree:add(fields.salt, tvb(offset, slen * 4))
offset = offset + slen * 4
-- Handle wrap
-- Handle ICV
local wrap_len = 8 + KK * klen
local wrap_tree = ext_type_msg_tree:add(fields.wrap, tvb(offset, wrap_len))
wrap_tree:add(fields.ICV, tvb(offset, 8))
offset = offset + 8
-- If KK == 2, first key is Even key
if KK == 2 then
wrap_tree:add(fields.even_key, tvb(offset, klen))
offset = offset + klen;
end
-- Handle Odd key
wrap_tree:add(fields.odd_key, tvb(offset, klen))
offset = offset + klen;
elseif ext_type >= 5 and ext_type <= 8 then
local value_size = tvb(offset + 2, 2):uint() * 4
local ext_msg_size = 2 + 2 + value_size
local type_array = { " [SRT_CMD_SID]", " [SRT_CMD_CONGESTION]", " [SRT_CMD_FILTER]", " [SRT_CMD_GROUP]" }
local field_array = { fields.sid, fields.congestion, fields.filter, fields.group }
local ext_type_msg_tree = subtree:add(fields.ext_type_msg_tree, tvb(offset, ext_msg_size)):append_text(type_array[ext_type - 4])
ext_type_msg_tree:add(fields.ext_type, tvb(offset, 2))
offset = offset + 2
-- Handle Ext Msg Value Size
ext_type_msg_tree:add(fields.ext_size, tvb(offset, 2)):append_text(" (byte/4)")
offset = offset + 2
-- Value
local value_table = {}
for pos = 0, value_size - 4, 4 do
table.insert(value_table, string.char(tvb(offset + pos + 3, 1):uint()))
table.insert(value_table, string.char(tvb(offset + pos + 2, 1):uint()))
table.insert(value_table, string.char(tvb(offset + pos + 1, 1):uint()))
table.insert(value_table, string.char(tvb(offset + pos, 1):uint()))
end
local value = table.concat(value_table)
ext_type_msg_tree:add(field_array[ext_type - 4], tvb(offset, value_size), value)
offset = offset + value_size
elseif ext_type == -1 then
local ext_type_msg_tree = subtree:add(fields.ext_type_msg_tree, tvb(offset, tvb:len() - offset)):append_text(" [SRT_CMD_NONE]")
ext_type_msg_tree:add(fields.ext_type, tvb(offset, 2))
offset = offset + 2
-- none
if offset == tvb:len() then
return
end
ext_type_msg_tree:add(fields.none, tvb(offset, tvb:len() - offset))
offset = tvb:len()
end
if offset == tvb:len() then
return
else
process_ext_type()
end
end
process_ext_type()
end,
[1] = function()
pinfo.cols.info:append(" [KEEPALIVE]")
pack_type_tree:append_text(" [KEEPALIVE]")
pack_type_tree:add(fields.msg_type, tvb(offset, 2)):append_text(" [KEEPALIVE]")
pack_type_tree:add(fields.reserve, tvb(offset + 2, 2)):append_text(" [Undefined]")
offset = offset + 4
-- Handle Additional Info, Time Stamp and Destination Socket
parse_three_param()
end,
[2] = function()
pinfo.cols.info:append(" [ACK]")
pack_type_tree:append_text(" [ACK]")
pack_type_tree:add(fields.msg_type, tvb(offset, 2))
pack_type_tree:add(fields.reserve, tvb(offset + 2, 2)):append_text(" [Undefined]")
offset = offset + 4
-- Handle ACK Number
subtree:add(fields.ack_num, tvb(offset, 4))
offset = offset + 4
-- Handle Time Stamp
subtree:add(fields.time_stamp, tvb(offset, 4)):append_text(" μs")
offset = offset + 4
-- Handle Destination Socket
subtree:add(fields.dst_sock, tvb(offset, 4))
offset = offset + 4
-- Handle Last Ack Packet Sequence
local last_ack_pack = tvb(offset, 4):uint()
pinfo.cols.info:append(" (Last ACK Seq:" .. last_ack_pack .. ")")
subtree:add(fields.last_ack_pack, tvb(offset, 4))
offset = offset + 4
-- Handle RTT
local rtt = tvb(offset, 4):int()
subtree:add(fields.rtt, tvb(offset, 4)):append_text(" μs")
offset = offset + 4
-- Handle RTT variance
if rtt < 0 then
subtree:add(fields.rtt_variance, tvb(offset, 4), -tvb(offset, 4):int())
else
subtree:add(fields.rtt_variance, tvb(offset, 4))
end
offset = offset + 4
-- Handle Available Buffer Size(pkts)
subtree:add(fields.buf_size, tvb(offset, 4)):append_text(" pkts")
offset = offset + 4
-- Handle Packets Receiving Rate(Pkts/sec)
subtree:add(fields.pack_rcv_rate, tvb(offset, 4)):append_text(" pkts/sec")
offset = offset + 4
-- Handle Estmated Link Capacity
subtree:add(fields.est_link_capacity, tvb(offset, 4)):append_text(" pkts/sec")
offset = offset + 4
-- Handle Receiving Rate(bps)
subtree:add(fields.rcv_rate, tvb(offset, 4)):append_text(" bps")
offset = offset + 4
end,
[3] = function()
pinfo.cols.info:append(" [NAK(loss Report)]")
pack_type_tree:append_text(" [NAK(loss Report)]")
pack_type_tree:add(fields.msg_type, tvb(offset, 2))
pack_type_tree:add(fields.reserve, tvb(offset + 2, 2)):append_text(" [Undefined]")
offset = offset + 4
-- Handle Additional Info, Timestamp and Destination Socket
parse_three_param()
-- Handle lost packet sequence
-- lua does not support changing loop variables within loops, but in the form of closures
-- https://blog.csdn.net/Ai102iA/article/details/75371239
local start = offset
local ending = tvb:len()
local lost_list_tree = subtree:add(fields.lost_list_tree, tvb(offset, ending - offset))
for start in function()
local first_bit = bit.rshift(tvb(start, 1):uint(), 7)
if first_bit == 1 then
local lost_pack_range_tree = lost_list_tree:add(fields.lost_pack_range_tree, tvb(start, 8))
local lost_start = bit.band(tvb(start, 4):uint(), 0x7FFFFFFF)
lost_pack_range_tree:append_text(" (" .. lost_start .. " -> " .. tvb(start + 4, 4):uint() .. ")")
lost_pack_range_tree:add(fields.lost_start, tvb(start, 4), lost_start)
start = start + 4
lost_pack_range_tree:add(fields.lost_up_to, tvb(start, 4))
start = start + 4
else
lost_list_tree:add(fields.lost_pack_seq, tvb(start, 4))
start = start + 4
end
return start
end
do
if start == ending then
break
end
end
end,
[4] = function()
pinfo.cols.info:append(" [Congestion Warning]")
pack_type_tree:append_text(" [Congestion Warning]")
pack_type_tree:add(fields.msg_type, tvb(offset, 2))
pack_type_tree:add(fields.reserve, tvb(offset + 2, 2)):append_text(" [Undefined]")
offset = offset + 4
end,
[5] = function()
pinfo.cols.info:append(" [Shutdown]")
pack_type_tree:append_text(" [Shutdown]")
pack_type_tree:add(fields.msg_type, tvb(offset, 2))
pack_type_tree:add(fields.reserve, tvb(offset + 2, 2)):append_text(" [Undefined]")
offset = offset + 4
-- Handle Additional Info, Timestamp and Destination Socket
parse_three_param()
end,
[6] = function()
pinfo.cols.info:append(" [ACKACK]")
pack_type_tree:append_text(" [ACKACK]")
pack_type_tree:add(fields.msg_type, tvb(offset, 2))
pack_type_tree:add(fields.reserve, tvb(offset + 2, 2)):append_text(" [Undefined]")
offset = offset + 4
-- Handle ACK sequence number
subtree:add(fields.ack_num, tvb(offset, 4))
offset = offset + 4
-- Handle Time Stamp
subtree:add(fields.time_stamp, tvb(offset, 4)):append_text(" μs")
offset = offset + 4
-- Handle Destination Socket
subtree:add(fields.dst_sock, tvb(offset, 4))
offset = offset + 4
-- Handle Control Information
subtree:add(fields.ctl_info, tvb(offset, 4))
offset = offset + 4
end,
[7] = function()
pinfo.cols.info:append(" [Drop Request]")
pack_type_tree:append_text(" [Drop Request]")
pack_type_tree:add(fields.msg_type, tvb(offset, 2)):append_text(" [Drop Request]")
pack_type_tree:add(fields.reserve, tvb(offset + 2, 2)):append_text(" [Undefined]")
offset = offset + 4
end,
[8] = function()
pinfo.cols.info:append(" [Peer Error]")
pack_type_tree:append_text(" [Peer Error]")
pack_type_tree:add(fields.msg_type, tvb(offset, 2)):append_text(" [Peer Error]")
pack_type_tree:add(fields.reserve, tvb(offset + 2, 2)):append_text(" [Undefined]")
offset = offset + 4
end
}
-- Handle based on msg_type
local case = switch[msg_type]
if case then
case()
else
-- default case
subtree:add(fields.msg_type, tvb(offset, 2)):append_text(" [Unknown Message Type]")
offset = offset + 4
end
else
-- If type field is '0x7FFF', it means an extended type, Handle Reserve field
offset = offset + 2
local msg_ext_type = tvb(offset, 2):uint()
if msg_ext_type == 0 then
pinfo.cols.info:append(" [Message Extension]")
pack_type_tree:add(fields.msg_ext_type, tvb(offset, 2)):append_text(" [Message Extension]")
offset = offset + 2
-- Handle Additional Info, Time Stamp and Destination Socket
parse_three_param()
-- Control information: defined by user
elseif msg_ext_type == 1 or ext_type == 2 then
if msg_ext_type == 1 then
pack_type_tree:add(fields.msg_ext_type, tvb(offset, 2)):append_text(" [SRT Handshake Request]")
pinfo.cols.info:append(" [SRT Handshake Request]")
elseif msg_ext_type == 2 then
pack_type_tree:add(fields.msg_ext_type, tvb(offset, 2)):append_text(" [SRT Handshake Response]")
pinfo.cols.info:append(" [SRT Handshake Response]")
end
offset = offset + 2
-- Ignore additional info (this field is not defined in this packet type)
subtree:add(fields.additional_info, tvb(offset, 4)):append_text(" [undefined]")
offset = offset + 4
-- Handle Time Stamp
subtree:add(fields.time_stamp, tvb(offset, 4)):append_text("μs")
offset = offset + 4
-- Handle Destination Socket
subtree:add(fields.dst_sock, tvb(offset, 4))
offset = offset + 4
-- Handle SRT Version field
subtree:add(fields.srt_version, tvb(offset, 4))
offset = ofssset + 4
-- Handle SRT Flags
local SRT_flags_tree = subtree:add(fields.srt_flags, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_tsbpdsnd, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_tsbpdrcv, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_haicrypt, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_tlpktdrop, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_nakreport, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_rexmitflg, tvb(offset, 4))
SRT_flags_tree:add(fields.srt_opt_stream, tvb(offset, 4))
offset = offset + 4
-- Handle TsbPd Resv
subtree:add(fields.tsbpb_resv, tvb(offset, 2))
offset = offset + 2
-- Handle TsbPb Delay
subtree:add(fields.tsbpb_delay, tvb(offset, 2))
offset = offset + 2
-- Handle Reserved field
subtree:add(fields.reserve, tvb(offset, 4))
offset = offset + 4
elseif msg_ext_type == 3 or msg_ext_type == 4 then
if msg_ext_type == 3 then
pack_type_tree:add(fields.msg_ext_type, tvb(offset, 2)):append_text(" [Encryption Keying Material Request]")
pinfo.cols.info:append(" [Encryption Keying Material Request]")
elseif msg_ext_type == 4 then
pack_type_tree:add(fields.msg_ext_type, tvb(offset, 2)):append_text(" [Encryption Keying Material Response]")
pinfo.cols.info:append(" [Encryption Keying Material Response]")
end
offset = offset + 2
-- Ignore additional info (this field is not defined in this packet type)
subtree:add(fields.additional_info, tvb(offset, 4)):append_text(" [undefined]")
offset = offset + 4
-- Handle Timestamp
subtree:add(fields.time_stamp, tvb(offset, 4)):append_text("μs")
offset = offset + 4
-- Handle Destination Socket
subtree:add(fields.dst_sock, tvb(offset, 4))
offset = offset + 4
-- Handle KmErr
if msg_ext_type == 4 then
subtree:add(fields.km_err, tvb(offset, 4))
offset = offset + 4
return
end
-- The encrypted message is not handled
end
end
else
-- 0 -> Data Packet
pack_type_tree:add(fields.pack_type, tvb(offset, 2))
pack_type_tree:append_text(" (Data Packet)")
local seq_num = tvb(offset, 4):uint()
pinfo.cols.info:append(" (Data Packet)(Seq Num:" .. seq_num .. ")")
-- The first 4 bytes are the package sequence number
subtree:add(fields.seq_num, tvb(offset, 4))
offset = offset + 4
data_flag_info_tree = subtree:add(fields.data_flag_info_tree, tvb(offset, 1))
-- Handle FF flag
local FF_state = bit.rshift(bit.band(tvb(offset, 1):uint(), 0xC0), 6)
if FF_state == 0 then
data_flag_info_tree:append_text(" [Middle packet]")
elseif FF_state == 1 then
data_flag_info_tree:append_text(" [Last packet]")
elseif FF_state == 2 then
data_flag_info_tree:append_text(" [First packet]")
else
data_flag_info_tree:append_text(" [Single packet]")
end
data_flag_info_tree:add(fields.FF_state, tvb(offset, 1))
-- Handle O flag
local O_state = bit.rshift(bit.band(tvb(offset, 1):uint(), 0x20), 5)
if O_state == 0 then
data_flag_info_tree:append_text(" [Data delivered unordered]")
else
data_flag_info_tree:append_text(" [Data delivered in order]")
end
data_flag_info_tree:add(fields.O_state, tvb(offset, 1))
-- Handle KK flag
local KK_state = bit.rshift(bit.band(tvb(offset, 1):uint(), 0x18), 3)
if KK_state == 1 then
data_flag_info_tree:append_text(" [Encrypted with even key]")
elseif KK_state == 2 then
data_flag_info_tree:append_text(" [Encrypted with odd key]")
end
data_flag_info_tree:add(fields.KK_state, tvb(offset, 1))
-- Handle R flag
local R_state = bit.rshift(bit.band(tvb(offset, 1):uint(), 0x04), 2)
if R_state == 1 then
data_flag_info_tree:append_text(" [Retransmit packet]")
pinfo.cols.info:append(" [Retransmit packet]")
end
data_flag_info_tree:add(fields.R_state, tvb(offset, 1))
-- Handle message number
local msg_num = tvb(offset, 4):uint()
msg_num = bit.band(tvb(offset, 4):uint(), 0x03FFFFFF)
-- subtree:add(fields.msg_num, bit.band(tvb(offset, 4):uint(), 0x03FFFFFF))
subtree:add(fields.msg_num, tvb(offset, 4), msg_num)
offset = offset + 4
-- Handle Timestamp
subtree:add(fields.time_stamp, tvb(offset, 4)):append_text(" μs")
offset = offset + 4
-- Handle destination socket
subtree:add(fields.dst_sock, tvb(offset, 4))
offset = offset + 4
end
end
-- Add the protocol into udp table
local port = 1935
local function enable_dissector()
DissectorTable.get("udp.port"):add(port, srt_dev)
end
-- Call it now - enabled by default
enable_dissector()
local function disable_dissector()
DissectorTable.get("udp.port"):remove(port, srt_dev)
end
-- Prefs changed will listen at new port
function srt_dev.prefs_changed()
if port ~= srt_dev.prefs.srt_udp_port then
if port ~= 0 then
disable_dissector()
end
port = srt_dev.prefs.srt_udp_port
if port ~= 0 then
enable_dissector()
end
end
end

View file

@ -41,7 +41,7 @@ proc ReadBack {fd} {
# Nothing more to read
if {$remain == 0} {
puts stderr "NOTHING MORE TO BE WRITTEN - exitting"
puts stderr "NOTHING MORE TO BE WRITTEN - exiting"
set ::theend 1
return
}

View file

@ -0,0 +1,10 @@
tmp
installers
*.exe
*~
~*
.#*
*.bak
*.autosave
.DS_Store
._*

View file

@ -0,0 +1,123 @@
# SRT Static Libraries Installer for Windows
This directory contains scripts to build a binary installer for
libsrt on Windows systems for Visual Studio applications using SRT.
## SRT developer: Building the libsrt installer
### Prerequisites
These first two steps need to be executed once only.
- Prerequisite 1: Install OpenSSL for Windows, both 64 and 32 bits.
This can be done automatically by running the PowerShell script `install-openssl.ps1`.
- Prerequisite 2: Install NSIS, the NullSoft Installation Scripting system.
This can be done automatically by running the PowerShell script `install-nsis.ps1`.
### Building the libsrt installer
To build the libsrt installer, simply run the PowerShell script `build-win-installer.ps1`.
Running it without parameters, for instance launching it from the Windows Explorer, is
sufficient to build the installer.
Optional parameters:
- `-Version name` :
Use the specified string as version number for libsrt. By default, if the
current commit has a tag, use that tag (initial "v" removed, for instance
`1.4.3`). Otherwise, the defaut version is a detailed version number (most
recent version, number of commits since then, short commit SHA, for instance
`1.4.3-32-g22cc924`). Use that option if necessary to specify some other
non-standard form of version string.
- `-NoPause` :
Do not wait for the user to press `<enter>` at end of execution. By default,
execute a `pause` instruction at the end of execution, which is useful
when the script was launched from Windows Explorer. Use that option when the
script is invoked from another PowerShell script.
The installer is then available in the directory `installers`.
The name of the installer is `libsrt-VERS.exe` where `VERS` is the SRT version number
(see the `-Version` option).
The installer shall then be published as a release asset in the `srt` repository
on GitHub, either as `libsrt-VERS.exe` or `libsrt-VERS-win-installer.zip`.
In the latter case, the archive shall contain `libsrt-VERS.exe`.
## SRT user: Using the libsrt installer
### Installing the SRT libraries
To install the SRT libraries, simply run the `libsrt-VERS.exe` installer which is
available in the [SRT release area](https://github.com/Haivision/srt/releases).
After installing the libsrt binaries, an environment variable named `LIBSRT` is
defined with the installation root (typically `C:\Program Files (x86)\libsrt`).
If there is a need for automation, in a CI/CD pipeline for instance, the download
of the latest `libsrt-VERS.exe` and its installation can be automated using the
sample PowerShell script `install-libsrt.ps1` which is available in this directory.
This script may be freely copied in the user's build environment.
When run without parameters (for instance from the Windows explorer), this
script downloads and installs the latest version of libsrt.
Optional parameters:
- `-Destination path` :
Specify a local directory where the libsrt package will be downloaded.
By default, use the `tmp` subdirectory from this script's directory.
- `-ForceDownload` :
Force a download even if the package is already downloaded in the
destination path. Note that the latest version is always checked.
If a older package is already present but a newer one is available
online, the newer one is always downloaded, even without this option.
- `-GitHubActions` :
When used in a GitHub Actions workflow, make sure that the `LIBSRT`
environment variable is propagated to subsequent jobs. In your GitHub
workflow, in the initial setup phase, use
`script-dir\install-libsrt.ps1 -GitHubActions -NoPause`.
- `-NoInstall` :
Do not install the package, only download it. By default, libsrt is installed.
- `-NoPause` :
Do not wait for the user to press `<enter>` at end of execution. By default,
execute a `pause` instruction at the end of execution, which is useful
when the script was launched from Windows Explorer. Use that option when the
script is invoked from another PowerShell script.
### Building Windows applications with libsrt
In the SRT installation root directory (specified in environment variable `LIBSRT`),
there is a Visual Studio property file named `libsrt.props`. Simply reference this
property file in your Visual Studio project to use libsrt.
You can also do that manually by editing the application project file (the XML
file named with a `.vcxproj` extension). Add the following line just before
the end of the file:
~~~
<Import Project="$(LIBSRT)\libsrt.props"/>
~~~
With this setup, just compile your application normally, either using the
Visual Studio IDE or the MSBuild command line tool.
## Files reference
This directory contains the following files:
| File name | Usage
| ----------------------- | -----
| build-win-installer.ps1 | PowerShell script to build the libsrt installer.
| install-libsrt.ps1 | Sample PowerShell script to automatically install libsrt (for user's projects).
| install-openssl.ps1 | PowerShell script to install OpenSSL (prerequisite to build the installer).
| install-nsis.ps1 | PowerShell script to install NSIS (prerequisite to build the installer).
| libsrt.nsi | NSIS installation script (used to build the installer).
| libsrt.props | Visual Studio property files to use libsrt (embedded in the installer).
| README.md | This text file.

View file

@ -0,0 +1,227 @@
#-----------------------------------------------------------------------------
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2021, Thierry Lelegard
#
# 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/.
#
#-----------------------------------------------------------------------------
<#
.SYNOPSIS
Build the SRT static libraries installer for Windows.
.PARAMETER Version
Use the specified string as version number from libsrt. By default, if
the current commit has a tag, use that tag (initial 'v' removed). Otherwise,
the defaut version is a detailed version number (most recent version, number
of commits since then, short commit SHA).
.PARAMETER NoPause
Do not wait for the user to press <enter> at end of execution. By default,
execute a "pause" instruction at the end of execution, which is useful
when the script was run from Windows Explorer.
#>
[CmdletBinding()]
param(
[string]$Version = "",
[switch]$NoPause = $false
)
Write-Output "Building the SRT static libraries installer for Windows"
# Directory containing this script:
$ScriptDir = $PSScriptRoot
# The root of the srt repository is two levels up.
$RepoDir = (Split-Path -Parent (Split-Path -Parent $ScriptDir))
# Output directory for final installers:
$OutDir = "$ScriptDir\installers"
# Temporary directory for build operations:
$TmpDir = "$ScriptDir\tmp"
#-----------------------------------------------------------------------------
# A function to exit this script with optional error message, using -NoPause
#-----------------------------------------------------------------------------
function Exit-Script([string]$Message = "")
{
$Code = 0
if ($Message -ne "") {
Write-Output "ERROR: $Message"
$Code = 1
}
if (-not $NoPause) {
pause
}
exit $Code
}
#-----------------------------------------------------------------------------
# Build SRT version strings
#-----------------------------------------------------------------------------
# By default, let git format a decent version number.
if (-not $Version) {
$Version = (git describe --tags ) -replace '-g','-'
}
$Version = $Version -replace '^v',''
# Split version string in pieces and make sure to get at least four elements.
$VField = ($Version -split "[-\. ]") + @("0", "0", "0", "0") | Select-String -Pattern '^\d*$'
$VersionInfo = "$($VField[0]).$($VField[1]).$($VField[2]).$($VField[3])"
Write-Output "SRT version: $Version"
Write-Output "Windows version info: $VersionInfo"
#-----------------------------------------------------------------------------
# Initialization phase, verify prerequisites
#-----------------------------------------------------------------------------
# Locate OpenSSL root from local installation.
$SslRoot = @{
"x64" = "C:\Program Files\OpenSSL-Win64";
"Win32" = "C:\Program Files (x86)\OpenSSL-Win32"
}
# Verify OpenSSL directories.
$Missing = 0
foreach ($file in @($SslRoot["x64"], $SslRoot["Win32"])) {
if (-not (Test-Path $file)) {
Write-Output "**** Missing $file"
$Missing = $Missing + 1
}
}
if ($Missing -gt 0) {
Exit-Script "Missing $Missing OpenSSL files, use install-openssl.ps1 to install OpenSSL"
}
# Locate MSBuild and CMake, regardless of Visual Studio version.
Write-Output "Searching MSBuild ..."
$MSRoots = @("C:\Program Files*\MSBuild", "C:\Program Files*\Microsoft Visual Studio", "C:\Program Files*\CMake*")
$MSBuild = Get-ChildItem -Recurse -Path $MSRoots -Include MSBuild.exe -ErrorAction Ignore |
ForEach-Object { (Get-Command $_).FileVersionInfo } |
Sort-Object -Unique -Property FileVersion |
ForEach-Object { $_.FileName} |
Select-Object -Last 1
if (-not $MSBuild) {
Exit-Script "MSBuild not found"
}
Write-Output "Searching CMake ..."
$CMake = Get-ChildItem -Recurse -Path $MSRoots -Include cmake.exe -ErrorAction Ignore |
ForEach-Object { (Get-Command $_).FileVersionInfo } |
Sort-Object -Unique -Property FileVersion |
ForEach-Object { $_.FileName} |
Select-Object -Last 1
if (-not $CMake) {
Exit-Script "CMake not found, check option 'C++ CMake tools for Windows' in Visual Studio installer"
}
# Locate NSIS, the Nullsoft Scriptable Installation System.
Write-Output "Searching NSIS ..."
$NSIS = Get-Item "C:\Program Files*\NSIS\makensis.exe" | ForEach-Object { $_.FullName} | Select-Object -Last 1
if (-not $NSIS) {
Exit-Script "NSIS not found, use install-nsis.ps1 to install NSIS"
}
Write-Output "MSBuild: $MSBuild"
Write-Output "CMake: $CMake"
Write-Output "NSIS: $NSIS"
# Create the directories for builds when necessary.
[void](New-Item -Path $TmpDir -ItemType Directory -Force)
[void](New-Item -Path $OutDir -ItemType Directory -Force)
#-----------------------------------------------------------------------------
# Configure and build SRT library using CMake on two architectures.
#-----------------------------------------------------------------------------
foreach ($Platform in @("x64", "Win32")) {
# Build directory. Cleanup to force a fresh cmake config.
$BuildDir = "$TmpDir\build.$Platform"
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $BuildDir
[void](New-Item -Path $BuildDir -ItemType Directory -Force)
# Run CMake.
Write-Output "Configuring build for platform $Platform ..."
$SRoot = $SslRoot[$Platform]
& $CMake -S $RepoDir -B $BuildDir -A $Platform `
-DENABLE_STDCXX_SYNC=ON `
-DOPENSSL_ROOT_DIR="$SRoot" `
-DOPENSSL_LIBRARIES="$SRoot\lib\libssl_static.lib;$SRoot\lib\libcrypto_static.lib" `
-DOPENSSL_INCLUDE_DIR="$SRoot\include"
# Patch version string in version.h
Get-Content "$BuildDir\version.h" |
ForEach-Object {
$_ -replace "#define *SRT_VERSION_STRING .*","#define SRT_VERSION_STRING `"$Version`""
} |
Out-File "$BuildDir\version.new" -Encoding ascii
Move-Item "$BuildDir\version.new" "$BuildDir\version.h" -Force
# Compile SRT.
Write-Output "Building for platform $Platform ..."
foreach ($Conf in @("Release", "Debug")) {
& $MSBuild "$BuildDir\SRT.sln" /nologo /maxcpucount /property:Configuration=$Conf /property:Platform=$Platform /target:srt_static
}
}
# Verify the presence of compiled libraries.
Write-Output "Checking compiled libraries ..."
$Missing = 0
foreach ($Conf in @("Release", "Debug")) {
foreach ($Platform in @("x64", "Win32")) {
$Path = "$TmpDir\build.$Platform\$Conf\srt_static.lib"
if (-not (Test-Path $Path)) {
Write-Output "**** Missing $Path"
$Missing = $Missing + 1
}
}
}
if ($Missing -gt 0) {
Exit-Script "Missing $Missing files"
}
#-----------------------------------------------------------------------------
# Build the binary installer.
#-----------------------------------------------------------------------------
$InstallExe = "$OutDir\libsrt-$Version.exe"
$InstallZip = "$OutDir\libsrt-$Version-win-installer.zip"
Write-Output "Building installer ..."
& $NSIS /V2 `
/DVersion="$Version" `
/DVersionInfo="$VersionInfo" `
/DOutDir="$OutDir" `
/DBuildRoot="$TmpDir" `
/DRepoDir="$RepoDir" `
"$ScriptDir\libsrt.nsi"
if (-not (Test-Path $InstallExe)) {
Exit-Script "**** Missing $InstallExe"
}
Write-Output "Building installer archive ..."
Remove-Item -Force -ErrorAction SilentlyContinue $InstallZip
Compress-Archive -Path $InstallExe -DestinationPath $InstallZip -CompressionLevel Optimal
if (-not (Test-Path $InstallZip)) {
Exit-Script "**** Missing $InstallZip"
}
Exit-Script

View file

@ -0,0 +1,131 @@
# SRT library download and install for Windows.
# Copyright (c) 2021, Thierry Lelegard
# All rights reserved.
<#
.SYNOPSIS
Download and install the libsrt library for Windows. This script is
provided to automate the build of Windows applications using libsrt.
.PARAMETER Destination
Specify a local directory where the libsrt package will be downloaded.
By default, use "tmp" subdirectory from this script.
.PARAMETER ForceDownload
Force a download even if the package is already downloaded.
.PARAMETER GitHubActions
When used in a GitHub Actions workflow, make sure that the LIBSRT
environment variable is propagated to subsequent jobs.
.PARAMETER NoInstall
Do not install the package. By default, libsrt is installed.
.PARAMETER NoPause
Do not wait for the user to press <enter> at end of execution. By default,
execute a "pause" instruction at the end of execution, which is useful
when the script was run from Windows Explorer.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[string]$Destination = "",
[switch]$ForceDownload = $false,
[switch]$GitHubActions = $false,
[switch]$NoInstall = $false,
[switch]$NoPause = $false
)
Write-Output "libsrt download and installation procedure"
# Default directory for downloaded products.
if (-not $Destination) {
$Destination = "$PSScriptRoot\tmp"
}
# A function to exit this script.
function Exit-Script([string]$Message = "")
{
$Code = 0
if ($Message -ne "") {
Write-Output "ERROR: $Message"
$Code = 1
}
if (-not $NoPause) {
pause
}
exit $Code
}
# Without this, Invoke-WebRequest is awfully slow.
$ProgressPreference = 'SilentlyContinue'
# Get the URL of the latest libsrt installer.
$URL = (Invoke-RestMethod "https://api.github.com/repos/Haivision/srt/releases?per_page=20" |
ForEach-Object { $_.assets } |
ForEach-Object { $_.browser_download_url } |
Select-String @("/libsrt-.*\.exe$", "/libsrt-.*-win-installer\.zip$") |
Select-Object -First 1)
if (-not $URL) {
Exit-Script "Could not find a libsrt installer on GitHub"
}
if (-not ($URL -match "\.zip$") -and -not ($URL -match "\.exe$")) {
Exit-Script "Unexpected URL, not .exe, not .zip: $URL"
}
# Installer name and path.
$InstName = (Split-Path -Leaf $URL)
$InstPath = "$Destination\$InstName"
# Create the directory for downloaded products when necessary.
[void](New-Item -Path $Destination -ItemType Directory -Force)
# Download installer
if (-not $ForceDownload -and (Test-Path $InstPath)) {
Write-Output "$InstName already downloaded, use -ForceDownload to download again"
}
else {
Write-Output "Downloading $URL ..."
Invoke-WebRequest $URL.ToString() -UseBasicParsing -UserAgent Download -OutFile $InstPath
if (-not (Test-Path $InstPath)) {
Exit-Script "$URL download failed"
}
}
# If installer is an archive, expect an exe with same name inside.
if ($InstName -match "\.zip$") {
# Expected installer name in archive.
$ZipName = $InstName
$ZipPath = $InstPath
$InstName = $ZipName -replace '-win-installer.zip','.exe'
$InstPath = "$Destination\$InstName"
# Extract the installer.
Remove-Item -Force $InstPath -ErrorAction SilentlyContinue
Write-Output "Expanding $ZipName ..."
Expand-Archive $ZipPath -DestinationPath $Destination
if (-not (Test-Path $InstPath)) {
Exit-Script "$InstName not found in $ZipName"
}
}
# Install libsrt
if (-not $NoInstall) {
Write-Output "Installing $InstName"
Start-Process -FilePath $InstPath -ArgumentList @("/S") -Wait
}
# Propagate LIBSRT in next jobs for GitHub Actions.
if ($GitHubActions -and (-not -not $env:GITHUB_ENV) -and (Test-Path $env:GITHUB_ENV)) {
$libsrt = [System.Environment]::GetEnvironmentVariable("LIBSRT","Machine")
Write-Output "LIBSRT=$libsrt" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
}
Exit-Script

View file

@ -0,0 +1,122 @@
#-----------------------------------------------------------------------------
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2021, Thierry Lelegard
#
# 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/.
#
#-----------------------------------------------------------------------------
<#
.SYNOPSIS
Download, expand and install NSIS, the NullSoft Installer Scripting.
.PARAMETER ForceDownload
Force a download even if NSIS is already downloaded.
.PARAMETER NoInstall
Do not install the NSIS package. By default, NSIS is installed.
.PARAMETER NoPause
Do not wait for the user to press <enter> at end of execution. By default,
execute a "pause" instruction at the end of execution, which is useful
when the script was run from Windows Explorer.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[switch]$ForceDownload = $false,
[switch]$NoInstall = $false,
[switch]$NoPause = $false
)
Write-Output "NSIS download and installation procedure"
$NSISPage = "https://nsis.sourceforge.io/Download"
$FallbackURL = "http://prdownloads.sourceforge.net/nsis/nsis-3.05-setup.exe?download"
# A function to exit this script.
function Exit-Script([string]$Message = "")
{
$Code = 0
if ($Message -ne "") {
Write-Output "ERROR: $Message"
$Code = 1
}
if (-not $NoPause) {
pause
}
exit $Code
}
# Local file names.
$RootDir = $PSScriptRoot
$TmpDir = "$RootDir\tmp"
# Create the directory for external products when necessary.
[void] (New-Item -Path $TmpDir -ItemType Directory -Force)
# Without this, Invoke-WebRequest is awfully slow.
$ProgressPreference = 'SilentlyContinue'
# Get the HTML page for NSIS downloads.
$status = 0
$message = ""
$Ref = $null
try {
$response = Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $NSISPage
$status = [int] [Math]::Floor($response.StatusCode / 100)
}
catch {
$message = $_.Exception.Message
}
if ($status -ne 1 -and $status -ne 2) {
# Error fetch NSIS download page.
if ($message -eq "" -and (Test-Path variable:response)) {
Write-Output "Status code $($response.StatusCode), $($response.StatusDescription)"
}
else {
Write-Output "#### Error accessing ${NSISPage}: $message"
}
}
else {
# Parse HTML page to locate the latest installer.
$Ref = $response.Links.href | Where-Object { $_ -like "*/nsis-*-setup.exe?download" } | Select-Object -First 1
}
if (-not $Ref) {
# Could not find a reference to NSIS installer.
$Url = [System.Uri]$FallbackURL
}
else {
# Build the absolute URL's from base URL (the download page) and href links.
$Url = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$NSISPage, $Ref)
}
$InstallerName = (Split-Path -Leaf $Url.LocalPath)
$InstallerPath = "$TmpDir\$InstallerName"
# Download installer
if (-not $ForceDownload -and (Test-Path $InstallerPath)) {
Write-Output "$InstallerName already downloaded, use -ForceDownload to download again"
}
else {
Write-Output "Downloading $Url ..."
Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $InstallerPath
if (-not (Test-Path $InstallerPath)) {
Exit-Script "$Url download failed"
}
}
# Install NSIS
if (-not $NoInstall) {
Write-Output "Installing $InstallerName"
Start-Process -FilePath $InstallerPath -ArgumentList @("/S") -Wait
}
Exit-Script

View file

@ -0,0 +1,119 @@
#-----------------------------------------------------------------------------
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2021, Thierry Lelegard
#
# 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/.
#
#-----------------------------------------------------------------------------
<#
.SYNOPSIS
Download, expand and install OpenSSL for Windows.
.PARAMETER ForceDownload
Force a download even if the OpenSSL installers are already downloaded.
.PARAMETER NoInstall
Do not install the OpenSSL packages. By default, OpenSSL is installed.
.PARAMETER NoPause
Do not wait for the user to press <enter> at end of execution. By default,
execute a "pause" instruction at the end of execution, which is useful
when the script was run from Windows Explorer.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[switch]$ForceDownload = $false,
[switch]$NoInstall = $false,
[switch]$NoPause = $false
)
Write-Output "OpenSSL download and installation procedure"
$OpenSSLHomePage = "http://slproweb.com/products/Win32OpenSSL.html"
# A function to exit this script.
function Exit-Script([string]$Message = "")
{
$Code = 0
if ($Message -ne "") {
Write-Output "ERROR: $Message"
$Code = 1
}
if (-not $NoPause) {
pause
}
exit $Code
}
# Local file names.
$RootDir = $PSScriptRoot
$TmpDir = "$RootDir\tmp"
# Create the directory for external products when necessary.
[void] (New-Item -Path $TmpDir -ItemType Directory -Force)
# Without this, Invoke-WebRequest is awfully slow.
$ProgressPreference = 'SilentlyContinue'
# Get the HTML page for OpenSSL downloads.
$status = 0
$message = ""
try {
$response = Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $OpenSSLHomePage
$status = [int] [Math]::Floor($response.StatusCode / 100)
}
catch {
$message = $_.Exception.Message
}
if ($status -ne 1 -and $status -ne 2) {
if ($message -eq "" -and (Test-Path variable:response)) {
Exit-Script "Status code $($response.StatusCode), $($response.StatusDescription)"
}
else {
Exit-Script "#### Error accessing ${OpenSSLHomePage}: $message"
}
}
# Parse HTML page to locate the latest MSI files.
$Ref32 = $response.Links.href | Where-Object { $_ -like "*/Win32OpenSSL-*.msi" } | Select-Object -First 1
$Ref64 = $response.Links.href | Where-Object { $_ -like "*/Win64OpenSSL-*.msi" } | Select-Object -First 1
# Build the absolute URL's from base URL (the download page) and href links.
$Url32 = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$OpenSSLHomePage, $Ref32)
$Url64 = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$OpenSSLHomePage, $Ref64)
# Download and install one MSI package.
function Download-Install([string]$Url)
{
$MsiName = (Split-Path -Leaf $Url.toString())
$MsiPath = "$TmpDir\$MsiName"
if (-not $ForceDownload -and (Test-Path $MsiPath)) {
Write-Output "$MsiName already downloaded, use -ForceDownload to download again"
}
else {
Write-Output "Downloading $Url ..."
Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $MsiPath
}
if (-not (Test-Path $MsiPath)) {
Exit-Script "$Url download failed"
}
if (-not $NoInstall) {
Write-Output "Installing $MsiName"
Start-Process msiexec.exe -ArgumentList @("/i", $MsiPath, "/qn", "/norestart") -Wait
}
}
# Download and install the two MSI packages.
Download-Install $Url32
Download-Install $Url64
Exit-Script

View file

@ -0,0 +1,217 @@
;-----------------------------------------------------------------------------
;
; SRT - Secure, Reliable, Transport
; Copyright (c) 2021, Thierry Lelegard
;
; 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/.
;
;-----------------------------------------------------------------------------
;
; NSIS script to build the SRT binary installer for Windows.
; Do not invoke NSIS directly, use PowerShell script build-win-installer.ps1
; to ensure that all parameters are properly passed.
;
;-----------------------------------------------------------------------------
Name "SRT"
Caption "SRT Libraries Installer"
!verbose push
!verbose 0
!include "MUI2.nsh"
!include "Sections.nsh"
!include "TextFunc.nsh"
!include "FileFunc.nsh"
!include "WinMessages.nsh"
!include "x64.nsh"
!verbose pop
!define ProductName "libsrt"
!define Build32Dir "${BuildRoot}\build.Win32"
!define Build64Dir "${BuildRoot}\build.x64"
!define SSL32Dir "C:\Program Files (x86)\OpenSSL-Win32"
!define SSL64Dir "C:\Program Files\OpenSSL-Win64"
; Installer file information.
VIProductVersion ${VersionInfo}
VIAddVersionKey ProductName "${ProductName}"
VIAddVersionKey ProductVersion "${Version}"
VIAddVersionKey Comments "The SRT static libraries for Visual C++ on Windows"
VIAddVersionKey CompanyName "Haivision"
VIAddVersionKey LegalCopyright "Copyright (c) 2021 Haivision Systems Inc."
VIAddVersionKey FileVersion "${VersionInfo}"
VIAddVersionKey FileDescription "SRT Installer"
; Name of binary installer file.
OutFile "${OutDir}\${ProductName}-${Version}.exe"
; Generate a Unicode installer (default is ANSI).
Unicode true
; Registry key for environment variables
!define EnvironmentKey '"SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
; Registry entry for product info and uninstallation info.
!define ProductKey "Software\${ProductName}"
!define UninstallKey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${ProductName}"
; Use XP manifest.
XPStyle on
; Request administrator privileges for Windows Vista and higher.
RequestExecutionLevel admin
; "Modern User Interface" (MUI) settings.
!define MUI_ABORTWARNING
; Default installation folder.
InstallDir "$PROGRAMFILES\${ProductName}"
; Get installation folder from registry if available from a previous installation.
InstallDirRegKey HKLM "${ProductKey}" "InstallDir"
; Installer pages.
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
; Uninstaller pages.
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
; Languages.
!insertmacro MUI_LANGUAGE "English"
; Installation initialization.
function .onInit
; In 64-bit installers, don't use registry redirection.
${If} ${RunningX64}
SetRegView 64
${EndIf}
functionEnd
; Uninstallation initialization.
function un.onInit
; In 64-bit installers, don't use registry redirection.
${If} ${RunningX64}
SetRegView 64
${EndIf}
functionEnd
; Installation section
Section "Install"
; Work on "all users" context, not current user.
SetShellVarContext all
; Delete obsolete files from previous versions.
Delete "$INSTDIR\LICENSE.pthread.txt"
Delete "$INSTDIR\include\srt\srt4udt.h"
Delete "$INSTDIR\include\srt\udt.h"
Delete "$INSTDIR\lib\Release-x64\pthread.lib"
Delete "$INSTDIR\lib\Release-Win32\pthread.lib"
Delete "$INSTDIR\lib\Debug-x64\srt.pdb"
Delete "$INSTDIR\lib\Debug-x64\pthread.pdb"
Delete "$INSTDIR\lib\Debug-x64\pthread.lib"
Delete "$INSTDIR\lib\Debug-Win32\srt.pdb"
Delete "$INSTDIR\lib\Debug-Win32\pthread.pdb"
Delete "$INSTDIR\lib\Debug-Win32\pthread.lib"
SetOutPath "$INSTDIR"
File /oname=LICENSE.txt "${RepoDir}\LICENSE"
File "libsrt.props"
; Header files.
CreateDirectory "$INSTDIR\include\srt"
SetOutPath "$INSTDIR\include\srt"
File "${RepoDir}\srtcore\logging_api.h"
File "${RepoDir}\srtcore\platform_sys.h"
File "${RepoDir}\srtcore\srt.h"
File "${Build64Dir}\version.h"
CreateDirectory "$INSTDIR\include\win"
SetOutPath "$INSTDIR\include\win"
File "${RepoDir}\common\win\syslog_defs.h"
; Libraries.
CreateDirectory "$INSTDIR\lib"
CreateDirectory "$INSTDIR\lib\Release-x64"
SetOutPath "$INSTDIR\lib\Release-x64"
File /oname=srt.lib "${Build64Dir}\Release\srt_static.lib"
File /oname=libcrypto.lib "${SSL64Dir}\lib\VC\static\libcrypto64MD.lib"
File /oname=libssl.lib "${SSL64Dir}\lib\VC\static\libssl64MD.lib"
CreateDirectory "$INSTDIR\lib\Debug-x64"
SetOutPath "$INSTDIR\lib\Debug-x64"
File /oname=srt.lib "${Build64Dir}\Debug\srt_static.lib"
File /oname=libcrypto.lib "${SSL64Dir}\lib\VC\static\libcrypto64MDd.lib"
File /oname=libssl.lib "${SSL64Dir}\lib\VC\static\libssl64MDd.lib"
CreateDirectory "$INSTDIR\lib\Release-Win32"
SetOutPath "$INSTDIR\lib\Release-Win32"
File /oname=srt.lib "${Build32Dir}\Release\srt_static.lib"
File /oname=libcrypto.lib "${SSL32Dir}\lib\VC\static\libcrypto32MD.lib"
File /oname=libssl.lib "${SSL32Dir}\lib\VC\static\libssl32MD.lib"
CreateDirectory "$INSTDIR\lib\Debug-Win32"
SetOutPath "$INSTDIR\lib\Debug-Win32"
File /oname=srt.lib "${Build32Dir}\Debug\srt_static.lib"
File /oname=libcrypto.lib "${SSL32Dir}\lib\VC\static\libcrypto32MDd.lib"
File /oname=libssl.lib "${SSL32Dir}\lib\VC\static\libssl32MDd.lib"
; Add an environment variable to installation root.
WriteRegStr HKLM ${EnvironmentKey} "LIBSRT" "$INSTDIR"
; Store installation folder in registry.
WriteRegStr HKLM "${ProductKey}" "InstallDir" $INSTDIR
; Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
; Declare uninstaller in "Add/Remove Software" control panel
WriteRegStr HKLM "${UninstallKey}" "DisplayName" "${ProductName}"
WriteRegStr HKLM "${UninstallKey}" "Publisher" "Haivision"
WriteRegStr HKLM "${UninstallKey}" "URLInfoAbout" "https://github.com/Haivision/srt"
WriteRegStr HKLM "${UninstallKey}" "DisplayVersion" "${Version}"
WriteRegStr HKLM "${UninstallKey}" "DisplayIcon" "$INSTDIR\Uninstall.exe"
WriteRegStr HKLM "${UninstallKey}" "UninstallString" "$INSTDIR\Uninstall.exe"
; Get estimated size of installed files
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD HKLM "${UninstallKey}" "EstimatedSize" "$0"
; Notify applications of environment modifications
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
SectionEnd
; Uninstallation section
Section "Uninstall"
; Work on "all users" context, not current user.
SetShellVarContext all
; Get installation folder from registry
ReadRegStr $0 HKLM "${ProductKey}" "InstallDir"
; Delete product registry entries
DeleteRegKey HKCU "${ProductKey}"
DeleteRegKey HKLM "${ProductKey}"
DeleteRegKey HKLM "${UninstallKey}"
DeleteRegValue HKLM ${EnvironmentKey} "LIBSRT"
; Delete product files.
RMDir /r "$0\include"
RMDir /r "$0\lib"
Delete "$0\libsrt.props"
Delete "$0\LICENSE*"
Delete "$0\Uninstall.exe"
RMDir "$0"
; Notify applications of environment modifications
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
SectionEnd

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Visual Studio or MSBuild property file to use SRT static library -->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Normalize platform name to x64 and Win32 (some projects use x86 or Win64) -->
<Choose>
<When Condition="'$(Platform)' == 'x86'">
<PropertyGroup Label="UserMacros">
<SrtPlatform>Win32</SrtPlatform>
</PropertyGroup>
</When>
<When Condition="'$(Platform)' == 'Win64'">
<PropertyGroup Label="UserMacros">
<SrtPlatform>x64</SrtPlatform>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup Label="UserMacros">
<SrtPlatform>$(Platform)</SrtPlatform>
</PropertyGroup>
</Otherwise>
</Choose>
<!-- Compilation and link options -->
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(LIBSRT)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(LIBSRT)\lib\$(Configuration)-$(SrtPlatform);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4099 %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
</Project>

View file

@ -1,360 +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 - 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 02/21/2013
modified by
Haivision Systems Inc.
*****************************************************************************/
#include "core.h"
#include "ccc.h"
#include <cmath>
#include <cstring>
CCC::CCC():
m_iSYNInterval(CUDT::m_iSYNInterval),
m_dPktSndPeriod(1.0),
m_dCWndSize(16.0),
m_iBandwidth(),
m_dMaxCWndSize(),
m_iMSS(),
m_iSndCurrSeqNo(),
m_iRcvRate(),
m_iRTT(),
m_pcParam(NULL),
m_iPSize(0),
m_UDT(),
m_iACKPeriod(0),
m_iACKInterval(0),
m_bUserDefinedRTO(false),
m_iRTO(-1),
m_PerfInfo()
{
}
CCC::~CCC()
{
delete [] m_pcParam;
}
void CCC::setACKTimer(int msINT)
{
m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT;
}
void CCC::setACKInterval(int pktINT)
{
m_iACKInterval = pktINT;
}
void CCC::setRTO(int usRTO)
{
m_bUserDefinedRTO = true;
m_iRTO = usRTO;
}
void CCC::sendCustomMsg(CPacket& pkt) const
{
CUDT* u = CUDT::getUDTHandle(m_UDT);
if (NULL != u)
{
pkt.m_iID = u->m_PeerID;
#ifdef SRT_ENABLE_CTRLTSTAMP
pkt.m_iTimeStamp = int(CTimer::getTime() - u->m_StartTime);
#endif
u->m_pSndQueue->sendto(u->m_pPeerAddr, pkt);
}
}
const CPerfMon* CCC::getPerfInfo()
{
try
{
CUDT* u = CUDT::getUDTHandle(m_UDT);
if (NULL != u)
u->sample(&m_PerfInfo, false);
}
catch (...)
{
return NULL;
}
return &m_PerfInfo;
}
void CCC::setMSS(int mss)
{
m_iMSS = mss;
}
void CCC::setBandwidth(int bw)
{
m_iBandwidth = bw;
}
void CCC::setSndCurrSeqNo(int32_t seqno)
{
m_iSndCurrSeqNo = seqno;
}
void CCC::setRcvRate(int rcvrate)
{
m_iRcvRate = rcvrate;
}
void CCC::setMaxCWndSize(int cwnd)
{
m_dMaxCWndSize = cwnd;
}
void CCC::setRTT(int rtt)
{
m_iRTT = rtt;
}
void CCC::setUserParam(const char* param, int size)
{
delete [] m_pcParam;
m_pcParam = new char[size];
memcpy(m_pcParam, param, size);
m_iPSize = size;
}
//
CUDTCC::CUDTCC():
m_iRCInterval(),
m_LastRCTime(),
m_bSlowStart(),
m_iLastAck(),
m_bLoss(),
m_iLastDecSeq(),
m_dLastDecPeriod(),
m_iNAKCount(),
m_iDecRandom(),
m_iAvgNAKNum(),
m_iDecCount()
{
}
void CUDTCC::init()
{
m_iRCInterval = m_iSYNInterval;
m_LastRCTime = CTimer::getTime();
setACKTimer(m_iRCInterval);
m_bSlowStart = true;
m_iLastAck = m_iSndCurrSeqNo;
m_bLoss = false;
m_iLastDecSeq = CSeqNo::decseq(m_iLastAck);
m_dLastDecPeriod = 1;
m_iAvgNAKNum = 0;
m_iNAKCount = 0;
m_iDecRandom = 1;
m_dCWndSize = 16;
m_dPktSndPeriod = 1;
}
void CUDTCC::onACK(int32_t ack)
{
int64_t B = 0;
double inc = 0;
// Note: 1/24/2012
// The minimum increase parameter is increased from "1.0 / m_iMSS" to 0.01
// because the original was too small and caused sending rate to stay at low level
// for long time.
const double min_inc = 0.01;
uint64_t currtime = CTimer::getTime();
if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval)
return;
m_LastRCTime = currtime;
#ifdef SRT_ENABLE_BSTATS
//m_iRcvRate is bytes/sec
if (m_bSlowStart)
{
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
m_iLastAck = ack;
if (m_dCWndSize > m_dMaxCWndSize)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
else
m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize;
}
}
else
m_dCWndSize = ((m_iRcvRate + m_iMSS -1) / m_iMSS) / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
#else
if (m_bSlowStart)
{
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
m_iLastAck = ack;
if (m_dCWndSize > m_dMaxCWndSize)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
else
m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize;
}
}
else
m_dCWndSize = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
#endif
// During Slow Start, no rate increase
if (m_bSlowStart)
return;
if (m_bLoss)
{
m_bLoss = false;
return;
}
//m_iBandwidth is pkts/sec
B = (int64_t)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod);
if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B))
B = m_iBandwidth / 9;
if (B <= 0)
inc = min_inc;
else
{
// inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS)
// Beta = 1.5 * 10^(-6)
inc = pow(10.0, ceil(log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS;
if (inc < min_inc)
inc = min_inc;
}
m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval);
}
void CUDTCC::onLoss(const int32_t* losslist, int)
{
//Slow Start stopped, if it hasn't yet
if (m_bSlowStart)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
{
// Set the sending rate to the receiving rate.
#ifdef SRT_ENABLE_BSTATS
//Need average packet size here for better send period
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
#else
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
#endif
return;
}
// If no receiving rate is observed, we have to compute the sending
// rate according to the current window size, and decrease it
// using the method below.
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
}
m_bLoss = true;
if (CSeqNo::seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0)
{
m_dLastDecPeriod = m_dPktSndPeriod;
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125);
m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125);
m_iNAKCount = 1;
m_iDecCount = 1;
m_iLastDecSeq = m_iSndCurrSeqNo;
// remove global synchronization using randomization
srand(m_iLastDecSeq);
m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX));
if (m_iDecRandom < 1)
m_iDecRandom = 1;
}
else if ((m_iDecCount ++ < 5) && (0 == (++ m_iNAKCount % m_iDecRandom)))
{
// 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125);
m_iLastDecSeq = m_iSndCurrSeqNo;
}
}
void CUDTCC::onTimeout()
{
if (m_bSlowStart)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
#ifdef SRT_ENABLE_BSTATS
// Need average packet size here
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
#else
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
#endif
else
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
}
else
{
/*
m_dLastDecPeriod = m_dPktSndPeriod;
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2);
m_iLastDecSeq = m_iLastAck;
*/
}
}

View file

@ -1,219 +0,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 02/28/2012
*****************************************************************************/
#ifndef __UDT_CCC_H__
#define __UDT_CCC_H__
#include "udt.h"
#include "packet.h"
class UDT_API CCC
{
friend class CUDT;
public:
CCC();
virtual ~CCC();
private:
CCC(const CCC&);
CCC& operator=(const CCC&) {return *this;}
public:
/// Callback function to be called (only) at the start of a UDT connection.
/// note that this is different from CCC(), which is always called.
virtual void init() {}
/// Callback function to be called when a UDT connection is closed.
virtual void close() {}
/// Callback function to be called when an ACK packet is received.
/// @param [in] ackno the data sequence number acknowledged by this ACK.
virtual void onACK(int32_t) {}
/// Callback function to be called when a loss report is received.
/// @param [in] losslist list of sequence number of packets, in the format describled in packet.cpp.
/// @param [in] size length of the loss list.
virtual void onLoss(const int32_t*, int) {}
/// Callback function to be called when a timeout event occurs.
virtual void onTimeout() {}
/// Callback function to be called when a data is sent.
/// @param [in] seqno the data sequence number.
/// @param [in] size the payload size.
virtual void onPktSent(const CPacket*) {}
/// Callback function to be called when a data is received.
/// @param [in] seqno the data sequence number.
/// @param [in] size the payload size.
virtual void onPktReceived(const CPacket*) {}
/// Callback function to Process a user defined packet.
/// @param [in] pkt the user defined packet.
virtual void processCustomMsg(const CPacket*) {}
protected:
/// Set periodical acknowldging and the ACK period.
/// @param [in] msINT the period to send an ACK.
void setACKTimer(int msINT);
/// Set packet-based acknowldging and the number of packets to send an ACK.
/// @param [in] pktINT the number of packets to send an ACK.
void setACKInterval(int pktINT);
/// Set RTO value.
/// @param [in] msRTO RTO in macroseconds.
void setRTO(int usRTO);
/// Send a user defined control packet.
/// @param [in] pkt user defined packet.
void sendCustomMsg(CPacket& pkt) const;
/// retrieve performance information.
/// @return Pointer to a performance info structure.
const CPerfMon* getPerfInfo();
/// Set user defined parameters.
/// @param [in] param the paramters in one buffer.
/// @param [in] size the size of the buffer.
void setUserParam(const char* param, int size);
private:
void setMSS(int mss);
void setMaxCWndSize(int cwnd);
void setBandwidth(int bw);
void setSndCurrSeqNo(int32_t seqno);
void setRcvRate(int rcvrate);
void setRTT(int rtt);
protected:
const int32_t& m_iSYNInterval; // UDT constant parameter, SYN
double m_dPktSndPeriod; // Packet sending period, in microseconds
double m_dCWndSize; // Congestion window size, in packets
int m_iBandwidth; // estimated bandwidth, packets per second
double m_dMaxCWndSize; // maximum cwnd size, in packets
int m_iMSS; // Maximum Packet Size, including all packet headers
int32_t m_iSndCurrSeqNo; // current maximum seq no sent out
int m_iRcvRate; // packet arrive rate at receiver side, packets per second
int m_iRTT; // current estimated RTT, microsecond
char* m_pcParam; // user defined parameter
int m_iPSize; // size of m_pcParam
private:
UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to
int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds
int m_iACKInterval; // How many packets to send one ACK, in packets
bool m_bUserDefinedRTO; // if the RTO value is defined by users
int m_iRTO; // RTO value, microseconds
CPerfMon m_PerfInfo; // protocol statistics information
};
class CCCVirtualFactory
{
public:
virtual ~CCCVirtualFactory() {}
virtual CCC* create() = 0;
virtual CCCVirtualFactory* clone() = 0;
};
template <class T>
class CCCFactory: public CCCVirtualFactory
{
public:
virtual ~CCCFactory() {}
virtual CCC* create() {return new T;}
virtual CCCVirtualFactory* clone() {return new CCCFactory<T>;}
};
class CUDTCC: public CCC
{
public:
CUDTCC();
public:
virtual void init();
virtual void onACK(int32_t);
virtual void onLoss(const int32_t*, int);
virtual void onTimeout();
private:
int m_iRCInterval; // UDT Rate control interval
uint64_t m_LastRCTime; // last rate increase time
bool m_bSlowStart; // if in slow start phase
int32_t m_iLastAck; // last ACKed seq no
bool m_bLoss; // if loss happened since last rate increase
int32_t m_iLastDecSeq; // max pkt seq no sent out when last decrease happened
double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened
int m_iNAKCount; // NAK counter
int m_iDecRandom; // random threshold on decrease by number of loss events
int m_iAvgNAKNum; // average number of NAKs per congestion
int m_iDecCount; // number of decreases in a congestion epoch
};
#endif

View file

@ -0,0 +1,79 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2020 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/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_F_ACCESS_CONTROL_H
#define INC_F_ACCESS_CONTROL_H
// A list of rejection codes that are SRT specific.
#define SRT_REJX_FALLBACK 1000 // A code used in case when the application wants to report some problem, but can't precisely specify it.
#define SRT_REJX_KEY_NOTSUP 1001 // The key used in the StreamID keyed string is not supported by the service.
#define SRT_REJX_FILEPATH 1002 // The resource type designates a file and the path is either wrong syntax or not found
#define SRT_REJX_HOSTNOTFOUND 1003 // The `h` host specification was not recognized by the service
// The list of http codes adopted for SRT.
// An example C++ header for HTTP codes can be found at:
// https://github.com/j-ulrich/http-status-codes-cpp
// Some of the unused code can be revived in the future, if there
// happens to be a good reason for it.
#define SRT_REJX_BAD_REQUEST 1400 // General syntax error in the SocketID specification (also a fallback code for undefined cases)
#define SRT_REJX_UNAUTHORIZED 1401 // Authentication failed, provided that the user was correctly identified and access to the required resource would be granted
#define SRT_REJX_OVERLOAD 1402 // The server is too heavily loaded, or you have exceeded credits for accessing the service and the resource.
#define SRT_REJX_FORBIDDEN 1403 // Access denied to the resource by any kind of reason.
#define SRT_REJX_NOTFOUND 1404 // Resource not found at this time.
#define SRT_REJX_BAD_MODE 1405 // The mode specified in `m` key in StreamID is not supported for this request.
#define SRT_REJX_UNACCEPTABLE 1406 // The requested parameters specified in SocketID cannot be satisfied for the requested resource. Also when m=publish and the data format is not acceptable.
// CODE NOT IN USE 407: unused: proxy functionality not predicted
// 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 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
#define SRT_REJX_NOTSUP_MEDIA 1415 // The media type is not supported by the application. This is the `t` key that specifies the media type as stream, file and auth, possibly extended by the application.
// CODE NOT IN USE 416: unused: no detailed specification defined
// CODE NOT IN USE 417: unused: expectations not supported
// CODE NOT IN USE 418: unused: sharks do not drink tea
// CODE NOT IN USE 419: not defined in HTTP
// CODE NOT IN USE 420: not defined in HTTP
// CODE NOT IN USE 421: unused: misdirection not supported
// CODE NOT IN USE 422: unused: aligned to general 400
#define SRT_REJX_LOCKED 1423 // The resource being accessed is locked for any access.
#define SRT_REJX_FAILED_DEPEND 1424 // The request failed because it specified a dependent session ID that has been disconnected.
// CODE NOT IN USE 425: unused: replaying not supported
// CODE NOT IN USE 426: unused: tempting, but it requires resend in connected
// CODE NOT IN USE 427: not defined in HTTP
// CODE NOT IN USE 428: unused: renders to 409
// CODE NOT IN USE 429: unused: renders to 402
// CODE NOT IN USE 451: unused: renders to 403
#define SRT_REJX_ISE 1500 // Unexpected internal server error
#define SRT_REJX_UNIMPLEMENTED 1501 // The request was recognized, but the current version doesn't support it.
#define SRT_REJX_GW 1502 // The server acts as a gateway and the target endpoint rejected the connection.
#define SRT_REJX_DOWN 1503 // The service has been temporarily taken over by a stub reporting this error. The real service can be down for maintenance or crashed.
// CODE NOT IN USE 504: unused: timeout not supported
#define SRT_REJX_VERSION 1505 // SRT version not supported. This might be either unsupported backward compatibility, or an upper value of a version.
// CODE NOT IN USE 506: unused: negotiation and references not supported
#define SRT_REJX_NOROOM 1507 // The data stream cannot be archived due to lacking storage space. This is in case when the request type was to send a file or the live stream to be archived.
// CODE NOT IN USE 508: unused: no redirection supported
// CODE NOT IN USE 509: not defined in HTTP
// CODE NOT IN USE 510: unused: extensions not supported
// CODE NOT IN USE 511: unused: intercepting proxies not supported
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
/*
* 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/.
*
*
*/
/*****************************************************************************
@ -45,14 +45,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/*****************************************************************************
written by
Yunhong Gu, last updated 09/28/2010
Yunhong Gu, last updated 09/28/2010
modified by
Haivision Systems Inc.
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_API_H__
#define __UDT_API_H__
#ifndef INC_SRT_API_H
#define INC_SRT_API_H
#include <map>
#include <vector>
@ -64,237 +63,425 @@ modified by
#include "cache.h"
#include "epoll.h"
#include "handshake.h"
#include "core.h"
#if ENABLE_BONDING
#include "group.h"
#endif
// Please refer to structure and locking information provided in the
// docs/dev/low-level-info.md document.
namespace srt
{
class CUDT;
/// @brief Class CUDTSocket is a control layer on top of the CUDT core functionality layer.
/// CUDTSocket owns CUDT.
class CUDTSocket
{
public:
CUDTSocket();
~CUDTSocket();
CUDTSocket()
: m_Status(SRTS_INIT)
, m_SocketID(0)
, m_ListenSocket(0)
, m_PeerID(0)
#if ENABLE_BONDING
, m_GroupMemberData()
, m_GroupOf()
#endif
, m_iISN(0)
, m_UDT(this)
, m_AcceptCond()
, m_AcceptLock()
, m_uiBackLog(0)
, m_iMuxID(-1)
{
construct();
}
SRT_SOCKSTATUS m_Status; //< current socket state
CUDTSocket(const CUDTSocket& ancestor)
: m_Status(SRTS_INIT)
, m_SocketID(0)
, m_ListenSocket(0)
, m_PeerID(0)
#if ENABLE_BONDING
, m_GroupMemberData()
, m_GroupOf()
#endif
, m_iISN(0)
, m_UDT(this, ancestor.m_UDT)
, m_AcceptCond()
, m_AcceptLock()
, m_uiBackLog(0)
, m_iMuxID(-1)
{
construct();
}
/// Time when the socket is closed.
/// When the socket is closed, it is not removed immediately from the list
/// of sockets in order to prevent other methods from accessing invalid address.
/// A timer is started and the socket will be removed after approximately
/// 1 second (see CUDTUnited::checkBrokenSockets()).
uint64_t m_ClosureTimeStamp;
~CUDTSocket();
int m_iIPversion; //< IP version
sockaddr* m_pSelfAddr; //< pointer to the local address of the socket
sockaddr* m_pPeerAddr; //< pointer to the peer address of the socket
void construct();
SRTSOCKET m_SocketID; //< socket ID
SRTSOCKET m_ListenSocket; //< ID of the listener socket; 0 means this is an independent socket
SRT_ATTR_GUARDED_BY(m_ControlLock)
sync::atomic<SRT_SOCKSTATUS> m_Status; //< current socket state
SRTSOCKET m_PeerID; //< peer socket ID
int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port
/// Time when the socket is closed.
/// When the socket is closed, it is not removed immediately from the list
/// of sockets in order to prevent other methods from accessing invalid address.
/// A timer is started and the socket will be removed after approximately
/// 1 second (see CUDTUnited::checkBrokenSockets()).
sync::steady_clock::time_point m_tsClosureTimeStamp;
CUDT* m_pUDT; //< pointer to the UDT entity
sockaddr_any m_SelfAddr; //< local address of the socket
sockaddr_any m_PeerAddr; //< peer address of the socket
std::set<SRTSOCKET>* m_pQueuedSockets; //< set of connections waiting for accept()
std::set<SRTSOCKET>* m_pAcceptSockets; //< set of accept()ed connections
SRTSOCKET m_SocketID; //< socket ID
SRTSOCKET m_ListenSocket; //< ID of the listener socket; 0 means this is an independent socket
pthread_cond_t m_AcceptCond; //< used to block "accept" call
pthread_mutex_t m_AcceptLock; //< mutex associated to m_AcceptCond
SRTSOCKET m_PeerID; //< peer socket ID
#if ENABLE_BONDING
groups::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member
CUDTGroup* m_GroupOf; //< Group this socket is a member of, or NULL if it isn't
#endif
unsigned int m_uiBackLog; //< maximum number of connections in queue
int m_iMuxID; //< multiplexer ID
pthread_mutex_t m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect
static int64_t getPeerSpec(SRTSOCKET id, int32_t isn)
{
return (id << 30) + isn;
}
int64_t getPeerSpec()
{
return getPeerSpec(m_PeerID, m_iISN);
}
int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port
private:
CUDTSocket(const CUDTSocket&);
CUDTSocket& operator=(const CUDTSocket&);
CUDT m_UDT; //< internal SRT socket logic
public:
std::set<SRTSOCKET> m_QueuedSockets; //< set of connections waiting for accept()
sync::Condition m_AcceptCond; //< used to block "accept" call
sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond
unsigned int m_uiBackLog; //< maximum number of connections in queue
// XXX A refactoring might be needed here.
// There are no reasons found why the socket can't contain a list iterator to a
// multiplexer INSTEAD of m_iMuxID. There's no danger in this solution because
// the multiplexer is never deleted until there's at least one socket using it.
//
// The multiplexer may even physically be contained in the CUDTUnited object,
// just track the multiple users of it (the listener and the accepted sockets).
// When deleting, you simply "unsubscribe" yourself from the multiplexer, which
// will unref it and remove the list element by the iterator kept by the
// socket.
int m_iMuxID; //< multiplexer ID
sync::Mutex m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect
CUDT& core() { return m_UDT; }
const CUDT& core() const { return m_UDT; }
static int64_t getPeerSpec(SRTSOCKET id, int32_t isn) { return (int64_t(id) << 30) + isn; }
int64_t getPeerSpec() { return getPeerSpec(m_PeerID, m_iISN); }
SRT_SOCKSTATUS getStatus();
/// This function shall be called always wherever
/// you'd like to call cudtsocket->m_pUDT->close(),
/// from within the GC thread only (that is, only when
/// the socket should be no longer visible in the
/// connection, including for sending remaining data).
void breakSocket_LOCKED();
/// This makes the socket no longer capable of performing any transmission
/// operation, but continues to be responsive in the connection in order
/// to finish sending the data that were scheduled for sending so far.
void setClosed();
/// 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.
void setBrokenClosed();
void removeFromGroup(bool broken);
// Instrumentally used by select() and also required for non-blocking
// mode check in groups
bool readReady();
bool writeReady() const;
bool broken() const;
private:
CUDTSocket& operator=(const CUDTSocket&);
};
////////////////////////////////////////////////////////////////////////////////
class CUDTUnited
{
friend class CUDT;
friend class CRendezvousQueue;
friend class CUDT;
friend class CUDTGroup;
friend class CRendezvousQueue;
friend class CCryptoControl;
public:
CUDTUnited();
~CUDTUnited();
CUDTUnited();
~CUDTUnited();
// Public constants
static const int32_t MAX_SOCKET_VAL = SRTGROUP_MASK - 1; // maximum value for a regular socket
public:
static std::string CONID(SRTSOCKET sock);
/// initialize the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int startup();
/// release the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int cleanup();
/// Create a new UDT socket.
/// @param [in] af IP version, IPv4 (AF_INET) or IPv6 (AF_INET6).
/// @param [in] type (ignored)
/// @return The new UDT socket ID, or INVALID_SOCK.
SRTSOCKET newSocket(int af, int );
/// Create a new UDT connection.
/// @param [in] listen the listening UDT socket;
/// @param [in] peer peer address.
/// @param [in,out] hs handshake information from peer side (in), negotiated value (out);
/// @return If the new connection is successfully created: 1 success, 0 already exist, -1 error.
int newConnection(const SRTSOCKET listen, const sockaddr* peer, CHandShake* hs, const CPacket& hspkt,
ref_t<SRT_REJECT_REASON> r_error);
int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq);
/// look up the UDT entity according to its ID.
/// @param [in] u the UDT socket ID.
/// @return Pointer to the UDT entity.
CUDT* lookup(const SRTSOCKET u);
/// Check the status of the UDT socket.
/// @param [in] u the UDT socket ID.
/// @return UDT socket status, or NONEXIST if not found.
SRT_SOCKSTATUS getStatus(const SRTSOCKET u);
// socket APIs
int bind(const SRTSOCKET u, const sockaddr* name, int namelen);
int bind(const SRTSOCKET u, UDPSOCKET udpsock);
int listen(const SRTSOCKET u, int backlog);
SRTSOCKET accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen);
int connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn);
int close(const SRTSOCKET u);
int getpeername(const SRTSOCKET u, sockaddr* name, int* namelen);
int getsockname(const SRTSOCKET u, sockaddr* name, int* namelen);
int select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout);
int selectEx(const std::vector<SRTSOCKET>& fds, std::vector<SRTSOCKET>* readfds, std::vector<SRTSOCKET>* writefds, std::vector<SRTSOCKET>* exceptfds, int64_t msTimeOut);
int epoll_create();
int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_remove_usock(const int eid, const SRTSOCKET u);
int epoll_remove_ssock(const int eid, const SYSSOCKET s);
int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds = NULL, std::set<SYSSOCKET>* lwfds = NULL);
int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
int32_t epoll_set(const int eid, int32_t flags);
int epoll_release(const int eid);
/// record the UDT exception.
/// @param [in] e pointer to a UDT exception instance.
void setError(CUDTException* e);
/// look up the most recent UDT exception.
/// @return pointer to a UDT exception instance.
CUDTException* getError();
private:
// void init();
private:
std::map<SRTSOCKET, CUDTSocket*> m_Sockets; // stores all the socket structures
pthread_mutex_t m_ControlLock; // used to synchronize UDT API
pthread_mutex_t m_IDLock; // used to synchronize ID generation
SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID
std::map<int64_t, std::set<SRTSOCKET> > m_PeerRec;// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn
private:
pthread_key_t m_TLSError; // thread local error record (last error)
static void TLSDestroy(void* e) {if (NULL != e) delete (CUDTException*)e;}
private:
CUDTSocket* locate(const SRTSOCKET u);
CUDTSocket* locate(const sockaddr* peer, const SRTSOCKET id, int32_t isn);
void updateMux(CUDTSocket* s, const sockaddr* addr = NULL, const UDPSOCKET* = NULL);
void updateListenerMux(CUDTSocket* s, const CUDTSocket* ls);
private:
std::map<int, CMultiplexer> m_mMultiplexer; // UDP multiplexer
pthread_mutex_t m_MultiplexerLock;
private:
CCache<CInfoBlock>* m_pCache; // UDT network information cache
private:
volatile bool m_bClosing;
pthread_mutex_t m_GCStopLock;
pthread_cond_t m_GCStopCond;
pthread_mutex_t m_InitLock;
int m_iInstanceCount; // number of startup() called by application
bool m_bGCStatus; // if the GC thread is working (true)
pthread_t m_GCThread;
static void* garbageCollect(void*);
std::map<SRTSOCKET, CUDTSocket*> m_ClosedSockets; // temporarily store closed sockets
void checkBrokenSockets();
void removeSocket(const SRTSOCKET u);
CEPoll m_EPoll; // handling epoll data structures and events
private:
CUDTUnited(const CUDTUnited&);
CUDTUnited& operator=(const CUDTUnited&);
};
// Debug support
inline std::string SockaddrToString(const sockaddr* sadr)
{
void* addr =
sadr->sa_family == AF_INET ?
(void*)&((sockaddr_in*)sadr)->sin_addr
: sadr->sa_family == AF_INET6 ?
(void*)&((sockaddr_in6*)sadr)->sin6_addr
: 0;
// (cast to (void*) is required because otherwise the 2-3 arguments
// of ?: operator would have different types, which isn't allowed in C++.
if ( !addr )
return "unknown:0";
std::ostringstream output;
char hostbuf[1024];
int flags;
#if ENABLE_GETNAMEINFO
flags = NI_NAMEREQD;
#else
flags = NI_NUMERICHOST | NI_NUMERICSERV;
#endif
if (!getnameinfo(sadr, sizeof(*sadr), hostbuf, 1024, NULL, 0, flags))
enum ErrorHandling
{
output << hostbuf;
ERH_RETURN,
ERH_THROW,
ERH_ABORT
};
static std::string CONID(SRTSOCKET sock);
/// initialize the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int startup();
/// release the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int cleanup();
/// Create a new UDT socket.
/// @param [out] pps Variable (optional) to which the new socket will be written, if succeeded
/// @return The new UDT socket ID, or INVALID_SOCK.
SRTSOCKET newSocket(CUDTSocket** pps = NULL);
/// Create (listener-side) a new socket associated with the incoming connection request.
/// @param [in] listen the listening socket ID.
/// @param [in] peer peer address.
/// @param [in,out] hs handshake information from peer side (in), negotiated value (out);
/// @param [out] w_error error code in case of failure.
/// @param [out] w_acpu reference to the existing associated socket if already exists.
/// @return 1: if the new connection was successfully created (accepted), @a w_acpu is NULL;
/// 0: the connection already exists (reference to the corresponding socket is returned in @a w_acpu).
/// -1: The connection processing failed due to memory alloation error, exceeding listener's backlog,
/// any error propagated from CUDT::open and CUDT::acceptAndRespond.
int newConnection(const SRTSOCKET listen,
const sockaddr_any& peer,
const CPacket& hspkt,
CHandShake& w_hs,
int& w_error,
CUDT*& w_acpu);
int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq);
int installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq);
/// Check the status of the UDT socket.
/// @param [in] u the UDT socket ID.
/// @return UDT socket status, or NONEXIST if not found.
SRT_SOCKSTATUS getStatus(const SRTSOCKET u);
// socket APIs
int bind(CUDTSocket* u, const sockaddr_any& name);
int bind(CUDTSocket* u, UDPSOCKET udpsock);
int listen(const SRTSOCKET u, int backlog);
SRTSOCKET accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen);
SRTSOCKET accept_bond(const SRTSOCKET listeners[], int lsize, int64_t msTimeOut);
int connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int tarlen);
int connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn);
int connectIn(CUDTSocket* s, const sockaddr_any& target, int32_t forced_isn);
#if ENABLE_BONDING
int groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize);
int singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target);
#endif
int close(const SRTSOCKET u);
int close(CUDTSocket* s);
void getpeername(const SRTSOCKET u, sockaddr* name, int* namelen);
void getsockname(const SRTSOCKET u, sockaddr* name, int* namelen);
int select(UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout);
int selectEx(const std::vector<SRTSOCKET>& fds,
std::vector<SRTSOCKET>* readfds,
std::vector<SRTSOCKET>* writefds,
std::vector<SRTSOCKET>* exceptfds,
int64_t msTimeOut);
int epoll_create();
int epoll_clear_usocks(int eid);
int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
int epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events);
int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_remove_usock(const int eid, const SRTSOCKET u);
template <class EntityType>
int epoll_remove_entity(const int eid, EntityType* ent);
int epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* ent);
#if ENABLE_BONDING
int epoll_remove_group_INTERNAL(const int eid, CUDTGroup* ent);
#endif
int epoll_remove_ssock(const int eid, const SYSSOCKET s);
int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
int32_t epoll_set(const int eid, int32_t flags);
int epoll_release(const int eid);
#if ENABLE_BONDING
// [[using locked(m_GlobControlLock)]]
CUDTGroup& addGroup(SRTSOCKET id, SRT_GROUP_TYPE type)
{
// This only ensures that the element exists.
// If the element was newly added, it will be NULL.
CUDTGroup*& g = m_Groups[id];
if (!g)
{
// This is a reference to the cell, so it will
// rewrite it into the map.
g = new CUDTGroup(type);
}
// Now we are sure that g is not NULL,
// and persistence of this object is in the map.
// The reference to the object can be safely returned here.
return *g;
}
output << ":" << ntohs(((sockaddr_in*)sadr)->sin_port); // TRICK: sin_port and sin6_port have the same offset and size
return output.str();
}
void deleteGroup(CUDTGroup* g);
void deleteGroup_LOCKED(CUDTGroup* g);
// [[using locked(m_GlobControlLock)]]
CUDTGroup* findPeerGroup_LOCKED(SRTSOCKET peergroup)
{
for (groups_t::iterator i = m_Groups.begin(); i != m_Groups.end(); ++i)
{
if (i->second->peerid() == peergroup)
return i->second;
}
return NULL;
}
#endif
CEPoll& epoll_ref() { return m_EPoll; }
private:
/// Generates a new socket ID. This function starts from a randomly
/// generated value (at initialization time) and goes backward with
/// with next calls. The possible values come from the range without
/// the SRTGROUP_MASK bit, and the group bit is set when the ID is
/// generated for groups. It is also internally checked if the
/// newly generated ID isn't already used by an existing socket or group.
///
/// Socket ID value range.
/// - [0]: reserved for handshake procedure. If the destination Socket ID is 0
/// (destination Socket ID unknown) the packet will be sent to the listening socket
/// or to a socket that is in the rendezvous connection phase.
/// - [1; 2 ^ 30): single socket ID range.
/// - (2 ^ 30; 2 ^ 31): group socket ID range. Effectively any positive number
/// from [1; 2 ^ 30) with bit 30 set to 1. Bit 31 is zero.
/// The most significant bit 31 (sign bit) is left unused so that checking for a value <= 0 identifies an invalid
/// socket ID.
///
/// @param group The socket id should be for socket group.
/// @return The new socket ID.
/// @throw CUDTException if after rolling over all possible ID values nothing can be returned
SRTSOCKET generateSocketID(bool group = false);
private:
typedef std::map<SRTSOCKET, CUDTSocket*> sockets_t; // stores all the socket structures
sockets_t m_Sockets;
#if ENABLE_BONDING
typedef std::map<SRTSOCKET, CUDTGroup*> groups_t;
groups_t m_Groups;
#endif
sync::Mutex m_GlobControlLock; // used to synchronize UDT API
sync::Mutex m_IDLock; // used to synchronize ID generation
SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID
SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one
std::map<int64_t, std::set<SRTSOCKET> >
m_PeerRec; // record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn
private:
friend struct FLookupSocketWithEvent_LOCKED;
CUDTSocket* locateSocket(SRTSOCKET u, ErrorHandling erh = ERH_RETURN);
// This function does the same as locateSocket, except that:
// - lock on m_GlobControlLock is expected (so that you don't unlock between finding and using)
// - only return NULL if not found
CUDTSocket* locateSocket_LOCKED(SRTSOCKET u);
CUDTSocket* locatePeer(const sockaddr_any& peer, const SRTSOCKET id, int32_t isn);
#if ENABLE_BONDING
CUDTGroup* locateAcquireGroup(SRTSOCKET u, ErrorHandling erh = ERH_RETURN);
CUDTGroup* acquireSocketsGroup(CUDTSocket* s);
struct GroupKeeper
{
CUDTGroup* group;
// This is intended for API functions to lock the group's existence
// for the lifetime of their call.
GroupKeeper(CUDTUnited& glob, SRTSOCKET id, ErrorHandling erh) { group = glob.locateAcquireGroup(id, erh); }
// This is intended for TSBPD thread that should lock the group's
// existence until it exits.
GroupKeeper(CUDTUnited& glob, CUDTSocket* s) { group = glob.acquireSocketsGroup(s); }
~GroupKeeper()
{
if (group)
{
// We have a guarantee that if `group` was set
// as non-NULL here, it is also acquired and will not
// be deleted until this busy flag is set back to false.
sync::ScopedLock cgroup(*group->exp_groupLock());
group->apiRelease();
// Only now that the group lock is lifted, can the
// group be now deleted and this pointer potentially dangling
}
}
};
#endif
void updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* = NULL);
bool updateListenerMux(CUDTSocket* s, const CUDTSocket* ls);
// Utility functions for updateMux
void configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af);
uint16_t installMuxer(CUDTSocket* w_s, CMultiplexer& sm);
/// @brief Checks if channel configuration matches the socket configuration.
/// @param cfgMuxer multiplexer configuration.
/// @param cfgSocket socket configuration.
/// @return tru if configurations match, false otherwise.
static bool channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket);
private:
std::map<int, CMultiplexer> m_mMultiplexer; // UDP multiplexer
sync::Mutex m_MultiplexerLock;
private:
CCache<CInfoBlock>* m_pCache; // UDT network information cache
private:
srt::sync::atomic<bool> m_bClosing;
sync::Mutex m_GCStopLock;
sync::Condition m_GCStopCond;
sync::Mutex m_InitLock;
int m_iInstanceCount; // number of startup() called by application
bool m_bGCStatus; // if the GC thread is working (true)
sync::CThread m_GCThread;
static void* garbageCollect(void*);
sockets_t m_ClosedSockets; // temporarily store closed sockets
#if ENABLE_BONDING
groups_t m_ClosedGroups;
#endif
void checkBrokenSockets();
void removeSocket(const SRTSOCKET u);
CEPoll m_EPoll; // handling epoll data structures and events
private:
CUDTUnited(const CUDTUnited&);
CUDTUnited& operator=(const CUDTUnited&);
};
} // namespace srt
#endif

View file

@ -0,0 +1,311 @@
//----------------------------------------------------------------------------
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or distribute
// this software, either in source code form or as a compiled binary, for any
// purpose, commercial or non-commercial, and by any means.
//
// In jurisdictions that recognize copyright laws, the author or authors of
// this software dedicate any and all copyright interest in the software to the
// public domain. We make this dedication for the benefit of the public at
// large and to the detriment of our heirs and successors. We intend this
// dedication to be an overt act of relinquishment in perpetuity of all present
// and future rights to this software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org/>
//-----------------------------------------------------------------------------
// SRT Project information:
// This file was adopted from a Public Domain project from
// https://github.com/mbitsnbites/atomic
// Only namespaces were changed to adopt it for SRT project.
#ifndef SRT_SYNC_ATOMIC_H_
#define SRT_SYNC_ATOMIC_H_
// Macro for disallowing copying of an object.
#if __cplusplus >= 201103L
#define ATOMIC_DISALLOW_COPY(T) \
T(const T&) = delete; \
T& operator=(const T&) = delete;
#else
#define ATOMIC_DISALLOW_COPY(T) \
T(const T&); \
T& operator=(const T&);
#endif
// A portable static assert.
#if __cplusplus >= 201103L
#define ATOMIC_STATIC_ASSERT(condition, message) \
static_assert((condition), message)
#else
// Based on: http://stackoverflow.com/a/809465/5778708
#define ATOMIC_STATIC_ASSERT(condition, message) \
_impl_STATIC_ASSERT_LINE(condition, __LINE__)
#define _impl_PASTE(a, b) a##b
#ifdef __GNUC__
#define _impl_UNUSED __attribute__((__unused__))
#else
#define _impl_UNUSED
#endif
#define _impl_STATIC_ASSERT_LINE(condition, line) \
typedef char _impl_PASTE( \
STATIC_ASSERT_failed_, \
line)[(2 * static_cast<int>(!!(condition))) - 1] _impl_UNUSED
#endif
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
// NOTE: Defined at the top level.
#elif __cplusplus >= 201103L
// NOTE: Prefer to use the c++11 std::atomic.
#define ATOMIC_USE_CPP11_ATOMIC
#elif (defined(__clang__) && defined(__clang_major__) && (__clang_major__ > 5)) \
|| defined(__xlc__)
// NOTE: Clang <6 does not support GCC __atomic_* intrinsics. I am unsure
// about Clang6. Since Clang sets __GNUC__ and __GNUC_MINOR__ of this era
// to <4.5, older Clang will catch the setting below to use the
// POSIX Mutex Implementation.
#define ATOMIC_USE_GCC_INTRINSICS
#elif defined(__GNUC__) \
&& ( (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) )
// NOTE: The __atomic_* family of intrisics were introduced in GCC-4.7.0.
// NOTE: This follows #if defined(__clang__), because most if, not all,
// versions of Clang define __GNUC__ and __GNUC_MINOR__ but often define
// them to 4.4 or an even earlier version. Most of the newish versions
// of Clang also support GCC Atomic Intrisics even if they set GCC version
// macros to <4.7.
#define ATOMIC_USE_GCC_INTRINSICS
#elif defined(__GNUC__) && !defined(ATOMIC_USE_SRT_SYNC_MUTEX)
// NOTE: GCC compiler built-ins for atomic operations are pure
// compiler extensions prior to GCC-4.7 and were grouped into the
// the __sync_* family of functions. GCC-4.7, both the c++11 and C11
// standards had been finalized, and GCC updated their built-ins to
// better reflect the new memory model and the new functions grouped
// into the __atomic_* family. Also the memory models were defined
// differently, than in pre 4.7.
// TODO: PORT to the pre GCC-4.7 __sync_* intrinsics. In the meantime use
// the POSIX Mutex Implementation.
#define ATOMIC_USE_SRT_SYNC_MUTEX 1
#elif defined(_MSC_VER)
#define ATOMIC_USE_MSVC_INTRINSICS
#include "atomic_msvc.h"
#else
#error Unsupported compiler / system.
#endif
// Include any necessary headers for the selected Atomic Implementation.
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
#include "sync.h"
#endif
#if defined(ATOMIC_USE_CPP11_ATOMIC)
#include <atomic>
#endif
namespace srt {
namespace sync {
template <typename T>
class atomic {
public:
ATOMIC_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 ||
sizeof(T) == 8,
"Only types of size 1, 2, 4 or 8 are supported");
atomic()
: value_(static_cast<T>(0))
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
, mutex_()
#endif
{
// No-Op
}
explicit atomic(const T value)
: value_(value)
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
, mutex_()
#endif
{
// No-Op
}
~atomic()
{
// No-Op
}
/// @brief Performs an atomic increment operation (value + 1).
/// @returns The new value of the atomic object.
T operator++() {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
const T t = ++value_;
return t;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
return msvc::interlocked<T>::increment(&value_);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
return ++value_;
#else
#error "Implement Me."
#endif
}
/// @brief Performs an atomic decrement operation (value - 1).
/// @returns The new value of the atomic object.
T operator--() {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
const T t = --value_;
return t;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
return msvc::interlocked<T>::decrement(&value_);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
return --value_;
#else
#error "Implement Me."
#endif
}
/// @brief Performs an atomic compare-and-swap (CAS) operation.
///
/// The value of the atomic object is only updated to the new value if the
/// old value of the atomic object matches @c expected_val.
///
/// @param expected_val The expected value of the atomic object.
/// @param new_val The new value to write to the atomic object.
/// @returns True if new_value was written to the atomic object.
bool compare_exchange(const T expected_val, const T new_val) {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
bool result = false;
if (expected_val == value_)
{
value_ = new_val;
result = true;
}
return result;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
T e = expected_val;
return __atomic_compare_exchange_n(
&value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
const T old_val =
msvc::interlocked<T>::compare_exchange(&value_, new_val, expected_val);
return (old_val == expected_val);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
T e = expected_val;
return value_.compare_exchange_weak(e, new_val);
#else
#error "Implement Me."
#endif
}
/// @brief Performs an atomic set operation.
///
/// The value of the atomic object is unconditionally updated to the new
/// value.
///
/// @param new_val The new value to write to the atomic object.
void store(const T new_val) {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
value_ = new_val;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
__atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
(void)msvc::interlocked<T>::exchange(&value_, new_val);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
value_.store(new_val);
#else
#error "Implement Me."
#endif
}
/// @returns the current value of the atomic object.
/// @note Be careful about how this is used, since any operations on the
/// returned value are inherently non-atomic.
T load() const {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
const T t = value_;
return t;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
return __atomic_load_n(&value_, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
// TODO(m): Is there a better solution for MSVC?
return value_;
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
return value_;
#else
#error "Implement Me."
#endif
}
/// @brief Performs an atomic exchange operation.
///
/// The value of the atomic object is unconditionally updated to the new
/// value, and the old value is returned.
///
/// @param new_val The new value to write to the atomic object.
/// @returns the old value.
T exchange(const T new_val) {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
const T t = value_;
value_ = new_val;
return t;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
return msvc::interlocked<T>::exchange(&value_, new_val);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
return value_.exchange(new_val);
#else
#error "Implement Me."
#endif
}
T operator=(const T new_value) {
store(new_value);
return new_value;
}
operator T() const {
return load();
}
private:
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
T value_;
mutable Mutex mutex_;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
volatile T value_;
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
volatile T value_;
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
std::atomic<T> value_;
#else
#error "Implement Me. (value_ type)"
#endif
ATOMIC_DISALLOW_COPY(atomic)
};
} // namespace sync
} // namespace srt
// Undef temporary defines.
#undef ATOMIC_USE_GCC_INTRINSICS
#undef ATOMIC_USE_MSVC_INTRINSICS
#undef ATOMIC_USE_CPP11_ATOMIC
#endif // ATOMIC_ATOMIC_H_

View file

@ -0,0 +1,91 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
#ifndef INC_SRT_SYNC_ATOMIC_CLOCK_H
#define INC_SRT_SYNC_ATOMIC_CLOCK_H
#include "sync.h"
#include "atomic.h"
namespace srt
{
namespace sync
{
template <class Clock>
class AtomicDuration
{
atomic<int64_t> dur;
typedef typename Clock::duration duration_type;
typedef typename Clock::time_point time_point_type;
public:
AtomicDuration() ATR_NOEXCEPT : dur(0) {}
duration_type load()
{
int64_t val = dur.load();
return duration_type(val);
}
void store(const duration_type& d)
{
dur.store(d.count());
}
AtomicDuration<Clock>& operator=(const duration_type& s)
{
dur = s.count();
return *this;
}
operator duration_type() const
{
return duration_type(dur);
}
};
template <class Clock>
class AtomicClock
{
atomic<uint64_t> dur;
typedef typename Clock::duration duration_type;
typedef typename Clock::time_point time_point_type;
public:
AtomicClock() ATR_NOEXCEPT : dur(0) {}
time_point_type load() const
{
int64_t val = dur.load();
return time_point_type(duration_type(val));
}
void store(const time_point_type& d)
{
dur.store(uint64_t(d.time_since_epoch().count()));
}
AtomicClock& operator=(const time_point_type& s)
{
dur = s.time_since_epoch().count();
return *this;
}
operator time_point_type() const
{
return time_point_type(duration_type(dur.load()));
}
};
} // namespace sync
} // namespace srt
#endif // INC_SRT_SYNC_ATOMIC_CLOCK_H

View file

@ -0,0 +1,245 @@
//-----------------------------------------------------------------------------
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or distribute
// this software, either in source code form or as a compiled binary, for any
// purpose, commercial or non-commercial, and by any means.
//
// In jurisdictions that recognize copyright laws, the author or authors of
// this software dedicate any and all copyright interest in the software to the
// public domain. We make this dedication for the benefit of the public at
// large and to the detriment of our heirs and successors. We intend this
// dedication to be an overt act of relinquishment in perpetuity of all present
// and future rights to this software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org/>
//-----------------------------------------------------------------------------
// SRT Project information:
// This file was adopted from a Public Domain project from
// https://github.com/mbitsnbites/atomic
// Only namespaces were changed to adopt it for SRT project.
#ifndef SRT_SYNC_ATOMIC_MSVC_H_
#define SRT_SYNC_ATOMIC_MSVC_H_
// Define which functions we need (don't include <intrin.h>).
extern "C" {
short _InterlockedIncrement16(short volatile*);
long _InterlockedIncrement(long volatile*);
__int64 _InterlockedIncrement64(__int64 volatile*);
short _InterlockedDecrement16(short volatile*);
long _InterlockedDecrement(long volatile*);
__int64 _InterlockedDecrement64(__int64 volatile*);
char _InterlockedExchange8(char volatile*, char);
short _InterlockedExchange16(short volatile*, short);
long __cdecl _InterlockedExchange(long volatile*, long);
__int64 _InterlockedExchange64(__int64 volatile*, __int64);
char _InterlockedCompareExchange8(char volatile*, char, char);
short _InterlockedCompareExchange16(short volatile*, short, short);
long __cdecl _InterlockedCompareExchange(long volatile*, long, long);
__int64 _InterlockedCompareExchange64(__int64 volatile*, __int64, __int64);
};
// Define which functions we want to use as inline intriniscs.
#pragma intrinsic(_InterlockedIncrement)
#pragma intrinsic(_InterlockedIncrement16)
#pragma intrinsic(_InterlockedDecrement)
#pragma intrinsic(_InterlockedDecrement16)
#pragma intrinsic(_InterlockedCompareExchange)
#pragma intrinsic(_InterlockedCompareExchange8)
#pragma intrinsic(_InterlockedCompareExchange16)
#pragma intrinsic(_InterlockedExchange)
#pragma intrinsic(_InterlockedExchange8)
#pragma intrinsic(_InterlockedExchange16)
#if defined(_M_X64)
#pragma intrinsic(_InterlockedIncrement64)
#pragma intrinsic(_InterlockedDecrement64)
#pragma intrinsic(_InterlockedCompareExchange64)
#pragma intrinsic(_InterlockedExchange64)
#endif // _M_X64
namespace srt {
namespace sync {
namespace msvc {
template <typename T, size_t N = sizeof(T)>
struct interlocked {
};
template <typename T>
struct interlocked<T, 1> {
static inline T increment(T volatile* x) {
// There's no _InterlockedIncrement8().
char old_val, new_val;
do {
old_val = static_cast<char>(*x);
new_val = old_val + static_cast<char>(1);
} while (_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
new_val,
old_val) != old_val);
return static_cast<T>(new_val);
}
static inline T decrement(T volatile* x) {
// There's no _InterlockedDecrement8().
char old_val, new_val;
do {
old_val = static_cast<char>(*x);
new_val = old_val - static_cast<char>(1);
} while (_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
new_val,
old_val) != old_val);
return static_cast<T>(new_val);
}
static inline T compare_exchange(T volatile* x,
const T new_val,
const T expected_val) {
return static_cast<T>(
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
static_cast<const char>(new_val),
static_cast<const char>(expected_val)));
}
static inline T exchange(T volatile* x, const T new_val) {
return static_cast<T>(_InterlockedExchange8(
reinterpret_cast<volatile char*>(x), static_cast<const char>(new_val)));
}
};
template <typename T>
struct interlocked<T, 2> {
static inline T increment(T volatile* x) {
return static_cast<T>(
_InterlockedIncrement16(reinterpret_cast<volatile short*>(x)));
}
static inline T decrement(T volatile* x) {
return static_cast<T>(
_InterlockedDecrement16(reinterpret_cast<volatile short*>(x)));
}
static inline T compare_exchange(T volatile* x,
const T new_val,
const T expected_val) {
return static_cast<T>(
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(x),
static_cast<const short>(new_val),
static_cast<const short>(expected_val)));
}
static inline T exchange(T volatile* x, const T new_val) {
return static_cast<T>(
_InterlockedExchange16(reinterpret_cast<volatile short*>(x),
static_cast<const short>(new_val)));
}
};
template <typename T>
struct interlocked<T, 4> {
static inline T increment(T volatile* x) {
return static_cast<T>(
_InterlockedIncrement(reinterpret_cast<volatile long*>(x)));
}
static inline T decrement(T volatile* x) {
return static_cast<T>(
_InterlockedDecrement(reinterpret_cast<volatile long*>(x)));
}
static inline T compare_exchange(T volatile* x,
const T new_val,
const T expected_val) {
return static_cast<T>(
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(x),
static_cast<const long>(new_val),
static_cast<const long>(expected_val)));
}
static inline T exchange(T volatile* x, const T new_val) {
return static_cast<T>(_InterlockedExchange(
reinterpret_cast<volatile long*>(x), static_cast<const long>(new_val)));
}
};
template <typename T>
struct interlocked<T, 8> {
static inline T increment(T volatile* x) {
#if defined(_M_X64)
return static_cast<T>(
_InterlockedIncrement64(reinterpret_cast<volatile __int64*>(x)));
#else
// There's no _InterlockedIncrement64() for 32-bit x86.
__int64 old_val, new_val;
do {
old_val = static_cast<__int64>(*x);
new_val = old_val + static_cast<__int64>(1);
} while (_InterlockedCompareExchange64(
reinterpret_cast<volatile __int64*>(x), new_val, old_val) !=
old_val);
return static_cast<T>(new_val);
#endif // _M_X64
}
static inline T decrement(T volatile* x) {
#if defined(_M_X64)
return static_cast<T>(
_InterlockedDecrement64(reinterpret_cast<volatile __int64*>(x)));
#else
// There's no _InterlockedDecrement64() for 32-bit x86.
__int64 old_val, new_val;
do {
old_val = static_cast<__int64>(*x);
new_val = old_val - static_cast<__int64>(1);
} while (_InterlockedCompareExchange64(
reinterpret_cast<volatile __int64*>(x), new_val, old_val) !=
old_val);
return static_cast<T>(new_val);
#endif // _M_X64
}
static inline T compare_exchange(T volatile* x,
const T new_val,
const T expected_val) {
return static_cast<T>(_InterlockedCompareExchange64(
reinterpret_cast<volatile __int64*>(x),
static_cast<const __int64>(new_val),
static_cast<const __int64>(expected_val)));
}
static inline T exchange(T volatile* x, const T new_val) {
#if defined(_M_X64)
return static_cast<T>(
_InterlockedExchange64(reinterpret_cast<volatile __int64*>(x),
static_cast<const __int64>(new_val)));
#else
// There's no _InterlockedExchange64 for 32-bit x86.
__int64 old_val;
do {
old_val = static_cast<__int64>(*x);
} while (_InterlockedCompareExchange64(
reinterpret_cast<volatile __int64*>(x), new_val, old_val) !=
old_val);
return static_cast<T>(old_val);
#endif // _M_X64
}
};
} // namespace msvc
} // namespace sync
} // namespace srt
#endif // ATOMIC_ATOMIC_MSVC_H_

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
/*
* 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/.
*
*
*/
/*****************************************************************************
@ -50,461 +50,562 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_BUFFER_H__
#define __UDT_BUFFER_H__
#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"
#include <fstream>
class CSndBuffer
// 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
{
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 ""; }
CSndBuffer(int size = 32, int mss = 1500);
~CSndBuffer();
typedef sync::steady_clock::time_point time_point;
public:
AvgBufSize()
: m_dBytesCountMAvg(0.0)
, m_dCountMAvg(0.0)
, m_dTimespanMAvg(0.0)
{
}
/// Insert a user buffer into the sending list.
/// @param [in] data pointer to the user data block.
/// @param [in] len size of the block.
/// @param [in] ttl time to live in milliseconds
/// @param [in] order if the block should be delivered in order, for DGRAM only
void addBuffer(const char* data, int len, int ttl, bool order, uint64_t srctime, ref_t<int32_t> r_msgno);
/// 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.
int addBufferFromFile(std::fstream& ifs, int len);
/// Find data position to pack a DATA packet from the furthest reading point.
/// @param [out] data the pointer to the data position.
/// @param [out] msgno message number of the packet.
/// @param [out] origintime origin time stamp of the message
/// @param [in] kflags Odd|Even crypto key flag
/// @return Actual length of data read.
int readData(char** data, int32_t& msgno, uint64_t& origintime, int kflgs);
/// Find data position to pack a DATA packet for a retransmission.
/// @param [out] data the pointer to the data position.
/// @param [in] offset offset from the last ACK point.
/// @param [out] msgno message number of the packet.
/// @param [out] origintime origin time stamp of the message
/// @param [out] msglen length of the message
/// @return Actual length of data read.
int readData(char** data, const int offset, int32_t& msgno, uint64_t& origintime, int& msglen);
/// Update the ACK point and may release/unmap/return the user data according to the flag.
/// @param [in] offset number of packets acknowledged.
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;
int dropLateData(int &bytes, uint64_t latetime);
#ifdef SRT_ENABLE_SNDBUFSZ_MAVG
void updAvgBufSize(uint64_t time);
int getAvgBufSize(ref_t<int> bytes, ref_t<int> timespan);
#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */
int getCurrBufSize(ref_t<int> bytes, ref_t<int> timespan);
uint64_t getInRatePeriod() const { return m_InRatePeriod; }
/// Retrieve input bitrate in bytes per second
int getInputRate() const { return m_iInRateBps; }
/// 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(uint64_t time, int pkts = 0, int bytes = 0);
void resetInputRateSmpPeriod(bool disable = false)
{
setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US);
}
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;
};
void increase();
void setInputRateSmpPeriod(int period);
/// 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();
private: // Constants
public:
uint64_t getInRatePeriod() const { return m_InRatePeriod; }
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
/// 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:
pthread_mutex_t m_BufLock; // used to synchronize buffer operation
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
};
struct Block
{
char* m_pcData; // pointer to the data block
int m_iLength; // length of the block
class CSndBuffer
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
int32_t m_iMsgNoBitset; // message number
uint64_t m_ullOriginTime_us; // original request time
uint64_t m_ullSourceTime_us;
int m_iTTL; // time to live (milliseconds)
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 ""; }
Block* m_pNext; // next block
/// @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();
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;
}
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);
} *m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock;
/// 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);
// 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)
/// 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);
struct Buffer
{
char* m_pcData; // buffer
int m_iSize; // size
Buffer* m_pNext; // next buffer
} *m_pBuffer; // physical buffer
/// 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;
int32_t m_iNextMsgNo; // next message number
/// 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);
int m_iSize; // buffer size (number of packets)
int m_iMSS; // maximum seqment/packet size
/// 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);
int m_iCount; // number of used blocks
/// 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);
int m_iBytesCount; // number of payload bytes in queue
uint64_t m_ullLastOriginTime_us;
void ackData(int offset);
#ifdef SRT_ENABLE_SNDBUFSZ_MAVG
uint64_t m_LastSamplingTime;
int m_iCountMAvg;
int m_iBytesCountMAvg;
int m_TimespanMAvg;
#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */
/// Read size of data still in the sending list.
/// @return Current size of the data in the sending list.
int getCurrBufSize() const;
int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime
int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime
uint64_t m_InRateStartTime;
uint64_t m_InRatePeriod; // usec
int m_iInRateBps; // Input Rate in Bytes/sec
int m_iAvgPayloadSz; // Average packet payload size
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:
CSndBuffer(const CSndBuffer&);
CSndBuffer& operator=(const CSndBuffer&);
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
{
public:
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 ""; }
/// 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 = 65536);
~CRcvBuffer();
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);
/// 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.
/// 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);
int addData(CUnit* unit, int offset);
/// 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);
/// 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.
/// 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);
int readBuffer(char* data, 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;
/// 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.
/// 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;
int readBufferToFile(std::fstream& ofs, int len);
/// 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);
/// 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.
/// 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);
void ackData(int len);
/// 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 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.
/// Query the received average payload size.
/// @return size (bytes) of payload size
unsigned getRcvAvgPayloadSize() const;
int getAvailBufSize() const;
struct ReadingState
{
time_point tsStart;
time_point tsLastAck;
time_point tsEnd;
int iNumAcknowledged;
int iNumUnacknowledged;
};
/// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now).
/// @return size of valid (continous) data for reading.
ReadingState debugGetReadingState() const;
int getRcvDataSize() 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;
/// Query how many data was received and acknowledged.
/// @param [out] bytes bytes
/// @param [out] spantime spantime
/// @return size in pkts of acked data.
/// 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);
int getRcvDataSize(int &bytes, int &spantime);
#if SRT_ENABLE_RCVBUFSZ_MAVG
/// 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(uint64_t now);
#endif /* SRT_ENABLE_RCVBUFSZ_MAVG */
/// Query the received average payload size.
/// @return size (bytes) of payload size
int getRcvAvgPayloadSize() 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);
/// 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, ref_t<SRT_MSGCTRL> mctrl);
/// 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(ref_t<uint64_t> tsbpdtime, ref_t<int32_t> curpktseq);
bool isRcvDataReady();
bool isRcvDataAvailable()
{
return m_iLastAckPos != m_iStartPos;
}
CPacket* getRcvReadyPacket();
/// 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
/// @return 0
int setRcvTsbPdMode(uint64_t timebase, uint32_t delay);
/// Add packet timestamp for drift caclculation and compensation
/// @param [in] timestamp packet time stamp
/// @param [ref] lock Mutex that should be locked for the operation
void addRcvTsbPdDriftSample(uint32_t timestamp, pthread_mutex_t& lock);
#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] 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.
/// @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, no missing packet but 1st not ready to play.
bool getRcvFirstMsg(ref_t<uint64_t> tsbpdtime, ref_t<bool> passack, ref_t<int32_t> skipseqno, ref_t<int32_t> curpktseq);
/// Update the ACK point of the buffer.
/// @param [in] len size of data to be skip & acknowledged.
void skipData(int len);
/// 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 reportBufferStats(); // Heavy logging Debug only
void readMsgHeavyLogging(int p);
#endif
private:
/// 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
/// @retval true 1st packet ready to play without discontinuity (no hole)
/// @retval false tsbpdtime = 0: no packet ready to play
/// 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);
bool getRcvReadyMsg(ref_t<uint64_t> tsbpdtime, ref_t<int32_t> curpktseq);
public:
// (This is exposed as used publicly in logs)
/// Get packet delivery local time base (adjusted for wrap around)
/// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min
/// @return local delivery time (usec)
uint64_t getTsbPdTimeBase(uint32_t timestamp_us);
/// Get packet local delivery time
/// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min
/// @return local delivery time (usec)
public:
uint64_t getPktTsbPdTime(uint32_t timestamp);
int debugGetSize() const;
bool empty() const;
// Required by PacketFilter facility to use as a storage
// for provided packets
CUnitQueue* getUnitQueue()
{
return m_pUnitQueue;
}
private:
/// 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(ref_t<int> start, ref_t<int> end, ref_t<bool> passack);
private:
CUnit** m_pUnit; // pointer to the protocol buffer (array of CUnit* items)
const int m_iSize; // size of the array of CUnit* items
CUnitQueue* m_pUnitQueue; // the shared unit queue
int m_iStartPos; // the head position for I/O (inclusive)
int m_iLastAckPos; // the last ACKed position (exclusive)
// EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1
int m_iMaxPos; // the furthest data position
int m_iNotch; // the starting read point of the first unit
pthread_mutex_t 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
int m_iAvgPayloadSz; // Average payload size for dropped bytes estimation
bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode
uint32_t m_uTsbPdDelay; // aggreed delay
uint64_t m_ullTsbPdTimeBase; // localtime base for TsbPd mode
// Note: m_ullTsbPdTimeBase cumulates values from:
// 1. Initial SRT_CMD_HSREQ packet returned value diff to current time:
// == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception
// 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected
// += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8).
// 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively
// from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE
// once the value of average drift exceeds this value in whatever direction.
// += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE
//
// XXX Application-supplied timestamps won't work therefore. This requires separate
// calculation of all these things above.
bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around
static const uint32_t TSBPD_WRAP_PERIOD = (30*1000000); //30 seconds (in usec)
static const int TSBPD_DRIFT_MAX_VALUE = 5000; // Max drift (usec) above which TsbPD Time Offset is adjusted
static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; // Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation
//int m_iTsbPdDrift; // recent drift in the packet time stamp
//int64_t m_TsbPdDriftSum; // Sum of sampled drift
//int m_iTsbPdDriftNbSamples; // Number of samples in sum and histogram
DriftTracer<TSBPD_DRIFT_MAX_SAMPLES, TSBPD_DRIFT_MAX_VALUE> m_DriftTracer;
#ifdef SRT_ENABLE_RCVBUFSZ_MAVG
uint64_t m_LastSamplingTime;
int m_TimespanMAvg;
int m_iCountMAvg;
int m_iBytesCountMAvg;
#endif /* SRT_ENABLE_RCVBUFSZ_MAVG */
#ifdef SRT_DEBUG_TSBPD_DRIFT
int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment)
int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment)
static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram
#endif /* SRT_DEBUG_TSBPD_DRIFT */
/// 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
unsigned long m_ulPdHisto[4][10];
#endif /* 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:
CRcvBuffer();
CRcvBuffer(const CRcvBuffer&);
CRcvBuffer& operator=(const CRcvBuffer&);
/// 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,367 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2020 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/.
*
*/
#ifndef INC_SRT_BUFFER_RCV_H
#define INC_SRT_BUFFER_RCV_H
#if ENABLE_NEW_RCVBUFFER
#include "buffer.h" // AvgBufSize
#include "common.h"
#include "queue.h"
#include "sync.h"
#include "tsbpd_time.h"
namespace srt
{
/*
* Circular receiver buffer.
*
* |<------------------- m_szSize ---------------------------->|
* | |<------------ m_iMaxPosInc ----------->| |
* | | | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+
* | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[]
* +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+
* | |
* | \__last pkt received
* |
* \___ m_iStartPos: first message to read
*
* m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?)
*
* thread safety:
* start_pos_: CUDT::m_RecvLock
* first_unack_pos_: CUDT::m_AckLock
* max_pos_inc_: none? (modified on add and ack
* first_nonread_pos_:
*/
class CRcvBufferNew
{
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);
~CRcvBufferNew();
public:
/// Insert a unit into the buffer.
/// Similar to CRcvBuffer::addData(CUnit* unit, int offset)
///
/// @param [in] unit pointer to a data unit containing new packet
/// @param [in] offset offset from last ACK point.
///
/// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo.
/// -3 if a packet is offset is ahead the buffer capacity.
// TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value.
int insert(CUnit* unit);
/// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno).
/// @param [in] seqno drop units up to this sequence number
/// @return number of dropped packets.
int dropUpTo(int32_t seqno);
/// @brief Drop all the packets in the receiver buffer.
/// The starting position and seqno are shifted right after the last packet in the buffer.
/// @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.
/// @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)
/// @return the number of packets actually dropped.
int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno);
/// Read the whole message from one or several packets.
///
/// @param [in,out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @param [in,out] message control data
///
/// @return actual number of bytes extracted from the buffer.
/// 0 if nothing to read.
/// -1 on failure.
int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL);
/// Read acknowledged data into a user buffer.
/// @param [in, out] dst pointer to the target user buffer.
/// @param [in] len length of user buffer.
/// @return size of data read. -1 on error.
int readBuffer(char* dst, int len);
/// Read acknowledged data directly into file.
/// @param [in] ofs C++ file stream.
/// @param [in] len expected length of data to write into the file.
/// @return size of data read. -1 on error.
int readBufferToFile(std::fstream& ofs, int len);
public:
/// Get the starting position of the buffer as a packet sequence number.
int getStartSeqNo() const { return m_iStartSeqNo; }
/// Sets the start seqno of the buffer.
/// Must be used with caution and only when the buffer is empty.
void setStartSeqNo(int seqno) { m_iStartSeqNo = seqno; }
/// Given the sequence number of the first unacknowledged packet
/// tells the size of the buffer available for packets.
/// Effective returns capacity of the buffer minus acknowledged packet still kept in it.
// TODO: Maybe does not need to return minus one slot now to distinguish full and empty buffer.
size_t getAvailSize(int iFirstUnackSeqNo) const
{
// Receiver buffer allows reading unacknowledged packets.
// Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo
// then it does not have acknowledged packets and its full capacity is available.
// Otherwise subtract the number of acknowledged but not yet read packets from its capacity.
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;
}
// Note: CSeqNo::seqlen(n, n) returns 1.
return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1;
}
/// @brief Checks if the buffer has packets available for reading regardless of the TSBPD.
/// @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.
int getRcvDataSize() const;
/// Get the number of packets, bytes and buffer timespan.
/// Differs from getRcvDataSize() that it counts all packets in the buffer, not only continious.
int getRcvDataSize(int& bytes, int& timespan) const;
struct PacketInfo
{
int seqno;
bool seq_gap; //< true if there are missing packets in the buffer, preceding current packet
time_point tsbpd_time;
};
/// Get information on the 1st message in queue.
/// Similar to CRcvBuffer::getRcvFirstMsg
/// 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.
/// @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, no missing packet but 1st not ready to play.
PacketInfo getFirstValidPacketInfo() const;
PacketInfo getFirstReadablePacketInfo(time_point time_now) const;
/// Get information on packets available to be read.
/// @returns a pair of sequence numbers (first available; first unavailable).
///
/// @note CSeqNo::seqoff(first, second) is 0 if nothing to read.
std::pair<int, int> getAvailablePacketsRange() const;
size_t countReadable() const;
bool empty() const
{
return (m_iMaxPosInc == 0);
}
/// Return buffer capacity.
/// One slot had to be empty in order to tell the difference between "empty buffer" and "full buffer".
/// E.g. m_iFirstNonreadPos would again point to m_iStartPos if m_szSize entries are added continiously.
/// TODO: Old receiver buffer capacity returns the actual size. Check for conflicts.
size_t capacity() const
{
return m_szSize - 1;
}
int64_t getDrift() const { return m_tsbpd.drift(); }
// TODO: make thread safe?
int debugGetSize() const
{
return getRcvDataSize();
}
/// Zero time to include all available packets.
/// TODO: Rename to 'canRead`.
bool isRcvDataReady(time_point time_now = time_point()) const;
int getRcvAvgDataSize(int& bytes, int& timespan);
void updRcvAvgDataSize(const time_point& now);
unsigned getRcvAvgPayloadSize() const { return m_uAvgPayloadSz; }
void getInternalTimeBase(time_point& w_timebase, bool& w_wrp, duration& w_udrift)
{
return m_tsbpd.getInternalTimeBase(w_timebase, w_wrp, w_udrift);
}
public: // Used for testing
/// Peek unit in position of seqno
const CUnit* peek(int32_t seqno);
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); }
private:
void countBytes(int pkts, int bytes);
void updateNonreadPos();
void releaseUnitInPos(int pos);
/// @brief Drop a unit from the buffer.
/// @param pos position in the m_entries of the unit to drop.
/// @return false if nothing to drop, true if the unit was dropped successfully.
bool dropUnitInPos(int pos);
/// Release entries following the current buffer position if they were already
/// read out of order (EntryState_Read) or dropped (EntryState_Drop).
void releaseNextFillerEntries();
bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); }
/// Find position of the last packet of the message.
int findLastMessagePkt();
/// Scan for availability of out of order packets.
void onInsertNotInOrderPacket(int insertpos);
// Check if m_iFirstReadableOutOfOrder is still readable.
bool checkFirstReadableOutOfOrder();
void updateFirstReadableOutOfOrder();
int scanNotInOrderMessageRight(int startPos, int msgNo) const;
int scanNotInOrderMessageLeft(int startPos, int msgNo) const;
typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg);
/// Read acknowledged data directly into file.
/// @param [in] ofs C++ file stream.
/// @param [in] len expected length of data to write into the file.
/// @return size of data read.
int readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg);
/// @brief Estimate timespan of the stored packets (acknowledged and unacknowledged).
/// @return timespan in milliseconds
int getTimespan_ms() const;
private:
// TODO: Call makeUnitGood upon assignment, and makeUnitFree upon clearing.
// TODO: CUnitPtr is not in use at the moment, but may be a smart pointer.
// class CUnitPtr
// {
// public:
// void operator=(CUnit* pUnit)
// {
// if (m_pUnit != NULL)
// {
// // m_pUnitQueue->makeUnitFree(m_entries[i].pUnit);
// }
// m_pUnit = pUnit;
// }
// private:
// CUnit* m_pUnit;
// };
enum EntryStatus
{
EntryState_Empty, //< No CUnit record.
EntryState_Avail, //< Entry is available for reading.
EntryState_Read, //< Entry has already been read (out of order).
EntryState_Drop //< Entry has been dropped.
};
struct Entry
{
Entry()
: pUnit(NULL)
, status(EntryState_Empty)
{}
CUnit* pUnit;
EntryStatus status;
};
//static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; }
FixedArray<Entry> m_entries;
const size_t m_szSize; // size of the array of units (buffer)
CUnitQueue* m_pUnitQueue; // the shared unit queue
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_iNotch; // the starting read point of the first unit
size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false
int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to
// read
bool m_bPeerRexmitFlag; // Needed to read message number correctly
const bool m_bMessageAPI; // Operation mode flag: message or stream.
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
///
/// @return 0
void setTsbPdMode(const time_point& timebase, bool wrap, duration delay);
void setPeerRexmitFlag(bool flag) { m_bPeerRexmitFlag = flag; }
void applyGroupTime(const time_point& timebase, bool wrp, uint32_t delay, const duration& udrift);
void applyGroupDrift(const time_point& timebase, bool wrp, const duration& udrift);
bool addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample);
time_point getPktTsbPdTime(uint32_t usPktTimestamp) const;
time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const;
void updateTsbPdTimeBase(uint32_t usPktTimestamp);
/// 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;
private:
CTsbpdTime m_tsbpd;
private: // Statistics
AvgBufSize m_mavg;
// TODO: m_BytesCountLock is probably not needed as the buffer has to be protected from simultaneous access.
mutable sync::Mutex m_BytesCountLock; // used to protect counters operations
int m_iBytesCount; // Number of payload bytes in the buffer
int m_iPktsCount; // Number of payload bytes in the buffer
unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation
};
} // namespace srt
#endif // ENABLE_NEW_RCVBUFFER
#endif // INC_SRT_BUFFER_RCV_H

View file

@ -38,10 +38,8 @@ written by
Yunhong Gu, last updated 05/05/2009
*****************************************************************************/
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include "platform_sys.h"
#include <cstring>
#include "cache.h"
@ -49,22 +47,22 @@ written by
using namespace std;
CInfoBlock& CInfoBlock::operator=(const CInfoBlock& obj)
srt::CInfoBlock& srt::CInfoBlock::copyFrom(const CInfoBlock& obj)
{
std::copy(obj.m_piIP, obj.m_piIP + 4, m_piIP);
m_iIPversion = obj.m_iIPversion;
m_ullTimeStamp = obj.m_ullTimeStamp;
m_iRTT = obj.m_iRTT;
m_iBandwidth = obj.m_iBandwidth;
m_iLossRate = obj.m_iLossRate;
m_iIPversion = obj.m_iIPversion;
m_ullTimeStamp = obj.m_ullTimeStamp;
m_iSRTT = obj.m_iSRTT;
m_iBandwidth = obj.m_iBandwidth;
m_iLossRate = obj.m_iLossRate;
m_iReorderDistance = obj.m_iReorderDistance;
m_dInterval = obj.m_dInterval;
m_dCWnd = obj.m_dCWnd;
m_dInterval = obj.m_dInterval;
m_dCWnd = obj.m_dCWnd;
return *this;
}
bool CInfoBlock::operator==(const CInfoBlock& obj)
bool srt::CInfoBlock::operator==(const CInfoBlock& obj)
{
if (m_iIPversion != obj.m_iIPversion)
return false;
@ -81,24 +79,24 @@ bool CInfoBlock::operator==(const CInfoBlock& obj)
return true;
}
CInfoBlock* CInfoBlock::clone()
srt::CInfoBlock* srt::CInfoBlock::clone()
{
CInfoBlock* obj = new CInfoBlock;
std::copy(m_piIP, m_piIP + 4, obj->m_piIP);
obj->m_iIPversion = m_iIPversion;
obj->m_ullTimeStamp = m_ullTimeStamp;
obj->m_iRTT = m_iRTT;
obj->m_iBandwidth = m_iBandwidth;
obj->m_iLossRate = m_iLossRate;
obj->m_iIPversion = m_iIPversion;
obj->m_ullTimeStamp = m_ullTimeStamp;
obj->m_iSRTT = m_iSRTT;
obj->m_iBandwidth = m_iBandwidth;
obj->m_iLossRate = m_iLossRate;
obj->m_iReorderDistance = m_iReorderDistance;
obj->m_dInterval = m_dInterval;
obj->m_dCWnd = m_dCWnd;
obj->m_dInterval = m_dInterval;
obj->m_dCWnd = m_dCWnd;
return obj;
}
int CInfoBlock::getKey()
int srt::CInfoBlock::getKey()
{
if (m_iIPversion == AF_INET)
return m_piIP[0];
@ -106,15 +104,15 @@ int CInfoBlock::getKey()
return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3];
}
void CInfoBlock::convert(const sockaddr* addr, int ver, uint32_t ip[])
void srt::CInfoBlock::convert(const sockaddr_any& addr, uint32_t aw_ip[4])
{
if (ver == AF_INET)
if (addr.family() == AF_INET)
{
ip[0] = ((sockaddr_in*)addr)->sin_addr.s_addr;
ip[1] = ip[2] = ip[3] = 0;
aw_ip[0] = addr.sin.sin_addr.s_addr;
aw_ip[1] = aw_ip[2] = aw_ip[3] = 0;
}
else
{
memcpy((char*)ip, (char*)((sockaddr_in6*)addr)->sin6_addr.s6_addr, 16);
memcpy((aw_ip), addr.sin6.sin6_addr.s6_addr, sizeof addr.sin6.sin6_addr.s6_addr);
}
}

View file

@ -38,15 +38,19 @@ written by
Yunhong Gu, last updated 01/27/2011
*****************************************************************************/
#ifndef __UDT_CACHE_H__
#define __UDT_CACHE_H__
#ifndef INC_SRT_CACHE_H
#define INC_SRT_CACHE_H
#include <list>
#include <vector>
#include "common.h"
#include "sync.h"
#include "netinet_any.h"
#include "udt.h"
namespace srt
{
class CCacheItem
{
public:
@ -82,13 +86,13 @@ public:
m_iCurrSize(0)
{
m_vHashPtr.resize(m_iHashSize);
CGuard::createMutex(m_Lock);
// Exception: -> CUDTUnited ctor
srt::sync::setupMutex(m_Lock, "Cache");
}
~CCache()
{
clear();
CGuard::releaseMutex(m_Lock);
}
public:
@ -98,7 +102,7 @@ public:
int lookup(T* data)
{
CGuard cacheguard(m_Lock);
srt::sync::ScopedLock cacheguard(m_Lock);
int key = data->getKey();
if (key < 0)
@ -126,7 +130,7 @@ public:
int update(T* data)
{
CGuard cacheguard(m_Lock);
srt::sync::ScopedLock cacheguard(m_Lock);
int key = data->getKey();
if (key < 0)
@ -223,7 +227,7 @@ private:
int m_iHashSize;
int m_iCurrSize;
pthread_mutex_t m_Lock;
srt::sync::Mutex m_Lock;
private:
CCache(const CCache&);
@ -234,23 +238,25 @@ private:
class CInfoBlock
{
public:
uint32_t m_piIP[4]; // IP address, machine read only, not human readable format
int m_iIPversion; // IP version
uint64_t m_ullTimeStamp; // last update time
int m_iRTT; // RTT
int m_iBandwidth; // estimated bandwidth
int m_iLossRate; // average loss rate
int m_iReorderDistance; // packet reordering distance
double m_dInterval; // inter-packet time, congestion control
double m_dCWnd; // congestion window size, congestion control
uint32_t m_piIP[4]; // IP address, machine read only, not human readable format.
int m_iIPversion; // Address family: AF_INET or AF_INET6.
uint64_t m_ullTimeStamp; // Last update time.
int m_iSRTT; // Smoothed RTT.
int m_iBandwidth; // Estimated link bandwidth.
int m_iLossRate; // Average loss rate.
int m_iReorderDistance; // Packet reordering distance.
double m_dInterval; // Inter-packet time (Congestion Control).
double m_dCWnd; // Congestion window size (Congestion Control).
public:
virtual ~CInfoBlock() {}
virtual CInfoBlock& operator=(const CInfoBlock& obj);
virtual bool operator==(const CInfoBlock& obj);
virtual CInfoBlock* clone();
virtual int getKey();
virtual void release() {}
CInfoBlock() {} // NOTE: leaves uninitialized
CInfoBlock& copyFrom(const CInfoBlock& obj);
CInfoBlock(const CInfoBlock& src) { copyFrom(src); }
CInfoBlock& operator=(const CInfoBlock& src) { return copyFrom(src); }
bool operator==(const CInfoBlock& obj);
CInfoBlock* clone();
int getKey();
void release() {}
public:
@ -259,8 +265,9 @@ public:
/// @param [in] ver IP version
/// @param [out] ip the result machine readable IP address in integer array
static void convert(const sockaddr* addr, int ver, uint32_t ip[]);
static void convert(const sockaddr_any& addr, uint32_t ip[4]);
};
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
/*
* 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/.
*
*
*/
/*****************************************************************************
@ -50,138 +50,118 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_CHANNEL_H__
#define __UDT_CHANNEL_H__
#ifndef INC_SRT_CHANNEL_H
#define INC_SRT_CHANNEL_H
#include "platform_sys.h"
#include "udt.h"
#include "packet.h"
#include "socketconfig.h"
#include "netinet_any.h"
namespace srt
{
class CChannel
{
void createSocket(int family);
public:
// XXX There's currently no way to access the socket ID set for
// whatever the channel 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 ""; }
// XXX There's currently no way to access the socket ID set for
// whatever the channel 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 ""; }
CChannel();
~CChannel();
CChannel();
CChannel(int version);
~CChannel();
/// Open a UDP channel.
/// @param [in] addr The local address that UDP will use.
/// Open a UDP channel.
/// @param [in] addr The local address that UDP will use.
void open(const sockaddr_any& addr);
void open(const sockaddr* addr = NULL);
void open(int family);
/// Open a UDP channel based on an existing UDP socket.
/// @param [in] udpsock UDP socket descriptor.
/// Open a UDP channel based on an existing UDP socket.
/// @param [in] udpsock UDP socket descriptor.
void attach(UDPSOCKET udpsock);
void attach(UDPSOCKET udpsock, const sockaddr_any& adr);
/// Disconnect and close the UDP entity.
/// Disconnect and close the UDP entity.
void close() const;
void close() const;
/// Get the UDP sending buffer size.
/// @return Current UDP sending buffer size.
/// Get the UDP sending buffer size.
/// @return Current UDP sending buffer size.
int getSndBufSize();
int getSndBufSize();
/// Get the UDP receiving buffer size.
/// @return Current UDP receiving buffer size.
/// Get the UDP receiving buffer size.
/// @return Current UDP receiving buffer size.
int getRcvBufSize();
int getRcvBufSize();
/// Set the UDP sending buffer size.
/// @param [in] size expected UDP sending buffer size.
/// Query the socket address that the channel is using.
/// @param [out] addr pointer to store the returned socket address.
void setSndBufSize(int size);
void getSockAddr(sockaddr_any& addr) const;
/// Set the UDP receiving buffer size.
/// @param [in] size expected UDP receiving buffer size.
/// Query the peer side socket address that the channel is connect to.
/// @param [out] addr pointer to store the returned socket address.
void setRcvBufSize(int size);
void getPeerAddr(sockaddr_any& addr) const;
/// Set the IPV6ONLY option.
/// @param [in] IPV6ONLY value.
/// Send a packet to the given address.
/// @param [in] addr pointer to the destination address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data sent.
void setIpV6Only(int ipV6Only);
int sendto(const sockaddr_any& addr, srt::CPacket& packet) const;
/// Query the socket address that the channel is using.
/// @param [out] addr pointer to store the returned socket address.
/// Receive a packet from the channel and record the source address.
/// @param [in] addr pointer to the source address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data received.
void getSockAddr(sockaddr* addr) const;
EReadStatus recvfrom(sockaddr_any& addr, srt::CPacket& packet) const;
/// Query the peer side socket address that the channel is connect to.
/// @param [out] addr pointer to store the returned socket address.
void setConfig(const CSrtMuxerConfig& config);
void getPeerAddr(sockaddr* addr) const;
/// Get the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return TTL.
/// Send a packet to the given address.
/// @param [in] addr pointer to the destination address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data sent.
int getIpTTL() const;
int sendto(const sockaddr* addr, CPacket& packet) const;
/// Get the IP Type of Service.
/// @return ToS.
/// Receive a packet from the channel and record the source address.
/// @param [in] addr pointer to the source address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data received.
int getIpToS() const;
EReadStatus recvfrom(sockaddr* addr, CPacket& packet) const;
#ifdef SRT_ENABLE_IPOPTS
/// Set the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return none.
void setIpTTL(int ttl);
/// Set the IP Type of Service.
/// @param [in] tos IP Type of Service.
void setIpToS(int tos);
/// 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
bool getBind(char* dst, size_t len);
#endif
int ioctlQuery(int type) const;
int sockoptQuery(int level, int option) const;
int ioctlQuery(int type) const;
int sockoptQuery(int level, int option) const;
const sockaddr* bindAddress() { return &m_BindAddr; }
const sockaddr_any& bindAddressAny() { return m_BindAddr; }
const sockaddr* bindAddress() { return m_BindAddr.get(); }
const sockaddr_any& bindAddressAny() { return m_BindAddr; }
private:
void setUDPSockOpt();
void setUDPSockOpt();
private:
const int m_iIPversion; // IP version
int m_iSockAddrSize; // socket address structure size (pre-defined to avoid run-time test)
UDPSOCKET m_iSocket; // socket descriptor
UDPSOCKET m_iSocket; // socket descriptor
#ifdef SRT_ENABLE_IPOPTS
int m_iIpTTL;
int m_iIpToS;
#endif
int m_iSndBufSize; // UDP sending buffer size
int m_iRcvBufSize; // UDP receiving buffer size
int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set)
sockaddr_any m_BindAddr;
// Mutable because when querying original settings
// this comprises the cache for extracted values,
// although the object itself isn't considered modified.
mutable CSrtMuxerConfig m_mcfg; // Note: ReuseAddr is unused and ineffective.
sockaddr_any m_BindAddr;
};
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,7 @@
// This is a controversial thing, so temporarily blocking
//#define SRT_ENABLE_SYSTEMBUFFER_TRACE
#include "platform_sys.h"
#ifdef SRT_ENABLE_SYSTEMBUFFER_TRACE
@ -34,8 +34,11 @@
#include "logging.h"
using namespace std;
using namespace srt::sync;
using namespace srt_logging;
namespace srt {
SrtCongestionControlBase::SrtCongestionControlBase(CUDT* parent)
{
m_parent = parent;
@ -58,7 +61,7 @@ void SrtCongestion::Check()
class LiveCC: public SrtCongestionControlBase
{
int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec)
size_t m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit
srt::sync::atomic<size_t> m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit
size_t m_zMaxPayloadSize;
// NAKREPORT stuff.
@ -74,12 +77,12 @@ public:
{
m_llSndMaxBW = BW_INFINITE; // 1 Gbbps in Bytes/sec BW_INFINITE
m_zMaxPayloadSize = parent->OPT_PayloadSize();
if ( m_zMaxPayloadSize == 0 )
if (m_zMaxPayloadSize == 0)
m_zMaxPayloadSize = parent->maxPayloadSize();
m_zSndAvgPayloadSize = m_zMaxPayloadSize;
m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec)
m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator
m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2)
HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize);
@ -90,11 +93,11 @@ public:
// from receiving thread.
parent->ConnectSignal(TEV_SEND, SSLOT(updatePayloadSize));
/*
* Readjust the max SndPeriod onACK (and onTimeout)
*/
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(updatePktSndPeriod_onTimer));
parent->ConnectSignal(TEV_ACK, SSLOT(updatePktSndPeriod_onAck));
//
// Adjust the max SndPeriod onACK and onTimeout.
//
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(onRTO));
parent->ConnectSignal(TEV_ACK, SSLOT(onAck));
}
bool checkTransArgs(SrtCongestion::TransAPI api, SrtCongestion::TransDir dir, const char* , size_t size, int , bool ) ATR_OVERRIDE
@ -151,24 +154,30 @@ private:
HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize);
}
void updatePktSndPeriod_onTimer(ETransmissionEvent , EventVariant var)
/// @brief On RTO event update an inter-packet send interval.
/// @param arg EventVariant::STAGE to distinguish between INIT and actual RTO.
void onRTO(ETransmissionEvent , EventVariant var)
{
if ( var.get<EventVariant::STAGE>() != TEV_CHT_INIT )
if (var.get<EventVariant::STAGE>() != TEV_CHT_INIT )
updatePktSndPeriod();
}
void updatePktSndPeriod_onAck(ETransmissionEvent , EventVariant )
/// @brief Handle an incoming ACK event.
/// Mainly updates a send interval between packets relying on the maximum BW limit.
void onAck(ETransmissionEvent, EventVariant )
{
updatePktSndPeriod();
}
/// @brief Updates a send interval between packets relying on the maximum BW limit.
void updatePktSndPeriod()
{
// packet = payload + header
const double pktsize = (double) m_zSndAvgPayloadSize + CPacket::SRT_DATA_HDR_SIZE;
const double pktsize = (double) m_zSndAvgPayloadSize.load() + CPacket::SRT_DATA_HDR_SIZE;
m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW);
HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod
<< " (pktsize=" << pktsize << ", bw=" << m_llSndMaxBW);
<< " by avg pktsize=" << m_zSndAvgPayloadSize
<< ", bw=" << m_llSndMaxBW);
}
void setMaxBW(int64_t maxbw)
@ -176,7 +185,6 @@ private:
m_llSndMaxBW = maxbw > 0 ? maxbw : BW_INFINITE;
updatePktSndPeriod();
#ifdef SRT_ENABLE_NOCWND
/*
* UDT default flow control should not trigger under normal SRT operation
* UDT stops sending if the number of packets in transit (not acknowledged)
@ -186,9 +194,6 @@ private:
*/
// XXX Consider making this a socket option.
m_dCWndSize = m_dMaxCWndSize;
#else
m_dCWndSize = 1000;
#endif
}
void updateBandwidth(int64_t maxbw, int64_t bw) ATR_OVERRIDE
@ -215,7 +220,7 @@ private:
return SrtCongestion::SRM_FASTREXMIT;
}
uint64_t updateNAKInterval(uint64_t nakint_tk, int /*rcv_speed*/, size_t /*loss_length*/) ATR_OVERRIDE
int64_t updateNAKInterval(int64_t nakint_us, int /*rcv_speed*/, size_t /*loss_length*/) ATR_OVERRIDE
{
/*
* duB:
@ -233,12 +238,12 @@ private:
// Note: this value will still be reshaped to defined minimum,
// as per minNAKInterval.
return nakint_tk / m_iNakReportAccel;
return nakint_us / m_iNakReportAccel;
}
uint64_t minNAKInterval() ATR_OVERRIDE
int64_t minNAKInterval() ATR_OVERRIDE
{
return m_iMinNakInterval_us * CTimer::getCPUFrequency();
return m_iMinNakInterval_us;
}
};
@ -250,7 +255,7 @@ class FileCC : public SrtCongestionControlBase
// Fields from CUDTCC
int m_iRCInterval; // UDT Rate control interval
uint64_t m_LastRCTime; // last rate increase time
steady_clock::time_point m_LastRCTime; // last rate increase time
bool m_bSlowStart; // if in slow start phase
int32_t m_iLastAck; // last ACKed seq no
bool m_bLoss; // if loss happened since last rate increase
@ -268,7 +273,7 @@ public:
FileCC(CUDT* parent)
: SrtCongestionControlBase(parent)
, m_iRCInterval(CUDT::COMM_SYN_INTERVAL_US)
, m_LastRCTime(CTimer::getTime())
, m_LastRCTime(steady_clock::now())
, m_bSlowStart(true)
, m_iLastAck(parent->sndSeqNo())
, m_bLoss(false)
@ -290,9 +295,9 @@ public:
m_dCWndSize = 16;
m_dPktSndPeriod = 1;
parent->ConnectSignal(TEV_ACK, SSLOT(updateSndPeriod));
parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(slowdownSndPeriod));
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(speedupToWindowSize));
parent->ConnectSignal(TEV_ACK, SSLOT(onACK));
parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(onLossReport));
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(onRTO));
HLOGC(cclog.Debug, log << "Creating FileCC");
}
@ -306,10 +311,11 @@ public:
return true;
}
/// Tells if an early ACK is needed (before the next Full ACK happening every 10ms).
/// In FileCC, treat non-full-payload as an end-of-message (stream)
/// and request ACK to be sent immediately.
bool needsQuickACK(const CPacket& pkt) ATR_OVERRIDE
{
// For FileCC, treat non-full-buffer situation as an end-of-message situation;
// request ACK to be sent immediately.
if (pkt.getLength() < m_parent->maxPayloadSize())
{
// This is not a regular fixed size packet...
@ -330,14 +336,15 @@ public:
}
private:
// SLOTS
void updateSndPeriod(ETransmissionEvent, EventVariant arg)
/// Handle icoming ACK event.
/// In slow start stage increase CWND. Leave slow start once maximum CWND is reached.
/// In congestion avoidance stage adjust inter packet send interval value to achieve maximum rate.
void onACK(ETransmissionEvent, EventVariant arg)
{
const int ack = arg.get<EventVariant::ACK>();
const uint64_t currtime = CTimer::getTime();
if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval)
const steady_clock::time_point currtime = steady_clock::now();
if (count_microseconds(currtime - m_LastRCTime) < m_iRCInterval)
return;
m_LastRCTime = currtime;
@ -360,11 +367,11 @@ private:
}
else
{
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval);
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:ENDED) wndsize="
<< m_dCWndSize << "/" << m_dMaxCWndSize
<< " sndperiod=" << m_dPktSndPeriod << "us = wndsize/(RTT+RCIV) RTT="
<< m_parent->RTT() << " RCIV=" << m_iRCInterval);
<< m_parent->SRTT() << " RCIV=" << m_iRCInterval);
}
}
else
@ -376,9 +383,9 @@ private:
}
else
{
m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->RTT() + m_iRCInterval) + 16;
m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->SRTT() + m_iRCInterval) + 16;
HLOGC(cclog.Debug, log << "FileCC: UPD (speed mode) wndsize="
<< m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->RTT()
<< m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->SRTT()
<< " sndperiod=" << m_dPktSndPeriod << "us. deliverRate = "
<< m_parent->deliveryRate() << " pkts/s)");
}
@ -393,7 +400,7 @@ private:
else
{
double inc = 0;
const int loss_bw = 2 * (1000000 / m_dLastDecPeriod); // 2 times last loss point
const int loss_bw = static_cast<int>(2 * (1000000 / m_dLastDecPeriod)); // 2 times last loss point
const int bw_pktps = min(loss_bw, m_parent->bandwidth());
int64_t B = (int64_t)(bw_pktps - 1000000.0 / m_dPktSndPeriod);
@ -456,9 +463,10 @@ private:
}
// When a lossreport has been received, it might be due to having
// reached the available bandwidth limit. Slowdown to avoid further losses.
void slowdownSndPeriod(ETransmissionEvent, EventVariant arg)
/// When a lossreport has been received, it might be due to having
/// reached the available bandwidth limit. Slowdown to avoid further losses.
/// Leave the slow start stage if it was active.
void onLossReport(ETransmissionEvent, EventVariant arg)
{
const int32_t* losslist = arg.get_ptr();
size_t losslist_size = arg.get_len();
@ -483,9 +491,9 @@ private:
}
else
{
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval);
HLOGC(cclog.Debug, log << "FileCC: LOSS, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (RTT="
<< m_parent->RTT() << " RCIV=" << m_iRCInterval << ")");
<< m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")");
}
}
@ -493,14 +501,14 @@ private:
m_bLoss = true;
// TODO: const int pktsInFlight = CSeqNo::seqoff(m_iLastAck, m_parent->sndSeqNo());
const int pktsInFlight = m_parent->RTT() / m_dPktSndPeriod;
const int pktsInFlight = static_cast<int>(m_parent->SRTT() / m_dPktSndPeriod);
const int numPktsLost = m_parent->sndLossLength();
const int lost_pcent_x10 = pktsInFlight > 0 ? (numPktsLost * 1000) / pktsInFlight : 0;
HLOGC(cclog.Debug, log << "FileCC: LOSS: "
<< "sent=" << CSeqNo::seqlen(m_iLastAck, m_parent->sndSeqNo()) << ", inFlight=" << pktsInFlight
<< ", lost=" << numPktsLost << " ("
<< lost_pcent_x10 / 10 << "." << lost_pcent_x10 % 10 << "\%)");
<< lost_pcent_x10 / 10 << "." << lost_pcent_x10 % 10 << "%)");
if (lost_pcent_x10 < 20) // 2.0%
{
HLOGC(cclog.Debug, log << "FileCC: LOSS: m_dLastDecPeriod=" << m_dLastDecPeriod << "->" << m_dPktSndPeriod);
@ -527,11 +535,8 @@ private:
m_iLastDecSeq = m_parent->sndSeqNo();
// remove global synchronization using randomization
srand(m_iLastDecSeq);
m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX));
if (m_iDecRandom < 1)
m_iDecRandom = 1;
m_iDecRandom = m_iAvgNAKNum > 1 ? genRandomInt(1, m_iAvgNAKNum) : 1;
SRT_ASSERT(m_iDecRandom >= 1);
HLOGC(cclog.Debug, log << "FileCC: LOSS:NEW lseqno=" << lossbegin
<< ", lastsentseqno=" << m_iLastDecSeq
<< ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin)
@ -562,7 +567,9 @@ private:
}
}
void speedupToWindowSize(ETransmissionEvent, EventVariant arg)
/// @brief On retransmission timeout leave slow start stage if it was active.
/// @param arg EventVariant::STAGE to distinguish between INIT and actual RTO.
void onRTO(ETransmissionEvent, EventVariant arg)
{
ECheckTimerStage stg = arg.get<EventVariant::STAGE>();
@ -583,9 +590,9 @@ private:
}
else
{
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval);
HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize="
<< setprecision(6) << m_dCWndSize << " RTT=" << m_parent->RTT() << " RCIV=" << m_iRCInterval << ")");
<< setprecision(6) << m_dCWndSize << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")");
}
}
else
@ -636,8 +643,18 @@ bool SrtCongestion::configure(CUDT* parent)
return !!congctl;
}
void SrtCongestion::dispose()
{
if (congctl)
{
delete congctl;
congctl = 0;
}
}
SrtCongestion::~SrtCongestion()
{
delete congctl;
congctl = 0;
dispose();
}
} // namespace srt

View file

@ -8,17 +8,19 @@
*
*/
#ifndef INC__CONGCTL_H
#define INC__CONGCTL_H
#ifndef INC_SRT_CONGCTL_H
#define INC_SRT_CONGCTL_H
#include <algorithm>
#include <map>
#include <string>
#include <utility>
namespace srt {
class CUDT;
class SrtCongestionControlBase;
typedef SrtCongestionControlBase* srtcc_create_t(CUDT* parent);
typedef SrtCongestionControlBase* srtcc_create_t(srt::CUDT* parent);
class SrtCongestion
{
@ -55,13 +57,24 @@ public:
bool operator()(NamePtr np) { return n == np.first; }
};
static NamePtr* find(const std::string& name)
{
NamePtr* end = congctls+N_CONTROLLERS;
NamePtr* try_selector = std::find_if(congctls, end, IsName(name));
return try_selector != end ? try_selector : NULL;
}
static bool exists(const std::string& name)
{
return find(name);
}
// You can call select() multiple times, until finally
// the 'configure' method is called.
bool select(const std::string& name)
{
NamePtr* end = congctls+N_CONTROLLERS;
NamePtr* try_selector = std::find_if(congctls, end, IsName(name));
if (try_selector == end)
NamePtr* try_selector = find(name);
if (!try_selector)
return false;
selector = try_selector - congctls;
return true;
@ -79,12 +92,18 @@ public:
// 1. The congctl is individual, so don't copy it. Set NULL.
// 2. The selected name is copied so that it's configured correctly.
SrtCongestion(const SrtCongestion& source): congctl(), selector(source.selector) {}
void operator=(const SrtCongestion& source) { congctl = 0; selector = source.selector; }
// This function will be called by the parent CUDT
// in appropriate time. It should select appropriate
// congctl basing on the value in selector, then
// pin oneself in into CUDT for receiving event signals.
bool configure(CUDT* parent);
bool configure(srt::CUDT* parent);
// This function will intentionally delete the contained object.
// This makes future calls to ready() return false. Calling
// configure on it again will create it again.
void dispose();
// Will delete the pinned in congctl object.
// This must be defined in *.cpp file due to virtual
@ -111,12 +130,13 @@ public:
};
};
class CPacket;
class SrtCongestionControlBase
{
protected:
// Here can be some common fields
CUDT* m_parent;
srt::CUDT* m_parent;
double m_dPktSndPeriod;
double m_dCWndSize;
@ -127,11 +147,11 @@ protected:
//int m_iMSS; // NOT REQUIRED. Use m_parent->MSS() instead.
//int32_t m_iSndCurrSeqNo; // NOT REQUIRED. Use m_parent->sndSeqNo().
//int m_iRcvRate; // NOT REQUIRED. Use m_parent->deliveryRate() instead.
//int m_RTT; // NOT REQUIRED. Use m_parent->RTT() instead.
//int m_RTT; // NOT REQUIRED. Use m_parent->SRTT() instead.
//char* m_pcParam; // Used to access m_llMaxBw. Use m_parent->maxBandwidth() instead.
// Constructor in protected section so that this class is semi-abstract.
SrtCongestionControlBase(CUDT* parent);
SrtCongestionControlBase(srt::CUDT* parent);
public:
// This could be also made abstract, but this causes a linkage
@ -169,11 +189,11 @@ public:
virtual int ACKTimeout_us() const { return 0; }
// Called when the settings concerning m_llMaxBW were changed.
// Arg 1: value of CUDT::m_llMaxBW
// Arg 2: value calculated out of CUDT::m_llInputBW and CUDT::m_iOverheadBW.
// Arg 1: value of CUDT's m_config.m_llMaxBW
// Arg 2: value calculated out of CUDT's m_config.llInputBW and m_config.iOverheadBW.
virtual void updateBandwidth(int64_t, int64_t) {}
virtual bool needsQuickACK(const CPacket&)
virtual bool needsQuickACK(const srt::CPacket&)
{
return false;
}
@ -186,21 +206,21 @@ public:
virtual SrtCongestion::RexmitMethod rexmitMethod() = 0; // Implementation enforced.
virtual uint64_t updateNAKInterval(uint64_t nakint_tk, int rcv_speed, size_t loss_length)
virtual int64_t updateNAKInterval(int64_t nakint_us, int rcv_speed, size_t loss_length)
{
if (rcv_speed > 0)
nakint_tk += (loss_length * uint64_t(1000000) / rcv_speed) * CTimer::getCPUFrequency();
nakint_us += (loss_length * int64_t(1000000) / rcv_speed);
return nakint_tk;
return nakint_us;
}
virtual uint64_t minNAKInterval()
virtual int64_t minNAKInterval()
{
return 0; // Leave default
}
};
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,8 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include <cstring>
#include <string>
#include <sstream>
@ -24,6 +26,7 @@ written by
#include "crypto.h"
#include "logging.h"
#include "core.h"
#include "api.h"
using namespace srt_logging;
@ -40,9 +43,10 @@ using namespace srt_logging;
*/
// 10* HAICRYPT_DEF_KM_PRE_ANNOUNCE
const int SRT_CRYPT_KM_PRE_ANNOUNCE = 0x10000;
const int SRT_CRYPT_KM_PRE_ANNOUNCE SRT_ATR_UNUSED = 0x10000;
#if ENABLE_LOGGING
namespace srt_logging
{
std::string KmStateStr(SRT_KM_STATE state)
{
switch (state)
@ -62,9 +66,21 @@ std::string KmStateStr(SRT_KM_STATE state)
}
}
}
} // namespace
using srt_logging::KmStateStr;
std::string CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srtlen)
void srt::CCryptoControl::globalInit()
{
#ifdef SRT_ENABLE_ENCRYPTION
// We need to force the Cryspr to be initialized during startup to avoid the
// possibility of multiple threads initialzing the same static data later on.
HaiCryptCryspr_Get_Instance();
#endif
}
#if ENABLE_LOGGING
std::string srt::CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srtlen)
{
std::ostringstream os;
os << hdr << ": cmd=" << cmd << "(" << (cmd == SRT_CMD_KMREQ ? "KMREQ":"KMRSP") <<") len="
@ -75,7 +91,7 @@ std::string CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srt
}
#endif
void CCryptoControl::updateKmState(int cmd, size_t srtlen SRT_ATR_UNUSED)
void srt::CCryptoControl::updateKmState(int cmd, size_t srtlen SRT_ATR_UNUSED)
{
if (cmd == SRT_CMD_KMREQ)
{
@ -83,43 +99,41 @@ void CCryptoControl::updateKmState(int cmd, size_t srtlen SRT_ATR_UNUSED)
{
m_SndKmState = SRT_KM_S_SECURING;
}
LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
LOGP(cnlog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
}
else
{
LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
LOGP(cnlog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
}
}
void CCryptoControl::createFakeSndContext()
void srt::CCryptoControl::createFakeSndContext()
{
if (!m_iSndKmKeyLen)
m_iSndKmKeyLen = 16;
if (!createCryptoCtx(Ref(m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX))
if (!createCryptoCtx(m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, (m_hSndCrypto)))
{
HLOGC(mglog.Debug, log << "Error: Can't create fake crypto context for sending - sending will return ERROR!");
HLOGC(cnlog.Debug, log << "Error: Can't create fake crypto context for sending - sending will return ERROR!");
m_hSndCrypto = 0;
}
}
int CCryptoControl::processSrtMsg_KMREQ(
int srt::CCryptoControl::processSrtMsg_KMREQ(
const uint32_t* srtdata SRT_ATR_UNUSED,
size_t bytelen SRT_ATR_UNUSED,
uint32_t* srtdata_out, ref_t<size_t> r_srtlen, int hsv SRT_ATR_UNUSED)
int hsv SRT_ATR_UNUSED,
uint32_t pw_srtdata_out[], size_t& w_srtlen)
{
size_t& srtlen = *r_srtlen;
//Receiver
/* All 32-bit msg fields swapped on reception
* But HaiCrypt expect network order message
* Re-swap to cancel it.
*/
#ifdef SRT_ENABLE_ENCRYPTION
srtlen = bytelen/sizeof(srtdata[SRT_KMR_KMSTATE]);
HtoNLA(srtdata_out, srtdata, srtlen);
unsigned char* kmdata = reinterpret_cast<unsigned char*>(srtdata_out);
std::vector<unsigned char> kmcopy(kmdata, kmdata + bytelen);
w_srtlen = bytelen/sizeof(srtdata[SRT_KMR_KMSTATE]);
HtoNLA((pw_srtdata_out), srtdata, w_srtlen);
unsigned char* kmdata = reinterpret_cast<unsigned char*>(pw_srtdata_out);
// The side that has received KMREQ is always an HSD_RESPONDER, regardless of
// what has called this function. The HSv5 handshake only enforces bidirectional
@ -131,11 +145,11 @@ int CCryptoControl::processSrtMsg_KMREQ(
// CHANGED. The first version made HSv5 reject the connection.
// This isn't well handled by applications, so the connection is
// still established, but unable to handle any transport.
//#define KMREQ_RESULT_REJECTION() if (bidirectional) { return SRT_CMD_NONE; } else { srtlen = 1; goto HSv4_ErrorReport; }
#define KMREQ_RESULT_REJECTION() { srtlen = 1; goto HSv4_ErrorReport; }
//#define KMREQ_RESULT_REJECTION() if (bidirectional) { return SRT_CMD_NONE; } else { w_srtlen = 1; goto HSv4_ErrorReport; }
#define KMREQ_RESULT_REJECTION() { w_srtlen = 1; goto HSv4_ErrorReport; }
int rc = HAICRYPT_OK; // needed before 'goto' run from KMREQ_RESULT_REJECTION macro
bool SRT_ATR_UNUSED wasb4 = false;
bool wasb4 SRT_ATR_UNUSED = false;
size_t sek_len = 0;
// What we have to do:
@ -147,16 +161,16 @@ int CCryptoControl::processSrtMsg_KMREQ(
// function normally return SRT_CMD_KMRSP.
if ( bytelen <= HCRYPT_MSG_KM_OFS_SALT ) //Sanity on message
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: size of the KM (" << bytelen << ") is too small, must be >" << HCRYPT_MSG_KM_OFS_SALT);
LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: size of the KM (" << bytelen << ") is too small, must be >" << HCRYPT_MSG_KM_OFS_SALT);
m_RcvKmState = SRT_KM_S_BADSECRET;
KMREQ_RESULT_REJECTION();
}
HLOGC(mglog.Debug, log << "KMREQ: getting SEK and creating receiver crypto");
HLOGC(cnlog.Debug, log << "KMREQ: getting SEK and creating receiver crypto");
sek_len = hcryptMsg_KM_GetSekLen(kmdata);
if ( sek_len == 0 )
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Received SEK is empty - REJECTING!");
LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: Received SEK is empty - REJECTING!");
m_RcvKmState = SRT_KM_S_BADSECRET;
KMREQ_RESULT_REJECTION();
}
@ -168,7 +182,7 @@ int CCryptoControl::processSrtMsg_KMREQ(
#if ENABLE_HEAVY_LOGGING
if (m_iSndKmKeyLen != m_iRcvKmKeyLen)
{
LOGC(mglog.Debug, log << "processSrtMsg_KMREQ: Agent's PBKEYLEN=" << m_iSndKmKeyLen
LOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: Agent's PBKEYLEN=" << m_iSndKmKeyLen
<< " overwritten by Peer's PBKEYLEN=" << m_iRcvKmKeyLen);
}
#endif
@ -179,22 +193,22 @@ int CCryptoControl::processSrtMsg_KMREQ(
// a wrong password.
if (m_KmSecret.len == 0) //We have a shared secret <==> encryption is on
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Agent does not declare encryption - won't decrypt incoming packets!");
LOGC(cnlog.Warn, log << "processSrtMsg_KMREQ: Agent does not declare encryption - won't decrypt incoming packets!");
m_RcvKmState = SRT_KM_S_NOSECRET;
KMREQ_RESULT_REJECTION();
}
wasb4 = m_hRcvCrypto;
if (!createCryptoCtx(Ref(m_hRcvCrypto), m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX))
if (!createCryptoCtx(m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX, (m_hRcvCrypto)))
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Can't create RCV CRYPTO CTX - must reject...");
LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: Can't create RCV CRYPTO CTX - must reject...");
m_RcvKmState = SRT_KM_S_NOSECRET;
KMREQ_RESULT_REJECTION();
}
if (!wasb4)
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: created RX ENC with KeyLen=" << m_iRcvKmKeyLen);
HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: created RX ENC with KeyLen=" << m_iRcvKmKeyLen);
}
// We have both sides set with password, so both are pending for security
m_RcvKmState = SRT_KM_S_SECURING;
@ -207,36 +221,36 @@ int CCryptoControl::processSrtMsg_KMREQ(
{
case HAICRYPT_OK:
m_RcvKmState = SRT_KM_S_SECURED;
HLOGC(mglog.Debug, log << "KMREQ/rcv: (snd) Rx process successful - SECURED.");
HLOGC(cnlog.Debug, log << "KMREQ/rcv: (snd) Rx process successful - SECURED.");
//Send back the whole message to confirm
break;
case HAICRYPT_ERROR_WRONG_SECRET: //Unmatched shared secret to decrypt wrapped key
m_RcvKmState = m_SndKmState = SRT_KM_S_BADSECRET;
//Send status KMRSP message to tel error
srtlen = 1;
LOGC(mglog.Error, log << "KMREQ/rcv: (snd) Rx process failure - BADSECRET");
w_srtlen = 1;
LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure - BADSECRET");
break;
case HAICRYPT_ERROR: //Other errors
default:
m_RcvKmState = m_SndKmState = SRT_KM_S_NOSECRET;
srtlen = 1;
LOGC(mglog.Error, log << "KMREQ/rcv: (snd) Rx process failure (IPE) - NOSECRET");
w_srtlen = 1;
LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure (IPE) - NOSECRET");
break;
}
LOGP(mglog.Note, FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen));
LOGP(cnlog.Note, FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen));
// Since now, when CCryptoControl::decrypt() encounters an error, it will print it, ONCE,
// until the next KMREQ is received as a key regeneration.
m_bErrorReported = false;
if (srtlen == 1)
if (w_srtlen == 1)
goto HSv4_ErrorReport;
// Configure the sender context also, if it succeeded to configure the
// receiver context and we are using bidirectional mode.
if ( bidirectional )
if (bidirectional)
{
// Note: 'bidirectional' means that we want a bidirectional key update,
// which happens only and exclusively with HSv5 handshake - not when the
@ -249,7 +263,7 @@ int CCryptoControl::processSrtMsg_KMREQ(
m_iSndKmKeyLen = m_iRcvKmKeyLen;
if (HaiCrypt_Clone(m_hRcvCrypto, HAICRYPT_CRYPTO_DIR_TX, &m_hSndCrypto) != HAICRYPT_OK)
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Can't create SND CRYPTO CTX - WILL NOT SEND-ENCRYPT correctly!");
LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: Can't create SND CRYPTO CTX - WILL NOT SEND-ENCRYPT correctly!");
if (hasPassphrase())
m_SndKmState = SRT_KM_S_BADSECRET;
else
@ -260,30 +274,30 @@ int CCryptoControl::processSrtMsg_KMREQ(
m_SndKmState = SRT_KM_S_SECURED;
}
LOGC(mglog.Note, log << FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen)
LOGC(cnlog.Note, log << FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen)
<< " SndKeyLen=" << m_iSndKmKeyLen
<< " TX CRYPTO CTX CLONED FROM RX"
);
// Write the KM message into the field from which it will be next sent.
memcpy(m_SndKmMsg[0].Msg, kmdata, bytelen);
memcpy((m_SndKmMsg[0].Msg), kmdata, bytelen);
m_SndKmMsg[0].MsgLen = bytelen;
m_SndKmMsg[0].iPeerRetry = 0; // Don't start sending them upon connection :)
}
else
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: NOT cloning RX to TX crypto: already in "
HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: NOT cloning RX to TX crypto: already in "
<< KmStateStr(m_SndKmState) << " state");
}
}
else
{
HLOGP(mglog.Debug, "processSrtMsg_KMREQ: NOT SECURED - not replaying failed security association to TX CRYPTO CTX");
HLOGP(cnlog.Debug, "processSrtMsg_KMREQ: NOT SECURED - not replaying failed security association to TX CRYPTO CTX");
}
}
else
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: NOT REPLAYING the key update to TX CRYPTO CTX.");
HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: NOT REPLAYING the key update to TX CRYPTO CTX.");
}
return SRT_CMD_KMRSP;
@ -304,17 +318,17 @@ HSv4_ErrorReport:
// It's ok that this is reported as error because this happens in a scenario,
// when non-encryption-enabled SRT application is contacted by encryption-enabled SRT
// application which tries to make a security association.
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Encryption not enabled at compile time - must reject...");
LOGC(cnlog.Warn, log << "processSrtMsg_KMREQ: Encryption not enabled at compile time - must reject...");
m_RcvKmState = SRT_KM_S_NOSECRET;
#endif
srtlen = 1;
w_srtlen = 1;
srtdata_out[SRT_KMR_KMSTATE] = m_RcvKmState;
pw_srtdata_out[SRT_KMR_KMSTATE] = m_RcvKmState;
return SRT_CMD_KMRSP;
}
int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int /* XXX unused? hsv*/)
int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int /* XXX unused? hsv*/)
{
/* All 32-bit msg fields (if present) swapped on reception
* But HaiCrypt expect network order message
@ -367,7 +381,7 @@ int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int
break;
default:
LOGC(mglog.Fatal, log << "processSrtMsg_KMRSP: IPE: unknown peer error state: "
LOGC(cnlog.Fatal, log << "processSrtMsg_KMRSP: IPE: unknown peer error state: "
<< KmStateStr(peerstate) << " (" << int(peerstate) << ")");
m_RcvKmState = SRT_KM_S_NOSECRET;
m_SndKmState = SRT_KM_S_NOSECRET;
@ -375,11 +389,11 @@ int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int
break;
}
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: received failure report. STATE: " << KmStateStr(m_RcvKmState));
LOGC(cnlog.Warn, log << "processSrtMsg_KMRSP: received failure report. STATE: " << KmStateStr(m_RcvKmState));
}
else
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: received key response len=" << len);
HLOGC(cnlog.Debug, log << "processSrtMsg_KMRSP: received key response len=" << len);
// XXX INSECURE << ": [" << FormatBinaryString((uint8_t*)srtd, len) << "]";
bool key1 = getKmMsg_acceptResponse(0, srtd, len);
bool key2 = true;
@ -389,40 +403,40 @@ int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int
if (key1 || key2)
{
m_SndKmState = m_RcvKmState = SRT_KM_S_SECURED;
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: KM response matches " << (key1 ? "EVEN" : "ODD") << " key");
HLOGC(cnlog.Debug, log << "processSrtMsg_KMRSP: KM response matches " << (key1 ? "EVEN" : "ODD") << " key");
retstatus = 1;
}
else
{
retstatus = -1;
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: IPE??? KM response key matches no key");
LOGC(cnlog.Error, log << "processSrtMsg_KMRSP: IPE??? KM response key matches no key");
/* XXX INSECURE
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: KM response: [" << FormatBinaryString((uint8_t*)srtd, len)
LOGC(cnlog.Error, log << "processSrtMsg_KMRSP: KM response: [" << FormatBinaryString((uint8_t*)srtd, len)
<< "] matches no key 0=[" << FormatBinaryString((uint8_t*)m_SndKmMsg[0].Msg, m_SndKmMsg[0].MsgLen)
<< "] 1=[" << FormatBinaryString((uint8_t*)m_SndKmMsg[1].Msg, m_SndKmMsg[1].MsgLen) << "]");
*/
m_SndKmState = m_RcvKmState = SRT_KM_S_BADSECRET;
}
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
HLOGC(cnlog.Debug, log << "processSrtMsg_KMRSP: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
<< "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry);
}
LOGP(mglog.Note, FormatKmMessage("processSrtMsg_KMRSP", SRT_CMD_KMRSP, len));
LOGP(cnlog.Note, FormatKmMessage("processSrtMsg_KMRSP", SRT_CMD_KMRSP, len));
return retstatus;
}
void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED)
void srt::CCryptoControl::sendKeysToPeer(CUDT* sock SRT_ATR_UNUSED, int iSRTT SRT_ATR_UNUSED, Whether2RegenKm regen SRT_ATR_UNUSED)
{
if ( !m_hSndCrypto || m_SndKmState == SRT_KM_S_UNSECURED)
if (!m_hSndCrypto || m_SndKmState == SRT_KM_S_UNSECURED)
{
HLOGC(mglog.Debug, log << "sendKeysToPeer: NOT sending/regenerating keys: "
HLOGC(cnlog.Debug, log << "sendKeysToPeer: NOT sending/regenerating keys: "
<< (m_hSndCrypto ? "CONNECTION UNSECURED" : "NO TX CRYPTO CTX created"));
return;
}
#ifdef SRT_ENABLE_ENCRYPTION
uint64_t now = 0;
srt::sync::steady_clock::time_point now = srt::sync::steady_clock::now();
/*
* Crypto Key Distribution to peer:
* If...
@ -432,38 +446,35 @@ void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED)
* - last sent Keying Material req should have been replied (RTT*1.5 elapsed);
* then (re-)send handshake request.
*/
if ( ((m_SndKmMsg[0].iPeerRetry > 0) || (m_SndKmMsg[1].iPeerRetry > 0))
&& ((m_SndKmLastTime + ((m_parent->RTT() * 3)/2)) <= (now = CTimer::getTime())))
if (((m_SndKmMsg[0].iPeerRetry > 0) || (m_SndKmMsg[1].iPeerRetry > 0))
&& ((m_SndKmLastTime + srt::sync::microseconds_from((iSRTT * 3)/2)) <= now))
{
for (int ki = 0; ki < 2; ki++)
{
if (m_SndKmMsg[ki].iPeerRetry > 0 && m_SndKmMsg[ki].MsgLen > 0)
{
m_SndKmMsg[ki].iPeerRetry--;
HLOGC(mglog.Debug, log << "sendKeysToPeer: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
HLOGC(cnlog.Debug, log << "sendKeysToPeer: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
<< " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry);
m_SndKmLastTime = now;
m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t));
sock->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen / sizeof(uint32_t));
}
}
}
if (now == 0)
{
HLOGC(mglog.Debug, log << "sendKeysToPeer: NO KEYS RESENT, will " <<
(regen ? "" : "NOT ") << "regenerate.");
}
if (regen)
{
regenCryptoKm(
true, // send UMSG_EXT + SRT_CMD_KMREQ to the peer, if regenerated the key
false // Do not apply the regenerated key to the to the receiver context
); // regenerate and send
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 CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional)
void srt::CCryptoControl::regenCryptoKm(CUDT* sock, bool bidirectional)
{
if (!m_hSndCrypto)
return;
@ -473,8 +484,8 @@ void CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional)
int nbo = HaiCrypt_Tx_ManageKeys(m_hSndCrypto, out_p, out_len_p, 2);
int sent = 0;
HLOGC(mglog.Debug, log << "regenCryptoKm: regenerating crypto keys nbo=" << nbo <<
" THEN=" << (sendit ? "SEND" : "KEEP") << " DIR=" << (bidirectional ? "BOTH" : "SENDER"));
HLOGC(cnlog.Debug, log << "regenCryptoKm: regenerating crypto keys nbo=" << nbo <<
" THEN=" << (sock ? "SEND" : "KEEP") << " DIR=" << (bidirectional ? "BOTH" : "SENDER"));
for (int i = 0; i < nbo && i < 2; i++)
{
@ -491,71 +502,69 @@ void CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional)
{
uint8_t* oldkey SRT_ATR_UNUSED = m_SndKmMsg[ki].Msg;
HLOGC(mglog.Debug, log << "new key[" << ki << "] index=" << kix
HLOGC(cnlog.Debug, log << "new key[" << ki << "] index=" << kix
<< " OLD=[" << m_SndKmMsg[ki].MsgLen << "]"
<< FormatBinaryString(m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen)
<< " NEW=[" << out_len_p[i] << "]"
<< FormatBinaryString((const uint8_t*)out_p[i], out_len_p[i]));
/* New Keying material, send to peer */
memcpy(m_SndKmMsg[ki].Msg, out_p[i], out_len_p[i]);
memcpy((m_SndKmMsg[ki].Msg), out_p[i], out_len_p[i]);
m_SndKmMsg[ki].MsgLen = out_len_p[i];
m_SndKmMsg[ki].iPeerRetry = SRT_MAX_KMRETRY;
if (bidirectional && !sendit)
if (bidirectional && !sock)
{
// "Send" this key also to myself, just to be applied to the receiver crypto,
// exactly the same way how this key is interpreted on the peer side into its receiver crypto
int rc = HaiCrypt_Rx_Process(m_hRcvCrypto, m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen, NULL, NULL, 0);
if ( rc < 0 )
{
LOGC(mglog.Fatal, log << "regenCryptoKm: IPE: applying key generated in snd crypto into rcv crypto: failed code=" << rc);
LOGC(cnlog.Fatal, log << "regenCryptoKm: IPE: applying key generated in snd crypto into rcv crypto: failed code=" << rc);
// The party won't be able to decrypt incoming data!
// Not sure if anything has to be reported.
}
}
if (sendit)
if (sock)
{
HLOGC(mglog.Debug, log << "regenCryptoKm: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
HLOGC(cnlog.Debug, log << "regenCryptoKm: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
<< " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry);
m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t));
sock->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen / sizeof(uint32_t));
sent++;
}
}
else if (out_len_p[i] == 0)
{
HLOGC(mglog.Debug, log << "no key[" << ki << "] index=" << kix << ": not generated");
HLOGC(cnlog.Debug, log << "no key[" << ki << "] index=" << kix << ": not generated");
}
else
{
HLOGC(mglog.Debug, log << "no key[" << ki << "] index=" << kix << ": key unchanged");
HLOGC(cnlog.Debug, log << "no key[" << ki << "] index=" << kix << ": key unchanged");
}
}
HLOGC(mglog.Debug, log << "regenCryptoKm: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
HLOGC(cnlog.Debug, log << "regenCryptoKm: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
<< "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry);
if (sent)
m_SndKmLastTime = CTimer::getTime();
m_SndKmLastTime = srt::sync::steady_clock::now();
}
#endif
CCryptoControl::CCryptoControl(CUDT* parent, SRTSOCKET id):
m_parent(parent), // should be initialized in createCC()
m_SocketID(id),
m_iSndKmKeyLen(0),
m_iRcvKmKeyLen(0),
m_SndKmState(SRT_KM_S_UNSECURED),
m_RcvKmState(SRT_KM_S_UNSECURED),
m_KmRefreshRatePkt(0),
m_KmPreAnnouncePkt(0),
m_bErrorReported(false)
srt::CCryptoControl::CCryptoControl(SRTSOCKET id)
: m_SocketID(id)
, m_iSndKmKeyLen(0)
, m_iRcvKmKeyLen(0)
, m_SndKmState(SRT_KM_S_UNSECURED)
, m_RcvKmState(SRT_KM_S_UNSECURED)
, m_KmRefreshRatePkt(0)
, m_KmPreAnnouncePkt(0)
, m_bErrorReported(false)
{
m_KmSecret.len = 0;
//send
m_SndKmLastTime = 0;
m_SndKmMsg[0].MsgLen = 0;
m_SndKmMsg[0].iPeerRetry = 0;
m_SndKmMsg[1].MsgLen = 0;
@ -565,14 +574,14 @@ m_bErrorReported(false)
m_hRcvCrypto = NULL;
}
bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool bidirectional SRT_ATR_UNUSED)
{
// NOTE: initiator creates m_hSndCrypto. When bidirectional,
// it creates also m_hRcvCrypto with the same key length.
// Acceptor creates nothing - it will create appropriate
// contexts when receiving KMREQ from the initiator.
HLOGC(mglog.Debug, log << "CCryptoControl::init: HS SIDE:"
HLOGC(cnlog.Debug, log << "CCryptoControl::init: HS SIDE:"
<< (side == HSD_INITIATOR ? "INITIATOR" : "RESPONDER")
<< " DIRECTION:" << (bidirectional ? "BOTH" : (side == HSD_INITIATOR) ? "SENDER" : "RECEIVER"));
@ -582,8 +591,8 @@ bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
// Set security-pending state, if a password was set.
m_SndKmState = hasPassphrase() ? SRT_KM_S_SECURING : SRT_KM_S_UNSECURED;
m_KmPreAnnouncePkt = m_parent->m_uKmPreAnnouncePkt;
m_KmRefreshRatePkt = m_parent->m_uKmRefreshRatePkt;
m_KmPreAnnouncePkt = cfg.uKmPreAnnouncePkt;
m_KmRefreshRatePkt = cfg.uKmRefreshRatePkt;
if ( side == HSD_INITIATOR )
{
@ -592,18 +601,18 @@ bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
#ifdef SRT_ENABLE_ENCRYPTION
if (m_iSndKmKeyLen == 0)
{
HLOGC(mglog.Debug, log << "CCryptoControl::init: PBKEYLEN still 0, setting default 16");
HLOGC(cnlog.Debug, log << "CCryptoControl::init: PBKEYLEN still 0, setting default 16");
m_iSndKmKeyLen = 16;
}
bool ok = createCryptoCtx(Ref(m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX);
HLOGC(mglog.Debug, log << "CCryptoControl::init: creating SND crypto context: " << ok);
bool ok = createCryptoCtx(m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, (m_hSndCrypto));
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);
HLOGC(mglog.Debug, log << "CCryptoControl::init: creating CLONED RCV crypto context: status=" << st);
HLOGC(cnlog.Debug, log << "CCryptoControl::init: creating CLONED RCV crypto context: status=" << st);
ok = st == 0;
}
@ -618,45 +627,51 @@ bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
}
regenCryptoKm(
false, // Do not send the key (will be attached it to the HSv5 handshake)
bidirectional // replicate the key to the receiver context, if bidirectional
);
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
);
#else
LOGC(mglog.Error, log << "CCryptoControl::init: encryption not supported");
// 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;
#endif
}
else
{
HLOGC(mglog.Debug, log << "CCryptoControl::init: CAN'T CREATE crypto: key length for SND = " << m_iSndKmKeyLen);
HLOGC(cnlog.Debug, log << "CCryptoControl::init: CAN'T CREATE crypto: key length for SND = " << m_iSndKmKeyLen);
}
}
else
{
HLOGC(mglog.Debug, log << "CCryptoControl::init: NOT creating crypto contexts - will be created upon reception of KMREQ");
HLOGC(cnlog.Debug, log << "CCryptoControl::init: NOT creating crypto contexts - will be created upon reception of KMREQ");
}
return true;
}
void CCryptoControl::close()
void srt::CCryptoControl::close()
{
/* Wipeout secrets */
memset(&m_KmSecret, 0, sizeof(m_KmSecret));
}
std::string CCryptoControl::CONID() const
std::string srt::CCryptoControl::CONID() const
{
if ( m_SocketID == 0 )
if (m_SocketID == 0)
return "";
std::ostringstream os;
os << "%" << m_SocketID << ":";
os << "@" << m_SocketID << ":";
return os.str();
}
#ifdef SRT_ENABLE_ENCRYPTION
#if ENABLE_HEAVY_LOGGING
namespace srt {
static std::string CryptoFlags(int flg)
{
using namespace std;
@ -673,13 +688,13 @@ static std::string CryptoFlags(int flg)
copy(f.begin(), f.end(), ostream_iterator<string>(os, "|"));
return os.str();
}
#endif
} // namespace srt
#endif // ENABLE_HEAVY_LOGGING
#ifdef SRT_ENABLE_ENCRYPTION
bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle> hCrypto, size_t keylen, HaiCrypt_CryptoDir cdir)
bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir, HaiCrypt_Handle& w_hCrypto)
{
if (*hCrypto)
if (w_hCrypto)
{
// XXX You can check here if the existing handle represents
// a correctly defined crypto. But this doesn't seem to be
@ -690,7 +705,7 @@ bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle> hCrypto, size_t keyl
if ((m_KmSecret.len <= 0) || (keylen <= 0))
{
LOGC(mglog.Error, log << CONID() << "cryptoCtx: missing secret (" << m_KmSecret.len << ") or key length (" << keylen << ")");
LOGC(cnlog.Error, log << CONID() << "cryptoCtx: IPE missing secret (" << m_KmSecret.len << ") or key length (" << keylen << ")");
return false;
}
@ -711,36 +726,35 @@ bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle> hCrypto, size_t keyl
crypto_cfg.secret = m_KmSecret;
//memcpy(&crypto_cfg.secret, &m_KmSecret, sizeof(crypto_cfg.secret));
HLOGC(mglog.Debug, log << "CRYPTO CFG: flags=" << CryptoFlags(crypto_cfg.flags) << " xport=" << crypto_cfg.xport << " cryspr=" << crypto_cfg.cryspr
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);
if (HaiCrypt_Create(&crypto_cfg, &hCrypto.get()) != HAICRYPT_OK)
if (HaiCrypt_Create(&crypto_cfg, (&w_hCrypto)) != HAICRYPT_OK)
{
LOGC(mglog.Error, log << CONID() << "cryptoCtx: could not create " << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " crypto ctx");
LOGC(cnlog.Error, log << CONID() << "cryptoCtx: could not create " << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " crypto ctx");
return false;
}
HLOGC(mglog.Debug, log << CONID() << "cryptoCtx: CREATED crypto for dir=" << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " keylen=" << keylen);
HLOGC(cnlog.Debug, log << CONID() << "cryptoCtx: CREATED crypto for dir=" << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " keylen=" << keylen);
return true;
}
#else
bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle>, size_t, HaiCrypt_CryptoDir)
bool srt::CCryptoControl::createCryptoCtx(size_t, HaiCrypt_CryptoDir, HaiCrypt_Handle&)
{
return false;
}
#endif
#endif // SRT_ENABLE_ENCRYPTION
EncryptionStatus CCryptoControl::encrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
srt::EncryptionStatus srt::CCryptoControl::encrypt(CPacket& w_packet SRT_ATR_UNUSED)
{
#ifdef SRT_ENABLE_ENCRYPTION
// Encryption not enabled - do nothing.
if ( getSndCryptoFlags() == EK_NOENC )
return ENCS_CLEAR;
CPacket& packet = *r_packet;
int rc = HaiCrypt_Tx_Data(m_hSndCrypto, (uint8_t*)packet.getHeader(), (uint8_t*)packet.m_pcData, packet.getLength());
int rc = HaiCrypt_Tx_Data(m_hSndCrypto, ((uint8_t*)w_packet.getHeader()), ((uint8_t*)w_packet.m_pcData), w_packet.getLength());
if (rc < 0)
{
return ENCS_FAILED;
@ -749,7 +763,7 @@ EncryptionStatus CCryptoControl::encrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
{
// XXX what happens if the encryption is said to be "succeeded",
// but the length is 0? Shouldn't this be treated as unwanted?
packet.setLength(rc);
w_packet.setLength(rc);
}
return ENCS_CLEAR;
@ -758,14 +772,12 @@ EncryptionStatus CCryptoControl::encrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
#endif
}
EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
srt::EncryptionStatus srt::CCryptoControl::decrypt(CPacket& w_packet SRT_ATR_UNUSED)
{
#ifdef SRT_ENABLE_ENCRYPTION
CPacket& packet = *r_packet;
if (packet.getMsgCryptoFlags() == EK_NOENC)
if (w_packet.getMsgCryptoFlags() == EK_NOENC)
{
HLOGC(mglog.Debug, log << "CPacket::decrypt: packet not encrypted");
HLOGC(cnlog.Debug, log << "CPacket::decrypt: packet not encrypted");
return ENCS_CLEAR; // not encrypted, no need do decrypt, no flags to be modified
}
@ -776,8 +788,8 @@ EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
// We were unaware that the peer has set password,
// but now here we are.
m_RcvKmState = SRT_KM_S_SECURING;
LOGC(mglog.Note, log << "SECURITY UPDATE: Peer has surprised Agent with encryption, but KMX is pending - current packet size="
<< packet.getLength() << " dropped");
LOGC(cnlog.Note, log << "SECURITY UPDATE: Peer has surprised Agent with encryption, but KMX is pending - current packet size="
<< w_packet.getLength() << " dropped");
return ENCS_FAILED;
}
else
@ -786,7 +798,7 @@ EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
// which means that it will be unable to decrypt
// sent payloads anyway.
m_RcvKmState = SRT_KM_S_NOSECRET;
LOGP(mglog.Error, "SECURITY FAILURE: Agent has no PW, but Peer sender has declared one, can't decrypt");
LOGP(cnlog.Warn, "SECURITY FAILURE: Agent has no PW, but Peer sender has declared one, can't decrypt");
// This only informs about the state change; it will be also caught by the condition below
}
}
@ -807,28 +819,28 @@ EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
if (!m_bErrorReported)
{
m_bErrorReported = true;
LOGC(mglog.Error, log << "SECURITY STATUS: " << KmStateStr(m_RcvKmState) << " - can't decrypt packet.");
LOGC(cnlog.Error, log << "SECURITY STATUS: " << KmStateStr(m_RcvKmState) << " - can't decrypt w_packet.");
}
HLOGC(mglog.Debug, log << "Packet still not decrypted, status=" << KmStateStr(m_RcvKmState)
<< " - dropping size=" << packet.getLength());
HLOGC(cnlog.Debug, log << "Packet still not decrypted, status=" << KmStateStr(m_RcvKmState)
<< " - dropping size=" << w_packet.getLength());
return ENCS_FAILED;
}
int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, (uint8_t *)packet.getHeader(), (uint8_t *)packet.m_pcData, packet.getLength());
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(mglog.Error, log << "decrypt ERROR (IPE): HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption");
LOGC(cnlog.Error, log << "decrypt ERROR (IPE): HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption");
// -1: decryption failure
// 0: key not received yet
return ENCS_FAILED;
}
// Otherwise: rc == decrypted text length.
packet.setLength(rc); /* In case clr txt size is different from cipher txt */
w_packet.setLength(rc); /* In case clr txt size is different from cipher txt */
// Decryption succeeded. Update flags.
packet.setMsgCryptoFlags(EK_NOENC);
w_packet.setMsgCryptoFlags(EK_NOENC);
HLOGC(mglog.Debug, log << "decrypt: successfully decrypted, resulting length=" << rc);
HLOGC(cnlog.Debug, log << "decrypt: successfully decrypted, resulting length=" << rc);
return ENCS_CLEAR;
#else
return ENCS_NOTSUP;
@ -836,9 +848,10 @@ EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
}
CCryptoControl::~CCryptoControl()
srt::CCryptoControl::~CCryptoControl()
{
#ifdef SRT_ENABLE_ENCRYPTION
close();
if (m_hSndCrypto)
{
HaiCrypt_Close(m_hSndCrypto);
@ -850,38 +863,3 @@ CCryptoControl::~CCryptoControl()
}
#endif
}
std::string SrtFlagString(int32_t flags)
{
#define LEN(arr) (sizeof (arr)/(sizeof ((arr)[0])))
std::string output;
static std::string namera[] = { "TSBPD-snd", "TSBPD-rcv", "haicrypt", "TLPktDrop", "NAKReport", "ReXmitFlag", "StreamAPI" };
size_t i = 0;
for ( ; i < LEN(namera); ++i )
{
if ( (flags & 1) == 1 )
{
output += "+" + namera[i] + " ";
}
else
{
output += "-" + namera[i] + " ";
}
flags >>= 1;
//if ( flags == 0 )
// break;
}
#undef LEN
if ( flags != 0 )
{
output += "+unknown";
}
return output;
}

View file

@ -13,8 +13,8 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__CRYPTO_H
#define INC__CRYPTO_H
#ifndef INC_SRT_CRYPTO_H
#define INC_SRT_CRYPTO_H
#include <cstring>
#include <string>
@ -28,33 +28,36 @@ written by
#include <haicrypt.h>
#include <hcrypt_msg.h>
#if ENABLE_LOGGING
std::string KmStateStr(SRT_KM_STATE state);
namespace srt_logging
{
extern Logger mglog;
std::string KmStateStr(SRT_KM_STATE state);
#if ENABLE_LOGGING
extern Logger cnlog;
#endif
}
#endif
namespace srt
{
class CUDT;
struct CSrtConfig;
// For KMREQ/KMRSP. Only one field is used.
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(int32_t);
const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(uint32_t);
enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1};
class CCryptoControl
{
//public:
class CUDT* m_parent;
SRTSOCKET m_SocketID;
SRTSOCKET m_SocketID;
size_t m_iSndKmKeyLen; //Key length
size_t m_iRcvKmKeyLen; //Key length from rx KM
size_t m_iSndKmKeyLen; //Key length
size_t m_iRcvKmKeyLen; //Key length from rx KM
// Temporarily allow these to be accessed.
public:
@ -69,7 +72,7 @@ private:
HaiCrypt_Secret m_KmSecret; //Key material shared secret
// Sender
uint64_t m_SndKmLastTime;
sync::steady_clock::time_point m_SndKmLastTime;
struct {
unsigned char Msg[HCRYPT_MSG_KM_MAX_SZ];
size_t MsgLen;
@ -82,6 +85,7 @@ private:
bool m_bErrorReported;
public:
static void globalInit();
bool sendingAllowed()
{
@ -106,9 +110,11 @@ public:
}
private:
#ifdef SRT_ENABLE_ENCRYPTION
void regenCryptoKm(bool sendit, bool bidirectional);
/// Regenerate cryptographic key material.
/// @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).
void regenCryptoKm(CUDT* sock, bool bidirectional);
#endif
public:
@ -119,7 +125,8 @@ public:
void updateKmState(int cmd, size_t srtlen);
// Detailed processing
int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, uint32_t* srtdata_out, ref_t<size_t> r_srtlen, int hsv);
int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, int hsv,
uint32_t srtdata_out[], size_t&);
// This returns:
// 1 - the given payload is the same as the currently used key
@ -158,18 +165,18 @@ public:
void getKmMsg_markSent(size_t ki, bool runtime)
{
#if ENABLE_LOGGING
using srt_logging::mglog;
using srt_logging::cnlog;
#endif
m_SndKmLastTime = CTimer::getTime();
m_SndKmLastTime = sync::steady_clock::now();
if (runtime)
{
m_SndKmMsg[ki].iPeerRetry--;
HLOGC(mglog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " retry=" << m_SndKmMsg[ki].iPeerRetry);
HLOGC(cnlog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " retry=" << m_SndKmMsg[ki].iPeerRetry);
}
else
{
HLOGC(mglog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " STILL IN USE.");
HLOGC(cnlog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " STILL IN USE.");
}
}
@ -191,25 +198,24 @@ public:
return false;
}
CCryptoControl(CUDT* parent, SRTSOCKET id);
CCryptoControl(SRTSOCKET id);
// DEBUG PURPOSES:
std::string CONID() const;
std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen);
bool init(HandshakeSide, bool);
bool init(HandshakeSide, const CSrtConfig&, bool);
void close();
// 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(Whether2RegenKm regen);
/// @return True if the handshake is in progress.
/// 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);
void setCryptoSecret(const HaiCrypt_Secret& secret)
{
m_KmSecret = secret;
//memcpy(&m_KmSecret, &secret, sizeof(m_KmSecret));
}
void setCryptoKeylen(size_t keylen)
@ -218,7 +224,7 @@ public:
m_iRcvKmKeyLen = keylen;
}
bool createCryptoCtx(ref_t<HaiCrypt_Handle> rh, size_t keylen, HaiCrypt_CryptoDir tx);
bool createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir tx, HaiCrypt_Handle& rh);
int getSndCryptoFlags() const
{
@ -253,16 +259,18 @@ public:
/// the encryption will fail.
/// XXX Encryption flags in the PH_MSGNO
/// field in the header must be correctly set before calling.
EncryptionStatus encrypt(ref_t<CPacket> r_packet);
EncryptionStatus encrypt(CPacket& w_packet);
/// Decrypts the packet. If the packet has ENCKEYSPEC part
/// in PH_MSGNO set to EK_NOENC, it does nothing. It decrypts
/// only if the encryption correctly configured, otherwise it
/// fails. After successful decryption, the ENCKEYSPEC part
// in PH_MSGNO is set to EK_NOENC.
EncryptionStatus decrypt(ref_t<CPacket> r_packet);
EncryptionStatus decrypt(CPacket& w_packet);
~CCryptoControl();
};
} // namespace srt
#endif // SRT_CONGESTION_CONTROL_H

View file

@ -50,37 +50,35 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifdef LINUX
#include <sys/epoll.h>
#include <unistd.h>
#endif
#if __APPLE__
#include "TargetConditionals.h"
#endif
#if defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#if defined(__ANDROID__) || defined(ANDROID)
#include <sys/select.h>
#endif
#define SRT_IMPORT_EVENT
#include "platform_sys.h"
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <iterator>
#if defined(__FreeBSD_kernel__)
#include <sys/event.h>
#endif
#include "common.h"
#include "epoll.h"
#include "logging.h"
#include "udt.h"
using namespace std;
using namespace srt::sync;
#if ENABLE_HEAVY_LOGGING
namespace srt {
static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0);
}
#endif
namespace srt_logging
{
extern Logger mglog;
extern Logger eilog, ealog;
}
using namespace srt_logging;
@ -89,20 +87,21 @@ using namespace srt_logging;
#define IF_DIRNAME(tested, flag, name) (tested & flag ? name : "")
#endif
CEPoll::CEPoll():
srt::CEPoll::CEPoll():
m_iIDSeed(0)
{
CGuard::createMutex(m_EPollLock);
// Exception -> CUDTUnited ctor.
setupMutex(m_EPollLock, "EPoll");
}
CEPoll::~CEPoll()
srt::CEPoll::~CEPoll()
{
CGuard::releaseMutex(m_EPollLock);
releaseMutex(m_EPollLock);
}
int CEPoll::create()
int srt::CEPoll::create(CEPollDesc** pout)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
if (++ m_iIDSeed >= 0x7FFFFFFF)
m_iIDSeed = 0;
@ -114,7 +113,31 @@ int CEPoll::create()
int localid = 0;
#ifdef LINUX
localid = epoll_create(1024);
// NOTE: epoll_create1() and EPOLL_CLOEXEC were introduced in GLIBC-2.9.
// So earlier versions of GLIBC, must use epoll_create() and set
// FD_CLOEXEC on the file descriptor returned by it after the fact.
#if defined(EPOLL_CLOEXEC)
int flags = 0;
#if ENABLE_SOCK_CLOEXEC
flags |= EPOLL_CLOEXEC;
#endif
localid = epoll_create1(flags);
#else
localid = epoll_create(1);
#if ENABLE_SOCK_CLOEXEC
if (localid != -1)
{
int fdFlags = fcntl(localid, F_GETFD);
if (fdFlags != -1)
{
fdFlags |= FD_CLOEXEC;
fcntl(localid, F_SETFD, fdFlags);
}
}
#endif
#endif
/* Possible reasons of -1 error:
EMFILE: The per-user limit on the number of epoll instances imposed by /proc/sys/fs/epoll/max_user_instances was encountered.
ENFILE: The system limit on the total number of open files has been reached.
@ -122,25 +145,82 @@ ENOMEM: There was insufficient memory to create the kernel object.
*/
if (localid < 0)
throw CUDTException(MJ_SETUP, MN_NONE, errno);
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
localid = kqueue();
if (localid < 0)
throw CUDTException(MJ_SETUP, MN_NONE, errno);
#else
// on Solaris, use /dev/poll
// TODO: Solaris, use port_getn()
// https://docs.oracle.com/cd/E86824_01/html/E54766/port-get-3c.html
// on Windows, select
#endif
pair<map<int, CEPollDesc>::iterator, bool> res = m_mPolls.insert(make_pair(m_iIDSeed, CEPollDesc(m_iIDSeed, localid)));
if (!res.second) // Insertion failed (no memory?)
throw CUDTException(MJ_SETUP, MN_NONE);
if (pout)
*pout = &res.first->second;
return m_iIDSeed;
}
int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
int srt::CEPoll::clear_usocks(int eid)
{
CGuard pg(m_EPollLock);
// This should remove all SRT sockets from given eid.
ScopedLock pg (m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
CEPollDesc& d = p->second;
d.clearAll();
return 0;
}
void srt::CEPoll::clear_ready_usocks(CEPollDesc& d, int direction)
{
if ((direction & ~SRT_EPOLL_EVENTTYPES) != 0)
{
// This is internal function, so simply report an IPE on incorrect usage.
LOGC(eilog.Error, log << "CEPoll::clear_ready_usocks: IPE, event flags exceed event types: " << direction);
return;
}
ScopedLock pg (m_EPollLock);
vector<SRTSOCKET> cleared;
CEPollDesc::enotice_t::iterator i = d.enotice_begin();
while (i != d.enotice_end())
{
IF_HEAVY_LOGGING(SRTSOCKET subsock = i->fd);
SRTSOCKET rs = d.clearEventSub(i++, direction);
// This function returns:
// - a valid socket - if there are no other subscription after 'direction' was cleared
// - SRT_INVALID_SOCK otherwise
// Valid sockets should be collected as sockets that no longer
// have a subscribed event should be deleted from subscriptions.
if (rs != SRT_INVALID_SOCK)
{
HLOGC(eilog.Debug, log << "CEPoll::clear_ready_usocks: @" << rs << " got all subscription cleared");
cleared.push_back(rs);
}
else
{
HLOGC(eilog.Debug, log << "CEPoll::clear_ready_usocks: @" << subsock << " is still subscribed");
}
}
for (size_t i = 0; i < cleared.size(); ++i)
d.removeSubscription(cleared[i]);
}
int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
{
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
@ -155,18 +235,18 @@ int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
else
{
ev.events = 0;
if (*events & UDT_EPOLL_IN)
if (*events & SRT_EPOLL_IN)
ev.events |= EPOLLIN;
if (*events & UDT_EPOLL_OUT)
if (*events & SRT_EPOLL_OUT)
ev.events |= EPOLLOUT;
if (*events & UDT_EPOLL_ERR)
if (*events & SRT_EPOLL_ERR)
ev.events |= EPOLLERR;
}
ev.data.fd = s;
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_ADD, s, &ev) < 0)
throw CUDTException();
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
struct kevent ke[2];
int num = 0;
@ -177,11 +257,11 @@ int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
}
else
{
if (*events & UDT_EPOLL_IN)
if (*events & SRT_EPOLL_IN)
{
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
}
if (*events & UDT_EPOLL_OUT)
if (*events & SRT_EPOLL_OUT)
{
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
}
@ -190,6 +270,10 @@ int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
throw CUDTException();
#else
// fake use 'events' to prevent warning. Remove when implemented.
(void)events;
(void)s;
#ifdef _MSC_VER
// Microsoft Visual Studio doesn't support the #warning directive - nonstandard anyway.
// Use #pragma message with the same text.
@ -206,9 +290,9 @@ int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
return 0;
}
int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
int srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
@ -218,7 +302,7 @@ int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
epoll_event ev; // ev is ignored, for compatibility with old Linux kernel only.
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_DEL, s, &ev) < 0)
throw CUDTException();
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
struct kevent ke;
//
@ -237,9 +321,10 @@ int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
}
// Need this to atomically modify polled events (ex: remove write/keep read)
int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
int srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
IF_HEAVY_LOGGING(ostringstream evd);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
@ -250,9 +335,12 @@ int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
int32_t evts = events ? *events : uint32_t(SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR);
bool edgeTriggered = evts & SRT_EPOLL_ET;
evts &= ~SRT_EPOLL_ET;
// et_evts = all events, if SRT_EPOLL_ET, or only those that are always ET otherwise.
int32_t et_evts = edgeTriggered ? evts : evts & SRT_EPOLL_ETONLY;
if (evts)
{
pair<CEPollDesc::ewatch_t::iterator, bool> iter_new = d.addWatch(u, evts, edgeTriggered);
pair<CEPollDesc::ewatch_t::iterator, bool> iter_new = d.addWatch(u, evts, et_evts);
CEPollDesc::Wait& wait = iter_new.first->second;
if (!iter_new.second)
{
@ -260,6 +348,7 @@ int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
// parameter, but others are probably unchanged. Change them
// forcefully and take out notices that are no longer valid.
const int removable = wait.watch & ~evts;
IF_HEAVY_LOGGING(PrintEpollEvent(evd, evts & (~wait.watch)));
// Check if there are any events that would be removed.
// If there are no removed events watched (for example, when
@ -272,11 +361,16 @@ int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
// Update the watch configuration, including edge
wait.watch = evts;
if (edgeTriggered)
wait.edge = evts;
wait.edge = et_evts;
// Now it should look exactly like newly added
// and the state is also updated
HLOGC(ealog.Debug, log << "srt_epoll_update_usock: UPDATED E" << eid << " for @" << u << " +" << evd.str());
}
else
{
IF_HEAVY_LOGGING(PrintEpollEvent(evd, evts));
HLOGC(ealog.Debug, log << "srt_epoll_update_usock: ADDED E" << eid << " for @" << u << " " << evd.str());
}
const int newstate = wait.watch & wait.state;
@ -287,20 +381,21 @@ int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
}
else if (edgeTriggered)
{
// Specified only SRT_EPOLL_ET flag, but no event flag. Error.
LOGC(ealog.Error, log << "srt_epoll_update_usock: Specified only SRT_EPOLL_ET flag, but no event flag. Error.");
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
else
{
// Update with no events means to remove subscription
HLOGC(ealog.Debug, log << "srt_epoll_update_usock: REMOVED E" << eid << " socket @" << u);
d.removeSubscription(u);
}
return 0;
}
int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
int srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
@ -315,18 +410,18 @@ int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
else
{
ev.events = 0;
if (*events & UDT_EPOLL_IN)
if (*events & SRT_EPOLL_IN)
ev.events |= EPOLLIN;
if (*events & UDT_EPOLL_OUT)
if (*events & SRT_EPOLL_OUT)
ev.events |= EPOLLOUT;
if (*events & UDT_EPOLL_ERR)
if (*events & SRT_EPOLL_ERR)
ev.events |= EPOLLERR;
}
ev.data.fd = s;
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_MOD, s, &ev) < 0)
throw CUDTException();
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
struct kevent ke[2];
int num = 0;
@ -345,17 +440,23 @@ int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
}
else
{
if (*events & UDT_EPOLL_IN)
if (*events & SRT_EPOLL_IN)
{
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
}
if (*events & UDT_EPOLL_OUT)
if (*events & SRT_EPOLL_OUT)
{
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
}
}
if (kevent(p->second.m_iLocalID, ke, num, NULL, 0, NULL) < 0)
throw CUDTException();
#else
// fake use 'events' to prevent warning. Remove when implemented.
(void)events;
(void)s;
#endif
// Assuming add is used if not inserted
// p->second.m_sLocals.insert(s);
@ -363,9 +464,9 @@ int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
return 0;
}
int CEPoll::setflags(const int eid, int32_t flags)
int srt::CEPoll::setflags(const int eid, int32_t flags)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
@ -388,7 +489,7 @@ int CEPoll::setflags(const int eid, int32_t flags)
return oflags;
}
int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut)
int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut)
{
// It is allowed to call this function witn fdsSize == 0
// and therefore also NULL fdsSet. This will then only report
@ -396,12 +497,12 @@ int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t m
if (fdsSize < 0 || (fdsSize > 0 && !fdsSet))
throw CUDTException(MJ_NOTSUP, MN_INVAL);
int64_t entertime = CTimer::getTime();
steady_clock::time_point entertime = steady_clock::now();
while (true)
{
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
@ -410,7 +511,7 @@ int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t m
if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty())
{
// Empty EID is not allowed, report error.
throw CUDTException(MJ_NOTSUP, MN_INVAL);
throw CUDTException(MJ_NOTSUP, MN_EEMPTY);
}
if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK) && (fdsSet == NULL || fdsSize == 0))
@ -444,16 +545,16 @@ int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t m
return total;
}
if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000)))
if ((msTimeOut >= 0) && (count_microseconds(srt::sync::steady_clock::now() - entertime) >= msTimeOut * int64_t(1000)))
break; // official wait does: throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
CTimer::waitForEvent();
CGlobEvent::waitForEvent();
}
return 0;
}
int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* lwfds)
int srt::CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* lwfds)
{
// if all fields is NULL and waiting time is infinite, then this would be a deadlock
if (!readfds && !writefds && !lrfds && !lwfds && (msTimeOut < 0))
@ -467,18 +568,16 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
int total = 0;
int64_t entertime = CTimer::getTime();
HLOGC(mglog.Debug, log << "CEPoll::wait: START for eid=" << eid);
srt::sync::steady_clock::time_point entertime = srt::sync::steady_clock::now();
while (true)
{
{
CGuard epollock(m_EPollLock);
ScopedLock epollock(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
{
LOGC(ealog.Error, log << "EID:" << eid << " INVALID.");
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
}
@ -487,7 +586,9 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty() && ed.m_sLocals.empty())
{
// Empty EID is not allowed, report error.
throw CUDTException(MJ_NOTSUP, MN_INVAL);
//throw CUDTException(MJ_NOTSUP, MN_INVAL);
LOGC(ealog.Error, log << "EID:" << eid << " no sockets to check, this would deadlock");
throw CUDTException(MJ_NOTSUP, MN_EEMPTY, 0);
}
if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK))
@ -507,13 +608,13 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
{
++it_next;
IF_HEAVY_LOGGING(++total_noticed);
if (readfds && ((it->events & UDT_EPOLL_IN) || (it->events & UDT_EPOLL_ERR)))
if (readfds && ((it->events & SRT_EPOLL_IN) || (it->events & SRT_EPOLL_ERR)))
{
if (readfds->insert(it->fd).second)
++total;
}
if (writefds && ((it->events & UDT_EPOLL_OUT) || (it->events & UDT_EPOLL_ERR)))
if (writefds && ((it->events & SRT_EPOLL_OUT) || (it->events & SRT_EPOLL_ERR)))
{
if (writefds->insert(it->fd).second)
++total;
@ -530,13 +631,14 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: REPORTED " << total << "/" << total_noticed
HLOGC(ealog.Debug, log << "CEPoll::wait: REPORTED " << total << "/" << total_noticed
<< debug_sockets.str());
if (lrfds || lwfds)
if ((lrfds || lwfds) && !ed.m_sLocals.empty())
{
#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);
@ -554,11 +656,12 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
++ total;
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: LINUX: picking up " << (total - prev_total) << " ready fds.");
HLOGC(ealog.Debug, log << "CEPoll::wait: LINUX: picking up " << (total - prev_total) << " ready fds.");
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
struct timespec tmout = {0, 0};
const int max_events = ed.m_sLocals.size();
SRT_ASSERT(max_events > 0);
struct kevent ke[max_events];
int nfds = kevent(ed.m_iLocalID, NULL, 0, ke, max_events, &tmout);
@ -578,7 +681,7 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: Darwin/BSD: picking up " << (total - prev_total) << " ready fds.");
HLOGC(ealog.Debug, log << "CEPoll::wait: Darwin/BSD: picking up " << (total - prev_total) << " ready fds.");
#else
//currently "select" is used for all non-Linux platforms.
@ -623,34 +726,127 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: select(otherSYS): picking up " << (total - prev_total) << " ready fds.");
HLOGC(ealog.Debug, log << "CEPoll::wait: select(otherSYS): picking up " << (total - prev_total) << " ready fds.");
#endif
}
} // END-LOCK: m_EPollLock
HLOGC(mglog.Debug, log << "CEPoll::wait: Total of " << total << " READY SOCKETS");
HLOGC(ealog.Debug, log << "CEPoll::wait: Total of " << total << " READY SOCKETS");
if (total > 0)
return total;
if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000)))
if ((msTimeOut >= 0) && (count_microseconds(srt::sync::steady_clock::now() - entertime) >= msTimeOut * int64_t(1000)))
{
HLOGP(mglog.Debug, "... not waiting longer - timeout");
HLOGC(ealog.Debug, log << "EID:" << eid << ": TIMEOUT.");
throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
}
CTimer::EWait wt ATR_UNUSED = CTimer::waitForEvent();
HLOGC(mglog.Debug, log << "CEPoll::wait: EVENT WAITING: "
<< (wt == CTimer::WT_TIMEOUT ? "CHECKPOINT" : wt == CTimer::WT_EVENT ? "TRIGGERED" : "ERROR"));
const bool wait_signaled SRT_ATR_UNUSED = CGlobEvent::waitForEvent();
HLOGC(ealog.Debug, log << "CEPoll::wait: EVENT WAITING: "
<< (wait_signaled ? "TRIGGERED" : "CHECKPOINT"));
}
return 0;
}
int CEPoll::release(const int eid)
int srt::CEPoll::swait(CEPollDesc& d, map<SRTSOCKET, int>& st, int64_t msTimeOut, bool report_by_exception)
{
CGuard pg(m_EPollLock);
{
ScopedLock lg (m_EPollLock);
if (!d.flags(SRT_EPOLL_ENABLE_EMPTY) && d.watch_empty() && msTimeOut < 0)
{
// no socket is being monitored, this may be a deadlock
LOGC(ealog.Error, log << "EID:" << d.m_iID << " no sockets to check, this would deadlock");
if (report_by_exception)
throw CUDTException(MJ_NOTSUP, MN_EEMPTY, 0);
return -1;
}
}
st.clear();
steady_clock::time_point entertime = steady_clock::now();
while (true)
{
{
// Not extracting separately because this function is
// for internal use only and we state that the eid could
// not be deleted or changed the target CEPollDesc in the
// meantime.
// Here we only prevent the pollset be updated simultaneously
// with unstable reading.
ScopedLock lg (m_EPollLock);
if (!d.flags(SRT_EPOLL_ENABLE_EMPTY) && d.watch_empty())
{
// Empty EID is not allowed, report error.
throw CUDTException(MJ_NOTSUP, MN_EEMPTY);
}
if (!d.m_sLocals.empty())
{
// XXX Add error log
// uwait should not be used with EIDs subscribed to system sockets
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
bool empty = d.enotice_empty();
if (!empty || msTimeOut == 0)
{
IF_HEAVY_LOGGING(ostringstream singles);
// If msTimeOut == 0, it means that we need the information
// immediately, we don't want to wait. Therefore in this case
// report also when none is ready.
int total = 0; // This is a list, so count it during iteration
CEPollDesc::enotice_t::iterator i = d.enotice_begin();
while (i != d.enotice_end())
{
++total;
st[i->fd] = i->events;
IF_HEAVY_LOGGING(singles << "@" << i->fd << ":");
IF_HEAVY_LOGGING(PrintEpollEvent(singles, i->events, i->parent->edgeOnly()));
const bool edged SRT_ATR_UNUSED = d.checkEdge(i++); // NOTE: potentially deletes `i`
IF_HEAVY_LOGGING(singles << (edged ? "<^> " : " "));
}
// Logging into 'singles' because it notifies as to whether
// the edge-triggered event has been cleared
HLOGC(ealog.Debug, log << "E" << d.m_iID << " rdy=" << total << ": "
<< singles.str()
<< " TRACKED: " << d.DisplayEpollWatch());
return total;
}
// Don't report any updates because this check happens
// extremely often.
}
if ((msTimeOut >= 0) && ((steady_clock::now() - entertime) >= microseconds_from(msTimeOut * int64_t(1000))))
{
HLOGC(ealog.Debug, log << "EID:" << d.m_iID << ": TIMEOUT.");
if (report_by_exception)
throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
return 0; // meaning "none is ready"
}
CGlobEvent::waitForEvent();
}
return 0;
}
bool srt::CEPoll::empty(const CEPollDesc& d) const
{
ScopedLock lg (m_EPollLock);
return d.watch_empty();
}
int srt::CEPoll::release(const int eid)
{
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator i = m_mPolls.find(eid);
if (i == m_mPolls.end())
@ -659,7 +855,7 @@ int CEPoll::release(const int eid)
#ifdef LINUX
// release local/system epoll descriptor
::close(i->second.m_iLocalID);
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
::close(i->second.m_iLocalID);
#endif
@ -669,16 +865,29 @@ int CEPoll::release(const int eid)
}
int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int events, const bool enable)
int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int events, const bool enable)
{
// As event flags no longer contain only event types, check now.
if ((events & ~SRT_EPOLL_EVENTTYPES) != 0)
{
LOGC(eilog.Fatal, log << "epoll/update: IPE: 'events' parameter shall not contain special flags!");
return -1; // still, ignored.
}
int nupdated = 0;
vector<int> lost;
CGuard pg(m_EPollLock);
IF_HEAVY_LOGGING(ostringstream debug);
IF_HEAVY_LOGGING(debug << "epoll/update: @" << uid << " " << (enable ? "+" : "-"));
IF_HEAVY_LOGGING(PrintEpollEvent(debug, events));
ScopedLock pg (m_EPollLock);
for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i)
{
map<int, CEPollDesc>::iterator p = m_mPolls.find(*i);
if (p == m_mPolls.end())
{
HLOGC(eilog.Note, log << "epoll/update: E" << *i << " was deleted in the meantime");
// EID invalid, though still present in the socket's subscriber list
// (dangling in the socket). Postpone to fix the subscruption and continue.
lost.push_back(*i);
@ -692,9 +901,12 @@ int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int e
if (!pwait)
{
// As this is mapped in the socket's data, it should be impossible.
LOGC(eilog.Error, log << "epoll/update: IPE: update struck E"
<< (*i) << " which is NOT SUBSCRIBED to @" << uid);
continue;
}
IF_HEAVY_LOGGING(string tracking = " TRACKING: " + ed.DisplayEpollWatch());
// compute new states
// New state to be set into the permanent state
@ -704,13 +916,21 @@ int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int e
// compute states changes!
int changes = pwait->state ^ newstate; // oldState XOR newState
if (!changes)
{
HLOGC(eilog.Debug, log << debug.str() << ": E" << (*i)
<< tracking << " NOT updated: no changes");
continue; // no changes!
}
// assign new state
pwait->state = newstate;
// filter change relating what is watching
changes &= pwait->watch;
if (!changes)
{
HLOGC(eilog.Debug, log << debug.str() << ": E" << (*i)
<< tracking << " NOT updated: not subscribed");
continue; // no change watching
}
// set events changes!
// This function will update the notice object associated with
@ -718,10 +938,75 @@ int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int e
// - if enable, it will set event flags, possibly in a new notice object
// - if !enable, it will clear event flags, possibly remove notice if resulted in 0
ed.updateEventNotice(*pwait, uid, events, enable);
++nupdated;
HLOGC(eilog.Debug, log << debug.str() << ": E" << (*i)
<< " TRACKING: " << ed.DisplayEpollWatch());
}
for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i)
eids.erase(*i);
return 0;
return nupdated;
}
// Debug use only.
#if ENABLE_HEAVY_LOGGING
namespace srt
{
static ostream& PrintEpollEvent(ostream& os, int events, int et_events)
{
static pair<int, const char*> const namemap [] = {
make_pair(SRT_EPOLL_IN, "R"),
make_pair(SRT_EPOLL_OUT, "W"),
make_pair(SRT_EPOLL_ERR, "E"),
make_pair(SRT_EPOLL_UPDATE, "U")
};
int N = Size(namemap);
for (int i = 0; i < N; ++i)
{
if (events & namemap[i].first)
{
os << "[";
if (et_events & namemap[i].first)
os << "^";
os << namemap[i].second << "]";
}
}
return os;
}
string DisplayEpollResults(const std::map<SRTSOCKET, int>& sockset)
{
typedef map<SRTSOCKET, int> fmap_t;
ostringstream os;
for (fmap_t::const_iterator i = sockset.begin(); i != sockset.end(); ++i)
{
os << "@" << i->first << ":";
PrintEpollEvent(os, i->second);
os << " ";
}
return os.str();
}
string CEPollDesc::DisplayEpollWatch()
{
ostringstream os;
for (ewatch_t::const_iterator i = m_USockWatchState.begin(); i != m_USockWatchState.end(); ++i)
{
os << "@" << i->first << ":";
PrintEpollEvent(os, i->second.watch, i->second.edge);
os << " ";
}
return os.str();
}
} // namespace srt
#endif

269
trunk/3rdparty/srt-1-fit/srtcore/epoll.h vendored Executable file → Normal file
View file

@ -50,8 +50,8 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_EPOLL_H__
#define __UDT_EPOLL_H__
#ifndef INC_SRT_EPOLL_H
#define INC_SRT_EPOLL_H
#include <map>
@ -59,8 +59,15 @@ modified by
#include <list>
#include "udt.h"
namespace srt
{
struct CEPollDesc
class CUDT;
class CRendezvousQueue;
class CUDTGroup;
class CEPollDesc
{
const int m_iID; // epoll ID
@ -86,40 +93,62 @@ struct CEPollDesc
{
/// Events the subscriber is interested with. Only those will be
/// regarded when updating event flags.
int watch;
int32_t watch;
/// Which events should be edge-triggered. When the event isn't
/// mentioned in `watch`, this bit flag is disregarded. Otherwise
/// it means that the event is to be waited for persistent state
/// if this flag is not present here, and for edge trigger, if
/// the flag is present here.
int edge;
int32_t edge;
/// The current persistent state. This is usually duplicated in
/// a dedicated state object in `m_USockEventNotice`, however the state
/// here will stay forever as is, regardless of the edge/persistent
/// subscription mode for the event.
int state;
int32_t state;
/// The iterator to `m_USockEventNotice` container that contains the
/// event notice object for this subscription, or the value from
/// `nullNotice()` if there is no such object.
enotice_t::iterator notit;
Wait(int sub, bool etr, enotice_t::iterator i)
Wait(explicit_t<int32_t> sub, explicit_t<int32_t> etr, enotice_t::iterator i)
:watch(sub)
,edge(etr ? sub : 0)
,edge(etr)
,state(0)
,notit(i)
{
}
int edgeOnly() { return edge & watch; }
/// Clear all flags for given direction from the notices
/// and subscriptions, and checks if this made the event list
/// for this watch completely empty.
/// @param direction event type that has to be cleared
/// @return true, if this cleared the last event (the caller
/// want to remove the subscription for this socket)
bool clear(int32_t direction)
{
if (watch & direction)
{
watch &= ~direction;
edge &= ~direction;
state &= ~direction;
return watch == 0;
}
return false;
}
};
typedef std::map<SRTSOCKET, Wait> ewatch_t;
private:
#if ENABLE_HEAVY_LOGGING
std::string DisplayEpollWatch();
#endif
/// Sockets that are subscribed for events in this eid.
ewatch_t m_USockWatchState;
@ -135,7 +164,10 @@ private:
enotice_t::iterator nullNotice() { return m_USockEventNotice.end(); }
public:
// Only CEPoll class should have access to it.
// Guarding private access to the class is not necessary
// within the epoll module.
friend class CEPoll;
CEPollDesc(int id, int localID)
: m_iID(id)
@ -147,13 +179,13 @@ public:
static const int32_t EF_NOCHECK_EMPTY = 1 << 0;
static const int32_t EF_CHECK_REP = 1 << 1;
int32_t flags() { return m_Flags; }
bool flags(int32_t f) { return (m_Flags & f) != 0; }
int32_t flags() const { return m_Flags; }
bool flags(int32_t f) const { return (m_Flags & f) != 0; }
void set_flags(int32_t flg) { m_Flags |= flg; }
void clr_flags(int32_t flg) { m_Flags &= ~flg; }
// Container accessors for ewatch_t.
bool watch_empty() { return m_USockWatchState.empty(); }
bool watch_empty() const { return m_USockWatchState.empty(); }
Wait* watch_find(SRTSOCKET sock)
{
ewatch_t::iterator i = m_USockWatchState.find(sock);
@ -165,13 +197,16 @@ public:
// Container accessors for enotice_t.
enotice_t::iterator enotice_begin() { return m_USockEventNotice.begin(); }
enotice_t::iterator enotice_end() { return m_USockEventNotice.end(); }
enotice_t::const_iterator enotice_begin() const { return m_USockEventNotice.begin(); }
enotice_t::const_iterator enotice_end() const { return m_USockEventNotice.end(); }
bool enotice_empty() const { return m_USockEventNotice.empty(); }
const int m_iLocalID; // local system epoll ID
std::set<SYSSOCKET> m_sLocals; // set of local (non-UDT) descriptors
std::pair<ewatch_t::iterator, bool> addWatch(SRTSOCKET sock, int32_t events, bool edgeTrg)
std::pair<ewatch_t::iterator, bool> addWatch(SRTSOCKET sock, explicit_t<int32_t> events, explicit_t<int32_t> et_events)
{
return m_USockWatchState.insert(std::make_pair(sock, Wait(events, edgeTrg, nullNotice())));
return m_USockWatchState.insert(std::make_pair(sock, Wait(events, et_events, nullNotice())));
}
void addEventNotice(Wait& wait, SRTSOCKET sock, int events)
@ -224,6 +259,12 @@ public:
m_USockWatchState.erase(i);
}
void clearAll()
{
m_USockEventNotice.clear();
m_USockWatchState.clear();
}
void removeExistingNotices(Wait& wait)
{
m_USockEventNotice.erase(wait.notit);
@ -281,12 +322,44 @@ public:
}
return false;
}
/// This should work in a loop around the notice container of
/// the given eid container and clear out the notice for
/// particular event type. If this has cleared effectively the
/// last existing event, it should return the socket id
/// so that the caller knows to remove it also from subscribers.
///
/// @param i iterator in the notice container
/// @param event event type to be cleared
/// @retval (socket) Socket to be removed from subscriptions
/// @retval SRT_INVALID_SOCK Nothing to be done (associated socket
/// still has other subscriptions)
SRTSOCKET clearEventSub(enotice_t::iterator i, int event)
{
// We need to remove the notice and subscription
// for this event. The 'i' iterator is safe to
// delete, even indirectly.
// This works merely like checkEdge, just on request to clear the
// identified event, if found.
if (i->events & event)
{
// The notice has a readiness flag on this event.
// This means that there exists also a subscription.
Wait* w = i->parent;
if (w->clear(event))
return i->fd;
}
return SRT_INVALID_SOCK;
}
};
class CEPoll
{
friend class CUDT;
friend class CRendezvousQueue;
friend class srt::CUDT;
friend class srt::CUDTGroup;
friend class srt::CRendezvousQueue;
public:
CEPoll();
@ -294,90 +367,122 @@ public:
public: // for CUDTUnited API
/// create a new EPoll.
/// @return new EPoll ID if success, otherwise an error number.
/// create a new EPoll.
/// @return new EPoll ID if success, otherwise an error number.
int create(CEPollDesc** ppd = 0);
int create();
/// add a UDT socket to an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT Socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
/// delete all user sockets (SRT sockets) from an EPoll
/// @param [in] eid EPoll ID.
/// @return 0
int clear_usocks(int eid);
int add_usock(const int eid, const SRTSOCKET& u, const int* events = NULL) { return update_usock(eid, u, events); }
/// add a system socket to an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] s system Socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
/// add a system socket to an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] s system Socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL);
/// remove a UDT socket event from an EPoll; socket will be removed if no events to watch.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @return 0 if success, otherwise an error number.
int remove_usock(const int eid, const SRTSOCKET& u) { static const int Null(0); return update_usock(eid, u, &Null);}
/// remove a system socket event from an EPoll; socket will be removed if no events to watch.
/// @param [in] eid EPoll ID.
/// @param [in] s system socket ID.
/// @return 0 if success, otherwise an error number.
/// remove a system socket event from an EPoll; socket will be removed if no events to watch.
/// @param [in] eid EPoll ID.
/// @param [in] s system socket ID.
/// @return 0 if success, otherwise an error number.
int remove_ssock(const int eid, const SYSSOCKET& s);
/// update a UDT socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
/// update a UDT socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int update_usock(const int eid, const SRTSOCKET& u, const int* events);
/// update a system socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
/// update a system socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL);
/// wait for EPoll events or timeout.
/// @param [in] eid EPoll ID.
/// @param [out] readfds UDT sockets available for reading.
/// @param [out] writefds UDT sockets available for writing.
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @param [out] lrfds system file descriptors for reading.
/// @param [out] lwfds system file descriptors for writing.
/// @return number of sockets available for IO.
/// wait for EPoll events or timeout.
/// @param [in] eid EPoll ID.
/// @param [out] readfds UDT sockets available for reading.
/// @param [out] writefds UDT sockets available for writing.
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @param [out] lrfds system file descriptors for reading.
/// @param [out] lwfds system file descriptors for writing.
/// @return number of sockets available for IO.
int wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds, std::set<SYSSOCKET>* lwfds);
/// wait for EPoll events or timeout optimized with explicit EPOLL_ERR event and the edge mode option.
/// @param [in] eid EPoll ID.
/// @param [out] fdsSet array of user socket events (SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR).
/// @param [int] fdsSize of fds array
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @return total of available events in the epoll system (can be greater than fdsSize)
typedef std::map<SRTSOCKET, int> fmap_t;
/// Lightweit and more internal-reaching version of `uwait` for internal use only.
/// This function wait for sockets to be ready and reports them in `st` map.
///
/// @param d the internal structure of the epoll container
/// @param st output container for the results: { socket_type, event }
/// @param msTimeOut timeout after which return with empty output is allowed
/// @param report_by_exception if true, errors will result in exception intead of returning -1
/// @retval -1 error occurred
/// @retval >=0 number of ready sockets (actually size of `st`)
int swait(CEPollDesc& d, fmap_t& st, int64_t msTimeOut, bool report_by_exception = true);
/// Empty subscription check - for internal use only.
bool empty(const CEPollDesc& d) const;
/// Reports which events are ready on the given socket.
/// @param mp socket event map retirned by `swait`
/// @param sock which socket to ask
/// @return event flags for given socket, or 0 if none
static int ready(const fmap_t& mp, SRTSOCKET sock)
{
fmap_t::const_iterator y = mp.find(sock);
if (y == mp.end())
return 0;
return y->second;
}
/// Reports whether socket is ready for given event.
/// @param mp socket event map retirned by `swait`
/// @param sock which socket to ask
/// @param event which events it should be ready for
/// @return true if the given socket is ready for given event
static bool isready(const fmap_t& mp, SRTSOCKET sock, SRT_EPOLL_OPT event)
{
return (ready(mp, sock) & event) != 0;
}
// Could be a template directly, but it's now hidden in the imp file.
void clear_ready_usocks(CEPollDesc& d, int direction);
/// wait for EPoll events or timeout optimized with explicit EPOLL_ERR event and the edge mode option.
/// @param [in] eid EPoll ID.
/// @param [out] fdsSet array of user socket events (SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR).
/// @param [int] fdsSize of fds array
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @return total of available events in the epoll system (can be greater than fdsSize)
int uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
/// close and release an EPoll.
/// @param [in] eid EPoll ID.
/// @return 0 if success, otherwise an error number.
/// close and release an EPoll.
/// @param [in] eid EPoll ID.
/// @return 0 if success, otherwise an error number.
int release(const int eid);
public: // for CUDT to acknowledge IO status
/// Update events available for a UDT socket.
/// @param [in] uid UDT socket ID.
/// @param [in] eids EPoll IDs to be set
/// @param [in] events Combination of events to update
/// @param [in] enable true -> enable, otherwise disable
/// @return 0 if success, otherwise an error number
/// Update events available for a UDT socket. At the end this function
/// counts the number of updated EIDs with given events.
/// @param [in] uid UDT socket ID.
/// @param [in] eids EPoll IDs to be set
/// @param [in] events Combination of events to update
/// @param [in] enable true -> enable, otherwise disable
/// @return -1 if invalid events, otherwise the number of changes
int update_events(const SRTSOCKET& uid, std::set<int>& eids, int events, bool enable);
@ -385,11 +490,17 @@ public: // for CUDT to acknowledge IO status
private:
int m_iIDSeed; // seed to generate a new ID
pthread_mutex_t m_SeedLock;
srt::sync::Mutex m_SeedLock;
std::map<int, CEPollDesc> m_mPolls; // all epolls
pthread_mutex_t m_EPollLock;
mutable srt::sync::Mutex m_EPollLock;
};
#if ENABLE_HEAVY_LOGGING
std::string DisplayEpollResults(const std::map<SRTSOCKET, int>& sockset);
#endif
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

View file

@ -9,8 +9,8 @@
*/
#ifndef INC__SRT_FEC_H
#define INC__SRT_FEC_H
#ifndef INC_SRT_FEC_H
#define INC_SRT_FEC_H
#include <string>
#include <map>
@ -19,6 +19,8 @@
#include "packetfilter_api.h"
namespace srt {
class FECFilterBuiltin: public SrtPacketFilterBase
{
SrtFilterConfig cfg;
@ -68,6 +70,12 @@ public:
SINGLE // Horizontal-only with no recursion
};
static Type FlipType(Type t)
{
SRT_ASSERT(t != SINGLE);
return (t == HORIZ) ? VERT : HORIZ;
}
};
struct RcvGroup: Group
@ -183,21 +191,43 @@ private:
// Receiving
void CheckLargeDrop(int32_t seqno);
int ExtendRows(int rowx);
int ExtendColumns(int colgx);
void MarkCellReceived(int32_t seq);
bool HangHorizontal(const CPacket& pkt, bool fec_ctl, loss_seqs_t& irrecover);
bool HangVertical(const CPacket& pkt, signed char fec_colx, loss_seqs_t& irrecover);
size_t ExtendRows(size_t rowx);
size_t ExtendColumns(size_t colgx);
enum ECellReceived
{
CELL_RECEIVED, //< mark cell for a received packet (no matter current value)
CELL_EXTEND, //< just make sure there's a place for a packet, set false if not
CELL_REMOVE //< even if a packet was marked true, remove the cell existence confirmation
};
void MarkCellReceived(int32_t seq, ECellReceived recv = CELL_RECEIVED);
enum EHangStatus
{
HANG_NOTDONE,
HANG_SUCCESS,
HANG_PAST,
HANG_CRAZY
};
friend bool operator <(FECFilterBuiltin::EHangStatus a, FECFilterBuiltin::EHangStatus b)
{
return int(a) < int(b);
}
EHangStatus HangHorizontal(const CPacket& pkt, bool fec_ctl, loss_seqs_t& irrecover);
EHangStatus HangVertical(const CPacket& pkt, signed char fec_colx, loss_seqs_t& irrecover);
void ClipControlPacket(Group& g, const CPacket& pkt);
void ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt);
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);
static void TranslateLossRecords(const std::set<int32_t>& loss, loss_seqs_t& irrecover);
void RcvCheckDismissColumn(int32_t seqno, int colgx, loss_seqs_t& irrecover);
int RcvGetRowGroupIndex(int32_t seq);
int RcvGetColumnGroupIndex(int32_t seq);
int RcvGetRowGroupIndex(int32_t seq, EHangStatus& w_status);
int RcvGetColumnGroupIndex(int32_t seqno, EHangStatus& w_status);
void CollectIrrecoverRow(RcvGroup& g, loss_seqs_t& irrecover) const;
bool IsLost(int32_t seq) const;
@ -243,6 +273,11 @@ public:
static const size_t EXTRA_SIZE = 4;
virtual SRT_ARQLevel arqLevel() ATR_OVERRIDE { return m_fallback_level; }
static const char defaultConfig [];
static bool verifyConfig(const SrtFilterConfig& config, std::string& w_errormsg);
};
} // namespace srt
#endif

View file

@ -3,6 +3,7 @@
SOURCES
api.cpp
buffer.cpp
buffer_rcv.cpp
cache.cpp
channel.cpp
common.cpp
@ -12,27 +13,48 @@ epoll.cpp
fec.cpp
handshake.cpp
list.cpp
logger_default.cpp
logger_defs.cpp
md5.cpp
packet.cpp
packetfilter.cpp
queue.cpp
congctl.cpp
socketconfig.cpp
srt_c_api.cpp
window.cpp
srt_compat.c
strerror_defs.cpp
sync.cpp
tsbpd_time.cpp
window.cpp
SOURCES - ENABLE_BONDING
group.cpp
group_backup.cpp
group_common.cpp
SOURCES - !ENABLE_STDCXX_SYNC
sync_posix.cpp
SOURCES - ENABLE_STDCXX_SYNC
sync_cxx11.cpp
SOURCES - EXTRA_WIN32_SHARED
srt_shared.rc
PUBLIC HEADERS
srt.h
logging_api.h
access_control.h
PROTECTED HEADERS
platform_sys.h
udt.h
srt4udt.h
PRIVATE HEADERS
api.h
buffer.h
buffer_rcv.h
cache.h
channel.h
common.h
@ -45,13 +67,18 @@ logging.h
md5.h
netinet_any.h
packet.h
sync.h
queue.h
congctl.h
srt4udt.h
socketconfig.h
srt_compat.h
stats.h
threadname.h
tsbpd_time.h
utilities.h
window.h
SOURCES WIN32 SHARED
srt_shared.rc
PRIVATE HEADERS - ENABLE_BONDING
group.h
group_backup.h
group_common.h

4717
trunk/3rdparty/srt-1-fit/srtcore/group.cpp vendored Normal file

File diff suppressed because it is too large Load diff

822
trunk/3rdparty/srt-1-fit/srtcore/group.h vendored Normal file
View file

@ -0,0 +1,822 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2020 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_GROUP_H
#define INC_SRT_GROUP_H
#include "srt.h"
#include "common.h"
#include "packet.h"
#include "group_common.h"
#include "group_backup.h"
namespace srt
{
#if ENABLE_HEAVY_LOGGING
const char* const srt_log_grp_state[] = {"PENDING", "IDLE", "RUNNING", "BROKEN"};
#endif
class CUDTGroup
{
friend class CUDTUnited;
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
typedef sync::steady_clock steady_clock;
typedef groups::SocketData SocketData;
typedef groups::SendBackupCtx SendBackupCtx;
typedef groups::BackupMemberState BackupMemberState;
public:
typedef SRT_MEMBERSTATUS GroupState;
// Note that the use of states may differ in particular group types:
//
// Broadcast: links that are freshly connected become PENDING and then IDLE only
// for a short moment to be activated immediately at the nearest sending operation.
//
// Balancing: like with broadcast, just that the link activation gets its shared percentage
// of traffic balancing
//
// Multicast: The link is never idle. The data are always sent over the UDP multicast link
// and the receiver simply gets subscribed and reads packets once it's ready.
//
// Backup: The link stays idle until it's activated, and the activation can only happen
// at the moment when the currently active link is "suspected of being likely broken"
// (the current active link fails to receive ACK in a time when two ACKs should already
// be received). After a while when the current active link is confirmed broken, it turns
// into broken state.
static const char* StateStr(GroupState);
static int32_t s_tokenGen;
static int32_t genToken() { ++s_tokenGen; if (s_tokenGen < 0) s_tokenGen = 0; return s_tokenGen;}
struct ConfigItem
{
SRT_SOCKOPT so;
std::vector<unsigned char> value;
template <class T>
bool get(T& refr)
{
if (sizeof(T) > value.size())
return false;
refr = *(T*)&value[0];
return true;
}
ConfigItem(SRT_SOCKOPT o, const void* val, int size)
: so(o)
{
value.resize(size);
unsigned char* begin = (unsigned char*)val;
std::copy(begin, begin + size, value.begin());
}
struct OfType
{
SRT_SOCKOPT so;
OfType(SRT_SOCKOPT soso)
: so(soso)
{
}
bool operator()(ConfigItem& ci) { return ci.so == so; }
};
};
typedef std::list<SocketData> group_t;
typedef group_t::iterator gli_t;
typedef std::vector< std::pair<SRTSOCKET, srt::CUDTSocket*> > sendable_t;
struct Sendstate
{
SRTSOCKET id;
SocketData* mb;
int stat;
int code;
};
CUDTGroup(SRT_GROUP_TYPE);
~CUDTGroup();
SocketData* add(SocketData data);
struct HaveID
{
SRTSOCKET id;
HaveID(SRTSOCKET sid)
: id(sid)
{
}
bool operator()(const SocketData& s) { return s.id == id; }
};
bool contains(SRTSOCKET id, SocketData*& w_f)
{
srt::sync::ScopedLock g(m_GroupLock);
gli_t f = std::find_if(m_Group.begin(), m_Group.end(), HaveID(id));
if (f == m_Group.end())
{
w_f = NULL;
return false;
}
w_f = &*f;
return true;
}
// NEED LOCKING
gli_t begin() { return m_Group.begin(); }
gli_t end() { return m_Group.end(); }
/// Remove the socket from the group container.
/// REMEMBER: the group spec should be taken from the socket
/// (set m_GroupOf and m_GroupMemberData to NULL
/// PRIOR TO calling this function.
/// @param id Socket ID to look for in the container to remove
/// @return true if the container still contains any sockets after the operation
bool remove(SRTSOCKET id)
{
using srt_logging::gmlog;
srt::sync::ScopedLock g(m_GroupLock);
bool empty = false;
HLOGC(gmlog.Debug, log << "group/remove: going to remove @" << id << " from $" << m_GroupID);
gli_t f = std::find_if(m_Group.begin(), m_Group.end(), HaveID(id));
if (f != m_Group.end())
{
m_Group.erase(f);
// Reset sequence numbers on a dead group so that they are
// initialized anew with the new alive connection within
// the group.
// XXX The problem is that this should be done after the
// socket is considered DISCONNECTED, not when it's being
// closed. After being disconnected, the sequence numbers
// are no longer valid, and will be reinitialized when the
// socket is connected again. This may stay as is for now
// as in SRT it's not predicted to do anything with the socket
// that was disconnected other than immediately closing it.
if (m_Group.empty())
{
// When the group is empty, there's no danger that this
// number will collide with any ISN provided by a socket.
// Also since now every socket will derive this ISN.
m_iLastSchedSeqNo = generateISN();
resetInitialRxSequence();
empty = true;
}
}
else
{
HLOGC(gmlog.Debug, log << "group/remove: IPE: id @" << id << " NOT FOUND");
empty = true; // not exactly true, but this is to cause error on group in the APP
}
if (m_Group.empty())
{
m_bOpened = false;
m_bConnected = false;
}
// XXX BUGFIX
m_Positions.erase(id);
return !empty;
}
bool groupEmpty()
{
srt::sync::ScopedLock g(m_GroupLock);
return m_Group.empty();
}
void setGroupConnected();
int send(const char* buf, int len, SRT_MSGCTRL& w_mc);
int sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc);
int sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc);
static int32_t generateISN();
private:
// For Backup, sending all previous packet
int sendBackupRexmit(srt::CUDT& core, SRT_MSGCTRL& w_mc);
// Support functions for sendBackup and sendBroadcast
/// Check if group member is idle.
/// @param d group member
/// @param[in,out] w_wipeme array of sockets to remove from group
/// @param[in,out] w_pendingLinks array of sockets pending for connection
/// @returns true if d is idle (standby), false otherwise
bool send_CheckIdle(const gli_t d, std::vector<SRTSOCKET>& w_wipeme, std::vector<SRTSOCKET>& w_pendingLinks);
/// This function checks if the member has just become idle (check if sender buffer is empty) to send a KEEPALIVE immidiatelly.
/// @todo Check it is some abandoned logic.
void sendBackup_CheckIdleTime(gli_t w_d);
/// Qualify states of member links.
/// [[using locked(this->m_GroupLock, m_pGlobal->m_GlobControlLock)]]
/// @param[out] w_sendBackupCtx the context will be updated with state qualifications
/// @param[in] currtime current timestamp
void sendBackup_QualifyMemberStates(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime);
void sendBackup_AssignBackupState(srt::CUDT& socket, BackupMemberState state, const steady_clock::time_point& currtime);
/// Qualify the state of the active link: fresh, stable, unstable, wary.
/// @retval active backup member state: fresh, stable, unstable, wary.
BackupMemberState sendBackup_QualifyActiveState(const gli_t d, const time_point currtime);
BackupMemberState sendBackup_QualifyIfStandBy(const gli_t d);
/// Sends the same payload over all active members.
/// @param[in] buf payload
/// @param[in] len payload length in bytes
/// @param[in,out] w_mc message control
/// @param[in] currtime current time
/// @param[in] currseq current packet sequence number
/// @param[out] w_nsuccessful number of members with successfull sending.
/// @param[in,out] maxActiveWeight
/// @param[in,out] sendBackupCtx context
/// @param[in,out] w_cx error
/// @return group send result: -1 if sending over all members has failed; number of bytes sent overwise.
int sendBackup_SendOverActive(const char* buf, int len, SRT_MSGCTRL& w_mc, const steady_clock::time_point& currtime, int32_t& w_curseq,
size_t& w_nsuccessful, uint16_t& w_maxActiveWeight, SendBackupCtx& w_sendBackupCtx, CUDTException& w_cx);
/// Check link sending status
/// @param[in] currtime Current time (logging only)
/// @param[in] send_status Result of sending over the socket
/// @param[in] lastseq Last sent sequence number before the current sending operation
/// @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.
///
/// @returns true if the sending operation result (submitted in stat) is a success, false otherwise.
bool sendBackup_CheckSendStatus(const time_point& currtime,
const int send_status,
const int32_t lastseq,
const int32_t pktseq,
CUDT& w_u,
int32_t& w_curseq,
int& w_final_stat);
void sendBackup_Buffering(const char* buf, const int len, int32_t& curseq, SRT_MSGCTRL& w_mc);
size_t sendBackup_TryActivateStandbyIfNeeded(
const char* buf,
const int len,
bool& w_none_succeeded,
SRT_MSGCTRL& w_mc,
int32_t& w_curseq,
int32_t& w_final_stat,
SendBackupCtx& w_sendBackupCtx,
CUDTException& w_cx,
const steady_clock::time_point& currtime);
/// Check if pending sockets are to be qualified as broken.
/// This qualification later results in removing the socket from a group and closing it.
/// @param[in,out] a context with a list of member sockets, some pending might qualified broken
void sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime);
/// Check if unstable sockets are to be qualified as broken.
/// The main reason for such qualification is if a socket is unstable for too long.
/// This qualification later results in removing the socket from a group and closing it.
/// @param[in,out] a context with a list of member sockets, some pending might qualified broken
void sendBackup_CheckUnstableSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime);
/// @brief Marks broken sockets as closed. Used in broadcast sending.
/// @param w_wipeme a list of sockets to close
void send_CloseBrokenSockets(std::vector<SRTSOCKET>& w_wipeme);
/// @brief Marks broken sockets as closed. Used in backup sending.
/// @param w_sendBackupCtx the context with a list of broken sockets
void sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx);
void sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx,
int& w_final_stat,
bool& w_none_succeeded,
SRT_MSGCTRL& w_mc,
CUDTException& w_cx);
void sendBackup_SilenceRedundantLinks(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime);
void send_CheckValidSockets();
public:
int recv(char* buf, int len, SRT_MSGCTRL& w_mc);
void close();
void setOpt(SRT_SOCKOPT optname, const void* optval, int optlen);
void getOpt(SRT_SOCKOPT optName, void* optval, int& w_optlen);
void deriveSettings(srt::CUDT* source);
bool applyFlags(uint32_t flags, HandshakeSide);
SRT_SOCKSTATUS getStatus();
void debugMasterData(SRTSOCKET slave);
bool isGroupReceiver()
{
// XXX add here also other group types, which
// predict group receiving.
return m_type == SRT_GTYPE_BROADCAST;
}
sync::Mutex* exp_groupLock() { return &m_GroupLock; }
void addEPoll(int eid);
void removeEPollEvents(const int eid);
void removeEPollID(const int eid);
/// @brief Update read-ready state.
/// @param sock member socket ID (unused)
/// @param sequence the latest packet sequence number available for reading.
void updateReadState(SRTSOCKET sock, int32_t sequence);
void updateWriteState();
void updateFailedLink();
void activateUpdateEvent(bool still_have_items);
int32_t getRcvBaseSeqNo();
/// Update the in-group array of packet providers per sequence number.
/// Also basing on the information already provided by possibly other sockets,
/// report the real status of packet loss, including packets maybe lost
/// by the caller provider, but already received from elsewhere. Note that
/// these packets are not ready for extraction until ACK-ed.
///
/// @param exp_sequence The previously received sequence at this socket
/// @param sequence The sequence of this packet
/// @param provider The core of the socket for which the packet was dispatched
/// @param time TSBPD time of this packet
/// @return The bitmap that marks by 'false' packets lost since next to exp_sequence
std::vector<bool> providePacket(int32_t exp_sequence, int32_t sequence, srt::CUDT* provider, uint64_t time);
/// This is called from the ACK action by particular socket, which
/// actually signs off the packet for extraction.
///
/// @param core The socket core for which the ACK was sent
/// @param ack The past-the-last-received ACK sequence number
void readyPackets(srt::CUDT* core, int32_t ack);
void syncWithSocket(const srt::CUDT& core, const HandshakeSide side);
int getGroupData(SRT_SOCKGROUPDATA* pdata, size_t* psize);
int getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize);
/// Predicted to be called from the reading function to fill
/// the group data array as requested.
void fillGroupData(SRT_MSGCTRL& w_out, //< MSGCTRL to be written
const SRT_MSGCTRL& in //< MSGCTRL read from the data-providing socket
);
void copyGroupData(const CUDTGroup::SocketData& source, SRT_SOCKGROUPDATA& w_target);
#if ENABLE_HEAVY_LOGGING
void debugGroup();
#else
void debugGroup() {}
#endif
void ackMessage(int32_t msgno);
void processKeepalive(SocketData*);
void internalKeepalive(SocketData*);
private:
// Check if there's at least one connected socket.
// If so, grab the status of all member sockets.
void getGroupCount(size_t& w_size, bool& w_still_alive);
srt::CUDTUnited& m_Global;
srt::sync::Mutex m_GroupLock;
SRTSOCKET m_GroupID;
SRTSOCKET m_PeerGroupID;
struct GroupContainer
{
std::list<SocketData> m_List;
/// This field is used only by some types of groups that need
/// to keep track as to which link was lately used. Note that
/// by removal of a node from the m_List container, this link
/// must be appropriately reset.
gli_t m_LastActiveLink;
GroupContainer()
: m_LastActiveLink(m_List.end())
{
}
// Property<gli_t> active = { m_LastActiveLink; }
SRTU_PROPERTY_RW(gli_t, active, m_LastActiveLink);
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 clear()
{
m_LastActiveLink = end();
m_List.clear();
}
size_t size() { return m_List.size(); }
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;
CallbackHolder<srt_connect_callback_fn> m_cbConnectHook;
void installConnectHook(srt_connect_callback_fn* hook, void* opaq)
{
m_cbConnectHook.set(opaq, hook);
}
public:
void apiAcquire() { ++m_iBusy; }
void apiRelease() { --m_iBusy; }
// A normal cycle of the send/recv functions is the following:
// - [Initial API call for a group]
// - GroupKeeper - ctor
// - LOCK: GlobControlLock
// - Find the group ID in the group container (break if not found)
// - LOCK: GroupLock of that group
// - Set BUSY flag
// - UNLOCK GroupLock
// - UNLOCK GlobControlLock
// - [Call the sending function (sendBroadcast/sendBackup)]
// - LOCK GroupLock
// - Preparation activities
// - Loop over group members
// - Send over a single socket
// - Check send status and conditions
// - Exit, if nothing else to be done
// - Check links to send extra
// - UNLOCK GroupLock
// - Wait for first ready link
// - LOCK GroupLock
// - Check status and find sendable link
// - Send over a single socket
// - Check status and update data
// - UNLOCK GroupLock, Exit
// - GroupKeeper - dtor
// - LOCK GroupLock
// - Clear BUSY flag
// - UNLOCK GroupLock
// END.
//
// The possibility for isStillBusy to go on is only the following:
// 1. Before calling the API function. As GlobControlLock is locked,
// the nearest lock on GlobControlLock by GroupKeeper can happen:
// - before the group is moved to ClosedGroups (this allows it to be found)
// - after the group is moved to ClosedGroups (this makes the group not found)
// - NOT after the group was deleted, as it could not be found and occupied.
//
// 2. Before release of GlobControlLock (acquired by GC), but before the
// API function locks GroupLock:
// - the GC call to isStillBusy locks GroupLock, but BUSY flag is already set
// - GC then avoids deletion of the group
//
// 3. In any further place up to the exit of the API implementation function,
// the BUSY flag is still set.
//
// 4. After exit of GroupKeeper destructor and unlock of GroupLock
// - the group is no longer being accessed and can be freely deleted.
// - the group also can no longer be found by ID.
bool isStillBusy()
{
sync::ScopedLock glk(m_GroupLock);
return m_iBusy || !m_Group.empty();
}
struct BufferedMessageStorage
{
size_t blocksize;
size_t maxstorage;
std::vector<char*> storage;
BufferedMessageStorage(size_t blk, size_t max = 0)
: blocksize(blk)
, maxstorage(max)
, storage()
{
}
char* get()
{
if (storage.empty())
return new char[blocksize];
// Get the element from the end
char* block = storage.back();
storage.pop_back();
return block;
}
void put(char* block)
{
if (storage.size() >= maxstorage)
{
// Simply delete
delete[] block;
return;
}
// Put the block into the spare buffer
storage.push_back(block);
}
~BufferedMessageStorage()
{
for (size_t i = 0; i < storage.size(); ++i)
delete[] storage[i];
}
};
struct BufferedMessage
{
static BufferedMessageStorage storage;
SRT_MSGCTRL mc;
mutable char* data;
size_t size;
BufferedMessage()
: data()
, size()
{
}
~BufferedMessage()
{
if (data)
storage.put(data);
}
// NOTE: size 's' must be checked against SRT_LIVE_MAX_PLSIZE
// before calling
void copy(const char* buf, size_t s)
{
size = s;
data = storage.get();
memcpy(data, buf, s);
}
BufferedMessage(const BufferedMessage& foreign)
: mc(foreign.mc)
, data(foreign.data)
, size(foreign.size)
{
foreign.data = 0;
}
BufferedMessage& operator=(const BufferedMessage& foreign)
{
data = foreign.data;
size = foreign.size;
mc = foreign.mc;
foreign.data = 0;
return *this;
}
private:
void swap_with(BufferedMessage& b)
{
std::swap(this->mc, b.mc);
std::swap(this->data, b.data);
std::swap(this->size, b.size);
}
};
typedef std::deque<BufferedMessage> senderBuffer_t;
// typedef StaticBuffer<BufferedMessage, 1000> senderBuffer_t;
private:
// Fields required for SRT_GTYPE_BACKUP groups.
senderBuffer_t m_SenderBuffer;
int32_t m_iSndOldestMsgNo; // oldest position in the sender buffer
sync::atomic<int32_t> m_iSndAckedMsgNo;
uint32_t m_uOPT_MinStabilityTimeout_us;
// THIS function must be called only in a function for a group type
// that does use sender buffer.
int32_t addMessageToBuffer(const char* buf, size_t len, SRT_MSGCTRL& w_mc);
std::set<int> m_sPollID; // set of epoll ID to trigger
int m_iMaxPayloadSize;
int m_iAvgPayloadSize;
bool m_bSynRecving;
bool m_bSynSending;
bool m_bTsbPd;
bool m_bTLPktDrop;
int64_t m_iTsbPdDelay_us;
int m_RcvEID;
class CEPollDesc* m_RcvEpolld;
int m_SndEID;
class CEPollDesc* m_SndEpolld;
int m_iSndTimeOut; // sending timeout in milliseconds
int m_iRcvTimeOut; // receiving timeout in milliseconds
// Start times for TsbPd. These times shall be synchronized
// between all sockets in the group. The first connected one
// defines it, others shall derive it. The value 0 decides if
// this has been already set.
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.
/// [acquires lock for CUDT::uglobal()->m_GlobControlLock]
/// [[using locked(m_GroupLock)]] temporally unlocks-locks internally
///
/// @returns list of read-ready sockets
/// @throws CUDTException(MJ_CONNECTION, MN_NOCONN, 0)
/// @throws CUDTException(MJ_AGAIN, MN_RDAVAIL, 0)
std::vector<srt::CUDTSocket*> recv_WaitForReadReady(const std::vector<srt::CUDTSocket*>& aliveMembers, std::set<srt::CUDTSocket*>& w_broken);
// This is the sequence number of a packet that has been previously
// delivered. Initially it should be set to SRT_SEQNO_NONE so that the sequence read
// from the first delivering socket will be taken as a good deal.
sync::atomic<int32_t> m_RcvBaseSeqNo;
bool m_bOpened; // Set to true when at least one link is at least pending
bool m_bConnected; // Set to true on first link confirmed connected
bool m_bClosing;
// There's no simple way of transforming config
// items that are predicted to be used on socket.
// Use some options for yourself, store the others
// for setting later on a socket.
std::vector<ConfigItem> m_config;
// Signal for the blocking user thread that the packet
// is ready to deliver.
sync::Condition m_RcvDataCond;
sync::Mutex m_RcvDataLock;
sync::atomic<int32_t> m_iLastSchedSeqNo; // represetnts the value of CUDT::m_iSndNextSeqNo for each running socket
sync::atomic<int32_t> m_iLastSchedMsgNo;
// Statistics
struct Stats
{
// Stats state
time_point tsActivateTime; // Time when this group sent or received the first data packet
time_point tsLastSampleTime; // Time reset when clearing stats
stats::Metric<stats::BytesPackets> sent; // number of packets sent from the application
stats::Metric<stats::BytesPackets> recv; // number of packets delivered from the group to the application
stats::Metric<stats::BytesPackets> recvDrop; // number of packets dropped by the group receiver (not received from any member)
stats::Metric<stats::BytesPackets> recvDiscard; // number of packets discarded as already delivered
void init()
{
tsActivateTime = srt::sync::steady_clock::time_point();
tsLastSampleTime = srt::sync::steady_clock::now();
sent.reset();
recv.reset();
recvDrop.reset();
recvDiscard.reset();
}
void reset()
{
tsLastSampleTime = srt::sync::steady_clock::now();
sent.resetTrace();
recv.resetTrace();
recvDrop.resetTrace();
recvDiscard.resetTrace();
}
} m_stats;
void updateAvgPayloadSize(int size)
{
if (m_iAvgPayloadSize == -1)
m_iAvgPayloadSize = size;
else
m_iAvgPayloadSize = avg_iir<4>(m_iAvgPayloadSize, size);
}
int avgRcvPacketSize()
{
// In case when no packet has been received yet, but already notified
// a dropped packet, its size will be SRT_LIVE_DEF_PLSIZE. It will be
// the value most matching in the typical uses, although no matter what
// value would be used here, each one would be wrong from some points
// of view. This one is simply the best choice for typical uses of groups
// provided that they are to be ued only for live mode.
return m_iAvgPayloadSize == -1 ? SRT_LIVE_DEF_PLSIZE : m_iAvgPayloadSize;
}
public:
void bstatsSocket(CBytePerfMon* perf, bool clear);
// Required after the call on newGroup on the listener side.
// On the listener side the group is lazily created just before
// accepting a new socket and therefore always open.
void setOpen() { m_bOpened = true; }
std::string CONID() const
{
#if ENABLE_LOGGING
std::ostringstream os;
os << "@" << m_GroupID << ":";
return os.str();
#else
return "";
#endif
}
void resetInitialRxSequence()
{
// The app-reader doesn't care about the real sequence number.
// The first provided one will be taken as a good deal; even if
// this is going to be past the ISN, at worst it will be caused
// by TLPKTDROP.
m_RcvBaseSeqNo = SRT_SEQNO_NONE;
}
bool applyGroupTime(time_point& w_start_time, time_point& w_peer_start_time)
{
using srt::sync::is_zero;
using srt_logging::gmlog;
if (is_zero(m_tsStartTime))
{
// The first socket, defines the group time for the whole group.
m_tsStartTime = w_start_time;
m_tsRcvPeerStartTime = w_peer_start_time;
return true;
}
// Sanity check. This should never happen, fix the bug if found!
if (is_zero(m_tsRcvPeerStartTime))
{
LOGC(gmlog.Error, log << "IPE: only StartTime is set, RcvPeerStartTime still 0!");
// Kinda fallback, but that's not too safe.
m_tsRcvPeerStartTime = w_peer_start_time;
}
// The redundant connection, derive the times
w_start_time = m_tsStartTime;
w_peer_start_time = m_tsRcvPeerStartTime;
return false;
}
// Live state synchronization
bool getBufferTimeBase(srt::CUDT* forthesakeof, time_point& w_tb, bool& w_wp, duration& w_dr);
bool applyGroupSequences(SRTSOCKET, int32_t& w_snd_isn, int32_t& w_rcv_isn);
/// @brief Synchronize TSBPD base time and clock drift among members using the @a srcMember as a reference.
/// @param srcMember a reference for synchronization.
void synchronizeDrift(const srt::CUDT* srcMember);
void updateLatestRcv(srt::CUDTSocket*);
// Property accessors
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, id, m_GroupID);
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, peerid, m_PeerGroupID);
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRT_GROUP_TYPE, type, m_type);
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);
};
} // namespace srt
#endif // INC_SRT_GROUP_H

View file

@ -0,0 +1,159 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include <algorithm>
#include <sstream>
#include "group_backup.h"
namespace srt
{
namespace groups
{
using namespace std;
using namespace srt_logging;
const char* stateToStr(BackupMemberState state)
{
switch (state)
{
case srt::groups::BKUPST_UNKNOWN:
return "UNKNOWN";
case srt::groups::BKUPST_PENDING:
return "PENDING";
case srt::groups::BKUPST_STANDBY:
return "STANDBY";
case srt::groups::BKUPST_ACTIVE_FRESH:
return "ACTIVE_FRESH";
case srt::groups::BKUPST_ACTIVE_STABLE:
return "ACTIVE_STABLE";
case srt::groups::BKUPST_ACTIVE_UNSTABLE:
return "ACTIVE_UNSTABLE";
case srt::groups::BKUPST_ACTIVE_UNSTABLE_WARY:
return "ACTIVE_UNSTABLE_WARY";
case srt::groups::BKUPST_BROKEN:
return "BROKEN";
default:
break;
}
return "WRONG_STATE";
}
/// @brief Compares group members by their weight (higher weight comes first), then state.
/// Higher weight comes first, same weight: stable, then fresh active.
struct FCompareByWeight
{
/// @returns true if the first argument is less than (i.e. is ordered before) the second.
bool operator()(const BackupMemberStateEntry& a, const BackupMemberStateEntry& b)
{
if (a.pSocketData != NULL && b.pSocketData != NULL
&& (a.pSocketData->weight != b.pSocketData->weight))
return a.pSocketData->weight > b.pSocketData->weight;
if (a.state != b.state)
{
SRT_STATIC_ASSERT(BKUPST_ACTIVE_STABLE > BKUPST_ACTIVE_FRESH, "Wrong ordering");
return a.state > b.state;
}
// the order does not matter, but comparator must return a different value for not equal a and b
return a.socketID < b.socketID;
}
};
void SendBackupCtx::recordMemberState(SocketData* pSockData, BackupMemberState st)
{
m_memberStates.push_back(BackupMemberStateEntry(pSockData, st));
++m_stateCounter[st];
if (st == BKUPST_STANDBY)
{
m_standbyMaxWeight = max(m_standbyMaxWeight, pSockData->weight);
}
else if (isStateActive(st))
{
m_activeMaxWeight = max(m_activeMaxWeight, pSockData->weight);
}
}
void SendBackupCtx::updateMemberState(const SocketData* pSockData, BackupMemberState st)
{
typedef vector<BackupMemberStateEntry>::iterator iter_t;
for (iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i)
{
if (i->pSocketData == NULL)
continue;
if (i->pSocketData != pSockData)
continue;
if (i->state == st)
return;
--m_stateCounter[i->state];
++m_stateCounter[st];
i->state = st;
return;
}
LOGC(gslog.Error,
log << "IPE: SendBackupCtx::updateMemberState failed to locate member");
}
void SendBackupCtx::sortByWeightAndState()
{
sort(m_memberStates.begin(), m_memberStates.end(), FCompareByWeight());
}
BackupMemberState SendBackupCtx::getMemberState(const SocketData* pSockData) const
{
typedef vector<BackupMemberStateEntry>::const_iterator const_iter_t;
for (const_iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i)
{
if (i->pSocketData != pSockData)
continue;
return i->state;
}
// The entry was not found
// TODO: Maybe throw an exception here?
return BKUPST_UNKNOWN;
}
unsigned SendBackupCtx::countMembersByState(BackupMemberState st) const
{
return m_stateCounter[st];
}
std::string SendBackupCtx::printMembers() const
{
stringstream ss;
typedef vector<BackupMemberStateEntry>::const_iterator const_iter_t;
for (const_iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i)
{
ss << "@" << i->socketID << " w " << i->pSocketData->weight << " state " << stateToStr(i->state) << ", ";
}
return ss.str();
}
} // namespace groups
} // namespace srt

View file

@ -0,0 +1,128 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_GROUP_BACKUP_H
#define INC_SRT_GROUP_BACKUP_H
#include "srt.h"
#include "common.h"
#include "group_common.h"
#include <list>
namespace srt
{
namespace groups
{
enum BackupMemberState
{
BKUPST_UNKNOWN = -1,
BKUPST_PENDING = 0,
BKUPST_STANDBY = 1,
BKUPST_BROKEN = 2,
BKUPST_ACTIVE_UNSTABLE = 3,
BKUPST_ACTIVE_UNSTABLE_WARY = 4,
BKUPST_ACTIVE_FRESH = 5,
BKUPST_ACTIVE_STABLE = 6,
BKUPST_E_SIZE = 7
};
const char* stateToStr(BackupMemberState state);
inline bool isStateActive(BackupMemberState state)
{
if (state == BKUPST_ACTIVE_FRESH
|| state == BKUPST_ACTIVE_STABLE
|| state == BKUPST_ACTIVE_UNSTABLE
|| state == BKUPST_ACTIVE_UNSTABLE_WARY)
{
return true;
}
return false;
}
struct BackupMemberStateEntry
{
BackupMemberStateEntry(SocketData* psock, BackupMemberState st)
: pSocketData(psock)
, socketID(psock->id)
, state(st)
{}
SocketData* pSocketData; // accessing pSocketDataIt requires m_GroupLock
SRTSOCKET socketID; // therefore socketID is saved separately (needed to close broken sockets)
BackupMemberState state;
};
/// @brief A context needed for main/backup sending function.
/// @todo Using gli_t here does not allow to safely store the context outside of the sendBackup calls.
class SendBackupCtx
{
public:
SendBackupCtx()
: m_stateCounter() // default init with zeros
, m_activeMaxWeight()
, m_standbyMaxWeight()
{
}
/// @brief Adds or updates a record of the member socket state.
/// @param pSocketDataIt Iterator to a socket
/// @param st State of the memmber socket
/// @todo Implement updating member state
void recordMemberState(SocketData* pSocketDataIt, BackupMemberState st);
/// @brief Updates a record of the member socket state.
/// @param pSocketDataIt Iterator to a socket
/// @param st State of the memmber socket
/// @todo To be replaced by recordMemberState
/// @todo Update max weights?
void updateMemberState(const SocketData* pSocketDataIt, BackupMemberState st);
/// @brief sorts members in order
/// Higher weight comes first, same weight: stable first, then fresh active.
void sortByWeightAndState();
BackupMemberState getMemberState(const SocketData* pSocketDataIt) const;
unsigned countMembersByState(BackupMemberState st) const;
const std::vector<BackupMemberStateEntry>& memberStates() const { return m_memberStates; }
uint16_t maxStandbyWeight() const { return m_standbyMaxWeight; }
uint16_t maxActiveWeight() const { return m_activeMaxWeight; }
std::string printMembers() const;
void setRateEstimate(const CRateEstimator& rate) { m_rateEstimate = rate; }
const CRateEstimator& getRateEstimate() const { return m_rateEstimate; }
private:
std::vector<BackupMemberStateEntry> m_memberStates; // TODO: consider std::map here?
unsigned m_stateCounter[BKUPST_E_SIZE];
uint16_t m_activeMaxWeight;
uint16_t m_standbyMaxWeight;
CRateEstimator m_rateEstimate; // The rate estimator state of the active link to copy to a backup on activation.
};
} // namespace groups
} // namespace srt
#endif // INC_SRT_GROUP_BACKUP_H

View file

@ -0,0 +1,63 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include "group_common.h"
#include "api.h"
namespace srt
{
namespace groups
{
SocketData prepareSocketData(CUDTSocket* s)
{
// This uses default SRT_GST_BROKEN because when the group operation is done,
// then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is
// recognized as an initial state of the fresh added socket to the group,
// so some "initial configuration" must be done on it, after which it's
// turned into SRT_GST_RUNNING, that is, it's treated as all others. When
// set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned
// up, however, unless the status is simultaneously SRTS_BROKEN.
// The order of operations is then:
// - add the socket to the group in this "broken" initial state
// - connect the socket (or get it extracted from accept)
// - update the socket state (should be SRTS_CONNECTED)
// - once the connection is established (may take time with connect), set SRT_GST_IDLE
// - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING
SocketData sd = {
s->m_SocketID,
s,
-1,
SRTS_INIT,
SRT_GST_BROKEN,
SRT_GST_BROKEN,
-1,
-1,
sockaddr_any(),
sockaddr_any(),
false,
false,
false,
0, // weight
0 // pktSndDropTotal
};
return sd;
}
} // namespace groups
} // namespace srt

View file

@ -0,0 +1,62 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_GROUP_COMMON_H
#define INC_SRT_GROUP_COMMON_H
#include "srt.h"
#include "common.h"
#include "core.h"
#include <list>
namespace srt
{
namespace groups
{
typedef SRT_MEMBERSTATUS GroupState;
struct SocketData
{
SRTSOCKET id; // same as ps->m_SocketID
CUDTSocket* ps;
int token;
SRT_SOCKSTATUS laststatus;
GroupState sndstate;
GroupState rcvstate;
int sndresult;
int rcvresult;
sockaddr_any agent;
sockaddr_any peer;
bool ready_read;
bool ready_write;
bool ready_error;
// Configuration
uint16_t weight;
// Stats
int64_t pktSndDropTotal;
};
SocketData prepareSocketData(CUDTSocket* s);
typedef std::list<SocketData> group_t;
typedef group_t::iterator gli_t;
} // namespace groups
} // namespace srt
#endif // INC_SRT_GROUP_COMMON_H

View file

@ -43,6 +43,8 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "platform_sys.h"
#include <cstring>
#include <string>
#include <sstream>
@ -50,32 +52,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include "udt.h"
#include "api.h"
#include "core.h"
#include "handshake.h"
#include "utilities.h"
using namespace std;
using namespace srt;
CHandShake::CHandShake():
m_iVersion(0),
m_iType(0), // Universal: UDT_UNDEFINED or no flags
m_iISN(0),
m_iMSS(0),
m_iFlightFlagSize(0),
m_iReqType(URQ_WAVEAHAND),
m_iID(0),
m_iCookie(0),
m_extension(false)
srt::CHandShake::CHandShake()
: m_iVersion(0)
, m_iType(0) // Universal: UDT_UNDEFINED or no flags
, m_iISN(0)
, m_iMSS(0)
, m_iFlightFlagSize(0)
, m_iReqType(URQ_WAVEAHAND)
, m_iID(0)
, m_iCookie(0)
, m_extension(false)
{
for (int i = 0; i < 4; ++ i)
m_piPeerIP[i] = 0;
}
int CHandShake::store_to(char* buf, ref_t<size_t> r_size)
int srt::CHandShake::store_to(char* buf, size_t& w_size)
{
size_t& size = *r_size;
if (size < m_iContentSize)
if (w_size < m_iContentSize)
return -1;
int32_t* p = reinterpret_cast<int32_t*>(buf);
@ -90,12 +93,12 @@ int CHandShake::store_to(char* buf, ref_t<size_t> r_size)
for (int i = 0; i < 4; ++ i)
*p++ = m_piPeerIP[i];
size = m_iContentSize;
w_size = m_iContentSize;
return 0;
}
int CHandShake::load_from(const char* buf, size_t size)
int srt::CHandShake::load_from(const char* buf, size_t size)
{
if (size < m_iContentSize)
return -1;
@ -118,6 +121,8 @@ int CHandShake::load_from(const char* buf, size_t size)
#ifdef ENABLE_LOGGING
namespace srt
{
const char* srt_rejectreason_name [] = {
"UNKNOWN",
"SYSTEM",
@ -134,15 +139,31 @@ const char* srt_rejectreason_name [] = {
"MESSAGEAPI",
"CONGESTION",
"FILTER",
"GROUP",
"TIMEOUT"
};
}
std::string RequestTypeStr(UDTRequestType rq)
std::string srt::RequestTypeStr(UDTRequestType rq)
{
if (rq >= URQ_FAILURE_TYPES)
{
SRT_REJECT_REASON rej = RejectReasonForURQ(rq);
int id = rej;
return std::string("ERROR:") + srt_rejectreason_name[id];
std::ostringstream rt;
rt << "ERROR:";
int id = RejectReasonForURQ(rq);
if (id < (int) Size(srt_rejectreason_name))
rt << srt_rejectreason_name[id];
else if (id < SRT_REJC_USERDEFINED)
{
if (id < SRT_REJC_PREDEFINED)
rt << "UNKNOWN:" << id;
else
rt << "PREDEFINED:" << (id - SRT_REJC_PREDEFINED);
}
else
rt << "USERDEFINED:" << (id - SRT_REJC_USERDEFINED);
return rt.str();
}
switch ( rq )
@ -156,7 +177,7 @@ std::string RequestTypeStr(UDTRequestType rq)
}
}
string CHandShake::RdvStateStr(CHandShake::RendezvousState s)
string srt::CHandShake::RdvStateStr(CHandShake::RendezvousState s)
{
switch (s)
{
@ -172,11 +193,22 @@ string CHandShake::RdvStateStr(CHandShake::RendezvousState s)
}
#endif
string CHandShake::show()
bool srt::CHandShake::valid()
{
if (m_iVersion < CUDT::HS_VERSION_UDT4
|| m_iISN < 0 || m_iISN >= CSeqNo::m_iMaxSeqNo
|| m_iMSS < 32
|| m_iFlightFlagSize < 2)
return false;
return true;
}
string srt::CHandShake::show()
{
ostringstream so;
so << "version=" << m_iVersion << " type=" << hex << m_iType << dec
so << "version=" << m_iVersion << " type=0x" << hex << m_iType << dec
<< " ISN=" << m_iISN << " MSS=" << m_iMSS << " FLW=" << m_iFlightFlagSize
<< " reqtype=" << RequestTypeStr(m_iReqType) << " srcID=" << m_iID
<< " cookie=" << hex << m_iCookie << dec
@ -191,9 +223,12 @@ string CHandShake::show()
// CHandShake, not CUDT.
if ( m_iVersion > CUDT::HS_VERSION_UDT4 )
{
so << "EXT: ";
if (m_iType == 0) // no flags at all
so << "none";
const int flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_iType);
so << "FLAGS: ";
if (flags == SrtHSRequest::SRT_MAGIC_CODE)
so << "MAGIC";
else if (m_iType == 0)
so << "NONE"; // no flags and no advertised pbkeylen
else
so << ExtensionFlagStr(m_iType);
}
@ -201,7 +236,7 @@ string CHandShake::show()
return so.str();
}
string CHandShake::ExtensionFlagStr(int32_t fl)
string srt::CHandShake::ExtensionFlagStr(int32_t fl)
{
std::ostringstream out;
if ( fl & HS_EXT_HSREQ )
@ -211,7 +246,7 @@ string CHandShake::ExtensionFlagStr(int32_t fl)
if ( fl & HS_EXT_CONFIG )
out << " config";
int kl = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(fl) << 6;
const int kl = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(fl) << 6;
if (kl != 0)
{
out << " AES-" << kl;
@ -228,7 +263,7 @@ string CHandShake::ExtensionFlagStr(int32_t fl)
// XXX This code isn't currently used. Left here because it can
// be used in future, should any refactoring for the "manual word placement"
// code be done.
bool SrtHSRequest::serialize(char* buf, size_t size) const
bool srt::SrtHSRequest::serialize(char* buf, size_t size) const
{
if (size < SRT_HS_SIZE)
return false;
@ -243,7 +278,7 @@ bool SrtHSRequest::serialize(char* buf, size_t size) const
}
bool SrtHSRequest::deserialize(const char* buf, size_t size)
bool srt::SrtHSRequest::deserialize(const char* buf, size_t size)
{
m_iSrtVersion = 0; // just to let users recognize if it succeeded or not.
@ -258,3 +293,35 @@ bool SrtHSRequest::deserialize(const char* buf, size_t size)
m_iSrtReserved = (*p++);
return true;
}
std::string srt::SrtFlagString(int32_t flags)
{
#define LEN(arr) (sizeof (arr)/(sizeof ((arr)[0])))
std::string output;
static std::string namera[] = { "TSBPD-snd", "TSBPD-rcv", "haicrypt", "TLPktDrop", "NAKReport", "ReXmitFlag", "StreamAPI" };
size_t i = 0;
for (; i < LEN(namera); ++i)
{
if ((flags & 1) == 1)
{
output += "+" + namera[i] + " ";
}
else
{
output += "-" + namera[i] + " ";
}
flags >>= 1;
}
#undef LEN
if (flags != 0)
{
output += "+unknown";
}
return output;
}

View file

@ -43,12 +43,17 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef INC__HANDSHAKE_H
#define INC__HANDSHAKE_H
#ifndef INC_SRT_HANDSHAKE_H
#define INC_SRT_HANDSHAKE_H
#include <vector>
#include "crypto.h"
#include "utilities.h"
namespace srt
{
typedef Bits<31, 16> HS_CMDSPEC_CMD;
typedef Bits<15, 0> HS_CMDSPEC_SIZE;
@ -93,6 +98,7 @@ const int SRT_CMD_REJECT = 0, // REJECT is only a symbol for return type
SRT_CMD_SID = 5,
SRT_CMD_CONGESTION = 6,
SRT_CMD_FILTER = 7,
SRT_CMD_GROUP = 8,
SRT_CMD_NONE = -1; // for cases when {no pong for ping is required} | {no extension block found}
enum SrtDataStruct
@ -102,7 +108,7 @@ enum SrtDataStruct
SRT_HS_LATENCY,
// Keep it always last
SRT_HS__SIZE
SRT_HS_E_SIZE
};
// For HSv5 the lo and hi part is used for particular side's latency
@ -112,30 +118,21 @@ typedef Bits<15, 0> SRT_HS_LATENCY_SND;
typedef Bits<15, 0> SRT_HS_LATENCY_LEG;
// XXX These structures are currently unused. The code can be changed
// so that these are used instead of manual tailoring of the messages.
struct SrtHandshakeExtension
{
protected:
uint32_t m_SrtCommand; // Used only in extension
public:
SrtHandshakeExtension(int cmd)
{
m_SrtCommand = cmd;
}
void setCommand(int cmd)
{
m_SrtCommand = cmd;
}
int16_t type;
std::vector<uint32_t> contents;
SrtHandshakeExtension(int16_t cmd): type(cmd) {}
};
// Implemented in core.cpp, so far
void SrtExtractHandshakeExtensions(const char* bufbegin, size_t size,
std::vector<SrtHandshakeExtension>& w_output);
struct SrtHSRequest: public SrtHandshakeExtension
{
typedef Bits<31, 16> SRT_HSTYPE_ENCFLAGS;
typedef Bits<15, 0> SRT_HSTYPE_HSFLAGS;
@ -154,6 +151,19 @@ struct SrtHSRequest: public SrtHandshakeExtension
return base | SRT_HSTYPE_ENCFLAGS::wrap( SRT_PBKEYLEN_BITS::unwrap(crypto_keylen) );
}
// Group handshake extension layout
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Group ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Group Type | Group's Flags | Group's Weight |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
typedef Bits<31, 24> HS_GROUP_TYPE;
typedef Bits<23, 16> HS_GROUP_FLAGS;
typedef Bits<15, 0> HS_GROUP_WEIGHT;
private:
friend class CHandShake;
@ -229,25 +239,37 @@ enum UDTRequestType
// --> CONCLUSION (with response extensions, if RESPONDER)
// <-- AGREEMENT (sent exclusively by INITIATOR upon reception of CONCLUSIOn with response extensions)
// Errors reported by the peer, also used as useless error codes
// in handshake processing functions.
URQ_FAILURE_TYPES = 1000
// This marks the beginning of values that are error codes.
URQ_FAILURE_TYPES = 1000,
// NOTE: codes above 1000 are reserved for failure codes for
// rejection reason, as per `SRT_REJECT_REASON` enum. DO NOT
// add any new values here.
// rejection reason, as per `SRT_REJECT_REASON` enum. The
// actual rejection code is the value of the request type
// minus URQ_FAILURE_TYPES.
// This is in order to return standard error codes for server
// data retrieval failures.
URQ_SERVER_FAILURE_TYPES = URQ_FAILURE_TYPES + SRT_REJC_PREDEFINED,
// This is for a completely user-defined reject reasons.
URQ_USER_FAILURE_TYPES = URQ_FAILURE_TYPES + SRT_REJC_USERDEFINED
};
inline UDTRequestType URQFailure(SRT_REJECT_REASON reason)
inline UDTRequestType URQFailure(int reason)
{
return UDTRequestType(URQ_FAILURE_TYPES + int(reason));
}
inline SRT_REJECT_REASON RejectReasonForURQ(UDTRequestType req)
inline int RejectReasonForURQ(UDTRequestType req)
{
if (req < URQ_FAILURE_TYPES || req - URQ_FAILURE_TYPES >= SRT_REJ__SIZE)
if (req < URQ_FAILURE_TYPES)
return SRT_REJ_UNKNOWN;
return SRT_REJECT_REASON(req - URQ_FAILURE_TYPES);
int reason = req - URQ_FAILURE_TYPES;
if (reason < SRT_REJC_PREDEFINED && reason >= SRT_REJ_E_SIZE)
return SRT_REJ_UNKNOWN;
return reason;
}
// DEPRECATED values. Use URQFailure(SRT_REJECT_REASON).
@ -265,20 +287,20 @@ inline std::string RequestTypeStr(UDTRequestType) { return ""; }
class CHandShake
{
public:
CHandShake();
CHandShake();
int store_to(char* buf, ref_t<size_t> size);
int load_from(const char* buf, size_t size);
int store_to(char* buf, size_t& size);
int load_from(const char* buf, size_t size);
public:
// This is the size of SERIALIZED handshake.
// Might be defined as simply sizeof(CHandShake), but the
// enum values would have to be forced as int32_t, which is only
// available in C++11. Theoretically they are all 32-bit, but
// such a statement is not reliable and not portable.
static const size_t m_iContentSize = 48; // Size of hand shake data
// This is the size of SERIALIZED handshake.
// Might be defined as simply sizeof(CHandShake), but the
// enum values would have to be forced as int32_t, which is only
// available in C++11. Theoretically they are all 32-bit, but
// such a statement is not reliable and not portable.
static const size_t m_iContentSize = 48; // Size of hand shake data
// Extension flags
// Extension flags
static const int32_t HS_EXT_HSREQ = BIT(0);
static const int32_t HS_EXT_KMREQ = BIT(1);
@ -290,53 +312,55 @@ public:
int32_t flags() { return m_iType; }
public:
int32_t m_iVersion; // UDT version (HS_VERSION_* symbols)
int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags
int32_t m_iISN; // random initial sequence number
int32_t m_iMSS; // maximum segment size
int32_t m_iFlightFlagSize; // flow control window size
UDTRequestType m_iReqType; // handshake stage
int32_t m_iID; // socket ID
int32_t m_iCookie; // cookie
uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to
int32_t m_iVersion; // UDT version (HS_VERSION_* symbols)
int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags
int32_t m_iISN; // random initial sequence number
int32_t m_iMSS; // maximum segment size
int32_t m_iFlightFlagSize; // flow control window size
UDTRequestType m_iReqType; // handshake stage
int32_t m_iID; // SRT socket ID of HS sender
int32_t m_iCookie; // cookie
uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to
bool m_extension;
bool m_extension;
std::string show();
bool valid();
std::string show();
// The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way).
//
// The WAVING state is the very initial state of the rendezvous connection and restored after the
// connection is closed.
// The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible
// situations are:
// - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE
// - "parallel arrangement" both parties transit to ATTENTION
//
// Parallel arrangement is a "virtually impossible" case, in which both parties must send the first
// URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same
// time, on machines with exactly the same performance and all things preceding the message sending
// have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if
// the clients have started at different times, the one who started first sends a message and the
// system of the receiver buffers this message even before the client binds the port for enough long
// time so that it outlasts also the possible second, repeated waveahand.
enum RendezvousState
{
RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events.
RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer.
RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION].
RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE].
RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state.
RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT.
};
// The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way).
//
// The WAVING state is the very initial state of the rendezvous connection and restored after the
// connection is closed.
// The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible
// situations are:
// - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE
// - "parallel arrangement" both parties transit to ATTENTION
//
// Parallel arrangement is a "virtually impossible" case, in which both parties must send the first
// URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same
// time, on machines with exactly the same performance and all things preceding the message sending
// have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if
// the clients have started at different times, the one who started first sends a message and the
// system of the receiver buffers this message even before the client binds the port for enough long
// time so that it outlasts also the possible second, repeated waveahand.
enum RendezvousState
{
RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events.
RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer.
RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION].
RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE].
RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state.
RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT.
};
#if ENABLE_LOGGING
static std::string RdvStateStr(RendezvousState s);
static std::string RdvStateStr(RendezvousState s);
#else
static std::string RdvStateStr(RendezvousState) { return ""; }
static std::string RdvStateStr(RendezvousState) { return ""; }
#endif
};
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
/*
* 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/.
*
*
*/
/*****************************************************************************
@ -50,60 +50,77 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_LIST_H__
#define __UDT_LIST_H__
#ifndef INC_SRT_LIST_H
#define INC_SRT_LIST_H
#include "udt.h"
#include "common.h"
namespace srt {
class CSndLossList
{
public:
CSndLossList(int size = 1024);
~CSndLossList();
CSndLossList(int size = 1024);
~CSndLossList();
/// Insert a seq. no. into the sender loss list.
/// @param [in] seqno1 sequence number starts.
/// @param [in] seqno2 sequence number ends.
/// @return number of packets that are not in the list previously.
/// Insert a seq. no. into the sender loss list.
/// @param [in] seqno1 sequence number starts.
/// @param [in] seqno2 sequence number ends.
/// @return number of packets that are not in the list previously.
int insert(int32_t seqno1, int32_t seqno2);
int insert(int32_t seqno1, int32_t seqno2);
/// Remove the given sequence number and all numbers that precede it.
/// @param [in] seqno sequence number.
void removeUpTo(int32_t seqno);
/// Remove ALL the seq. no. that are not greater than the parameter.
/// @param [in] seqno sequence number.
/// Read the loss length.∏
/// @return The length of the list.
int getLossLength() const;
void remove(int32_t seqno);
/// Read the first (smallest) loss seq. no. in the list and remove it.
/// @return The seq. no. or -1 if the list is empty.
int32_t popLostSeq();
/// Read the loss length.
/// @return The length of the list.
int getLossLength() const;
/// Read the first (smallest) loss seq. no. in the list and remove it.
/// @return The seq. no. or -1 if the list is empty.
int32_t popLostSeq();
void traceState() const;
private:
struct Seq
{
int32_t data1; // sequence number starts
int32_t data2; // seqnence number ends
int next; // next node in the list
}* m_caSeq;
struct Seq
{
int32_t seqstart; // sequence number starts
int32_t seqend; // sequence number ends
int inext; // index of the next node in the list
} * m_caSeq;
int m_iHead; // first node
int m_iLength; // loss length
int m_iSize; // size of the static array
int m_iLastInsertPos; // position of last insert node
int m_iHead; // first node
int m_iLength; // loss length
const int m_iSize; // size of the static array
int m_iLastInsertPos; // position of last insert node
mutable pthread_mutex_t m_ListLock; // used to synchronize list operation
mutable srt::sync::Mutex m_ListLock; // used to synchronize list operation
private:
CSndLossList(const CSndLossList&);
CSndLossList& operator=(const CSndLossList&);
/// Inserts an element to the beginning and updates head pointer.
/// No lock.
void insertHead(int pos, int32_t seqno1, int32_t seqno2);
/// Inserts an element after previous element.
/// No lock.
void insertAfter(int pos, int pos_after, int32_t seqno1, int32_t seqno2);
/// Check if it is possible to coalesce element at loc with further elements.
/// @param loc - last changed location
void coalesce(int loc);
/// Update existing element with the new range (increase only)
/// @param pos position of the element being updated
/// @param seqno1 first sequence number in range
/// @param seqno2 last sequence number in range (SRT_SEQNO_NONE if no range)
bool updateElement(int pos, int32_t seqno1, int32_t seqno2);
private:
CSndLossList(const CSndLossList&);
CSndLossList& operator=(const CSndLossList&);
};
////////////////////////////////////////////////////////////////////////////////
@ -111,123 +128,124 @@ private:
class CRcvLossList
{
public:
CRcvLossList(int size = 1024);
~CRcvLossList();
CRcvLossList(int size = 1024);
~CRcvLossList();
/// 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.
/// 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);
void insert(int32_t seqno1, int32_t seqno2);
/// Remove a loss seq. no. from the receiver's loss list.
/// @param [in] seqno sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
/// Remove a loss seq. no. from the receiver's loss list.
/// @param [in] seqno sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
bool remove(int32_t seqno);
bool remove(int32_t seqno);
/// Remove all packets between seqno1 and seqno2.
/// @param [in] seqno1 start sequence number.
/// @param [in] seqno2 end sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
/// Remove all packets between seqno1 and seqno2.
/// @param [in] seqno1 start sequence number.
/// @param [in] seqno2 end sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
bool remove(int32_t seqno1, int32_t seqno2);
bool remove(int32_t seqno1, int32_t seqno2);
/// 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.
/// @return True if found; otherwise false.
/// 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.
/// @return True if found; otherwise false.
bool find(int32_t seqno1, int32_t seqno2) const;
bool find(int32_t seqno1, int32_t seqno2) const;
/// Read the loss length.
/// @return the length of the list.
/// Read the loss length.
/// @return the length of the list.
int getLossLength() const;
int getLossLength() const;
/// Read the first (smallest) seq. no. in the list.
/// @return the sequence number or -1 if the list is empty.
/// Read the first (smallest) seq. no. in the list.
/// @return the sequence number or -1 if the list is empty.
int getFirstLostSeq() const;
int32_t getFirstLostSeq() const;
/// Get a encoded loss array for NAK report.
/// @param [out] array the result list of seq. no. to be included in NAK.
/// @param [out] len physical length of the result array.
/// @param [in] limit maximum length of the array.
/// Get a encoded loss array for NAK report.
/// @param [out] array the result list of seq. no. to be included in NAK.
/// @param [out] len physical length of the result array.
/// @param [in] limit maximum length of the array.
void getLossArray(int32_t* array, int& len, int limit);
void getLossArray(int32_t* array, int& len, int limit);
private:
struct Seq
{
int32_t data1; // sequence number starts
int32_t data2; // sequence number ends
int next; // next node in the list
int prior; // prior node in the list;
}* m_caSeq;
struct Seq
{
int32_t seqstart; // sequence number starts
int32_t seqend; // sequence number ends
int inext; // index of the next node in the list
int iprior; // index of the previous node in the list
} * m_caSeq;
int m_iHead; // first node in the list
int m_iTail; // last node in the list;
int m_iLength; // loss length
int m_iSize; // size of the static array
int m_iHead; // first node in the list
int m_iTail; // last node in the list;
int m_iLength; // loss length
int m_iSize; // size of the static array
int m_iLargestSeq; // largest seq ever seen
private:
CRcvLossList(const CRcvLossList&);
CRcvLossList& operator=(const CRcvLossList&);
CRcvLossList(const CRcvLossList&);
CRcvLossList& operator=(const CRcvLossList&);
public:
struct iterator
{
int32_t head;
Seq* seq;
struct iterator
{
int32_t head;
Seq* seq;
iterator(Seq* str, int32_t v)
: head(v)
, seq(str)
{
}
iterator(Seq* str, int32_t v): head(v), seq(str) {}
iterator next() const
{
if (head == -1)
return *this; // should report error, but we can only throw exception, so simply ignore it.
iterator next() const
{
if ( head == -1 )
return *this; // should report error, but we can only throw exception, so simply ignore it.
return iterator(seq, seq[head].inext);
}
return iterator(seq, seq[head].next);
}
iterator& operator++()
{
*this = next();
return *this;
}
iterator& operator++()
{
*this = next();
return *this;
}
iterator operator++(int)
{
iterator old(seq, head);
*this = next();
return old;
}
iterator operator++(int)
{
iterator old (seq, head);
*this = next();
return old;
}
bool operator==(const iterator& second) const
{
// Ignore seq - should be the same and this is only a sanity check.
return head == second.head;
}
bool operator==(const iterator& second) const
{
// Ignore seq - should be the same and this is only a sanity check.
return head == second.head;
}
bool operator!=(const iterator& second) const { return !(*this == second); }
bool operator!=(const iterator& second) const { return !(*this == second); }
std::pair<int32_t, int32_t> operator*()
{
return std::make_pair(seq[head].data1, seq[head].data2);
}
};
iterator begin() { return iterator(m_caSeq, m_iHead); }
iterator end() { return iterator(m_caSeq, -1); }
std::pair<int32_t, int32_t> operator*() { return std::make_pair(seq[head].seqstart, seq[head].seqend); }
};
iterator begin() { return iterator(m_caSeq, m_iHead); }
iterator end() { return iterator(m_caSeq, -1); }
};
struct CRcvFreshLoss
{
int32_t seq[2];
int ttl;
uint64_t timestamp;
int32_t seq[2];
int ttl;
srt::sync::steady_clock::time_point timestamp;
CRcvFreshLoss(int32_t seqlo, int32_t seqhi, int initial_ttl);
@ -236,15 +254,18 @@ struct CRcvFreshLoss
#ifdef DELETE
#undef DELETE
#endif
enum Emod {
NONE, //< the given sequence was not found in this range
enum Emod
{
NONE, //< the given sequence was not found in this range
STRIPPED, //< it was equal to first or last, already taken care of
SPLIT, //< found in the middle, you have to split this range into two
DELETE //< This was a range of one element exactly equal to sequence. Simply delete it.
SPLIT, //< found in the middle, you have to split this range into two
DELETE //< This was a range of one element exactly equal to sequence. Simply delete it.
};
Emod revoke(int32_t sequence);
Emod revoke(int32_t lo, int32_t hi);
};
} // namespace srt
#endif

View file

@ -0,0 +1,53 @@
/*
WARNING: Generated from ../scripts/generate-logging-defs.tcl
DO NOT MODIFY.
Copyright applies as per the generator script.
*/
#include "srt.h"
#include "logging.h"
#include "logger_defs.h"
namespace srt_logging
{
AllFaOn::AllFaOn()
{
allfa.set(SRT_LOGFA_GENERAL, true);
allfa.set(SRT_LOGFA_SOCKMGMT, true);
allfa.set(SRT_LOGFA_CONN, true);
allfa.set(SRT_LOGFA_XTIMER, true);
allfa.set(SRT_LOGFA_TSBPD, true);
allfa.set(SRT_LOGFA_RSRC, true);
allfa.set(SRT_LOGFA_CONGEST, true);
allfa.set(SRT_LOGFA_PFILTER, true);
allfa.set(SRT_LOGFA_API_CTRL, true);
allfa.set(SRT_LOGFA_QUE_CTRL, true);
allfa.set(SRT_LOGFA_EPOLL_UPD, true);
allfa.set(SRT_LOGFA_API_RECV, true);
allfa.set(SRT_LOGFA_BUF_RECV, true);
allfa.set(SRT_LOGFA_QUE_RECV, true);
allfa.set(SRT_LOGFA_CHN_RECV, true);
allfa.set(SRT_LOGFA_GRP_RECV, true);
allfa.set(SRT_LOGFA_API_SEND, true);
allfa.set(SRT_LOGFA_BUF_SEND, true);
allfa.set(SRT_LOGFA_QUE_SEND, true);
allfa.set(SRT_LOGFA_CHN_SEND, true);
allfa.set(SRT_LOGFA_GRP_SEND, true);
allfa.set(SRT_LOGFA_INTERNAL, true);
allfa.set(SRT_LOGFA_QUE_MGMT, true);
allfa.set(SRT_LOGFA_CHN_MGMT, true);
allfa.set(SRT_LOGFA_GRP_MGMT, true);
allfa.set(SRT_LOGFA_EPOLL_API, true);
}
} // namespace srt_logging

View file

@ -0,0 +1,55 @@
/*
WARNING: Generated from ../scripts/generate-logging-defs.tcl
DO NOT MODIFY.
Copyright applies as per the generator script.
*/
#include "srt.h"
#include "logging.h"
#include "logger_defs.h"
namespace srt_logging { AllFaOn logger_fa_all; }
// We need it outside the namespace to preserve the global name.
// It's a part of "hidden API" (used by applications)
SRT_API srt_logging::LogConfig srt_logger_config(srt_logging::logger_fa_all.allfa);
namespace srt_logging
{
Logger gglog(SRT_LOGFA_GENERAL, srt_logger_config, "SRT.gg");
Logger smlog(SRT_LOGFA_SOCKMGMT, srt_logger_config, "SRT.sm");
Logger cnlog(SRT_LOGFA_CONN, srt_logger_config, "SRT.cn");
Logger xtlog(SRT_LOGFA_XTIMER, srt_logger_config, "SRT.xt");
Logger tslog(SRT_LOGFA_TSBPD, srt_logger_config, "SRT.ts");
Logger rslog(SRT_LOGFA_RSRC, srt_logger_config, "SRT.rs");
Logger cclog(SRT_LOGFA_CONGEST, srt_logger_config, "SRT.cc");
Logger pflog(SRT_LOGFA_PFILTER, srt_logger_config, "SRT.pf");
Logger aclog(SRT_LOGFA_API_CTRL, srt_logger_config, "SRT.ac");
Logger qclog(SRT_LOGFA_QUE_CTRL, srt_logger_config, "SRT.qc");
Logger eilog(SRT_LOGFA_EPOLL_UPD, srt_logger_config, "SRT.ei");
Logger arlog(SRT_LOGFA_API_RECV, srt_logger_config, "SRT.ar");
Logger brlog(SRT_LOGFA_BUF_RECV, srt_logger_config, "SRT.br");
Logger qrlog(SRT_LOGFA_QUE_RECV, srt_logger_config, "SRT.qr");
Logger krlog(SRT_LOGFA_CHN_RECV, srt_logger_config, "SRT.kr");
Logger grlog(SRT_LOGFA_GRP_RECV, srt_logger_config, "SRT.gr");
Logger aslog(SRT_LOGFA_API_SEND, srt_logger_config, "SRT.as");
Logger bslog(SRT_LOGFA_BUF_SEND, srt_logger_config, "SRT.bs");
Logger qslog(SRT_LOGFA_QUE_SEND, srt_logger_config, "SRT.qs");
Logger kslog(SRT_LOGFA_CHN_SEND, srt_logger_config, "SRT.ks");
Logger gslog(SRT_LOGFA_GRP_SEND, srt_logger_config, "SRT.gs");
Logger inlog(SRT_LOGFA_INTERNAL, srt_logger_config, "SRT.in");
Logger qmlog(SRT_LOGFA_QUE_MGMT, srt_logger_config, "SRT.qm");
Logger kmlog(SRT_LOGFA_CHN_MGMT, srt_logger_config, "SRT.km");
Logger gmlog(SRT_LOGFA_GRP_MGMT, srt_logger_config, "SRT.gm");
Logger ealog(SRT_LOGFA_EPOLL_API, srt_logger_config, "SRT.ea");
} // namespace srt_logging

View file

@ -0,0 +1,61 @@
/*
WARNING: Generated from ../scripts/generate-logging-defs.tcl
DO NOT MODIFY.
Copyright applies as per the generator script.
*/
#ifndef INC_SRT_LOGGER_DEFS_H
#define INC_SRT_LOGGER_DEFS_H
#include "srt.h"
#include "logging.h"
namespace srt_logging
{
struct AllFaOn
{
LogConfig::fa_bitset_t allfa;
AllFaOn();
};
extern Logger gglog;
extern Logger smlog;
extern Logger cnlog;
extern Logger xtlog;
extern Logger tslog;
extern Logger rslog;
extern Logger cclog;
extern Logger pflog;
extern Logger aclog;
extern Logger qclog;
extern Logger eilog;
extern Logger arlog;
extern Logger brlog;
extern Logger qrlog;
extern Logger krlog;
extern Logger grlog;
extern Logger aslog;
extern Logger bslog;
extern Logger qslog;
extern Logger kslog;
extern Logger gslog;
extern Logger inlog;
extern Logger qmlog;
extern Logger kmlog;
extern Logger gmlog;
extern Logger ealog;
} // namespace srt_logging
#endif

View file

@ -13,8 +13,8 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__SRT_LOGGING_H
#define INC__SRT_LOGGING_H
#ifndef INC_SRT_LOGGING_H
#define INC_SRT_LOGGING_H
#include <iostream>
@ -28,16 +28,13 @@ written by
#else
#include <sys/time.h>
#endif
#include <pthread.h>
#if HAVE_CXX11
#include <mutex>
#endif
#include "srt.h"
#include "utilities.h"
#include "threadname.h"
#include "logging_api.h"
#include "srt_compat.h"
#include "sync.h"
#ifdef __GNUC__
#define PRINTF_LIKE __attribute__((format(printf,2,3)))
@ -53,17 +50,24 @@ written by
// LOGC uses an iostream-like syntax, using the special 'log' symbol.
// This symbol isn't visible outside the log macro parameters.
// Usage: LOGC(mglog.Debug, log << param1 << param2 << param3);
#define LOGC(logdes, args) if (logdes.CheckEnabled()) { srt_logging::LogDispatcher::Proxy log(logdes); log.setloc(__FILE__, __LINE__, __FUNCTION__); args; }
// Usage: LOGC(gglog.Debug, log << param1 << param2 << param3);
#define LOGC(logdes, args) if (logdes.CheckEnabled()) \
{ \
srt_logging::LogDispatcher::Proxy log(logdes); \
log.setloc(__FILE__, __LINE__, __FUNCTION__); \
const srt_logging::LogDispatcher::Proxy& log_prox SRT_ATR_UNUSED = args; \
}
// LOGF uses printf-like style formatting.
// Usage: LOGF(mglog.Debug, "%s: %d", param1.c_str(), int(param2));
// Usage: LOGF(gglog.Debug, "%s: %d", param1.c_str(), int(param2));
#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.
// Usage: LOGP(mglog.Debug, param1, param2, param3);
// Usage: LOGP(gglog.Debug, param1, param2, param3);
#define LOGP(logdes, ...) if (logdes.CheckEnabled()) logdes.printloc(__FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__)
#define IF_LOGGING(instr) instr
#if ENABLE_HEAVY_LOGGING
#define HLOGC LOGC
@ -93,6 +97,7 @@ written by
#define HLOGP(...)
#define IF_HEAVY_LOGGING(instr) (void)0
#define IF_LOGGING(instr) (void)0
#endif
@ -107,29 +112,30 @@ struct LogConfig
std::ostream* log_stream;
SRT_LOG_HANDLER_FN* loghandler_fn;
void* loghandler_opaque;
pthread_mutex_t mutex;
srt::sync::Mutex mutex;
int flags;
LogConfig(const fa_bitset_t& initial_fa):
enabled_fa(initial_fa),
max_level(LogLevel::warning),
log_stream(&std::cerr)
LogConfig(const fa_bitset_t& efa,
LogLevel::type l = LogLevel::warning,
std::ostream* ls = &std::cerr)
: enabled_fa(efa)
, max_level(l)
, log_stream(ls)
, loghandler_fn()
, loghandler_opaque()
, flags()
{
pthread_mutex_init(&mutex, 0);
}
LogConfig(const fa_bitset_t& efa, LogLevel::type l, std::ostream* ls):
enabled_fa(efa), max_level(l), log_stream(ls)
{
pthread_mutex_init(&mutex, 0);
}
~LogConfig()
{
pthread_mutex_destroy(&mutex);
}
void lock() { pthread_mutex_lock(&mutex); }
void unlock() { pthread_mutex_unlock(&mutex); }
SRT_ATTR_ACQUIRE(mutex)
void lock() { mutex.lock(); }
SRT_ATTR_RELEASE(mutex)
void unlock() { mutex.unlock(); }
};
// The LogDispatcher class represents the object that is responsible for
@ -142,7 +148,6 @@ private:
static const size_t MAX_PREFIX_SIZE = 32;
char prefix[MAX_PREFIX_SIZE+1];
LogConfig* src_config;
pthread_mutex_t mutex;
bool isset(int flg) { return (src_config->flags & flg) != 0; }
@ -169,12 +174,10 @@ public:
strcat(prefix, ":");
strcat(prefix, logger_pfx);
}
pthread_mutex_init(&mutex, 0);
}
~LogDispatcher()
{
pthread_mutex_destroy(&mutex);
}
bool CheckEnabled();
@ -244,6 +247,11 @@ public:
return *this;
}
DummyProxy& vform(const char*, va_list)
{
return *this;
}
DummyProxy& setloc(const char* , int , std::string)
{
return *this;
@ -292,7 +300,7 @@ struct LogDispatcher::Proxy
// or better __func__.
std::string ExtractName(std::string pretty_function);
Proxy(LogDispatcher& guy);
Proxy(LogDispatcher& guy);
// Copy constructor is needed due to noncopyable ostringstream.
// This is used only in creation of the default object, so just
@ -407,7 +415,6 @@ inline bool LogDispatcher::CheckEnabled()
return configured_enabled_fa && level <= configured_maxlevel;
}
SRT_API std::string FormatTime(uint64_t time);
#if HAVE_CXX11
@ -423,7 +430,7 @@ inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args)
}
template <class... Args>
inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, Args&&... args ATR_UNUSED)
inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, Args&&... args SRT_ATR_UNUSED)
{
#ifdef ENABLE_LOGGING
std::ostringstream serr;
@ -441,7 +448,7 @@ inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line AT
#else
template <class Arg>
inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, const Arg& arg ATR_UNUSED)
inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, const Arg& arg SRT_ATR_UNUSED)
{
#ifdef ENABLE_LOGGING
std::ostringstream serr;

Some files were not shown because too many files have changed in this diff Show more