From e3bca883e19c679e78b4d96cda843212add8471d Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 16 May 2021 16:14:00 +0800 Subject: [PATCH] SuqashSRS4: Build SRT native --- .circleci/config.yml | 65 +- README.md | 4 + trunk/3rdparty/README.md | 4 + trunk/3rdparty/ffmpeg-4-fit/LICENSE.md | 129 + trunk/3rdparty/srt-1-fit/CMakeLists.txt | 1031 ++ trunk/3rdparty/srt-1-fit/LICENSE | 373 + .../srt-1-fit/cmake_object_lib_support.c | 9 + .../srt-1-fit/common/filelist_win32.maf | 10 + .../srt-1-fit/common/win/ATTIC/winporting.h | 63 + .../srt-1-fit/common/win/syslog_defs.h | 45 + trunk/3rdparty/srt-1-fit/common/win/unistd.h | 52 + trunk/3rdparty/srt-1-fit/common/win/wintime.h | 56 + trunk/3rdparty/srt-1-fit/common/win_time.cpp | 65 + trunk/3rdparty/srt-1-fit/configure | 249 + trunk/3rdparty/srt-1-fit/configure-data.tcl | 361 + .../srt-1-fit/haicrypt/cryspr-config.h | 22 + .../srt-1-fit/haicrypt/cryspr-gnutls.c | 178 + .../srt-1-fit/haicrypt/cryspr-gnutls.h | 61 + .../srt-1-fit/haicrypt/cryspr-mbedtls.c | 235 + .../srt-1-fit/haicrypt/cryspr-mbedtls.h | 63 + .../srt-1-fit/haicrypt/cryspr-openssl.c | 218 + .../srt-1-fit/haicrypt/cryspr-openssl.h | 65 + trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c | 709 ++ trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h | 203 + .../srt-1-fit/haicrypt/filelist-gnutls.maf | 27 + .../srt-1-fit/haicrypt/filelist-mbedtls.maf | 25 + .../srt-1-fit/haicrypt/filelist-openssl.maf | 25 + trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h | 138 + .../srt-1-fit/haicrypt/haicrypt_log.cpp | 120 + .../srt-1-fit/haicrypt/haicrypt_log.h | 34 + trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c | 342 + trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h | 169 + .../3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h | 97 + .../srt-1-fit/haicrypt/hcrypt_ctx_rx.c | 199 + .../srt-1-fit/haicrypt/hcrypt_ctx_tx.c | 420 + .../3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h | 155 + trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c | 146 + trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_sa.c | 96 + trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c | 174 + trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ut.c | 224 + .../srt-1-fit/haicrypt/hcrypt_xpt_srt.c | 171 + .../srt-1-fit/haicrypt/hcrypt_xpt_sta.c | 180 + trunk/3rdparty/srt-1-fit/nuget.config | 11 + .../srt-1-fit/scripts/FindMbedTLS.cmake | 115 + trunk/3rdparty/srt-1-fit/scripts/check-deps | 56 + .../srt-1-fit/scripts/gather-package.bat | 35 + .../scripts/generate-configure-options.tcl | 115 + .../scripts/googletest-download.cmake | 19 + .../srt-1-fit/scripts/googletest.cmake | 32 + .../3rdparty/srt-1-fit/scripts/haiUtil.cmake | 291 + trunk/3rdparty/srt-1-fit/scripts/iOS.cmake | 173 + trunk/3rdparty/srt-1-fit/scripts/mafread.tcl | 58 + .../scripts/set-version-metadata.ps1 | 47 + trunk/3rdparty/srt-1-fit/scripts/srt-ffplay | 31 + trunk/3rdparty/srt-1-fit/scripts/srt.pc.in | 12 + .../srt-1-fit/scripts/tcp-echo-client.tcl | 110 + .../srt-1-fit/scripts/tcp-echo-server.tcl | 51 + .../3rdparty/srt-1-fit/srtcore/ATTIC/ccc.cpp | 360 + trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.h | 219 + trunk/3rdparty/srt-1-fit/srtcore/README.md | 75 + trunk/3rdparty/srt-1-fit/srtcore/api.cpp | 3245 ++++++ trunk/3rdparty/srt-1-fit/srtcore/api.h | 300 + trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp | 1955 ++++ trunk/3rdparty/srt-1-fit/srtcore/buffer.h | 510 + trunk/3rdparty/srt-1-fit/srtcore/cache.cpp | 120 + trunk/3rdparty/srt-1-fit/srtcore/cache.h | 266 + trunk/3rdparty/srt-1-fit/srtcore/channel.cpp | 765 ++ trunk/3rdparty/srt-1-fit/srtcore/channel.h | 187 + trunk/3rdparty/srt-1-fit/srtcore/common.cpp | 1044 ++ trunk/3rdparty/srt-1-fit/srtcore/common.h | 850 ++ trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp | 643 ++ trunk/3rdparty/srt-1-fit/srtcore/congctl.h | 206 + trunk/3rdparty/srt-1-fit/srtcore/core.cpp | 9705 +++++++++++++++++ trunk/3rdparty/srt-1-fit/srtcore/core.h | 873 ++ trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp | 887 ++ trunk/3rdparty/srt-1-fit/srtcore/crypto.h | 268 + trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp | 727 ++ trunk/3rdparty/srt-1-fit/srtcore/epoll.h | 395 + trunk/3rdparty/srt-1-fit/srtcore/fec.cpp | 2253 ++++ trunk/3rdparty/srt-1-fit/srtcore/fec.h | 248 + trunk/3rdparty/srt-1-fit/srtcore/filelist.maf | 57 + .../3rdparty/srt-1-fit/srtcore/handshake.cpp | 260 + trunk/3rdparty/srt-1-fit/srtcore/handshake.h | 342 + trunk/3rdparty/srt-1-fit/srtcore/list.cpp | 769 ++ trunk/3rdparty/srt-1-fit/srtcore/list.h | 250 + trunk/3rdparty/srt-1-fit/srtcore/logging.h | 481 + .../3rdparty/srt-1-fit/srtcore/logging_api.h | 108 + trunk/3rdparty/srt-1-fit/srtcore/md5.cpp | 381 + trunk/3rdparty/srt-1-fit/srtcore/md5.h | 91 + .../3rdparty/srt-1-fit/srtcore/netinet_any.h | 133 + trunk/3rdparty/srt-1-fit/srtcore/packet.cpp | 494 + trunk/3rdparty/srt-1-fit/srtcore/packet.h | 421 + .../srt-1-fit/srtcore/packetfilter.cpp | 294 + .../3rdparty/srt-1-fit/srtcore/packetfilter.h | 198 + .../srt-1-fit/srtcore/packetfilter_api.h | 140 + .../srt-1-fit/srtcore/packetfilter_builtin.h | 18 + .../3rdparty/srt-1-fit/srtcore/platform_sys.h | 34 + trunk/3rdparty/srt-1-fit/srtcore/queue.cpp | 1684 +++ trunk/3rdparty/srt-1-fit/srtcore/queue.h | 548 + trunk/3rdparty/srt-1-fit/srtcore/srt.h | 747 ++ trunk/3rdparty/srt-1-fit/srtcore/srt4udt.h | 71 + .../3rdparty/srt-1-fit/srtcore/srt_c_api.cpp | 318 + trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c | 140 + trunk/3rdparty/srt-1-fit/srtcore/srt_compat.h | 105 + .../3rdparty/srt-1-fit/srtcore/srt_shared.rc | 45 + trunk/3rdparty/srt-1-fit/srtcore/threadname.h | 87 + trunk/3rdparty/srt-1-fit/srtcore/udt.h | 413 + trunk/3rdparty/srt-1-fit/srtcore/utilities.h | 969 ++ trunk/3rdparty/srt-1-fit/srtcore/version.h.in | 34 + trunk/3rdparty/srt-1-fit/srtcore/window.cpp | 250 + trunk/3rdparty/srt-1-fit/srtcore/window.h | 352 + trunk/auto/depends.sh | 85 +- trunk/auto/options.sh | 5 +- trunk/conf/full.conf | 4 - trunk/conf/srt.conf | 6 + trunk/conf/srt2rtc.conf | 48 + trunk/configure | 21 +- trunk/src/app/srs_app_config.cpp | 19 +- trunk/src/app/srs_app_config.hpp | 1 - trunk/src/app/srs_app_coworkers.cpp | 4 +- trunk/src/app/srs_app_coworkers.hpp | 6 +- trunk/src/app/srs_app_dvr.hpp | 2 +- trunk/src/app/srs_app_edge.cpp | 8 +- trunk/src/app/srs_app_edge.hpp | 14 +- trunk/src/app/srs_app_forward.cpp | 2 +- trunk/src/app/srs_app_forward.hpp | 2 +- trunk/src/app/srs_app_gb28181.cpp | 2 +- trunk/src/app/srs_app_gb28181.hpp | 4 +- trunk/src/app/srs_app_hds.hpp | 2 +- trunk/src/app/srs_app_hls.hpp | 2 +- trunk/src/app/srs_app_http_conn.cpp | 4 +- trunk/src/app/srs_app_http_conn.hpp | 8 +- trunk/src/app/srs_app_http_stream.cpp | 42 +- trunk/src/app/srs_app_http_stream.hpp | 34 +- trunk/src/app/srs_app_recv_thread.cpp | 4 +- trunk/src/app/srs_app_recv_thread.hpp | 12 +- trunk/src/app/srs_app_rtc_conn.cpp | 30 +- trunk/src/app/srs_app_rtc_conn.hpp | 24 +- trunk/src/app/srs_app_rtc_server.cpp | 2 +- trunk/src/app/srs_app_rtc_server.hpp | 2 +- trunk/src/app/srs_app_rtc_source.cpp | 110 +- trunk/src/app/srs_app_rtc_source.hpp | 86 +- trunk/src/app/srs_app_rtmp_conn.cpp | 26 +- trunk/src/app/srs_app_rtmp_conn.hpp | 22 +- trunk/src/app/srs_app_server.cpp | 4 +- trunk/src/app/srs_app_server.hpp | 8 +- trunk/src/app/srs_app_source.cpp | 160 +- trunk/src/app/srs_app_source.hpp | 81 +- trunk/src/app/srs_app_threads.cpp | 4 +- trunk/src/core/srs_core_version4.hpp | 2 +- 150 files changed, 45007 insertions(+), 398 deletions(-) create mode 100644 trunk/3rdparty/ffmpeg-4-fit/LICENSE.md create mode 100644 trunk/3rdparty/srt-1-fit/CMakeLists.txt create mode 100644 trunk/3rdparty/srt-1-fit/LICENSE create mode 100644 trunk/3rdparty/srt-1-fit/cmake_object_lib_support.c create mode 100644 trunk/3rdparty/srt-1-fit/common/filelist_win32.maf create mode 100644 trunk/3rdparty/srt-1-fit/common/win/ATTIC/winporting.h create mode 100644 trunk/3rdparty/srt-1-fit/common/win/syslog_defs.h create mode 100644 trunk/3rdparty/srt-1-fit/common/win/unistd.h create mode 100644 trunk/3rdparty/srt-1-fit/common/win/wintime.h create mode 100644 trunk/3rdparty/srt-1-fit/common/win_time.cpp create mode 100755 trunk/3rdparty/srt-1-fit/configure create mode 100644 trunk/3rdparty/srt-1-fit/configure-data.tcl create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/cryspr-config.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/cryspr-gnutls.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/cryspr-gnutls.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/filelist-gnutls.maf create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/filelist-mbedtls.maf create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/filelist-openssl.maf create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.cpp create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_rx.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_tx.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_sa.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ut.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_xpt_srt.c create mode 100644 trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_xpt_sta.c create mode 100644 trunk/3rdparty/srt-1-fit/nuget.config create mode 100644 trunk/3rdparty/srt-1-fit/scripts/FindMbedTLS.cmake create mode 100755 trunk/3rdparty/srt-1-fit/scripts/check-deps create mode 100644 trunk/3rdparty/srt-1-fit/scripts/gather-package.bat create mode 100755 trunk/3rdparty/srt-1-fit/scripts/generate-configure-options.tcl create mode 100644 trunk/3rdparty/srt-1-fit/scripts/googletest-download.cmake create mode 100644 trunk/3rdparty/srt-1-fit/scripts/googletest.cmake create mode 100644 trunk/3rdparty/srt-1-fit/scripts/haiUtil.cmake create mode 100644 trunk/3rdparty/srt-1-fit/scripts/iOS.cmake create mode 100755 trunk/3rdparty/srt-1-fit/scripts/mafread.tcl create mode 100644 trunk/3rdparty/srt-1-fit/scripts/set-version-metadata.ps1 create mode 100755 trunk/3rdparty/srt-1-fit/scripts/srt-ffplay create mode 100644 trunk/3rdparty/srt-1-fit/scripts/srt.pc.in create mode 100644 trunk/3rdparty/srt-1-fit/scripts/tcp-echo-client.tcl create mode 100644 trunk/3rdparty/srt-1-fit/scripts/tcp-echo-server.tcl create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/README.md create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/api.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/api.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/buffer.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/cache.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/cache.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/channel.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/channel.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/common.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/common.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/congctl.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/core.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/core.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/crypto.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp create mode 100755 trunk/3rdparty/srt-1-fit/srtcore/epoll.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/fec.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/fec.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/filelist.maf create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/handshake.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/list.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/list.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/logging.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/logging_api.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/md5.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/md5.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/netinet_any.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/packet.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/packet.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/packetfilter_builtin.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/queue.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/queue.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/srt.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/srt4udt.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/srt_compat.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/srt_shared.rc create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/threadname.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/udt.h create mode 100755 trunk/3rdparty/srt-1-fit/srtcore/utilities.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/version.h.in create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/window.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/window.h create mode 100644 trunk/conf/srt2rtc.conf diff --git a/.circleci/config.yml b/.circleci/config.yml index fc1945ec4..3438cfba3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,6 +24,14 @@ jobs: - run: | echo "Build and run SRS with GB28181" && cd trunk && ./configure --gb28181=on && make + build-c7-srt: + docker: + - image: ossrs/srs:dev + steps: + - checkout + - run: | + echo "Build and run SRS with SRT" && + cd trunk && ./configure --srt=on && make build-c8-baseline: docker: - image: ossrs/srs:dev8 @@ -32,14 +40,62 @@ jobs: - run: | echo "Build and run SRS for CentOS8" && cd trunk && ./configure && make - build-u20-baseline: + build-c8-srt: docker: - image: ossrs/srs:dev8 + steps: + - checkout + - run: | + echo "Build and run SRS with SRT for CentOS8" && + cd trunk && ./configure --srt=on && make + build-u16-baseline: + docker: + - image: ossrs/srs:ubuntu16 + steps: + - checkout + - run: | + echo "Build and run SRS for Ubuntu16" && + cd trunk && ./configure && make + build-u16-srt: + docker: + - image: ossrs/srs:ubuntu16 + steps: + - checkout + - run: | + echo "Build and run SRS with SRT for Ubuntu16" && + cd trunk && ./configure --srt=on && make + build-u18-baseline: + docker: + - image: ossrs/srs:ubuntu18 + steps: + - checkout + - run: | + echo "Build and run SRS for Ubuntu18" && + cd trunk && ./configure && make + build-u18-srt: + docker: + - image: ossrs/srs:ubuntu18 + steps: + - checkout + - run: | + echo "Build and run SRS with SRT for Ubuntu18" && + cd trunk && ./configure --srt=on && make + build-u20-baseline: + docker: + - image: ossrs/srs:ubuntu20 steps: - checkout - run: | echo "Build and run SRS for Ubuntu20" && cd trunk && ./configure && make + build-u20-srt: + docker: + - image: ossrs/srs:ubuntu20 + steps: + - checkout + - run: | + echo "Build and run SRS with SRT for Ubuntu20" && + cd trunk && ./configure --srt=on && make run-utest: docker: - image: ossrs/srs:dev @@ -76,6 +132,13 @@ workflows: - run-regression-test - build-c7-noasm - build-c7-gb28181 + - build-c7-srt - build-c8-baseline + - build-c8-srt + - build-u16-baseline + - build-u16-srt + - build-u18-baseline + - build-u18-srt - build-u20-baseline + - build-u20-srt - build-c7-ansi-noff diff --git a/README.md b/README.md index 00814271c..e3cfefdf6 100755 --- a/README.md +++ b/README.md @@ -182,6 +182,10 @@ The ports used by SRS: ## V4 changes +* v4.0, 2021-05-15, SRT: Build SRT from source by SRS. 4.0.115 +* v4.0, 2021-05-15, Rename SrsConsumer* to SrsLiveConsumer*. 4.0.114 +* v4.0, 2021-05-15, Rename SrsRtcStream* to SrsRtcSource*. 4.0.113 +* v4.0, 2021-05-15, Rename SrsSource* to SrsLiveSource*. 4.0.112 * v4.0, 2021-05-15, Rename SrsRtpPacket2 to SrsRtpPacket. 4.0.111 * v4.0, 2021-05-14, RTC: Remove [Object Cache Pool](https://github.com/ossrs/srs/commit/14bfc98122bba369572417c19ebb2a61b373fc45#commitcomment-47655008), no effect. 4.0.110 * v4.0, 2021-05-14, Change virtual public to public. 4.0.109 diff --git a/trunk/3rdparty/README.md b/trunk/3rdparty/README.md index 5d5ef35db..4b97c9ee7 100644 --- a/trunk/3rdparty/README.md +++ b/trunk/3rdparty/README.md @@ -4,6 +4,10 @@ http-parser-2.1.zip nginx-1.5.7.zip * for srs to support hls streaming. +srt-1-fit +srt-1.4.1.tar.gz +* https://github.com/Haivision/srt/releases/tag/v1.4.1 + openssl-1.1.1b.tar.gz openssl-1.1.0e.zip openssl-OpenSSL_1_0_2u.tar.gz diff --git a/trunk/3rdparty/ffmpeg-4-fit/LICENSE.md b/trunk/3rdparty/ffmpeg-4-fit/LICENSE.md new file mode 100644 index 000000000..613070e1b --- /dev/null +++ b/trunk/3rdparty/ffmpeg-4-fit/LICENSE.md @@ -0,0 +1,129 @@ +# License + +Most files in FFmpeg are under the GNU Lesser General Public License version 2.1 +or later (LGPL v2.1+). Read the file `COPYING.LGPLv2.1` for details. Some other +files have MIT/X11/BSD-style licenses. In combination the LGPL v2.1+ applies to +FFmpeg. + +Some optional parts of FFmpeg are licensed under the GNU General Public License +version 2 or later (GPL v2+). See the file `COPYING.GPLv2` for details. None of +these parts are used by default, you have to explicitly pass `--enable-gpl` to +configure to activate them. In this case, FFmpeg's license changes to GPL v2+. + +Specifically, the GPL parts of FFmpeg are: + +- libpostproc +- optional x86 optimization in the files + - `libavcodec/x86/flac_dsp_gpl.asm` + - `libavcodec/x86/idct_mmx.c` + - `libavfilter/x86/vf_removegrain.asm` +- the following building and testing tools + - `compat/solaris/make_sunver.pl` + - `doc/t2h.pm` + - `doc/texi2pod.pl` + - `libswresample/tests/swresample.c` + - `tests/checkasm/*` + - `tests/tiny_ssim.c` +- the following filters in libavfilter: + - `signature_lookup.c` + - `vf_blackframe.c` + - `vf_boxblur.c` + - `vf_colormatrix.c` + - `vf_cover_rect.c` + - `vf_cropdetect.c` + - `vf_delogo.c` + - `vf_eq.c` + - `vf_find_rect.c` + - `vf_fspp.c` + - `vf_histeq.c` + - `vf_hqdn3d.c` + - `vf_kerndeint.c` + - `vf_lensfun.c` (GPL version 3 or later) + - `vf_mcdeint.c` + - `vf_mpdecimate.c` + - `vf_nnedi.c` + - `vf_owdenoise.c` + - `vf_perspective.c` + - `vf_phase.c` + - `vf_pp.c` + - `vf_pp7.c` + - `vf_pullup.c` + - `vf_repeatfields.c` + - `vf_sab.c` + - `vf_signature.c` + - `vf_smartblur.c` + - `vf_spp.c` + - `vf_stereo3d.c` + - `vf_super2xsai.c` + - `vf_tinterlace.c` + - `vf_uspp.c` + - `vf_vaguedenoiser.c` + - `vsrc_mptestsrc.c` + +Should you, for whatever reason, prefer to use version 3 of the (L)GPL, then +the configure parameter `--enable-version3` will activate this licensing option +for you. Read the file `COPYING.LGPLv3` or, if you have enabled GPL parts, +`COPYING.GPLv3` to learn the exact legal terms that apply in this case. + +There are a handful of files under other licensing terms, namely: + +* The files `libavcodec/jfdctfst.c`, `libavcodec/jfdctint_template.c` and + `libavcodec/jrevdct.c` are taken from libjpeg, see the top of the files for + licensing details. Specifically note that you must credit the IJG in the + documentation accompanying your program if you only distribute executables. + You must also indicate any changes including additions and deletions to + those three files in the documentation. +* `tests/reference.pnm` is under the expat license. + + +## External libraries + +FFmpeg can be combined with a number of external libraries, which sometimes +affect the licensing of binaries resulting from the combination. + +### Compatible libraries + +The following libraries are under GPL version 2: +- avisynth +- frei0r +- libcdio +- libdavs2 +- librubberband +- libvidstab +- libx264 +- libx265 +- libxavs +- libxavs2 +- libxvid + +When combining them with FFmpeg, FFmpeg needs to be licensed as GPL as well by +passing `--enable-gpl` to configure. + +The following libraries are under LGPL version 3: +- gmp +- libaribb24 +- liblensfun + +When combining them with FFmpeg, use the configure option `--enable-version3` to +upgrade FFmpeg to the LGPL v3. + +The VMAF, mbedTLS, RK MPI, OpenCORE and VisualOn libraries are under the Apache License +2.0. That license is incompatible with the LGPL v2.1 and the GPL v2, but not with +version 3 of those licenses. So to combine these libraries with FFmpeg, the +license version needs to be upgraded by passing `--enable-version3` to configure. + +The smbclient library is under the GPL v3, to combine it with FFmpeg, +the options `--enable-gpl` and `--enable-version3` have to be passed to +configure to upgrade FFmpeg to the GPL v3. + +### Incompatible libraries + +There are certain libraries you can combine with FFmpeg whose licenses are not +compatible with the GPL and/or the LGPL. If you wish to enable these +libraries, even in circumstances that their license may be incompatible, pass +`--enable-nonfree` to configure. This will cause the resulting binary to be +unredistributable. + +The Fraunhofer FDK AAC and OpenSSL libraries are under licenses which are +incompatible with the GPLv2 and v3. To the best of our knowledge, they are +compatible with the LGPL. diff --git a/trunk/3rdparty/srt-1-fit/CMakeLists.txt b/trunk/3rdparty/srt-1-fit/CMakeLists.txt new file mode 100644 index 000000000..7fafe77cc --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/CMakeLists.txt @@ -0,0 +1,1031 @@ +# +# 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/. +# + +cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) +# XXX This can be potentially done in future, but there still exist +# some dependent project using cmake 2.8 - this can't be done this way. +#cmake_minimum_required (VERSION 3.0.2 FATAL_ERROR) +#project(SRT VERSION "1.4.1") +project(SRT C CXX) + +set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") +include(haiUtil) +include(FindPkgConfig) +# XXX See 'if (MINGW)' condition below, may need fixing. +include(FindThreads) +include(CheckFunctionExists) + +# Platform shortcuts +string(TOLOWER ${CMAKE_SYSTEM_NAME} SYSNAME_LC) +set_if(DARWIN ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") +set_if(LINUX ${CMAKE_SYSTEM_NAME} MATCHES "Linux") +set_if(BSD ${SYSNAME_LC} MATCHES "bsd$") +set_if(MICROSOFT WIN32 AND (NOT MINGW AND NOT CYGWIN)) +set_if(GNU ${CMAKE_SYSTEM_NAME} MATCHES "GNU") +set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR CYGWIN OR GNU) + +# Not sure what to do in case of compiling by MSVC. +# This will make installdir in C:\Program Files\SRT then +# inside "bin" and "lib64" directories. At least this maintains +# the current status. Shall this be not desired, override values +# of CMAKE_INSTALL_BINDIR, CMAKE_INSTALL_LIBDIR and CMAKE_INSTALL_INCLUDEDIR. +if (NOT DEFINED CMAKE_INSTALL_LIBDIR) + include(GNUInstallDirs) +endif() + +set (SRT_VERSION 1.4.1) +set_version_variables(SRT_VERSION ${SRT_VERSION}) + +# The CMAKE_BUILD_TYPE seems not to be always set, weird. +if (NOT DEFINED ENABLE_DEBUG) + + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set (ENABLE_DEBUG ON) + else() + set (ENABLE_DEBUG OFF) + endif() +endif() + +# Set CMAKE_BUILD_TYPE properly, now that you know +# that ENABLE_DEBUG is set as it should. + +if (ENABLE_DEBUG EQUAL 2) + set (CMAKE_BUILD_TYPE "RelWithDebInfo") +elseif (ENABLE_DEBUG) # 1, ON, YES, TRUE, Y, or any other non-zero number + set (CMAKE_BUILD_TYPE "Debug") +else() + set (CMAKE_BUILD_TYPE "Release") +endif() + +message(STATUS "BUILD TYPE: ${CMAKE_BUILD_TYPE}") + +getVarsWith(ENFORCE_ enforcers) +foreach(ef ${enforcers}) + set (val ${${ef}}) + if (NOT val STREQUAL "") + set(val =${val}) + endif() + string(LENGTH ENFORCE_ pflen) + string(LENGTH ${ef} eflen) + math(EXPR alen ${eflen}-${pflen}) + string(SUBSTRING ${ef} ${pflen} ${alen} ef) + message(STATUS "FORCED PP VARIABLE: ${ef}${val}") + add_definitions(-D${ef}${val}) +endforeach() + +# option defaults +# XXX CHANGE: Logging is enabled now by default, +# use ENABLE_LOGGING=NO in cmake or +# --disable-logging in configure. +set(ENABLE_HEAVY_LOGGING_DEFAULT OFF) + +# Always turn logging on if the build type is debug +if (ENABLE_DEBUG) + set(ENABLE_HEAVY_LOGGING_DEFAULT ON) +endif() + + +# options +option(CYGWIN_USE_POSIX "Should the POSIX API be used for cygwin. Ignored if the system isn't cygwin." OFF) +option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" ON) +option(ENABLE_APPS "Should the Support Applications be Built?" ON) +option(ENABLE_PROFILE "Should instrument the code for profiling. Ignored for non-GNU compiler." $ENV{HAI_BUILD_PROFILE}) +option(ENABLE_LOGGING "Should logging be enabled" ON) +option(ENABLE_HEAVY_LOGGING "Should heavy debug logging be enabled" ${ENABLE_HEAVY_LOGGING_DEFAULT}) +option(ENABLE_HAICRYPT_LOGGING "Should logging in haicrypt be enabled" 0) +option(ENABLE_SHARED "Should libsrt be built as a shared library" ON) +option(ENABLE_STATIC "Should libsrt be built as a static library" ON) +option(ENABLE_RELATIVE_LIBPATH "Should application contain relative library paths, like ../lib" OFF) +option(ENABLE_SUFLIP "Should suflip tool be built" OFF) +option(ENABLE_GETNAMEINFO "In-logs sockaddr-to-string should do rev-dns" OFF) +option(ENABLE_UNITTESTS "Enable unit tests" OFF) +option(ENABLE_ENCRYPTION "Enable encryption in SRT" ON) +option(ENABLE_CXX_DEPS "Extra library dependencies in srt.pc for the CXX libraries useful with C language" ON) +option(USE_STATIC_LIBSTDCXX "Should use static rather than shared libstdc++" OFF) +option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON) +option(ENABLE_CODE_COVERAGE "Enable code coverage reporting" OFF) +option(ENABLE_MONOTONIC_CLOCK "Enforced clock_gettime with monotonic clock on GC CV /temporary fix for #729/" OFF) +option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) +option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potentially higher CPU load" OFF) +option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) + +set(TARGET_srt "srt" CACHE STRING "The name for the SRT library") + +if (NOT ENABLE_LOGGING) + set (ENABLE_HEAVY_LOGGING OFF) + message(STATUS "LOGGING: DISABLED") +else() + if (ENABLE_HEAVY_LOGGING) + message(STATUS "LOGGING: HEAVY") + else() + message(STATUS "LOGGING: ENABLED") + endif() +endif() + +if (USE_BUSY_WAITING) + message(STATUS "USE_BUSY_WAITING: ON") + list(APPEND SRT_EXTRA_CFLAGS "-DUSE_BUSY_WAITING=1") +else() + message(STATUS "USE_BUSY_WAITING: OFF (default)") +endif() + +if ( CYGWIN AND NOT CYGWIN_USE_POSIX ) + set(WIN32 1) + set(CMAKE_LEGACY_CYGWIN_WIN32 1) + add_definitions(-DWIN32=1 -DCYGWIN=1) + message(STATUS "HAVE CYGWIN. Setting backward compat CMAKE_LEGACY_CYGWIN_WIN32 and -DWIN32") +endif() + +if (NOT USE_ENCLIB) + if (USE_GNUTLS) + message("NOTE: USE_GNUTLS is deprecated. Use -DUSE_ENCLIB=gnutls instead.") + set (USE_ENCLIB gnutls) + else() + set (USE_ENCLIB openssl) + endif() + +endif() + +set(USE_ENCLIB "${USE_ENCLIB}" CACHE STRING "The crypto library that SRT uses") +set_property(CACHE USE_ENCLIB PROPERTY STRINGS "openssl" "gnutls" "mbedtls") + +# Make sure DLLs and executabes go to the same path regardles of subdirectory +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +# Handle WITH_COMPILER_PREFIX and WITH_COMPILER_TYPE options +if (DEFINED WITH_COMPILER_PREFIX) + message(STATUS "Handling compiler with WITH_COMPILER_PREFIX=${WITH_COMPILER_PREFIX}") + # Check also type. Default is gcc. + if (NOT DEFINED WITH_COMPILER_TYPE) + set (WITH_COMPILER_TYPE gcc) + endif() + + if (${WITH_COMPILER_TYPE} STREQUAL gcc) + set (CMAKE_C_COMPILER ${WITH_COMPILER_PREFIX}gcc) + set (CMAKE_CXX_COMPILER ${WITH_COMPILER_PREFIX}g++) + elseif (${WITH_COMPILER_TYPE} STREQUAL cc) + set (CMAKE_C_COMPILER ${WITH_COMPILER_PREFIX}cc) + set (CMAKE_CXX_COMPILER ${WITH_COMPILER_PREFIX}c++) + else() + # Use blindly for C compiler and ++ for C++. + # At least this matches clang. + set (CMAKE_C_COMPILER ${WITH_COMPILER_PREFIX}${WITH_COMPILER_TYPE}) + set (CMAKE_CXX_COMPILER ${WITH_COMPILER_PREFIX}${WITH_COMPILER_TYPE}++) + endif() + + message(STATUS "Compiler type: ${WITH_COMPILER_TYPE}. C: ${CMAKE_C_COMPILER}; C++: ${CMAKE_CXX_COMPILER}") +else() + message(STATUS "No WITH_COMPILER_PREFIX - using C++ compiler ${CMAKE_CXX_COMPILER}") +endif() + + +if (DEFINED WITH_SRT_TARGET) + set (TARGET_haisrt ${WITH_SRT_TARGET}) +endif() + +# When you use crosscompiling, you have to take care that PKG_CONFIG_PATH +# and CMAKE_PREFIX_PATH are set properly. + +# symbol exists in win32, but function does not. +if(WIN32) + if(ENABLE_INET_PTON) + set(CMAKE_REQUIRED_LIBRARIES ws2_32) + check_function_exists(inet_pton HAVE_INET_PTON) + add_definitions(-D_WIN32_WINNT=0x0600) + else() + add_definitions(-D_WIN32_WINNT=0x0501) + endif() +else() + check_function_exists(inet_pton HAVE_INET_PTON) +endif() +if (DEFINED HAVE_INET_PTON) + add_definitions(-DHAVE_INET_PTON=1) +endif() + +if (ENABLE_MONOTONIC_CLOCK) + add_definitions(-DENABLE_MONOTONIC_CLOCK=1) +endif() + +if (ENABLE_ENCRYPTION) + if ("${USE_ENCLIB}" STREQUAL "gnutls") + set (SSL_REQUIRED_MODULES "gnutls nettle") + if (WIN32) + if (MINGW) + set (SSL_REQUIRED_MODULES "${SSL_REQUIRED_MODULES} zlib") + endif() + endif() + + pkg_check_modules (SSL REQUIRED ${SSL_REQUIRED_MODULES}) + + add_definitions( + -DUSE_GNUTLS=1 + ) + + link_directories( + ${SSL_LIBRARY_DIRS} + ) + else() # Common for mbedtls and openssl + if ("${USE_ENCLIB}" STREQUAL "mbedtls") + add_definitions(-DUSE_MBEDTLS=1) + set (SSL_REQUIRED_MODULES "mbedtls mbedcrypto") + else() + add_definitions(-DUSE_OPENSSL=1) + set (SSL_REQUIRED_MODULES "openssl libcrypto") + endif() + # Try using pkg-config method first if enabled, + # fall back to find_package method otherwise + if (USE_OPENSSL_PC) + pkg_check_modules(SSL ${SSL_REQUIRED_MODULES}) + endif() + if (SSL_FOUND) + # We have some cases when pkg-config is improperly configured + # When it doesn't ship the -L and -I options, and the CMAKE_PREFIX_PATH + # is set (also through `configure`), then we have this problem. If so, + # set forcefully the -I and -L contents to prefix/include and + # prefix/lib. + if ("${SSL_LIBRARY_DIRS}" STREQUAL "") + if (NOT "${CMAKE_PREFIX_PATH}" STREQUAL "") + message(STATUS "WARNING: pkg-config has incorrect prefix - enforcing target path prefix: ${CMAKE_PREFIX_PATH}") + set (SSL_LIBRARY_DIRS ${CMAKE_PREFIX_PATH}/${CMAKE_INSTALL_LIBDIR}) + set (SSL_INCLUDE_DIRS ${CMAKE_PREFIX_PATH}/include) + endif() + endif() + + link_directories( + ${SSL_LIBRARY_DIRS} + ) + message(STATUS "SSL via pkg-config: -L ${SSL_LIBRARY_DIRS} -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") + else() + if ("${USE_ENCLIB}" STREQUAL "mbedtls") + if ("${SSL_LIBRARY_DIRS}" STREQUAL "") + set(MBEDTLS_PREFIX "${CMAKE_PREFIX_PATH}" CACHE PATH "The path of mbedtls") + find_package(MbedTLS REQUIRED) + set (SSL_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set (SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) + endif() + if ("${SSL_LIBRARIES}" STREQUAL "") + set (SSL_LIBRARIES mbedtls mbedcrypto) + endif() + message(STATUS "SSL enforced mbedtls: -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") + else() + find_package(OpenSSL REQUIRED) + set (SSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) + set (SSL_LIBRARIES ${OPENSSL_LIBRARIES}) + message(STATUS "SSL via find_package(OpenSSL): -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") + endif() + endif() + endif() + + add_definitions(-DSRT_ENABLE_ENCRYPTION) + message(STATUS "ENCRYPTION: ENABLED, using: ${SSL_REQUIRED_MODULES}") + message (STATUS "SSL libraries: ${SSL_LIBRARIES}") +else() + message(STATUS "ENCRYPTION: DISABLED") +endif() + +if ( USE_GNUSTL ) + pkg_check_modules (GNUSTL REQUIRED gnustl) + link_directories(${GNUSTL_LIBRARY_DIRS}) + include_directories(${GNUSTL_INCLUDE_DIRS}) + set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) +endif() + +# Detect if the compiler is GNU compatable for flags +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Intel|Clang|AppleClang") + message(STATUS "COMPILER: ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER}) - GNU compat") + set(HAVE_COMPILER_GNU_COMPAT 1) +else() + message(STATUS "COMPILER: ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER}) - NOT GNU compat") + set(HAVE_COMPILER_GNU_COMPAT 0) +endif() + +if (DISABLE_CXX11) + set (ENABLE_CXX11 0) +elseif( DEFINED ENABLE_CXX11 ) +else() + set (ENABLE_CXX11 1) +endif() + +if (NOT ENABLE_CXX11) + message(WARNING "Parts that require C++11 support will be disabled (srt-live-transmit)") +endif() + +# add extra warning flags for gccish compilers +if (HAVE_COMPILER_GNU_COMPAT) + set (SRT_GCC_WARN "-Wall -Wextra") +else() + # cpp debugging on Windows :D + #set (SRT_GCC_WARN "/showIncludes") +endif() + +if (USE_STATIC_LIBSTDCXX) + if (HAVE_COMPILER_GNU_COMPAT) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++") + else() + message(FATAL_ERROR "On non-GNU-compat compiler it's not known how to use static C++ standard library.") + endif() +endif() + +# We need clock_gettime, but on some systems this is only provided +# by librt. Check if librt is required. +if (ENABLE_MONOTONIC_CLOCK AND LINUX) + # "requires" - exits on FATAL_ERROR when clock_gettime not available + test_requires_clock_gettime(NEED_CLOCK_GETTIME) + set (WITH_EXTRALIBS "${WITH_EXTRALIBS} ${NEED_CLOCK_GETTIME}") +endif() + + +# This options is necessary on some systems; on a cross-ARM compiler it +# has been detected, for example, that -lrt is necessary for some applications +# because clock_gettime is needed by some functions and it is alternatively +# provided by libc, but only in newer versions. This options is rarely necessary, +# but may help in several corner cases in unusual platforms. +if (WITH_EXTRALIBS) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WITH_EXTRALIBS}") +endif() + +# CMake has only discovered in 3.3 version that some set-finder is +# necessary. Using variables for shortcut to a clumsy check syntax. + +set (srt_libspec_shared ${ENABLE_SHARED}) +set (srt_libspec_static ${ENABLE_STATIC}) + +set (haicrypt_libspec VIRTUAL) + +set (srtpack_libspec_common) +if (srt_libspec_shared) + list(APPEND srtpack_libspec_common ${TARGET_srt}_shared) + +endif() +if (srt_libspec_static) + list(APPEND srtpack_libspec_common ${TARGET_srt}_static) +endif() + +set (SRT_SRC_HAICRYPT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/haicrypt) +set (SRT_SRC_SRTCORE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/srtcore) +set (SRT_SRC_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/common) +set (SRT_SRC_TOOLS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools) +set (SRT_SRC_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test) + +if(WIN32) + message(STATUS "DETECTED SYSTEM: WINDOWS; WIN32=1; PTW32_STATIC_LIB=1") + add_definitions(-DWIN32=1 -DPTW32_STATIC_LIB=1) +elseif(DARWIN) + message(STATUS "DETECTED SYSTEM: DARWIN; OSX=1") + add_definitions(-DOSX=1) +elseif(BSD) + message(STATUS "DETECTED SYSTEM: BSD; BSD=1") + add_definitions(-DBSD=1) +elseif(LINUX) + add_definitions(-DLINUX=1) + message(STATUS "DETECTED SYSTEM: LINUX; LINUX=1" ) +elseif(CYGWIN) + add_definitions(-DCYGWIN=1) + message(STATUS "DETECTED SYSTEM: CYGWIN (posix mode); CYGWIN=1") +elseif(GNU) + add_definitions(-DGNU=1) + message(STATUS "DETECTED SYSTEM: GNU; GNU=1" ) +else() + message(FATAL_ERROR "Unsupported system: ${CMAKE_SYSTEM_NAME}") +endif() + +add_definitions( + -D_GNU_SOURCE + -DHAI_PATCH=1 + -DHAI_ENABLE_SRT=1 + -DSRT_VERSION="${SRT_VERSION}" +) + +# This is obligatory include directory for all targets. This is only +# for private headers. Installable headers should be exclusively used DIRECTLY. +include_directories(${SRT_SRC_COMMON_DIR} ${SRT_SRC_SRTCORE_DIR} ${SRT_SRC_HAICRYPT_DIR}) + +if (ENABLE_LOGGING) + list(APPEND SRT_EXTRA_CFLAGS "-DENABLE_LOGGING=1") + if (ENABLE_HEAVY_LOGGING) + list(APPEND SRT_EXTRA_CFLAGS "-DENABLE_HEAVY_LOGGING=1") + endif() + if (ENABLE_HAICRYPT_LOGGING) + if (ENABLE_HAICRYPT_LOGGING STREQUAL 2) # Allow value 2 for INSECURE DEBUG logging + message(WARNING " *** ENABLED INSECURE HAICRYPT LOGGING - USE FOR TESTING ONLY!!! ***") + list(APPEND SRT_EXTRA_CFLAGS "-DENABLE_HAICRYPT_LOGGING=2") + else() + list(APPEND SRT_EXTRA_CFLAGS "-DENABLE_HAICRYPT_LOGGING=1") + endif() + endif() +endif() + +if (ENABLE_GETNAMEINFO) + list(APPEND SRT_EXTRA_CFLAGS "-DENABLE_GETNAMEINFO=1") +endif() + +if (ENABLE_THREAD_CHECK) + add_definitions( + -DSRT_ENABLE_THREADCHECK=1 + -DFUGU_PLATFORM=1 + -I${WITH_THREAD_CHECK_INCLUDEDIR} + ) +endif() + +if (ENABLE_PROFILE) + if (HAVE_COMPILER_GNU_COMPAT) + # They are actually cflags, not definitions, but CMake is stupid enough. + add_definitions(-g -pg) + link_libraries(-g -pg) + else() + message(FATAL_ERROR "Profiling option is not supported on this platform") + endif() +endif() + +if (ENABLE_CODE_COVERAGE) + if (HAVE_COMPILER_GNU_COMPAT) + add_definitions(-g -O0 --coverage) + link_libraries(--coverage) + else() + message(FATAL_ERROR "Code coverage option is not supported on this platform") + endif() +endif() + +if (PTHREAD_LIBRARY AND PTHREAD_INCLUDE_DIR) + message(STATUS "Pthread library: ${PTHREAD_LIBRARY}") + message(STATUS "Pthread include dir: ${PTHREAD_INCLUDE_DIR}") +elseif (MICROSOFT) + find_package(pthreads QUIET) + + if (NOT PTHREAD_INCLUDE_DIR OR NOT PTHREAD_LIBRARY) + #search package folders with GLOB to add as extra hint for headers + file(GLOB PTHREAD_PACKAGE_INCLUDE_HINT ./packages/cinegy.pthreads-win*/sources) + if (PTHREAD_PACKAGE_INCLUDE_HINT) + message(STATUS "PTHREAD_PACKAGE_INCLUDE_HINT value: ${PTHREAD_PACKAGE_INCLUDE_HINT}") + endif() + + # find pthread header + find_path(PTHREAD_INCLUDE_DIR pthread.h HINTS C:/pthread-win32/include ${PTHREAD_PACKAGE_INCLUDE_HINT}) + + if (PTHREAD_INCLUDE_DIR) + message(STATUS "Pthread include dir: ${PTHREAD_INCLUDE_DIR}") + else() + message(FATAL_ERROR "Failed to find pthread.h. Specify PTHREAD_INCLUDE_DIR.") + endif() + + #search package folders with GLOB to add as extra hint for libs + file(GLOB PTHREAD_PACKAGE_LIB_HINT ./packages/cinegy.pthreads-win*/runtimes/win-*/native/release) + if (PTHREAD_PACKAGE_LIB_HINT) + message(STATUS "PTHREAD_PACKAGE_LIB_HINT value: ${PTHREAD_PACKAGE_LIB_HINT}") + endif() + + #find pthread library + set(PTHREAD_LIB_SUFFIX "") + if (ENABLE_DEBUG) + set(PTHREAD_LIB_SUFFIX "d") + endif () + + set(PTHREAD_COMPILER_FLAG "") + if (MICROSOFT) + set(PTHREAD_COMPILER_FLAG "V") + elseif (MINGW) + set(PTHREAD_COMPILER_FLAG "G") + endif () + + foreach(EXHAND C CE SE) + foreach(COMPAT 1 2) + list(APPEND PTHREAD_W32_LIBRARY "pthread${PTHREAD_COMPILER_FLAG}${EXHAND}${PTHREAD_LIB_SUFFIX}${COMPAT}") + endforeach() + endforeach() + + find_library(PTHREAD_LIBRARY NAMES ${PTHREAD_W32_LIBRARY} pthread pthread_dll pthread_lib HINTS C:/pthread-win32/lib C:/pthread-win64/lib ${PTHREAD_PACKAGE_LIB_HINT}) + if (PTHREAD_LIBRARY) + message(STATUS "Pthread library: ${PTHREAD_LIBRARY}") + else() + message(FATAL_ERROR "Failed to find pthread library. Specify PTHREAD_LIBRARY.") + endif() + endif() +else () + find_package(Threads REQUIRED) + set(PTHREAD_LIBRARY ${CMAKE_THREAD_LIBS_INIT}) +endif() + +# This is required in some projects that add some other sources +# to the SRT library to be compiled together (aka "virtual library"). +if (DEFINED SRT_EXTRA_LIB_INC) + include(${SRT_EXTRA_LIB_INC}.cmake) + # Expected to provide variables: + # - SOURCES_srt_extra + # - EXTRA_stransmit +endif() + +# --------------------------------------------------------------------------- + +# --- +# Target: haicrypt. +# Completing sources and installable headers. Flag settings will follow. +# --- +if (ENABLE_ENCRYPTION) + set (HAICRYPT_FILELIST_MAF "filelist-${USE_ENCLIB}.maf") + + MafReadDir(haicrypt ${HAICRYPT_FILELIST_MAF} + SOURCES SOURCES_haicrypt + PUBLIC_HEADERS HEADERS_haicrypt + PROTECTED_HEADERS HEADERS_haicrypt + ) +endif() + +if (WIN32) + MafReadDir(common filelist_win32.maf + SOURCES SOURCES_common + PUBLIC_HEADERS HEADERS_srt_win32 + PROTECTED_HEADERS HEADERS_srt_win32 + ) + message(STATUS "WINDOWS detected: adding compat sources: ${SOURCES_common}") +endif() + + +# Make the OBJECT library for haicrypt and srt. Then they'll be bound into +# real libraries later, either one common, or separate. + +# This is needed for Xcode to properly handle CMake OBJECT Libraries +# From docs (https://cmake.org/cmake/help/latest/command/add_library.html#object-libraries): +# +# ... Some native build systems (such as Xcode) may not like targets that have only object files, +# so consider adding at least one real source file to any target that references $. +set(OBJECT_LIB_SUPPORT "${PROJECT_SOURCE_DIR}/cmake_object_lib_support.c") + +add_library(haicrypt_virtual OBJECT ${SOURCES_haicrypt} ${SOURCES_common}) + +# NOTE: The "virtual library" is a library specification that cmake +# doesn't support (the library of OBJECT type is something in kind of that, +# but not fully supported - for example it doesn't support transitive flags, +# so this can't be used desired way). It's a private-only dependency type, +# where the project isn't compiled into any library file at all - instead, all +# of its source files are incorporated directly to the source list of the +# project that depends on it. In cmake this must be handled manually. + + +# --- +# Target: srt. DEFINITION ONLY. Haicrypt flag settings follow. +# --- +MafReadDir(srtcore filelist.maf + SOURCES SOURCES_srt + PUBLIC_HEADERS HEADERS_srt + PROTECTED_HEADERS HEADERS_srt + PRIVATE_HEADERS HEADERS_srt_private +) + +# Auto generated version file and add it to the HEADERS_srt list. +if(DEFINED ENV{APPVEYOR_BUILD_NUMBER}) + set(SRT_VERSION_BUILD ON) + set(APPVEYOR_BUILD_NUMBER_STRING $ENV{APPVEYOR_BUILD_NUMBER}) + message(STATUS "AppVeyor build environment detected: Adding build number to version header") +endif() + +configure_file("srtcore/version.h.in" "version.h" @ONLY) + +list(INSERT HEADERS_srt 0 "${CMAKE_CURRENT_BINARY_DIR}/version.h") +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + +add_library(srt_virtual OBJECT ${SOURCES_srt} ${SOURCES_srt_extra} ${HEADERS_srt}) + +if (ENABLE_SHARED) + # Set this to sources as well, as it won't be automatically handled + foreach (tar srt_virtual haicrypt_virtual) + set_target_properties(${tar} PROPERTIES POSITION_INDEPENDENT_CODE 1) + endforeach() + + #add resource files to shared library, to set DLL metadata on Windows DLLs + if (MICROSOFT) + MafReadDir(srtcore filelist.maf + SOURCES_WIN32_SHARED SOURCES_srt_shared_win32 + ) + + message(STATUS "WINDOWS detected: Adding sources to SRT shared project: ${SOURCES_srt_shared_win32}") + endif() +endif() + +# Manual handling of dependency on virtual library +# By setting the target, all settings applied to the haicrypt target +# will now apply to the dependent library. +#list(APPEND SOURCES_srt ${SOURCES_haicrypt}) +set (VIRTUAL_srt $ $) + +if (srt_libspec_shared) + add_library(${TARGET_srt}_shared SHARED ${OBJECT_LIB_SUPPORT} ${VIRTUAL_srt} ${SOURCES_srt_shared_win32} ${HEADERS_srt_shared_win32}) + # shared libraries need PIC + set (CMAKE_POSITION_INDEPENDENT_CODE ON) + set_property(TARGET ${TARGET_srt}_shared PROPERTY OUTPUT_NAME ${TARGET_srt}) + set_target_properties (${TARGET_srt}_shared PROPERTIES VERSION ${SRT_VERSION} SOVERSION ${SRT_VERSION_MAJOR}) + list (APPEND INSTALL_TARGETS ${TARGET_srt}_shared) + if (ENABLE_ENCRYPTION) + target_link_libraries(${TARGET_srt}_shared PRIVATE ${SSL_LIBRARIES}) + endif() + if (MICROSOFT) + target_link_libraries(${TARGET_srt}_shared PRIVATE ws2_32.lib) + set_target_properties(${TARGET_srt}_shared PROPERTIES LINK_FLAGS "/DELAYLOAD:libeay32.dll") + elseif (MINGW) + target_link_libraries(${TARGET_srt}_shared PRIVATE wsock32.lib ws2_32.lib) + elseif (APPLE) + set_property(TARGET ${TARGET_srt}_shared PROPERTY MACOSX_RPATH ON) + endif() + if (USE_GNUSTL) + target_link_libraries(${TARGET_srt}_shared PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) + endif() +endif() + +if (srt_libspec_static) + add_library(${TARGET_srt}_static STATIC ${OBJECT_LIB_SUPPORT} ${VIRTUAL_srt}) + + # For Windows, leave the name to be "srt_static.lib". + # Windows generates two different library files: + # - a usual static library for static linkage + # - a shared library exposer, which allows pre-resolution and later dynamic + # linkage when running the executable + # Both having unfortunately the same names created by MSVC compiler. + # It's not the case of Cygwin/MINGW - they are named there libsrt.a and libsrt.dll.a + if (MICROSOFT) + # Keep _static suffix. By unknown reason, the name must still be set explicitly. + set_property(TARGET ${TARGET_srt}_static PROPERTY OUTPUT_NAME ${TARGET_srt}_static) + else() + set_property(TARGET ${TARGET_srt}_static PROPERTY OUTPUT_NAME ${TARGET_srt}) + endif() + + list (APPEND INSTALL_TARGETS ${TARGET_srt}_static) + if (ENABLE_ENCRYPTION) + target_link_libraries(${TARGET_srt}_static PRIVATE ${SSL_LIBRARIES}) + endif() + if (MICROSOFT) + target_link_libraries(${TARGET_srt}_static PRIVATE ws2_32.lib) + elseif (MINGW) + target_link_libraries(${TARGET_srt}_static PRIVATE wsock32 ws2_32) + endif() + if (USE_GNUSTL) + target_link_libraries(${TARGET_srt}_static PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) + endif() +endif() + + + +# --- +# And back to target: haicrypt. Both targets must be defined +# prior to setting flags, and after defining the list of sources +# can no longer be extended. +# +# For haicrypt.spec = VIRTUAL, these settings apply to srt. +# Otherwise they apply to haicrypt. +# --- + + +target_include_directories(haicrypt_virtual PRIVATE ${SSL_INCLUDE_DIRS}) + +if (MICROSOFT) + set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ws2_32.lib) +elseif (MINGW) + set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} -lwsock32 -lws2_32) +endif() + +# --- +# So, back to target: srt. Setting the rest of the settings for srt target. +# --- + +# Applying this to public includes is not transitive enough. +# On Windows, apps require this as well, so it's safer to +# spread this to all targets. +if (PTHREAD_INCLUDE_DIR) + include_directories(${PTHREAD_INCLUDE_DIR}) +endif() + +# Link libraries must be applied directly to the derivatives +# as virtual libraries (OBJECT-type) cannot have linkage declarations +# transitive or not. + +foreach(tar ${srtpack_libspec_common}) + message(STATUS "ADDING TRANSITIVE LINK DEP to:${tar} : ${PTHREAD_LIBRARY} ${dep}") + target_link_libraries (${tar} PUBLIC ${PTHREAD_LIBRARY} ${dep}) +endforeach() + + +set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ${PTHREAD_LIBRARY}) + +target_compile_definitions(srt_virtual PRIVATE -DSRT_EXPORTS ) +target_compile_definitions(haicrypt_virtual PUBLIC -DHAICRYPT_DYNAMIC) +if (ENABLE_SHARED) + target_compile_definitions(srt_virtual PUBLIC -DSRT_DYNAMIC) + target_compile_definitions(haicrypt_virtual PRIVATE -DHAICRYPT_EXPORTS) +endif() + +if (srt_libspec_shared) +if (MICROSOFT) + target_link_libraries(${TARGET_srt}_shared PUBLIC Ws2_32.lib) +endif() +endif() + +# Cygwin installs the *.dll libraries in bin directory and uses PATH. + +set (INSTALL_SHARED_DIR ${CMAKE_INSTALL_LIBDIR}) +if (CYGWIN) + set (INSTALL_SHARED_DIR ${CMAKE_INSTALL_BINDIR}) +endif() + +message(STATUS "INSTALL DIRS: bin=${CMAKE_INSTALL_BINDIR} lib=${CMAKE_INSTALL_LIBDIR} shlib=${INSTALL_SHARED_DIR} include=${CMAKE_INSTALL_INCLUDEDIR}") + +install(TARGETS ${INSTALL_TARGETS} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${INSTALL_SHARED_DIR} +) +install(FILES ${HEADERS_srt} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/srt) +if (WIN32) + install(FILES ${HEADERS_srt_win32} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/srt/win) +endif() + +# --- +# That's all for target definition +# --- + +join_arguments(SRT_EXTRA_CFLAGS ${SRT_EXTRA_CFLAGS}) + +#message(STATUS "Target srt: LIBSPEC: ${srtpack_libspec_common} SOURCES: {${SOURCES_srt}} HEADERS: {${HEADERS_srt}}") + +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SRT_DEBUG_OPT} ${SRT_EXTRA_CFLAGS} ${SRT_GCC_WARN}") +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SRT_DEBUG_OPT} ${SRT_EXTRA_CFLAGS} ${SRT_GCC_WARN}") + +# PC file generation. +if (NOT DEFINED INSTALLDIR) + set (INSTALLDIR ${CMAKE_INSTALL_PREFIX}) + get_filename_component(INSTALLDIR ${INSTALLDIR} ABSOLUTE) +endif() + +# Required if linking a C application. +# This may cause trouble when you want to compile your app with static libstdc++; +# if your build requires it, you'd probably remove -lstdc++ from the list +# obtained by `pkg-config --libs`. +if(ENABLE_CXX_DEPS) + foreach(LIB ${CMAKE_CXX_IMPLICIT_LINK_LIBRARIES}) + if(IS_ABSOLUTE ${LIB} AND EXISTS ${LIB}) + set(SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ${LIB}) + else() + set(SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} "-l${LIB}") + endif() + endforeach() +endif() + +join_arguments(SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE}) + +# haisrt.pc left temporarily for backward compatibility. To be removed in future! +configure_file(scripts/srt.pc.in haisrt.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/haisrt.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +configure_file(scripts/srt.pc.in srt.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/srt.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + +# Applications + +if (HAVE_COMPILER_GNU_COMPAT AND ENABLE_CXX11) + message(STATUS "C++ VERSION: Setting C++11 compat flag for gnu compiler") + set (CFLAGS_CXX_STANDARD "-std=c++11") +else() + message(STATUS "C++ VERSION: leaving default, not a GNU compiler, assuming C++11 or newer is default.") + set (CFLAGS_CXX_STANDARD "") +endif() + +# If static is available, link apps against static one. +# Otherwise link against shared one. + +if (srt_libspec_static) + set (srt_link_library ${TARGET_srt}_static) + if (ENABLE_RELATIVE_LIBPATH) + message(STATUS "ENABLE_RELATIVE_LIBPATH=ON will be ignored due to static linking.") + endif() +elseif(srt_libspec_shared) + set (srt_link_library ${TARGET_srt}_shared) +else() + message(FATAL_ERROR "Either ENABLE_STATIC or ENABLE_SHARED has to be ON!") +endif() + +macro(srt_add_program name) + add_executable(${name} ${ARGN}) + target_include_directories(${name} PRIVATE apps) + target_include_directories(${name} PRIVATE common) + install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endmacro() + +macro(srt_make_application name) + target_compile_options(${name} PRIVATE ${CFLAGS_CXX_STANDARD}) + + # This is recommended by cmake, but it doesn't work anyway. + # What is needed is that this below CMAKE_INSTALL_RPATH (yes, relative) + # is added as is. + # set (CMAKE_SKIP_RPATH FALSE) + # set (CMAKE_SKIP_BUILD_RPATH FALSE) + # set (CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + # set (CMAKE_INSTALL_RPATH "../${CMAKE_INSTALL_LIBDIR}") + # set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + # set (FORCE_RPATH BUILD_WITH_INSTALL_RPATH TRUE INSTALL_RPATH_USE_LINK_PATH TRUE) + + if (LINUX AND ENABLE_RELATIVE_LIBPATH AND NOT srt_libspec_static) + # This is only needed on Linux, on Windows (including Cygwin) the library file will + # be placed into the binrary directory anyway. + # XXX not sure about Mac. + # See this name used already in install(${TARGET_srt} LIBRARY DESTINATION...). + set(FORCE_RPATH LINK_FLAGS -Wl,-rpath,.,-rpath,../${CMAKE_INSTALL_LIBDIR} BUILD_WITH_INSTALL_RPATH TRUE INSTALL_RPATH_USE_LINK_PATH TRUE) + endif() + + # We state that Darwin always uses CLANG compiler, which honors this flag the same way. + set_target_properties(${name} PROPERTIES COMPILE_FLAGS "${CFLAGS_CXX_STANDARD}" ${FORCE_RPATH}) + + target_link_libraries(${name} ${srt_link_library}) + if (USE_GNUSTL) + target_link_libraries(${name} PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) + endif() + if (srt_libspec_static AND CMAKE_DL_LIBS) + target_link_libraries(${name} ${CMAKE_DL_LIBS}) + endif() +endmacro() + +macro(srt_add_application name) # ARGN=sources... + srt_add_program(${name} apps/${name}.cpp ${ARGN}) + srt_make_application(${name}) + install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endmacro() + +## FIXME: transmitmedia.cpp does not build on OpenBSD +## Issue: https://github.com/Haivision/srt/issues/590 +if (BSD + AND ${SYSNAME_LC} MATCHES "^openbsd$") + set(ENABLE_APPS OFF) +endif() +## The applications currently require c++11. +if (NOT ENABLE_CXX11) + set(ENABLE_APPS OFF) +endif() + +if (ENABLE_APPS) + + # Make a virtual library of all shared app files + MafReadDir(apps support.maf + SOURCES SOURCES_support + ) + + # A special trick that makes the shared application sources + # to be compiled once for all applications. Maybe this virtual + # library should be changed into a static one and made useful + # for users. + add_library(srtsupport_virtual OBJECT ${SOURCES_support}) + target_compile_options(srtsupport_virtual PUBLIC ${CFLAGS_CXX_STANDARD}) + set (VIRTUAL_srtsupport $) + + # Applications + + srt_add_application(srt-live-transmit ${VIRTUAL_srtsupport}) + if (DEFINED EXTRA_stransmit) + set_target_properties(srt-live-transmit PROPERTIES COMPILE_FLAGS "${EXTRA_stransmit}") + endif() + srt_add_application(srt-file-transmit ${VIRTUAL_srtsupport}) + + if (MINGW) + # FIXME: with MINGW, it fails to build apps that require C++11 + # https://github.com/Haivision/srt/issues/177 + message(WARNING "On MinGW, some C++11 apps are blocked due to lacking proper C++11 headers for . FIX IF POSSIBLE.") + else() + # srt-multiplex temporarily blocked + #srt_add_application(srt-multiplex ${VIRTUAL_srtsupport}) + srt_add_application(srt-tunnel ${VIRTUAL_srtsupport}) + target_compile_definitions(srt-tunnel PUBLIC -DSRT_ENABLE_VERBOSE_LOCK) + endif() + + if (ENABLE_TESTING) + + macro(srt_add_testprogram name) + # Variables in macros are not local. Clear them forcefully. + set (SOURCES_app_indir "") + set (SOURCES_app "") + # Unlike Silvercat, in cmake you must know the full list + # of source files at the moment when defining the target + # and it can't be altered later. + # + # For testing applications, every application has its exclusive + # list of source files in its own Manifest file. + MafReadDir(testing ${name}.maf SOURCES SOURCES_app) + srt_add_program(${name} ${SOURCES_app}) + endmacro() + + srt_add_testprogram(utility-test) + if (NOT WIN32) + # This program is symlinked under git-cygwin. + # Avoid misleading syntax error. + srt_add_testprogram(uriparser-test) + target_compile_options(uriparser-test PRIVATE -DTEST) + target_compile_options(uriparser-test PRIVATE ${CFLAGS_CXX_STANDARD}) + endif() + + srt_add_testprogram(srt-test-live) + srt_make_application(srt-test-live) + + srt_add_testprogram(srt-test-file) + srt_make_application(srt-test-file) + + srt_add_testprogram(srt-test-relay) + srt_make_application(srt-test-relay) + target_compile_definitions(srt-test-relay PUBLIC -DSRT_ENABLE_VERBOSE_LOCK) + + srt_add_testprogram(srt-test-multiplex) + srt_make_application(srt-test-multiplex) + endif() + +endif() + +if (ENABLE_EXAMPLES) + + # No examples should need C++11 + macro(srt_add_example mainsrc) + get_filename_component(name ${mainsrc} NAME_WE) + srt_add_program(${name} examples/${mainsrc} ${ARGN}) + endmacro() + + srt_add_example(sendfile.cpp) + srt_make_application(sendfile) + + srt_add_example(recvfile.cpp) + srt_make_application(recvfile) + + srt_add_example(recvlive.cpp) + srt_make_application(recvlive) + + srt_add_example(test-c-client.c) + srt_make_application(test-c-client) + + srt_add_example(test-c-server.c) + srt_make_application(test-c-server) + + srt_add_example(testcapi-connect.c) + target_link_libraries(testcapi-connect ${srt_link_library} ${DEPENDS_srt}) + +endif() + + +if (ENABLE_UNITTESTS AND ENABLE_CXX11) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + find_package(GTest 1.8) + if (NOT GTEST_FOUND) + message(STATUS "GTEST not found! Fetching from git.") + include(googletest) + fetch_googletest( + ${PROJECT_SOURCE_DIR}/scripts + ${PROJECT_BINARY_DIR}/googletest + ) + set(GTEST_BOTH_LIBRARIES "gtest_main" CACHE STRING "Add gtest_main target") + endif() + + MafReadDir(test filelist.maf + SOURCES SOURCES_unittests + ) + + message(STATUS "Unit test sources: ${SOURCES_unittests}") + + srt_add_program(test-srt ${SOURCES_unittests}) + srt_make_application(test-srt) + target_include_directories(test-srt PRIVATE ${SSL_INCLUDE_DIRS}) + + target_link_libraries( + test-srt + ${GTEST_BOTH_LIBRARIES} + ${srt_link_library} + ${PTHREAD_LIBRARY} + ) + + add_test( + NAME + test-srt + COMMAND + ${CMAKE_BINARY_DIR}/test-srt + ) + + enable_testing() + +endif() + + +install(PROGRAMS scripts/srt-ffplay DESTINATION ${CMAKE_INSTALL_BINDIR}) + + +if (DEFINED SRT_EXTRA_APPS_INC) + include(${SRT_EXTRA_APPS_INC}.cmake) + # No extra variables expected. Just use the variables + # already provided and define additional targets. +endif() + +if ( ENABLE_SUFLIP ) + set (SOURCES_suflip + ${CMAKE_CURRENT_SOURCE_DIR}/apps/suflip.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/common/uriparser.cpp + ) + srt_add_program(suflip ${SOURCES_suflip}) + target_link_libraries(suflip ${srt_link_library}) + install(TARGETS suflip RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif () diff --git a/trunk/3rdparty/srt-1-fit/LICENSE b/trunk/3rdparty/srt-1-fit/LICENSE new file mode 100644 index 000000000..14e2f777f --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + 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/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/trunk/3rdparty/srt-1-fit/cmake_object_lib_support.c b/trunk/3rdparty/srt-1-fit/cmake_object_lib_support.c new file mode 100644 index 000000000..dd1d89038 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/cmake_object_lib_support.c @@ -0,0 +1,9 @@ +// DO NOT DELETE +// This file is needed for Xcode to properly handle CMake OBJECT Libraries +// From docs (https://cmake.org/cmake/help/latest/command/add_library.html#object-libraries): +// +// ... Some native build systems (such as Xcode) may not like targets that have only object files, +// so consider adding at least one real source file to any target that references $. + +// Just a dummy symbol to avoid compiler warnings +int srt_object_lib_dummy = 0; diff --git a/trunk/3rdparty/srt-1-fit/common/filelist_win32.maf b/trunk/3rdparty/srt-1-fit/common/filelist_win32.maf new file mode 100644 index 000000000..2762c6554 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/common/filelist_win32.maf @@ -0,0 +1,10 @@ + +PUBLIC HEADERS +win/syslog_defs.h +# +# These are included by platform_sys.h header contained in ../srtcore/filelist.maf +# +win/unistd.h + +SOURCES +win_time.cpp diff --git a/trunk/3rdparty/srt-1-fit/common/win/ATTIC/winporting.h b/trunk/3rdparty/srt-1-fit/common/win/ATTIC/winporting.h new file mode 100644 index 000000000..098ed2d3b --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/common/win/ATTIC/winporting.h @@ -0,0 +1,63 @@ +#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 +#include + +#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_ diff --git a/trunk/3rdparty/srt-1-fit/common/win/syslog_defs.h b/trunk/3rdparty/srt-1-fit/common/win/syslog_defs.h new file mode 100644 index 000000000..6d761b5e5 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/common/win/syslog_defs.h @@ -0,0 +1,45 @@ +#ifndef INC__WINDOWS_SYSLOG_DEFS_H +#define INC__WINDOWS_SYSLOG_DEFS_H + +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +#define LOG_PRIMASK 0x07 + +#define LOG_PRI(p) ((p) & LOG_PRIMASK) +#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri)) + +#define LOG_KERN (0<<3) +#define LOG_USER (1<<3) +#define LOG_MAIL (2<<3) +#define LOG_DAEMON (3<<3) +#define LOG_AUTH (4<<3) +#define LOG_SYSLOG (5<<3) +#define LOG_LPR (6<<3) +#define LOG_NEWS (7<<3) +#define LOG_UUCP (8<<3) +#define LOG_CRON (9<<3) +#define LOG_AUTHPRIV (10<<3) +#define LOG_FTP (11<<3) + +/* Codes through 15 are reserved for system use */ +#define LOG_LOCAL0 (16<<3) +#define LOG_LOCAL1 (17<<3) +#define LOG_LOCAL2 (18<<3) +#define LOG_LOCAL3 (19<<3) +#define LOG_LOCAL4 (20<<3) +#define LOG_LOCAL5 (21<<3) +#define LOG_LOCAL6 (22<<3) +#define LOG_LOCAL7 (23<<3) + +#define LOG_NFACILITIES 24 +#define LOG_FACMASK 0x03f8 +#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3) + +#endif diff --git a/trunk/3rdparty/srt-1-fit/common/win/unistd.h b/trunk/3rdparty/srt-1-fit/common/win/unistd.h new file mode 100644 index 000000000..9c61cad8a --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/common/win/unistd.h @@ -0,0 +1,52 @@ +#ifndef _UNISTD_H +#define _UNISTD_H 1 + +/* This file intended to serve as a drop-in replacement for + * unistd.h on Windows + * Please add functionality as neeeded + */ + +#include +#include +//#include /* getopt at: https://gist.github.com/ashelly/7776712*/ +#include /* for getpid() and the exec..() family */ +#include /* for _getcwd() and _chdir() */ + +#define srandom srand +#define random rand + +/* Values for the second argument to access. + These may be OR'd together. */ +#define R_OK 4 /* Test for read permission. */ +#define W_OK 2 /* Test for write permission. */ +//#define X_OK 1 /* execute permission - unsupported in windows*/ +#define F_OK 0 /* Test for existence. */ + +#define access _access +#define dup2 _dup2 +#define execve _execve +#define ftruncate _chsize +#define unlink _unlink +#define fileno _fileno +#define getcwd _getcwd +#define chdir _chdir +#define isatty _isatty +#define lseek _lseek +/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */ + +#define ssize_t int + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +/* should be in some equivalent to */ +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#endif /* unistd.h */ diff --git a/trunk/3rdparty/srt-1-fit/common/win/wintime.h b/trunk/3rdparty/srt-1-fit/common/win/wintime.h new file mode 100644 index 000000000..781bbe563 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/common/win/wintime.h @@ -0,0 +1,56 @@ +#ifndef INC__WIN_WINTIME +#define INC__WIN_WINTIME + +#include +#include +// HACK: This include is a workaround for a bug in the MinGW headers +// 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 +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_MSC_VER) + #define SRTCOMPAT_WINTIME_STATIC_INLINE_DECL static inline +#else + // NOTE: MVC Does not like static inline for C functions in some versions. + // so just use static for MVC. + #define SRTCOMPAT_WINTIME_STATIC_INLINE_DECL static +#endif + +#ifndef _TIMEZONE_DEFINED /* also in sys/time.h */ +#define _TIMEZONE_DEFINED +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; +#endif + +void SRTCompat_timeradd( + struct timeval *a, struct timeval *b, struct timeval *result); +SRTCOMPAT_WINTIME_STATIC_INLINE_DECL void timeradd( + struct timeval *a, struct timeval *b, struct timeval *result) +{ + SRTCompat_timeradd(a, b, result); +} + +int SRTCompat_gettimeofday( + struct timeval* tp, struct timezone* tz); +SRTCOMPAT_WINTIME_STATIC_INLINE_DECL int gettimeofday( + struct timeval* tp, struct timezone* tz) +{ + return SRTCompat_gettimeofday(tp, tz); +} + +#undef SRTCOMPAT_WINTIME_STATIC_INLINE_DECL + +#ifdef __cplusplus +} +#endif + +#endif // INC__WIN_WINTIME diff --git a/trunk/3rdparty/srt-1-fit/common/win_time.cpp b/trunk/3rdparty/srt-1-fit/common/win_time.cpp new file mode 100644 index 000000000..83ea7b098 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/common/win_time.cpp @@ -0,0 +1,65 @@ +/* + * 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. + *****************************************************************************/ + +#include "win/wintime.h" +#include + +void SRTCompat_timeradd(struct timeval *a, struct timeval *b, struct timeval *result) +{ + result->tv_sec = a->tv_sec + b->tv_sec; + result->tv_usec = a->tv_usec + b->tv_usec; + if (result->tv_usec >= 1000000) + { + result->tv_sec++; + result->tv_usec -= 1000000; + } +} + +int SRTCompat_gettimeofday(struct timeval* tp, struct timezone* tz) +{ + 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); + } + return 0; +} diff --git a/trunk/3rdparty/srt-1-fit/configure b/trunk/3rdparty/srt-1-fit/configure new file mode 100755 index 000000000..946df108d --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/configure @@ -0,0 +1,249 @@ +#!/usr/bin/tclsh + +# +# 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/. +# + +# This is a general-purpose configure script, which is a user-friendly +# wrapper to call the "cmake" command. + +# There are two options that are handled specifically: +# +# --help: show the list of official options +# --prefix: alias to --cmake-install-prefix + +# The processing done automatically on all options by default is: +# Every option like: +# --long-c++-option +# --cmake-special-option=ON +# Turns into: +# -DLONG_CXX_OPTION=1 +# -DCMAKE_SPECIAL_OPTION=ON +# +# In the configuration file, "configure-data.tcl", you can add +# special processing for options and define explicit options +# in the "::options" dictionary. Explicit options (in contrast +# to "blind" options) have additional properties: +# +# - only those options are mentioned with --help +# - you can pass a value for this option without = character +# - you can specify --disable-option instead of --enable-option=0 +# +# In "configure-data.tcl", beside ::options, you can define "preprocess" and +# "postprocess" procedures. In "preprocess", use ::optval array to modify the +# list of options to be processed further. Additionally in "postprocess" +# procedure you can influence directly the options for "cmake" command in +# ::cmakeopt variable (modifying ::optval in "postprocess" is useless). + +# The idea is that CMakeLists.txt contains things that are highly +# customizable, but no system or option autodetection AWA "sensible +# defaults" are provided. This is done by this script. + + +set here [file dirname $argv0] + +set options "" +set toolchain_changers "" + +source $here/configure-data.tcl + +# Update alias with default alias +dict set alias --prefix --cmake-install-prefix= + +proc resolve opt { + set type arg + set pos [string first $opt =] + if { $pos == -1 } { + set type bool + set mark "" + } else { + set type arg + set mark [string range $opt $pos+1 end] + set opt [string range $opt 0 $pos-1] + } + set var [string toupper [string map {- _ + x} $opt]] + return [list --$opt $var $type $mark] +} + +# Check if a --disable option has its --enable counterpart. If so, +# then just invert the option. +proc resolve_disablers {} { + set enablers "" + set optkeys_len [llength $::optkeys] + for {set pos 0} {$pos < $optkeys_len} {incr pos} { + set opt [lindex $::optkeys $pos] + if { [string match --disable-* $opt] } { + set inverted enable-[string range $opt 10 end] + if { $inverted in [dict keys $::options] } { + lset ::optkeys $pos --$inverted + set val $::optval($opt) + unset ::optval($opt) + if { $val == "" || ![string is boolean $val] } { + set ::optval(--$inverted) 0 + } else { + set ::optval(--$inverted) [expr {!$val}] + } + + puts "NOTE: $opt changed into --$inverted=$::optval(--$inverted)" + } + } + } +} + +foreach {o desc} $options { + lassign [resolve $o] optname optvar opttype optmark + set opt($optname) [list $optvar $opttype $optmark] + set info($optname) $desc +} + + +if { $argv == "--help" } { + puts stderr "Usage: ./configure \[options\]" + puts stderr "OPTIONS:" + foreach o [lsort [array names opt]] { + lassign $opt($o) unu type mark + set imark "" + if { $mark != "" } { + set imark "=$mark" + } + puts stderr "\t$o$imark - $info($o)" + } + + puts stderr "NOTE1: Option list may be incomplete. Refer to variables in CMakeLists.txt" + puts stderr "NOTE2: Non-internal options turn e.g. --enable-c++11 into cmake -DENABLE_CXX11=1" + puts stderr "NOTE3: You can use --disable-x instead of --enable-x=0 for the above options." + + exit 1 +} + +if { [info proc init] != "" } { + init +} + +#parray opt + +set saveopt "" +set optkeys "" + +set dryrun 0 +set type "" + +foreach a $argv { + if { [info exists val] } { unset val } + + if { $saveopt != "" } { + set optval($saveopt) $a + set saveopt "" + continue + } + + if { [string range $a 0 1] != "--" } { + error "Unexpected argument '$a'. Options must start with --" + } + + if { $a == "--dryrun" } { + set dryrun 1 + continue + } + + set type "" + + if { [string first = $a] != -1 } { + lassign [split $a =] a val + } + + if { [dict exists $::alias $a] } { + set aname [dict get $::alias $a] + if { [string first = $aname] != -1 } { + lassign [split $aname =] a aval + set type arg + } + } + + if { ![info exists opt($a)] } { + #puts stderr "WARNING: Unknown option: $a" + # But still, simply turn the option to assign-based use. + lassign [resolve [string range $a 2 end]] oname var + if { ![info exists val] && $type == "" } { + set type bool + } + } else { + lassign $opt($a) var type + } + + if { $type == "bool" } { + if { ![info exists val] } { + set val 1 + } + set optval($a) $val + } elseif { [info exists val] } { + set optval($a) $val + } else { + set saveopt $a + } + + lappend optkeys $a +} + +if { $saveopt != "" } { + error "Extra unhandled argument: $saveopt" +} + +set cmakeopt "" + +resolve_disablers + +if { [info proc preprocess] != "" } { + preprocess +} + +# Check if there were new values added not added to optkeys +foreach a [array names optval] { + if { $a ni $optkeys } { + lappend optkeys $a + } +} + + +foreach a $optkeys { + + if { ![info exists optval($a)] } { + continue ;# user action might have removed it. + } + + if { ![info exists opt($a)] } { + #puts stderr "WARNING: Unknown option: $a" + # But still, simply turn the option to assign-based use. + lassign [resolve [string range $a 2 end]] oname var + if { ![info exists val] && $type == "" } { + set type bool + } + } else { + lassign $opt($a) var type + } + + set val $optval($a) + lappend cmakeopt "-D$var=$val" +} + + +if { [info proc postprocess] != "" } { + postprocess +} + +#puts "VARSPEC: $cmakeopt" + +set cmd [list cmake $here {*}$cmakeopt] +puts "Running: $cmd" +if { !$dryrun} { + if { [catch {exec 2>@stderr >@stdout {*}$cmd} result] } { + puts "CONFIGURE: cmake reported error: $result" + } +} else { + puts "(not really - dry run)" +} diff --git a/trunk/3rdparty/srt-1-fit/configure-data.tcl b/trunk/3rdparty/srt-1-fit/configure-data.tcl new file mode 100644 index 000000000..446b9514b --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/configure-data.tcl @@ -0,0 +1,361 @@ +# +# 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/. +# + +# API description: + +# Expected variables: +# - options: dictionary "option-name" : "description" +# if there's '=' in option name, it expects an argument. Otherwise it's boolean. +# - alias: optional, you can make shortcuts to longer named options. Remember to use = in target name. +# +# Optional procedures: +# - preprocess: run before command-line arguments ($argv) are reviewed +# - postprocess: run after options are reviewed and all data filled in +# +# Available variables in postprocess: +# +# - optval (array): contains all option names with their assigned values +# - cmakeopt (scalar): a list of all options for "cmake" command line + +# Options processed here internally, not passed to cmake +set internal_options { + with-compiler-prefix= "set C/C++ toolchains gcc and g++" + with-compiler-type= "compiler type: gcc(default), cc, others simply add ++ for C++" + with-srt-name= "Override srt library name" + with-haicrypt-name= "Override haicrypt library name (if compiled separately)" +} + +# 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-apps "Should the Support Applications be Built? (default: ON)" + 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-logging "Should logging be enabled (default: ON)" + enable-debug=<0,1,2> "Enable debug mode (0=disabled, 1=debug, 2=rel-with-debug)" + enable-haicrypt-logging "Should logging in haicrypt be enabled (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 that implements THREAD_* macros" + openssl-crypto-library= "Path to a library." + openssl-include-dir= "Path to a file." + openssl-ssl-library= "Path to a library." + pkg-config-executable= "pkg-config executable" + pthread-include-dir= "Path to a file." + pthread-library= "Path to a library." + 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" + 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)" +} + +set options $internal_options$cmake_options + +# Just example. Available in the system. +set alias { + --prefix --cmake-install-prefix= +} + +proc pkg-config args { + return [string trim [exec pkg-config {*}$args]] +} + +proc flagval v { + set out "" + foreach o $v { + lappend out [string trim [string range $o 2 en]] + } + return $out +} + +set haicrypt_name "" +set srt_name "" + +proc preprocess {} { + + # Prepare windows basic path info + set ::CYGWIN 0 + set e [catch {exec uname -o} res] + # We have Cygwin, if uname -o returns "cygwin" and does not fail. + if { !$e && $res == "Cygwin" } { + set ::CYGWIN 1 + puts "CYGWIN DETECTED" + } + + set ::HAVE_LINUX [expr {$::tcl_platform(os) == "Linux"}] + set ::HAVE_DARWIN [expr {$::tcl_platform(os) == "Darwin"}] + + set ::CYGWIN_USE_POSIX 0 + if { "--cygwin-use-posix" in $::optkeys } { + set ::CYGWIN_USE_POSIX 1 + } + + set ::HAVE_WINDOWS 0 + if { $::tcl_platform(platform) == "windows" } { + puts "WINDOWS PLATFORM detected" + set ::HAVE_WINDOWS 1 + } + + if { $::CYGWIN && !$::CYGWIN_USE_POSIX } { + puts "CYGWIN - MINGW enforced" + # Make Cygwin tools see it right, to compile for MinGW + + if { "--with-compiler-prefix" ni $::optkeys } { + set ::optval(--with-compiler-prefix) /bin/x86_64-w64-mingw32- + } + + # Extract drive C: information + set drive_path [exec mount -p | tail -1 | cut {-d } -f 1] + set ::DRIVE_C $drive_path/c + set ::HAVE_WINDOWS 1 + } else { + + # Don't check for Windows, non-Windows parts will not use it. + set ::DRIVE_C C: + } + + # Alias to old name --with-gnutls, which enforces using gnutls instead of openssl + if { [info exists ::optval(--with-gnutls)] } { + unset ::optval(--with-gnutls) + set ::optval(--use-enclib) gnutls + puts "WARNING: --with-gnutls is a deprecated alias to --use-enclib=gnutls, please use the latter one" + } + + # Alias to old name --use-gnutls, which enforces using gnutls instead of openssl + if { [info exists ::optval(--use-gnutls)] } { + unset ::optval(--use-gnutls) + set ::optval(--use-enclib) gnutls + puts "WARNING: --use-gnutls is a deprecated alias to --use-enclib=gnutls, please use the latter one" + } + + if { [info exists ::optval(--with-target-path)] } { + set ::target_path $::optval(--with-target-path) + unset ::optval(--with-target-path) + puts "NOTE: Explicit target path: $::target_path" + } + + if { "--with-srt-name" in $::optkeys } { + set ::srt_name $::optval(--with-srt-name) + unset ::optval(--with-srt-name) + } + + if { "--with-haicrypt-name" in $::optkeys } { + set ::haicrypt_name $::optval(--with-haicrypt-name) + unset ::optval(--with-haicrypt-name) + } +} + +proc GetCompilerCommand {} { + # Expect that the compiler was set through: + # --with-compiler-prefix + # --cmake-c[++]-compiler + # (cmake-toolchain-file will set things up without the need to check things here) + + if { [info exists ::optval(--with-compiler-prefix)] } { + set prefix $::optval(--with-compiler-prefix) + return ${prefix}gcc + } + + if { [info exists ::optval(--cmake-c-compiler)] } { + return $::optval(--cmake-c-compiler) + } + + if { [info exists ::optval(--cmake-c++-compiler)] } { + return $::optval(--cmake-c++-compiler) + } + + if { [info exists ::optval(--cmake-cxx-compiler)] } { + return $::optval(--cmake-cxx-compiler) + } + + puts "NOTE: Cannot obtain compiler, assuming toolchain file will do what's necessary" + + return "" +} + +proc postprocess {} { + + set iscross 0 + + # Check if there was any option that changed the toolchain. If so, don't apply any autodetection-based toolchain change. + set all_options [array names ::optval] + set toolchain_changed no + foreach changer { + --with-compiler-prefix + --cmake-c-compiler + --cmake-c++-compiler + --cmake-cxx-compiler + --cmake-toolchain-file + } { + if { $changer in $all_options } { + puts "NOTE: toolchain changed by '$changer' option" + set toolchain_changed yes + break + } + } + + set cygwin_posix 0 + if { "--cygwin-use-posix" in $all_options } { + # Will enforce OpenSSL autodetection + set cygwin_posix 1 + } + + if { $toolchain_changed } { + # Check characteristics of the compiler - in particular, whether the target is different + # than the current target. + set compiler_path "" + set cmd [GetCompilerCommand] + if { $cmd != "" } { + set gcc_version [exec $cmd -v 2>@1] + set target "" + set compiler_path [file dirname $cmd] + foreach l [split $gcc_version \n] { + if { [string match Target:* $l] } { + set target [lindex $l 1] ;# [0]Target: [1]x86_64-some-things-further + set target_platform [lindex [split $target -] 0] ;# [0]x86_64 [1]redhat [2]linux + break + } + } + + if { $target_platform == "" } { + puts "NOTE: can't obtain target from gcc -v: $l" + } else { + if { $target_platform != $::tcl_platform(machine) } { + puts "NOTE: foreign target type detected ($target)" ;# - setting CROSSCOMPILING flag" + #lappend ::cmakeopt "-DHAVE_CROSSCOMPILER=1" + set iscross 1 + } + } + } + } + + if { $::srt_name != "" } { + lappend ::cmakeopt "-DTARGET_srt=$::srt_name" + } + + if { $::haicrypt_name != "" } { + lappend ::cmakeopt "-DTARGET_haicrypt=$::haicrypt_name" + } + + set have_openssl 0 + if { [lsearch -glob $::optkeys --openssl*] != -1 } { + set have_openssl 1 + } + + set have_gnutls 0 + if { [lsearch -glob $::optkeys --use-gnutls] != -1 } { + set have_gnutls 1 + } + + if { $have_openssl && $have_gnutls } { + puts "NOTE: SSL library is exclusively selectable. Thus, --use-gnutls option will be ignored" + set have_gnutls 0 + } + + if { $have_gnutls } { + lappend ::cmakeopt "-DUSE_GNUTLS=ON" + } + + if {$iscross} { + + proc check-target-path {path} { + puts "Checking path '$path'" + if { [file isdir $path] + && [file isdir $path/bin] + && [file isdir $path/include] + && ([file isdir $path/lib] || [file isdir $path/lib64]) } { + return yes + } + return no + } + + if { ![info exists ::target_path] } { + # Try to autodetect the target path by having the basic 3 directories. + set target_path "" + set compiler_prefix [file dirname $compiler_path] ;# strip 'bin' directory + puts "NOTE: no --with-target-path found, will try to autodetect at $compiler_path" + foreach path [list $compiler_path $compiler_prefix/$target] { + if { [check-target-path $path] } { + set target_path $path + puts "NOTE: target path detected: $target_path" + break + } + } + + if { $target_path == "" } { + puts "ERROR: Can't determine compiler's platform files root path (using compiler command path). Specify --with-target-path." + exit 1 + } + } else { + set target_path $::target_path + # Still, check if correct. + if { ![check-target-path $target_path] } { + puts "ERROR: path in --with-target-path does not contain typical subdirectories" + exit 1 + } + puts "NOTE: Using explicit target path: $target_path" + } + + # Add this for cmake, should it need for something + lappend ::cmakeopt "-DCMAKE_PREFIX_PATH=$target_path" + + # Add explicitly the path for pkg-config + # which lib + if { [file isdir $target_path/lib64/pkgconfig] } { + set ::env(PKG_CONFIG_PATH) $target_path/lib64/pkgconfig + puts "PKG_CONFIG_PATH: Found pkgconfig in lib64 for '$target_path' - using it" + } elseif { [file isdir $target_path/lib/pkgconfig] } { + set ::env(PKG_CONFIG_PATH) $target_path/lib/pkgconfig + puts "PKG_CONFIG_PATH: Found pkgconfig in lib for '$target_path' - using it" + } else { + puts "PKG_CONFIG_PATH: NOT changed, no pkgconfig in '$target_path'" + } + # Otherwise don't set PKG_CONFIG_PATH and we'll see. + } + + if { $::HAVE_DARWIN && !$toolchain_changed} { + + if { $have_gnutls } { + # Use gnutls explicitly, as found in brew + set er [catch {exec brew info gnutls} res] + if { $er } { + error "Cannot find gnutls in brew" + } + } else { + # ON Darwin there's a problem with linking against the Mac-provided OpenSSL. + # This must use brew-provided OpenSSL. + # + if { !$have_openssl } { + + set er [catch {exec brew info openssl} res] + if { $er } { + error "You must have OpenSSL installed from 'brew' tool. The standard Mac version is inappropriate." + } + + lappend ::cmakeopt "-DOPENSSL_INCLUDE_DIR=/usr/local/opt/openssl/include" + lappend ::cmakeopt "-DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib/libcrypto.a" + } + } + } + +} + diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-config.h b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-config.h new file mode 100644 index 000000000..747653d12 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-config.h @@ -0,0 +1,22 @@ +#ifndef INC__CRYSPR_CONFIG_H +#define INC__CRYSPR_CONFIG_H + +// Size of the single block for encryption. +// This might need tweaking for particular implementation library. +#define CRYSPR_AESBLKSZ 16 /* 128-bit */ + +#if defined(USE_OPENSSL) +#include "cryspr-openssl.h" +#define cryspr4SRT() crysprOpenSSL() +#elif defined(USE_GNUTLS) +#include "cryspr-gnutls.h" +#define cryspr4SRT() crysprGnuTLS() +#elif defined(USE_MBEDTLS) +#include "cryspr-mbedtls.h" +#define cryspr4SRT() crysprMbedtls() +#else +#error Cryspr implementation not selected. Please define USE_* + OPENSSL/GNUTLS/MBEDTLS. +#endif + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-gnutls.c b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-gnutls.c new file mode 100644 index 000000000..217a70e8a --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-gnutls.c @@ -0,0 +1,178 @@ +/* + * 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. + + 2019-06-27 (jdube) + GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT) +*****************************************************************************/ + +#include "hcrypt.h" + +#include + +typedef struct tag_crysprGnuTLS_AES_cb { + CRYSPR_cb ccb; /* CRYSPR control block */ + /* Add other cryptolib specific data here */ +} crysprGnuTLS_cb; + + +int crysprGnuTLS_Prng(unsigned char *rn, int len) +{ + return(gnutls_rnd(GNUTLS_RND_KEY,(rn),(len)) < 0 ? -1 : 0); +} + +int crysprGnuTLS_AES_SetKey( + 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 */ +{ + 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"); + return -1; + } + aes_set_encrypt_key (aes_key, kstr_len, kstr); + } else { /* Decrypt key */ + if (!(kstr_len == 16 || kstr_len == 24 || kstr_len == 32)) { + HCRYPT_LOG(LOG_ERR, "%s", "AES_set_decrypt_key(kek) bad length\n"); + return -1; + } + aes_set_decrypt_key (aes_key, kstr_len, kstr); + } + return(0); +} + +int crysprGnuTLS_AES_EcbCipher( /* AES Electronic Codebook cipher*/ + bool bEncrypt, /* true:encrypt, false:decrypt */ + CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */ + const unsigned char *indata,/* src (clear text)*/ + size_t inlen, /* length */ + unsigned char *out_txt, /* dst (cipher text) */ + size_t *outlen) /* dst len */ +{ + int nblk = inlen/CRYSPR_AESBLKSZ; + int nmore = inlen%CRYSPR_AESBLKSZ; + int i; + + if (bEncrypt) { + /* Encrypt packet payload, block by block, in output buffer */ + for (i=0; i +#include //gnutls_rnd() + +#include //has AES cipher +#include //has CTR cipher mode +#include //has Password-based Key Derivation Function 2 +//#include //No need for sha1 since we have pbkdf2 + + +/* 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 . +*/ +#define CRYSPR_HAS_AESKWRAP 0 + +/* 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 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_XXX. +*/ +typedef struct aes_ctx CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */ + +struct tag_CRYSPR_methods *crysprGnuTLS(void); + +#endif /* CRYSPR_GNUTLS_H */ + diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.c b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.c new file mode 100644 index 000000000..c10727a0a --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.c @@ -0,0 +1,235 @@ +/* + * 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. + + 2019-06-27 (jdube) + GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT) +*****************************************************************************/ + +#include "hcrypt.h" + +#include + +#include +#include +#include +#include + +// 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 */ +} crysprMbedtls_cb; + + +int crysprMbedtls_Prng(unsigned char *rn, int len) +{ + int ret = mbedtls_ctr_drbg_random( &crysprMbedtls_ctr_drbg, rn, len ); + if (ret != 0) + { + return -1; + } + + return 0; +} + +int crysprMbedtls_AES_SetKey( + 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 */ +{ + 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; + + // mbedtls uses the "bits" convention (128, 192, 254), just like openssl. + // kstr_len is in "bytes" convention (16, 24, 32). + + if (bEncrypt) { /* Encrypt key */ + ret = mbedtls_aes_setkey_enc(aes_key, kstr, kstr_len*8); + } else { /* Decrypt key */ + ret = mbedtls_aes_setkey_dec(aes_key, kstr, kstr_len*8); + } + + return ret == 0 ? 0 : -1; +} + +int crysprMbedtls_AES_EcbCipher( /* AES Electronic Codebook cipher*/ + bool bEncrypt, /* true:encrypt, false:decrypt */ + CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */ + const unsigned char *indata,/* src (clear text)*/ + size_t inlen, /* length */ + unsigned char *out_txt, /* dst (cipher text) */ + size_t *outlen) /* dst len */ +{ + int nblk = inlen/CRYSPR_AESBLKSZ; + int nmore = inlen%CRYSPR_AESBLKSZ; + int i; + + if (bEncrypt) { + /* Encrypt packet payload, block by block, in output buffer */ + for (i = 0; i < nblk; i++) { + // NOTE: CRYSPR_AESBLKSZ is implicitly the ONLY POSSIBLE + // size of the block. + mbedtls_aes_crypt_ecb(aes_key, MBEDTLS_AES_ENCRYPT, + &indata[(i*CRYSPR_AESBLKSZ)], + &out_txt[(i*CRYSPR_AESBLKSZ)]); + } + /* Encrypt last incomplete block */ + if (0 < nmore) { + unsigned char intxt[CRYSPR_AESBLKSZ]; + + memcpy(intxt, &indata[(nblk*CRYSPR_AESBLKSZ)], nmore); + memset(intxt+nmore, 0, CRYSPR_AESBLKSZ-nmore); + mbedtls_aes_crypt_ecb(aes_key, MBEDTLS_AES_ENCRYPT, + intxt, + &out_txt[(nblk*CRYSPR_AESBLKSZ)]); + nblk++; + } + if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ; + } else { /* Decrypt */ + for (i=0; i +#include + + +/* 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 . +*/ +#define CRYSPR_HAS_AESKWRAP 0 + +/* 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 + +// mbedtls uses in the enc/dec functions 16-byte blocks +// for xcryption. This is not marked by any constant. See +// e.g. , mbedtls_aes_crypt_ecb signature. +#if CRYSPR_AESBLKSZ != 16 +#error mbedtls requires AES single block size 16 bytes, implicitly. +#endif + +/* +#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_XXX. +*/ +typedef struct mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */ + +struct tag_CRYSPR_methods *crysprMbedtls(void); + +#endif /* CRYSPR_GNUTLS_H */ + diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.c b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.c new file mode 100644 index 000000000..a5d50ac65 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.c @@ -0,0 +1,218 @@ +/* + * 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. + + 2019-06-26 (jdube) + OpenSSL CRYSPR/4SRT (CRYypto Service PRovider for SRT). +*****************************************************************************/ + +#include "hcrypt.h" + +#include + + +typedef struct tag_crysprOpenSSL_AES_cb { + CRYSPR_cb ccb; + /* Add cryptolib specific data here */ +} crysprOpenSSL_cb; + + +int crysprOpenSSL_Prng(unsigned char *rn, int len) +{ + return(RAND_bytes(rn, len) <= 0 ? -1 : 0); +} + +int crysprOpenSSL_AES_SetKey( + 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 */ +{ + 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"); + return(-1); + } + } else { /* Decrypt key */ + if (AES_set_decrypt_key(kstr, kstr_len * 8, aes_key)) { + HCRYPT_LOG(LOG_ERR, "%s", "AES_set_decrypt_key(kek) failed\n"); + return(-1); + } + } + return(0); +} + +#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP) + +int crysprOpenSSL_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) /* in/out dst len */ +{ + int nblk = inlen/CRYSPR_AESBLKSZ; + int nmore = inlen%CRYSPR_AESBLKSZ; + size_t outsiz = (outlen ? *outlen : 0); + int i; + + if (outsiz % CRYSPR_AESBLKSZ) return(-1); /* output buf size must be a multiple of AES block size (16) */ + if (bEncrypt) { + if (outsiz > 16 && outsiz < (nblk+nmore)*CRYSPR_AESBLKSZ) return(-1); /* output buf size must have room for PKCS7 padding */ + /* Encrypt packet payload, block by block, in output buffer */ + for (i=0; i= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)) + CRYPTO_ctr128_encrypt(indata, out_txt, + inlen, aes_key, iv, ctr, &blk_ofs, (block128_f) AES_encrypt); +#else + AES_ctr128_encrypt(indata, out_txt, + inlen, aes_key, iv, ctr, &blk_ofs); +#endif + return 0; +} + +/* +* Password-based Key Derivation Function +*/ +int crysprOpenSSL_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_KmWrap(CRYSPR_cb *cryspr_cb, + unsigned char *wrap, + 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 + + return(((seklen + HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_wrap_key(kek, NULL, wrap, sek, seklen)) ? 0 : -1); +} + +int crysprOpenSSL_KmUnwrap( + CRYSPR_cb *cryspr_cb, + unsigned char *sek, //Stream encrypting key + 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 + + 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_methods; + +CRYSPR_methods *crysprOpenSSL(void) +{ + if(NULL == crysprOpenSSL_methods.open) { + crysprInit(&crysprOpenSSL_methods); //Default/fallback methods + + crysprOpenSSL_methods.prng = crysprOpenSSL_Prng; + //--CryptoLib Primitive API----------------------------------------------- + crysprOpenSSL_methods.aes_set_key = crysprOpenSSL_AES_SetKey; + #if CRYSPR_HAS_AESCTR + crysprOpenSSL_methods.aes_ctr_cipher = crysprOpenSSL_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_methods.aes_ecb_cipher = crysprOpenSSL_AES_EcbCipher; + #endif + #if !CRYSPR_HAS_PBKDF2 + crysprOpenSSL_methods.sha1_msg_digest= NULL; //Required to use eventual default/fallback KmPbkdf2 + #endif + + //--Crypto Session API----------------------------------------- + // crysprOpenSSL_methods.open = + // crysprOpenSSL_methods.close = + //--Keying material (km) encryption + +#if CRYSPR_HAS_PBKDF2 + crysprOpenSSL_methods.km_pbkdf2 = crysprOpenSSL_KmPbkdf2; +#else +#error There is no default/fallback method for PBKDF2 +#endif + // crysprOpenSSL_methods.km_setkey = +#if CRYSPR_HAS_AESKWRAP + crysprOpenSSL_methods.km_wrap = crysprOpenSSL_KmWrap; + crysprOpenSSL_methods.km_unwrap = crysprOpenSSL_KmUnwrap; +#endif + + //--Media stream (ms) encryption + // crysprOpenSSL_methods.ms_setkey = + // crysprOpenSSL_methods.ms_encrypt = + // crysprOpenSSL_methods.ms_decrypt = + } + return(&crysprOpenSSL_methods); +} + + diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.h b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.h new file mode 100644 index 000000000..75a52518f --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.h @@ -0,0 +1,65 @@ +/* + * 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. + + 2019-06-26 (jdube) + OpenSSL Direct AES CRYSPR/4SRT (CRYypto Service PRovider for SRT). +*****************************************************************************/ + +#ifndef CRYSPR_OPENSSL_H +#define CRYSPR_OPENSSL_H + +#include /* PKCS5_xxx() */ +#include /* AES_xxx() */ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)) +# include /* CRYPTO_xxx() */ +#endif +#include +#include +#include /* 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 (OPENSSL_VERSION_NUMBER < 0x0090808fL) //0.9.8h +#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 AES_KEY CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */ + +struct tag_CRYSPR_methods *crysprOpenSSL(void); + +#endif /* CRYSPR_OPENSSL_H */ + diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c new file mode 100644 index 000000000..e59cbfc76 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c @@ -0,0 +1,709 @@ +/* + * 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. + + 2019-06-28 (jdube) + CRYSPR/4SRT Initial implementation. +*****************************************************************************/ + +#include "hcrypt.h" +#include "cryspr.h" + +#include +#include + +int crysprStub_Prng(unsigned char *rn, int len) +{ + (void)rn; + (void)len; + return(0); +} + +int crysprStub_AES_SetKey( + 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)bEncrypt; + (void)kstr; + (void)kstr_len; + (void)aes_key; + + return(0); +} + +int crysprStub_AES_EcbCipher( + bool bEncrypt, /* true:encrypt, false:decrypt */ + CRYSPR_AESCTX *aes_key, /* AES context */ + const unsigned char *indata,/* src (clear text)*/ + size_t inlen, /* length */ + unsigned char *out_txt, /* dst (cipher text) */ + size_t *outlen) /* dst len */ +{ + (void)bEncrypt; + (void)aes_key; + (void)indata; + (void)inlen; + (void)out_txt; + (void)outlen; + + return -1; +} + +int crysprStub_AES_CtrCipher( + bool bEncrypt, /* true:encrypt, false:decrypt */ + CRYSPR_AESCTX *aes_key, /* AES context */ + unsigned char *iv, /* iv */ + const unsigned char *indata,/* src */ + size_t inlen, /* length */ + unsigned char *out_txt) /* dest */ +{ + (void)bEncrypt; + (void)aes_key; + (void)iv; + (void)indata; + (void)inlen; + (void)out_txt; + + return(-1); +} + +unsigned char *crysprStub_SHA1_MsgDigest( + const unsigned char *m, /* in: message */ + size_t m_len, /* message length */ + unsigned char *md) /* out: message digest buffer *160 bytes */ +{ + (void)m; + (void)m_len; + (void)md; + + return(NULL);//return md; +} + +/* +* Password-based Key Derivation Function +*/ +int crysprStub_KmPbkdf2( + CRYSPR_cb *cryspr_cb, + char *passwd, /* passphrase */ + size_t passwd_len, /* passphrase len */ + unsigned char *salt, /* salt */ + size_t salt_len, /* salt_len */ + int itr, /* iterations */ + size_t key_len, /* key_len */ + unsigned char *out) /* derived key */ +{ + (void)cryspr_cb; + (void)passwd; + (void)passwd_len; + (void)salt; + (void)salt_len; + (void)itr; + (void)key_len; + (void)out; + + /* >>Todo: + * develop PBKDF2 using SHA1 primitive cryspr_cb->cryspr->sha1_msg_digest() for cryptolibs not providing it + */ + return(-1); +} + +static int crysprFallback_KmSetKey(CRYSPR_cb *cryspr_cb, bool bWrap, const unsigned char *kek, size_t kek_len) +{ + CRYSPR_AESCTX *aes_kek = &cryspr_cb->aes_kek; + + 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"); + return(-1); + } + return(0); +} + +/* +* AES_wrap_key()/AES_unwrap_key() introduced in openssl 0.9.8h +* Here is an implementation using AES native API for cryspr not providing it. +*/ + +static const unsigned char default_iv[] = { + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, +}; +int crysprFallback_AES_WrapKey(CRYSPR_cb *cryspr_cb, + unsigned char *out, + const unsigned char *in, + unsigned int inlen) +{ + unsigned char *A, B[16], *R; + const unsigned char *iv = default_iv; + unsigned int i, j, t; + if ((inlen & 0x7) || (inlen < 8)) + return -1; + A = B; + t = 1; + memcpy(out + 8, in, inlen); + + memcpy(A, iv, 8); + + for (j = 0; j < 6; j++) + { + R = out + 8; + for (i = 0; i < inlen; i += 8, t++, R += 8) + { + memcpy(B + 8, R, 8); + { + size_t outlen = 16; + cryspr_cb->cryspr->aes_ecb_cipher(true, &cryspr_cb->aes_kek, B, 16, B, &outlen); + } + A[7] ^= (unsigned char)(t & 0xff); + if (t > 0xff) + { + A[6] ^= (unsigned char)((t >> 8) & 0xff); + A[5] ^= (unsigned char)((t >> 16) & 0xff); + A[4] ^= (unsigned char)((t >> 24) & 0xff); + } + memcpy(R, B + 8, 8); + } + } + memcpy(out, A, 8); + return 0; +} + +int crysprFallback_AES_UnwrapKey(CRYSPR_cb *cryspr_cb, + unsigned char *out, + const unsigned char *in, + unsigned int inlen) +{ + unsigned char *A, B[16], *R; + const unsigned char *iv = default_iv; + unsigned int i, j, t; + inlen -= 8; + if (inlen & 0x7) + return -1; + if (inlen < 8) + return -1; + A = B; + t = 6 * (inlen >> 3); + memcpy(A, in, 8); + memcpy(out, in + 8, inlen); + for (j = 0; j < 6; j++) + { + R = out + inlen - 8; + for (i = 0; i < inlen; i += 8, t--, R -= 8) + { + A[7] ^= (unsigned char)(t & 0xff); + if (t > 0xff) + { + A[6] ^= (unsigned char)((t >> 8) & 0xff); + A[5] ^= (unsigned char)((t >> 16) & 0xff); + A[4] ^= (unsigned char)((t >> 24) & 0xff); + } + memcpy(B + 8, R, 8); + { + size_t outlen = 16; + cryspr_cb->cryspr->aes_ecb_cipher(false, &cryspr_cb->aes_kek, B, 16, B, &outlen); + } + memcpy(R, B + 8, 8); + } + } + if (memcmp(A, iv, 8)) + { + memset(out, 0, inlen); + return -1; + } + return 0; +} + +static unsigned char *_crysprFallback_GetOutbuf(CRYSPR_cb *cryspr_cb, size_t pfx_len, size_t out_len) +{ + unsigned char *out_buf; + + if ((pfx_len + out_len) > (cryspr_cb->outbuf_siz - cryspr_cb->outbuf_ofs)) { + /* Not enough room left, circle buffers */ + cryspr_cb->outbuf_ofs = 0; + } + out_buf = &cryspr_cb->outbuf[cryspr_cb->outbuf_ofs]; + cryspr_cb->outbuf_ofs += (pfx_len + out_len); + return(out_buf); +} + +static CRYSPR_cb *crysprFallback_Open(CRYSPR_methods *cryspr, 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 !CRYSPR_HAS_AESCTR + memsiz += HCRYPT_CTR_STREAM_SZ; +#endif /* !CRYSPR_HAS_AESCTR */ + + cryspr_cb = malloc(memsiz); + if (NULL == cryspr_cb) { + HCRYPT_LOG(LOG_ERR, "malloc(%zd) failed\n", memsiz); + return(NULL); + } + membuf = (unsigned char *)cryspr_cb; + membuf += sizeof(*cryspr_cb); + +#if !CRYSPR_HAS_AESCTR + cryspr_cb->ctr_stream = membuf; + membuf += HCRYPT_CTR_STREAM_SZ; + cryspr_cb->ctr_stream_siz = HCRYPT_CTR_STREAM_SZ; + cryspr_cb->ctr_stream_len = 0; +#endif /* !CRYSPR_HAS_AESCTR */ + + cryspr_cb->outbuf = membuf; + cryspr_cb->outbuf_siz = CRYSPR_OUTMSGMAX * padded_len; + cryspr_cb->outbuf_ofs = 0; +// membuf += cryspr_cb->outbuf_siz; + + cryspr_cb->cryspr=(CRYSPR_methods *)cryspr; + + return(cryspr_cb); +} + +static int crysprFallback_Close(CRYSPR_cb *cryspr_cb) +{ + if (NULL != cryspr_cb) { + free(cryspr_cb); + } + return(0); +} + +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 */ + + 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)) { + 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)) { + HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_decrypt_key(sek) failed\n"); + return(-1); + } + } + return(0); +} + +#if !CRYSPR_HAS_AESCTR +static int _crysprFallback_AES_SetCtrStream(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, size_t len, unsigned char *iv) +{ + /* Counter stream: + * 0 1 2 3 4 5 nblk + * +---+---+---+---+---+---+---+---+ + * |blk|blk|blk|blk|blk|blk|...|blk| + * +---+---+---+---+---+---+---+---+ + */ + + /* 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) + */ + unsigned char ctr[HCRYPT_CTR_BLK_SZ]; + unsigned nblk; + + ASSERT(NULL != cryspr_cb); + ASSERT(NULL != ctx); + + memcpy(ctr, iv, HCRYPT_CTR_BLK_SZ); + + nblk = (len + (HCRYPT_CTR_BLK_SZ-1))/HCRYPT_CTR_BLK_SZ; + if ((nblk * HCRYPT_CTR_BLK_SZ) <= cryspr_cb->ctr_stream_siz) { + unsigned blk; + unsigned char *csp = &cryspr_cb->ctr_stream[0]; + + for(blk = 0; blk < nblk; blk++) { + memcpy(csp, ctr, HCRYPT_CTR_BLK_SZ); + csp += HCRYPT_CTR_BLK_SZ; + if (0 == ++(ctr[HCRYPT_CTR_BLK_SZ-1])) ++(ctr[HCRYPT_CTR_BLK_SZ-2]); + } + cryspr_cb->ctr_stream_len = nblk * HCRYPT_CTR_BLK_SZ; + } else { + HCRYPT_LOG(LOG_ERR, "packet too long(%zd)\n", len); + return(-1); + } + return(0); +} +#endif + +static int crysprFallback_MsEncrypt( + CRYSPR_cb *cryspr_cb, + hcrypt_Ctx *ctx, + hcrypt_DataDesc *in_data, int nbin ATR_UNUSED, + void *out_p[], size_t out_len_p[], int *nbout_p) +{ + unsigned char *out_msg; + size_t out_len = 0; //payload size + int pfx_len; + + ASSERT(NULL != ctx); + ASSERT(NULL != cryspr_cb); + ASSERT((NULL != in_data) || (1 == nbin)); //Only one in_data[] supported + + /* + * Get message prefix length + * to reserve room for unencrypted message header in output buffer + */ + pfx_len = ctx->msg_info->pfx_len; + + /* 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 { + /* input data too big */ + return(-1); + } + + if (out_len > 0) { + /* 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; + } + } else { + /* + * Nothing out + * This is not an error for implementations using deferred/async processing + * with co-processor, DSP, crypto hardware, etc. + * Submitted input data could be returned encrypted in a next call. + */ + if (nbout_p != NULL) *nbout_p = 0; + return(-1); + } + return(0); +} + +static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, + hcrypt_DataDesc *in_data, int nbin ATR_UNUSED, void *out_p[], size_t out_len_p[], int *nbout_p) +{ + unsigned char *out_txt; + size_t out_len; + int iret = 0; + + ASSERT(NULL != cryspr_cb); + ASSERT(NULL != ctx); + ASSERT((NULL != in_data) || (1 == nbin)); //Only one in_data[] supported + + /* Reserve output buffer (w/no header) */ + out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, in_data[0].len); + + if (NULL != out_txt) { + switch(ctx->mode) { + case HCRYPT_CTX_MODE_AESCTR: + { +#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(false, aes_key, iv, in_data[0].payload, in_data[0].len, + out_txt); + 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)]; + unsigned char iv[CRYSPR_AESBLKSZ]; + int iret = 0; + + /* Get input packet index (in network order) */ + hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1); + + /* + * Compute the Initial Vector + * IV (128-bit): + * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | 0s | pki | ctr | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * XOR + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | nonce + + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * pki (32-bit): packet index + * ctr (16-bit): block counter + * nonce (112-bit): number used once (salt) + */ + hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv); + + /* Create CtrStream. May be longer than in_len (next cipher block size boundary) */ + iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv); + if (iret) { + return(iret); + } + /* Reserve output buffer for cryspr */ + out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, cryspr_cb->ctr_stream_len); + + /* Create KeyStream (encrypt CtrStream) */ + iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key, + cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len, + out_txt, &out_len); + if (iret) { + HCRYPT_LOG(LOG_ERR, "%s", "crysprNatural_AES_ecb_cipher(encrypt failed\n"); + return(iret); + } + +#endif /*CRYSPR_HAS_AESCTR*/ + break; + } + case HCRYPT_CTX_MODE_CLRTXT: + memcpy(out_txt, in_data[0].payload, in_data[0].len); + out_len = in_data[0].len; + break; + default: + return(-1); + } + } else { + return(-1); + } + + if (out_len > 0) { + if (NULL == out_p) { + /* + * Application did not provided output buffer, + * so copy encrypted message back in input buffer + */ +#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_txt, out_len); + }else{ + /* Copy output data back in input buffer */ + memcpy(in_data[0].payload, out_txt, out_len); + } +#else /* CRYSPR_HAS_AESCTR */ + /* Copy output data back in input buffer */ + memcpy(in_data[0].payload, out_txt, out_len); +#endif /* CRYSPR_HAS_AESCTR */ + } else { + /* Copy header in output buffer if needed */ +#if !CRYSPR_HAS_AESCTR + if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) { + hcrypt_XorStream(out_txt, in_data[0].payload, out_len); + } +#endif /* CRYSPR_HAS_AESCTR */ + out_p[0] = out_txt; + out_len_p[0] = out_len; + *nbout_p = 1; + } + iret = 0; + } else { + if (NULL != nbout_p) *nbout_p = 0; + iret = -1; + } + +#if 0 + { /* Debug decryption errors */ + static int nberr = 0; + + if (out_txt[0] != 0x47){ + if ((++nberr == 1) + || ((nberr > 500) && (0 == ((((unsigned char *)&MSmsg->pki)[2] & 0x0F)|((unsigned char *)&MSmsg->pki)[3])))) { + HCRYPT_LOG(LOG_DEBUG, "keyindex=%d\n", hcryptCtx_GetKeyIndex(ctx)); + HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek"); + HCRYPT_PRINTKEY(ctx->salt, ctx->salt_len, "salt"); + } + } else { + nberr = 0; + } + } +#endif + return(iret); +} + + +CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr) +{ + /* CryptoLib Primitive API */ + cryspr->prng = crysprStub_Prng; + cryspr->aes_set_key = crysprStub_AES_SetKey; + cryspr->aes_ecb_cipher = crysprStub_AES_EcbCipher; + cryspr->aes_ctr_cipher = crysprStub_AES_CtrCipher; + cryspr->sha1_msg_digest = crysprStub_SHA1_MsgDigest; + + + /* Crypto Session API */ + cryspr->open = crysprFallback_Open; + cryspr->close = crysprFallback_Close; + //Keying material (km) encryption + cryspr->km_pbkdf2 = crysprStub_KmPbkdf2; + cryspr->km_setkey = crysprFallback_KmSetKey; + cryspr->km_wrap = crysprFallback_AES_WrapKey; + cryspr->km_unwrap = crysprFallback_AES_UnwrapKey; + //Media stream (ms) encryption + cryspr->ms_setkey = crysprFallback_MsSetKey; + cryspr->ms_encrypt = crysprFallback_MsEncrypt; + cryspr->ms_decrypt = crysprFallback_MsDecrypt; + + return(cryspr); +} + +HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance(void) +{ + return((HaiCrypt_Cryspr)cryspr4SRT()); +} diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h new file mode 100644 index 000000000..45a300fef --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h @@ -0,0 +1,203 @@ +/* + * 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. + + 2019-06-28 (jdube) + CRYSPR/4SRT Initial implementation. +*****************************************************************************/ + +#ifndef CRYSPR_H +#define CRYSPR_H + +#include +#include + +#if !defined(HAISRT_VERSION_INT) +#include "haicrypt.h" +#include "hcrypt_msg.h" +#else +// Included by haisrt.h or similar +#include "haisrt/haicrypt.h" +#include "haisrt/hcrypt_msg.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cryspr-config.h" + +typedef struct tag_CRYSPR_cb { + CRYSPR_AESCTX aes_kek; /* Key Encrypting Key (KEK) */ + CRYSPR_AESCTX aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */ + + struct tag_CRYSPR_methods *cryspr; + +#if !CRYSPR_HAS_AESCTR + /* Reserve room to build the counter stream ourself */ +#define HCRYPT_CTR_BLK_SZ CRYSPR_AESBLKSZ +#define HCRYPT_CTR_STREAM_SZ 2048 + unsigned char * ctr_stream; + size_t ctr_stream_len; /* Content size */ + size_t ctr_stream_siz; /* Allocated length */ +#endif /* !CRYSPR_HAS_AESCTR */ + +#define CRYSPR_OUTMSGMAX 6 + uint8_t * outbuf; /* output circle buffer */ + size_t outbuf_ofs; /* write offset in circle buffer */ + size_t outbuf_siz; /* circle buffer size */ +} CRYSPR_cb; + +typedef struct tag_CRYSPR_methods { + /* + * prng: + * Pseudo-Random Number Generator + */ + int (*prng)( + unsigned char *rn, /* out: pseudo random number */ + int rn_len); + + int (*aes_set_key)( + 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) */ + CRYSPR_AESCTX *aeskey); /* Cryptolib Specific AES key context */ + + int (*aes_ecb_cipher)( + bool bEncrypt, /* true:encrypt false:decrypt */ + CRYSPR_AESCTX *aes_key, /* ctx */ + const unsigned char *indata, /* src (clear text)*/ + size_t inlen, /* src length */ + unsigned char *out_txt, /* dst (cipher text) */ + size_t *outlen); /* dst length */ + + int (*aes_ctr_cipher)( + bool bEncrypt, /* true:encrypt false:decrypt (don't care with CTR) */ + CRYSPR_AESCTX *aes_key, /* ctx */ + unsigned char *iv, /* iv */ + const unsigned char *indata, /* src (clear text) */ + size_t inlen, /* src length */ + unsigned char *out_txt);/* dest */ + + unsigned char *(*sha1_msg_digest)( + const unsigned char *m, /* in: message */ + size_t m_len, /* message length */ + unsigned char *md); /* out: message digest buffer *160 bytes */ + + /* + * open: + * Create a cipher instance + * Allocate output buffers + */ + CRYSPR_cb *(*open)( + struct tag_CRYSPR_methods *cryspr, + size_t max_len); /* Maximum packet length that will be encrypted/decrypted */ + + /* + * close: + * Release any cipher resources + */ + int (*close)( + CRYSPR_cb *cryspr_data); /* Cipher handle, internal data */ + + /* + * pbkdf2_hmac_sha1 + * Password-based Key Derivation Function 2 + */ + int (*km_pbkdf2)( + CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */ + char *passwd, /* passphrase */ + size_t passwd_len, /* passphrase len */ + unsigned char *salt, /* salt */ + size_t salt_len, /* salt_len */ + int itr, /* iterations */ + size_t out_len, /* key_len */ + unsigned char *out); /* derived key */ + + /* + * km_setkey: + * Set the Key Encypting Key for Wrap (Encryption) or UnWrap (Decryption). + * Context (ctx) tells if it's for Wrap or Unwrap + * A Context flags (ctx->flags) also tells if this is for wrap(encryption) or unwrap(decryption) context (HCRYPT_CTX_F_ENCRYPT) + */ + int (*km_setkey)( + CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */ + bool bWrap, /* True: Wrap KEK, False: Unwrap KEK */ + const unsigned char *kek, size_t kek_len); /* KEK: Key Encrypting Key */ + + /* + * km_wrap: + * wrap media stream key + */ + int (*km_wrap)(CRYSPR_cb *cryspr_cb, + unsigned char *wrap, + const unsigned char *sek, + unsigned int seklen); + + /* + * km_unwrap: + * wrap media stream key + */ + int (*km_unwrap)(CRYSPR_cb *cryspr_cb, + unsigned char *sek, + const unsigned char *wrap, + unsigned int wraplen); + /* + * setkey: + * Set the Odd or Even, Encryption or Decryption key. + * Context (ctx) tells if it's for Odd or Even key (hcryptCtx_GetKeyIndex(ctx)) + * A Context flags (ctx->flags) also tells if this is an encryption or decryption context (HCRYPT_CTX_F_ENCRYPT) + */ + int (*ms_setkey)( + CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */ + hcrypt_Ctx *ctx, /* HaiCrypt Context (cipher, keys, Odd/Even, etc..) */ + const unsigned char *key, size_t kwelen); /* New Key */ + + /* + * encrypt: + * Submit a list of nbin clear transport packets (hcrypt_DataDesc *in_data) to encryption + * returns *nbout encrypted data packets of length out_len_p[] into out_p[] + * + * If cipher implements deferred encryption (co-processor, async encryption), + * it may return no encrypted packets, or encrypted packets for clear text packets of a previous call. + */ + int (*ms_encrypt)( + CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */ + hcrypt_Ctx *ctx, /* HaiCrypt Context (cipher, keys, Odd/Even, etc..) */ + hcrypt_DataDesc *in_data, int nbin, /* Clear text transport packets: header and payload */ + void *out_p[], size_t out_len_p[], int *nbout); /* Encrypted packets */ + + /* + * decrypt: + * Submit a list of nbin encrypted transport packets (hcrypt_DataDesc *in_data) to decryption + * returns *nbout clear text data packets of length out_len_p[] into out_p[] + * + * If cipher implements deferred decryption (co-processor, async encryption), + * it may return no decrypted packets, or decrypted packets for encrypted packets of a previous call. + */ + int (*ms_decrypt)( + CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */ + hcrypt_Ctx *ctx, /* HaiCrypt Context (cipher, keys, Odd/Even, etc..) */ + hcrypt_DataDesc *in_data, int nbin, /* Clear text transport packets: header and payload */ + void *out_p[], size_t out_len_p[], int *nbout); /* Encrypted packets */ + +} CRYSPR_methods; + +CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr); + +#ifdef __cplusplus +} +#endif + +#endif /* CRYSPR_H */ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/filelist-gnutls.maf b/trunk/3rdparty/srt-1-fit/haicrypt/filelist-gnutls.maf new file mode 100644 index 000000000..8a3871447 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/filelist-gnutls.maf @@ -0,0 +1,27 @@ +# This file is currently reserved for future refactoring, when all headers +# are going to be moved here. This is the list of headers considered to be +# attached to the installation package. Once possible, please move the below +# header files from ../include back to this directory. +PUBLIC HEADERS +haicrypt.h +hcrypt_ctx.h +hcrypt_msg.h + +PRIVATE HEADERS +hcrypt.h +cryspr.h +cryspr-gnutls.h +haicrypt_log.h + +SOURCES +cryspr.c +cryspr-gnutls.c +hcrypt.c +hcrypt_ctx_rx.c +hcrypt_ctx_tx.c +hcrypt_rx.c +hcrypt_sa.c +hcrypt_tx.c +hcrypt_xpt_srt.c +hcrypt_xpt_sta.c +haicrypt_log.cpp diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/filelist-mbedtls.maf b/trunk/3rdparty/srt-1-fit/haicrypt/filelist-mbedtls.maf new file mode 100644 index 000000000..835c1f535 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/filelist-mbedtls.maf @@ -0,0 +1,25 @@ +# HaiCrypt library contents + +PUBLIC HEADERS +haicrypt.h +hcrypt_ctx.h +hcrypt_msg.h + +PRIVATE HEADERS +hcrypt.h +cryspr.h +cryspr-mbedtls.h +haicrypt_log.h + +SOURCES +cryspr.c +cryspr-mbedtls.c +hcrypt.c +hcrypt_ctx_rx.c +hcrypt_ctx_tx.c +hcrypt_rx.c +hcrypt_sa.c +hcrypt_tx.c +hcrypt_xpt_srt.c +hcrypt_xpt_sta.c +haicrypt_log.cpp diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/filelist-openssl.maf b/trunk/3rdparty/srt-1-fit/haicrypt/filelist-openssl.maf new file mode 100644 index 000000000..e3ef0a1aa --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/filelist-openssl.maf @@ -0,0 +1,25 @@ +# HaiCrypt library contents + +PUBLIC HEADERS +haicrypt.h +hcrypt_ctx.h +hcrypt_msg.h + +PRIVATE HEADERS +hcrypt.h +cryspr.h +cryspr-openssl.h +haicrypt_log.h + +SOURCES +cryspr.c +cryspr-openssl.c +hcrypt.c +hcrypt_ctx_rx.c +hcrypt_ctx_tx.c +hcrypt_rx.c +hcrypt_sa.c +hcrypt_tx.c +hcrypt_xpt_srt.c +hcrypt_xpt_sta.c +haicrypt_log.cpp diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h new file mode 100644 index 000000000..79763b7af --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h @@ -0,0 +1,138 @@ +/* + * 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. +*****************************************************************************/ + +#ifndef HAICRYPT_H +#define HAICRYPT_H + +#include +#include + +#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 */ + +#define HAICRYPT_CIPHER_BLK_SZ 16 /* AES Block Size */ + +#define HAICRYPT_PWD_MAX_SZ 80 /* MAX password (for Password-based Key Derivation) */ +#define HAICRYPT_KEY_MAX_SZ 32 /* MAX key */ +#define HAICRYPT_SECRET_MAX_SZ (HAICRYPT_PWD_MAX_SZ > HAICRYPT_KEY_MAX_SZ ? HAICRYPT_PWD_MAX_SZ : HAICRYPT_KEY_MAX_SZ) + + +#define HAICRYPT_SALT_SZ 16 + +#define HAICRYPT_WRAPKEY_SIGN_SZ 8 /* RFC3394 AES KeyWrap signature size */ + +#define HAICRYPT_PBKDF2_SALT_LEN 8 /* PKCS#5 PBKDF2 Password based key derivation salt length */ +#define HAICRYPT_PBKDF2_ITER_CNT 2048 /* PKCS#5 PBKDF2 Password based key derivation iteration count */ + +#define HAICRYPT_TS_PKT_SZ 188 /* Transport Stream packet size */ + +typedef struct { +#define HAICRYPT_SECTYP_UNDEF 0 +#define HAICRYPT_SECTYP_PRESHARED 1 /* Preshared KEK */ +#define HAICRYPT_SECTYP_PASSPHRASE 2 /* Password */ + unsigned typ; + size_t len; + unsigned char str[HAICRYPT_SECRET_MAX_SZ]; +}HaiCrypt_Secret; + +typedef struct { +#define HAICRYPT_CFG_F_TX 0x01 /* !TX -> RX */ +#define HAICRYPT_CFG_F_CRYPTO 0x02 /* Perform crypto Tx:Encrypt Rx:Decrypt */ +#define HAICRYPT_CFG_F_FEC 0x04 /* Do Forward Error Correction */ + unsigned flags; + + HaiCrypt_Secret secret; /* Security Association */ + + HaiCrypt_Cryspr cryspr; /* CRYSPR implementation */ +#define HAICRYPT_DEF_KEY_LENGTH 16 /* default key length (bytes) */ + size_t key_len; /* SEK length (bytes) */ +#define HAICRYPT_DEF_DATA_MAX_LENGTH 1500 /* default packet data length (bytes) */ + size_t data_max_len; /* Maximum data_len passed to HaiCrypt (bytes) */ + +#define HAICRYPT_XPT_STANDALONE 0 +#define HAICRYPT_XPT_SRT 1 + int xport; + +#define HAICRYPT_DEF_KM_TX_PERIOD 1000 /* Keying Material Default Tx Period (msec) */ + unsigned int km_tx_period_ms; /* Keying Material Tx period (msec) */ +#define HAICRYPT_DEF_KM_REFRESH_RATE 0x1000000 /* Keying Material Default Refresh Rate (pkts) */ + unsigned int km_refresh_rate_pkt; /* Keying Material Refresh Rate (pkts) */ +#define HAICRYPT_DEF_KM_PRE_ANNOUNCE 0x1000 /* Keying Material Default Pre/Post Announce (pkts) */ + unsigned int km_pre_announce_pkt; /* Keying Material Pre/Post Announce (pkts) */ +}HaiCrypt_Cfg; + +typedef enum HaiCrypt_CryptoDir { HAICRYPT_CRYPTO_DIR_RX, HAICRYPT_CRYPTO_DIR_TX } HaiCrypt_CryptoDir; + +//typedef void *HaiCrypt_Handle; +// internally it will be correctly interpreted, +// for the outsider it's just some kinda incomplete type +// but still if you use any kinda pointer instead, you'll get complaints +typedef struct hcrypt_Session_str* HaiCrypt_Handle; + + + +HAICRYPT_API 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); + +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); + +/* Status values */ + +#define HAICRYPT_ERROR -1 +#define HAICRYPT_ERROR_WRONG_SECRET -2 +#define HAICRYPT_OK 0 + + +#ifdef __cplusplus +} +#endif + +#endif /* HAICRYPT_H */ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.cpp b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.cpp new file mode 100644 index 000000000..85a25b1fd --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.cpp @@ -0,0 +1,120 @@ +/* + * 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/. + * + */ + +#if ENABLE_HAICRYPT_LOGGING + +#include "hcrypt.h" +#include "haicrypt.h" +#include "../srtcore/srt.h" +#include "../srtcore/logging.h" + +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"); + +extern "C" { + +int HaiCrypt_SetLogLevel(int level, int logfa) +{ + srt_setloglevel(level); + if (logfa != SRT_LOGFA_GENERAL) // General can't be turned on or off + { + srt_addlogfa(logfa); + } + return 0; +} + +// HaiCrypt will be using its own FA, which will be turned off by default. + +// Templates made C way. +// It's tempting to use the HAICRYPT_DEFINE_LOG_DISPATCHER macro here because it would provide the +// exact signature that is needed here, the problem is though that this would expand the LOGLEVEL +// parameter, which is also a macro, into the value that the macro designates, which would generate +// the HaiCrypt_LogF_0 instead of HaiCrypt_LogF_LOG_DEBUG, for example. +#define HAICRYPT_DEFINE_LOG_DISPATCHER(LOGLEVEL, dispatcher) \ + int HaiCrypt_LogF_##LOGLEVEL ( const char* file, int line, const char* function, const char* format, ...) \ +{ \ + va_list ap; \ + va_start(ap, format); \ + srt_logging::LogDispatcher& lg = hclog.dispatcher; \ + if (!lg.CheckEnabled()) return -1; \ + lg().setloc(file, line, function).vform(format, ap); \ + va_end(ap); \ + return 0; \ +} + + +HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_DEBUG, Debug); +HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_NOTICE, Note); +HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_INFO, Note); +HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_WARNING, Warn); +HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_ERR, Error); +HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_CRIT, Fatal); +HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_ALERT, Fatal); +HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_EMERG, Fatal); + + +static void DumpCfgFlags(int flags, std::ostream& out) +{ + static struct { int flg; const char* desc; } flgtable [] = { +#define HCRYPTF(name) { HAICRYPT_CFG_F_##name, #name } + HCRYPTF(TX), + HCRYPTF(CRYPTO), + HCRYPTF(FEC) +#undef HCRYPTF + }; + size_t flgtable_size = sizeof(flgtable)/sizeof(flgtable[0]); + size_t i; + + out << "{"; + const char* sep = ""; + const char* sep_bar = " | "; + for (i = 0; i < flgtable_size; ++i) + { + if ( (flgtable[i].flg & flags) != 0 ) + { + out << sep << flgtable[i].desc; + sep = sep_bar; + } + } + out << "}"; +} + +void HaiCrypt_DumpConfig(const HaiCrypt_Cfg* cfg) +{ + std::ostringstream cfg_flags; + + DumpCfgFlags(cfg->flags, cfg_flags); + + 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") + << " key_len=" << cfg->key_len << " data_max_len=" << cfg->data_max_len); + + + LOGC(hclog.Debug, log << "CFG DUMP: txperiod=" + << cfg->km_tx_period_ms << "ms kmrefresh=" << cfg->km_refresh_rate_pkt + << " kmpreannounce=" << cfg->km_pre_announce_pkt + << " secret " + << "{tp=" << (cfg->secret.typ == 1 ? "PSK" : cfg->secret.typ == 2 ? "PWD" : "???") + << " len=" << cfg->secret.len << " pwd=" << cfg->secret.str << "}"); + +} + +} // extern "C" + +#endif // Block for the whole file diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.h b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.h new file mode 100644 index 000000000..665bc70ca --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.h @@ -0,0 +1,34 @@ +#ifndef IMC__HAICRYPT_LOG_H +#define IMC__HAICRYPT_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define HAICRYPT_DECLARE_LOG_DISPATCHER(LOGLEVEL) \ + int HaiCrypt_LogF_##LOGLEVEL ( const char* file, int line, const char* function, const char* format, ...) + +// Now declare all dispatcher functions + +HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_DEBUG); +HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_NOTICE); +HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_INFO); +HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_WARNING); +HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_ERR); +HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_CRIT); +HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_ALERT); +HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_EMERG); + +#define HCRYPT_LOG_INIT() +#define HCRYPT_LOG_EXIT() +#define HCRYPT_LOG(lvl, fmt, ...) HaiCrypt_LogF_##lvl (__FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__) + +#if ENABLE_HAICRYPT_LOGGING == 2 +#define HCRYPT_DEV 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif // macroguard diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c new file mode 100644 index 000000000..00f777b2a --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c @@ -0,0 +1,342 @@ +/* + * 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 /* snprintf */ +#include /* NULL, malloc, free */ +#include /* memcpy, memset */ +#ifdef _WIN32 + #include + #include +#else + #include /* timerclear */ +#endif + +#include "hcrypt.h" + +#if ENABLE_HAICRYPT_LOGGING +void HaiCrypt_DumpConfig(const HaiCrypt_Cfg* cfg); +#else +#define HaiCrypt_DumpConfig(x) (void)0 +#endif + + +static hcrypt_Session* sHaiCrypt_PrepareHandle(const HaiCrypt_Cfg* cfg, HaiCrypt_CryptoDir tx) +{ + hcrypt_Session *crypto; + unsigned char *mem_buf; + size_t mem_siz, inbuf_siz; + + HaiCrypt_DumpConfig(cfg); + + HCRYPT_PRINTKEY(cfg->secret.str, cfg->secret.len, "cfgkey"); + + inbuf_siz = 0; + inbuf_siz = hcryptMsg_PaddedLen(cfg->data_max_len, 128/8); + + /* Allocate crypto session control struct */ + mem_siz = sizeof(hcrypt_Session) // structure + + inbuf_siz; + + crypto = malloc(mem_siz); + if (NULL == crypto) { + HCRYPT_LOG(LOG_ERR, "%s\n", "malloc failed"); + return NULL; + } + mem_buf = (unsigned char *)crypto; + mem_buf += sizeof(*crypto); + memset(crypto, 0, sizeof(*crypto)); + + if (inbuf_siz) { + crypto->inbuf = mem_buf; + crypto->inbuf_siz = inbuf_siz; + } + + crypto->cryspr = cfg->cryspr; + crypto->cfg.data_max_len = cfg->data_max_len; + + /* 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(); + break; + default: + HCRYPT_LOG(LOG_ERR, "invalid xport: %d\n", cfg->xport); + free(crypto); + return NULL; + } + + timerclear(&crypto->km.tx_last); + crypto->km.tx_period.tv_sec = cfg->km_tx_period_ms / 1000; + crypto->km.tx_period.tv_usec = (cfg->km_tx_period_ms % 1000) * 1000; + + crypto->km.refresh_rate = cfg->km_refresh_rate_pkt; + crypto->km.pre_announce = cfg->km_pre_announce_pkt; + + /* Indentify each context */ + crypto->ctx_pair[0].flags = HCRYPT_MSG_F_eSEK | (tx ? HCRYPT_CTX_F_ENCRYPT : 0); + crypto->ctx_pair[1].flags = HCRYPT_MSG_F_oSEK | (tx ? HCRYPT_CTX_F_ENCRYPT : 0); + /* Point to each other */ + crypto->ctx_pair[0].alt = &crypto->ctx_pair[1]; + crypto->ctx_pair[1].alt = &crypto->ctx_pair[0]; + + crypto->cryspr_cb = crypto->cryspr->open(crypto->cryspr, cfg->data_max_len); + if (NULL == crypto->cryspr_cb) { + free(crypto); + return NULL; + } + + return crypto; +} + +int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc) +{ + ASSERT(cfg != NULL); + ASSERT(phhc != NULL); + + hcrypt_Session *crypto; + HaiCrypt_CryptoDir tx = (HaiCrypt_CryptoDir)(HAICRYPT_CFG_F_TX & cfg->flags); + + *phhc = NULL; + + HCRYPT_LOG_INIT(); + //Test log + HCRYPT_LOG(LOG_INFO, "creating crypto context(flags=0x%x)\n", cfg->flags); + + if (!(HAICRYPT_CFG_F_CRYPTO & cfg->flags)) { + HCRYPT_LOG(LOG_INFO, "no supported flags set (0x%x)\n", cfg->flags); + return(-1); + } else if ((16 != cfg->key_len) /* SEK length */ + && (24 != cfg->key_len) + && (32 != cfg->key_len)) { + HCRYPT_LOG(LOG_ERR, "invalid key length (%d). Expected: 16, 24, 32\n", (int)cfg->key_len); + return(-1); + } else if ((HAICRYPT_SECTYP_PASSPHRASE == cfg->secret.typ) + && ((0 == cfg->secret.len) || (sizeof(cfg->secret.str) < cfg->secret.len))) { /* KEK length */ + HCRYPT_LOG(LOG_ERR, "invalid secret passphrase length (%d)\n", (int)cfg->secret.len); + return(-1); + } else if ((HAICRYPT_SECTYP_PRESHARED == cfg->secret.typ) + && (cfg->key_len > cfg->secret.len)) { + HCRYPT_LOG(LOG_ERR, "preshared secret length (%d) smaller than key length (%d)\n", + (int)cfg->secret.len, (int)cfg->key_len); + return(-1); + } else if (NULL == cfg->cryspr) { + HCRYPT_LOG(LOG_ERR, "%s\n", "no cryspr specified"); + return(-1); + } else if (0 == cfg->data_max_len) { + HCRYPT_LOG(LOG_ERR, "%s\n", "no data_max_len specified"); + return(-1); + } + + crypto = sHaiCrypt_PrepareHandle(cfg, tx); + if (!crypto) + return -1; + + if (tx) { /* Encoder */ + /* Configure initial context */ + if (hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[0], cfg) + || hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[1], cfg)) { + free(crypto); + return(-1); + } + /* Generate keys for first (default) context */ + if (hcryptCtx_Tx_Rekey(crypto, &crypto->ctx_pair[0])) { + free(crypto); + return(-1); + } + crypto->ctx = &crypto->ctx_pair[0]; + crypto->ctx->flags |= (HCRYPT_CTX_F_ANNOUNCE | HCRYPT_CTX_F_TTSEND); + crypto->ctx->status = HCRYPT_CTX_S_ACTIVE; + } else { /* Decoder */ + /* Configure contexts */ + if (hcryptCtx_Rx_Init(crypto, &crypto->ctx_pair[0], cfg) + || hcryptCtx_Rx_Init(crypto, &crypto->ctx_pair[1], cfg)) { + free(crypto); + return(-1); + } + } + + *phhc = (void *)crypto; + return(0); +} + +int HaiCrypt_ExtractConfig(HaiCrypt_Handle hhcSrc, HaiCrypt_Cfg* pcfg) +{ + hcrypt_Session *crypto = (hcrypt_Session *)hhcSrc; + hcrypt_Ctx* ctx = crypto->ctx; + if (!ctx) + { + // Fall back to the first of the pair; + // Should this be not initialized, ignore it. + ctx = &crypto->ctx_pair[0]; + + // We assume that when ctx != NULL, it is active or keyed anyway. + if (ctx->status != HCRYPT_CTX_S_KEYED && ctx->status != HCRYPT_CTX_S_ACTIVE) + return -1; + } + + pcfg->flags = HAICRYPT_CFG_F_CRYPTO; + if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) == HCRYPT_CTX_F_ENCRYPT) + pcfg->flags |= HAICRYPT_CFG_F_TX; + + /* Set this explicitly - this use of this library is SRT only. */ + pcfg->xport = HAICRYPT_XPT_SRT; + pcfg->cryspr = crypto->cryspr; + pcfg->key_len = ctx->cfg.key_len; + if (pcfg->key_len == 0) // not initialized - usual in RX + { + pcfg->key_len = ctx->sek_len; + } + pcfg->data_max_len = crypto->cfg.data_max_len; + pcfg->km_tx_period_ms = 0;//No HaiCrypt KM inject period, handled in SRT; + + pcfg->km_refresh_rate_pkt = crypto->km.refresh_rate; + pcfg->km_pre_announce_pkt = crypto->km.pre_announce; + + /* As SRT is using only the PASSPHRASE type, never PRESHARED, + * this is so assumed here, although there are completely no + * premises as to which is currently used by the hhcSrc. + */ + pcfg->secret.typ = HAICRYPT_SECTYP_PASSPHRASE; + pcfg->secret.len = ctx->cfg.pwd_len; + memcpy(pcfg->secret.str, ctx->cfg.pwd, pcfg->secret.len); + + return 0; +} + +int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handle *phhc) +{ + hcrypt_Session *cryptoSrc = (hcrypt_Session *)hhcSrc; + hcrypt_Session *cryptoClone; + unsigned char *mem_buf; + size_t mem_siz, inbuf_siz; + + *phhc = NULL; + + ASSERT(NULL != hhcSrc); + + HCRYPT_LOG(LOG_INFO, "%s\n", "creating CLONED crypto context"); + + if (tx) { + HaiCrypt_Cfg crypto_config; + HaiCrypt_ExtractConfig(hhcSrc, &crypto_config); + + /* + * Just invert the direction written in flags and use the + * standard way of creating the context, as you already have a config. + */ + crypto_config.flags |= HAICRYPT_CFG_F_TX; + cryptoClone = sHaiCrypt_PrepareHandle(&crypto_config, tx); + if (!cryptoClone) + return -1; + + /* Configure initial context */ + if (hcryptCtx_Tx_Init(cryptoClone, &cryptoClone->ctx_pair[0], &crypto_config) + || hcryptCtx_Tx_Init(cryptoClone, &cryptoClone->ctx_pair[1], &crypto_config)) { + free(cryptoClone); + return(-1); + } + /* Clone keys for first (default) context from the source RX crypto */ + if (hcryptCtx_Tx_CloneKey(cryptoClone, &cryptoClone->ctx_pair[0], cryptoSrc)) { + free(cryptoClone); + return(-1); + } + cryptoClone->ctx = &cryptoClone->ctx_pair[0]; + cryptoClone->ctx->flags |= (HCRYPT_CTX_F_ANNOUNCE | HCRYPT_CTX_F_TTSEND); + cryptoClone->ctx->status = HCRYPT_CTX_S_ACTIVE; + + } else { /* Receiver */ + + /* + * If cryspr has no special input buffer alignment requirement, + * handle it in the crypto session. + */ + inbuf_siz = cryptoSrc->inbuf_siz ; + + /* Allocate crypto session control struct */ + mem_siz = sizeof(hcrypt_Session) // structure + + inbuf_siz; + + cryptoClone = malloc(mem_siz); + if (NULL == cryptoClone) { + HCRYPT_LOG(LOG_ERR, "%s\n", "malloc failed"); + return(-1); + } + mem_buf = (unsigned char *)cryptoClone; + mem_buf += sizeof(*cryptoClone); + memcpy(cryptoClone, cryptoSrc, sizeof(*cryptoClone)); + + if (inbuf_siz) { + cryptoClone->inbuf = mem_buf; + mem_buf += inbuf_siz; + } + timerclear(&cryptoClone->km.tx_last); + + /* Adjust pointers pointing into cryproSrc after copy + msg_info and crysprs are extern statics so this is ok*/ + cryptoClone->ctx_pair[0].alt = &cryptoClone->ctx_pair[1]; + cryptoClone->ctx_pair[1].alt = &cryptoClone->ctx_pair[0]; + + /* create a new cryspr (OpenSSL) context */ + cryptoClone->cryspr_cb = cryptoClone->cryspr->open(cryptoClone->cryspr, cryptoClone->cfg.data_max_len); + if (NULL == cryptoClone->cryspr_cb) { + //shred + free(cryptoClone); + return(-1); + } + + + /* Configure contexts */ + if (hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[0], NULL) + || hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[1], NULL)) { + free(cryptoClone); + return(-1); + } + + /* Clear salt to force later regeneration of KEK as AES decrypting key, + copyed one is encrypting key */ + cryptoClone->ctx_pair[0].flags &= ~HCRYPT_CTX_F_ENCRYPT; + cryptoClone->ctx_pair[1].flags &= ~HCRYPT_CTX_F_ENCRYPT; + memset(cryptoClone->ctx_pair[0].salt, 0, sizeof(cryptoClone->ctx_pair[0].salt)); + cryptoClone->ctx_pair[0].salt_len = 0; + } + + *phhc = (void *)cryptoClone; + return(0); +} + +int HaiCrypt_Close(HaiCrypt_Handle hhc) +{ + hcrypt_Session *crypto = (hcrypt_Session *)hhc; + int rc = -1; + + if (crypto) { + if (crypto->cryspr && crypto->cryspr->close) crypto->cryspr->close(crypto->cryspr_cb); + free(crypto); + rc = 0; + } + HCRYPT_LOG_EXIT(); + return rc; +} diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h new file mode 100644 index 000000000..f2193f586 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h @@ -0,0 +1,169 @@ +/* + * 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. + 2014-03-26 (jsantiago) + OS-X Build. + 2014-03-27 (jdube) + Remove dependency on internal Crypto API. + 2016-07-22 (jsantiago) + MINGW-W64 Build. +*****************************************************************************/ + +#ifndef HCRYPT_H +#define HCRYPT_H + +#include + +#ifdef _WIN32 + #include + #include + #if defined(_MSC_VER) + #pragma warning(disable:4267) + #pragma warning(disable:4018) + #endif +#else + #include +#endif + +#ifdef __GNUC__ +#define ATR_UNUSED __attribute__((unused)) +#else +#define ATR_UNUSED +#endif + +#include "haicrypt.h" +#include "hcrypt_msg.h" +#include "hcrypt_ctx.h" +#include "cryspr.h" + +//#define HCRYPT_DEV 1 /* Development: should not be defined in committed code */ + +#ifdef HAICRYPT_SUPPORT_CRYPTO_API +/* See CRYPTOFEC_OBJECT in session structure */ +#define CRYPTO_API_SERVER 1 /* Enable handler's structures */ +#include "crypto_api.h" +#endif /* HAICRYPT_SUPPORT_CRYPTO_API */ + +typedef struct hcrypt_Session_str { +#ifdef HAICRYPT_SUPPORT_CRYPTO_API + /* + * Resv matches internal upper layer handle (crypto_api) + * They are not used in HaiCrypt. + * This make 3 layers using the same handle. + * To get rid of this dependency for a portable HaiCrypt, + * revise caller (crypto_hc.c) to allocate its own buffer. + */ + CRYPTOFEC_OBJECT resv; /* See above comment */ +#endif /* HAICRYPT_SUPPORT_CRYPTO_API */ + + hcrypt_Ctx ctx_pair[2]; /* Even(0)/Odd(1) crypto contexts */ + hcrypt_Ctx * ctx; /* Current context */ + + CRYSPR_methods * cryspr; + CRYSPR_cb * cryspr_cb; + + unsigned char * inbuf; /* allocated if cipher has no getinbuf() func */ + size_t inbuf_siz; + + int se; /* Stream Encapsulation (HCRYPT_SE_xxx) */ + hcrypt_MsgInfo * msg_info; + + struct { + size_t data_max_len; + }cfg; + + struct { + struct timeval tx_period; /* Keying Material tx period (milliseconds) */ + struct timeval tx_last; /* Keying Material last tx time */ + unsigned int refresh_rate; /* SEK use period */ + unsigned int pre_announce; /* Pre/Post next/old SEK announce */ + }km; +} hcrypt_Session; + +#if ENABLE_HAICRYPT_LOGGING +#include "haicrypt_log.h" +#else + +#define HCRYPT_LOG_INIT() +#define HCRYPT_LOG_EXIT() +#define HCRYPT_LOG(lvl, fmt, ...) + +#endif + +#ifdef HCRYPT_DEV +#define HCRYPT_PRINTKEY(key, len, tag) HCRYPT_LOG(LOG_DEBUG, \ + "%s[%d]=0x%02x%02x..%02x%02x\n", tag, len, \ + (key)[0], (key)[1], (key)[(len)-2], (key)[(len)-1]) +#else /* HCRYPT_DEV */ +#define HCRYPT_PRINTKEY(key,len,tag) +#endif /* HCRYPT_DEV */ + +#ifndef ASSERT +#include +#define ASSERT(c) assert(c) +#endif + + +/* HaiCrypt-TP CTR mode 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) + */ +#define hcrypt_SetCtrIV(pki, nonce, iv) do { \ + memset(&(iv)[0], 0, 128/8); \ + memcpy(&(iv)[10], (pki), HCRYPT_PKI_SZ); \ + hcrypt_XorStream(&(iv)[0], (nonce), 112/8); \ + } while(0) + +#define hcrypt_XorStream(dst, strm, len) do { \ + int __XORSTREAMi; \ + for (__XORSTREAMi = 0 \ + ;__XORSTREAMi < (int)(len) \ + ;__XORSTREAMi += 1) { \ + (dst)[__XORSTREAMi] ^= (strm)[__XORSTREAMi]; \ + } \ + } while(0) + + +int hcryptCtx_SetSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Secret *secret); +int hcryptCtx_GenSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx); + +int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg); +int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx); +int hcryptCtx_Tx_CloneKey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const hcrypt_Session* cryptoSrc); +int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto); +int hcryptCtx_Tx_PreSwitch(hcrypt_Session *crypto); +int hcryptCtx_Tx_Switch(hcrypt_Session *crypto); +int hcryptCtx_Tx_PostSwitch(hcrypt_Session *crypto); +int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *alt_sek); +int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto); +int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto, void *out_p[], size_t out_len_p[], int maxout); + +int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg); +int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *msg, size_t msg_len); + +#endif /* HCRYPT_H */ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h new file mode 100644 index 000000000..9c60c0854 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h @@ -0,0 +1,97 @@ +/* + * 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. +*****************************************************************************/ + +#ifndef HCRYPT_CTX_H +#define HCRYPT_CTX_H + +#include +#include +#include "hcrypt.h" + +#if !defined(HAISRT_VERSION_INT) +#include "haicrypt.h" +#include "hcrypt_msg.h" +#else +// Included by haisrt.h or similar +#include "haisrt/haicrypt.h" +#include "haisrt/hcrypt_msg.h" +#endif + +typedef struct { + unsigned char *pfx; //Prefix described by transport msg info (in ctx) + unsigned char *payload; + size_t len; //Payload size +}hcrypt_DataDesc; + + +typedef struct tag_hcrypt_Ctx { + struct tag_hcrypt_Ctx * alt; /* Alternative ctx (even/odd) */ + +#define HCRYPT_CTX_F_MSG 0x00FF /* Aligned wiht message header flags */ +#define HCRYPT_CTX_F_eSEK HCRYPT_MSG_F_eSEK +#define HCRYPT_CTX_F_oSEK HCRYPT_MSG_F_oSEK +#define HCRYPT_CTX_F_xSEK HCRYPT_MSG_F_xSEK + +#define HCRYPT_CTX_F_ENCRYPT 0x0100 /* 0:decrypt 1:encrypt */ +#define HCRYPT_CTX_F_ANNOUNCE 0x0200 /* Announce KM */ +#define HCRYPT_CTX_F_TTSEND 0x0400 /* time to send */ + unsigned flags; +#define hcryptCtx_GetKeyFlags(ctx) ((ctx)->flags & HCRYPT_CTX_F_xSEK) +#define hcryptCtx_GetKeyIndex(ctx) (((ctx)->flags & HCRYPT_CTX_F_xSEK)>>1) + +#define HCRYPT_CTX_S_INIT 1 +#define HCRYPT_CTX_S_SARDY 2 /* Security Association (KEK) ready */ +#define HCRYPT_CTX_S_KEYED 3 /* Media Stream Encrypting Key (SEK) ready */ +#define HCRYPT_CTX_S_ACTIVE 4 /* Announced and in use */ +#define HCRYPT_CTX_S_DEPRECATED 5 /* Still announced but no longer used */ + unsigned status; + +#define HCRYPT_CTX_MODE_CLRTXT 0 /* NULL cipher (for tests) */ +#define HCRYPT_CTX_MODE_AESECB 1 /* Electronic Code Book mode */ +#define HCRYPT_CTX_MODE_AESCTR 2 /* Counter mode */ +#define HCRYPT_CTX_MODE_AESCBC 3 /* Cipher-block chaining mode */ + unsigned mode; + + struct { + size_t key_len; + size_t pwd_len; + char pwd[HAICRYPT_PWD_MAX_SZ]; + } cfg; + + size_t salt_len; + unsigned char salt[HAICRYPT_SALT_SZ]; + + size_t sek_len; + unsigned char sek[HAICRYPT_KEY_MAX_SZ]; + + hcrypt_MsgInfo * msg_info; /* Transport message handler */ + unsigned pkt_cnt; /* Key usage counter */ + +#define HCRYPT_CTX_MAX_KM_PFX_SZ 16 + size_t KMmsg_len; + unsigned char KMmsg_cache[HCRYPT_CTX_MAX_KM_PFX_SZ + HCRYPT_MSG_KM_MAX_SZ]; + +#define HCRYPT_CTX_MAX_MS_PFX_SZ 16 + unsigned char MSpfx_cache[HCRYPT_CTX_MAX_MS_PFX_SZ]; +} hcrypt_Ctx; + + +#endif /* HCRYPT_CTX_H */ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_rx.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_rx.c new file mode 100644 index 000000000..9bd8a8e63 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_rx.c @@ -0,0 +1,199 @@ +/* + * 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 /* memcpy */ +#include "hcrypt.h" + +int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg) +{ + ctx->mode = HCRYPT_CTX_MODE_AESCTR; + ctx->status = HCRYPT_CTX_S_INIT; + + ctx->msg_info = crypto->msg_info; + + if (cfg && hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) { + return(-1); + } + ctx->status = HCRYPT_CTX_S_SARDY; + return(0); +} + +int hcryptCtx_Rx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *sek, size_t sek_len) +{ + if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, sek, sek_len)) { + HCRYPT_LOG(LOG_ERR, "cryspr setkey[%d](sek) failed\n", hcryptCtx_GetKeyIndex(ctx)); + return(-1); + } + memcpy(ctx->sek, sek, sek_len); + ctx->sek_len = sek_len; + + HCRYPT_LOG(LOG_INFO, "updated context[%d]\n", hcryptCtx_GetKeyIndex(ctx)); + HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek"); + ctx->status = HCRYPT_CTX_S_KEYED; + return(0); +} + +/* Parse Keying Material message */ +int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t msg_len) +{ + size_t sek_len, salt_len; + unsigned char seks[HAICRYPT_KEY_MAX_SZ * 2]; + int sek_cnt; + size_t kek_len = 0; + hcrypt_Ctx *ctx; + int do_pbkdf = 0; + + if (NULL == crypto) { + HCRYPT_LOG(LOG_ERR, "Rx_ParseKM: invalid params: crypto=%p\n", crypto); + return(-1); + } + + /* Validate message content */ + { + if (msg_len <= HCRYPT_MSG_KM_OFS_SALT) { + HCRYPT_LOG(LOG_WARNING, "KMmsg length too small (%zd)\n", msg_len); + return(-1); + } + salt_len = hcryptMsg_KM_GetSaltLen(km_msg); + sek_len = hcryptMsg_KM_GetSekLen(km_msg); + + if ((salt_len > HAICRYPT_SALT_SZ) + || (sek_len > HAICRYPT_KEY_MAX_SZ)) { + HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported salt/key length\n"); + return(-1); + } + if ((16 != sek_len) + && (24 != sek_len) + && (32 != sek_len)) { + HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported key length\n"); + return(-1); + } + if (hcryptMsg_KM_HasBothSek(km_msg)) { + sek_cnt = 2; + } else { + sek_cnt = 1; + } + if (msg_len != (HCRYPT_MSG_KM_OFS_SALT + salt_len + (sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) { + HCRYPT_LOG(LOG_WARNING, "KMmsg length inconsistent (%zd,%zd,%zd)\n", + salt_len, sek_len, msg_len); + return(-1); + } + + /* Check options support */ + if ((HCRYPT_CIPHER_AES_CTR != km_msg[HCRYPT_MSG_KM_OFS_CIPHER]) + || (HCRYPT_AUTH_NONE != km_msg[HCRYPT_MSG_KM_OFS_AUTH])) { + HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported option\n"); + return(-1); + } + + if (crypto->se != km_msg[HCRYPT_MSG_KM_OFS_SE]) { + HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg invalid SE\n"); + return(-1); + } + + /* Check KEKI here and pick right key */ + //>>todo + /* + * We support no key exchange, + * KEK is preshared or derived from a passphrase + */ + } + + /* Pick the context updated by this KMmsg */ + if (hcryptMsg_KM_HasBothSek(km_msg) && (NULL != crypto->ctx)) { + ctx = crypto->ctx->alt; /* 2 SEK KM, start with inactive ctx */ + } else { + ctx = &crypto->ctx_pair[hcryptMsg_KM_GetKeyIndex(km_msg)]; + } + if (NULL == ctx) { + HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg invalid flags (no SEK)\n"); + return(-1); + } + + /* Check Salt and get if new */ + if ((salt_len != ctx->salt_len) + || (0 != memcmp(ctx->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len))) { + /* Salt changed (or 1st KMmsg received) */ + memcpy(ctx->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len); + ctx->salt_len = salt_len; + do_pbkdf = 1; /* Impact on password derived kek */ + } + + /* Check SEK length and get if new */ + if (sek_len != ctx->sek_len) { + /* Key length changed or 1st KMmsg received */ + ctx->sek_len = sek_len; + do_pbkdf = 1; /* Impact on password derived kek */ + } + + /* + * Regenerate KEK if it is password derived + * and Salt or SEK length changed + */ + if (ctx->cfg.pwd_len && do_pbkdf) { + if (hcryptCtx_GenSecret(crypto, ctx)) { + return(-1); + } + ctx->status = HCRYPT_CTX_S_SARDY; + kek_len = sek_len; /* KEK changed */ + } + + /* Unwrap SEK(s) and set in context */ + if (0 > crypto->cryspr->km_unwrap(crypto->cryspr_cb, seks, + &km_msg[HCRYPT_MSG_KM_OFS_SALT + salt_len], + (sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) { + HCRYPT_LOG(LOG_WARNING, "%s", "unwrap key failed\n"); + return(-2); //Report unmatched shared secret + } + /* + * First SEK in KMmsg is eSEK if both SEK present + */ + hcryptCtx_Rx_Rekey(crypto, ctx, + ((2 == sek_cnt) && (ctx->flags & HCRYPT_MSG_F_oSEK)) ? &seks[sek_len] : &seks[0], + sek_len); + + /* + * Refresh KMmsg cache to detect Keying Material changes + */ + ctx->KMmsg_len = msg_len; + memcpy(ctx->KMmsg_cache, km_msg, msg_len); + + /* update other (alternate) context if both SEK provided */ + if (2 == sek_cnt) { + hcrypt_Ctx *alt = ctx->alt; + + memcpy(alt->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len); + alt->salt_len = salt_len; + + if (kek_len) { /* New or changed KEK */ +// memcpy(&alt->aes_kek, &ctx->aes_kek, sizeof(alt->aes_kek)); + alt->status = HCRYPT_CTX_S_SARDY; + } + + hcryptCtx_Rx_Rekey(crypto, alt, + ((2 == sek_cnt) && (alt->flags & HCRYPT_MSG_F_oSEK)) ? &seks[sek_len] : &seks[0], + sek_len); + + alt->KMmsg_len = msg_len; + memcpy(alt->KMmsg_cache, km_msg, msg_len); + } + return(0); +} diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_tx.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_tx.c new file mode 100644 index 000000000..77560a543 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_tx.c @@ -0,0 +1,420 @@ +/* + * 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 /* memcpy */ +#ifdef _WIN32 + #include + #include + #include +#else + #include +#endif +#include "hcrypt.h" + +int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg) +{ + ctx->cfg.key_len = cfg->key_len; + + ctx->mode = HCRYPT_CTX_MODE_AESCTR; + ctx->status = HCRYPT_CTX_S_INIT; + + ctx->msg_info = crypto->msg_info; + + if (hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) { + return(-1); + } + return(0); +} + +int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx) +{ + int iret; + + ASSERT(HCRYPT_CTX_S_SARDY <= ctx->status); + + /* Generate Salt */ + ctx->salt_len = HAICRYPT_SALT_SZ; + if (0 > (iret = crypto->cryspr->prng(ctx->salt, ctx->salt_len))) { + HCRYPT_LOG(LOG_ERR, "PRNG(salt[%zd]) failed\n", ctx->salt_len); + return(iret); + } + + /* Generate SEK */ + ctx->sek_len = ctx->cfg.key_len; + if (0 > (iret = crypto->cryspr->prng(ctx->sek, ctx->sek_len))) { + HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", ctx->sek_len); + return(iret); + } + + /* Set SEK in cryspr */ + if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, ctx->sek, ctx->sek_len)) { + HCRYPT_LOG(LOG_ERR, "cryspr setkey(sek[%zd]) failed\n", ctx->sek_len); + return(-1); + } + + HCRYPT_LOG(LOG_NOTICE, "rekeyed crypto context[%d]\n", (ctx->flags & HCRYPT_CTX_F_xSEK)/2); + 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); + } + + /* Assemble the new Keying Material message */ + if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, ctx, NULL))) { + return(iret); + } + if ((HCRYPT_CTX_S_KEYED <= ctx->alt->status) + && hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) { + /* + * previous context KM announced in alternate (odd/even) KM, + * reassemble it without our KM + */ + hcryptCtx_Tx_AsmKM(crypto, ctx->alt, NULL); + } + + /* Initialize the Media Stream message prefix cache */ + ctx->msg_info->resetCache(ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, ctx->flags & HCRYPT_CTX_F_xSEK); + ctx->pkt_cnt = 1; + + ctx->status = HCRYPT_CTX_S_KEYED; + return(0); +} + +int hcryptCtx_Tx_CloneKey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const hcrypt_Session* cryptoSrc) +{ + int iret; + + 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]; + } + + /* 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); + + /* Set SEK in cryspr */ + if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, ctx->sek, ctx->sek_len)) { + HCRYPT_LOG(LOG_ERR, "cryspr setkey(sek[%zd]) failed\n", ctx->sek_len); + return(-1); + } + + HCRYPT_LOG(LOG_NOTICE, "clone-keyed crypto context[%d]\n", (ctx->flags & HCRYPT_CTX_F_xSEK)/2); + 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); + } + + /* Assemble the new Keying Material message */ + if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, ctx, NULL))) { + return(iret); + } + if ((HCRYPT_CTX_S_KEYED <= ctx->alt->status) + && hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) { + /* + * previous context KM announced in alternate (odd/even) KM, + * reassemble it without our KM + */ + hcryptCtx_Tx_AsmKM(crypto, ctx->alt, NULL); + } + + /* Initialize the Media Stream message prefix cache */ + ctx->msg_info->resetCache(ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, ctx->flags & HCRYPT_CTX_F_xSEK); + ctx->pkt_cnt = 1; + + ctx->status = HCRYPT_CTX_S_KEYED; + return(0); +} + +/* + * Refresh the alternate context from the current. + * Regenerates the SEK but keep the salt, doing so also + * preserve the KEK generated from secret password and salt. + */ + +int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto) +{ + hcrypt_Ctx *ctx = crypto->ctx; + hcrypt_Ctx *new_ctx; + int iret; + + ASSERT(NULL != ctx); + ASSERT(HCRYPT_CTX_S_ACTIVE == ctx->status); + + /* Pick the alternative (inactive) context */ + new_ctx = ctx->alt; + + ASSERT(HCRYPT_CTX_S_SARDY <= new_ctx->status); + + /* Keep same KEK, configuration, and salt */ +// memcpy(&new_ctx->aes_kek, &ctx->aes_kek, sizeof(new_ctx->aes_kek)); + memcpy(&new_ctx->cfg, &ctx->cfg, sizeof(new_ctx->cfg)); + + new_ctx->salt_len = ctx->salt_len; + memcpy(new_ctx->salt, ctx->salt, HAICRYPT_SALT_SZ); + + /* Generate new SEK */ + new_ctx->sek_len = new_ctx->cfg.key_len; + + HCRYPT_LOG(LOG_DEBUG, "refresh/generate SEK. salt_len=%d sek_len=%d\n", (int)new_ctx->salt_len, (int)new_ctx->sek_len); + + if (0 > crypto->cryspr->prng(new_ctx->sek, new_ctx->sek_len)) { + HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", new_ctx->sek_len); + return(-1); + } + /* Cryspr's dependent key */ + if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, new_ctx, new_ctx->sek, new_ctx->sek_len)) { + HCRYPT_LOG(LOG_ERR, "refresh cryspr setkey(sek[%d]) failed\n", new_ctx->sek_len); + return(-1); + } + + HCRYPT_PRINTKEY(new_ctx->sek, new_ctx->sek_len, "sek"); + + /* Assemble the new KMmsg with new and current SEK */ + if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, new_ctx, ctx->sek))) { + return(iret); + } + + /* Initialize the message prefix cache */ + new_ctx->msg_info->resetCache(new_ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, new_ctx->flags & HCRYPT_MSG_F_xSEK); + new_ctx->pkt_cnt = 0; + + new_ctx->status = HCRYPT_CTX_S_KEYED; + return(0); +} + +/* + * Prepare context switch + * both odd & even keys announced + */ +int hcryptCtx_Tx_PreSwitch(hcrypt_Session *crypto) +{ + hcrypt_Ctx *ctx = crypto->ctx; + + ASSERT(NULL != ctx); + ASSERT(HCRYPT_CTX_S_ACTIVE == ctx->status); + ASSERT(HCRYPT_CTX_S_KEYED == ctx->alt->status); + + ctx->alt->flags |= HCRYPT_CTX_F_ANNOUNCE; + ctx->alt->flags |= HCRYPT_CTX_F_TTSEND; //Send now + + /* Stop announcing current context if next one contains its key */ + if (hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) { + ctx->flags &= ~HCRYPT_CTX_F_ANNOUNCE; + } + return(0); +} + +int hcryptCtx_Tx_Switch(hcrypt_Session *crypto) +{ + hcrypt_Ctx *ctx = crypto->ctx; + + ASSERT(HCRYPT_CTX_S_KEYED <= ctx->alt->status); + + ctx->status = HCRYPT_CTX_S_DEPRECATED; + ctx->alt->status = HCRYPT_CTX_S_ACTIVE; + + ctx->alt->flags |= HCRYPT_CTX_F_ANNOUNCE; // Already cleared if new KM has both SEK + crypto->ctx = ctx->alt; + return(0); +} + +int hcryptCtx_Tx_PostSwitch(hcrypt_Session *crypto) +{ + hcrypt_Ctx *ctx = crypto->ctx; + hcrypt_Ctx *old_ctx = ctx->alt; + + /* Stop announcing old context (if announced) */ + old_ctx->flags &= ~HCRYPT_CTX_F_ANNOUNCE; + old_ctx->status = HCRYPT_CTX_S_SARDY; + + /* If current context KM announce both, reassemble it */ + if (hcryptMsg_KM_HasBothSek(ctx->KMmsg_cache)) { + hcryptCtx_Tx_AsmKM(crypto, ctx, NULL); + } + return(0); +} + +/* Assemble Keying Material message */ +int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *alt_sek) +{ + unsigned char *km_msg; + size_t msg_len; + int sek_cnt = (NULL == alt_sek ? 1 : 2); + unsigned char sek_buf[HAICRYPT_KEY_MAX_SZ * 2]; + unsigned char *seks; + + if (NULL == ctx) { + HCRYPT_LOG(LOG_ERR, "%s", "crypto context undefined\n"); + return(-1); + } + + msg_len = HCRYPT_MSG_KM_OFS_SALT + + ctx->salt_len + + (ctx->sek_len * sek_cnt) + + HAICRYPT_WRAPKEY_SIGN_SZ; + + km_msg = &ctx->KMmsg_cache[0]; + ctx->KMmsg_len = 0; + + memset(km_msg, 0, msg_len); + ctx->msg_info->resetCache(km_msg, HCRYPT_MSG_PT_KM, + 2 == sek_cnt ? HCRYPT_MSG_F_xSEK : (ctx->flags & HCRYPT_MSG_F_xSEK)); + + /* crypto->KMmsg_cache[4..7]: KEKI=0 */ + km_msg[HCRYPT_MSG_KM_OFS_CIPHER] = HCRYPT_CIPHER_AES_CTR; + km_msg[HCRYPT_MSG_KM_OFS_AUTH] = HCRYPT_AUTH_NONE; + km_msg[HCRYPT_MSG_KM_OFS_SE] = crypto->se; + hcryptMsg_KM_SetSaltLen(km_msg, ctx->salt_len); + hcryptMsg_KM_SetSekLen(km_msg, ctx->sek_len); + + memcpy(&km_msg[HCRYPT_MSG_KM_OFS_SALT], ctx->salt, ctx->salt_len); + + if (2 == sek_cnt) { + /* Even SEK first in dual SEK KMmsg */ + if (HCRYPT_MSG_F_eSEK & ctx->flags) { + memcpy(&sek_buf[0], ctx->sek, ctx->sek_len); + memcpy(&sek_buf[ctx->sek_len], alt_sek, ctx->sek_len); + } else { + memcpy(&sek_buf[0], alt_sek, ctx->sek_len); + memcpy(&sek_buf[ctx->sek_len], ctx->sek, ctx->sek_len); + } + seks = sek_buf; + } else { + seks = ctx->sek; + } + if (0 > crypto->cryspr->km_wrap(crypto->cryspr_cb, + &km_msg[HCRYPT_MSG_KM_OFS_SALT + ctx->salt_len], + seks, sek_cnt * ctx->sek_len)) { + + HCRYPT_LOG(LOG_ERR, "%s", "wrap key failed\n"); + return(-1); + } + ctx->KMmsg_len = msg_len; + return(0); +} + +int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto) +{ + hcrypt_Ctx *ctx = crypto->ctx; + + 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); + + if ((ctx->pkt_cnt > crypto->km.refresh_rate) + || (ctx->pkt_cnt == 0)) { //rolled over + /* + * End of crypto period for current SEK, + * switch to other (even/odd) SEK + */ + HCRYPT_LOG(LOG_INFO, "KM[%d] Activated\n", + (ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2); + + hcryptCtx_Tx_Switch(crypto); + + } else + if ((ctx->pkt_cnt > (crypto->km.refresh_rate - crypto->km.pre_announce)) + && !(ctx->alt->flags & HCRYPT_CTX_F_ANNOUNCE)) { + /* + * End of crypto period approach for this SEK, + * prepare next SEK for announcement + */ + hcryptCtx_Tx_Refresh(crypto); + + HCRYPT_LOG(LOG_INFO, "KM[%d] Pre-announced\n", + (ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2); + + hcryptCtx_Tx_PreSwitch(crypto); + + } else + if ((ctx->alt->status == HCRYPT_CTX_S_DEPRECATED) + && (ctx->pkt_cnt > crypto->km.pre_announce)) { + /* + * Deprecated SEK is no longer needed (for late packets), + * decommission it + */ + HCRYPT_LOG(LOG_INFO, "KM[%d] Deprecated\n", + (ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2); + + hcryptCtx_Tx_PostSwitch(crypto); + } + + /* Check if it is time to send Keying Material */ + if (timerisset(&crypto->km.tx_period)) { /* tx_period=0.0 -> out-of-stream Keying Material distribution */ + struct timeval now, nxt_tx; + + gettimeofday(&now, NULL); + timeradd(&crypto->km.tx_last, &crypto->km.tx_period, &nxt_tx); + if (timercmp(&now, &nxt_tx, >)) { + if (crypto->ctx_pair[0].flags & HCRYPT_CTX_F_ANNOUNCE) crypto->ctx_pair[0].flags |= HCRYPT_CTX_F_TTSEND; + if (crypto->ctx_pair[1].flags & HCRYPT_CTX_F_ANNOUNCE) crypto->ctx_pair[1].flags |= HCRYPT_CTX_F_TTSEND; + } + } + + return(0); +} + +int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto, + void *out_p[], size_t out_len_p[], int maxout ATR_UNUSED) +{ + int i, nbout = 0; + + ASSERT(maxout >= 2); + for (i=0; i<2; i++) { + if (crypto->ctx_pair[i].flags & HCRYPT_CTX_F_TTSEND) { /* Time To Send */ + HCRYPT_LOG(LOG_DEBUG, "Send KMmsg[%d] len=%zd\n", i, + crypto->ctx_pair[i].KMmsg_len); + /* Send Keying Material */ + out_p[nbout] = crypto->ctx_pair[i].KMmsg_cache; + out_len_p[nbout] = crypto->ctx_pair[i].KMmsg_len; + nbout++; + crypto->ctx_pair[i].flags &= ~HCRYPT_CTX_F_TTSEND; + } + } + if (nbout) { + struct timeval now; + gettimeofday(&now, NULL); + crypto->km.tx_last = now; + } + return(nbout); +} + + diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h new file mode 100644 index 000000000..a915c2fa0 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h @@ -0,0 +1,155 @@ +/* + * 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. +*****************************************************************************/ + +#ifndef HCRYPT_MSG_H +#define HCRYPT_MSG_H + +/* + * HaiCrypt Transport Message Header info + */ + + +#ifndef HCRYPT_DSP +#include + +typedef uint32_t hcrypt_Pki; +#endif /* HCRYPT_DSP */ + + +#define HCRYPT_MSG_VERSION 1 /* Current HaiCrypt version */ + +#define HCRYPT_MSG_SIGN (('H'-'@')<<10 | ('A'-'@')<<5 | ('I'-'@')) /* Haivision PnP Mfr ID 'HAI' */ + +#define HCRYPT_PKI_SZ 4 /* Packet Index size (CTR mode cipher) */ + +#define HCRYPT_MSG_PT_MS 1 /* Media stream */ +#define HCRYPT_MSG_PT_KM 2 /* Keying Material */ +#define HCRYPT_MSG_PT_RESV7 7 /* Reserved to dicriminate MPEG-TS packet (SyncByte=0x47) */ + + +#define HCRYPT_MSG_F_eSEK 0x01 /* Even Stream Encrypting Key */ +#define HCRYPT_MSG_F_oSEK 0x02 /* Odd Stream Encrypting Key */ +#define HCRYPT_MSG_F_xSEK 0x03 /* Both Stream Encrypting Keys */ + +typedef struct { + int hdr_len; // data and control common prefix portion + int pfx_len; // Message Prefix len. Also payload offset + unsigned (*getKeyFlags)(unsigned char *msg); + hcrypt_Pki (*getPki)(unsigned char *msg, int nwko); + void (*setPki)(unsigned char *msg, hcrypt_Pki); + void (*resetCache)(unsigned char *pfx_cache, unsigned pkt_type, unsigned flags); + void (*indexMsg)(unsigned char *msg, unsigned char *pfx_cache); + int (*parseMsg)(unsigned char *msg); +}hcrypt_MsgInfo; + + +#define hcryptMsg_GetKeyIndex(mi,msg) ((mi)->getKeyFlags(msg)>>1) +#define hcryptMsg_GetPki(mi,msg,nwko) ((mi)->getPki(msg,nwko)) +#define hcryptMsg_SetPki(mi,msg,pki) (mi)->setPki(msg, pki) + +#define hcryptMsg_HasEvenSek(mi,msg) ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_eSEK) +#define hcryptMsg_HasOddSek(mi,msg) ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_oSEK) +#define hcryptMsg_HasBothSek(mi,msg) (HCRYPT_MSG_F_xSEK == ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_xSEK)) +#define hcryptMsg_HasNoSek(mi,msg) (0 == ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_xSEK)) + +#define hcryptMsg_PaddedLen(len, fact) ((((len)+(fact)-1)/(fact))*(fact)) + + +/* + * HaiCrypt KMmsg (Keying Material): + * + * 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 | KEKI | + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + *+0x08 | Cipher | Auth | SE | Resv1 | + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + *+0x0C | Resv2 | Slen/4 | Klen/4 | + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + *+0x10 | Salt | + * | ... | + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * | Wrap | + * | ... | + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + */ + + + +#define HCRYPT_MSG_KM_OFS_VERSION 0 +#define HCRYPT_MSG_KM_OFS_PT 0 +#define HCRYPT_MSG_KM_OFS_SIGN 1 +#define HCRYPT_MSG_KM_OFS_KFLGS 3 +#define HCRYPT_MSG_KM_RSH_KFLGS 0 /* Right shift (in byte) */ + +#define HCRYPT_MSG_KM_OFS_KEKI 4 +#define HCRYPT_MSG_KM_OFS_CIPHER 8 +#define HCRYPT_MSG_KM_OFS_AUTH 9 +#define HCRYPT_MSG_KM_OFS_SE 10 +#define HCRYPT_MSG_KM_OFS_RESV2 12 +#define HCRYPT_MSG_KM_OFS_SLEN 14 +#define HCRYPT_MSG_KM_OFS_KLEN 15 +#define HCRYPT_MSG_KM_OFS_SALT 16 + +#define HCRYPT_MSG_KM_MAX_SZ (0 \ + + HCRYPT_MSG_KM_OFS_SALT \ + + HAICRYPT_SALT_SZ \ + + (HAICRYPT_KEY_MAX_SZ * 2) \ + + HAICRYPT_WRAPKEY_SIGN_SZ) + +#define HCRYPT_CIPHER_NONE 0 +#define HCRYPT_CIPHER_AES_ECB 1 +#define HCRYPT_CIPHER_AES_CTR 2 +#define HCRYPT_CIPHER_AES_CBC 3 + +#define HCRYPT_AUTH_NONE 0 + +#define HCRYPT_SE_TSUDP 1 + hcrypt_MsgInfo * hcryptMsg_STA_MsgInfo(void); +#define HCRYPT_SE_TSSRT 2 + hcrypt_MsgInfo * hcryptMsg_SRT_MsgInfo(void); + +#define hcryptMsg_KM_GetVersion(msg) (((msg)[HCRYPT_MSG_KM_OFS_VERSION]>>4)& 0xF) +#define hcryptMsg_KM_GetPktType(msg) (((msg)[HCRYPT_MSG_KM_OFS_PT]) & 0xF) +#define hcryptMsg_KM_GetSign(msg) (((msg)[HCRYPT_MSG_KM_OFS_SIGN]<<8) | (msg)[HCRYPT_MSG_KM_OFS_SIGN+1]) + +#define hcryptMsg_KM_GetKeyIndex(msg) (((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_xSEK)>>1) + +#define hcryptMsg_KM_HasEvenSek(msg) ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_eSEK) +#define hcryptMsg_KM_HasOddSek(msg) ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_oSEK) +#define hcryptMsg_KM_HasBothSek(msg) (HCRYPT_MSG_F_xSEK == ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_xSEK)) +#define hcryptMsg_KM_HasNoSek(msg) (0 == ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_xSEK)) + +#define hcryptMsg_KM_GetCipher(msg) ((msg)[HCRYPT_MSG_KM_OFS_CIPHER]) +#define hcryptMsg_KM_GetAuth(msg) ((msg)[HCRYPT_MSG_KM_OFS_AUTH]) +#define hcryptMsg_KM_GetSE(msg) ((msg)[HCRYPT_MSG_KM_OFS_SE]) + +#define hcryptMsg_KM_GetSaltLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_SLEN] * 4) +#define hcryptMsg_KM_GetSekLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_KLEN] * 4) + +#define hcryptMsg_KM_SetSaltLen(msg,len)do {(msg)[HCRYPT_MSG_KM_OFS_SLEN] = (len)/4;} while(0) +#define hcryptMsg_KM_SetSekLen(msg,len) do {(msg)[HCRYPT_MSG_KM_OFS_KLEN] = (len)/4;} while(0) + + +#endif /* HCRYPT_MSG_H */ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c new file mode 100644 index 000000000..e77c73b35 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c @@ -0,0 +1,146 @@ +/* + * 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 /* NULL */ +#include /* memcmp */ +#include "hcrypt.h" + +int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc, + unsigned char *in_pfx, unsigned char *data, size_t data_len) +{ + hcrypt_Session *crypto = (hcrypt_Session *)hhc; + hcrypt_Ctx *ctx; + int nb = -1; + + if ((NULL == crypto) + || (NULL == data)) { + HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n"); + return(nb); + } + + ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_pfx)]; + + ASSERT(NULL != ctx); /* Header check should prevent this error */ + ASSERT(NULL != crypto->cryspr); /* Header check should prevent this error */ + + crypto->ctx = ctx; /* Context of last received msg */ + if (NULL == crypto->cryspr->ms_decrypt) { + HCRYPT_LOG(LOG_ERR, "%s", "cryspr had no decryptor\n"); + } else if (ctx->status >= HCRYPT_CTX_S_KEYED) { + hcrypt_DataDesc indata; + indata.pfx = in_pfx; + indata.payload = data; + indata.len = data_len; + + if (0 > (nb = crypto->cryspr->ms_decrypt(crypto->cryspr_cb, ctx, &indata, 1, NULL, NULL, NULL))) { + HCRYPT_LOG(LOG_ERR, "%s", "ms_decrypt failed\n"); + } else { + nb = indata.len; + } + } else { /* No key received yet */ + nb = 0; + } + return(nb); +} + +int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc, + unsigned char *in_msg, size_t in_len, + void *out_p[], size_t out_len_p[], int maxout) +{ + hcrypt_Session *crypto = (hcrypt_Session *)hhc; + hcrypt_Ctx *ctx; + int nbout = maxout; + int msg_type; + + if ((NULL == crypto) + || (NULL == in_msg)) { + + HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n"); + return(-1); + } + + /* Validate HaiCrypt message */ + if (0 > (msg_type = crypto->msg_info->parseMsg(in_msg))) { + return(-1); + } + + switch(msg_type) { + case HCRYPT_MSG_PT_MS: /* MSmsg */ + ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_msg)]; + + if ((NULL == out_p) + || (NULL == out_len_p)) { + HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n"); + return(-1); + } + ASSERT(NULL != ctx); /* Header check should prevent this error */ + ASSERT(NULL != crypto->cryspr); /* Header check should prevent this error */ + + crypto->ctx = ctx; /* Context of last received msg */ + if (NULL == crypto->cryspr->ms_decrypt) { + HCRYPT_LOG(LOG_ERR, "%s", "cryspr had no decryptor\n"); + nbout = -1; + } else if (ctx->status >= HCRYPT_CTX_S_KEYED) { + hcrypt_DataDesc indata; + indata.pfx = in_msg; + indata.payload = &in_msg[crypto->msg_info->pfx_len]; + indata.len = in_len - crypto->msg_info->pfx_len; + + if (crypto->cryspr->ms_decrypt(crypto->cryspr_cb, ctx, &indata, 1, out_p, out_len_p, &nbout)) { + HCRYPT_LOG(LOG_ERR, "%s", "ms_decrypt failed\n"); + nbout = -1; + } + } else { /* No key received yet */ + nbout = 0; + } + break; + + case HCRYPT_MSG_PT_KM: /* KMmsg */ + /* Even or Both SEKs check with even context */ + ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_msg)]; + + ASSERT(NULL != ctx); /* Header check should prevent this error */ + + if ((ctx->status < HCRYPT_CTX_S_KEYED) /* No key deciphered yet */ + || (in_len != ctx->KMmsg_len) /* or not same size */ + || (0 != memcmp(ctx->KMmsg_cache, in_msg, in_len))) { /* or different */ + + nbout = hcryptCtx_Rx_ParseKM(crypto, in_msg, in_len); + //-2: unmatched shared secret + //-1: other failures + //0: success + } else { + nbout = 0; + } + if (NULL != out_p) out_p[0] = NULL; + if (NULL != out_len_p) out_len_p[0] = 0; + break; + + default: + HCRYPT_LOG(LOG_WARNING, "%s", "unknown packet type\n"); + nbout = 0; + break; + } + + return(nbout); +} + + diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_sa.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_sa.c new file mode 100644 index 000000000..995334d81 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_sa.c @@ -0,0 +1,96 @@ +/* + * 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. +*****************************************************************************/ + +/* + * For now: + * Pre-shared or password derived KEK (Key Encrypting Key) + * Future: + * Certificate-based association + */ +#include /* memcpy */ +#include "hcrypt.h" + +int hcryptCtx_SetSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Secret *secret) +{ + int iret; + (void)crypto; + + switch(secret->typ) { + case HAICRYPT_SECTYP_PRESHARED: + ASSERT(secret->len <= HAICRYPT_KEY_MAX_SZ); + 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), + secret->str, secret->len))) { + HCRYPT_LOG(LOG_ERR, "km_setkey(pdkek[%zd]) failed (rc=%d)\n", secret->len, iret); + return(-1); + } + ctx->status = HCRYPT_CTX_S_SARDY; + break; + + case HAICRYPT_SECTYP_PASSPHRASE: + ASSERT(secret->len <= sizeof(ctx->cfg.pwd)); + memcpy(ctx->cfg.pwd, secret->str, secret->len); + ctx->cfg.pwd_len = secret->len; + /* KEK will be derived from password with Salt */ + ctx->status = HCRYPT_CTX_S_SARDY; + break; + + default: + HCRYPT_LOG(LOG_ERR, "Unknown secret type %d\n", + secret->typ); + return(-1); + } + return(0); +} + +int hcryptCtx_GenSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx) +{ + /* + * KEK need same length as the key it protects (SEK) + * KEK = PBKDF2(Pwd, LSB(64, Salt), Iter, Klen) + */ + unsigned char kek[HAICRYPT_KEY_MAX_SZ]; + size_t kek_len = ctx->sek_len; + size_t pbkdf_salt_len = (ctx->salt_len >= HAICRYPT_PBKDF2_SALT_LEN + ? HAICRYPT_PBKDF2_SALT_LEN + : ctx->salt_len); + int iret = 0; + (void)crypto; + + iret = crypto->cryspr->km_pbkdf2(crypto->cryspr_cb, ctx->cfg.pwd, ctx->cfg.pwd_len, + &ctx->salt[ctx->salt_len - pbkdf_salt_len], pbkdf_salt_len, + HAICRYPT_PBKDF2_ITER_CNT, kek_len, kek); + + if(iret) { + HCRYPT_LOG(LOG_ERR, "km_pbkdf2() failed (rc=%d)\n", iret); + return(-1); + } + HCRYPT_PRINTKEY(ctx->cfg.pwd, ctx->cfg.pwd_len, "pwd"); + 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))) { + HCRYPT_LOG(LOG_ERR, "km_setkey(pdkek[%zd]) failed (rc=%d)\n", kek_len, iret); + return(-1); + } + return(0); +} + diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c new file mode 100644 index 000000000..d1ceab26c --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c @@ -0,0 +1,174 @@ +/* + * 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 +#include /* NULL */ +#include /* memcpy */ +#ifdef _WIN32 + #include + #include + #include +#else + #include /* htonl */ +#endif +#include "hcrypt.h" + +int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_pp) +{ + hcrypt_Session *crypto = (hcrypt_Session *)hhc; + + ASSERT(NULL != crypto); + ASSERT(NULL != crypto->cryspr); + + int pad_factor = (HCRYPT_CTX_MODE_AESECB == crypto->ctx->mode ? 128/8 : 1); + +#ifndef _WIN32 + ASSERT(crypto->inbuf != NULL); +#endif + size_t in_len = crypto->msg_info->pfx_len + hcryptMsg_PaddedLen(data_len, pad_factor); + *in_pp = crypto->inbuf; + if (in_len > crypto->inbuf_siz) { + *in_pp = NULL; + return(-1); + } + return(crypto->msg_info->pfx_len); +} + +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; + int nbout = 0; + + if ((NULL == crypto) + || (NULL == (ctx = crypto->ctx)) + || (NULL == out_p) + || (NULL == out_len_p)) { + HCRYPT_LOG(LOG_ERR, "ManageKeys: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx); + return(-1); + } + + /* Manage Key Material (refresh, announce, decommission) */ + hcryptCtx_Tx_ManageKM(crypto); + + if (NULL == (ctx = crypto->ctx)) { + HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n"); + return(-1); + } + ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE); + + nbout = hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout); + return(nbout); +} + +int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc) +{ + hcrypt_Session *crypto = (hcrypt_Session *)hhc; + hcrypt_Ctx *ctx = NULL; + + if ((NULL == crypto) + || (NULL == (ctx = crypto->ctx))){ + HCRYPT_LOG(LOG_ERR, "GetKeyFlags: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx); + return(-1); + } + return(hcryptCtx_GetKeyFlags(ctx)); +} + +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; + int nbout = 0; + + if ((NULL == crypto) + || (NULL == (ctx = crypto->ctx))){ + HCRYPT_LOG(LOG_ERR, "Tx_Data: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx); + return(-1); + } + /* Get/Set packet index */ + ctx->msg_info->indexMsg(in_pfx, ctx->MSpfx_cache); + + /* Encrypt */ + { + hcrypt_DataDesc indata; + indata.pfx = in_pfx; + indata.payload = in_data; + indata.len = in_len; + + if (0 > (nbout = crypto->cryspr->ms_encrypt(crypto->cryspr_cb, ctx, &indata, 1, NULL, NULL, NULL))) { + HCRYPT_LOG(LOG_ERR, "%s", "ms_encrypt failed\n"); + return(nbout); + } + } + ctx->pkt_cnt++; + + return(nbout); +} + +int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc, + unsigned char *in_msg, size_t in_len, + void *out_p[], size_t out_len_p[], int maxout) +{ + hcrypt_Session *crypto = (hcrypt_Session *)hhc; + hcrypt_Ctx *ctx = NULL; + int nb, nbout = 0; + + if ((NULL == crypto) + || (NULL == (ctx = crypto->ctx)) + || (NULL == out_p) + || (NULL == out_len_p)) { + HCRYPT_LOG(LOG_ERR, "Tx_Process: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx); + return(-1); + } + + /* Manage Key Material (refresh, announce, decommission) */ + hcryptCtx_Tx_ManageKM(crypto); + + if (NULL == (ctx = crypto->ctx)) { + HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n"); + return(-1); + } + ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE); + + nbout += hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout); + + /* Get packet index */ + ctx->msg_info->indexMsg(in_msg, ctx->MSpfx_cache); + + /* Encrypt */ + nb = maxout - nbout; + { + hcrypt_DataDesc indata; + indata.pfx = in_msg; + indata.payload = &in_msg[ctx->msg_info->pfx_len]; + indata.len = in_len - ctx->msg_info->pfx_len; + + if (crypto->cryspr->ms_encrypt(crypto->cryspr_cb, ctx, &indata, 1, &out_p[nbout], &out_len_p[nbout], &nb)) { + HCRYPT_LOG(LOG_ERR, "%s", "ms_encrypt failed\n"); + return(nbout); + } + } + nbout += nb; + ctx->pkt_cnt++; + + return(nbout); +} diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ut.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ut.c new file mode 100644 index 000000000..b07ca09f6 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ut.c @@ -0,0 +1,224 @@ +/* + * 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-07-11 (jdube) + HaiCrypt initial implementation. +*****************************************************************************/ + +#include /* memcpy */ +#include +#include +#include "hcrypt.h" + +#ifndef _WIN32 + +/* RFC6070 PBKDF2 Tests Vectors */ + +static struct TestVector { + size_t pwd_len; + const char *pwd; + size_t salt_len; + const unsigned char *salt; + int cnt; + size_t dk_len; + unsigned char dk[32]; +} tv[] = { + { /* 1 */ + .pwd_len = 8, .pwd = "password", + .salt_len = 4, .salt = (unsigned char *)"salt", + .cnt = 1, + .dk_len = 20, + .dk = { + 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, + 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, + 0x2f, 0xe0, 0x37, 0xa6 + } + }, + { /* 2 */ + .pwd_len = 8, .pwd = "password", + .salt_len = 4, .salt = (unsigned char *)"salt", + .cnt = 2, + .dk_len = 20, + .dk = { + 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, + 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, + 0xd8, 0xde, 0x89, 0x57 + } + }, + { /* 3 */ + .pwd_len = 8, .pwd = "password", + .salt_len = 4, .salt = (unsigned char *)"salt", + .cnt = 4096, + .dk_len = 20, + .dk = { + 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, + 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, + 0x65, 0xa4, 0x29, 0xc1 + } + }, + { /* 4 */ + .pwd_len = 8, .pwd = "password", + .salt_len = 4, .salt = (unsigned char *)"salt", + .cnt = 16777216, + .dk_len = 20, + .dk = { + 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4, + 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c, + 0x26, 0x34, 0xe9, 0x84 + } + }, + { /* 5 */ + .pwd_len = 24, .pwd = "passwordPASSWORDpassword", + .salt_len = 36, .salt = (unsigned char *)"saltSALTsaltSALTsaltSALTsaltSALTsalt", + .cnt = 4096, + .dk_len = 25, + .dk = { + 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, + 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, + 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, + 0x38 + } + }, + { /* 6 */ + .pwd_len = 9, .pwd = "pass\0word", + .salt_len = 5, .salt = (unsigned char *)"sa\0lt", + .cnt = 4096, + .dk_len = 16, + .dk = { + 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, + 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3 + } + }, +}; + +#include + +static int hc_ut_pbkdf2(unsigned verbose) +{ + int i; + int nbt = sizeof(tv)/sizeof(tv[0]); + int nbe = 0; + unsigned char dk[32]; + struct timeval tstart, tstop, tdiff; + + for (i=0; i HaiCrypt_Tx_Data(hcrypto, &pkt[0], &pkt[16], UT_PKTSZ)) nbe++; + if (0 == (i % 1000)) { + printf("\b\b\b\b\b\b%6d", i); + fflush(stdout); + } + } + gettimeofday(&tstop, NULL); + timersub(&tstop, &tstart, &tdiff); + printf("\nhaicrypt: encrypted %ld packets in %lu.%06lu sec (%ld.%03ld kbps)\n", + UT_NBPKTS, tdiff.tv_sec, (unsigned long)tdiff.tv_usec, + (((UT_NBPKTS * UT_PKTSZ*10)/((tdiff.tv_sec*10) + (tdiff.tv_usec/100))) / 1000), + (((UT_NBPKTS * UT_PKTSZ*10)/((tdiff.tv_sec*10) + (tdiff.tv_usec/100))) % 1000)); + + HaiCrypt_Close(hcrypto); + return(nbe); +} + +int main(int argc, char *argv[]) +{ + + int nbe = 0; + (void)argc; + (void)argv; + nbe += hc_ut_encrypt_ctr_speed(); + nbe += hc_ut_pbkdf2(1); + + printf("haicrypt unit test %s: %d errors found\n", nbe ? "failed" : "passed", nbe); + return(nbe); +} + +#endif // _WIN32 diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_xpt_srt.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_xpt_srt.c new file mode 100644 index 000000000..2a2f5a3cd --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_xpt_srt.c @@ -0,0 +1,171 @@ +/* + * 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. + + 2014-03-11 (jdube) + Adaptation for SRT. +*****************************************************************************/ + +#include /* memset, memcpy */ +#ifdef _WIN32 + #include + #include +#else + #include /* htonl, ntohl */ +#endif +#include "hcrypt.h" + +/* + * HaiCrypt SRT (Secure Reliable Transport) Media Stream (MS) Msg Prefix: + * This is UDT data header with Crypto Key Flags (KF) added. + * Header is in 32bit host order words in the context of the functions of this handler. + * + * 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| Packet Sequence Number (pki) | + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * 0x04 |FF |o|KF | Message Number | + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * 0x08 | Time Stamp | + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * 0x0C | Destination Socket ID) | + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * | Payload... | + */ + + +/* + * HaiCrypt Standalone Transport Keying Material (KM) Msg header kept in SRT + * Message and 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_SRT_HDR_SZ 16 +#define HCRYPT_MSG_SRT_PFX_SZ 16 + +#define HCRYPT_MSG_SRT_OFS_PKI 0 +#define HCRYPT_MSG_SRT_OFS_MSGNO 4 +#define HCRYPT_MSG_SRT_SHF_KFLGS 27 //shift + +static hcrypt_MsgInfo _hcMsg_SRT_MsgInfo; + +static unsigned hcryptMsg_SRT_GetKeyFlags(unsigned char *msg) +{ + uint32_t msgno; + memcpy(&msgno, &msg[HCRYPT_MSG_SRT_OFS_MSGNO], sizeof(msgno)); //header is in host order + return((unsigned)((msgno >> HCRYPT_MSG_SRT_SHF_KFLGS) & HCRYPT_MSG_F_xSEK)); +} + +static hcrypt_Pki hcryptMsg_SRT_GetPki(unsigned char *msg, int nwkorder) +{ + hcrypt_Pki pki; + memcpy(&pki, &msg[HCRYPT_MSG_SRT_OFS_PKI], sizeof(pki)); //header is in host order + return (nwkorder ? htonl(pki) : pki); +} + +static void hcryptMsg_SRT_SetPki(unsigned char *msg, hcrypt_Pki pki) +{ + memcpy(&msg[HCRYPT_MSG_SRT_OFS_PKI], &pki, sizeof(pki)); //header is in host order +} + +static void hcryptMsg_SRT_ResetCache(unsigned char *pfx_cache, unsigned pkt_type, unsigned kflgs) +{ + switch(pkt_type) { + case HCRYPT_MSG_PT_MS: /* Media Stream */ + /* Nothing to do, header filled by protocol */ + break; + case HCRYPT_MSG_PT_KM: /* Keying Material */ + pfx_cache[HCRYPT_MSG_KM_OFS_VERSION] = (unsigned char)((HCRYPT_MSG_VERSION << 4) | pkt_type); // version || PT + pfx_cache[HCRYPT_MSG_KM_OFS_SIGN] = (unsigned char)((HCRYPT_MSG_SIGN >> 8) & 0xFF); // Haivision PnP Mfr ID + pfx_cache[HCRYPT_MSG_KM_OFS_SIGN+1] = (unsigned char)(HCRYPT_MSG_SIGN & 0xFF); + pfx_cache[HCRYPT_MSG_KM_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx + break; + default: + break; + } +} + +static void hcryptMsg_SRT_IndexMsg(unsigned char *msg, unsigned char *pfx_cache) +{ + (void)msg; + (void)pfx_cache; + return; //nothing to do, header and index maintained by SRT +} + +static int hcryptMsg_SRT_ParseMsg(unsigned char *msg) +{ + int rc; + + if ((HCRYPT_MSG_VERSION == hcryptMsg_KM_GetVersion(msg)) /* Version 1 */ + && (HCRYPT_MSG_PT_KM == hcryptMsg_KM_GetPktType(msg)) /* Keying Material */ + && (HCRYPT_MSG_SIGN == hcryptMsg_KM_GetSign(msg))) { /* 'HAI' PnP Mfr ID */ + rc = HCRYPT_MSG_PT_KM; + } else { + //Assume it's data. + //SRT does not call this for MS msg + rc = HCRYPT_MSG_PT_MS; + } + + switch(rc) { + case HCRYPT_MSG_PT_MS: + if (hcryptMsg_HasNoSek(&_hcMsg_SRT_MsgInfo, msg) + || hcryptMsg_HasBothSek(&_hcMsg_SRT_MsgInfo, msg)) { + HCRYPT_LOG(LOG_ERR, "invalid MS msg flgs: %02x\n", + hcryptMsg_GetKeyIndex(&_hcMsg_SRT_MsgInfo, msg)); + return(-1); + } + break; + case HCRYPT_MSG_PT_KM: + if (HCRYPT_SE_TSSRT != hcryptMsg_KM_GetSE(msg)) { //Check Stream Encapsulation (SE) + HCRYPT_LOG(LOG_ERR, "invalid KM msg SE: %d\n", + hcryptMsg_KM_GetSE(msg)); + return(-1); + } + 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_SRT_MsgInfo; + +hcrypt_MsgInfo *hcryptMsg_SRT_MsgInfo(void) +{ + _hcMsg_SRT_MsgInfo.hdr_len = HCRYPT_MSG_SRT_HDR_SZ; + _hcMsg_SRT_MsgInfo.pfx_len = HCRYPT_MSG_SRT_PFX_SZ; + _hcMsg_SRT_MsgInfo.getKeyFlags = hcryptMsg_SRT_GetKeyFlags; + _hcMsg_SRT_MsgInfo.getPki = hcryptMsg_SRT_GetPki; + _hcMsg_SRT_MsgInfo.setPki = hcryptMsg_SRT_SetPki; + _hcMsg_SRT_MsgInfo.resetCache = hcryptMsg_SRT_ResetCache; + _hcMsg_SRT_MsgInfo.indexMsg = hcryptMsg_SRT_IndexMsg; + _hcMsg_SRT_MsgInfo.parseMsg = hcryptMsg_SRT_ParseMsg; + + return(&_hcMsg_SRT_MsgInfo); +} + diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_xpt_sta.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_xpt_sta.c new file mode 100644 index 000000000..cc2651be0 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_xpt_sta.c @@ -0,0 +1,180 @@ +/* + * 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 /* memset, memcpy */ +#include /* time() */ +#ifdef _WIN32 + #include + #include +#else + #include /* 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); +} + diff --git a/trunk/3rdparty/srt-1-fit/nuget.config b/trunk/3rdparty/srt-1-fit/nuget.config new file mode 100644 index 000000000..9178fe083 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/nuget.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/3rdparty/srt-1-fit/scripts/FindMbedTLS.cmake b/trunk/3rdparty/srt-1-fit/scripts/FindMbedTLS.cmake new file mode 100644 index 000000000..7dca6e47e --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/FindMbedTLS.cmake @@ -0,0 +1,115 @@ +# original file from obs-studio: +# https://github.com/obsproject/obs-studio +# /cmake/Modules/FindMbedTLS.cmake +# +# Once done these will be defined: +# +# LIBMBEDTLS_FOUND +# LIBMBEDTLS_INCLUDE_DIRS +# LIBMBEDTLS_LIBRARIES +# +# For use in OBS: +# +# MBEDTLS_INCLUDE_DIR + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_check_modules(_MBEDTLS QUIET mbedtls) +endif() + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_lib_suffix 64) +else() + set(_lib_suffix 32) +endif() + +# If we're on MacOS or Linux, please try to statically-link mbedtls. +if(STATIC_MBEDTLS AND (APPLE OR UNIX)) + set(_MBEDTLS_LIBRARIES libmbedtls.a) + set(_MBEDCRYPTO_LIBRARIES libmbedcrypto.a) + set(_MBEDX509_LIBRARIES libmbedx509.a) +endif() + +find_path(MBEDTLS_INCLUDE_DIR + NAMES mbedtls/ssl.h + HINTS + ${MBEDTLS_PREFIX} + PATHS + /usr/include /usr/local/include /opt/local/include /sw/include + PATH_SUFFIXES + include) + +find_library(MBEDTLS_LIB + NAMES ${_MBEDTLS_LIBRARIES} mbedtls libmbedtls + HINTS + ${MBEDTLS_PREFIX} + PATHS + /usr/lib /usr/local/lib /opt/local/lib /sw/lib + PATH_SUFFIXES + lib${_lib_suffix} lib + libs${_lib_suffix} libs + bin${_lib_suffix} bin + ../lib${_lib_suffix} ../lib + ../libs${_lib_suffix} ../libs + ../bin${_lib_suffix} ../bin) + +find_library(MBEDCRYPTO_LIB + NAMES ${_MBEDCRYPTO_LIBRARIES} mbedcrypto libmbedcrypto + HINTS + ${MBEDTLS_PREFIX} + PATHS + /usr/lib /usr/local/lib /opt/local/lib /sw/lib + PATH_SUFFIXES + lib${_lib_suffix} lib + libs${_lib_suffix} libs + bin${_lib_suffix} bin + ../lib${_lib_suffix} ../lib + ../libs${_lib_suffix} ../libs + ../bin${_lib_suffix} ../bin) + +find_library(MBEDX509_LIB + NAMES ${_MBEDX509_LIBRARIES} mbedx509 libmbedx509 + HINTS + ${MBEDTLS_PREFIX} + PATHS + /usr/lib /usr/local/lib /opt/local/lib /sw/lib + PATH_SUFFIXES + lib${_lib_suffix} lib + libs${_lib_suffix} libs + bin${_lib_suffix} bin + ../lib${_lib_suffix} ../lib + ../libs${_lib_suffix} ../libs + ../bin${_lib_suffix} ../bin) + +# Sometimes mbedtls is split between three libs, and sometimes it isn't. +# If it isn't, let's check if the symbols we need are all in MBEDTLS_LIB. +if(MBEDTLS_LIB AND NOT MBEDCRYPTO_LIB AND NOT MBEDX509_LIB) + set(CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_LIB}) + set(CMAKE_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR}) + check_symbol_exists(mbedtls_x509_crt_init "mbedtls/x509_crt.h" MBEDTLS_INCLUDES_X509) + check_symbol_exists(mbedtls_sha256_init "mbedtls/sha256.h" MBEDTLS_INCLUDES_CRYPTO) + unset(CMAKE_REQUIRED_INCLUDES) + unset(CMAKE_REQUIRED_LIBRARIES) +endif() + +# If we find all three libraries, then go ahead. +if(MBEDTLS_LIB AND MBEDCRYPTO_LIB AND MBEDX509_LIB) + set(LIBMBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set(LIBMBEDTLS_LIBRARIES ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB} ${MBEDX509_LIB}) + set(MBEDTLS_INCLUDE_DIRS ${LIBMBEDTLS_INCLUDE_DIRS}) + set(MBEDTLS_LIBRARIES ${LIBMBEDTLS_LIBRARIES}) + +# Otherwise, if we find MBEDTLS_LIB, and it has both CRYPTO and x509 +# within the single lib (i.e. a windows build environment), then also +# feel free to go ahead. +elseif(MBEDTLS_LIB AND MBEDTLS_INCLUDES_CRYPTO AND MBEDTLS_INCLUDES_X509) + set(LIBMBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set(LIBMBEDTLS_LIBRARIES ${MBEDTLS_LIB}) + set(MBEDTLS_INCLUDE_DIRS ${LIBMBEDTLS_INCLUDE_DIRS}) + set(MBEDTLS_LIBRARIES ${LIBMBEDTLS_LIBRARIES}) +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) +mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) diff --git a/trunk/3rdparty/srt-1-fit/scripts/check-deps b/trunk/3rdparty/srt-1-fit/scripts/check-deps new file mode 100755 index 000000000..4e5f3d3b1 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/check-deps @@ -0,0 +1,56 @@ +#!/bin/bash +# Now Check if Tcl is installed, run it if so. The backslash extends the comment and hides the line below against Tcl interpreter \ +exec tclsh "$0" "$@" || echo "Please install 'tcl' package first - it's required to run any other scripts here." && exit 1 + +# +# 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/. +# + +if { [catch {package require Tcl 8.5}] } { + puts stderr "Tcl version at least 8.5 required, please upgrade" + exit 1 +} + +set ok 1 + +if { [catch {exec pkg-config --exists openssl}] } { + set ok 0 + puts "Openssl: NOT INSTALLED, please install (libssl-dev\[el\], openssl-dev\[el\] etc.)" +} else { + puts "Openssl: found version [exec pkg-config --modversion openssl] -- ok" +} + + +set nothave [catch {set cmake [exec cmake --version]}] + +if { $nothave } { + puts "CMake version >= 2.8 required - please install cmake" + set ok 0 +} else { + set cmakel1 [lindex [split $cmake \n] 0] + set cv [lindex $cmakel1 end] + if { [package vcompare $cv 2.8] == -1 } { + puts "CMake version >= 2.8 required - please upgrade cmake" + set ok 0 + } else { + puts "Cmake version $cv -- ok." + } +} + + +# May others also apply + +if { $ok } { + puts "All dependencies satisfied, you should be good to go." + exit 0 +} + +puts "Please fix the above findings before compiling" +exit 1 + + diff --git a/trunk/3rdparty/srt-1-fit/scripts/gather-package.bat b/trunk/3rdparty/srt-1-fit/scripts/gather-package.bat new file mode 100644 index 000000000..e56a7bf3e --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/gather-package.bat @@ -0,0 +1,35 @@ +rem Create empty directories for package bundle +@echo off + +IF "%PLATFORM%"=="x86" ( + SET FOLDER_PLATFORM="32" +) ELSE IF "%PLATFORM%"=="x64" ( + SET FOLDER_PLATFORM="64" +) ELSE ( + echo "Platform %PLATFORM% is not supported" + exit 1 +) + +md %APPVEYOR_BUILD_FOLDER%\package +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% + +rem Gather SRT includes, binaries and libs +copy %APPVEYOR_BUILD_FOLDER%\version.h %APPVEYOR_BUILD_FOLDER%\package\include\ +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\ +) + +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% +exit 0 diff --git a/trunk/3rdparty/srt-1-fit/scripts/generate-configure-options.tcl b/trunk/3rdparty/srt-1-fit/scripts/generate-configure-options.tcl new file mode 100755 index 000000000..647a06772 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/generate-configure-options.tcl @@ -0,0 +1,115 @@ +#!/usr/bin/tclsh + +set cachefile [lindex $argv 0] + +if { $cachefile == "" } { + puts stderr "Usage: [file tail $argv0] " + exit 1 +} + +set struct { + name + type + value + description +} + +set fd [open $cachefile r] + +set cached "" + +set dbase "" + +while {[gets $fd line] != -1 } { + set line [string trim $line] + + # Hash comment + if { [string index $line 0] == "#" } { + continue + } + + # empty line + if { $line == "" } { + set cached "" + continue + } + + if { [string range $line 0 1] == "//" } { + set linepart [string range $line 2 end] + # Variable description. Add to cache. + if { $cached != "" && [string index $cached end] != " " && [string index $linepart 0] != " " } { + append cached " " + } + append cached $linepart + } + + # Possibly a variable + if [string is alpha [string index $line 0]] { + # Note: this skips variables starting grom underscore. + + if { [string range $line 0 5] == "CMAKE_" } { + # Skip variables with CMAKE_ prefix, they are internal. + continue + } + + lassign [split $line =] vartype value + lassign [split $vartype :] var type + + # Store the variable now + set storage [list $var $type $value $cached] + set cached "" + lappend dbase $storage + + continue + } + + #puts stderr "Ignored line: $line" + + # Ignored. +} + +# Now look over the stored variables + +set lenlimit 80 + +foreach stor $dbase { + + lassign $stor {*}$struct + + if { [string length $description] > $lenlimit } { + set description [string range $description 0 $lenlimit-2]... + } + + if { $type in {STATIC INTERNAL} } { + continue + } + + # Check special case of CXX to turn back to c++. + set pos [string first CXX $name] + if { $pos != -1 } { + # Check around, actually after XX should be no letter. + if { $pos+3 >= [string length $name] || ![string is alpha [string index $name $pos+3]] } { + set name [string replace $name $pos $pos+2 C++] + } + } + + set optname [string tolower [string map {_ -} $name]] + + # Variables of type bool are just empty. + # Variables of other types must have = added. + # Lowercase cmake type will be used here. + set optassign "" + set def "" + if { $type != "BOOL" } { + set optassign "=<[string tolower $type]>" + } else { + # Supply default for boolean option + set def " (default: $value)" + } + + puts " $optname$optassign \"$description$def\"" +} + + + + diff --git a/trunk/3rdparty/srt-1-fit/scripts/googletest-download.cmake b/trunk/3rdparty/srt-1-fit/scripts/googletest-download.cmake new file mode 100644 index 000000000..55f2c5a55 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/googletest-download.cmake @@ -0,0 +1,19 @@ +# code copied from https://crascit.com/2015/07/25/cmake-gtest/ +cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) + +project(googletest-download NONE) + +include(ExternalProject) + +ExternalProject_Add( + googletest + SOURCE_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-src" + BINARY_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-build" + GIT_REPOSITORY + https://github.com/google/googletest.git + GIT_TAG release-1.8.1 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/trunk/3rdparty/srt-1-fit/scripts/googletest.cmake b/trunk/3rdparty/srt-1-fit/scripts/googletest.cmake new file mode 100644 index 000000000..57e5c937a --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/googletest.cmake @@ -0,0 +1,32 @@ +# the following code to fetch googletest +# is inspired by and adapted after https://crascit.com/2015/07/25/cmake-gtest/ +# download and unpack googletest at configure time + +macro(fetch_googletest _download_module_path _download_root) + set(GOOGLETEST_DOWNLOAD_ROOT ${_download_root}) + configure_file( + ${_download_module_path}/googletest-download.cmake + ${_download_root}/CMakeLists.txt + @ONLY + ) + unset(GOOGLETEST_DOWNLOAD_ROOT) + + execute_process( + COMMAND + "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" . + WORKING_DIRECTORY + ${_download_root} + ) + execute_process( + COMMAND + "${CMAKE_COMMAND}" --build . + WORKING_DIRECTORY + ${_download_root} + ) + + # adds the targers: gtest, gtest_main, gmock, gmock_main + add_subdirectory( + ${_download_root}/googletest-src + ${_download_root}/googletest-build + ) +endmacro() diff --git a/trunk/3rdparty/srt-1-fit/scripts/haiUtil.cmake b/trunk/3rdparty/srt-1-fit/scripts/haiUtil.cmake new file mode 100644 index 000000000..b05a2b685 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/haiUtil.cmake @@ -0,0 +1,291 @@ +# +# 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/. +# + +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) +endfunction() + +# Splits a version formed as "major.minor.patch" recorded in variable 'prefix' +# and writes it into variables started with 'prefix' and ended with _MAJOR, _MINOR and _PATCH. +MACRO(set_version_variables prefix value) + string(REPLACE "." ";" VERSION_LIST ${value}) + list(GET VERSION_LIST 0 ${prefix}_MAJOR) + list(GET VERSION_LIST 1 ${prefix}_MINOR) + list(GET VERSION_LIST 2 ${prefix}_PATCH) + set(${prefix}_DEFINESTR "") +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}) +ENDMACRO(set_if) + +FUNCTION(join_arguments outvar) + set (output) + + foreach (i ${ARGN}) + set(output "${output} ${i}") + endforeach() + + 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. +MACRO(MafReadDir directory maffile) + # 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 + SOURCES_WIN32_SHARED # windows specific SOURCES + PRIVATE_HEADERS_WIN32_SHARED # windows specific PRIVATE_HEADERS + OPTIONS + ) + 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 ${directory}/${maffile} MAFREAD_CONTENTS) + STRING(REGEX REPLACE ";" "\\\\;" MAFREAD_CONTENTS "${MAFREAD_CONTENTS}") + STRING(REGEX REPLACE "\n" ";" MAFREAD_CONTENTS "${MAFREAD_CONTENTS}") + + # Once correctly read, declare this file as dependency of the build file. + # Normally you should use cmake_configure_depends(), but this is + # available only since 3.0 version. + 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() + + # The unnamed section becomes SOURCES + set (MAFREAD_VARIABLE ${MAFREAD_VAR_SOURCES}) + set (MAFREAD_UNASSIGNED "") + + # Default section type. Another is 'flags'. + set (MAFREAD_SECTION_TYPE file) + + 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}") + if (${MAFREAD_SECTION_TYPE} STREQUAL file) + get_filename_component(MAFREAD_OLINE ${directory}/${MAFREAD_OLINE} ABSOLUTE) + endif() + 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}) + + # The cmake's version of 'if (MAFREAD_SECNAME[0] == '-')' - sigh... + string(SUBSTRING ${MAFREAD_SECNAME} 0 1 MAFREAD_SECNAME0) + if (${MAFREAD_SECNAME0} STREQUAL "-") + set (MAFREAD_SECTION_TYPE option) + string(SUBSTRING ${MAFREAD_SECNAME} 1 -1 MAFREAD_SECNAME) + else() + set (MAFREAD_SECTION_TYPE file) + endif() + 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(MafReadDir) + +# NOTE: This is historical only. Not in use. +# It should be a similar interface to mafread.tcl like +# the above MafRead macro. +MACRO(GetMafHeaders directory outvar) + EXECUTE_PROCESS( + COMMAND ${CMAKE_MODULE_PATH}/mafread.tcl + ${CMAKE_SOURCE_DIR}/${directory}/HEADERS.maf + "PUBLIC HEADERS" + "PROTECTED HEADERS" + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE ${outvar} + ) + SEPARATE_ARGUMENTS(${outvar}) + adddirname(${CMAKE_SOURCE_DIR}/${directory} "${${outvar}}" ${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) +endfunction() + +function (check_testcode_compiles testcode libraries _successful) + set (save_required_libraries ${CMAKE_REQUIRED_LIBRARIES}) + set (CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES} ${libraries}") + + check_cxx_source_compiles("${testcode}" ${_successful}) + set (${_successful} ${${_successful}} PARENT_SCOPE) + set (CMAKE_REQUIRED_LIBRARIES ${save_required_libraries}) +endfunction() + +function (test_requires_clock_gettime _result) + # 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 + + set (code " + #include + int main() { + timespec res\; + int result = clock_gettime(CLOCK_MONOTONIC, &res)\; + return result == 0\; + } + ") + + 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) + 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) + return() + endif() + + message(FATAL_ERROR "clock_gettime() is not available on this system") +endfunction() diff --git a/trunk/3rdparty/srt-1-fit/scripts/iOS.cmake b/trunk/3rdparty/srt-1-fit/scripts/iOS.cmake new file mode 100644 index 000000000..73544a29b --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/iOS.cmake @@ -0,0 +1,173 @@ +# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake +# files which are included with CMake 2.8.4 +# It has been altered for iOS development + +# Options: +# +# IOS_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64 +# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders +# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch. +# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. +# +# IOS_ARCH = arm64 (default for OS), armv7, armv7s, i386 (default for SIMULATOR), x86_64 (default for SIMULATOR64) +# +# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder +# By default this location is automatcially chosen based on the IOS_PLATFORM value above. +# If set manually, it will override the default location and force the user of a particular Developer Platform +# +# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder +# By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value. +# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path. +# If set manually, this will force the use of a specific SDK version +# +# IOS_DISABLE_BITCODE - set to 1 if you want to disable bitcode generation + +# Standard settings +set (CMAKE_SYSTEM_NAME Darwin) +set (CMAKE_SYSTEM_VERSION 1) +set (UNIX True) +set (APPLE True) +set (IOS True) + +# Required as of cmake 2.8.10 +set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE) + +# Determine the cmake host system version so we know where to find the iOS SDKs +find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin) +if (CMAKE_UNAME) + exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION) + string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") +endif (CMAKE_UNAME) + + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(CMAKE_AR ar CACHE FILEPATH "" FORCE) + +set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +if (NOT DEFINED IOS_DISABLE_BITCODE) + set (EMBED_OPTIONS "-fembed-bitcode") +endif(NOT DEFINED IOS_DISABLE_BITCODE) + +if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR ENABLE_DEBUG) + set(IOS_DEBUG_OPTIONS "-glldb -gmodules") +else() + set(IOS_DEBUG_OPTIONS "-fvisibility=hidden -fvisibility-inlines-hidden") +endif() + +set (CMAKE_C_FLAGS_INIT "${IOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}") +set (CMAKE_CXX_FLAGS_INIT "${IOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}") + +set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_C_LINK_FLAGS}") +set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_CXX_LINK_FLAGS}") + + +set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib") +set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle") +set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") + +# Specify install_name_tool and pkg-config since it outside of SDK path and therefore can't be found by CMake +if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) +endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + +if (NOT DEFINED PKG_CONFIG_EXECUTABLE) + find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config) + if (DEFINED PKG_CONFIG_EXECUTABLE) + execute_process(COMMAND pkg-config --version OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING) + endif(DEFINED PKG_CONFIG_EXECUTABLE) +endif(NOT DEFINED PKG_CONFIG_EXECUTABLE) + + +# fffio Specify path to install shared library on device +set (CMAKE_INSTALL_NAME_DIR "@executable_path/Frameworks") +set (CMAKE_BUILD_WITH_INSTALL_NAME_DIR TRUE) + +# Setup iOS platform unless specified manually with IOS_PLATFORM +if (NOT DEFINED IOS_PLATFORM) + set (IOS_PLATFORM "OS") +endif (NOT DEFINED IOS_PLATFORM) +set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform") + +# Check the platform selection and setup for developer root +if (${IOS_PLATFORM} STREQUAL OS) + set (IOS_PLATFORM_LOCATION "iPhoneOS.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos") +elseif (${IOS_PLATFORM} STREQUAL SIMULATOR) + set (SIMULATOR true) + set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") +elseif (${IOS_PLATFORM} STREQUAL SIMULATOR64) + set (SIMULATOR true) + set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") +else (${IOS_PLATFORM} STREQUAL OS) + message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR") +endif (${IOS_PLATFORM} STREQUAL OS) + +# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT +if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) + exec_program(/usr/bin/xcode-select ARGS -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR) + set (CMAKE_IOS_DEVELOPER_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer") +endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) +set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform") + +# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT +if (NOT DEFINED CMAKE_IOS_SDK_ROOT) + file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*") + if (_CMAKE_IOS_SDKS) + list (SORT _CMAKE_IOS_SDKS) + list (REVERSE _CMAKE_IOS_SDKS) + list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT) + else (_CMAKE_IOS_SDKS) + message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.") + endif (_CMAKE_IOS_SDKS) + message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}") +endif (NOT DEFINED CMAKE_IOS_SDK_ROOT) +set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK") + +# Set the sysroot default to the most recent SDK +set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support") + +# set the architecture for iOS +if (NOT DEFINED IOS_ARCH) + if (${IOS_PLATFORM} STREQUAL OS) + set (IOS_ARCH arm64) + elseif (${IOS_PLATFORM} STREQUAL SIMULATOR) + set (IOS_ARCH i386) + elseif (${IOS_PLATFORM} STREQUAL SIMULATOR64) + 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 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") + +# default to searching for frameworks first +set (CMAKE_FIND_FRAMEWORK FIRST) + +# set up the default search directories for frameworks +set (CMAKE_SYSTEM_FRAMEWORK_PATH + ${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks + ${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks + ${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks +) + +# only search the iOS sdks, not the remainder of the host filesystem +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + diff --git a/trunk/3rdparty/srt-1-fit/scripts/mafread.tcl b/trunk/3rdparty/srt-1-fit/scripts/mafread.tcl new file mode 100755 index 000000000..e5e1239fe --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/mafread.tcl @@ -0,0 +1,58 @@ +#!/usr/bin/tclsh + +proc is-section line { + return [regexp {^[A-Z ]+$} $line] +} + +# First argument is Manifest file, others are sections. +set sections [lassign $argv maffile] + +if { $sections == "" } { + puts stderr "Usage: [file tail $argv0]
" + exit 1 +} + +# NOTE: If the file doesn't exist, simply print nothing. +# If there's no manifest file under this name, it means that +# there are no files that satisfy given manifest and section. +if { [catch {set fd [open $maffile r]}] } { + exit +} + +set extracted "" +set insection 0 + +while { [gets $fd line] >= 0 } { + set oline [string trim $line] + if { $oline == "" } { + continue + } + + if { [string index $oline 0] == "#" } { + continue + } + + if { !$insection } { + # An opportunity to see if this is a section name + if { ![is-section $line] } { + continue + } + + # If it is, then check if this is OUR section + if { $oline in $sections } { + set insection 1 + continue + } + } else { + # We are inside the interesting section, so collect filenames + # Check if this is a next section name - if it is, stop reading. + if { [is-section $line] } { + continue + } + + # Otherwise read the current filename + lappend extracted $oline + } +} + +puts $extracted diff --git a/trunk/3rdparty/srt-1-fit/scripts/set-version-metadata.ps1 b/trunk/3rdparty/srt-1-fit/scripts/set-version-metadata.ps1 new file mode 100644 index 000000000..cd9b4be3e --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/set-version-metadata.ps1 @@ -0,0 +1,47 @@ +# Script for reading generated version values and updating metadata properties + +#read major / minor version values from version.h (generated by cmake via version.h.in) +$majorVer=99 +$minorVer=99 +$patchVer=0 +$buildNum=0 + +#define regular expressions to be used when checking for #define statements +$versionSniffingRegex = "(\s*#define\s+(\S+)\s+)(\d+)" + +#read generated file, load values from this with regular expression +Get-Content ".\version.h" | Where-Object { $_ -match $versionSniffingRegex } | ForEach-Object { + switch ($Matches[2]) + { + "SRT_VERSION_MAJOR" { $majorVer = $Matches[3] } + "SRT_VERSION_MINOR" { $minorVer = $Matches[3] } + "SRT_VERSION_PATCH" { $patchVer = $Matches[3] } + "SRT_VERSION_BUILD" { $buildNum = $Matches[3] } + } +} + +$FileDescriptionBranchCommitValue = "SRT Local Build" + +if($Env:APPVEYOR){ + #make AppVeyor update with this new version number + Update-AppveyorBuild -Version "$majorVer.$minorVer.$patchVer.$buildNum" + $FileDescriptionBranchCommitValue = "$Env:APPVEYOR_REPO_NAME - $($Env:APPVEYOR_REPO_BRANCH) ($($Env:APPVEYOR_REPO_COMMIT.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 { + $fileName = $_ + Write-Host "Processing metadata changes for file: $fileName" + + $FileLines = Get-Content -path $fileName + + for($i=0;$i -lt $FileLines.Count;$i++) + { + $FileLines[$i] = $FileLines[$i] -Replace $FileDescriptionStringRegex, "`${1}$FileDescriptionBranchCommitValue`${3}" + } + + [System.IO.File]::WriteAllLines($fileName.FullName, $FileLines) +} + diff --git a/trunk/3rdparty/srt-1-fit/scripts/srt-ffplay b/trunk/3rdparty/srt-1-fit/scripts/srt-ffplay new file mode 100755 index 000000000..b12a2bcb1 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/srt-ffplay @@ -0,0 +1,31 @@ +#!/bin/bash + +# +# 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/. +# + +FFPLAY=`type -p ffplay || echo none` +if [[ $FFPLAY == "none" ]]; then + echo >&2 "ERROR: ffplay not available to call. Please install ffplay first." + exit 1 +fi + +DIRNAME=`dirname $0` +if [[ ! -x $DIRNAME/srt-live-transmit ]]; then + echo >&2 "ERROR: you need 'srt-live-transmit' tool from SRT package in the same directory as this script." + exit 1 +fi + +SRCLOC=$1 +if [[ -z $SRCLOC ]]; then + echo >&2 "Usage: `basename $0` " + exit 1 +fi + +$DIRNAME/srt-live-transmit "$1" file://con/ | ffplay - + diff --git a/trunk/3rdparty/srt-1-fit/scripts/srt.pc.in b/trunk/3rdparty/srt-1-fit/scripts/srt.pc.in new file mode 100644 index 000000000..98d1e0997 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/srt.pc.in @@ -0,0 +1,12 @@ +prefix=@INSTALLDIR@ +exec_prefix=${prefix} +libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: srt +Description: SRT library set +Version: @SRT_VERSION@ +Libs: -L${libdir} -l@TARGET_srt@ @IFNEEDED_LINK_HAICRYPT@ @IFNEEDED_SRTBASE@ @IFNEEDED_SRT_LDFLAGS@ +Libs.private: @SRT_LIBS_PRIVATE@ +Cflags: -I${includedir} -I${includedir}/srt +Requires.private: @SSL_REQUIRED_MODULES@ diff --git a/trunk/3rdparty/srt-1-fit/scripts/tcp-echo-client.tcl b/trunk/3rdparty/srt-1-fit/scripts/tcp-echo-client.tcl new file mode 100644 index 000000000..78ddf8125 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/tcp-echo-client.tcl @@ -0,0 +1,110 @@ +#!/usr/bin/tclsh + +set read_running 0 +set write_running 0 +set read_eof 0 +set theend 0 + +set nread 0 +set nwritten 0 + +proc ReadBack {fd} { + + if { !$::write_running } { + puts stderr "ERROR: connection closed unexpectedly!" + set ::theend 1 + return + } + + set r [read $fd 4096] + if {$r == ""} { + + if {[eof $fd]} { + puts stderr "EOF on socket" + set ::read_running 0 + return + } + + # --- puts stderr "SPURIOUS, not reading" + return + } + + # --- puts stderr "REPRINTING [string bytelength $r] bytes" + puts -nonewline stdout $r + incr ::nwritten [string bytelength $r] + # --- puts stderr "DONE" + + set remain [expr {$::nread - $::nwritten}] + if { $::read_eof } { + puts stderr "Finishing... read=$::nread written=$::nwritten diff=[expr {$::nwritten - $::nread}] - [expr {100.0*$remain/$::nread}]%" + } + + # Nothing more to read + if {$remain == 0} { + puts stderr "NOTHING MORE TO BE WRITTEN - exitting" + set ::theend 1 + return + } + + after idle "ReadBack $fd" +} + +proc SendToSocket {fd} { + global theend + + if { !$::write_running } { + # --- puts stderr "SERVER DOWN, not reading" + fileevent stdin readable {} + return + } + + if { $::read_eof } { + # Don't read, already EOF. + + } + # --- puts stderr "READING cin" + set r [read stdin 4096] + if {$r == ""} { + if {[eof stdin]} { + if {!$::read_eof} { + puts stderr "EOF, setting server off" + set ::read_eof 1 + } + # Just enough when the next SendToSocket will + # not be scheduled. + return + } + # --- puts stderr "SPURIOUS, not reading" + return + } + + # --- puts stderr "SENDING [string bytelength $r] bytes" + # Set blocking for a short moment of sending + # in order to prevent losing data that must wait + fconfigure $fd -blocking yes + puts -nonewline $fd $r + incr ::nread [string bytelength $r] + fconfigure $fd -blocking no + + # --- if {[fblocked stdin]} { + # --- # Nothing more to read + # --- return + # --- } + after idle "SendToSocket $fd" +} + +set fd [socket {*}$argv] +fconfigure $fd -encoding binary -translation binary -blocking no -buffering none +fileevent $fd readable "ReadBack $fd" + +fconfigure stdin -encoding binary -translation binary -blocking no +fconfigure stdout -encoding binary -translation binary +fileevent stdin readable "SendToSocket $fd" + +# --- puts stderr "READY, sending" +set read_running 1 +set write_running 1 + +vwait theend + +close $fd diff --git a/trunk/3rdparty/srt-1-fit/scripts/tcp-echo-server.tcl b/trunk/3rdparty/srt-1-fit/scripts/tcp-echo-server.tcl new file mode 100644 index 000000000..8bee4d73d --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/tcp-echo-server.tcl @@ -0,0 +1,51 @@ +#!/usr/bin/tclsh + +proc SpawnEchoServer {fd host port} { + fconfigure $fd -encoding binary -translation binary -blocking no -buffering none + fileevent $fd readable "EchoBack $fd" + # --- puts stderr "Connected: [fconfigure $fd -peername]" +} + +proc EchoBack {fd} { + + # --- puts stderr "READ-READY" + + while 1 { + + # --- puts stderr "READING 4096" + set r [read $fd 4096] + if {$r == ""} { + if {[eof $fd]} { + # --- puts stderr "EOF. Closing" + close $fd + return + } + + # --- puts stderr "SPURIOUS, giving up read" + return + } + + # Set blocking for a short moment of sending + # in order to prevent losing data that must wait + + # --- puts stderr "SENDING [string bytelength $r] bytes" + fconfigure $fd -blocking yes + puts -nonewline $fd $r + fconfigure $fd -blocking no + + if {[fblocked $fd]} { + # --- puts stderr "NO MORE DATA" + # Nothing more to read + return + } + + # --- puts stderr "AGAIN" + + } + +} + +socket -server SpawnEchoServer $argv +puts stderr "SERVER READY" + +vwait tk diff --git a/trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.cpp b/trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.cpp new file mode 100644 index 000000000..f4ad38608 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.cpp @@ -0,0 +1,360 @@ +/* + * 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 +#include + +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; + */ + } +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.h b/trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.h new file mode 100644 index 000000000..5bf93032c --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.h @@ -0,0 +1,219 @@ +/***************************************************************************** +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 CCCFactory: public CCCVirtualFactory +{ +public: + virtual ~CCCFactory() {} + + virtual CCC* create() {return new T;} + virtual CCCVirtualFactory* clone() {return new CCCFactory;} +}; + +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 diff --git a/trunk/3rdparty/srt-1-fit/srtcore/README.md b/trunk/3rdparty/srt-1-fit/srtcore/README.md new file mode 100644 index 000000000..4a5d58273 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/README.md @@ -0,0 +1,75 @@ +SRT Core +======== + +These files are contents of the SRT library. Beside files that are used exclusively +and internally by the library, this directory also contains: + + - common files: usually header files, which can be used also by other projects, +even if they don't link against SRT + + - public and protected header files - header files for the library, which will +be picked up from here + +Which header files are public, protected and private, it's defined in the manifest +file together with all source files that the SRT library comprises of: `filelist.maf`. + + +Common files +============ + +This directory holds the files that may be used separately by both SRT library +itself and the internal applications. + +Source files are added to SRT library, so apps don't have to use them. However +these source files might be used by some internal applications that do not +link against SRT library. + +Header files contained here might be required by internal applications no +matter if they link against SRT or not. They are here because simultaneously +they are used also by the SRT library. + + +Utilities +========= + +1. threadname.h + +This is a utility that is useful for debugging and it allows a thread to be given +a name. This name is used in the logging messages, as well as you can see it also +inside the debugger. + +This is currently supported only on Linux; some more portable and more reliable +way is needed. + +2. utilities.h + +A set of various reusable components, all defined as C++ classes or C++ inline +functions. + +3. `netinet_any.h` + +This defines a `sockaddr_any` type, which simplifies dealing with the BSD socket API +using `sockaddr`, `sockaddr_in` and `sockaddr_in6` structures. + + +Compat and portability +====================== + +1. `srt_compat.h` + +This part contains some portability problem resolutions, including: + - `strerror` in a version that is both portable and thread safe + - `localtime` in a version that is both portable and thread safe + +2. win directory + +This contains various header files that are used on Windows platform only. +They provide various facilities available OOTB on POSIX systems. + +3. `platform_sys.h` + +This is a file that is responsible to include whatever system include +files must be included for whatever system API must be provided for +the needs of SRT library. This is a part of public headers. + + diff --git a/trunk/3rdparty/srt-1-fit/srtcore/api.cpp b/trunk/3rdparty/srt-1-fit/srtcore/api.cpp new file mode 100644 index 000000000..8b1003891 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/api.cpp @@ -0,0 +1,3245 @@ +/* + * 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 07/09/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#include +#include +#include +#include + +#include +#include "platform_sys.h" +#include "api.h" +#include "core.h" +#include "logging.h" +#include "threadname.h" +#include "srt.h" + +#ifdef _WIN32 + #include +#endif + +#ifdef _MSC_VER + #pragma warning(error: 4530) +#endif + +using namespace std; +using namespace srt_logging; +extern LogConfig srt_logger_config; + + +CUDTSocket::CUDTSocket(): +m_Status(SRTS_INIT), +m_ClosureTimeStamp(0), +m_iIPversion(0), +m_pSelfAddr(NULL), +m_pPeerAddr(NULL), +m_SocketID(0), +m_ListenSocket(0), +m_PeerID(0), +m_iISN(0), +m_pUDT(NULL), +m_pQueuedSockets(NULL), +m_pAcceptSockets(NULL), +m_AcceptCond(), +m_AcceptLock(), +m_uiBackLog(0), +m_iMuxID(-1) +{ + pthread_mutex_init(&m_AcceptLock, NULL); + pthread_cond_init(&m_AcceptCond, NULL); + pthread_mutex_init(&m_ControlLock, NULL); +} + +CUDTSocket::~CUDTSocket() +{ + if (m_iIPversion == AF_INET) + { + delete (sockaddr_in*)m_pSelfAddr; + delete (sockaddr_in*)m_pPeerAddr; + } + else + { + delete (sockaddr_in6*)m_pSelfAddr; + delete (sockaddr_in6*)m_pPeerAddr; + } + + delete m_pUDT; + m_pUDT = NULL; + + delete m_pQueuedSockets; + delete m_pAcceptSockets; + + pthread_mutex_destroy(&m_AcceptLock); + pthread_cond_destroy(&m_AcceptCond); + pthread_mutex_destroy(&m_ControlLock); +} + +//////////////////////////////////////////////////////////////////////////////// + +CUDTUnited::CUDTUnited(): +m_Sockets(), +m_ControlLock(), +m_IDLock(), +m_SocketIDGenerator(0), +m_TLSError(), +m_mMultiplexer(), +m_MultiplexerLock(), +m_pCache(NULL), +m_bClosing(false), +m_GCStopLock(), +m_GCStopCond(), +m_InitLock(), +m_iInstanceCount(0), +m_bGCStatus(false), +m_GCThread(), +m_ClosedSockets() +{ + // Socket ID MUST start from a random value + // Note. Don't use CTimer here, because s_UDTUnited is a static instance of CUDTUnited + // with dynamic initialization (calling this constructor), while CTimer has + // a static member s_ullCPUFrequency with dynamic initialization. + // The order of initialization is not guaranteed. + timeval t; + gettimeofday(&t, 0); + srand((unsigned int)t.tv_usec); + m_SocketIDGenerator = 1 + (int)((1 << 30) * (double(rand()) / RAND_MAX)); + + pthread_mutex_init(&m_ControlLock, NULL); + pthread_mutex_init(&m_IDLock, NULL); + pthread_mutex_init(&m_InitLock, NULL); + + pthread_key_create(&m_TLSError, TLSDestroy); + + m_pCache = new CCache; +} + +CUDTUnited::~CUDTUnited() +{ + // Call it if it wasn't called already. + // This will happen at the end of main() of the application, + // when the user didn't call srt_cleanup(). + if (m_bGCStatus) + { + cleanup(); + } + + pthread_mutex_destroy(&m_ControlLock); + pthread_mutex_destroy(&m_IDLock); + pthread_mutex_destroy(&m_InitLock); + + delete (CUDTException*)pthread_getspecific(m_TLSError); + pthread_key_delete(m_TLSError); + + delete m_pCache; +} + +std::string CUDTUnited::CONID(SRTSOCKET sock) +{ + if ( sock == 0 ) + return ""; + + std::ostringstream os; + os << "@" << sock << ":"; + return os.str(); +} + +int CUDTUnited::startup() +{ + CGuard gcinit(m_InitLock); + + if (m_iInstanceCount++ > 0) + return 0; + + // Global initialization code + #ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2, 2); + + if (0 != WSAStartup(wVersionRequested, &wsaData)) + throw CUDTException(MJ_SETUP, MN_NONE, WSAGetLastError()); + #endif + + PacketFilter::globalInit(); + + //init CTimer::EventLock + + if (m_bGCStatus) + return true; + + m_bClosing = false; + pthread_mutex_init(&m_GCStopLock, NULL); +#if ENABLE_MONOTONIC_CLOCK + pthread_condattr_t CondAttribs; + pthread_condattr_init(&CondAttribs); + pthread_condattr_setclock(&CondAttribs, CLOCK_MONOTONIC); + pthread_cond_init(&m_GCStopCond, &CondAttribs); +#else + pthread_cond_init(&m_GCStopCond, NULL); +#endif + { + ThreadName tn("SRT:GC"); + pthread_create(&m_GCThread, NULL, garbageCollect, this); + } + + m_bGCStatus = true; + + return 0; +} + +int CUDTUnited::cleanup() +{ + CGuard gcinit(m_InitLock); + + if (--m_iInstanceCount > 0) + return 0; + + //destroy CTimer::EventLock + + if (!m_bGCStatus) + return 0; + + m_bClosing = true; + pthread_cond_signal(&m_GCStopCond); + pthread_join(m_GCThread, NULL); + + // XXX There's some weird bug here causing this + // to hangup on Windows. This might be either something + // bigger, or some problem in pthread-win32. As this is + // the application cleanup section, this can be temporarily + // tolerated with simply exit the application without cleanup, + // counting on that the system will take care of it anyway. +#ifndef _WIN32 + pthread_mutex_destroy(&m_GCStopLock); + pthread_cond_destroy(&m_GCStopCond); +#endif + + m_bGCStatus = false; + + // Global destruction code + #ifdef _WIN32 + WSACleanup(); + #endif + + return 0; +} + +SRTSOCKET CUDTUnited::newSocket(int af, int) +{ + + CUDTSocket* ns = NULL; + + try + { + // XXX REFACTOR: + // Use sockaddr_any for m_pSelfAddr and just initialize it + // with 'af'. + ns = new CUDTSocket; + ns->m_pUDT = new CUDT; + if (af == AF_INET) + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in); + ((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0; + } + else + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6); + ((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0; + } + } + catch (...) + { + delete ns; + throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + } + + CGuard::enterCS(m_IDLock); + ns->m_SocketID = -- m_SocketIDGenerator; + CGuard::leaveCS(m_IDLock); + + ns->m_Status = SRTS_INIT; + ns->m_ListenSocket = 0; + ns->m_pUDT->m_SocketID = ns->m_SocketID; + // The "Socket type" is deprecated. For the sake of + // HSv4 there will be only a "socket type" field set + // in the handshake, always to UDT_DGRAM. + //ns->m_pUDT->m_iSockType = (type == SOCK_STREAM) ? UDT_STREAM : UDT_DGRAM; + ns->m_pUDT->m_iSockType = UDT_DGRAM; + ns->m_pUDT->m_iIPversion = ns->m_iIPversion = af; + ns->m_pUDT->m_pCache = m_pCache; + + // protect the m_Sockets structure. + CGuard::enterCS(m_ControlLock); + try + { + HLOGC(mglog.Debug, log << CONID(ns->m_SocketID) + << "newSocket: mapping socket " + << ns->m_SocketID); + m_Sockets[ns->m_SocketID] = ns; + } + catch (...) + { + //failure and rollback + CGuard::leaveCS(m_ControlLock); + delete ns; + ns = NULL; + } + CGuard::leaveCS(m_ControlLock); + + if (!ns) + throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + + return ns->m_SocketID; +} + +int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr* peer, CHandShake* hs, const CPacket& hspkt, + ref_t r_error) +{ + CUDTSocket* ns = NULL; + + *r_error = SRT_REJ_IPE; + + // Can't manage this error through an exception because this is + // running in the listener loop. + CUDTSocket* ls = locate(listen); + if (!ls) + { + LOGC(mglog.Error, log << "IPE: newConnection by listener socket id=" << listen << " which DOES NOT EXIST."); + return -1; + } + + // if this connection has already been processed + if ((ns = locate(peer, hs->m_iID, hs->m_iISN)) != NULL) + { + if (ns->m_pUDT->m_bBroken) + { + // last connection from the "peer" address has been broken + ns->m_Status = SRTS_CLOSED; + ns->m_ClosureTimeStamp = CTimer::getTime(); + + CGuard::enterCS(ls->m_AcceptLock); + ls->m_pQueuedSockets->erase(ns->m_SocketID); + ls->m_pAcceptSockets->erase(ns->m_SocketID); + CGuard::leaveCS(ls->m_AcceptLock); + } + else + { + // connection already exist, this is a repeated connection request + // respond with existing HS information + + hs->m_iISN = ns->m_pUDT->m_iISN; + hs->m_iMSS = ns->m_pUDT->m_iMSS; + hs->m_iFlightFlagSize = ns->m_pUDT->m_iFlightFlagSize; + hs->m_iReqType = URQ_CONCLUSION; + hs->m_iID = ns->m_SocketID; + + return 0; + + //except for this situation a new connection should be started + } + } + + // exceeding backlog, refuse the connection request + if (ls->m_pQueuedSockets->size() >= ls->m_uiBackLog) + { + *r_error = SRT_REJ_BACKLOG; + LOGC(mglog.Error, log << "newConnection: listen backlog=" << ls->m_uiBackLog << " EXCEEDED"); + return -1; + } + + try + { + ns = new CUDTSocket; + ns->m_pUDT = new CUDT(*(ls->m_pUDT)); + if (ls->m_iIPversion == AF_INET) + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in); + ((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0; + ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in); + memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in)); + } + else + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6); + ((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0; + ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in6); + memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in6)); + } + } + catch (...) + { + *r_error = SRT_REJ_RESOURCE; + delete ns; + LOGC(mglog.Error, log << "IPE: newConnection: unexpected exception (probably std::bad_alloc)"); + return -1; + } + + CGuard::enterCS(m_IDLock); + ns->m_SocketID = -- m_SocketIDGenerator; + HLOGF(mglog.Debug, "newConnection: generated socket id %d", ns->m_SocketID); + CGuard::leaveCS(m_IDLock); + + ns->m_ListenSocket = listen; + ns->m_iIPversion = ls->m_iIPversion; + ns->m_pUDT->m_SocketID = ns->m_SocketID; + ns->m_PeerID = hs->m_iID; + ns->m_iISN = hs->m_iISN; + + int error = 0; + + // Set the error code for all prospective problems below. + // It won't be interpreted when result was successful. + *r_error = SRT_REJ_RESOURCE; + + // These can throw exception only when the memory allocation failed. + // CUDT::connect() translates exception into CUDTException. + // CUDT::open() may only throw original std::bad_alloc from new. + // This is only to make the library extra safe (when your machine lacks + // memory, it will continue to work, but fail to accept connection). + try + { + // This assignment must happen b4 the call to CUDT::connect() because + // this call causes sending the SRT Handshake through this socket. + // Without this mapping the socket cannot be found and therefore + // the SRT Handshake message would fail. + HLOGF(mglog.Debug, + "newConnection: incoming %s, mapping socket %d", + SockaddrToString(peer).c_str(), ns->m_SocketID); + { + CGuard cg(m_ControlLock); + m_Sockets[ns->m_SocketID] = ns; + } + + // bind to the same addr of listening socket + ns->m_pUDT->open(); + updateListenerMux(ns, ls); + if (ls->m_pUDT->m_cbAcceptHook) + { + if (!ls->m_pUDT->runAcceptHook(ns->m_pUDT, peer, hs, hspkt)) + { + error = 1; + goto ERR_ROLLBACK; + } + } + ns->m_pUDT->acceptAndRespond(peer, hs, hspkt); + } + catch (...) + { + // Extract the error that was set in this new failed entity. + *r_error = ns->m_pUDT->m_RejectReason; + error = 1; + goto ERR_ROLLBACK; + } + + ns->m_Status = SRTS_CONNECTED; + + // copy address information of local node + ns->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(ns->m_pSelfAddr); + CIPAddress::pton(ns->m_pSelfAddr, ns->m_pUDT->m_piSelfIP, ns->m_iIPversion); + + // protect the m_Sockets structure. + CGuard::enterCS(m_ControlLock); + try + { + HLOGF(mglog.Debug, + "newConnection: mapping peer %d to that socket (%d)\n", + ns->m_PeerID, ns->m_SocketID); + m_PeerRec[ns->getPeerSpec()].insert(ns->m_SocketID); + } + catch (...) + { + error = 2; + } + CGuard::leaveCS(m_ControlLock); + + CGuard::enterCS(ls->m_AcceptLock); + try + { + ls->m_pQueuedSockets->insert(ns->m_SocketID); + } + catch (...) + { + error = 3; + } + CGuard::leaveCS(ls->m_AcceptLock); + + // acknowledge users waiting for new connections on the listening socket + m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, true); + + CTimer::triggerEvent(); + + ERR_ROLLBACK: + // XXX the exact value of 'error' is ignored + if (error > 0) + { +#if ENABLE_LOGGING + static const char* why [] = { + "UNKNOWN ERROR", + "CONNECTION REJECTED", + "IPE when mapping a socket", + "IPE when inserting a socket" + }; + LOGC(mglog.Error, log << CONID(ns->m_SocketID) << "newConnection: connection rejected due to: " << why[error]); +#endif + SRTSOCKET id = ns->m_SocketID; + ns->m_pUDT->close(); + ns->m_Status = SRTS_CLOSED; + ns->m_ClosureTimeStamp = CTimer::getTime(); + // The mapped socket should be now unmapped to preserve the situation that + // was in the original UDT code. + // In SRT additionally the acceptAndRespond() function (it was called probably + // connect() in UDT code) may fail, in which case this socket should not be + // further processed and should be removed. + { + CGuard cg(m_ControlLock); + m_Sockets.erase(id); + m_ClosedSockets[id] = ns; + } + + return -1; + } + + // wake up a waiting accept() call + pthread_mutex_lock(&(ls->m_AcceptLock)); + pthread_cond_signal(&(ls->m_AcceptCond)); + pthread_mutex_unlock(&(ls->m_AcceptLock)); + + return 1; +} + +int CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +{ + try + { + CUDT* lc = lookup(lsn); + lc->installAcceptHook(hook, opaq); + + } + catch (CUDTException& e) + { + setError(new CUDTException(e)); + return SRT_ERROR; + } + + return 0; +} + +CUDT* CUDTUnited::lookup(const SRTSOCKET u) +{ + // protects the m_Sockets structure + CGuard cg(m_ControlLock); + + map::iterator i = m_Sockets.find(u); + + if ((i == m_Sockets.end()) || (i->second->m_Status == SRTS_CLOSED)) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + return i->second->m_pUDT; +} + +SRT_SOCKSTATUS CUDTUnited::getStatus(const SRTSOCKET u) +{ + // protects the m_Sockets structure + CGuard cg(m_ControlLock); + + map::const_iterator i = m_Sockets.find(u); + + if (i == m_Sockets.end()) + { + if (m_ClosedSockets.find(u) != m_ClosedSockets.end()) + return SRTS_CLOSED; + + return SRTS_NONEXIST; + } + const CUDTSocket* s = i->second; + + if (s->m_pUDT->m_bBroken) + return SRTS_BROKEN; + + // TTL in CRendezvousQueue::updateConnStatus() will set m_bConnecting to false. + // Although m_Status is still SRTS_CONNECTING, the connection is in fact to be closed due to TTL expiry. + // In this case m_bConnected is also false. Both checks are required to avoid hitting + // a regular state transition from CONNECTING to CONNECTED. + if ((s->m_Status == SRTS_CONNECTING) && !s->m_pUDT->m_bConnecting && !s->m_pUDT->m_bConnected) + return SRTS_BROKEN; + + return s->m_Status; +} + +int CUDTUnited::bind(const SRTSOCKET u, const sockaddr* name, int namelen) +{ + CUDTSocket* s = locate(u); + if (!s) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + CGuard cg(s->m_ControlLock); + + // cannot bind a socket more than once + if (s->m_Status != SRTS_INIT) + throw CUDTException(MJ_NOTSUP, MN_NONE, 0); + + // check the size of SOCKADDR structure + if (s->m_iIPversion == AF_INET) + { + if (namelen != sizeof(sockaddr_in)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + else + { + if (namelen != sizeof(sockaddr_in6)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + s->m_pUDT->open(); + updateMux(s, name); + s->m_Status = SRTS_OPENED; + + // copy address information of local node + s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); + + return 0; +} + +int CUDTUnited::bind(SRTSOCKET u, UDPSOCKET udpsock) +{ + CUDTSocket* s = locate(u); + if (!s) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + CGuard cg(s->m_ControlLock); + + // cannot bind a socket more than once + if (s->m_Status != SRTS_INIT) + throw CUDTException(MJ_NOTSUP, MN_NONE, 0); + + sockaddr_in name4; + sockaddr_in6 name6; + sockaddr* name; + socklen_t namelen; + + if (s->m_iIPversion == AF_INET) + { + namelen = sizeof(sockaddr_in); + name = (sockaddr*)&name4; + } + else + { + namelen = sizeof(sockaddr_in6); + name = (sockaddr*)&name6; + } + + if (::getsockname(udpsock, name, &namelen) == -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL); + + s->m_pUDT->open(); + updateMux(s, name, &udpsock); + s->m_Status = SRTS_OPENED; + + // copy address information of local node + s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); + + return 0; +} + +int CUDTUnited::listen(const SRTSOCKET u, int backlog) +{ + if (backlog <= 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + // Don't search for the socket if it's already -1; + // this never is a valid socket. + if (u == UDT::INVALID_SOCK) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + CUDTSocket* s = locate(u); + if (!s) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + CGuard cg(s->m_ControlLock); + + // NOTE: since now the socket is protected against simultaneous access. + // In the meantime the socket might have been closed, which means that + // it could have changed the state. It could be also set listen in another + // thread, so check it out. + + // do nothing if the socket is already listening + if (s->m_Status == SRTS_LISTENING) + return 0; + + // a socket can listen only if is in OPENED status + if (s->m_Status != SRTS_OPENED) + throw CUDTException(MJ_NOTSUP, MN_ISUNBOUND, 0); + + // [[using assert(s->m_Status == OPENED)]]; + + // listen is not supported in rendezvous connection setup + if (s->m_pUDT->m_bRendezvous) + throw CUDTException(MJ_NOTSUP, MN_ISRENDEZVOUS, 0); + + s->m_uiBackLog = backlog; + + try + { + s->m_pQueuedSockets = new set; + s->m_pAcceptSockets = new set; + } + catch (...) + { + delete s->m_pQueuedSockets; + delete s->m_pAcceptSockets; + + // XXX Translated std::bad_alloc into CUDTException specifying + // memory allocation failure... + throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + } + + // [[using assert(s->m_Status == OPENED)]]; // (still, unchanged) + + s->m_pUDT->setListenState(); // propagates CUDTException, + // if thrown, remains in OPENED state if so. + s->m_Status = SRTS_LISTENING; + + return 0; +} + +SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen) +{ + if ((addr) && (!addrlen)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + CUDTSocket* ls = locate(listen); + + if (ls == NULL) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + // the "listen" socket must be in LISTENING status + if (ls->m_Status != SRTS_LISTENING) + throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0); + + // no "accept" in rendezvous connection setup + if (ls->m_pUDT->m_bRendezvous) + throw CUDTException(MJ_NOTSUP, MN_ISRENDEZVOUS, 0); + + SRTSOCKET u = CUDT::INVALID_SOCK; + bool accepted = false; + + // !!only one conection can be set up each time!! + while (!accepted) + { + CGuard cg(ls->m_AcceptLock); + + if ((ls->m_Status != SRTS_LISTENING) || ls->m_pUDT->m_bBroken) + { + // This socket has been closed. + accepted = true; + } + else if (ls->m_pQueuedSockets->size() > 0) + { + // XXX REFACTORING REQUIRED HERE! + // Actually this should at best be something like that: + // set::iterator b = ls->m_pQueuedSockets->begin(); + // u = *b; + // ls->m_pQueuedSockets->erase(b); + // ls->m_pAcceptSockets->insert(u); + // + // It is also questionable why m_pQueuedSockets should be of type 'set'. + // There's no quick-searching capabilities of that container used anywhere except + // checkBrokenSockets and garbageCollect, which aren't performance-critical, + // whereas it's mainly used for getting the first element and iterating + // over elements, which is slow in case of std::set. It's also doubtful + // as to whether the sorting capability of std::set is properly used; + // the first is taken here, which is actually the socket with lowest + // possible descriptor value (as default operator< and ascending sorting + // used for std::set where SRTSOCKET=int). + // + // Consider using std::list or std::vector here. + + u = *(ls->m_pQueuedSockets->begin()); + ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), u); + ls->m_pQueuedSockets->erase(ls->m_pQueuedSockets->begin()); + accepted = true; + } + else if (!ls->m_pUDT->m_bSynRecving) + { + accepted = true; + } + + if (!accepted && (ls->m_Status == SRTS_LISTENING)) + pthread_cond_wait(&(ls->m_AcceptCond), &(ls->m_AcceptLock)); + + if (ls->m_pQueuedSockets->empty()) + m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, false); + } + + if (u == CUDT::INVALID_SOCK) + { + // non-blocking receiving, no connection available + if (!ls->m_pUDT->m_bSynRecving) + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + + // listening socket is closed + throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0); + } + + if ((addr != NULL) && (addrlen != NULL)) + { + CUDTSocket* s = locate(u); + if (s == NULL) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + CGuard cg(s->m_ControlLock); + + if (AF_INET == s->m_iIPversion) + *addrlen = sizeof(sockaddr_in); + else + *addrlen = sizeof(sockaddr_in6); + + // copy address information of peer node + memcpy(addr, s->m_pPeerAddr, *addrlen); + } + + return u; +} + +int CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +{ + CUDTSocket* s = locate(u); + if (!s) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + CGuard cg(s->m_ControlLock); + + // XXX Consider translating this to using sockaddr_any, + // this should take out all the "IP version check" things. + if (AF_INET == s->m_iIPversion) + { + if (namelen != sizeof(sockaddr_in)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + else + { + if (namelen != sizeof(sockaddr_in6)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + // a socket can "connect" only if it is in INIT or OPENED status + if (s->m_Status == SRTS_INIT) + { + if (!s->m_pUDT->m_bRendezvous) + { + s->m_pUDT->open(); // XXX here use the AF_* family value from 'name' + updateMux(s); // <<---- updateMux + // -> C(Snd|Rcv)Queue::init + // -> pthread_create(...C(Snd|Rcv)Queue::worker...) + s->m_Status = SRTS_OPENED; + } + else + throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0); + } + else if (s->m_Status != SRTS_OPENED) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + // connect_complete() may be called before connect() returns. + // So we need to update the status before connect() is called, + // otherwise the status may be overwritten with wrong value + // (CONNECTED vs. CONNECTING). + s->m_Status = SRTS_CONNECTING; + + /* + * In blocking mode, connect can block for up to 30 seconds for + * rendez-vous mode. Holding the s->m_ControlLock prevent close + * from cancelling the connect + */ + try + { + // InvertedGuard unlocks in the constructor, then locks in the + // destructor, no matter if an exception has fired. + InvertedGuard l_unlocker( s->m_pUDT->m_bSynRecving ? &s->m_ControlLock : 0 ); + s->m_pUDT->startConnect(name, forced_isn); + } + catch (CUDTException& e) // Interceptor, just to change the state. + { + s->m_Status = SRTS_OPENED; + throw e; + } + + // record peer address + delete s->m_pPeerAddr; + if (AF_INET == s->m_iIPversion) + { + s->m_pPeerAddr = (sockaddr*)(new sockaddr_in); + memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in)); + } + else + { + s->m_pPeerAddr = (sockaddr*)(new sockaddr_in6); + memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in6)); + } + + // CGuard destructor will delete cg and unlock s->m_ControlLock + + return 0; +} + + +int CUDTUnited::close(const SRTSOCKET u) +{ + CUDTSocket* s = locate(u); + if (!s) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + HLOGC(mglog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock"); + + CGuard socket_cg(s->m_ControlLock); + + HLOGC(mglog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing from listening, closing CUDT)"); + + bool synch_close_snd = s->m_pUDT->m_bSynSending; + //bool synch_close_rcv = s->m_pUDT->m_bSynRecving; + + if (s->m_Status == SRTS_LISTENING) + { + if (s->m_pUDT->m_bBroken) + return 0; + + s->m_ClosureTimeStamp = CTimer::getTime(); + s->m_pUDT->m_bBroken = true; + + // Change towards original UDT: + // Leave all the closing activities for garbageCollect to happen, + // however remove the listener from the RcvQueue IMMEDIATELY. + // Even though garbageCollect would eventually remove the listener + // as well, there would be some time interval between now and the + // moment when it's done, and during this time the application will + // be unable to bind to this port that the about-to-delete listener + // is currently occupying (due to blocked slot in the RcvQueue). + + HLOGC(mglog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing listener immediately)"); + { + CGuard cg(s->m_pUDT->m_ConnectionLock); + s->m_pUDT->m_bListening = false; + s->m_pUDT->m_pRcvQueue->removeListener(s->m_pUDT); + } + + // broadcast all "accept" waiting + pthread_mutex_lock(&(s->m_AcceptLock)); + pthread_cond_broadcast(&(s->m_AcceptCond)); + pthread_mutex_unlock(&(s->m_AcceptLock)); + + } + else + { + s->m_pUDT->close(); + + // synchronize with garbage collection. + HLOGC(mglog.Debug, log << "@" << u << "U::close done. GLOBAL CLOSE: " << s->m_pUDT->CONID() << ". Acquiring GLOBAL control lock"); + CGuard manager_cg(m_ControlLock); + + // since "s" is located before m_ControlLock, locate it again in case + // it became invalid + map::iterator i = m_Sockets.find(u); + if ((i == m_Sockets.end()) || (i->second->m_Status == SRTS_CLOSED)) + { + HLOGC(mglog.Debug, log << "@" << u << "U::close: NOT AN ACTIVE SOCKET, returning."); + return 0; + } + s = i->second; + + s->m_Status = SRTS_CLOSED; + + // a socket will not be immediately removed when it is closed + // in order to prevent other methods from accessing invalid address + // a timer is started and the socket will be removed after approximately + // 1 second + s->m_ClosureTimeStamp = CTimer::getTime(); + + m_Sockets.erase(s->m_SocketID); + m_ClosedSockets[s->m_SocketID] = s; + HLOGC(mglog.Debug, log << "@" << u << "U::close: Socket MOVED TO CLOSED for collecting later."); + + CTimer::triggerEvent(); + } + + HLOGC(mglog.Debug, log << "%" << u << ": GLOBAL: CLOSING DONE"); + + // Check if the ID is still in closed sockets before you access it + // (the last triggerEvent could have deleted it). + if ( synch_close_snd ) + { +#if SRT_ENABLE_CLOSE_SYNCH + + HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: sync-waiting for releasing sender resources..."); + for (;;) + { + CSndBuffer* sb = s->m_pUDT->m_pSndBuffer; + + // Disconnected from buffer - nothing more to check. + if (!sb) + { + HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: sending buffer disconnected. Allowed to close."); + break; + } + + // Sender buffer empty + if (sb->getCurrBufSize() == 0) + { + HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: sending buffer depleted. Allowed to close."); + break; + } + + // Ok, now you are keeping GC thread hands off the internal data. + // You can check then if it has already deleted the socket or not. + // The socket is either in m_ClosedSockets or is already gone. + + // Done the other way, but still done. You can stop waiting. + bool isgone = false; + { + CGuard manager_cg(m_ControlLock); + isgone = m_ClosedSockets.count(u) == 0; + } + if (!isgone) + { + isgone = !s->m_pUDT->m_bOpened; + } + if (isgone) + { + HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: ... gone in the meantime, whatever. Exiting close()."); + break; + } + + HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: ... still waiting for any update."); + CTimer::EWait wt = CTimer::waitForEvent(); + + if ( wt == CTimer::WT_ERROR ) + { + HLOGC(mglog.Debug, log << "GLOBAL CLOSING: ... ERROR WHEN WAITING FOR EVENT. Exiting close() to prevent hangup."); + break; + } + + // Continue waiting in case when an event happened or 1s waiting time passed for checkpoint. + } +#endif + } + + /* + This code is PUT ASIDE for now. + Most likely this will be never required. + It had to hold the closing activity until the time when the receiver buffer is depleted. + However the closing of the socket should only happen when the receiver has received + an information about that the reading is no longer possible (error report from recv/recvfile). + When this happens, the receiver buffer is definitely depleted already and there's no need to check + anything. + + Should there appear any other conditions in future under which the closing process should be + delayed until the receiver buffer is empty, this code can be filled here. + + if ( synch_close_rcv ) + { + ... + } + */ + + return 0; +} + +int CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* name, int* namelen) +{ + if (getStatus(u) != SRTS_CONNECTED) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + CUDTSocket* s = locate(u); + + if (!s) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + if (!s->m_pUDT->m_bConnected || s->m_pUDT->m_bBroken) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + if (AF_INET == s->m_iIPversion) + *namelen = sizeof(sockaddr_in); + else + *namelen = sizeof(sockaddr_in6); + + // copy address information of peer node + memcpy(name, s->m_pPeerAddr, *namelen); + + return 0; +} + +int CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* name, int* namelen) +{ + CUDTSocket* s = locate(u); + + if (!s) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + if (s->m_pUDT->m_bBroken) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + + if (s->m_Status == SRTS_INIT) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + if (AF_INET == s->m_iIPversion) + *namelen = sizeof(sockaddr_in); + else + *namelen = sizeof(sockaddr_in6); + + // copy address information of local node + memcpy(name, s->m_pSelfAddr, *namelen); + + return 0; +} + +int CUDTUnited::select( + ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout) +{ + uint64_t entertime = CTimer::getTime(); + + uint64_t to; + if (!timeout) + to = 0xFFFFFFFFFFFFFFFFULL; + else + to = timeout->tv_sec * 1000000 + timeout->tv_usec; + + // initialize results + int count = 0; + set rs, ws, es; + + // retrieve related UDT sockets + vector ru, wu, eu; + CUDTSocket* s; + if (readfds) + for (set::iterator i1 = readfds->begin(); + i1 != readfds->end(); ++ i1) + { + if (getStatus(*i1) == SRTS_BROKEN) + { + rs.insert(*i1); + ++ count; + } + else if (!(s = locate(*i1))) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + else + ru.push_back(s); + } + if (writefds) + for (set::iterator i2 = writefds->begin(); + i2 != writefds->end(); ++ i2) + { + if (getStatus(*i2) == SRTS_BROKEN) + { + ws.insert(*i2); + ++ count; + } + else if (!(s = locate(*i2))) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + else + wu.push_back(s); + } + if (exceptfds) + for (set::iterator i3 = exceptfds->begin(); + i3 != exceptfds->end(); ++ i3) + { + if (getStatus(*i3) == SRTS_BROKEN) + { + es.insert(*i3); + ++ count; + } + else if (!(s = locate(*i3))) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + else + eu.push_back(s); + } + + do + { + // query read sockets + for (vector::iterator j1 = ru.begin(); j1 != ru.end(); ++ j1) + { + s = *j1; + + if ((s->m_pUDT->m_bConnected + && s->m_pUDT->m_pRcvBuffer->isRcvDataReady() + ) + || (!s->m_pUDT->m_bListening + && (s->m_pUDT->m_bBroken || !s->m_pUDT->m_bConnected)) + || (s->m_pUDT->m_bListening && (s->m_pQueuedSockets->size() > 0)) + || (s->m_Status == SRTS_CLOSED)) + { + rs.insert(s->m_SocketID); + ++ count; + } + } + + // query write sockets + for (vector::iterator j2 = wu.begin(); j2 != wu.end(); ++ j2) + { + s = *j2; + + if ((s->m_pUDT->m_bConnected + && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() + < s->m_pUDT->m_iSndBufSize)) + || s->m_pUDT->m_bBroken + || !s->m_pUDT->m_bConnected + || (s->m_Status == SRTS_CLOSED)) + { + ws.insert(s->m_SocketID); + ++ count; + } + } + + // query exceptions on sockets + for (vector::iterator j3 = eu.begin(); j3 != eu.end(); ++ j3) + { + // check connection request status, not supported now + } + + if (0 < count) + break; + + CTimer::waitForEvent(); + } while (to > CTimer::getTime() - entertime); + + if (readfds) + *readfds = rs; + + if (writefds) + *writefds = ws; + + if (exceptfds) + *exceptfds = es; + + return count; +} + +int CUDTUnited::selectEx( + const vector& fds, + vector* readfds, + vector* writefds, + vector* exceptfds, + int64_t msTimeOut) +{ + uint64_t entertime = CTimer::getTime(); + + uint64_t to; + if (msTimeOut >= 0) + to = msTimeOut * 1000; + else + to = 0xFFFFFFFFFFFFFFFFULL; + + // initialize results + int count = 0; + if (readfds) + readfds->clear(); + if (writefds) + writefds->clear(); + if (exceptfds) + exceptfds->clear(); + + do + { + for (vector::const_iterator i = fds.begin(); + i != fds.end(); ++ i) + { + CUDTSocket* s = locate(*i); + + if ((!s) || s->m_pUDT->m_bBroken || (s->m_Status == SRTS_CLOSED)) + { + if (exceptfds) + { + exceptfds->push_back(*i); + ++ count; + } + continue; + } + + if (readfds) + { + if ((s->m_pUDT->m_bConnected + && s->m_pUDT->m_pRcvBuffer->isRcvDataReady() + ) + || (s->m_pUDT->m_bListening + && (s->m_pQueuedSockets->size() > 0))) + { + readfds->push_back(s->m_SocketID); + ++ count; + } + } + + if (writefds) + { + if (s->m_pUDT->m_bConnected + && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() + < s->m_pUDT->m_iSndBufSize)) + { + writefds->push_back(s->m_SocketID); + ++ count; + } + } + } + + if (count > 0) + break; + + CTimer::waitForEvent(); + } while (to > CTimer::getTime() - entertime); + + return count; +} + +int CUDTUnited::epoll_create() +{ + return m_EPoll.create(); +} + +int CUDTUnited::epoll_add_usock( + const int eid, const SRTSOCKET u, const int* events) +{ + CUDTSocket* s = locate(u); + int ret = -1; + if (s) + { + ret = m_EPoll.add_usock(eid, u, events); + s->m_pUDT->addEPoll(eid); + } + else + { + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); + } + + return ret; +} + +int CUDTUnited::epoll_add_ssock( + const int eid, const SYSSOCKET s, const int* events) +{ + return m_EPoll.add_ssock(eid, s, events); +} + +int CUDTUnited::epoll_update_usock( + const int eid, const SRTSOCKET u, const int* events) +{ + CUDTSocket* s = locate(u); + int ret = -1; + if (s) + { + ret = m_EPoll.update_usock(eid, u, events); + s->m_pUDT->addEPoll(eid); + } + else + { + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); + } + + return ret; +} + +int CUDTUnited::epoll_update_ssock( + const int eid, const SYSSOCKET s, const int* events) +{ + return m_EPoll.update_ssock(eid, s, events); +} + +int CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) +{ + int ret = m_EPoll.remove_usock(eid, u); + + CUDTSocket* s = locate(u); + if (s) + { + s->m_pUDT->removeEPoll(eid); + } + //else + //{ + // throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); + //} + + return ret; +} + +int CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) +{ + return m_EPoll.remove_ssock(eid, s); +} + +int CUDTUnited::epoll_wait( + const int eid, + set* readfds, + set* writefds, + int64_t msTimeOut, + set* lrfds, + set* lwfds) +{ + return m_EPoll.wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); +} + +int CUDTUnited::epoll_uwait( + const int eid, + SRT_EPOLL_EVENT* fdsSet, + int fdsSize, + int64_t msTimeOut) +{ + return m_EPoll.uwait(eid, fdsSet, fdsSize, msTimeOut); +} + +int32_t CUDTUnited::epoll_set(int eid, int32_t flags) +{ + return m_EPoll.setflags(eid, flags); +} + +int CUDTUnited::epoll_release(const int eid) +{ + return m_EPoll.release(eid); +} + +CUDTSocket* CUDTUnited::locate(const SRTSOCKET u) +{ + CGuard cg(m_ControlLock); + + map::iterator i = m_Sockets.find(u); + + if ((i == m_Sockets.end()) || (i->second->m_Status == SRTS_CLOSED)) + return NULL; + + return i->second; +} + +CUDTSocket* CUDTUnited::locate( + const sockaddr* peer, + const SRTSOCKET id, + int32_t isn) +{ + CGuard cg(m_ControlLock); + + map >::iterator i = m_PeerRec.find( + CUDTSocket::getPeerSpec(id, isn)); + if (i == m_PeerRec.end()) + return NULL; + + for (set::iterator j = i->second.begin(); + j != i->second.end(); ++ j) + { + map::iterator k = m_Sockets.find(*j); + // this socket might have been closed and moved m_ClosedSockets + if (k == m_Sockets.end()) + continue; + + if (CIPAddress::ipcmp( + peer, k->second->m_pPeerAddr, k->second->m_iIPversion)) + { + return k->second; + } + } + + return NULL; +} + +void CUDTUnited::checkBrokenSockets() +{ + CGuard cg(m_ControlLock); + + // set of sockets To Be Closed and To Be Removed + vector tbc; + vector tbr; + + for (map::iterator i = m_Sockets.begin(); + i != m_Sockets.end(); ++ i) + { + CUDTSocket* s = i->second; + + // HLOGF(mglog.Debug, "checking EXISTING socket: %d\n", i->first); + // check broken connection + if (s->m_pUDT->m_bBroken) + { + if (s->m_Status == SRTS_LISTENING) + { + uint64_t elapsed = CTimer::getTime() - s->m_ClosureTimeStamp; + // for a listening socket, it should wait an extra 3 seconds + // in case a client is connecting + if (elapsed < 3000000) // XXX MAKE A SYMBOLIC CONSTANT HERE! + { + // HLOGF(mglog.Debug, "STILL KEEPING socket %d + // (listener, too early, w8 %fs)\n", i->first, + // double(elapsed)/1000000); + continue; + } + } + else if ((s->m_pUDT->m_pRcvBuffer != NULL) + // FIXED: calling isRcvDataAvailable() just to get the information + // whether there are any data waiting in the buffer, + // NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when + // this function is called (isRcvDataReady also checks if the + // available data is "ready to play"). + && s->m_pUDT->m_pRcvBuffer->isRcvDataAvailable() + && (s->m_pUDT->m_iBrokenCounter -- > 0)) + { + // HLOGF(mglog.Debug, "STILL KEEPING socket (still have data): + // %d\n", i->first); + // if there is still data in the receiver buffer, wait longer + continue; + } + + // HLOGF(mglog.Debug, "moving socket to CLOSED: %d\n", i->first); + + //close broken connections and start removal timer + s->m_Status = SRTS_CLOSED; + s->m_ClosureTimeStamp = CTimer::getTime(); + tbc.push_back(i->first); + m_ClosedSockets[i->first] = s; + + // remove from listener's queue + map::iterator ls = m_Sockets.find( + s->m_ListenSocket); + if (ls == m_Sockets.end()) + { + ls = m_ClosedSockets.find(s->m_ListenSocket); + if (ls == m_ClosedSockets.end()) + continue; + } + + CGuard::enterCS(ls->second->m_AcceptLock); + ls->second->m_pQueuedSockets->erase(s->m_SocketID); + ls->second->m_pAcceptSockets->erase(s->m_SocketID); + CGuard::leaveCS(ls->second->m_AcceptLock); + } + } + + for (map::iterator j = m_ClosedSockets.begin(); + j != m_ClosedSockets.end(); ++ j) + { + // HLOGF(mglog.Debug, "checking CLOSED socket: %d\n", j->first); + if (j->second->m_pUDT->m_ullLingerExpiration > 0) + { + // asynchronous close: + if ((!j->second->m_pUDT->m_pSndBuffer) + || (0 == j->second->m_pUDT->m_pSndBuffer->getCurrBufSize()) + || (j->second->m_pUDT->m_ullLingerExpiration <= CTimer::getTime())) + { + j->second->m_pUDT->m_ullLingerExpiration = 0; + j->second->m_pUDT->m_bClosing = true; + j->second->m_ClosureTimeStamp = CTimer::getTime(); + } + } + + // timeout 1 second to destroy a socket AND it has been removed from + // RcvUList + if ((CTimer::getTime() - j->second->m_ClosureTimeStamp > 1000000) + && ((!j->second->m_pUDT->m_pRNode) + || !j->second->m_pUDT->m_pRNode->m_bOnList)) + { + // HLOGF(mglog.Debug, "will unref socket: %d\n", j->first); + tbr.push_back(j->first); + } + } + + // move closed sockets to the ClosedSockets structure + for (vector::iterator k = tbc.begin(); k != tbc.end(); ++ k) + m_Sockets.erase(*k); + + // remove those timeout sockets + for (vector::iterator l = tbr.begin(); l != tbr.end(); ++ l) + removeSocket(*l); +} + +void CUDTUnited::removeSocket(const SRTSOCKET u) +{ + map::iterator i = m_ClosedSockets.find(u); + + // invalid socket ID + if (i == m_ClosedSockets.end()) + return; + + // decrease multiplexer reference count, and remove it if necessary + const int mid = i->second->m_iMuxID; + + if (i->second->m_pQueuedSockets) + { + CGuard cg(i->second->m_AcceptLock); + + // if it is a listener, close all un-accepted sockets in its queue + // and remove them later + for (set::iterator q = i->second->m_pQueuedSockets->begin(); + q != i->second->m_pQueuedSockets->end(); ++ q) + { + m_Sockets[*q]->m_pUDT->m_bBroken = true; + m_Sockets[*q]->m_pUDT->close(); + m_Sockets[*q]->m_ClosureTimeStamp = CTimer::getTime(); + m_Sockets[*q]->m_Status = SRTS_CLOSED; + m_ClosedSockets[*q] = m_Sockets[*q]; + m_Sockets.erase(*q); + } + + } + + // remove from peer rec + map >::iterator j = m_PeerRec.find( + i->second->getPeerSpec()); + if (j != m_PeerRec.end()) + { + j->second.erase(u); + if (j->second.empty()) + m_PeerRec.erase(j); + } + + /* + * Socket may be deleted while still having ePoll events set that would + * remains forever causing epoll_wait to unblock continuously for inexistent + * sockets. Get rid of all events for this socket. + */ + m_EPoll.update_events(u, i->second->m_pUDT->m_sPollID, + UDT_EPOLL_IN|UDT_EPOLL_OUT|UDT_EPOLL_ERR, false); + + // delete this one + HLOGC(mglog.Debug, log << "GC/removeSocket: closing associated UDT %" << u); + i->second->m_pUDT->close(); + HLOGC(mglog.Debug, log << "GC/removeSocket: DELETING SOCKET %" << u); + delete i->second; + m_ClosedSockets.erase(i); + + if (mid == -1) + return; + + map::iterator m; + m = m_mMultiplexer.find(mid); + if (m == m_mMultiplexer.end()) + { + LOGC(mglog.Fatal, log << "IPE: For socket %" << u << " MUXER id=" << mid << " NOT FOUND!"); + return; + } + + CMultiplexer& mx = m->second; + + mx.m_iRefCount --; + // HLOGF(mglog.Debug, "unrefing underlying socket for %u: %u\n", + // u, mx.m_iRefCount); + if (0 == mx.m_iRefCount) + { + HLOGC(mglog.Debug, log << "MUXER id=" << mid << " lost last socket %" + << u << " - deleting muxer bound to port " + << mx.m_pChannel->bindAddressAny().hport()); + // The channel has no access to the queues and + // it looks like the multiplexer is the master of all of them. + // The queues must be silenced before closing the channel + // because this will cause error to be returned in any operation + // being currently done in the queues, if any. + mx.m_pSndQueue->setClosing(); + mx.m_pRcvQueue->setClosing(); + delete mx.m_pSndQueue; + delete mx.m_pRcvQueue; + mx.m_pChannel->close(); + delete mx.m_pTimer; + delete mx.m_pChannel; + m_mMultiplexer.erase(m); + } +} + +void CUDTUnited::setError(CUDTException* e) +{ + delete (CUDTException*)pthread_getspecific(m_TLSError); + pthread_setspecific(m_TLSError, e); +} + +CUDTException* CUDTUnited::getError() +{ + if(!pthread_getspecific(m_TLSError)) + pthread_setspecific(m_TLSError, new CUDTException); + return (CUDTException*)pthread_getspecific(m_TLSError); +} + + +void CUDTUnited::updateMux( + CUDTSocket* s, const sockaddr* addr, const UDPSOCKET* udpsock) +{ + CGuard cg(m_ControlLock); + + if ((s->m_pUDT->m_bReuseAddr) && (addr)) + { + int port = (AF_INET == s->m_pUDT->m_iIPversion) + ? ntohs(((sockaddr_in*)addr)->sin_port) + : ntohs(((sockaddr_in6*)addr)->sin6_port); + + // find a reusable address + for (map::iterator i = m_mMultiplexer.begin(); + i != m_mMultiplexer.end(); ++ i) + { + if ((i->second.m_iIPversion == s->m_pUDT->m_iIPversion) + && (i->second.m_iMSS == s->m_pUDT->m_iMSS) +#ifdef SRT_ENABLE_IPOPTS + && (i->second.m_iIpTTL == s->m_pUDT->m_iIpTTL) + && (i->second.m_iIpToS == s->m_pUDT->m_iIpToS) +#endif + && (i->second.m_iIpV6Only == s->m_pUDT->m_iIpV6Only) + && i->second.m_bReusable) + { + if (i->second.m_iPort == port) + { + // HLOGF(mglog.Debug, "reusing multiplexer for port + // %hd\n", port); + // reuse the existing multiplexer + ++ i->second.m_iRefCount; + s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; + s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; + s->m_iMuxID = i->second.m_iID; + return; + } + } + } + } + + // a new multiplexer is needed + CMultiplexer m; + m.m_iMSS = s->m_pUDT->m_iMSS; + m.m_iIPversion = s->m_pUDT->m_iIPversion; +#ifdef SRT_ENABLE_IPOPTS + m.m_iIpTTL = s->m_pUDT->m_iIpTTL; + m.m_iIpToS = s->m_pUDT->m_iIpToS; +#endif + m.m_iRefCount = 1; + m.m_iIpV6Only = s->m_pUDT->m_iIpV6Only; + m.m_bReusable = s->m_pUDT->m_bReuseAddr; + m.m_iID = s->m_SocketID; + + m.m_pChannel = new CChannel(s->m_pUDT->m_iIPversion); +#ifdef SRT_ENABLE_IPOPTS + m.m_pChannel->setIpTTL(s->m_pUDT->m_iIpTTL); + m.m_pChannel->setIpToS(s->m_pUDT->m_iIpToS); +#endif + m.m_pChannel->setSndBufSize(s->m_pUDT->m_iUDPSndBufSize); + m.m_pChannel->setRcvBufSize(s->m_pUDT->m_iUDPRcvBufSize); + if (s->m_pUDT->m_iIpV6Only != -1) + m.m_pChannel->setIpV6Only(s->m_pUDT->m_iIpV6Only); + + try + { + if (udpsock) + m.m_pChannel->attach(*udpsock); + else + m.m_pChannel->open(addr); + } + catch (CUDTException& e) + { + m.m_pChannel->close(); + delete m.m_pChannel; + throw; + } + + // XXX Simplify this. Use sockaddr_any. + sockaddr* sa = (AF_INET == s->m_pUDT->m_iIPversion) + ? (sockaddr*) new sockaddr_in + : (sockaddr*) new sockaddr_in6; + m.m_pChannel->getSockAddr(sa); + m.m_iPort = (AF_INET == s->m_pUDT->m_iIPversion) + ? ntohs(((sockaddr_in*)sa)->sin_port) + : ntohs(((sockaddr_in6*)sa)->sin6_port); + + if (AF_INET == s->m_pUDT->m_iIPversion) + delete (sockaddr_in*)sa; + else + delete (sockaddr_in6*)sa; + + m.m_pTimer = new CTimer; + + m.m_pSndQueue = new CSndQueue; + m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); + m.m_pRcvQueue = new CRcvQueue; + m.m_pRcvQueue->init( + 32, s->m_pUDT->maxPayloadSize(), m.m_iIPversion, 1024, + m.m_pChannel, m.m_pTimer); + + m_mMultiplexer[m.m_iID] = m; + + s->m_pUDT->m_pSndQueue = m.m_pSndQueue; + s->m_pUDT->m_pRcvQueue = m.m_pRcvQueue; + s->m_iMuxID = m.m_iID; + + HLOGF(mglog.Debug, + "creating new multiplexer for port %i\n", m.m_iPort); +} + +// XXX This functionality needs strong refactoring. +// +// This function is going to find a multiplexer for the port contained +// in the 'ls' listening socket, by searching through the multiplexer +// container. +// +// Somehow, however, it's not even predicted a situation that the multiplexer +// for that port doesn't exist - that is, this function WILL find the +// multiplexer. How can it be so certain? It's because the listener has +// already created the multiplexer during the call to bind(), so if it +// didn't, this function wouldn't even have a chance to be called. +// +// Why can't then the multiplexer be recorded in the 'ls' listening socket data +// to be accessed immediately, especially when one listener can't bind to more +// than one multiplexer at a time (well, even if it could, there's still no +// reason why this should be extracted by "querying")? +// +// Maybe because the multiplexer container is a map, not a list. +// Why is this then a map? Because it's addressed by MuxID. Why do we need +// mux id? Because we don't have a list... ? +// +// But what's the multiplexer ID? It's a socket ID for which it was originally +// created. +// +// Is this then shared? Yes, only between the listener socket and the accepted +// sockets, or in case of "bound" connecting sockets (by binding you can +// enforce the port number, which can be the same for multiple SRT sockets). +// Not shared in case of unbound connecting socket or rendezvous socket. +// +// Ok, in which situation do we need dispatching by mux id? Only when the +// socket is being deleted. How does the deleting procedure know the muxer id? +// Because it is recorded here at the time when it's found, as... the socket ID +// of the actual listener socket being actually the first socket to create the +// multiplexer, so the multiplexer gets its id. +// +// Still, 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. +void CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) +{ + CGuard cg(m_ControlLock); + + int port = (AF_INET == ls->m_iIPversion) + ? ntohs(((sockaddr_in*)ls->m_pSelfAddr)->sin_port) + : ntohs(((sockaddr_in6*)ls->m_pSelfAddr)->sin6_port); + + // find the listener's address + for (map::iterator i = m_mMultiplexer.begin(); + i != m_mMultiplexer.end(); ++ i) + { + if (i->second.m_iPort == port) + { + HLOGF(mglog.Debug, + "updateMux: reusing multiplexer for port %i\n", port); + // reuse the existing multiplexer + ++ i->second.m_iRefCount; + s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; + s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; + s->m_iMuxID = i->second.m_iID; + return; + } + } +} + +void* CUDTUnited::garbageCollect(void* p) +{ + CUDTUnited* self = (CUDTUnited*)p; + + THREAD_STATE_INIT("SRT:GC"); + + CGuard gcguard(self->m_GCStopLock); + + while (!self->m_bClosing) + { + INCREMENT_THREAD_ITERATIONS(); + self->checkBrokenSockets(); + + //#ifdef _WIN32 + // self->checkTLSValue(); + //#endif + + timespec timeout; +#if ENABLE_MONOTONIC_CLOCK + clock_gettime(CLOCK_MONOTONIC, &timeout); + timeout.tv_sec++; + HLOGC(mglog.Debug, log << "GC: sleep until " << FormatTime(uint64_t(timeout.tv_nsec)/1000 + 1000000*(timeout.tv_sec))); +#else + timeval now; + gettimeofday(&now, 0); + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = now.tv_usec * 1000; + + HLOGC(mglog.Debug, log << "GC: sleep until " << FormatTime(uint64_t(now.tv_usec) + 1000000*(timeout.tv_sec))); +#endif + pthread_cond_timedwait( + &self->m_GCStopCond, &self->m_GCStopLock, &timeout); + } + + // remove all sockets and multiplexers + HLOGC(mglog.Debug, log << "GC: GLOBAL EXIT - releasing all pending sockets. Acquring control lock..."); + CGuard::enterCS(self->m_ControlLock); + for (map::iterator i = self->m_Sockets.begin(); + i != self->m_Sockets.end(); ++ i) + { + i->second->m_pUDT->m_bBroken = true; + i->second->m_pUDT->close(); + i->second->m_Status = SRTS_CLOSED; + i->second->m_ClosureTimeStamp = CTimer::getTime(); + self->m_ClosedSockets[i->first] = i->second; + + // remove from listener's queue + map::iterator ls = self->m_Sockets.find( + i->second->m_ListenSocket); + if (ls == self->m_Sockets.end()) + { + ls = self->m_ClosedSockets.find(i->second->m_ListenSocket); + if (ls == self->m_ClosedSockets.end()) + continue; + } + + CGuard::enterCS(ls->second->m_AcceptLock); + ls->second->m_pQueuedSockets->erase(i->second->m_SocketID); + ls->second->m_pAcceptSockets->erase(i->second->m_SocketID); + CGuard::leaveCS(ls->second->m_AcceptLock); + } + self->m_Sockets.clear(); + + for (map::iterator j = self->m_ClosedSockets.begin(); + j != self->m_ClosedSockets.end(); ++ j) + { + j->second->m_ClosureTimeStamp = 0; + } + CGuard::leaveCS(self->m_ControlLock); + + HLOGC(mglog.Debug, log << "GC: GLOBAL EXIT - releasing all CLOSED sockets."); + while (true) + { + self->checkBrokenSockets(); + + CGuard::enterCS(self->m_ControlLock); + bool empty = self->m_ClosedSockets.empty(); + CGuard::leaveCS(self->m_ControlLock); + + if (empty) + break; + + CTimer::sleep(); + } + + THREAD_EXIT(); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// + +int CUDT::startup() +{ + return s_UDTUnited.startup(); +} + +int CUDT::cleanup() +{ + return s_UDTUnited.cleanup(); +} + +SRTSOCKET CUDT::socket(int af, int, int) +{ + if (!s_UDTUnited.m_bGCStatus) + s_UDTUnited.startup(); + + try + { + return s_UDTUnited.newSocket(af, 0); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return INVALID_SOCK; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return INVALID_SOCK; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "socket: UNEXPECTED EXCEPTION: " + << typeid(ee).name() + << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return INVALID_SOCK; + } +} + +int CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) +{ + try + { + return s_UDTUnited.bind(u, name, namelen); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "bind: UNEXPECTED EXCEPTION: " + << typeid(ee).name() + << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) +{ + try + { + return s_UDTUnited.bind(u, udpsock); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "bind/udp: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::listen(SRTSOCKET u, int backlog) +{ + try + { + return s_UDTUnited.listen(u, backlog); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "listen: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +SRTSOCKET CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) +{ + try + { + return s_UDTUnited.accept(u, addr, addrlen); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return INVALID_SOCK; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "accept: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return INVALID_SOCK; + } +} + +int CUDT::connect( + SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +{ + try + { + return s_UDTUnited.connect(u, name, namelen, forced_isn); + } + catch (const CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::close(SRTSOCKET u) +{ + try + { + return s_UDTUnited.close(u); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "close: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) +{ + try + { + return s_UDTUnited.getpeername(u, name, namelen); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "getpeername: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) +{ + try + { + return s_UDTUnited.getsockname(u, name, namelen);; + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "getsockname: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::getsockopt( + SRTSOCKET u, int, SRT_SOCKOPT optname, void* optval, int* optlen) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + udt->getOpt(optname, optval, *optlen); + return 0; + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "getsockopt: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + udt->setOpt(optname, optval, optlen); + return 0; + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "setsockopt: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::send(SRTSOCKET u, const char* buf, int len, int) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->send(buf, len); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "send: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::recv(SRTSOCKET u, char* buf, int len, int) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recv(buf, len); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "recv: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::sendmsg( + SRTSOCKET u, const char* buf, int len, int ttl, bool inorder, + uint64_t srctime) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->sendmsg(buf, len, ttl, inorder, srctime); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "sendmsg: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::sendmsg2( + SRTSOCKET u, const char* buf, int len, ref_t r_m) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->sendmsg2(buf, len, r_m); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "sendmsg: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::recvmsg(SRTSOCKET u, char* buf, int len, uint64_t& srctime) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recvmsg(buf, len, srctime); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "recvmsg: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, ref_t r_m) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recvmsg2(buf, len, r_m); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "recvmsg: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} +int64_t CUDT::sendfile( + SRTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->sendfile(ifs, offset, size, block); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "sendfile: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int64_t CUDT::recvfile( + SRTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recvfile(ofs, offset, size, block); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "recvfile: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::select( + int, + ud_set* readfds, + ud_set* writefds, + ud_set* exceptfds, + const timeval* timeout) +{ + if ((!readfds) && (!writefds) && (!exceptfds)) + { + s_UDTUnited.setError(new CUDTException(MJ_NOTSUP, MN_INVAL, 0)); + return ERROR; + } + + try + { + return s_UDTUnited.select(readfds, writefds, exceptfds, timeout); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "select: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::selectEx( + const vector& fds, + vector* readfds, + vector* writefds, + vector* exceptfds, + int64_t msTimeOut) +{ + if ((!readfds) && (!writefds) && (!exceptfds)) + { + s_UDTUnited.setError(new CUDTException(MJ_NOTSUP, MN_INVAL, 0)); + return ERROR; + } + + try + { + return s_UDTUnited.selectEx(fds, readfds, writefds, exceptfds, msTimeOut); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "selectEx: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN)); + return ERROR; + } +} + +int CUDT::epoll_create() +{ + try + { + return s_UDTUnited.epoll_create(); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_create: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) +{ + try + { + return s_UDTUnited.epoll_add_usock(eid, u, events); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_add_usock: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +{ + try + { + return s_UDTUnited.epoll_add_ssock(eid, s, events); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_add_ssock: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::epoll_update_usock( + const int eid, const SRTSOCKET u, const int* events) +{ + try + { + return s_UDTUnited.epoll_update_usock(eid, u, events); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_update_usock: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::epoll_update_ssock( + const int eid, const SYSSOCKET s, const int* events) +{ + try + { + return s_UDTUnited.epoll_update_ssock(eid, s, events); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_update_ssock: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + + +int CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) +{ + try + { + return s_UDTUnited.epoll_remove_usock(eid, u); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_remove_usock: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) +{ + try + { + return s_UDTUnited.epoll_remove_ssock(eid, s); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_remove_ssock: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::epoll_wait( + const int eid, + set* readfds, + set* writefds, + int64_t msTimeOut, + set* lrfds, + set* lwfds) +{ + try + { + return s_UDTUnited.epoll_wait( + eid, readfds, writefds, msTimeOut, lrfds, lwfds); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_wait: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::epoll_uwait( + const int eid, + SRT_EPOLL_EVENT* fdsSet, + int fdsSize, + int64_t msTimeOut) +{ + try + { + return s_UDTUnited.epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_uwait: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int32_t CUDT::epoll_set( + const int eid, + int32_t flags) +{ + try + { + return s_UDTUnited.epoll_set(eid, flags); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_set: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +int CUDT::epoll_release(const int eid) +{ + try + { + return s_UDTUnited.epoll_release(eid); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "epoll_release: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +CUDTException& CUDT::getlasterror() +{ + return *s_UDTUnited.getError(); +} + +int CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + udt->bstats(perf, clear, instantaneous); + return 0; + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "bstats: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return ERROR; + } +} + +CUDT* CUDT::getUDTHandle(SRTSOCKET u) +{ + try + { + return s_UDTUnited.lookup(u); + } + catch (const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return NULL; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "getUDTHandle: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return NULL; + } +} + +vector CUDT::existingSockets() +{ + vector out; + for (std::map::iterator i + = s_UDTUnited.m_Sockets.begin(); + i != s_UDTUnited.m_Sockets.end(); ++i) + { + out.push_back(i->first); + } + return out; +} + +SRT_SOCKSTATUS CUDT::getsockstate(SRTSOCKET u) +{ + try + { + return s_UDTUnited.getStatus(u); + } + catch (const CUDTException &e) + { + s_UDTUnited.setError(new CUDTException(e)); + return SRTS_NONEXIST; + } + catch (const std::exception& ee) + { + LOGC(mglog.Fatal, log << "getsockstate: UNEXPECTED EXCEPTION: " + << typeid(ee).name() << ": " << ee.what()); + s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0)); + return SRTS_NONEXIST; + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +namespace UDT +{ + +int startup() +{ + return CUDT::startup(); +} + +int cleanup() +{ + return CUDT::cleanup(); +} + +SRTSOCKET socket(int af, int type, int protocol) +{ + return CUDT::socket(af, type, protocol); +} + +int bind(SRTSOCKET u, const struct sockaddr* name, int namelen) +{ + return CUDT::bind(u, name, namelen); +} + +int bind2(SRTSOCKET u, UDPSOCKET udpsock) +{ + return CUDT::bind(u, udpsock); +} + +int listen(SRTSOCKET u, int backlog) +{ + return CUDT::listen(u, backlog); +} + +SRTSOCKET accept(SRTSOCKET u, struct sockaddr* addr, int* addrlen) +{ + return CUDT::accept(u, addr, addrlen); +} + +int connect(SRTSOCKET u, const struct sockaddr* name, int namelen) +{ + return CUDT::connect(u, name, namelen, 0); +} + +int close(SRTSOCKET u) +{ + return CUDT::close(u); +} + +int getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen) +{ + return CUDT::getpeername(u, name, namelen); +} + +int getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen) +{ + return CUDT::getsockname(u, name, namelen); +} + +int getsockopt( + SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen) +{ + return CUDT::getsockopt(u, level, optname, optval, optlen); +} + +int setsockopt( + SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen) +{ + return CUDT::setsockopt(u, level, optname, optval, optlen); +} + +// DEVELOPER API + +int connect_debug( + SRTSOCKET u, const struct sockaddr* name, int namelen, int32_t forced_isn) +{ + return CUDT::connect(u, name, namelen, forced_isn); +} + +int send(SRTSOCKET u, const char* buf, int len, int flags) +{ + return CUDT::send(u, buf, len, flags); +} + +int recv(SRTSOCKET u, char* buf, int len, int flags) +{ + return CUDT::recv(u, buf, len, flags); +} + + +int sendmsg( + SRTSOCKET u, const char* buf, int len, int ttl, bool inorder, + uint64_t srctime) +{ + return CUDT::sendmsg(u, buf, len, ttl, inorder, srctime); +} + +int recvmsg(SRTSOCKET u, char* buf, int len, uint64_t& srctime) +{ + return CUDT::recvmsg(u, buf, len, srctime); +} + +int recvmsg(SRTSOCKET u, char* buf, int len) +{ + uint64_t srctime; + + return CUDT::recvmsg(u, buf, len, srctime); +} + +int64_t sendfile( + SRTSOCKET u, + fstream& ifs, + int64_t& offset, + int64_t size, + int block) +{ + return CUDT::sendfile(u, ifs, offset, size, block); +} + +int64_t recvfile( + SRTSOCKET u, + fstream& ofs, + int64_t& offset, + int64_t size, + int block) +{ + return CUDT::recvfile(u, ofs, offset, size, block); +} + +int64_t sendfile2( + SRTSOCKET u, + const char* path, + int64_t* offset, + int64_t size, + int block) +{ + fstream ifs(path, ios::binary | ios::in); + int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block); + ifs.close(); + return ret; +} + +int64_t recvfile2( + SRTSOCKET u, + const char* path, + int64_t* offset, + int64_t size, + int block) +{ + fstream ofs(path, ios::binary | ios::out); + int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block); + ofs.close(); + return ret; +} + +int select( + int nfds, + UDSET* readfds, + UDSET* writefds, + UDSET* exceptfds, + const struct timeval* timeout) +{ + return CUDT::select(nfds, readfds, writefds, exceptfds, timeout); +} + +int selectEx( + const vector& fds, + vector* readfds, + vector* writefds, + vector* exceptfds, + int64_t msTimeOut) +{ + return CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut); +} + +int epoll_create() +{ + return CUDT::epoll_create(); +} + +int epoll_add_usock(int eid, SRTSOCKET u, const int* events) +{ + return CUDT::epoll_add_usock(eid, u, events); +} + +int epoll_add_ssock(int eid, SYSSOCKET s, const int* events) +{ + return CUDT::epoll_add_ssock(eid, s, events); +} + +int epoll_update_usock(int eid, SRTSOCKET u, const int* events) +{ + return CUDT::epoll_update_usock(eid, u, events); +} + +int epoll_update_ssock(int eid, SYSSOCKET s, const int* events) +{ + return CUDT::epoll_update_ssock(eid, s, events); +} + +int epoll_remove_usock(int eid, SRTSOCKET u) +{ + return CUDT::epoll_remove_usock(eid, u); +} + +int epoll_remove_ssock(int eid, SYSSOCKET s) +{ + return CUDT::epoll_remove_ssock(eid, s); +} + +int epoll_wait( + int eid, + set* readfds, + set* writefds, + int64_t msTimeOut, + set* lrfds, + set* lwfds) +{ + return CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); +} + +/* + +#define SET_RESULT(val, num, fds, it) \ + if (val != NULL) \ + { \ + if (val->empty()) \ + { \ + if (num) *num = 0; \ + } \ + else \ + { \ + if (*num > static_cast(val->size())) \ + *num = val->size(); \ + int count = 0; \ + for (it = val->begin(); it != val->end(); ++ it) \ + { \ + if (count >= *num) \ + break; \ + fds[count ++] = *it; \ + } \ + } \ + } + +*/ + +template +inline void set_result(set* val, int* num, SOCKTYPE* fds) +{ + if ( !val || !num || !fds ) + return; + + if (*num > int(val->size())) + *num = int(val->size()); // will get 0 if val->empty() + int count = 0; + + // This loop will run 0 times if val->empty() + for (typename set::const_iterator it = val->begin(); it != val->end(); ++ it) + { + if (count >= *num) + break; + fds[count ++] = *it; + } +} + +int epoll_wait2( + int eid, SRTSOCKET* readfds, + int* rnum, SRTSOCKET* writefds, + int* wnum, + int64_t msTimeOut, + SYSSOCKET* lrfds, + int* lrnum, + SYSSOCKET* lwfds, + int* lwnum) +{ + // This API is an alternative format for epoll_wait, created for + // compatability with other languages. Users need to pass in an array + // for holding the returned sockets, with the maximum array length + // stored in *rnum, etc., which will be updated with returned number + // of sockets. + + set readset; + set writeset; + set lrset; + set lwset; + set* rval = NULL; + set* wval = NULL; + set* lrval = NULL; + set* lwval = NULL; + if ((readfds != NULL) && (rnum != NULL)) + rval = &readset; + if ((writefds != NULL) && (wnum != NULL)) + wval = &writeset; + if ((lrfds != NULL) && (lrnum != NULL)) + lrval = &lrset; + if ((lwfds != NULL) && (lwnum != NULL)) + lwval = &lwset; + + int ret = CUDT::epoll_wait(eid, rval, wval, msTimeOut, lrval, lwval); + if (ret > 0) + { + //set::const_iterator i; + //SET_RESULT(rval, rnum, readfds, i); + set_result(rval, rnum, readfds); + //SET_RESULT(wval, wnum, writefds, i); + set_result(wval, wnum, writefds); + + //set::const_iterator j; + //SET_RESULT(lrval, lrnum, lrfds, j); + set_result(lrval, lrnum, lrfds); + //SET_RESULT(lwval, lwnum, lwfds, j); + set_result(lwval, lwnum, lwfds); + } + return ret; +} + +int epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut) +{ + return CUDT::epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); +} + +int epoll_release(int eid) +{ + return CUDT::epoll_release(eid); +} + +ERRORINFO& getlasterror() +{ + return CUDT::getlasterror(); +} + +int getlasterror_code() +{ + return CUDT::getlasterror().getErrorCode(); +} + +const char* getlasterror_desc() +{ + return CUDT::getlasterror().getErrorMessage(); +} + +int getlasterror_errno() +{ + return CUDT::getlasterror().getErrno(); +} + +// Get error string of a given error code +const char* geterror_desc(int code, int err) +{ + CUDTException e (CodeMajor(code/1000), CodeMinor(code%1000), err); + return(e.getErrorMessage()); +} + +int bstats(SRTSOCKET u, TRACEBSTATS* perf, bool clear) +{ + return CUDT::bstats(u, perf, clear); +} + +SRT_SOCKSTATUS getsockstate(SRTSOCKET u) +{ + return CUDT::getsockstate(u); +} + +void setloglevel(LogLevel::type ll) +{ + CGuard gg(srt_logger_config.mutex); + srt_logger_config.max_level = ll; +} + +void addlogfa(LogFA fa) +{ + CGuard gg(srt_logger_config.mutex); + srt_logger_config.enabled_fa.set(fa, true); +} + +void dellogfa(LogFA fa) +{ + CGuard gg(srt_logger_config.mutex); + srt_logger_config.enabled_fa.set(fa, false); +} + +void resetlogfa(set fas) +{ + CGuard gg(srt_logger_config.mutex); + for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) + srt_logger_config.enabled_fa.set(i, fas.count(i)); +} + +void resetlogfa(const int* fara, size_t fara_size) +{ + CGuard gg(srt_logger_config.mutex); + srt_logger_config.enabled_fa.reset(); + for (const int* i = fara; i != fara + fara_size; ++i) + srt_logger_config.enabled_fa.set(*i, true); +} + +void setlogstream(std::ostream& stream) +{ + CGuard gg(srt_logger_config.mutex); + srt_logger_config.log_stream = &stream; +} + +void setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler) +{ + CGuard gg(srt_logger_config.mutex); + srt_logger_config.loghandler_opaque = opaque; + srt_logger_config.loghandler_fn = handler; +} + +void setlogflags(int flags) +{ + CGuard gg(srt_logger_config.mutex); + srt_logger_config.flags = flags; +} + +SRT_API bool setstreamid(SRTSOCKET u, const std::string& sid) +{ + return CUDT::setstreamid(u, sid); +} +SRT_API std::string getstreamid(SRTSOCKET u) +{ + return CUDT::getstreamid(u); +} + +SRT_REJECT_REASON getrejectreason(SRTSOCKET u) +{ + return CUDT::rejectReason(u); +} + +} // namespace UDT diff --git a/trunk/3rdparty/srt-1-fit/srtcore/api.h b/trunk/3rdparty/srt-1-fit/srtcore/api.h new file mode 100644 index 000000000..7ee6293ea --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/api.h @@ -0,0 +1,300 @@ +/* + * 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 - 2010, 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 09/28/2010 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef __UDT_API_H__ +#define __UDT_API_H__ + + +#include +#include +#include +#include "netinet_any.h" +#include "udt.h" +#include "packet.h" +#include "queue.h" +#include "cache.h" +#include "epoll.h" +#include "handshake.h" + +class CUDT; + +class CUDTSocket +{ +public: + CUDTSocket(); + ~CUDTSocket(); + + SRT_SOCKSTATUS m_Status; //< current socket state + + /// 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; + + 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 + + SRTSOCKET m_SocketID; //< socket ID + SRTSOCKET m_ListenSocket; //< ID of the listener socket; 0 means this is an independent socket + + SRTSOCKET m_PeerID; //< peer socket ID + int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port + + CUDT* m_pUDT; //< pointer to the UDT entity + + std::set* m_pQueuedSockets; //< set of connections waiting for accept() + std::set* m_pAcceptSockets; //< set of accept()ed connections + + pthread_cond_t m_AcceptCond; //< used to block "accept" call + pthread_mutex_t m_AcceptLock; //< mutex associated to m_AcceptCond + + 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); + } + +private: + CUDTSocket(const CUDTSocket&); + CUDTSocket& operator=(const CUDTSocket&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CUDTUnited +{ +friend class CUDT; +friend class CRendezvousQueue; + +public: + CUDTUnited(); + ~CUDTUnited(); + +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 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& fds, std::vector* readfds, std::vector* writefds, std::vector* 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* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* 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 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 > 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 m_mMultiplexer; // UDP multiplexer + pthread_mutex_t m_MultiplexerLock; + +private: + CCache* 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 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)) + { + output << hostbuf; + } + + output << ":" << ntohs(((sockaddr_in*)sadr)->sin_port); // TRICK: sin_port and sin6_port have the same offset and size + return output.str(); +} + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp b/trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp new file mode 100644 index 000000000..1d46b688f --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp @@ -0,0 +1,1955 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 03/12/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#include +#include +#include "buffer.h" +#include "packet.h" +#include "core.h" // provides some constants +#include "logging.h" + +using namespace std; +using namespace srt_logging; + +CSndBuffer::CSndBuffer(int size, int mss) + : m_BufLock() + , m_pBlock(NULL) + , m_pFirstBlock(NULL) + , m_pCurrBlock(NULL) + , m_pLastBlock(NULL) + , m_pBuffer(NULL) + , m_iNextMsgNo(1) + , m_iSize(size) + , m_iMSS(mss) + , m_iCount(0) + , m_iBytesCount(0) + , m_ullLastOriginTime_us(0) +#ifdef SRT_ENABLE_SNDBUFSZ_MAVG + , m_LastSamplingTime(0) + , m_iCountMAvg(0) + , m_iBytesCountMAvg(0) + , m_TimespanMAvg(0) +#endif + , m_iInRatePktsCount(0) + , m_iInRateBytesCount(0) + , m_InRateStartTime(0) + , m_InRatePeriod(INPUTRATE_FAST_START_US) // 0.5 sec (fast start) + , m_iInRateBps(INPUTRATE_INITIAL_BYTESPS) +{ + // initial physical buffer of "size" + m_pBuffer = new Buffer; + m_pBuffer->m_pcData = new char [m_iSize * m_iMSS]; + m_pBuffer->m_iSize = m_iSize; + m_pBuffer->m_pNext = NULL; + + // circular linked list for out bound packets + m_pBlock = new Block; + Block* pb = m_pBlock; + for (int i = 1; i < m_iSize; ++ i) + { + pb->m_pNext = new Block; + pb->m_iMsgNoBitset = 0; + pb = pb->m_pNext; + } + pb->m_pNext = m_pBlock; + + pb = m_pBlock; + char* pc = m_pBuffer->m_pcData; + for (int i = 0; i < m_iSize; ++ i) + { + pb->m_pcData = pc; + pb = pb->m_pNext; + pc += m_iMSS; + } + + m_pFirstBlock = m_pCurrBlock = m_pLastBlock = m_pBlock; + + pthread_mutex_init(&m_BufLock, NULL); +} + +CSndBuffer::~CSndBuffer() +{ + Block* pb = m_pBlock->m_pNext; + while (pb != m_pBlock) + { + Block* temp = pb; + pb = pb->m_pNext; + delete temp; + } + delete m_pBlock; + + while (m_pBuffer != NULL) + { + Buffer* temp = m_pBuffer; + m_pBuffer = m_pBuffer->m_pNext; + delete [] temp->m_pcData; + delete temp; + } + + pthread_mutex_destroy(&m_BufLock); +} + +void CSndBuffer::addBuffer(const char* data, int len, int ttl, bool order, uint64_t srctime, ref_t r_msgno) +{ + int32_t& msgno = *r_msgno; + + int size = len / m_iMSS; + if ((len % m_iMSS) != 0) + size ++; + + HLOGC(mglog.Debug, log << "addBuffer: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << size << " buffers for " << len << " bytes"); + + // dynamically increase sender buffer + while (size + m_iCount >= m_iSize) + { + HLOGC(mglog.Debug, log << "addBuffer: ... still lacking " << (size + m_iCount - m_iSize) << " buffers..."); + increase(); + } + + const uint64_t time = CTimer::getTime(); + int32_t inorder = order ? MSGNO_PACKET_INORDER::mask : 0; + + HLOGC(dlog.Debug, log << CONID() << "addBuffer: adding " + << size << " packets (" << len << " bytes) to send, msgno=" << m_iNextMsgNo + << (inorder ? "" : " NOT") << " in order"); + + Block* s = m_pLastBlock; + msgno = m_iNextMsgNo; + for (int i = 0; i < size; ++ i) + { + int pktlen = len - i * m_iMSS; + if (pktlen > m_iMSS) + pktlen = m_iMSS; + + HLOGC(dlog.Debug, log << "addBuffer: spreading from=" << (i*m_iMSS) << " size=" << pktlen << " TO BUFFER:" << (void*)s->m_pcData); + memcpy(s->m_pcData, data + i * m_iMSS, pktlen); + s->m_iLength = pktlen; + + s->m_iMsgNoBitset = m_iNextMsgNo | inorder; + if (i == 0) + s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST); + if (i == size - 1) + s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST); + // NOTE: if i is neither 0 nor size-1, it resuls with PB_SUBSEQUENT. + // if i == 0 == size-1, it results with PB_SOLO. + // Packets assigned to one message can be: + // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] - 4 packets per message + // [PB_FIRST] [PB_LAST] - 2 packets per message + // [PB_SOLO] - 1 packet per message + + s->m_ullSourceTime_us = srctime; + s->m_ullOriginTime_us = time; + s->m_iTTL = ttl; + + // XXX unchecked condition: s->m_pNext == NULL. + // Should never happen, as the call to increase() should ensure enough buffers. + SRT_ASSERT(s->m_pNext); + s = s->m_pNext; + } + m_pLastBlock = s; + + CGuard::enterCS(m_BufLock); + m_iCount += size; + + m_iBytesCount += len; + m_ullLastOriginTime_us = time; + + updateInputRate(time, size, len); + +#ifdef SRT_ENABLE_SNDBUFSZ_MAVG + updAvgBufSize(time); +#endif + + CGuard::leaveCS(m_BufLock); + + + // MSGNO_SEQ::mask has a form: 00000011111111... + // At least it's known that it's from some index inside til the end (to bit 0). + // If this value has been reached in a step of incrementation, it means that the + // maximum value has been reached. Casting to int32_t to ensure the same sign + // in comparison, although it's far from reaching the sign bit. + + m_iNextMsgNo ++; + if (m_iNextMsgNo == int32_t(MSGNO_SEQ::mask)) + m_iNextMsgNo = 1; +} + +void CSndBuffer::setInputRateSmpPeriod(int period) +{ + m_InRatePeriod = (uint64_t)period; //(usec) 0=no input rate calculation +} + +void CSndBuffer::updateInputRate(uint64_t time, int pkts, int bytes) +{ + //no input rate calculation + if (m_InRatePeriod == 0) + return; + + if (m_InRateStartTime == 0) + { + m_InRateStartTime = time; + return; + } + + m_iInRatePktsCount += pkts; + m_iInRateBytesCount += bytes; + + // Trigger early update in fast start mode + const bool early_update = (m_InRatePeriod < INPUTRATE_RUNNING_US) + && (m_iInRatePktsCount > INPUTRATE_MAX_PACKETS); + + const uint64_t period_us = (time - m_InRateStartTime); + if (early_update || period_us > m_InRatePeriod) + { + //Required Byte/sec rate (payload + headers) + m_iInRateBytesCount += (m_iInRatePktsCount * CPacket::SRT_DATA_HDR_SIZE); + m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us); + HLOGC(dlog.Debug, log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount + << " rate=" << (m_iInRateBps*8)/1000 + << "kbps interval=" << period_us); + m_iInRatePktsCount = 0; + m_iInRateBytesCount = 0; + m_InRateStartTime = time; + + setInputRateSmpPeriod(INPUTRATE_RUNNING_US); + } +} + + +int CSndBuffer::addBufferFromFile(fstream& ifs, int len) +{ + int size = len / m_iMSS; + if ((len % m_iMSS) != 0) + size ++; + + HLOGC(mglog.Debug, log << "addBufferFromFile: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << size << " buffers for " << len << " bytes"); + + // dynamically increase sender buffer + while (size + m_iCount >= m_iSize) + { + HLOGC(mglog.Debug, log << "addBufferFromFile: ... still lacking " << (size + m_iCount - m_iSize) << " buffers..."); + increase(); + } + + HLOGC(dlog.Debug, log << CONID() << "addBufferFromFile: adding " + << size << " packets (" << len << " bytes) to send, msgno=" << m_iNextMsgNo); + + Block* s = m_pLastBlock; + int total = 0; + for (int i = 0; i < size; ++ i) + { + if (ifs.bad() || ifs.fail() || ifs.eof()) + break; + + int pktlen = len - i * m_iMSS; + if (pktlen > m_iMSS) + pktlen = m_iMSS; + + HLOGC(dlog.Debug, log << "addBufferFromFile: reading from=" << (i*m_iMSS) << " size=" << pktlen << " TO BUFFER:" << (void*)s->m_pcData); + ifs.read(s->m_pcData, pktlen); + if ((pktlen = int(ifs.gcount())) <= 0) + break; + + // currently file transfer is only available in streaming mode, message is always in order, ttl = infinite + s->m_iMsgNoBitset = m_iNextMsgNo | MSGNO_PACKET_INORDER::mask; + if (i == 0) + s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST); + if (i == size - 1) + s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST); + // NOTE: PB_FIRST | PB_LAST == PB_SOLO. + // none of PB_FIRST & PB_LAST == PB_SUBSEQUENT. + + s->m_iLength = pktlen; + s->m_iTTL = -1; + s = s->m_pNext; + + total += pktlen; + } + m_pLastBlock = s; + + CGuard::enterCS(m_BufLock); + m_iCount += size; + m_iBytesCount += total; + + CGuard::leaveCS(m_BufLock); + + m_iNextMsgNo ++; + if (m_iNextMsgNo == int32_t(MSGNO_SEQ::mask)) + m_iNextMsgNo = 1; + + return total; +} + +int CSndBuffer::readData(char** data, int32_t& msgno_bitset, uint64_t& srctime, int kflgs) +{ + // No data to read + if (m_pCurrBlock == m_pLastBlock) + return 0; + + // Make the packet REFLECT the data stored in the buffer. + *data = m_pCurrBlock->m_pcData; + int readlen = m_pCurrBlock->m_iLength; + + // XXX This is probably done because the encryption should happen + // just once, and so this sets the encryption flags to both msgno bitset + // IN THE PACKET and IN THE BLOCK. This is probably to make the encryption + // happen at the time when scheduling a new packet to send, but the packet + // must remain in the send buffer until it's ACKed. For the case of rexmit + // the packet will be taken "as is" (that is, already encrypted). + // + // The problem is in the order of things: + // 0. When the application stores the data, some of the flags for PH_MSGNO are set. + // 1. The readData() is called to get the original data sent by the application. + // 2. The data are original and must be encrypted. They WILL BE encrypted, later. + // 3. So far we are in readData() so the encryption flags must be updated NOW because + // later we won't have access to the block's data. + // 4. After exiting from readData(), the packet is being encrypted. It's immediately + // sent, however the data must remain in the sending buffer until they are ACKed. + // 5. In case when rexmission is needed, the second overloaded version of readData + // is being called, and the buffer + PH_MSGNO value is extracted. All interesting + // flags must be present and correct at that time. + // + // The only sensible way to fix this problem is to encrypt the packet not after + // extracting from here, but when the packet is stored into CSndBuffer. The appropriate + // flags for PH_MSGNO will be applied directly there. Then here the value for setting + // PH_MSGNO will be set as is. + + if (kflgs == -1) + { + HLOGC(dlog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING."); + readlen = 0; + } + else + { + m_pCurrBlock->m_iMsgNoBitset |= MSGNO_ENCKEYSPEC::wrap(kflgs); + } + msgno_bitset = m_pCurrBlock->m_iMsgNoBitset; + + srctime = + m_pCurrBlock->m_ullSourceTime_us ? m_pCurrBlock->m_ullSourceTime_us : + m_pCurrBlock->m_ullOriginTime_us; + + m_pCurrBlock = m_pCurrBlock->m_pNext; + + HLOGC(dlog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send"); + + return readlen; +} + +int CSndBuffer::readData(char** data, const int offset, int32_t& msgno_bitset, uint64_t& srctime, int& msglen) +{ + CGuard bufferguard(m_BufLock); + + Block* p = m_pFirstBlock; + + // XXX Suboptimal procedure to keep the blocks identifiable + // by sequence number. Consider using some circular buffer. + for (int i = 0; i < offset; ++ i) + p = p->m_pNext; + + // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. + + // If so, then inform the caller that it should first take care of the whole + // message (all blocks with that message id). Shift the m_pCurrBlock pointer + // to the position past the last of them. Then return -1 and set the + // msgno_bitset return reference to the message id that should be dropped as + // a whole. + + // After taking care of that, the caller should immediately call this function again, + // this time possibly in order to find the real data to be sent. + + // if found block is stale + // (This is for messages that have declared TTL - messages that fail to be sent + // before the TTL defined time comes, will be dropped). + if ((p->m_iTTL >= 0) && ((CTimer::getTime() - p->m_ullOriginTime_us) / 1000 > (uint64_t)p->m_iTTL)) + { + int32_t msgno = p->getMsgSeq(); + msglen = 1; + p = p->m_pNext; + bool move = false; + while (msgno == p->getMsgSeq()) + { + if (p == m_pCurrBlock) + move = true; + p = p->m_pNext; + if (move) + m_pCurrBlock = p; + msglen ++; + } + + HLOGC(dlog.Debug, log << "CSndBuffer::readData: due to TTL exceeded, " << msglen << " messages to drop, up to " << msgno); + + // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. + // This means that in this case it should be written by the message sequence value only + // (not the whole 4-byte bitset written at PH_MSGNO). + msgno_bitset = msgno; + return -1; + } + + *data = p->m_pcData; + int readlen = p->m_iLength; + + // XXX Here the value predicted to be applied to PH_MSGNO field is extracted. + // As this function is predicted to extract the data to send as a rexmited packet, + // the packet must be in the form ready to send - so, in case of encryption, + // encrypted, and with all ENC flags already set. So, the first call to send + // the packet originally (the other overload of this function) must set these + // flags. + msgno_bitset = p->m_iMsgNoBitset; + + srctime = + p->m_ullSourceTime_us ? p->m_ullSourceTime_us : + p->m_ullOriginTime_us; + + HLOGC(dlog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send [REXMIT]"); + + return readlen; +} + +void CSndBuffer::ackData(int offset) +{ + CGuard bufferguard(m_BufLock); + + bool move = false; + for (int i = 0; i < offset; ++ i) + { + m_iBytesCount -= m_pFirstBlock->m_iLength; + if (m_pFirstBlock == m_pCurrBlock) + move = true; + m_pFirstBlock = m_pFirstBlock->m_pNext; + } + if (move) + m_pCurrBlock = m_pFirstBlock; + + m_iCount -= offset; + +#ifdef SRT_ENABLE_SNDBUFSZ_MAVG + updAvgBufSize(CTimer::getTime()); +#endif + + CTimer::triggerEvent(); +} + +int CSndBuffer::getCurrBufSize() const +{ + return m_iCount; +} + +#ifdef SRT_ENABLE_SNDBUFSZ_MAVG + +int CSndBuffer::getAvgBufSize(ref_t r_bytes, ref_t r_tsp) +{ + int& bytes = *r_bytes; + int& timespan = *r_tsp; + CGuard bufferguard(m_BufLock); /* Consistency of pkts vs. bytes vs. spantime */ + + /* update stats in case there was no add/ack activity lately */ + updAvgBufSize(CTimer::getTime()); + + bytes = m_iBytesCountMAvg; + timespan = m_TimespanMAvg; + return(m_iCountMAvg); +} + +void CSndBuffer::updAvgBufSize(uint64_t now) +{ + const uint64_t elapsed_ms = (now - m_LastSamplingTime) / 1000; //ms since last sampling + + if ((1000000 / SRT_MAVG_SAMPLING_RATE) / 1000 > elapsed_ms) + return; + + if (1000 < elapsed_ms) + { + /* No sampling in last 1 sec, initialize average */ + m_iCountMAvg = getCurrBufSize(Ref(m_iBytesCountMAvg), Ref(m_TimespanMAvg)); + m_LastSamplingTime = now; + } + else //((1000000 / SRT_MAVG_SAMPLING_RATE) / 1000 <= elapsed_ms) + { + /* + * weight last average value between -1 sec and last sampling time (LST) + * and new value between last sampling time and now + * |elapsed_ms| + * +----------------------------------+-------+ + * -1 LST 0(now) + */ + int instspan; + int bytescount; + int count = getCurrBufSize(Ref(bytescount), Ref(instspan)); + + HLOGC(dlog.Debug, log << "updAvgBufSize: " << elapsed_ms + << ": " << count << " " << bytescount + << " " << instspan << "ms"); + + m_iCountMAvg = (int)(((count * (1000 - elapsed_ms)) + (count * elapsed_ms)) / 1000); + m_iBytesCountMAvg = (int)(((bytescount * (1000 - elapsed_ms)) + (bytescount * elapsed_ms)) / 1000); + m_TimespanMAvg = (int)(((instspan * (1000 - elapsed_ms)) + (instspan * elapsed_ms)) / 1000); + m_LastSamplingTime = now; + } +} + +#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */ + +int CSndBuffer::getCurrBufSize(ref_t bytes, ref_t timespan) +{ + *bytes = m_iBytesCount; + /* + * Timespan can be less then 1000 us (1 ms) if few packets. + * Also, if there is only one pkt in buffer, the time difference will be 0. + * Therefore, always add 1 ms if not empty. + */ + *timespan = 0 < m_iCount ? int((m_ullLastOriginTime_us - m_pFirstBlock->m_ullOriginTime_us) / 1000) + 1 : 0; + + return m_iCount; +} + +int CSndBuffer::dropLateData(int &bytes, uint64_t latetime) +{ + int dpkts = 0; + int dbytes = 0; + bool move = false; + + CGuard bufferguard(m_BufLock); + for (int i = 0; i < m_iCount && m_pFirstBlock->m_ullOriginTime_us < latetime; ++ i) + { + dpkts++; + dbytes += m_pFirstBlock->m_iLength; + + if (m_pFirstBlock == m_pCurrBlock) move = true; + m_pFirstBlock = m_pFirstBlock->m_pNext; + } + if (move) m_pCurrBlock = m_pFirstBlock; + m_iCount -= dpkts; + + m_iBytesCount -= dbytes; + bytes = dbytes; + +#ifdef SRT_ENABLE_SNDBUFSZ_MAVG + updAvgBufSize(CTimer::getTime()); +#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */ + +// CTimer::triggerEvent(); + return(dpkts); +} + +void CSndBuffer::increase() +{ + int unitsize = m_pBuffer->m_iSize; + + // new physical buffer + Buffer* nbuf = NULL; + try + { + nbuf = new Buffer; + nbuf->m_pcData = new char [unitsize * m_iMSS]; + } + catch (...) + { + delete nbuf; + throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + } + nbuf->m_iSize = unitsize; + nbuf->m_pNext = NULL; + + // insert the buffer at the end of the buffer list + Buffer* p = m_pBuffer; + while (p->m_pNext != NULL) + p = p->m_pNext; + p->m_pNext = nbuf; + + // new packet blocks + Block* nblk = NULL; + try + { + nblk = new Block; + } + catch (...) + { + delete nblk; + throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + } + Block* pb = nblk; + for (int i = 1; i < unitsize; ++ i) + { + pb->m_pNext = new Block; + pb = pb->m_pNext; + } + + // insert the new blocks onto the existing one + pb->m_pNext = m_pLastBlock->m_pNext; + m_pLastBlock->m_pNext = nblk; + + pb = nblk; + char* pc = nbuf->m_pcData; + for (int i = 0; i < unitsize; ++ i) + { + pb->m_pcData = pc; + pb = pb->m_pNext; + pc += m_iMSS; + } + + m_iSize += unitsize; + + HLOGC(dlog.Debug, log << "CSndBuffer: BUFFER FULL - adding " << (unitsize*m_iMSS) << " bytes spread to " << unitsize << " blocks" + << " (total size: " << m_iSize << " bytes)"); + +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +* RcvBuffer (circular buffer): +* +* |<------------------- m_iSize ----------------------------->| +* | |<--- acked pkts -->|<--- m_iMaxPos --->| | +* | | | | | +* +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +* | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] +* +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +* | | | | +* | | \__last pkt received +* | \___ m_iLastAckPos: last ack sent +* \___ m_iStartPos: first message to read +* +* m_pUnit[i]->m_iFlag: 0:free, 1:good, 2:passack, 3:dropped +* +* thread safety: +* m_iStartPos: CUDT::m_RecvLock +* m_iLastAckPos: CUDT::m_AckLock +* m_iMaxPos: none? (modified on add and ack +*/ + + +// XXX Init values moved to in-class. +//const uint32_t CRcvBuffer::TSBPD_WRAP_PERIOD = (30*1000000); //30 seconds (in usec) +//const int CRcvBuffer::TSBPD_DRIFT_MAX_VALUE = 5000; // usec +//const int CRcvBuffer::TSBPD_DRIFT_MAX_SAMPLES = 1000; // ACK-ACK packets +#ifdef SRT_DEBUG_TSBPD_DRIFT +//const int CRcvBuffer::TSBPD_DRIFT_PRT_SAMPLES = 200; // ACK-ACK packets +#endif + +CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize_pkts): +m_pUnit(NULL), +m_iSize(bufsize_pkts), +m_pUnitQueue(queue), +m_iStartPos(0), +m_iLastAckPos(0), +m_iMaxPos(0), +m_iNotch(0) +,m_BytesCountLock() +,m_iBytesCount(0) +,m_iAckedPktsCount(0) +,m_iAckedBytesCount(0) +,m_iAvgPayloadSz(7*188) +,m_bTsbPdMode(false) +,m_uTsbPdDelay(0) +,m_ullTsbPdTimeBase(0) +,m_bTsbPdWrapCheck(false) +//,m_iTsbPdDrift(0) +//,m_TsbPdDriftSum(0) +//,m_iTsbPdDriftNbSamples(0) +#ifdef SRT_ENABLE_RCVBUFSZ_MAVG +,m_LastSamplingTime(0) +,m_TimespanMAvg(0) +,m_iCountMAvg(0) +,m_iBytesCountMAvg(0) +#endif +{ + m_pUnit = new CUnit* [m_iSize]; + for (int i = 0; i < m_iSize; ++ i) + m_pUnit[i] = NULL; + +#ifdef SRT_DEBUG_TSBPD_DRIFT + memset(m_TsbPdDriftHisto100us, 0, sizeof(m_TsbPdDriftHisto100us)); + memset(m_TsbPdDriftHisto1ms, 0, sizeof(m_TsbPdDriftHisto1ms)); +#endif + + pthread_mutex_init(&m_BytesCountLock, NULL); +} + +CRcvBuffer::~CRcvBuffer() +{ + for (int i = 0; i < m_iSize; ++ i) + { + if (m_pUnit[i] != NULL) + { + m_pUnitQueue->makeUnitFree(m_pUnit[i]); + } + } + + delete [] m_pUnit; + + pthread_mutex_destroy(&m_BytesCountLock); +} + +void CRcvBuffer::countBytes(int pkts, int bytes, bool acked) +{ + /* + * Byte counter changes from both sides (Recv & Ack) of the buffer + * so the higher level lock is not enough for thread safe op. + * + * pkts are... + * added (bytes>0, acked=false), + * acked (bytes>0, acked=true), + * removed (bytes<0, acked=n/a) + */ + CGuard cg(m_BytesCountLock); + + if (!acked) //adding new pkt in RcvBuffer + { + m_iBytesCount += bytes; /* added or removed bytes from rcv buffer */ + if (bytes > 0) /* Assuming one pkt when adding bytes */ + m_iAvgPayloadSz = ((m_iAvgPayloadSz * (100 - 1)) + bytes) / 100; + } + else // acking/removing pkts to/from buffer + { + m_iAckedPktsCount += pkts; /* acked or removed pkts from rcv buffer */ + m_iAckedBytesCount += bytes; /* acked or removed bytes from rcv buffer */ + + if (bytes < 0) m_iBytesCount += bytes; /* removed bytes from rcv buffer */ + } +} + +int CRcvBuffer::addData(CUnit* unit, int offset) +{ + SRT_ASSERT(unit != NULL); + if (offset >= getAvailBufSize()) + return -1; + + const int pos = (m_iLastAckPos + offset) % m_iSize; + if (offset >= m_iMaxPos) + m_iMaxPos = offset + 1; + + if (m_pUnit[pos] != NULL) { + HLOGC(dlog.Debug, log << "addData: unit %" << unit->m_Packet.m_iSeqNo + << " rejected, already exists"); + return -1; + } + m_pUnit[pos] = unit; + countBytes(1, (int) unit->m_Packet.getLength()); + + m_pUnitQueue->makeUnitGood(unit); + + HLOGC(dlog.Debug, log << "addData: unit %" << unit->m_Packet.m_iSeqNo + << " accepted, off=" << offset << " POS=" << pos); + return 0; +} + +int CRcvBuffer::readBuffer(char* data, int len) +{ + int p = m_iStartPos; + int lastack = m_iLastAckPos; + int rs = len; +#if ENABLE_HEAVY_LOGGING + char* begin = data; +#endif + + const uint64_t now = (m_bTsbPdMode ? CTimer::getTime() : uint64_t()); + + HLOGC(dlog.Debug, log << CONID() << "readBuffer: start=" << p << " lastack=" << lastack); + while ((p != lastack) && (rs > 0)) + { + if (m_pUnit[p] == NULL) + { + LOGC(dlog.Error, log << CONID() << " IPE readBuffer on null packet pointer"); + return -1; + } + + if (m_bTsbPdMode) + { + HLOGC(dlog.Debug, log << CONID() << "readBuffer: chk if time2play: NOW=" << now << " PKT TS=" << getPktTsbPdTime(m_pUnit[p]->m_Packet.getMsgTimeStamp())); + if ((getPktTsbPdTime(m_pUnit[p]->m_Packet.getMsgTimeStamp()) > now)) + break; /* too early for this unit, return whatever was copied */ + } + + int unitsize = (int) m_pUnit[p]->m_Packet.getLength() - m_iNotch; + if (unitsize > rs) + unitsize = rs; + + HLOGC(dlog.Debug, log << CONID() << "readBuffer: copying buffer #" << p + << " targetpos=" << int(data-begin) << " sourcepos=" << m_iNotch << " size=" << unitsize << " left=" << (unitsize-rs)); + memcpy(data, m_pUnit[p]->m_Packet.m_pcData + m_iNotch, unitsize); + data += unitsize; + + if ((rs > unitsize) || (rs == int(m_pUnit[p]->m_Packet.getLength()) - m_iNotch)) + { + CUnit* tmp = m_pUnit[p]; + m_pUnit[p] = NULL; + m_pUnitQueue->makeUnitFree(tmp); + + if (++ p == m_iSize) + p = 0; + + m_iNotch = 0; + } + else + m_iNotch += rs; + + rs -= unitsize; + } + + /* we removed acked bytes form receive buffer */ + countBytes(-1, -(len - rs), true); + m_iStartPos = p; + + return len - rs; +} + +int CRcvBuffer::readBufferToFile(fstream& ofs, int len) +{ + int p = m_iStartPos; + int lastack = m_iLastAckPos; + int rs = len; + + while ((p != lastack) && (rs > 0)) + { + int unitsize = (int) m_pUnit[p]->m_Packet.getLength() - m_iNotch; + if (unitsize > rs) + unitsize = rs; + + ofs.write(m_pUnit[p]->m_Packet.m_pcData + m_iNotch, unitsize); + if (ofs.fail()) + break; + + if ((rs > unitsize) || (rs == int(m_pUnit[p]->m_Packet.getLength()) - m_iNotch)) + { + CUnit* tmp = m_pUnit[p]; + m_pUnit[p] = NULL; + m_pUnitQueue->makeUnitFree(tmp); + + if (++ p == m_iSize) + p = 0; + + m_iNotch = 0; + } + else + m_iNotch += rs; + + rs -= unitsize; + } + + /* we removed acked bytes form receive buffer */ + countBytes(-1, -(len - rs), true); + m_iStartPos = p; + + return len - rs; +} + +void CRcvBuffer::ackData(int len) +{ + SRT_ASSERT(len < m_iSize); + SRT_ASSERT(len > 0); + + { + int pkts = 0; + int bytes = 0; + for (int i = m_iLastAckPos, n = (m_iLastAckPos + len) % m_iSize; i != n; i = (i + 1) % m_iSize) + { + if (m_pUnit[i] == NULL) + continue; + + pkts++; + bytes += (int) m_pUnit[i]->m_Packet.getLength(); + } + if (pkts > 0) countBytes(pkts, bytes, true); + } + m_iLastAckPos = (m_iLastAckPos + len) % m_iSize; + m_iMaxPos -= len; + if (m_iMaxPos < 0) + m_iMaxPos = 0; + + CTimer::triggerEvent(); +} + +void CRcvBuffer::skipData(int len) +{ + /* + * Caller need protect both AckLock and RecvLock + * to move both m_iStartPos and m_iLastAckPost + */ + if (m_iStartPos == m_iLastAckPos) + m_iStartPos = (m_iStartPos + len) % m_iSize; + m_iLastAckPos = (m_iLastAckPos + len) % m_iSize; + m_iMaxPos -= len; + if (m_iMaxPos < 0) + m_iMaxPos = 0; +} + +bool CRcvBuffer::getRcvFirstMsg(ref_t r_tsbpdtime, ref_t r_passack, ref_t r_skipseqno, ref_t r_curpktseq) +{ + int32_t& skipseqno = *r_skipseqno; + bool& passack = *r_passack; + skipseqno = -1; + passack = false; + // tsbpdtime will be retrieved by the below call + // Returned values: + // - tsbpdtime: real time when the packet is ready to play (whether ready to play or not) + // - passack: false (the report concerns a packet with an exactly next sequence) + // - skipseqno == -1: no packets to skip towards the first RTP + // - ppkt: that exactly packet that is reported (for debugging purposes) + // - @return: whether the reported packet is ready to play + + /* Check the acknowledged packets */ + if (getRcvReadyMsg(r_tsbpdtime, r_curpktseq)) + { + HLOGC(dlog.Debug, log << "getRcvFirstMsg: ready CONTIG packet: %" << (*r_curpktseq)); + return true; + } + else if (*r_tsbpdtime != 0) + { + HLOGC(dlog.Debug, log << "getRcvFirstMsg: no packets found"); + return false; + } + + // getRcvReadyMsg returned false and tsbpdtime == 0. + + // Below this line we have only two options: + // - m_iMaxPos == 0, which means that no more packets are in the buffer + // - returned: tsbpdtime=0, passack=true, skipseqno=-1, ppkt=0, @return false + // - m_iMaxPos > 0, which means that there are packets arrived after a lost packet: + // - returned: tsbpdtime=PKT.TS, passack=true, skipseqno=PKT.SEQ, ppkt=PKT, @return LOCAL(PKT.TS) <= NOW + + /* + * No acked packets ready but caller want to know next packet to wait for + * Check the not yet acked packets that may be stuck by missing packet(s). + */ + bool haslost = false; + *r_tsbpdtime = 0; // redundant, for clarity + passack = true; + + // XXX SUSPECTED ISSUE with this algorithm: + // The above call to getRcvReadyMsg() should report as to whether: + // - there is an EXACTLY NEXT SEQUENCE packet + // - this packet is ready to play. + // + // Situations handled after the call are when: + // - there's the next sequence packet available and it is ready to play + // - there are no packets at all, ready to play or not + // + // So, the remaining situation is that THERE ARE PACKETS that follow + // the current sequence, but they are not ready to play. This includes + // packets that have the exactly next sequence and packets that jump + // over a lost packet. + // + // As the getRcvReadyMsg() function walks through the incoming units + // to see if there's anything that satisfies these conditions, it *SHOULD* + // be also capable of checking if the next available packet, if it is + // there, is the next sequence packet or not. Retrieving this exactly + // packet would be most useful, as the test for play-readiness and + // sequentiality can be done on it directly. + // + // When done so, the below loop would be completely unnecessary. + + // Logical description of the below algorithm: + // 1. Check if the VERY FIRST PACKET is valid; if so then: + // - check if it's ready to play, return boolean value that marks it. + + for (int i = m_iLastAckPos, n = (m_iLastAckPos + m_iMaxPos) % m_iSize; i != n; i = (i + 1) % m_iSize) + { + if ( !m_pUnit[i] + || m_pUnit[i]->m_iFlag != CUnit::GOOD ) + { + /* There are packets in the sequence not received yet */ + haslost = true; + HLOGC(dlog.Debug, log << "getRcvFirstMsg: empty hole at *" << i); + } + else + { + /* We got the 1st valid packet */ + *r_tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); + if (*r_tsbpdtime <= CTimer::getTime()) + { + /* Packet ready to play */ + if (haslost) + { + /* + * Packet stuck on non-acked side because of missing packets. + * Tell 1st valid packet seqno so caller can skip (drop) the missing packets. + */ + skipseqno = m_pUnit[i]->m_Packet.m_iSeqNo; + *r_curpktseq = skipseqno; + } + + HLOGC(dlog.Debug, log << "getRcvFirstMsg: found ready packet, nSKIPPED: " + << ((i - m_iLastAckPos + m_iSize) % m_iSize)); + + // NOTE: if haslost is not set, it means that this is the VERY FIRST + // packet, that is, packet currently at pos = m_iLastAckPos. There's no + // possibility that it is so otherwise because: + // - if this first good packet is ready to play, THIS HERE RETURNS NOW. + // ... + return true; + } + HLOGC(dlog.Debug, log << "getRcvFirstMsg: found NOT READY packet, nSKIPPED: " + << ((i - m_iLastAckPos + m_iSize) % m_iSize)); + // ... and if this first good packet WASN'T ready to play, THIS HERE RETURNS NOW, TOO, + // just states that there's no ready packet to play. + // ... + return false; + } + // ... and if this first packet WASN'T GOOD, the loop continues, however since now + // the 'haslost' is set, which means that it continues only to find the first valid + // packet after stating that the very first packet isn't valid. + } + HLOGC(dlog.Debug, log << "getRcvFirstMsg: found NO PACKETS"); + return false; +} + +bool CRcvBuffer::getRcvReadyMsg(ref_t tsbpdtime, ref_t curpktseq) +{ + *tsbpdtime = 0; + + IF_HEAVY_LOGGING(const char* reason = "NOT RECEIVED"); + + for (int i = m_iStartPos, n = m_iLastAckPos; i != n; i = (i + 1) % m_iSize) + { + bool freeunit = false; + + /* Skip any invalid skipped/dropped packets */ + if (m_pUnit[i] == NULL) + { + HLOGC(mglog.Debug, log << "getRcvReadyMsg: POS=" << i + << " +" << ((i - m_iStartPos + m_iSize) % m_iSize) + << " SKIPPED - no unit there"); + if (++ m_iStartPos == m_iSize) + m_iStartPos = 0; + continue; + } + + *curpktseq = m_pUnit[i]->m_Packet.getSeqNo(); + + if (m_pUnit[i]->m_iFlag != CUnit::GOOD) + { + HLOGC(mglog.Debug, log << "getRcvReadyMsg: POS=" << i + << " +" << ((i - m_iStartPos + m_iSize) % m_iSize) + << " SKIPPED - unit not good"); + freeunit = true; + } + else + { + *tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); + int64_t towait = (*tsbpdtime - CTimer::getTime()); + if (towait > 0) + { + HLOGC(mglog.Debug, log << "getRcvReadyMsg: POS=" << i + << " +" << ((i - m_iStartPos + m_iSize) % m_iSize) + << " pkt %" << curpktseq.get() + << " NOT ready to play (only in " << (towait/1000.0) << "ms)"); + return false; + } + + if (m_pUnit[i]->m_Packet.getMsgCryptoFlags() != EK_NOENC) + { + IF_HEAVY_LOGGING(reason = "DECRYPTION FAILED"); + freeunit = true; /* packet not decrypted */ + } + else + { + HLOGC(mglog.Debug, log << "getRcvReadyMsg: POS=" << i + << " +" << ((i - m_iStartPos + m_iSize) % m_iSize) + << " pkt %" << curpktseq.get() + << " ready to play (delayed " << (-towait/1000.0) << "ms)"); + return true; + } + } + + if (freeunit) + { + HLOGC(mglog.Debug, log << "getRcvReadyMsg: POS=" << i << " FREED"); + /* removed skipped, dropped, undecryptable bytes from rcv buffer */ + const int rmbytes = (int)m_pUnit[i]->m_Packet.getLength(); + countBytes(-1, -rmbytes, true); + + CUnit* tmp = m_pUnit[i]; + m_pUnit[i] = NULL; + m_pUnitQueue->makeUnitFree(tmp); + + if (++m_iStartPos == m_iSize) + m_iStartPos = 0; + } + } + + HLOGC(mglog.Debug, log << "getRcvReadyMsg: nothing to deliver: " << reason); + return false; +} + + +/* +* Return receivable data status (packet timestamp ready to play if TsbPd mode) +* Return playtime (tsbpdtime) of 1st packet in queue, ready to play or not +* +* Return data ready to be received (packet timestamp ready to play if TsbPd mode) +* Using getRcvDataSize() to know if there is something to read as it was widely +* used in the code (core.cpp) is expensive in TsbPD mode, hence this simpler function +* that only check if first packet in queue is ready. +*/ +bool CRcvBuffer::isRcvDataReady(ref_t tsbpdtime, ref_t curpktseq) +{ + *tsbpdtime = 0; + + if (m_bTsbPdMode) + { + CPacket* pkt = getRcvReadyPacket(); + if (!pkt) + return false; + + /* + * Acknowledged data is available, + * Only say ready if time to deliver. + * Report the timestamp, ready or not. + */ + *curpktseq = pkt->getSeqNo(); + *tsbpdtime = getPktTsbPdTime(pkt->getMsgTimeStamp()); + + return (*tsbpdtime <= CTimer::getTime()); + } + + return isRcvDataAvailable(); +} + +// XXX This function may be called only after checking +// if m_bTsbPdMode. +CPacket* CRcvBuffer::getRcvReadyPacket() +{ + for (int i = m_iStartPos, n = m_iLastAckPos; i != n; i = (i + 1) % m_iSize) + { + /* + * Skip missing packets that did not arrive in time. + */ + if ( m_pUnit[i] && m_pUnit[i]->m_iFlag == CUnit::GOOD ) + return &m_pUnit[i]->m_Packet; + } + + return 0; +} + +#if ENABLE_HEAVY_LOGGING +// This function is for debug purposes only and it's called only +// from within HLOG* macros. +void CRcvBuffer::reportBufferStats() +{ + int nmissing = 0; + int32_t low_seq= -1, high_seq = -1; + int32_t low_ts = 0, high_ts = 0; + + for (int i = m_iStartPos, n = m_iLastAckPos; i != n; i = (i + 1) % m_iSize) + { + if ( m_pUnit[i] && m_pUnit[i]->m_iFlag == CUnit::GOOD ) + { + low_seq = m_pUnit[i]->m_Packet.m_iSeqNo; + low_ts = m_pUnit[i]->m_Packet.m_iTimeStamp; + break; + } + ++nmissing; + } + + // Not sure if a packet MUST BE at the last ack pos position, so check, just in case. + int n = m_iLastAckPos; + if (m_pUnit[n] && m_pUnit[n]->m_iFlag == CUnit::GOOD) + { + high_ts = m_pUnit[n]->m_Packet.m_iTimeStamp; + high_seq = m_pUnit[n]->m_Packet.m_iSeqNo; + } + else + { + // Possibilities are: + // m_iStartPos == m_iLastAckPos, high_ts == low_ts, defined. + // No packet: low_ts == 0, so high_ts == 0, too. + high_ts = low_ts; + } + // The 32-bit timestamps are relative and roll over oftten; what + // we really need is the timestamp difference. The only place where + // we can ask for the time base is the upper time because when trying + // to receive the time base for the lower time we'd break the requirement + // for monotonic clock. + + uint64_t upper_time = high_ts; + uint64_t lower_time = low_ts; + + if (lower_time > upper_time) + upper_time += uint64_t(CPacket::MAX_TIMESTAMP)+1; + + int32_t timespan = upper_time - lower_time; + int seqspan = 0; + if (low_seq != -1 && high_seq != -1) + { + seqspan = CSeqNo::seqoff(low_seq, high_seq); + } + + LOGC(dlog.Debug, log << "RCV BUF STATS: seqspan=%(" << low_seq << "-" << high_seq << ":" << seqspan << ") missing=" << nmissing << "pkts"); + LOGC(dlog.Debug, log << "RCV BUF STATS: timespan=" << timespan << "us (lo=" << FormatTime(lower_time) << " hi=" << FormatTime(upper_time) << ")"); +} + +#endif // ENABLE_HEAVY_LOGGING + +bool CRcvBuffer::isRcvDataReady() +{ + uint64_t tsbpdtime; + int32_t seq; + + return isRcvDataReady(Ref(tsbpdtime), Ref(seq)); +} + +int CRcvBuffer::getAvailBufSize() const +{ + // One slot must be empty in order to tell the difference between "empty buffer" and "full buffer" + return m_iSize - getRcvDataSize() - 1; +} + +int CRcvBuffer::getRcvDataSize() const +{ + if (m_iLastAckPos >= m_iStartPos) + return m_iLastAckPos - m_iStartPos; + + return m_iSize + m_iLastAckPos - m_iStartPos; +} + +int CRcvBuffer::debugGetSize() const +{ + // Does exactly the same as getRcvDataSize, but + // it should be used FOR INFORMATIONAL PURPOSES ONLY. + // The source values might be changed in another thread + // during the calculation, although worst case the + // resulting value may differ to the real buffer size by 1. + int from = m_iStartPos, to = m_iLastAckPos; + int size = to - from; + if (size < 0) + size += m_iSize; + + return size; +} + + +bool CRcvBuffer::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; +} + + +#ifdef SRT_ENABLE_RCVBUFSZ_MAVG +/* Return moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ +int CRcvBuffer::getRcvAvgDataSize(int &bytes, int ×pan) +{ + timespan = m_TimespanMAvg; + bytes = m_iBytesCountMAvg; + return(m_iCountMAvg); +} + +/* Update moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ +void CRcvBuffer::updRcvAvgDataSize(uint64_t now) +{ + const uint64_t elapsed_ms = (now - m_LastSamplingTime) / 1000; //ms since last sampling + + if ((1000000 / SRT_MAVG_SAMPLING_RATE) / 1000 > elapsed_ms) + return; /* Last sampling too recent, skip */ + + if (1000 < elapsed_ms) + { + /* No sampling in last 1 sec, initialize/reset moving average */ + m_iCountMAvg = getRcvDataSize(m_iBytesCountMAvg, m_TimespanMAvg); + m_LastSamplingTime = now; + + HLOGC(dlog.Debug, log << "getRcvDataSize: " << m_iCountMAvg << " " << m_iBytesCountMAvg + << " " << m_TimespanMAvg << " ms elapsed_ms: " << elapsed_ms << " ms"); + } + else if ((1000000 / SRT_MAVG_SAMPLING_RATE) / 1000 <= elapsed_ms) + { + /* + * Weight last average value between -1 sec and last sampling time (LST) + * and new value between last sampling time and now + * |elapsed_ms| + * +----------------------------------+-------+ + * -1 LST 0(now) + */ + int instspan; + int bytescount; + int count = getRcvDataSize(bytescount, instspan); + + m_iCountMAvg = (int)(((count * (1000 - elapsed_ms)) + (count * elapsed_ms)) / 1000); + m_iBytesCountMAvg = (int)(((bytescount * (1000 - elapsed_ms)) + (bytescount * elapsed_ms)) / 1000); + m_TimespanMAvg = (int)(((instspan * (1000 - elapsed_ms)) + (instspan * elapsed_ms)) / 1000); + m_LastSamplingTime = now; + + HLOGC(dlog.Debug, log << "getRcvDataSize: " << count << " " << bytescount << " " << instspan + << " ms elapsed_ms: " << elapsed_ms << " ms"); + } +} +#endif /* SRT_ENABLE_RCVBUFSZ_MAVG */ + +/* Return acked data pkts, bytes, and timespan (ms) of the receive buffer */ +int CRcvBuffer::getRcvDataSize(int &bytes, int ×pan) +{ + timespan = 0; + if (m_bTsbPdMode) + { + // Get a valid startpos. + // Skip invalid entries in the beginning, if any. + int startpos = m_iStartPos; + for (; startpos != m_iLastAckPos; startpos = (startpos + 1) % m_iSize) + { + if ((NULL != m_pUnit[startpos]) && (CUnit::GOOD == m_pUnit[startpos]->m_iFlag)) + break; + } + + int endpos = m_iLastAckPos; + + if (m_iLastAckPos != startpos) + { + /* + * |<--- DataSpan ---->|<- m_iMaxPos ->| + * +---+---+---+---+---+---+---+---+---+---+---+--- + * | | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | | m_pUnits[] + * +---+---+---+---+---+---+---+---+---+---+---+--- + * | | + * \_ m_iStartPos \_ m_iLastAckPos + * + * m_pUnits[startpos] shall be valid (->m_iFlag==CUnit::GOOD). + * If m_pUnits[m_iLastAckPos-1] is not valid (NULL or ->m_iFlag!=CUnit::GOOD), + * it means m_pUnits[m_iLastAckPos] is valid since a valid unit is needed to skip. + * Favor m_pUnits[m_iLastAckPos] if valid over [m_iLastAckPos-1] to include the whole acked interval. + */ + if ((m_iMaxPos <= 0) + || (!m_pUnit[m_iLastAckPos]) + || (m_pUnit[m_iLastAckPos]->m_iFlag != CUnit::GOOD)) + { + endpos = (m_iLastAckPos == 0 ? m_iSize - 1 : m_iLastAckPos - 1); + } + + if ((NULL != m_pUnit[endpos]) && (NULL != m_pUnit[startpos])) + { + const uint64_t startstamp = getPktTsbPdTime(m_pUnit[startpos]->m_Packet.getMsgTimeStamp()); + const uint64_t endstamp = getPktTsbPdTime(m_pUnit[endpos]->m_Packet.getMsgTimeStamp()); + /* + * There are sampling conditions where spantime is < 0 (big unsigned value). + * It has been observed after changing the SRT latency from 450 to 200 on the sender. + * + * Possible packet order corruption when dropping packet, + * cause by bad thread protection when adding packet in queue + * was later discovered and fixed. Security below kept. + * + * DateTime RecvRate LostRate DropRate AvailBw RTT RecvBufs PdDelay + * 2014-12-08T15:04:25-0500 4712 110 0 96509 33.710 393 450 + * 2014-12-08T15:04:35-0500 4512 95 0 107771 33.493 1496542976 200 + * 2014-12-08T15:04:40-0500 4213 106 3 107352 53.657 9499425 200 + * 2014-12-08T15:04:45-0500 4575 104 0 102194 53.614 59666 200 + * 2014-12-08T15:04:50-0500 4475 124 0 100543 53.526 505 200 + */ + if (endstamp > startstamp) + timespan = (int)((endstamp - startstamp) / 1000); + } + /* + * Timespan can be less then 1000 us (1 ms) if few packets. + * Also, if there is only one pkt in buffer, the time difference will be 0. + * Therefore, always add 1 ms if not empty. + */ + if (0 < m_iAckedPktsCount) + timespan += 1; + } + } + HLOGF(dlog.Debug, "getRcvDataSize: %6d %6d %6d ms\n", m_iAckedPktsCount, m_iAckedBytesCount, timespan); + bytes = m_iAckedBytesCount; + return m_iAckedPktsCount; +} + +int CRcvBuffer::getRcvAvgPayloadSize() const +{ + return m_iAvgPayloadSz; +} + +void CRcvBuffer::dropMsg(int32_t msgno, bool using_rexmit_flag) +{ + for (int i = m_iStartPos, n = (m_iLastAckPos + m_iMaxPos) % m_iSize; i != n; i = (i + 1) % m_iSize) + if ((m_pUnit[i] != NULL) + && (m_pUnit[i]->m_Packet.getMsgSeq(using_rexmit_flag) == msgno)) + m_pUnit[i]->m_iFlag = CUnit::DROPPED; +} + +uint64_t CRcvBuffer::getTsbPdTimeBase(uint32_t timestamp_us) +{ + /* + * Packet timestamps wrap around every 01h11m35s (32-bit in usec) + * When added to the peer start time (base time), + * wrapped around timestamps don't provide a valid local packet delevery time. + * + * A wrap check period starts 30 seconds before the wrap point. + * In this period, timestamps smaller than 30 seconds are considered to have wrapped around (then adjusted). + * The wrap check period ends 30 seconds after the wrap point, afterwhich time base has been adjusted. + */ + uint64_t carryover = 0; + + // This function should generally return the timebase for the given timestamp_us. + // It's assumed that the timestamp_us, for which this function is being called, + // is received as monotonic clock. This function then traces the changes in the + // timestamps passed as argument and catches the moment when the 64-bit timebase + // should be increased by a "segment length" (MAX_TIMESTAMP+1). + + // The checks will be provided for the following split: + // [INITIAL30][FOLLOWING30]....[LAST30] <-- == CPacket::MAX_TIMESTAMP + // + // The following actions should be taken: + // 1. Check if this is [LAST30]. If so, ENTER TSBPD-wrap-check state + // 2. Then, it should turn into [INITIAL30] at some point. If so, use carryover MAX+1. + // 3. Then it should switch to [FOLLOWING30]. If this is detected, + // - EXIT TSBPD-wrap-check state + // - save the carryover as the current time base. + + if (m_bTsbPdWrapCheck) + { + // Wrap check period. + + if (timestamp_us < TSBPD_WRAP_PERIOD) + { + carryover = uint64_t(CPacket::MAX_TIMESTAMP) + 1; + } + // + else if ((timestamp_us >= TSBPD_WRAP_PERIOD) + && (timestamp_us <= (TSBPD_WRAP_PERIOD * 2))) + { + /* Exiting wrap check period (if for packet delivery head) */ + m_bTsbPdWrapCheck = false; + m_ullTsbPdTimeBase += uint64_t(CPacket::MAX_TIMESTAMP) + 1; + tslog.Debug("tsbpd wrap period ends"); + } + } + // Check if timestamp_us is in the last 30 seconds before reaching the MAX_TIMESTAMP. + else if (timestamp_us > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD)) + { + /* Approching wrap around point, start wrap check period (if for packet delivery head) */ + m_bTsbPdWrapCheck = true; + tslog.Debug("tsbpd wrap period begins"); + } + + return (m_ullTsbPdTimeBase + carryover); +} + +uint64_t CRcvBuffer::getPktTsbPdTime(uint32_t timestamp) +{ + return(getTsbPdTimeBase(timestamp) + m_uTsbPdDelay + timestamp + m_DriftTracer.drift()); +} + +int CRcvBuffer::setRcvTsbPdMode(uint64_t timebase, uint32_t delay) +{ + m_bTsbPdMode = true; + m_bTsbPdWrapCheck = false; + + // Timebase passed here comes is calculated as: + // >>> CTimer::getTime() - ctrlpkt->m_iTimeStamp + // where ctrlpkt is the packet with SRT_CMD_HSREQ message. + // + // This function is called in the HSREQ reception handler only. + m_ullTsbPdTimeBase = timebase; + // XXX Seems like this may not work correctly. + // At least this solution this way won't work with application-supplied + // timestamps. For that case the timestamps should be taken exclusively + // from the data packets because in case of application-supplied timestamps + // they come from completely different server and undergo different rules + // of network latency and drift. + m_uTsbPdDelay = delay; + return 0; +} + +#ifdef SRT_DEBUG_TSBPD_DRIFT +void CRcvBuffer::printDriftHistogram(int64_t iDrift) +{ + /* + * Build histogram of drift values + * First line (ms): <=-10.0 -9.0 ... -1.0 - 0.0 + 1.0 ... 9.0 >=10.0 + * Second line (ms): -0.9 ... -0.1 - 0.0 + 0.1 ... 0.9 + * 0 0 0 0 0 0 0 0 0 0 - 0 + 0 0 0 1 0 0 0 0 0 0 + * 0 0 0 0 0 0 0 0 0 - 0 + 0 0 0 0 0 0 0 0 0 + */ + iDrift /= 100; // uSec to 100 uSec (0.1ms) + if (-10 < iDrift && iDrift < 10) + { + /* Fill 100us histogram -900 .. 900 us 100 us increments */ + m_TsbPdDriftHisto100us[10 + iDrift]++; + } + else + { + /* Fill 1ms histogram <=-10.0, -9.0 .. 9.0, >=10.0 ms in 1 ms increments */ + iDrift /= 10; // 100uSec to 1ms + if (-10 < iDrift && iDrift < 10) m_TsbPdDriftHisto1ms[10 + iDrift]++; + else if (iDrift <= -10) m_TsbPdDriftHisto1ms[0]++; + else m_TsbPdDriftHisto1ms[20]++; + } + + if ((m_iTsbPdDriftNbSamples % TSBPD_DRIFT_PRT_SAMPLES) == 0) + { + int *histo = m_TsbPdDriftHisto1ms; + + fprintf(stderr, "%4d %4d %4d %4d %4d %4d %4d %4d %4d %4d - %4d + ", + histo[0],histo[1],histo[2],histo[3],histo[4], + histo[5],histo[6],histo[7],histo[8],histo[9],histo[10]); + fprintf(stderr, "%4d %4d %4d %4d %4d %4d %4d %4d %4d %4d\n", + histo[11],histo[12],histo[13],histo[14],histo[15], + histo[16],histo[17],histo[18],histo[19],histo[20]); + + histo = m_TsbPdDriftHisto100us; + fprintf(stderr, " %4d %4d %4d %4d %4d %4d %4d %4d %4d - %4d + ", + histo[1],histo[2],histo[3],histo[4],histo[5], + histo[6],histo[7],histo[8],histo[9],histo[10]); + fprintf(stderr, "%4d %4d %4d %4d %4d %4d %4d %4d %4d\n", + histo[11],histo[12],histo[13],histo[14],histo[15], + histo[16],histo[17],histo[18],histo[19]); + } +} + +void CRcvBuffer::printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg) +{ + char szTime[32] = {}; + uint64_t now = CTimer::getTime(); + time_t tnow = (time_t)(now/1000000); + strftime(szTime, sizeof(szTime), "%H:%M:%S", localtime(&tnow)); + fprintf(stderr, "%s.%03d: tsbpd offset=%d drift=%d usec\n", + szTime, (int)((now%1000000)/1000), tsbPdOffset, tsbPdDriftAvg); + memset(m_TsbPdDriftHisto100us, 0, sizeof(m_TsbPdDriftHisto100us)); + memset(m_TsbPdDriftHisto1ms, 0, sizeof(m_TsbPdDriftHisto1ms)); +} +#endif /* SRT_DEBUG_TSBPD_DRIFT */ + +void CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp, pthread_mutex_t& mutex_to_lock) +{ + if (!m_bTsbPdMode) // Not checked unless in TSBPD mode + return; + /* + * TsbPD time drift correction + * TsbPD time slowly drift over long period depleting decoder buffer or raising latency + * Re-evaluate the time adjustment value using a receiver control packet (ACK-ACK). + * ACK-ACK timestamp is RTT/2 ago (in sender's time base) + * Data packet have origin time stamp which is older when retransmitted so not suitable for this. + * + * Every TSBPD_DRIFT_MAX_SAMPLES packets, the average drift is calculated + * if -TSBPD_DRIFT_MAX_VALUE < avgTsbPdDrift < TSBPD_DRIFT_MAX_VALUE uSec, pass drift value to RcvBuffer to adjust delevery time. + * if outside this range, adjust this->TsbPdTimeOffset and RcvBuffer->TsbPdTimeBase by +-TSBPD_DRIFT_MAX_VALUE uSec + * to maintain TsbPdDrift values in reasonable range (-5ms .. +5ms). + */ + + // Note important thing: this function is being called _EXCLUSIVELY_ in the handler + // of UMSG_ACKACK command reception. This means that the timestamp used here comes + // from the CONTROL domain, not DATA domain (timestamps from DATA domain may be + // either schedule time or a time supplied by the application). + + int64_t iDrift = CTimer::getTime() - (getTsbPdTimeBase(timestamp) + timestamp); + + CGuard::enterCS(mutex_to_lock); + + bool updated = m_DriftTracer.update(iDrift); + +#ifdef SRT_DEBUG_TSBPD_DRIFT + printDriftHistogram(iDrift); +#endif /* SRT_DEBUG_TSBPD_DRIFT */ + + if ( updated ) + { +#ifdef SRT_DEBUG_TSBPD_DRIFT + printDriftOffset(m_DriftTracer.overdrift(), m_DriftTracer.drift()); +#endif /* SRT_DEBUG_TSBPD_DRIFT */ + +#if ENABLE_HEAVY_LOGGING + uint64_t oldbase = m_ullTsbPdTimeBase; +#endif + m_ullTsbPdTimeBase += m_DriftTracer.overdrift(); + + HLOGC(dlog.Debug, log << "DRIFT=" << (iDrift/1000.0) << "ms AVG=" + << (m_DriftTracer.drift()/1000.0) << "ms, TB: " + << FormatTime(oldbase) << " UPDATED TO: " << FormatTime(m_ullTsbPdTimeBase)); + } + else + { + HLOGC(dlog.Debug, log << "DRIFT=" << (iDrift/1000.0) << "ms TB REMAINS: " << FormatTime(m_ullTsbPdTimeBase)); + } + + CGuard::leaveCS(mutex_to_lock); +} + +int CRcvBuffer::readMsg(char* data, int len) +{ + SRT_MSGCTRL dummy = srt_msgctrl_default; + return readMsg(data, len, Ref(dummy)); +} + + +int CRcvBuffer::readMsg(char* data, int len, ref_t r_msgctl) +{ + SRT_MSGCTRL& msgctl = *r_msgctl; + int p, q; + bool passack; + bool empty = true; + uint64_t& rplaytime = msgctl.srctime; + +#ifdef ENABLE_HEAVY_LOGGING + reportBufferStats(); +#endif + + if (m_bTsbPdMode) + { + passack = false; + int seq = 0; + + if (getRcvReadyMsg(Ref(rplaytime), Ref(seq))) + { + empty = false; + + // In TSBPD mode you always read one message + // at a time and a message always fits in one UDP packet, + // so in one "unit". + p = q = m_iStartPos; + +#ifdef SRT_DEBUG_TSBPD_OUTJITTER + uint64_t now = CTimer::getTime(); + if ((now - rplaytime)/10 < 10) + m_ulPdHisto[0][(now - rplaytime)/10]++; + else if ((now - rplaytime)/100 < 10) + m_ulPdHisto[1][(now - rplaytime)/100]++; + else if ((now - rplaytime)/1000 < 10) + m_ulPdHisto[2][(now - rplaytime)/1000]++; + else + m_ulPdHisto[3][1]++; +#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ + } + } + else + { + rplaytime = 0; + if (scanMsg(Ref(p), Ref(q), Ref(passack))) + empty = false; + + } + + if (empty) + return 0; + + // This should happen just once. By 'empty' condition + // we have a guarantee that m_pUnit[p] exists and is valid. + CPacket& pkt1 = m_pUnit[p]->m_Packet; + + // This returns the sequence number and message number to + // the API caller. + msgctl.pktseq = pkt1.getSeqNo(); + msgctl.msgno = pkt1.getMsgSeq(); + + SRT_ASSERT(len > 0); + int rs = len > 0 ? len : 0; + while (p != (q + 1) % m_iSize) + { + const int pktlen = (int)m_pUnit[p]->m_Packet.getLength(); + // When unitsize is less than pktlen, only a fragment is copied to the output 'data', + // but still the whole packet is removed from the receiver buffer. + if (pktlen > 0) + countBytes(-1, -pktlen, true); + + const int unitsize = ((rs >= 0) && (pktlen > rs)) ? rs : pktlen; + + HLOGC(mglog.Debug, log << "readMsg: checking unit POS=" << p); + + if (unitsize > 0) + { + memcpy(data, m_pUnit[p]->m_Packet.m_pcData, unitsize); + data += unitsize; + rs -= unitsize; + +#if ENABLE_HEAVY_LOGGING + { + static uint64_t prev_now; + static uint64_t prev_srctime; + CPacket& pkt = m_pUnit[p]->m_Packet; + + int32_t seq = pkt.m_iSeqNo; + + uint64_t nowtime = CTimer::getTime(); + //CTimer::rdtsc(nowtime); + uint64_t srctime = getPktTsbPdTime(m_pUnit[p]->m_Packet.getMsgTimeStamp()); + + int64_t timediff = nowtime - srctime; + int64_t nowdiff = prev_now ? (nowtime - prev_now) : 0; + uint64_t srctimediff = prev_srctime ? (srctime - prev_srctime) : 0; + + HLOGC(dlog.Debug, log << CONID() << "readMsg: DELIVERED seq=" << seq + << " from POS=" << p << " T=" + << FormatTime(srctime) << " in " << (timediff/1000.0) + << "ms - TIME-PREVIOUS: PKT: " << (srctimediff/1000.0) + << " LOCAL: " << (nowdiff/1000.0) + << " !" << BufferStamp(pkt.data(), pkt.size())); + + prev_now = nowtime; + prev_srctime = srctime; + } +#endif + } + else + { + HLOGC(dlog.Debug, log << CONID() << "readMsg: SKIPPED POS=" << p << " - ZERO SIZE UNIT"); + } + + if (!passack) + { + HLOGC(dlog.Debug, log << CONID() << "readMsg: FREEING UNIT POS=" << p); + CUnit* tmp = m_pUnit[p]; + m_pUnit[p] = NULL; + m_pUnitQueue->makeUnitFree(tmp); + } + else + { + HLOGC(dlog.Debug, log << CONID() << "readMsg: PASSACK UNIT POS=" << p); + m_pUnit[p]->m_iFlag = CUnit::PASSACK; + } + + if (++ p == m_iSize) + p = 0; + } + + if (!passack) + m_iStartPos = (q + 1) % m_iSize; + + return len - rs; +} + + +bool CRcvBuffer::scanMsg(ref_t r_p, ref_t r_q, ref_t passack) +{ + int& p = *r_p; + int& q = *r_q; + + // empty buffer + if ((m_iStartPos == m_iLastAckPos) && (m_iMaxPos <= 0)) + { + HLOGC(mglog.Debug, log << "scanMsg: empty buffer"); + return false; + } + + int rmpkts = 0; + int rmbytes = 0; + //skip all bad msgs at the beginning + while (m_iStartPos != m_iLastAckPos) + { + // Roll up to the first valid unit + if (!m_pUnit[m_iStartPos]) + { + if (++ m_iStartPos == m_iSize) + m_iStartPos = 0; + continue; + } + + // Note: PB_FIRST | PB_LAST == PB_SOLO. + // testing if boundary() & PB_FIRST tests if the msg is first OR solo. + if ( m_pUnit[m_iStartPos]->m_iFlag == CUnit::GOOD + && m_pUnit[m_iStartPos]->m_Packet.getMsgBoundary() & PB_FIRST ) + { + bool good = true; + + // look ahead for the whole message + + // We expect to see either of: + // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] + // [PB_SOLO] + // but not: + // [PB_FIRST] NULL ... + // [PB_FIRST] FREE/PASSACK/DROPPED... + // If the message didn't look as expected, interrupt this. + + // This begins with a message starting at m_iStartPos + // up to m_iLastAckPos OR until the PB_LAST message is found. + // If any of the units on this way isn't good, this OUTER loop + // will be interrupted. + for (int i = m_iStartPos; i != m_iLastAckPos;) + { + if (!m_pUnit[i] || m_pUnit[i]->m_iFlag != CUnit::GOOD) + { + good = false; + break; + } + + // Likewise, boundary() & PB_LAST will be satisfied for last OR solo. + if ( m_pUnit[i]->m_Packet.getMsgBoundary() & PB_LAST ) + break; + + if (++ i == m_iSize) + i = 0; + } + + if (good) + break; + } + + CUnit* tmp = m_pUnit[m_iStartPos]; + m_pUnit[m_iStartPos] = NULL; + rmpkts++; + rmbytes += (int) tmp->m_Packet.getLength(); + m_pUnitQueue->makeUnitFree(tmp); + + if (++ m_iStartPos == m_iSize) + m_iStartPos = 0; + } + /* we removed bytes form receive buffer */ + countBytes(-rmpkts, -rmbytes, true); + + // Not sure if this is correct, but this above 'while' loop exits + // under the following conditions only: + // - m_iStartPos == m_iLastAckPos (that makes passack = true) + // - found at least GOOD unit with PB_FIRST and not all messages up to PB_LAST are good, + // in which case it returns with m_iStartPos <% m_iLastAckPos (earlier) + // Also all units that lied before m_iStartPos are removed. + + p = -1; // message head + q = m_iStartPos; // message tail + *passack = m_iStartPos == m_iLastAckPos; + bool found = false; + + // looking for the first message + //>>m_pUnit[size + m_iMaxPos] is not valid + + // XXX Would be nice to make some very thorough refactoring here. + + // This rolls by q variable from m_iStartPos up to m_iLastAckPos, + // actually from the first message up to the one with PB_LAST + // or PB_SOLO boundary. + + // The 'i' variable used in this loop is just a stub, and the + // upper value is just to make it "virtually infinite, but with + // no exaggeration" (actually it makes sure that this loop does + // not roll more than around the whole cyclic container). This variable + // isn't used inside the loop at all. + + for (int i = 0, n = m_iMaxPos + getRcvDataSize(); i < n; ++ i) + { + if (m_pUnit[q] && m_pUnit[q]->m_iFlag == CUnit::GOOD) + { + // Equivalent pseudocode: + // PacketBoundary bound = m_pUnit[q]->m_Packet.getMsgBoundary(); + // if ( IsSet(bound, PB_FIRST) ) + // p = q; + // if ( IsSet(bound, PB_LAST) && p != -1 ) + // found = true; + // + // Not implemented this way because it uselessly check p for -1 + // also after setting it explicitly. + + switch (m_pUnit[q]->m_Packet.getMsgBoundary()) + { + case PB_SOLO: // 11 + p = q; + found = true; + break; + + case PB_FIRST: // 10 + p = q; + break; + + case PB_LAST: // 01 + if (p != -1) + found = true; + break; + + case PB_SUBSEQUENT: + ; // do nothing (caught first, rolling for last) + } + } + else + { + // a hole in this message, not valid, restart search + p = -1; + } + + // 'found' is set when the current iteration hit a message with PB_LAST + // (including PB_SOLO since the very first message). + if (found) + { + // the msg has to be ack'ed or it is allowed to read out of order, and was not read before + if (!*passack || !m_pUnit[q]->m_Packet.getMsgOrderFlag()) + { + HLOGC(mglog.Debug, log << "scanMsg: found next-to-broken message, delivering OUT OF ORDER."); + break; + } + + found = false; + } + + if (++ q == m_iSize) + q = 0; + + if (q == m_iLastAckPos) + *passack = true; + } + + // no msg found + if (!found) + { + // NOTE: + // This situation may only happen if: + // - Found a packet with PB_FIRST, so p = q at the moment when it was found + // - Possibly found following components of that message up to shifted q + // - Found no terminal packet (PB_LAST) for that message. + + // if the message is larger than the receiver buffer, return part of the message + if ((p != -1) && ((q + 1) % m_iSize == p)) + { + HLOGC(mglog.Debug, log << "scanMsg: BUFFER FULL and message is INCOMPLETE. Returning PARTIAL MESSAGE."); + found = true; + } + else + { + HLOGC(mglog.Debug, log << "scanMsg: PARTIAL or NO MESSAGE found: p=" << p << " q=" << q); + } + } + else + { + HLOGC(mglog.Debug, log << "scanMsg: extracted message p=" << p << " q=" << q << " (" << ((q-p+m_iSize+1)%m_iSize) << " packets)"); + } + + return found; +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer.h b/trunk/3rdparty/srt-1-fit/srtcore/buffer.h new file mode 100644 index 000000000..a267d8b44 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/buffer.h @@ -0,0 +1,510 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 05/05/2009 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef __UDT_BUFFER_H__ +#define __UDT_BUFFER_H__ + + +#include "udt.h" +#include "list.h" +#include "queue.h" +#include "utilities.h" +#include + +class CSndBuffer +{ +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(); + +public: + + /// 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 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 bytes, ref_t timespan); +#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */ + int getCurrBufSize(ref_t bytes, ref_t 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); + } + + +private: + + void increase(); + void setInputRateSmpPeriod(int period); + +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 + + struct Block + { + char* m_pcData; // pointer to the data block + int m_iLength; // length of the block + + 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) + + 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) + int m_iMSS; // maximum seqment/packet size + + int m_iCount; // number of used blocks + + int m_iBytesCount; // number of payload bytes in queue + uint64_t m_ullLastOriginTime_us; + +#ifdef SRT_ENABLE_SNDBUFSZ_MAVG + uint64_t m_LastSamplingTime; + int m_iCountMAvg; + int m_iBytesCountMAvg; + int m_TimespanMAvg; +#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */ + + 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 + +private: + CSndBuffer(const CSndBuffer&); + CSndBuffer& operator=(const CSndBuffer&); +}; + +//////////////////////////////////////////////////////////////////////////////// + + +class CRcvBuffer +{ +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(); + + +public: + + /// Write data into the buffer. + /// @param [in] unit pointer to a data unit containing new packet + /// @param [in] offset offset from last ACK point. + /// @return 0 is success, -1 if data is repeated. + + int addData(CUnit* unit, int offset); + + /// Read data into a user buffer. + /// @param [in] data pointer to user buffer. + /// @param [in] len length of user buffer. + /// @return size of data read. + + int readBuffer(char* data, int len); + + /// Read data directly into file. + /// @param [in] file C++ file stream. + /// @param [in] len expected length of data to write into the file. + /// @return size of data read. + + int readBufferToFile(std::fstream& ofs, int len); + + /// Update the ACK point of the buffer. + /// @param [in] len number of units to be acknowledged. + /// @return 1 if a user buffer is fulfilled, otherwise 0. + + void ackData(int len); + + /// Query how many buffer space left for data receiving. + /// Actually only acknowledged packets, that are still in the buffer, + /// are considered to take buffer space. + /// + /// @return size of available buffer space (including user buffer) for data receiving. + /// Not counting unacknowledged packets. + + int getAvailBufSize() const; + + /// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now). + /// @return size of valid (continous) data for reading. + + int getRcvDataSize() const; + + /// Query how many data was received and acknowledged. + /// @param [out] bytes bytes + /// @param [out] spantime spantime + /// @return size in pkts of acked data. + + int getRcvDataSize(int &bytes, int &spantime); +#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 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 tsbpdtime, ref_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 tsbpdtime, ref_t passack, ref_t skipseqno, ref_t curpktseq); + + /// 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(); // Heavy logging Debug only +#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 + + + bool getRcvReadyMsg(ref_t tsbpdtime, ref_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 start, ref_t end, ref_t 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 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 */ + +#ifdef SRT_DEBUG_TSBPD_OUTJITTER + unsigned long m_ulPdHisto[4][10]; +#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ + +private: + CRcvBuffer(); + CRcvBuffer(const CRcvBuffer&); + CRcvBuffer& operator=(const CRcvBuffer&); +}; + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/cache.cpp b/trunk/3rdparty/srt-1-fit/srtcore/cache.cpp new file mode 100644 index 000000000..fdde5998f --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/cache.cpp @@ -0,0 +1,120 @@ +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 05/05/2009 +*****************************************************************************/ + +#ifdef _WIN32 + #include + #include +#endif + +#include +#include "cache.h" +#include "core.h" + +using namespace std; + +CInfoBlock& CInfoBlock::operator=(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_iReorderDistance = obj.m_iReorderDistance; + m_dInterval = obj.m_dInterval; + m_dCWnd = obj.m_dCWnd; + + return *this; +} + +bool CInfoBlock::operator==(const CInfoBlock& obj) +{ + if (m_iIPversion != obj.m_iIPversion) + return false; + + else if (m_iIPversion == AF_INET) + return (m_piIP[0] == obj.m_piIP[0]); + + for (int i = 0; i < 4; ++ i) + { + if (m_piIP[i] != obj.m_piIP[i]) + return false; + } + + return true; +} + +CInfoBlock* 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_iReorderDistance = m_iReorderDistance; + obj->m_dInterval = m_dInterval; + obj->m_dCWnd = m_dCWnd; + + return obj; +} + +int CInfoBlock::getKey() +{ + if (m_iIPversion == AF_INET) + return m_piIP[0]; + + return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3]; +} + +void CInfoBlock::convert(const sockaddr* addr, int ver, uint32_t ip[]) +{ + if (ver == AF_INET) + { + ip[0] = ((sockaddr_in*)addr)->sin_addr.s_addr; + ip[1] = ip[2] = ip[3] = 0; + } + else + { + memcpy((char*)ip, (char*)((sockaddr_in6*)addr)->sin6_addr.s6_addr, 16); + } +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/cache.h b/trunk/3rdparty/srt-1-fit/srtcore/cache.h new file mode 100644 index 000000000..346f9d813 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/cache.h @@ -0,0 +1,266 @@ +/***************************************************************************** +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 01/27/2011 +*****************************************************************************/ + +#ifndef __UDT_CACHE_H__ +#define __UDT_CACHE_H__ + +#include +#include + +#include "common.h" +#include "udt.h" + +class CCacheItem +{ +public: + virtual ~CCacheItem() {} + +public: + virtual CCacheItem& operator=(const CCacheItem&) = 0; + + // The "==" operator SHOULD only compare key values. + virtual bool operator==(const CCacheItem&) = 0; + + /// get a deep copy clone of the current item + /// @return Pointer to the new item, or NULL if failed. + + virtual CCacheItem* clone() = 0; + + /// get a random key value between 0 and MAX_INT to be used for the hash in cache + /// @return A random hash key. + + virtual int getKey() = 0; + + // If there is any shared resources between the cache item and its clone, + // the shared resource should be released by this function. + virtual void release() {} +}; + +template class CCache +{ +public: + CCache(int size = 1024): + m_iMaxSize(size), + m_iHashSize(size * 3), + m_iCurrSize(0) + { + m_vHashPtr.resize(m_iHashSize); + CGuard::createMutex(m_Lock); + } + + ~CCache() + { + clear(); + CGuard::releaseMutex(m_Lock); + } + +public: + /// find the matching item in the cache. + /// @param [in,out] data storage for the retrieved item; initially it must carry the key information + /// @return 0 if found a match, otherwise -1. + + int lookup(T* data) + { + CGuard cacheguard(m_Lock); + + int key = data->getKey(); + if (key < 0) + return -1; + if (key >= m_iMaxSize) + key %= m_iHashSize; + + const ItemPtrList& item_list = m_vHashPtr[key]; + for (typename ItemPtrList::const_iterator i = item_list.begin(); i != item_list.end(); ++ i) + { + if (*data == ***i) + { + // copy the cached info + *data = ***i; + return 0; + } + } + + return -1; + } + + /// update an item in the cache, or insert one if it doesn't exist; oldest item may be removed + /// @param [in] data the new item to updated/inserted to the cache + /// @return 0 if success, otherwise -1. + + int update(T* data) + { + CGuard cacheguard(m_Lock); + + int key = data->getKey(); + if (key < 0) + return -1; + if (key >= m_iMaxSize) + key %= m_iHashSize; + + T* curr = NULL; + + ItemPtrList& item_list = m_vHashPtr[key]; + for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i) + { + if (*data == ***i) + { + // update the existing entry with the new value + ***i = *data; + curr = **i; + + // remove the current entry + m_StorageList.erase(*i); + item_list.erase(i); + + // re-insert to the front + m_StorageList.push_front(curr); + item_list.push_front(m_StorageList.begin()); + + return 0; + } + } + + // create new entry and insert to front + curr = data->clone(); + m_StorageList.push_front(curr); + item_list.push_front(m_StorageList.begin()); + + ++ m_iCurrSize; + if (m_iCurrSize >= m_iMaxSize) + { + // Cache overflow, remove oldest entry. + T* last_data = m_StorageList.back(); + int last_key = last_data->getKey() % m_iHashSize; + + ItemPtrList& last_item_list = m_vHashPtr[last_key]; + for (typename ItemPtrList::iterator i = last_item_list.begin(); i != last_item_list.end(); ++ i) + { + if (*last_data == ***i) + { + last_item_list.erase(i); + break; + } + } + + last_data->release(); + delete last_data; + m_StorageList.pop_back(); + -- m_iCurrSize; + } + + return 0; + } + + /// Specify the cache size (i.e., max number of items). + /// @param [in] size max cache size. + + void setSizeLimit(int size) + { + m_iMaxSize = size; + m_iHashSize = size * 3; + m_vHashPtr.resize(m_iHashSize); + } + + /// Clear all entries in the cache, restore to initialization state. + + void clear() + { + for (typename std::list::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++ i) + { + (*i)->release(); + delete *i; + } + m_StorageList.clear(); + for (typename std::vector::iterator i = m_vHashPtr.begin(); i != m_vHashPtr.end(); ++ i) + i->clear(); + m_iCurrSize = 0; + } + +private: + std::list m_StorageList; + typedef typename std::list::iterator ItemPtr; + typedef std::list ItemPtrList; + std::vector m_vHashPtr; + + int m_iMaxSize; + int m_iHashSize; + int m_iCurrSize; + + pthread_mutex_t m_Lock; + +private: + CCache(const CCache&); + CCache& operator=(const CCache&); +}; + + +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 + +public: + virtual ~CInfoBlock() {} + virtual CInfoBlock& operator=(const CInfoBlock& obj); + virtual bool operator==(const CInfoBlock& obj); + virtual CInfoBlock* clone(); + virtual int getKey(); + virtual void release() {} + +public: + + /// convert sockaddr structure to an integer array + /// @param [in] addr network address + /// @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[]); +}; + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/channel.cpp b/trunk/3rdparty/srt-1-fit/srtcore/channel.cpp new file mode 100644 index 000000000..dad6fd8db --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/channel.cpp @@ -0,0 +1,765 @@ +/* + * 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 01/27/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef _WIN32 + #if __APPLE__ + #include "TargetConditionals.h" + #endif + #include + #include + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #include +#endif + +#include +#include // Logging +#include +#include + +#include "channel.h" +#include "packet.h" +#include "api.h" // SockaddrToString - possibly move it to somewhere else +#include "logging.h" +#include "utilities.h" + +#ifdef _WIN32 + typedef int socklen_t; +#endif + +#ifndef _WIN32 + #define NET_ERROR errno +#else + #define NET_ERROR WSAGetLastError() +#endif + +using namespace std; +using namespace srt_logging; + +CChannel::CChannel(): +m_iIPversion(AF_INET), +m_iSockAddrSize(sizeof(sockaddr_in)), +m_iSocket(), +#ifdef SRT_ENABLE_IPOPTS +m_iIpTTL(-1), /* IPv4 TTL or IPv6 HOPs [1..255] (-1:undefined) */ +m_iIpToS(-1), /* IPv4 Type of Service or IPv6 Traffic Class [0x00..0xff] (-1:undefined) */ +#endif +m_iSndBufSize(65536), +m_iRcvBufSize(65536), +m_iIpV6Only(-1) +{ +} + +CChannel::CChannel(int version): +m_iIPversion(version), +m_iSocket(), +#ifdef SRT_ENABLE_IPOPTS +m_iIpTTL(-1), +m_iIpToS(-1), +#endif +m_iSndBufSize(65536), +m_iRcvBufSize(65536), +m_iIpV6Only(-1), +m_BindAddr(version) +{ + SRT_ASSERT(version == AF_INET || version == AF_INET6); + m_iSockAddrSize = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); +} + +CChannel::~CChannel() +{ +} + +void CChannel::open(const sockaddr* addr) +{ + // construct an socket + m_iSocket = ::socket(m_iIPversion, SOCK_DGRAM, 0); + + #ifdef _WIN32 + if (INVALID_SOCKET == m_iSocket) + #else + if (m_iSocket < 0) + #endif + throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); + + if ((m_iIpV6Only != -1) && (m_iIPversion == AF_INET6)) // (not an error if it fails) + ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)(&m_iIpV6Only), sizeof(m_iIpV6Only)); + + if (NULL != addr) + { + socklen_t namelen = m_iSockAddrSize; + + if (0 != ::bind(m_iSocket, addr, namelen)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + memcpy(&m_BindAddr, addr, namelen); + m_BindAddr.len = namelen; + } + else + { + //sendto or WSASendTo will also automatically bind the socket + addrinfo hints; + addrinfo* res; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = m_iIPversion; + hints.ai_socktype = SOCK_DGRAM; + + if (0 != ::getaddrinfo(NULL, "0", &hints, &res)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + + // On Windows ai_addrlen has type size_t (unsigned), while bind takes int. + if (0 != ::bind(m_iSocket, res->ai_addr, (socklen_t)res->ai_addrlen)) + { + ::freeaddrinfo(res); + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + memcpy(&m_BindAddr, res->ai_addr, res->ai_addrlen); + m_BindAddr.len = (socklen_t) res->ai_addrlen; + + ::freeaddrinfo(res); + } + + HLOGC(mglog.Debug, log << "CHANNEL: Bound to local address: " << SockaddrToString(&m_BindAddr)); + + setUDPSockOpt(); +} + +void CChannel::attach(UDPSOCKET udpsock) +{ + m_iSocket = udpsock; + setUDPSockOpt(); +} + +void CChannel::setUDPSockOpt() +{ + #if defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1) + // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value + int maxsize = 64000; + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&maxsize, sizeof(int)); + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int))) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&maxsize, sizeof(int)); + #else + // for other systems, if requested is greated than maximum, the maximum value will be automactally used + if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) || + (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int)))) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + #endif + + SRT_ASSERT(m_iIPversion == AF_INET || m_iIPversion == AF_INET6); + +#ifdef SRT_ENABLE_IPOPTS + if (-1 != m_iIpTTL) + { + if (m_iIPversion == AF_INET) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_iIpTTL, sizeof(m_iIpTTL))) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + else + { + // If IPv6 address is unspecified, set BOTH IP_TTL and IPV6_UNICAST_HOPS. + + // For specified IPv6 address, set IPV6_UNICAST_HOPS ONLY UNLESS it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char*)&m_iIpTTL, sizeof(m_iIpTTL))) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } + // For specified IPv6 address, set IP_TTL ONLY WHEN it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_iIpTTL, sizeof(m_iIpTTL))) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } + } + } + + if (-1 != m_iIpToS) + { + if (m_iIPversion == AF_INET) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_iIpToS, sizeof(m_iIpToS))) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + else + { + // If IPv6 address is unspecified, set BOTH IP_TOS and IPV6_TCLASS. + +#ifdef IPV6_TCLASS + // For specified IPv6 address, set IPV6_TCLASS ONLY UNLESS it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*)&m_iIpToS, sizeof(m_iIpToS))) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } +#endif + + // For specified IPv6 address, set IP_TOS ONLY WHEN it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_iIpToS, sizeof(m_iIpToS))) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } + } + } +#endif + + +#ifdef UNIX + // Set non-blocking I/O + // UNIX does not support SO_RCVTIMEO + int opts = ::fcntl(m_iSocket, F_GETFL); + if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); +#elif defined(_WIN32) + u_long nonBlocking = 1; + if (0 != ioctlsocket (m_iSocket, FIONBIO, &nonBlocking)) + throw CUDTException (MJ_SETUP, MN_NORES, NET_ERROR); +#else + timeval tv; + tv.tv_sec = 0; +#if defined (BSD) || defined (OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1) + // Known BSD bug as the day I wrote this code. + // A small time out value will cause the socket to block forever. + tv.tv_usec = 10000; +#else + tv.tv_usec = 100; +#endif + // Set receiving time-out value + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(timeval))) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); +#endif +} + +void CChannel::close() const +{ + #ifndef _WIN32 + ::close(m_iSocket); + #else + ::closesocket(m_iSocket); + #endif +} + +int CChannel::getSndBufSize() +{ + socklen_t size = sizeof(socklen_t); + ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char *)&m_iSndBufSize, &size); + return m_iSndBufSize; +} + +int CChannel::getRcvBufSize() +{ + socklen_t size = sizeof(socklen_t); + ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char *)&m_iRcvBufSize, &size); + return m_iRcvBufSize; +} + +void CChannel::setSndBufSize(int size) +{ + m_iSndBufSize = size; +} + +void CChannel::setRcvBufSize(int size) +{ + m_iRcvBufSize = size; +} + +void CChannel::setIpV6Only(int ipV6Only) +{ + m_iIpV6Only = ipV6Only; +} + +#ifdef SRT_ENABLE_IPOPTS +int CChannel::getIpTTL() const +{ + socklen_t size = sizeof(m_iIpTTL); + if (m_iIPversion == AF_INET) + { + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (char *)&m_iIpTTL, &size); + } + else + { + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&m_iIpTTL, &size); + } + return m_iIpTTL; +} + +int CChannel::getIpToS() const +{ + socklen_t size = sizeof(m_iIpToS); + if (m_iIPversion == AF_INET) + { + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (char *)&m_iIpToS, &size); + } + else + { +#ifdef IPV6_TCLASS + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (char *)&m_iIpToS, &size); +#endif + } + return m_iIpToS; +} + +void CChannel::setIpTTL(int ttl) +{ + m_iIpTTL = ttl; +} + +void CChannel::setIpToS(int tos) +{ + m_iIpToS = tos; +} + +#endif + +int CChannel::ioctlQuery(int SRT_ATR_UNUSED type) const +{ +#ifdef unix + int value = 0; + int res = ::ioctl(m_iSocket, type, &value); + if ( res != -1 ) + return value; +#endif + return -1; +} + +int CChannel::sockoptQuery(int SRT_ATR_UNUSED level, int SRT_ATR_UNUSED option) const +{ +#ifdef unix + int value = 0; + socklen_t len = sizeof (int); + int res = ::getsockopt(m_iSocket, level, option, &value, &len); + if ( res != -1 ) + return value; +#endif + return -1; +} + +void CChannel::getSockAddr(sockaddr* addr) const +{ + socklen_t namelen = m_iSockAddrSize; + ::getsockname(m_iSocket, addr, &namelen); +} + +void CChannel::getPeerAddr(sockaddr* addr) const +{ + socklen_t namelen = m_iSockAddrSize; + ::getpeername(m_iSocket, addr, &namelen); +} + + +int CChannel::sendto(const sockaddr* addr, CPacket& packet) const +{ +#if ENABLE_HEAVY_LOGGING + std::ostringstream spec; + + if (packet.isControl()) + { + spec << " type=CONTROL" + << " cmd=" << MessageTypeStr(packet.getType(), packet.getExtendedType()) + << " arg=" << packet.header(SRT_PH_MSGNO); + } + else + { + spec << " type=DATA" + << " %" << packet.getSeqNo() + << " msgno=" << MSGNO_SEQ::unwrap(packet.m_iMsgNo) + << packet.MessageFlagStr() + << " !" << BufferStamp(packet.m_pcData, packet.getLength()); + } + + LOGC(mglog.Debug, log << "CChannel::sendto: SENDING NOW DST=" << SockaddrToString(addr) + << " target=@" << packet.m_iID + << " size=" << packet.getLength() + << " pkt.ts=" << FormatTime(packet.m_iTimeStamp) + << spec.str()); +#endif + +#ifdef SRT_TEST_FAKE_LOSS + +#define FAKELOSS_STRING_0(x) #x +#define FAKELOSS_STRING(x) FAKELOSS_STRING_0(x) + const char* fakeloss_text = FAKELOSS_STRING(SRT_TEST_FAKE_LOSS); +#undef FAKELOSS_STRING +#undef FAKELOSS_WRAP + + static int dcounter = 0; + static int flwcounter = 0; + + struct FakelossConfig + { + pair config; + FakelossConfig(const char* f) + { + vector out; + Split(f, '+', back_inserter(out)); + + config.first = atoi(out[0].c_str()); + config.second = out.size() > 1 ? atoi(out[1].c_str()) : 8; + } + }; + static FakelossConfig fakeloss = fakeloss_text; + + if (!packet.isControl()) + { + if (dcounter == 0) + { + timeval tv; + gettimeofday(&tv, 0); + srand(tv.tv_usec & 0xFFFF); + } + ++dcounter; + + if (flwcounter) + { + // This is a counter of how many packets in a row shall be lost + --flwcounter; + HLOGC(mglog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (" << flwcounter << " more to drop)"); + return packet.getLength(); // fake successful sendinf + } + + if (dcounter > 8) + { + // Make a random number in the range between 8 and 24 + int rnd = rand() % 16 + SRT_TEST_FAKE_LOSS; + + if (dcounter > rnd) + { + dcounter = 1; + HLOGC(mglog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (will drop " << fakeloss.config.first << " more)"); + flwcounter = fakeloss.config.first; + return packet.getLength(); // fake successful sendinf + } + } + } + +#endif + + // convert control information into network order + // XXX USE HtoNLA! + if (packet.isControl()) + for (ptrdiff_t i = 0, n = packet.getLength() / 4; i < n; ++i) + *((uint32_t *)packet.m_pcData + i) = htonl(*((uint32_t *)packet.m_pcData + i)); + + // convert packet header into network order + //for (int j = 0; j < 4; ++ j) + // packet.m_nHeader[j] = htonl(packet.m_nHeader[j]); + uint32_t* p = packet.m_nHeader; + for (int j = 0; j < 4; ++ j) + { + *p = htonl(*p); + ++ p; + } + + #ifndef _WIN32 + msghdr mh; + mh.msg_name = (sockaddr*)addr; + mh.msg_namelen = m_iSockAddrSize; + mh.msg_iov = (iovec*)packet.m_PacketVector; + mh.msg_iovlen = 2; + mh.msg_control = NULL; + mh.msg_controllen = 0; + mh.msg_flags = 0; + + int res = ::sendmsg(m_iSocket, &mh, 0); + #else + DWORD size = (DWORD) (CPacket::HDR_SIZE + packet.getLength()); + int addrsize = m_iSockAddrSize; + int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr, addrsize, NULL, NULL); + res = (0 == res) ? size : -1; + #endif + + // convert back into local host order + //for (int k = 0; k < 4; ++ k) + // packet.m_nHeader[k] = ntohl(packet.m_nHeader[k]); + p = packet.m_nHeader; + for (int k = 0; k < 4; ++ k) + { + *p = ntohl(*p); + ++ p; + } + + if (packet.isControl()) + { + for (ptrdiff_t l = 0, n = packet.getLength() / 4; l < n; ++ l) + *((uint32_t *)packet.m_pcData + l) = ntohl(*((uint32_t *)packet.m_pcData + l)); + } + + return res; +} + +EReadStatus CChannel::recvfrom(sockaddr* addr, CPacket& packet) const +{ + EReadStatus status = RST_OK; + int msg_flags = 0; + int recv_size = -1; + +#if defined(UNIX) || defined(_WIN32) + fd_set set; + timeval tv; + FD_ZERO(&set); + FD_SET(m_iSocket, &set); + tv.tv_sec = 0; + tv.tv_usec = 10000; + const int select_ret = ::select((int) m_iSocket + 1, &set, NULL, &set, &tv); +#else + const int select_ret = 1; // the socket is expected to be in the blocking mode itself +#endif + + if (select_ret == 0) // timeout + { + packet.setLength(-1); + return RST_AGAIN; + } + +#ifndef _WIN32 + if (select_ret > 0) + { + msghdr mh; + mh.msg_name = addr; + mh.msg_namelen = m_iSockAddrSize; + mh.msg_iov = packet.m_PacketVector; + mh.msg_iovlen = 2; + mh.msg_control = NULL; + mh.msg_controllen = 0; + mh.msg_flags = 0; + + recv_size = ::recvmsg(m_iSocket, &mh, 0); + msg_flags = mh.msg_flags; + } + + // Note that there are exactly four groups of possible errors + // reported by recvmsg(): + + // 1. Temporary error, can't get the data, but you can try again. + // Codes: EAGAIN/EWOULDBLOCK, EINTR, ECONNREFUSED + // Return: RST_AGAIN. + // + // 2. Problems that should never happen due to unused configurations. + // Codes: ECONNREFUSED, ENOTCONN + // Return: RST_ERROR, just formally treat this as IPE. + // + // 3. Unexpected runtime errors: + // Codes: EINVAL, EFAULT, ENOMEM, ENOTSOCK + // Return: RST_ERROR. Except ENOMEM, this can only be an IPE. ENOMEM + // should make the program stop as lacking memory will kill the program anyway soon. + // + // 4. Expected socket closed in the meantime by another thread. + // Codes: EBADF + // Return: RST_ERROR. This will simply make the worker thread exit, which is + // expected to happen after CChannel::close() is called by another thread. + + // We do not handle <= SOCKET_ERROR as they are handled further by checking the recv_size + if (select_ret == -1 || recv_size == -1) + { + const int err = NET_ERROR; + if (err == EAGAIN || err == EINTR || err == ECONNREFUSED) // For EAGAIN, this isn't an error, just a useless call. + { + status = RST_AGAIN; + } + else + { + HLOGC(mglog.Debug, log << CONID() << "(sys)recvmsg: " << SysStrError(err) << " [" << err << "]"); + status = RST_ERROR; + } + + goto Return_error; + } + +#else + // XXX REFACTORING NEEDED! + // This procedure uses the WSARecvFrom function that just reads + // into one buffer. On Windows, the equivalent for recvmsg, WSARecvMsg + // uses the equivalent of msghdr - WSAMSG, which has different field + // names and also uses the equivalet of iovec - WSABUF, which has different + // field names and layout. It is important that this code be translated + // to the "proper" solution, however this requires that CPacket::m_PacketVector + // also uses the "platform independent" (or, better, platform-suitable) type + // which can be appropriate for the appropriate system function, not just iovec + // (see a specifically provided definition for iovec for windows in packet.h). + // + // For the time being, the msg_flags variable is defined in both cases + // so that it can be checked independently, however it won't have any other + // value one Windows than 0, unless this procedure below is rewritten + // to use WSARecvMsg(). + + int recv_ret = SOCKET_ERROR; + DWORD flag = 0; + + if (select_ret > 0) // the total number of socket handles that are ready + { + DWORD size = (DWORD) (CPacket::HDR_SIZE + packet.getLength()); + int addrsize = m_iSockAddrSize; + + recv_ret = ::WSARecvFrom(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, &flag, addr, &addrsize, NULL, NULL); + if (recv_ret == 0) + recv_size = size; + } + + // We do not handle <= SOCKET_ERROR as they are handled further by checking the recv_size + if (select_ret == SOCKET_ERROR || recv_ret == SOCKET_ERROR) // == SOCKET_ERROR + { + recv_size = -1; + // On Windows this is a little bit more complicated, so simply treat every error + // as an "again" situation. This should still be probably fixed, but it needs more + // thorough research. For example, the problem usually reported from here is + // WSAETIMEDOUT, which isn't mentioned in the documentation of WSARecvFrom at all. + // + // These below errors are treated as "fatal", all others are treated as "again". + static const int fatals [] = + { + WSAEFAULT, + WSAEINVAL, + WSAENETDOWN, + WSANOTINITIALISED, + WSA_OPERATION_ABORTED + }; + static const int* fatals_end = fatals + Size(fatals); + const int err = NET_ERROR; + if (std::find(fatals, fatals_end, err) != fatals_end) + { + HLOGC(mglog.Debug, log << CONID() << "(sys)WSARecvFrom: " << SysStrError(err) << " [" << err << "]"); + status = RST_ERROR; + } + else + { + status = RST_AGAIN; + } + + goto Return_error; + } + + // Not sure if this problem has ever occurred on Windows, just a sanity check. + if (flag & MSG_PARTIAL) + msg_flags = 1; +#endif + + + // Sanity check for a case when it didn't fill in even the header + if (size_t(recv_size) < CPacket::HDR_SIZE) + { + status = RST_AGAIN; + HLOGC(mglog.Debug, log << CONID() << "POSSIBLE ATTACK: received too short packet with " << recv_size << " bytes"); + goto Return_error; + } + + // Fix for an issue with Linux Kernel found during tests at Tencent. + // + // There was a bug in older Linux Kernel which caused that when the internal + // buffer was depleted during reading from the network, not the whole buffer + // was copied from the packet, EVEN THOUGH THE GIVEN BUFFER WAS OF ENOUGH SIZE. + // It was still very kind of the buggy procedure, though, that at least + // they inform the caller about that this has happened by setting MSG_TRUNC + // flag. + // + // Normally this flag should be set only if there was too small buffer given + // by the caller, so as this code knows that the size is enough, it never + // predicted this to happen. Just for a case then when you run this on a buggy + // system that suffers of this problem, the fix for this case is left here. + // + // When this happens, then you have at best a fragment of the buffer and it's + // useless anyway. This is solved by dropping the packet and fake that no + // packet was received, so the packet will be then retransmitted. + if ( msg_flags != 0 ) + { + HLOGC(mglog.Debug, log << CONID() << "NET ERROR: packet size=" << recv_size + << " msg_flags=0x" << hex << msg_flags << ", possibly MSG_TRUNC (0x" << hex << int(MSG_TRUNC) << ")"); + status = RST_AGAIN; + goto Return_error; + } + + packet.setLength(recv_size - CPacket::HDR_SIZE); + + // convert back into local host order + // XXX use NtoHLA(). + //for (int i = 0; i < 4; ++ i) + // packet.m_nHeader[i] = ntohl(packet.m_nHeader[i]); + { + uint32_t* p = packet.m_nHeader; + for (size_t i = 0; i < SRT_PH__SIZE; ++ i) + { + *p = ntohl(*p); + ++ p; + } + } + + if (packet.isControl()) + { + for (size_t j = 0, n = packet.getLength() / sizeof (uint32_t); j < n; ++ j) + *((uint32_t *)packet.m_pcData + j) = ntohl(*((uint32_t *)packet.m_pcData + j)); + } + + return RST_OK; + +Return_error: + packet.setLength(-1); + return status; +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/channel.h b/trunk/3rdparty/srt-1-fit/srtcore/channel.h new file mode 100644 index 000000000..0efdcaa8f --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/channel.h @@ -0,0 +1,187 @@ +/* + * 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 01/27/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef __UDT_CHANNEL_H__ +#define __UDT_CHANNEL_H__ + + +#include "udt.h" +#include "packet.h" +#include "netinet_any.h" + +class CChannel +{ +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 ""; } + + CChannel(); + CChannel(int version); + ~CChannel(); + + /// Open a UDP channel. + /// @param [in] addr The local address that UDP will use. + + void open(const sockaddr* addr = NULL); + + /// Open a UDP channel based on an existing UDP socket. + /// @param [in] udpsock UDP socket descriptor. + + void attach(UDPSOCKET udpsock); + + /// Disconnect and close the UDP entity. + + void close() const; + + /// Get the UDP sending buffer size. + /// @return Current UDP sending buffer size. + + int getSndBufSize(); + + /// Get the UDP receiving buffer size. + /// @return Current UDP receiving buffer size. + + int getRcvBufSize(); + + /// Set the UDP sending buffer size. + /// @param [in] size expected UDP sending buffer size. + + void setSndBufSize(int size); + + /// Set the UDP receiving buffer size. + /// @param [in] size expected UDP receiving buffer size. + + void setRcvBufSize(int size); + + /// Set the IPV6ONLY option. + /// @param [in] IPV6ONLY value. + + void setIpV6Only(int ipV6Only); + + /// Query the socket address that the channel is using. + /// @param [out] addr pointer to store the returned socket address. + + void getSockAddr(sockaddr* addr) const; + + /// Query the peer side socket address that the channel is connect to. + /// @param [out] addr pointer to store the returned socket address. + + void getPeerAddr(sockaddr* addr) const; + + /// 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 sendto(const sockaddr* addr, CPacket& packet) const; + + /// 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. + + 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; +#endif + + 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; } + +private: + 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 +#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; +}; + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/common.cpp b/trunk/3rdparty/srt-1-fit/srtcore/common.cpp new file mode 100644 index 000000000..5614de4af --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/common.cpp @@ -0,0 +1,1044 @@ +/* + * 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 - 2016, 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 07/25/2010 +modified by + Haivision Systems Inc. +*****************************************************************************/ + + +#ifndef _WIN32 + #include + #include + #include + #if __APPLE__ + #include "TargetConditionals.h" + #endif + #if defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1) + #include + #endif +#else + #include + #include + #include +#ifndef __MINGW__ + #include +#endif +#endif + +#include +#include +#include +#include +#include +#include "srt.h" +#include "md5.h" +#include "common.h" +#include "logging.h" +#include "threadname.h" + +#include // SysStrError + +bool CTimer::m_bUseMicroSecond = false; +uint64_t CTimer::s_ullCPUFrequency = CTimer::readCPUFrequency(); + +pthread_mutex_t CTimer::m_EventLock = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t CTimer::m_EventCond = PTHREAD_COND_INITIALIZER; + +CTimer::CTimer(): +m_ullSchedTime_tk(), +m_TickCond(), +m_TickLock() +{ + pthread_mutex_init(&m_TickLock, NULL); + +#if ENABLE_MONOTONIC_CLOCK + pthread_condattr_t CondAttribs; + pthread_condattr_init(&CondAttribs); + pthread_condattr_setclock(&CondAttribs, CLOCK_MONOTONIC); + pthread_cond_init(&m_TickCond, &CondAttribs); +#else + pthread_cond_init(&m_TickCond, NULL); +#endif +} + +CTimer::~CTimer() +{ + pthread_mutex_destroy(&m_TickLock); + pthread_cond_destroy(&m_TickCond); +} + +void CTimer::rdtsc(uint64_t &x) +{ + if (m_bUseMicroSecond) + { + x = getTime(); + return; + } + + #ifdef IA32 + uint32_t lval, hval; + //asm volatile ("push %eax; push %ebx; push %ecx; push %edx"); + //asm volatile ("xor %eax, %eax; cpuid"); + asm volatile ("rdtsc" : "=a" (lval), "=d" (hval)); + //asm volatile ("pop %edx; pop %ecx; pop %ebx; pop %eax"); + x = hval; + x = (x << 32) | lval; + #elif defined(IA64) + asm ("mov %0=ar.itc" : "=r"(x) :: "memory"); + #elif defined(AMD64) + uint32_t lval, hval; + asm ("rdtsc" : "=a" (lval), "=d" (hval)); + x = hval; + x = (x << 32) | lval; + #elif defined(_WIN32) + // This function should not fail, because we checked the QPC + // when calling to QueryPerformanceFrequency. If it failed, + // the m_bUseMicroSecond was set to true. + QueryPerformanceCounter((LARGE_INTEGER *)&x); + #elif defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1) + x = mach_absolute_time(); + #else + // use system call to read time clock for other archs + x = getTime(); + #endif +} + +uint64_t CTimer::readCPUFrequency() +{ + uint64_t frequency = 1; // 1 tick per microsecond. + +#if defined(IA32) || defined(IA64) || defined(AMD64) + uint64_t t1, t2; + + rdtsc(t1); + timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + nanosleep(&ts, NULL); + rdtsc(t2); + + // CPU clocks per microsecond + frequency = (t2 - t1) / 100000; +#elif defined(_WIN32) + LARGE_INTEGER counts_per_sec; + if (QueryPerformanceFrequency(&counts_per_sec)) + frequency = counts_per_sec.QuadPart / 1000000; +#elif defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1) + mach_timebase_info_data_t info; + mach_timebase_info(&info); + frequency = info.denom * uint64_t(1000) / info.numer; +#endif + + // Fall back to microsecond if the resolution is not high enough. + if (frequency < 10) + { + frequency = 1; + m_bUseMicroSecond = true; + } + return frequency; +} + +uint64_t CTimer::getCPUFrequency() +{ + return s_ullCPUFrequency; +} + +void CTimer::sleep(uint64_t interval_tk) +{ + uint64_t t; + rdtsc(t); + + // sleep next "interval" time + sleepto(t + interval_tk); +} + +void CTimer::sleepto(uint64_t nexttime_tk) +{ + // Use class member such that the method can be interrupted by others + m_ullSchedTime_tk = nexttime_tk; + + uint64_t t; + rdtsc(t); + +#if USE_BUSY_WAITING +#if defined(_WIN32) + const uint64_t threshold_us = 10000; // 10 ms on Windows: bad accuracy of timers +#else + const uint64_t threshold_us = 1000; // 1 ms on non-Windows platforms +#endif +#endif + + while (t < m_ullSchedTime_tk) + { +#if USE_BUSY_WAITING + uint64_t wait_us = (m_ullSchedTime_tk - t) / s_ullCPUFrequency; + if (wait_us <= 2 * threshold_us) + break; + wait_us -= threshold_us; +#else + const uint64_t wait_us = (m_ullSchedTime_tk - t) / s_ullCPUFrequency; + if (wait_us == 0) + break; +#endif + + timespec timeout; +#if ENABLE_MONOTONIC_CLOCK + clock_gettime(CLOCK_MONOTONIC, &timeout); + const uint64_t time_us = timeout.tv_sec * uint64_t(1000000) + (timeout.tv_nsec / 1000) + wait_us; + timeout.tv_sec = time_us / 1000000; + timeout.tv_nsec = (time_us % 1000000) * 1000; +#else + timeval now; + gettimeofday(&now, 0); + const uint64_t time_us = now.tv_sec * uint64_t(1000000) + now.tv_usec + wait_us; + timeout.tv_sec = time_us / 1000000; + timeout.tv_nsec = (time_us % 1000000) * 1000; +#endif + + THREAD_PAUSED(); + pthread_mutex_lock(&m_TickLock); + pthread_cond_timedwait(&m_TickCond, &m_TickLock, &timeout); + pthread_mutex_unlock(&m_TickLock); + THREAD_RESUMED(); + + rdtsc(t); + } + +#if USE_BUSY_WAITING + while (t < m_ullSchedTime_tk) + { +#ifdef IA32 + __asm__ volatile ("pause; rep; nop; nop; nop; nop; nop;"); +#elif IA64 + __asm__ volatile ("nop 0; nop 0; nop 0; nop 0; nop 0;"); +#elif AMD64 + __asm__ volatile ("nop; nop; nop; nop; nop;"); +#elif defined(_WIN32) && !defined(__MINGW__) + __nop(); + __nop(); + __nop(); + __nop(); + __nop(); +#endif + + rdtsc(t); + } +#endif +} + +void CTimer::interrupt() +{ + // schedule the sleepto time to the current CCs, so that it will stop + rdtsc(m_ullSchedTime_tk); + tick(); +} + +void CTimer::tick() +{ + pthread_cond_signal(&m_TickCond); +} + +uint64_t CTimer::getTime() +{ + // XXX Do further study on that. Currently Cygwin is also using gettimeofday, + // however Cygwin platform is supported only for testing purposes. + + //For other systems without microsecond level resolution, add to this conditional compile +#if defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1) + // Otherwise we will have an infinite recursive functions calls + if (m_bUseMicroSecond == false) + { + uint64_t x; + rdtsc(x); + return x / s_ullCPUFrequency; + } + // Specific fix may be necessary if rdtsc is not available either. + // Going further on Apple platforms might cause issue, fixed with PR #301. + // But it is very unlikely for the latest platforms. +#endif + timeval t; + gettimeofday(&t, 0); + return t.tv_sec * uint64_t(1000000) + t.tv_usec; +} + +void CTimer::triggerEvent() +{ + pthread_cond_signal(&m_EventCond); +} + +CTimer::EWait CTimer::waitForEvent() +{ + timeval now; + timespec timeout; + gettimeofday(&now, 0); + if (now.tv_usec < 990000) + { + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = (now.tv_usec + 10000) * 1000; + } + else + { + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = (now.tv_usec + 10000 - 1000000) * 1000; + } + pthread_mutex_lock(&m_EventLock); + int reason = pthread_cond_timedwait(&m_EventCond, &m_EventLock, &timeout); + pthread_mutex_unlock(&m_EventLock); + + return reason == ETIMEDOUT ? WT_TIMEOUT : reason == 0 ? WT_EVENT : WT_ERROR; +} + +void CTimer::sleep() +{ + #ifndef _WIN32 + usleep(10); + #else + Sleep(1); + #endif +} + +int CTimer::condTimedWaitUS(pthread_cond_t* cond, pthread_mutex_t* mutex, uint64_t delay) { + timeval now; + gettimeofday(&now, 0); + const uint64_t time_us = now.tv_sec * uint64_t(1000000) + now.tv_usec + delay; + timespec timeout; + timeout.tv_sec = time_us / 1000000; + timeout.tv_nsec = (time_us % 1000000) * 1000; + + return pthread_cond_timedwait(cond, mutex, &timeout); +} + + +// Automatically lock in constructor +CGuard::CGuard(pthread_mutex_t& lock, bool shouldwork): + m_Mutex(lock), + m_iLocked(-1) +{ + if (shouldwork) + m_iLocked = pthread_mutex_lock(&m_Mutex); +} + +// Automatically unlock in destructor +CGuard::~CGuard() +{ + if (m_iLocked == 0) + pthread_mutex_unlock(&m_Mutex); +} + +// After calling this on a scoped lock wrapper (CGuard), +// the mutex will be unlocked right now, and no longer +// in destructor +void CGuard::forceUnlock() +{ + if (m_iLocked == 0) + { + pthread_mutex_unlock(&m_Mutex); + m_iLocked = -1; + } +} + +int CGuard::enterCS(pthread_mutex_t& lock) +{ + return pthread_mutex_lock(&lock); +} + +int CGuard::leaveCS(pthread_mutex_t& lock) +{ + return pthread_mutex_unlock(&lock); +} + +void CGuard::createMutex(pthread_mutex_t& lock) +{ + pthread_mutex_init(&lock, NULL); +} + +void CGuard::releaseMutex(pthread_mutex_t& lock) +{ + pthread_mutex_destroy(&lock); +} + +void CGuard::createCond(pthread_cond_t& cond) +{ + pthread_cond_init(&cond, NULL); +} + +void CGuard::releaseCond(pthread_cond_t& cond) +{ + pthread_cond_destroy(&cond); +} + +// +CUDTException::CUDTException(CodeMajor major, CodeMinor minor, int err): +m_iMajor(major), +m_iMinor(minor) +{ + if (err == -1) + #ifndef _WIN32 + m_iErrno = errno; + #else + m_iErrno = GetLastError(); + #endif + else + m_iErrno = err; +} + +CUDTException::CUDTException(const CUDTException& e): +m_iMajor(e.m_iMajor), +m_iMinor(e.m_iMinor), +m_iErrno(e.m_iErrno), +m_strMsg() +{ +} + +CUDTException::~CUDTException() +{ +} + +const char* CUDTException::getErrorMessage() +{ + // translate "Major:Minor" code into text message. + + switch (m_iMajor) + { + case MJ_SUCCESS: + m_strMsg = "Success"; + break; + + case MJ_SETUP: + m_strMsg = "Connection setup failure"; + + switch (m_iMinor) + { + case MN_TIMEOUT: + m_strMsg += ": connection time out"; + break; + + case MN_REJECTED: + m_strMsg += ": connection rejected"; + break; + + case MN_NORES: + m_strMsg += ": unable to create/configure SRT socket"; + break; + + case MN_SECURITY: + m_strMsg += ": abort for security reasons"; + break; + + default: + break; + } + + break; + + case MJ_CONNECTION: + switch (m_iMinor) + { + case MN_CONNLOST: + m_strMsg = "Connection was broken"; + break; + + case MN_NOCONN: + m_strMsg = "Connection does not exist"; + break; + + default: + break; + } + + break; + + case MJ_SYSTEMRES: + m_strMsg = "System resource failure"; + + switch (m_iMinor) + { + case MN_THREAD: + m_strMsg += ": unable to create new threads"; + break; + + case MN_MEMORY: + m_strMsg += ": unable to allocate buffers"; + break; + + default: + break; + } + + break; + + case MJ_FILESYSTEM: + m_strMsg = "File system failure"; + + switch (m_iMinor) + { + case MN_SEEKGFAIL: + m_strMsg += ": cannot seek read position"; + break; + + case MN_READFAIL: + m_strMsg += ": failure in read"; + break; + + case MN_SEEKPFAIL: + m_strMsg += ": cannot seek write position"; + break; + + case MN_WRITEFAIL: + m_strMsg += ": failure in write"; + break; + + default: + break; + } + + break; + + case MJ_NOTSUP: + m_strMsg = "Operation not supported"; + + switch (m_iMinor) + { + case MN_ISBOUND: + m_strMsg += ": Cannot do this operation on a BOUND socket"; + break; + + case MN_ISCONNECTED: + m_strMsg += ": Cannot do this operation on a CONNECTED socket"; + break; + + case MN_INVAL: + m_strMsg += ": Bad parameters"; + break; + + case MN_SIDINVAL: + m_strMsg += ": Invalid socket ID"; + break; + + case MN_ISUNBOUND: + m_strMsg += ": Cannot do this operation on an UNBOUND socket"; + break; + + case MN_NOLISTEN: + m_strMsg += ": Socket is not in listening state"; + break; + + case MN_ISRENDEZVOUS: + m_strMsg += ": Listen/accept is not supported in rendezous connection setup"; + break; + + case MN_ISRENDUNBOUND: + m_strMsg += ": Cannot call connect on UNBOUND socket in rendezvous connection setup"; + break; + + case MN_INVALMSGAPI: + m_strMsg += ": Incorrect use of Message API (sendmsg/recvmsg)."; + break; + + case MN_INVALBUFFERAPI: + m_strMsg += ": Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile)."; + break; + + case MN_BUSY: + m_strMsg += ": Another socket is already listening on the same port"; + break; + + case MN_XSIZE: + m_strMsg += ": Message is too large to send (it must be less than the SRT send buffer size)"; + break; + + case MN_EIDINVAL: + m_strMsg += ": Invalid epoll ID"; + break; + + default: + break; + } + + break; + + case MJ_AGAIN: + m_strMsg = "Non-blocking call failure"; + + switch (m_iMinor) + { + case MN_WRAVAIL: + m_strMsg += ": no buffer available for sending"; + break; + + case MN_RDAVAIL: + m_strMsg += ": no data available for reading"; + break; + + case MN_XMTIMEOUT: + m_strMsg += ": transmission timed out"; + break; + +#ifdef SRT_ENABLE_ECN + case MN_CONGESTION: + m_strMsg += ": early congestion notification"; + break; +#endif /* SRT_ENABLE_ECN */ + default: + break; + } + + break; + + case MJ_PEERERROR: + m_strMsg = "The peer side has signalled an error"; + + break; + + default: + m_strMsg = "Unknown error"; + } + + // Adding "errno" information + if ((MJ_SUCCESS != m_iMajor) && (0 < m_iErrno)) + { + m_strMsg += ": " + SysStrError(m_iErrno); + } + + return m_strMsg.c_str(); +} + +#define UDT_XCODE(mj, mn) (int(mj)*1000)+int(mn) + +int CUDTException::getErrorCode() const +{ + return UDT_XCODE(m_iMajor, m_iMinor); +} + +int CUDTException::getErrno() const +{ + return m_iErrno; +} + + +void CUDTException::clear() +{ + m_iMajor = MJ_SUCCESS; + m_iMinor = MN_NONE; + m_iErrno = 0; +} + +#undef UDT_XCODE + +// +bool CIPAddress::ipcmp(const sockaddr* addr1, const sockaddr* addr2, int ver) +{ + if (AF_INET == ver) + { + sockaddr_in* a1 = (sockaddr_in*)addr1; + sockaddr_in* a2 = (sockaddr_in*)addr2; + + if ((a1->sin_port == a2->sin_port) && (a1->sin_addr.s_addr == a2->sin_addr.s_addr)) + return true; + } + else + { + sockaddr_in6* a1 = (sockaddr_in6*)addr1; + sockaddr_in6* a2 = (sockaddr_in6*)addr2; + + if (a1->sin6_port == a2->sin6_port) + { + for (int i = 0; i < 16; ++ i) + if (*((char*)&(a1->sin6_addr) + i) != *((char*)&(a2->sin6_addr) + i)) + return false; + + return true; + } + } + + return false; +} + +void CIPAddress::ntop(const sockaddr* addr, uint32_t ip[4], int ver) +{ + if (AF_INET == ver) + { + sockaddr_in* a = (sockaddr_in*)addr; + ip[0] = a->sin_addr.s_addr; + } + else + { + sockaddr_in6* a = (sockaddr_in6*)addr; + ip[3] = (a->sin6_addr.s6_addr[15] << 24) + (a->sin6_addr.s6_addr[14] << 16) + (a->sin6_addr.s6_addr[13] << 8) + a->sin6_addr.s6_addr[12]; + ip[2] = (a->sin6_addr.s6_addr[11] << 24) + (a->sin6_addr.s6_addr[10] << 16) + (a->sin6_addr.s6_addr[9] << 8) + a->sin6_addr.s6_addr[8]; + ip[1] = (a->sin6_addr.s6_addr[7] << 24) + (a->sin6_addr.s6_addr[6] << 16) + (a->sin6_addr.s6_addr[5] << 8) + a->sin6_addr.s6_addr[4]; + ip[0] = (a->sin6_addr.s6_addr[3] << 24) + (a->sin6_addr.s6_addr[2] << 16) + (a->sin6_addr.s6_addr[1] << 8) + a->sin6_addr.s6_addr[0]; + } +} + +void CIPAddress::pton(sockaddr* addr, const uint32_t ip[4], int ver) +{ + if (AF_INET == ver) + { + sockaddr_in* a = (sockaddr_in*)addr; + a->sin_addr.s_addr = ip[0]; + } + else + { + sockaddr_in6* a = (sockaddr_in6*)addr; + for (int i = 0; i < 4; ++ i) + { + a->sin6_addr.s6_addr[i * 4] = ip[i] & 0xFF; + a->sin6_addr.s6_addr[i * 4 + 1] = (unsigned char)((ip[i] & 0xFF00) >> 8); + a->sin6_addr.s6_addr[i * 4 + 2] = (unsigned char)((ip[i] & 0xFF0000) >> 16); + a->sin6_addr.s6_addr[i * 4 + 3] = (unsigned char)((ip[i] & 0xFF000000) >> 24); + } + } +} + +using namespace std; + + +static string ShowIP4(const sockaddr_in* sin) +{ + ostringstream os; + union + { + in_addr sinaddr; + unsigned char ip[4]; + }; + sinaddr = sin->sin_addr; + + os << int(ip[0]); + os << "."; + os << int(ip[1]); + os << "."; + os << int(ip[2]); + os << "."; + os << int(ip[3]); + return os.str(); +} + +static string ShowIP6(const sockaddr_in6* sin) +{ + ostringstream os; + os.setf(ios::uppercase); + + bool sep = false; + for (size_t i = 0; i < 16; ++i) + { + int v = sin->sin6_addr.s6_addr[i]; + if ( v ) + { + if ( sep ) + os << ":"; + + os << hex << v; + sep = true; + } + } + + return os.str(); +} + +string CIPAddress::show(const sockaddr* adr) +{ + if ( adr->sa_family == AF_INET ) + return ShowIP4((const sockaddr_in*)adr); + else if ( adr->sa_family == AF_INET6 ) + return ShowIP6((const sockaddr_in6*)adr); + else + return "(unsupported sockaddr type)"; +} + +// +void CMD5::compute(const char* input, unsigned char result[16]) +{ + md5_state_t state; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)input, strlen(input)); + md5_finish(&state, result); +} + +std::string MessageTypeStr(UDTMessageType mt, uint32_t extt) +{ + using std::string; + + static const char* const udt_types [] = { + "handshake", + "keepalive", + "ack", + "lossreport", + "cgwarning", //4 + "shutdown", + "ackack", + "dropreq", + "peererror", //8 + }; + + static const char* const srt_types [] = { + "EXT:none", + "EXT:hsreq", + "EXT:hsrsp", + "EXT:kmreq", + "EXT:kmrsp", + "EXT:sid", + "EXT:congctl" + }; + + + if ( mt == UMSG_EXT ) + { + if ( extt >= Size(srt_types) ) + return "EXT:unknown"; + + return srt_types[extt]; + } + + if ( size_t(mt) > Size(udt_types) ) + return "unknown"; + + return udt_types[mt]; +} + +std::string ConnectStatusStr(EConnectStatus cst) +{ + return + cst == CONN_CONTINUE ? "INDUCED/CONCLUDING" + : cst == CONN_RUNNING ? "RUNNING" + : cst == CONN_ACCEPT ? "ACCEPTED" + : cst == CONN_RENDEZVOUS ? "RENDEZVOUS (HSv5)" + : cst == CONN_AGAIN ? "AGAIN" + : cst == CONN_CONFUSED ? "MISSING HANDSHAKE" + : "REJECTED"; +} + +std::string TransmissionEventStr(ETransmissionEvent ev) +{ + static const char* const vals [] = + { + "init", + "ack", + "ackack", + "lossreport", + "checktimer", + "send", + "receive", + "custom" + }; + + size_t vals_size = Size(vals); + + if (size_t(ev) >= vals_size) + return "UNKNOWN"; + return vals[ev]; +} + +extern const char* const srt_rejectreason_msg [] = { + "Unknown or erroneous", + "Error in system calls", + "Peer rejected connection", + "Resource allocation failure", + "Rogue peer or incorrect parameters", + "Listener's backlog exceeded", + "Internal Program Error", + "Socket is being closed", + "Peer version too old", + "Rendezvous-mode cookie collision", + "Incorrect passphrase", + "Password required or unexpected", + "MessageAPI/StreamAPI collision", + "Congestion controller type collision", + "Packet Filter type collision" +}; + +const char* srt_rejectreason_str(SRT_REJECT_REASON rid) +{ + int id = rid; + static const size_t ra_size = Size(srt_rejectreason_msg); + if (size_t(id) >= ra_size) + return srt_rejectreason_msg[0]; + return srt_rejectreason_msg[id]; +} + +// Some logging imps +#if ENABLE_LOGGING + +namespace srt_logging +{ + +std::string FormatTime(uint64_t time) +{ + using namespace std; + + time_t sec = time/1000000; + time_t usec = time%1000000; + + time_t tt = sec; + struct tm tm = SysLocalTime(tt); + + char tmp_buf[512]; + strftime(tmp_buf, 512, "%X.", &tm); + + ostringstream out; + out << tmp_buf << setfill('0') << setw(6) << usec; + return out.str(); +} + +LogDispatcher::Proxy::Proxy(LogDispatcher& guy) : that(guy), that_enabled(that.CheckEnabled()) +{ + if (that_enabled) + { + i_file = ""; + i_line = 0; + flags = that.src_config->flags; + // Create logger prefix + that.CreateLogLinePrefix(os); + } +} + +LogDispatcher::Proxy LogDispatcher::operator()() +{ + return Proxy(*this); +} + +void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) +{ + using namespace std; + + char tmp_buf[512]; + if ( !isset(SRT_LOGF_DISABLE_TIME) ) + { + // Not necessary if sending through the queue. + timeval tv; + gettimeofday(&tv, 0); + struct tm tm = SysLocalTime((time_t) tv.tv_sec); + + strftime(tmp_buf, 512, "%X.", &tm); + serr << tmp_buf << setw(6) << setfill('0') << tv.tv_usec; + } + + string out_prefix; + if ( !isset(SRT_LOGF_DISABLE_SEVERITY) ) + { + out_prefix = prefix; + } + + // Note: ThreadName::get needs a buffer of size min. ThreadName::BUFSIZE + if ( !isset(SRT_LOGF_DISABLE_THREADNAME) && ThreadName::get(tmp_buf) ) + { + serr << "/" << tmp_buf << out_prefix << ": "; + } + else + { + serr << out_prefix << ": "; + } +} + +std::string LogDispatcher::Proxy::ExtractName(std::string pretty_function) +{ + if ( pretty_function == "" ) + return ""; + size_t pos = pretty_function.find('('); + if ( pos == std::string::npos ) + return pretty_function; // return unchanged. + + pretty_function = pretty_function.substr(0, pos); + + // There are also template instantiations where the instantiating + // parameters are encrypted inside. Therefore, search for the first + // open < and if found, search for symmetric >. + + int depth = 1; + pos = pretty_function.find('<'); + if ( pos != std::string::npos ) + { + size_t end = pos+1; + for(;;) + { + ++pos; + if ( pos == pretty_function.size() ) + { + --pos; + break; + } + if ( pretty_function[pos] == '<' ) + { + ++depth; + continue; + } + + if ( pretty_function[pos] == '>' ) + { + --depth; + if ( depth <= 0 ) + break; + continue; + } + } + + std::string afterpart = pretty_function.substr(pos+1); + pretty_function = pretty_function.substr(0, end) + ">" + afterpart; + } + + // Now see how many :: can be found in the name. + // If this occurs more than once, take the last two. + pos = pretty_function.rfind("::"); + + if ( pos == std::string::npos || pos < 2 ) + return pretty_function; // return whatever this is. No scope name. + + // Find the next occurrence of :: - if found, copy up to it. If not, + // return whatever is found. + pos -= 2; + pos = pretty_function.rfind("::", pos); + if ( pos == std::string::npos ) + return pretty_function; // nothing to cut + + return pretty_function.substr(pos+2); +} + +} // (end namespace srt_logging) + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/common.h b/trunk/3rdparty/srt-1-fit/srtcore/common.h new file mode 100644 index 000000000..95d9161f0 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/common.h @@ -0,0 +1,850 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 08/01/2009 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef __UDT_COMMON_H__ +#define __UDT_COMMON_H__ + +#define _CRT_SECURE_NO_WARNINGS 1 // silences windows complaints for sscanf + +#include +#ifndef _WIN32 + #include + #include +#else + // #include + //#include +#endif +#include +#include "udt.h" +#include "utilities.h" + + +#ifdef _DEBUG +#include +#define SRT_ASSERT(cond) assert(cond) +#else +#define SRT_ASSERT(cond) +#endif + + +enum UDTSockType +{ + UDT_UNDEFINED = 0, // initial trap representation + UDT_STREAM = 1, + UDT_DGRAM +}; + + +/// The message types used by UDT protocol. This is a part of UDT +/// protocol and should never be changed. +enum UDTMessageType +{ + UMSG_HANDSHAKE = 0, //< Connection Handshake. Control: see @a CHandShake. + UMSG_KEEPALIVE = 1, //< Keep-alive. + UMSG_ACK = 2, //< Acknowledgement. Control: past-the-end sequence number up to which packets have been received. + UMSG_LOSSREPORT = 3, //< Negative Acknowledgement (NAK). Control: Loss list. + UMSG_CGWARNING = 4, //< Congestion warning. + UMSG_SHUTDOWN = 5, //< Shutdown. + UMSG_ACKACK = 6, //< Acknowledgement of Acknowledgement. Add info: The ACK sequence number + UMSG_DROPREQ = 7, //< Message Drop Request. Add info: Message ID. Control Info: (first, last) number of the message. + UMSG_PEERERROR = 8, //< Signal from the Peer side. Add info: Error code. + // ... add extra code types here + UMSG_END_OF_TYPES, + UMSG_EXT = 0x7FFF //< For the use of user-defined control packets. +}; + +// This side's role is: INITIATOR prepares the environment first, and sends +// appropriate information to the peer. The peer must be RESPONDER and be ready +// to receive it. It's important for the encryption: the INITIATOR side generates +// the KM, and sends it to RESPONDER. RESPONDER awaits KM received from the +// INITIATOR. Note that in bidirectional mode - that is always with HSv5 - the +// INITIATOR creates both sending and receiving contexts, then sends the key to +// RESPONDER, which creates both sending and receiving contexts, using the same +// key received from INITIATOR. +// +// The method of selection: +// +// In HSv4, it's always data sender (the party that sets SRTO_SENDER flag on the +// socket) INITIATOR, and receiver - RESPONDER. The HSREQ and KMREQ are done +// AFTER the UDT connection is done using UMSG_EXT extension messages. As this +// is unidirectional, the INITIATOR prepares the sending context only, the +// RESPONDER - receiving context only. +// +// In HSv5, for caller-listener configuration, it's simple: caller is INITIATOR, +// listener is RESPONDER. In case of rendezvous the parties are equivalent, +// so the role is resolved by "cookie contest". Rendezvous sockets both know +// each other's cookie generated during the URQ_WAVEAHAND handshake phase. +// The cookies are simply compared as integer numbers; the party which's cookie +// is a greater number becomes an INITIATOR, and the other party becomes a +// RESPONDER. +// +// The case of a draw - that both occasionally have baked identical cookies - +// is treated as an extremely rare and virtually impossible case, so this +// results in connection rejected. +enum HandshakeSide +{ + HSD_DRAW, + HSD_INITIATOR, //< Side that initiates HSREQ/KMREQ. HSv4: data sender, HSv5: connecting socket or winner rendezvous socket + HSD_RESPONDER //< Side that expects HSREQ/KMREQ from the peer. HSv4: data receiver, HSv5: accepted socket or loser rendezvous socket +}; + +// For debug +std::string MessageTypeStr(UDTMessageType mt, uint32_t extt = 0); + +//////////////////////////////////////////////////////////////////////////////// + +// Commonly used by various reading facilities +enum EReadStatus +{ + RST_OK = 0, //< A new portion of data has been received + RST_AGAIN, //< Nothing has been received, try again + RST_ERROR = -1 //< Irrecoverable error, please close descriptor and stop reading. +}; + +enum EConnectStatus +{ + CONN_ACCEPT = 0, //< Received final handshake that confirms connection established + CONN_REJECT = -1, //< Error during processing handshake. + CONN_CONTINUE = 1, //< induction->conclusion phase + CONN_RENDEZVOUS = 2, //< pass to a separate rendezvous processing (HSv5 only) + CONN_CONFUSED = 3, //< listener thinks it's connected, but caller missed conclusion + CONN_RUNNING = 10, //< no connection in progress, already connected + CONN_AGAIN = -2 //< No data was read, don't change any state. +}; + +std::string ConnectStatusStr(EConnectStatus est); + + +const int64_t BW_INFINITE = 1000000000/8; //Infinite=> 1 Gbps + + +enum ETransmissionEvent +{ + TEV_INIT, // --> After creation, and after any parameters were updated. + TEV_ACK, // --> When handling UMSG_ACK - older CCC:onAck() + TEV_ACKACK, // --> UDT does only RTT sync, can be read from CUDT::RTT(). + TEV_LOSSREPORT, // --> When handling UMSG_LOSSREPORT - older CCC::onLoss() + TEV_CHECKTIMER, // --> See TEV_CHT_REXMIT + TEV_SEND, // --> When the packet is scheduled for sending - older CCC::onPktSent + TEV_RECEIVE, // --> When a data packet was received - older CCC::onPktReceived + TEV_CUSTOM, // --> probably dead call - older CCC::processCustomMsg + + TEV__SIZE +}; + +std::string TransmissionEventStr(ETransmissionEvent ev); + +// Special parameter for TEV_CHECKTIMER +enum ECheckTimerStage +{ + TEV_CHT_INIT, // --> UDT: just update parameters, don't call any CCC::* + TEV_CHT_FASTREXMIT, // --> not available on UDT + TEV_CHT_REXMIT // --> CCC::onTimeout() in UDT +}; + +enum EInitEvent +{ + TEV_INIT_RESET = 0, + TEV_INIT_INPUTBW, + TEV_INIT_OHEADBW +}; + +class CPacket; + +// XXX Use some more standard less hand-crafted solution, if possible +// XXX Consider creating a mapping between TEV_* values and associated types, +// so that the type is compiler-enforced when calling updateCC() and when +// connecting signals to slots. +struct EventVariant +{ + enum Type {UNDEFINED, PACKET, ARRAY, ACK, STAGE, INIT} type; + union U + { + CPacket* packet; + int32_t ack; + struct + { + int32_t* ptr; + size_t len; + } array; + ECheckTimerStage stage; + EInitEvent init; + } u; + + EventVariant() + { + type = UNDEFINED; + memset(&u, 0, sizeof u); + } + + template + struct VariantFor; + + template + void Assign(Arg arg) + { + type = tp; + (u.*(VariantFor::field())) = arg; + //(u.*field) = arg; + } + + void operator=(CPacket* arg) { Assign(arg); }; + void operator=(int32_t arg) { Assign(arg); }; + void operator=(ECheckTimerStage arg) { Assign(arg); }; + void operator=(EInitEvent arg) { Assign(arg); }; + + // Note: UNDEFINED and ARRAY don't have assignment operator. + // For ARRAY you'll use 'set' function. For UNDEFINED there's nothing. + + + template + EventVariant(T arg) + { + *this = arg; + } + + const int32_t* get_ptr() const + { + return u.array.ptr; + } + + size_t get_len() + { + return u.array.len; + } + + void set(int32_t* ptr, size_t len) + { + type = ARRAY; + u.array.ptr = ptr; + u.array.len = len; + } + + EventVariant(int32_t* ptr, size_t len) + { + set(ptr, len); + } + + template + typename VariantFor::type get() + { + return u.*(VariantFor::field()); + } +}; + +/* + Maybe later. + This had to be a solution for automatic extraction of the + type hidden in particular EventArg for particular event so + that it's not runtime-mistaken. + + In order that this make sense there would be required an array + indexed by event id (just like a slot array m_Slots in CUDT), + where the "type distiller" function would be extracted and then + combined with the user-connected slot function this would call + it already with correct type. Note that also the ConnectSignal + function would have to get the signal id by template parameter, + not function parameter. For example: + + m_parent->ConnectSignal(SSLOT(updateOnSent)); + + in which updateOnSent would have to receive an appropriate type. + This has a disadvantage that you can't connect multiple signals + with different argument types to the same slot, you'd have to + make slot wrappers to translate arguments. + + It seems that a better idea would be to create binders that would + translate the argument from EventArg to the correct type according + to the rules imposed by particular event id. But I'd not make it + until there's a green light on C++11 for SRT, so maybe in a far future. + +template +class EventArgType; +#define MAP_EVENT_TYPE(tev, tp) template<> class EventArgType { typedef tp type; } +*/ + + +// The 'type' field wouldn't be even necessary if we + +template<> struct EventVariant::VariantFor +{ + typedef CPacket* type; + static type U::*field() {return &U::packet;} +}; + +template<> struct EventVariant::VariantFor +{ + typedef int32_t type; + static type U::*field() { return &U::ack; } +}; + +template<> struct EventVariant::VariantFor +{ + typedef ECheckTimerStage type; + static type U::*field() { return &U::stage; } +}; + +template<> struct EventVariant::VariantFor +{ + typedef EInitEvent type; + static type U::*field() { return &U::init; } +}; + +// Using a hand-crafted solution because there's a non-backward-compatible +// change between C++03 and others on the way up to C++17 (and we want this +// code to be compliant with all C++ standards): +// +// - there's std::mem_fun in C++03 - deprecated in C++11, removed in C++17 +// - std::function in C++11 would be perfect, but not in C++03 + +// This can be changed in future to use C++11 way, but only after C++03 +// compatibility is finally abaondoned. Until then, this stays with a custom +// class. + +class EventSlotBase +{ +public: + virtual void emit(ETransmissionEvent tev, EventVariant var) = 0; + typedef void dispatcher_t(void* opaque, ETransmissionEvent tev, EventVariant var); + + virtual ~EventSlotBase() {} +}; + +class SimpleEventSlot: public EventSlotBase +{ +public: + void* opaque; + dispatcher_t* dispatcher; + + SimpleEventSlot(void* op, dispatcher_t* disp): opaque(op), dispatcher(disp) {} + + void emit(ETransmissionEvent tev, EventVariant var) ATR_OVERRIDE + { + (*dispatcher)(opaque, tev, var); + } +}; + +template +class ObjectEventSlot: public EventSlotBase +{ +public: + typedef void (Class::*method_ptr_t)(ETransmissionEvent tev, EventVariant var); + + method_ptr_t pm; + Class* po; + + ObjectEventSlot(Class* o, method_ptr_t m): pm(m), po(o) {} + + void emit(ETransmissionEvent tev, EventVariant var) ATR_OVERRIDE + { + (po->*pm)(tev, var); + } +}; + + +struct EventSlot +{ + mutable EventSlotBase* slot; + // Create empty slot. Calls are ignored. + EventSlot(): slot(0) {} + + // "Stealing" copy constructor, following the auto_ptr method. + // This isn't very nice, but no other way to do it in C++03 + // without rvalue-reference and move. + EventSlot(const EventSlot& victim) + { + slot = victim.slot; // Should MOVE. + victim.slot = 0; + } + + EventSlot(void* op, EventSlotBase::dispatcher_t* disp) + { + slot = new SimpleEventSlot(op, disp); + } + + template + EventSlot(ObjectClass* obj, typename ObjectEventSlot::method_ptr_t method) + { + slot = new ObjectEventSlot(obj, method); + } + + void emit(ETransmissionEvent tev, EventVariant var) + { + if (!slot) + return; + slot->emit(tev, var); + } + + ~EventSlot() + { + if (slot) + delete slot; + } +}; + + +// Old UDT library specific classes, moved from utilities as utilities +// should now be general-purpose. + +class CTimer +{ +public: + CTimer(); + ~CTimer(); + +public: + + /// Sleep for "interval_tk" CCs. + /// @param [in] interval_tk CCs to sleep. + + void sleep(uint64_t interval_tk); + + /// Seelp until CC "nexttime_tk". + /// @param [in] nexttime_tk next time the caller is waken up. + + void sleepto(uint64_t nexttime_tk); + + /// Stop the sleep() or sleepto() methods. + + void interrupt(); + + /// trigger the clock for a tick, for better granuality in no_busy_waiting timer. + + void tick(); + +public: + + /// Read the CPU clock cycle into x. + /// @param [out] x to record cpu clock cycles. + + static void rdtsc(uint64_t &x); + + /// return the CPU frequency. + /// @return CPU frequency. + + static uint64_t getCPUFrequency(); + + /// check the current time, 64bit, in microseconds. + /// @return current time in microseconds. + + static uint64_t getTime(); + + /// trigger an event such as new connection, close, new data, etc. for "select" call. + + static void triggerEvent(); + + enum EWait {WT_EVENT, WT_ERROR, WT_TIMEOUT}; + + /// wait for an event to br triggered by "triggerEvent". + /// @retval WT_EVENT The event has happened + /// @retval WT_TIMEOUT The event hasn't happened, the function exited due to timeout + /// @retval WT_ERROR The function has exit due to an error + + static EWait waitForEvent(); + + /// sleep for a short interval. exact sleep time does not matter + + static void sleep(); + + /// Wait for condition with timeout + /// @param [in] cond Condition variable to wait for + /// @param [in] mutex locked mutex associated with the condition variable + /// @param [in] delay timeout in microseconds + /// @retval 0 Wait was successfull + /// @retval ETIMEDOUT The wait timed out + + static int condTimedWaitUS(pthread_cond_t* cond, pthread_mutex_t* mutex, uint64_t delay); + +private: + uint64_t getTimeInMicroSec(); + +private: + uint64_t m_ullSchedTime_tk; // next schedulled time + + pthread_cond_t m_TickCond; + pthread_mutex_t m_TickLock; + + static pthread_cond_t m_EventCond; + static pthread_mutex_t m_EventLock; + +private: + static uint64_t s_ullCPUFrequency; // CPU frequency : clock cycles per microsecond + static uint64_t readCPUFrequency(); + static bool m_bUseMicroSecond; // No higher resolution timer available, use gettimeofday(). +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CGuard +{ +public: + /// Constructs CGuard, which locks the given mutex for + /// the scope where this object exists. + /// @param lock Mutex to lock + /// @param if_condition If this is false, CGuard will do completely nothing + CGuard(pthread_mutex_t& lock, bool if_condition = true); + ~CGuard(); + +public: + static int enterCS(pthread_mutex_t& lock); + static int leaveCS(pthread_mutex_t& lock); + + static void createMutex(pthread_mutex_t& lock); + static void releaseMutex(pthread_mutex_t& lock); + + static void createCond(pthread_cond_t& cond); + static void releaseCond(pthread_cond_t& cond); + + void forceUnlock(); + +private: + pthread_mutex_t& m_Mutex; // Alias name of the mutex to be protected + int m_iLocked; // Locking status + + CGuard& operator=(const CGuard&); +}; + +class InvertedGuard +{ + pthread_mutex_t* m_pMutex; +public: + + InvertedGuard(pthread_mutex_t* smutex): m_pMutex(smutex) + { + if ( !smutex ) + return; + + CGuard::leaveCS(*smutex); + } + + ~InvertedGuard() + { + if ( !m_pMutex ) + return; + + CGuard::enterCS(*m_pMutex); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +// UDT Sequence Number 0 - (2^31 - 1) + +// seqcmp: compare two seq#, considering the wraping +// seqlen: length from the 1st to the 2nd seq#, including both +// seqoff: offset from the 2nd to the 1st seq# +// incseq: increase the seq# by 1 +// decseq: decrease the seq# by 1 +// incseq: increase the seq# by a given offset + +class CSeqNo +{ +public: + + /// This behaves like seq1 - seq2, in comparison to numbers, + /// and with the statement that only the sign of the result matters. + /// That is, it returns a negative value if seq1 < seq2, + /// positive if seq1 > seq2, and zero if they are equal. + /// The only correct application of this function is when you + /// compare two values and it works faster than seqoff. However + /// the result's meaning is only in its sign. DO NOT USE THE + /// VALUE for any other purpose. It is not meant to be the + /// distance between two sequence numbers. + /// + /// Example: to check if (seq1 %> seq2): seqcmp(seq1, seq2) > 0. + inline static int seqcmp(int32_t seq1, int32_t seq2) + {return (abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1);} + + /// This function measures a length of the range from seq1 to seq2, + /// WITH A PRECONDITION that certainly @a seq1 is earlier than @a seq2. + /// This can also include an enormously large distance between them, + /// that is, exceeding the m_iSeqNoTH value (can be also used to test + /// if this distance is larger). Prior to calling this function the + /// caller must be certain that @a seq2 is a sequence coming from a + /// later time than @a seq1, and still, of course, this distance didn't + /// exceed m_iMaxSeqNo. + inline static int seqlen(int32_t seq1, int32_t seq2) + {return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);} + + /// This behaves like seq2 - seq1, with the precondition that the true + /// distance between two sequence numbers never exceeds m_iSeqNoTH. + /// That is, if the difference in numeric values of these two arguments + /// exceeds m_iSeqNoTH, it is treated as if the later of these two + /// sequence numbers has overflown and actually a segment of the + /// MAX+1 value should be added to it to get the proper result. + /// + /// Note: this function does more calculations than seqcmp, so it should + /// be used if you need the exact distance between two sequences. If + /// you are only interested with their relationship, use seqcmp. + inline static int seqoff(int32_t seq1, int32_t seq2) + { + if (abs(seq1 - seq2) < m_iSeqNoTH) + return seq2 - seq1; + + if (seq1 < seq2) + return seq2 - seq1 - m_iMaxSeqNo - 1; + + return seq2 - seq1 + m_iMaxSeqNo + 1; + } + + inline static int32_t incseq(int32_t seq) + {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} + + inline static int32_t decseq(int32_t seq) + {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} + + inline static int32_t incseq(int32_t seq, int32_t inc) + {return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;} + // m_iMaxSeqNo >= inc + sec --- inc + sec <= m_iMaxSeqNo + // if inc + sec > m_iMaxSeqNo then return seq + inc - (m_iMaxSeqNo+1) + + inline static int32_t decseq(int32_t seq, int32_t dec) + { + // Check if seq - dec < 0, but before it would have happened + if ( seq < dec ) + { + int32_t left = dec - seq; // This is so many that is left after dragging dec to 0 + // So now decrement the (m_iMaxSeqNo+1) by "left" + return m_iMaxSeqNo - left + 1; + } + return seq - dec; + } + +public: + static const int32_t m_iSeqNoTH = 0x3FFFFFFF; // threshold for comparing seq. no. + static const int32_t m_iMaxSeqNo = 0x7FFFFFFF; // maximum sequence number used in UDT +}; + +//////////////////////////////////////////////////////////////////////////////// + +// UDT ACK Sub-sequence Number: 0 - (2^31 - 1) + +class CAckNo +{ +public: + inline static int32_t incack(int32_t ackno) + {return (ackno == m_iMaxAckSeqNo) ? 0 : ackno + 1;} + +public: + static const int32_t m_iMaxAckSeqNo = 0x7FFFFFFF; // maximum ACK sub-sequence number used in UDT +}; + + + +//////////////////////////////////////////////////////////////////////////////// + +struct CIPAddress +{ + static bool ipcmp(const struct sockaddr* addr1, const struct sockaddr* addr2, int ver = AF_INET); + static void ntop(const struct sockaddr* addr, uint32_t ip[4], int ver = AF_INET); + static void pton(struct sockaddr* addr, const uint32_t ip[4], int ver = AF_INET); + static std::string show(const struct sockaddr* adr); +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct CMD5 +{ + static void compute(const char* input, unsigned char result[16]); +}; + +// Debug stats +template +class StatsLossRecords +{ + int32_t initseq; + std::bitset array; + +public: + + StatsLossRecords(): initseq(-1) {} + + // To check if this structure still keeps record of that sequence. + // This is to check if the information about this not being found + // is still reliable. + bool exists(int32_t seq) + { + return initseq != -1 && CSeqNo::seqcmp(seq, initseq) >= 0; + } + + int32_t base() { return initseq; } + + void clear() + { + initseq = -1; + array.reset(); + } + + void add(int32_t lo, int32_t hi) + { + int32_t end = CSeqNo::incseq(hi); + for (int32_t i = lo; i != end; i = CSeqNo::incseq(i)) + add(i); + } + + void add(int32_t seq) + { + if ( array.none() ) + { + // May happen it wasn't initialized. Set it as initial loss sequence. + initseq = seq; + array[0] = true; + return; + } + + // Calculate the distance between this seq and the oldest one. + int seqdiff = CSeqNo::seqoff(initseq, seq); + if ( seqdiff > int(SIZE) ) + { + // Size exceeded. Drop the oldest sequences. + // First calculate how many must be removed. + size_t toremove = seqdiff - SIZE; + // Now, since that position, find the nearest 1 + while ( !array[toremove] && toremove <= SIZE ) + ++toremove; + + // All have to be dropped, so simply reset the array + if ( toremove == SIZE ) + { + initseq = seq; + array[0] = true; + return; + } + + // Now do the shift of the first found 1 to position 0 + // and its index add to initseq + initseq += toremove; + seqdiff -= toremove; + array >>= toremove; + } + + // Now set appropriate bit that represents this seq + array[seqdiff] = true; + } + + StatsLossRecords& operator << (int32_t seq) + { + add(seq); + return *this; + } + + void remove(int32_t seq) + { + // Check if is in range. If not, ignore. + int seqdiff = CSeqNo::seqoff(initseq, seq); + if ( seqdiff < 0 ) + return; // already out of array + if ( seqdiff > SIZE ) + return; // never was added! + + array[seqdiff] = true; + } + + bool find(int32_t seq) const + { + int seqdiff = CSeqNo::seqoff(initseq, seq); + if ( seqdiff < 0 ) + return false; // already out of array + if ( size_t(seqdiff) > SIZE ) + return false; // never was added! + + return array[seqdiff]; + } + +#if HAVE_CXX11 + + std::string to_string() const + { + std::string out; + for (size_t i = 0; i < SIZE; ++i) + { + if ( array[i] ) + out += std::to_string(initseq+i) + " "; + } + + return out; + } +#endif +}; + + +// Version parsing +inline ATR_CONSTEXPR uint32_t SrtVersion(int major, int minor, int patch) +{ + return patch + minor*0x100 + major*0x10000; +} + +inline int32_t SrtParseVersion(const char* v) +{ + int major, minor, patch; + int result = sscanf(v, "%d.%d.%d", &major, &minor, &patch); + + if (result != 3) + { + return 0; + } + + return major*0x10000 + minor*0x100 + patch; +} + +inline std::string SrtVersionString(int version) +{ + int patch = version % 0x100; + int minor = (version/0x100)%0x100; + int major = version/0x10000; + + char buf[20]; + sprintf(buf, "%d.%d.%d", major, minor, patch); + return buf; +} + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp b/trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp new file mode 100644 index 000000000..dde90ac27 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp @@ -0,0 +1,643 @@ +/* + * 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/. + * + */ + + +// This is a controversial thing, so temporarily blocking +//#define SRT_ENABLE_SYSTEMBUFFER_TRACE + + + + +#ifdef SRT_ENABLE_SYSTEMBUFFER_TRACE +#if defined(unix) +// XXX will be nonportable +#include +#endif +#endif + +#include +#include + + +#include "common.h" +#include "core.h" +#include "queue.h" +#include "packet.h" +#include "congctl.h" +#include "logging.h" + +using namespace std; +using namespace srt_logging; + +SrtCongestionControlBase::SrtCongestionControlBase(CUDT* parent) +{ + m_parent = parent; + m_dMaxCWndSize = m_parent->flowWindowSize(); + // RcvRate (deliveryRate()), RTT and Bandwidth can be read directly from CUDT when needed. + m_dCWndSize = 1000; + m_dPktSndPeriod = 1; +} + +void SrtCongestion::Check() +{ + if (!congctl) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); +} + +// Useful macro to shorthand passing a method as argument +// Requires "Me" name by which a class refers to itself +#define SSLOT(method) EventSlot(this, &Me:: method) + +class LiveCC: public SrtCongestionControlBase +{ + int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec) + size_t m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit + size_t m_zMaxPayloadSize; + + // NAKREPORT stuff. + int m_iMinNakInterval_us; // Minimum NAK Report Period (usec) + int m_iNakReportAccel; // NAK Report Period (RTT) accelerator + + typedef LiveCC Me; // required for SSLOT macro + +public: + + LiveCC(CUDT* parent) + : SrtCongestionControlBase(parent) + { + m_llSndMaxBW = BW_INFINITE; // 1 Gbbps in Bytes/sec BW_INFINITE + m_zMaxPayloadSize = parent->OPT_PayloadSize(); + 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 + + HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize); + + updatePktSndPeriod(); + + + // NOTE: TEV_SEND gets dispatched from Sending thread, all others + // 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)); + } + + bool checkTransArgs(SrtCongestion::TransAPI api, SrtCongestion::TransDir dir, const char* , size_t size, int , bool ) ATR_OVERRIDE + { + if (api != SrtCongestion::STA_MESSAGE) + { + LOGC(cclog.Error, log << "LiveCC: invalid API use. Only sendmsg/recvmsg allowed."); + return false; + } + + if (dir == SrtCongestion::STAD_SEND) + { + // For sending, check if the size of data doesn't exceed the maximum live packet size. + if (size > m_zMaxPayloadSize) + { + LOGC(cclog.Error, log << "LiveCC: payload size: " << size << " exceeds maximum allowed " << m_zMaxPayloadSize); + return false; + } + } + else + { + // For receiving, check if the buffer has enough space to keep the payload. + if (size < m_zMaxPayloadSize) + { + LOGC(cclog.Error, log << "LiveCC: buffer size: " << size << " is too small for the maximum possible " << m_zMaxPayloadSize); + return false; + } + } + + return true; + } + + // XXX You can decide here if the not-fully-packed packet should require immediate ACK or not. + // bool needsQuickACK(const CPacket& pkt) ATR_OVERRIDE + + virtual int64_t sndBandwidth() ATR_OVERRIDE { return m_llSndMaxBW; } + +private: + // SLOTS: + + // TEV_SEND -> CPacket*. + void updatePayloadSize(ETransmissionEvent, EventVariant var) + { + const CPacket& packet = *var.get(); + + // XXX NOTE: TEV_SEND is sent from CSndQueue::worker thread, which is + // different to threads running any other events (TEV_CHECKTIMER and TEV_ACK). + // The m_zSndAvgPayloadSize field is however left unguarded because + // there's no other modifier of this field. + // Worst case scenario, the procedure running in CRcvQueue::worker + // thread will pick up a "slightly outdated" average value from this + // field - this is insignificant. + m_zSndAvgPayloadSize = avg_iir<128, size_t>(m_zSndAvgPayloadSize, packet.getLength()); + HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize); + } + + void updatePktSndPeriod_onTimer(ETransmissionEvent , EventVariant var) + { + if ( var.get() != TEV_CHT_INIT ) + updatePktSndPeriod(); + } + + void updatePktSndPeriod_onAck(ETransmissionEvent , EventVariant ) + { + updatePktSndPeriod(); + } + + void updatePktSndPeriod() + { + // packet = payload + header + const double pktsize = (double) m_zSndAvgPayloadSize + 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); + } + + void setMaxBW(int64_t maxbw) + { + 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) + * is larger than the congestion window. + * Up to SRT 1.0.6, this value was set at 1000 pkts, which may be insufficient + * for satellite links with ~1000 msec RTT and high bit rate. + */ + // 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 + { + // bw is the bandwidth calculated with regard to the + // SRTO_INPUTBW and SRTO_OHEADBW parameters. The maxbw + // value simply represents the SRTO_MAXBW setting. + if (maxbw) + { + setMaxBW(maxbw); + return; + } + + if (bw == 0) + { + return; + } + + setMaxBW(bw); + } + + SrtCongestion::RexmitMethod rexmitMethod() ATR_OVERRIDE + { + return SrtCongestion::SRM_FASTREXMIT; + } + + uint64_t updateNAKInterval(uint64_t nakint_tk, int /*rcv_speed*/, size_t /*loss_length*/) ATR_OVERRIDE + { + /* + * duB: + * The RTT accounts for the time for the last NAK to reach sender and start resending lost pkts. + * The rcv_speed add the time to resend all the pkts in the loss list. + * + * For realtime Transport Stream content, pkts/sec is not a good indication of time to transmit + * since packets are not filled to m_iMSS and packet size average is lower than (7*188) + * for low bit rates. + * If NAK report is lost, another cycle (RTT) is requred which is bad for low latency so we + * accelerate the NAK Reports frequency, at the cost of possible duplicate resend. + * Finally, the UDT4 native minimum NAK interval (m_ullMinNakInt_tk) is 300 ms which is too high + * (~10 i30 video frames) to maintain low latency. + */ + + // Note: this value will still be reshaped to defined minimum, + // as per minNAKInterval. + return nakint_tk / m_iNakReportAccel; + } + + uint64_t minNAKInterval() ATR_OVERRIDE + { + return m_iMinNakInterval_us * CTimer::getCPUFrequency(); + } + +}; + + +class FileCC : public SrtCongestionControlBase +{ + typedef FileCC Me; // Required by SSLOT macro + + // Fields from CUDTCC + 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 + + int64_t m_maxSR; + +public: + + FileCC(CUDT* parent) + : SrtCongestionControlBase(parent) + , m_iRCInterval(CUDT::COMM_SYN_INTERVAL_US) + , m_LastRCTime(CTimer::getTime()) + , m_bSlowStart(true) + , m_iLastAck(parent->sndSeqNo()) + , m_bLoss(false) + , m_iLastDecSeq(CSeqNo::decseq(m_iLastAck)) + , m_dLastDecPeriod(1) + , m_iNAKCount(0) + , m_iDecRandom(1) + , m_iAvgNAKNum(0) + , m_iDecCount(0) + , m_maxSR(0) + { + // Note that this function is called at the moment of + // calling m_Smoother.configure(this). It is placed more less + // at the same position as the series-of-parameter-setting-then-init + // in the original UDT code. So, old CUDTCC::init() can be moved + // to constructor. + + // SmotherBase + m_dCWndSize = 16; + m_dPktSndPeriod = 1; + + parent->ConnectSignal(TEV_ACK, SSLOT(updateSndPeriod)); + parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(slowdownSndPeriod)); + parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(speedupToWindowSize)); + + HLOGC(cclog.Debug, log << "Creating FileCC"); + } + + bool checkTransArgs(SrtCongestion::TransAPI, SrtCongestion::TransDir, const char*, size_t, int, bool) ATR_OVERRIDE + { + // XXX + // The FileCC has currently no restrictions, although it should be + // rather required that the "message" mode or "buffer" mode be used on both sides the same. + // This must be somehow checked separately. + return true; + } + + 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... + // an irregular sized packet usually indicates the end of a message, so send an ACK immediately + return true; + } + + return false; + } + + void updateBandwidth(int64_t maxbw, int64_t) ATR_OVERRIDE + { + if (maxbw != 0) + { + m_maxSR = maxbw; + HLOGC(cclog.Debug, log << "FileCC: updated BW: " << m_maxSR); + } + } + +private: + + // SLOTS + void updateSndPeriod(ETransmissionEvent, EventVariant arg) + { + const int ack = arg.get(); + + const uint64_t currtime = CTimer::getTime(); + if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval) + return; + + m_LastRCTime = currtime; + + if (m_bSlowStart) + { + m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack); + m_iLastAck = ack; + + if (m_dCWndSize > m_dMaxCWndSize) + { + m_bSlowStart = false; + if (m_parent->deliveryRate() > 0) + { + m_dPktSndPeriod = 1000000.0 / m_parent->deliveryRate(); + HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:ENDED) wndsize=" + << m_dCWndSize << "/" << m_dMaxCWndSize + << " sndperiod=" << m_dPktSndPeriod << "us = 1M/(" + << m_parent->deliveryRate() << " pkts/s)"); + } + else + { + m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + 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); + } + } + else + { + HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:KEPT) wndsize=" + << m_dCWndSize << "/" << m_dMaxCWndSize + << " sndperiod=" << m_dPktSndPeriod << "us"); + } + } + else + { + m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->RTT() + m_iRCInterval) + 16; + HLOGC(cclog.Debug, log << "FileCC: UPD (speed mode) wndsize=" + << m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->RTT() + << " sndperiod=" << m_dPktSndPeriod << "us. deliverRate = " + << m_parent->deliveryRate() << " pkts/s)"); + } + + if (!m_bSlowStart) + { + if (m_bLoss) + { + m_bLoss = false; + } + // During Slow Start, no rate increase + else + { + double inc = 0; + const int loss_bw = 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); + if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((bw_pktps / 9) < B)) + B = bw_pktps / 9; + if (B <= 0) + inc = 1.0 / m_parent->MSS(); + 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_parent->MSS() * 8.0))) * 0.0000015 / m_parent->MSS(); + inc = max(inc, 1.0 / m_parent->MSS()); + } + + HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:OFF) loss_bw=" << loss_bw + << " bandwidth=" << m_parent->bandwidth() << " inc=" << inc + << " m_dPktSndPeriod=" << m_dPktSndPeriod + << "->" << (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval)); + + m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval); + } + } + +#if ENABLE_HEAVY_LOGGING + // Try to do reverse-calculation for m_dPktSndPeriod, as per minSP below + // sndperiod = mega / (maxbw / MSS) + // 1/sndperiod = (maxbw/MSS) / mega + // mega/sndperiod = maxbw/MSS + // maxbw = (MSS*mega)/sndperiod + uint64_t usedbw = (m_parent->MSS() * 1000000.0) / m_dPktSndPeriod; + +#if defined(unix) && defined (SRT_ENABLE_SYSTEMBUFFER_TRACE) + // Check the outgoing system queue level + int udp_buffer_size = m_parent->sndQueue()->sockoptQuery(SOL_SOCKET, SO_SNDBUF); + int udp_buffer_level = m_parent->sndQueue()->ioctlQuery(TIOCOUTQ); + int udp_buffer_free = udp_buffer_size - udp_buffer_level; +#else + int udp_buffer_free = -1; +#endif + + HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:" + << (m_bSlowStart ? "ON" : "OFF") << ") wndsize=" << m_dCWndSize + << " sndperiod=" << m_dPktSndPeriod << "us BANDWIDTH USED:" << usedbw << " (limit: " << m_maxSR << ")" + " SYSTEM BUFFER LEFT: " << udp_buffer_free); +#endif + + //set maximum transfer rate + if (m_maxSR) + { + double minSP = 1000000.0 / (double(m_maxSR) / m_parent->MSS()); + if (m_dPktSndPeriod < minSP) + { + m_dPktSndPeriod = minSP; + HLOGC(cclog.Debug, log << "FileCC: BW limited to " << m_maxSR + << " - SLOWDOWN sndperiod=" << m_dPktSndPeriod << "us"); + } + } + + } + + // 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) + { + const int32_t* losslist = arg.get_ptr(); + size_t losslist_size = arg.get_len(); + + // Sanity check. Should be impossible that TEV_LOSSREPORT event + // is called with a nonempty loss list. + if (losslist_size == 0) + { + LOGC(cclog.Error, log << "IPE: FileCC: empty loss list!"); + return; + } + + //Slow Start stopped, if it hasn't yet + if (m_bSlowStart) + { + m_bSlowStart = false; + if (m_parent->deliveryRate() > 0) + { + m_dPktSndPeriod = 1000000.0 / m_parent->deliveryRate(); + HLOGC(cclog.Debug, log << "FileCC: LOSS, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS mega/rate (rate=" + << m_parent->deliveryRate() << ")"); + } + else + { + m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + 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_bLoss = true; + + // TODO: const int pktsInFlight = CSeqNo::seqoff(m_iLastAck, m_parent->sndSeqNo()); + const int pktsInFlight = m_parent->RTT() / 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 << "\%)"); + if (lost_pcent_x10 < 20) // 2.0% + { + HLOGC(cclog.Debug, log << "FileCC: LOSS: m_dLastDecPeriod=" << m_dLastDecPeriod << "->" << m_dPktSndPeriod); + m_dLastDecPeriod = m_dPktSndPeriod; + return; + } + + // In contradiction to UDT, TEV_LOSSREPORT will be reported also when + // the lossreport is being sent again, periodically, as a result of + // NAKREPORT feature. You should make sure that NAKREPORT is off when + // using FileCC, so relying on SRTO_TRANSTYPE rather than + // just SRTO_CONGESTION is recommended. + int32_t lossbegin = SEQNO_VALUE::unwrap(losslist[0]); + + if (CSeqNo::seqcmp(lossbegin, m_iLastDecSeq) > 0) + { + m_dLastDecPeriod = m_dPktSndPeriod; + m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.03); + + const double loss_share_factor = 0.03; + m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * (1 - loss_share_factor) + m_iNAKCount * loss_share_factor); + m_iNAKCount = 1; + m_iDecCount = 1; + + 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; + HLOGC(cclog.Debug, log << "FileCC: LOSS:NEW lseqno=" << lossbegin + << ", lastsentseqno=" << m_iLastDecSeq + << ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin) + << ", rand=" << m_iDecRandom + << " avg NAK:" << m_iAvgNAKNum + << ", sndperiod=" << m_dPktSndPeriod << "us"); + } + 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.03); + m_iLastDecSeq = m_parent->sndSeqNo(); + HLOGC(cclog.Debug, log << "FileCC: LOSS:PERIOD lseqno=" << lossbegin + << ", lastsentseqno=" << m_iLastDecSeq + << ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin) + << ", deccnt=" << m_iDecCount + << ", decrnd=" << m_iDecRandom + << ", sndperiod=" << m_dPktSndPeriod << "us"); + } + else + { + HLOGC(cclog.Debug, log << "FileCC: LOSS:STILL lseqno=" << lossbegin + << ", lastsentseqno=" << m_iLastDecSeq + << ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin) + << ", deccnt=" << m_iDecCount + << ", decrnd=" << m_iDecRandom + << ", sndperiod=" << m_dPktSndPeriod << "us"); + } + } + + void speedupToWindowSize(ETransmissionEvent, EventVariant arg) + { + ECheckTimerStage stg = arg.get(); + + // TEV_INIT is in the beginning of checkTimers(), used + // only to synchronize back the values (which is done in updateCC + // after emitting the signal). + if (stg == TEV_CHT_INIT) + return; + + if (m_bSlowStart) + { + m_bSlowStart = false; + if (m_parent->deliveryRate() > 0) + { + m_dPktSndPeriod = 1000000.0 / m_parent->deliveryRate(); + HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS mega/rate (rate=" + << m_parent->deliveryRate() << ")"); + } + else + { + m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + 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 << ")"); + } + } + else + { + // XXX This code is a copy of legacy CUDTCC::onTimeout() body. + // This part was commented out there already. + /* + m_dLastDecPeriod = m_dPktSndPeriod; + m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2); + m_iLastDecSeq = m_iLastAck; + */ + } + } + + SrtCongestion::RexmitMethod rexmitMethod() ATR_OVERRIDE + { + return SrtCongestion::SRM_LATEREXMIT; + } +}; + + +#undef SSLOT + +template +struct Creator +{ + static SrtCongestionControlBase* Create(CUDT* parent) { return new Target(parent); } +}; + +SrtCongestion::NamePtr SrtCongestion::congctls[N_CONTROLLERS] = +{ + {"live", Creator::Create }, + {"file", Creator::Create } +}; + + +bool SrtCongestion::configure(CUDT* parent) +{ + if (selector == N_CONTROLLERS) + return false; + + // Found a congctl, so call the creation function + congctl = (*congctls[selector].second)(parent); + + // The congctl should have pinned in all events + // that are of its interest. It's stated that + // it's ready after creation. + return !!congctl; +} + +SrtCongestion::~SrtCongestion() +{ + delete congctl; + congctl = 0; +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/congctl.h b/trunk/3rdparty/srt-1-fit/srtcore/congctl.h new file mode 100644 index 000000000..0e1729f0f --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/congctl.h @@ -0,0 +1,206 @@ +/* + * 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/. + * + */ + +#ifndef INC__CONGCTL_H +#define INC__CONGCTL_H + +#include +#include +#include + +class CUDT; +class SrtCongestionControlBase; + +typedef SrtCongestionControlBase* srtcc_create_t(CUDT* parent); + +class SrtCongestion +{ + // Temporarily changed to linear searching, until this is exposed + // for a user-defined controller. + // Note that this is a pointer to function :) + + static const size_t N_CONTROLLERS = 2; + // The first/second is to mimic the map. + typedef struct { const char* first; srtcc_create_t* second; } NamePtr; + static NamePtr congctls[N_CONTROLLERS]; + + // This is a congctl container. + SrtCongestionControlBase* congctl; + size_t selector; + + void Check(); + +public: + + // If you predict to allow something to be done on controller also + // before it is configured, call this first. If you need it configured, + // you can rely on Check(). + bool ready() { return congctl; } + SrtCongestionControlBase* operator->() { Check(); return congctl; } + + // In the beginning it's uninitialized + SrtCongestion(): congctl(), selector(N_CONTROLLERS) {} + + struct IsName + { + std::string n; + IsName(std::string nn): n(nn) {} + bool operator()(NamePtr np) { return n == np.first; } + }; + + // 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) + return false; + selector = try_selector - congctls; + return true; + } + + std::string selected_name() + { + if (selector == N_CONTROLLERS) + return ""; + return congctls[selector].first; + } + + // Copy constructor - important when listener-spawning + // Things being done: + // 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) {} + + // 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); + + // Will delete the pinned in congctl object. + // This must be defined in *.cpp file due to virtual + // destruction. + ~SrtCongestion(); + + enum RexmitMethod + { + SRM_LATEREXMIT, + SRM_FASTREXMIT + }; + + enum TransAPI + { + STA_MESSAGE = 0x1, // sendmsg/recvmsg functions + STA_BUFFER = 0x2, // send/recv functions + STA_FILE = 0x3, // sendfile/recvfile functions + }; + + enum TransDir + { + STAD_RECV = 0, + STAD_SEND = 1 + }; +}; + + +class SrtCongestionControlBase +{ +protected: + // Here can be some common fields + CUDT* m_parent; + + double m_dPktSndPeriod; + double m_dCWndSize; + + //int m_iBandwidth; // NOT REQUIRED. Use m_parent->bandwidth() instead. + double m_dMaxCWndSize; + + //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. + //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); +public: + + // This could be also made abstract, but this causes a linkage + // problem in C++: this would constitute the first virtual method, + // and C++ compiler uses the location of the first virtual method as the + // file to which it also emits the virtual call table. When this is + // abstract, there would have to be simultaneously either defined + // an empty method in congctl.cpp file (obviously never called), + // or simply left empty body here. + virtual ~SrtCongestionControlBase() { } + + // All these functions that return values interesting for processing + // by CUDT can be overridden. Normally they should refer to the fields + // and these fields should keep the values as a state. + virtual double pktSndPeriod_us() { return m_dPktSndPeriod; } + virtual double cgWindowSize() { return m_dCWndSize; } + virtual double cgWindowMaxSize() { return m_dMaxCWndSize; } + + virtual int64_t sndBandwidth() { return 0; } + + // If user-defined, will return nonzero value. + // If not, it will be internally calculated. + virtual int RTO() { return 0; } + + // Maximum number of packets to trigger ACK sending. + // Specifies the number of packets to receive before sending the ACK. + // Used by CUDT together with ACKTimeout_us() to trigger ACK packet sending. + virtual int ACKMaxPackets() const { return 0; } + + // Periodical interval to send an ACK, in microseconds. + // If user-defined, this value will be used to calculate + // the next ACK time every time ACK is considered to be sent (see CUDT::checkTimers). + // Otherwise this will be calculated internally in CUDT, normally taken + // from CUDT::COMM_SYN_INTERVAL_US. + 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. + virtual void updateBandwidth(int64_t, int64_t) {} + + virtual bool needsQuickACK(const CPacket&) + { + return false; + } + + // Particular controller is allowed to agree or disagree on the use of particular API. + virtual bool checkTransArgs(SrtCongestion::TransAPI , SrtCongestion::TransDir , const char* /*buffer*/, size_t /*size*/, int /*ttl*/, bool /*inorder*/) + { + return true; + } + + virtual SrtCongestion::RexmitMethod rexmitMethod() = 0; // Implementation enforced. + + virtual uint64_t updateNAKInterval(uint64_t nakint_tk, int rcv_speed, size_t loss_length) + { + if (rcv_speed > 0) + nakint_tk += (loss_length * uint64_t(1000000) / rcv_speed) * CTimer::getCPUFrequency(); + + return nakint_tk; + } + + virtual uint64_t minNAKInterval() + { + return 0; // Leave default + } +}; + + + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/core.cpp b/trunk/3rdparty/srt-1-fit/srtcore/core.cpp new file mode 100644 index 000000000..edadafaaa --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/core.cpp @@ -0,0 +1,9705 @@ +/* + * 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/28/2012 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#else +#include +#include +#endif +#include +#include +#include "srt.h" +#include "queue.h" +#include "core.h" +#include "logging.h" +#include "crypto.h" +#include "logging_api.h" // Required due to containing extern srt_logger_config + +// Again, just in case when some "smart guy" provided such a global macro +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif + +using namespace std; + +namespace srt_logging +{ + +struct AllFaOn +{ + LogConfig::fa_bitset_t allfa; + + AllFaOn() + { + // allfa.set(SRT_LOGFA_BSTATS, true); + allfa.set(SRT_LOGFA_CONTROL, true); + allfa.set(SRT_LOGFA_DATA, true); + allfa.set(SRT_LOGFA_TSBPD, true); + allfa.set(SRT_LOGFA_REXMIT, true); + allfa.set(SRT_LOGFA_CONGEST, true); +#if ENABLE_HAICRYPT_LOGGING + allfa.set(SRT_LOGFA_HAICRYPT, true); +#endif + } +} logger_fa_all; + +} // namespace srt_logging + +// 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 glog(SRT_LOGFA_GENERAL, srt_logger_config, "SRT.g"); +// Unused. If not found useful, maybe reuse for another FA. +// Logger blog(SRT_LOGFA_BSTATS, srt_logger_config, "SRT.b"); +Logger mglog(SRT_LOGFA_CONTROL, srt_logger_config, "SRT.c"); +Logger dlog(SRT_LOGFA_DATA, srt_logger_config, "SRT.d"); +Logger tslog(SRT_LOGFA_TSBPD, srt_logger_config, "SRT.t"); +Logger rxlog(SRT_LOGFA_REXMIT, srt_logger_config, "SRT.r"); +Logger cclog(SRT_LOGFA_CONGEST, srt_logger_config, "SRT.cc"); + +} // namespace srt_logging + +using namespace srt_logging; + +CUDTUnited CUDT::s_UDTUnited; + +const SRTSOCKET UDT::INVALID_SOCK = CUDT::INVALID_SOCK; +const int UDT::ERROR = CUDT::ERROR; + +// SRT Version constants +#define SRT_VERSION_UNK 0 +#define SRT_VERSION_MAJ1 0x010000 /* Version 1 major */ +#define SRT_VERSION_MAJ(v) (0xFF0000 & (v)) /* Major number ensuring backward compatibility */ +#define SRT_VERSION_MIN(v) (0x00FF00 & (v)) +#define SRT_VERSION_PCH(v) (0x0000FF & (v)) + +// NOTE: SRT_VERSION is primarily defined in the build file. +const int32_t SRT_DEF_VERSION = SrtParseVersion(SRT_VERSION); + +//#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ +#define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ +#define SRT_CMD_HSREQ_SZ 12 /* Current version packet size */ +#if SRT_CMD_HSREQ_SZ > SRT_CMD_MAXSZ +#error SRT_CMD_MAXSZ too small +#endif +/* Handshake Request (Network Order) + 0[31..0]: SRT version SRT_DEF_VERSION + 1[31..0]: Options 0 [ | SRT_OPT_TSBPDSND ][ | SRT_OPT_HAICRYPT ] + 2[31..16]: TsbPD resv 0 + 2[15..0]: TsbPD delay [0..60000] msec +*/ + +//#define SRT_CMD_HSRSP 2 /* SRT Handshake Response (receiver) */ +#define SRT_CMD_HSRSP_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ +#define SRT_CMD_HSRSP_SZ 12 /* Current version packet size */ +#if SRT_CMD_HSRSP_SZ > SRT_CMD_MAXSZ +#error SRT_CMD_MAXSZ too small +#endif +/* Handshake Response (Network Order) + 0[31..0]: SRT version SRT_DEF_VERSION + 1[31..0]: Options 0 [ | SRT_OPT_TSBPDRCV [| SRT_OPT_TLPKTDROP ]][ | SRT_OPT_HAICRYPT] + [ | SRT_OPT_NAKREPORT ] [ | SRT_OPT_REXMITFLG ] + 2[31..16]: TsbPD resv 0 + 2[15..0]: TsbPD delay [0..60000] msec +*/ + +void CUDT::construct() +{ + m_pSndBuffer = NULL; + m_pRcvBuffer = NULL; + m_pSndLossList = NULL; + m_pRcvLossList = NULL; + m_iReorderTolerance = 0; + m_iMaxReorderTolerance = 0; // Sensible optimal value is 10, 0 preserves old behavior + m_iConsecEarlyDelivery = 0; // how many times so far the packet considered lost has been received before TTL expires + m_iConsecOrderedDelivery = 0; + + m_pSndQueue = NULL; + m_pRcvQueue = NULL; + m_pPeerAddr = NULL; + m_pSNode = NULL; + m_pRNode = NULL; + + m_ullSndHsLastTime_us = 0; + m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; // Will be reset to 0 for HSv5, this value is important for HSv4 + + // Initial status + m_bOpened = false; + m_bListening = false; + m_bConnecting = false; + m_bConnected = false; + m_bClosing = false; + m_bShutdown = false; + m_bBroken = false; + m_bPeerHealth = true; + m_RejectReason = SRT_REJ_UNKNOWN; + m_ullLingerExpiration = 0; + m_llLastReqTime = 0; + + m_lSrtVersion = SRT_DEF_VERSION; + m_lPeerSrtVersion = 0; // not defined until connected. + m_lMinimumPeerSrtVersion = SRT_VERSION_MAJ1; + + m_iTsbPdDelay_ms = 0; + m_iPeerTsbPdDelay_ms = 0; + + m_bPeerTsbPd = false; + m_iPeerTsbPdDelay_ms = 0; + m_bTsbPd = false; + m_bTsbPdAckWakeup = false; + m_bPeerTLPktDrop = false; + + m_uKmRefreshRatePkt = 0; + m_uKmPreAnnouncePkt = 0; + + // Initilize mutex and condition variables + initSynch(); +} + +CUDT::CUDT() +{ + construct(); + + (void)SRT_DEF_VERSION; + + // Default UDT configurations + m_iMSS = 1500; + m_bSynSending = true; + m_bSynRecving = true; + m_iFlightFlagSize = 25600; + m_iSndBufSize = 8192; + m_iRcvBufSize = 8192; // Rcv buffer MUST NOT be bigger than Flight Flag size + + // Linger: LIVE mode defaults, please refer to `SRTO_TRANSTYPE` option + // for other modes. + m_Linger.l_onoff = 0; + m_Linger.l_linger = 0; + m_iUDPSndBufSize = 65536; + m_iUDPRcvBufSize = m_iRcvBufSize * m_iMSS; + m_iSockType = UDT_DGRAM; + m_iIPversion = AF_INET; + m_bRendezvous = false; +#ifdef SRT_ENABLE_CONNTIMEO + m_iConnTimeOut = 3000; +#endif + m_iSndTimeOut = -1; + m_iRcvTimeOut = -1; + m_bReuseAddr = true; + m_llMaxBW = -1; +#ifdef SRT_ENABLE_IPOPTS + m_iIpTTL = -1; + m_iIpToS = -1; +#endif + m_CryptoSecret.len = 0; + m_iSndCryptoKeyLen = 0; + // Cfg + m_bDataSender = false; // Sender only if true: does not recv data + m_bOPT_TsbPd = true; // Enable TsbPd on sender + m_iOPT_TsbPdDelay = SRT_LIVE_DEF_LATENCY_MS; + m_iOPT_PeerTsbPdDelay = 0; // Peer's TsbPd delay as receiver (here is its minimum value, if used) + m_bOPT_TLPktDrop = true; + m_iOPT_SndDropDelay = 0; + m_bOPT_StrictEncryption = true; + m_iOPT_PeerIdleTimeout = COMM_RESPONSE_TIMEOUT_MS; + m_bTLPktDrop = true; // Too-late Packet Drop + m_bMessageAPI = true; + m_zOPT_ExpPayloadSize = SRT_LIVE_DEF_PLSIZE; + m_iIpV6Only = -1; + // Runtime + m_bRcvNakReport = true; // Receiver's Periodic NAK Reports + m_llInputBW = 0; // Application provided input bandwidth (internal input rate sampling == 0) + m_iOverheadBW = 25; // Percent above input stream rate (applies if m_llMaxBW == 0) + m_OPT_PktFilterConfigString = ""; + + m_pCache = NULL; + + // Default congctl is "live". + // Available builtin congctl: "file". + // Other congctls can be registerred. + + // Note that 'select' returns false if there's no such congctl. + // If so, congctl becomes unselected. Calling 'configure' on an + // unselected congctl results in exception. + m_CongCtl.select("live"); +} + +CUDT::CUDT(const CUDT &ancestor) +{ + construct(); + + // XXX Consider all below fields (except m_bReuseAddr) to be put + // into a separate class for easier copying. + + // Default UDT configurations + m_iMSS = ancestor.m_iMSS; + m_bSynSending = ancestor.m_bSynSending; + m_bSynRecving = ancestor.m_bSynRecving; + m_iFlightFlagSize = ancestor.m_iFlightFlagSize; + m_iSndBufSize = ancestor.m_iSndBufSize; + m_iRcvBufSize = ancestor.m_iRcvBufSize; + m_Linger = ancestor.m_Linger; + m_iUDPSndBufSize = ancestor.m_iUDPSndBufSize; + m_iUDPRcvBufSize = ancestor.m_iUDPRcvBufSize; + m_iSockType = ancestor.m_iSockType; + m_iIPversion = ancestor.m_iIPversion; + m_bRendezvous = ancestor.m_bRendezvous; +#ifdef SRT_ENABLE_CONNTIMEO + m_iConnTimeOut = ancestor.m_iConnTimeOut; +#endif + m_iSndTimeOut = ancestor.m_iSndTimeOut; + m_iRcvTimeOut = ancestor.m_iRcvTimeOut; + m_bReuseAddr = true; // this must be true, because all accepted sockets share the same port with the listener + m_llMaxBW = ancestor.m_llMaxBW; +#ifdef SRT_ENABLE_IPOPTS + m_iIpTTL = ancestor.m_iIpTTL; + m_iIpToS = ancestor.m_iIpToS; +#endif + m_llInputBW = ancestor.m_llInputBW; + m_iOverheadBW = ancestor.m_iOverheadBW; + m_bDataSender = ancestor.m_bDataSender; + m_bOPT_TsbPd = ancestor.m_bOPT_TsbPd; + m_iOPT_TsbPdDelay = ancestor.m_iOPT_TsbPdDelay; + m_iOPT_PeerTsbPdDelay = ancestor.m_iOPT_PeerTsbPdDelay; + m_bOPT_TLPktDrop = ancestor.m_bOPT_TLPktDrop; + m_iOPT_SndDropDelay = ancestor.m_iOPT_SndDropDelay; + m_bOPT_StrictEncryption = ancestor.m_bOPT_StrictEncryption; + m_iOPT_PeerIdleTimeout = ancestor.m_iOPT_PeerIdleTimeout; + m_zOPT_ExpPayloadSize = ancestor.m_zOPT_ExpPayloadSize; + m_bTLPktDrop = ancestor.m_bTLPktDrop; + m_bMessageAPI = ancestor.m_bMessageAPI; + m_iIpV6Only = ancestor.m_iIpV6Only; + m_iReorderTolerance = ancestor.m_iMaxReorderTolerance; // Initialize with maximum value + m_iMaxReorderTolerance = ancestor.m_iMaxReorderTolerance; + // Runtime + m_bRcvNakReport = ancestor.m_bRcvNakReport; + m_OPT_PktFilterConfigString = ancestor.m_OPT_PktFilterConfigString; + + m_CryptoSecret = ancestor.m_CryptoSecret; + m_iSndCryptoKeyLen = ancestor.m_iSndCryptoKeyLen; + + m_uKmRefreshRatePkt = ancestor.m_uKmRefreshRatePkt; + m_uKmPreAnnouncePkt = ancestor.m_uKmPreAnnouncePkt; + + m_pCache = ancestor.m_pCache; + + // SrtCongestion's copy constructor copies the selection, + // but not the underlying congctl object. After + // copy-constructed, the 'configure' must be called on it again. + m_CongCtl = ancestor.m_CongCtl; +} + +CUDT::~CUDT() +{ + // release mutex/condtion variables + destroySynch(); + + // Wipeout critical data + memset(&m_CryptoSecret, 0, sizeof(m_CryptoSecret)); + + // destroy the data structures + delete m_pSndBuffer; + delete m_pRcvBuffer; + delete m_pSndLossList; + delete m_pRcvLossList; + delete m_pPeerAddr; + delete m_pSNode; + delete m_pRNode; +} + +// This function is to make it possible for both C and C++ +// API to accept both bool and int types for boolean options. +// (it's not that C couldn't use , it's that people +// often forget to use correct type). +static bool bool_int_value(const void *optval, int optlen) +{ + if (optlen == sizeof(bool)) + { + return *(bool *)optval; + } + + if (optlen == sizeof(int)) + { + return 0 != *(int *)optval; // 0!= is a windows warning-killer int-to-bool conversion + } + return false; +} + +void CUDT::setOpt(SRT_SOCKOPT optName, const void *optval, int optlen) +{ + if (m_bBroken || m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + + CGuard cg(m_ConnectionLock); + CGuard sendguard(m_SendLock); + CGuard recvguard(m_RecvLock); + + switch (optName) + { + case SRTO_MSS: + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + + if (*(int *)optval < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + m_iMSS = *(int *)optval; + + // Packet size cannot be greater than UDP buffer size + if (m_iMSS > m_iUDPSndBufSize) + m_iMSS = m_iUDPSndBufSize; + if (m_iMSS > m_iUDPRcvBufSize) + m_iMSS = m_iUDPRcvBufSize; + + break; + + case SRTO_SNDSYN: + m_bSynSending = bool_int_value(optval, optlen); + break; + + case SRTO_RCVSYN: + m_bSynRecving = bool_int_value(optval, optlen); + break; + + case SRTO_FC: + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + if (*(int *)optval < 1) + throw CUDTException(MJ_NOTSUP, MN_INVAL); + + // Mimimum recv flight flag size is 32 packets + if (*(int *)optval > 32) + m_iFlightFlagSize = *(int *)optval; + else + m_iFlightFlagSize = 32; + + break; + + case SRTO_SNDBUF: + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + + if (*(int *)optval <= 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + m_iSndBufSize = *(int *)optval / (m_iMSS - CPacket::UDP_HDR_SIZE); + + break; + + case SRTO_RCVBUF: + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + + if (*(int *)optval <= 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + { + // This weird cast through int is required because + // API requires 'int', and internals require 'size_t'; + // their size is different on 64-bit systems. + size_t val = size_t(*(int *)optval); + + // Mimimum recv buffer size is 32 packets + size_t mssin_size = m_iMSS - CPacket::UDP_HDR_SIZE; + + // XXX This magic 32 deserves some constant + if (val > mssin_size * 32) + m_iRcvBufSize = val / mssin_size; + else + m_iRcvBufSize = 32; + + // recv buffer MUST not be greater than FC size + if (m_iRcvBufSize > m_iFlightFlagSize) + m_iRcvBufSize = m_iFlightFlagSize; + } + + break; + + case SRTO_LINGER: + m_Linger = *(linger *)optval; + break; + + case SRTO_UDP_SNDBUF: + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + + m_iUDPSndBufSize = *(int *)optval; + + if (m_iUDPSndBufSize < m_iMSS) + m_iUDPSndBufSize = m_iMSS; + + break; + + case SRTO_UDP_RCVBUF: + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + + m_iUDPRcvBufSize = *(int *)optval; + + if (m_iUDPRcvBufSize < m_iMSS) + m_iUDPRcvBufSize = m_iMSS; + + break; + + case SRTO_RENDEZVOUS: + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + m_bRendezvous = bool_int_value(optval, optlen); + break; + + case SRTO_SNDTIMEO: + m_iSndTimeOut = *(int *)optval; + break; + + case SRTO_RCVTIMEO: + m_iRcvTimeOut = *(int *)optval; + break; + + case SRTO_REUSEADDR: + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + m_bReuseAddr = bool_int_value(optval, optlen); + break; + + case SRTO_MAXBW: + m_llMaxBW = *(int64_t *)optval; + + // This can be done on both connected and unconnected socket. + // When not connected, this will do nothing, however this + // event will be repeated just after connecting anyway. + if (m_bConnected) + updateCC(TEV_INIT, TEV_INIT_RESET); + break; + +#ifdef SRT_ENABLE_IPOPTS + case SRTO_IPTTL: + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + if (!(*(int *)optval == -1) && !((*(int *)optval >= 1) && (*(int *)optval <= 255))) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + m_iIpTTL = *(int *)optval; + break; + + case SRTO_IPTOS: + if (m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); + m_iIpToS = *(int *)optval; + break; +#endif + + case SRTO_INPUTBW: + m_llInputBW = *(int64_t *)optval; + // (only if connected; if not, then the value + // from m_iOverheadBW will be used initially) + if (m_bConnected) + updateCC(TEV_INIT, TEV_INIT_INPUTBW); + break; + + case SRTO_OHEADBW: + if ((*(int *)optval < 5) || (*(int *)optval > 100)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + m_iOverheadBW = *(int *)optval; + + // Changed overhead BW, so spread the change + // (only if connected; if not, then the value + // from m_iOverheadBW will be used initially) + if (m_bConnected) + updateCC(TEV_INIT, TEV_INIT_OHEADBW); + break; + + case SRTO_SENDER: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_bDataSender = bool_int_value(optval, optlen); + break; + + case SRTO_TSBPDMODE: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_bOPT_TsbPd = bool_int_value(optval, optlen); + break; + + case SRTO_LATENCY: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_iOPT_TsbPdDelay = *(int *)optval; + m_iOPT_PeerTsbPdDelay = *(int *)optval; + break; + + case SRTO_RCVLATENCY: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_iOPT_TsbPdDelay = *(int *)optval; + break; + + case SRTO_PEERLATENCY: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_iOPT_PeerTsbPdDelay = *(int *)optval; + break; + + case SRTO_TLPKTDROP: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_bOPT_TLPktDrop = bool_int_value(optval, optlen); + break; + + case SRTO_SNDDROPDELAY: + // Surprise: you may be connected to alter this option. + // The application may manipulate this option on sender while transmitting. + m_iOPT_SndDropDelay = *(int *)optval; + break; + + case SRTO_PASSPHRASE: + // For consistency, throw exception when connected, + // no matter if otherwise the password can be set. + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + +#ifdef SRT_ENABLE_ENCRYPTION + // Password must be 10-80 characters. + // Or it can be empty to clear the password. + if ((optlen != 0) && (optlen < 10 || optlen > HAICRYPT_SECRET_MAX_SZ)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + memset(&m_CryptoSecret, 0, sizeof(m_CryptoSecret)); + m_CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE; + m_CryptoSecret.len = (optlen <= (int)sizeof(m_CryptoSecret.str) ? optlen : (int)sizeof(m_CryptoSecret.str)); + memcpy(m_CryptoSecret.str, optval, m_CryptoSecret.len); +#else + if (optlen == 0) + break; + + LOGC(mglog.Error, log << "SRTO_PASSPHRASE: encryption not enabled at compile time"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + break; + + case SRTO_PBKEYLEN: + case _DEPRECATED_SRTO_SNDPBKEYLEN: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); +#ifdef SRT_ENABLE_ENCRYPTION + { + int v = *(int *)optval; + int allowed[4] = { + 0, // Default value, if this results for initiator, defaults to 16. See below. + 16, // AES-128 + 24, // AES-192 + 32 // AES-256 + }; + int *allowed_end = allowed + 4; + if (find(allowed, allowed_end, v) == allowed_end) + { + LOGC(mglog.Error, + log << "Invalid value for option SRTO_PBKEYLEN: " << v << "; allowed are: 0, 16, 24, 32"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + // Note: This works a little different in HSv4 and HSv5. + + // HSv4: + // The party that is set SRTO_SENDER will send KMREQ, and it will + // use default value 16, if SRTO_PBKEYLEN is the default value 0. + // The responder that receives KMRSP has nothing to say about + // PBKEYLEN anyway and it will take the length of the key from + // the initiator (sender) as a good deal. + // + // HSv5: + // The initiator (independently on the sender) will send KMREQ, + // and as it should be the sender to decide about the PBKEYLEN. + // Your application should do the following then: + // 1. The sender should set PBKEYLEN to the required value. + // 2. If the sender is initiator, it will create the key using + // its preset PBKEYLEN (or default 16, if not set) and the + // receiver-responder will take it as a good deal. + // 3. Leave the PBKEYLEN value on the receiver as default 0. + // 4. If sender is responder, it should then advertise the PBKEYLEN + // value in the initial handshake messages (URQ_INDUCTION if + // listener, and both URQ_WAVEAHAND and URQ_CONCLUSION in case + // of rendezvous, as it is the matter of luck who of them will + // eventually become the initiator). This way the receiver + // being an initiator will set m_iSndCryptoKeyLen before setting + // up KMREQ for sending to the sender-responder. + // + // Note that in HSv5 if both sides set PBKEYLEN, the responder + // wins, unless the initiator is a sender (the effective PBKEYLEN + // will be the one advertised by the responder). If none sets, + // PBKEYLEN will default to 16. + + m_iSndCryptoKeyLen = v; + } +#else + LOGC(mglog.Error, log << "SRTO_PBKEYLEN: encryption not enabled at compile time"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + break; + + case SRTO_NAKREPORT: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_bRcvNakReport = bool_int_value(optval, optlen); + break; + +#ifdef SRT_ENABLE_CONNTIMEO + case SRTO_CONNTIMEO: + m_iConnTimeOut = *(int *)optval; + break; +#endif + + case SRTO_LOSSMAXTTL: + m_iMaxReorderTolerance = *(int *)optval; + if (!m_bConnected) + m_iReorderTolerance = m_iMaxReorderTolerance; + break; + + case SRTO_VERSION: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_lSrtVersion = *(uint32_t *)optval; + break; + + case SRTO_MINVERSION: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_lMinimumPeerSrtVersion = *(uint32_t *)optval; + break; + + case SRTO_STREAMID: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + if (size_t(optlen) > MAX_SID_LENGTH) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + m_sStreamName.assign((const char *)optval, optlen); + break; + + case SRTO_CONGESTION: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + { + string val; + if (optlen == -1) + val = (const char *)optval; + else + val.assign((const char *)optval, optlen); + + // Translate alias + if (val == "vod") + val = "file"; + + bool res = m_CongCtl.select(val); + if (!res) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + break; + + case SRTO_MESSAGEAPI: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + m_bMessageAPI = bool_int_value(optval, optlen); + break; + + case SRTO_PAYLOADSIZE: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + if (*(int *)optval > SRT_LIVE_MAX_PLSIZE) + { + LOGC(mglog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (m_OPT_PktFilterConfigString != "") + { + // This means that the filter might have been installed before, + // and the fix to the maximum payload size was already applied. + // This needs to be checked now. + SrtFilterConfig fc; + if (!ParseFilterConfig(m_OPT_PktFilterConfigString, fc)) + { + // Break silently. This should not happen + LOGC(mglog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (m_zOPT_ExpPayloadSize > efc_max_payload_size) + { + LOGC(mglog.Error, + log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size + << " required for packet filter header"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } + + m_zOPT_ExpPayloadSize = *(int *)optval; + break; + + case SRTO_TRANSTYPE: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + // XXX Note that here the configuration for SRTT_LIVE + // is the same as DEFAULT VALUES for these fields set + // in CUDT::CUDT. + switch (*(SRT_TRANSTYPE *)optval) + { + case SRTT_LIVE: + // Default live options: + // - tsbpd: on + // - latency: 120ms + // - linger: off + // - congctl: live + // - extraction method: message (reading call extracts one message) + m_bOPT_TsbPd = true; + m_iOPT_TsbPdDelay = SRT_LIVE_DEF_LATENCY_MS; + m_iOPT_PeerTsbPdDelay = 0; + m_bOPT_TLPktDrop = true; + m_iOPT_SndDropDelay = 0; + m_bMessageAPI = true; + m_bRcvNakReport = true; + m_zOPT_ExpPayloadSize = SRT_LIVE_DEF_PLSIZE; + m_Linger.l_onoff = 0; + m_Linger.l_linger = 0; + m_CongCtl.select("live"); + break; + + case SRTT_FILE: + // File transfer mode: + // - tsbpd: off + // - latency: 0 + // - linger: 2 minutes (180s) + // - congctl: file (original UDT congestion control) + // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) + m_bOPT_TsbPd = false; + m_iOPT_TsbPdDelay = 0; + m_iOPT_PeerTsbPdDelay = 0; + m_bOPT_TLPktDrop = false; + m_iOPT_SndDropDelay = -1; + m_bMessageAPI = false; + m_bRcvNakReport = false; + m_zOPT_ExpPayloadSize = 0; // use maximum + m_Linger.l_onoff = 1; + m_Linger.l_linger = 180; // 2 minutes + m_CongCtl.select("file"); + break; + + default: + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + break; + + case SRTO_KMREFRESHRATE: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + // If you first change the KMREFRESHRATE, KMPREANNOUNCE + // will be set to the maximum allowed value + m_uKmRefreshRatePkt = *(int *)optval; + if (m_uKmPreAnnouncePkt == 0 || m_uKmPreAnnouncePkt > (m_uKmRefreshRatePkt - 1) / 2) + { + m_uKmPreAnnouncePkt = (m_uKmRefreshRatePkt - 1) / 2; + LOGC(mglog.Warn, + log << "SRTO_KMREFRESHRATE=0x" << hex << m_uKmRefreshRatePkt << ": setting SRTO_KMPREANNOUNCE=0x" + << hex << m_uKmPreAnnouncePkt); + } + break; + + case SRTO_KMPREANNOUNCE: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + { + int val = *(int *)optval; + int kmref = m_uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : m_uKmRefreshRatePkt; + if (val > (kmref - 1) / 2) + { + LOGC(mglog.Error, + log << "SRTO_KMPREANNOUNCE=0x" << hex << val << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) + << " - OPTION REJECTED."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + m_uKmPreAnnouncePkt = val; + } + break; + + case SRTO_ENFORCEDENCRYPTION: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + m_bOPT_StrictEncryption = bool_int_value(optval, optlen); + break; + + case SRTO_PEERIDLETIMEO: + + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + m_iOPT_PeerIdleTimeout = *(int *)optval; + break; + + case SRTO_IPV6ONLY: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + m_iIpV6Only = *(int *)optval; + break; + + case SRTO_PACKETFILTER: + if (m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + { + string arg((char *)optval, optlen); + // Parse the configuration string prematurely + SrtFilterConfig fc; + if (!ParseFilterConfig(arg, fc)) + { + LOGC(mglog.Error, + log << "SRTO_FILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " + "FILTERTYPE (" + << fc.type << ") must be installed (or builtin)"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (m_zOPT_ExpPayloadSize > efc_max_payload_size) + { + LOGC(mglog.Warn, + log << "Due to filter-required extra " << fc.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " + << efc_max_payload_size << " bytes"); + m_zOPT_ExpPayloadSize = efc_max_payload_size; + } + + m_OPT_PktFilterConfigString = arg; + } + break; + + default: + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } +} + +void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) +{ + CGuard cg(m_ConnectionLock); + + switch (optName) + { + case SRTO_MSS: + *(int *)optval = m_iMSS; + optlen = sizeof(int); + break; + + case SRTO_SNDSYN: + *(bool *)optval = m_bSynSending; + optlen = sizeof(bool); + break; + + case SRTO_RCVSYN: + *(bool *)optval = m_bSynRecving; + optlen = sizeof(bool); + break; + + case SRTO_ISN: + *(int *)optval = m_iISN; + optlen = sizeof(int); + break; + + case SRTO_FC: + *(int *)optval = m_iFlightFlagSize; + optlen = sizeof(int); + break; + + case SRTO_SNDBUF: + *(int *)optval = m_iSndBufSize * (m_iMSS - CPacket::UDP_HDR_SIZE); + optlen = sizeof(int); + break; + + case SRTO_RCVBUF: + *(int *)optval = m_iRcvBufSize * (m_iMSS - CPacket::UDP_HDR_SIZE); + optlen = sizeof(int); + break; + + case SRTO_LINGER: + if (optlen < (int)(sizeof(linger))) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + *(linger *)optval = m_Linger; + optlen = sizeof(linger); + break; + + case SRTO_UDP_SNDBUF: + *(int *)optval = m_iUDPSndBufSize; + optlen = sizeof(int); + break; + + case SRTO_UDP_RCVBUF: + *(int *)optval = m_iUDPRcvBufSize; + optlen = sizeof(int); + break; + + case SRTO_RENDEZVOUS: + *(bool *)optval = m_bRendezvous; + optlen = sizeof(bool); + break; + + case SRTO_SNDTIMEO: + *(int *)optval = m_iSndTimeOut; + optlen = sizeof(int); + break; + + case SRTO_RCVTIMEO: + *(int *)optval = m_iRcvTimeOut; + optlen = sizeof(int); + break; + + case SRTO_REUSEADDR: + *(bool *)optval = m_bReuseAddr; + optlen = sizeof(bool); + break; + + case SRTO_MAXBW: + *(int64_t *)optval = m_llMaxBW; + optlen = sizeof(int64_t); + break; + + case SRTO_STATE: + *(int32_t *)optval = s_UDTUnited.getStatus(m_SocketID); + optlen = sizeof(int32_t); + break; + + case SRTO_EVENT: + { + int32_t event = 0; + if (m_bBroken) + event |= UDT_EPOLL_ERR; + else + { + CGuard::enterCS(m_RecvLock); + if (m_pRcvBuffer && m_pRcvBuffer->isRcvDataReady()) + event |= UDT_EPOLL_IN; + CGuard::leaveCS(m_RecvLock); + if (m_pSndBuffer && (m_iSndBufSize > m_pSndBuffer->getCurrBufSize())) + event |= UDT_EPOLL_OUT; + } + *(int32_t *)optval = event; + optlen = sizeof(int32_t); + break; + } + + case SRTO_SNDDATA: + if (m_pSndBuffer) + *(int32_t *)optval = m_pSndBuffer->getCurrBufSize(); + else + *(int32_t *)optval = 0; + optlen = sizeof(int32_t); + break; + + case SRTO_RCVDATA: + if (m_pRcvBuffer) + { + CGuard::enterCS(m_RecvLock); + *(int32_t *)optval = m_pRcvBuffer->getRcvDataSize(); + CGuard::leaveCS(m_RecvLock); + } + else + *(int32_t *)optval = 0; + optlen = sizeof(int32_t); + break; + +#ifdef SRT_ENABLE_IPOPTS + case SRTO_IPTTL: + if (m_bOpened) + *(int32_t *)optval = m_pSndQueue->getIpTTL(); + else + *(int32_t *)optval = m_iIpTTL; + optlen = sizeof(int32_t); + break; + + case SRTO_IPTOS: + if (m_bOpened) + *(int32_t *)optval = m_pSndQueue->getIpToS(); + else + *(int32_t *)optval = m_iIpToS; + optlen = sizeof(int32_t); + break; +#endif + + case SRTO_SENDER: + *(int32_t *)optval = m_bDataSender; + optlen = sizeof(int32_t); + break; + + case SRTO_TSBPDMODE: + *(int32_t *)optval = m_bOPT_TsbPd; + optlen = sizeof(int32_t); + break; + + case SRTO_LATENCY: + case SRTO_RCVLATENCY: + *(int32_t *)optval = m_iTsbPdDelay_ms; + optlen = sizeof(int32_t); + break; + + case SRTO_PEERLATENCY: + *(int32_t *)optval = m_iPeerTsbPdDelay_ms; + optlen = sizeof(int32_t); + break; + + case SRTO_TLPKTDROP: + *(int32_t *)optval = m_bTLPktDrop; + optlen = sizeof(int32_t); + break; + + case SRTO_SNDDROPDELAY: + *(int32_t *)optval = m_iOPT_SndDropDelay; + optlen = sizeof(int32_t); + break; + + case SRTO_PBKEYLEN: + if (m_pCryptoControl) + *(int32_t *)optval = m_pCryptoControl->KeyLen(); // Running Key length. + else + *(int32_t *)optval = m_iSndCryptoKeyLen; // May be 0. + optlen = sizeof(int32_t); + break; + + case SRTO_KMSTATE: + if (!m_pCryptoControl) + *(int32_t *)optval = SRT_KM_S_UNSECURED; + else if (m_bDataSender) + *(int32_t *)optval = m_pCryptoControl->m_SndKmState; + else + *(int32_t *)optval = m_pCryptoControl->m_RcvKmState; + optlen = sizeof(int32_t); + break; + + case SRTO_SNDKMSTATE: // State imposed by Agent depending on PW and KMX + if (m_pCryptoControl) + *(int32_t *)optval = m_pCryptoControl->m_SndKmState; + else + *(int32_t *)optval = SRT_KM_S_UNSECURED; + optlen = sizeof(int32_t); + break; + + case SRTO_RCVKMSTATE: // State returned by Peer as informed during KMX + if (m_pCryptoControl) + *(int32_t *)optval = m_pCryptoControl->m_RcvKmState; + else + *(int32_t *)optval = SRT_KM_S_UNSECURED; + optlen = sizeof(int32_t); + break; + + case SRTO_LOSSMAXTTL: + *(int32_t*)optval = m_iMaxReorderTolerance; + optlen = sizeof(int32_t); + break; + + case SRTO_NAKREPORT: + *(bool *)optval = m_bRcvNakReport; + optlen = sizeof(bool); + break; + + case SRTO_VERSION: + *(int32_t *)optval = m_lSrtVersion; + optlen = sizeof(int32_t); + break; + + case SRTO_PEERVERSION: + *(int32_t *)optval = m_lPeerSrtVersion; + optlen = sizeof(int32_t); + break; + +#ifdef SRT_ENABLE_CONNTIMEO + case SRTO_CONNTIMEO: + *(int *)optval = m_iConnTimeOut; + optlen = sizeof(int); + break; +#endif + + case SRTO_MINVERSION: + *(uint32_t *)optval = m_lMinimumPeerSrtVersion; + optlen = sizeof(uint32_t); + break; + + case SRTO_STREAMID: + if (size_t(optlen) < m_sStreamName.size() + 1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + strcpy((char *)optval, m_sStreamName.c_str()); + optlen = m_sStreamName.size(); + break; + + case SRTO_CONGESTION: + { + string tt = m_CongCtl.selected_name(); + strcpy((char *)optval, tt.c_str()); + optlen = tt.size(); + } + break; + + case SRTO_MESSAGEAPI: + optlen = sizeof(bool); + *(bool *)optval = m_bMessageAPI; + break; + + case SRTO_PAYLOADSIZE: + optlen = sizeof(int); + *(int *)optval = m_zOPT_ExpPayloadSize; + break; + + case SRTO_ENFORCEDENCRYPTION: + optlen = sizeof(int32_t); // also with TSBPDMODE and SENDER + *(int32_t *)optval = m_bOPT_StrictEncryption; + break; + + case SRTO_IPV6ONLY: + optlen = sizeof(int); + *(int *)optval = m_iIpV6Only; + break; + + case SRTO_PEERIDLETIMEO: + *(int *)optval = m_iOPT_PeerIdleTimeout; + optlen = sizeof(int); + break; + + case SRTO_PACKETFILTER: + if (size_t(optlen) < m_OPT_PktFilterConfigString.size() + 1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + strcpy((char *)optval, m_OPT_PktFilterConfigString.c_str()); + optlen = m_OPT_PktFilterConfigString.size(); + break; + + default: + throw CUDTException(MJ_NOTSUP, MN_NONE, 0); + } +} + +bool CUDT::setstreamid(SRTSOCKET u, const std::string &sid) +{ + CUDT *that = getUDTHandle(u); + if (!that) + return false; + + if (sid.size() > MAX_SID_LENGTH) + return false; + + if (that->m_bConnected) + return false; + + that->m_sStreamName = sid; + return true; +} + +std::string CUDT::getstreamid(SRTSOCKET u) +{ + CUDT *that = getUDTHandle(u); + if (!that) + return ""; + + return that->m_sStreamName; +} + +// XXX REFACTOR: Make common code for CUDT constructor and clearData, +// possibly using CUDT::construct. +void CUDT::clearData() +{ + // Initial sequence number, loss, acknowledgement, etc. + int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE; + + m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; + + HLOGC(mglog.Debug, log << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + + m_iEXPCount = 1; + m_iBandwidth = 1; // pkts/sec + // XXX use some constant for this 16 + m_iDeliveryRate = 16; + m_iByteDeliveryRate = 16 * m_iMaxSRTPayloadSize; + m_iAckSeqNo = 0; + m_ullLastAckTime_tk = 0; + + // trace information + CGuard::enterCS(m_StatsLock); + m_stats.startTime = CTimer::getTime(); + m_stats.sentTotal = m_stats.recvTotal = m_stats.sndLossTotal = m_stats.rcvLossTotal = m_stats.retransTotal = + m_stats.sentACKTotal = m_stats.recvACKTotal = m_stats.sentNAKTotal = m_stats.recvNAKTotal = 0; + m_stats.lastSampleTime = CTimer::getTime(); + m_stats.traceSent = m_stats.traceRecv = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans = + m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; + m_stats.traceRcvRetrans = 0; + m_stats.traceReorderDistance = 0; + m_stats.traceBelatedTime = 0.0; + m_stats.traceRcvBelated = 0; + + m_stats.sndDropTotal = 0; + m_stats.traceSndDrop = 0; + m_stats.rcvDropTotal = 0; + m_stats.traceRcvDrop = 0; + + m_stats.m_rcvUndecryptTotal = 0; + m_stats.traceRcvUndecrypt = 0; + + m_stats.bytesSentTotal = 0; + m_stats.bytesRecvTotal = 0; + m_stats.bytesRetransTotal = 0; + m_stats.traceBytesSent = 0; + m_stats.traceBytesRecv = 0; + m_stats.sndFilterExtra = 0; + m_stats.rcvFilterExtra = 0; + m_stats.rcvFilterSupply = 0; + m_stats.rcvFilterLoss = 0; + + m_stats.traceBytesRetrans = 0; +#ifdef SRT_ENABLE_LOSTBYTESCOUNT + m_stats.traceRcvBytesLoss = 0; +#endif + m_stats.sndBytesDropTotal = 0; + m_stats.rcvBytesDropTotal = 0; + m_stats.traceSndBytesDrop = 0; + m_stats.traceRcvBytesDrop = 0; + m_stats.m_rcvBytesUndecryptTotal = 0; + m_stats.traceRcvBytesUndecrypt = 0; + + m_stats.sndDuration = m_stats.m_sndDurationTotal = 0; + CGuard::leaveCS(m_StatsLock); + + // Resetting these data because this happens when agent isn't connected. + m_bPeerTsbPd = false; + m_iPeerTsbPdDelay_ms = 0; + + m_bTsbPd = m_bOPT_TsbPd; // Take the values from user-configurable options + m_iTsbPdDelay_ms = m_iOPT_TsbPdDelay; + m_bTLPktDrop = m_bOPT_TLPktDrop; + m_bPeerTLPktDrop = false; + + m_bPeerNakReport = false; + + m_bPeerRexmitFlag = false; + + m_RdvState = CHandShake::RDV_INVALID; + m_ullRcvPeerStartTime = 0; +} + +void CUDT::open() +{ + CGuard cg(m_ConnectionLock); + + clearData(); + + // structures for queue + if (m_pSNode == NULL) + m_pSNode = new CSNode; + m_pSNode->m_pUDT = this; + m_pSNode->m_llTimeStamp_tk = 1; + m_pSNode->m_iHeapLoc = -1; + + if (m_pRNode == NULL) + m_pRNode = new CRNode; + m_pRNode->m_pUDT = this; + m_pRNode->m_llTimeStamp_tk = 1; + m_pRNode->m_pPrev = m_pRNode->m_pNext = NULL; + m_pRNode->m_bOnList = false; + + m_iRTT = 10 * COMM_SYN_INTERVAL_US; + m_iRTTVar = m_iRTT >> 1; + m_ullCPUFrequency = CTimer::getCPUFrequency(); + + // set minimum NAK and EXP timeout to 300ms + /* + XXX This code is blocked because the value of + m_ullMinNakInt_tk will be overwritten again in setupCC. + And in setupCC it will have an opportunity to make the + value overridden according to the statements in the SrtCongestion. + + #ifdef SRT_ENABLE_NAKREPORT + if (m_bRcvNakReport) + m_ullMinNakInt_tk = m_iMinNakInterval_us * m_ullCPUFrequency; + else + #endif + */ + // Set up timers + m_ullMinNakInt_tk = 300000 * m_ullCPUFrequency; + m_ullMinExpInt_tk = 300000 * m_ullCPUFrequency; + + m_ullACKInt_tk = COMM_SYN_INTERVAL_US * m_ullCPUFrequency; + m_ullNAKInt_tk = m_ullMinNakInt_tk; + + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + m_ullLastRspTime_tk = currtime_tk; + m_ullNextACKTime_tk = currtime_tk + m_ullACKInt_tk; + m_ullNextNAKTime_tk = currtime_tk + m_ullNAKInt_tk; + m_ullLastRspAckTime_tk = currtime_tk; + m_ullLastSndTime_tk = currtime_tk; + m_iReXmitCount = 1; + + m_iPktCount = 0; + m_iLightACKCount = 1; + + m_ullTargetTime_tk = 0; + m_ullTimeDiff_tk = 0; + + // Now UDT is opened. + m_bOpened = true; +} + +void CUDT::setListenState() +{ + CGuard cg(m_ConnectionLock); + + if (!m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_NONE, 0); + + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + // listen can be called more than once + if (m_bListening) + return; + + // if there is already another socket listening on the same port + if (m_pRcvQueue->setListener(this) < 0) + throw CUDTException(MJ_NOTSUP, MN_BUSY, 0); + + m_bListening = true; +} + +size_t CUDT::fillSrtHandshake(uint32_t *srtdata, size_t srtlen, int msgtype, int hs_version) +{ + if (srtlen < SRT_HS__SIZE) + { + LOGC(mglog.Fatal, + log << "IPE: fillSrtHandshake: buffer too small: " << srtlen << " (expected: " << SRT_HS__SIZE << ")"); + return 0; + } + + srtlen = SRT_HS__SIZE; // We use only that much space. + + memset(srtdata, 0, sizeof(uint32_t) * srtlen); + /* Current version (1.x.x) SRT handshake */ + srtdata[SRT_HS_VERSION] = m_lSrtVersion; /* Required version */ + srtdata[SRT_HS_FLAGS] |= SrtVersionCapabilities(); + + switch (msgtype) + { + case SRT_CMD_HSREQ: + return fillSrtHandshake_HSREQ(srtdata, srtlen, hs_version); + case SRT_CMD_HSRSP: + return fillSrtHandshake_HSRSP(srtdata, srtlen, hs_version); + default: + LOGC(mglog.Fatal, log << "IPE: createSrtHandshake/sendSrtMsg called with value " << msgtype); + return 0; + } +} + +size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *srtdata, size_t /* srtlen - unused */, int hs_version) +{ + // INITIATOR sends HSREQ. + + // The TSBPD(SND|RCV) options are being set only if the TSBPD is set in the current agent. + // The agent has a decisive power only in the range of RECEIVING the data, however it can + // also influence the peer's latency. If agent doesn't set TSBPD mode, it doesn't send any + // latency flags, although the peer might still want to do Rx with TSBPD. When agent sets + // TsbPd mode, it defines latency values for Rx (itself) and Tx (peer's Rx). If peer does + // not set TsbPd mode, it will simply ignore the proposed latency (PeerTsbPdDelay), although + // if it has received the Rx latency as well, it must honor it and respond accordingly + // (the latter is only in case of HSv5 and bidirectional connection). + if (m_bOPT_TsbPd) + { + m_iTsbPdDelay_ms = m_iOPT_TsbPdDelay; + m_iPeerTsbPdDelay_ms = m_iOPT_PeerTsbPdDelay; + /* + * Sent data is real-time, use Time-based Packet Delivery, + * set option bit and configured delay + */ + srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; + + if (hs_version < CUDT::HS_VERSION_SRT1) + { + // HSv4 - this uses only one value. + srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iPeerTsbPdDelay_ms); + } + else + { + // HSv5 - this will be understood only since this version when this exists. + srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); + + m_bTsbPd = true; + // And in the reverse direction. + srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; + srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); + + // This wasn't there for HSv4, this setting is only for the receiver. + // HSv5 is bidirectional, so every party is a receiver. + + if (m_bTLPktDrop) + srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; + } + } + + if (m_bRcvNakReport) + srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; + + // I support SRT_OPT_REXMITFLG. Do you? + srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; + + // Declare the API used. The flag is set for "stream" API because + // the older versions will never set this flag, but all old SRT versions use message API. + if (!m_bMessageAPI) + srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM; + + HLOGC(mglog.Debug, + log << "HSREQ/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]) + << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]) << "] FLAGS[" + << SrtFlagString(srtdata[SRT_HS_FLAGS]) << "]"); + + return 3; +} + +size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused */, int hs_version) +{ + // Setting m_ullRcvPeerStartTime is done in processSrtMsg_HSREQ(), so + // this condition will be skipped only if this function is called without + // getting first received HSREQ. Doesn't look possible in both HSv4 and HSv5. + if (m_ullRcvPeerStartTime != 0) + { + // If Agent doesn't set TSBPD, it will not set the TSBPD flag back to the Peer. + // The peer doesn't have be disturbed by it anyway. + if (m_bTsbPd) + { + /* + * We got and transposed peer start time (HandShake request timestamp), + * we can support Timestamp-based Packet Delivery + */ + srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; + + if (hs_version < HS_VERSION_SRT1) + { + // HSv4 - this uses only one value + srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iTsbPdDelay_ms); + } + else + { + // HSv5 - this puts "agent's" latency into RCV field and "peer's" - + // into SND field. + srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); + } + } + else + { + HLOGC(mglog.Debug, log << "HSRSP/snd: TSBPD off, NOT responding TSBPDRCV flag."); + } + + // Hsv5, only when peer has declared TSBPD mode. + // The flag was already set, and the value already "maximized" in processSrtMsg_HSREQ(). + if (m_bPeerTsbPd && hs_version >= HS_VERSION_SRT1) + { + // HSv5 is bidirectional - so send the TSBPDSND flag, and place also the + // peer's latency into SND field. + srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; + srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); + + HLOGC(mglog.Debug, + log << "HSRSP/snd: HSv5 peer uses TSBPD, responding TSBPDSND latency=" << m_iPeerTsbPdDelay_ms); + } + else + { + HLOGC(mglog.Debug, + log << "HSRSP/snd: HSv" << (hs_version == CUDT::HS_VERSION_UDT4 ? 4 : 5) + << " with peer TSBPD=" << (m_bPeerTsbPd ? "on" : "off") << " - NOT responding TSBPDSND"); + } + + if (m_bTLPktDrop) + srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; + } + else + { + LOGC(mglog.Fatal, log << "IPE: fillSrtHandshake_HSRSP: m_ullRcvPeerStartTime NOT SET!"); + return 0; + } + + if (m_bRcvNakReport) + { + // HSv5: Note that this setting is independent on the value of + // m_bPeerNakReport, which represent this setting in the peer. + + srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; + /* + * NAK Report is so efficient at controlling bandwidth that sender TLPktDrop + * is not needed. SRT 1.0.5 to 1.0.7 sender TLPktDrop combined with SRT 1.0 + * Timestamp-Based Packet Delivery was not well implemented and could drop + * big I-Frame tail before sending once on low latency setups. + * Disabling TLPktDrop in the receiver SRT Handshake Reply prevents the sender + * from enabling Too-Late Packet Drop. + */ + if (m_lPeerSrtVersion <= SrtVersion(1, 0, 7)) + srtdata[SRT_HS_FLAGS] &= ~SRT_OPT_TLPKTDROP; + } + + if (m_lSrtVersion >= SrtVersion(1, 2, 0)) + { + if (!m_bPeerRexmitFlag) + { + // Peer does not request to use rexmit flag, if so, + // we won't use as well. + HLOGC(mglog.Debug, log << "HSRSP/snd: AGENT understands REXMIT flag, but PEER DOES NOT. NOT setting."); + } + else + { + // Request that the rexmit bit be used as a part of msgno. + srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; + HLOGF(mglog.Debug, "HSRSP/snd: AGENT UNDERSTANDS REXMIT flag and PEER reported that it does, too."); + } + } + else + { + // Since this is now in the code, it can occur only in case when you change the + // version specification in the build configuration. + HLOGF(mglog.Debug, "HSRSP/snd: AGENT DOES NOT UNDERSTAND REXMIT flag"); + } + + HLOGC(mglog.Debug, + log << "HSRSP/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]) + << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]) << "] FLAGS[" + << SrtFlagString(srtdata[SRT_HS_FLAGS]) << "]"); + + return 3; +} + +size_t CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size) +{ + size_t srtlen = fillSrtHandshake(srtdata, size, cmd, handshakeVersion()); + HLOGF(mglog.Debug, + "CMD:%s(%d) Len:%d Version: %s Flags: %08X (%s) sdelay:%d", + MessageTypeStr(UMSG_EXT, cmd).c_str(), + cmd, + (int)(srtlen * sizeof(int32_t)), + SrtVersionString(srtdata[SRT_HS_VERSION]).c_str(), + srtdata[SRT_HS_FLAGS], + SrtFlagString(srtdata[SRT_HS_FLAGS]).c_str(), + srtdata[SRT_HS_LATENCY]); + + return srtlen; +} + +void CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, int srtlen_in) +{ + CPacket srtpkt; + int32_t srtcmd = (int32_t)cmd; + + static const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ / sizeof(int32_t); + + // This is in order to issue a compile error if the SRT_CMD_MAXSZ is + // too small to keep all the data. As this is "static const", declaring + // an array of such specified size in C++ isn't considered VLA. + static const int SRTDATA_SIZE = SRTDATA_MAXSIZE >= SRT_HS__SIZE ? SRTDATA_MAXSIZE : -1; + + // This will be effectively larger than SRT_HS__SIZE, but it will be also used + // for incoming data. We have a guarantee that it won't be larger than SRTDATA_MAXSIZE. + uint32_t srtdata[SRTDATA_SIZE]; + + int srtlen = 0; + + if (cmd == SRT_CMD_REJECT) + { + // This is a value returned by processSrtMsg underlying layer, potentially + // to be reported here. Should this happen, just send a rejection message. + cmd = SRT_CMD_HSRSP; + srtdata[SRT_HS_VERSION] = 0; + } + + switch (cmd) + { + case SRT_CMD_HSREQ: + case SRT_CMD_HSRSP: + srtlen = prepareSrtHsMsg(cmd, srtdata, SRTDATA_SIZE); + break; + + case SRT_CMD_KMREQ: // Sender + case SRT_CMD_KMRSP: // Receiver + srtlen = srtlen_in; + /* Msg already in network order + * But CChannel:sendto will swap again (assuming 32-bit fields) + * Pre-swap to cancel it. + */ + HtoNLA(srtdata, srtdata_in, srtlen); + m_pCryptoControl->updateKmState(cmd, srtlen); // <-- THIS function can't be moved to CUDT + + break; + + default: + LOGF(mglog.Error, "sndSrtMsg: cmd=%d unsupported", cmd); + break; + } + + if (srtlen > 0) + { + /* srtpkt.pack will set message data in network order */ + srtpkt.pack(UMSG_EXT, &srtcmd, srtdata, srtlen * sizeof(int32_t)); + addressAndSend(srtpkt); + } +} + +// PREREQUISITE: +// pkt must be set the buffer and configured for UMSG_HANDSHAKE. +// Note that this function replaces also serialization for the HSv4. +bool CUDT::createSrtHandshake(ref_t r_pkt, + ref_t r_hs, + int srths_cmd, + int srtkm_cmd, + const uint32_t * kmdata, + size_t kmdata_wordsize /* IN WORDS, NOT BYTES!!! */) +{ + CPacket & pkt = *r_pkt; + CHandShake &hs = *r_hs; + + // This function might be called before the opposite version was recognized. + // Check if the version is exactly 4 because this means that the peer has already + // sent something - asynchronously, and usually in rendezvous - and we already know + // that the peer is version 4. In this case, agent must behave as HSv4, til the end. + if (m_ConnRes.m_iVersion == HS_VERSION_UDT4) + { + hs.m_iVersion = HS_VERSION_UDT4; + hs.m_iType = UDT_DGRAM; + if (hs.m_extension) + { + // Should be impossible + LOGC(mglog.Error, log << "createSrtHandshake: IPE: EXTENSION SET WHEN peer reports version 4 - fixing..."); + hs.m_extension = false; + } + } + else + { + hs.m_iType = 0; // Prepare it for flags + } + + HLOGC(mglog.Debug, + log << "createSrtHandshake: buf size=" << pkt.getLength() << " hsx=" << MessageTypeStr(UMSG_EXT, srths_cmd) + << " kmx=" << MessageTypeStr(UMSG_EXT, srtkm_cmd) << " kmdata_wordsize=" << kmdata_wordsize + << " version=" << hs.m_iVersion); + + // Once you are certain that the version is HSv5, set the enc type flags + // to advertise pbkeylen. Otherwise make sure that the old interpretation + // will correctly pick up the type field. PBKEYLEN should be advertized + // regardless of what URQ stage the handshake is (note that in case of rendezvous + // CONCLUSION might be the FIRST MESSAGE EVER RECEIVED by a party). + if (hs.m_iVersion > HS_VERSION_UDT4) + { + // Check if there was a failure to receie HSREQ before trying to craft HSRSP. + // If fillSrtHandshake_HSRSP catches the condition of m_ullRcvPeerStartTime == 0, + // it will return size 0, which will mess up with further extension procedures; + // PREVENT THIS HERE. + if (hs.m_iReqType == URQ_CONCLUSION && srths_cmd == SRT_CMD_HSRSP && m_ullRcvPeerStartTime == 0) + { + LOGC(mglog.Error, + log << "createSrtHandshake: IPE (non-fatal): Attempting to craft HSRSP without received HSREQ. " + "BLOCKING extensions."); + hs.m_extension = false; + } + + // The situation when this function is called without requested extensions + // is URQ_CONCLUSION in rendezvous mode in some of the transitions. + // In this case for version 5 just clear the m_iType field, as it has + // different meaning in HSv5 and contains extension flags. + // + // Keep 0 in the SRT_HSTYPE_HSFLAGS field, but still advertise PBKEYLEN + // in the SRT_HSTYPE_ENCFLAGS field. + hs.m_iType = SrtHSRequest::wrapFlags(false /*no magic in HSFLAGS*/, m_iSndCryptoKeyLen); + bool whether SRT_ATR_UNUSED = m_iSndCryptoKeyLen != 0; + HLOGC(mglog.Debug, + log << "createSrtHandshake: " << (whether ? "" : "NOT ") + << " Advertising PBKEYLEN - value = " << m_iSndCryptoKeyLen); + + // Note: This is required only when sending a HS message without SRT extensions. + // When this is to be sent with SRT extensions, then KMREQ will be attached here + // and the PBKEYLEN will be extracted from it. If this is going to attach KMRSP + // here, it's already too late (it should've been advertised before getting the first + // handshake message with KMREQ). + } + else + { + hs.m_iType = UDT_DGRAM; + } + + // values > URQ_CONCLUSION include also error types + // if (hs.m_iVersion == HS_VERSION_UDT4 || hs.m_iReqType > URQ_CONCLUSION) <--- This condition was checked b4 and + // it's only valid for caller-listener mode + if (!hs.m_extension) + { + // Serialize only the basic handshake, if this is predicted for + // Hsv4 peer or this is URQ_INDUCTION or URQ_WAVEAHAND. + size_t hs_size = pkt.getLength(); + hs.store_to(pkt.m_pcData, Ref(hs_size)); + pkt.setLength(hs_size); + HLOGC(mglog.Debug, log << "createSrtHandshake: (no ext) size=" << hs_size << " data: " << hs.show()); + return true; + } + + // Sanity check, applies to HSv5 only cases. + if (srths_cmd == SRT_CMD_HSREQ && m_SrtHsSide == HSD_RESPONDER) + { + m_RejectReason = SRT_REJ_IPE; + LOGC(mglog.Fatal, log << "IPE: SRT_CMD_HSREQ was requested to be sent in HSv5 by an INITIATOR side!"); + return false; // should cause rejection + } + + string logext = "HSX"; + + bool have_kmreq = false; + bool have_sid = false; + bool have_congctl = false; + bool have_filter = false; + + // Install the SRT extensions + hs.m_iType |= CHandShake::HS_EXT_HSREQ; + + if (srths_cmd == SRT_CMD_HSREQ) + { + if (m_sStreamName != "") + { + have_sid = true; + hs.m_iType |= CHandShake::HS_EXT_CONFIG; + logext += ",SID"; + } + } + + // If this is a response, we have also information + // on the peer. If Peer is NOT filter capable, don't + // put filter config, even if agent is capable. + bool peer_filter_capable = true; + if (srths_cmd == SRT_CMD_HSRSP) + { + if (m_sPeerPktFilterConfigString != "") + { + peer_filter_capable = true; + } + else if (IsSet(m_lPeerSrtFlags, SRT_OPT_FILTERCAP)) + { + peer_filter_capable = true; + } + else + { + peer_filter_capable = false; + } + } + + // Now, if this is INITIATOR, then it has its + // filter config already set, if configured, otherwise + // it should not attach the filter config extension. + + // If this is a RESPONDER, then it has already received + // the filter config string from the peer and therefore + // possibly confronted with the contents of m_OPT_FECConfigString, + // and if it decided to go with filter, it will be nonempty. + if (peer_filter_capable && m_OPT_PktFilterConfigString != "") + { + have_filter = true; + hs.m_iType |= CHandShake::HS_EXT_CONFIG; + logext += ",filter"; + } + + string sm = m_CongCtl.selected_name(); + if (sm != "" && sm != "live") + { + have_congctl = true; + hs.m_iType |= CHandShake::HS_EXT_CONFIG; + logext += ",CONGCTL"; + } + + // Prevent adding KMRSP only in case when BOTH: + // - Agent has set no password + // - no KMREQ has arrived from Peer + // KMRSP must be always sent when: + // - Agent set a password, Peer did not send KMREQ: Agent sets snd=NOSECRET. + // - Agent set no password, but Peer sent KMREQ: Ageng sets rcv=NOSECRET. + if (m_CryptoSecret.len > 0 || kmdata_wordsize > 0) + { + have_kmreq = true; + hs.m_iType |= CHandShake::HS_EXT_KMREQ; + logext += ",KMX"; + } + + HLOGC(mglog.Debug, log << "createSrtHandshake: (ext: " << logext << ") data: " << hs.show()); + + // NOTE: The HSREQ is practically always required, although may happen + // in future that CONCLUSION can be sent multiple times for a separate + // stream encryption support, and this way it won't enclose HSREQ. + // Also, KMREQ may occur multiple times. + + // So, initially store the UDT legacy handshake. + size_t hs_size = pkt.getLength(), total_ra_size = (hs_size / sizeof(uint32_t)); // Maximum size of data + hs.store_to(pkt.m_pcData, Ref(hs_size)); // hs_size is updated + + size_t ra_size = hs_size / sizeof(int32_t); + + // Now attach the SRT handshake for HSREQ + size_t offset = ra_size; + uint32_t *p = reinterpret_cast(pkt.m_pcData); + // NOTE: since this point, ra_size has a size in int32_t elements, NOT BYTES. + + // The first 4-byte item is the CMD/LENGTH spec. + uint32_t *pcmdspec = p + offset; // Remember the location to be filled later, when we know the length + ++offset; + + // Now use the original function to store the actual SRT_HS data + // ra_size after that + // NOTE: so far, ra_size is m_iMaxSRTPayloadSize expressed in number of elements. + // WILL BE CHANGED HERE. + ra_size = fillSrtHandshake(p + offset, total_ra_size - offset, srths_cmd, HS_VERSION_SRT1); + *pcmdspec = HS_CMDSPEC_CMD::wrap(srths_cmd) | HS_CMDSPEC_SIZE::wrap(ra_size); + + HLOGC(mglog.Debug, + log << "createSrtHandshake: after HSREQ: offset=" << offset << " HSREQ size=" << ra_size + << " space left: " << (total_ra_size - offset)); + + if (have_sid) + { + // Use only in REQ phase and only if stream name is set + offset += ra_size; + pcmdspec = p + offset; + ++offset; + + // Now prepare the string with 4-byte alignment. The string size is limited + // to half the payload size. Just a sanity check to not pack too much into + // the conclusion packet. + size_t size_limit = m_iMaxSRTPayloadSize / 2; + + if (m_sStreamName.size() >= size_limit) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, + log << "createSrtHandshake: stream id too long, limited to " << (size_limit - 1) << " bytes"); + return false; + } + + size_t wordsize = (m_sStreamName.size() + 3) / 4; + size_t aligned_bytesize = wordsize * 4; + + memset(p + offset, 0, aligned_bytesize); + memcpy(p + offset, m_sStreamName.data(), m_sStreamName.size()); + // Preswap to little endian (in place due to possible padding zeros) + HtoILA((uint32_t *)(p + offset), (uint32_t *)(p + offset), wordsize); + + ra_size = wordsize; + *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_SID) | HS_CMDSPEC_SIZE::wrap(ra_size); + + HLOGC(mglog.Debug, + log << "createSrtHandshake: after SID [" << m_sStreamName << "] length=" << m_sStreamName.size() + << " alignedln=" << aligned_bytesize << ": offset=" << offset << " SID size=" << ra_size + << " space left: " << (total_ra_size - offset)); + } + + if (have_congctl) + { + // Pass the congctl to the other side as informational. + // The other side should reject connection if it uses a different congctl. + // The other side should also respond with the congctl it uses, if its non-default (for backward compatibility). + + // XXX Consider change the congctl settings in the listener socket to "adaptive" + // congctl and also "adaptive" value of CUDT::m_bMessageAPI so that the caller + // may ask for whatever kind of transmission it wants, or select transmission + // type differently for different connections, however with the same listener. + + offset += ra_size; + pcmdspec = p + offset; + ++offset; + + size_t wordsize = (sm.size() + 3) / 4; + size_t aligned_bytesize = wordsize * 4; + + memset(p + offset, 0, aligned_bytesize); + + memcpy(p + offset, sm.data(), sm.size()); + // Preswap to little endian (in place due to possible padding zeros) + HtoILA((uint32_t *)(p + offset), (uint32_t *)(p + offset), wordsize); + + ra_size = wordsize; + *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_CONGESTION) | HS_CMDSPEC_SIZE::wrap(ra_size); + + HLOGC(mglog.Debug, + log << "createSrtHandshake: after CONGCTL [" << sm << "] length=" << sm.size() + << " alignedln=" << aligned_bytesize << ": offset=" << offset << " CONGCTL size=" << ra_size + << " space left: " << (total_ra_size - offset)); + } + + if (have_filter) + { + offset += ra_size; + pcmdspec = p + offset; + ++offset; + + size_t wordsize = (m_OPT_PktFilterConfigString.size() + 3) / 4; + size_t aligned_bytesize = wordsize * 4; + + memset(p + offset, 0, aligned_bytesize); + memcpy(p + offset, m_OPT_PktFilterConfigString.data(), m_OPT_PktFilterConfigString.size()); + + ra_size = wordsize; + *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_FILTER) | HS_CMDSPEC_SIZE::wrap(ra_size); + + HLOGC(mglog.Debug, + log << "createSrtHandshake: after filter [" << m_OPT_PktFilterConfigString << "] length=" + << m_OPT_PktFilterConfigString.size() << " alignedln=" << aligned_bytesize << ": offset=" << offset + << " filter size=" << ra_size << " space left: " << (total_ra_size - offset)); + } + + // When encryption turned on + if (have_kmreq) + { + HLOGC(mglog.Debug, + log << "createSrtHandshake: " + << (m_CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION")); + if (srtkm_cmd == SRT_CMD_KMREQ) + { + bool have_any_keys = false; + for (size_t ki = 0; ki < 2; ++ki) + { + // Skip those that have expired + if (!m_pCryptoControl->getKmMsg_needSend(ki, false)) + continue; + + m_pCryptoControl->getKmMsg_markSent(ki, false); + + offset += ra_size; + + size_t msglen = m_pCryptoControl->getKmMsg_size(ki); + // Make ra_size back in element unit + // Add one extra word if the size isn't aligned to 32-bit. + ra_size = (msglen / sizeof(uint32_t)) + (msglen % sizeof(uint32_t) ? 1 : 0); + + // Store the CMD + SIZE in the next field + *(p + offset) = HS_CMDSPEC_CMD::wrap(srtkm_cmd) | HS_CMDSPEC_SIZE::wrap(ra_size); + ++offset; + + // Copy the key - do the endian inversion because another endian inversion + // will be done for every control message before sending, and this KM message + // is ALREADY in network order. + const uint32_t *keydata = reinterpret_cast(m_pCryptoControl->getKmMsg_data(ki)); + + HLOGC(mglog.Debug, + log << "createSrtHandshake: KMREQ: adding key #" << ki << " length=" << ra_size + << " words (KmMsg_size=" << msglen << ")"); + // XXX INSECURE ": [" << FormatBinaryString((uint8_t*)keydata, msglen) << "]"; + + // Yes, I know HtoNLA and NtoHLA do exactly the same operation, but I want + // to be clear about the true intention. + NtoHLA(p + offset, keydata, ra_size); + have_any_keys = true; + } + + if (!have_any_keys) + { + m_RejectReason = SRT_REJ_IPE; + LOGC(mglog.Error, log << "createSrtHandshake: IPE: all keys have expired, no KM to send."); + return false; + } + } + else if (srtkm_cmd == SRT_CMD_KMRSP) + { + uint32_t failure_kmrsp[] = {SRT_KM_S_UNSECURED}; + const uint32_t *keydata = 0; + + // Shift the starting point with the value of previously added block, + // to start with the new one. + offset += ra_size; + + if (kmdata_wordsize == 0) + { + LOGC(mglog.Error, + log << "createSrtHandshake: Agent has PW, but Peer sent no KMREQ. Sending error KMRSP response"); + ra_size = 1; + keydata = failure_kmrsp; + + // Update the KM state as well + m_pCryptoControl->m_SndKmState = SRT_KM_S_NOSECRET; // Agent has PW, but Peer won't decrypt + m_pCryptoControl->m_RcvKmState = SRT_KM_S_UNSECURED; // Peer won't encrypt as well. + } + else + { + if (!kmdata) + { + m_RejectReason = SRT_REJ_IPE; + LOGC(mglog.Fatal, log << "createSrtHandshake: IPE: srtkm_cmd=SRT_CMD_KMRSP and no kmdata!"); + return false; + } + ra_size = kmdata_wordsize; + keydata = reinterpret_cast(kmdata); + } + + *(p + offset) = HS_CMDSPEC_CMD::wrap(srtkm_cmd) | HS_CMDSPEC_SIZE::wrap(ra_size); + ++offset; // Once cell, containting CMD spec and size + HLOGC(mglog.Debug, + log << "createSrtHandshake: KMRSP: applying returned key length=" + << ra_size); // XXX INSECURE << " words: [" << FormatBinaryString((uint8_t*)kmdata, + // kmdata_wordsize*sizeof(uint32_t)) << "]"; + + NtoHLA(p + offset, keydata, ra_size); + } + else + { + m_RejectReason = SRT_REJ_IPE; + LOGC(mglog.Fatal, log << "createSrtHandshake: IPE: wrong value of srtkm_cmd: " << srtkm_cmd); + return false; + } + } + + // ra_size + offset has a value in element unit. + // Switch it again to byte unit. + pkt.setLength((ra_size + offset) * sizeof(int32_t)); + + HLOGC(mglog.Debug, + log << "createSrtHandshake: filled HSv5 handshake flags: " << CHandShake::ExtensionFlagStr(hs.m_iType) + << " length: " << pkt.getLength() << " bytes"); + + return true; +} + +static int +FindExtensionBlock(uint32_t *begin, size_t total_length, ref_t r_out_len, ref_t r_next_block) +{ + // Check if there's anything to process + if (total_length == 0) + { + *r_next_block = NULL; + *r_out_len = 0; + return SRT_CMD_NONE; + } + + size_t & out_len = *r_out_len; + uint32_t *&next_block = *r_next_block; + // This function extracts the block command from the block and its length. + // The command value is returned as a function result. + // The size of that command block is stored into out_len. + // The beginning of the prospective next block is stored in next_block. + + // The caller must be aware that: + // - exactly one element holds the block header (cmd+size), so the actual data are after this one. + // - the returned size is the number of uint32_t elements since that first data element + // - the remaining size should be manually calculated as total_length - 1 - out_len, or + // simply, as next_block - begin. + + // Note that if the total_length is too short to extract the whole block, it will return + // SRT_CMD_NONE. Note that total_length includes this first CMDSPEC word. + // + // When SRT_CMD_NONE is returned, it means that nothing has been extracted and nothing else + // can be further extracted from this block. + + int cmd = HS_CMDSPEC_CMD::unwrap(*begin); + size_t size = HS_CMDSPEC_SIZE::unwrap(*begin); + + if (size + 1 > total_length) + return SRT_CMD_NONE; + + out_len = size; + + if (total_length == size + 1) + next_block = NULL; + else + next_block = begin + 1 + size; + + return cmd; +} + +static inline bool NextExtensionBlock(ref_t begin, uint32_t *next, ref_t length) +{ + if (!next) + return false; + + *length = *length - (next - *begin); + *begin = next; + return true; +} + +bool CUDT::processSrtMsg(const CPacket *ctrlpkt) +{ + uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData; + size_t len = ctrlpkt->getLength(); + int etype = ctrlpkt->getExtendedType(); + uint32_t ts = ctrlpkt->m_iTimeStamp; + + int res = SRT_CMD_NONE; + + HLOGC(mglog.Debug, log << "Dispatching message type=" << etype << " data length=" << (len / sizeof(int32_t))); + switch (etype) + { + case SRT_CMD_HSREQ: + { + res = processSrtMsg_HSREQ(srtdata, len, ts, CUDT::HS_VERSION_UDT4); + break; + } + case SRT_CMD_HSRSP: + { + res = processSrtMsg_HSRSP(srtdata, len, ts, CUDT::HS_VERSION_UDT4); + break; + } + case SRT_CMD_KMREQ: + // Special case when the data need to be processed here + // and the appropriate message must be constructed for sending. + // No further processing required + { + uint32_t srtdata_out[SRTDATA_MAXSIZE]; + size_t len_out = 0; + res = m_pCryptoControl->processSrtMsg_KMREQ(srtdata, len, srtdata_out, Ref(len_out), CUDT::HS_VERSION_UDT4); + if (res == SRT_CMD_KMRSP) + { + if (len_out == 1) + { + if (m_bOPT_StrictEncryption) + { + LOGC(mglog.Error, + log << "KMREQ FAILURE: " << KmStateStr(SRT_KM_STATE(srtdata_out[0])) + << " - rejecting per strict encryption"); + return false; + } + HLOGC(mglog.Debug, + log << "MKREQ -> KMRSP FAILURE state: " << KmStateStr(SRT_KM_STATE(srtdata_out[0]))); + } + else + { + HLOGC(mglog.Debug, log << "KMREQ -> requested to send KMRSP length=" << len_out); + } + sendSrtMsg(SRT_CMD_KMRSP, srtdata_out, len_out); + } + // XXX Dead code. processSrtMsg_KMREQ now doesn't return any other value now. + // Please review later. + else + { + LOGC(mglog.Error, log << "KMREQ failed to process the request - ignoring"); + } + + return true; // already done what's necessary + } + + case SRT_CMD_KMRSP: + { + // KMRSP doesn't expect any following action + m_pCryptoControl->processSrtMsg_KMRSP(srtdata, len, CUDT::HS_VERSION_UDT4); + return true; // nothing to do + } + + default: + return false; + } + + if (res == SRT_CMD_NONE) + return true; + + // Send the message that the message handler requested. + sendSrtMsg(res); + + return true; +} + +int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t len, uint32_t ts, int hsv) +{ + // Set this start time in the beginning, regardless as to whether TSBPD is being + // used or not. This must be done in the Initiator as well as Responder. + + /* + * Compute peer StartTime in our time reference + * This takes time zone, time drift into account. + * Also includes current packet transit time (rtt/2) + */ +#if 0 // Debug PeerStartTime if not 1st HS packet + { + uint64_t oldPeerStartTime = m_ullRcvPeerStartTime; + m_ullRcvPeerStartTime = CTimer::getTime() - (uint64_t)((uint32_t)ts); + if (oldPeerStartTime) { + LOGC(mglog.Note, log << "rcvSrtMsg: 2nd PeerStartTime diff=" << + (m_ullRcvPeerStartTime - oldPeerStartTime) << " usec"); + + } + } +#else + m_ullRcvPeerStartTime = CTimer::getTime() - (uint64_t)((uint32_t)ts); +#endif + + // Prepare the initial runtime values of latency basing on the option values. + // They are going to get the value fixed HERE. + m_iTsbPdDelay_ms = m_iOPT_TsbPdDelay; + m_iPeerTsbPdDelay_ms = m_iOPT_PeerTsbPdDelay; + + if (len < SRT_CMD_HSREQ_MINSZ) + { + m_RejectReason = SRT_REJ_ROGUE; + /* Packet smaller than minimum compatible packet size */ + LOGF(mglog.Error, "HSREQ/rcv: cmd=%d(HSREQ) len=%" PRIzu " invalid", SRT_CMD_HSREQ, len); + return SRT_CMD_NONE; + } + + LOGF(mglog.Note, + "HSREQ/rcv: cmd=%d(HSREQ) len=%" PRIzu " vers=0x%x opts=0x%x delay=%d", + SRT_CMD_HSREQ, + len, + srtdata[SRT_HS_VERSION], + srtdata[SRT_HS_FLAGS], + SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); + + m_lPeerSrtVersion = srtdata[SRT_HS_VERSION]; + m_lPeerSrtFlags = srtdata[SRT_HS_FLAGS]; + + if (hsv == CUDT::HS_VERSION_UDT4) + { + if (m_lPeerSrtVersion >= SRT_VERSION_FEAT_HSv5) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, + log << "HSREQ/rcv: With HSv4 version >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5) + << " is not acceptable."); + return SRT_CMD_REJECT; + } + } + else + { + if (m_lPeerSrtVersion < SRT_VERSION_FEAT_HSv5) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, + log << "HSREQ/rcv: With HSv5 version must be >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5) << " ."); + return SRT_CMD_REJECT; + } + } + + // Check also if the version satisfies the minimum required version + if (m_lPeerSrtVersion < m_lMinimumPeerSrtVersion) + { + m_RejectReason = SRT_REJ_VERSION; + LOGC(mglog.Error, + log << "HSREQ/rcv: Peer version: " << SrtVersionString(m_lPeerSrtVersion) + << " is too old for requested: " << SrtVersionString(m_lMinimumPeerSrtVersion) << " - REJECTING"); + return SRT_CMD_REJECT; + } + + HLOGC(mglog.Debug, + log << "HSREQ/rcv: PEER Version: " << SrtVersionString(m_lPeerSrtVersion) << " Flags: " << m_lPeerSrtFlags + << "(" << SrtFlagString(m_lPeerSrtFlags) << ")"); + + m_bPeerRexmitFlag = IsSet(m_lPeerSrtFlags, SRT_OPT_REXMITFLG); + HLOGF(mglog.Debug, "HSREQ/rcv: peer %s REXMIT flag", m_bPeerRexmitFlag ? "UNDERSTANDS" : "DOES NOT UNDERSTAND"); + + // Check if both use the same API type. Reject if not. + bool peer_message_api = !IsSet(m_lPeerSrtFlags, SRT_OPT_STREAM); + if (peer_message_api != m_bMessageAPI) + { + m_RejectReason = SRT_REJ_MESSAGEAPI; + LOGC(mglog.Error, + log << "HSREQ/rcv: Agent uses " << (m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, but the Peer declares " + << (peer_message_api ? "MESSAGE" : "STREAM") << " API. Not compatible transmission type, rejecting."); + return SRT_CMD_REJECT; + } + + if (len < SRT_HS_LATENCY + 1) + { + // 3 is the size when containing VERSION, FLAGS and LATENCY. Less size + // makes it contain only the first two. Let's make it acceptable, as long + // as the latency flags aren't set. + if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDSND) || IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV)) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, + log << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, but TSBPD flags are set. Rejecting."); + return SRT_CMD_REJECT; + } + + LOGC(mglog.Warn, log << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, not getting any TSBPD settings."); + // Don't process any further settings in this case. Turn off TSBPD, just for a case. + m_bTsbPd = false; + m_bPeerTsbPd = false; + return SRT_CMD_HSRSP; + } + + uint32_t latencystr = srtdata[SRT_HS_LATENCY]; + + if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDSND)) + { + // TimeStamp-based Packet Delivery feature enabled + if (!m_bTsbPd) + { + LOGC(mglog.Warn, log << "HSREQ/rcv: Agent did not set rcv-TSBPD - ignoring proposed latency from peer"); + + // Note: also don't set the peer TSBPD flag HERE because + // - in HSv4 it will be a sender, so it doesn't matter anyway + // - in HSv5 if it's going to receive, the TSBPDRCV flag will define it. + } + else + { + int peer_decl_latency; + if (hsv < CUDT::HS_VERSION_SRT1) + { + // In HSv4 there is only one value and this is the latency + // that the sender peer proposes for the agent. + peer_decl_latency = SRT_HS_LATENCY_LEG::unwrap(latencystr); + } + else + { + // In HSv5 there are latency declared for sending and receiving separately. + + // SRT_HS_LATENCY_SND is the value that the peer proposes to be the + // value used by agent when receiving data. We take this as a local latency value. + peer_decl_latency = SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]); + } + + // Use the maximum latency out of latency from our settings and the latency + // "proposed" by the peer. + int maxdelay = std::max(m_iTsbPdDelay_ms, peer_decl_latency); + HLOGC(mglog.Debug, + log << "HSREQ/rcv: LOCAL/RCV LATENCY: Agent:" << m_iTsbPdDelay_ms << " Peer:" << peer_decl_latency + << " Selecting:" << maxdelay); + m_iTsbPdDelay_ms = maxdelay; + } + } + else + { + std::string how_about_agent = m_bTsbPd ? "BUT AGENT DOES" : "and nor does Agent"; + HLOGC(mglog.Debug, log << "HSREQ/rcv: Peer DOES NOT USE latency for sending - " << how_about_agent); + } + + // This happens when the HSv5 RESPONDER receives the HSREQ message; it declares + // that the peer INITIATOR will receive the data and informs about its predefined + // latency. We need to maximize this with our setting of the peer's latency and + // record as peer's latency, which will be then sent back with HSRSP. + if (hsv > CUDT::HS_VERSION_UDT4 && IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV)) + { + // So, PEER uses TSBPD, set the flag. + // NOTE: it doesn't matter, if AGENT uses TSBPD. + m_bPeerTsbPd = true; + + // SRT_HS_LATENCY_RCV is the value that the peer declares as to be + // used by it when receiving data. We take this as a peer's value, + // and select the maximum of this one and our proposed latency for the peer. + int peer_decl_latency = SRT_HS_LATENCY_RCV::unwrap(latencystr); + int maxdelay = std::max(m_iPeerTsbPdDelay_ms, peer_decl_latency); + HLOGC(mglog.Debug, + log << "HSREQ/rcv: PEER/RCV LATENCY: Agent:" << m_iPeerTsbPdDelay_ms << " Peer:" << peer_decl_latency + << " Selecting:" << maxdelay); + m_iPeerTsbPdDelay_ms = maxdelay; + } + else + { + std::string how_about_agent = m_bTsbPd ? "BUT AGENT DOES" : "and nor does Agent"; + HLOGC(mglog.Debug, log << "HSREQ/rcv: Peer DOES NOT USE latency for receiving - " << how_about_agent); + } + + if (hsv > CUDT::HS_VERSION_UDT4) + { + // This is HSv5, do the same things as required for the sending party in HSv4, + // as in HSv5 this can also be a sender. + if (IsSet(m_lPeerSrtFlags, SRT_OPT_TLPKTDROP)) + { + // Too late packets dropping feature supported + m_bPeerTLPktDrop = true; + } + if (IsSet(m_lPeerSrtFlags, SRT_OPT_NAKREPORT)) + { + // Peer will send Periodic NAK Reports + m_bPeerNakReport = true; + } + } + + return SRT_CMD_HSRSP; +} + +int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t len, uint32_t ts, int hsv) +{ + // XXX Check for mis-version + // With HSv4 we accept only version less than 1.2.0 + if (hsv == CUDT::HS_VERSION_UDT4 && srtdata[SRT_HS_VERSION] >= SRT_VERSION_FEAT_HSv5) + { + LOGC(mglog.Error, log << "HSRSP/rcv: With HSv4 version >= 1.2.0 is not acceptable."); + return SRT_CMD_NONE; + } + + if (len < SRT_CMD_HSRSP_MINSZ) + { + /* Packet smaller than minimum compatible packet size */ + LOGF(mglog.Error, "HSRSP/rcv: cmd=%d(HSRSP) len=%" PRIzu " invalid", SRT_CMD_HSRSP, len); + return SRT_CMD_NONE; + } + + // Set this start time in the beginning, regardless as to whether TSBPD is being + // used or not. This must be done in the Initiator as well as Responder. In case when + // agent is sender only (HSv4) this value simply won't be used. + + /* + * Compute peer StartTime in our time reference + * This takes time zone, time drift into account. + * Also includes current packet transit time (rtt/2) + */ +#if 0 // Debug PeerStartTime if not 1st HS packet + { + uint64_t oldPeerStartTime = m_ullRcvPeerStartTime; + m_ullRcvPeerStartTime = CTimer::getTime() - (uint64_t)((uint32_t)ts); + if (oldPeerStartTime) { + LOGC(mglog.Note, log << "rcvSrtMsg: 2nd PeerStartTime diff=" << + (m_ullRcvPeerStartTime - oldPeerStartTime) << " usec"); + + } + } +#else + m_ullRcvPeerStartTime = CTimer::getTime() - (uint64_t)((uint32_t)ts); +#endif + + m_lPeerSrtVersion = srtdata[SRT_HS_VERSION]; + m_lPeerSrtFlags = srtdata[SRT_HS_FLAGS]; + + HLOGF(mglog.Debug, + "HSRSP/rcv: Version: %s Flags: SND:%08X (%s)", + SrtVersionString(m_lPeerSrtVersion).c_str(), + m_lPeerSrtFlags, + SrtFlagString(m_lPeerSrtFlags).c_str()); + + if (hsv == CUDT::HS_VERSION_UDT4) + { + // The old HSv4 way: extract just one value and put it under peer. + if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV)) + { + // TsbPd feature enabled + m_bPeerTsbPd = true; + m_iPeerTsbPdDelay_ms = SRT_HS_LATENCY_LEG::unwrap(srtdata[SRT_HS_LATENCY]); + HLOGC(mglog.Debug, + log << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms + << " (Agent: declared:" << m_iTsbPdDelay_ms << " rcv:" << m_iTsbPdDelay_ms << ")"); + } + // TSBPDSND isn't set in HSv4 by the RESPONDER, because HSv4 RESPONDER is always RECEIVER. + } + else + { + // HSv5 way: extract the receiver latency and sender latency, if used. + + if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV)) + { + // TsbPd feature enabled + m_bPeerTsbPd = true; + m_iPeerTsbPdDelay_ms = SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]); + HLOGC(mglog.Debug, log << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms << "ms"); + } + else + { + HLOGC(mglog.Debug, log << "HSRSP/rcv: Peer (responder) DOES NOT USE latency"); + } + + if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDSND)) + { + if (!m_bTsbPd) + { + LOGC(mglog.Warn, + log << "HSRSP/rcv: BUG? Peer (responder) declares sending latency, but Agent turned off TSBPD."); + } + else + { + // Take this value as a good deal. In case when the Peer did not "correct" the latency + // because it has TSBPD turned off, just stay with the present value defined in options. + m_iTsbPdDelay_ms = SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]); + HLOGC(mglog.Debug, log << "HSRSP/rcv: LATENCY Agent/rcv: " << m_iTsbPdDelay_ms << "ms"); + } + } + } + + if ((m_lSrtVersion >= SrtVersion(1, 0, 5)) && IsSet(m_lPeerSrtFlags, SRT_OPT_TLPKTDROP)) + { + // Too late packets dropping feature supported + m_bPeerTLPktDrop = true; + } + + if ((m_lSrtVersion >= SrtVersion(1, 1, 0)) && IsSet(m_lPeerSrtFlags, SRT_OPT_NAKREPORT)) + { + // Peer will send Periodic NAK Reports + m_bPeerNakReport = true; + } + + if (m_lSrtVersion >= SrtVersion(1, 2, 0)) + { + if (IsSet(m_lPeerSrtFlags, SRT_OPT_REXMITFLG)) + { + // Peer will use REXMIT flag in packet retransmission. + m_bPeerRexmitFlag = true; + HLOGP(mglog.Debug, "HSRSP/rcv: 1.2.0+ Agent understands REXMIT flag and so does peer."); + } + else + { + HLOGP(mglog.Debug, "HSRSP/rcv: Agent understands REXMIT flag, but PEER DOES NOT"); + } + } + else + { + HLOGF(mglog.Debug, "HSRSP/rcv: <1.2.0 Agent DOESN'T understand REXMIT flag"); + } + + handshakeDone(); + + return SRT_CMD_NONE; +} + +// This function is called only when the URQ_CONCLUSION handshake has been received from the peer. +bool CUDT::interpretSrtHandshake(const CHandShake &hs, + const CPacket & hspkt, + uint32_t *out_data SRT_ATR_UNUSED, + size_t * out_len) +{ + // Initialize out_len to 0 to handle the unencrypted case + if (out_len) + *out_len = 0; + + // The version=0 statement as rejection is used only since HSv5. + // The HSv4 sends the AGREEMENT handshake message with version=0, do not misinterpret it. + if (m_ConnRes.m_iVersion > HS_VERSION_UDT4 && hs.m_iVersion == 0) + { + m_RejectReason = SRT_REJ_PEER; + LOGC(mglog.Error, log << "HS VERSION = 0, meaning the handshake has been rejected."); + return false; + } + + if (hs.m_iVersion < HS_VERSION_SRT1) + return true; // do nothing + + // Anyway, check if the handshake contains any extra data. + if (hspkt.getLength() <= CHandShake::m_iContentSize) + { + m_RejectReason = SRT_REJ_ROGUE; + // This would mean that the handshake was at least HSv5, but somehow no extras were added. + // Dismiss it then, however this has to be logged. + LOGC(mglog.Error, log << "HS VERSION=" << hs.m_iVersion << " but no handshake extension found!"); + return false; + } + + // We still believe it should work, let's check the flags. + int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs.m_iType); + if (ext_flags == 0) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, log << "HS VERSION=" << hs.m_iVersion << " but no handshake extension flags are set!"); + return false; + } + + HLOGC(mglog.Debug, + log << "HS VERSION=" << hs.m_iVersion << " EXTENSIONS: " << CHandShake::ExtensionFlagStr(ext_flags)); + + // Ok, now find the beginning of an int32_t array that follows the UDT handshake. + uint32_t *p = reinterpret_cast(hspkt.m_pcData + CHandShake::m_iContentSize); + size_t size = hspkt.getLength() - CHandShake::m_iContentSize; // Due to previous cond check we grant it's >0 + + if (IsSet(ext_flags, CHandShake::HS_EXT_HSREQ)) + { + HLOGC(mglog.Debug, log << "interpretSrtHandshake: extracting HSREQ/RSP type extension"); + uint32_t *begin = p; + uint32_t *next = 0; + size_t length = size / sizeof(uint32_t); + size_t blocklen = 0; + + for (;;) // this is ONE SHOT LOOP + { + int cmd = FindExtensionBlock(begin, length, Ref(blocklen), Ref(next)); + + size_t bytelen = blocklen * sizeof(uint32_t); + + if (cmd == SRT_CMD_HSREQ) + { + // Set is the size as it should, then give it for interpretation for + // the proper function. + if (blocklen < SRT_HS__SIZE) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, + log << "HS-ext HSREQ found but invalid size: " << bytelen << " (expected: " << SRT_HS__SIZE + << ")"); + return false; // don't interpret + } + + int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + // Interpreted? Then it should be responded with SRT_CMD_HSRSP. + if (rescmd != SRT_CMD_HSRSP) + { + // m_RejectReason already set + LOGC(mglog.Error, + log << "interpretSrtHandshake: process HSREQ returned unexpected value " << rescmd); + return false; + } + handshakeDone(); + updateAfterSrtHandshake(SRT_CMD_HSREQ, HS_VERSION_SRT1); + } + else if (cmd == SRT_CMD_HSRSP) + { + // Set is the size as it should, then give it for interpretation for + // the proper function. + if (blocklen < SRT_HS__SIZE) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, + log << "HS-ext HSRSP found but invalid size: " << bytelen << " (expected: " << SRT_HS__SIZE + << ")"); + + return false; // don't interpret + } + + int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + // Interpreted? Then it should be responded with SRT_CMD_NONE. + // (nothing to be responded for HSRSP, unless there was some kinda problem) + if (rescmd != SRT_CMD_NONE) + { + // Just formally; the current code doesn't seem to return anything else. + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, + log << "interpretSrtHandshake: process HSRSP returned unexpected value " << rescmd); + return false; + } + handshakeDone(); + updateAfterSrtHandshake(SRT_CMD_HSRSP, HS_VERSION_SRT1); + } + else if (cmd == SRT_CMD_NONE) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, log << "interpretSrtHandshake: no HSREQ/HSRSP block found in the handshake msg!"); + // This means that there can be no more processing done by FindExtensionBlock(). + // And we haven't found what we need - otherwise one of the above cases would pass + // and lead to exit this loop immediately. + return false; + } + else + { + // Any other kind of message extracted. Search on. + length -= (next - begin); + begin = next; + if (begin) + continue; + } + + break; + } + } + + HLOGC(mglog.Debug, log << "interpretSrtHandshake: HSREQ done, checking KMREQ"); + + // Now check the encrypted + + bool encrypted = false; + + if (IsSet(ext_flags, CHandShake::HS_EXT_KMREQ)) + { + HLOGC(mglog.Debug, log << "interpretSrtHandshake: extracting KMREQ/RSP type extension"); + +#ifdef SRT_ENABLE_ENCRYPTION + if (!m_pCryptoControl->hasPassphrase()) + { + if (m_bOPT_StrictEncryption) + { + m_RejectReason = SRT_REJ_UNSECURE; + LOGC( + mglog.Error, + log << "HS KMREQ: Peer declares encryption, but agent does not - rejecting per strict requirement"); + return false; + } + + LOGC(mglog.Error, + log << "HS KMREQ: Peer declares encryption, but agent does not - still allowing connection."); + + // Still allow for connection, and allow Agent to send unencrypted stream to the peer. + // Also normally allow the key to be processed; worst case it will send the failure response. + } + + uint32_t *begin = p; + uint32_t *next = 0; + size_t length = size / sizeof(uint32_t); + size_t blocklen = 0; + + for (;;) // This is one shot loop, unless REPEATED by 'continue'. + { + int cmd = FindExtensionBlock(begin, length, Ref(blocklen), Ref(next)); + + HLOGC(mglog.Debug, + log << "interpretSrtHandshake: found extension: (" << cmd << ") " << MessageTypeStr(UMSG_EXT, cmd)); + + size_t bytelen = blocklen * sizeof(uint32_t); + if (cmd == SRT_CMD_KMREQ) + { + if (!out_data || !out_len) + { + m_RejectReason = SRT_REJ_IPE; + LOGC(mglog.Fatal, log << "IPE: HS/KMREQ extracted without passing target buffer!"); + return false; + } + + int res = + m_pCryptoControl->processSrtMsg_KMREQ(begin + 1, bytelen, out_data, Ref(*out_len), HS_VERSION_SRT1); + if (res != SRT_CMD_KMRSP) + { + m_RejectReason = SRT_REJ_IPE; + // Something went wrong. + HLOGC(mglog.Debug, + log << "interpretSrtHandshake: IPE/EPE KMREQ processing failed - returned " << res); + return false; + } + if (*out_len == 1) + { + // This means that there was an abnormal encryption situation occurred. + // This is inacceptable in case of strict encryption. + if (m_bOPT_StrictEncryption) + { + if (m_pCryptoControl->m_RcvKmState == SRT_KM_S_BADSECRET) + { + m_RejectReason = SRT_REJ_BADSECRET; + } + else + { + m_RejectReason = SRT_REJ_UNSECURE; + } + LOGC(mglog.Error, + log << "interpretSrtHandshake: KMREQ result abnornal - rejecting per strict encryption"); + return false; + } + } + encrypted = true; + } + else if (cmd == SRT_CMD_KMRSP) + { + int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, HS_VERSION_SRT1); + if (m_bOPT_StrictEncryption && res == -1) + { + m_RejectReason = SRT_REJ_UNSECURE; + LOGC(mglog.Error, log << "KMRSP failed - rejecting connection as per strict encryption."); + return false; + } + encrypted = true; + } + else if (cmd == SRT_CMD_NONE) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, log << "HS KMREQ expected - none found!"); + return false; + } + else + { + HLOGC(mglog.Debug, log << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd)); + if (NextExtensionBlock(Ref(begin), next, Ref(length))) + continue; + } + + break; + } +#else + // When encryption is not enabled at compile time, behave as if encryption wasn't set, + // so accordingly to StrictEncryption flag. + + if (m_bOPT_StrictEncryption) + { + m_RejectReason = SRT_REJ_UNSECURE; + LOGC(mglog.Error, + log << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - rejecting " + "per strict requirement"); + return false; + } + + LOGC(mglog.Error, + log << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - still allowing " + "connection."); + encrypted = true; +#endif + } + + bool have_congctl = false; + bool have_filter = false; + string agsm = m_CongCtl.selected_name(); + if (agsm == "") + { + agsm = "live"; + m_CongCtl.select("live"); + } + + if (IsSet(ext_flags, CHandShake::HS_EXT_CONFIG)) + { + HLOGC(mglog.Debug, log << "interpretSrtHandshake: extracting various CONFIG extensions"); + + uint32_t *begin = p; + uint32_t *next = 0; + size_t length = size / sizeof(uint32_t); + size_t blocklen = 0; + + for (;;) // This is one shot loop, unless REPEATED by 'continue'. + { + int cmd = FindExtensionBlock(begin, length, Ref(blocklen), Ref(next)); + + HLOGC(mglog.Debug, + log << "interpretSrtHandshake: found extension: (" << cmd << ") " << MessageTypeStr(UMSG_EXT, cmd)); + + const size_t bytelen = blocklen * sizeof(uint32_t); + if (cmd == SRT_CMD_SID) + { + if (!bytelen || bytelen > MAX_SID_LENGTH) + { + LOGC(mglog.Error, + log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +MAX_SID_LENGTH + << " - PROTOCOL ERROR, REJECTING"); + return false; + } + // Copied through a cleared array. This is because the length is aligned to 4 + // where the padding is filled by zero bytes. For the case when the string is + // exactly of a 4-divisible length, we make a big array with maximum allowed size + // filled with zeros. Copying to this array should then copy either only the valid + // characters of the string (if the lenght is divisible by 4), or the string with + // padding zeros. In all these cases in the resulting array we should have all + // subsequent characters of the string plus at least one '\0' at the end. This will + // make it a perfect NUL-terminated string, to be used to initialize a string. + char target[MAX_SID_LENGTH + 1]; + memset(target, 0, MAX_SID_LENGTH + 1); + memcpy(target, begin + 1, bytelen); + + // Un-swap on big endian machines + ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen); + + m_sStreamName = target; + HLOGC(mglog.Debug, + log << "CONNECTOR'S REQUESTED SID [" << m_sStreamName << "] (bytelen=" << bytelen + << " blocklen=" << blocklen << ")"); + } + else if (cmd == SRT_CMD_CONGESTION) + { + if (have_congctl) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, log << "CONGCTL BLOCK REPEATED!"); + return false; + } + + if (!bytelen || bytelen > MAX_SID_LENGTH) + { + LOGC(mglog.Error, + log << "interpretSrtHandshake: CONGESTION-control type length " << bytelen << " is 0 or > " + << +MAX_SID_LENGTH << " - PROTOCOL ERROR, REJECTING"); + return false; + } + // Declare that congctl has been received + have_congctl = true; + + char target[MAX_SID_LENGTH + 1]; + memset(target, 0, MAX_SID_LENGTH + 1); + memcpy(target, begin + 1, bytelen); + // Un-swap on big endian machines + ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen); + + string sm = target; + + // As the congctl has been declared by the peer, + // check if your congctl is compatible. + // sm cannot be empty, but the agent's sm can be empty meaning live. + if (sm != agsm) + { + m_RejectReason = SRT_REJ_CONGESTION; + LOGC(mglog.Error, + log << "PEER'S CONGCTL '" << sm << "' does not match AGENT'S CONGCTL '" << agsm << "'"); + return false; + } + + HLOGC(mglog.Debug, + log << "CONNECTOR'S CONGCTL [" << sm << "] (bytelen=" << bytelen << " blocklen=" << blocklen + << ")"); + } + else if (cmd == SRT_CMD_FILTER) + { + if (have_filter) + { + m_RejectReason = SRT_REJ_FILTER; + LOGC(mglog.Error, log << "FILTER BLOCK REPEATED!"); + return false; + } + // Declare that filter has been received + have_filter = true; + + // XXX This is the maximum string, but filter config + // shall be normally limited somehow, especially if used + // together with SID! + char target[MAX_SID_LENGTH + 1]; + memset(target, 0, MAX_SID_LENGTH + 1); + memcpy(target, begin + 1, bytelen); + string fltcfg = target; + + HLOGC(mglog.Debug, + log << "PEER'S FILTER CONFIG [" << fltcfg << "] (bytelen=" << bytelen << " blocklen=" << blocklen + << ")"); + + if (!checkApplyFilterConfig(fltcfg)) + { + LOGC(mglog.Error, log << "PEER'S FILTER CONFIG [" << fltcfg << "] has been rejected"); + return false; + } + } + else if (cmd == SRT_CMD_NONE) + { + break; + } + else + { + // Found some block that is not interesting here. Skip this and get the next one. + HLOGC(mglog.Debug, log << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd)); + } + + if (!NextExtensionBlock(Ref(begin), next, Ref(length))) + break; + } + } + + // Post-checks + // Check if peer declared encryption + if (!encrypted && m_CryptoSecret.len > 0) + { + if (m_bOPT_StrictEncryption) + { + m_RejectReason = SRT_REJ_UNSECURE; + LOGC(mglog.Error, + log << "HS EXT: Agent declares encryption, but Peer does not - rejecting connection per strict " + "requirement."); + return false; + } + + LOGC(mglog.Error, + log << "HS EXT: Agent declares encryption, but Peer does not (Agent can still receive unencrypted packets " + "from Peer)."); + + // This is required so that the sender is still allowed to send data, when encryption is required, + // just this will be for waste because the receiver won't decrypt them anyway. + m_pCryptoControl->createFakeSndContext(); + m_pCryptoControl->m_SndKmState = SRT_KM_S_NOSECRET; // Because Peer did not send KMX, though Agent has pw + m_pCryptoControl->m_RcvKmState = SRT_KM_S_UNSECURED; // Because Peer has no PW, as has sent no KMREQ. + return true; + } + + // If agent has set some nondefault congctl, then congctl is expected from the peer. + if (agsm != "live" && !have_congctl) + { + m_RejectReason = SRT_REJ_CONGESTION; + LOGC(mglog.Error, + log << "HS EXT: Agent uses '" << agsm << "' congctl, but peer DID NOT DECLARE congctl (assuming 'live')."); + return false; + } + + // Ok, finished, for now. + return true; +} + +bool CUDT::checkApplyFilterConfig(const std::string &confstr) +{ + SrtFilterConfig cfg; + if (!ParseFilterConfig(confstr, cfg)) + return false; + + // Now extract the type, if present, and + // check if you have this type of corrector available. + if (!PacketFilter::correctConfig(cfg)) + return false; + + // Now parse your own string, if you have it. + if (m_OPT_PktFilterConfigString != "") + { + // - for rendezvous, both must be exactly the same, or only one side specified. + if (m_bRendezvous && m_OPT_PktFilterConfigString != confstr) + { + return false; + } + + SrtFilterConfig mycfg; + if (!ParseFilterConfig(m_OPT_PktFilterConfigString, mycfg)) + return false; + + // Check only if both have set a filter of the same type. + if (mycfg.type != cfg.type) + return false; + + // If so, then: + // - for caller-listener configuration, accept the listener version. + if (m_SrtHsSide == HSD_INITIATOR) + { + // This is a caller, this should apply all parameters received + // from the listener, forcefully. + for (map::iterator x = cfg.parameters.begin(); x != cfg.parameters.end(); ++x) + { + mycfg.parameters[x->first] = x->second; + } + } + else + { + // On a listener, only apply those that you haven't set + for (map::iterator x = cfg.parameters.begin(); x != cfg.parameters.end(); ++x) + { + if (!mycfg.parameters.count(x->first)) + mycfg.parameters[x->first] = x->second; + } + } + + HLOGC(mglog.Debug, + log << "checkApplyFilterConfig: param: LOCAL: " << Printable(mycfg.parameters) + << " FORGN: " << Printable(cfg.parameters)); + + ostringstream myos; + myos << mycfg.type; + for (map::iterator x = mycfg.parameters.begin(); x != mycfg.parameters.end(); ++x) + { + myos << "," << x->first << ":" << x->second; + } + + m_OPT_PktFilterConfigString = myos.str(); + + HLOGC(mglog.Debug, log << "checkApplyFilterConfig: Effective config: " << m_OPT_PktFilterConfigString); + } + else + { + // Take the foreign configuration as a good deal. + HLOGC(mglog.Debug, log << "checkApplyFilterConfig: Good deal config: " << m_OPT_PktFilterConfigString); + m_OPT_PktFilterConfigString = confstr; + } + + size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - cfg.extra_size; + if (m_zOPT_ExpPayloadSize > efc_max_payload_size) + { + LOGC(mglog.Warn, + log << "Due to filter-required extra " << cfg.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " + << efc_max_payload_size << " bytes"); + m_zOPT_ExpPayloadSize = efc_max_payload_size; + } + + return true; +} + +void CUDT::startConnect(const sockaddr *serv_addr, int32_t forced_isn) +{ + CGuard cg(m_ConnectionLock); + + HLOGC(mglog.Debug, log << "startConnect: -> " << SockaddrToString(serv_addr) << "..."); + + if (!m_bOpened) + throw CUDTException(MJ_NOTSUP, MN_NONE, 0); + + if (m_bListening) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + if (m_bConnecting || m_bConnected) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + + // record peer/server address + delete m_pPeerAddr; + m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr *)new sockaddr_in : (sockaddr *)new sockaddr_in6; + memcpy(m_pPeerAddr, serv_addr, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + + // register this socket in the rendezvous queue + // RendezevousQueue is used to temporarily store incoming handshake, non-rendezvous connections also require this + // function +#ifdef SRT_ENABLE_CONNTIMEO + uint64_t ttl = m_iConnTimeOut * uint64_t(1000); +#else + uint64_t ttl = 3000000; +#endif + // XXX DEBUG + // ttl = 0x1000000000000000; + // XXX + if (m_bRendezvous) + ttl *= 10; + ttl += CTimer::getTime(); + m_pRcvQueue->registerConnector(m_SocketID, this, m_iIPversion, serv_addr, ttl); + + // The m_iType is used in the INDUCTION for nothing. This value is only regarded + // in CONCLUSION handshake, however this must be created after the handshake version + // is already known. UDT_DGRAM is the value that was the only valid in the old SRT + // with HSv4 (it supported only live transmission), for HSv5 it will be changed to + // handle handshake extension flags. + m_ConnReq.m_iType = UDT_DGRAM; + + // This is my current configuration + if (m_bRendezvous) + { + // For rendezvous, use version 5 in the waveahand and the cookie. + // In case when you get the version 4 waveahand, simply switch to + // the legacy HSv4 rendezvous and this time send version 4 CONCLUSION. + + // The HSv4 client simply won't check the version nor the cookie and it + // will be sending its waveahands with version 4. Only when the party + // has sent version 5 waveahand should the agent continue with HSv5 + // rendezvous. + m_ConnReq.m_iVersion = HS_VERSION_SRT1; + // m_ConnReq.m_iVersion = HS_VERSION_UDT4; // <--- Change in order to do regression test. + m_ConnReq.m_iReqType = URQ_WAVEAHAND; + m_ConnReq.m_iCookie = bake(serv_addr); + + // This will be also passed to a HSv4 rendezvous, but fortunately the old + // SRT didn't read this field from URQ_WAVEAHAND message, only URQ_CONCLUSION. + m_ConnReq.m_iType = SrtHSRequest::wrapFlags(false /* no MAGIC here */, m_iSndCryptoKeyLen); + bool whether SRT_ATR_UNUSED = m_iSndCryptoKeyLen != 0; + HLOGC(mglog.Debug, + log << "startConnect (rnd): " << (whether ? "" : "NOT ") + << " Advertising PBKEYLEN - value = " << m_iSndCryptoKeyLen); + m_RdvState = CHandShake::RDV_WAVING; + m_SrtHsSide = HSD_DRAW; // initially not resolved. + } + else + { + // For caller-listener configuration, set the version 4 for INDUCTION + // due to a serious problem in UDT code being also in the older SRT versions: + // the listener peer simply sents the EXACT COPY of the caller's induction + // handshake, except the cookie, which means that when the caller sents version 5, + // the listener will respond with version 5, which is a false information. Therefore + // HSv5 clients MUST send HS_VERSION_UDT4 from the caller, regardless of currently + // supported handshake version. + // + // The HSv5 listener should only respond with INDUCTION with m_iVersion == HS_VERSION_SRT1. + m_ConnReq.m_iVersion = HS_VERSION_UDT4; + m_ConnReq.m_iReqType = URQ_INDUCTION; + m_ConnReq.m_iCookie = 0; + m_RdvState = CHandShake::RDV_INVALID; + } + + m_ConnReq.m_iMSS = m_iMSS; + m_ConnReq.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize; + m_ConnReq.m_iID = m_SocketID; + CIPAddress::ntop(serv_addr, m_ConnReq.m_piPeerIP, m_iIPversion); + + if (forced_isn == 0) + { + // Random Initial Sequence Number (normal mode) + srand((unsigned int)CTimer::getTime()); + m_iISN = m_ConnReq.m_iISN = (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX)); + } + else + { + // Predefined ISN (for debug purposes) + m_iISN = m_ConnReq.m_iISN = forced_isn; + } + + m_iLastDecSeq = m_iISN - 1; + m_iSndLastAck = m_iISN; + m_iSndLastDataAck = m_iISN; + m_iSndLastFullAck = m_iISN; + m_iSndCurrSeqNo = m_iISN - 1; + m_iSndLastAck2 = m_iISN; + m_ullSndLastAck2Time = CTimer::getTime(); + + // Inform the server my configurations. + CPacket reqpkt; + reqpkt.setControl(UMSG_HANDSHAKE); + reqpkt.allocate(m_iMaxSRTPayloadSize); + // XXX NOTE: Now the memory for the payload part is allocated automatically, + // and such allocated memory is also automatically deallocated in the + // destructor. If you use CPacket::allocate, remember that you must not: + // - delete this memory + // - assign to m_pcData. + // If you use only manual assignment to m_pCData, this is then manual + // allocation and so it won't be deallocated in the destructor. + // + // (Desired would be to disallow modification of m_pcData outside the + // control of methods.) + + // ID = 0, connection request + reqpkt.m_iID = 0; + + size_t hs_size = m_iMaxSRTPayloadSize; + m_ConnReq.store_to(reqpkt.m_pcData, Ref(hs_size)); + + // Note that CPacket::allocate() sets also the size + // to the size of the allocated buffer, which not + // necessarily is to be the size of the data. + reqpkt.setLength(hs_size); + + uint64_t now = CTimer::getTime(); + reqpkt.m_iTimeStamp = int32_t(now - m_stats.startTime); + + HLOGC(mglog.Debug, + log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (" << now << "). SENDING HS: " << m_ConnReq.show()); + + /* + * Race condition if non-block connect response thread scheduled before we set m_bConnecting to true? + * Connect response will be ignored and connecting will wait until timeout. + * Maybe m_ConnectionLock handling problem? Not used in CUDT::connect(const CPacket& response) + */ + m_llLastReqTime = now; + m_bConnecting = true; + m_pSndQueue->sendto(serv_addr, reqpkt); + + // + /// + //// ---> CONTINUE TO: .CUDT::processConnectRequest() + /// (Take the part under condition: hs.m_iReqType == URQ_INDUCTION) + //// <--- RETURN WHEN: m_pSndQueue->sendto() is called. + //// .... SKIP UNTIL m_pRcvQueue->recvfrom() HERE.... + //// (the first "sendto" will not be called due to being too early) + /// + // + + // asynchronous connect, return immediately + if (!m_bSynRecving) + { + HLOGC(mglog.Debug, log << CONID() << "startConnect: ASYNC MODE DETECTED. Deferring the process to RcvQ:worker"); + return; + } + + // Wait for the negotiated configurations from the peer side. + + // This packet only prepares the storage where we will read the + // next incoming packet. + CPacket response; + response.setControl(UMSG_HANDSHAKE); + response.allocate(m_iMaxSRTPayloadSize); + + CUDTException e; + EConnectStatus cst = CONN_CONTINUE; + + while (!m_bClosing) + { + int64_t tdiff = CTimer::getTime() - m_llLastReqTime; + // avoid sending too many requests, at most 1 request per 250ms + + // SHORT VERSION: + // The immediate first run of this loop WILL SKIP THIS PART, so + // the processing really begins AFTER THIS CONDITION. + // + // Note that some procedures inside may set m_llLastReqTime to 0, + // which will result of this condition to trigger immediately in + // the next iteration. + if (tdiff > 250000) + { + HLOGC(mglog.Debug, + log << "startConnect: LOOP: time to send (" << tdiff << " > 250000). size=" << reqpkt.getLength()); + + if (m_bRendezvous) + reqpkt.m_iID = m_ConnRes.m_iID; + + now = CTimer::getTime(); +#if ENABLE_HEAVY_LOGGING + { + CHandShake debughs; + debughs.load_from(reqpkt.m_pcData, reqpkt.getLength()); + HLOGC(mglog.Debug, + log << CONID() << "startConnect: REQ-TIME HIGH (" << now + << "). cont/sending HS to peer: " << debughs.show()); + } +#endif + + m_llLastReqTime = now; + reqpkt.m_iTimeStamp = int32_t(now - m_stats.startTime); + m_pSndQueue->sendto(serv_addr, reqpkt); + } + else + { + HLOGC(mglog.Debug, log << "startConnect: LOOP: too early to send - " << tdiff << " < 250000"); + } + + cst = CONN_CONTINUE; + response.setLength(m_iMaxSRTPayloadSize); + if (m_pRcvQueue->recvfrom(m_SocketID, Ref(response)) > 0) + { + HLOGC(mglog.Debug, log << CONID() << "startConnect: got response for connect request"); + cst = processConnectResponse(response, &e, true /*synchro*/); + + HLOGC(mglog.Debug, log << CONID() << "startConnect: response processing result: " << ConnectStatusStr(cst)); + + // Expected is that: + // - the peer responded with URQ_INDUCTION + cookie. This above function + // should check that and craft the URQ_CONCLUSION handshake, in which + // case this function returns CONN_CONTINUE. As an extra action taken + // for that case, we set the SECURING mode if encryption requested, + // and serialize again the handshake, possibly together with HS extension + // blocks, if HSv5 peer responded. The serialized handshake will be then + // sent again, as the loop is repeated. + // - the peer responded with URQ_CONCLUSION. This handshake was accepted + // as a connection, and for >= HSv5 the HS extension blocks have been + // also read and interpreted. In this case this function returns: + // - CONN_ACCEPT, if everything was correct - break this loop and return normally + // - CONN_REJECT in case of any problems with the delivered handshake + // (incorrect data or data conflict) - throw error exception + // - the peer responded with any of URQ_ERROR_*. - throw error exception + // + // The error exception should make the API connect() function fail, if blocking + // or mark the failure for that socket in epoll, if non-blocking. + + if (cst == CONN_RENDEZVOUS) + { + // When this function returned CONN_RENDEZVOUS, this requires + // very special processing for the Rendezvous-v5 algorithm. This MAY + // involve also preparing a new handshake form, also interpreting the + // SRT handshake extension and crafting SRT handshake extension for the + // peer, which should be next sent. When this function returns CONN_CONTINUE, + // it means that it has done all that was required, however none of the below + // things has to be done (this function will do it by itself if needed). + // Otherwise the handshake rolling can be interrupted and considered complete. + cst = processRendezvous(Ref(reqpkt), response, serv_addr, true /*synchro*/, RST_OK); + if (cst == CONN_CONTINUE) + continue; + break; + } + + if (cst == CONN_REJECT) + sendCtrl(UMSG_SHUTDOWN); + + if (cst != CONN_CONTINUE && cst != CONN_CONFUSED) + break; // --> OUTSIDE-LOOP + + // IMPORTANT + // [[using assert(m_pCryptoControl != nullptr)]]; + + // new request/response should be sent out immediately on receving a response + HLOGC(mglog.Debug, + log << "startConnect: SYNC CONNECTION STATUS:" << ConnectStatusStr(cst) << ", REQ-TIME: LOW."); + m_llLastReqTime = 0; + + // Now serialize the handshake again to the existing buffer so that it's + // then sent later in this loop. + + // First, set the size back to the original size, m_iMaxSRTPayloadSize because + // this is the size of the originally allocated space. It might have been + // shrunk by serializing the INDUCTION handshake (which was required before + // sending this packet to the output queue) and therefore be too + // small to store the CONCLUSION handshake (with HSv5 extensions). + reqpkt.setLength(m_iMaxSRTPayloadSize); + + HLOGC(mglog.Debug, log << "startConnect: creating HS CONCLUSION: buffer size=" << reqpkt.getLength()); + + // NOTE: BUGFIX: SERIALIZE AGAIN. + // The original UDT code didn't do it, so it was theoretically + // turned into conclusion, but was sending still the original + // induction handshake challenge message. It was working only + // thanks to that simultaneously there were being sent handshake + // messages from a separate thread (CSndQueue::worker) from + // RendezvousQueue, this time serialized properly, which caused + // that with blocking mode there was a kinda initial "drunk + // passenger with taxi driver talk" until the RendezvousQueue sends + // (when "the time comes") the right CONCLUSION handshake + // challenge message. + // + // Now that this is fixed, the handshake messages from RendezvousQueue + // are sent only when there is a rendezvous mode or non-blocking mode. + if (!createSrtHandshake(Ref(reqpkt), Ref(m_ConnReq), SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0)) + { + LOGC(mglog.Error, log << "createSrtHandshake failed - REJECTING."); + cst = CONN_REJECT; + break; + } + // These last 2 parameters designate the buffer, which is in use only for SRT_CMD_KMRSP. + // If m_ConnReq.m_iVersion == HS_VERSION_UDT4, this function will do nothing, + // except just serializing the UDT handshake. + // The trick is that the HS challenge is with version HS_VERSION_UDT4, but the + // listener should respond with HS_VERSION_SRT1, if it is HSv5 capable. + } + + HLOGC(mglog.Debug, + log << "startConnect: timeout from Q:recvfrom, looping again; cst=" << ConnectStatusStr(cst)); + +#if ENABLE_HEAVY_LOGGING + // Non-fatal assertion + if (cst == CONN_REJECT) // Might be returned by processRendezvous + { + LOGC(mglog.Error, + log << "startConnect: IPE: cst=REJECT NOT EXPECTED HERE, the loop should've been interrupted!"); + break; + } +#endif + + if (CTimer::getTime() > ttl) + { + // timeout + e = CUDTException(MJ_SETUP, MN_TIMEOUT, 0); + break; + } + } + + // <--- OUTSIDE-LOOP + // Here will fall the break when not CONN_CONTINUE. + // CONN_RENDEZVOUS is handled by processRendezvous. + // CONN_ACCEPT will skip this and pass on. + if (cst == CONN_REJECT) + { + e = CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + + if (e.getErrorCode() == 0) + { + if (m_bClosing) // if the socket is closed before connection... + e = CUDTException(MJ_SETUP); // XXX NO MN ? + else if (m_ConnRes.m_iReqType > URQ_FAILURE_TYPES) // connection request rejected + { + m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType); + e = CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + else if ((!m_bRendezvous) && (m_ConnRes.m_iISN != m_iISN)) // secuity check + e = CUDTException(MJ_SETUP, MN_SECURITY, 0); + } + + if (e.getErrorCode() != 0) + { + m_bConnecting = false; + // The process is to be abnormally terminated, remove the connector + // now because most likely no other processing part has done anything with it. + m_pRcvQueue->removeConnector(m_SocketID); + throw e; + } + + HLOGC(mglog.Debug, log << CONID() << "startConnect: handshake exchange succeeded"); + + // Parameters at the end. + HLOGC(mglog.Debug, + log << "startConnect: END. Parameters:" + " mss=" + << m_iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() + << " cwnd-size=" << m_CongCtl->cgWindowSize() << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); +} + +// Asynchronous connection +EConnectStatus CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NOEXCEPT +{ + EConnectStatus cst = CONN_CONTINUE; + CUDTException e; + + CGuard cg(m_ConnectionLock); // FIX + HLOGC(mglog.Debug, log << CONID() << "processAsyncConnectResponse: got response for connect request, processing"); + cst = processConnectResponse(pkt, &e, false); + + HLOGC(mglog.Debug, + log << CONID() << "processAsyncConnectResponse: response processing result: " << ConnectStatusStr(cst) + << "REQ-TIME LOW to enforce immediate response"); + m_llLastReqTime = 0; + + return cst; +} + +bool CUDT::processAsyncConnectRequest(EReadStatus rst, + EConnectStatus cst, + const CPacket & response, + const sockaddr *serv_addr) +{ + // IMPORTANT! + + // This function is called, still asynchronously, but in the order + // of call just after the call to the above processAsyncConnectResponse. + // This should have got the original value returned from + // processConnectResponse through processAsyncConnectResponse. + + CPacket request; + request.setControl(UMSG_HANDSHAKE); + request.allocate(m_iMaxSRTPayloadSize); + uint64_t now = CTimer::getTime(); + request.m_iTimeStamp = int(now - m_stats.startTime); + + HLOGC(mglog.Debug, + log << "processAsyncConnectRequest: REQ-TIME: HIGH (" << now << "). Should prevent too quick responses."); + m_llLastReqTime = now; + // ID = 0, connection request + request.m_iID = !m_bRendezvous ? 0 : m_ConnRes.m_iID; + + bool status = true; + + if (cst == CONN_RENDEZVOUS) + { + HLOGC(mglog.Debug, log << "processAsyncConnectRequest: passing to processRendezvous"); + cst = processRendezvous(Ref(request), response, serv_addr, false /*asynchro*/, rst); + if (cst == CONN_ACCEPT) + { + HLOGC(mglog.Debug, + log << "processAsyncConnectRequest: processRendezvous completed the process and responded by itself. " + "Done."); + return true; + } + + if (cst != CONN_CONTINUE) + { + // processRendezvous already set the reject reason + LOGC(mglog.Error, + log << "processAsyncConnectRequest: REJECT reported from processRendezvous, not processing further."); + status = false; + } + } + else if (cst == CONN_REJECT) + { + // m_RejectReason already set at worker_ProcessAddressedPacket. + LOGC(mglog.Error, + log << "processAsyncConnectRequest: REJECT reported from HS processing, not processing further."); + return false; + } + else + { + // (this procedure will be also run for HSv4 rendezvous) + HLOGC(mglog.Debug, log << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength()); + if (!createSrtHandshake(Ref(request), Ref(m_ConnReq), SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0)) + { + // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired". + LOGC(mglog.Error, log << "IPE: processAsyncConnectRequest: createSrtHandshake failed, dismissing."); + status = false; + } + else + { + HLOGC(mglog.Debug, + log << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType) + << " to socket " << request.m_iID << " size=" << request.getLength()); + } + } + + if (!status) + { + return false; + /* XXX Shouldn't it send a single response packet for the rejection? + // Set the version to 0 as "handshake rejection" status and serialize it + CHandShake zhs; + size_t size = request.getLength(); + zhs.store_to(request.m_pcData, Ref(size)); + request.setLength(size); + */ + } + + HLOGC(mglog.Debug, log << "processAsyncConnectRequest: sending request packet, setting REQ-TIME HIGH."); + m_llLastReqTime = CTimer::getTime(); + m_pSndQueue->sendto(serv_addr, request); + return status; +} + +void CUDT::cookieContest() +{ + if (m_SrtHsSide != HSD_DRAW) + return; + + HLOGC(mglog.Debug, log << "cookieContest: agent=" << m_ConnReq.m_iCookie << " peer=" << m_ConnRes.m_iCookie); + + if (m_ConnReq.m_iCookie == 0 || m_ConnRes.m_iCookie == 0) + { + // Note that it's virtually impossible that Agent's cookie is not ready, this + // shall be considered IPE. + // Not all cookies are ready, don't start the contest. + return; + } + + // INITIATOR/RESPONDER role is resolved by COOKIE CONTEST. + // + // The cookie contest must be repeated every time because it + // may change the state at some point. + int better_cookie = m_ConnReq.m_iCookie - m_ConnRes.m_iCookie; + + if (better_cookie > 0) + { + m_SrtHsSide = HSD_INITIATOR; + return; + } + + if (better_cookie < 0) + { + m_SrtHsSide = HSD_RESPONDER; + return; + } + + // DRAW! The only way to continue would be to force the + // cookies to be regenerated and to start over. But it's + // not worth a shot - this is an extremely rare case. + // This can simply do reject so that it can be started again. + + // Pretend then that the cookie contest wasn't done so that + // it's done again. Cookies are baked every time anew, however + // the successful initial contest remains valid no matter how + // cookies will change. + + m_SrtHsSide = HSD_DRAW; +} + +EConnectStatus CUDT::processRendezvous( + ref_t reqpkt, const CPacket &response, const sockaddr *serv_addr, bool synchro, EReadStatus rst) +{ + if (m_RdvState == CHandShake::RDV_CONNECTED) + { + HLOGC(mglog.Debug, log << "processRendezvous: already in CONNECTED state."); + return CONN_ACCEPT; + } + + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + CPacket &rpkt = *reqpkt; + + cookieContest(); + + // We know that the other side was contacted and the other side has sent + // the handshake message - we know then both cookies. If it's a draw, it's + // a very rare case of creating identical cookies. + if (m_SrtHsSide == HSD_DRAW) + { + m_RejectReason = SRT_REJ_RDVCOOKIE; + LOGC(mglog.Error, + log << "COOKIE CONTEST UNRESOLVED: can't assign connection roles, please wait another minute."); + return CONN_REJECT; + } + + UDTRequestType rsp_type = URQ_FAILURE_TYPES; // just to track uninitialized errors + + // We can assume that the Handshake packet received here as 'response' + // is already serialized in m_ConnRes. Check extra flags that are meaningful + // for further processing here. + + int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + bool needs_extension = ext_flags != 0; // Initial value: received HS has extensions. + bool needs_hsrsp; + rendezvousSwitchState(Ref(rsp_type), Ref(needs_extension), Ref(needs_hsrsp)); + if (rsp_type > URQ_FAILURE_TYPES) + { + m_RejectReason = RejectReasonForURQ(rsp_type); + HLOGC(mglog.Debug, + log << "processRendezvous: rejecting due to switch-state response: " << RequestTypeStr(rsp_type)); + return CONN_REJECT; + } + checkUpdateCryptoKeyLen("processRendezvous", m_ConnRes.m_iType); + + // We have three possibilities here as it comes to HSREQ extensions: + + // 1. The agent is loser in attention state, it sends EMPTY conclusion (without extensions) + // 2. The agent is loser in initiated state, it interprets incoming HSREQ and creates HSRSP + // 3. The agent is winner in attention or fine state, it sends HSREQ extension + m_ConnReq.m_iReqType = rsp_type; + m_ConnReq.m_extension = needs_extension; + + // This must be done before prepareConnectionObjects(). + applyResponseSettings(); + + // This must be done before interpreting and creating HSv5 extensions. + if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, 0)) + { + // m_RejectReason already handled + HLOGC(mglog.Debug, log << "processRendezvous: rejecting due to problems in prepareConnectionObjects."); + return CONN_REJECT; + } + + // Case 2. + if (needs_hsrsp) + { + // This means that we have received HSREQ extension with the handshake, so we need to interpret + // it and craft the response. + if (rst == RST_OK) + { + // We have JUST RECEIVED packet in this session (not that this is called as periodic update). + // Sanity check + m_llLastReqTime = 0; + if (response.getLength() == size_t(-1)) + { + m_RejectReason = SRT_REJ_IPE; + LOGC(mglog.Fatal, + log << "IPE: rst=RST_OK, but the packet has set -1 length - REJECTING (REQ-TIME: LOW)"); + return CONN_REJECT; + } + + if (!interpretSrtHandshake(m_ConnRes, response, kmdata, &kmdatasize)) + { + HLOGC(mglog.Debug, + log << "processRendezvous: rejecting due to problems in interpretSrtHandshake REQ-TIME: LOW."); + return CONN_REJECT; + } + + // Pass on, inform about the shortened response-waiting period. + HLOGC(mglog.Debug, log << "processRendezvous: setting REQ-TIME: LOW. Forced to respond immediately."); + } + else + { + // If the last CONCLUSION message didn't contain the KMX extension, there's + // no key recorded yet, so it can't be extracted. Mark this kmdatasize empty though. + int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + if (IsSet(hs_flags, CHandShake::HS_EXT_KMREQ)) + { + // This is a periodic handshake update, so you need to extract the KM data from the + // first message, provided that it is there. + size_t msgsize = m_pCryptoControl->getKmMsg_size(0); + if (msgsize == 0) + { + switch (m_pCryptoControl->m_RcvKmState) + { + // If the KMX process ended up with a failure, the KMX is not recorded. + // In this case as the KMRSP answer the "failure status" should be crafted. + case SRT_KM_S_NOSECRET: + case SRT_KM_S_BADSECRET: + { + HLOGC(mglog.Debug, + log << "processRendezvous: No KMX recorded, status = NOSECRET. Respond with NOSECRET."); + + // Just do the same thing as in CCryptoControl::processSrtMsg_KMREQ for that case, + // that is, copy the NOSECRET code into KMX message. + memcpy(kmdata, &m_pCryptoControl->m_RcvKmState, sizeof(int32_t)); + kmdatasize = 1; + } + break; + + default: + // Remaining values: + // UNSECURED: should not fall here at alll + // SECURING: should not happen in HSv5 + // SECURED: should have received the recorded KMX correctly (getKmMsg_size(0) > 0) + { + m_RejectReason = SRT_REJ_IPE; + // Remaining situations: + // - password only on this site: shouldn't be considered to be sent to a no-password site + LOGC(mglog.Error, + log << "processRendezvous: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" + << KmStateStr(m_pCryptoControl->m_RcvKmState) + << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState)); + return CONN_REJECT; + } + break; + } + } + else + { + kmdatasize = msgsize / 4; + if (msgsize > kmdatasize * 4) + { + // Sanity check + LOGC(mglog.Error, log << "IPE: KMX data not aligned to 4 bytes! size=" << msgsize); + memset(kmdata + (kmdatasize * 4), 0, msgsize - (kmdatasize * 4)); + ++kmdatasize; + } + + HLOGC(mglog.Debug, + log << "processRendezvous: getting KM DATA from the fore-recorded KMX from KMREQ, size=" + << kmdatasize); + memcpy(kmdata, m_pCryptoControl->getKmMsg_data(0), msgsize); + } + } + else + { + HLOGC(mglog.Debug, log << "processRendezvous: no KMX flag - not extracting KM data for KMRSP"); + kmdatasize = 0; + } + } + + // No matter the value of needs_extension, the extension is always needed + // when HSREQ was interpreted (to store HSRSP extension). + m_ConnReq.m_extension = true; + + HLOGC(mglog.Debug, + log << "processRendezvous: HSREQ extension ok, creating HSRSP response. kmdatasize=" << kmdatasize); + + rpkt.setLength(m_iMaxSRTPayloadSize); + if (!createSrtHandshake(reqpkt, Ref(m_ConnReq), SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize)) + { + HLOGC(mglog.Debug, + log << "processRendezvous: rejecting due to problems in createSrtHandshake. REQ-TIME: LOW"); + m_llLastReqTime = 0; + return CONN_REJECT; + } + + // This means that it has received URQ_CONCLUSION with HSREQ, agent is then in RDV_FINE + // state, it sends here URQ_CONCLUSION with HSREQ/KMREQ extensions and it awaits URQ_AGREEMENT. + return CONN_CONTINUE; + } + + // Special case: if URQ_AGREEMENT is to be sent, when this side is INITIATOR, + // then it must have received HSRSP, so it must interpret it. Otherwise it would + // end up with URQ_DONE, which means that it is the other side to interpret HSRSP. + if (m_SrtHsSide == HSD_INITIATOR && m_ConnReq.m_iReqType == URQ_AGREEMENT) + { + // The same is done in CUDT::postConnect(), however this section will + // not be done in case of rendezvous. The section in postConnect() is + // predicted to run only in regular CALLER handling. + + if (rst != RST_OK || response.getLength() == size_t(-1)) + { + // Actually the -1 length would be an IPE, but it's likely that this was reported already. + HLOGC( + mglog.Debug, + log << "processRendezvous: no INCOMING packet, NOT interpreting extensions (relying on exising data)"); + } + else + { + HLOGC(mglog.Debug, + log << "processRendezvous: INITIATOR, will send AGREEMENT - interpreting HSRSP extension"); + if (!interpretSrtHandshake(m_ConnRes, response, 0, 0)) + { + // m_RejectReason is already set, so set the reqtype accordingly + m_ConnReq.m_iReqType = URQFailure(m_RejectReason); + } + } + // This should be false, make a kinda assert here. + if (needs_extension) + { + LOGC(mglog.Fatal, log << "IPE: INITIATOR responding AGREEMENT should declare no extensions to HS"); + m_ConnReq.m_extension = false; + } + } + + HLOGC(mglog.Debug, + log << CONID() << "processRendezvous: COOKIES Agent/Peer: " << m_ConnReq.m_iCookie << "/" + << m_ConnRes.m_iCookie << " HSD:" << (m_SrtHsSide == HSD_INITIATOR ? "initiator" : "responder") + << " STATE:" << CHandShake::RdvStateStr(m_RdvState) << " ..."); + + if (rsp_type == URQ_DONE) + { + HLOGC(mglog.Debug, log << "... WON'T SEND any response, both sides considered connected"); + } + else + { + HLOGC(mglog.Debug, + log << "... WILL SEND " << RequestTypeStr(rsp_type) << " " << (m_ConnReq.m_extension ? "with" : "without") + << " SRT HS extensions"); + } + + // This marks the information for the serializer that + // the SRT handshake extension is required. + // Rest of the data will be filled together with + // serialization. + m_ConnReq.m_extension = needs_extension; + + rpkt.setLength(m_iMaxSRTPayloadSize); + if (m_RdvState == CHandShake::RDV_CONNECTED) + { + // When synchro=false, don't lock a mutex for rendezvous queue. + // This is required when this function is called in the + // receive queue worker thread - it would lock itself. + int cst = postConnect(response, true, 0, synchro); + if (cst == CONN_REJECT) + { + // m_RejectReason already set + HLOGC(mglog.Debug, log << "processRendezvous: rejecting due to problems in postConnect."); + return CONN_REJECT; + } + } + + // URQ_DONE or URQ_AGREEMENT can be the result if the state is RDV_CONNECTED. + // If URQ_DONE, then there's nothing to be done, when URQ_AGREEMENT then return + // CONN_CONTINUE to make the caller send again the contents if the packet buffer, + // this time with URQ_AGREEMENT message, but still consider yourself connected. + if (rsp_type == URQ_DONE) + { + HLOGC(mglog.Debug, log << "processRendezvous: rsp=DONE, reporting ACCEPT (nothing to respond)"); + return CONN_ACCEPT; + } + + // createSrtHandshake moved here because if the above conditions are satisfied, + // no response is going to be send, so nothing needs to be "created". + + // needs_extension here distinguishes between cases 1 and 3. + // NOTE: in case when interpretSrtHandshake was run under the conditions above (to interpret HSRSP), + // then createSrtHandshake below will create only empty AGREEMENT message. + if (!createSrtHandshake(reqpkt, Ref(m_ConnReq), SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0)) + { + // m_RejectReason already set + LOGC(mglog.Error, log << "createSrtHandshake failed (IPE?), connection rejected. REQ-TIME: LOW"); + m_llLastReqTime = 0; + return CONN_REJECT; + } + + if (rsp_type == URQ_AGREEMENT && m_RdvState == CHandShake::RDV_CONNECTED) + { + // We are using our own serialization method (not the one called after + // processConnectResponse, this is skipped in case when this function + // is called), so we can also send this immediately. Agreement must be + // sent just once and the party must switch into CONNECTED state - in + // contrast to CONCLUSION messages, which should be sent in loop repeatedly. + // + // Even though in theory the AGREEMENT message sent just once may miss + // the target (as normal thing in UDP), this is little probable to happen, + // and this doesn't matter much because even if the other party doesn't + // get AGREEMENT, but will get payload or KEEPALIVE messages, it will + // turn into connected state as well. The AGREEMENT is rather kinda + // catalyzer here and may turn the entity on the right track faster. When + // AGREEMENT is missed, it may have kinda initial tearing. + + const uint64_t now = CTimer::getTime(); + m_llLastReqTime = now; + rpkt.m_iTimeStamp = int32_t(now - m_stats.startTime); + HLOGC(mglog.Debug, + log << "processRendezvous: rsp=AGREEMENT, reporting ACCEPT and sending just this one, REQ-TIME HIGH (" + << now << ")."); + + m_pSndQueue->sendto(serv_addr, rpkt); + + return CONN_ACCEPT; + } + + if (rst == RST_OK) + { + // the request time must be updated so that the next handshake can be sent out immediately + HLOGC(mglog.Debug, + log << "processRendezvous: rsp=" << RequestTypeStr(m_ConnReq.m_iReqType) + << " REQ-TIME: LOW to send immediately, consider yourself conencted"); + m_llLastReqTime = 0; + } + else + { + HLOGC(mglog.Debug, log << "processRendezvous: REQ-TIME: remains previous value, consider yourself connected"); + } + return CONN_CONTINUE; +} + +EConnectStatus CUDT::processConnectResponse(const CPacket &response, CUDTException *eout, bool synchro) ATR_NOEXCEPT +{ + // NOTE: ASSUMED LOCK ON: m_ConnectionLock. + + // this is the 2nd half of a connection request. If the connection is setup successfully this returns 0. + // Returned values: + // - CONN_REJECT: there was some error when processing the response, connection should be rejected + // - CONN_ACCEPT: the handshake is done and finished correctly + // - CONN_CONTINUE: the induction handshake has been processed correctly, and expects CONCLUSION handshake + + if (!m_bConnecting) + return CONN_REJECT; + + // This is required in HSv5 rendezvous, in which it should send the URQ_AGREEMENT message to + // the peer, however switch to connected state. + HLOGC(mglog.Debug, + log << "processConnectResponse: TYPE:" + << (response.isControl() ? MessageTypeStr(response.getType(), response.getExtendedType()) + : string("DATA"))); + // ConnectStatus res = CONN_REJECT; // used later for status - must be declared here due to goto POST_CONNECT. + + // For HSv4, the data sender is INITIATOR, and the data receiver is RESPONDER, + // regardless of the connecting side affiliation. This will be changed for HSv5. + bool bidirectional = false; + HandshakeSide hsd = m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER; + // (defined here due to 'goto' below). + + // SRT peer may send the SRT handshake private message (type 0x7fff) before a keep-alive. + + // This condition is checked when the current agent is trying to do connect() in rendezvous mode, + // but the peer was faster to send a handshake packet earlier. This makes it continue with connecting + // process if the peer is already behaving as if the connection was already established. + + // This value will check either the initial value, which is less than SRT1, or + // the value previously loaded to m_ConnReq during the previous handshake response. + // For the initial form this value should not be checked. + bool hsv5 = m_ConnRes.m_iVersion >= HS_VERSION_SRT1; + + if (m_bRendezvous && + (m_RdvState == CHandShake::RDV_CONNECTED // somehow Rendezvous-v5 switched it to CONNECTED. + || !response.isControl() // WAS A PAYLOAD PACKET. + || (response.getType() == UMSG_KEEPALIVE) // OR WAS A UMSG_KEEPALIVE message. + || (response.getType() == UMSG_EXT) // OR WAS a CONTROL packet of some extended type (i.e. any SRT specific) + ) + // This may happen if this is an initial state in which the socket type was not yet set. + // If this is a field that holds the response handshake record from the peer, this means that it wasn't received + // yet. HSv5: added version check because in HSv5 the m_iType field has different meaning and it may be 0 in + // case when the handshake does not carry SRT extensions. + && (hsv5 || m_ConnRes.m_iType != UDT_UNDEFINED)) + { + // a data packet or a keep-alive packet comes, which means the peer side is already connected + // in this situation, the previously recorded response will be used + // In HSv5 this situation is theoretically possible if this party has missed the URQ_AGREEMENT message. + HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: already connected - pinning in"); + if (hsv5) + { + m_RdvState = CHandShake::RDV_CONNECTED; + } + + return postConnect(response, hsv5, eout, synchro); + } + + if (!response.isControl(UMSG_HANDSHAKE)) + { + m_RejectReason = SRT_REJ_ROGUE; + if (!response.isControl()) + { + LOGC(mglog.Error, log << CONID() << "processConnectResponse: received DATA while HANDSHAKE expected"); + } + else + { + LOGC(mglog.Error, + log << CONID() + << "processConnectResponse: CONFUSED: expected UMSG_HANDSHAKE as connection not yet established, " + "got: " + << MessageTypeStr(response.getType(), response.getExtendedType())); + } + return CONN_CONFUSED; + } + + if (m_ConnRes.load_from(response.m_pcData, response.getLength()) == -1) + { + m_RejectReason = SRT_REJ_ROGUE; + // Handshake data were too small to reach the Handshake structure. Reject. + LOGC(mglog.Error, + log << CONID() + << "processConnectResponse: HANDSHAKE data buffer too small - possible blueboxing. Rejecting."); + return CONN_REJECT; + } + + HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: HS RECEIVED: " << m_ConnRes.show()); + if (m_ConnRes.m_iReqType > URQ_FAILURE_TYPES) + { + m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType); + return CONN_REJECT; + } + + if (size_t(m_ConnRes.m_iMSS) > CPacket::ETH_MAX_MTU_SIZE) + { + // Yes, we do abort to prevent buffer overrun. Set your MSS correctly + // and you'll avoid problems. + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Fatal, log << "MSS size " << m_iMSS << "exceeds MTU size!"); + return CONN_REJECT; + } + + // (see createCrypter() call below) + // + // The CCryptoControl attached object must be created early + // because it will be required to create a conclusion handshake in HSv5 + // + if (m_bRendezvous) + { + // SANITY CHECK: A rendezvous socket should reject any caller requests (it's not a listener) + if (m_ConnRes.m_iReqType == URQ_INDUCTION) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, + log << CONID() + << "processConnectResponse: Rendezvous-point received INDUCTION handshake (expected WAVEAHAND). " + "Rejecting."); + return CONN_REJECT; + } + + // The procedure for version 5 is completely different and changes the states + // differently, so the old code will still maintain HSv4 the old way. + + if (m_ConnRes.m_iVersion > HS_VERSION_UDT4) + { + HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: Rendezvous HSv5 DETECTED."); + return CONN_RENDEZVOUS; // --> will continue in CUDT::processRendezvous(). + } + + HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: Rendsezvous HSv4 DETECTED."); + // So, here it has either received URQ_WAVEAHAND handshake message (while it should be in URQ_WAVEAHAND itself) + // or it has received URQ_CONCLUSION/URQ_AGREEMENT message while this box has already sent URQ_WAVEAHAND to the + // peer, and DID NOT send the URQ_CONCLUSION yet. + + if (m_ConnReq.m_iReqType == URQ_WAVEAHAND || m_ConnRes.m_iReqType == URQ_WAVEAHAND) + { + HLOGC(mglog.Debug, + log << CONID() << "processConnectResponse: REQ-TIME LOW. got HS RDV. Agent state:" + << RequestTypeStr(m_ConnReq.m_iReqType) << " Peer HS:" << m_ConnRes.show()); + + // Here we could have received WAVEAHAND or CONCLUSION. + // For HSv4 simply switch to CONCLUSION for the sake of further handshake rolling. + // For HSv5, make the cookie contest and basing on this decide, which party + // should provide the HSREQ/KMREQ attachment. + + if (!createCrypter(hsd, false /* unidirectional */)) + { + m_RejectReason = SRT_REJ_RESOURCE; + m_ConnReq.m_iReqType = URQFailure(SRT_REJ_RESOURCE); + // the request time must be updated so that the next handshake can be sent out immediately. + m_llLastReqTime = 0; + return CONN_REJECT; + } + + m_ConnReq.m_iReqType = URQ_CONCLUSION; + // the request time must be updated so that the next handshake can be sent out immediately. + m_llLastReqTime = 0; + return CONN_CONTINUE; + } + else + { + HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: Rendezvous HSv4 PAST waveahand"); + } + } + else + { + // set cookie + if (m_ConnRes.m_iReqType == URQ_INDUCTION) + { + HLOGC(mglog.Debug, + log << CONID() << "processConnectResponse: REQ-TIME LOW; got INDUCTION HS response (cookie:" << hex + << m_ConnRes.m_iCookie << " version:" << dec << m_ConnRes.m_iVersion + << "), sending CONCLUSION HS with this cookie"); + + m_ConnReq.m_iCookie = m_ConnRes.m_iCookie; + m_ConnReq.m_iReqType = URQ_CONCLUSION; + + // Here test if the LISTENER has responded with version HS_VERSION_SRT1, + // it means that it is HSv5 capable. It can still accept the HSv4 handshake. + if (m_ConnRes.m_iVersion > HS_VERSION_UDT4) + { + int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + + if (hs_flags != SrtHSRequest::SRT_MAGIC_CODE) + { + LOGC(mglog.Warn, log << "processConnectResponse: Listener HSv5 did not set the SRT_MAGIC_CODE"); + } + + checkUpdateCryptoKeyLen("processConnectResponse", m_ConnRes.m_iType); + + // This will catch HS_VERSION_SRT1 and any newer. + // Set your highest version. + m_ConnReq.m_iVersion = HS_VERSION_SRT1; + // CONTROVERSIAL: use 0 as m_iType according to the meaning in HSv5. + // The HSv4 client might not understand it, which means that agent + // must switch itself to HSv4 rendezvous, and this time iType sould + // be set to UDT_DGRAM value. + m_ConnReq.m_iType = 0; + + // This marks the information for the serializer that + // the SRT handshake extension is required. + // Rest of the data will be filled together with + // serialization. + m_ConnReq.m_extension = true; + + // For HSv5, the caller is INITIATOR and the listener is RESPONDER. + // The m_bDataSender value should be completely ignored and the + // connection is always bidirectional. + bidirectional = true; + hsd = HSD_INITIATOR; + } + m_llLastReqTime = 0; + if (!createCrypter(hsd, bidirectional)) + { + m_RejectReason = SRT_REJ_RESOURCE; + return CONN_REJECT; + } + // NOTE: This setup sets URQ_CONCLUSION and appropriate data in the handshake structure. + // The full handshake to be sent will be filled back in the caller function -- CUDT::startConnect(). + return CONN_CONTINUE; + } + } + + return postConnect(response, false, eout, synchro); +} + +void CUDT::applyResponseSettings() +{ + // Re-configure according to the negotiated values. + m_iMSS = m_ConnRes.m_iMSS; + m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; + int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE; + m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; + m_iPeerISN = m_ConnRes.m_iISN; + m_iRcvLastAck = m_ConnRes.m_iISN; +#ifdef ENABLE_LOGGING + m_iDebugPrevLastAck = m_iRcvLastAck; +#endif + m_iRcvLastSkipAck = m_iRcvLastAck; + m_iRcvLastAckAck = m_ConnRes.m_iISN; + m_iRcvCurrSeqNo = m_ConnRes.m_iISN - 1; + m_iRcvCurrPhySeqNo = m_ConnRes.m_iISN - 1; + m_PeerID = m_ConnRes.m_iID; + memcpy(m_piSelfIP, m_ConnRes.m_piPeerIP, 16); + + HLOGC(mglog.Debug, + log << CONID() << "applyResponseSettings: HANSHAKE CONCLUDED. SETTING: payload-size=" << m_iMaxSRTPayloadSize + << " mss=" << m_ConnRes.m_iMSS << " flw=" << m_ConnRes.m_iFlightFlagSize << " isn=" << m_ConnRes.m_iISN + << " peerID=" << m_ConnRes.m_iID); +} + +EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout, bool synchro) +{ + if (m_ConnRes.m_iVersion < HS_VERSION_SRT1) + m_ullRcvPeerStartTime = 0; // will be set correctly in SRT HS. + + // This procedure isn't being executed in rendezvous because + // in rendezvous it's completed before calling this function. + if (!rendezvous) + { + // NOTE: THIS function must be called before calling prepareConnectionObjects. + // The reason why it's not part of prepareConnectionObjects is that the activities + // done there are done SIMILAR way in acceptAndRespond, which also calls this + // function. In fact, prepareConnectionObjects() represents the code that was + // done separately in processConnectResponse() and acceptAndRespond(), so this way + // this code is now common. Now acceptAndRespond() does "manually" something similar + // to applyResponseSettings(), just a little bit differently. This SHOULD be made + // common as a part of refactoring job, just needs a bit more time. + // + // Currently just this function must be called always BEFORE prepareConnectionObjects + // everywhere except acceptAndRespond(). + applyResponseSettings(); + + // This will actually be done also in rendezvous HSv4, + // however in this case the HSREQ extension will not be attached, + // so it will simply go the "old way". + bool ok = prepareConnectionObjects(m_ConnRes, m_SrtHsSide, eout); + // May happen that 'response' contains a data packet that was sent in rendezvous mode. + // In this situation the interpretation of handshake was already done earlier. + if (ok && response.isControl()) + { + ok = interpretSrtHandshake(m_ConnRes, response, 0, 0); + if (!ok && eout) + { + *eout = CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + } + if (!ok) // m_RejectReason already set + return CONN_REJECT; + } + + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP); + if (m_pCache->lookup(&ib) >= 0) + { + m_iRTT = ib.m_iRTT; + m_iBandwidth = ib.m_iBandwidth; + } + + SRT_REJECT_REASON rr = setupCC(); + if (rr != SRT_REJ_UNKNOWN) + { + m_RejectReason = rr; + return CONN_REJECT; + } + + // And, I am connected too. + m_bConnecting = false; + m_bConnected = true; + + // register this socket for receiving data packets + m_pRNode->m_bOnList = true; + m_pRcvQueue->setNewEntry(this); + + // XXX Problem around CONN_CONFUSED! + // If some too-eager packets were received from a listener + // that thinks it's connected, but his last handshake was missed, + // they are collected by CRcvQueue::storePkt. The removeConnector + // function will want to delete them all, so it would be nice + // if these packets can be re-delivered. Of course the listener + // should be prepared to resend them (as every packet can be lost + // on UDP), but it's kinda overkill when we have them already and + // can dispatch them. + + // Remove from rendezvous queue (in this particular case it's + // actually removing the socket that undergoes asynchronous HS processing). + // Removing at THIS point because since when setNewEntry is called, + // the next iteration in the CRcvQueue::worker loop will be dispatching + // packets normally, as within-connection, so the "connector" won't + // play any role since this time. + // The connector, however, must stay alive until the setNewEntry is called + // because otherwise the packets that are coming for this socket before the + // connection process is complete will be rejected as "attack", instead of + // being enqueued for later pickup from the queue. + m_pRcvQueue->removeConnector(m_SocketID, synchro); + + // acknowledge the management module. + CUDTSocket* s = s_UDTUnited.locate(m_SocketID); + if (!s) + { + if (eout) + { + *eout = CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + } + + m_RejectReason = SRT_REJ_CLOSE; + return CONN_REJECT; + } + + // copy address information of local node + // the local port must be correctly assigned BEFORE CUDT::startConnect(), + // otherwise if startConnect() fails, the multiplexer cannot be located + // by garbage collection and will cause leak + s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); + CIPAddress::pton(s->m_pSelfAddr, s->m_pUDT->m_piSelfIP, s->m_iIPversion); + + s->m_Status = SRTS_CONNECTED; + + // acknowledde any waiting epolls to write + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + + LOGC(mglog.Note, log << "Connection established to: " << SockaddrToString(m_pPeerAddr)); + + return CONN_ACCEPT; +} + +void CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t typefield) +{ + int enc_flags = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(typefield); + + // potentially 0-7 values are possible. + // When 0, don't change anything - it should rely on the value 0. + // When 1, 5, 6, 7, this is kinda internal error - ignore. + if (enc_flags >= 2 && enc_flags <= 4) // 2 = 128, 3 = 192, 4 = 256 + { + int rcv_pbkeylen = SrtHSRequest::SRT_PBKEYLEN_BITS::wrap(enc_flags); + if (m_iSndCryptoKeyLen == 0) + { + m_iSndCryptoKeyLen = rcv_pbkeylen; + HLOGC(mglog.Debug, log << loghdr << ": PBKEYLEN adopted from advertised value: " << m_iSndCryptoKeyLen); + } + else if (m_iSndCryptoKeyLen != rcv_pbkeylen) + { + // Conflict. Use SRTO_SENDER flag to check if this side should accept + // the enforcement, otherwise simply let it win. + if (!m_bDataSender) + { + LOGC(mglog.Warn, + log << loghdr << ": PBKEYLEN conflict - OVERRIDDEN " << m_iSndCryptoKeyLen << " by " + << rcv_pbkeylen << " from PEER (as AGENT is not SRTO_SENDER)"); + m_iSndCryptoKeyLen = rcv_pbkeylen; + } + else + { + LOGC(mglog.Warn, + log << loghdr << ": PBKEYLEN conflict - keep " << m_iSndCryptoKeyLen + << "; peer-advertised PBKEYLEN " << rcv_pbkeylen << " rejected because Agent is SRTO_SENDER"); + } + } + } + else if (enc_flags != 0) + { + LOGC(mglog.Error, log << loghdr << ": IPE: enc_flags outside allowed 2, 3, 4: " << enc_flags); + } + else + { + HLOGC(mglog.Debug, log << loghdr << ": No encryption flags found in type field: " << typefield); + } +} + +// Rendezvous +void CUDT::rendezvousSwitchState(ref_t rsptype, ref_t needs_extension, ref_t needs_hsrsp) +{ + UDTRequestType req = m_ConnRes.m_iReqType; + int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + bool has_extension = !!hs_flags; // it holds flags, if no flags, there are no extensions. + + const HandshakeSide &hsd = m_SrtHsSide; + // Note important possibilities that are considered here: + + // 1. The serial arrangement. This happens when one party has missed the + // URQ_WAVEAHAND message, it sent its own URQ_WAVEAHAND message, and then the + // firstmost message it received from the peer is URQ_CONCLUSION, as a response + // for agent's URQ_WAVEAHAND. + // + // In this case, Agent switches to RDV_FINE state and Peer switches to RDV_ATTENTION state. + // + // 2. The parallel arrangement. This happens when the URQ_WAVEAHAND message sent + // by both parties are almost in a perfect synch (a rare, but possible case). In this + // case, both parties receive one another's URQ_WAVEAHAND message and both switch to + // RDV_ATTENTION state. + // + // It's not possible to predict neither which arrangement will happen, or which + // party will be RDV_FINE in case when the serial arrangement has happened. What + // will actually happen will depend on random conditions. + // + // No matter this randomity, we have a limited number of possible conditions: + // + // Stating that "agent" is the party that has received the URQ_WAVEAHAND in whatever + // arrangement, we are certain, that "agent" switched to RDV_ATTENTION, and peer: + // + // - switched to RDV_ATTENTION state (so, both are in the same state independently) + // - switched to RDV_FINE state (so, the message interchange is actually more-less sequenced) + // + // In particular, there's no possibility of a situation that both are in RDV_FINE state + // because the agent can switch to RDV_FINE state only if it received URQ_CONCLUSION from + // the peer, while the peer could not send URQ_CONCLUSION without switching off RDV_WAVING + // (actually to RDV_ATTENTION). There's also no exit to RDV_FINE from RDV_ATTENTION. + + // DEFAULT STATEMENT: don't attach extensions to URQ_CONCLUSION, neither HSREQ nor HSRSP. + *needs_extension = false; + *needs_hsrsp = false; + + string reason; + +#if ENABLE_HEAVY_LOGGING + + HLOGC(mglog.Debug, log << "rendezvousSwitchState: HS: " << m_ConnRes.show()); + + struct LogAtTheEnd + { + CHandShake::RendezvousState ost; + UDTRequestType orq; + const CHandShake::RendezvousState &nst; + const UDTRequestType & nrq; + bool & needext; + bool & needrsp; + string & reason; + + ~LogAtTheEnd() + { + HLOGC(mglog.Debug, + log << "rendezvousSwitchState: STATE[" << CHandShake::RdvStateStr(ost) << "->" + << CHandShake::RdvStateStr(nst) << "] REQTYPE[" << RequestTypeStr(orq) << "->" + << RequestTypeStr(nrq) << "] " + << "ext:" << (needext ? (needrsp ? "HSRSP" : "HSREQ") : "NONE") + << (reason == "" ? string() : "reason:" + reason)); + } + } l_logend = {m_RdvState, req, m_RdvState, *rsptype, *needs_extension, *needs_hsrsp, reason}; + +#endif + + switch (m_RdvState) + { + case CHandShake::RDV_INVALID: + return; + + case CHandShake::RDV_WAVING: + { + if (req == URQ_WAVEAHAND) + { + m_RdvState = CHandShake::RDV_ATTENTION; + + // NOTE: if this->isWinner(), attach HSREQ + *rsptype = URQ_CONCLUSION; + if (hsd == HSD_INITIATOR) + *needs_extension = true; + return; + } + + if (req == URQ_CONCLUSION) + { + m_RdvState = CHandShake::RDV_FINE; + *rsptype = URQ_CONCLUSION; + + *needs_extension = true; // (see below - this needs to craft either HSREQ or HSRSP) + // if this->isWinner(), then craft HSREQ for that response. + // if this->isLoser(), then this packet should bring HSREQ, so craft HSRSP for the response. + if (hsd == HSD_RESPONDER) + *needs_hsrsp = true; + return; + } + } + reason = "WAVING -> WAVEAHAND or CONCLUSION"; + break; + + case CHandShake::RDV_ATTENTION: + { + if (req == URQ_WAVEAHAND) + { + // This is only possible if the URQ_CONCLUSION sent to the peer + // was lost on track. The peer is then simply unaware that the + // agent has switched to ATTENTION state and continues sending + // waveahands. In this case, just remain in ATTENTION state and + // retry with URQ_CONCLUSION, as normally. + *rsptype = URQ_CONCLUSION; + if (hsd == HSD_INITIATOR) + *needs_extension = true; + return; + } + + if (req == URQ_CONCLUSION) + { + // We have two possibilities here: + // + // WINNER (HSD_INITIATOR): send URQ_AGREEMENT + if (hsd == HSD_INITIATOR) + { + // WINNER should get a response with HSRSP, otherwise this is kinda empty conclusion. + // If no HSRSP attached, stay in this state. + if (hs_flags == 0) + { + HLOGC( + mglog.Debug, + log << "rendezvousSwitchState: " + "{INITIATOR}[ATTENTION] awaits CONCLUSION+HSRSP, got CONCLUSION, remain in [ATTENTION]"); + *rsptype = URQ_CONCLUSION; + *needs_extension = true; // If you expect to receive HSRSP, continue sending HSREQ + return; + } + m_RdvState = CHandShake::RDV_CONNECTED; + *rsptype = URQ_AGREEMENT; + return; + } + + // LOSER (HSD_RESPONDER): send URQ_CONCLUSION and attach HSRSP extension, then expect URQ_AGREEMENT + if (hsd == HSD_RESPONDER) + { + // If no HSREQ attached, stay in this state. + // (Although this seems completely impossible). + if (hs_flags == 0) + { + LOGC( + mglog.Warn, + log << "rendezvousSwitchState: (IPE!)" + "{RESPONDER}[ATTENTION] awaits CONCLUSION+HSREQ, got CONCLUSION, remain in [ATTENTION]"); + *rsptype = URQ_CONCLUSION; + *needs_extension = false; // If you received WITHOUT extensions, respond WITHOUT extensions (wait + // for the right message) + return; + } + m_RdvState = CHandShake::RDV_INITIATED; + *rsptype = URQ_CONCLUSION; + *needs_extension = true; + *needs_hsrsp = true; + return; + } + + LOGC(mglog.Error, log << "RENDEZVOUS COOKIE DRAW! Cannot resolve to a valid state."); + // Fallback for cookie draw + m_RdvState = CHandShake::RDV_INVALID; + *rsptype = URQFailure(SRT_REJ_RDVCOOKIE); + return; + } + + if (req == URQ_AGREEMENT) + { + // This means that the peer has received our URQ_CONCLUSION, but + // the agent missed the peer's URQ_CONCLUSION (received only initial + // URQ_WAVEAHAND). + if (hsd == HSD_INITIATOR) + { + // In this case the missed URQ_CONCLUSION was sent without extensions, + // whereas the peer received our URQ_CONCLUSION with HSREQ, and therefore + // it sent URQ_AGREEMENT already with HSRSP. This isn't a problem for + // us, we can go on with it, especially that the peer is already switched + // into CHandShake::RDV_CONNECTED state. + m_RdvState = CHandShake::RDV_CONNECTED; + + // Both sides are connected, no need to send anything anymore. + *rsptype = URQ_DONE; + return; + } + + if (hsd == HSD_RESPONDER) + { + // In this case the missed URQ_CONCLUSION was sent with extensions, so + // we have to request this once again. Send URQ_CONCLUSION in order to + // inform the other party that we need the conclusion message once again. + // The ATTENTION state should be maintained. + *rsptype = URQ_CONCLUSION; + *needs_extension = true; + *needs_hsrsp = true; + return; + } + } + } + reason = "ATTENTION -> WAVEAHAND(conclusion), CONCLUSION(agreement/conclusion), AGREEMENT (done/conclusion)"; + break; + + case CHandShake::RDV_FINE: + { + // In FINE state we can't receive URQ_WAVEAHAND because if the peer has already + // sent URQ_CONCLUSION, it's already in CHandShake::RDV_ATTENTION, and in this state it can + // only send URQ_CONCLUSION, whereas when it isn't in CHandShake::RDV_ATTENTION, it couldn't + // have sent URQ_CONCLUSION, and if it didn't, the agent wouldn't be in CHandShake::RDV_FINE state. + + if (req == URQ_CONCLUSION) + { + // There's only one case when it should receive CONCLUSION in FINE state: + // When it's the winner. If so, it should then contain HSREQ extension. + // In case of loser, it shouldn't receive CONCLUSION at all - it should + // receive AGREEMENT. + + // The winner case, received CONCLUSION + HSRSP - switch to CONNECTED and send AGREEMENT. + // So, check first if HAS EXTENSION + + bool correct_switch = false; + if (hsd == HSD_INITIATOR && !has_extension) + { + // Received REPEATED empty conclusion that has initially switched it into FINE state. + // To exit FINE state we need the CONCLUSION message with HSRSP. + HLOGC(mglog.Debug, + log << "rendezvousSwitchState: {INITIATOR}[FINE] m_RecvLock); + self->m_bTsbPdAckWakeup = true; + while (!self->m_bClosing) + { + int32_t current_pkt_seq = 0; + uint64_t tsbpdtime = 0; + bool rxready = false; + + CGuard::enterCS(self->m_RcvBufferLock); + +#ifdef SRT_ENABLE_RCVBUFSZ_MAVG + self->m_pRcvBuffer->updRcvAvgDataSize(CTimer::getTime()); +#endif + + if (self->m_bTLPktDrop) + { + int32_t skiptoseqno = -1; + bool passack = true; // Get next packet to wait for even if not acked + + rxready = self->m_pRcvBuffer->getRcvFirstMsg( + Ref(tsbpdtime), Ref(passack), Ref(skiptoseqno), Ref(current_pkt_seq)); + + HLOGC(tslog.Debug, + log << boolalpha << "NEXT PKT CHECK: rdy=" << rxready << " passack=" << passack << " skipto=%" + << skiptoseqno << " current=%" << current_pkt_seq << " buf-base=%" << self->m_iRcvLastSkipAck); + /* + * VALUES RETURNED: + * + * rxready: if true, packet at head of queue ready to play + * tsbpdtime: timestamp of packet at head of queue, ready or not. 0 if none. + * passack: if true, ready head of queue not yet acknowledged + * skiptoseqno: sequence number of packet at head of queue if ready to play but + * some preceeding packets are missing (need to be skipped). -1 if none. + */ + if (rxready) + { + /* Packet ready to play according to time stamp but... */ + int seqlen = CSeqNo::seqoff(self->m_iRcvLastSkipAck, skiptoseqno); + + if (skiptoseqno != -1 && seqlen > 0) + { + /* + * skiptoseqno != -1, + * packet ready to play but preceeded by missing packets (hole). + */ + + /* Update drop/skip stats */ + CGuard::enterCS(self->m_StatsLock); + self->m_stats.rcvDropTotal += seqlen; + self->m_stats.traceRcvDrop += seqlen; + /* Estimate dropped/skipped bytes from average payload */ + int avgpayloadsz = self->m_pRcvBuffer->getRcvAvgPayloadSize(); + self->m_stats.rcvBytesDropTotal += seqlen * avgpayloadsz; + self->m_stats.traceRcvBytesDrop += seqlen * avgpayloadsz; + CGuard::leaveCS(self->m_StatsLock); + + self->dropFromLossLists(self->m_iRcvLastSkipAck, + CSeqNo::decseq(skiptoseqno)); // remove(from,to-inclusive) + self->m_pRcvBuffer->skipData(seqlen); + + self->m_iRcvLastSkipAck = skiptoseqno; + +#if ENABLE_LOGGING + int64_t timediff = 0; + if (tsbpdtime) + timediff = int64_t(tsbpdtime) - int64_t(CTimer::getTime()); +#if ENABLE_HEAVY_LOGGING + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: DROPSEQ: up to seq=" << CSeqNo::decseq(skiptoseqno) << " (" + << seqlen << " packets) playable at " << FormatTime(tsbpdtime) << " delayed " + << (timediff / 1000) << "." << (timediff % 1000) << " ms"); +#endif + LOGC(dlog.Debug, log << "RCV-DROPPED packet delay=" << (timediff / 1000) << "ms"); +#endif + + tsbpdtime = 0; // Next sent ack will unblock + rxready = false; + } + else if (passack) + { + /* Packets ready to play but not yet acknowledged (should happen within 10ms) */ + rxready = false; + tsbpdtime = 0; // Next sent ack will unblock + } /* else packet ready to play */ + } /* else packets not ready to play */ + } + else + { + rxready = self->m_pRcvBuffer->isRcvDataReady(Ref(tsbpdtime), Ref(current_pkt_seq)); + } + CGuard::leaveCS(self->m_RcvBufferLock); + + if (rxready) + { + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << current_pkt_seq << " (belated " + << ((CTimer::getTime() - tsbpdtime) / 1000.0) << "ms)"); + /* + * There are packets ready to be delivered + * signal a waiting "recv" call if there is any data available + */ + if (self->m_bSynRecving) + { + pthread_cond_signal(&self->m_RecvDataCond); + } + /* + * Set EPOLL_IN to wakeup any thread waiting on epoll + */ + self->s_UDTUnited.m_EPoll.update_events(self->m_SocketID, self->m_sPollID, UDT_EPOLL_IN, true); + CTimer::triggerEvent(); + tsbpdtime = 0; + } + + if (tsbpdtime != 0) + { + int64_t timediff = int64_t(tsbpdtime) - int64_t(CTimer::getTime()); + /* + * Buffer at head of queue is not ready to play. + * Schedule wakeup when it will be. + */ + self->m_bTsbPdAckWakeup = false; + THREAD_PAUSED(); + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << current_pkt_seq + << " T=" << FormatTime(tsbpdtime) << " - waiting " << (timediff / 1000.0) << "ms"); + CTimer::condTimedWaitUS(&self->m_RcvTsbPdCond, &self->m_RecvLock, timediff); + THREAD_RESUMED(); + } + else + { + /* + * We have just signaled epoll; or + * receive queue is empty; or + * next buffer to deliver is not in receive queue (missing packet in sequence). + * + * Block until woken up by one of the following event: + * - All ready-to-play packets have been pulled and EPOLL_IN cleared (then loop to block until next pkt time + * if any) + * - New buffers ACKed + * - Closing the connection + */ + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack"); + self->m_bTsbPdAckWakeup = true; + THREAD_PAUSED(); + pthread_cond_wait(&self->m_RcvTsbPdCond, &self->m_RecvLock); + THREAD_RESUMED(); + } + } + CGuard::leaveCS(self->m_RecvLock); + THREAD_EXIT(); + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); + return NULL; +} + +bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout) +{ + // This will be lazily created due to being the common + // code with HSv5 rendezvous, in which this will be run + // in a little bit "randomly selected" moment, but must + // be run once in the whole connection process. + if (m_pSndBuffer) + { + HLOGC(mglog.Debug, log << "prepareConnectionObjects: (lazy) already created."); + return true; + } + + bool bidirectional = false; + if (hs.m_iVersion > HS_VERSION_UDT4) + { + bidirectional = true; // HSv5 is always bidirectional + } + + // HSD_DRAW is received only if this side is listener. + // If this side is caller with HSv5, HSD_INITIATOR should be passed. + // If this is a rendezvous connection with HSv5, the handshake role + // is taken from m_SrtHsSide field. + if (hsd == HSD_DRAW) + { + if (bidirectional) + { + hsd = HSD_RESPONDER; // In HSv5, listener is always RESPONDER and caller always INITIATOR. + } + else + { + hsd = m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER; + } + } + + try + { + m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize); + m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_iRcvBufSize); + // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. + m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); + m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize); + } + catch (...) + { + // Simply reject. + if (eout) + { + *eout = CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + } + m_RejectReason = SRT_REJ_RESOURCE; + return false; + } + + if (!createCrypter(hsd, bidirectional)) // Make sure CC is created (lazy) + { + m_RejectReason = SRT_REJ_RESOURCE; + return false; + } + + return true; +} + +void CUDT::acceptAndRespond(const sockaddr *peer, CHandShake *hs, const CPacket &hspkt) +{ + HLOGC(mglog.Debug, log << "acceptAndRespond: setting up data according to handshake"); + + CGuard cg(m_ConnectionLock); + + m_ullRcvPeerStartTime = 0; // will be set correctly at SRT HS + + // Uses the smaller MSS between the peers + if (hs->m_iMSS > m_iMSS) + hs->m_iMSS = m_iMSS; + else + m_iMSS = hs->m_iMSS; + + // exchange info for maximum flow window size + m_iFlowWindowSize = hs->m_iFlightFlagSize; + hs->m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize; + + m_iPeerISN = hs->m_iISN; + + m_iRcvLastAck = hs->m_iISN; +#ifdef ENABLE_LOGGING + m_iDebugPrevLastAck = m_iRcvLastAck; +#endif + m_iRcvLastSkipAck = m_iRcvLastAck; + m_iRcvLastAckAck = hs->m_iISN; + m_iRcvCurrSeqNo = hs->m_iISN - 1; + m_iRcvCurrPhySeqNo = hs->m_iISN - 1; + + m_PeerID = hs->m_iID; + hs->m_iID = m_SocketID; + + // use peer's ISN and send it back for security check + m_iISN = hs->m_iISN; + + m_iLastDecSeq = m_iISN - 1; + m_iSndLastAck = m_iISN; + m_iSndLastDataAck = m_iISN; + m_iSndLastFullAck = m_iISN; + m_iSndCurrSeqNo = m_iISN - 1; + m_iSndLastAck2 = m_iISN; + m_ullSndLastAck2Time = CTimer::getTime(); + + // this is a reponse handshake + hs->m_iReqType = URQ_CONCLUSION; + + if (hs->m_iVersion > HS_VERSION_UDT4) + { + // The version is agreed; this code is executed only in case + // when AGENT is listener. In this case, conclusion response + // must always contain HSv5 handshake extensions. + hs->m_extension = true; + } + + // get local IP address and send the peer its IP address (because UDP cannot get local IP address) + memcpy(m_piSelfIP, hs->m_piPeerIP, 16); + CIPAddress::ntop(peer, hs->m_piPeerIP, m_iIPversion); + + int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE; + m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; + HLOGC(mglog.Debug, log << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + + // Prepare all structures + if (!prepareConnectionObjects(*hs, HSD_DRAW, 0)) + { + HLOGC(mglog.Debug, log << "acceptAndRespond: prepareConnectionObjects failed - responding with REJECT."); + // If the SRT Handshake extension was provided and wasn't interpreted + // correctly, the connection should be rejected. + // + // Respond with the rejection message and exit with exception + // so that the caller will know that this new socket should be deleted. + hs->m_iReqType = URQFailure(m_RejectReason); + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + // Since now you can use m_pCryptoControl + + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(peer, m_iIPversion, ib.m_piIP); + if (m_pCache->lookup(&ib) >= 0) + { + m_iRTT = ib.m_iRTT; + m_iBandwidth = ib.m_iBandwidth; + } + + // This should extract the HSREQ and KMREQ portion in the handshake packet. + // This could still be a HSv4 packet and contain no such parts, which will leave + // this entity as "non-SRT-handshaken", and await further HSREQ and KMREQ sent + // as UMSG_EXT. + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + if (!interpretSrtHandshake(*hs, hspkt, kmdata, &kmdatasize)) + { + HLOGC(mglog.Debug, log << "acceptAndRespond: interpretSrtHandshake failed - responding with REJECT."); + // If the SRT Handshake extension was provided and wasn't interpreted + // correctly, the connection should be rejected. + // + // Respond with the rejection message and return false from + // this function so that the caller will know that this new + // socket should be deleted. + hs->m_iReqType = URQFailure(m_RejectReason); + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + + SRT_REJECT_REASON rr = setupCC(); + // UNKNOWN used as a "no error" value + if (rr != SRT_REJ_UNKNOWN) + { + hs->m_iReqType = URQFailure(rr); + m_RejectReason = rr; + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + + m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr *)new sockaddr_in : (sockaddr *)new sockaddr_in6; + memcpy(m_pPeerAddr, peer, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + + // And of course, it is connected. + m_bConnected = true; + + // register this socket for receiving data packets + m_pRNode->m_bOnList = true; + m_pRcvQueue->setNewEntry(this); + + // send the response to the peer, see listen() for more discussions about this + // XXX Here create CONCLUSION RESPONSE with: + // - just the UDT handshake, if HS_VERSION_UDT4, + // - if higher, the UDT handshake, the SRT HSRSP, the SRT KMRSP + size_t size = m_iMaxSRTPayloadSize; + // Allocate the maximum possible memory for an SRT payload. + // This is a maximum you can send once. + CPacket response; + response.setControl(UMSG_HANDSHAKE); + response.allocate(size); + + // This will serialize the handshake according to its current form. + HLOGC(mglog.Debug, + log << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); + if (!createSrtHandshake(Ref(response), Ref(*hs), SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize)) + { + LOGC(mglog.Error, log << "acceptAndRespond: error creating handshake response"); + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + + // Set target socket ID to the value from received handshake's source ID. + response.m_iID = m_PeerID; + +#if ENABLE_HEAVY_LOGGING + { + // To make sure what REALLY is being sent, parse back the handshake + // data that have been just written into the buffer. + CHandShake debughs; + debughs.load_from(response.m_pcData, response.getLength()); + HLOGC(mglog.Debug, + log << CONID() << "acceptAndRespond: sending HS to peer, reqtype=" << RequestTypeStr(debughs.m_iReqType) + << " version=" << debughs.m_iVersion << " (connreq:" << RequestTypeStr(m_ConnReq.m_iReqType) + << "), target_socket=" << response.m_iID << ", my_socket=" << debughs.m_iID); + } +#endif + + // NOTE: BLOCK THIS instruction in order to cause the final + // handshake to be missed and cause the problem solved in PR #417. + // When missed this message, the caller should not accept packets + // coming as connected, but continue repeated handshake until finally + // received the listener's handshake. + m_pSndQueue->sendto(peer, response); +} + +// This function is required to be called when a caller receives an INDUCTION +// response from the listener and would like to create a CONCLUSION that includes +// the SRT handshake extension. This extension requires that the crypter object +// be created, but it's still too early for it to be completely configured. +// This function then precreates the object so that the handshake extension can +// be created, as this happens before the completion of the connection (and +// therefore configuration of the crypter object), which can only take place upon +// reception of CONCLUSION response from the listener. +bool CUDT::createCrypter(HandshakeSide side, bool bidirectional) +{ + // Lazy initialization + if (m_pCryptoControl) + return true; + + // Write back this value, when it was just determined. + m_SrtHsSide = side; + + m_pCryptoControl.reset(new CCryptoControl(this, m_SocketID)); + + // XXX These below are a little bit controversial. + // These data should probably be filled only upon + // reception of the conclusion handshake - otherwise + // they have outdated values. + m_pCryptoControl->setCryptoSecret(m_CryptoSecret); + + if (bidirectional || m_bDataSender) + { + HLOGC(mglog.Debug, log << "createCrypter: setting RCV/SND KeyLen=" << m_iSndCryptoKeyLen); + m_pCryptoControl->setCryptoKeylen(m_iSndCryptoKeyLen); + } + + return m_pCryptoControl->init(side, bidirectional); +} + +SRT_REJECT_REASON CUDT::setupCC() +{ + // Prepare configuration object, + // Create the CCC object and configure it. + + // UDT also sets back the congestion window: ??? + // m_dCongestionWindow = m_pCC->m_dCWndSize; + + // XXX Not sure about that. May happen that AGENT wants + // tsbpd mode, but PEER doesn't, even in bidirectional mode. + // This way, the reception side should get precedense. + // if (bidirectional || m_bDataSender || m_bTwoWayData) + // m_bPeerTsbPd = m_bOPT_TsbPd; + + // SrtCongestion will retrieve whatever parameters it needs + // from *this. + if (!m_CongCtl.configure(this)) + { + return SRT_REJ_CONGESTION; + } + + // Configure filter module + if (m_OPT_PktFilterConfigString != "") + { + // This string, when nonempty, defines that the corrector shall be + // configured. Otherwise it's left uninitialized. + + // At this point we state everything is checked and the appropriate + // corrector type is already selected, so now create it. + HLOGC(mglog.Debug, log << "filter: Configuring Corrector: " << m_OPT_PktFilterConfigString); + if (!m_PacketFilter.configure(this, m_pRcvBuffer->getUnitQueue(), m_OPT_PktFilterConfigString)) + { + return SRT_REJ_FILTER; + } + + m_PktFilterRexmitLevel = m_PacketFilter.arqLevel(); + } + else + { + // When we have no filter, ARQ should work in ALWAYS mode. + m_PktFilterRexmitLevel = SRT_ARQ_ALWAYS; + } + + // Override the value of minimum NAK interval, per SrtCongestion's wish. + // When default 0 value is returned, the current value set by CUDT + // is preserved. + uint64_t min_nak_tk = m_CongCtl->minNAKInterval(); + if (min_nak_tk) + m_ullMinNakInt_tk = min_nak_tk; + + // Update timers + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + m_ullLastRspTime_tk = currtime_tk; + m_ullNextACKTime_tk = currtime_tk + m_ullACKInt_tk; + m_ullNextNAKTime_tk = currtime_tk + m_ullNAKInt_tk; + m_ullLastRspAckTime_tk = currtime_tk; + m_ullLastSndTime_tk = currtime_tk; + + HLOGC(mglog.Debug, + log << "setupCC: setting parameters: mss=" << m_iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize + << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)" + << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); + + updateCC(TEV_INIT, TEV_INIT_RESET); + return SRT_REJ_UNKNOWN; +} + +void CUDT::considerLegacySrtHandshake(uint64_t timebase) +{ + // Do a fast pre-check first - this simply declares that agent uses HSv5 + // and the legacy SRT Handshake is not to be done. Second check is whether + // agent is sender (=initiator in HSv4). + if (!isTsbPd() || !m_bDataSender) + return; + + if (m_iSndHsRetryCnt <= 0) + { + HLOGC(mglog.Debug, log << "Legacy HSREQ: not needed, expire counter=" << m_iSndHsRetryCnt); + return; + } + + uint64_t now = CTimer::getTime(); + if (timebase != 0) + { + // Then this should be done only if it's the right time, + // the TSBPD mode is on, and when the counter is "still rolling". + /* + * SRT Handshake with peer: + * If... + * - we want TsbPd mode; and + * - we have not tried more than CSRTCC_MAXRETRY times (peer may not be SRT); and + * - and did not get answer back from peer + * - last sent handshake req should have been replied (RTT*1.5 elapsed); and + * then (re-)send handshake request. + */ + if (timebase > now) // too early + { + HLOGC(mglog.Debug, log << "Legacy HSREQ: TOO EARLY, will still retry " << m_iSndHsRetryCnt << " times"); + return; + } + } + // If 0 timebase, it means that this is the initial sending with the very first + // payload packet sent. Send only if this is still set to maximum+1 value. + else if (m_iSndHsRetryCnt < SRT_MAX_HSRETRY + 1) + { + HLOGC(mglog.Debug, + log << "Legacy HSREQ: INITIAL, REPEATED, so not to be done. Will repeat on sending " << m_iSndHsRetryCnt + << " times"); + return; + } + + HLOGC(mglog.Debug, log << "Legacy HSREQ: SENDING, will repeat " << m_iSndHsRetryCnt << " times if no response"); + m_iSndHsRetryCnt--; + m_ullSndHsLastTime_us = now; + sendSrtMsg(SRT_CMD_HSREQ); +} + +void CUDT::checkSndTimers(Whether2RegenKm regen) +{ + if (m_SrtHsSide == HSD_INITIATOR) + { + HLOGC(mglog.Debug, log << "checkSndTimers: HS SIDE: INITIATOR, considering legacy handshake with timebase"); + // Legacy method for HSREQ, only if initiator. + considerLegacySrtHandshake(m_ullSndHsLastTime_us + m_iRTT * 3 / 2); + } + else + { + HLOGC(mglog.Debug, + log << "checkSndTimers: HS SIDE: " << (m_SrtHsSide == HSD_RESPONDER ? "RESPONDER" : "DRAW (IPE?)") + << " - not considering legacy handshake"); + } + + // This must be done always on sender, regardless of HS side. + // When regen == DONT_REGEN_KM, it's a handshake call, so do + // it only for initiator. + if (regen || m_SrtHsSide == HSD_INITIATOR) + { + // Don't call this function in "non-regen mode" (sending only), + // if this side is RESPONDER. This shall be called only with + // regeneration request, which is required by the sender. + if (m_pCryptoControl) + m_pCryptoControl->sendKeysToPeer(regen); + } +} + +void CUDT::addressAndSend(CPacket &pkt) +{ + pkt.m_iID = m_PeerID; + pkt.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime); + + m_pSndQueue->sendto(m_pPeerAddr, pkt); +} + +bool CUDT::close() +{ + // NOTE: this function is called from within the garbage collector thread. + + if (!m_bOpened) + { + return false; + } + + HLOGC(mglog.Debug, log << CONID() << " - closing socket:"); + + if (m_Linger.l_onoff != 0) + { + uint64_t entertime = CTimer::getTime(); + + HLOGC(mglog.Debug, log << CONID() << " ... (linger)"); + while (!m_bBroken && m_bConnected && (m_pSndBuffer->getCurrBufSize() > 0) && + (CTimer::getTime() - entertime < m_Linger.l_linger * uint64_t(1000000))) + { + // linger has been checked by previous close() call and has expired + if (m_ullLingerExpiration >= entertime) + break; + + if (!m_bSynSending) + { + // if this socket enables asynchronous sending, return immediately and let GC to close it later + if (m_ullLingerExpiration == 0) + m_ullLingerExpiration = entertime + m_Linger.l_linger * uint64_t(1000000); + + HLOGC(mglog.Debug, + log << "CUDT::close: linger-nonblocking, setting expire time T=" + << FormatTime(m_ullLingerExpiration)); + + return false; + } + +#ifndef _WIN32 + timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); +#else + Sleep(1); +#endif + } + } + + // remove this socket from the snd queue + if (m_bConnected) + m_pSndQueue->m_pSndUList->remove(this); + + /* + * update_events below useless + * removing usock for EPolls right after (remove_usocks) clears it (in other HAI patch). + * + * What is in EPoll shall be the responsibility of the application, if it want local close event, + * it would remove the socket from the EPoll after close. + */ + // trigger any pending IO events. + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true); + // then remove itself from all epoll monitoring + try + { + for (set::iterator i = m_sPollID.begin(); i != m_sPollID.end(); ++i) + s_UDTUnited.m_EPoll.remove_usock(*i, m_SocketID); + } + catch (...) + { + } + + // XXX What's this, could any of the above actions make it !m_bOpened? + if (!m_bOpened) + { + return true; + } + + // Inform the threads handler to stop. + m_bClosing = true; + + HLOGC(mglog.Debug, log << CONID() << "CLOSING STATE. Acquiring connection lock"); + + CGuard cg(m_ConnectionLock); + + // Signal the sender and recver if they are waiting for data. + releaseSynch(); + + HLOGC(mglog.Debug, log << CONID() << "CLOSING, removing from listener/connector"); + + if (m_bListening) + { + m_bListening = false; + m_pRcvQueue->removeListener(this); + } + else if (m_bConnecting) + { + m_pRcvQueue->removeConnector(m_SocketID); + } + + if (m_bConnected) + { + if (!m_bShutdown) + { + HLOGC(mglog.Debug, log << CONID() << "CLOSING - sending SHUTDOWN to the peer"); + sendCtrl(UMSG_SHUTDOWN); + } + + m_pCryptoControl->close(); + + // Store current connection information. + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP); + ib.m_iRTT = m_iRTT; + ib.m_iBandwidth = m_iBandwidth; + m_pCache->update(&ib); + + m_bConnected = false; + } + + if (m_bTsbPd && !pthread_equal(m_RcvTsbPdThread, pthread_t())) + { + HLOGC(mglog.Debug, log << "CLOSING, joining TSBPD thread..."); + void *retval; + int ret SRT_ATR_UNUSED = pthread_join(m_RcvTsbPdThread, &retval); + HLOGC(mglog.Debug, log << "... " << (ret == 0 ? "SUCCEEDED" : "FAILED")); + } + + HLOGC(mglog.Debug, log << "CLOSING, joining send/receive threads"); + + // waiting all send and recv calls to stop + CGuard sendguard(m_SendLock); + CGuard recvguard(m_RecvLock); + + // Locking m_RcvBufferLock to protect calling to m_pCryptoControl->decrypt(Ref(packet)) + // from the processData(...) function while resetting Crypto Control. + CGuard::enterCS(m_RcvBufferLock); + m_pCryptoControl.reset(); + CGuard::leaveCS(m_RcvBufferLock); + + m_lSrtVersion = SRT_DEF_VERSION; + m_lPeerSrtVersion = SRT_VERSION_UNK; + m_lMinimumPeerSrtVersion = SRT_VERSION_MAJ1; + m_ullRcvPeerStartTime = 0; + + m_bOpened = false; + + return true; +} + +/* + Old, mostly original UDT based version of CUDT::send. + Left for historical reasons. + +int CUDT::send(const char* data, int len) +{ + // throw an exception if not connected + if (m_bBroken || m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + else if (!m_bConnected || !m_CongCtl.ready()) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + if (len <= 0) + return 0; + + // Check if the current congctl accepts the call with given parameters. + if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_BUFFER, SrtCongestion::STAD_SEND, data, len, -1, false)) + throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); + + CGuard sendguard(m_SendLock); + + if (m_pSndBuffer->getCurrBufSize() == 0) + { + // delay the EXP timer to avoid mis-fired timeout + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + // (fix keepalive) m_ullLastRspTime_tk = currtime_tk; + m_ullLastRspAckTime_tk = currtime_tk; + m_iReXmitCount = 1; + } + if (sndBuffersLeft() <= 0) + { + if (!m_bSynSending) + throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0); + else + { + { + // wait here during a blocking sending + CGuard sendblock_lock(m_SendBlockLock); + if (m_iSndTimeOut < 0) + { + while (stillConnected() && (sndBuffersLeft() <= 0) && m_bPeerHealth) + pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * uint64_t(1000); + timespec locktime; + + locktime.tv_sec = exptime / 1000000; + locktime.tv_nsec = (exptime % 1000000) * 1000; + + while (stillConnected() && (sndBuffersLeft() <= 0) && m_bPeerHealth && (CTimer::getTime() < exptime)) + pthread_cond_timedwait(&m_SendBlockCond, &m_SendBlockLock, &locktime); + } + } + + // check the connection status + if (m_bBroken || m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + else if (!m_bConnected) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + else if (!m_bPeerHealth) + { + m_bPeerHealth = true; + throw CUDTException(MJ_PEERERROR); + } + } + } + + if (sndBuffersLeft() <= 0) + { + if (m_iSndTimeOut >= 0) + throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); + + return 0; + } + + int size = min(len, sndBuffersLeft() * m_iMaxSRTPayloadSize); + + // record total time used for sending + if (m_pSndBuffer->getCurrBufSize() == 0) + m_llSndDurationCounter = CTimer::getTime(); + + // insert the user buffer into the sending list + m_pSndBuffer->addBuffer(data, size); // inorder=false, ttl=-1 + + // insert this socket to snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); + + if (sndBuffersLeft() <= 0) + { + // write is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); + } + + return size; +} +*/ + +int CUDT::receiveBuffer(char *data, int len) +{ + if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_BUFFER, SrtCongestion::STAD_RECV, data, len, -1, false)) + throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); + + CGuard recvguard(m_RecvLock); + + if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + { + if (m_bShutdown) + { + // For stream API, return 0 as a sign of EOF for transmission. + // That's a bit controversial because theoretically the + // UMSG_SHUTDOWN message may be lost as every UDP packet, although + // another theory states that this will never happen because this + // packet has a total size of 42 bytes and such packets are + // declared as never dropped - but still, this is UDP so there's no + // guarantee. + + // The most reliable way to inform the party that the transmission + // has ended would be to send a single empty packet (that is, + // a data packet that contains only an SRT header in the UDP + // payload), which is a normal data packet that can undergo + // normal sequence check and retransmission rules, so it's ensured + // that this packet will be received. Receiving such a packet should + // make this function return 0, potentially also without breaking + // the connection and potentially also with losing no ability to + // send some larger portion of data next time. + HLOGC(mglog.Debug, log << "STREAM API, SHUTDOWN: marking as EOF"); + return 0; + } + HLOGC(mglog.Debug, + log << (m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + << " SHUTDOWN. Reporting as BROKEN."); + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + + if (!m_pRcvBuffer->isRcvDataReady()) + { + if (!m_bSynRecving) + { + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + } + else + { + /* Kick TsbPd thread to schedule next wakeup (if running) */ + if (m_iRcvTimeOut < 0) + { + while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) + { + // Do not block forever, check connection status each 1 sec. + CTimer::condTimedWaitUS(&m_RecvDataCond, &m_RecvLock, 1000000); + } + } + else + { + uint64_t exptime = CTimer::getTime() + m_iRcvTimeOut * 1000; + while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) + { + CTimer::condTimedWaitUS(&m_RecvDataCond, &m_RecvLock, m_iRcvTimeOut * 1000); + if (CTimer::getTime() >= exptime) + break; + } + } + } + } + + // throw an exception if not connected + if (!m_bConnected) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + { + // See at the beginning + if (!m_bMessageAPI && m_bShutdown) + { + HLOGC(mglog.Debug, log << "STREAM API, SHUTDOWN: marking as EOF"); + return 0; + } + HLOGC(mglog.Debug, + log << (m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + << " SHUTDOWN. Reporting as BROKEN."); + + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + + const int res = m_pRcvBuffer->readBuffer(data, len); + + /* Kick TsbPd thread to schedule next wakeup (if running) */ + if (m_bTsbPd) + { + HLOGP(tslog.Debug, "Ping TSBPD thread to schedule wakeup"); + pthread_cond_signal(&m_RcvTsbPdCond); + } + + if (!m_pRcvBuffer->isRcvDataReady()) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + if ((res <= 0) && (m_iRcvTimeOut >= 0)) + throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); + + return res; +} + +void CUDT::checkNeedDrop(ref_t bCongestion) +{ + if (!m_bPeerTLPktDrop) + return; + + if (!m_bMessageAPI) + { + LOGC(dlog.Error, log << "The SRTO_TLPKTDROP flag can only be used with message API."); + throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); + } + + int bytes, timespan_ms; + // (returns buffer size in buffer units, ignored) + m_pSndBuffer->getCurrBufSize(Ref(bytes), Ref(timespan_ms)); + + // high threshold (msec) at tsbpd_delay plus sender/receiver reaction time (2 * 10ms) + // Minimum value must accomodate an I-Frame (~8 x average frame size) + // >>need picture rate or app to set min treshold + // >>using 1 sec for worse case 1 frame using all bit budget. + // picture rate would be useful in auto SRT setting for min latency + // XXX Make SRT_TLPKTDROP_MINTHRESHOLD_MS option-configurable + int threshold_ms = 0; + if (m_iOPT_SndDropDelay >= 0) + { + threshold_ms = std::max(m_iPeerTsbPdDelay_ms + m_iOPT_SndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) + + (2 * COMM_SYN_INTERVAL_US / 1000); + } + + if (threshold_ms && timespan_ms > threshold_ms) + { + // protect packet retransmission + CGuard::enterCS(m_RecvAckLock); + int dbytes; + int dpkts = m_pSndBuffer->dropLateData(dbytes, CTimer::getTime() - (threshold_ms * 1000)); + if (dpkts > 0) + { + CGuard::enterCS(m_StatsLock); + m_stats.traceSndDrop += dpkts; + m_stats.sndDropTotal += dpkts; + m_stats.traceSndBytesDrop += dbytes; + m_stats.sndBytesDropTotal += dbytes; + CGuard::leaveCS(m_StatsLock); + +#if ENABLE_HEAVY_LOGGING + int32_t realack = m_iSndLastDataAck; +#endif + int32_t fakeack = CSeqNo::incseq(m_iSndLastDataAck, dpkts); + + m_iSndLastAck = fakeack; + m_iSndLastDataAck = fakeack; + + int32_t minlastack = CSeqNo::decseq(m_iSndLastDataAck); + m_pSndLossList->remove(minlastack); + /* If we dropped packets not yet sent, advance current position */ + // THIS MEANS: m_iSndCurrSeqNo = MAX(m_iSndCurrSeqNo, m_iSndLastDataAck-1) + if (CSeqNo::seqcmp(m_iSndCurrSeqNo, minlastack) < 0) + { + m_iSndCurrSeqNo = minlastack; + } + LOGC(dlog.Error, log << "SND-DROPPED " << dpkts << " packets - lost delaying for " << timespan_ms << "ms"); + + HLOGC(dlog.Debug, + log << "drop,now " << CTimer::getTime() << "us," << realack << "-" << m_iSndCurrSeqNo << " seqs," + << dpkts << " pkts," << dbytes << " bytes," << timespan_ms << " ms"); + } + *bCongestion = true; + CGuard::leaveCS(m_RecvAckLock); + } + else if (timespan_ms > (m_iPeerTsbPdDelay_ms / 2)) + { + HLOGC(mglog.Debug, + log << "cong, NOW: " << CTimer::getTime() << "us, BYTES " << bytes << ", TMSPAN " << timespan_ms << "ms"); + + *bCongestion = true; + } +} + +int CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, uint64_t srctime) +{ + SRT_MSGCTRL mctrl = srt_msgctrl_default; + mctrl.msgttl = msttl; + mctrl.inorder = inorder; + mctrl.srctime = srctime; + return this->sendmsg2(data, len, Ref(mctrl)); +} + +int CUDT::sendmsg2(const char *data, int len, ref_t r_mctrl) +{ + SRT_MSGCTRL &mctrl = *r_mctrl; + bool bCongestion = false; + + // throw an exception if not connected + if (m_bBroken || m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + else if (!m_bConnected || !m_CongCtl.ready()) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + if (len <= 0) + { + LOGC(dlog.Error, log << "INVALID: Data size for sending declared with length: " << len); + return 0; + } + + int msttl = mctrl.msgttl; + bool inorder = mctrl.inorder; + + // Sendmsg isn't restricted to the congctl type, however the congctl + // may want to have something to say here. + // NOTE: SrtCongestion is also allowed to throw CUDTException() by itself! + { + SrtCongestion::TransAPI api = SrtCongestion::STA_MESSAGE; + CodeMinor mn = MN_INVALMSGAPI; + if (!m_bMessageAPI) + { + api = SrtCongestion::STA_BUFFER; + mn = MN_INVALBUFFERAPI; + } + + if (!m_CongCtl->checkTransArgs(api, SrtCongestion::STAD_SEND, data, len, msttl, inorder)) + throw CUDTException(MJ_NOTSUP, mn, 0); + } + + // NOTE: the length restrictions differ in STREAM API and in MESSAGE API: + + // - STREAM API: + // At least 1 byte free sending buffer space is needed + // (in practice, one unit buffer of 1456 bytes). + // This function will send as much as possible, and return + // how much was actually sent. + + // - MESSAGE API: + // At least so many bytes free in the sending buffer is needed, + // as the length of the data, otherwise this function will block + // or return MJ_AGAIN until this condition is satisfied. The EXACTLY + // such number of data will be then written out, and this function + // will effectively return either -1 (error) or the value of 'len'. + // This call will be also rejected from upside when trying to send + // out a message of a length that exceeds the total size of the sending + // buffer (configurable by SRTO_SNDBUF). + + if (m_bMessageAPI && len > int(m_iSndBufSize * m_iMaxSRTPayloadSize)) + { + LOGC(dlog.Error, + log << "Message length (" << len << ") exceeds the size of sending buffer: " + << (m_iSndBufSize * m_iMaxSRTPayloadSize) << ". Use SRTO_SNDBUF if needed."); + throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); + } + + /* XXX + This might be worth preserving for several occasions, but it + must be at least conditional because it breaks backward compat. + if (!m_pCryptoControl || !m_pCryptoControl->isSndEncryptionOK()) + { + LOGC(dlog.Error, log << "Encryption is required, but the peer did not supply correct credentials. Sending + rejected."); throw CUDTException(MJ_SETUP, MN_SECURITY, 0); + } + */ + + CGuard sendguard(m_SendLock); + + if (m_pSndBuffer->getCurrBufSize() == 0) + { + // delay the EXP timer to avoid mis-fired timeout + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + + CGuard ack_lock(m_RecvAckLock); + m_ullLastRspAckTime_tk = currtime_tk; // (fix keepalive) + m_iReXmitCount = 1; // can be modified in checkRexmitTimer and processCtrlAck (receiver's thread) + } + + // checkNeedDrop(...) may lock m_RecvAckLock + // to modify m_pSndBuffer and m_pSndLossList + checkNeedDrop(Ref(bCongestion)); + + int minlen = 1; // Minimum sender buffer space required for STREAM API + if (m_bMessageAPI) + { + // For MESSAGE API the minimum outgoing buffer space required is + // the size that can carry over the whole message as passed here. + minlen = (len + m_iMaxSRTPayloadSize - 1) / m_iMaxSRTPayloadSize; + } + + if (sndBuffersLeft() < minlen) + { + //>>We should not get here if SRT_ENABLE_TLPKTDROP + // XXX Check if this needs to be removed, or put to an 'else' condition for m_bTLPktDrop. + if (!m_bSynSending) + throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0); + + { + // wait here during a blocking sending + CGuard sendblock_lock(m_SendBlockLock); + + if (m_iSndTimeOut < 0) + { + while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth) + pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); + } + else + { + const uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * uint64_t(1000); + + while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth && exptime > CTimer::getTime()) + CTimer::condTimedWaitUS(&m_SendBlockCond, &m_SendBlockLock, m_iSndTimeOut * uint64_t(1000)); + } + } + + // check the connection status + if (m_bBroken || m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + else if (!m_bConnected) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + else if (!m_bPeerHealth) + { + m_bPeerHealth = true; + throw CUDTException(MJ_PEERERROR); + } + + /* + * The code below is to return ETIMEOUT when blocking mode could not get free buffer in time. + * If no free buffer available in non-blocking mode, we alredy returned. If buffer availaible, + * we test twice if this code is outside the else section. + * This fix move it in the else (blocking-mode) section + */ + if (sndBuffersLeft() < minlen) + { + if (m_iSndTimeOut >= 0) + throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); + + // XXX This looks very weird here, however most likely + // this will happen only in the following case, when + // the above loop has been interrupted, which happens when: + // 1. The buffers left gets enough for minlen - but this is excluded + // in the first condition here. + // 2. In the case of sending timeout, the above loop was interrupted + // due to reaching timeout, but this is excluded by the second + // condition here + // 3. The 'stillConnected()' or m_bPeerHealth condition is false, of which: + // - broken/closing status is checked and responded with CONNECTION/CONNLOST + // - not connected status is checked and responded with CONNECTION/NOCONN + // - m_bPeerHealth condition is checked and responded with PEERERROR + // + // ERGO: never happens? + LOGC(mglog.Fatal, + log << "IPE: sendmsg: the loop exited, while not enough size, still connected, peer healthy. " + "Impossible."); + + return 0; + } + } + + // If the sender's buffer is empty, + // record total time used for sending + if (m_pSndBuffer->getCurrBufSize() == 0) + { + CGuard::enterCS(m_StatsLock); + m_stats.sndDurationCounter = CTimer::getTime(); + CGuard::leaveCS(m_StatsLock); + } + + int size = len; + if (!m_bMessageAPI) + { + // For STREAM API it's allowed to send less bytes than the given buffer. + // Just return how many bytes were actually scheduled for writing. + // XXX May be reasonable to add a flag that requires that the function + // not return until the buffer is sent completely. + size = min(len, sndBuffersLeft() * m_iMaxSRTPayloadSize); + } + + { + CGuard recvAckLock(m_RecvAckLock); + // insert the user buffer into the sending list + // This should be protected by a mutex. m_SendLock does this. + m_pSndBuffer->addBuffer(data, size, mctrl.msgttl, mctrl.inorder, mctrl.srctime, Ref(mctrl.msgno)); + HLOGC(dlog.Debug, log << CONID() << "sock:SENDING srctime: " << mctrl.srctime << "us DATA SIZE: " << size); + + if (sndBuffersLeft() < 1) // XXX Not sure if it should test if any space in the buffer, or as requried. + { + // write is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); + } + } + + // insert this socket to the snd list if it is not on the list yet + // m_pSndUList->pop may lock CSndUList::m_ListLock and then m_RecvAckLock + m_pSndQueue->m_pSndUList->update(this, CSndUList::rescheduleIf(bCongestion)); + +#ifdef SRT_ENABLE_ECN + if (bCongestion) + throw CUDTException(MJ_AGAIN, MN_CONGESTION, 0); +#endif /* SRT_ENABLE_ECN */ + return size; +} + +int CUDT::recv(char *data, int len) +{ + if (!m_bConnected || !m_CongCtl.ready()) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + if (len <= 0) + { + LOGC(dlog.Error, log << "Length of '" << len << "' supplied to srt_recv."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (m_bMessageAPI) + { + SRT_MSGCTRL mctrl = srt_msgctrl_default; + return receiveMessage(data, len, Ref(mctrl)); + } + + return receiveBuffer(data, len); +} + +int CUDT::recvmsg(char *data, int len, uint64_t &srctime) +{ + if (!m_bConnected || !m_CongCtl.ready()) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + if (len <= 0) + { + LOGC(dlog.Error, log << "Length of '" << len << "' supplied to srt_recvmsg."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (m_bMessageAPI) + { + SRT_MSGCTRL mctrl = srt_msgctrl_default; + int ret = receiveMessage(data, len, Ref(mctrl)); + srctime = mctrl.srctime; + return ret; + } + + return receiveBuffer(data, len); +} + +int CUDT::recvmsg2(char *data, int len, ref_t mctrl) +{ + if (!m_bConnected || !m_CongCtl.ready()) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + if (len <= 0) + { + LOGC(dlog.Error, log << "Length of '" << len << "' supplied to srt_recvmsg."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (m_bMessageAPI) + return receiveMessage(data, len, mctrl); + + return receiveBuffer(data, len); +} + +int CUDT::receiveMessage(char *data, int len, ref_t r_mctrl) +{ + SRT_MSGCTRL &mctrl = *r_mctrl; + // Recvmsg isn't restricted to the congctl type, it's the most + // basic method of passing the data. You can retrieve data as + // they come in, however you need to match the size of the buffer. + if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_MESSAGE, SrtCongestion::STAD_RECV, data, len, -1, false)) + throw CUDTException(MJ_NOTSUP, MN_INVALMSGAPI, 0); + + CGuard recvguard(m_RecvLock); + + /* XXX DEBUG STUFF - enable when required + char charbool[2] = {'0', '1'}; + char ptrn [] = "RECVMSG/BEGIN BROKEN 1 CONN 1 CLOSING 1 SYNCR 1 NMSG "; + int pos [] = {21, 28, 38, 46, 53}; + ptrn[pos[0]] = charbool[m_bBroken]; + ptrn[pos[1]] = charbool[m_bConnected]; + ptrn[pos[2]] = charbool[m_bClosing]; + ptrn[pos[3]] = charbool[m_bSynRecving]; + int wrtlen = sprintf(ptrn + pos[4], "%d", m_pRcvBuffer->getRcvMsgNum()); + strcpy(ptrn + pos[4] + wrtlen, "\n"); + fputs(ptrn, stderr); + // */ + + if (m_bBroken || m_bClosing) + { + int res = m_pRcvBuffer->readMsg(data, len); + mctrl.srctime = 0; + + /* Kick TsbPd thread to schedule next wakeup (if running) */ + if (m_bTsbPd) + pthread_cond_signal(&m_RcvTsbPdCond); + + if (!m_pRcvBuffer->isRcvDataReady()) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + if (res == 0) + { + if (!m_bMessageAPI && m_bShutdown) + return 0; + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + else + return res; + } + + if (!m_bSynRecving) + { + + int res = m_pRcvBuffer->readMsg(data, len, r_mctrl); + if (res == 0) + { + // read is not available any more + + // Kick TsbPd thread to schedule next wakeup (if running) + if (m_bTsbPd) + pthread_cond_signal(&m_RcvTsbPdCond); + + // Shut up EPoll if no more messages in non-blocking mode + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + } + else + { + if (!m_pRcvBuffer->isRcvDataReady()) + { + // Kick TsbPd thread to schedule next wakeup (if running) + if (m_bTsbPd) + pthread_cond_signal(&m_RcvTsbPdCond); + + // Shut up EPoll if no more messages in non-blocking mode + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + + // After signaling the tsbpd for ready data, report the bandwidth. + double bw SRT_ATR_UNUSED = Bps2Mbps(m_iBandwidth * m_iMaxSRTPayloadSize); + HLOGC(mglog.Debug, + log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth + << " buffers per second)"); + } + return res; + } + } + + int res = 0; + bool timeout = false; + // Do not block forever, check connection status each 1 sec. + uint64_t recvtmo = m_iRcvTimeOut < 0 ? 1000 : m_iRcvTimeOut; + + do + { + if (stillConnected() && !timeout && (!m_pRcvBuffer->isRcvDataReady())) + { + /* Kick TsbPd thread to schedule next wakeup (if running) */ + if (m_bTsbPd) + { + HLOGP(tslog.Debug, "recvmsg: KICK tsbpd()"); + pthread_cond_signal(&m_RcvTsbPdCond); + } + + do + { + if (CTimer::condTimedWaitUS(&m_RecvDataCond, &m_RecvLock, recvtmo * 1000) == ETIMEDOUT) + { + if (!(m_iRcvTimeOut < 0)) + timeout = true; + HLOGP(tslog.Debug, "recvmsg: DATA COND: EXPIRED -- trying to get data anyway"); + } + else + { + HLOGP(tslog.Debug, "recvmsg: DATA COND: KICKED."); + } + } while (stillConnected() && !timeout && (!m_pRcvBuffer->isRcvDataReady())); + } + + /* XXX DEBUG STUFF - enable when required + LOGC(dlog.Debug, "RECVMSG/GO-ON BROKEN " << m_bBroken << " CONN " << m_bConnected + << " CLOSING " << m_bClosing << " TMOUT " << timeout + << " NMSG " << m_pRcvBuffer->getRcvMsgNum()); + */ + + res = m_pRcvBuffer->readMsg(data, len, r_mctrl); + + if (m_bBroken || m_bClosing) + { + if (!m_bMessageAPI && m_bShutdown) + return 0; + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + else if (!m_bConnected) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + } while ((res == 0) && !timeout); + + if (!m_pRcvBuffer->isRcvDataReady()) + { + // Falling here means usually that res == 0 && timeout == true. + // res == 0 would repeat the above loop, unless there was also a timeout. + // timeout has interrupted the above loop, but with res > 0 this condition + // wouldn't be satisfied. + + // read is not available any more + + // Kick TsbPd thread to schedule next wakeup (if running) + if (m_bTsbPd) + { + HLOGP(tslog.Debug, "recvmsg: KICK tsbpd() (buffer empty)"); + pthread_cond_signal(&m_RcvTsbPdCond); + } + + // Shut up EPoll if no more messages in non-blocking mode + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + // Unblock when required + // LOGC(tslog.Debug, "RECVMSG/EXIT RES " << res << " RCVTIMEOUT"); + + if ((res <= 0) && (m_iRcvTimeOut >= 0)) + throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); + + return res; +} + +int64_t CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int block) +{ + if (m_bBroken || m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + else if (!m_bConnected || !m_CongCtl.ready()) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + + if (size <= 0 && size != -1) + return 0; + + if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_FILE, SrtCongestion::STAD_SEND, 0, size, -1, false)) + throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); + + if (!m_pCryptoControl || !m_pCryptoControl->isSndEncryptionOK()) + { + LOGC(dlog.Error, + log << "Encryption is required, but the peer did not supply correct credentials. Sending rejected."); + throw CUDTException(MJ_SETUP, MN_SECURITY, 0); + } + + CGuard sendguard(m_SendLock); + + if (m_pSndBuffer->getCurrBufSize() == 0) + { + // delay the EXP timer to avoid mis-fired timeout + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + // (fix keepalive) m_ullLastRspTime_tk = currtime_tk; + m_ullLastRspAckTime_tk = currtime_tk; + m_iReXmitCount = 1; + } + + // positioning... + try + { + if (size == -1) + { + ifs.seekg(0, std::ios::end); + size = ifs.tellg(); + if (offset > size) + throw 0; // let it be caught below + } + + // This will also set the position back to the beginning + // in case when it was moved to the end for measuring the size. + // This will also fail if the offset exceeds size, so measuring + // the size can be skipped if not needed. + ifs.seekg((streamoff)offset); + if (!ifs.good()) + throw 0; + } + catch (...) + { + // XXX It would be nice to note that this is reported + // by exception only if explicitly requested by setting + // the exception flags in the stream. Here it's fixed so + // that when this isn't set, the exception is "thrown manually". + throw CUDTException(MJ_FILESYSTEM, MN_SEEKGFAIL); + } + + int64_t tosend = size; + int unitsize; + + // sending block by block + while (tosend > 0) + { + if (ifs.fail()) + throw CUDTException(MJ_FILESYSTEM, MN_WRITEFAIL); + + if (ifs.eof()) + break; + + unitsize = int((tosend >= block) ? block : tosend); + + { + CGuard lk(m_SendBlockLock); + + while (stillConnected() && (sndBuffersLeft() <= 0) && m_bPeerHealth) + pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); + } + + if (m_bBroken || m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + else if (!m_bConnected) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + else if (!m_bPeerHealth) + { + // reset peer health status, once this error returns, the app should handle the situation at the peer side + m_bPeerHealth = true; + throw CUDTException(MJ_PEERERROR); + } + + // record total time used for sending + if (m_pSndBuffer->getCurrBufSize() == 0) + { + CGuard::enterCS(m_StatsLock); + m_stats.sndDurationCounter = CTimer::getTime(); + CGuard::leaveCS(m_StatsLock); + } + + { + CGuard recvAckLock(m_RecvAckLock); + const int64_t sentsize = m_pSndBuffer->addBufferFromFile(ifs, unitsize); + + if (sentsize > 0) + { + tosend -= sentsize; + offset += sentsize; + } + + if (sndBuffersLeft() <= 0) + { + // write is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); + } + } + + // insert this socket to snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); + } + + return size - tosend; +} + +int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) +{ + if (!m_bConnected || !m_CongCtl.ready()) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + { + if (!m_bMessageAPI && m_bShutdown) + return 0; + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + + if (size <= 0) + return 0; + + if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_FILE, SrtCongestion::STAD_RECV, 0, size, -1, false)) + throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); + + if (m_bTsbPd) + { + LOGC(dlog.Error, log << "Reading from file is incompatible with TSBPD mode and would cause a deadlock\n"); + throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); + } + + CGuard recvguard(m_RecvLock); + + // Well, actually as this works over a FILE (fstream), not just a stream, + // the size can be measured anyway and predicted if setting the offset might + // have a chance to work or not. + + // positioning... + try + { + if (offset > 0) + { + // Don't do anything around here if the offset == 0, as this + // is the default offset after opening. Whether this operation + // is performed correctly, it highly depends on how the file + // has been open. For example, if you want to overwrite parts + // of an existing file, the file must exist, and the ios::trunc + // flag must not be set. If the file is open for only ios::out, + // then the file will be truncated since the offset position on + // at the time when first written; if ios::in|ios::out, then + // it won't be truncated, just overwritten. + + // What is required here is that if offset is 0, don't try to + // change the offset because this might be impossible with + // the current flag set anyway. + + // Also check the status and CAUSE exception manually because + // you don't know, as well, whether the user has set exception + // flags. + + ofs.seekp((streamoff)offset); + if (!ofs.good()) + throw 0; // just to get caught :) + } + } + catch (...) + { + // XXX It would be nice to note that this is reported + // by exception only if explicitly requested by setting + // the exception flags in the stream. For a case, when it's not, + // an additional explicit throwing happens when failbit is set. + throw CUDTException(MJ_FILESYSTEM, MN_SEEKPFAIL); + } + + int64_t torecv = size; + int unitsize = block; + int recvsize; + + // receiving... "recvfile" is always blocking + while (torecv > 0) + { + if (ofs.fail()) + { + // send the sender a signal so it will not be blocked forever + int32_t err_code = CUDTException::EFILE; + sendCtrl(UMSG_PEERERROR, &err_code); + + throw CUDTException(MJ_FILESYSTEM, MN_WRITEFAIL); + } + + pthread_mutex_lock(&m_RecvDataLock); + while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) + pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); + pthread_mutex_unlock(&m_RecvDataLock); + + if (!m_bConnected) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + { + + if (!m_bMessageAPI && m_bShutdown) + return 0; + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + + unitsize = int((torecv == -1 || torecv >= block) ? block : torecv); + recvsize = m_pRcvBuffer->readBufferToFile(ofs, unitsize); + + if (recvsize > 0) + { + torecv -= recvsize; + offset += recvsize; + } + } + + if (!m_pRcvBuffer->isRcvDataReady()) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + return size - torecv; +} + +void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) +{ + if (!m_bConnected) + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + if (m_bBroken || m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + + CGuard statsguard(m_StatsLock); + + uint64_t currtime = CTimer::getTime(); + perf->msTimeStamp = (currtime - m_stats.startTime) / 1000; + + perf->pktSent = m_stats.traceSent; + perf->pktRecv = m_stats.traceRecv; + perf->pktSndLoss = m_stats.traceSndLoss; + perf->pktRcvLoss = m_stats.traceRcvLoss; + perf->pktRetrans = m_stats.traceRetrans; + perf->pktRcvRetrans = m_stats.traceRcvRetrans; + perf->pktSentACK = m_stats.sentACK; + perf->pktRecvACK = m_stats.recvACK; + perf->pktSentNAK = m_stats.sentNAK; + perf->pktRecvNAK = m_stats.recvNAK; + perf->usSndDuration = m_stats.sndDuration; + perf->pktReorderDistance = m_stats.traceReorderDistance; + perf->pktReorderTolerance = m_iReorderTolerance; + perf->pktRcvAvgBelatedTime = m_stats.traceBelatedTime; + perf->pktRcvBelated = m_stats.traceRcvBelated; + + perf->pktSndFilterExtra = m_stats.sndFilterExtra; + perf->pktRcvFilterExtra = m_stats.rcvFilterExtra; + perf->pktRcvFilterSupply = m_stats.rcvFilterSupply; + perf->pktRcvFilterLoss = m_stats.rcvFilterLoss; + + /* perf byte counters include all headers (SRT+UDP+IP) */ + const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; + perf->byteSent = m_stats.traceBytesSent + (m_stats.traceSent * pktHdrSize); + perf->byteRecv = m_stats.traceBytesRecv + (m_stats.traceRecv * pktHdrSize); + perf->byteRetrans = m_stats.traceBytesRetrans + (m_stats.traceRetrans * pktHdrSize); +#ifdef SRT_ENABLE_LOSTBYTESCOUNT + perf->byteRcvLoss = m_stats.traceRcvBytesLoss + (m_stats.traceRcvLoss * pktHdrSize); +#endif + + perf->pktSndDrop = m_stats.traceSndDrop; + perf->pktRcvDrop = m_stats.traceRcvDrop + m_stats.traceRcvUndecrypt; + perf->byteSndDrop = m_stats.traceSndBytesDrop + (m_stats.traceSndDrop * pktHdrSize); + perf->byteRcvDrop = + m_stats.traceRcvBytesDrop + (m_stats.traceRcvDrop * pktHdrSize) + m_stats.traceRcvBytesUndecrypt; + perf->pktRcvUndecrypt = m_stats.traceRcvUndecrypt; + perf->byteRcvUndecrypt = m_stats.traceRcvBytesUndecrypt; + + perf->pktSentTotal = m_stats.sentTotal; + perf->pktRecvTotal = m_stats.recvTotal; + perf->pktSndLossTotal = m_stats.sndLossTotal; + perf->pktRcvLossTotal = m_stats.rcvLossTotal; + perf->pktRetransTotal = m_stats.retransTotal; + perf->pktSentACKTotal = m_stats.sentACKTotal; + perf->pktRecvACKTotal = m_stats.recvACKTotal; + perf->pktSentNAKTotal = m_stats.sentNAKTotal; + perf->pktRecvNAKTotal = m_stats.recvNAKTotal; + perf->usSndDurationTotal = m_stats.m_sndDurationTotal; + + perf->byteSentTotal = m_stats.bytesSentTotal + (m_stats.sentTotal * pktHdrSize); + perf->byteRecvTotal = m_stats.bytesRecvTotal + (m_stats.recvTotal * pktHdrSize); + perf->byteRetransTotal = m_stats.bytesRetransTotal + (m_stats.retransTotal * pktHdrSize); + perf->pktSndFilterExtraTotal = m_stats.sndFilterExtraTotal; + perf->pktRcvFilterExtraTotal = m_stats.rcvFilterExtraTotal; + perf->pktRcvFilterSupplyTotal = m_stats.rcvFilterSupplyTotal; + perf->pktRcvFilterLossTotal = m_stats.rcvFilterLossTotal; + +#ifdef SRT_ENABLE_LOSTBYTESCOUNT + perf->byteRcvLossTotal = m_stats.rcvBytesLossTotal + (m_stats.rcvLossTotal * pktHdrSize); +#endif + perf->pktSndDropTotal = m_stats.sndDropTotal; + perf->pktRcvDropTotal = m_stats.rcvDropTotal + m_stats.m_rcvUndecryptTotal; + perf->byteSndDropTotal = m_stats.sndBytesDropTotal + (m_stats.sndDropTotal * pktHdrSize); + perf->byteRcvDropTotal = + m_stats.rcvBytesDropTotal + (m_stats.rcvDropTotal * pktHdrSize) + m_stats.m_rcvBytesUndecryptTotal; + perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; + perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; + //< + + double interval = double(currtime - m_stats.lastSampleTime); + + //>mod + perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; + perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; + //< + + perf->usPktSndPeriod = m_ullInterval_tk / double(m_ullCPUFrequency); + perf->pktFlowWindow = m_iFlowWindowSize; + perf->pktCongestionWindow = (int)m_dCongestionWindow; + perf->pktFlightSize = CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)) - 1; + perf->msRTT = (double)m_iRTT / 1000.0; + //>new + perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; + perf->msRcvTsbPdDelay = m_bTsbPd ? m_iTsbPdDelay_ms : 0; + perf->byteMSS = m_iMSS; + + perf->mbpsMaxBW = m_llMaxBW > 0 ? Bps2Mbps(m_llMaxBW) : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) : 0; + + //< + uint32_t availbw = (uint64_t)(m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth); + + perf->mbpsBandwidth = Bps2Mbps(availbw * (m_iMaxSRTPayloadSize + pktHdrSize)); + + if (pthread_mutex_trylock(&m_ConnectionLock) == 0) + { + if (m_pSndBuffer) + { +#ifdef SRT_ENABLE_SNDBUFSZ_MAVG + if (instantaneous) + { + /* Get instant SndBuf instead of moving average for application-based Algorithm + (such as NAE) in need of fast reaction to network condition changes. */ + perf->pktSndBuf = m_pSndBuffer->getCurrBufSize(Ref(perf->byteSndBuf), Ref(perf->msSndBuf)); + } + else + { + perf->pktSndBuf = m_pSndBuffer->getAvgBufSize(Ref(perf->byteSndBuf), Ref(perf->msSndBuf)); + } +#else + perf->pktSndBuf = m_pSndBuffer->getCurrBufSize(Ref(perf->byteSndBuf), Ref(perf->msSndBuf)); +#endif + perf->byteSndBuf += (perf->pktSndBuf * pktHdrSize); + //< + perf->byteAvailSndBuf = (m_iSndBufSize - perf->pktSndBuf) * m_iMSS; + } + else + { + perf->byteAvailSndBuf = 0; + // new> + perf->pktSndBuf = 0; + perf->byteSndBuf = 0; + perf->msSndBuf = 0; + //< + } + + if (m_pRcvBuffer) + { + perf->byteAvailRcvBuf = m_pRcvBuffer->getAvailBufSize() * m_iMSS; + // new> +#ifdef SRT_ENABLE_RCVBUFSZ_MAVG + if (instantaneous) // no need for historical API for Rcv side + { + perf->pktRcvBuf = m_pRcvBuffer->getRcvDataSize(perf->byteRcvBuf, perf->msRcvBuf); + } + else + { + perf->pktRcvBuf = m_pRcvBuffer->getRcvAvgDataSize(perf->byteRcvBuf, perf->msRcvBuf); + } +#else + perf->pktRcvBuf = m_pRcvBuffer->getRcvDataSize(perf->byteRcvBuf, perf->msRcvBuf); +#endif + //< + } + else + { + perf->byteAvailRcvBuf = 0; + // new> + perf->pktRcvBuf = 0; + perf->byteRcvBuf = 0; + perf->msRcvBuf = 0; + //< + } + + pthread_mutex_unlock(&m_ConnectionLock); + } + else + { + perf->byteAvailSndBuf = 0; + perf->byteAvailRcvBuf = 0; + // new> + perf->pktSndBuf = 0; + perf->byteSndBuf = 0; + perf->msSndBuf = 0; + + perf->byteRcvBuf = 0; + perf->msRcvBuf = 0; + //< + } + + if (clear) + { + m_stats.traceSndDrop = 0; + m_stats.traceRcvDrop = 0; + m_stats.traceSndBytesDrop = 0; + m_stats.traceRcvBytesDrop = 0; + m_stats.traceRcvUndecrypt = 0; + m_stats.traceRcvBytesUndecrypt = 0; + // new> + m_stats.traceBytesSent = m_stats.traceBytesRecv = m_stats.traceBytesRetrans = 0; + //< + m_stats.traceSent = m_stats.traceRecv = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans = + m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; + m_stats.sndDuration = 0; + m_stats.traceRcvRetrans = 0; + m_stats.traceRcvBelated = 0; +#ifdef SRT_ENABLE_LOSTBYTESCOUNT + m_stats.traceRcvBytesLoss = 0; +#endif + + m_stats.sndFilterExtra = 0; + m_stats.rcvFilterExtra = 0; + + m_stats.rcvFilterSupply = 0; + m_stats.rcvFilterLoss = 0; + + m_stats.lastSampleTime = currtime; + } +} + +void CUDT::updateCC(ETransmissionEvent evt, EventVariant arg) +{ + // Special things that must be done HERE, not in SrtCongestion, + // because it involves the input buffer in CUDT. It would be + // slightly dangerous to give SrtCongestion access to it. + + // According to the rules, the congctl should be ready at the same + // time when the sending buffer. For sanity check, check both first. + if (!m_CongCtl.ready() || !m_pSndBuffer) + { + LOGC(mglog.Error, + log << "updateCC: CAN'T DO UPDATE - congctl " << (m_CongCtl.ready() ? "ready" : "NOT READY") + << "; sending buffer " << (m_pSndBuffer ? "NOT CREATED" : "created")); + + return; + } + + HLOGC(mglog.Debug, log << "updateCC: EVENT:" << TransmissionEventStr(evt)); + + if (evt == TEV_INIT) + { + // only_input uses: + // 0: in the beginning and when SRTO_MAXBW was changed + // 1: SRTO_INPUTBW was changed + // 2: SRTO_OHEADBW was changed + EInitEvent only_input = arg.get(); + // false = TEV_INIT_RESET: in the beginning, or when MAXBW was changed. + + if (only_input && m_llMaxBW) + { + HLOGC(mglog.Debug, log << "updateCC/TEV_INIT: non-RESET stage and m_llMaxBW already set to " << m_llMaxBW); + // Don't change + } + else // either m_llMaxBW == 0 or only_input == TEV_INIT_RESET + { + // Use the values: + // - if SRTO_MAXBW is >0, use it. + // - if SRTO_MAXBW == 0, use SRTO_INPUTBW + SRTO_OHEADBW + // - if SRTO_INPUTBW == 0, pass 0 to requst in-buffer sampling + // Bytes/s + int bw = m_llMaxBW != 0 ? m_llMaxBW : // When used SRTO_MAXBW + m_llInputBW != 0 ? withOverhead(m_llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW + 0; // When both MAXBW and INPUTBW are 0, request in-buffer sampling + + // Note: setting bw == 0 uses BW_INFINITE value in LiveCC + m_CongCtl->updateBandwidth(m_llMaxBW, bw); + + if (only_input == TEV_INIT_OHEADBW) + { + // On updated SRTO_OHEADBW don't change input rate. + // This only influences the call to withOverhead(). + } + else + { + // No need to calculate input reate if the bandwidth is set + const bool disable_in_rate_calc = (bw != 0); + m_pSndBuffer->resetInputRateSmpPeriod(disable_in_rate_calc); + } + + HLOGC(mglog.Debug, + log << "updateCC/TEV_INIT: updating BW=" << m_llMaxBW + << (only_input == TEV_INIT_RESET + ? " (UNCHANGED)" + : only_input == TEV_INIT_OHEADBW ? " (only Overhead)" : " (updated sampling rate)")); + } + } + + // This part is also required only by LiveCC, however not + // moved there due to that it needs access to CSndBuffer. + if (evt == TEV_ACK || evt == TEV_LOSSREPORT || evt == TEV_CHECKTIMER) + { + // Specific part done when MaxBW is set to 0 (auto) and InputBW is 0. + // This requests internal input rate sampling. + if (m_llMaxBW == 0 && m_llInputBW == 0) + { + // Get auto-calculated input rate, Bytes per second + const int64_t inputbw = m_pSndBuffer->getInputRate(); + + /* + * On blocked transmitter (tx full) and until connection closes, + * auto input rate falls to 0 but there may be still lot of packet to retransmit + * Calling updateBandwidth with 0 sets maxBW to default BW_INFINITE (1 Gbps) + * and sendrate skyrockets for retransmission. + * Keep previously set maximum in that case (inputbw == 0). + */ + if (inputbw != 0) + m_CongCtl->updateBandwidth(0, withOverhead(inputbw)); // Bytes/sec + } + } + + HLOGC(mglog.Debug, log << "udpateCC: emitting signal for EVENT:" << TransmissionEventStr(evt)); + + // Now execute a congctl-defined action for that event. + EmitSignal(evt, arg); + + // This should be done with every event except ACKACK and SEND/RECEIVE + // After any action was done by the congctl, update the congestion window and sending interval. + if (evt != TEV_ACKACK && evt != TEV_SEND && evt != TEV_RECEIVE) + { + // This part comes from original UDT. + // NOTE: THESE things come from CCC class: + // - m_dPktSndPeriod + // - m_dCWndSize + m_ullInterval_tk = (uint64_t)(m_CongCtl->pktSndPeriod_us() * m_ullCPUFrequency); + m_dCongestionWindow = m_CongCtl->cgWindowSize(); +#if ENABLE_HEAVY_LOGGING + HLOGC(mglog.Debug, + log << "updateCC: updated values from congctl: interval=" << m_ullInterval_tk << "tk (" + << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" << std::setprecision(3) << m_dCongestionWindow); +#endif + } + + HLOGC(mglog.Debug, log << "udpateCC: finished handling for EVENT:" << TransmissionEventStr(evt)); + +#if 0 // debug + static int callcnt = 0; + if (!(callcnt++ % 250)) cerr << "SndPeriod=" << (m_ullInterval_tk/m_ullCPUFrequency) << "\n"); + +#endif +} + +void CUDT::initSynch() +{ + pthread_mutex_init(&m_SendBlockLock, NULL); + pthread_cond_init(&m_SendBlockCond, NULL); + pthread_mutex_init(&m_RecvDataLock, NULL); + pthread_cond_init(&m_RecvDataCond, NULL); + pthread_mutex_init(&m_SendLock, NULL); + pthread_mutex_init(&m_RecvLock, NULL); + pthread_mutex_init(&m_RcvLossLock, NULL); + pthread_mutex_init(&m_RecvAckLock, NULL); + pthread_mutex_init(&m_RcvBufferLock, NULL); + pthread_mutex_init(&m_ConnectionLock, NULL); + pthread_mutex_init(&m_StatsLock, NULL); + + memset(&m_RcvTsbPdThread, 0, sizeof m_RcvTsbPdThread); + pthread_cond_init(&m_RcvTsbPdCond, NULL); +} + +void CUDT::destroySynch() +{ + pthread_mutex_destroy(&m_SendBlockLock); + pthread_cond_destroy(&m_SendBlockCond); + pthread_mutex_destroy(&m_RecvDataLock); + pthread_cond_destroy(&m_RecvDataCond); + pthread_mutex_destroy(&m_SendLock); + pthread_mutex_destroy(&m_RecvLock); + pthread_mutex_destroy(&m_RcvLossLock); + pthread_mutex_destroy(&m_RecvAckLock); + pthread_mutex_destroy(&m_RcvBufferLock); + pthread_mutex_destroy(&m_ConnectionLock); + pthread_mutex_destroy(&m_StatsLock); + pthread_cond_destroy(&m_RcvTsbPdCond); +} + +void CUDT::releaseSynch() +{ + // wake up user calls + pthread_mutex_lock(&m_SendBlockLock); + pthread_cond_signal(&m_SendBlockCond); + pthread_mutex_unlock(&m_SendBlockLock); + + pthread_mutex_lock(&m_SendLock); + pthread_mutex_unlock(&m_SendLock); + + pthread_mutex_lock(&m_RecvDataLock); + pthread_cond_signal(&m_RecvDataCond); + pthread_mutex_unlock(&m_RecvDataLock); + + pthread_mutex_lock(&m_RecvLock); + pthread_cond_signal(&m_RcvTsbPdCond); + pthread_mutex_unlock(&m_RecvLock); + + pthread_mutex_lock(&m_RecvDataLock); + if (!pthread_equal(m_RcvTsbPdThread, pthread_t())) + { + pthread_join(m_RcvTsbPdThread, NULL); + m_RcvTsbPdThread = pthread_t(); + } + pthread_mutex_unlock(&m_RecvDataLock); + + pthread_mutex_lock(&m_RecvLock); + pthread_mutex_unlock(&m_RecvLock); +} + +#if ENABLE_HEAVY_LOGGING +static void DebugAck(string hdr, int prev, int ack) +{ + if (!prev) + { + HLOGC(mglog.Debug, log << hdr << "ACK " << ack); + return; + } + + prev = CSeqNo::incseq(prev); + int diff = CSeqNo::seqoff(prev, ack); + if (diff < 0) + { + HLOGC(mglog.Debug, log << hdr << "ACK ERROR: " << prev << "-" << ack << "(diff " << diff << ")"); + return; + } + + bool shorted = diff > 100; // sanity + if (shorted) + ack = CSeqNo::incseq(prev, 100); + + ostringstream ackv; + for (; prev != ack; prev = CSeqNo::incseq(prev)) + ackv << prev << " "; + if (shorted) + ackv << "..."; + HLOGC(mglog.Debug, log << hdr << "ACK (" << (diff + 1) << "): " << ackv.str() << ack); +} +#else +static inline void DebugAck(string, int, int) {} +#endif + +void CUDT::sendCtrl(UDTMessageType pkttype, const void *lparam, void *rparam, int size) +{ + CPacket ctrlpkt; + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + + ctrlpkt.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime); + + int nbsent = 0; + int local_prevack = 0; + +#if ENABLE_HEAVY_LOGGING + struct SaveBack + { + int & target; + const int &source; + + ~SaveBack() { target = source; } + } l_saveback = {m_iDebugPrevLastAck, m_iRcvLastAck}; + (void)l_saveback; // kill compiler warning: unused variable `l_saveback` [-Wunused-variable] + + local_prevack = m_iDebugPrevLastAck; +#endif + + switch (pkttype) + { + case UMSG_ACK: // 010 - Acknowledgement + { + int32_t ack; + + // If there is no loss, the ACK is the current largest sequence number plus 1; + // Otherwise it is the smallest sequence number in the receiver loss list. + if (m_pRcvLossList->getLossLength() == 0) + ack = CSeqNo::incseq(m_iRcvCurrSeqNo); + else + ack = m_pRcvLossList->getFirstLostSeq(); + + if (m_iRcvLastAckAck == ack) + break; + + // send out a lite ACK + // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number + if (size == SEND_LITE_ACK) + { + ctrlpkt.pack(pkttype, NULL, &ack, size); + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + DebugAck("sendCtrl(lite):" + CONID(), local_prevack, ack); + break; + } + + // There are new received packets to acknowledge, update related information. + /* tsbpd thread may also call ackData when skipping packet so protect code */ + CGuard::enterCS(m_RcvBufferLock); + + // IF ack > m_iRcvLastAck + if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) + { + int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); + + IF_HEAVY_LOGGING(int32_t oldack = m_iRcvLastSkipAck); + m_iRcvLastAck = ack; + m_iRcvLastSkipAck = ack; + + // XXX Unknown as to whether it matters. + // This if (acksize) causes that ackData() won't be called. + // With size == 0 it wouldn't do anything except calling CTimer::triggerEvent(). + // This, again, signals the condition, CTimer::m_EventCond. + // This releases CTimer::waitForEvent() call used in CUDTUnited::selectEx(). + // Preventing to call this on zero size makes sense, if it prevents false alerts. + if (acksize > 0) + m_pRcvBuffer->ackData(acksize); + CGuard::leaveCS(m_RcvBufferLock); + + // If TSBPD is enabled, then INSTEAD OF signaling m_RecvDataCond, + // signal m_RcvTsbPdCond. This will kick in the tsbpd thread, which + // will signal m_RecvDataCond when there's time to play for particular + // data packet. + HLOGC(dlog.Debug, + log << "ACK: clip %" << oldack << "-%" << ack << ", REVOKED " << acksize << " from RCV buffer"); + + if (m_bTsbPd) + { + /* Newly acknowledged data, signal TsbPD thread */ + pthread_mutex_lock(&m_RecvLock); + if (m_bTsbPdAckWakeup) + pthread_cond_signal(&m_RcvTsbPdCond); + pthread_mutex_unlock(&m_RecvLock); + } + else + { + if (m_bSynRecving) + { + // signal a waiting "recv" call if there is any data available + pthread_mutex_lock(&m_RecvDataLock); + pthread_cond_signal(&m_RecvDataCond); + pthread_mutex_unlock(&m_RecvDataLock); + } + // acknowledge any waiting epolls to read + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true); + CTimer::triggerEvent(); + } + CGuard::enterCS(m_RcvBufferLock); + } + else if (ack == m_iRcvLastAck) + { + // If the ACK was just sent already AND elapsed time did not exceed RTT, + if ((currtime_tk - m_ullLastAckTime_tk) < ((m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency)) + { + CGuard::leaveCS(m_RcvBufferLock); + break; + } + } + else + { + // Not possible (m_iRcvCurrSeqNo+1 < m_iRcvLastAck ?) + CGuard::leaveCS(m_RcvBufferLock); + break; + } + + // [[using assert( ack >= m_iRcvLastAck && is_periodic_ack ) ]] + + // Send out the ACK only if has not been received by the sender before + if (CSeqNo::seqcmp(m_iRcvLastAck, m_iRcvLastAckAck) > 0) + { + // NOTE: The BSTATS feature turns on extra fields above size 6 + // also known as ACKD_TOTAL_SIZE_VER100. + int32_t data[ACKD_TOTAL_SIZE]; + + // Case you care, CAckNo::incack does exactly the same thing as + // CSeqNo::incseq. Logically the ACK number is a different thing + // than sequence number (it's a "journal" for ACK request-response, + // and starts from 0, unlike sequence, which starts from a random + // number), but still the numbers are from exactly the same domain. + m_iAckSeqNo = CAckNo::incack(m_iAckSeqNo); + data[ACKD_RCVLASTACK] = m_iRcvLastAck; + data[ACKD_RTT] = m_iRTT; + data[ACKD_RTTVAR] = m_iRTTVar; + data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); + // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock + if (data[ACKD_BUFFERLEFT] < 2) + data[ACKD_BUFFERLEFT] = 2; + + // NOTE: m_CongCtl->ACKTimeout_us() should be taken into account. + if (currtime_tk - m_ullLastAckTime_tk > m_ullACKInt_tk) + { + int rcvRate; + int ctrlsz = ACKD_TOTAL_SIZE_UDTBASE * ACKD_FIELD_SIZE; // Minimum required size + + data[ACKD_RCVSPEED] = m_RcvTimeWindow.getPktRcvSpeed(Ref(rcvRate)); + data[ACKD_BANDWIDTH] = m_RcvTimeWindow.getBandwidth(); + + //>>Patch while incompatible (1.0.2) receiver floating around + if (m_lPeerSrtVersion == SrtVersion(1, 0, 2)) + { + data[ACKD_RCVRATE] = rcvRate; // bytes/sec + data[ACKD_XMRATE] = data[ACKD_BANDWIDTH] * m_iMaxSRTPayloadSize; // bytes/sec + ctrlsz = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER102; + } + else if (m_lPeerSrtVersion >= SrtVersion(1, 0, 3)) + { + // Normal, currently expected version. + data[ACKD_RCVRATE] = rcvRate; // bytes/sec + ctrlsz = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER101; + } + // ELSE: leave the buffer with ...UDTBASE size. + + ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, ctrlsz); + CTimer::rdtsc(m_ullLastAckTime_tk); + } + else + { + ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL); + } + + ctrlpkt.m_iID = m_PeerID; + ctrlpkt.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime); + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + DebugAck("sendCtrl: " + CONID(), local_prevack, ack); + + m_ACKWindow.store(m_iAckSeqNo, m_iRcvLastAck); + + CGuard::enterCS(m_StatsLock); + ++m_stats.sentACK; + ++m_stats.sentACKTotal; + CGuard::leaveCS(m_StatsLock); + } + CGuard::leaveCS(m_RcvBufferLock); + break; + } + + case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement + ctrlpkt.pack(pkttype, lparam); + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case UMSG_LOSSREPORT: // 011 - Loss Report + { + // Explicitly defined lost sequences + if (rparam) + { + int32_t *lossdata = (int32_t *)rparam; + + size_t bytes = sizeof(*lossdata) * size; + ctrlpkt.pack(pkttype, NULL, lossdata, bytes); + + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + CGuard::enterCS(m_StatsLock); + ++m_stats.sentNAK; + ++m_stats.sentNAKTotal; + CGuard::leaveCS(m_StatsLock); + } + // Call with no arguments - get loss list from internal data. + else if (m_pRcvLossList->getLossLength() > 0) + { + // this is periodically NAK report; make sure NAK cannot be sent back too often + + // read loss list from the local receiver loss list + int32_t *data = new int32_t[m_iMaxSRTPayloadSize / 4]; + int losslen; + m_pRcvLossList->getLossArray(data, losslen, m_iMaxSRTPayloadSize / 4); + + if (0 < losslen) + { + ctrlpkt.pack(pkttype, NULL, data, losslen * 4); + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + CGuard::enterCS(m_StatsLock); + ++m_stats.sentNAK; + ++m_stats.sentNAKTotal; + CGuard::leaveCS(m_StatsLock); + } + + delete[] data; + } + + // update next NAK time, which should wait enough time for the retansmission, but not too long + m_ullNAKInt_tk = (m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency; + + // Fix the NAKreport period according to the congctl + m_ullNAKInt_tk = m_CongCtl->updateNAKInterval( + m_ullNAKInt_tk, m_RcvTimeWindow.getPktRcvSpeed(), m_pRcvLossList->getLossLength()); + + // This is necessary because a congctl need not wish to define + // its own minimum interval, in which case the default one is used. + if (m_ullNAKInt_tk < m_ullMinNakInt_tk) + m_ullNAKInt_tk = m_ullMinNakInt_tk; + + break; + } + + case UMSG_CGWARNING: // 100 - Congestion Warning + ctrlpkt.pack(pkttype); + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + CTimer::rdtsc(m_ullLastWarningTime); + + break; + + case UMSG_KEEPALIVE: // 001 - Keep-alive + ctrlpkt.pack(pkttype); + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case UMSG_HANDSHAKE: // 000 - Handshake + ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case UMSG_SHUTDOWN: // 101 - Shutdown + ctrlpkt.pack(pkttype); + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case UMSG_DROPREQ: // 111 - Msg drop request + ctrlpkt.pack(pkttype, lparam, rparam, 8); + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error + ctrlpkt.pack(pkttype, lparam); + ctrlpkt.m_iID = m_PeerID; + nbsent = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case UMSG_EXT: // 0x7FFF - Resevered for future use + break; + + default: + break; + } + + // Fix keepalive + if (nbsent) + m_ullLastSndTime_tk = currtime_tk; +} + +void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) +{ + // Update sender's loss list and acknowledge packets in the sender's buffer + { + // m_RecvAckLock protects sender's loss list and epoll + CGuard ack_lock(m_RecvAckLock); + + const int offset = CSeqNo::seqoff(m_iSndLastDataAck, ackdata_seqno); + // IF distance between m_iSndLastDataAck and ack is nonempty... + if (offset <= 0) + return; + + // update sending variables + m_iSndLastDataAck = ackdata_seqno; + + // remove any loss that predates 'ack' (not to be considered loss anymore) + m_pSndLossList->remove(CSeqNo::decseq(m_iSndLastDataAck)); + + // acknowledge the sending buffer (remove data that predate 'ack') + m_pSndBuffer->ackData(offset); + + // acknowledde any waiting epolls to write + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + } + + // insert this socket to snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); + + if (m_bSynSending) + { + CGuard lk(m_SendBlockLock); + pthread_cond_signal(&m_SendBlockCond); + } + + const int64_t currtime = CTimer::getTime(); + // record total time used for sending + CGuard::enterCS(m_StatsLock); + m_stats.sndDuration += currtime - m_stats.sndDurationCounter; + m_stats.m_sndDurationTotal += currtime - m_stats.sndDurationCounter; + m_stats.sndDurationCounter = currtime; + CGuard::leaveCS(m_StatsLock); +} + +void CUDT::processCtrlAck(const CPacket &ctrlpkt, const uint64_t currtime_tk) +{ + const int32_t *ackdata = (const int32_t *)ctrlpkt.m_pcData; + const int32_t ackdata_seqno = ackdata[ACKD_RCVLASTACK]; + + const bool isLiteAck = ctrlpkt.getLength() == (size_t)SEND_LITE_ACK; + HLOGC(mglog.Debug, + log << CONID() << "ACK covers: " << m_iSndLastDataAck << " - " << ackdata_seqno << " [ACK=" << m_iSndLastAck + << "]" << (isLiteAck ? "[LITE]" : "[FULL]")); + + updateSndLossListOnACK(ackdata_seqno); + + // Process a lite ACK + if (isLiteAck) + { + if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0) + { + CGuard ack_lock(m_RecvAckLock); + m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ackdata_seqno); + m_iSndLastAck = ackdata_seqno; + + // TODO: m_ullLastRspAckTime_tk should be protected with m_RecvAckLock + // because the sendmsg2 may want to change it at the same time. + m_ullLastRspAckTime_tk = currtime_tk; + m_iReXmitCount = 1; // Reset re-transmit count since last ACK + } + + return; + } + + // Decide to send ACKACK or not + { + // Sequence number of the ACK packet + const int32_t ack_seqno = ctrlpkt.getAckSeqNo(); + + // Send ACK acknowledgement (UMSG_ACKACK). + // There can be less ACKACK packets in the stream, than the number of ACK packets. + // Only send ACKACK every syn interval or if ACK packet with the sequence number + // already acknowledged (with ACKACK) has come again, which probably means ACKACK was lost. + const uint64_t now = CTimer::getTime(); + if ((now - m_ullSndLastAck2Time > (uint64_t)COMM_SYN_INTERVAL_US) || (ack_seqno == m_iSndLastAck2)) + { + sendCtrl(UMSG_ACKACK, &ack_seqno); + m_iSndLastAck2 = ack_seqno; + m_ullSndLastAck2Time = now; + } + } + + // + // Begin of the new code with TLPKTDROP. + // + + // Protect packet retransmission + CGuard::enterCS(m_RecvAckLock); + + // Check the validation of the ack + if (CSeqNo::seqcmp(ackdata_seqno, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0) + { + CGuard::leaveCS(m_RecvAckLock); + // this should not happen: attack or bug + LOGC(glog.Error, + log << CONID() << "ATTACK/IPE: incoming ack seq " << ackdata_seqno << " exceeds current " + << m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!"); + m_bBroken = true; + m_iBrokenCounter = 0; + return; + } + + if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0) + { + // Update Flow Window Size, must update before and together with m_iSndLastAck + m_iFlowWindowSize = ackdata[ACKD_BUFFERLEFT]; + m_iSndLastAck = ackdata_seqno; + m_ullLastRspAckTime_tk = currtime_tk; // Should be protected with m_RecvAckLock + m_iReXmitCount = 1; // Reset re-transmit count since last ACK + } + + /* + * We must not ignore full ack received by peer + * if data has been artificially acked by late packet drop. + * Therefore, a distinct ack state is used for received Ack (iSndLastFullAck) + * and ack position in send buffer (m_iSndLastDataAck). + * Otherwise, when severe congestion causing packet drops (and m_iSndLastDataAck update) + * occures, we drop received acks (as duplicates) and do not update stats like RTT, + * which may go crazy and stay there, preventing proper stream recovery. + */ + + if (CSeqNo::seqoff(m_iSndLastFullAck, ackdata_seqno) <= 0) + { + // discard it if it is a repeated ACK + CGuard::leaveCS(m_RecvAckLock); + return; + } + m_iSndLastFullAck = ackdata_seqno; + + // + // END of the new code with TLPKTDROP + // + CGuard::leaveCS(m_RecvAckLock); + + size_t acksize = ctrlpkt.getLength(); // TEMPORARY VALUE FOR CHECKING + bool wrongsize = 0 != (acksize % ACKD_FIELD_SIZE); + acksize = acksize / ACKD_FIELD_SIZE; // ACTUAL VALUE + + if (wrongsize) + { + // Issue a log, but don't do anything but skipping the "odd" bytes from the payload. + LOGC(mglog.Error, + log << CONID() << "Received UMSG_ACK payload is not evened up to 4-byte based field size - cutting to " + << acksize << " fields"); + } + + // Start with checking the base size. + if (acksize < ACKD_TOTAL_SIZE_SMALL) + { + LOGC(mglog.Error, log << CONID() << "Invalid ACK size " << acksize << " fields - less than minimum required!"); + // Ack is already interpreted, just skip further parts. + return; + } + // This check covers fields up to ACKD_BUFFERLEFT. + + // Update RTT + // m_iRTT = ackdata[ACKD_RTT]; + // m_iRTTVar = ackdata[ACKD_RTTVAR]; + // XXX These ^^^ commented-out were blocked in UDT; + // the current RTT calculations are exactly the same as in UDT4. + const int rtt = ackdata[ACKD_RTT]; + + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); + m_iRTT = avg_iir<8>(m_iRTT, rtt); + + /* Version-dependent fields: + * Original UDT (total size: ACKD_TOTAL_SIZE_SMALL): + * ACKD_RCVLASTACK + * ACKD_RTT + * ACKD_RTTVAR + * ACKD_BUFFERLEFT + * Additional UDT fields, not always attached: + * ACKD_RCVSPEED + * ACKD_BANDWIDTH + * SRT extension version 1.0.2 (bstats): + * ACKD_RCVRATE + * SRT extension version 1.0.4: + * ACKD_XMRATE + */ + + if (acksize > ACKD_TOTAL_SIZE_SMALL) + { + // This means that ACKD_RCVSPEED and ACKD_BANDWIDTH fields are available. + int pktps = ackdata[ACKD_RCVSPEED]; + int bandwidth = ackdata[ACKD_BANDWIDTH]; + int bytesps; + + /* SRT v1.0.2 Bytes-based stats: bandwidth (pcData[ACKD_XMRATE]) and delivery rate (pcData[ACKD_RCVRATE]) in + * bytes/sec instead of pkts/sec */ + /* SRT v1.0.3 Bytes-based stats: only delivery rate (pcData[ACKD_RCVRATE]) in bytes/sec instead of pkts/sec */ + if (acksize > ACKD_TOTAL_SIZE_UDTBASE) + bytesps = ackdata[ACKD_RCVRATE]; + else + bytesps = pktps * m_iMaxSRTPayloadSize; + + m_iBandwidth = avg_iir<8>(m_iBandwidth, bandwidth); + m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate, pktps); + m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate, bytesps); + // XXX not sure if ACKD_XMRATE is of any use. This is simply + // calculated as ACKD_BANDWIDTH * m_iMaxSRTPayloadSize. + + // Update Estimated Bandwidth and packet delivery rate + // m_iRcvRate = m_iDeliveryRate; + // ^^ This has been removed because with the SrtCongestion class + // instead of reading the m_iRcvRate local field this will read + // cudt->deliveryRate() instead. + } + + checkSndTimers(REGEN_KM); + updateCC(TEV_ACK, ackdata_seqno); + + CGuard::enterCS(m_StatsLock); + ++m_stats.recvACK; + ++m_stats.recvACKTotal; + CGuard::leaveCS(m_StatsLock); +} + +void CUDT::processCtrl(CPacket &ctrlpkt) +{ + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + m_ullLastRspTime_tk = currtime_tk; + bool using_rexmit_flag = m_bPeerRexmitFlag; + + HLOGC(mglog.Debug, + log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID); + + switch (ctrlpkt.getType()) + { + case UMSG_ACK: // 010 - Acknowledgement + processCtrlAck(ctrlpkt, currtime_tk); + break; + + case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement + { + int32_t ack = 0; + int rtt = -1; + + // update RTT + rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack); + if (rtt <= 0) + { + LOGC(mglog.Error, + log << "IPE: ACK node overwritten when acknowledging " << ctrlpkt.getAckSeqNo() + << " (ack extracted: " << ack << ")"); + break; + } + + // if increasing delay detected... + // sendCtrl(UMSG_CGWARNING); + + // RTT EWMA + m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2; + m_iRTT = (m_iRTT * 7 + rtt) >> 3; + + updateCC(TEV_ACKACK, ack); + + // This function will put a lock on m_RecvLock by itself, as needed. + // It must be done inside because this function reads the current time + // and if waiting for the lock has caused a delay, the time will be + // inaccurate. Additionally it won't lock if TSBPD mode is off, and + // won't update anything. Note that if you set TSBPD mode and use + // srt_recvfile (which doesn't make any sense), you'll have a deadlock. + m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock); + + // update last ACK that has been received by the sender + if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) + m_iRcvLastAckAck = ack; + + break; + } + + case UMSG_LOSSREPORT: // 011 - Loss Report + { + int32_t *losslist = (int32_t *)(ctrlpkt.m_pcData); + size_t losslist_len = ctrlpkt.getLength() / 4; + + bool secure = true; + + // protect packet retransmission + CGuard::enterCS(m_RecvAckLock); + + // This variable is used in "normal" logs, so it may cause a warning + // when logging is forcefully off. + int32_t wrong_loss SRT_ATR_UNUSED = CSeqNo::m_iMaxSeqNo; + + // decode loss list message and insert loss into the sender loss list + for (int i = 0, n = (int)(ctrlpkt.getLength() / 4); i < n; ++i) + { + if (IsSet(losslist[i], LOSSDATA_SEQNO_RANGE_FIRST)) + { + // Then it's this is a specification with HI in a consecutive cell. + int32_t losslist_lo = SEQNO_VALUE::unwrap(losslist[i]); + int32_t losslist_hi = losslist[i + 1]; + // specification means that the consecutive cell has been already interpreted. + ++i; + + HLOGF(mglog.Debug, + "received UMSG_LOSSREPORT: %d-%d (%d packets)...", + losslist_lo, + losslist_hi, + CSeqNo::seqoff(losslist_lo, losslist_hi) + 1); + + if ((CSeqNo::seqcmp(losslist_lo, losslist_hi) > 0) || + (CSeqNo::seqcmp(losslist_hi, m_iSndCurrSeqNo) > 0)) + { + // seq_a must not be greater than seq_b; seq_b must not be greater than the most recent sent seq + secure = false; + wrong_loss = losslist_hi; + // XXX leaveCS: really necessary? 'break' will break the 'for' loop, not the 'switch' statement. + // and the leaveCS is done again next to the 'for' loop end. + CGuard::leaveCS(m_RecvAckLock); + break; + } + + int num = 0; + if (CSeqNo::seqcmp(losslist_lo, m_iSndLastAck) >= 0) + num = m_pSndLossList->insert(losslist_lo, losslist_hi); + else if (CSeqNo::seqcmp(losslist_hi, m_iSndLastAck) >= 0) + { + // This should be theoretically impossible because this would mean + // that the received packet loss report informs about the loss that predates + // the ACK sequence. + // However, this can happen if the packet reordering has caused the earlier sent + // LOSSREPORT will be delivered after later sent ACK. Whatever, ACK should be + // more important, so simply drop the part that predates ACK. + num = m_pSndLossList->insert(m_iSndLastAck, losslist_hi); + } + + CGuard::enterCS(m_StatsLock); + m_stats.traceSndLoss += num; + m_stats.sndLossTotal += num; + CGuard::leaveCS(m_StatsLock); + } + else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0) + { + HLOGF(mglog.Debug, "received UMSG_LOSSREPORT: %d (1 packet)...", losslist[i]); + + if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0) + { + // seq_a must not be greater than the most recent sent seq + secure = false; + wrong_loss = losslist[i]; + CGuard::leaveCS(m_RecvAckLock); + break; + } + + int num = m_pSndLossList->insert(losslist[i], losslist[i]); + + CGuard::enterCS(m_StatsLock); + m_stats.traceSndLoss += num; + m_stats.sndLossTotal += num; + CGuard::leaveCS(m_StatsLock); + } + } + CGuard::leaveCS(m_RecvAckLock); + + updateCC(TEV_LOSSREPORT, EventVariant(losslist, losslist_len)); + + if (!secure) + { + LOGC(mglog.Warn, + log << "out-of-band LOSSREPORT received; BUG or ATTACK - last sent %" << m_iSndCurrSeqNo + << " vs loss %" << wrong_loss); + // this should not happen: attack or bug + m_bBroken = true; + m_iBrokenCounter = 0; + break; + } + + // the lost packet (retransmission) should be sent out immediately + m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); + + CGuard::enterCS(m_StatsLock); + ++m_stats.recvNAK; + ++m_stats.recvNAKTotal; + CGuard::leaveCS(m_StatsLock); + + break; + } + + case UMSG_CGWARNING: // 100 - Delay Warning + // One way packet delay is increasing, so decrease the sending rate + m_ullInterval_tk = (uint64_t)ceil(m_ullInterval_tk * 1.125); + m_iLastDecSeq = m_iSndCurrSeqNo; + // XXX Note as interesting fact: this is only prepared for handling, + // but nothing in the code is sending this message. Probably predicted + // for a custom congctl. There's a predicted place to call it under + // UMSG_ACKACK handling, but it's commented out. + + break; + + case UMSG_KEEPALIVE: // 001 - Keep-alive + // The only purpose of keep-alive packet is to tell that the peer is still alive + // nothing needs to be done. + + break; + + case UMSG_HANDSHAKE: // 000 - Handshake + { + CHandShake req; + req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength()); + + HLOGC(mglog.Debug, log << "processCtrl: got HS: " << req.show()); + + if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...??? + || (m_bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION + { + // The peer side has not received the handshake message, so it keeps querying + // resend the handshake packet + + // This condition embraces cases when: + // - this is normal accept() and URQ_INDUCTION was received + // - this is rendezvous accept() and there's coming any kind of URQ except AGREEMENT (should be RENDEZVOUS + // or CONCLUSION) + // - this is any of URQ_ERROR_* - well... + CHandShake initdata; + initdata.m_iISN = m_iISN; + initdata.m_iMSS = m_iMSS; + initdata.m_iFlightFlagSize = m_iFlightFlagSize; + + // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT. + // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION. + initdata.m_iReqType = (!m_bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT; + initdata.m_iID = m_SocketID; + + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + bool have_hsreq = false; + if (req.m_iVersion > HS_VERSION_UDT4) + { + initdata.m_iVersion = HS_VERSION_SRT1; // if I remember correctly, this is induction/listener... + int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + if (hs_flags != 0) // has SRT extensions + { + HLOGC(mglog.Debug, + log << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType) + << " WITH SRT ext"); + have_hsreq = interpretSrtHandshake(req, ctrlpkt, kmdata, &kmdatasize); + if (!have_hsreq) + { + initdata.m_iVersion = 0; + m_RejectReason = SRT_REJ_ROGUE; + initdata.m_iReqType = URQFailure(m_RejectReason); + } + else + { + // Extensions are added only in case of CONCLUSION (not AGREEMENT). + // Actually what is expected here is that this may either process the + // belated-repeated handshake from a caller (and then it's CONCLUSION, + // and should be added with HSRSP/KMRSP), or it's a belated handshake + // of Rendezvous when it has already considered itself connected. + // Sanity check - according to the rules, there should be no such situation + if (m_bRendezvous && m_SrtHsSide == HSD_RESPONDER) + { + LOGC(mglog.Error, + log << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in " + "handshake phase."); + } + + // The 'extension' flag will be set from this variable; set it to false + // in case when the AGREEMENT response is to be sent. + have_hsreq = initdata.m_iReqType == URQ_CONCLUSION; + HLOGC(mglog.Debug, + log << "processCtrl/HS: processing ok, reqtype=" << RequestTypeStr(initdata.m_iReqType) + << " kmdatasize=" << kmdatasize); + } + } + else + { + HLOGC(mglog.Debug, log << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType)); + } + } + else + { + initdata.m_iVersion = HS_VERSION_UDT4; + } + + initdata.m_extension = have_hsreq; + + HLOGC(mglog.Debug, + log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) + << (have_hsreq ? " WITH SRT HS response extensions" : "")); + + // XXX here interpret SRT handshake extension + CPacket response; + response.setControl(UMSG_HANDSHAKE); + response.allocate(m_iMaxSRTPayloadSize); + + // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. + // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. + if (createSrtHandshake(Ref(response), Ref(initdata), SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize)) + { + response.m_iID = m_PeerID; + response.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime); + int nbsent = m_pSndQueue->sendto(m_pPeerAddr, response); + if (nbsent) + { + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + m_ullLastSndTime_tk = currtime_tk; + } + } + } + else + { + HLOGC(mglog.Debug, log << "processCtrl: ... not INDUCTION, not ERROR, not rendezvous - IGNORED."); + } + + break; + } + + case UMSG_SHUTDOWN: // 101 - Shutdown + m_bShutdown = true; + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 60; + + // Signal the sender and recver if they are waiting for data. + releaseSynch(); + // Unblock any call so they learn the connection_broken error + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true); + + CTimer::triggerEvent(); + + break; + + case UMSG_DROPREQ: // 111 - Msg drop request + CGuard::enterCS(m_RecvLock); + m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); + CGuard::leaveCS(m_RecvLock); + + dropFromLossLists(*(int32_t *)ctrlpkt.m_pcData, *(int32_t *)(ctrlpkt.m_pcData + 4)); + + // move forward with current recv seq no. + if ((CSeqNo::seqcmp(*(int32_t *)ctrlpkt.m_pcData, CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) && + (CSeqNo::seqcmp(*(int32_t *)(ctrlpkt.m_pcData + 4), m_iRcvCurrSeqNo) > 0)) + { + m_iRcvCurrSeqNo = *(int32_t *)(ctrlpkt.m_pcData + 4); + } + + break; + + case UMSG_PEERERROR: // 1000 - An error has happened to the peer side + // int err_type = packet.getAddInfo(); + + // currently only this error is signalled from the peer side + // if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately + // giving the app a chance to fix the issue + + m_bPeerHealth = false; + + break; + + case UMSG_EXT: // 0x7FFF - reserved and user defined messages + HLOGF(mglog.Debug, "CONTROL EXT MSG RECEIVED: %08X\n", ctrlpkt.getExtendedType()); + { + // This has currently two roles in SRT: + // - HSv4 (legacy) handshake + // - refreshed KMX (initial KMX is done still in the HS process in HSv5) + bool understood = processSrtMsg(&ctrlpkt); + // CAREFUL HERE! This only means that this update comes from the UMSG_EXT + // message received, REGARDLESS OF WHAT IT IS. This version doesn't mean + // the handshake version, but the reason of calling this function. + // + // Fortunately, the only messages taken into account in this function + // are HSREQ and HSRSP, which should *never* be interchanged when both + // parties are HSv5. + if (understood) + { + updateAfterSrtHandshake(ctrlpkt.getExtendedType(), HS_VERSION_UDT4); + } + else + { + updateCC(TEV_CUSTOM, &ctrlpkt); + } + } + break; + + default: + break; + } +} + +void CUDT::updateSrtRcvSettings() +{ + if (m_bTsbPd) + { + /* We are TsbPd receiver */ + CGuard::enterCS(m_RecvLock); + m_pRcvBuffer->setRcvTsbPdMode(m_ullRcvPeerStartTime, m_iTsbPdDelay_ms * 1000); + CGuard::leaveCS(m_RecvLock); + + HLOGF(mglog.Debug, + "AFTER HS: Set Rcv TsbPd mode: delay=%u.%03u secs", + m_iTsbPdDelay_ms / 1000, + m_iTsbPdDelay_ms % 1000); + } + else + { + HLOGC(mglog.Debug, log << "AFTER HS: Rcv TsbPd mode not set"); + } +} + +void CUDT::updateSrtSndSettings() +{ + if (m_bPeerTsbPd) + { + /* We are TsbPd sender */ + // XXX Check what happened here. + // m_iPeerTsbPdDelay_ms = m_CongCtl->getSndPeerTsbPdDelay();// + ((m_iRTT + (4 * m_iRTTVar)) / 1000); + /* + * For sender to apply Too-Late Packet Drop + * option (m_bTLPktDrop) must be enabled and receiving peer shall support it + */ + HLOGF(mglog.Debug, + "AFTER HS: Set Snd TsbPd mode %s: delay=%d.%03d secs", + m_bPeerTLPktDrop ? "with TLPktDrop" : "without TLPktDrop", + m_iPeerTsbPdDelay_ms / 1000, + m_iPeerTsbPdDelay_ms % 1000); + } + else + { + HLOGC(mglog.Debug, log << "AFTER HS: Snd TsbPd mode not set"); + } +} + +void CUDT::updateAfterSrtHandshake(int srt_cmd, int hsv) +{ + + switch (srt_cmd) + { + case SRT_CMD_HSREQ: + case SRT_CMD_HSRSP: + break; + default: + return; + } + + // The only possibility here is one of these two: + // - Agent is RESPONDER and it receives HSREQ. + // - Agent is INITIATOR and it receives HSRSP. + // + // In HSv4, INITIATOR is sender and RESPONDER is receiver. + // In HSv5, both are sender AND receiver. + // + // This function will be called only ONCE in this + // instance, through either HSREQ or HSRSP. + + if (hsv > HS_VERSION_UDT4) + { + updateSrtRcvSettings(); + updateSrtSndSettings(); + } + else if (srt_cmd == SRT_CMD_HSRSP) + { + // HSv4 INITIATOR is sender + updateSrtSndSettings(); + } + else + { + // HSv4 RESPONDER is receiver + updateSrtRcvSettings(); + } +} + +int CUDT::packLostData(CPacket &packet, uint64_t &origintime) +{ + // protect m_iSndLastDataAck from updating by ACK processing + CGuard ackguard(m_RecvAckLock); + + while ((packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0) + { + const int offset = CSeqNo::seqoff(m_iSndLastDataAck, packet.m_iSeqNo); + if (offset < 0) + { + LOGC(dlog.Error, + log << "IPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " << packet.m_iSeqNo + << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset << ". Continue"); + continue; + } + + int msglen; + + const int payload = m_pSndBuffer->readData(&(packet.m_pcData), offset, packet.m_iMsgNo, origintime, msglen); + SRT_ASSERT(payload != 0); + if (payload == -1) + { + int32_t seqpair[2]; + seqpair[0] = packet.m_iSeqNo; + seqpair[1] = CSeqNo::incseq(seqpair[0], msglen); + sendCtrl(UMSG_DROPREQ, &packet.m_iMsgNo, seqpair, 8); + + // only one msg drop request is necessary + m_pSndLossList->remove(seqpair[1]); + + // skip all dropped packets + if (CSeqNo::seqcmp(m_iSndCurrSeqNo, CSeqNo::incseq(seqpair[1])) < 0) + m_iSndCurrSeqNo = CSeqNo::incseq(seqpair[1]); + + continue; + } + // NOTE: This is just a sanity check. Returning 0 is impossible to happen + // in case of retransmission. If the offset was a positive value, then the + // block must exist in the old blocks because it wasn't yet cut off by ACK + // and has been already recorded as sent (otherwise the peer wouldn't send + // back the loss report). May something happen here in case when the send + // loss record has been updated by the FASTREXMIT. + else if (payload == 0) + continue; + + // At this point we no longer need the ACK lock, + // because we are going to return from the function. + // Therefore unlocking in order not to block other threads. + ackguard.forceUnlock(); + + CGuard::enterCS(m_StatsLock); + ++m_stats.traceRetrans; + ++m_stats.retransTotal; + m_stats.traceBytesRetrans += payload; + m_stats.bytesRetransTotal += payload; + CGuard::leaveCS(m_StatsLock); + + // Despite the contextual interpretation of packet.m_iMsgNo around + // CSndBuffer::readData version 2 (version 1 doesn't return -1), in this particular + // case we can be sure that this is exactly the value of PH_MSGNO as a bitset. + // So, set here the rexmit flag if the peer understands it. + if (m_bPeerRexmitFlag) + { + packet.m_iMsgNo |= PACKET_SND_REXMIT; + } + + return payload; + } + + return 0; +} + +int CUDT::packData(CPacket &packet, uint64_t &ts_tk) +{ + int payload = 0; + bool probe = false; + uint64_t origintime = 0; + bool new_packet_packed = false; + bool filter_ctl_pkt = false; + + int kflg = EK_NOENC; + + uint64_t entertime_tk; + CTimer::rdtsc(entertime_tk); + +#if 0 // debug: TimeDiff histogram + static int lldiffhisto[23] = {0}; + static int llnodiff = 0; + if (m_ullTargetTime_tk != 0) + { + int ofs = 11 + ((entertime_tk - m_ullTargetTime_tk)/(int64_t)m_ullCPUFrequency)/1000; + if (ofs < 0) ofs = 0; + else if (ofs > 22) ofs = 22; + lldiffhisto[ofs]++; + } + else if(m_ullTargetTime_tk == 0) + { + llnodiff++; + } + static int callcnt = 0; + if (!(callcnt++ % 5000)) { + fprintf(stderr, "%6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d\n", + lldiffhisto[0],lldiffhisto[1],lldiffhisto[2],lldiffhisto[3],lldiffhisto[4],lldiffhisto[5], + lldiffhisto[6],lldiffhisto[7],lldiffhisto[8],lldiffhisto[9],lldiffhisto[10],lldiffhisto[11]); + fprintf(stderr, "%6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d\n", + lldiffhisto[12],lldiffhisto[13],lldiffhisto[14],lldiffhisto[15],lldiffhisto[16],lldiffhisto[17], + lldiffhisto[18],lldiffhisto[19],lldiffhisto[20],lldiffhisto[21],lldiffhisto[21],llnodiff); + } +#endif + if ((0 != m_ullTargetTime_tk) && (entertime_tk > m_ullTargetTime_tk)) + m_ullTimeDiff_tk += entertime_tk - m_ullTargetTime_tk; + + string reason; + + payload = packLostData(packet, origintime); + if (payload > 0) + { + reason = "reXmit"; + } + else if (m_PacketFilter && + m_PacketFilter.packControlPacket(Ref(packet), m_iSndCurrSeqNo, m_pCryptoControl->getSndCryptoFlags())) + { + HLOGC(mglog.Debug, log << "filter: filter/CTL packet ready - packing instead of data."); + payload = packet.getLength(); + reason = "filter"; + filter_ctl_pkt = true; // Mark that this packet ALREADY HAS timestamp field and it should not be set + + // Stats + + { + CGuard lg(m_StatsLock); + ++m_stats.sndFilterExtra; + ++m_stats.sndFilterExtraTotal; + } + } + else + { + // If no loss, and no packetfilter control packet, pack a new packet. + + // check congestion/flow window limit + int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + int seqdiff = CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)); + if (cwnd >= seqdiff) + { + // XXX Here it's needed to set kflg to msgno_bitset in the block stored in the + // send buffer. This should be somehow avoided, the crypto flags should be set + // together with encrypting, and the packet should be sent as is, when rexmitting. + // It would be nice to research as to whether CSndBuffer::Block::m_iMsgNoBitset field + // isn't a useless redundant state copy. If it is, then taking the flags here can be removed. + kflg = m_pCryptoControl->getSndCryptoFlags(); + payload = m_pSndBuffer->readData(&(packet.m_pcData), packet.m_iMsgNo, origintime, kflg); + if (payload) + { + m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo); + // m_pCryptoControl->m_iSndCurrSeqNo = m_iSndCurrSeqNo; + + packet.m_iSeqNo = m_iSndCurrSeqNo; + + // every 16 (0xF) packets, a packet pair is sent + if ((packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + probe = true; + + new_packet_packed = true; + } + else + { + m_ullTargetTime_tk = 0; + m_ullTimeDiff_tk = 0; + ts_tk = 0; + return 0; + } + } + else + { + HLOGC(dlog.Debug, + log << "packData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow + << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << seqdiff); + m_ullTargetTime_tk = 0; + m_ullTimeDiff_tk = 0; + ts_tk = 0; + return 0; + } + + reason = "normal"; + } + + // Normally packet.m_iTimeStamp field is set exactly here, + // usually as taken from m_StartTime and current time, unless live + // mode in which case it is based on 'origintime' as set during scheduling. + // In case when this is a filter control packet, the m_iTimeStamp field already + // contains the exactly needed value, and it's a timestamp clip, not a real + // timestamp. + if (!filter_ctl_pkt) + { + if (m_bPeerTsbPd) + { + /* + * When timestamp is carried over in this sending stream from a received stream, + * it may be older than the session start time causing a negative packet time + * that may block the receiver's Timestamp-based Packet Delivery. + * XXX Isn't it then better to not decrease it by m_StartTime? As long as it + * doesn't screw up the start time on the other side. + */ + if (origintime >= m_stats.startTime) + packet.m_iTimeStamp = int(origintime - m_stats.startTime); + else + packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime); + } + else + { + packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime); + } + } + + packet.m_iID = m_PeerID; + packet.setLength(payload); + + /* Encrypt if 1st time this packet is sent and crypto is enabled */ + if (kflg) + { + // XXX Encryption flags are already set on the packet before calling this. + // See readData() above. + if (m_pCryptoControl->encrypt(Ref(packet))) + { + // Encryption failed + //>>Add stats for crypto failure + ts_tk = 0; + LOGC(dlog.Error, log << "ENCRYPT FAILED - packet won't be sent, size=" << payload); + return -1; // Encryption failed + } + payload = packet.getLength(); /* Cipher may change length */ + reason += " (encrypted)"; + } + + if (new_packet_packed && m_PacketFilter) + { + HLOGC(mglog.Debug, log << "filter: Feeding packet for source clip"); + m_PacketFilter.feedSource(Ref(packet)); + } + +#if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr() + HLOGC(mglog.Debug, + log << CONID() << "packData: " << reason << " packet seq=" << packet.m_iSeqNo << " (ACK=" << m_iSndLastAck + << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << packet.MessageFlagStr() << ")"); +#endif + + // Fix keepalive + m_ullLastSndTime_tk = entertime_tk; + + considerLegacySrtHandshake(0); + + // WARNING: TEV_SEND is the only event that is reported from + // the CSndQueue::worker thread. All others are reported from + // CRcvQueue::worker. If you connect to this signal, make sure + // that you are aware of prospective simultaneous access. + updateCC(TEV_SEND, &packet); + + // XXX This was a blocked code also originally in UDT. Probably not required. + // Left untouched for historical reasons. + // Might be possible that it was because of that this is send from + // different thread than the rest of the signals. + // m_pSndTimeWindow->onPktSent(packet.m_iTimeStamp); + + CGuard::enterCS(m_StatsLock); + m_stats.traceBytesSent += payload; + m_stats.bytesSentTotal += payload; + ++m_stats.traceSent; + ++m_stats.sentTotal; + CGuard::leaveCS(m_StatsLock); + + if (probe) + { + // sends out probing packet pair + ts_tk = entertime_tk; + probe = false; + } + else + { +#if USE_BUSY_WAITING + ts_tk = entertime_tk + m_ullInterval_tk; +#else + if (m_ullTimeDiff_tk >= m_ullInterval_tk) + { + ts_tk = entertime_tk; + m_ullTimeDiff_tk -= m_ullInterval_tk; + } + else + { + ts_tk = entertime_tk + m_ullInterval_tk - m_ullTimeDiff_tk; + m_ullTimeDiff_tk = 0; + } +#endif + } + + m_ullTargetTime_tk = ts_tk; + + return payload; +} + +// This is a close request, but called from the +void CUDT::processClose() +{ + sendCtrl(UMSG_SHUTDOWN); + + m_bShutdown = true; + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 60; + + HLOGP(mglog.Debug, "processClose: sent message and set flags"); + + if (m_bTsbPd) + { + HLOGP(mglog.Debug, "processClose: lock-and-signal TSBPD"); + CGuard rl(m_RecvLock); + pthread_cond_signal(&m_RcvTsbPdCond); + } + + // Signal the sender and recver if they are waiting for data. + releaseSynch(); + // Unblock any call so they learn the connection_broken error + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true); + + HLOGP(mglog.Debug, "processClose: triggering timer event to spread the bad news"); + CTimer::triggerEvent(); +} + +void CUDT::sendLossReport(const std::vector > &loss_seqs) +{ + typedef vector > loss_seqs_t; + + vector seqbuffer; + seqbuffer.reserve(2 * loss_seqs.size()); // pessimistic + for (loss_seqs_t::const_iterator i = loss_seqs.begin(); i != loss_seqs.end(); ++i) + { + if (i->first == i->second) + { + seqbuffer.push_back(i->first); + HLOGF(mglog.Debug, "lost packet %d: sending LOSSREPORT", i->first); + } + else + { + seqbuffer.push_back(i->first | LOSSDATA_SEQNO_RANGE_FIRST); + seqbuffer.push_back(i->second); + HLOGF(mglog.Debug, + "lost packets %d-%d (%d packets): sending LOSSREPORT", + i->first, + i->second, + 1 + CSeqNo::seqcmp(i->second, i->first)); + } + } + + if (!seqbuffer.empty()) + { + sendCtrl(UMSG_LOSSREPORT, NULL, &seqbuffer[0], seqbuffer.size()); + } +} + +int CUDT::processData(CUnit *in_unit) +{ + CPacket &packet = in_unit->m_Packet; + + // XXX This should be called (exclusively) here: + // m_pRcvBuffer->addLocalTsbPdDriftSample(packet.getMsgTimeStamp()); + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + m_ullLastRspTime_tk = currtime_tk; + + // We are receiving data, start tsbpd thread if TsbPd is enabled + if (m_bTsbPd && pthread_equal(m_RcvTsbPdThread, pthread_t())) + { + HLOGP(mglog.Debug, "Spawning TSBPD thread"); + int st = 0; + { + ThreadName tn("SRT:TsbPd"); + st = pthread_create(&m_RcvTsbPdThread, NULL, CUDT::tsbpd, this); + } + if (st != 0) + return -1; + } + + const int pktrexmitflag = m_bPeerRexmitFlag ? (packet.getRexmitFlag() ? 1 : 0) : 2; +#if ENABLE_HEAVY_LOGGING + static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; + string rexmit_reason; +#endif + + if (pktrexmitflag == 1) + { + // This packet was retransmitted + CGuard::enterCS(m_StatsLock); + m_stats.traceRcvRetrans++; + CGuard::leaveCS(m_StatsLock); + +#if ENABLE_HEAVY_LOGGING + // Check if packet was retransmitted on request or on ack timeout + // Search the sequence in the loss record. + rexmit_reason = " by "; + if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo)) + rexmit_reason += "REQUEST"; + else + rexmit_reason += "ACK-TMOUT"; +#endif + } + + HLOGC(dlog.Debug, + log << CONID() << "processData: RECEIVED DATA: size=" << packet.getLength() << " seq=" << packet.getSeqNo()); + + updateCC(TEV_RECEIVE, &packet); + ++m_iPktCount; + + const int pktsz = packet.getLength(); + // Update time information + // XXX Note that this adds the byte size of a packet + // of which we don't yet know as to whether this has + // carried out some useful data or some excessive data + // that will be later discarded. + // FIXME: before adding this on the rcv time window, + // make sure that this packet isn't going to be + // effectively discarded, as repeated retransmission, + // for example, burdens the link, but doesn't better the speed. + m_RcvTimeWindow.onPktArrival(pktsz); + + // Probe the packet pair if needed. + // Conditions and any extra data required for the packet + // this function will extract and test as needed. + + const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0; + const bool retransmitted = m_bPeerRexmitFlag && packet.getRexmitFlag(); + + // Retransmitted and unordered packets do not provide expected measurement. + // We expect the 16th and 17th packet to be sent regularly, + // otherwise measurement must be rejected. + m_RcvTimeWindow.probeArrival(packet, unordered || retransmitted); + + CGuard::enterCS(m_StatsLock); + m_stats.traceBytesRecv += pktsz; + m_stats.bytesRecvTotal += pktsz; + ++m_stats.traceRecv; + ++m_stats.recvTotal; + CGuard::leaveCS(m_StatsLock); + + typedef vector > loss_seqs_t; + loss_seqs_t filter_loss_seqs; + loss_seqs_t srt_loss_seqs; + vector incoming; + bool was_sent_in_order = true; + bool reorder_prevent_lossreport = false; + + // If the peer doesn't understand REXMIT flag, send rexmit request + // always immediately. + int initial_loss_ttl = 0; + if (m_bPeerRexmitFlag) + initial_loss_ttl = m_iReorderTolerance; + + // After introduction of packet filtering, the "recordable loss detection" + // does not exactly match the true loss detection. When a FEC filter is + // working, for example, then getting one group filled with all packet but + // the last one and the FEC control packet, in this special case this packet + // won't be notified at all as lost because it will be recovered by the + // filter immediately before anyone notices what happened (and the loss + // detection for the further functionality is checked only afterwards, + // and in this case the immediate recovery makes the loss to not be noticed + // at all). + // + // Because of that the check for losses must happen BEFORE passing the packet + // to the filter and before the filter could recover the packet before anyone + // notices :) + + if (packet.getMsgSeq() != 0) // disregard filter-control packets, their seq may mean nothing + { + int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + if (diff > 1) + { + CGuard lg(m_StatsLock); + int loss = diff - 1; // loss is all that is above diff == 1 + m_stats.traceRcvLoss += loss; + m_stats.rcvLossTotal += loss; + uint64_t lossbytes = loss * m_pRcvBuffer->getRcvAvgPayloadSize(); + m_stats.traceRcvBytesLoss += lossbytes; + m_stats.rcvBytesLossTotal += lossbytes; + HLOGC(mglog.Debug, + log << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " + << CSeqNo::decseq(packet.m_iSeqNo) << "]"); + } + + if (diff > 0) + { + // Record if it was further than latest + m_iRcvCurrPhySeqNo = packet.m_iSeqNo; + } + } + + { + // Start of offset protected section + // Prevent TsbPd thread from modifying Ack position while adding data + // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() + CGuard recvbuf_acklock(m_RcvBufferLock); + + // vector undec_units; + if (m_PacketFilter) + { + // Stuff this data into the filter + m_PacketFilter.receive(in_unit, Ref(incoming), Ref(filter_loss_seqs)); + HLOGC(mglog.Debug, + log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) + << " loss to report, " + << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" + : "REPORT ONLY THOSE")); + } + else + { + // Stuff in just one packet that has come in. + incoming.push_back(in_unit); + } + + bool excessive = true; // stays true unless it was successfully added + + // Needed for possibly check for needsQuickACK. + bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_iRcvLastSkipAck) < 0); + + // Loop over all incoming packets that were filtered out. + // In case when there is no filter, there's just one packet in 'incoming', + // the one that came in the input of this function. + for (vector::iterator i = incoming.begin(); i != incoming.end(); ++i) + { + CUnit * u = *i; + CPacket &rpkt = u->m_Packet; + + // m_iRcvLastSkipAck is the base sequence number for the receiver buffer. + // This is the offset in the buffer; if this is negative, it means that + // this sequence is already in the past and the buffer is not interested. + // Meaning, this packet will be rejected, even if it could potentially be + // one of missing packets in the transmission. + int32_t offset = CSeqNo::seqoff(m_iRcvLastSkipAck, rpkt.m_iSeqNo); + + IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); + + if (offset < 0) + { + IF_HEAVY_LOGGING(exc_type = "BELATED"); + uint64_t tsbpdtime = m_pRcvBuffer->getPktTsbPdTime(rpkt.getMsgTimeStamp()); + uint64_t bltime = + CountIIR(uint64_t(m_stats.traceBelatedTime) * 1000, CTimer::getTime() - tsbpdtime, 0.2); + + CGuard::enterCS(m_StatsLock); + m_stats.traceBelatedTime = double(bltime) / 1000.0; + m_stats.traceRcvBelated++; + CGuard::leaveCS(m_StatsLock); + HLOGC(mglog.Debug, + log << CONID() << "RECEIVED: seq=" << packet.m_iSeqNo << " offset=" << offset << " (BELATED/" + << rexmitstat[pktrexmitflag] << rexmit_reason << ") FLAGS: " << packet.MessageFlagStr()); + continue; + } + + const int avail_bufsize = m_pRcvBuffer->getAvailBufSize(); + if (offset >= avail_bufsize) + { + // This is already a sequence discrepancy. Probably there could be found + // some way to make it continue reception by overriding the sequence and + // make a kinda TLKPTDROP, but there has been found no reliable way to do this. + if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty()) + { + // Only in live mode. In File mode this shall not be possible + // because the sender should stop sending in this situation. + // In Live mode this means that there is a gap between the + // lowest sequence in the empty buffer and the incoming sequence + // that exceeds the buffer size. Receiving data in this situation + // is no longer possible and this is a point of no return. + LOGC(mglog.Error, + log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION. offset=" << offset + << " avail=" << avail_bufsize << " ack.seq=" << m_iRcvLastSkipAck + << " pkt.seq=" << rpkt.m_iSeqNo << " rcv-remain=" << m_pRcvBuffer->debugGetSize()); + + // This is a scoped lock with AckLock, but for the moment + // when processClose() is called this lock must be taken out, + // otherwise this will cause a deadlock. We don't need this + // lock anymore, and at 'return' it will be unlocked anyway. + recvbuf_acklock.forceUnlock(); + processClose(); + return -1; + } + else + { + LOGC(mglog.Error, + log << CONID() << "No room to store incoming packet: offset=" << offset + << " avail=" << avail_bufsize << " ack.seq=" << m_iRcvLastSkipAck + << " pkt.seq=" << rpkt.m_iSeqNo << " rcv-remain=" << m_pRcvBuffer->debugGetSize()); + return -1; + } + } + + bool adding_successful = true; + if (m_pRcvBuffer->addData(*i, offset) < 0) + { + // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. + // So this packet is "redundant". + IF_HEAVY_LOGGING(exc_type = "UNACKED"); + adding_successful = false; + } + else + { + IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); + excessive = false; + if (u->m_Packet.getMsgCryptoFlags()) + { + EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt(Ref(u->m_Packet)) : ENCS_NOTSUP; + if (rc != ENCS_CLEAR) + { + // Could not decrypt + // Keep packet in received buffer + // Crypto flags are still set + // It will be acknowledged + { + CGuard lg(m_StatsLock); + m_stats.traceRcvUndecrypt += 1; + m_stats.traceRcvBytesUndecrypt += pktsz; + m_stats.m_rcvUndecryptTotal += 1; + m_stats.m_rcvBytesUndecryptTotal += pktsz; + } + + // Log message degraded to debug because it may happen very often + HLOGC(dlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); + adding_successful = false; + IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + } + } + } + + HLOGC(mglog.Debug, + log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo << " offset=" << offset + << " BUFr=" << avail_bufsize + << " (" << exc_type << "/" << rexmitstat[pktrexmitflag] << rexmit_reason << ") FLAGS: " + << packet.MessageFlagStr()); + + // Decryption should have made the crypto flags EK_NOENC. + // Otherwise it's an error. + if (adding_successful) + { + HLOGC(dlog.Debug, + log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + { + int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + + srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); + + if (initial_loss_ttl) + { + // pack loss list for (possibly belated) NAK + // The LOSSREPORT will be sent in a while. + + for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) + { + m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); + } + HLOGC(mglog.Debug, + log << "FreshLoss: added sequences: " << Printable(srt_loss_seqs) + << " tolerance: " << initial_loss_ttl); + reorder_prevent_lossreport = true; + } + } + } + + // Update the current largest sequence number that has been received. + // Or it is a retransmitted packet, remove it from receiver loss list. + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + { + m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + } + else + { + unlose(rpkt); // was BELATED or RETRANSMITTED + was_sent_in_order &= 0 != pktrexmitflag; + } + } + + // This is moved earlier after introducing filter because it shouldn't + // be executed in case when the packet was rejected by the receiver buffer. + // However now the 'excessive' condition may be true also in case when + // a truly non-excessive packet has been received, just it has been temporarily + // stored for better times by the filter module. This way 'excessive' is also true, + // although the old condition that a packet with a newer sequence number has arrived + // or arrived out of order may still be satisfied. + if (!incoming_belated && was_sent_in_order) + { + // Basing on some special case in the packet, it might be required + // to enforce sending ACK immediately (earlier than normally after + // a given period). + if (m_CongCtl->needsQuickACK(packet)) + { + CTimer::rdtsc(m_ullNextACKTime_tk); + } + } + + if (excessive) + { + return -1; + } + + } // End of recvbuf_acklock + + if (m_bClosing) + { + // RcvQueue worker thread can call processData while closing (or close while processData) + // This race condition exists in the UDT design but the protection against TsbPd thread + // (with AckLock) and decryption enlarged the probability window. + // Application can crash deep in decrypt stack since crypto context is deleted in close. + // RcvQueue worker thread will not necessarily be deleted with this connection as it can be + // used by others (socket multiplexer). + return -1; + } + + if (incoming.empty()) + { + // Treat as excessive. This is when a filter cumulates packets + // until the loss is rebuilt, or eats up a filter control packet + return -1; + } + + if (!srt_loss_seqs.empty()) + { + // A loss is detected + { + // TODO: Can unlock rcvloss after m_pRcvLossList->insert(...)? + // And probably protect m_FreshLoss as well. + + HLOGC(mglog.Debug, log << "processData: LOSS DETECTED, %: " << Printable(srt_loss_seqs) << " - RECORDING."); + // if record_loss == false, nothing will be contained here + // Insert lost sequence numbers to the receiver loss list + CGuard lg(m_RcvLossLock); + for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) + { + // If loss found, insert them to the receiver loss list + m_pRcvLossList->insert(i->first, i->second); + } + } + + const bool report_recorded_loss = !m_PacketFilter || m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS; + if (!reorder_prevent_lossreport && report_recorded_loss) + { + HLOGC(mglog.Debug, log << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); + sendLossReport(srt_loss_seqs); + } + + if (m_bTsbPd) + { + pthread_mutex_lock(&m_RecvLock); + pthread_cond_signal(&m_RcvTsbPdCond); + pthread_mutex_unlock(&m_RecvLock); + } + } + + // Separately report loss records of those reported by a filter. + // ALWAYS report whatever has been reported back by a filter. Note that + // the filter never reports anything when rexmit fallback level is ALWAYS or NEVER. + // With ALWAYS only those are reported that were recorded here by SRT. + // With NEVER, nothing is to be reported. + if (!filter_loss_seqs.empty()) + { + HLOGC(mglog.Debug, log << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); + sendLossReport(filter_loss_seqs); + + if (m_bTsbPd) + { + pthread_mutex_lock(&m_RecvLock); + pthread_cond_signal(&m_RcvTsbPdCond); + pthread_mutex_unlock(&m_RecvLock); + } + } + + // Now review the list of FreshLoss to see if there's any "old enough" to send UMSG_LOSSREPORT to it. + + // PERFORMANCE CONSIDERATIONS: + // This list is quite inefficient as a data type and finding the candidate to send UMSG_LOSSREPORT + // is linear time. On the other hand, there are some special cases that are important for performance: + // - only the first (plus some following) could have had TTL drown to 0 + // - the only (little likely) possibility that the next-to-first record has TTL=0 is when there was + // a loss range split (due to unlose() of one sequence) + // - first found record with TTL>0 means end of "ready to LOSSREPORT" records + // So: + // All you have to do is: + // - start with first element and continue with next elements, as long as they have TTL=0 + // If so, send the loss report and remove this element. + // - Since the first element that has TTL>0, iterate until the end of container and decrease TTL. + // + // This will be efficient becase the loop to increment one field (without any condition check) + // can be quite well optimized. + + vector lossdata; + { + CGuard lg(m_RcvLossLock); + + // XXX There was a mysterious crash around m_FreshLoss. When the initial_loss_ttl is 0 + // (that is, "belated loss report" feature is off), don't even touch m_FreshLoss. + if (initial_loss_ttl && !m_FreshLoss.empty()) + { + deque::iterator i = m_FreshLoss.begin(); + + // Phase 1: take while TTL <= 0. + // There can be more than one record with the same TTL, if it has happened before + // that there was an 'unlost' (@c unlose) sequence that has split one detected loss + // into two records. + for (; i != m_FreshLoss.end() && i->ttl <= 0; ++i) + { + HLOGF(mglog.Debug, + "Packet seq %d-%d (%d packets) considered lost - sending LOSSREPORT", + i->seq[0], + i->seq[1], + CSeqNo::seqoff(i->seq[0], i->seq[1]) + 1); + addLossRecord(lossdata, i->seq[0], i->seq[1]); + } + + // Remove elements that have been processed and prepared for lossreport. + if (i != m_FreshLoss.begin()) + { + m_FreshLoss.erase(m_FreshLoss.begin(), i); + i = m_FreshLoss.begin(); + } + + if (m_FreshLoss.empty()) + { + HLOGP(mglog.Debug, "NO MORE FRESH LOSS RECORDS."); + } + else + { + HLOGF(mglog.Debug, + "STILL %" PRIzu " FRESH LOSS RECORDS, FIRST: %d-%d (%d) TTL: %d", + m_FreshLoss.size(), + i->seq[0], + i->seq[1], + 1 + CSeqNo::seqoff(i->seq[0], i->seq[1]), + i->ttl); + } + + // Phase 2: rest of the records should have TTL decreased. + for (; i != m_FreshLoss.end(); ++i) + --i->ttl; + } + } + if (!lossdata.empty()) + { + sendCtrl(UMSG_LOSSREPORT, NULL, &lossdata[0], lossdata.size()); + } + + // was_sent_in_order means either of: + // - packet was sent in order (first if branch above) + // - packet was sent as old, but was a retransmitted packet + + if (m_bPeerRexmitFlag && was_sent_in_order) + { + ++m_iConsecOrderedDelivery; + if (m_iConsecOrderedDelivery >= 50) + { + m_iConsecOrderedDelivery = 0; + if (m_iReorderTolerance > 0) + { + m_iReorderTolerance--; + CGuard::enterCS(m_StatsLock); + m_stats.traceReorderDistance--; + CGuard::leaveCS(m_StatsLock); + HLOGF(mglog.Debug, + "ORDERED DELIVERY of 50 packets in a row - decreasing tolerance to %d", + m_iReorderTolerance); + } + } + } + + return 0; +} + +/// This function is called when a packet has arrived, which was behind the current +/// received sequence - that is, belated or retransmitted. Try to remove the packet +/// from both loss records: the general loss record and the fresh loss record. +/// +/// Additionally, check - if supported by the peer - whether the "latecoming" packet +/// has been sent due to retransmission or due to reordering, by checking the rexmit +/// support flag and rexmit flag itself. If this packet was surely ORIGINALLY SENT +/// it means that the current network connection suffers of packet reordering. This +/// way try to introduce a dynamic tolerance by calculating the difference between +/// the current packet reception sequence and this packet's sequence. This value +/// will be set to the tolerance value, which means that later packet retransmission +/// will not be required immediately, but only after receiving N next packets that +/// do not include the lacking packet. +/// The tolerance is not increased infinitely - it's bordered by m_iMaxReorderTolerance. +/// This value can be set in options - SRT_LOSSMAXTTL. +void CUDT::unlose(const CPacket &packet) +{ + CGuard lg(m_RcvLossLock); + int32_t sequence = packet.m_iSeqNo; + m_pRcvLossList->remove(sequence); + + // Rest of this code concerns only the "belated lossreport" feature. + + bool has_increased_tolerance = false; + bool was_reordered = false; + + if (m_bPeerRexmitFlag) + { + // If the peer understands the REXMIT flag, it means that the REXMIT flag is contained + // in the PH_MSGNO field. + + // The packet is considered coming originally (just possibly out of order), if REXMIT + // flag is NOT set. + was_reordered = !packet.getRexmitFlag(); + if (was_reordered) + { + HLOGF(mglog.Debug, "received out-of-band packet seq %d", sequence); + + const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo)); + CGuard::enterCS(m_StatsLock); + m_stats.traceReorderDistance = max(seqdiff, m_stats.traceReorderDistance); + CGuard::leaveCS(m_StatsLock); + if (seqdiff > m_iReorderTolerance) + { + const int new_tolerance = min(seqdiff, m_iMaxReorderTolerance); + HLOGF(mglog.Debug, + "Belated by %d seqs - Reorder tolerance %s %d", + seqdiff, + (new_tolerance == m_iReorderTolerance) ? "REMAINS with" : "increased to", + new_tolerance); + m_iReorderTolerance = new_tolerance; + has_increased_tolerance = + true; // Yes, even if reorder tolerance is already at maximum - this prevents decreasing tolerance. + } + } + else + { + HLOGC(mglog.Debug, log << CONID() << "received reXmitted packet seq=" << sequence); + } + } + else + { + HLOGF(mglog.Debug, "received reXmitted or belated packet seq %d (distinction not supported by peer)", sequence); + } + + // Don't do anything if "belated loss report" feature is not used. + // In that case the FreshLoss list isn't being filled in at all, the + // loss report is sent directly. + // Note that this condition blocks two things being done in this function: + // - remove given sequence from the fresh loss record + // (in this case it's empty anyway) + // - decrease current reorder tolerance based on whether packets come in order + // (current reorder tolerance is 0 anyway) + if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0) + return; + + size_t i = 0; + int had_ttl = 0; + for (i = 0; i < m_FreshLoss.size(); ++i) + { + had_ttl = m_FreshLoss[i].ttl; + switch (m_FreshLoss[i].revoke(sequence)) + { + case CRcvFreshLoss::NONE: + continue; // Not found. Search again. + + case CRcvFreshLoss::STRIPPED: + goto breakbreak; // Found and the modification is applied. We're done here. + + case CRcvFreshLoss::DELETE: + // No more elements. Kill it. + m_FreshLoss.erase(m_FreshLoss.begin() + i); + // Every loss is unique. We're done here. + goto breakbreak; + + case CRcvFreshLoss::SPLIT: + // Oh, this will be more complicated. This means that it was in between. + { + // So create a new element that will hold the upper part of the range, + // and this one modify to be the lower part of the range. + + // Keep the current end-of-sequence value for the second element + int32_t next_end = m_FreshLoss[i].seq[1]; + + // seq-1 set to the end of this element + m_FreshLoss[i].seq[1] = CSeqNo::decseq(sequence); + // seq+1 set to the begin of the next element + int32_t next_begin = CSeqNo::incseq(sequence); + + // Use position of the NEXT element because insertion happens BEFORE pointed element. + // Use the same TTL (will stay the same in the other one). + m_FreshLoss.insert(m_FreshLoss.begin() + i + 1, + CRcvFreshLoss(next_begin, next_end, m_FreshLoss[i].ttl)); + } + goto breakbreak; + } + } + + // Could have made the "return" instruction instead of goto, but maybe there will be something + // to add in future, so keeping that. +breakbreak:; + + if (i != m_FreshLoss.size()) + { + HLOGF(mglog.Debug, "sequence %d removed from belated lossreport record", sequence); + } + + if (was_reordered) + { + m_iConsecOrderedDelivery = 0; + if (has_increased_tolerance) + { + m_iConsecEarlyDelivery = 0; // reset counter + } + else if (had_ttl > 2) + { + ++m_iConsecEarlyDelivery; // otherwise, and if it arrived quite earlier, increase counter + HLOGF(mglog.Debug, "... arrived at TTL %d case %d", had_ttl, m_iConsecEarlyDelivery); + + // After 10 consecutive + if (m_iConsecEarlyDelivery >= 10) + { + m_iConsecEarlyDelivery = 0; + if (m_iReorderTolerance > 0) + { + m_iReorderTolerance--; + CGuard::enterCS(m_StatsLock); + m_stats.traceReorderDistance--; + CGuard::leaveCS(m_StatsLock); + HLOGF(mglog.Debug, + "... reached %d times - decreasing tolerance to %d", + m_iConsecEarlyDelivery, + m_iReorderTolerance); + } + } + } + // If hasn't increased tolerance, but the packet appeared at TTL less than 2, do nothing. + } +} + +void CUDT::dropFromLossLists(int32_t from, int32_t to) +{ + CGuard lg(m_RcvLossLock); + m_pRcvLossList->remove(from, to); + + HLOGF(mglog.Debug, "TLPKTDROP seq %d-%d (%d packets)", from, to, CSeqNo::seqoff(from, to)); + + if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0) + return; + + // All code below concerns only "belated lossreport" feature. + + // It's highly unlikely that this is waiting to send a belated UMSG_LOSSREPORT, + // so treat it rather as a sanity check. + + // It's enough to check if the first element of the list starts with a sequence older than 'to'. + // If not, just do nothing. + + size_t delete_index = 0; + for (size_t i = 0; i < m_FreshLoss.size(); ++i) + { + CRcvFreshLoss::Emod result = m_FreshLoss[i].revoke(from, to); + switch (result) + { + case CRcvFreshLoss::DELETE: + delete_index = i + 1; // PAST THE END + continue; // There may be further ranges that are included in this one, so check on. + + case CRcvFreshLoss::NONE: + case CRcvFreshLoss::STRIPPED: + break; // THIS BREAKS ONLY 'switch', not 'for'! + + case CRcvFreshLoss::SPLIT:; // This function never returns it. It's only a compiler shut-up. + } + + break; // Now this breaks also FOR. + } + + m_FreshLoss.erase(m_FreshLoss.begin(), + m_FreshLoss.begin() + delete_index); // with delete_index == 0 will do nothing +} + +// This function, as the name states, should bake a new cookie. +int32_t CUDT::bake(const sockaddr *addr, int32_t current_cookie, int correction) +{ + static unsigned int distractor = 0; + unsigned int rollover = distractor + 10; + + for (;;) + { + // SYN cookie + char clienthost[NI_MAXHOST]; + char clientport[NI_MAXSERV]; + getnameinfo(addr, + (m_iIPversion == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6), + clienthost, + sizeof(clienthost), + clientport, + sizeof(clientport), + NI_NUMERICHOST | NI_NUMERICSERV); + int64_t timestamp = ((CTimer::getTime() - m_stats.startTime) / 60000000) + distractor - + correction; // secret changes every one minute + stringstream cookiestr; + cookiestr << clienthost << ":" << clientport << ":" << timestamp; + union { + unsigned char cookie[16]; + int32_t cookie_val; + }; + CMD5::compute(cookiestr.str().c_str(), cookie); + + if (cookie_val != current_cookie) + return cookie_val; + + ++distractor; + + // This is just to make the loop formally breakable, + // but this is virtually impossible to happen. + if (distractor == rollover) + return cookie_val; + } +} + +// XXX This is quite a mystery, why this function has a return value +// and what the purpose for it was. There's just one call of this +// function in the whole code and in that call the return value is +// ignored. Actually this call happens in the CRcvQueue::worker thread, +// where it makes a response for incoming UDP packet that might be +// a connection request. Should any error occur in this process, there +// is no way to "report error" that happened here. Basing on that +// these values in original UDT code were quite like the values +// for m_iReqType, they have been changed to URQ_* symbols, which +// may mean that the intent for the return value was to send this +// value back as a control packet back to the connector. +// +// This function is run when the CRcvQueue object is reading packets +// from the multiplexer (@c CRcvQueue::worker_RetrieveUnit) and the +// target socket ID is 0. +// +// XXX Make this function return EConnectStatus enum type (extend if needed), +// and this will be directly passed to the caller. +SRT_REJECT_REASON CUDT::processConnectRequest(const sockaddr *addr, CPacket &packet) +{ + // XXX ASSUMPTIONS: + // [[using assert(packet.m_iID == 0)]] + + HLOGC(mglog.Debug, log << "processConnectRequest: received a connection request"); + + if (m_bClosing) + { + m_RejectReason = SRT_REJ_CLOSE; + HLOGC(mglog.Debug, log << "processConnectRequest: ... NOT. Rejecting because closing."); + return m_RejectReason; + } + + /* + * Closing a listening socket only set bBroken + * If a connect packet is received while closing it gets through + * processing and crashes later. + */ + if (m_bBroken) + { + m_RejectReason = SRT_REJ_CLOSE; + HLOGC(mglog.Debug, log << "processConnectRequest: ... NOT. Rejecting because broken."); + return m_RejectReason; + } + size_t exp_len = + CHandShake::m_iContentSize; // When CHandShake::m_iContentSize is used in log, the file fails to link! + + // NOTE!!! Old version of SRT code checks if the size of the HS packet + // is EQUAL to the above CHandShake::m_iContentSize. + + // Changed to < exp_len because we actually need that the packet + // be at least of a size for handshake, although it may contain + // more data, depending on what's inside. + if (packet.getLength() < exp_len) + { + m_RejectReason = SRT_REJ_ROGUE; + HLOGC(mglog.Debug, + log << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() << " (expected: " << exp_len + << ")"); + return m_RejectReason; + } + + // Dunno why the original UDT4 code only MUCH LATER was checking if the packet was UMSG_HANDSHAKE. + // It doesn't seem to make sense to deserialize it into the handshake structure if we are not + // sure that the packet contains the handshake at all! + if (!packet.isControl(UMSG_HANDSHAKE)) + { + m_RejectReason = SRT_REJ_ROGUE; + LOGC(mglog.Error, log << "processConnectRequest: the packet received as handshake is not a handshake message"); + return m_RejectReason; + } + + CHandShake hs; + hs.load_from(packet.m_pcData, packet.getLength()); + + // XXX MOST LIKELY this hs should be now copied into m_ConnRes field, which holds + // the handshake structure sent from the peer (no matter the role or mode). + // This should simplify the createSrtHandshake() function which can this time + // simply write the crafted handshake structure into m_ConnReq, which needs no + // participation of the local handshake and passing it as a parameter through + // newConnection() -> acceptAndRespond() -> createSrtHandshake(). This is also + // required as a source of the peer's information used in processing in other + // structures. + + int32_t cookie_val = bake(addr); + + HLOGC(mglog.Debug, log << "processConnectRequest: new cookie: " << hex << cookie_val); + + // REQUEST:INDUCTION. + // Set a cookie, a target ID, and send back the same as + // RESPONSE:INDUCTION. + if (hs.m_iReqType == URQ_INDUCTION) + { + HLOGC(mglog.Debug, log << "processConnectRequest: received type=induction, sending back with cookie+socket"); + + // XXX That looks weird - the calculated md5 sum out of the given host/port/timestamp + // is 16 bytes long, but CHandShake::m_iCookie has 4 bytes. This then effectively copies + // only the first 4 bytes. Moreover, it's dangerous on some platforms because the char + // array need not be aligned to int32_t - changed to union in a hope that using int32_t + // inside a union will enforce whole union to be aligned to int32_t. + hs.m_iCookie = cookie_val; + packet.m_iID = hs.m_iID; + + // Ok, now's the time. The listener sets here the version 5 handshake, + // even though the request was 4. This is because the old client would + // simply return THE SAME version, not even looking into it, giving the + // listener false impression as if it supported version 5. + // + // If the caller was really HSv4, it will simply ignore the version 5 in INDUCTION; + // it will respond with CONCLUSION, but with its own set version, which is version 4. + // + // If the caller was really HSv5, it will RECOGNIZE this version 5 in INDUCTION, so + // it will respond with version 5 when sending CONCLUSION. + + hs.m_iVersion = HS_VERSION_SRT1; + + // Additionally, set this field to a MAGIC value. This field isn't used during INDUCTION + // by HSv4 client, HSv5 client can use it to additionally verify that this is a HSv5 listener. + // In this field we also advertise the PBKEYLEN value. When 0, it's considered not advertised. + hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_iSndCryptoKeyLen); + bool whether SRT_ATR_UNUSED = m_iSndCryptoKeyLen != 0; + HLOGC(mglog.Debug, + log << "processConnectRequest: " << (whether ? "" : "NOT ") + << " Advertising PBKEYLEN - value = " << m_iSndCryptoKeyLen); + + size_t size = packet.getLength(); + hs.store_to(packet.m_pcData, Ref(size)); + packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime); + m_pSndQueue->sendto(addr, packet); + return SRT_REJ_UNKNOWN; // EXCEPTION: this is a "no-error" code. + } + + // Otherwise this should be REQUEST:CONCLUSION. + // Should then come with the correct cookie that was + // set in the above INDUCTION, in the HS_VERSION_SRT1 + // should also contain extra data. + + HLOGC(mglog.Debug, + log << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) << " - checking cookie..."); + if (hs.m_iCookie != cookie_val) + { + cookie_val = bake(addr, cookie_val, -1); // SHOULD generate an earlier, distracted cookie + + if (hs.m_iCookie != cookie_val) + { + m_RejectReason = SRT_REJ_RDVCOOKIE; + HLOGC(mglog.Debug, log << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); + return m_RejectReason; + } + + HLOGC(mglog.Debug, log << "processConnectRequest: ... correct (FIXED) cookie. Proceeding."); + } + else + { + HLOGC(mglog.Debug, log << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); + } + + int32_t id = hs.m_iID; + + // HANDSHAKE: The old client sees the version that does not match HS_VERSION_UDT4 (5). + // In this case it will respond with URQ_ERROR_REJECT. Rest of the data are the same + // as in the handshake request. When this message is received, the connector side should + // switch itself to the version number HS_VERSION_UDT4 and continue the old way (that is, + // continue sending URQ_INDUCTION, but this time with HS_VERSION_UDT4). + + bool accepted_hs = true; + + if (hs.m_iVersion == HS_VERSION_SRT1) + { + // No further check required. + // The m_iType contains handshake extension flags. + } + else if (hs.m_iVersion == HS_VERSION_UDT4) + { + // In UDT, and so in older SRT version, the hs.m_iType field should contain + // the socket type, although SRT only allowed this field to be UDT_DGRAM. + // Older SRT version contained that value in a field, but now that this can + // only contain UDT_DGRAM the field itself has been abandoned. + // For the sake of any old client that reports version 4 handshake, interpret + // this hs.m_iType field as a socket type and check if it's UDT_DGRAM. + + // Note that in HSv5 hs.m_iType contains extension flags. + if (hs.m_iType != UDT_DGRAM) + { + m_RejectReason = SRT_REJ_ROGUE; + accepted_hs = false; + } + } + else + { + // Unsupported version + // (NOTE: This includes "version=0" which is a rejection flag). + m_RejectReason = SRT_REJ_VERSION; + accepted_hs = false; + } + + if (!accepted_hs) + { + HLOGC(mglog.Debug, + log << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason + << " MSG: " << srt_rejectreason_str(m_RejectReason)); + // mismatch, reject the request + hs.m_iReqType = URQFailure(m_RejectReason); + size_t size = CHandShake::m_iContentSize; + hs.store_to(packet.m_pcData, Ref(size)); + packet.m_iID = id; + packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime); + m_pSndQueue->sendto(addr, packet); + } + else + { + SRT_REJECT_REASON error = SRT_REJ_UNKNOWN; + int result = s_UDTUnited.newConnection(m_SocketID, addr, &hs, packet, Ref(error)); + + // This is listener - m_RejectReason need not be set + // because listener has no functionality of giving the app + // insight into rejected callers. + + // ---> + // (global.) CUDTUnited::updateListenerMux + // (new Socket.) CUDT::acceptAndRespond + if (result == -1) + { + hs.m_iReqType = URQFailure(error); + LOGF(mglog.Error, "UU:newConnection: rsp(REJECT): %d - %s", hs.m_iReqType, srt_rejectreason_str(error)); + } + + // CONFUSION WARNING! + // + // The newConnection() will call acceptAndRespond() if the processing + // was successful - IN WHICH CASE THIS PROCEDURE SHOULD DO NOTHING. + // Ok, almost nothing - see update_events below. + // + // If newConnection() failed, acceptAndRespond() will not be called. + // Ok, more precisely, the thing that acceptAndRespond() is expected to do + // will not be done (this includes sending any response to the peer). + // + // Now read CAREFULLY. The newConnection() will return: + // + // - -1: The connection processing failed due to errors like: + // - memory alloation error + // - listen backlog exceeded + // - any error propagated from CUDT::open and CUDT::acceptAndRespond + // - 0: The connection already exists + // - 1: Connection accepted. + // + // So, update_events is called only if the connection is established. + // Both 0 (repeated) and -1 (error) require that a response be sent. + // The CPacket object that has arrived as a connection request is here + // reused for the connection rejection response (see URQ_ERROR_REJECT set + // as m_iReqType). + + // send back a response if connection failed or connection already existed + // new connection response should be sent in acceptAndRespond() + if (result != 1) + { + HLOGC(mglog.Debug, + log << CONID() << "processConnectRequest: sending ABNORMAL handshake info req=" + << RequestTypeStr(hs.m_iReqType)); + size_t size = CHandShake::m_iContentSize; + hs.store_to(packet.m_pcData, Ref(size)); + packet.m_iID = id; + packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime); + m_pSndQueue->sendto(addr, packet); + } + else + { + // a new connection has been created, enable epoll for write + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + } + } + LOGC(mglog.Note, log << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); + + return RejectReasonForURQ(hs.m_iReqType); +} + +void CUDT::addLossRecord(std::vector &lr, int32_t lo, int32_t hi) +{ + if (lo == hi) + lr.push_back(lo); + else + { + lr.push_back(lo | LOSSDATA_SEQNO_RANGE_FIRST); + lr.push_back(hi); + } +} + +void CUDT::checkACKTimer(uint64_t currtime_tk) +{ + if (currtime_tk > m_ullNextACKTime_tk // ACK time has come + // OR the number of sent packets since last ACK has reached + // the congctl-defined value of ACK Interval + // (note that none of the builtin congctls defines ACK Interval) + || (m_CongCtl->ACKMaxPackets() > 0 && m_iPktCount >= m_CongCtl->ACKMaxPackets())) + { + // ACK timer expired or ACK interval is reached + sendCtrl(UMSG_ACK); + CTimer::rdtsc(currtime_tk); + + const int ack_interval_tk = + m_CongCtl->ACKTimeout_us() > 0 ? m_CongCtl->ACKTimeout_us() * m_ullCPUFrequency : m_ullACKInt_tk; + m_ullNextACKTime_tk = currtime_tk + ack_interval_tk; + + m_iPktCount = 0; + m_iLightACKCount = 1; + } + // Or the transfer rate is so high that the number of packets + // have reached the value of SelfClockInterval * LightACKCount before + // the time has come according to m_ullNextACKTime_tk. In this case a "lite ACK" + // is sent, which doesn't contain statistical data and nothing more + // than just the ACK number. The "fat ACK" packets will be still sent + // normally according to the timely rules. + else if (m_iPktCount >= SELF_CLOCK_INTERVAL * m_iLightACKCount) + { + // send a "light" ACK + sendCtrl(UMSG_ACK, NULL, NULL, SEND_LITE_ACK); + ++m_iLightACKCount; + } +} + +void CUDT::checkNAKTimer(uint64_t currtime_tk) +{ + // XXX The problem with working NAKREPORT with SRT_ARQ_ONREQ + // is not that it would be inappropriate, but because it's not + // implemented. The reason for it is that the structure of the + // loss list container (m_pRcvLossList) is such that it is expected + // that the loss records are ordered by sequence numbers (so + // that two ranges sticking together are merged in place). + // Unfortunately in case of SRT_ARQ_ONREQ losses must be recorded + // as before, but they should not be reported, until confirmed + // by the filter. By this reason they appear often out of order + // and for adding them properly the loss list container wasn't + // prepared. This then requires some more effort to implement. + if (!m_bRcvNakReport || m_PktFilterRexmitLevel != SRT_ARQ_ALWAYS) + return; + + /* + * m_bRcvNakReport enables NAK reports for SRT. + * Retransmission based on timeout is bandwidth consuming, + * not knowing what to retransmit when the only NAK sent by receiver is lost, + * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT). + */ + const int loss_len = m_pRcvLossList->getLossLength(); + SRT_ASSERT(loss_len >= 0); + + if (loss_len > 0) + { + if (currtime_tk <= m_ullNextNAKTime_tk) + return; // wait for next NAK time + + sendCtrl(UMSG_LOSSREPORT); + } + + m_ullNextNAKTime_tk = currtime_tk + m_ullNAKInt_tk; +} + +bool CUDT::checkExpTimer(uint64_t currtime_tk) +{ + // In UDT the m_bUserDefinedRTO and m_iRTO were in CCC class. + // There's nothing in the original code that alters these values. + + uint64_t next_exp_time_tk; + if (m_CongCtl->RTO()) + { + next_exp_time_tk = m_ullLastRspTime_tk + m_CongCtl->RTO() * m_ullCPUFrequency; + } + else + { + uint64_t exp_int_tk = (m_iEXPCount * (m_iRTT + 4 * m_iRTTVar) + COMM_SYN_INTERVAL_US) * m_ullCPUFrequency; + if (exp_int_tk < m_iEXPCount * m_ullMinExpInt_tk) + exp_int_tk = m_iEXPCount * m_ullMinExpInt_tk; + next_exp_time_tk = m_ullLastRspTime_tk + exp_int_tk; + } + + if (currtime_tk <= next_exp_time_tk) + return false; + + // ms -> us + const int PEER_IDLE_TMO_US = m_iOPT_PeerIdleTimeout * 1000; + // Haven't received any information from the peer, is it dead?! + // timeout: at least 16 expirations and must be greater than 5 seconds + if ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && + (currtime_tk - m_ullLastRspTime_tk > PEER_IDLE_TMO_US * m_ullCPUFrequency)) + { + // + // Connection is broken. + // UDT does not signal any information about this instead of to stop quietly. + // Application will detect this when it calls any UDT methods next time. + // + HLOGC(mglog.Debug, + log << "CONNECTION EXPIRED after " << ((currtime_tk - m_ullLastRspTime_tk) / m_ullCPUFrequency) << "ms"); + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 30; + + // update snd U list to remove this socket + m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); + + releaseSynch(); + + // app can call any UDT API to learn the connection_broken error + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN | UDT_EPOLL_OUT | UDT_EPOLL_ERR, true); + + CTimer::triggerEvent(); + + return true; + } + + HLOGC(mglog.Debug, + log << "EXP TIMER: count=" << m_iEXPCount << "/" << (+COMM_RESPONSE_MAX_EXP) << " elapsed=" + << ((currtime_tk - m_ullLastRspTime_tk) / m_ullCPUFrequency) << "/" << (+PEER_IDLE_TMO_US) << "us"); + + ++m_iEXPCount; + + /* + * (keepalive fix) + * duB: + * It seems there is confusion of the direction of the Response here. + * LastRspTime is supposed to be when receiving (data/ctrl) from peer + * as shown in processCtrl and processData, + * Here we set because we sent something? + * + * Disabling this code that prevent quick reconnection when peer disappear + */ + // Reset last response time since we've just sent a heart-beat. + // (fixed) m_ullLastRspTime_tk = currtime_tk; + + return false; +} + +void CUDT::checkRexmitTimer(uint64_t currtime_tk) +{ + /* There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT. + * + * LATEREXMIT is only used with FileCC. + * The mode is triggered when some time has passed since the last ACK from + * the receiver, while there is still some unacknowledged data in the sender's buffer, + * and the loss list is empty. + * + * FASTREXMIT is only used with LiveCC. + * The mode is triggered if the receiver does not send periodic NAK reports, + * when some time has passed since the last ACK from the receiver, + * while there is still some unacknowledged data in the sender's buffer. + * + * In case the above conditions are met, the unacknowledged packets + * in the sender's buffer will be added to loss list and retransmitted. + */ + + const uint64_t rtt_syn = (m_iRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); + const uint64_t exp_int = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US) * m_ullCPUFrequency; + + if (currtime_tk <= (m_ullLastRspAckTime_tk + exp_int)) + return; + + // If there is no unacknowledged data in the sending buffer, + // then there is nothing to retransmit. + if (m_pSndBuffer->getCurrBufSize() <= 0) + return; + + const bool is_laterexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_LATEREXMIT; + const bool is_fastrexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_FASTREXMIT; + + // If the receiver will send periodic NAK reports, then FASTREXMIT is inactive. + // MIND that probably some method of "blind rexmit" MUST BE DONE, when TLPKTDROP is off. + if (is_fastrexmit && m_bPeerNakReport) + return; + + // We need to retransmit only when the data in the sender's buffer was already sent. + // Otherwise it might still be sent regulary. + bool retransmit = false; + // - the sender loss list is empty (the receiver didn't send any LOSSREPORT, or LOSSREPORT was lost on track) + if (is_laterexmit && (CSeqNo::incseq(m_iSndCurrSeqNo) != m_iSndLastAck) && m_pSndLossList->getLossLength() == 0) + retransmit = true; + + if (is_fastrexmit && (CSeqNo::seqoff(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0)) + retransmit = true; + + if (retransmit) + { + // Sender: Insert all the packets sent after last received acknowledgement into the sender loss list. + CGuard acklock(m_RecvAckLock); // Protect packet retransmission + // Resend all unacknowledged packets on timeout, but only if there is no packet in the loss list + const int32_t csn = m_iSndCurrSeqNo; + const int num = m_pSndLossList->insert(m_iSndLastAck, csn); + if (num > 0) + { + CGuard::enterCS(m_StatsLock); + m_stats.traceSndLoss += num; + m_stats.sndLossTotal += num; + CGuard::leaveCS(m_StatsLock); + + HLOGC(mglog.Debug, + log << CONID() << "ENFORCED " << (is_laterexmit ? "LATEREXMIT" : "FASTREXMIT") + << " by ACK-TMOUT (scheduling): " << CSeqNo::incseq(m_iSndLastAck) << "-" << csn << " (" + << CSeqNo::seqoff(m_iSndLastAck, csn) << " packets)"); + } + } + + ++m_iReXmitCount; + + checkSndTimers(DONT_REGEN_KM); + const ECheckTimerStage stage = is_fastrexmit ? TEV_CHT_FASTREXMIT : TEV_CHT_REXMIT; + updateCC(TEV_CHECKTIMER, stage); + + // immediately restart transmission + m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); +} + +void CUDT::checkTimers() +{ + // update CC parameters + updateCC(TEV_CHECKTIMER, TEV_CHT_INIT); + // uint64_t minint = (uint64_t)(m_ullCPUFrequency * m_pSndTimeWindow->getMinPktSndInt() * 0.9); + // if (m_ullInterval_tk < minint) + // m_ullInterval_tk = minint; + // NOTE: This commented-out ^^^ code was commented out in original UDT. Leaving for historical reasons + + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + + // This is a very heavy log, unblock only for temporary debugging! +#if 0 + HLOGC(mglog.Debug, log << CONID() << "checkTimers: nextacktime=" << FormatTime(m_ullNextACKTime_tk) + << " AckInterval=" << m_iACKInterval + << " pkt-count=" << m_iPktCount << " liteack-count=" << m_iLightACKCount); +#endif + + // Check if it is time to send ACK + checkACKTimer(currtime_tk); + + // Check if it is time to send a loss report + checkNAKTimer(currtime_tk); + + // Check if the connection is expired + if (checkExpTimer(currtime_tk)) + return; + + // Check if FAST or LATE packet retransmission is required + checkRexmitTimer(currtime_tk); + + // uint64_t exp_int = (m_iRTT + 4 * m_iRTTVar + COMM_SYN_INTERVAL_US) * m_ullCPUFrequency; + if (currtime_tk > m_ullLastSndTime_tk + (COMM_KEEPALIVE_PERIOD_US * m_ullCPUFrequency)) + { + sendCtrl(UMSG_KEEPALIVE); + HLOGP(mglog.Debug, "KEEPALIVE"); + } +} + +void CUDT::addEPoll(const int eid) +{ + CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + m_sPollID.insert(eid); + CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + + if (!stillConnected()) + return; + + CGuard::enterCS(m_RecvLock); + if (m_pRcvBuffer->isRcvDataReady()) + { + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true); + } + CGuard::leaveCS(m_RecvLock); + + if (m_iSndBufSize > m_pSndBuffer->getCurrBufSize()) + { + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + } +} + +void CUDT::removeEPoll(const int eid) +{ + // clear IO events notifications; + // since this happens after the epoll ID has been removed, they cannot be set again + set remove; + remove.insert(eid); + s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, UDT_EPOLL_IN | UDT_EPOLL_OUT, false); + + CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + m_sPollID.erase(eid); + CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); +} + +void CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl) +{ + if (evt >= TEV__SIZE) + return; // sanity check + + m_Slots[evt].push_back(sl); +} + +void CUDT::DisconnectSignal(ETransmissionEvent evt) +{ + if (evt >= TEV__SIZE) + return; // sanity check + + m_Slots[evt].clear(); +} + +void CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) +{ + for (std::vector::iterator i = m_Slots[tev].begin(); i != m_Slots[tev].end(); ++i) + { + i->emit(tev, var); + } +} + +int CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) +{ + CUDTSocket *s = s_UDTUnited.locate(u); + if (!s || !s->m_pUDT) + return -1; + + CSndBuffer *b = s->m_pUDT->m_pSndBuffer; + + if (!b) + return -1; + + int bytecount, timespan; + int count = b->getCurrBufSize(Ref(bytecount), Ref(timespan)); + + if (blocks) + *blocks = count; + + if (bytes) + *bytes = bytecount; + + return std::abs(timespan); +} + +SRT_REJECT_REASON CUDT::rejectReason(SRTSOCKET u) +{ + CUDTSocket *s = s_UDTUnited.locate(u); + if (!s || !s->m_pUDT) + return SRT_REJ_UNKNOWN; + + return s->m_pUDT->m_RejectReason; +} + +bool CUDT::runAcceptHook(CUDT *acore, const sockaddr *peer, const CHandShake *hs, const CPacket &hspkt) +{ + // Prepare the information for the hook. + + // We need streamid. + char target[MAX_SID_LENGTH + 1]; + memset(target, 0, MAX_SID_LENGTH + 1); + + // Just for a case, check the length. + // This wasn't done before, and we could risk memory crash. + // In case of error, this will remain unset and the empty + // string will be passed as streamid. + + int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs->m_iType); + + // This tests if there are any extensions. + if (hspkt.getLength() > CHandShake::m_iContentSize + 4 && IsSet(ext_flags, CHandShake::HS_EXT_CONFIG)) + { + uint32_t *begin = reinterpret_cast(hspkt.m_pcData + CHandShake::m_iContentSize); + size_t size = hspkt.getLength() - CHandShake::m_iContentSize; // Due to previous cond check we grant it's >0 + uint32_t *next = 0; + size_t length = size / sizeof(uint32_t); + size_t blocklen = 0; + + for (;;) // ONE SHOT, but continuable loop + { + int cmd = FindExtensionBlock(begin, length, Ref(blocklen), Ref(next)); + + const size_t bytelen = blocklen * sizeof(uint32_t); + + if (cmd == SRT_CMD_SID) + { + if (!bytelen || bytelen > MAX_SID_LENGTH) + { + LOGC(mglog.Error, + log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +MAX_SID_LENGTH + << " - PROTOCOL ERROR, REJECTING"); + return false; + } + // See comment at CUDT::interpretSrtHandshake(). + memcpy(target, begin + 1, bytelen); + + // Un-swap on big endian machines + ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen); + + // Nothing more expected from connection block. + break; + } + else if (cmd == SRT_CMD_NONE) + { + // End of blocks + break; + } + else + { + // Any other kind of message extracted. Search on. + length -= (next - begin); + begin = next; + if (begin) + continue; + } + + break; + } + } + + try + { + int result = CALLBACK_CALL(m_cbAcceptHook, acore->m_SocketID, hs->m_iVersion, peer, target); + if (result == -1) + return false; + } + catch (...) + { + LOGP(mglog.Error, "runAcceptHook: hook interrupted by exception"); + return false; + } + + return true; +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/core.h b/trunk/3rdparty/srt-1-fit/srtcore/core.h new file mode 100644 index 000000000..2b0a78741 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/core.h @@ -0,0 +1,873 @@ +/* + * 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/28/2012 +modified by + Haivision Systems Inc. +*****************************************************************************/ + + +#ifndef __UDT_CORE_H__ +#define __UDT_CORE_H__ + +#include +#include + +#include "srt.h" +#include "common.h" +#include "list.h" +#include "buffer.h" +#include "window.h" +#include "packet.h" +#include "channel.h" +#include "api.h" +#include "cache.h" +#include "queue.h" +#include "handshake.h" +#include "congctl.h" +#include "packetfilter.h" +#include "utilities.h" + +#include + +namespace srt_logging +{ + +extern Logger + glog, +// blog, + mglog, + dlog, + tslog, + rxlog, + cclog; + +} + + +// XXX Utility function - to be moved to utilities.h? +template +inline T CountIIR(T base, T newval, double factor) +{ + if ( base == 0.0 ) + return newval; + + T diff = newval - base; + return base+T(diff*factor); +} + +// XXX Probably a better rework for that can be done - this can be +// turned into a serializable structure, just like it's for CHandShake. +enum AckDataItem +{ + ACKD_RCVLASTACK = 0, + ACKD_RTT = 1, + ACKD_RTTVAR = 2, + ACKD_BUFFERLEFT = 3, + ACKD_TOTAL_SIZE_SMALL = 4, + + // Extra fields existing in UDT (not always sent) + + ACKD_RCVSPEED = 4, // length would be 16 + ACKD_BANDWIDTH = 5, + ACKD_TOTAL_SIZE_UDTBASE = 6, // length = 24 + // Extra stats for SRT + + ACKD_RCVRATE = 6, + ACKD_TOTAL_SIZE_VER101 = 7, // length = 28 + ACKD_XMRATE = 7, // XXX This is a weird compat stuff. Version 1.1.3 defines it as ACKD_BANDWIDTH*m_iMaxSRTPayloadSize when set. Never got. + // XXX NOTE: field number 7 may be used for something in future, need to confirm destruction of all !compat 1.0.2 version + + ACKD_TOTAL_SIZE_VER102 = 8, // 32 +// FEATURE BLOCKED. Probably not to be restored. +// ACKD_ACKBITMAP = 8, + ACKD_TOTAL_SIZE = ACKD_TOTAL_SIZE_VER102 // length = 32 (or more) +}; +const size_t ACKD_FIELD_SIZE = sizeof(int32_t); + +// For HSv4 legacy handshake +#define SRT_MAX_HSRETRY 10 /* Maximum SRT handshake retry */ + +enum SeqPairItems +{ + SEQ_BEGIN = 0, SEQ_END = 1, SEQ_SIZE = 2 +}; + +// Extended SRT Congestion control class - only an incomplete definition required +class CCryptoControl; + +// XXX REFACTOR: The 'CUDT' class is to be merged with 'CUDTSocket'. +// There's no reason for separating them, there's no case of having them +// anyhow managed separately. After this is done, with a small help with +// separating the internal abnormal path management (exceptions) from the +// API (return values), through CUDTUnited, this class may become in future +// an officially exposed C++ API. +class CUDT +{ + friend class CUDTSocket; + friend class CUDTUnited; + friend class CCC; + friend struct CUDTComp; + friend class CCache; + friend class CRendezvousQueue; + friend class CSndQueue; + friend class CRcvQueue; + friend class CSndUList; + friend class CRcvUList; + friend class PacketFilter; + +private: // constructor and desctructor + + void construct(); + void clearData(); + CUDT(); + CUDT(const CUDT& ancestor); + const CUDT& operator=(const CUDT&) {return *this;} + ~CUDT(); + +public: //API + static int startup(); + static int cleanup(); + static SRTSOCKET socket(int af, int type = SOCK_STREAM, int protocol = 0); + static int bind(SRTSOCKET u, const sockaddr* name, int namelen); + static int bind(SRTSOCKET u, UDPSOCKET udpsock); + static int listen(SRTSOCKET u, int backlog); + static SRTSOCKET accept(SRTSOCKET u, sockaddr* addr, int* addrlen); + static int connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); + static int close(SRTSOCKET u); + static int getpeername(SRTSOCKET u, sockaddr* name, int* namelen); + static int getsockname(SRTSOCKET u, sockaddr* name, int* namelen); + static int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); + static int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); + static int send(SRTSOCKET u, const char* buf, int len, int flags); + static int recv(SRTSOCKET u, char* buf, int len, int flags); + static int sendmsg(SRTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false, uint64_t srctime = 0); + static int recvmsg(SRTSOCKET u, char* buf, int len, uint64_t& srctime); + static int sendmsg2(SRTSOCKET u, const char* buf, int len, ref_t mctrl); + static int recvmsg2(SRTSOCKET u, char* buf, int len, ref_t mctrl); + static int64_t sendfile(SRTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = SRT_DEFAULT_SENDFILE_BLOCK); + static int64_t recvfile(SRTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = SRT_DEFAULT_RECVFILE_BLOCK); + static int select(int nfds, ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout); + static int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); + static int epoll_create(); + static int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + static int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static int epoll_remove_usock(const int eid, const SRTSOCKET u); + static int epoll_remove_ssock(const int eid, const SYSSOCKET s); + static int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + static int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* wrfds = NULL); + static int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); + static int32_t epoll_set(const int eid, int32_t flags); + static int epoll_release(const int eid); + static CUDTException& getlasterror(); + static int bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true, bool instantaneous = false); + static SRT_SOCKSTATUS getsockstate(SRTSOCKET u); + static bool setstreamid(SRTSOCKET u, const std::string& sid); + static std::string getstreamid(SRTSOCKET u); + static int getsndbuffer(SRTSOCKET u, size_t* blocks, size_t* bytes); + static SRT_REJECT_REASON rejectReason(SRTSOCKET s); + + static int setError(const CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return SRT_ERROR; + } + +public: // internal API + static const SRTSOCKET INVALID_SOCK = -1; // invalid socket descriptor + static const int ERROR = -1; // socket api error returned value + + static const int HS_VERSION_UDT4 = 4; + static const int HS_VERSION_SRT1 = 5; + + // Parameters + // + // Note: use notation with X*1000*1000* ... instead of million zeros in a row. + // In C++17 there is a possible notation of 5'000'000 for convenience, but that's + // something only for a far future. + static const int COMM_RESPONSE_TIMEOUT_MS = 5*1000; // 5 seconds + static const int COMM_RESPONSE_MAX_EXP = 16; + static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; + static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; + static const int32_t COMM_SYN_INTERVAL_US = 10*1000; + + int handshakeVersion() + { + return m_ConnRes.m_iVersion; + } + + std::string CONID() const + { +#if ENABLE_LOGGING + std::ostringstream os; + os << "%" << m_SocketID << ":"; + return os.str(); +#else + return ""; +#endif + } + + SRTSOCKET socketID() { return m_SocketID; } + + static CUDT* getUDTHandle(SRTSOCKET u); + static std::vector existingSockets(); + + void addressAndSend(CPacket& pkt); + void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, int srtlen_in = 0); + + bool isTsbPd() { return m_bOPT_TsbPd; } + int RTT() { return m_iRTT; } + int32_t sndSeqNo() { return m_iSndCurrSeqNo; } + int32_t rcvSeqNo() { return m_iRcvCurrSeqNo; } + int flowWindowSize() { return m_iFlowWindowSize; } + int32_t deliveryRate() { return m_iDeliveryRate; } + int bandwidth() { return m_iBandwidth; } + int64_t maxBandwidth() { return m_llMaxBW; } + int MSS() { return m_iMSS; } + size_t maxPayloadSize() { return m_iMaxSRTPayloadSize; } + size_t OPT_PayloadSize() { return m_zOPT_ExpPayloadSize; } + uint64_t minNAKInterval() { return m_ullMinNakInt_tk; } + int32_t ISN() { return m_iISN; } + int sndLossLength() { return m_pSndLossList->getLossLength(); } + + // XXX See CUDT::tsbpd() to see how to implement it. This should + // do the same as TLPKTDROP feature when skipping packets that are agreed + // to be lost. Note that this is predicted to be called with TSBPD off. + // This is to be exposed for the application so that it can require this + // sequence to be skipped, if that packet has been otherwise arrived through + // a different channel. + void skipIncoming(int32_t seq); + + void ConnectSignal(ETransmissionEvent tev, EventSlot sl); + void DisconnectSignal(ETransmissionEvent tev); + +private: + /// initialize a UDT entity and bind to a local address. + + void open(); + + /// Start listening to any connection request. + + void setListenState(); + + /// Connect to a UDT entity listening at address "peer". + /// @param peer [in] The address of the listening UDT entity. + + void startConnect(const sockaddr* peer, int32_t forced_isn); + + /// Process the response handshake packet. Failure reasons can be: + /// * Socket is not in connecting state + /// * Response @a pkt is not a handshake control message + /// * Rendezvous socket has once processed a regular handshake + /// @param pkt [in] handshake packet. + /// @retval 0 Connection successful + /// @retval 1 Connection in progress (m_ConnReq turned into RESPONSE) + /// @retval -1 Connection failed + + SRT_ATR_NODISCARD EConnectStatus processConnectResponse(const CPacket& pkt, CUDTException* eout, bool synchro) ATR_NOEXCEPT; + + + // This function works in case of HSv5 rendezvous. It changes the state + // according to the present state and received message type, as well as the + // INITIATOR/RESPONDER side resolved through cookieContest(). + // The resulting data are: + // - rsptype: handshake message type that should be sent back to the peer (nothing if URQ_DONE) + // - needs_extension: the HSREQ/KMREQ or HSRSP/KMRSP extensions should be attached to the handshake message. + // - RETURNED VALUE: if true, it means a URQ_CONCLUSION message was received with HSRSP/KMRSP extensions and needs HSRSP/KMRSP. + void rendezvousSwitchState(ref_t rsptype, ref_t needs_extension, ref_t needs_hsrsp); + void cookieContest(); + + /// Interpret the incoming handshake packet in order to perform appropriate + /// rendezvous FSM state transition if needed, and craft the response, serialized + /// into the packet to be next sent. + /// @param reqpkt Packet to be written with handshake data + /// @param response incoming handshake response packet to be interpreted + /// @param serv_addr incoming packet's address + /// @param synchro True when this function was called in blocking mode + /// @param rst Current read status to know if the HS packet was freshly received from the peer, or this is only a periodic update (RST_AGAIN) + SRT_ATR_NODISCARD EConnectStatus processRendezvous(ref_t reqpkt, const CPacket &response, const sockaddr* serv_addr, bool synchro, EReadStatus); + SRT_ATR_NODISCARD bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); + SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket& response, bool rendezvous, CUDTException* eout, bool synchro); + void applyResponseSettings(); + SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT; + SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr* serv_addr); + + void checkUpdateCryptoKeyLen(const char* loghdr, int32_t typefield); + + SRT_ATR_NODISCARD size_t fillSrtHandshake_HSREQ(uint32_t* srtdata, size_t srtlen, int hs_version); + SRT_ATR_NODISCARD size_t fillSrtHandshake_HSRSP(uint32_t* srtdata, size_t srtlen, int hs_version); + SRT_ATR_NODISCARD size_t fillSrtHandshake(uint32_t* srtdata, size_t srtlen, int msgtype, int hs_version); + + SRT_ATR_NODISCARD bool createSrtHandshake(ref_t reqpkt, ref_t hs, + int srths_cmd, int srtkm_cmd, const uint32_t* data, size_t datalen); + + SRT_ATR_NODISCARD size_t prepareSrtHsMsg(int cmd, uint32_t* srtdata, size_t size); + + SRT_ATR_NODISCARD bool processSrtMsg(const CPacket *ctrlpkt); + SRT_ATR_NODISCARD int processSrtMsg_HSREQ(const uint32_t* srtdata, size_t len, uint32_t ts, int hsv); + SRT_ATR_NODISCARD int processSrtMsg_HSRSP(const uint32_t* srtdata, size_t len, uint32_t ts, int hsv); + SRT_ATR_NODISCARD bool interpretSrtHandshake(const CHandShake& hs, const CPacket& hspkt, uint32_t* out_data, size_t* out_len); + SRT_ATR_NODISCARD bool checkApplyFilterConfig(const std::string& cs); + + void updateAfterSrtHandshake(int srt_cmd, int hsv); + + void updateSrtRcvSettings(); + void updateSrtSndSettings(); + + void checkNeedDrop(ref_t bCongestion); + + /// Connect to a UDT entity listening at address "peer", which has sent "hs" request. + /// @param peer [in] The address of the listening UDT entity. + /// @param hs [in/out] The handshake information sent by the peer side (in), negotiated value (out). + + void acceptAndRespond(const sockaddr* peer, CHandShake* hs, const CPacket& hspkt); + bool runAcceptHook(CUDT* acore, const sockaddr* peer, const CHandShake* hs, const CPacket& hspkt); + + /// Close the opened UDT entity. + + bool close(); + + /// Request UDT to send out a data block "data" with size of "len". + /// @param data [in] The address of the application data to be sent. + /// @param len [in] The size of the data block. + /// @return Actual size of data sent. + + SRT_ATR_NODISCARD int send(const char* data, int len) + { + return sendmsg(data, len, -1, false, 0); + } + + /// Request UDT to receive data to a memory block "data" with size of "len". + /// @param data [out] data received. + /// @param len [in] The desired size of data to be received. + /// @return Actual size of data received. + + SRT_ATR_NODISCARD int recv(char* data, int len); + + /// send a message of a memory block "data" with size of "len". + /// @param data [out] data received. + /// @param len [in] The desired size of data to be received. + /// @param ttl [in] the time-to-live of the message. + /// @param inorder [in] if the message should be delivered in order. + /// @param srctime [in] Time when the data were ready to send. + /// @return Actual size of data sent. + + SRT_ATR_NODISCARD int sendmsg(const char* data, int len, int ttl, bool inorder, uint64_t srctime); + /// Receive a message to buffer "data". + /// @param data [out] data received. + /// @param len [in] size of the buffer. + /// @return Actual size of data received. + + SRT_ATR_NODISCARD int sendmsg2(const char* data, int len, ref_t m); + + SRT_ATR_NODISCARD int recvmsg(char* data, int len, uint64_t& srctime); + + SRT_ATR_NODISCARD int recvmsg2(char* data, int len, ref_t m); + + SRT_ATR_NODISCARD int receiveMessage(char* data, int len, ref_t m); + SRT_ATR_NODISCARD int receiveBuffer(char* data, int len); + + /// Request UDT to send out a file described as "fd", starting from "offset", with size of "size". + /// @param ifs [in] The input file stream. + /// @param offset [in, out] From where to read and send data; output is the new offset when the call returns. + /// @param size [in] How many data to be sent. + /// @param block [in] size of block per read from disk + /// @return Actual size of data sent. + + SRT_ATR_NODISCARD int64_t sendfile(std::fstream& ifs, int64_t& offset, int64_t size, int block = 366000); + + /// Request UDT to receive data into a file described as "fd", starting from "offset", with expected size of "size". + /// @param ofs [out] The output file stream. + /// @param offset [in, out] From where to write data; output is the new offset when the call returns. + /// @param size [in] How many data to be received. + /// @param block [in] size of block per write to disk + /// @return Actual size of data received. + + SRT_ATR_NODISCARD int64_t recvfile(std::fstream& ofs, int64_t& offset, int64_t size, int block = 7320000); + + /// Configure UDT options. + /// @param optName [in] The enum name of a UDT option. + /// @param optval [in] The value to be set. + /// @param optlen [in] size of "optval". + + void setOpt(SRT_SOCKOPT optName, const void* optval, int optlen); + + /// Read UDT options. + /// @param optName [in] The enum name of a UDT option. + /// @param optval [in] The value to be returned. + /// @param optlen [out] size of "optval". + + void getOpt(SRT_SOCKOPT optName, void* optval, int& optlen); + + /// read the performance data with bytes counters since bstats() + /// + /// @param perf [in, out] pointer to a CPerfMon structure to record the performance data. + /// @param clear [in] flag to decide if the local performance trace should be cleared. + /// @param instantaneous [in] flag to request instantaneous data + /// instead of moving averages. + void bstats(CBytePerfMon* perf, bool clear = true, bool instantaneous = false); + + /// Mark sequence contained in the given packet as not lost. This + /// removes the loss record from both current receiver loss list and + /// the receiver fresh loss list. + void unlose(const CPacket& oldpacket); + void dropFromLossLists(int32_t from, int32_t to); + + void considerLegacySrtHandshake(uint64_t timebase); + void checkSndTimers(Whether2RegenKm regen = DONT_REGEN_KM); + void handshakeDone() + { + m_iSndHsRetryCnt = 0; + } + + int64_t withOverhead(int64_t basebw) + { + return (basebw * (100 + m_iOverheadBW))/100; + } + + static double Bps2Mbps(int64_t basebw) + { + return double(basebw) * 8.0/1000000.0; + } + + bool stillConnected() + { + // Still connected is when: + // - no "broken" condition appeared (security, protocol error, response timeout) + return !m_bBroken + // - still connected (no one called srt_close()) + && m_bConnected + // - isn't currently closing (srt_close() called, response timeout, shutdown) + && !m_bClosing; + } + + int sndSpaceLeft() + { + return sndBuffersLeft() * m_iMaxSRTPayloadSize; + } + + int sndBuffersLeft() + { + return m_iSndBufSize - m_pSndBuffer->getCurrBufSize(); + } + + + // TSBPD thread main function. + static void* tsbpd(void* param); + + static CUDTUnited s_UDTUnited; // UDT global management base + +private: // Identification + SRTSOCKET m_SocketID; // UDT socket number + + // XXX Deprecated field. In any place where it's used, UDT_DGRAM is + // the only allowed value. The functionality of distinguishing the transmission + // method is now in m_CongCtl. + UDTSockType m_iSockType; // Type of the UDT connection (SOCK_STREAM or SOCK_DGRAM) + SRTSOCKET m_PeerID; // peer id, for multiplexer + + int m_iMaxSRTPayloadSize; // Maximum/regular payload size, in bytes + size_t m_zOPT_ExpPayloadSize; // Expected average payload size (user option) + + // Options + int m_iMSS; // Maximum Segment Size, in bytes + bool m_bSynSending; // Sending syncronization mode + bool m_bSynRecving; // Receiving syncronization mode + int m_iFlightFlagSize; // Maximum number of packets in flight from the peer side + int m_iSndBufSize; // Maximum UDT sender buffer size + int m_iRcvBufSize; // Maximum UDT receiver buffer size + linger m_Linger; // Linger information on close + int m_iUDPSndBufSize; // UDP sending buffer size + int m_iUDPRcvBufSize; // UDP receiving buffer size + int m_iIPversion; // IP version + bool m_bRendezvous; // Rendezvous connection mode +#ifdef SRT_ENABLE_CONNTIMEO + int m_iConnTimeOut; // connect timeout in milliseconds +#endif + int m_iSndTimeOut; // sending timeout in milliseconds + int m_iRcvTimeOut; // receiving timeout in milliseconds + bool m_bReuseAddr; // reuse an exiting port or not, for UDP multiplexer + int64_t m_llMaxBW; // maximum data transfer rate (threshold) +#ifdef SRT_ENABLE_IPOPTS + int m_iIpTTL; + int m_iIpToS; +#endif + // These fields keep the options for encryption + // (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is + // created later and takes values from these. + HaiCrypt_Secret m_CryptoSecret; + int m_iSndCryptoKeyLen; + + // XXX Consider removing. The m_bDataSender stays here + // in order to maintain the HS side selection in HSv4. + bool m_bDataSender; + + // HSv4 (legacy handshake) support) + uint64_t m_ullSndHsLastTime_us; //Last SRT handshake request time + int m_iSndHsRetryCnt; //SRT handshake retries left + + bool m_bMessageAPI; + bool m_bOPT_TsbPd; // Whether AGENT will do TSBPD Rx (whether peer does, is not agent's problem) + int m_iOPT_TsbPdDelay; // Agent's Rx latency + int m_iOPT_PeerTsbPdDelay; // Peer's Rx latency for the traffic made by Agent's Tx. + bool m_bOPT_TLPktDrop; // Whether Agent WILL DO TLPKTDROP on Rx. + int m_iOPT_SndDropDelay; // Extra delay when deciding to snd-drop for TLPKTDROP, -1 to off + bool m_bOPT_StrictEncryption; // Off by default. When on, any connection other than nopw-nopw & pw1-pw1 is rejected. + std::string m_sStreamName; + int m_iOPT_PeerIdleTimeout; // Timeout for hearing anything from the peer. + + int m_iTsbPdDelay_ms; // Rx delay to absorb burst in milliseconds + int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst in milliseconds + bool m_bTLPktDrop; // Enable Too-late Packet Drop + int64_t m_llInputBW; // Input stream rate (bytes/sec) + int m_iOverheadBW; // Percent above input stream rate (applies if m_llMaxBW == 0) + bool m_bRcvNakReport; // Enable Receiver Periodic NAK Reports + int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set) +private: + UniquePtr m_pCryptoControl; // congestion control SRT class (small data extension) + CCache* m_pCache; // network information cache + + // Congestion control + std::vector m_Slots[TEV__SIZE]; + SrtCongestion m_CongCtl; + + // Packet filtering + PacketFilter m_PacketFilter; + std::string m_OPT_PktFilterConfigString; + SRT_ARQLevel m_PktFilterRexmitLevel; + std::string m_sPeerPktFilterConfigString; + + // Attached tool function + void EmitSignal(ETransmissionEvent tev, EventVariant var); + + // Internal state + volatile bool m_bListening; // If the UDT entit is listening to connection + volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed + volatile bool m_bConnected; // Whether the connection is on or off + volatile bool m_bClosing; // If the UDT entity is closing + volatile bool m_bShutdown; // If the peer side has shutdown the connection + volatile bool m_bBroken; // If the connection has been broken + volatile bool m_bPeerHealth; // If the peer status is normal + volatile SRT_REJECT_REASON m_RejectReason; + bool m_bOpened; // If the UDT entity has been opened + int m_iBrokenCounter; // a counter (number of GC checks) to let the GC tag this socket as disconnected + + int m_iEXPCount; // Expiration counter + int m_iBandwidth; // Estimated bandwidth, number of packets per second + int m_iRTT; // RTT, in microseconds + int m_iRTTVar; // RTT variance + int m_iDeliveryRate; // Packet arrival rate at the receiver side + int m_iByteDeliveryRate; // Byte arrival rate at the receiver side + + uint64_t m_ullLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) + + CHandShake m_ConnReq; // connection request + CHandShake m_ConnRes; // connection response + CHandShake::RendezvousState m_RdvState; // HSv5 rendezvous state + HandshakeSide m_SrtHsSide; // HSv5 rendezvous handshake side resolved from cookie contest (DRAW if not yet resolved) + int64_t m_llLastReqTime; // last time when a connection request is sent + +private: // Sending related data + CSndBuffer* m_pSndBuffer; // Sender buffer + CSndLossList* m_pSndLossList; // Sender loss list + CPktTimeWindow<16, 16> m_SndTimeWindow; // Packet sending time window + + volatile uint64_t m_ullInterval_tk; // Inter-packet time, in CPU clock cycles + uint64_t m_ullTimeDiff_tk; // aggregate difference in inter-packet time + + volatile int m_iFlowWindowSize; // Flow control window size + volatile double m_dCongestionWindow; // congestion window size + + volatile int32_t m_iSndLastFullAck; // Last full ACK received + volatile int32_t m_iSndLastAck; // Last ACK received + volatile int32_t m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list + volatile int32_t m_iSndCurrSeqNo; // The largest sequence number that has been sent + int32_t m_iLastDecSeq; // Sequence number sent last decrease occurs + int32_t m_iSndLastAck2; // Last ACK2 sent back + uint64_t m_ullSndLastAck2Time; // The time when last ACK2 was sent back + int32_t m_iISN; // Initial Sequence Number + bool m_bPeerTsbPd; // Peer accept TimeStamp-Based Rx mode + bool m_bPeerTLPktDrop; // Enable sender late packet dropping + bool m_bPeerNakReport; // Sender's peer (receiver) issues Periodic NAK Reports + bool m_bPeerRexmitFlag; // Receiver supports rexmit flag in payload packets + int32_t m_iReXmitCount; // Re-Transmit Count since last ACK + +private: // Receiving related data + CRcvBuffer* m_pRcvBuffer; //< Receiver buffer + CRcvLossList* m_pRcvLossList; //< Receiver loss list + std::deque m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. + int m_iReorderTolerance; //< Current value of dynamic reorder tolerance + int m_iMaxReorderTolerance; //< Maximum allowed value for dynamic reorder tolerance + int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came m_ACKWindow; //< ACK history window + CPktTimeWindow<16, 64> m_RcvTimeWindow; //< Packet arrival time window + + int32_t m_iRcvLastAck; //< Last sent ACK +#ifdef ENABLE_LOGGING + int32_t m_iDebugPrevLastAck; +#endif + int32_t m_iRcvLastSkipAck; // Last dropped sequence ACK + uint64_t m_ullLastAckTime_tk; // Timestamp of last ACK + int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged + int32_t m_iAckSeqNo; // Last ACK sequence number + int32_t m_iRcvCurrSeqNo; // Largest received sequence number + int32_t m_iRcvCurrPhySeqNo; // Same as m_iRcvCurrSeqNo, but physical only (disregarding a filter) + + uint64_t m_ullLastWarningTime; // Last time that a warning message is sent + + int32_t m_iPeerISN; // Initial Sequence Number of the peer side + uint64_t m_ullRcvPeerStartTime; + + uint32_t m_lSrtVersion; + uint32_t m_lMinimumPeerSrtVersion; + uint32_t m_lPeerSrtVersion; + uint32_t m_lPeerSrtFlags; + + bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets + pthread_t m_RcvTsbPdThread; // Rcv TsbPD Thread handle + pthread_cond_t m_RcvTsbPdCond; + bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent + + CallbackHolder m_cbAcceptHook; + + // FORWARDER +public: + static int installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) + { + return s_UDTUnited.installAcceptHook(lsn, hook, opaq); + } +private: + void installAcceptHook(srt_listen_callback_fn* hook, void* opaq) + { + m_cbAcceptHook.set(opaq, hook); + } + + +private: // synchronization: mutexes and conditions + pthread_mutex_t m_ConnectionLock; // used to synchronize connection operation + + pthread_cond_t m_SendBlockCond; // used to block "send" call + pthread_mutex_t m_SendBlockLock; // lock associated to m_SendBlockCond + + pthread_mutex_t m_RcvBufferLock; // Protects the state of the m_pRcvBuffer + + // Protects access to m_iSndCurrSeqNo, m_iSndLastAck + pthread_mutex_t m_RecvAckLock; // Protects the state changes while processing incomming ACK (UDT_EPOLL_OUT) + + + pthread_cond_t m_RecvDataCond; // used to block "recv" when there is no data + pthread_mutex_t m_RecvDataLock; // lock associated to m_RecvDataCond + + pthread_mutex_t m_SendLock; // used to synchronize "send" call + pthread_mutex_t m_RecvLock; // used to synchronize "recv" call + + pthread_mutex_t m_RcvLossLock; // Protects the receiver loss list (access: CRcvQueue::worker, CUDT::tsbpd) + + pthread_mutex_t m_StatsLock; // used to synchronize access to trace statistics + + void initSynch(); + void destroySynch(); + void releaseSynch(); + +private: // Common connection Congestion Control setup + SRT_REJECT_REASON setupCC(); + void updateCC(ETransmissionEvent, EventVariant arg); + bool createCrypter(HandshakeSide side, bool bidi); + +private: // Generation and processing of packets + void sendCtrl(UDTMessageType pkttype, const void* lparam = NULL, void* rparam = NULL, int size = 0); + + void processCtrl(CPacket& ctrlpkt); + void sendLossReport(const std::vector< std::pair >& losslist); + void processCtrlAck(const CPacket& ctrlpkt, const uint64_t currtime_tk); + + /// + /// @param ackdata_seqno sequence number of a data packet being acknowledged + void updateSndLossListOnACK(int32_t ackdata_seqno); + + /// Pack a packet from a list of lost packets. + /// + /// @param packet [in, out] a packet structure to fill + /// @param origintime [in, out] origin timestamp of the packet + /// + /// @return payload size on success, <=0 on failure + int packLostData(CPacket& packet, uint64_t& origintime); + + int packData(CPacket& packet, uint64_t& ts); + int processData(CUnit* unit); + void processClose(); + SRT_REJECT_REASON processConnectRequest(const sockaddr* addr, CPacket& packet); + static void addLossRecord(std::vector& lossrecord, int32_t lo, int32_t hi); + int32_t bake(const sockaddr* addr, int32_t previous_cookie = 0, int correction = 0); + +private: // Trace + + struct CoreStats + { + uint64_t startTime; // timestamp when the UDT entity is started + int64_t sentTotal; // total number of sent data packets, including retransmissions + int64_t recvTotal; // total number of received packets + int sndLossTotal; // total number of lost packets (sender side) + int rcvLossTotal; // total number of lost packets (receiver side) + int retransTotal; // total number of retransmitted packets + int sentACKTotal; // total number of sent ACK packets + int recvACKTotal; // total number of received ACK packets + int sentNAKTotal; // total number of sent NAK packets + int recvNAKTotal; // total number of received NAK packets + int sndDropTotal; + int rcvDropTotal; + uint64_t bytesSentTotal; // total number of bytes sent, including retransmissions + uint64_t bytesRecvTotal; // total number of received bytes + uint64_t rcvBytesLossTotal; // total number of loss bytes (estimate) + uint64_t bytesRetransTotal; // total number of retransmitted bytes + uint64_t sndBytesDropTotal; + uint64_t rcvBytesDropTotal; + int m_rcvUndecryptTotal; + uint64_t m_rcvBytesUndecryptTotal; + + int sndFilterExtraTotal; + int rcvFilterExtraTotal; + int rcvFilterSupplyTotal; + int rcvFilterLossTotal; + + int64_t m_sndDurationTotal; // total real time for sending + + uint64_t lastSampleTime; // last performance sample time + int64_t traceSent; // number of packets sent in the last trace interval + int64_t traceRecv; // number of packets received in the last trace interval + int traceSndLoss; // number of lost packets in the last trace interval (sender side) + int traceRcvLoss; // number of lost packets in the last trace interval (receiver side) + int traceRetrans; // number of retransmitted packets in the last trace interval + int sentACK; // number of ACKs sent in the last trace interval + int recvACK; // number of ACKs received in the last trace interval + int sentNAK; // number of NAKs sent in the last trace interval + int recvNAK; // number of NAKs received in the last trace interval + int traceSndDrop; + int traceRcvDrop; + int traceRcvRetrans; + int traceReorderDistance; + double traceBelatedTime; + int64_t traceRcvBelated; + uint64_t traceBytesSent; // number of bytes sent in the last trace interval + uint64_t traceBytesRecv; // number of bytes sent in the last trace interval + uint64_t traceRcvBytesLoss; // number of bytes bytes lost in the last trace interval (estimate) + uint64_t traceBytesRetrans; // number of bytes retransmitted in the last trace interval + uint64_t traceSndBytesDrop; + uint64_t traceRcvBytesDrop; + int traceRcvUndecrypt; + uint64_t traceRcvBytesUndecrypt; + + int sndFilterExtra; + int rcvFilterExtra; + int rcvFilterSupply; + int rcvFilterLoss; + + int64_t sndDuration; // real time for sending + int64_t sndDurationCounter; // timers to record the sending duration + } m_stats; + +public: + + static const int SELF_CLOCK_INTERVAL = 64; // ACK interval for self-clocking + static const int SEND_LITE_ACK = sizeof(int32_t); // special size for ack containing only ack seq + static const int PACKETPAIR_MASK = 0xF; + + static const size_t MAX_SID_LENGTH = 512; + +private: // Timers + uint64_t m_ullCPUFrequency; // CPU clock frequency, used for Timer, ticks per microsecond + uint64_t m_ullNextACKTime_tk; // Next ACK time, in CPU clock cycles, same below + uint64_t m_ullNextNAKTime_tk; // Next NAK time + + volatile uint64_t m_ullACKInt_tk; // ACK interval + volatile uint64_t m_ullNAKInt_tk; // NAK interval + volatile uint64_t m_ullLastRspTime_tk; // time stamp of last response from the peer + volatile uint64_t m_ullLastRspAckTime_tk; // time stamp of last ACK from the peer, protect with m_RecvAckLock + volatile uint64_t m_ullLastSndTime_tk; // time stamp of last data/ctrl sent (in system ticks) + uint64_t m_ullMinNakInt_tk; // NAK timeout lower bound; too small value can cause unnecessary retransmission + uint64_t m_ullMinExpInt_tk; // timeout lower bound threshold: too small timeout can cause problem + + int m_iPktCount; // packet counter for ACK + int m_iLightACKCount; // light ACK counter + + uint64_t m_ullTargetTime_tk; // scheduled time of next packet sending + + void checkTimers(); + void checkACKTimer (uint64_t currtime_tk); + void checkNAKTimer(uint64_t currtime_tk); + bool checkExpTimer (uint64_t currtime_tk); // returns true if the connection is expired + void checkRexmitTimer(uint64_t currtime_tk); + +public: // For the use of CCryptoControl + // HaiCrypt configuration + unsigned int m_uKmRefreshRatePkt; + unsigned int m_uKmPreAnnouncePkt; + + +private: // for UDP multiplexer + CSndQueue* m_pSndQueue; // packet sending queue + CRcvQueue* m_pRcvQueue; // packet receiving queue + sockaddr* m_pPeerAddr; // peer address + uint32_t m_piSelfIP[4]; // local UDP IP address + CSNode* m_pSNode; // node information for UDT list used in snd queue + CRNode* m_pRNode; // node information for UDT list used in rcv queue + +public: // For SrtCongestion + const CSndQueue* sndQueue() { return m_pSndQueue; } + const CRcvQueue* rcvQueue() { return m_pRcvQueue; } + +private: // for epoll + std::set m_sPollID; // set of epoll ID to trigger + void addEPoll(const int eid); + void removeEPoll(const int eid); +}; + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp b/trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp new file mode 100644 index 000000000..3c09570a1 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp @@ -0,0 +1,887 @@ +/* + * 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. + *****************************************************************************/ + +#include +#include +#include +#include + +#include "udt.h" +#include "utilities.h" +#include +#include "crypto.h" +#include "logging.h" +#include "core.h" + +using namespace srt_logging; + +#define SRT_MAX_KMRETRY 10 + +//#define SRT_CMD_KMREQ 3 /* HaiCryptTP SRT Keying Material */ +//#define SRT_CMD_KMRSP 4 /* HaiCryptTP SRT Keying Material ACK */ +#define SRT_CMD_KMREQ_SZ HCRYPT_MSG_KM_MAX_SZ /* */ +#if SRT_CMD_KMREQ_SZ > SRT_CMD_MAXSZ +#error SRT_CMD_MAXSZ too small +#endif +/* Key Material Request (Network Order) + See HaiCryptTP SRT (hcrypt_xpt_srt.c) +*/ + +// 10* HAICRYPT_DEF_KM_PRE_ANNOUNCE +const int SRT_CRYPT_KM_PRE_ANNOUNCE = 0x10000; + +#if ENABLE_LOGGING +std::string KmStateStr(SRT_KM_STATE state) +{ + switch (state) + { +#define TAKE(val) case SRT_KM_S_##val : return #val + TAKE(UNSECURED); + TAKE(SECURED); + TAKE(SECURING); + TAKE(NOSECRET); + TAKE(BADSECRET); +#undef TAKE + default: + { + char buf[256]; + sprintf(buf, "??? (%d)", state); + return buf; + } + } +} + + +std::string CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srtlen) +{ + std::ostringstream os; + os << hdr << ": cmd=" << cmd << "(" << (cmd == SRT_CMD_KMREQ ? "KMREQ":"KMRSP") <<") len=" + << size_t(srtlen*sizeof(int32_t)) << " KmState: SND=" + << KmStateStr(m_SndKmState) + << " RCV=" << KmStateStr(m_RcvKmState); + return os.str(); +} +#endif + +void CCryptoControl::updateKmState(int cmd, size_t srtlen SRT_ATR_UNUSED) +{ + if (cmd == SRT_CMD_KMREQ) + { + if ( SRT_KM_S_UNSECURED == m_SndKmState) + { + m_SndKmState = SRT_KM_S_SECURING; + } + LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen)); + } + else + { + LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen)); + } +} + +void CCryptoControl::createFakeSndContext() +{ + if (!m_iSndKmKeyLen) + m_iSndKmKeyLen = 16; + + if (!createCryptoCtx(Ref(m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX)) + { + HLOGC(mglog.Debug, log << "Error: Can't create fake crypto context for sending - sending will return ERROR!"); + m_hSndCrypto = 0; + } +} + +int CCryptoControl::processSrtMsg_KMREQ( + const uint32_t* srtdata SRT_ATR_UNUSED, + size_t bytelen SRT_ATR_UNUSED, + uint32_t* srtdata_out, ref_t r_srtlen, int hsv SRT_ATR_UNUSED) +{ + 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(srtdata_out); + + std::vector kmcopy(kmdata, kmdata + bytelen); + + // The side that has received KMREQ is always an HSD_RESPONDER, regardless of + // what has called this function. The HSv5 handshake only enforces bidirectional + // connection. + + bool bidirectional = hsv > CUDT::HS_VERSION_UDT4; + + // Local macro to return rejection appropriately. + // 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; } + + int rc = HAICRYPT_OK; // needed before 'goto' run from KMREQ_RESULT_REJECTION macro + bool SRT_ATR_UNUSED wasb4 = false; + size_t sek_len = 0; + + // What we have to do: + // If encryption is on (we know that by having m_KmSecret nonempty), create + // the crypto context (if bidirectional, create for both sending and receiving). + // Both crypto contexts should be set with the same length of the key. + // The problem with interpretinting this should be reported as SRT_CMD_NONE, + // should be appropriately handled by the caller, as it expects that this + // 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); + m_RcvKmState = SRT_KM_S_BADSECRET; + KMREQ_RESULT_REJECTION(); + } + + HLOGC(mglog.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!"); + m_RcvKmState = SRT_KM_S_BADSECRET; + KMREQ_RESULT_REJECTION(); + } + + // Write the key length + m_iRcvKmKeyLen = sek_len; + // Overwrite the key length anyway - it doesn't make sense to somehow + // keep the original setting because it will only make KMX impossible. +#if ENABLE_HEAVY_LOGGING + if (m_iSndKmKeyLen != m_iRcvKmKeyLen) + { + LOGC(mglog.Debug, log << "processSrtMsg_KMREQ: Agent's PBKEYLEN=" << m_iSndKmKeyLen + << " overwritten by Peer's PBKEYLEN=" << m_iRcvKmKeyLen); + } +#endif + m_iSndKmKeyLen = m_iRcvKmKeyLen; + + // This is checked only now so that the SRTO_PBKEYLEN return always the correct value, + // even if encryption is not possible because Agent didn't set a password, or supplied + // 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!"); + m_RcvKmState = SRT_KM_S_NOSECRET; + KMREQ_RESULT_REJECTION(); + } + wasb4 = m_hRcvCrypto; + + if (!createCryptoCtx(Ref(m_hRcvCrypto), m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX)) + { + LOGC(mglog.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); + } + // We have both sides set with password, so both are pending for security + m_RcvKmState = SRT_KM_S_SECURING; + // m_SndKmState is set to SECURING or UNSECURED in init(), + // or it might have been set to SECURED, NOSECRET or BADSECRET in the previous + // handshake iteration (handshakes may be sent multiple times for the same connection). + + rc = HaiCrypt_Rx_Process(m_hRcvCrypto, kmdata, bytelen, NULL, NULL, 0); + switch(rc >= 0 ? HAICRYPT_OK : rc) + { + case HAICRYPT_OK: + m_RcvKmState = SRT_KM_S_SECURED; + HLOGC(mglog.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"); + 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"); + break; + } + + LOGP(mglog.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) + goto HSv4_ErrorReport; + + // Configure the sender context also, if it succeeded to configure the + // receiver context and we are using bidirectional mode. + if ( bidirectional ) + { + // Note: 'bidirectional' means that we want a bidirectional key update, + // which happens only and exclusively with HSv5 handshake - not when the + // usual key update through UMSG_EXT+SRT_CMD_KMREQ was done (which is used + // in HSv4 versions also to initialize the first key, unlike HSv5). + if (m_RcvKmState == SRT_KM_S_SECURED) + { + if (m_SndKmState == SRT_KM_S_SECURING && !m_hSndCrypto) + { + 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!"); + if (hasPassphrase()) + m_SndKmState = SRT_KM_S_BADSECRET; + else + m_SndKmState = SRT_KM_S_NOSECRET; + } + else + { + m_SndKmState = SRT_KM_S_SECURED; + } + + LOGC(mglog.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); + 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 " + << KmStateStr(m_SndKmState) << " state"); + } + } + else + { + HLOGP(mglog.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."); + } + + return SRT_CMD_KMRSP; + +HSv4_ErrorReport: + + if (bidirectional && hasPassphrase()) + { + // If the Forward KMX process has failed, the reverse-KMX process was not done at all. + // This will lead to incorrect object configuration and will fail to properly declare + // the transmission state. + // Create the "fake crypto" with the passphrsae you currently have. + createFakeSndContext(); + } +#undef KMREQ_RESULT_REJECTION + +#else + // 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..."); + m_RcvKmState = SRT_KM_S_NOSECRET; +#endif + + srtlen = 1; + + 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*/) +{ + /* All 32-bit msg fields (if present) swapped on reception + * But HaiCrypt expect network order message + * Re-swap to cancel it. + */ + uint32_t srtd[SRTDATA_MAXSIZE]; + size_t srtlen = len/sizeof(uint32_t); + HtoNLA(srtd, srtdata, srtlen); + + int retstatus = -1; + + // Unused? + //bool bidirectional = hsv > CUDT::HS_VERSION_UDT4; + + // 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) // Error report. Set accordingly. + { + SRT_KM_STATE peerstate = SRT_KM_STATE(srtd[SRT_KMR_KMSTATE]); /* Bad or no passphrase */ + m_SndKmMsg[0].iPeerRetry = 0; + m_SndKmMsg[1].iPeerRetry = 0; + + switch (peerstate) + { + case SRT_KM_S_BADSECRET: + m_SndKmState = m_RcvKmState = SRT_KM_S_BADSECRET; + retstatus = -1; + break; + + // Default embraces two cases: + // NOSECRET: this KMRSP was sent by secured Peer, but Agent supplied no password. + // UNSECURED: this KMRSP was sent by unsecure Peer because Agent sent KMREQ. + + case SRT_KM_S_NOSECRET: + // This means that the peer did not set the password, while Agent did. + m_RcvKmState = SRT_KM_S_UNSECURED; + m_SndKmState = SRT_KM_S_NOSECRET; + retstatus = -1; + break; + + case SRT_KM_S_UNSECURED: + // This means that KMRSP was sent without KMREQ, to inform the Agent, + // that the Peer, unlike Agent, does use password. Agent can send then, + // but can't decrypt what Peer would send. + m_RcvKmState = SRT_KM_S_NOSECRET; + m_SndKmState = SRT_KM_S_UNSECURED; + retstatus = 0; + break; + + default: + LOGC(mglog.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; + retstatus = -1; //This is IPE + break; + } + + LOGC(mglog.Error, log << "processSrtMsg_KMRSP: received failure report. STATE: " << KmStateStr(m_RcvKmState)); + } + else + { + HLOGC(mglog.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; + if ( !key1 ) + key2 = getKmMsg_acceptResponse(1, srtd, len); // <--- NOTE SEQUENCING! + + if (key1 || key2) + { + m_SndKmState = m_RcvKmState = SRT_KM_S_SECURED; + HLOGC(mglog.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"); + /* XXX INSECURE + LOGC(mglog.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 + << "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry); + } + + LOGP(mglog.Note, FormatKmMessage("processSrtMsg_KMRSP", SRT_CMD_KMRSP, len)); + + return retstatus; +} + +void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED) +{ + if ( !m_hSndCrypto || m_SndKmState == SRT_KM_S_UNSECURED) + { + HLOGC(mglog.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; + /* + * Crypto Key Distribution to peer: + * If... + * - we want encryption; and + * - we have not tried more than CSRTCC_MAXRETRY times (peer may not be SRT); and + * - and did not get answer back from peer; and + * - 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()))) + { + 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 + << " 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)); + } + } + } + + 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 +#endif +} + +#ifdef SRT_ENABLE_ENCRYPTION +void CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional) +{ + if (!m_hSndCrypto) + return; + + void *out_p[2]; + size_t out_len_p[2]; + 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")); + + for (int i = 0; i < nbo && i < 2; i++) + { + /* + * New connection keying material + * or regenerated after crypto_cfg.km_refresh_rate_pkt packets . + * Send to peer + */ + // XXX Need to make it clearer and less hardcoded values + int kix = hcryptMsg_KM_GetKeyIndex((unsigned char *)(out_p[i])); + int ki = kix & 0x1; + if ((out_len_p[i] != m_SndKmMsg[ki].MsgLen) + || (0 != memcmp(out_p[i], m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen))) + { + + uint8_t* oldkey SRT_ATR_UNUSED = m_SndKmMsg[ki].Msg; + HLOGC(mglog.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]); + m_SndKmMsg[ki].MsgLen = out_len_p[i]; + m_SndKmMsg[ki].iPeerRetry = SRT_MAX_KMRETRY; + + if (bidirectional && !sendit) + { + // "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); + // The party won't be able to decrypt incoming data! + // Not sure if anything has to be reported. + } + } + + if (sendit) + { + HLOGC(mglog.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)); + sent++; + } + } + else if (out_len_p[i] == 0) + { + HLOGC(mglog.Debug, log << "no key[" << ki << "] index=" << kix << ": not generated"); + } + else + { + HLOGC(mglog.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 + << "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry); + + if (sent) + m_SndKmLastTime = CTimer::getTime(); +} +#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) +{ + + m_KmSecret.len = 0; + //send + m_SndKmLastTime = 0; + m_SndKmMsg[0].MsgLen = 0; + m_SndKmMsg[0].iPeerRetry = 0; + m_SndKmMsg[1].MsgLen = 0; + m_SndKmMsg[1].iPeerRetry = 0; + m_hSndCrypto = NULL; + //recv + m_hRcvCrypto = NULL; +} + +bool CCryptoControl::init(HandshakeSide side, 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:" + << (side == HSD_INITIATOR ? "INITIATOR" : "RESPONDER") + << " DIRECTION:" << (bidirectional ? "BOTH" : (side == HSD_INITIATOR) ? "SENDER" : "RECEIVER")); + + // Set UNSECURED state as default + m_RcvKmState = SRT_KM_S_UNSECURED; + + // 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; + + if ( side == HSD_INITIATOR ) + { + if (hasPassphrase()) + { +#ifdef SRT_ENABLE_ENCRYPTION + if (m_iSndKmKeyLen == 0) + { + HLOGC(mglog.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); + + 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); + ok = st == 0; + } + + // Note: this is sanity check, it should never happen. + if (!ok) + { + m_SndKmState = SRT_KM_S_NOSECRET; // wanted to secure, but error occurred. + if (bidirectional) + m_RcvKmState = SRT_KM_S_NOSECRET; + + return false; + } + + 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 + ); +#else + LOGC(mglog.Error, log << "CCryptoControl::init: encryption not supported"); + return true; +#endif + } + else + { + HLOGC(mglog.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"); + } + + return true; +} + +void CCryptoControl::close() +{ + /* Wipeout secrets */ + memset(&m_KmSecret, 0, sizeof(m_KmSecret)); +} + +std::string CCryptoControl::CONID() const +{ + if ( m_SocketID == 0 ) + return ""; + + std::ostringstream os; + os << "%" << m_SocketID << ":"; + + return os.str(); +} + +#if ENABLE_HEAVY_LOGGING +static std::string CryptoFlags(int flg) +{ + using namespace std; + + vector f; + if (flg & HAICRYPT_CFG_F_CRYPTO) + f.push_back("crypto"); + if (flg & HAICRYPT_CFG_F_TX) + f.push_back("TX"); + if (flg & HAICRYPT_CFG_F_FEC) + f.push_back("fec"); + + ostringstream os; + copy(f.begin(), f.end(), ostream_iterator(os, "|")); + return os.str(); +} +#endif + +#ifdef SRT_ENABLE_ENCRYPTION +bool CCryptoControl::createCryptoCtx(ref_t hCrypto, size_t keylen, HaiCrypt_CryptoDir cdir) +{ + + if (*hCrypto) + { + // XXX You can check here if the existing handle represents + // a correctly defined crypto. But this doesn't seem to be + // necessary - the whole CCryptoControl facility seems to be valid only + // within the frames of one connection. + return true; + } + + if ((m_KmSecret.len <= 0) || (keylen <= 0)) + { + LOGC(mglog.Error, log << CONID() << "cryptoCtx: missing secret (" << m_KmSecret.len << ") or key length (" << keylen << ")"); + return false; + } + + HaiCrypt_Cfg crypto_cfg; + memset(&crypto_cfg, 0, sizeof(crypto_cfg)); +#if 0//test key refresh (fast rate) + m_KmRefreshRatePkt = 2000; + m_KmPreAnnouncePkt = 500; +#endif + crypto_cfg.flags = HAICRYPT_CFG_F_CRYPTO | (cdir == HAICRYPT_CRYPTO_DIR_TX ? HAICRYPT_CFG_F_TX : 0); + crypto_cfg.xport = HAICRYPT_XPT_SRT; + crypto_cfg.cryspr = HaiCryptCryspr_Get_Instance(); + crypto_cfg.key_len = (size_t)keylen; + crypto_cfg.data_max_len = HAICRYPT_DEF_DATA_MAX_LENGTH; //MTU + crypto_cfg.km_tx_period_ms = 0;//No HaiCrypt KM inject period, handled in SRT; + crypto_cfg.km_refresh_rate_pkt = m_KmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : m_KmRefreshRatePkt; + crypto_cfg.km_pre_announce_pkt = m_KmPreAnnouncePkt == 0 ? SRT_CRYPT_KM_PRE_ANNOUNCE : m_KmPreAnnouncePkt; + crypto_cfg.secret = m_KmSecret; + //memcpy(&crypto_cfg.secret, &m_KmSecret, sizeof(crypto_cfg.secret)); + + HLOGC(mglog.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) + { + LOGC(mglog.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); + + return true; +} +#else +bool CCryptoControl::createCryptoCtx(ref_t, size_t, HaiCrypt_CryptoDir) +{ + return false; +} +#endif + + +EncryptionStatus CCryptoControl::encrypt(ref_t r_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()); + if (rc < 0) + { + return ENCS_FAILED; + } + else if ( rc > 0 ) + { + // 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); + } + + return ENCS_CLEAR; +#else + return ENCS_NOTSUP; +#endif +} + +EncryptionStatus CCryptoControl::decrypt(ref_t r_packet SRT_ATR_UNUSED) +{ +#ifdef SRT_ENABLE_ENCRYPTION + CPacket& packet = *r_packet; + + if (packet.getMsgCryptoFlags() == EK_NOENC) + { + HLOGC(mglog.Debug, log << "CPacket::decrypt: packet not encrypted"); + return ENCS_CLEAR; // not encrypted, no need do decrypt, no flags to be modified + } + + if (m_RcvKmState == SRT_KM_S_UNSECURED) + { + if (m_KmSecret.len != 0) + { + // 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"); + return ENCS_FAILED; + } + else + { + // Peer has set a password, but Agent did not, + // 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"); + // This only informs about the state change; it will be also caught by the condition below + } + } + + if (m_RcvKmState != SRT_KM_S_SECURED) + { + // If not "secured", it means that it won't be able to decrypt packets, + // so there's no point to even try to send them to HaiCrypt_Rx_Data. + // Actually the current conditions concerning m_hRcvCrypto are such that this object + // is cretaed in case of SRT_KM_S_BADSECRET, so it will simply fail to decrypt, + // but with SRT_KM_S_NOSECRET m_hRcvCrypto is not even created (is NULL), which + // will then cause an error to be reported, misleadingly. Simply don't try to + // decrypt anything as long as you are not sure that the connection is secured. + + // This problem will occur every time a packet comes in, it's worth reporting, + // but not with every single packet arriving. Print it once and turn off the flag; + // it will be restored at the next attempt of KMX. + if (!m_bErrorReported) + { + m_bErrorReported = true; + LOGC(mglog.Error, log << "SECURITY STATUS: " << KmStateStr(m_RcvKmState) << " - can't decrypt packet."); + } + HLOGC(mglog.Debug, log << "Packet still not decrypted, status=" << KmStateStr(m_RcvKmState) + << " - dropping size=" << packet.getLength()); + return ENCS_FAILED; + } + + int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, (uint8_t *)packet.getHeader(), (uint8_t *)packet.m_pcData, packet.getLength()); + if ( rc <= 0 ) + { + LOGC(mglog.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 */ + + // Decryption succeeded. Update flags. + packet.setMsgCryptoFlags(EK_NOENC); + + HLOGC(mglog.Debug, log << "decrypt: successfully decrypted, resulting length=" << rc); + return ENCS_CLEAR; +#else + return ENCS_NOTSUP; +#endif +} + + +CCryptoControl::~CCryptoControl() +{ +#ifdef SRT_ENABLE_ENCRYPTION + if (m_hSndCrypto) + { + HaiCrypt_Close(m_hSndCrypto); + } + + if (m_hRcvCrypto) + { + HaiCrypt_Close(m_hRcvCrypto); + } +#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; +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/crypto.h b/trunk/3rdparty/srt-1-fit/srtcore/crypto.h new file mode 100644 index 000000000..31744e663 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/crypto.h @@ -0,0 +1,268 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef INC__CRYPTO_H +#define INC__CRYPTO_H + +#include +#include + +// UDT +#include "udt.h" +#include "packet.h" +#include "utilities.h" +#include "logging.h" + +#include +#include + +#if ENABLE_LOGGING + +std::string KmStateStr(SRT_KM_STATE state); + +namespace srt_logging +{ +extern Logger mglog; +} + +#endif + +// 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); + +enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1}; + +class CCryptoControl +{ +//public: + class CUDT* m_parent; + SRTSOCKET m_SocketID; + + size_t m_iSndKmKeyLen; //Key length + size_t m_iRcvKmKeyLen; //Key length from rx KM + + // Temporarily allow these to be accessed. +public: + SRT_KM_STATE m_SndKmState; //Sender Km State (imposed by agent) + SRT_KM_STATE m_RcvKmState; //Receiver Km State (informed by peer) + +private: + // Partial haicrypt configuration, consider + // putting the whole HaiCrypt_Cfg object here. + int m_KmRefreshRatePkt; + int m_KmPreAnnouncePkt; + + HaiCrypt_Secret m_KmSecret; //Key material shared secret + // Sender + uint64_t m_SndKmLastTime; + struct { + unsigned char Msg[HCRYPT_MSG_KM_MAX_SZ]; + size_t MsgLen; + int iPeerRetry; + } m_SndKmMsg[2]; + HaiCrypt_Handle m_hSndCrypto; + // Receiver + HaiCrypt_Handle m_hRcvCrypto; + + bool m_bErrorReported; + +public: + + bool sendingAllowed() + { + // This function is called to state as to whether the + // crypter allows the packet to be sent over the link. + // This is possible in two cases: + // - when Agent didn't set a password, no matter the crypto state + if (m_KmSecret.len == 0) + return true; + // - when Agent did set a password and the crypto state is SECURED. + if (m_KmSecret.len > 0 && m_SndKmState == SRT_KM_S_SECURED + // && m_iRcvPeerKmState == SRT_KM_S_SECURED ? + ) + return true; + + return false; + } + + bool hasPassphrase() const + { + return m_KmSecret.len > 0; + } + +private: + +#ifdef SRT_ENABLE_ENCRYPTION + void regenCryptoKm(bool sendit, bool bidirectional); +#endif + +public: + + size_t KeyLen() { return m_iSndKmKeyLen; } + + // Needed for CUDT + 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 r_srtlen, int hsv); + + // This returns: + // 1 - the given payload is the same as the currently used key + // 0 - there's no key in agent or the payload is error message with agent NOSECRET. + // -1 - the payload is error message with other state or it doesn't match the key + int processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int hsv); + void createFakeSndContext(); + + const unsigned char* getKmMsg_data(size_t ki) const { return m_SndKmMsg[ki].Msg; } + size_t getKmMsg_size(size_t ki) const { return m_SndKmMsg[ki].MsgLen; } + + /// Check if the key stored at @c ki shall be sent. When during the handshake, + /// it only matters if the KM message for that index is recorded at all. + /// Otherwise returns true only if also the retry counter didn't expire. + /// + /// @param ki Key index (0 or 1) + /// @param runtime True, if this happens as a key update + /// during transmission (otherwise it's during the handshake) + /// @return Whether the KM message at given index needs to be sent. + bool getKmMsg_needSend(size_t ki, bool runtime) const + { + if (runtime) + return (m_SndKmMsg[ki].iPeerRetry > 0 && m_SndKmMsg[ki].MsgLen > 0); + else + return m_SndKmMsg[ki].MsgLen > 0; + } + + /// Mark the key as already sent. When no 'runtime' (during the handshake) + /// it actually does nothing so that this will be retried as long as the handshake + /// itself is being retried. Otherwise this is during transmission and will expire + /// after several retries. + /// + /// @param ki Key index (0 or 1) + /// @param runtime True, if this happens as a key update + /// during transmission (otherwise it's during the handshake) + void getKmMsg_markSent(size_t ki, bool runtime) + { +#if ENABLE_LOGGING + using srt_logging::mglog; +#endif + + m_SndKmLastTime = CTimer::getTime(); + if (runtime) + { + m_SndKmMsg[ki].iPeerRetry--; + HLOGC(mglog.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."); + } + } + + /// Check if the response returned by KMRSP matches the recorded KM message. + /// When it is, set also the retry counter to 0 to prevent further retries. + /// + /// @param ki KM message index (0 or 1) + /// @param srtmsg Message received through KMRSP + /// @param bytesize Size of the message + /// @return True if the message is identical to the recorded KM message at given index. + bool getKmMsg_acceptResponse(size_t ki, const uint32_t* srtmsg, size_t bytesize) + { + if ( m_SndKmMsg[ki].MsgLen == bytesize + && 0 == memcmp(m_SndKmMsg[ki].Msg, srtmsg, m_SndKmMsg[ki].MsgLen)) + { + m_SndKmMsg[ki].iPeerRetry = 0; + return true; + } + return false; + } + + CCryptoControl(CUDT* parent, SRTSOCKET id); + + // DEBUG PURPOSES: + std::string CONID() const; + std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen); + + bool init(HandshakeSide, 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); + + + void setCryptoSecret(const HaiCrypt_Secret& secret) + { + m_KmSecret = secret; + //memcpy(&m_KmSecret, &secret, sizeof(m_KmSecret)); + } + + void setCryptoKeylen(size_t keylen) + { + m_iSndKmKeyLen = keylen; + m_iRcvKmKeyLen = keylen; + } + + bool createCryptoCtx(ref_t rh, size_t keylen, HaiCrypt_CryptoDir tx); + + int getSndCryptoFlags() const + { +#ifdef SRT_ENABLE_ENCRYPTION + return(m_hSndCrypto ? + HaiCrypt_Tx_GetKeyFlags(m_hSndCrypto) : + // When encryption isn't on, check if it was required + // If it was, return -1 as flags, which means that + // encryption was requested and not possible. + hasPassphrase() ? -1 : + 0); +#else + return 0; +#endif + } + + bool isSndEncryptionOK() const + { + // Similar to this above, just quickly check if the encryption + // is required and possible, or not possible + if (!hasPassphrase()) + return true; // no encryption required + + if (m_hSndCrypto) + return true; // encryption is required and possible + + return false; + } + + /// Encrypts the packet. If encryption is not turned on, it + /// does nothing. If the encryption is not correctly configured, + /// 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 r_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 r_packet); + + ~CCryptoControl(); +}; + +#endif // SRT_CONGESTION_CONTROL_H diff --git a/trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp b/trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp new file mode 100644 index 000000000..84a63c854 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp @@ -0,0 +1,727 @@ +/* + * 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 01/01/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifdef LINUX + #include + #include +#endif +#if __APPLE__ + #include "TargetConditionals.h" +#endif +#if defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1) + #include + #include + #include + #include +#endif +#if defined(__ANDROID__) || defined(ANDROID) + #include +#endif +#include +#include +#include +#include + +#include "common.h" +#include "epoll.h" +#include "logging.h" +#include "udt.h" + +using namespace std; + +namespace srt_logging +{ +extern Logger mglog; +} + +using namespace srt_logging; + +#if ENABLE_HEAVY_LOGGING +#define IF_DIRNAME(tested, flag, name) (tested & flag ? name : "") +#endif + +CEPoll::CEPoll(): +m_iIDSeed(0) +{ + CGuard::createMutex(m_EPollLock); +} + +CEPoll::~CEPoll() +{ + CGuard::releaseMutex(m_EPollLock); +} + +int CEPoll::create() +{ + CGuard pg(m_EPollLock); + + if (++ m_iIDSeed >= 0x7FFFFFFF) + m_iIDSeed = 0; + + // Check if an item already exists. Should not ever happen. + if (m_mPolls.find(m_iIDSeed) != m_mPolls.end()) + throw CUDTException(MJ_SETUP, MN_NONE); + + int localid = 0; + + #ifdef LINUX + localid = epoll_create(1024); + /* 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. +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) + localid = kqueue(); + if (localid < 0) + throw CUDTException(MJ_SETUP, MN_NONE, errno); + #else + // on Solaris, use /dev/poll + // on Windows, select + #endif + + pair::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); + + return m_iIDSeed; +} + +int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + +#ifdef LINUX + epoll_event ev; + memset(&ev, 0, sizeof(epoll_event)); + + if (NULL == events) + ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; + else + { + ev.events = 0; + if (*events & UDT_EPOLL_IN) + ev.events |= EPOLLIN; + if (*events & UDT_EPOLL_OUT) + ev.events |= EPOLLOUT; + if (*events & UDT_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) + struct kevent ke[2]; + int num = 0; + + if (NULL == events) + { + EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL); + EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL); + } + else + { + if (*events & UDT_EPOLL_IN) + { + EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL); + } + if (*events & UDT_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 + +#ifdef _MSC_VER +// Microsoft Visual Studio doesn't support the #warning directive - nonstandard anyway. +// Use #pragma message with the same text. +// All other compilers should be ok :) +#pragma message("WARNING: Unsupported system for epoll. The epoll_add_ssock() API call won't work on this platform.") +#else +#warning "Unsupported system for epoll. The epoll_add_ssock() API call won't work on this platform." +#endif + +#endif + + p->second.m_sLocals.insert(s); + + return 0; +} + +int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + +#ifdef LINUX + 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) + struct kevent ke; + + // + // Since I don't know what was set before + // Just clear out both read and write + // + EV_SET(&ke, s, EVFILT_READ, EV_DELETE, 0, 0, NULL); + kevent(p->second.m_iLocalID, &ke, 1, NULL, 0, NULL); + EV_SET(&ke, s, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + kevent(p->second.m_iLocalID, &ke, 1, NULL, 0, NULL); +#endif + + p->second.m_sLocals.erase(s); + + return 0; +} + +// 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) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + + CEPollDesc& d = p->second; + + 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; + if (evts) + { + pair iter_new = d.addWatch(u, evts, edgeTriggered); + CEPollDesc::Wait& wait = iter_new.first->second; + if (!iter_new.second) + { + // The object exists. We only are certain about the `u` + // parameter, but others are probably unchanged. Change them + // forcefully and take out notices that are no longer valid. + const int removable = wait.watch & ~evts; + + // Check if there are any events that would be removed. + // If there are no removed events watched (for example, when + // only new events are being added to existing socket), + // there's nothing to remove, but might be something to update. + if (removable) + { + d.removeExcessEvents(wait, evts); + } + + // Update the watch configuration, including edge + wait.watch = evts; + if (edgeTriggered) + wait.edge = evts; + + // Now it should look exactly like newly added + // and the state is also updated + } + + const int newstate = wait.watch & wait.state; + if (newstate) + { + d.addEventNotice(wait, u, newstate); + } + } + else if (edgeTriggered) + { + // 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 + d.removeSubscription(u); + } + return 0; +} + +int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + +#ifdef LINUX + epoll_event ev; + memset(&ev, 0, sizeof(epoll_event)); + + if (NULL == events) + ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; + else + { + ev.events = 0; + if (*events & UDT_EPOLL_IN) + ev.events |= EPOLLIN; + if (*events & UDT_EPOLL_OUT) + ev.events |= EPOLLOUT; + if (*events & UDT_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) + struct kevent ke[2]; + int num = 0; + + // + // Since I don't know what was set before + // Just clear out both read and write + // + EV_SET(&ke[0], s, EVFILT_READ, EV_DELETE, 0, 0, NULL); + kevent(p->second.m_iLocalID, ke, 1, NULL, 0, NULL); + EV_SET(&ke[0], s, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + kevent(p->second.m_iLocalID, ke, 1, NULL, 0, NULL); + if (NULL == events) + { + EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL); + EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL); + } + else + { + if (*events & UDT_EPOLL_IN) + { + EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL); + } + if (*events & UDT_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(); +#endif +// Assuming add is used if not inserted +// p->second.m_sLocals.insert(s); + + return 0; +} + +int CEPoll::setflags(const int eid, int32_t flags) +{ + CGuard pg(m_EPollLock); + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + CEPollDesc& ed = p->second; + + int32_t oflags = ed.flags(); + + if (flags == -1) + return oflags; + + if (flags == 0) + { + ed.clr_flags(~int32_t()); + } + else + { + ed.set_flags(flags); + } + + return oflags; +} + +int 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 + // the number of ready sockets, just without information which. + if (fdsSize < 0 || (fdsSize > 0 && !fdsSet)) + throw CUDTException(MJ_NOTSUP, MN_INVAL); + + int64_t entertime = CTimer::getTime(); + + while (true) + { + { + CGuard pg(m_EPollLock); + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + CEPollDesc& ed = p->second; + + if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty()) + { + // Empty EID is not allowed, report error. + throw CUDTException(MJ_NOTSUP, MN_INVAL); + } + + if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK) && (fdsSet == NULL || fdsSize == 0)) + { + // Empty EID is not allowed, report error. + throw CUDTException(MJ_NOTSUP, MN_INVAL); + } + + if (!ed.m_sLocals.empty()) + { + // XXX Add error log + // uwait should not be used with EIDs subscribed to system sockets + throw CUDTException(MJ_NOTSUP, MN_INVAL); + } + + int total = 0; // This is a list, so count it during iteration + CEPollDesc::enotice_t::iterator i = ed.enotice_begin(); + while (i != ed.enotice_end()) + { + int pos = total; // previous past-the-end position + ++total; + + if (total > fdsSize) + break; + + fdsSet[pos] = *i; + + ed.checkEdge(i++); // NOTE: potentially deletes `i` + } + if (total) + return total; + } + + if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000))) + break; // official wait does: throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); + + CTimer::waitForEvent(); + } + + return 0; +} + +int CEPoll::wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) +{ + // if all fields is NULL and waiting time is infinite, then this would be a deadlock + if (!readfds && !writefds && !lrfds && !lwfds && (msTimeOut < 0)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + // Clear these sets in case the app forget to do it. + if (readfds) readfds->clear(); + if (writefds) writefds->clear(); + if (lrfds) lrfds->clear(); + if (lwfds) lwfds->clear(); + + int total = 0; + + int64_t entertime = CTimer::getTime(); + + HLOGC(mglog.Debug, log << "CEPoll::wait: START for eid=" << eid); + + while (true) + { + { + CGuard epollock(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + { + throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + } + + CEPollDesc& ed = p->second; + + 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); + } + + if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK)) + { + // Empty report is not allowed, report error. + if (!ed.m_sLocals.empty() && (!lrfds || !lwfds)) + throw CUDTException(MJ_NOTSUP, MN_INVAL); + + if (!ed.watch_empty() && (!readfds || !writefds)) + throw CUDTException(MJ_NOTSUP, MN_INVAL); + } + + IF_HEAVY_LOGGING(int total_noticed = 0); + IF_HEAVY_LOGGING(ostringstream debug_sockets); + // Sockets with exceptions are returned to both read and write sets. + for (CEPollDesc::enotice_t::iterator it = ed.enotice_begin(), it_next = it; it != ed.enotice_end(); it = it_next) + { + ++it_next; + IF_HEAVY_LOGGING(++total_noticed); + if (readfds && ((it->events & UDT_EPOLL_IN) || (it->events & UDT_EPOLL_ERR))) + { + if (readfds->insert(it->fd).second) + ++total; + } + + if (writefds && ((it->events & UDT_EPOLL_OUT) || (it->events & UDT_EPOLL_ERR))) + { + if (writefds->insert(it->fd).second) + ++total; + } + + IF_HEAVY_LOGGING(debug_sockets << " " << it->fd << ":" + << IF_DIRNAME(it->events, SRT_EPOLL_IN, "R") + << IF_DIRNAME(it->events, SRT_EPOLL_OUT, "W") + << IF_DIRNAME(it->events, SRT_EPOLL_ERR, "E")); + + if (ed.checkEdge(it)) // NOTE: potentially erases 'it'. + { + IF_HEAVY_LOGGING(debug_sockets << "!"); + } + } + + HLOGC(mglog.Debug, log << "CEPoll::wait: REPORTED " << total << "/" << total_noticed + << debug_sockets.str()); + + if (lrfds || lwfds) + { +#ifdef LINUX + const int max_events = ed.m_sLocals.size(); + epoll_event ev[max_events]; + int nfds = ::epoll_wait(ed.m_iLocalID, ev, max_events, 0); + + IF_HEAVY_LOGGING(const int prev_total = total); + for (int i = 0; i < nfds; ++ i) + { + if ((NULL != lrfds) && (ev[i].events & EPOLLIN)) + { + lrfds->insert(ev[i].data.fd); + ++ total; + } + if ((NULL != lwfds) && (ev[i].events & EPOLLOUT)) + { + lwfds->insert(ev[i].data.fd); + ++ total; + } + } + HLOGC(mglog.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) + struct timespec tmout = {0, 0}; + const int max_events = ed.m_sLocals.size(); + struct kevent ke[max_events]; + + int nfds = kevent(ed.m_iLocalID, NULL, 0, ke, max_events, &tmout); + IF_HEAVY_LOGGING(const int prev_total = total); + + for (int i = 0; i < nfds; ++ i) + { + if ((NULL != lrfds) && (ke[i].filter == EVFILT_READ)) + { + lrfds->insert(ke[i].ident); + ++ total; + } + if ((NULL != lwfds) && (ke[i].filter == EVFILT_WRITE)) + { + lwfds->insert(ke[i].ident); + ++ total; + } + } + + HLOGC(mglog.Debug, log << "CEPoll::wait: Darwin/BSD: picking up " << (total - prev_total) << " ready fds."); + +#else + //currently "select" is used for all non-Linux platforms. + //faster approaches can be applied for specific systems in the future. + + //"select" has a limitation on the number of sockets + int max_fd = 0; + + fd_set rqreadfds; + fd_set rqwritefds; + FD_ZERO(&rqreadfds); + FD_ZERO(&rqwritefds); + + for (set::const_iterator i = ed.m_sLocals.begin(); i != ed.m_sLocals.end(); ++ i) + { + if (lrfds) + FD_SET(*i, &rqreadfds); + if (lwfds) + FD_SET(*i, &rqwritefds); + if ((int)*i > max_fd) + max_fd = *i; + } + + IF_HEAVY_LOGGING(const int prev_total = total); + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + if (::select(max_fd + 1, &rqreadfds, &rqwritefds, NULL, &tv) > 0) + { + for (set::const_iterator i = ed.m_sLocals.begin(); i != ed.m_sLocals.end(); ++ i) + { + if (lrfds && FD_ISSET(*i, &rqreadfds)) + { + lrfds->insert(*i); + ++ total; + } + if (lwfds && FD_ISSET(*i, &rqwritefds)) + { + lwfds->insert(*i); + ++ total; + } + } + } + + HLOGC(mglog.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"); + + if (total > 0) + return total; + + if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000))) + { + HLOGP(mglog.Debug, "... not waiting longer - 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")); + } + + return 0; +} + +int CEPoll::release(const int eid) +{ + CGuard pg(m_EPollLock); + + map::iterator i = m_mPolls.find(eid); + if (i == m_mPolls.end()) + throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + + #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) + ::close(i->second.m_iLocalID); + #endif + + m_mPolls.erase(i); + + return 0; +} + + +int CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const int events, const bool enable) +{ + vector lost; + + CGuard pg(m_EPollLock); + for (set::iterator i = eids.begin(); i != eids.end(); ++ i) + { + map::iterator p = m_mPolls.find(*i); + if (p == m_mPolls.end()) + { + // 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); + continue; + } + + CEPollDesc& ed = p->second; + + // Check if this EID is subscribed for this socket. + CEPollDesc::Wait* pwait = ed.watch_find(uid); + if (!pwait) + { + // As this is mapped in the socket's data, it should be impossible. + continue; + } + + // compute new states + + // New state to be set into the permanent state + const int newstate = enable ? pwait->state | events // SET event bits if enable + : pwait->state & (~events); // CLEAR event bits + + // compute states changes! + int changes = pwait->state ^ newstate; // oldState XOR newState + if (!changes) + continue; // no changes! + // assign new state + pwait->state = newstate; + // filter change relating what is watching + changes &= pwait->watch; + if (!changes) + continue; // no change watching + // set events changes! + + // This function will update the notice object associated with + // the given events, that is: + // - 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); + } + + for (vector::iterator i = lost.begin(); i != lost.end(); ++ i) + eids.erase(*i); + + return 0; +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/epoll.h b/trunk/3rdparty/srt-1-fit/srtcore/epoll.h new file mode 100755 index 000000000..1d0463ffc --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/epoll.h @@ -0,0 +1,395 @@ +/* + * 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 - 2010, 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 08/20/2010 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef __UDT_EPOLL_H__ +#define __UDT_EPOLL_H__ + + +#include +#include +#include +#include "udt.h" + + +struct CEPollDesc +{ + const int m_iID; // epoll ID + + struct Wait; + + struct Notice: public SRT_EPOLL_EVENT + { + Wait* parent; + + Notice(Wait* p, SRTSOCKET sock, int ev): parent(p) + { + fd = sock; + events = ev; + } + }; + + /// The type for `m_USockEventNotice`, the pair contains: + /// * The back-pointer to the subscriber object for which this event notice serves + /// * The events currently being on + typedef std::list enotice_t; + + struct Wait + { + /// Events the subscriber is interested with. Only those will be + /// regarded when updating event flags. + int 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; + + /// 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; + + /// 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) + :watch(sub) + ,edge(etr ? sub : 0) + ,state(0) + ,notit(i) + { + } + + int edgeOnly() { return edge & watch; } + }; + + typedef std::map ewatch_t; + +private: + + /// Sockets that are subscribed for events in this eid. + ewatch_t m_USockWatchState; + + /// Objects representing changes in SRT sockets. + /// Objects are removed from here when an event is registerred as edge-triggered. + /// Otherwise it is removed only when all events as per subscription + /// are no longer on. + enotice_t m_USockEventNotice; + + // Special behavior + int32_t m_Flags; + + enotice_t::iterator nullNotice() { return m_USockEventNotice.end(); } + +public: + + CEPollDesc(int id, int localID) + : m_iID(id) + , m_Flags(0) + , m_iLocalID(localID) + { + } + + 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; } + 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(); } + Wait* watch_find(SRTSOCKET sock) + { + ewatch_t::iterator i = m_USockWatchState.find(sock); + if (i == m_USockWatchState.end()) + return NULL; + return &i->second; + } + + // Container accessors for enotice_t. + enotice_t::iterator enotice_begin() { return m_USockEventNotice.begin(); } + enotice_t::iterator enotice_end() { return m_USockEventNotice.end(); } + + const int m_iLocalID; // local system epoll ID + std::set m_sLocals; // set of local (non-UDT) descriptors + + std::pair addWatch(SRTSOCKET sock, int32_t events, bool edgeTrg) + { + return m_USockWatchState.insert(std::make_pair(sock, Wait(events, edgeTrg, nullNotice()))); + } + + void addEventNotice(Wait& wait, SRTSOCKET sock, int events) + { + // `events` contains bits to be set, so: + // + // 1. If no notice object exists, add it exactly with `events`. + // 2. If it exists, only set the bits from `events`. + // ASSUME: 'events' is not 0, that is, we have some readiness + + if (wait.notit == nullNotice()) // No notice object + { + // Add new event notice and bind to the wait object. + m_USockEventNotice.push_back(Notice(&wait, sock, events)); + wait.notit = --m_USockEventNotice.end(); + + return; + } + + // We have an existing event notice, so update it + wait.notit->events |= events; + } + + // This function only updates the corresponding event notice object + // according to the change in the events. + void updateEventNotice(Wait& wait, SRTSOCKET sock, int events, bool enable) + { + if (enable) + { + addEventNotice(wait, sock, events); + } + else + { + removeExcessEvents(wait, ~events); + } + } + + void removeSubscription(SRTSOCKET u) + { + std::map::iterator i = m_USockWatchState.find(u); + if (i == m_USockWatchState.end()) + return; + + if (i->second.notit != nullNotice()) + { + m_USockEventNotice.erase(i->second.notit); + // NOTE: no need to update the Wait::notit field + // because the Wait object is about to be removed anyway. + } + m_USockWatchState.erase(i); + } + + void removeExistingNotices(Wait& wait) + { + m_USockEventNotice.erase(wait.notit); + wait.notit = nullNotice(); + } + + void removeEvents(Wait& wait) + { + if (wait.notit == nullNotice()) + return; + removeExistingNotices(wait); + } + + // This function removes notices referring to + // events that are NOT present in @a nevts, but + // may be among subscriptions and therefore potentially + // have an associated notice. + void removeExcessEvents(Wait& wait, int nevts) + { + // Update the event notice, should it exist + // If the watch points to a null notice, there's simply + // no notice there, so nothing to update or prospectively + // remove - but may be something to add. + if (wait.notit == nullNotice()) + return; + + // `events` contains bits to be cleared. + // 1. If there is no notice event, do nothing - clear already. + // 2. If there is a notice event, update by clearing the bits + // 2.1. If this made resulting state to be 0, also remove the notice. + + const int newstate = wait.notit->events & nevts; + if (newstate) + { + wait.notit->events = newstate; + } + else + { + // If the new state is full 0 (no events), + // then remove the corresponding notice object + removeExistingNotices(wait); + } + } + + bool checkEdge(enotice_t::iterator i) + { + // This function should check if this event was subscribed + // as edge-triggered, and if so, clear the event from the notice. + // Update events and check edge mode at the subscriber + i->events &= ~i->parent->edgeOnly(); + if(!i->events) + { + removeExistingNotices(*i->parent); + return true; + } + return false; + } +}; + +class CEPoll +{ +friend class CUDT; +friend class CRendezvousQueue; + +public: + CEPoll(); + ~CEPoll(); + +public: // for CUDTUnited API + + /// create a new EPoll. + /// @return new EPoll ID if success, otherwise an error number. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + int wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds, std::set* 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) + + 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. + + 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 + + int update_events(const SRTSOCKET& uid, std::set& eids, int events, bool enable); + + int setflags(const int eid, int32_t flags); + +private: + int m_iIDSeed; // seed to generate a new ID + pthread_mutex_t m_SeedLock; + + std::map m_mPolls; // all epolls + pthread_mutex_t m_EPollLock; +}; + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/fec.cpp b/trunk/3rdparty/srt-1-fit/srtcore/fec.cpp new file mode 100644 index 000000000..b1e810e52 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/fec.cpp @@ -0,0 +1,2253 @@ +/* + * 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/. + * + */ + + +#include +#include +#include +#include + +#include "packetfilter.h" +#include "core.h" +#include "packet.h" +#include "logging.h" + +#include "fec.h" + +using namespace std; +using namespace srt_logging; + +FECFilterBuiltin::FECFilterBuiltin(const SrtFilterInitializer &init, std::vector &provided, const string &confstr) + : SrtPacketFilterBase(init) + , m_fallback_level(SRT_ARQ_ONREQ) + , m_arrangement_staircase(true) + , rcv(provided) +{ + if (!ParseFilterConfig(confstr, cfg)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + // Configuration supported: + // - row only (number_rows == 1) + // - columns only, no row FEC/CTL (number_rows < -1) + // - columns and rows (both > 1) + + // Disallowed configurations: + // - number_cols < 1 + // - number_rows [-1, 0] + + string arspec = map_get(cfg.parameters, "layout"); + + string shorter = arspec.size() > 5 ? arspec.substr(0, 5) : arspec; + if (shorter == "even") + m_arrangement_staircase = false; + else if (shorter != "" && shorter != "stair") + { + LOGC(mglog.Error, log << "FILTER/FEC: CONFIG: value for 'layout' must be 'even' or 'staircase'"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + string colspec = map_get(cfg.parameters, "cols"), rowspec = map_get(cfg.parameters, "rows"); + + int out_rows = 1; + int out_cols = atoi(colspec.c_str()); + + if (colspec == "" || out_cols < 2) + { + LOGC(mglog.Error, log << "FILTER/FEC: CONFIG: at least 'cols' must be specified and > 1"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + m_number_cols = out_cols; + + if (rowspec != "") + { + out_rows = atoi(rowspec.c_str()); + if (out_rows >= -1 && out_rows < 1) + { + LOGC(mglog.Error, log << "FILTER/FEC: CONFIG: 'rows' must be >=1 or negative < -1"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } + + if (out_rows < 0) + { + m_number_rows = -out_rows; + m_cols_only = true; + } + else + { + m_number_rows = out_rows; + m_cols_only = false; + } + + // Extra interpret level, if found, default never. + // Check only those that are managed. + string level = cfg.parameters["arq"]; + int lv = -1; + if (level != "") + { + static const char* levelnames [] = { "never", "onreq", "always" }; + + for (size_t i = 0; i < Size(levelnames); ++i) + { + if (level == levelnames[i]) + { + lv = i; + break; + } + } + + if (lv == -1) + { + LOGC(mglog.Error, log << "FILTER/FEC: CONFIG: 'arq': value '" << level << "' unknown"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + m_fallback_level = SRT_ARQLevel(lv); + } + else + { + m_fallback_level = SRT_ARQ_ONREQ; + } + + + // Required to store in the header when rebuilding + rcv.id = socketID(); + + // Setup the bit matrix, initialize everything with false. + + // Vertical size (y) + rcv.cells.resize(sizeCol() * sizeRow(), false); + + // These sequence numbers are both the value of ISN-1 at the moment + // when the handshake is done. The sender ISN is generated here, the + // receiver ISN by the peer. Both should be known after the handshake. + // Later they will be updated as packets are transmitted. + + int32_t snd_isn = CSeqNo::incseq(sndISN()); + int32_t rcv_isn = CSeqNo::incseq(rcvISN()); + + // Alright, now we need to get the ISN from m_parent + // to extract the sequence number allowing qualification to the group. + // The base values must be prepared so that feedSource can qualify them. + + // SEPARATE FOR SENDING AND RECEIVING! + + // Now, assignment of the groups requires: + // For row groups, simply the size of the group suffices. + // For column groups, you need a whole matrix of all sequence + // numbers that are base sequence numbers for the group. + // Sequences that belong to this group are: + // 1. First packet has seq+1 towards the base. + // 2. Every next packet has this value + the size of the row group. + // So: group dispatching is: + // - get the column number + // - extract the group data for that column + // - check if the sequence is later than the group base sequence, if not, report no group for the packet + // - sanity check, if the seqdiff divided by row size gets 0 remainder + // - The result from the above division can't exceed the column size, otherwise + // it's another group. The number of currently collected data should be in 'collected'. + + // Now set up the group starting sequences. + // The very first group in both dimensions will have the value of ISN in particular direction. + + // Set up sender part. + // + // Size: rows + // Step: 1 (next packet in group is 1 past the previous one) + // Slip: rows (first packet in the next group is distant to first packet in the previous group by 'rows') + HLOGC(mglog.Debug, log << "FEC: INIT: ISN { snd=" << snd_isn << " rcv=" << rcv_isn << " }; sender single row"); + ConfigureGroup(snd.row, snd_isn, 1, sizeRow()); + + // In the beginning we need just one reception group. New reception + // groups will be created in tact with receiving packets outside this one. + // The value of rcv.row[0].base will be used as an absolute base for calculating + // the index of the group for a given received packet. + rcv.rowq.resize(1); + HLOGP(mglog.Debug, "FEC: INIT: receiver first row"); + ConfigureGroup(rcv.rowq[0], rcv_isn, 1, sizeRow()); + + if (sizeCol() > 1) + { + // Size: cols + // Step: rows (the next packet in the group is one row later) + // Slip: rows+1 (the first packet in the next group is later by 1 column + one whole row down) + + HLOGP(mglog.Debug, "FEC: INIT: sender first N columns"); + ConfigureColumns(snd.cols, snd_isn); + HLOGP(mglog.Debug, "FEC: INIT: receiver first N columns"); + ConfigureColumns(rcv.colq, rcv_isn); + } + + // The bit markers that mark the received/lost packets will be expanded + // as packets come in. + rcv.cell_base = rcv_isn; +} + +template +void FECFilterBuiltin::ConfigureColumns(Container& which, int32_t isn) +{ + // This is to initialize the first set of groups. + + // which: group vector. + // numberCols(): number of packets in one group + // sizeCol(): seqdiff between two packets consecutive in the group + // m_column_slip: seqdiff between the first packet in one group and first packet in the next group + // isn: sequence number of the first packet in the first group + + size_t zero = which.size(); + + // The first series of initialization should embrace: + // - if multiplyer == 1, EVERYTHING (also the case of SOLID matrix) + // - if more, ONLY THE FIRST SQUARE. + which.resize(zero + numberCols()); + + if (!m_arrangement_staircase) + { + HLOGC(mglog.Debug, log << "ConfigureColumns: new " + << numberCols() << " columns, START AT: " << zero); + // With even arrangement, just use a plain loop. + // Initialize straight way all groups in the size. + int32_t seqno = isn; + for (size_t i = zero; i < which.size(); ++i) + { + // ARGS: + // - seqno: sequence number of the first packet in the group + // - step: distance between two consecutive packets in the group + // - drop: distance between base sequence numbers in groups in consecutive series + // (meaning: with row size 6, group with index 2 and 8 are in the + // same column 2, lying in 0 and 1 series respectively). + ConfigureGroup(which[i], seqno, sizeRow(), sizeCol() * numberCols()); + seqno = CSeqNo::incseq(seqno); + } + return; + } + + // With staircase, the next column's base sequence is + // shifted by 1 AND the length of the row. When this shift + // becomes below the column 0 bottom, reset it to the row 0 + // and continue. + + // Start here. The 'isn' is still the absolute base sequence value. + size_t offset = 0; + + HLOGC(mglog.Debug, log << "ConfigureColumns: " << (which.size() - zero) + << " columns, START AT: " << zero); + + for (size_t i = zero; i < which.size(); ++i) + { + int32_t seq = CSeqNo::incseq(isn, offset); + size_t col = i - zero; + + HLOGC(mglog.Debug, log << "ConfigureColumns: [" << col << "]: -> ConfigureGroup..."); + ConfigureGroup(which[i], seq, sizeRow(), sizeCol() * numberCols()); + + if (col % numberRows() == numberRows() - 1) + { + offset = col + 1; // +1 because we want it for the next column + HLOGC(mglog.Debug, log << "ConfigureColumns: [" << (col+1) << "]... (resetting to row 0: +" + << offset << " %" << CSeqNo::incseq(isn, offset) << ")"); + } + else + { + offset += 1 + sizeRow(); + HLOGC(mglog.Debug, log << "ConfigureColumns: [" << (col+1) << "] ... (continue +" + << offset << " %" << CSeqNo::incseq(isn, offset) << ")"); + } + } +} + +void FECFilterBuiltin::ConfigureGroup(Group& g, int32_t seqno, size_t gstep, size_t drop) +{ + g.base = seqno; + g.step = gstep; + + // This actually rewrites the size of the group here, but + // by having this value precalculated we simply close the + // group by adding this value to the base sequence. + g.drop = drop; + g.collected = 0; + + // Now the buffer spaces for clips. + g.payload_clip.resize(payloadSize()); + g.length_clip = 0; + g.flag_clip = 0; + g.timestamp_clip = 0; + + HLOGC(mglog.Debug, log << "FEC: ConfigureGroup: base %" << seqno << " step=" << gstep << " drop=" << drop); + + // Preallocate the buffer that will be used for storing it for + // the needs of passing the data through the network. + // This will be filled with zeros initially, which is unnecessary, + // but it happeens just once after connection. +} + + +void FECFilterBuiltin::ResetGroup(Group& g) +{ + int32_t new_seq_base = CSeqNo::incseq(g.base, g.drop); + + HLOGC(mglog.Debug, log << "FEC: ResetGroup (step=" << g.step << "): base %" << g.base << " -> %" << new_seq_base); + + g.base = new_seq_base; + g.collected = 0; + + // This isn't necessary for ConfigureGroup because the + // vector after resizing is filled with a given value, + // by default the default value of the type, char(), that is 0. + g.length_clip = 0; + g.flag_clip = 0; + g.timestamp_clip = 0; + memset(&g.payload_clip[0], 0, g.payload_clip.size()); +} + +void FECFilterBuiltin::feedSource(CPacket& packet) +{ + // Hang on the matrix. Find by packet->getSeqNo(). + + // (The "absolute base" is the cell 0 in vertical groups) + int32_t base = snd.row.base; + + // (we are guaranteed that this packet is a data packet, so + // we don't have to check if this isn't a control packet) + int baseoff = CSeqNo::seqoff(base, packet.getSeqNo()); + + int horiz_pos = baseoff; + + if (CheckGroupClose(snd.row, horiz_pos, sizeRow())) + { + HLOGC(mglog.Debug, log << "FEC:... HORIZ group closed, B=%" << snd.row.base); + } + ClipPacket(snd.row, packet); + snd.row.collected++; + + // Don't do any column feeding if using column size 1 + if (sizeCol() < 2) + { + // The above logging instruction in case of no columns + HLOGC(mglog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() + << " B:%" << baseoff << " H:*[" << horiz_pos << "]" + << " size=" << packet.size() + << " TS=" << packet.getMsgTimeStamp() + << " !" << BufferStamp(packet.data(), packet.size())); + HLOGC(mglog.Debug, log << "FEC collected: H: " << snd.row.collected); + return; + } + + // 1. Get the number of group in both vertical and horizontal groups: + // - Vertical: offset towards base (% row size, but with updated Base seq unnecessary) + // (Just for a case). + int vert_gx = baseoff % sizeRow(); + + // 2. Define the position of this packet in the group + // - Horizontal: offset towards base (of the given group, not absolute!) + // - Vertical: (seq-base)/column_size + int32_t vert_base = snd.cols[vert_gx].base; + int vert_off = CSeqNo::seqoff(vert_base, packet.getSeqNo()); + + // It MAY HAPPEN that the base is newer than the sequence of the packet. + // This may normally happen in the beginning period, where the bases + // set up initially for all columns got the shift, so they are kinda from + // the future, and "this sequence" is in a group that is already closed. + // In this case simply can't clip the packet in the column group. + + HLOGC(mglog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() << " rowoff=" << baseoff + << " column=" << vert_gx << " .base=%" << vert_base << " coloff=" << vert_off); + + if (vert_off >= 0 && sizeCol() > 1) + { + // BEWARE! X % Y with different signedness upgrades int to unsigned! + + // SANITY: check if the rule applies on the group + if (vert_off % sizeRow()) + { + LOGC(mglog.Fatal, log << "FEC:feedSource: IPE: VGroup #" << vert_gx << " base=%" << vert_base + << " WRONG with horiz base=%" << base << "coloff(" << vert_off + << ") % sizeRow(" << sizeRow() << ") = " << (vert_off % sizeRow())); + + // Do not place it, it would be wrong. + return; + } + + int vert_pos = vert_off / sizeRow(); + + HLOGC(mglog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() + << " B:%" << baseoff << " H:*[" << horiz_pos << "] V(B=%" << vert_base + << ")[col=" << vert_gx << "][" << vert_pos << "/" << sizeCol() << "] " + << " size=" << packet.size() + << " TS=" << packet.getMsgTimeStamp() + << " !" << BufferStamp(packet.data(), packet.size())); + + // 3. The group should be check for the necessity of being closed. + // Note that FEC packet extraction doesn't change the state of the + // VERTICAL groups (it can be potentially extracted multiple times), + // only the horizontal in order to mark that the vertical FEC is + // extracted already. So, anyway, check if the group limit was reached + // and it wasn't closed. + // 4. Apply the clip + // 5. Increase collected. + + if (CheckGroupClose(snd.cols[vert_gx], vert_pos, sizeCol())) + { + HLOGC(mglog.Debug, log << "FEC:... VERT group closed, B=%" << snd.cols[vert_gx].base); + } + ClipPacket(snd.cols[vert_gx], packet); + snd.cols[vert_gx].collected++; + } + else + { + + HLOGC(mglog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() + << " B:%" << baseoff << " H:*[" << horiz_pos << "] V(B=%" << vert_base + << ")[col=" << vert_gx << "]" + << " size=" << packet.size() + << " TS=" << packet.getMsgTimeStamp() + << " !" << BufferStamp(packet.data(), packet.size())); + } + HLOGC(mglog.Debug, log << "FEC collected: H: " << snd.row.collected << " V[" << vert_gx << "]: " << snd.cols[vert_gx].collected); +} + +bool FECFilterBuiltin::CheckGroupClose(Group& g, size_t pos, size_t size) +{ + if (pos < size) + return false; + + ResetGroup(g); + return true; +} + +void FECFilterBuiltin::ClipPacket(Group& g, const CPacket& pkt) +{ + // Both length and timestamp must be taken as NETWORK ORDER + // before applying the clip. + + uint16_t length_net = htons(pkt.size()); + uint8_t kflg = uint8_t(pkt.getMsgCryptoFlags()); + + // NOTE: Unlike length, the TIMESTAMP is NOT endian-reordered + // because it will be written into the TIMESTAMP field in the + // header, and header is inverted automatically when sending, + // unlike the contents of the payload, where the length will be written. + uint32_t timestamp_hw = pkt.getMsgTimeStamp(); + + ClipData(g, length_net, kflg, timestamp_hw, pkt.data(), pkt.size()); + + HLOGC(mglog.Debug, log << "FEC DATA PKT CLIP: " << hex + << "FLAGS=" << unsigned(kflg) << " LENGTH[ne]=" << (length_net) + << " TS[he]=" << timestamp_hw + << " CLIP STATE: FLAGS=" << unsigned(g.flag_clip) + << " LENGTH[ne]=" << g.length_clip + << " TS[he]=" << g.timestamp_clip + << " PL4=" << (*(uint32_t*)&g.payload_clip[0])); +} + +// Clipping a control packet does merely the same, just the packet has +// different contents, so it must be differetly interpreted. +void FECFilterBuiltin::ClipControlPacket(Group& g, const CPacket& pkt) +{ + // Both length and timestamp must be taken as NETWORK ORDER + // before applying the clip. + + const char* fec_header = pkt.data(); + const char* payload = fec_header + 4; + size_t payload_clip_len = pkt.size() - 4; + + const uint8_t* flag_clip = (const uint8_t*)(fec_header + 1); + const uint16_t* length_clip = (const uint16_t*)(fec_header + 2); + + uint32_t timestamp_hw = pkt.getMsgTimeStamp(); + + ClipData(g, *length_clip, *flag_clip, timestamp_hw, payload, payload_clip_len); + + HLOGC(mglog.Debug, log << "FEC/CTL CLIP: " << hex + << "FLAGS=" << unsigned(*flag_clip) << " LENGTH[ne]=" << (*length_clip) + << " TS[he]=" << timestamp_hw + << " CLIP STATE: FLAGS=" << unsigned(g.flag_clip) + << " LENGTH[ne]=" << g.length_clip + << " TS[he]=" << g.timestamp_clip + << " PL4=" << (*(uint32_t*)&g.payload_clip[0])); +} + +void FECFilterBuiltin::ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt) +{ + uint16_t length_net = htons(pkt.length); + uint8_t kflg = MSGNO_ENCKEYSPEC::unwrap(pkt.hdr[SRT_PH_MSGNO]); + + // NOTE: Unlike length, the TIMESTAMP is NOT endian-reordered + // because it will be written into the TIMESTAMP field in the + // header, and header is inverted automatically when sending, + // unlike the contents of the payload, where the length will be written. + uint32_t timestamp_hw = pkt.hdr[SRT_PH_TIMESTAMP]; + + ClipData(g, length_net, kflg, timestamp_hw, pkt.buffer, pkt.length); + + HLOGC(mglog.Debug, log << "FEC REBUILT DATA CLIP: " << hex + << "FLAGS=" << unsigned(kflg) << " LENGTH[ne]=" << (length_net) + << " TS[he]=" << timestamp_hw + << " CLIP STATE: FLAGS=" << unsigned(g.flag_clip) + << " LENGTH[ne]=" << g.length_clip + << " TS[he]=" << g.timestamp_clip + << " PL4=" << (*(uint32_t*)&g.payload_clip[0])); +} + +void FECFilterBuiltin::ClipData(Group& g, uint16_t length_net, uint8_t kflg, + uint32_t timestamp_hw, const char* payload, size_t payload_size) +{ + g.length_clip = g.length_clip ^ length_net; + g.flag_clip = g.flag_clip ^ kflg; + g.timestamp_clip = g.timestamp_clip ^ timestamp_hw; + + // Payload goes "as is". + for (size_t i = 0; i < payload_size; ++i) + { + g.payload_clip[i] = g.payload_clip[i] ^ payload[i]; + } + + // Fill the rest with zeros. When this packet is going to be + // recovered, the payload extraced from this process will have + // the maximum lenght, but it will be cut to the right length + // and these padding 0s taken out. + for (size_t i = payload_size; i < payloadSize(); ++i) + g.payload_clip[i] = g.payload_clip[i] ^ 0; +} + +bool FECFilterBuiltin::packControlPacket(SrtPacket& rpkt, int32_t seq) +{ + // If the FEC packet is not yet ready for extraction, do nothing and return false. + // Check if seq is the last sequence of the group. + + // Check VERTICAL group first, then HORIZONTAL. + // + // This is because when it happens that HORIZONTAL group is to be + // FEC-CTL reported, it also shifts the base to the next row, whereas + // this base sequence is used to determine the column index that is + // needed to reach the right column group and it must stay unupdated + // until the last packet in this row is checked for VERTICAL groups. + + // If it's ready for extraction, extract it, and write into the packet. + // + // NOTE: seq is the sequence number of the LAST PACKET SENT regularly. + // This is only about to be shifted forward by 1 to be placed on the + // data packet. The packet in `r_packet` doesn't have the sequence number + // installed yet + + // For BOTH vertical and horizontal snd groups: + // - Check if the "full group" condition is satisfied (all packets from the group are clipped) + // - If not, simply return false and do nothing + // - If so, store the current clip state into the referenced packet, give it the 'seq' sequence + + // After packing the FEC packet: + // - update the base sequence in the group for which it's packed + // - make sure that pointers are reset to not suggest the packet is ready + + // Handle the special case of m_number_rows == 1, which + // means we don't use columns. + if (m_number_rows <= 1) + { + HLOGC(mglog.Debug, log << "FEC/CTL not checking VERT group - rows only config"); + // PASS ON to Horizontal group check + } + else + { + int offset_to_row_base = CSeqNo::seqoff(snd.row.base, seq); + int vert_gx = (offset_to_row_base + m_number_cols) % m_number_cols; + + // This can actually happen only for the very first sent packet. + // It looks like "following the last packet from the previous group", + // however there was no previous group because this is the first packet. + if (offset_to_row_base < 0) + { + HLOGC(mglog.Debug, log << "FEC/CTL not checking VERT group [" << vert_gx << "] - negative offset_to_row_base %" + << snd.row.base << " -> %" << seq << " (" << offset_to_row_base + << ") (collected " << snd.cols[abs(vert_gx)].collected << "/" << sizeCol() << ")"); + // PASS ON to Horizontal group check + } + else + { + if (snd.cols[vert_gx].collected >= m_number_rows) + { + HLOGC(mglog.Debug, log << "FEC/CTL ready for VERT group [" << vert_gx << "]: %" << seq + << " (base %" << snd.cols[vert_gx].base << ")"); + // SHIP THE VERTICAL FEC packet. + PackControl(snd.cols[vert_gx], vert_gx, rpkt, seq); + + // RESET THE GROUP THAT WAS SENT + ResetGroup(snd.cols[vert_gx]); + return true; + } + + HLOGC(mglog.Debug, log << "FEC/CTL NOT ready for VERT group [" << vert_gx << "]: %" << seq + << " (base %" << snd.cols[vert_gx].base << ")" + << " - collected " << snd.cols[vert_gx].collected << "/" << m_number_rows); + } + } + + if (snd.row.collected >= m_number_cols) + { + if (!m_cols_only) + { + HLOGC(mglog.Debug, log << "FEC/CTL ready for HORIZ group: %" << seq << " (base %" << snd.row.base << ")"); + // SHIP THE HORIZONTAL FEC packet. + PackControl(snd.row, -1, rpkt, seq); + + HLOGC(mglog.Debug, log << "...PACKET size=" << rpkt.length + << " TS=" << rpkt.hdr[SRT_PH_TIMESTAMP] + << " !" << BufferStamp(rpkt.buffer, rpkt.length)); + + } + + // RESET THE HORIZONTAL GROUP. + // ALWAYS, even in columns-only. + ResetGroup(snd.row); + + if (!m_cols_only) + { + // In columns-only you didn't pack anything, so check + // for column control. + return true; + } + } + else + { + HLOGC(mglog.Debug, log << "FEC/CTL NOT ready for HORIZ group: %" << seq + << " (base %" << snd.row.base << ")" + << " - collected " << snd.row.collected << "/" << m_number_cols); + } + + return false; +} + +void FECFilterBuiltin::PackControl(const Group& g, signed char index, SrtPacket& pkt, int32_t seq) +{ + // Allocate as much space as needed, regardless of the PAYLOADSIZE value. + + static const size_t INDEX_SIZE = 1; + + size_t total_size = + INDEX_SIZE + + sizeof(g.flag_clip) + + sizeof(g.length_clip) + + g.payload_clip.size(); + + // Sanity +#if ENABLE_DEBUG + if (g.output_buffer.size() < total_size) + { + LOGC(mglog.Fatal, log << "OUTPUT BUFFER TOO SMALL!"); + abort(); + } +#endif + + char* out = pkt.buffer; + size_t off = 0; + // Spread the index. This is the index of the payload in the vertical group. + // For horizontal group this value is always -1. + out[off++] = index; + // Flags, currently only the encryption flags + out[off++] = g.flag_clip; + + // Ok, now the length clip + memcpy(out+off, &g.length_clip, sizeof g.length_clip); + off += sizeof g.length_clip; + + // And finally the payload clip + memcpy(out+off, &g.payload_clip[0], g.payload_clip.size()); + + // Ready. Now fill the header and finalize other data. + pkt.length = total_size; + + pkt.hdr[SRT_PH_TIMESTAMP] = g.timestamp_clip; + pkt.hdr[SRT_PH_SEQNO] = seq; + + HLOGC(mglog.Debug, log << "FEC: PackControl: hdr(" + << (total_size - g.payload_clip.size()) << "): INDEX=" + << int(index) << " LENGTH[ne]=" << hex << g.length_clip + << " FLAGS=" << int(g.flag_clip) << " TS=" << g.timestamp_clip + << " PL(" << dec << g.payload_clip.size() << ")[0-4]=" << hex + << (*(uint32_t*)&g.payload_clip[0])); + +} + +bool FECFilterBuiltin::receive(const CPacket& rpkt, loss_seqs_t& loss_seqs) +{ + // Add this packet to the group where it belongs. + // Light up the cell of this packet to mark it received. + // Check if any of the groups to which the packet belongs + // have changed the status into RECOVERABLE. + // + // The group has RECOVERABLE status when it has FEC + // packet received and the number of collected packets counts + // exactly group_size - 1. + + bool want_packet = false; + + struct IsFec + { + bool row; + bool col; + signed char colx; + } isfec = { false, false, -1 }; + + // The sequence number must be checked prematurely, or it can otherwise + // cause large resource allocation. This might be even survived, provided + // that this will make the packet seen as exceeding the series 0 matrix, + // so all matrices in previous series should be dismissed thereafter. But + // this short living resource spike may be destructive, so let's do + // matrix dismissal FIRST before this packet is going to be handled. + CheckLargeDrop(rpkt.getSeqNo()); + + if (rpkt.getMsgSeq() == 0) + { + // Interpret the first byte of the contents. + const char* payload = rpkt.data(); + isfec.colx = payload[0]; + if (isfec.colx == -1) + { + isfec.row = true; + } + else + { + isfec.col = true; + } + + HLOGC(mglog.Debug, log << "FEC: RECEIVED %" << rpkt.getSeqNo() << " msgno=0, FEC/CTL packet. INDEX=" << int(payload[0])); + } + else + { + // Data packet, check if this packet was already received. + // If so, ignore it. This may happen if you have configured + // FEC and ARQ to cooperate, so a packet once rebuilt might + // be simultaneously also retransmitted. This may confuse the tables. + int celloff = CSeqNo::seqoff(rcv.cell_base, rpkt.getSeqNo()); + bool past = celloff < 0; + bool exists = celloff < int(rcv.cells.size()) && !past && rcv.cells[celloff]; + + if (past || exists) + { + HLOGC(mglog.Debug, log << "FEC: packet %" << rpkt.getSeqNo() << " " + << (past ? "in the PAST" : "already known") << ", IGNORING."); + + return true; + } + + want_packet = true; + + HLOGC(mglog.Debug, log << "FEC: RECEIVED %" << rpkt.getSeqNo() << " msgno=" << rpkt.getMsgSeq() << " DATA PACKET."); + MarkCellReceived(rpkt.getSeqNo()); + } + + // Remember this simply every time a packet comes in. In live mode usually + // this flag is ORD_RELAXED (false), but some earlier versions used ORD_REQUIRED. + // Even though this flag is now usually ORD_RELAXED, it's fate in live mode + // isn't completely decided yet, so stay flexible. We believe at least that this + // flag will stay unchanged during whole connection. + rcv.order_required = rpkt.getMsgOrderFlag(); + + loss_seqs_t irrecover_row, irrecover_col; + + bool ok = true; + if (!isfec.col) // == regular packet or FEC/ROW + { + // Don't manage this packet for horizontal group, + // if it was a vertical FEC/CTL packet. + ok = HangHorizontal(rpkt, isfec.row, irrecover_row); + HLOGC(mglog.Debug, log << "FEC: HangHorizontal %" << rpkt.getSeqNo() + << " msgno=" << rpkt.getMsgSeq() + << " RESULT=" << boolalpha << ok << " IRRECOVERABLE: " << Printable(irrecover_row)); + } + + if (!ok) + { + // Just informative. + LOGC(mglog.Warn, log << "FEC/H: rebuilding/hanging FAILED."); + } + + // Don't do HangVertical in case of row-only configuration + if (!isfec.row && m_number_rows > 1) // == regular packet or FEC/COL + { + ok = HangVertical(rpkt, isfec.colx, irrecover_col); + HLOGC(mglog.Debug, log << "FEC: HangVertical %" << rpkt.getSeqNo() + << " msgno=" << rpkt.getMsgSeq() + << " RESULT=" << boolalpha << ok << " IRRECOVERABLE: " << Printable(irrecover_col)); + } + + if (!ok) + { + // Just informative. + LOGC(mglog.Warn, log << "FEC/V: rebuilding/hanging FAILED."); + } + + // Pack the following packets as irrecoverable: + if (m_fallback_level == SRT_ARQ_ONREQ) + { + // Use irrecover_row with rows only because there is + // never anything collected in irrecover_col. + if (m_number_rows == 1) + loss_seqs = irrecover_row; + else + loss_seqs = irrecover_col; + } + + return want_packet; + // Get the packet from the incoming stream, already recognized + // as data packet, and then: + // + // (Note that the default builtin FEC mechanism uses such rules: + // - allows SRT to get the packet, even if it follows the loss + // - depending on m_fallback_level, confirms or denies the need that SRT handle the loss + // - in loss_seqs we return those that are not recoverable at the current level + // - FEC has no extra header provided, so regular data are passed as is + //) + // So, the needs to implement: + // + // 1. If this is a FEC packet, close the group, check for lost packets, try to recover. + // Check if there is recovery possible, if so, request a new unit and pack the recovered packet there. + // Report the loss to be reported by SRT according to m_fallback_level: + // - ARQ_ALWAYS: N/A for a FEC packet + // - ARQ_EARLY: When Horizontal group is closed and the packet is not recoverable, report this in loss_seqs + // - ARQ_LATELY: When Horizontal and Vertical group is closed and the packet is not recoverable, report it. + // - ARQ_NEVER: Always return empty loss_seqs + // + // 2. If this is a regular packet, use it for building the FEC group. + // - ARQ_ALWAYS: always return true and leave loss_seqs empty. + // - others: return false and return nothing in loss_seqs +} + +void FECFilterBuiltin::CheckLargeDrop(int32_t seqno) +{ + // Ok, first try to pick up the column and series + + int offset = CSeqNo::seqoff(rcv.rowq[0].base, seqno); + if (offset < 0) + { + return; + } + + // For row-only configuration, check only parts referring + // to a row. + if (m_number_rows == 1) + { + // We have no columns. So just check if exceeds 5* the row size. + // If so, clear the rows and reconfigure them. + if (offset > int(5 * sizeRow())) + { + // Calculate the new row base, without breaking the current + // layout. Make a skip by some number of rows so that the new + // first row is prepared to receive this packet. + + int32_t oldbase = rcv.rowq[0].base; + size_t rowdist = offset / sizeRow(); + int32_t newbase = CSeqNo::incseq(oldbase, rowdist * sizeRow()); + + LOGC(mglog.Warn, log << "FEC: LARGE DROP detected! Resetting row groups. Base: %" << oldbase + << " -> %" << newbase << "(shift by " << CSeqNo::seqoff(oldbase, newbase) << ")"); + + rcv.rowq.clear(); + rcv.cells.clear(); + + rcv.rowq.resize(1); + HLOGP(mglog.Debug, "FEC: RE-INIT: receiver first row"); + ConfigureGroup(rcv.rowq[0], newbase, 1, sizeRow()); + } + + return; + } + + bool reset_anyway = false; + if (offset != CSeqNo::seqoff(rcv.colq[0].base, seqno)) + { + reset_anyway = true; + HLOGC(mglog.Debug, log << "FEC: IPE: row.base %" << rcv.rowq[0].base << " != %" << rcv.colq[0].base << " - resetting"); + } + + // Number of column - regardless of series. + int colx = offset % numberCols(); + + // Base sequence from the group series 0 in this column + + // [[assert rcv.colq.size() >= numberCols()]]; + int32_t colbase = rcv.colq[colx].base; + + // Offset between this base and seqno + int coloff = CSeqNo::seqoff(colbase, seqno); + + // Might be that it's in the row above the column, + // still it's not a large-drop + if (coloff < 0) + { + return; + } + + size_t matrix = numberRows() * numberCols(); + + int colseries = coloff / matrix; + + if (colseries > 2 || reset_anyway) + { + // Ok, now define the new ABSOLUTE BASE. This is the base of the column 0 + // column group from the series previous towards this one. + int32_t oldbase = rcv.colq[0].base; + int32_t newbase = CSeqNo::incseq(oldbase, (colseries-1) * matrix); + + LOGC(mglog.Warn, log << "FEC: LARGE DROP detected! Resetting all groups. Base: %" << oldbase + << " -> %" << newbase << "(shift by " << CSeqNo::seqoff(oldbase, newbase) << ")"); + + rcv.rowq.clear(); + rcv.colq.clear(); + rcv.cells.clear(); + + rcv.rowq.resize(1); + HLOGP(mglog.Debug, "FEC: RE-INIT: receiver first row"); + ConfigureGroup(rcv.rowq[0], newbase, 1, sizeRow()); + + // Size: cols + // Step: rows (the next packet in the group is one row later) + // Slip: rows+1 (the first packet in the next group is later by 1 column + one whole row down) + HLOGP(mglog.Debug, "FEC: RE-INIT: receiver first N columns"); + ConfigureColumns(rcv.colq, newbase); + + rcv.cell_base = newbase; + } +} + +void FECFilterBuiltin::CollectIrrecoverRow(RcvGroup& g, loss_seqs_t& irrecover) const +{ + if (g.dismissed) + return; // already collected + + // Obtain the group's packet shift + + int32_t base = rcv.cell_base; + int offset = CSeqNo::seqoff(base, g.base); + if (offset < 0) + { + LOGC(mglog.Error, log << "FEC: IPE: row base %" << g.base << " is PAST to cell base %" << base); + return; + } + + size_t maxoff = offset + m_number_cols; + // Sanity check, if all cells are really filled. + if (maxoff > rcv.cells.size()) + { + LOGC(mglog.Error, log << "FEC: IPE: Collecting loss from row %" + << g.base << "+" << m_number_cols << " while cells <= %" + << CSeqNo::seqoff(rcv.cell_base, rcv.cells.size()-1)); + return; + } + + bool last = true; + loss_seqs_t::value_type val; + for (size_t i = offset; i < maxoff; ++i) + { + bool gone = last; + last = rcv.cells[i]; + + if (gone && !last) + { + // Switch full -> loss. Store the sequence, as single (for now) + val.first = val.second = CSeqNo::incseq(base, i); + } + else if (last && !gone) + { + val.second = CSeqNo::incseq(base, i); + irrecover.push_back(val); + } + } + + // If it happened that 0 cells were until the end, we are + // sure that we have the val.first set to the first of the loss list + // and we've reached the end. Otherwise 'last' would be true. + if (!last) + { + val.second = CSeqNo::incseq(base, int(maxoff)-1); + irrecover.push_back(val); + } + + g.dismissed = true; +} + +static inline char CellMark(const std::deque& cells, int index) +{ + if (index >= int(cells.size())) + return '/'; + + return cells[index] ? '#' : '.'; +} + +#if ENABLE_HEAVY_LOGGING +static void DebugPrintCells(int32_t base, const std::deque& cells, int row_size) +{ + int i = 0; + // Shift to the first empty cell + for ( ; i < int(cells.size()); ++i) + if (cells[i] == false) + break; + + if (i == int(cells.size())) + { + LOGC(mglog.Debug, log << "FEC: ... cell[0-" << (cells.size()-1) << "]: ALL CELLS EXIST"); + return; + } + + // Ok, we have some empty cells, so just adjust to the start of a row. + i -= i % row_size; + if (i < 0) + i = 0; // you never know... + + for ( ; i < int(cells.size()); i += row_size ) + { + std::ostringstream os; + os << "cell[" << i << "-" << (i+row_size-1) << "] %" << CSeqNo::incseq(base, i) << ":"; + for (int y = 0; y < row_size; ++y) + { + os << " " << CellMark(cells, i+y); + } + LOGP(mglog.Debug, os.str()); + } +} +#else +static void DebugPrintCells(int32_t /*base*/, const std::deque& /*cells*/, int /*row_size*/) {} +#endif + +bool FECFilterBuiltin::HangHorizontal(const CPacket& rpkt, bool isfec, loss_seqs_t& irrecover) +{ + int32_t seq = rpkt.getSeqNo(); + + int rowx = RcvGetRowGroupIndex(seq); + if (rowx == -1) + return false; + + RcvGroup& rowg = rcv.rowq[rowx]; + // Clip the packet into the horizontal group. + + // If this was a regular packet, increase the number of collected. + // If this was a FEC/CTL packet, keep this number, just set the fec flag. + if (isfec) + { + if (!rowg.fec) + { + ClipControlPacket(rowg, rpkt); + rowg.fec = true; + HLOGC(mglog.Debug, log << "FEC/H: FEC/CTL packet clipped, %" << seq << " base=%" << rowg.base); + } + else + { + HLOGC(mglog.Debug, log << "FEC/H: FEC/CTL at %" << seq << " DUPLICATED, skipping."); + } + } + else + { + ClipPacket(rowg, rpkt); + rowg.collected++; + HLOGC(mglog.Debug, log << "FEC/H: DATA packet clipped, %" << seq + << ", received " << rowg.collected << "/" << sizeRow() + << " base=%" << rowg.base); + } + + if (rowg.fec && rowg.collected == m_number_cols - 1) + { + HLOGC(mglog.Debug, log << "FEC/H: HAVE " << rowg.collected << " collected & FEC; REBUILDING..."); + // The group will provide the information for rebuilding. + // The sequence of the lost packet can be checked in cells. + // With the condition of 'collected == m_number_cols - 1', there + // should be only one lacking packet, so just rely on first found. + RcvRebuild(rowg, RcvGetLossSeqHoriz(rowg), + m_number_rows == 1 ? Group::SINGLE : Group::HORIZ); + +#if ENABLE_HEAVY_LOGGING + std::ostringstream os; + for (size_t i = 0; i < rcv.rebuilt.size(); ++i) + { + os << " " << rcv.rebuilt[i].hdr[SRT_PH_SEQNO]; + } + + LOGC(mglog.Debug, log << "FEC: ... cached rebuilt packets (" << rcv.rebuilt.size() << "):" << os.str()); +#endif + } + + // When there are only rows, dismiss the oldest row when you have + // collected at least 1 packet in the next group. Do not dismiss + // any groups here otherwise - all will be decided during column + // processing. + + bool want_collect_irrecover = false; + bool want_remove_cells = false; + + if (rcv.rowq.size() > 1) + { + if (m_number_rows == 1) + { + want_remove_cells = true; + want_collect_irrecover = true; + } + else if (m_fallback_level == SRT_ARQ_ONREQ) + { + want_collect_irrecover = true; + } + } + + if (want_collect_irrecover) + { + int current = rcv.rowq.size() - 2; + // We know we have at least 2 rows. + // This value is then 0 or more. + int past = current - 1; + + // To trigger irrecoverable collection, the current sequence + // must be further than 1/3 of the row size to start from + // the previous row. Otherwise, start with the past-previous + // one, as long as it still exists. + + bool early SRT_ATR_UNUSED = false; + if (past > 0) + { + // If you already have at least 3 rows, sweep starting from + // the before-previous one (this will become 0 when the number + // of rows is exactly 3). + --past; + } + else + { + // If you have 2 rows, then in the current row (1) there must + // be the sequence passing already the 1/3 of the size. Otherwise + // decrease past to make it -1 and not pass the next test. + if (CSeqNo::seqoff(rcv.rowq[1].base, seq) <= int(m_number_cols/3)) + { + --past; + } + else + { + early = true; + } + } + + if (past >= 0) + { + // Collect irrecoverable since the 'past' index up to 0. + // If want_remove_cells, also remove these rows and corresponding cells. + + int nrowremove = 1 + past; + HLOGC(mglog.Debug, log << "Collecting irrecoverable packets from " << nrowremove << " ROWS per offset " + << CSeqNo::seqoff(rcv.rowq[1].base, seq) << " vs. " << m_number_cols << "/3"); + + for (int i = 0; i <= past; ++i) + { + CollectIrrecoverRow(rcv.rowq[i], irrecover); + } + + if (want_remove_cells) + { + size_t npktremove = sizeRow() * nrowremove; + size_t ersize = min(npktremove, rcv.cells.size()); + + HLOGC(mglog.Debug, log << "FEC/H: Dismissing rows n=" << nrowremove + << ", starting at %" << rcv.rowq[0].base + << " AND " << npktremove << " CELLS, base switch %" + << rcv.cell_base << " -> %" << rcv.rowq[past].base); + + rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + 1 + past); + rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + ersize); + + // We state that we have removed as many cells as for the removed + // rows. In case when the number of cells proved to be less than that, + // it will simply remove all cells. So now set the cell base to be + // in sync with the row base. + rcv.cell_base = rcv.rowq[0].base; + DebugPrintCells(rcv.cell_base, rcv.cells, sizeRow()); + } + } + else + { + HLOGC(mglog.Debug, log << "FEC: NOT collecting irrecover from rows: distance=" + << CSeqNo::seqoff(rcv.rowq[0].base, seq)); + } + + } + + return true; +} + +int32_t FECFilterBuiltin::RcvGetLossSeqHoriz(Group& g) +{ + int baseoff = CSeqNo::seqoff(rcv.cell_base, g.base); + if (baseoff < 0) + { + LOGC(mglog.Error, log << "FEC: IPE: negative cell offset, cell_base=%" << rcv.cell_base << " Group's base: %" << g.base << " - NOT ATTEMPTING TO REBUILD"); + return -1; + } + + // This is a row, so start from the first cell for this group + // and search lineraly for the first loss. + + int offset = -1; + + for (size_t cix = baseoff; cix < baseoff + m_number_cols; ++cix) + { + if (!rcv.CellAt(cix)) + { + offset = cix; +#if ENABLE_HEAVY_LOGGING + // For heavy logging case, show all cells in the range + LOGC(mglog.Debug, log << "FEC/H: cell %" << CSeqNo::incseq(rcv.cell_base, cix) + << " (+" << cix << "): MISSING"); + +#else + + // Find just one. No more that just one shall be found + // because it was checked earlier that we have collected + // all but just one packet. + break; +#endif + } +#if ENABLE_HEAVY_LOGGING + else + { + LOGC(mglog.Debug, log << "FEC/H: cell %" << CSeqNo::incseq(rcv.cell_base, cix) + << " (+" << cix << "): exists"); + } +#endif + } + + if (offset == -1) + { + LOGC(mglog.Fatal, log << "FEC/H: IPE: rebuilding attempt, but no lost packet found"); + return -1; // sanity, shouldn't happen + } + + // Now that we have an offset towards the first packet in the cells, + // translate it to the sequence number of the lost packet. + return CSeqNo::incseq(rcv.cell_base, offset); +} + +int32_t FECFilterBuiltin::RcvGetLossSeqVert(Group& g) +{ + int baseoff = CSeqNo::seqoff(rcv.cell_base, g.base); + if (baseoff < 0) + { + LOGC(mglog.Error, log << "FEC: IPE: negative cell offset, cell_base=%" << rcv.cell_base << " Group's base: %" << g.base << " - NOT ATTEMPTING TO REBUILD"); + return -1; + } + + // This is a row, so start from the first cell for this group + // and search lineraly for the first loss. + + int offset = -1; + + for (size_t col = 0; col < sizeCol(); ++col) + { + size_t cix = baseoff + (col * sizeRow()); + if (!rcv.CellAt(cix)) + { + offset = cix; +#if ENABLE_HEAVY_LOGGING + // For heavy logging case, show all cells in the range + LOGC(mglog.Debug, log << "FEC/V: cell %" << CSeqNo::incseq(rcv.cell_base, cix) + << " (+" << cix << "): MISSING"); + +#else + + // Find just one. No more that just one shall be found + // because it was checked earlier that we have collected + // all but just one packet. + break; +#endif + } +#if ENABLE_HEAVY_LOGGING + else + { + LOGC(mglog.Debug, log << "FEC/V: cell %" << CSeqNo::incseq(rcv.cell_base, cix) + << " (+" << cix << "): exists"); + } +#endif + } + + if (offset == -1) + { + LOGC(mglog.Fatal, log << "FEC/V: IPE: rebuilding attempt, but no lost packet found"); + return -1; // sanity, shouldn't happen + } + + // Now that we have an offset towards the first packet in the cells, + // translate it to the sequence number of the lost packet. + return CSeqNo::incseq(rcv.cell_base, offset); +} + +void FECFilterBuiltin::RcvRebuild(Group& g, int32_t seqno, Group::Type tp) +{ + if (seqno == -1) + return; + + uint16_t length_hw = ntohs(g.length_clip); + if (length_hw > payloadSize()) + { + LOGC(mglog.Error, log << "FEC: DECLIPPED length '" << length_hw << "' exceeds payload size. NOT REBUILDING."); + return; + } + + // Rebuild the packet + // (length_hw is automatically converted through PrivPacket constructor) + rcv.rebuilt.push_back( length_hw ); + + Receive::PrivPacket& p = rcv.rebuilt.back(); + + p.hdr[SRT_PH_SEQNO] = seqno; + + // This is for live mode only, for now, so the message + // number will be always 1, PB_SOLO, INORDER, and flags from clip. + // The REXMIT flag is set to 1 to fake that the packet was + // retransmitted. It is necessary because this packet will + // come out of sequence order, and if such a packet has + // no rexmit flag set, it's treated as reordered by network, + // which isn't true here. + p.hdr[SRT_PH_MSGNO] = 1 + | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO) + | MSGNO_PACKET_INORDER::wrap(rcv.order_required) + | MSGNO_ENCKEYSPEC::wrap(g.flag_clip) + | MSGNO_REXMIT::wrap(true) + ; + + p.hdr[SRT_PH_TIMESTAMP] = g.timestamp_clip; + p.hdr[SRT_PH_ID] = rcv.id; + + // Header ready, now we rebuild the contents + // First, rebuild the length. + + // Allocate the buffer and assign to a packet. + // This is only temporary, it will be copied to + // the target place when needed, with the buffer coming + // from the unit queue. + + // The payload clip may be longer than length_hw, but it + // contains only trailing zeros for completion, which are skipped. + copy(g.payload_clip.begin(), g.payload_clip.end(), p.buffer); + + HLOGC(mglog.Debug, log << "FEC: REBUILT: %" << seqno + << " msgno=" << MSGNO_SEQ::unwrap(p.hdr[SRT_PH_MSGNO]) + << " flags=" << PacketMessageFlagStr(p.hdr[SRT_PH_MSGNO]) + << " TS=" << p.hdr[SRT_PH_TIMESTAMP] << " ID=" << dec << p.hdr[SRT_PH_ID] + << " size=" << length_hw + << " !" << BufferStamp(p.buffer, p.length)); + + // If this is a single request (filled from row and m_number_cols == 1), + // do not attempt recursive rebuilding + if (tp == Group::SINGLE) + return; + + // Mark this packet received + MarkCellReceived(seqno); + + // This flips HORIZ/VERT + Group::Type crosstype = Group::Type(!tp); + + if (crosstype == Group::HORIZ) + { + // Find this packet in the horizontal group + int rowx = RcvGetRowGroupIndex(seqno); + if (rowx == -1) + return; // can't access any group to rebuild + RcvGroup& rowg = rcv.rowq[rowx]; + + // Sanity check. It's impossible that the packet was already + // rebuilt and any attempt to rebuild a lacking packet was made. + if (rowg.collected > m_number_cols - 1) + { + return; + } + + // Same as ClipPacket for the incoming packet, just this + // is extracting the data directly from the rebuilt one. + ClipRebuiltPacket(rowg, p); + rowg.collected++; + HLOGC(mglog.Debug, log << "FEC/H: REBUILT packet clipped, %" << seqno + << ", received " << rowg.collected << "/" << m_number_cols + << " FOR base=%" << rowg.base); + + // Similar as by HangHorizontal, just don't collect irrecoverable packets. + // They are already known when the packets were collected. + if (rowg.fec && rowg.collected == m_number_cols - 1) + { + HLOGC(mglog.Debug, log << "FEC/H: with FEC-rebuilt HAVE " << rowg.collected << " collected & FEC; REBUILDING"); + // The group will provide the information for rebuilding. + // The sequence of the lost packet can be checked in cells. + // With the condition of 'collected == m_number_cols - 1', there + // should be only one lacking packet, so just rely on first found. + + // NOTE: RECURSIVE CALL. + RcvRebuild(rowg, RcvGetLossSeqHoriz(rowg), crosstype); + } + } + else // crosstype == Group::VERT + { + // Find this packet in the vertical group + int colx = RcvGetColumnGroupIndex(seqno); + if (colx == -1) + return; // can't access any group to rebuild + RcvGroup& colg = rcv.colq[colx]; + + // Sanity check. It's impossible that the packet was already + // rebuilt and any attempt to rebuild a lacking packet was made. + if (colg.collected > m_number_rows - 1) + { + return; + } + + // Same as ClipPacket for the incoming packet, just this + // is extracting the data directly from the rebuilt one. + ClipRebuiltPacket(colg, p); + colg.collected++; + HLOGC(mglog.Debug, log << "FEC/V: REBUILT packet clipped, %" << seqno + << ", received " << colg.collected << "/" << m_number_rows + << " FOR base=%" << colg.base); + + // Similar as by HangVertical, just don't collect irrecoverable packets. + // They are already known when the packets were collected. + if (colg.fec && colg.collected == m_number_rows - 1) + { + HLOGC(mglog.Debug, log << "FEC/V: with FEC-rebuilt HAVE " << colg.collected << " collected & FEC; REBUILDING"); + // The group will provide the information for rebuilding. + // The sequence of the lost packet can be checked in cells. + // With the condition of 'collected == m_number_rows - 1', there + // should be only one lacking packet, so just rely on first found. + + // NOTE: RECURSIVE CALL. + RcvRebuild(colg, RcvGetLossSeqVert(colg), crosstype); + } + } + +} + +int FECFilterBuiltin::ExtendRows(int rowx) +{ + // Check if oversize. Oversize is when the + // index is > 2*m_number_cols. If so, shrink + // the container first. + +#if ENABLE_HEAVY_LOGGING + LOGC(mglog.Debug, log << "FEC: ROW STATS BEFORE: n=" << rcv.rowq.size()); + + for (size_t i = 0; i < rcv.rowq.size(); ++i) + LOGC(mglog.Debug, log << "... [" << i << "] " << rcv.rowq[i].DisplayStats()); +#endif + + if (rowx > int(m_number_cols*3)) + { + LOGC(mglog.Error, log << "FEC/H: OFFSET=" << rowx << " exceeds maximum row container size, SHRINKING rows and cells"); + + rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + m_number_cols); + rowx -= m_number_cols; + + // With rows, delete also an appropriate number of cells. + int nerase = min(int(rcv.cells.size()), CSeqNo::seqoff(rcv.cell_base, rcv.rowq[0].base)); + rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + nerase); + rcv.cell_base = rcv.rowq[0].base; + } + + // Create and configure next groups. + size_t old = rcv.rowq.size(); + + // First, add the number of groups. + rcv.rowq.resize(rowx + 1); + + // Starting from old size + for (size_t i = old; i < rcv.rowq.size(); ++i) + { + // Initialize the base for the row group + int32_t ibase = CSeqNo::incseq(rcv.rowq[0].base, i*m_number_cols); + ConfigureGroup(rcv.rowq[i], ibase, 1, m_number_cols); + } + +#if ENABLE_HEAVY_LOGGING + LOGC(mglog.Debug, log << "FEC: ROW STATS AFTER: n=" << rcv.rowq.size()); + + for (size_t i = 0; i < rcv.rowq.size(); ++i) + LOGC(mglog.Debug, log << "... [" << i << "] " << rcv.rowq[i].DisplayStats()); +#endif + + return rowx; +} + +int FECFilterBuiltin::RcvGetRowGroupIndex(int32_t seq) +{ + RcvGroup& head = rcv.rowq[0]; + int32_t base = head.base; + + int offset = CSeqNo::seqoff(base, seq); + + // Discard the packet, if older than base. + if (offset < 0) + { + HLOGC(mglog.Debug, log << "FEC/H: Packet %" << seq << " is in the past, ignoring"); + return -1; + } + + // Hang in the receiver group first. + size_t rowx = offset / m_number_cols; + + /* + Don't. + Leaving this code for future if needed, but this check should not be done. + The resource management for "crazy" sequence numbers is done in the beginning, + so simply TRUST THIS SEQUENCE, no matter what. After the check it won't do any harm. + if (rowx > numberRows()*2) // past twice the matrix + { + LOGC(mglog.Error, log << "FEC/H: Packet %" << seq << " is in the far future, ignoring"); + return -1; + } + */ + + // The packet might have come completely out of the blue. + // The row group container must be prepared to extend + // itself in order to give place for the packet. + + // First, possibly extend the row container + if (rowx >= rcv.rowq.size()) + { + rowx = ExtendRows(rowx); + } + + return rowx; +} + +void FECFilterBuiltin::MarkCellReceived(int32_t seq) +{ + // Mark the packet as received. This will allow later to + // determine, which exactly packet is lost and needs rebuilding. + int cellsize = rcv.cells.size(); + int cell_offset = CSeqNo::seqoff(rcv.cell_base, seq); + bool resized SRT_ATR_UNUSED = false; + if (cell_offset >= cellsize) + { + // Expand the cell container with zeros, excluding the 'cell_offset'. + // Resize normally up to the required size, just set the lastmost + // item to true. + resized = true; + rcv.cells.resize(cell_offset+1, false); + } + rcv.cells[cell_offset] = true; + + HLOGC(mglog.Debug, log << "FEC: MARK CELL RECEIVED: %" << seq << " - cells base=%" + << rcv.cell_base << "[" << cell_offset << "]+" << rcv.cells.size() + << (resized ? "(resized)":"") << " :"); + + DebugPrintCells(rcv.cell_base, rcv.cells, sizeRow()); +} + +bool FECFilterBuiltin::IsLost(int32_t seq) const +{ + int offset = CSeqNo::seqoff(rcv.cell_base, seq); + if (offset < 0) + { + LOGC(mglog.Error, log << "FEC: IsLost: IPE: %" << seq + << " is earlier than the cell base %" << rcv.cell_base); + return true; // fake we have the packet - this is to collect losses only + } + if (offset >= int(rcv.cells.size())) + { + // XXX IPE! + LOGC(mglog.Error, log << "FEC: IsLost: IPE: %" << seq << " is past the cells %" + << rcv.cell_base << " + " << rcv.cells.size()); + return true; + } + + return rcv.cells[offset]; +} + +bool FECFilterBuiltin::HangVertical(const CPacket& rpkt, signed char fec_col, loss_seqs_t& irrecover) +{ + bool fec_ctl = (fec_col != -1); + // Now hang the packet in the vertical group + + int32_t seq = rpkt.getSeqNo(); + + // Ok, now we have the column index, we know it exists. + // Apply the packet. + + int colgx = RcvGetColumnGroupIndex(seq); + if (colgx == -1) + return false; + + RcvGroup& colg = rcv.colq[colgx]; + + if (fec_ctl) + { + if (!colg.fec) + { + ClipControlPacket(colg, rpkt); + colg.fec = true; + HLOGC(mglog.Debug, log << "FEC/V: FEC/CTL packet clipped, %" << seq << " FOR COLUMN " << int(fec_col) + << " base=%" << colg.base); + } + else + { + HLOGC(mglog.Debug, log << "FEC/V: FEC/CTL at %" << seq << " COLUMN " << int(fec_col) << " DUPLICATED, skipping."); + } + } + else + { + // Data packet, clip it as data + ClipPacket(colg, rpkt); + colg.collected++; + HLOGC(mglog.Debug, log << "FEC/V: DATA packet clipped, %" << seq + << ", received " << colg.collected << "/" << sizeCol() + << " base=%" << colg.base); + } + + if (colg.fec && colg.collected == m_number_rows - 1) + { + HLOGC(mglog.Debug, log << "FEC/V: HAVE " << colg.collected << " collected & FEC; REBUILDING"); + RcvRebuild(colg, RcvGetLossSeqVert(colg), Group::VERT); + } + + // Column dismissal takes place under very strictly specified condition, + // so simply call it in general here. At least it may happen potentially + // at any time of when a packet has been received. + RcvCheckDismissColumn(rpkt.getSeqNo(), colgx, irrecover); + +#if ENABLE_HEAVY_LOGGING + LOGC(mglog.Debug, log << "FEC: COL STATS ATM: n=" << rcv.colq.size()); + + for (size_t i = 0; i < rcv.colq.size(); ++i) + LOGC(mglog.Debug, log << "... [" << i << "] " << rcv.colq[i].DisplayStats()); +#endif + + return true; +} + +void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t& irrecover) +{ + // The first check we need to do is: + // + // - get the column number + // - get the series for this column + // - if series is 0, just return + + int series = colgx / numberCols(); + if (series == 0) + return; + + // - STARTING from the same column series 0: + // - unless DISMISSED, collect all irrecoverable packets from this group + // - mark this column DISMISSED + // - SAME CHECK for previous group, until index 0 + + set loss; + + int colx SRT_ATR_UNUSED = colgx % numberCols(); + + HLOGC(mglog.Debug, log << "FEC/V: going to DISMISS cols past %" << seq + << " at INDEX=" << colgx << " col=" << colx + << " series=" << series << " - looking up candidates..."); + + // Walk through all column groups in series 0. Collect irrecov's + // from every group, for which the incoming 'seq' is in future. + for (size_t i = 0; i < numberCols(); ++i) + { + RcvGroup& pg = rcv.colq[i]; + if (pg.dismissed) + { + HLOGC(mglog.Debug, log << "FEC/V: ... [" << i << "] base=%" + << pg.base << " ALREADY DISMISSED, skipping."); + continue; + } + + // With multi-staircase it may happen that THIS column contains + // sequences that are all in the past, but the PREVIOUS column + // has some in the future, because THIS column is the top of + // the second staircase, and PREVIOUS is the bottom stair of + // the first staircase. When this is confirmed, simply skip + // the columns that have the highest sequence in the future + // because they can't be dismissed yet. Jump them over, so maybe + // they can be dismissed in future. + int this_col_offset = CSeqNo::seqoff(pg.base, seq); + int last_seq_offset = this_col_offset - (sizeCol()-1)*sizeRow(); + + if (last_seq_offset < 0) + { + HLOGC(mglog.Debug, log << "FEC/V: ... [" << i << "] base=%" + << pg.base << " TOO EARLY (last=%" + << CSeqNo::incseq(pg.base, (sizeCol()-1)*sizeRow()) + << ")"); + continue; + } + + // NOTE: If it was standing on the second staircase top, there's + // still a chance that it hits the staircase top of the first + // staircase and will dismiss it as well. + + HLOGC(mglog.Debug, log << "FEC/V: ... [" << i << "] base=%" + << pg.base << " - PAST last=%" + << CSeqNo::incseq(pg.base, (sizeCol()-1)*sizeRow()) + << " - collecting losses."); + + pg.dismissed = true; // mark irrecover already collected + for (size_t sof = 0; sof < pg.step * sizeCol(); sof += pg.step) + { + int32_t lseq = CSeqNo::incseq(pg.base, sof); + if (!IsLost(lseq)) + { + loss.insert(lseq); + HLOGC(mglog.Debug, log << "FEC: ... cell +" << sof << " %" << lseq + << " lost"); + } + else + { + HLOGC(mglog.Debug, log << "FEC: ... cell +" << sof << " %" << lseq + << " EXISTS"); + } + } + } + + // COLUMN DISMISAL: + + // 1. We can only dismiss ONE SERIES OF COLUMNS - OR NOTHING. + // 2. The triggering 'seq' must be past ANY sequence embraced + // by any group in the first series of columns. + + // Useful information: + // + // 1. It's not known from upside, which column contains a sequence + // number that reaches FURTHEST. The safe statement is then: + // - For even arrangement, it must be past BASE0 + matrix size + // - For staircase arrangement - BASE0 + matrix size * 2. + + int32_t base0 = rcv.colq[0].base; + int this_off = CSeqNo::seqoff(base0, seq); + + int mindist = + m_arrangement_staircase ? + (numberCols() * numberRows() * 2) + : + (numberCols() * numberRows()); + + bool any_dismiss SRT_ATR_UNUSED = false; + + // if (base0 +% mindist) <% seq + if (this_off < mindist) + { + HLOGC(mglog.Debug, log << "FEC/V: NOT dismissing any columns at %" << seq + << ", need to pass %" << CSeqNo::incseq(base0, mindist)); + } + else if (rcv.colq.size() < numberCols()) + { + HLOGC(mglog.Debug, log << "FEC/V: IPE: about to dismiss past %" << seq + << " with required %" << CSeqNo::incseq(base0, mindist) + << " but col container size still " << rcv.colq.size()); + } + else if (rcv.rowq.size() < numberRows()) + { + HLOGC(mglog.Debug, log << "FEC/V: IPE: about to dismiss past %" << seq + << " with required %" << CSeqNo::incseq(base0, mindist) + << " but row container size still " << rcv.rowq.size()); + } + else + { + // The condition for dismissal is now. The number of dismissed columns + // is numberCols(), regardless of the required 'mindinst'. + any_dismiss = true; + + int32_t newbase = rcv.colq[numberCols()].base; + int32_t newbase_row = rcv.rowq[numberRows()].base; + int matrix_size = numberCols() * numberRows(); + + HLOGC(mglog.Debug, log << "FEC/V: DISMISSING " << numberCols() << " COLS. Base %" + << rcv.colq[0].base << " -> %" << newbase + << " AND " << numberRows() << " ROWS Base %" + << rcv.rowq[0].base << " -> %" << newbase_row + << " AND " << matrix_size << " cells"); + + rcv.colq.erase(rcv.colq.begin(), rcv.colq.begin() + numberCols()); + +#if ENABLE_HEAVY_LOGGING + LOGC(mglog.Debug, log << "FEC: COL STATS BEFORE: n=" << rcv.colq.size()); + + for (size_t i = 0; i < rcv.colq.size(); ++i) + LOGC(mglog.Debug, log << "... [" << i << "] " << rcv.colq[i].DisplayStats()); +#endif + + // Now erase accordingly one matrix of rows. + // Sanity check + if (newbase_row != newbase) + { + LOGC(mglog.Fatal, log << "FEC/V: IPE: DISCREPANCY in base0 col=%" + << newbase << " row=%" << newbase_row << " - DELETING ALL ROWS"); + + // Delete all rows and reinitialize them. + rcv.rowq.clear(); + rcv.rowq.resize(1); + ConfigureGroup(rcv.rowq[0], newbase, 1, sizeRow()); + } + else + { + // Remove "legally" a matrix of rows. + rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + numberRows()); + } + + // And now accordingly remove cells. Exactly one matrix of cells. + // Sanity check first. + int32_t newbase_cell = CSeqNo::incseq(rcv.cell_base, matrix_size); + if (newbase != newbase_cell) + { + LOGC(mglog.Fatal, log << "FEC/V: IPE: DISCREPANCY in base0 col=%" + << newbase << " row=%" << newbase_row << " - DELETING ALL ROWS"); + + // Try to shift it gently first. Find the cell that matches the base. + int shift = CSeqNo::seqoff(rcv.cell_base, newbase); + if (shift < 0) + rcv.cells.clear(); + else + rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + shift); + } + else + { + rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + matrix_size); + } + rcv.cell_base = newbase; + DebugPrintCells(rcv.cell_base, rcv.cells, sizeRow()); + } + + /* + OLD UNUSED CODE, leaving for historical reasons + + // - check the last sequence of last column in series 0 + // - if passed sequence number is earlier than this, just return + // - now that seq is newer than the last in the last column, + // - dismiss whole series 0 column groups + + // First, index of the last column + size_t lastx = numberCols()-1; + if (lastx < rcv.colq.size()) + { + int32_t lastbase = rcv.colq[lastx].base; + + // Compare the base sequence with the sequence that caused the update + int dist = CSeqNo::seqoff(lastbase, seq); + + // Shift this distance by the distance between the first and last + // sequence managed by a singled column. This counts (sizeCol()-1)*step. + dist -= (sizeCol()-1) * rcv.colq[lastx].step; + + // Now, if this value is in the past (negative), it means that the + // 'seq' number is covered by this group or any earlier group. If so, + // do nothing. If this value is positive, it means that this + // sequence is in future towards the group that is in the last + // column of series 0. If so, whole series 0 may be now dismissed. + + // NOTE: we don't care if lost packets have been collected for + // the groups being dismissed. They *SHOULD* be, just as a fallback + // SRT - if needed - will simply send LOSSREPORT request for all + // packets that are lossreported and all older ones. + + if (dist > 0 && rcv.colq.size() > numberCols() ) + { + any_dismiss = true; + int32_t newbase = rcv.colq[numberCols()].base; + rcv.colq.erase(rcv.colq.begin(), rcv.colq.begin() + numberCols()); + + // colgx is INVALIDATED after removal + int newcolgx SRT_ATR_UNUSED = colgx - numberCols(); + + // After a column series was dismissed, now dismiss also + // the same number of rows. + // Do some sanity checks first. + + size_t nrowrem = 0; + int32_t oldrowbase = rcv.rowq[0].base; // before it gets deleted + if (rcv.rowq.size() > numberRows()) + { + int32_t newrowbase = rcv.rowq[numberRows()].base; + if (newbase != newrowbase) + { + LOGC(mglog.Error, log << "FEC: IPE: ROW/COL base DISCREPANCY: Looking up lineraly for the right row."); + + // Fallback implementation in order not to break everything + for (size_t r = 0; r < rcv.rowq.size(); ++r) + { + if (CSeqNo::seqoff(newbase, rcv.rowq[r].base) >= 0) + { + rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + r); + nrowrem = r; + break; + } + } + } + else + { + rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + numberRows()); + nrowrem = numberRows(); + } + } + + // If rows were removed, so remove also cells + if (nrowrem > 0) + { + int32_t newbase = rcv.rowq[0].base; + + // This value SHOULD be == nrowrem * sizeRow(), but this + // calculation is safe against bugs. Report them, if found, though. + int nrem = CSeqNo::seqoff(rcv.cell_base, newbase); + + if (oldrowbase != rcv.cell_base) + { + LOGC(mglog.Error, log << "FEC: CELL/ROW base discrepancy, calculating and resynchronizing"); + } + else + { + HLOGC(mglog.Debug, log << "FEC: will remove " << nrem << " cells, SHOULD BE = " + << (nrowrem * sizeRow())); + } + + if (nrem > 0) + { + // Now collect losses from all rows about to be dismissed. + for (int sof = 0; sof < nrem; sof++) + { + int32_t lseq = CSeqNo::incseq(rcv.cell_base, sof); + if (!IsLost(lseq)) + loss.insert(lseq); + } + + HLOGC(mglog.Debug, log << "FEC: ERASING unused cells (" << nrem << "): %" + << rcv.cell_base << " - %" << newbase + << ", losses collected: " << Printable(loss)); + + rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + nrem); + rcv.cell_base = newbase; + + DebugPrintCells(rcv.cell_base, rcv.cells, sizeRow()); + } + else + { + HLOGC(mglog.Debug, log << "FEC: NOT ERASING cells, base %" << rcv.cell_base + << " vs row base %" << rcv.rowq[0].base); + } + } + + HLOGC(mglog.Debug, log << "FEC/V: updated g=" << colgx << " -> " << newcolgx << " %" + << rcv.colq[newcolgx].base << ", DISMISS up to g=" << numberCols() + << " base=%" << lastbase + << " ROW=%" << rcv.rowq[0].base << "+" << nrowrem); + +} +} + +// */ + +// Now all collected lost packets translate into the range list format +TranslateLossRecords(loss, irrecover); + +HLOGC(mglog.Debug, log << "FEC: ... COLLECTED IRRECOVER: " << Printable(loss) << (any_dismiss ? " CELLS DISMISSED" : " nothing dismissed")); +} + +void FECFilterBuiltin::TranslateLossRecords(const set& loss, loss_seqs_t& irrecover) +{ + if (loss.empty()) + return; + + // size() >= 1 granted + set::iterator i = loss.begin(); + + int32_t fi_start = *i; + int32_t fi_end = fi_start; + ++i; + for (; i != loss.end(); ++i) + { + int dist = CSeqNo::seqoff(fi_end, *i); + if (dist == 1) + ++fi_end; + else + { + // Jumped over some sequences, cut the range. + irrecover.push_back(make_pair(fi_start, fi_end)); + fi_start = fi_end = *i; + } + } + + // And ship the last one + irrecover.push_back(make_pair(fi_start, fi_end)); +} + +int FECFilterBuiltin::RcvGetColumnGroupIndex(int32_t seqno) +{ + // The column is only the column, not yet + // exactly the index of the column group in the container. + // It's like: + + // 0 1 2 3 4 + // [A] ' ' ' ' + // [A] [A] ' ' ' + // [A] [A] [A] ' ' + // [A] [A] [A] [A] '++ + // [A]+[A] [A] [A] [A]+ + // [B] [A]+[A] [A] [A]+ + // [B] [B] [A]+[A] [A]+ + // [B] [B] [B] [A]+[A]+ + // [B] [B] [B] [B] [A]++ + // [B]+[B] [B] [B] [B] + // [B] [B] [B] [B] + // [B] [B] [B] + // [B] [B] + // [B] + // + // The same groups laid out in the container: + // + // [A]0 [A]1 [A]2 [A]3 [A]4 [B]0 [B]1 [B]2 [B]3 [B]4 + + // This means, vert_gx returns the number for the + // logical column, but we only know this column, + // not the exact group that is assigned to this + // packet. That is, in the above picture, if the + // vert_gx resulted in 3, we still don't know if + // this is A3 or B3. + // + // To know it, we need to first take the base + // sequence number for the very first column group + // in the container. Every next group in this + // container is shifted by the 'slip' factor, + // which in this case is m_number_cols + 1. The base + // sequence shifted by m_number_cols*m_number_rows + // becomes the sequence number of the next + // group in the same column. + // + // If a single column is dismissed, then the + // column 1 will become column 0, but the column + // 1 in the next series will also become column 0. + // All due to that the very base sequence for + // all groups will be the one in the first series + // column 1, now 0. + // + // Therefore, once we have the column, let's extract + // the column base sequence. + // As we can't count on that packets will surely come to close a group + // or just a particular sequence number will be received, we simply have + // to check all past groups at the moment when this packet is received: + // + // 1. Take the sequence number and determine both the GROUP INDEX and the + // COLUMN INDEX of this group. + // + // Important thing here is that the column group base is the base sequence + // number of the very first group, which NEED NOT GO HAND IN HAND with the + // row group sequence base. The rules are then: + // + // OFFSET = DISTANCE( colq[0].base TO seq ) + // + // COLUMN_INDEX = OFFSET % m_number_cols + // + // COLUMN_BASE = colq[COLUMN_INDEX].base + // + // COLUMN_OFFSET = DISTANCE(COLUMN_BASE TO seq) + // + // COLUMN_SERIES = COLUMN_OFFSET / (m_number_cols * m_number_rows) + // + // GROUP_INDEX = COLUMN_INDEX + (COLUMN_SERIES * m_number_cols) + + int offset = CSeqNo::seqoff(rcv.colq[0].base, seqno); + if (offset < 0) + { + HLOGC(mglog.Debug, log << "FEC/V: %" << seqno << " in the past of col ABSOLUTE base %" << rcv.colq[0].base); + return -1; + } + + if (offset > CSeqNo::m_iSeqNoTH/2) + { + LOGC(mglog.Error, log << "FEC/V: IPE/ATTACK: pkt %" << seqno << " has CRAZY OFFSET towards the base %" << rcv.colq[0].base); + return -1; + } + + int colx = offset % m_number_cols; + int32_t colbase = rcv.colq[colx].base; + int coloff = CSeqNo::seqoff(colbase, seqno); + if (coloff < 0) + { + HLOGC(mglog.Debug, log << "FEC/V: %" << seqno << " in the past of col #" << colx << " base %" << colbase); + // This means that this sequence number predates the earliest + // sequence number supported by the very first column. + return -1; + } + + int colseries = coloff / (m_number_cols * m_number_rows); + size_t colgx = colx + (colseries * m_number_cols); + + HLOGC(mglog.Debug, log << "FEC/V: Lookup group for %" << seqno << ": cg_base=%" << rcv.colq[0].base + << " column=" << colx << " with base %" << colbase << ": SERIES=" << colseries + << " INDEX:" << colgx); + + // Check oversize. Dismiss some earlier items if it exceeds the size. + // before you extend the size enormously. + if (colgx > m_number_rows * m_number_cols * 2) + { + // That's too much + LOGC(mglog.Error, log << "FEC/V: IPE or ATTACK: offset " << colgx << " is too crazy, ABORTING lookup"); + return -1; + } + + if (colgx >= rcv.colq.size()) + { + colgx = ExtendColumns(colgx); + } + + return colgx; + + // + // Even though column groups are arranged in a "staircase", it only means + // that DISTANCE(colq[0].base TO colq[1].base) == 1 + m_number_cols, not 1. + // But still, this DISTANCE(...) % m_number_cols == 1. So: + // + // COLUMN_INDEX = GROUP_INDEX % m_number_cols + // + // What is special here is that, group base sequence numbers with m_number_cols == 5 + // is, for example, these column groups in order of how they are arranged in + // the container have their [INDEX] BASE (stating the very first has base == 10): + // + // [0] 10, [1] 16, [2] 22, [3] 28, [4] 34, [5] 40, [6] 46 + // + // Therefore, if we get the sequence number 51, then: + // + // OFFSET = 51 - 10 = 41 + // + // COLUMN_INDEX = 41 % 5[m_number_cols] == 1 + // + // COLUMN_BASE = colq[1].base == 16 + // + // COLUMN_OFFSET = DISTANCE(colq[1].base TO 51) = 51 - 16 = 35 + // + // COLUMN_SERIES = COLUMN_OFFSET / (m_number_cols * m_number_rows) + // = 35 / (5*5) = 35/25 = 1 + // + // GROUP_INDEX = COLUMN_INDEX + (COLUMN_SERIES * m_number_cols) + // = 1 + 1*5 = 6 + // + // --> We have identified column group with index 6, this one + // that is designated above as [6] 46. This column group collects + // the following sequence numbers: + // - 46 + // - 51 (the one we were searching) + // - 56 + // - 61 + // - 66 + // + // We have then column group with index 6. Now we go backward in the column + // group container starting from the direct previous column up to either + // the start of container or the size == m_number_cols (5), to see if the + // group "is closed". So: + // + // END = min(m_number_cols, colq.size()) <--- should be 5 or less, if there's less in the container + // + // FOR EACH i = 1 TO END: + // g = colq[GROUP_INDEX - i] + // gs = SHIFT(seq, -i) + // gmax = SHIFT(g.base, m_number_cols * m_number_rows) + // IF ( gs %> gmax ) + // DISMISS COLUMNS from 0 to GROUP_INDEX - i; break + +} + +int FECFilterBuiltin::ExtendColumns(int colgx) +{ + if (colgx > int(sizeRow() * 2)) + { + // This shouldn't happen because columns should be dismissed + // once the last row of the first series is closed. + LOGC(mglog.Error, log << "FEC/V: OFFSET=" << colgx << " exceeds maximum col container size, SHRINKING container by " << sizeRow()); + + // Delete one series of columns. + int32_t oldbase SRT_ATR_UNUSED = rcv.colq[0].base; + rcv.colq.erase(rcv.colq.begin(), rcv.colq.begin() + numberCols()); + colgx -= numberCols(); + int32_t newbase = rcv.colq[0].base; + + // Delete also appropriate number of rows for one series + rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + numberRows()); + + // Sanity-check if the resulting row absolute base is equal to column + if (rcv.rowq[0].base != newbase) + { + LOGC(mglog.Error, log << "FEC/V: IPE: removal of " << numberRows() + << " rows ships no same seq: rowbase=%" + << rcv.rowq[0].base + << " colbase=%" << oldbase << " -> %" << newbase << " - RESETTING ROWS"); + + // How much you need, depends on the columns. + size_t nseries = rcv.colq.size() / numberCols() + 1; + size_t needrows = nseries * numberRows(); + + rcv.rowq.clear(); + rcv.rowq.resize(needrows); + int32_t rowbase = newbase; + for (size_t i = 0; i < rcv.rowq.size(); ++i) + { + ConfigureGroup(rcv.rowq[i], rowbase, 1, sizeRow()); + rowbase = CSeqNo::incseq(newbase, sizeRow()); + } + } + + size_t ncellrem = CSeqNo::seqoff(rcv.cell_base, newbase); + rcv.cells.erase(rcv.cells.begin(), rcv.cells.begin() + ncellrem); + rcv.cell_base = newbase; + + // Note that after this shift, column groups that were + // in particular column, remain in that column. + } + +#if ENABLE_HEAVY_LOGGING + LOGC(mglog.Debug, log << "FEC: COL STATS BEFORE: n=" << rcv.colq.size()); + + for (size_t i = 0; i < rcv.colq.size(); ++i) + LOGC(mglog.Debug, log << "... [" << i << "] " << rcv.colq[i].DisplayStats()); +#endif + + // First, obtain the "series" of columns, possibly fixed. + int series = colgx / numberCols(); + + // Now, the base of the series is the base increased by one matrix size. + + int32_t base = rcv.colq[0].base; + + // This is the base for series 0, but this procedure must be prepared + // for that the series will not necessarily be 1, may be greater. + // Extension requires to be done in order to achieve this very index + // existing in the column, so you need to add whole series in loop + // until the series covering this shift is created. + + // Check, up to which series the columns are initialized. + // Start with the series that doesn't exist + int old_series = rcv.colq.size() / numberCols(); + + // Each iteration of this loop adds one series of columns. + // One series count numberCols() columns. + for (int s = old_series; s <= series; ++s) + { + // We start with the base in series 0, the calculation of the + // sequence number must happen anew for each one anyway, so it + // doesn't matter from which start point. + + // Every base sequence for a series of columns is the series 0 + // base increased by one matrix size times series number. + // THIS REMAINS TRUE NO MATTER IF WE USE STRAIGNT OR STAIRCASE ARRANGEMENT. + int32_t sbase = CSeqNo::incseq(base, (numberCols()*numberRows()) * s); + HLOGC(mglog.Debug, log << "FEC/V: EXTENDING column groups series " << s + << ", size " << rcv.colq.size() << " -> " + << (rcv.colq.size() + numberCols()) + << ", base=%" << base << " -> %" << sbase); + + // Every call to this function extends the given container + // by 'gsize' number and configures each so added column accordingly. + ConfigureColumns(rcv.colq, sbase); + } + +#if ENABLE_HEAVY_LOGGING + LOGC(mglog.Debug, log << "FEC: COL STATS BEFORE: n=" << rcv.colq.size()); + + for (size_t i = 0; i < rcv.colq.size(); ++i) + LOGC(mglog.Debug, log << "... [" << i << "] " << rcv.colq[i].DisplayStats()); +#endif + + return colgx; +} + diff --git a/trunk/3rdparty/srt-1-fit/srtcore/fec.h b/trunk/3rdparty/srt-1-fit/srtcore/fec.h new file mode 100644 index 000000000..5912a1967 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/fec.h @@ -0,0 +1,248 @@ +/* + * 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/. + * + */ + + +#ifndef INC__SRT_FEC_H +#define INC__SRT_FEC_H + +#include +#include +#include +#include + +#include "packetfilter_api.h" + +class FECFilterBuiltin: public SrtPacketFilterBase +{ + SrtFilterConfig cfg; + size_t m_number_cols; + size_t m_number_rows; + + // Configuration + SRT_ARQLevel m_fallback_level; + bool m_cols_only; + bool m_arrangement_staircase; + +public: + + size_t numberCols() const { return m_number_cols; } + size_t numberRows() const { return m_number_rows; } + + size_t sizeCol() const { return m_number_rows; } + size_t sizeRow() const { return m_number_cols; } + + struct Group + { + int32_t base; //< Sequence of the first packet in the group + size_t step; //< by how many packets the sequence should increase to get the next packet + size_t drop; //< by how much the sequence should increase to get to the next series + size_t collected; //< how many packets were taken to collect the clip + + Group(): base(CSeqNo::m_iMaxSeqNo), step(0), drop(0), collected(0) + { + } + + uint16_t length_clip; + uint8_t flag_clip; + uint32_t timestamp_clip; + std::vector payload_clip; + + // This is mutable because it's an intermediate buffer for + // the purpose of output. + //mutable vector output_buffer; + + enum Type + { + HORIZ, // Horizontal, recursive + VERT, // Vertical, recursive + + // NOTE: HORIZ/VERT are defined as 0/1 so that not-inversion + // can flip between them. + SINGLE // Horizontal-only with no recursion + }; + + }; + + struct RcvGroup: Group + { + bool fec; + bool dismissed; + RcvGroup(): fec(false), dismissed(false) {} + +#if ENABLE_HEAVY_LOGGING + std::string DisplayStats() + { + if (base == CSeqNo::m_iMaxSeqNo) + return "UNINITIALIZED!!!"; + + std::ostringstream os; + os << "base=" << base << " step=" << step << " drop=" << drop << " collected=" << collected + << " " << (fec ? "+" : "-") << "FEC " << (dismissed ? "DISMISSED" : "active"); + return os.str(); + } +#endif + }; + +private: + + // Row Groups: every item represents a single row group and collects clips for one row. + // Col Groups: every item represents a signel column group and collect clips for packets represented in one column + + struct Send + { + // We need only ONE horizontal group. Simply after the group + // is closed (last packet supplied), and the FEC packet extracted, + // the group is no longer in use. + Group row; + std::vector cols; + } snd; + + struct Receive + { + SRTSOCKET id; + bool order_required; + + Receive(std::vector& provided): id(SRT_INVALID_SOCK), order_required(false), rebuilt(provided) + { + } + + // In reception we need to keep as many horizontal groups as required + // for possible later tracking. A horizontal group should be dismissed + // when the size of this container exceeds the `m_number_rows` (size of the column). + // + // The 'std::deque' type is used here for a trial implementation. A desired solution + // would be a kind of a ring buffer where new groups are added and old (exceeding + // the size) automatically dismissed. + std::deque rowq; + + // Base index at the oldest column platform determines + // the base index of the queue. Meaning, first you need + // to determnine the column index, where the index 0 is + // the fistmost element of this queue. After determining + // the column index, there must be also a second factor + // deteremined - which column series it is. So, this can + // start by extracting the base sequence of the element + // at the index column. This is the series 0. Now, the + // distance between these two sequences, divided by + // rowsize*colsize should return %index-in-column, + // /number-series. The latter multiplied by the row size + // is the offset between the firstmost column and the + // searched column. + std::deque colq; + + // This keeps the value of "packet received or not". + // The sequence number of the first cell is rowq[0].base. + // When dropping a row, + // - the firstmost element of rowq is removed + // - the length of one row is removed from this std::vector + int32_t cell_base; + std::deque cells; + + // Note this function will automatically extend the container + // with empty cells if the index exceeds the size, HOWEVER + // the caller must make sure that this index isn't any "crazy", + // that is, it fits somehow in reasonable ranges. + bool CellAt(size_t index) + { + if (index >= cells.size()) + { + // Cells not prepared for this sequence yet, + // so extend in advance. + cells.resize(index+1, false); + return false; // It wasn't marked, anyway. + } + + return cells[index]; + } + + typedef SrtPacket PrivPacket; + std::vector& rebuilt; + } rcv; + + void ConfigureGroup(Group& g, int32_t seqno, size_t gstep, size_t drop); + template + void ConfigureColumns(Container& which, int32_t isn); + + void ResetGroup(Group& g); + + // Universal + void ClipData(Group& g, uint16_t length_net, uint8_t kflg, + uint32_t timestamp_hw, const char* payload, size_t payload_size); + void ClipPacket(Group& g, const CPacket& pkt); + + // Sending + bool CheckGroupClose(Group& g, size_t pos, size_t size); + void PackControl(const Group& g, signed char groupix, SrtPacket& pkt, int32_t seqno); + + // 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); + 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); + + static void TranslateLossRecords(const std::set& 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); + void CollectIrrecoverRow(RcvGroup& g, loss_seqs_t& irrecover) const; + bool IsLost(int32_t seq) const; + +public: + + FECFilterBuiltin(const SrtFilterInitializer& init, std::vector& provided, const std::string& confstr); + + // Sender side + + // This function creates and stores the FEC control packet with + // a prediction to be immediately sent. This is called in the function + // that normally is prepared for extracting a data packet from the sender + // buffer and send it over the channel. + virtual bool packControlPacket(SrtPacket& r_packet, int32_t seq) ATR_OVERRIDE; + + // This is called at the moment when the sender queue decided to pick up + // a new packet from the scheduled packets. This should be then used to + // continue filling the group, possibly followed by final calculating the + // FEC control packet ready to send. + virtual void feedSource(CPacket& r_packet) ATR_OVERRIDE; + + // Receiver side + + // This function is called at the moment when a new data packet has + // arrived (no matter if subsequent or recovered). The 'state' value + // defines the configured level of loss state required to send the + // loss report. + virtual bool receive(const CPacket& pkt, loss_seqs_t& loss_seqs) ATR_OVERRIDE; + + // Configuration + + // This is the size that is needed extra by packets operated by this corrector. + // It should be subtracted from a current maximum value for SRTO_PAYLOADSIZE + + // The default FEC uses extra space only for FEC/CTL packet. + // The timestamp clip is placed in the timestamp field in the header. + // The payload contains: + // - the length clip + // - the flag spec + // - the payload clip + // The payload clip takes simply the current length of SRTO_PAYLOADSIZE. + // So extra 4 bytes are needed, 2 for flags, 2 for length clip. + static const size_t EXTRA_SIZE = 4; + + virtual SRT_ARQLevel arqLevel() ATR_OVERRIDE { return m_fallback_level; } +}; + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/filelist.maf b/trunk/3rdparty/srt-1-fit/srtcore/filelist.maf new file mode 100644 index 000000000..e2a6983c4 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/filelist.maf @@ -0,0 +1,57 @@ + + +SOURCES +api.cpp +buffer.cpp +cache.cpp +channel.cpp +common.cpp +core.cpp +crypto.cpp +epoll.cpp +fec.cpp +handshake.cpp +list.cpp +md5.cpp +packet.cpp +packetfilter.cpp +queue.cpp +congctl.cpp +srt_c_api.cpp +window.cpp +srt_compat.c + +PUBLIC HEADERS +srt.h +logging_api.h + +PROTECTED HEADERS +platform_sys.h +udt.h +srt4udt.h + +PRIVATE HEADERS +api.h +buffer.h +cache.h +channel.h +common.h +core.h +crypto.h +epoll.h +handshake.h +list.h +logging.h +md5.h +netinet_any.h +packet.h +queue.h +congctl.h +srt4udt.h +srt_compat.h +threadname.h +utilities.h +window.h + +SOURCES WIN32 SHARED +srt_shared.rc diff --git a/trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp b/trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp new file mode 100644 index 000000000..11104365c --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp @@ -0,0 +1,260 @@ +/* + * 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. +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "udt.h" +#include "core.h" +#include "handshake.h" +#include "utilities.h" + +using namespace std; + + +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 r_size) +{ + size_t& size = *r_size; + if (size < m_iContentSize) + return -1; + + int32_t* p = reinterpret_cast(buf); + *p++ = m_iVersion; + *p++ = m_iType; + *p++ = m_iISN; + *p++ = m_iMSS; + *p++ = m_iFlightFlagSize; + *p++ = int32_t(m_iReqType); + *p++ = m_iID; + *p++ = m_iCookie; + for (int i = 0; i < 4; ++ i) + *p++ = m_piPeerIP[i]; + + size = m_iContentSize; + + return 0; +} + +int CHandShake::load_from(const char* buf, size_t size) +{ + if (size < m_iContentSize) + return -1; + + const int32_t* p = reinterpret_cast(buf); + + m_iVersion = *p++; + m_iType = *p++; + m_iISN = *p++; + m_iMSS = *p++; + m_iFlightFlagSize = *p++; + m_iReqType = UDTRequestType(*p++); + m_iID = *p++; + m_iCookie = *p++; + for (int i = 0; i < 4; ++ i) + m_piPeerIP[i] = *p++; + + return 0; +} + +#ifdef ENABLE_LOGGING + +const char* srt_rejectreason_name [] = { + "UNKNOWN", + "SYSTEM", + "PEER", + "RESOURCE", + "ROGUE", + "BACKLOG", + "IPE", + "CLOSE", + "VERSION", + "RDVCOOKIE", + "BADSECRET", + "UNSECURE", + "MESSAGEAPI", + "CONGESTION", + "FILTER", +}; + +std::string 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]; + } + + switch ( rq ) + { + case URQ_INDUCTION: return "induction"; + case URQ_WAVEAHAND: return "waveahand"; + case URQ_CONCLUSION: return "conclusion"; + case URQ_AGREEMENT: return "agreement"; + + default: return "INVALID"; + } +} + +string CHandShake::RdvStateStr(CHandShake::RendezvousState s) +{ + switch (s) + { + case RDV_WAVING: return "waving"; + case RDV_ATTENTION: return "attention"; + case RDV_FINE: return "fine"; + case RDV_INITIATED: return "initiated"; + case RDV_CONNECTED: return "connected"; + default: ; + } + + return "invalid"; +} +#endif + +string CHandShake::show() +{ + ostringstream so; + + so << "version=" << m_iVersion << " type=" << 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 + << " srcIP="; + + const unsigned char* p = (const unsigned char*)m_piPeerIP; + const unsigned char* pe = p + 4 * (sizeof(uint32_t)); + + copy(p, pe, ostream_iterator(so, ".")); + + // XXX HS version symbols should be probably declared inside + // CHandShake, not CUDT. + if ( m_iVersion > CUDT::HS_VERSION_UDT4 ) + { + so << "EXT: "; + if (m_iType == 0) // no flags at all + so << "none"; + else + so << ExtensionFlagStr(m_iType); + } + + return so.str(); +} + +string CHandShake::ExtensionFlagStr(int32_t fl) +{ + std::ostringstream out; + if ( fl & HS_EXT_HSREQ ) + out << " hsx"; + if ( fl & HS_EXT_KMREQ ) + out << " kmx"; + if ( fl & HS_EXT_CONFIG ) + out << " config"; + + int kl = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(fl) << 6; + if (kl != 0) + { + out << " AES-" << kl; + } + else + { + out << " no-pbklen"; + } + + return out.str(); +} + + +// 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 +{ + if (size < SRT_HS_SIZE) + return false; + + int32_t* p = reinterpret_cast(buf); + + *p++ = m_iSrtVersion; + *p++ = m_iSrtFlags; + *p++ = m_iSrtTsbpd; + *p++ = 0; // SURPRISE! Seriously, use (something) if this "reserved" is going to be used for something. + return true; +} + + +bool SrtHSRequest::deserialize(const char* buf, size_t size) +{ + m_iSrtVersion = 0; // just to let users recognize if it succeeded or not. + + if (size < SRT_HS_SIZE) + return false; + + const int32_t* p = reinterpret_cast(buf); + + m_iSrtVersion = (*p++); + m_iSrtFlags = (*p++); + m_iSrtTsbpd = (*p++); + m_iSrtReserved = (*p++); + return true; +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/handshake.h b/trunk/3rdparty/srt-1-fit/srtcore/handshake.h new file mode 100644 index 000000000..4b4c7d807 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/handshake.h @@ -0,0 +1,342 @@ +/* + * 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. +*****************************************************************************/ + +#ifndef INC__HANDSHAKE_H +#define INC__HANDSHAKE_H + +#include "crypto.h" +#include "utilities.h" + +typedef Bits<31, 16> HS_CMDSPEC_CMD; +typedef Bits<15, 0> HS_CMDSPEC_SIZE; + +// NOTE: Some of these flags represent CAPABILITIES, that is, +// as long as these flags are defined, they must be always set +// (unless they are deprecated). +enum SrtOptions +{ + SRT_OPT_TSBPDSND = BIT(0), /* Timestamp-based Packet delivery real-time data sender */ + SRT_OPT_TSBPDRCV = BIT(1), /* Timestamp-based Packet delivery real-time data receiver */ + SRT_OPT_HAICRYPT = BIT(2), /* CAPABILITY: HaiCrypt AES-128/192/256-CTR */ + SRT_OPT_TLPKTDROP = BIT(3), /* Drop real-time data packets too late to be processed in time */ + SRT_OPT_NAKREPORT = BIT(4), /* Periodic NAK report */ + SRT_OPT_REXMITFLG = BIT(5), // CAPABILITY: One bit in payload packet msgno is "retransmitted" flag + // (this flag can be reused for something else, when pre-1.2.0 versions are all abandoned) + SRT_OPT_STREAM = BIT(6), // STREAM MODE (not MESSAGE mode) + SRT_OPT_FILTERCAP = BIT(7), // CAPABILITY: Packet filter supported +}; + +inline int SrtVersionCapabilities() +{ + // NOTE: SRT_OPT_REXMITFLG is not included here because + // SRT is prepared to handle also peers that don't have this + // capability, so a listener responding to a peer that doesn't + // support it should NOT set this flag. + // + // This state will remain until this backward compatibility is + // decided to be broken, in which case this flag will be always + // set, and clients that do not support this capability will be + // rejected. + return SRT_OPT_HAICRYPT | SRT_OPT_FILTERCAP; +} + + +std::string SrtFlagString(int32_t flags); + +const int SRT_CMD_REJECT = 0, // REJECT is only a symbol for return type + SRT_CMD_HSREQ = 1, + SRT_CMD_HSRSP = 2, + SRT_CMD_KMREQ = 3, + SRT_CMD_KMRSP = 4, + SRT_CMD_SID = 5, + SRT_CMD_CONGESTION = 6, + SRT_CMD_FILTER = 7, + SRT_CMD_NONE = -1; // for cases when {no pong for ping is required} | {no extension block found} + +enum SrtDataStruct +{ + SRT_HS_VERSION = 0, + SRT_HS_FLAGS, + SRT_HS_LATENCY, + + // Keep it always last + SRT_HS__SIZE +}; + +// For HSv5 the lo and hi part is used for particular side's latency +typedef Bits<31, 16> SRT_HS_LATENCY_RCV; +typedef Bits<15, 0> SRT_HS_LATENCY_SND; +// For HSv4 only the lower part is used. +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; + } + +}; + +struct SrtHSRequest: public SrtHandshakeExtension +{ + + typedef Bits<31, 16> SRT_HSTYPE_ENCFLAGS; + typedef Bits<15, 0> SRT_HSTYPE_HSFLAGS; + + // For translating PBKEYLEN into crypto flags + // This value is 16, 24, 32; after cutting off + // the leftmost 3 bits, it is 2, 3, 4. + typedef Bits<5, 3> SRT_PBKEYLEN_BITS; + + // This value fits ins SRT_HSTYPE_HSFLAGS. + // .... HAIVISIOn + static const int32_t SRT_MAGIC_CODE = 0x4A17; + + static int32_t wrapFlags(bool withmagic, int crypto_keylen) + { + int32_t base = withmagic ? SRT_MAGIC_CODE : 0; + return base | SRT_HSTYPE_ENCFLAGS::wrap( SRT_PBKEYLEN_BITS::unwrap(crypto_keylen) ); + } + +private: + friend class CHandShake; + + static const size_t SRT_HS_SIZE = 4*sizeof(uint32_t); // 4 existing fields + static const size_t SRT_EXT_HS_SIZE = 2*sizeof(uint32_t) + SRT_HS_SIZE; // SRT magic and SRT HS type, used only in UDT HS ext + + typedef Bits<15, 0> SRT_TSBPD_DELAY; + + uint32_t m_iSrtVersion; + uint32_t m_iSrtFlags; + uint32_t m_iSrtTsbpd; + uint32_t m_iSrtReserved; + +public: + + SrtHSRequest(): SrtHandshakeExtension(SRT_CMD_HSREQ), m_iSrtVersion(), m_iSrtFlags(), m_iSrtTsbpd(), m_iSrtReserved() {} + + void setVersion(uint32_t v) { m_iSrtVersion = v; } + uint32_t version() const { return m_iSrtVersion; } + + void setFlag(SrtOptions opt) { m_iSrtFlags |= uint32_t(opt); } + void clearFlag(SrtOptions opt) { m_iSrtFlags &= ~opt; } + uint32_t flags() const { return m_iSrtFlags; } + + void setTsbPdDelay(uint16_t delay) { m_iSrtTsbpd |= SRT_TSBPD_DELAY::wrap(delay); } + // Unknown what the 1-16 bits have to be used for. + uint16_t tsbPdDelay() const + { + return SRT_TSBPD_DELAY::unwrap(m_iSrtTsbpd); + } + + size_t size() const { return SRT_EXT_HS_SIZE; } + + bool serialize(char* p, size_t size) const; + bool deserialize(const char* mem, size_t size); +}; + +struct SrtKMRequest: public SrtHandshakeExtension +{ + uint32_t m_iKmState; + char m_aKey[1]; // dynamic size +}; + + +//////////////////////////////////////////////////////////////////////////////// + +enum UDTRequestType +{ + URQ_INDUCTION_TYPES = 0, // XXX used to check in one place. Consdr rm. + + URQ_INDUCTION = 1, // First part for client-server connection + URQ_WAVEAHAND = 0, // First part for rendezvous connection + + URQ_CONCLUSION = -1, // Second part of handshake negotiation + URQ_AGREEMENT = -2, // Extra (last) step for rendezvous only + URQ_DONE = -3, // Special value used only in state-switching, to state that nothing should be sent in response + + // Note: the client-server connection uses: + // --> INDUCTION (empty) + // <-- INDUCTION (cookie) + // --> CONCLUSION (cookie) + // <-- CONCLUSION (ok) + + // The rendezvous HSv4 (legacy): + // --> WAVEAHAND (effective only if peer is also connecting) + // <-- CONCLUSION (empty) (consider yourself connected upon reception) + // --> AGREEMENT (sent as a response for conclusion, requires no response) + + // The rendezvous HSv5 (using SRT extensions): + // --> WAVEAHAND (with cookie) + // --- (selecting INITIATOR/RESPONDER by cookie contest - comparing one another's cookie) + // <-- CONCLUSION (without extensions, if RESPONDER, with extensions, if INITIATOR) + // --> 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 + + // 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. +}; + +inline UDTRequestType URQFailure(SRT_REJECT_REASON reason) +{ + return UDTRequestType(URQ_FAILURE_TYPES + int(reason)); +} + +inline SRT_REJECT_REASON RejectReasonForURQ(UDTRequestType req) +{ + if (req < URQ_FAILURE_TYPES || req - URQ_FAILURE_TYPES >= SRT_REJ__SIZE) + return SRT_REJ_UNKNOWN; + return SRT_REJECT_REASON(req - URQ_FAILURE_TYPES); +} + +// DEPRECATED values. Use URQFailure(SRT_REJECT_REASON). +const UDTRequestType URQ_ERROR_REJECT SRT_ATR_DEPRECATED = (UDTRequestType)1002; // == 1000 + SRT_REJ_PEER +const UDTRequestType URQ_ERROR_INVALID SRT_ATR_DEPRECATED = (UDTRequestType)1004; // == 1000 + SRT_REJ_ROGUE + +// XXX Change all uses of that field to UDTRequestType when possible +#if ENABLE_LOGGING +std::string RequestTypeStr(UDTRequestType); +#else +inline std::string RequestTypeStr(UDTRequestType) { return ""; } +#endif + + +class CHandShake +{ +public: + CHandShake(); + + int store_to(char* buf, ref_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 + + // Extension flags + + static const int32_t HS_EXT_HSREQ = BIT(0); + static const int32_t HS_EXT_KMREQ = BIT(1); + static const int32_t HS_EXT_CONFIG = BIT(2); + + static std::string ExtensionFlagStr(int32_t fl); + + // Applicable only when m_iVersion == HS_VERSION_SRT1 + 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 + + bool m_extension; + + 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. +}; + +#if ENABLE_LOGGING +static std::string RdvStateStr(RendezvousState s); +#else +static std::string RdvStateStr(RendezvousState) { return ""; } +#endif + +}; + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/list.cpp b/trunk/3rdparty/srt-1-fit/srtcore/list.cpp new file mode 100644 index 000000000..f5a077243 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/list.cpp @@ -0,0 +1,769 @@ +/* + * 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 01/22/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#include "list.h" +#include "packet.h" + +CSndLossList::CSndLossList(int size): +m_caSeq(), +m_iHead(-1), +m_iLength(0), +m_iSize(size), +m_iLastInsertPos(-1), +m_ListLock() +{ + m_caSeq = new Seq[size]; + + // -1 means there is no data in the node + for (int i = 0; i < size; ++ i) + { + m_caSeq[i].data1 = -1; + m_caSeq[i].data2 = -1; + } + + // sender list needs mutex protection + pthread_mutex_init(&m_ListLock, 0); +} + +CSndLossList::~CSndLossList() +{ + delete [] m_caSeq; + pthread_mutex_destroy(&m_ListLock); +} + +int CSndLossList::insert(int32_t seqno1, int32_t seqno2) +{ + CGuard listguard(m_ListLock); + + if (0 == m_iLength) + { + // insert data into an empty list + + m_iHead = 0; + m_caSeq[m_iHead].data1 = seqno1; + if (seqno2 != seqno1) + m_caSeq[m_iHead].data2 = seqno2; + + m_caSeq[m_iHead].next = -1; + m_iLastInsertPos = m_iHead; + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + + return m_iLength; + } + + // otherwise find the position where the data can be inserted + int origlen = m_iLength; + int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno1); + int loc = (m_iHead + offset + m_iSize) % m_iSize; + + if (offset < 0) + { + // Insert data prior to the head pointer + + m_caSeq[loc].data1 = seqno1; + if (seqno2 != seqno1) + m_caSeq[loc].data2 = seqno2; + + // new node becomes head + m_caSeq[loc].next = m_iHead; + m_iHead = loc; + m_iLastInsertPos = loc; + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + } + else if (offset > 0) + { + if (seqno1 == m_caSeq[loc].data1) + { + m_iLastInsertPos = loc; + + // first seqno is equivlent, compare the second + if (-1 == m_caSeq[loc].data2) + { + if (seqno2 != seqno1) + { + m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1; + m_caSeq[loc].data2 = seqno2; + } + } + else if (CSeqNo::seqcmp(seqno2, m_caSeq[loc].data2) > 0) + { + // new seq pair is longer than old pair, e.g., insert [3, 7] to [3, 5], becomes [3, 7] + m_iLength += CSeqNo::seqlen(m_caSeq[loc].data2, seqno2) - 1; + m_caSeq[loc].data2 = seqno2; + } + else + // Do nothing if it is already there + return 0; + } + else + { + // searching the prior node + int i; + if ((-1 != m_iLastInsertPos) && (CSeqNo::seqcmp(m_caSeq[m_iLastInsertPos].data1, seqno1) < 0)) + i = m_iLastInsertPos; + else + i = m_iHead; + + while ((-1 != m_caSeq[i].next) && (CSeqNo::seqcmp(m_caSeq[m_caSeq[i].next].data1, seqno1) < 0)) + i = m_caSeq[i].next; + + if ((-1 == m_caSeq[i].data2) || (CSeqNo::seqcmp(m_caSeq[i].data2, seqno1) < 0)) + { + m_iLastInsertPos = loc; + + // no overlap, create new node + m_caSeq[loc].data1 = seqno1; + if (seqno2 != seqno1) + m_caSeq[loc].data2 = seqno2; + + m_caSeq[loc].next = m_caSeq[i].next; + m_caSeq[i].next = loc; + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + } + else + { + m_iLastInsertPos = i; + + // overlap, coalesce with prior node, insert(3, 7) to [2, 5], ... becomes [2, 7] + if (CSeqNo::seqcmp(m_caSeq[i].data2, seqno2) < 0) + { + m_iLength += CSeqNo::seqlen(m_caSeq[i].data2, seqno2) - 1; + m_caSeq[i].data2 = seqno2; + + loc = i; + } + else + return 0; + } + } + } + else + { + m_iLastInsertPos = m_iHead; + + // insert to head node + if (seqno2 != seqno1) + { + if (-1 == m_caSeq[loc].data2) + { + m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1; + m_caSeq[loc].data2 = seqno2; + } + else if (CSeqNo::seqcmp(seqno2, m_caSeq[loc].data2) > 0) + { + m_iLength += CSeqNo::seqlen(m_caSeq[loc].data2, seqno2) - 1; + m_caSeq[loc].data2 = seqno2; + } + else + return 0; + } + else + return 0; + } + + // coalesce with next node. E.g., [3, 7], ..., [6, 9] becomes [3, 9] + while ((-1 != m_caSeq[loc].next) && (-1 != m_caSeq[loc].data2)) + { + const int i = m_caSeq[loc].next; + + if (CSeqNo::seqcmp(m_caSeq[i].data1, CSeqNo::incseq(m_caSeq[loc].data2)) > 0) + break; + + // coalesce if there is overlap + if (-1 != m_caSeq[i].data2) + { + if (CSeqNo::seqcmp(m_caSeq[i].data2, m_caSeq[loc].data2) > 0) + { + if (CSeqNo::seqcmp(m_caSeq[loc].data2, m_caSeq[i].data1) >= 0) + m_iLength -= CSeqNo::seqlen(m_caSeq[i].data1, m_caSeq[loc].data2); + + m_caSeq[loc].data2 = m_caSeq[i].data2; + } + else + m_iLength -= CSeqNo::seqlen(m_caSeq[i].data1, m_caSeq[i].data2); + } + else + { + if (m_caSeq[i].data1 == CSeqNo::incseq(m_caSeq[loc].data2)) + m_caSeq[loc].data2 = m_caSeq[i].data1; + else + m_iLength--; + } + + m_caSeq[i].data1 = -1; + m_caSeq[i].data2 = -1; + m_caSeq[loc].next = m_caSeq[i].next; + } + + return m_iLength - origlen; +} + +void CSndLossList::remove(int32_t seqno) +{ + CGuard listguard(m_ListLock); + + if (0 == m_iLength) + return; + + // Remove all from the head pointer to a node with a larger seq. no. or the list is empty + int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno); + int loc = (m_iHead + offset + m_iSize) % m_iSize; + + if (0 == offset) + { + // It is the head. Remove the head and point to the next node + loc = (loc + 1) % m_iSize; + + if (-1 == m_caSeq[m_iHead].data2) + loc = m_caSeq[m_iHead].next; + else + { + m_caSeq[loc].data1 = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_caSeq[m_iHead].data2, CSeqNo::incseq(seqno)) > 0) + m_caSeq[loc].data2 = m_caSeq[m_iHead].data2; + + m_caSeq[m_iHead].data2 = -1; + + m_caSeq[loc].next = m_caSeq[m_iHead].next; + } + + m_caSeq[m_iHead].data1 = -1; + + if (m_iLastInsertPos == m_iHead) + m_iLastInsertPos = -1; + + m_iHead = loc; + + m_iLength --; + } + else if (offset > 0) + { + int h = m_iHead; + + if (seqno == m_caSeq[loc].data1) + { + // target node is not empty, remove part/all of the seqno in the node. + int temp = loc; + loc = (loc + 1) % m_iSize; + + if (-1 == m_caSeq[temp].data2) + m_iHead = m_caSeq[temp].next; + else + { + // remove part, e.g., [3, 7] becomes [], [4, 7] after remove(3) + m_caSeq[loc].data1 = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_caSeq[temp].data2, m_caSeq[loc].data1) > 0) + m_caSeq[loc].data2 = m_caSeq[temp].data2; + m_iHead = loc; + m_caSeq[loc].next = m_caSeq[temp].next; + m_caSeq[temp].next = loc; + m_caSeq[temp].data2 = -1; + } + } + else + { + // target node is empty, check prior node + int i = m_iHead; + while ((-1 != m_caSeq[i].next) && (CSeqNo::seqcmp(m_caSeq[m_caSeq[i].next].data1, seqno) < 0)) + i = m_caSeq[i].next; + + loc = (loc + 1) % m_iSize; + + if (-1 == m_caSeq[i].data2) + m_iHead = m_caSeq[i].next; + else if (CSeqNo::seqcmp(m_caSeq[i].data2, seqno) > 0) + { + // remove part/all seqno in the prior node + m_caSeq[loc].data1 = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_caSeq[i].data2, m_caSeq[loc].data1) > 0) + m_caSeq[loc].data2 = m_caSeq[i].data2; + + m_caSeq[i].data2 = seqno; + + m_caSeq[loc].next = m_caSeq[i].next; + m_caSeq[i].next = loc; + + m_iHead = loc; + } + else + m_iHead = m_caSeq[i].next; + } + + // Remove all nodes prior to the new head + while (h != m_iHead) + { + if (m_caSeq[h].data2 != -1) + { + m_iLength -= CSeqNo::seqlen(m_caSeq[h].data1, m_caSeq[h].data2); + m_caSeq[h].data2 = -1; + } + else + m_iLength --; + + m_caSeq[h].data1 = -1; + + if (m_iLastInsertPos == h) + m_iLastInsertPos = -1; + + h = m_caSeq[h].next; + } + } +} + +int CSndLossList::getLossLength() const +{ + CGuard listguard(m_ListLock); + + return m_iLength; +} + +int32_t CSndLossList::popLostSeq() +{ + CGuard listguard(m_ListLock); + + if (0 == m_iLength) + return -1; + + if (m_iLastInsertPos == m_iHead) + m_iLastInsertPos = -1; + + // return the first loss seq. no. + int32_t seqno = m_caSeq[m_iHead].data1; + + // head moves to the next node + if (-1 == m_caSeq[m_iHead].data2) + { + //[3, -1] becomes [], and head moves to next node in the list + m_caSeq[m_iHead].data1 = -1; + m_iHead = m_caSeq[m_iHead].next; + } + else + { + // shift to next node, e.g., [3, 7] becomes [], [4, 7] + int loc = (m_iHead + 1) % m_iSize; + + m_caSeq[loc].data1 = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_caSeq[m_iHead].data2, m_caSeq[loc].data1) > 0) + m_caSeq[loc].data2 = m_caSeq[m_iHead].data2; + + m_caSeq[m_iHead].data1 = -1; + m_caSeq[m_iHead].data2 = -1; + + m_caSeq[loc].next = m_caSeq[m_iHead].next; + m_iHead = loc; + } + + m_iLength --; + + return seqno; +} + +//////////////////////////////////////////////////////////////////////////////// + +CRcvLossList::CRcvLossList(int size): +m_caSeq(), +m_iHead(-1), +m_iTail(-1), +m_iLength(0), +m_iSize(size) +{ + m_caSeq = new Seq[m_iSize]; + + // -1 means there is no data in the node + for (int i = 0; i < size; ++ i) + { + m_caSeq[i].data1 = -1; + m_caSeq[i].data2 = -1; + } +} + +CRcvLossList::~CRcvLossList() +{ + delete [] m_caSeq; +} + +void CRcvLossList::insert(int32_t seqno1, int32_t seqno2) +{ + // Data to be inserted must be larger than all those in the list + // guaranteed by the UDT receiver + + if (0 == m_iLength) + { + // insert data into an empty list + m_iHead = 0; + m_iTail = 0; + m_caSeq[m_iHead].data1 = seqno1; + if (seqno2 != seqno1) + m_caSeq[m_iHead].data2 = seqno2; + + m_caSeq[m_iHead].next = -1; + m_caSeq[m_iHead].prior = -1; + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + + return; + } + + // otherwise searching for the position where the node should be + int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno1); + int loc = (m_iHead + offset) % m_iSize; + + if ((-1 != m_caSeq[m_iTail].data2) && (CSeqNo::incseq(m_caSeq[m_iTail].data2) == seqno1)) + { + // coalesce with prior node, e.g., [2, 5], [6, 7] becomes [2, 7] + loc = m_iTail; + m_caSeq[loc].data2 = seqno2; + } + else + { + // create new node + m_caSeq[loc].data1 = seqno1; + + if (seqno2 != seqno1) + m_caSeq[loc].data2 = seqno2; + + m_caSeq[m_iTail].next = loc; + m_caSeq[loc].prior = m_iTail; + m_caSeq[loc].next = -1; + m_iTail = loc; + } + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); +} + +bool CRcvLossList::remove(int32_t seqno) +{ + if (0 == m_iLength) + return false; + + // locate the position of "seqno" in the list + int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno); + if (offset < 0) + return false; + + int loc = (m_iHead + offset) % m_iSize; + + if (seqno == m_caSeq[loc].data1) + { + // This is a seq. no. that starts the loss sequence + + if (-1 == m_caSeq[loc].data2) + { + // there is only 1 loss in the sequence, delete it from the node + if (m_iHead == loc) + { + m_iHead = m_caSeq[m_iHead].next; + if (-1 != m_iHead) + m_caSeq[m_iHead].prior = -1; + } + else + { + m_caSeq[m_caSeq[loc].prior].next = m_caSeq[loc].next; + if (-1 != m_caSeq[loc].next) + m_caSeq[m_caSeq[loc].next].prior = m_caSeq[loc].prior; + else + m_iTail = m_caSeq[loc].prior; + } + + m_caSeq[loc].data1 = -1; + } + else + { + // there are more than 1 loss in the sequence + // move the node to the next and update the starter as the next loss inSeqNo(seqno) + + // find next node + int i = (loc + 1) % m_iSize; + + // remove the "seqno" and change the starter as next seq. no. + m_caSeq[i].data1 = CSeqNo::incseq(m_caSeq[loc].data1); + + // process the sequence end + if (CSeqNo::seqcmp(m_caSeq[loc].data2, CSeqNo::incseq(m_caSeq[loc].data1)) > 0) + m_caSeq[i].data2 = m_caSeq[loc].data2; + + // remove the current node + m_caSeq[loc].data1 = -1; + m_caSeq[loc].data2 = -1; + + // update list pointer + m_caSeq[i].next = m_caSeq[loc].next; + m_caSeq[i].prior = m_caSeq[loc].prior; + + if (m_iHead == loc) + m_iHead = i; + else + m_caSeq[m_caSeq[i].prior].next = i; + + if (m_iTail == loc) + m_iTail = i; + else + m_caSeq[m_caSeq[i].next].prior = i; + } + + m_iLength --; + + return true; + } + + // There is no loss sequence in the current position + // the "seqno" may be contained in a previous node + + // searching previous node + int i = (loc - 1 + m_iSize) % m_iSize; + while (-1 == m_caSeq[i].data1) + i = (i - 1 + m_iSize) % m_iSize; + + // not contained in this node, return + if ((-1 == m_caSeq[i].data2) || (CSeqNo::seqcmp(seqno, m_caSeq[i].data2) > 0)) + return false; + + if (seqno == m_caSeq[i].data2) + { + // it is the sequence end + + if (seqno == CSeqNo::incseq(m_caSeq[i].data1)) + m_caSeq[i].data2 = -1; + else + m_caSeq[i].data2 = CSeqNo::decseq(seqno); + } + else + { + // split the sequence + + // construct the second sequence from CSeqNo::incseq(seqno) to the original sequence end + // located at "loc + 1" + loc = (loc + 1) % m_iSize; + + m_caSeq[loc].data1 = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_caSeq[i].data2, m_caSeq[loc].data1) > 0) + m_caSeq[loc].data2 = m_caSeq[i].data2; + + // the first (original) sequence is between the original sequence start to CSeqNo::decseq(seqno) + if (seqno == CSeqNo::incseq(m_caSeq[i].data1)) + m_caSeq[i].data2 = -1; + else + m_caSeq[i].data2 = CSeqNo::decseq(seqno); + + // update the list pointer + m_caSeq[loc].next = m_caSeq[i].next; + m_caSeq[i].next = loc; + m_caSeq[loc].prior = i; + + if (m_iTail == i) + m_iTail = loc; + else + m_caSeq[m_caSeq[loc].next].prior = loc; + } + + m_iLength --; + + return true; +} + +bool CRcvLossList::remove(int32_t seqno1, int32_t seqno2) +{ + if (seqno1 <= seqno2) + { + for (int32_t i = seqno1; i <= seqno2; ++ i) + remove(i); + } + else + { + for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++ j) + remove(j); + for (int32_t k = 0; k <= seqno2; ++ k) + remove(k); + } + + return true; +} + +bool CRcvLossList::find(int32_t seqno1, int32_t seqno2) const +{ + if (0 == m_iLength) + return false; + + int p = m_iHead; + + while (-1 != p) + { + if ((CSeqNo::seqcmp(m_caSeq[p].data1, seqno1) == 0) || + ((CSeqNo::seqcmp(m_caSeq[p].data1, seqno1) > 0) && (CSeqNo::seqcmp(m_caSeq[p].data1, seqno2) <= 0)) || + ((CSeqNo::seqcmp(m_caSeq[p].data1, seqno1) < 0) && (m_caSeq[p].data2 != -1) && CSeqNo::seqcmp(m_caSeq[p].data2, seqno1) >= 0)) + return true; + + p = m_caSeq[p].next; + } + + return false; +} + +int CRcvLossList::getLossLength() const +{ + return m_iLength; +} + +int CRcvLossList::getFirstLostSeq() const +{ + if (0 == m_iLength) + return -1; + + return m_caSeq[m_iHead].data1; +} + +void CRcvLossList::getLossArray(int32_t* array, int& len, int limit) +{ + len = 0; + + int i = m_iHead; + + while ((len < limit - 1) && (-1 != i)) + { + array[len] = m_caSeq[i].data1; + if (-1 != m_caSeq[i].data2) + { + // there are more than 1 loss in the sequence + array[len] |= LOSSDATA_SEQNO_RANGE_FIRST; + ++ len; + array[len] = m_caSeq[i].data2; + } + + ++ len; + + i = m_caSeq[i].next; + } +} + + +CRcvFreshLoss::CRcvFreshLoss(int32_t seqlo, int32_t seqhi, int initial_age): ttl(initial_age) +{ + CTimer::rdtsc(timestamp); + seq[0] = seqlo; + seq[1] = seqhi; +} + + +CRcvFreshLoss::Emod CRcvFreshLoss::revoke(int32_t sequence) +{ + int32_t diffbegin = CSeqNo::seqcmp(sequence, seq[0]); + int32_t diffend = CSeqNo::seqcmp(sequence, seq[1]); + + if ( diffbegin < 0 || diffend > 0 ) + { + return NONE; // not within the range at all. + } + + if ( diffbegin == 0 ) + { + if ( diffend == 0 ) // exactly at begin and end + { + return DELETE; + } + + // only exactly at begin. Shrink the range + seq[0] = CSeqNo::incseq(seq[0]); + return STRIPPED; + } + + if ( diffend == 0 ) // exactly at end + { + seq[1] = CSeqNo::decseq(seq[1]); + return STRIPPED; + } + + return SPLIT; +} + +CRcvFreshLoss::Emod CRcvFreshLoss::revoke(int32_t lo, int32_t hi) +{ + // This should only if the range lo-hi is anyhow covered by seq[0]-seq[1]. + + // Note: if the checked item contains sequences that are OLDER + // than the oldest sequence in this range, they should be deleted, + // even though this wasn't explicitly requested. + + // LOHI: + // ITEM: <--- delete + // If the sequence range is older than the range to be revoked, + // delete it anyway. + if ( CSeqNo::seqcmp(lo, seq[1]) > 0 ) + return DELETE; + + // LOHI: + // ITEM: <-- NOTFOUND + // This element is newer than the given sequence, so match failed. + if ( CSeqNo::seqcmp(hi, seq[0]) < 0 ) + return NONE; + + // LOHI: + // ITEM: + // RESULT: + // 2. If the 'hi' is in the middle (less than seq[1]), delete partially. + // That is, take care of this range for itself and return STRIPPED. + if ( CSeqNo::seqcmp(hi, seq[1]) < 0 ) + { + seq[0] = CSeqNo::incseq(hi); + return STRIPPED; + } + + // LOHI: + // ITEM: + // RESULT: DELETE. + // 3. Otherwise delete the record, even if this was covering only part of this range. + // This is not possible that the sequences OLDER THAN THIS are not required to be + // revoken together with this one. + + return DELETE; +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/list.h b/trunk/3rdparty/srt-1-fit/srtcore/list.h new file mode 100644 index 000000000..e25a26a96 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/list.h @@ -0,0 +1,250 @@ +/* + * 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 01/22/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef __UDT_LIST_H__ +#define __UDT_LIST_H__ + + +#include "udt.h" +#include "common.h" + + +class CSndLossList +{ +public: + 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. + + int insert(int32_t seqno1, int32_t seqno2); + + /// Remove ALL the seq. no. that are not greater than the parameter. + /// @param [in] seqno sequence number. + + void remove(int32_t seqno); + + /// 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(); + +private: + struct Seq + { + int32_t data1; // sequence number starts + int32_t data2; // seqnence number ends + int next; // 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 + + mutable pthread_mutex_t m_ListLock; // used to synchronize list operation + +private: + CSndLossList(const CSndLossList&); + CSndLossList& operator=(const CSndLossList&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CRcvLossList +{ +public: + 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. + + 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). + + 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). + + 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. + + bool find(int32_t seqno1, int32_t seqno2) const; + + /// Read the loss length. + /// @return the length of the list. + + int getLossLength() const; + + /// Read the first (smallest) seq. no. in the list. + /// @return the sequence number or -1 if the list is empty. + + int 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. + + 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; + + 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 + +private: + CRcvLossList(const CRcvLossList&); + CRcvLossList& operator=(const CRcvLossList&); +public: + + struct iterator + { + int32_t head; + Seq* seq; + + 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. + + return iterator(seq, seq[head].next); + } + + iterator& operator++() + { + *this = next(); + return *this; + } + + 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 { return !(*this == second); } + + std::pair 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); } + +}; + +struct CRcvFreshLoss +{ + int32_t seq[2]; + int ttl; + uint64_t timestamp; + + CRcvFreshLoss(int32_t seqlo, int32_t seqhi, int initial_ttl); + +// Don't WTF when looking at this. The Windows system headers define +// a publicly visible preprocessor macro with that name. REALLY! +#ifdef DELETE +#undef DELETE +#endif + 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. + }; + + Emod revoke(int32_t sequence); + Emod revoke(int32_t lo, int32_t hi); +}; + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/logging.h b/trunk/3rdparty/srt-1-fit/srtcore/logging.h new file mode 100644 index 000000000..8543472f0 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/logging.h @@ -0,0 +1,481 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef INC__SRT_LOGGING_H +#define INC__SRT_LOGGING_H + + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include "win/wintime.h" +#include +#else +#include +#endif +#include +#if HAVE_CXX11 +#include +#endif + +#include "srt.h" +#include "utilities.h" +#include "threadname.h" +#include "logging_api.h" +#include "srt_compat.h" + +#ifdef __GNUC__ +#define PRINTF_LIKE __attribute__((format(printf,2,3))) +#else +#define PRINTF_LIKE +#endif + +#if ENABLE_LOGGING + +// GENERAL NOTE: All logger functions ADD THEIR OWN \n (EOL). Don't add any your own EOL character. +// The logging system may not add the EOL character, if appropriate flag was set in log settings. +// Anyway, treat the whole contents of eventually formatted message as exactly one line. + +// 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; } + +// LOGF uses printf-like style formatting. +// Usage: LOGF(mglog.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); +#define LOGP(logdes, ...) if (logdes.CheckEnabled()) logdes.printloc(__FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__) + +#if ENABLE_HEAVY_LOGGING + +#define HLOGC LOGC +#define HLOGP LOGP +#define HLOGF LOGF + +#define IF_HEAVY_LOGGING(instr) instr + +#else + +#define HLOGC(...) +#define HLOGF(...) +#define HLOGP(...) + +#define IF_HEAVY_LOGGING(instr) (void)0 + +#endif + +#else + +#define LOGC(...) +#define LOGF(...) +#define LOGP(...) + +#define HLOGC(...) +#define HLOGF(...) +#define HLOGP(...) + +#define IF_HEAVY_LOGGING(instr) (void)0 + +#endif + +namespace srt_logging +{ + +struct LogConfig +{ + typedef std::bitset fa_bitset_t; + fa_bitset_t enabled_fa; // NOTE: assumed atomic reading + LogLevel::type max_level; // NOTE: assumed atomic reading + std::ostream* log_stream; + SRT_LOG_HANDLER_FN* loghandler_fn; + void* loghandler_opaque; + pthread_mutex_t mutex; + int flags; + + LogConfig(const fa_bitset_t& initial_fa): + enabled_fa(initial_fa), + max_level(LogLevel::warning), + log_stream(&std::cerr) + { + 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); } +}; + +// The LogDispatcher class represents the object that is responsible for +// a decision whether to log something or not, and if so, print the log. +struct SRT_API LogDispatcher +{ +private: + int fa; + LogLevel::type level; + 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; } + +public: + + LogDispatcher(int functional_area, LogLevel::type log_level, const char* your_pfx, + const char* logger_pfx /*[[nullable]]*/, LogConfig& config): + fa(functional_area), + level(log_level), + src_config(&config) + { + // XXX stpcpy desired, but not enough portable + // Composing the exact prefix is not critical, so simply + // cut the prefix, if the length is exceeded + + // See Logger::Logger; we know this has normally 2 characters, + // except !!FATAL!!, which has 9. Still less than 32. + strcpy(prefix, your_pfx); + + // If the size of the FA name together with severity exceeds the size, + // just skip the former. + if (logger_pfx && strlen(prefix) + strlen(logger_pfx) + 1 < MAX_PREFIX_SIZE) + { + strcat(prefix, ":"); + strcat(prefix, logger_pfx); + } + pthread_mutex_init(&mutex, 0); + } + + ~LogDispatcher() + { + pthread_mutex_destroy(&mutex); + } + + bool CheckEnabled(); + + void CreateLogLinePrefix(std::ostringstream&); + void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); + + // log.Debug("This is the ", nth, " time"); <--- C++11 only. + // log.Debug() << "This is the " << nth << " time"; <--- C++03 available. + +#if HAVE_CXX11 + + template + void PrintLogLine(const char* file, int line, const std::string& area, Args&&... args); + + template + void operator()(Arg1&& arg1, Args&&... args) + { + PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg1, args...); + } + + template + void printloc(const char* file, int line, const std::string& area, Arg1&& arg1, Args&&... args) + { + PrintLogLine(file, line, area, arg1, args...); + } +#else + template + void PrintLogLine(const char* file, int line, const std::string& area, const Arg& arg); + + // For C++03 (older) standard provide only with one argument. + template + void operator()(const Arg& arg) + { + PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg); + } + + void printloc(const char* file, int line, const std::string& area, const std::string& arg1) + { + PrintLogLine(file, line, area, arg1); + } +#endif + +#if ENABLE_LOGGING + + struct Proxy; + friend struct Proxy; + + Proxy operator()(); +#else + + // Dummy proxy that does nothing + struct DummyProxy + { + DummyProxy(LogDispatcher&) + { + } + + template + DummyProxy& operator<<(const T& ) // predicted for temporary objects + { + return *this; + } + + DummyProxy& form(const char*, ...) + { + return *this; + } + + DummyProxy& setloc(const char* , int , std::string) + { + return *this; + } + }; + + DummyProxy operator()() + { + return DummyProxy(*this); + } + +#endif + +}; + +#if ENABLE_LOGGING + +struct LogDispatcher::Proxy +{ + LogDispatcher& that; + + std::ostringstream os; + + // Cache the 'enabled' state in the beginning. If the logging + // becomes enabled or disabled in the middle of the log, we don't + // want it to be partially printed anyway. + bool that_enabled; + int flags; + + // CACHE!!! + const char* i_file; + int i_line; + std::string area; + + Proxy& setloc(const char* f, int l, std::string a) + { + i_file = f; + i_line = l; + area = a; + return *this; + } + + // Left for future. Not sure if it's more convenient + // to use this to translate __PRETTY_FUNCTION__ to + // something short, or just let's leave __FUNCTION__ + // or better __func__. + std::string ExtractName(std::string pretty_function); + + Proxy(LogDispatcher& guy); + + // Copy constructor is needed due to noncopyable ostringstream. + // This is used only in creation of the default object, so just + // use the default values, just copy the location cache. + Proxy(const Proxy& p): that(p.that), area(p.area) + { + i_file = p.i_file; + i_line = p.i_line; + that_enabled = false; + flags = p.flags; + } + + + template + Proxy& operator<<(const T& arg) // predicted for temporary objects + { + if ( that_enabled ) + { + os << arg; + } + return *this; + } + + ~Proxy() + { + if ( that_enabled ) + { + if ( (flags & SRT_LOGF_DISABLE_EOL) == 0 ) + os << std::endl; + that.SendLogLine(i_file, i_line, area, os.str()); + } + // Needed in destructor? + //os.clear(); + //os.str(""); + } + + Proxy& form(const char* fmts, ...) PRINTF_LIKE + { + if ( !that_enabled ) + return *this; + + if ( !fmts || fmts[0] == '\0' ) + return *this; + + va_list ap; + va_start(ap, fmts); + vform(fmts, ap); + va_end(ap); + return *this; + } + + Proxy& vform(const char* fmts, va_list ap) + { + char buf[512]; + + vsprintf(buf, fmts, ap); + size_t len = strlen(buf); + if ( buf[len-1] == '\n' ) + { + // Remove EOL character, should it happen to be at the end. + // The EOL will be added at the end anyway. + buf[len-1] = '\0'; + } + + os << buf; + return *this; + } +}; + + +#endif + +class Logger +{ + int m_fa; + LogConfig& m_config; + +public: + + LogDispatcher Debug; + LogDispatcher Note; + LogDispatcher Warn; + LogDispatcher Error; + LogDispatcher Fatal; + + Logger(int functional_area, LogConfig& config, const char* logger_pfx = NULL): + m_fa(functional_area), + m_config(config), + Debug ( m_fa, LogLevel::debug, " D", logger_pfx, m_config ), + Note ( m_fa, LogLevel::note, ".N", logger_pfx, m_config ), + Warn ( m_fa, LogLevel::warning, "!W", logger_pfx, m_config ), + Error ( m_fa, LogLevel::error, "*E", logger_pfx, m_config ), + Fatal ( m_fa, LogLevel::fatal, "!!FATAL!!", logger_pfx, m_config ) + { + } + +}; + +inline bool LogDispatcher::CheckEnabled() +{ + // Don't use enabler caching. Check enabled state every time. + + // These assume to be atomically read, so the lock is not needed + // (note that writing to this field is still mutex-protected). + // It's also no problem if the level was changed at the moment + // when the enabler check is tested here. Worst case, the log + // will be printed just a moment after it was turned off. + const LogConfig* config = src_config; // to enforce using const operator[] + int configured_enabled_fa = config->enabled_fa[fa]; + int configured_maxlevel = config->max_level; + + return configured_enabled_fa && level <= configured_maxlevel; +} + +SRT_API std::string FormatTime(uint64_t time); + +#if HAVE_CXX11 + +//extern std::mutex Debug_mutex; + +inline void PrintArgs(std::ostream&) {} + +template +inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args) +{ + serr << arg1; + PrintArgs(serr, args...); +} + +template +inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, Args&&... args ATR_UNUSED) +{ +#ifdef ENABLE_LOGGING + std::ostringstream serr; + CreateLogLinePrefix(serr); + PrintArgs(serr, args...); + + if ( !isset(SRT_LOGF_DISABLE_EOL) ) + serr << std::endl; + + // Not sure, but it wasn't ever used. + SendLogLine(file, line, area, serr.str()); +#endif +} + +#else + +template +inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, const Arg& arg ATR_UNUSED) +{ +#ifdef ENABLE_LOGGING + std::ostringstream serr; + CreateLogLinePrefix(serr); + serr << arg; + + if ( !isset(SRT_LOGF_DISABLE_EOL) ) + serr << std::endl; + + // Not sure, but it wasn't ever used. + SendLogLine(file, line, area, serr.str()); +#endif +} + +#endif + +// SendLogLine can be compiled normally. It's intermediately used by: +// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING +// - PrintLogLine, which has empty body when !ENABLE_LOGGING +inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) +{ + src_config->lock(); + if ( src_config->loghandler_fn ) + { + (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); + } + else if ( src_config->log_stream ) + { + (*src_config->log_stream) << msg; + (*src_config->log_stream).flush(); + } + src_config->unlock(); +} + +} + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/logging_api.h b/trunk/3rdparty/srt-1-fit/srtcore/logging_api.h new file mode 100644 index 000000000..71c94b19c --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/logging_api.h @@ -0,0 +1,108 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef INC__SRT_LOGGING_API_H +#define INC__SRT_LOGGING_API_H + +// These are required for access functions: +// - adding FA (requires set) +// - setting a log stream (requires iostream) +#ifdef __cplusplus +#include +#include +#endif + +#include +#ifdef _WIN32 +#include "win/syslog_defs.h" +#else +#include +#endif + +// Syslog is included so that it provides log level names. +// Haivision log standard requires the same names plus extra one: +#ifndef LOG_DEBUG_TRACE +#define LOG_DEBUG_TRACE 8 +#endif +// It's unused anyway, just for the record. +#define SRT_LOG_LEVEL_MIN LOG_CRIT +#define SRT_LOG_LEVEL_MAX LOG_DEBUG + +// Flags +#define SRT_LOGF_DISABLE_TIME 1 +#define SRT_LOGF_DISABLE_THREADNAME 2 +#define SRT_LOGF_DISABLE_SEVERITY 4 +#define SRT_LOGF_DISABLE_EOL 8 + +// Handler type. +typedef void SRT_LOG_HANDLER_FN(void* opaque, int level, const char* file, int line, const char* area, const char* message); + +#ifdef __cplusplus +namespace srt_logging +{ + + +struct LogFA +{ +private: + int value; +public: + operator int() const { return value; } + + LogFA(int v): value(v) + { + // Generally this was what it has to be used for. + // Unfortunately it couldn't be agreed with the + //logging_fa_all.insert(v); + } +}; + +const LogFA LOGFA_GENERAL = 0; + + + +namespace LogLevel +{ + // There are 3 general levels: + + // A. fatal - this means the application WILL crash. + // B. unexpected: + // - error: this was unexpected for the library + // - warning: this was expected by the library, but may be harmful for the application + // C. expected: + // - note: a significant, but rarely occurring event + // - debug: may occur even very often and enabling it can harm performance + + enum type + { + fatal = LOG_CRIT, + // Fatal vs. Error: with Error, you can still continue. + error = LOG_ERR, + // Error vs. Warning: Warning isn't considered a problem for the library. + warning = LOG_WARNING, + // Warning vs. Note: Note means something unusual, but completely correct behavior. + note = LOG_NOTICE, + // Note vs. Debug: Debug may occur even multiple times in a millisecond. + // (Well, worth noting that Error and Warning potentially also can). + debug = LOG_DEBUG + }; +} + +class Logger; + +} +#endif + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/md5.cpp b/trunk/3rdparty/srt-1-fit/srtcore/md5.cpp new file mode 100644 index 000000000..d6fd5d370 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/md5.cpp @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.cpp,v 1.3 2008/01/20 22:52:04 lilyco Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/md5.h b/trunk/3rdparty/srt-1-fit/srtcore/md5.h new file mode 100644 index 000000000..f7402e7e2 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.2 2007/12/24 05:58:37 lilyco Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/trunk/3rdparty/srt-1-fit/srtcore/netinet_any.h b/trunk/3rdparty/srt-1-fit/srtcore/netinet_any.h new file mode 100644 index 000000000..0418a1a84 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/netinet_any.h @@ -0,0 +1,133 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef INC__NETINET_ANY_H +#define INC__NETINET_ANY_H + +#include +#include "platform_sys.h" + +// This structure should replace every use of sockaddr and its currently +// used specializations, sockaddr_in and sockaddr_in6. This is to simplify +// the use of the original BSD API that relies on type-violating type casts. +// You can use the instances of sockaddr_any in every place where sockaddr is +// required. + +struct sockaddr_any +{ + union + { + sockaddr_in sin; + sockaddr_in6 sin6; + sockaddr sa; + }; + socklen_t len; + + sockaddr_any(int domain = AF_INET) + { + memset(this, 0, sizeof *this); + sa.sa_family = domain; + len = size(); + } + + socklen_t size() const + { + switch (sa.sa_family) + { + case AF_INET: return socklen_t(sizeof sin); + case AF_INET6: return socklen_t(sizeof sin6); + + default: return 0; // fallback, impossible + } + } + + int family() const { return sa.sa_family; } + + // port is in exactly the same location in both sin and sin6 + // and has the same size. This is actually yet another common + // field, just not mentioned in the sockaddr structure. + uint16_t& r_port() { return sin.sin_port; } + uint16_t r_port() const { return sin.sin_port; } + int hport() const { return ntohs(sin.sin_port); } + + void hport(int value) + { + // Port is fortunately located at the same position + // in both sockaddr_in and sockaddr_in6 and has the + // same size. + sin.sin_port = htons(value); + } + + sockaddr* get() { return &sa; } + sockaddr* operator&() { return &sa; } + + const sockaddr* get() const { return &sa; } + const sockaddr* operator&() const { return &sa; } + + template struct TypeMap; + + template + typename TypeMap::type& get(); + + struct Equal + { + bool operator()(const sockaddr_any& c1, const sockaddr_any& c2) + { + return memcmp(&c1, &c2, sizeof(c1)) == 0; + } + }; + + struct EqualAddress + { + bool operator()(const sockaddr_any& c1, const sockaddr_any& c2) + { + if ( c1.sa.sa_family == AF_INET ) + { + return c1.sin.sin_addr.s_addr == c2.sin.sin_addr.s_addr; + } + + if ( c1.sa.sa_family == AF_INET6 ) + { + return memcmp(&c1.sin6.sin6_addr, &c2.sin6.sin6_addr, sizeof (in6_addr)) == 0; + } + + return false; + } + + }; + + bool equal_address(const sockaddr_any& rhs) const + { + return EqualAddress()(*this, rhs); + } + + struct Less + { + bool operator()(const sockaddr_any& c1, const sockaddr_any& c2) + { + return memcmp(&c1, &c2, sizeof(c1)) < 0; + } + }; +}; + +template<> struct sockaddr_any::TypeMap { typedef sockaddr_in type; }; +template<> struct sockaddr_any::TypeMap { typedef sockaddr_in6 type; }; + +template <> +inline sockaddr_any::TypeMap::type& sockaddr_any::get() { return sin; } +template <> +inline sockaddr_any::TypeMap::type& sockaddr_any::get() { return sin6; } + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packet.cpp b/trunk/3rdparty/srt-1-fit/srtcore/packet.cpp new file mode 100644 index 000000000..0b9143958 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/packet.cpp @@ -0,0 +1,494 @@ +/* + * 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/12/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + + +////////////////////////////////////////////////////////////////////////////// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Packet Header | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// ~ Data / Control Information Field ~ +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0| Sequence Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |ff |o|kf |r| Message Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Time Stamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Destination Socket ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// bit 0: +// 0: Data Packet +// 1: Control Packet +// bit ff: +// 11: solo message packet +// 10: first packet of a message +// 01: last packet of a message +// bit o: +// 0: in order delivery not required +// 1: in order delivery required +// bit kf: HaiCrypt Key Flags +// 00: not encrypted +// 01: encrypted with even key +// 10: encrypted with odd key +// bit r: retransmission flag (set to 1 if this packet was sent again) +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |1| Type | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Additional Info | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Time Stamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Destination Socket ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// bit 1-15: Message type -- see @a UDTMessageType +// 0: Protocol Connection Handshake (UMSG_HANDSHAKE} +// Add. Info: Undefined +// Control Info: Handshake information (see @a CHandShake) +// 1: Keep-alive (UMSG_KEEPALIVE) +// Add. Info: Undefined +// Control Info: None +// 2: Acknowledgement (UMSG_ACK) +// Add. Info: The ACK sequence number +// Control Info: The sequence number to which (but not include) all the previous packets have beed received +// Optional: RTT +// RTT Variance +// available receiver buffer size (in bytes) +// advertised flow window size (number of packets) +// estimated bandwidth (number of packets per second) +// 3: Negative Acknowledgement (UMSG_LOSSREPORT) +// Add. Info: Undefined +// Control Info: Loss list (see loss list coding below) +// 4: Congestion/Delay Warning (UMSG_CGWARNING) +// Add. Info: Undefined +// Control Info: None +// 5: Shutdown (UMSG_SHUTDOWN) +// Add. Info: Undefined +// Control Info: None +// 6: Acknowledgement of Acknowledement (UMSG_ACKACK) +// Add. Info: The ACK sequence number +// Control Info: None +// 7: Message Drop Request (UMSG_DROPREQ) +// Add. Info: Message ID +// Control Info: first sequence number of the message +// last seqeunce number of the message +// 8: Error Signal from the Peer Side (UMSG_PEERERROR) +// Add. Info: Error code +// Control Info: None +// 0x7FFF: Explained by bits 16 - 31 (UMSG_EXT) +// +// bit 16 - 31: +// This space is used for future expansion or user defined control packets. +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |1| Sequence Number a (first) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0| Sequence Number b (last) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0| Sequence Number (single) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Loss List Field Coding: +// For any consectutive lost seqeunce numbers that the differnece between +// the last and first is more than 1, only record the first (a) and the +// the last (b) sequence numbers in the loss list field, and modify the +// the first bit of a to 1. +// For any single loss or consectutive loss less than 2 packets, use +// the original sequence numbers in the field. + + +#include +#include "packet.h" +#include "logging.h" + +namespace srt_logging +{ + extern Logger mglog; +} +using namespace srt_logging; + +// Set up the aliases in the constructure +CPacket::CPacket(): +__pad(), +m_data_owned(false), +m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])), +m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])), +m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])), +m_iID((int32_t&)(m_nHeader[SRT_PH_ID])), +m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) +{ + m_nHeader.clear(); + + // The part at PV_HEADER will be always set to a builtin buffer + // containing SRT header. + m_PacketVector[PV_HEADER].set(m_nHeader.raw(), HDR_SIZE); + + // The part at PV_DATA is zero-initialized. It should be + // set (through m_pcData and setLength()) to some externally + // provided buffer before calling CChannel::sendto(). + m_PacketVector[PV_DATA].set(NULL, 0); +} + +void CPacket::allocate(size_t alloc_buffer_size) +{ + m_PacketVector[PV_DATA].set(new char[alloc_buffer_size], alloc_buffer_size); + m_data_owned = true; +} + +void CPacket::deallocate() +{ + if (m_data_owned) + delete [] (char*)m_PacketVector[PV_DATA].data(); + m_PacketVector[PV_DATA].set(NULL, 0); +} + +CPacket::~CPacket() +{ + // PV_HEADER is always owned, PV_DATA may use a "borrowed" buffer. + // Delete the internal buffer only if it was declared as owned. + if (m_data_owned) + delete[](char*)m_PacketVector[PV_DATA].data(); +} + + +size_t CPacket::getLength() const +{ + return m_PacketVector[PV_DATA].size(); +} + +void CPacket::setLength(size_t len) +{ + m_PacketVector[PV_DATA].setLength(len); +} + +void CPacket::pack(UDTMessageType pkttype, const void* lparam, void* rparam, int size) +{ + // Set (bit-0 = 1) and (bit-1~15 = type) + setControl(pkttype); + + // Set additional information and control information field + switch (pkttype) + { + case UMSG_ACK: //0010 - Acknowledgement (ACK) + // ACK packet seq. no. + if (NULL != lparam) + m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam; + + // data ACK seq. no. + // optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated link capacity (packets per second) + m_PacketVector[PV_DATA].set(rparam, size); + + break; + + case UMSG_ACKACK: //0110 - Acknowledgement of Acknowledgement (ACK-2) + // ACK packet seq. no. + m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam; + + // control info field should be none + // but "writev" does not allow this + m_PacketVector[PV_DATA].set((void *)&__pad, 4); + + break; + + case UMSG_LOSSREPORT: //0011 - Loss Report (NAK) + // loss list + m_PacketVector[PV_DATA].set(rparam, size); + + break; + + case UMSG_CGWARNING: //0100 - Congestion Warning + // control info field should be none + // but "writev" does not allow this + m_PacketVector[PV_DATA].set((void *)&__pad, 4); + + break; + + case UMSG_KEEPALIVE: //0001 - Keep-alive + // control info field should be none + // but "writev" does not allow this + m_PacketVector[PV_DATA].set((void *)&__pad, 4); + + break; + + case UMSG_HANDSHAKE: //0000 - Handshake + // control info filed is handshake info + m_PacketVector[PV_DATA].set(rparam, size); + + break; + + case UMSG_SHUTDOWN: //0101 - Shutdown + // control info field should be none + // but "writev" does not allow this + m_PacketVector[PV_DATA].set((void *)&__pad, 4); + + break; + + case UMSG_DROPREQ: //0111 - Message Drop Request + // msg id + m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam; + + //first seq no, last seq no + m_PacketVector[PV_DATA].set(rparam, size); + + break; + + case UMSG_PEERERROR: //1000 - Error Signal from the Peer Side + // Error type + m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam; + + // control info field should be none + // but "writev" does not allow this + m_PacketVector[PV_DATA].set((void *)&__pad, 4); + + break; + + case UMSG_EXT: //0x7FFF - Reserved for user defined control packets + // for extended control packet + // "lparam" contains the extended type information for bit 16 - 31 + // "rparam" is the control information + m_nHeader[SRT_PH_SEQNO] |= *(int32_t *)lparam; + + if (NULL != rparam) + { + m_PacketVector[PV_DATA].set(rparam, size); + } + else + { + m_PacketVector[PV_DATA].set((void *)&__pad, 4); + } + + break; + + default: + break; + } +} + +IOVector* CPacket::getPacketVector() +{ + return m_PacketVector; +} + +UDTMessageType CPacket::getType() const +{ + return UDTMessageType(SEQNO_MSGTYPE::unwrap(m_nHeader[SRT_PH_SEQNO])); +} + +int CPacket::getExtendedType() const +{ + return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]); +} + +int32_t CPacket::getAckSeqNo() const +{ + // read additional information field + // This field is used only in UMSG_ACK and UMSG_ACKACK, + // so 'getAckSeqNo' symbolically defines the only use of it + // in case of CONTROL PACKET. + return m_nHeader[SRT_PH_MSGNO]; +} + +uint16_t CPacket::getControlFlags() const +{ + // This returns exactly the "extended type" value, + // which is not used at all in case when the standard + // type message is interpreted. This can be used to pass + // additional special flags. + return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]); +} + +PacketBoundary CPacket::getMsgBoundary() const +{ + return PacketBoundary(MSGNO_PACKET_BOUNDARY::unwrap(m_nHeader[SRT_PH_MSGNO])); +} + +bool CPacket::getMsgOrderFlag() const +{ + return 0!= MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]); +} + +int32_t CPacket::getMsgSeq(bool has_rexmit) const +{ + if ( has_rexmit ) + { + return MSGNO_SEQ::unwrap(m_nHeader[SRT_PH_MSGNO]); + } + else + { + return MSGNO_SEQ_OLD::unwrap(m_nHeader[SRT_PH_MSGNO]); + } +} + +bool CPacket::getRexmitFlag() const +{ + // return false; // + return 0 != MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]); +} + +EncryptionKeySpec CPacket::getMsgCryptoFlags() const +{ + return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(m_nHeader[SRT_PH_MSGNO])); +} + +// This is required as the encryption/decryption happens in place. +// This is required to clear off the flags after decryption or set +// crypto flags after encrypting a packet. +void CPacket::setMsgCryptoFlags(EncryptionKeySpec spec) +{ + int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask; + m_nHeader[SRT_PH_MSGNO] = clr_msgno | EncryptionKeyBits(spec); +} + +/* + Leaving old code for historical reasons. This is moved to CSRTCC. +EncryptionStatus CPacket::encrypt(HaiCrypt_Handle hcrypto) +{ + if ( !hcrypto ) + { + LOGC(mglog.Error, log << "IPE: NULL crypto passed to CPacket::encrypt!"); + return ENCS_FAILED; + } + + int rc = HaiCrypt_Tx_Data(hcrypto, (uint8_t *)m_nHeader.raw(), (uint8_t *)m_pcData, m_PacketVector[PV_DATA].iov_len); + if ( rc < 0 ) + { + // -1: encryption failure + // 0: key not received yet + return ENCS_FAILED; + } else if (rc > 0) { + m_PacketVector[PV_DATA].iov_len = rc; + } + return ENCS_CLEAR; +} + +EncryptionStatus CPacket::decrypt(HaiCrypt_Handle hcrypto) +{ + if (getMsgCryptoFlags() == EK_NOENC) + { + //HLOGC(mglog.Debug, log << "CPacket::decrypt: packet not encrypted"); + return ENCS_CLEAR; // not encrypted, no need do decrypt, no flags to be modified + } + + if (!hcrypto) + { + LOGC(mglog.Error, log << "IPE: NULL crypto passed to CPacket::decrypt!"); + return ENCS_FAILED; // "invalid argument" (leave encryption flags untouched) + } + + int rc = HaiCrypt_Rx_Data(hcrypto, (uint8_t *)m_nHeader.raw(), (uint8_t *)m_pcData, m_PacketVector[PV_DATA].iov_len); + if ( rc <= 0 ) + { + // -1: decryption failure + // 0: key not received yet + return ENCS_FAILED; + } + // Otherwise: rc == decrypted text length. + m_PacketVector[PV_DATA].iov_len = rc; // In case clr txt size is different from cipher txt + + // Decryption succeeded. Update flags. + m_nHeader[SRT_PH_MSGNO] &= ~MSGNO_ENCKEYSPEC::mask; // sets EK_NOENC to ENCKEYSPEC bits. + + return ENCS_CLEAR; +} + +*/ + +uint32_t CPacket::getMsgTimeStamp() const +{ + // SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests + return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK; +} + +CPacket* CPacket::clone() const +{ + CPacket* pkt = new CPacket; + memcpy(pkt->m_nHeader, m_nHeader, HDR_SIZE); + pkt->m_pcData = new char[m_PacketVector[PV_DATA].size()]; + memcpy(pkt->m_pcData, m_pcData, m_PacketVector[PV_DATA].size()); + pkt->m_PacketVector[PV_DATA].setLength(m_PacketVector[PV_DATA].size()); + + return pkt; +} + +// Useful for debugging +std::string PacketMessageFlagStr(uint32_t msgno_field) +{ + using namespace std; + + stringstream out; + + static const char* const boundary [] = { "PB_SUBSEQUENT", "PB_LAST", "PB_FIRST", "PB_SOLO" }; + static const char* const order [] = { "ORD_RELAXED", "ORD_REQUIRED" }; + static const char* const crypto [] = { "EK_NOENC", "EK_EVEN", "EK_ODD", "EK*ERROR" }; + static const char* const rexmit [] = { "SN_ORIGINAL", "SN_REXMIT" }; + + out << boundary[MSGNO_PACKET_BOUNDARY::unwrap(msgno_field)] << " "; + out << order[MSGNO_PACKET_INORDER::unwrap(msgno_field)] << " "; + out << crypto[MSGNO_ENCKEYSPEC::unwrap(msgno_field)] << " "; + out << rexmit[MSGNO_REXMIT::unwrap(msgno_field)]; + + return out.str(); +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packet.h b/trunk/3rdparty/srt-1-fit/srtcore/packet.h new file mode 100644 index 000000000..e80e100af --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/packet.h @@ -0,0 +1,421 @@ +/* + * 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 01/02/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef __UDT_PACKET_H__ +#define __UDT_PACKET_H__ + +#include "udt.h" +#include "common.h" +#include "utilities.h" +#include "packetfilter_api.h" + +////////////////////////////////////////////////////////////////////////////// +// The purpose of the IOVector class is to proide a platform-independet interface +// to the WSABUF on Windows and iovec on Linux, that can be easilly converted +// to the native structure for use in WSARecvFrom() and recvmsg(...) functions +class IOVector +#ifdef _WIN32 + : public WSABUF +#else + : public iovec +#endif +{ +public: + + inline void set(void *buffer, size_t length) + { +#ifdef _WIN32 + len = (ULONG)length; + buf = (CHAR*)buffer; +#else + iov_base = (void*)buffer; + iov_len = length; +#endif + } + + inline char*& dataRef() + { +#ifdef _WIN32 + return buf; +#else + return (char*&) iov_base; +#endif + } + + inline char* data() + { +#ifdef _WIN32 + return buf; +#else + return (char*)iov_base; +#endif + } + + inline size_t size() const + { +#ifdef _WIN32 + return (size_t) len; +#else + return iov_len; +#endif + } + + inline void setLength(size_t length) + { +#ifdef _WIN32 + len = length; +#else + iov_len = length; +#endif + } +}; + + +/// To define packets in order in the buffer. This is public due to being used in buffer. +enum PacketBoundary +{ + PB_SUBSEQUENT = 0, // 00 +/// 01: last packet of a message + PB_LAST = 1, // 01 +/// 10: first packet of a message + PB_FIRST = 2, // 10 +/// 11: solo message packet + PB_SOLO = 3, // 11 +}; + +// Breakdown of the PM_SEQNO field in the header: +// C| X X ... X, where: +typedef Bits<31> SEQNO_CONTROL; +// 1|T T T T T T T T T T T T T T T|E E...E +typedef Bits<30, 16> SEQNO_MSGTYPE; +typedef Bits<15, 0> SEQNO_EXTTYPE; +// 0|S S ... S +typedef Bits<30, 0> SEQNO_VALUE; + +// This bit cannot be used by SEQNO anyway, so it's additionally used +// in LOSSREPORT data specification to define that this value is the +// BEGIN value for a SEQNO range (to distinguish it from a SOLO loss SEQNO value). +const int32_t LOSSDATA_SEQNO_RANGE_FIRST = SEQNO_CONTROL::mask; + +// Just cosmetics for readability. +const int32_t LOSSDATA_SEQNO_RANGE_LAST = 0, LOSSDATA_SEQNO_SOLO = 0; + +inline int32_t CreateControlSeqNo(UDTMessageType type) +{ + return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(size_t(type)); +} + +inline int32_t CreateControlExtSeqNo(int exttype) +{ + return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(size_t(UMSG_EXT)) | SEQNO_EXTTYPE::wrap(exttype); +} + +// MSGNO breakdown: B B|O|K K|R|M M M M M M M M M M...M +typedef Bits<31, 30> MSGNO_PACKET_BOUNDARY; +typedef Bits<29> MSGNO_PACKET_INORDER; +typedef Bits<28, 27> MSGNO_ENCKEYSPEC; +#if 1 // can block rexmit flag +// New bit breakdown - rexmit flag supported. +typedef Bits<26> MSGNO_REXMIT; +typedef Bits<25, 0> MSGNO_SEQ; +// Old bit breakdown - no rexmit flag +typedef Bits<26, 0> MSGNO_SEQ_OLD; +// This symbol is for older SRT version, where the peer does not support the MSGNO_REXMIT flag. +// The message should be extracted as PMASK_MSGNO_SEQ, if REXMIT is supported, and PMASK_MSGNO_SEQ_OLD otherwise. + +const uint32_t PACKET_SND_NORMAL = 0, PACKET_SND_REXMIT = MSGNO_REXMIT::mask; + +#else +// Old bit breakdown - no rexmit flag +typedef Bits<26, 0> MSGNO_SEQ; +#endif + + +// constexpr in C++11 ! +inline int32_t PacketBoundaryBits(PacketBoundary o) { return MSGNO_PACKET_BOUNDARY::wrap(int32_t(o)); } + + +enum EncryptionKeySpec +{ + EK_NOENC = 0, + EK_EVEN = 1, + EK_ODD = 2 +}; + +enum EncryptionStatus +{ + ENCS_CLEAR = 0, + ENCS_FAILED = -1, + ENCS_NOTSUP = -2 +}; + +const int32_t PMASK_MSGNO_ENCKEYSPEC = MSGNO_ENCKEYSPEC::mask; +inline int32_t EncryptionKeyBits(EncryptionKeySpec f) +{ + return MSGNO_ENCKEYSPEC::wrap(int32_t(f)); +} +inline EncryptionKeySpec GetEncryptionKeySpec(int32_t msgno) +{ + return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(msgno)); +} + +const int32_t PUMASK_SEQNO_PROBE = 0xF; + +std::string PacketMessageFlagStr(uint32_t msgno_field); + +class CChannel; + +class CPacket +{ +friend class CChannel; +friend class CSndQueue; +friend class CRcvQueue; + +public: + CPacket(); + ~CPacket(); + + void allocate(size_t size); + void deallocate(); + + /// Get the payload or the control information field length. + /// @return the payload or the control information field length. + + size_t getLength() const; + + /// Set the payload or the control information field length. + /// @param len [in] the payload or the control information field length. + + void setLength(size_t len); + + /// Pack a Control packet. + /// @param pkttype [in] packet type filed. + /// @param lparam [in] pointer to the first data structure, explained by the packet type. + /// @param rparam [in] pointer to the second data structure, explained by the packet type. + /// @param size [in] size of rparam, in number of bytes; + + void pack(UDTMessageType pkttype, const void* lparam = NULL, void* rparam = NULL, int size = 0); + + /// Read the packet vector. + /// @return Pointer to the packet vector. + + IOVector* getPacketVector(); + + uint32_t* getHeader() { return m_nHeader; } + + /// Read the packet flag. + /// @return packet flag (0 or 1). + + // XXX DEPRECATED. Use isControl() instead + ATR_DEPRECATED + int getFlag() const + { + return isControl() ? 1 : 0; + } + + /// Read the packet type. + /// @return packet type filed (000 ~ 111). + + UDTMessageType getType() const; + + bool isControl(UDTMessageType type) const + { + return isControl() && type == getType(); + } + + bool isControl() const + { + // read bit 0 + return 0!= SEQNO_CONTROL::unwrap(m_nHeader[SRT_PH_SEQNO]); + } + + void setControl(UDTMessageType type) + { + m_nHeader[SRT_PH_SEQNO] = SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(type); + } + + /// Read the extended packet type. + /// @return extended packet type filed (0x000 ~ 0xFFF). + + int getExtendedType() const; + + /// Read the ACK-2 seq. no. + /// @return packet header field (bit 16~31). + + int32_t getAckSeqNo() const; + uint16_t getControlFlags() const; + + // Note: this will return a "singular" value, if the packet + // contains the control message + int32_t getSeqNo() const + { + return m_nHeader[SRT_PH_SEQNO]; + } + + /// Read the message boundary flag bit. + /// @return packet header field [1] (bit 0~1). + + PacketBoundary getMsgBoundary() const; + + /// Read the message inorder delivery flag bit. + /// @return packet header field [1] (bit 2). + + bool getMsgOrderFlag() const; + + /// Read the rexmit flag (true if the packet was sent due to retransmission). + /// If the peer does not support retransmission flag, the current agent cannot use it as well + /// (because the peer will understand this bit as a part of MSGNO field). + + bool getRexmitFlag() const; + + /// Read the message sequence number. + /// @return packet header field [1] + + int32_t getMsgSeq(bool has_rexmit = true) const; + + /// Read the message crypto key bits. + /// @return packet header field [1] (bit 3~4). + + EncryptionKeySpec getMsgCryptoFlags() const; + void setMsgCryptoFlags(EncryptionKeySpec spec); + + /// Read the message time stamp. + /// @return packet header field [2] (bit 0~31, bit 0-26 if SRT_DEBUG_TSBPD_WRAP). + + uint32_t getMsgTimeStamp() const; + +#ifdef SRT_DEBUG_TSBPD_WRAP //Receiver + static const uint32_t MAX_TIMESTAMP = 0x07FFFFFF; //27 bit fast wraparound for tests (~2m15s) +#else + static const uint32_t MAX_TIMESTAMP = 0xFFFFFFFF; //Full 32 bit (01h11m35s) +#endif + +protected: + static const uint32_t TIMESTAMP_MASK = MAX_TIMESTAMP; // this value to be also used as a mask +public: + + /// Clone this packet. + /// @return Pointer to the new packet. + + CPacket* clone() const; + + enum PacketVectorFields + { + PV_HEADER = 0, + PV_DATA = 1, + + PV_SIZE = 2 + }; + +protected: + // Length in bytes + + // DynamicStruct is the same as array of given type and size, just it + // enforces that you index it using a symbol from symbolic enum type, not by a bare integer. + + typedef DynamicStruct HEADER_TYPE; + HEADER_TYPE m_nHeader; //< The 128-bit header field + + // XXX NOTE: iovec here is not portable. On Windows there's a different + // (although similar) structure defined, which means that this way the + // Windows function that is an equivalent of `recvmsg` cannot be used. + // For example, something like that: + // class IoVector: public iovec { public: size_t size() { return iov_len; } char* data() { return iov_base; } }; + // class IoVector: public WSAMSG { public: size_t size() { return len; } char* data() { return buf; } }; + IOVector m_PacketVector[PV_SIZE]; //< The 2-demension vector of UDT packet [header, data] + + int32_t __pad; + bool m_data_owned; + +protected: + CPacket& operator=(const CPacket&); + CPacket (const CPacket&); + +public: + + int32_t& m_iSeqNo; // alias: sequence number + int32_t& m_iMsgNo; // alias: message number + int32_t& m_iTimeStamp; // alias: timestamp + int32_t& m_iID; // alias: socket ID + char*& m_pcData; // alias: data/control information + + //static const int m_iPktHdrSize; // packet header size + static const size_t HDR_SIZE = sizeof(HEADER_TYPE); // packet header size = SRT_PH__SIZE * sizeof(uint32_t) + + // Used in many computations + // Actually this can be also calculated as: sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr). + static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }. + + static const size_t SRT_DATA_HDR_SIZE = UDP_HDR_SIZE + HDR_SIZE; + + // Some well known data + static const size_t ETH_MAX_MTU_SIZE = 1500; + + // And derived + static const size_t SRT_MAX_PAYLOAD_SIZE = ETH_MAX_MTU_SIZE - SRT_DATA_HDR_SIZE; + + // Packet interface + char* data() { return m_pcData; } + const char* data() const { return m_pcData; } + size_t size() const { return getLength(); } + uint32_t header(SrtPktHeaderFields field) const { return m_nHeader[field]; } + + std::string MessageFlagStr() +#if ENABLE_LOGGING + { return PacketMessageFlagStr(m_nHeader[SRT_PH_MSGNO]); } +#else + { return ""; } +#endif +}; + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp new file mode 100644 index 000000000..99dd4de78 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp @@ -0,0 +1,294 @@ +/* + * 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/. + * + */ + + +#include +#include +#include +#include +#include + +#include "packetfilter.h" +#include "packetfilter_builtin.h" +#include "core.h" +#include "packet.h" +#include "logging.h" + +using namespace std; +using namespace srt_logging; + +bool ParseFilterConfig(std::string s, SrtFilterConfig& out) +{ + vector parts; + Split(s, ',', back_inserter(parts)); + + out.type = parts[0]; + PacketFilter::Factory* fac = PacketFilter::find(out.type); + if (!fac) + return false; + + for (vector::iterator i = parts.begin()+1; i != parts.end(); ++i) + { + vector keyval; + Split(*i, ':', back_inserter(keyval)); + if (keyval.size() != 2) + return false; + out.parameters[keyval[0]] = keyval[1]; + } + + // Extract characteristic data + out.extra_size = fac->ExtraSize(); + + return true; +} + +struct SortBySequence +{ + bool operator()(const CUnit* u1, const CUnit* u2) + { + int32_t s1 = u1->m_Packet.getSeqNo(); + int32_t s2 = u2->m_Packet.getSeqNo(); + + return CSeqNo::seqcmp(s1, s2) < 0; + } +}; + +void PacketFilter::receive(CUnit* unit, ref_t< std::vector > r_incoming, ref_t r_loss_seqs) +{ + const CPacket& rpkt = unit->m_Packet; + + if (m_filter->receive(rpkt, *r_loss_seqs)) + { + // For the sake of rebuilding MARK THIS UNIT GOOD, otherwise the + // unit factory will supply it from getNextAvailUnit() as if it were not in use. + unit->m_iFlag = CUnit::GOOD; + HLOGC(mglog.Debug, log << "FILTER: PASSTHRU current packet %" << unit->m_Packet.getSeqNo()); + r_incoming.get().push_back(unit); + } + else + { + // Packet not to be passthru, update stats + CGuard lg(m_parent->m_StatsLock); + ++m_parent->m_stats.rcvFilterExtra; + ++m_parent->m_stats.rcvFilterExtraTotal; + } + + // r_loss_seqs enters empty into this function and can be only filled here. + for (loss_seqs_t::iterator i = r_loss_seqs.get().begin(); + i != r_loss_seqs.get().end(); ++i) + { + // Sequences here are low-high, if there happens any negative distance + // here, simply skip and report IPE. + int dist = CSeqNo::seqoff(i->first, i->second) + 1; + if (dist > 0) + { + CGuard lg(m_parent->m_StatsLock); + m_parent->m_stats.rcvFilterLoss += dist; + m_parent->m_stats.rcvFilterLossTotal += dist; + } + else + { + LOGC(mglog.Error, log << "FILTER: IPE: loss record: invalid loss: %" + << i->first << " - %" << i->second); + } + } + + // Pack first recovered packets, if any. + if (!m_provided.empty()) + { + HLOGC(mglog.Debug, log << "FILTER: inserting REBUILT packets (" << m_provided.size() << "):"); + + size_t nsupply = m_provided.size(); + InsertRebuilt(*r_incoming, m_unitq); + + CGuard lg(m_parent->m_StatsLock); + m_parent->m_stats.rcvFilterSupply += nsupply; + m_parent->m_stats.rcvFilterSupplyTotal += nsupply; + } + + // Now that all units have been filled as they should be, + // SET THEM ALL FREE. This is because now it's up to the + // buffer to decide as to whether it wants them or not. + // Wanted units will be set GOOD flag, unwanted will remain + // with FREE and therefore will be returned at the next + // call to getNextAvailUnit(). + unit->m_iFlag = CUnit::FREE; + vector& inco = *r_incoming; + for (vector::iterator i = inco.begin(); i != inco.end(); ++i) + { + CUnit* u = *i; + u->m_iFlag = CUnit::FREE; + } + + // Packets must be sorted by sequence number, ascending, in order + // not to challenge the SRT's contiguity checker. + sort(inco.begin(), inco.end(), SortBySequence()); + + // For now, report immediately the irrecoverable packets + // from the row. + + // Later, the `irrecover_row` or `irrecover_col` will be + // reported only, depending on level settings. For example, + // with default LATELY level, packets will be reported as + // irrecoverable only when they are irrecoverable in the + // vertical group. + + // With "always", do not report any losses, SRT will simply check + // them itself. + + return; + +} + +bool PacketFilter::packControlPacket(ref_t r_packet, int32_t seq, int kflg) +{ + bool have = m_filter->packControlPacket(m_sndctlpkt, seq); + if (!have) + return false; + + // Now this should be repacked back to CPacket. + // The header must be copied, it's always part of CPacket. + uint32_t* hdr = r_packet.get().getHeader(); + memcpy(hdr, m_sndctlpkt.hdr, SRT_PH__SIZE * sizeof(*hdr)); + + // The buffer can be assigned. + r_packet.get().m_pcData = m_sndctlpkt.buffer; + r_packet.get().setLength(m_sndctlpkt.length); + + // This sets only the Packet Boundary flags, while all other things: + // - Order + // - Rexmit + // - Crypto + // - Message Number + // will be set to 0/false + r_packet.get().m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + + // ... and then fix only the Crypto flags + r_packet.get().setMsgCryptoFlags(EncryptionKeySpec(kflg)); + + // Don't set the ID, it will be later set for any kind of packet. + // Write the timestamp clip into the timestamp field. + return true; +} + + +void PacketFilter::InsertRebuilt(vector& incoming, CUnitQueue* uq) +{ + if (m_provided.empty()) + return; + + for (vector::iterator i = m_provided.begin(); i != m_provided.end(); ++i) + { + CUnit* u = uq->getNextAvailUnit(); + if (!u) + { + LOGC(mglog.Error, log << "FILTER: LOCAL STORAGE DEPLETED. Can't return rebuilt packets."); + break; + } + + // LOCK the unit as GOOD because otherwise the next + // call to getNextAvailUnit will return THE SAME UNIT. + u->m_iFlag = CUnit::GOOD; + // After returning from this function, all units will be + // set back to FREE so that the buffer can decide whether + // it wants them or not. + + CPacket& packet = u->m_Packet; + + memcpy(packet.getHeader(), i->hdr, CPacket::HDR_SIZE); + memcpy(packet.m_pcData, i->buffer, i->length); + packet.setLength(i->length); + + HLOGC(mglog.Debug, log << "FILTER: PROVIDING rebuilt packet %" << packet.getSeqNo()); + + incoming.push_back(u); + } + + m_provided.clear(); +} + +bool PacketFilter::IsBuiltin(const string& s) +{ + return builtin_filters.count(s); +} + +std::set PacketFilter::builtin_filters; +PacketFilter::filters_map_t PacketFilter::filters; + +PacketFilter::Factory::~Factory() +{ +} + +void PacketFilter::globalInit() +{ + // Add here builtin packet filters and mark them + // as builtin. This will disallow users to register + // external filters with the same name. + + filters["fec"] = new Creator; + builtin_filters.insert("fec"); +} + +bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr) +{ + m_parent = parent; + + SrtFilterConfig cfg; + if (!ParseFilterConfig(confstr, cfg)) + return false; + + // Extract the "type" key from parameters, or use + // builtin if lacking. + filters_map_t::iterator selector = filters.find(cfg.type); + if (selector == filters.end()) + return false; + + SrtFilterInitializer init; + init.socket_id = parent->socketID(); + init.snd_isn = parent->sndSeqNo(); + init.rcv_isn = parent->rcvSeqNo(); + init.payload_size = parent->OPT_PayloadSize(); + + + // Found a filter, so call the creation function + m_filter = selector->second->Create(init, m_provided, confstr); + if (!m_filter) + return false; + + m_unitq = uq; + + // The filter should have pinned in all events + // that are of its interest. It's stated that + // it's ready after creation. + return true; +} + +bool PacketFilter::correctConfig(const SrtFilterConfig& conf) +{ + const string* pname = map_getp(conf.parameters, "type"); + + if (!pname) + return true; // default, parameters ignored + + if (*pname == "adaptive") + return true; + + filters_map_t::iterator x = filters.find(*pname); + if (x == filters.end()) + return false; + + return true; +} + +PacketFilter::~PacketFilter() +{ + delete m_filter; +} + diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h new file mode 100644 index 000000000..fd5bf67d6 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h @@ -0,0 +1,198 @@ +/* + * 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/. + * + */ + +#ifndef INC__PACKETFILTER_H +#define INC__PACKETFILTER_H + +#include +#include +#include + +#include "packet.h" +#include "queue.h" +#include "utilities.h" +#include "packetfilter_api.h" + +class PacketFilter +{ + friend class SrtPacketFilterBase; + +public: + + typedef std::vector< std::pair > loss_seqs_t; + + typedef SrtPacketFilterBase* filter_create_t(const SrtFilterInitializer& init, std::vector&, const std::string& config); + +private: + friend bool ParseFilterConfig(std::string s, SrtFilterConfig& out); + class Factory + { + public: + virtual SrtPacketFilterBase* Create(const SrtFilterInitializer& init, std::vector& provided, const std::string& confstr) = 0; + + // Characteristic data + virtual size_t ExtraSize() = 0; + + virtual ~Factory(); + }; + + template + class Creator: public Factory + { + virtual SrtPacketFilterBase* Create(const SrtFilterInitializer& init, + std::vector& provided, + const std::string& confstr) ATR_OVERRIDE + { return new Target(init, provided, confstr); } + + // Import the extra size data + virtual size_t ExtraSize() ATR_OVERRIDE { return Target::EXTRA_SIZE; } + + public: + Creator() {} + virtual ~Creator() {} + }; + + + // We need a private wrapper for the auto-pointer, can't use + // std::unique_ptr here due to no C++11. + struct ManagedPtr + { + Factory* f; + mutable bool owns; + + // Accept whatever + ManagedPtr(Factory* ff): f(ff), owns(true) {} + ManagedPtr(): f(NULL), owns(false) {} + ~ManagedPtr() + { + if (owns) + delete f; + } + + void copy_internal(const ManagedPtr& other) + { + other.owns = false; + f = other.f; + owns = true; + } + + ManagedPtr(const ManagedPtr& other) + { + copy_internal(other); + } + + void operator=(const ManagedPtr& other) + { + if (owns) + delete f; + copy_internal(other); + } + + Factory* operator->() { return f; } + Factory* get() { return f; } + }; + + // The list of builtin names that are reserved. + static std::set builtin_filters; + + // Temporarily changed to linear searching, until this is exposed + // for a user-defined filter. + typedef std::map filters_map_t; + static filters_map_t filters; + + // This is a filter container. + SrtPacketFilterBase* m_filter; + void Check() + { +#if ENABLE_DEBUG + if (!m_filter) + abort(); +#endif + // Don't do any check for now. + } + +public: + + static void globalInit(); + + static bool IsBuiltin(const std::string&); + + template + static bool add(const std::string& name) + { + if (IsBuiltin(name)) + return false; + + filters[name] = new Creator; + return true; + } + + static Factory* find(const std::string& type) + { + filters_map_t::iterator i = filters.find(type); + if (i == filters.end()) + return NULL; // No matter what to return - this is "undefined behavior" to be prevented + return i->second.get(); + } + + // Filter is optional, so this check should be done always + // manually. + bool installed() const { return m_filter; } + operator bool() const { return installed(); } + + SrtPacketFilterBase* operator->() { Check(); return m_filter; } + + // In the beginning it's initialized as first, builtin default. + // Still, it will be created only when requested. + PacketFilter(): m_filter(), m_parent(), m_sndctlpkt(0), m_unitq() {} + + // Copy constructor - important when listener-spawning + // Things being done: + // 1. The filter is individual, so don't copy it. Set NULL. + // 2. This will be configued anyway basing on possibly a new rule set. + PacketFilter(const PacketFilter& source SRT_ATR_UNUSED): m_filter(), m_sndctlpkt(0), m_unitq() {} + + // This function will be called by the parent CUDT + // in appropriate time. It should select appropriate + // filter basing on the value in selector, then + // pin oneself in into CUDT for receiving event signals. + bool configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr); + + static bool correctConfig(const SrtFilterConfig& c); + + // Will delete the pinned in filter object. + // This must be defined in *.cpp file due to virtual + // destruction. + ~PacketFilter(); + + // Simple wrappers + void feedSource(ref_t r_packet); + SRT_ARQLevel arqLevel(); + bool packControlPacket(ref_t r_packet, int32_t seq, int kflg); + void receive(CUnit* unit, ref_t< std::vector > r_incoming, ref_t r_loss_seqs); + +protected: + void InsertRebuilt(std::vector& incoming, CUnitQueue* uq); + + CUDT* m_parent; + + // Sender part + SrtPacket m_sndctlpkt; + + // Receiver part + CUnitQueue* m_unitq; + std::vector m_provided; +}; + + +inline void PacketFilter::feedSource(ref_t r_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource(*r_packet); } +inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_filter->arqLevel(); } + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h new file mode 100644 index 000000000..787c90aee --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h @@ -0,0 +1,140 @@ +/* + * 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/. + * + */ + +#ifndef INC__PACKETFILTER_API_H +#define INC__PACKETFILTER_API_H + +enum SrtPktHeaderFields +{ + SRT_PH_SEQNO = 0, //< sequence number + SRT_PH_MSGNO = 1, //< message number + SRT_PH_TIMESTAMP = 2, //< time stamp + SRT_PH_ID = 3, //< socket ID + + // Must be the last value - this is size of all, not a field id + SRT_PH__SIZE +}; + + +enum SRT_ARQLevel +{ + SRT_ARQ_NEVER, //< Never send LOSSREPORT + SRT_ARQ_ONREQ, //< Only record the loss, but report only those that are returned in receive() + SRT_ARQ_ALWAYS, //< always send LOSSREPORT immediately after detecting a loss +}; + + +struct SrtFilterConfig +{ + std::string type; + std::map parameters; + size_t extra_size; // needed for filter option check against payload size +}; + +struct SrtFilterInitializer +{ + SRTSOCKET socket_id; + int32_t snd_isn; + int32_t rcv_isn; + size_t payload_size; +}; + +struct SrtPacket +{ + uint32_t hdr[SRT_PH__SIZE]; + char buffer[SRT_LIVE_MAX_PLSIZE]; + size_t length; + + SrtPacket(size_t size): length(size) + { + memset(hdr, 0, sizeof(hdr)); + } + + uint32_t header(SrtPktHeaderFields field) { return hdr[field]; } + char* data() { return buffer; } + const char* data() const { return buffer; } + size_t size() const { return length; } +}; + + +bool ParseFilterConfig(std::string s, SrtFilterConfig& out); + + +class SrtPacketFilterBase +{ + SrtFilterInitializer initParams; + +protected: + + SRTSOCKET socketID() const { return initParams.socket_id; } + int32_t sndISN() const { return initParams.snd_isn; } + int32_t rcvISN() const { return initParams.rcv_isn; } + size_t payloadSize() const { return initParams.payload_size; } + + friend class PacketFilter; + + // Beside the size of the rows, special values: + // 0: if you have 0 specified for rows, there are only columns + // -1: Only during the handshake, use the value specified by peer. + // -N: The N value still specifies the size, but in particular + // dimension there is no filter control packet formed nor expected. + +public: + + typedef std::vector< std::pair > loss_seqs_t; + +protected: + + SrtPacketFilterBase(const SrtFilterInitializer& i): initParams(i) + { + } + + // Sender side + + /// This function creates and stores the filter control packet with + /// a prediction to be immediately sent. This is called in the function + /// that normally is prepared for extracting a data packet from the sender + /// buffer and send it over the channel. The returned value informs the + /// caller whether the control packet was available and therefore provided. + /// @param [OUT] packet Target place where the packet should be stored + /// @param [IN] seq Sequence number of the packet last requested for sending + /// @return true if the control packet has been provided + virtual bool packControlPacket(SrtPacket& packet, int32_t seq) = 0; + + /// This is called at the moment when the sender queue decided to pick up + /// a new packet from the scheduled packets. This should be then used to + /// continue filling the group, possibly followed by final calculating the + /// control packet ready to send. The packet received by this function is + /// potentially allowed to be modified. + /// @param [INOUT] packet The packet about to send + virtual void feedSource(CPacket& packet) = 0; + + + // Receiver side + + // This function is called at the moment when a new data packet has + // arrived (no matter if subsequent or recovered). The 'state' value + // defines the configured level of loss state required to send the + // loss report. + virtual bool receive(const CPacket& pkt, loss_seqs_t& loss_seqs) = 0; + + // Backward configuration. + // This should have some stable value after the configuration is parsed, + // and it should be a stable value set ONCE, after the filter module is ready. + virtual SRT_ARQLevel arqLevel() = 0; + + virtual ~SrtPacketFilterBase() + { + } +}; + + + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_builtin.h b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_builtin.h new file mode 100644 index 000000000..91e293a13 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_builtin.h @@ -0,0 +1,18 @@ +/* + * 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/. + * + */ + + +#ifndef INC__PACKETFILTER_BUILTIN_H +#define INC__PACKETFILTER_BUILTIN_H + +// Integration header +#include "fec.h" + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h b/trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h new file mode 100644 index 000000000..fae95803f --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h @@ -0,0 +1,34 @@ +/* + * 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/. + * + */ +#ifndef INC__PLATFORM_SYS_H +#define INC__PLATFORM_SYS_H + +#ifdef _WIN32 + #include + #include + #include + #include + #include + #include + #if defined(_MSC_VER) + #pragma warning(disable:4251) + #endif +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/queue.cpp b/trunk/3rdparty/srt-1-fit/srtcore/queue.cpp new file mode 100644 index 000000000..5ca8f0315 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/queue.cpp @@ -0,0 +1,1684 @@ +/* + * 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 05/05/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifdef _WIN32 +#include +#include +#endif +#include + +#include "common.h" +#include "core.h" +#include "netinet_any.h" +#include "threadname.h" +#include "logging.h" +#include "queue.h" + +using namespace std; +using namespace srt_logging; + +CUnitQueue::CUnitQueue() + : m_pQEntry(NULL) + , m_pCurrQueue(NULL) + , m_pLastQueue(NULL) + , m_iSize(0) + , m_iCount(0) + , m_iMSS() + , m_iIPversion() +{ +} + +CUnitQueue::~CUnitQueue() +{ + CQEntry *p = m_pQEntry; + + while (p != NULL) + { + delete[] p->m_pUnit; + delete[] p->m_pBuffer; + + CQEntry *q = p; + if (p == m_pLastQueue) + p = NULL; + else + p = p->m_pNext; + delete q; + } +} + +int CUnitQueue::init(int size, int mss, int version) +{ + CQEntry *tempq = NULL; + CUnit * tempu = NULL; + char * tempb = NULL; + + try + { + tempq = new CQEntry; + tempu = new CUnit[size]; + tempb = new char[size * mss]; + } + catch (...) + { + delete tempq; + delete[] tempu; + delete[] tempb; + + return -1; + } + + for (int i = 0; i < size; ++i) + { + tempu[i].m_iFlag = CUnit::FREE; + tempu[i].m_Packet.m_pcData = tempb + i * mss; + } + tempq->m_pUnit = tempu; + tempq->m_pBuffer = tempb; + tempq->m_iSize = size; + + m_pQEntry = m_pCurrQueue = m_pLastQueue = tempq; + m_pQEntry->m_pNext = m_pQEntry; + + m_pAvailUnit = m_pCurrQueue->m_pUnit; + + m_iSize = size; + m_iMSS = mss; + m_iIPversion = version; + + return 0; +} + +int CUnitQueue::increase() +{ + // adjust/correct m_iCount + int real_count = 0; + CQEntry *p = m_pQEntry; + while (p != NULL) + { + CUnit *u = p->m_pUnit; + for (CUnit *end = u + p->m_iSize; u != end; ++u) + if (u->m_iFlag != CUnit::FREE) + ++real_count; + + if (p == m_pLastQueue) + p = NULL; + else + p = p->m_pNext; + } + m_iCount = real_count; + if (double(m_iCount) / m_iSize < 0.9) + return -1; + + CQEntry *tempq = NULL; + CUnit * tempu = NULL; + char * tempb = NULL; + + // all queues have the same size + int size = m_pQEntry->m_iSize; + + try + { + tempq = new CQEntry; + tempu = new CUnit[size]; + tempb = new char[size * m_iMSS]; + } + catch (...) + { + delete tempq; + delete[] tempu; + delete[] tempb; + + return -1; + } + + for (int i = 0; i < size; ++i) + { + tempu[i].m_iFlag = CUnit::FREE; + tempu[i].m_Packet.m_pcData = tempb + i * m_iMSS; + } + tempq->m_pUnit = tempu; + tempq->m_pBuffer = tempb; + tempq->m_iSize = size; + + m_pLastQueue->m_pNext = tempq; + m_pLastQueue = tempq; + m_pLastQueue->m_pNext = m_pQEntry; + + m_iSize += size; + + return 0; +} + +int CUnitQueue::shrink() +{ + // currently queue cannot be shrunk. + return -1; +} + +CUnit *CUnitQueue::getNextAvailUnit() +{ + if (m_iCount * 10 > m_iSize * 9) + increase(); + + if (m_iCount >= m_iSize) + return NULL; + + CQEntry *entrance = m_pCurrQueue; + + do + { + for (CUnit *sentinel = m_pCurrQueue->m_pUnit + m_pCurrQueue->m_iSize - 1; m_pAvailUnit != sentinel; + ++m_pAvailUnit) + if (m_pAvailUnit->m_iFlag == CUnit::FREE) + return m_pAvailUnit; + + if (m_pCurrQueue->m_pUnit->m_iFlag == CUnit::FREE) + { + m_pAvailUnit = m_pCurrQueue->m_pUnit; + return m_pAvailUnit; + } + + m_pCurrQueue = m_pCurrQueue->m_pNext; + m_pAvailUnit = m_pCurrQueue->m_pUnit; + } while (m_pCurrQueue != entrance); + + increase(); + + return NULL; +} + +void CUnitQueue::makeUnitFree(CUnit *unit) +{ + SRT_ASSERT(unit != NULL); + SRT_ASSERT(unit->m_iFlag != CUnit::FREE); + unit->m_iFlag = CUnit::FREE; + --m_iCount; +} + +void CUnitQueue::makeUnitGood(CUnit *unit) +{ + SRT_ASSERT(unit != NULL); + SRT_ASSERT(unit->m_iFlag == CUnit::FREE); + unit->m_iFlag = CUnit::GOOD; + ++m_iCount; +} + +CSndUList::CSndUList() + : m_pHeap(NULL) + , m_iArrayLength(512) + , m_iLastEntry(-1) + , m_ListLock() + , m_pWindowLock(NULL) + , m_pWindowCond(NULL) + , m_pTimer(NULL) +{ + m_pHeap = new CSNode *[m_iArrayLength]; + pthread_mutex_init(&m_ListLock, NULL); +} + +CSndUList::~CSndUList() +{ + delete[] m_pHeap; + pthread_mutex_destroy(&m_ListLock); +} + +void CSndUList::update(const CUDT *u, EReschedule reschedule) +{ + CGuard listguard(m_ListLock); + + CSNode *n = u->m_pSNode; + + if (n->m_iHeapLoc >= 0) + { + if (!reschedule) // EReschedule to bool conversion, predicted. + return; + + if (n->m_iHeapLoc == 0) + { + n->m_llTimeStamp_tk = 1; + m_pTimer->interrupt(); + return; + } + + remove_(u); + insert_norealloc_(1, u); + return; + } + + insert_(1, u); +} + +int CSndUList::pop(sockaddr *&addr, CPacket &pkt) +{ + CGuard listguard(m_ListLock); + + if (-1 == m_iLastEntry) + return -1; + + // no pop until the next schedulled time + uint64_t ts; + CTimer::rdtsc(ts); + if (ts < m_pHeap[0]->m_llTimeStamp_tk) + return -1; + + CUDT *u = m_pHeap[0]->m_pUDT; + remove_(u); + +#define UST(field) ((u->m_b##field) ? "+" : "-") << #field << " " + + HLOGC(mglog.Debug, + log << "SND:pop: requesting packet from @" << u->socketID() << " STATUS: " << UST(Listening) + << UST(Connecting) << UST(Connected) << UST(Closing) << UST(Shutdown) << UST(Broken) << UST(PeerHealth) + << UST(Opened)); +#undef UST + + if (!u->m_bConnected || u->m_bBroken) + return -1; + + // pack a packet from the socket + if (u->packData(pkt, ts) <= 0) + return -1; + + addr = u->m_pPeerAddr; + + // insert a new entry, ts is the next processing time + if (ts > 0) + insert_norealloc_(ts, u); + + return 1; +} + +void CSndUList::remove(const CUDT *u) +{ + CGuard listguard(m_ListLock); + + remove_(u); +} + +uint64_t CSndUList::getNextProcTime() +{ + CGuard listguard(m_ListLock); + + if (-1 == m_iLastEntry) + return 0; + + return m_pHeap[0]->m_llTimeStamp_tk; +} + +void CSndUList::realloc_() +{ + CSNode **temp = NULL; + + try + { + temp = new CSNode *[2 * m_iArrayLength]; + } + catch (...) + { + throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + } + + memcpy(temp, m_pHeap, sizeof(CSNode *) * m_iArrayLength); + m_iArrayLength *= 2; + delete[] m_pHeap; + m_pHeap = temp; +} + +void CSndUList::insert_(int64_t ts, const CUDT *u) +{ + // increase the heap array size if necessary + if (m_iLastEntry == m_iArrayLength - 1) + realloc_(); + + insert_norealloc_(ts, u); +} + +void CSndUList::insert_norealloc_(int64_t ts, const CUDT *u) +{ + CSNode *n = u->m_pSNode; + + // do not insert repeated node + if (n->m_iHeapLoc >= 0) + return; + + SRT_ASSERT(m_iLastEntry < m_iArrayLength); + + m_iLastEntry++; + m_pHeap[m_iLastEntry] = n; + n->m_llTimeStamp_tk = ts; + + int q = m_iLastEntry; + int p = q; + while (p != 0) + { + p = (q - 1) >> 1; + if (m_pHeap[p]->m_llTimeStamp_tk <= m_pHeap[q]->m_llTimeStamp_tk) + break; + + swap(m_pHeap[p], m_pHeap[q]); + m_pHeap[q]->m_iHeapLoc = q; + q = p; + } + + n->m_iHeapLoc = q; + + // an earlier event has been inserted, wake up sending worker + if (n->m_iHeapLoc == 0) + m_pTimer->interrupt(); + + // first entry, activate the sending queue + if (0 == m_iLastEntry) + { + pthread_mutex_lock(m_pWindowLock); + pthread_cond_signal(m_pWindowCond); + pthread_mutex_unlock(m_pWindowLock); + } +} + +void CSndUList::remove_(const CUDT *u) +{ + CSNode *n = u->m_pSNode; + + if (n->m_iHeapLoc >= 0) + { + // remove the node from heap + m_pHeap[n->m_iHeapLoc] = m_pHeap[m_iLastEntry]; + m_iLastEntry--; + m_pHeap[n->m_iHeapLoc]->m_iHeapLoc = n->m_iHeapLoc; + + int q = n->m_iHeapLoc; + int p = q * 2 + 1; + while (p <= m_iLastEntry) + { + if ((p + 1 <= m_iLastEntry) && (m_pHeap[p]->m_llTimeStamp_tk > m_pHeap[p + 1]->m_llTimeStamp_tk)) + p++; + + if (m_pHeap[q]->m_llTimeStamp_tk > m_pHeap[p]->m_llTimeStamp_tk) + { + swap(m_pHeap[p], m_pHeap[q]); + m_pHeap[p]->m_iHeapLoc = p; + m_pHeap[q]->m_iHeapLoc = q; + + q = p; + p = q * 2 + 1; + } + else + break; + } + + n->m_iHeapLoc = -1; + } + + // the only event has been deleted, wake up immediately + if (0 == m_iLastEntry) + m_pTimer->interrupt(); +} + +// +CSndQueue::CSndQueue() + : m_WorkerThread() + , m_pSndUList(NULL) + , m_pChannel(NULL) + , m_pTimer(NULL) + , m_WindowLock() + , m_WindowCond() + , m_bClosing(false) +{ + pthread_cond_init(&m_WindowCond, NULL); + pthread_mutex_init(&m_WindowLock, NULL); +} + +CSndQueue::~CSndQueue() +{ + m_bClosing = true; + + if (m_pTimer != NULL) + { + m_pTimer->interrupt(); + } + + pthread_mutex_lock(&m_WindowLock); + pthread_cond_signal(&m_WindowCond); + pthread_mutex_unlock(&m_WindowLock); + if (!pthread_equal(m_WorkerThread, pthread_t())) + pthread_join(m_WorkerThread, NULL); + pthread_cond_destroy(&m_WindowCond); + pthread_mutex_destroy(&m_WindowLock); + + delete m_pSndUList; +} + +void CSndQueue::init(CChannel *c, CTimer *t) +{ + m_pChannel = c; + m_pTimer = t; + m_pSndUList = new CSndUList; + m_pSndUList->m_pWindowLock = &m_WindowLock; + m_pSndUList->m_pWindowCond = &m_WindowCond; + m_pSndUList->m_pTimer = m_pTimer; + + ThreadName tn("SRT:SndQ:worker"); + if (0 != pthread_create(&m_WorkerThread, NULL, CSndQueue::worker, this)) + { + m_WorkerThread = pthread_t(); + throw CUDTException(MJ_SYSTEMRES, MN_THREAD); + } +} + +#ifdef SRT_ENABLE_IPOPTS +int CSndQueue::getIpTTL() const { return m_pChannel ? m_pChannel->getIpTTL() : -1; } + +int CSndQueue::getIpToS() const { return m_pChannel ? m_pChannel->getIpToS() : -1; } +#endif + +void *CSndQueue::worker(void *param) +{ + CSndQueue *self = (CSndQueue *)param; + + THREAD_STATE_INIT("SRT:SndQ:worker"); + +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + CTimer::rdtsc(self->m_ullDbgTime); + self->m_ullDbgPeriod = uint64_t(5000000) * CTimer::getCPUFrequency(); + self->m_ullDbgTime += self->m_ullDbgPeriod; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + + while (!self->m_bClosing) + { + uint64_t next_time = self->m_pSndUList->getNextProcTime(); + +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lIteration++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + + if (next_time <= 0) + { +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lNotReadyTs++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + + // wait here if there is no sockets with data to be sent + THREAD_PAUSED(); + pthread_mutex_lock(&self->m_WindowLock); + if (!self->m_bClosing && (self->m_pSndUList->m_iLastEntry < 0)) + { + pthread_cond_wait(&self->m_WindowCond, &self->m_WindowLock); + +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lCondWait++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + } + THREAD_RESUMED(); + pthread_mutex_unlock(&self->m_WindowLock); + + continue; + } + + // wait until next processing time of the first socket on the list + uint64_t currtime; + CTimer::rdtsc(currtime); + +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + if (self->m_ullDbgTime <= currtime) + { + fprintf(stdout, + "SndQueue %lu slt:%lu nrp:%lu snt:%lu nrt:%lu ctw:%lu\n", + self->m_WorkerStats.lIteration, + self->m_WorkerStats.lSleepTo, + self->m_WorkerStats.lNotReadyPop, + self->m_WorkerStats.lSendTo, + self->m_WorkerStats.lNotReadyTs, + self->m_WorkerStats.lCondWait); + memset(&self->m_WorkerStats, 0, sizeof(self->m_WorkerStats)); + self->m_ullDbgTime = currtime + self->m_ullDbgPeriod; + } +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + + THREAD_PAUSED(); + if (currtime < next_time) + { + self->m_pTimer->sleepto(next_time); + +#if defined(HAI_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lSleepTo++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + } + THREAD_RESUMED(); + + // it is time to send the next pkt + sockaddr *addr; + CPacket pkt; + if (self->m_pSndUList->pop(addr, pkt) < 0) + { + continue; + +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lNotReadyPop++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + } + if (pkt.isControl()) + { + HLOGC(mglog.Debug, + log << self->CONID() << "chn:SENDING: " << MessageTypeStr(pkt.getType(), pkt.getExtendedType())); + } + else + { + HLOGC(dlog.Debug, + log << self->CONID() << "chn:SENDING SIZE " << pkt.getLength() << " SEQ: " << pkt.getSeqNo()); + } + self->m_pChannel->sendto(addr, pkt); + +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lSendTo++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + } + + THREAD_EXIT(); + return NULL; +} + +int CSndQueue::sendto(const sockaddr *addr, CPacket &packet) +{ + // send out the packet immediately (high priority), this is a control packet + m_pChannel->sendto(addr, packet); + return (int)packet.getLength(); +} + +// +CRcvUList::CRcvUList() + : m_pUList(NULL) + , m_pLast(NULL) +{ +} + +CRcvUList::~CRcvUList() {} + +void CRcvUList::insert(const CUDT *u) +{ + CRNode *n = u->m_pRNode; + CTimer::rdtsc(n->m_llTimeStamp_tk); + + if (NULL == m_pUList) + { + // empty list, insert as the single node + n->m_pPrev = n->m_pNext = NULL; + m_pLast = m_pUList = n; + + return; + } + + // always insert at the end for RcvUList + n->m_pPrev = m_pLast; + n->m_pNext = NULL; + m_pLast->m_pNext = n; + m_pLast = n; +} + +void CRcvUList::remove(const CUDT *u) +{ + CRNode *n = u->m_pRNode; + + if (!n->m_bOnList) + return; + + if (NULL == n->m_pPrev) + { + // n is the first node + m_pUList = n->m_pNext; + if (NULL == m_pUList) + m_pLast = NULL; + else + m_pUList->m_pPrev = NULL; + } + else + { + n->m_pPrev->m_pNext = n->m_pNext; + if (NULL == n->m_pNext) + { + // n is the last node + m_pLast = n->m_pPrev; + } + else + n->m_pNext->m_pPrev = n->m_pPrev; + } + + n->m_pNext = n->m_pPrev = NULL; +} + +void CRcvUList::update(const CUDT *u) +{ + CRNode *n = u->m_pRNode; + + if (!n->m_bOnList) + return; + + CTimer::rdtsc(n->m_llTimeStamp_tk); + + // if n is the last node, do not need to change + if (NULL == n->m_pNext) + return; + + if (NULL == n->m_pPrev) + { + m_pUList = n->m_pNext; + m_pUList->m_pPrev = NULL; + } + else + { + n->m_pPrev->m_pNext = n->m_pNext; + n->m_pNext->m_pPrev = n->m_pPrev; + } + + n->m_pPrev = m_pLast; + n->m_pNext = NULL; + m_pLast->m_pNext = n; + m_pLast = n; +} + +// +CHash::CHash() + : m_pBucket(NULL) + , m_iHashSize(0) +{ +} + +CHash::~CHash() +{ + for (int i = 0; i < m_iHashSize; ++i) + { + CBucket *b = m_pBucket[i]; + while (NULL != b) + { + CBucket *n = b->m_pNext; + delete b; + b = n; + } + } + + delete[] m_pBucket; +} + +void CHash::init(int size) +{ + m_pBucket = new CBucket *[size]; + + for (int i = 0; i < size; ++i) + m_pBucket[i] = NULL; + + m_iHashSize = size; +} + +CUDT *CHash::lookup(int32_t id) +{ + // simple hash function (% hash table size); suitable for socket descriptors + CBucket *b = m_pBucket[id % m_iHashSize]; + + while (NULL != b) + { + if (id == b->m_iID) + return b->m_pUDT; + b = b->m_pNext; + } + + return NULL; +} + +void CHash::insert(int32_t id, CUDT *u) +{ + CBucket *b = m_pBucket[id % m_iHashSize]; + + CBucket *n = new CBucket; + n->m_iID = id; + n->m_pUDT = u; + n->m_pNext = b; + + m_pBucket[id % m_iHashSize] = n; +} + +void CHash::remove(int32_t id) +{ + CBucket *b = m_pBucket[id % m_iHashSize]; + CBucket *p = NULL; + + while (NULL != b) + { + if (id == b->m_iID) + { + if (NULL == p) + m_pBucket[id % m_iHashSize] = b->m_pNext; + else + p->m_pNext = b->m_pNext; + + delete b; + + return; + } + + p = b; + b = b->m_pNext; + } +} + +// +CRendezvousQueue::CRendezvousQueue() + : m_lRendezvousID() + , m_RIDVectorLock() +{ + pthread_mutex_init(&m_RIDVectorLock, NULL); +} + +CRendezvousQueue::~CRendezvousQueue() +{ + pthread_mutex_destroy(&m_RIDVectorLock); + + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) + { + if (AF_INET == i->m_iIPversion) + delete (sockaddr_in *)i->m_pPeerAddr; + else + delete (sockaddr_in6 *)i->m_pPeerAddr; + } + + m_lRendezvousID.clear(); +} + +void CRendezvousQueue::insert(const SRTSOCKET &id, CUDT *u, int ipv, const sockaddr *addr, uint64_t ttl) +{ + CGuard vg(m_RIDVectorLock); + + CRL r; + r.m_iID = id; + r.m_pUDT = u; + r.m_iIPversion = ipv; + r.m_pPeerAddr = (AF_INET == ipv) ? (sockaddr *)new sockaddr_in : (sockaddr *)new sockaddr_in6; + memcpy(r.m_pPeerAddr, addr, (AF_INET == ipv) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + r.m_ullTTL = ttl; + + m_lRendezvousID.push_back(r); +} + +void CRendezvousQueue::remove(const SRTSOCKET &id, bool should_lock) +{ + CGuard vg(m_RIDVectorLock, should_lock); + + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) + { + if (i->m_iID == id) + { + if (AF_INET == i->m_iIPversion) + delete (sockaddr_in *)i->m_pPeerAddr; + else + delete (sockaddr_in6 *)i->m_pPeerAddr; + + m_lRendezvousID.erase(i); + + return; + } + } +} + +CUDT *CRendezvousQueue::retrieve(const sockaddr *addr, ref_t r_id) +{ + CGuard vg(m_RIDVectorLock); + SRTSOCKET &id = *r_id; + + // TODO: optimize search + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) + { + if (CIPAddress::ipcmp(addr, i->m_pPeerAddr, i->m_iIPversion) && ((id == 0) || (id == i->m_iID))) + { + id = i->m_iID; + return i->m_pUDT; + } + } + + return NULL; +} + +void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket &response) +{ + CGuard vg(m_RIDVectorLock); + + if (m_lRendezvousID.empty()) + return; + + HLOGC(mglog.Debug, + log << "updateConnStatus: updating after getting pkt id=" << response.m_iID + << " status: " << ConnectStatusStr(cst)); + +#if ENABLE_HEAVY_LOGGING + int debug_nupd = 0; + int debug_nrun = 0; + int debug_nfail = 0; +#endif + + for (list::iterator i = m_lRendezvousID.begin(), i_next = i; i != m_lRendezvousID.end(); i = i_next) + { + ++i_next; + // NOTE: This is a SAFE LOOP. + // Incrementation will be done at the end, after the processing did not + // REMOVE the currently processed element. When the element was removed, + // the iterator value for the next iteration will be taken from erase()'s result. + + // RST_AGAIN happens in case when the last attempt to read a packet from the UDP + // socket has read nothing. In this case it would be a repeated update, while + // still waiting for a response from the peer. When we have any other state here + // (most expectably CONN_CONTINUE or CONN_RENDEZVOUS, which means that a packet has + // just arrived in this iteration), do the update immetiately (in SRT this also + // involves additional incoming data interpretation, which wasn't the case in UDT). + + // Use "slow" cyclic responding in case when + // - RST_AGAIN (no packet was received for whichever socket) + // - a packet was received, but not for THIS socket + if (rst == RST_AGAIN || i->m_iID != response.m_iID) + { + // If no packet has been received from the peer, + // avoid sending too many requests, at most 1 request per 250ms + const uint64_t then = i->m_pUDT->m_llLastReqTime; + const uint64_t now = CTimer::getTime(); + const bool nowstime = (now - then) > 250000; + HLOGC(mglog.Debug, + log << "RID:%" << i->m_iID << " then=" << then << " now=" << now << " passed=" << (now - then) + << "<=> 250000 -- now's " << (nowstime ? "" : "NOT ") << "the time"); + + if (!nowstime) + continue; + } + + HLOGC(mglog.Debug, log << "RID:%" << i->m_iID << " cst=" << ConnectStatusStr(cst) << " -- sending update NOW."); + +#if ENABLE_HEAVY_LOGGING + ++debug_nrun; +#endif + + // XXX This looks like a loop that rolls in infinity without any sleeps + // inside and makes it once per about 50 calls send a hs conclusion + // for a randomly sampled rendezvous ID of a socket out of the list. + // Ok, probably the rendezvous ID should be just one so not much to + // sample from, but if so, why the container? + // + // This must be somehow fixed! + // + // Maybe the time should be simply checked once and the whole loop not + // done when "it's not the time"? + if (CTimer::getTime() >= i->m_ullTTL) + { + HLOGC(mglog.Debug, + log << "RendezvousQueue: EXPIRED (" << (i->m_ullTTL ? "enforced on FAILURE" : "passed TTL") + << ". removing from queue"); + // connection timer expired, acknowledge app via epoll + i->m_pUDT->m_bConnecting = false; + CUDT::s_UDTUnited.m_EPoll.update_events(i->m_iID, i->m_pUDT->m_sPollID, UDT_EPOLL_ERR, true); + /* + * Setting m_bConnecting to false but keeping socket in rendezvous queue is not a good idea. + * Next CUDT::close will not remove it from rendezvous queue (because !m_bConnecting) + * and may crash here on next pass. + */ + if (AF_INET == i->m_iIPversion) + delete (sockaddr_in *)i->m_pPeerAddr; + else + delete (sockaddr_in6 *)i->m_pPeerAddr; + + // i_next was preincremented, but this is guaranteed to point to + // the element next to erased one. + i_next = m_lRendezvousID.erase(i); + continue; + } + + // This queue is used only in case of Async mode (rendezvous or caller-listener). + // Synchronous connection requests are handled in startConnect() completely. + if (!i->m_pUDT->m_bSynRecving) + { +#if ENABLE_HEAVY_LOGGING + ++debug_nupd; +#endif + // IMPORTANT INFORMATION concerning changes towards UDT legacy. + // In the UDT code there was no attempt to interpret any incoming data. + // All data from the incoming packet were considered to be already deployed into + // m_ConnRes field, and m_ConnReq field was considered at this time accordingly updated. + // Therefore this procedure did only one thing: craft a new handshake packet and send it. + // In SRT this may also interpret extra data (extensions in case when Agent is Responder) + // and the `response` packet may sometimes contain no data. Therefore the passed `rst` + // must be checked to distinguish the call by periodic update (RST_AGAIN) from a call + // due to have received the packet (RST_OK). + // + // In the below call, only the underlying `processRendezvous` function will be attempting + // to interpret these data (for caller-listener this was already done by `processConnectRequest` + // before calling this function), and it checks for the data presence. + + EReadStatus read_st = rst; + EConnectStatus conn_st = cst; + + if (i->m_iID != response.m_iID) + { + read_st = RST_AGAIN; + conn_st = CONN_AGAIN; + } + + if (!i->m_pUDT->processAsyncConnectRequest(read_st, conn_st, response, i->m_pPeerAddr)) + { + // cst == CONN_REJECT can only be result of worker_ProcessAddressedPacket and + // its already set in this case. + LOGC(mglog.Error, log << "RendezvousQueue: processAsyncConnectRequest FAILED. Setting TTL as EXPIRED."); + i->m_pUDT->sendCtrl(UMSG_SHUTDOWN); + i->m_ullTTL = 0; // Make it expire right now, will be picked up at the next iteration +#if ENABLE_HEAVY_LOGGING + ++debug_nfail; +#endif + } + + // NOTE: safe loop, the incrementation was done before the loop body, + // so the `i' node can be safely deleted. Just the body must end here. + continue; + } + } + + HLOGC(mglog.Debug, + log << "updateConnStatus: " << debug_nupd << "/" << debug_nrun << " sockets updated (" + << (debug_nrun - debug_nupd) << " useless). REMOVED " << debug_nfail << " sockets."); +} + +// +CRcvQueue::CRcvQueue() + : m_WorkerThread() + , m_UnitQueue() + , m_pRcvUList(NULL) + , m_pHash(NULL) + , m_pChannel(NULL) + , m_pTimer(NULL) + , m_iPayloadSize() + , m_bClosing(false) + , m_LSLock() + , m_pListener(NULL) + , m_pRendezvousQueue(NULL) + , m_vNewEntry() + , m_IDLock() + , m_mBuffer() + , m_PassLock() + , m_PassCond() +{ + pthread_mutex_init(&m_PassLock, NULL); + pthread_cond_init(&m_PassCond, NULL); + pthread_mutex_init(&m_LSLock, NULL); + pthread_mutex_init(&m_IDLock, NULL); +} + +CRcvQueue::~CRcvQueue() +{ + m_bClosing = true; + if (!pthread_equal(m_WorkerThread, pthread_t())) + pthread_join(m_WorkerThread, NULL); + pthread_mutex_destroy(&m_PassLock); + pthread_cond_destroy(&m_PassCond); + pthread_mutex_destroy(&m_LSLock); + pthread_mutex_destroy(&m_IDLock); + + delete m_pRcvUList; + delete m_pHash; + delete m_pRendezvousQueue; + + // remove all queued messages + for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) + { + while (!i->second.empty()) + { + CPacket *pkt = i->second.front(); + delete[] pkt->m_pcData; + delete pkt; + i->second.pop(); + } + } +} + +void CRcvQueue::init(int qsize, int payload, int version, int hsize, CChannel *cc, CTimer *t) +{ + m_iPayloadSize = payload; + + m_UnitQueue.init(qsize, payload, version); + + m_pHash = new CHash; + m_pHash->init(hsize); + + m_pChannel = cc; + m_pTimer = t; + + m_pRcvUList = new CRcvUList; + m_pRendezvousQueue = new CRendezvousQueue; + + ThreadName tn("SRT:RcvQ:worker"); + if (0 != pthread_create(&m_WorkerThread, NULL, CRcvQueue::worker, this)) + { + m_WorkerThread = pthread_t(); + throw CUDTException(MJ_SYSTEMRES, MN_THREAD); + } +} + +void *CRcvQueue::worker(void *param) +{ + CRcvQueue * self = (CRcvQueue *)param; + sockaddr_any sa(self->m_UnitQueue.getIPversion()); + int32_t id = 0; + + THREAD_STATE_INIT("SRT:RcvQ:worker"); + + CUnit * unit = 0; + EConnectStatus cst = CONN_AGAIN; + while (!self->m_bClosing) + { + bool have_received = false; + EReadStatus rst = self->worker_RetrieveUnit(Ref(id), Ref(unit), &sa); + if (rst == RST_OK) + { + if (id < 0) + { + // User error on peer. May log something, but generally can only ignore it. + // XXX Think maybe about sending some "connection rejection response". + HLOGC(mglog.Debug, + log << self->CONID() << "RECEIVED negative socket id '" << id + << "', rejecting (POSSIBLE ATTACK)"); + continue; + } + + // NOTE: cst state is being changed here. + // This state should be maintained through any next failed calls to worker_RetrieveUnit. + // Any error switches this to rejection, just for a case. + + // Note to rendezvous connection. This can accept: + // - ID == 0 - take the first waiting rendezvous socket + // - ID > 0 - find the rendezvous socket that has this ID. + if (id == 0) + { + // ID 0 is for connection request, which should be passed to the listening socket or rendezvous sockets + cst = self->worker_ProcessConnectionRequest(unit, &sa); + } + else + { + // Otherwise ID is expected to be associated with: + // - an enqueued rendezvous socket + // - a socket connected to a peer + cst = self->worker_ProcessAddressedPacket(id, unit, &sa); + // CAN RETURN CONN_REJECT, but m_RejectReason is already set + } + HLOGC(mglog.Debug, log << self->CONID() << "worker: result for the unit: " << ConnectStatusStr(cst)); + if (cst == CONN_AGAIN) + { + HLOGC(mglog.Debug, log << self->CONID() << "worker: packet not dispatched, continuing reading."); + continue; + } + have_received = true; + } + else if (rst == RST_ERROR) + { + // According to the description by CChannel::recvfrom, this can be either of: + // - IPE: all errors except EBADF + // - socket was closed in the meantime by another thread: EBADF + // If EBADF, then it's expected that the "closing" state is also set. + // Check that just to report possible errors, but interrupt the loop anyway. + if (self->m_bClosing) + { + HLOGC(mglog.Debug, + log << self->CONID() << "CChannel reported error, but Queue is closing - INTERRUPTING worker."); + } + else + { + LOGC(mglog.Fatal, + log << self->CONID() + << "CChannel reported ERROR DURING TRANSMISSION - IPE. INTERRUPTING worker anyway."); + } + cst = CONN_REJECT; + break; + } + // OTHERWISE: this is an "AGAIN" situation. No data was read, but the process should continue. + + // take care of the timing event for all UDT sockets + uint64_t currtime_tk; + CTimer::rdtsc(currtime_tk); + + CRNode * ul = self->m_pRcvUList->m_pUList; + const uint64_t ctime_tk = currtime_tk - CUDT::COMM_SYN_INTERVAL_US * CTimer::getCPUFrequency(); + while ((NULL != ul) && (ul->m_llTimeStamp_tk < ctime_tk)) + { + CUDT *u = ul->m_pUDT; + + if (u->m_bConnected && !u->m_bBroken && !u->m_bClosing) + { + u->checkTimers(); + self->m_pRcvUList->update(u); + } + else + { + HLOGC(mglog.Debug, + log << CUDTUnited::CONID(u->m_SocketID) << " SOCKET broken, REMOVING FROM RCV QUEUE/MAP."); + // the socket must be removed from Hash table first, then RcvUList + self->m_pHash->remove(u->m_SocketID); + self->m_pRcvUList->remove(u); + u->m_pRNode->m_bOnList = false; + } + + ul = self->m_pRcvUList->m_pUList; + } + + if (have_received) + { + HLOGC(mglog.Debug, + log << "worker: RECEIVED PACKET --> updateConnStatus. cst=" << ConnectStatusStr(cst) << " id=" << id + << " pkt-payload-size=" << unit->m_Packet.getLength()); + } + + // Check connection requests status for all sockets in the RendezvousQueue. + // Pass the connection status from the last call of: + // worker_ProcessAddressedPacket ---> + // worker_TryAsyncRend_OrStore ---> + // CUDT::processAsyncConnectResponse ---> + // CUDT::processConnectResponse + self->m_pRendezvousQueue->updateConnStatus(rst, cst, unit->m_Packet); + + // XXX updateConnStatus may have removed the connector from the list, + // however there's still m_mBuffer in CRcvQueue for that socket to care about. + } + + THREAD_EXIT(); + return NULL; +} + +#if ENABLE_LOGGING +static string PacketInfo(const CPacket &pkt) +{ + ostringstream os; + os << "TARGET=" << pkt.m_iID << " "; + + if (pkt.isControl()) + { + os << "CONTROL: " << MessageTypeStr(pkt.getType(), pkt.getExtendedType()) << " size=" << pkt.getLength(); + } + else + { + // It's hard to extract the information about peer's supported rexmit flag. + // This is only a log, nothing crucial, so we can risk displaying incorrect message number. + // Declaring that the peer supports rexmit flag cuts off the highest bit from + // the displayed number. + os << "DATA: msg=" << pkt.getMsgSeq(true) << " seq=" << pkt.getSeqNo() << " size=" << pkt.getLength() + << " flags: " << PacketMessageFlagStr(pkt.m_iMsgNo); + } + + return os.str(); +} +#endif + +EReadStatus CRcvQueue::worker_RetrieveUnit(ref_t r_id, ref_t r_unit, sockaddr *addr) +{ +#if !USE_BUSY_WAITING + // This might be not really necessary, and probably + // not good for extensive bidirectional communication. + m_pTimer->tick(); +#endif + + // check waiting list, if new socket, insert it to the list + while (ifNewEntry()) + { + CUDT *ne = getNewEntry(); + if (ne) + { + HLOGC(mglog.Debug, + log << CUDTUnited::CONID(ne->m_SocketID) + << " SOCKET pending for connection - ADDING TO RCV QUEUE/MAP"); + m_pRcvUList->insert(ne); + m_pHash->insert(ne->m_SocketID, ne); + } + } + // find next available slot for incoming packet + *r_unit = m_UnitQueue.getNextAvailUnit(); + if (!*r_unit) + { + // no space, skip this packet + CPacket temp; + temp.m_pcData = new char[m_iPayloadSize]; + temp.setLength(m_iPayloadSize); + THREAD_PAUSED(); + EReadStatus rst = m_pChannel->recvfrom(addr, temp); + THREAD_RESUMED(); +#if ENABLE_LOGGING + LOGC(mglog.Error, log << CONID() << "LOCAL STORAGE DEPLETED. Dropping 1 packet: " << PacketInfo(temp)); +#endif + delete[] temp.m_pcData; + + // Be transparent for RST_ERROR, but ignore the correct + // data read and fake that the packet was dropped. + return rst == RST_ERROR ? RST_ERROR : RST_AGAIN; + } + + r_unit->m_Packet.setLength(m_iPayloadSize); + + // reading next incoming packet, recvfrom returns -1 is nothing has been received + THREAD_PAUSED(); + EReadStatus rst = m_pChannel->recvfrom(addr, r_unit->m_Packet); + THREAD_RESUMED(); + + if (rst == RST_OK) + { + *r_id = r_unit->m_Packet.m_iID; + HLOGC(mglog.Debug, + log << "INCOMING PACKET: BOUND=" << SockaddrToString(m_pChannel->bindAddress()) << " " + << PacketInfo(r_unit->m_Packet)); + } + return rst; +} + +EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit *unit, const sockaddr *addr) +{ + HLOGC(mglog.Debug, + log << "Got sockID=0 from " << SockaddrToString(addr) + << " - trying to resolve it as a connection request..."); + // Introduced protection because it may potentially happen + // that another thread could have closed the socket at + // the same time and inject a bug between checking the + // pointer for NULL and using it. + SRT_REJECT_REASON listener_ret = SRT_REJ_UNKNOWN; + bool have_listener = false; + { + CGuard cg(m_LSLock); + if (m_pListener) + { + LOGC(mglog.Note, + log << "PASSING request from: " << SockaddrToString(addr) << " to agent:" << m_pListener->socketID()); + listener_ret = m_pListener->processConnectRequest(addr, unit->m_Packet); + + // This function does return a code, but it's hard to say as to whether + // anything can be done about it. In case when it's stated possible, the + // listener will try to send some rejection response to the caller, but + // that's already done inside this function. So it's only used for + // displaying the error in logs. + + have_listener = true; + } + } + + // NOTE: Rendezvous sockets do bind(), but not listen(). It means that the socket is + // ready to accept connection requests, but they are not being redirected to the listener + // socket, as this is not a listener socket at all. This goes then HERE. + + if (have_listener) // That is, the above block with m_pListener->processConnectRequest was executed + { + LOGC(mglog.Note, + log << CONID() << "Listener managed the connection request from: " << SockaddrToString(addr) + << " result:" << RequestTypeStr(UDTRequestType(listener_ret))); + return listener_ret == SRT_REJ_UNKNOWN ? CONN_CONTINUE : CONN_REJECT; + } + + // If there's no listener waiting for the packet, just store it into the queue. + return worker_TryAsyncRend_OrStore(0, unit, addr); // 0 id because the packet came in with that very ID. +} + +EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit *unit, const sockaddr *addr) +{ + CUDT *u = m_pHash->lookup(id); + if (!u) + { + // Pass this to either async rendezvous connection, + // or store the packet in the queue. + HLOGC(mglog.Debug, log << "worker_ProcessAddressedPacket: resending to target socket %" << id); + return worker_TryAsyncRend_OrStore(id, unit, addr); + } + + // Found associated CUDT - process this as control or data packet + // addressed to an associated socket. + if (!CIPAddress::ipcmp(addr, u->m_pPeerAddr, u->m_iIPversion)) + { + HLOGC(mglog.Debug, + log << CONID() << "Packet for SID=" << id << " asoc with " << SockaddrToString(u->m_pPeerAddr) + << " received from " << SockaddrToString(addr) << " (CONSIDERED ATTACK ATTEMPT)"); + // This came not from the address that is the peer associated + // with the socket. Ignore it. + return CONN_AGAIN; + } + + if (!u->m_bConnected || u->m_bBroken || u->m_bClosing) + { + u->m_RejectReason = SRT_REJ_CLOSE; + // The socket is currently in the process of being disconnected + // or destroyed. Ignore. + // XXX send UMSG_SHUTDOWN in this case? + // XXX May it require mutex protection? + return CONN_REJECT; + } + + if (unit->m_Packet.isControl()) + u->processCtrl(unit->m_Packet); + else + u->processData(unit); + + u->checkTimers(); + m_pRcvUList->update(u); + + return CONN_CONTINUE; +} + +// This function responds to the fact that a packet has come +// for a socket that does not expect to receive a normal connection +// request. This can be then: +// - a normal packet of whatever kind, just to be processed by the message loop +// - a rendezvous connection +// This function then tries to manage the packet as a rendezvous connection +// request in ASYNC mode; when this is not applicable, it stores the packet +// in the "receiving queue" so that it will be picked up in the "main" thread. +EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit *unit, const sockaddr *addr) +{ + // This 'retrieve' requires that 'id' be either one of those + // stored in the rendezvous queue (see CRcvQueue::registerConnector) + // or simply 0, but then at least the address must match one of these. + // If the id was 0, it will be set to the actual socket ID of the returned CUDT. + CUDT *u = m_pRendezvousQueue->retrieve(addr, Ref(id)); + if (!u) + { + // this socket is then completely unknown to the system. + // Note that this situation may also happen at a very unfortunate + // coincidence that the socket is already bound, but the registerConnector() + // has not yet started. In case of rendezvous this may mean that the other + // side just started sending its handshake packets, the local side has already + // run the CRcvQueue::worker thread, and this worker thread is trying to dispatch + // the handshake packet too early, before the dispatcher has a chance to see + // this socket registerred in the RendezvousQueue, which causes the packet unable + // to be dispatched. Therefore simply treat every "out of band" packet (with socket + // not belonging to the connection and not registered as rendezvous) as "possible + // attach" and ignore it. This also should better protect the rendezvous socket + // against a rogue connector. + if (id == 0) + { + HLOGC(mglog.Debug, + log << CONID() << "AsyncOrRND: no sockets expect connection from " << SockaddrToString(addr) + << " - POSSIBLE ATTACK, ignore packet"); + } + else + { + HLOGC(mglog.Debug, + log << CONID() << "AsyncOrRND: no sockets expect socket " << id << " from " << SockaddrToString(addr) + << " - POSSIBLE ATTACK, ignore packet"); + } + return CONN_AGAIN; // This means that the packet should be ignored. + } + + // asynchronous connect: call connect here + // otherwise wait for the UDT socket to retrieve this packet + if (!u->m_bSynRecving) + { + HLOGC(mglog.Debug, log << "AsyncOrRND: packet RESOLVED TO ID=" << id << " -- continuing as ASYNC CONNECT"); + // This is practically same as processConnectResponse, just this applies + // appropriate mutex lock - which can't be done here because it's intentionally private. + // OTOH it can't be applied to processConnectResponse because the synchronous + // call to this method applies the lock by itself, and same-thread-double-locking is nonportable (crashable). + EConnectStatus cst = u->processAsyncConnectResponse(unit->m_Packet); + + if (cst == CONN_CONFUSED) + { + LOGC(mglog.Warn, log << "AsyncOrRND: PACKET NOT HANDSHAKE - re-requesting handshake from peer"); + storePkt(id, unit->m_Packet.clone()); + if (!u->processAsyncConnectRequest(RST_AGAIN, CONN_CONTINUE, unit->m_Packet, u->m_pPeerAddr)) + { + // Reuse previous behavior to reject a packet + cst = CONN_REJECT; + } + else + { + cst = CONN_CONTINUE; + } + } + + // It might be that this is a data packet, which has turned the connection + // into "connected" state, removed the connector (so since now every next packet + // will land directly in the queue), but this data packet shall still be delivered. + if (cst == CONN_ACCEPT && !unit->m_Packet.isControl()) + { + // The process as called through processAsyncConnectResponse() should have put the + // socket into the pending queue for pending connection (don't ask me, this is so). + // This pending queue is being purged every time in the beginning of this loop, so + // currently the socket is in the pending queue, but not yet in the connection queue. + // It will be done at the next iteration of the reading loop, but it will be too late, + // we have a pending data packet now and we must either dispatch it to an already connected + // socket or disregard it, and rather prefer the former. So do this transformation now + // that we KNOW (by the cst == CONN_ACCEPT result) that the socket should be inserted + // into the pending anteroom. + + CUDT *ne = getNewEntry(); // This function actuall removes the entry and returns it. + // This **should** now always return a non-null value, but check it first + // because if this accidentally isn't true, the call to worker_ProcessAddressedPacket will + // result in redirecting it to here and so on until the call stack overflow. In case of + // this "accident" simply disregard the packet from any further processing, it will be later + // loss-recovered. + // XXX (Probably the old contents of UDT's CRcvQueue::worker should be shaped a little bit + // differently throughout the functions). + if (ne) + { + HLOGC(mglog.Debug, + log << CUDTUnited::CONID(ne->m_SocketID) + << " SOCKET pending for connection - ADDING TO RCV QUEUE/MAP"); + m_pRcvUList->insert(ne); + m_pHash->insert(ne->m_SocketID, ne); + + // The current situation is that this has passed processAsyncConnectResponse, but actually + // this packet *SHOULD HAVE BEEN* handled by worker_ProcessAddressedPacket, however the + // connection state wasn't completed at the moment when dispatching this packet. This has + // been now completed inside the call to processAsyncConnectResponse, but this is still a + // data packet that should have expected the connection to be already established. Therefore + // redirect it once again into worker_ProcessAddressedPacket here. + + HLOGC(mglog.Debug, + log << "AsyncOrRND: packet SWITCHED TO CONNECTED with ID=" << id + << " -- passing to worker_ProcessAddressedPacket"); + + // Theoretically we should check if m_pHash->lookup(ne->m_SocketID) returns 'ne', but this + // has been just added to m_pHash, so the check would be extremely paranoid here. + cst = worker_ProcessAddressedPacket(id, unit, addr); + if (cst == CONN_REJECT) + return cst; + return CONN_ACCEPT; // this function usually will return CONN_CONTINUE, which doesn't represent current + // situation. + } + else + { + LOGC(mglog.Error, + log << "IPE: AsyncOrRND: packet SWITCHED TO CONNECTED, but ID=" << id + << " is still not present in the socket ID dispatch hash - DISREGARDING"); + } + } + return cst; + } + HLOGC(mglog.Debug, + log << "AsyncOrRND: packet RESOLVED TO ID=" << id << " -- continuing through CENTRAL PACKET QUEUE"); + // This is where also the packets for rendezvous connection will be landing, + // in case of a synchronous connection. + storePkt(id, unit->m_Packet.clone()); + + return CONN_CONTINUE; +} + +int CRcvQueue::recvfrom(int32_t id, ref_t r_packet) +{ + CGuard bufferlock(m_PassLock); + CPacket &packet = *r_packet; + + map >::iterator i = m_mBuffer.find(id); + + if (i == m_mBuffer.end()) + { + CTimer::condTimedWaitUS(&m_PassCond, &m_PassLock, 1000000); + + i = m_mBuffer.find(id); + if (i == m_mBuffer.end()) + { + packet.setLength(-1); + return -1; + } + } + + // retrieve the earliest packet + CPacket *newpkt = i->second.front(); + + if (packet.getLength() < newpkt->getLength()) + { + packet.setLength(-1); + return -1; + } + + // copy packet content + // XXX Check if this wouldn't be better done by providing + // copy constructor for DynamicStruct. + // XXX Another thing: this looks wasteful. This expects an already + // allocated memory on the packet, this thing gets the packet, + // copies it into the passed packet and then the source packet + // gets deleted. Why not simply return the originally stored packet, + // without copying, allocation and deallocation? + memcpy(packet.m_nHeader, newpkt->m_nHeader, CPacket::HDR_SIZE); + memcpy(packet.m_pcData, newpkt->m_pcData, newpkt->getLength()); + packet.setLength(newpkt->getLength()); + + delete[] newpkt->m_pcData; + delete newpkt; + + // remove this message from queue, + // if no more messages left for this socket, release its data structure + i->second.pop(); + if (i->second.empty()) + m_mBuffer.erase(i); + + return (int)packet.getLength(); +} + +int CRcvQueue::setListener(CUDT *u) +{ + CGuard lslock(m_LSLock); + + if (NULL != m_pListener) + return -1; + + m_pListener = u; + return 0; +} + +void CRcvQueue::removeListener(const CUDT *u) +{ + CGuard lslock(m_LSLock); + + if (u == m_pListener) + m_pListener = NULL; +} + +void CRcvQueue::registerConnector(const SRTSOCKET &id, CUDT *u, int ipv, const sockaddr *addr, uint64_t ttl) +{ + HLOGC(mglog.Debug, + log << "registerConnector: adding %" << id << " addr=" << SockaddrToString(addr) << " TTL=" << ttl); + m_pRendezvousQueue->insert(id, u, ipv, addr, ttl); +} + +void CRcvQueue::removeConnector(const SRTSOCKET &id, bool should_lock) +{ + HLOGC(mglog.Debug, log << "removeConnector: removing %" << id); + m_pRendezvousQueue->remove(id, should_lock); + + CGuard bufferlock(m_PassLock); + + map >::iterator i = m_mBuffer.find(id); + if (i != m_mBuffer.end()) + { + HLOGC(mglog.Debug, + log << "removeConnector: ... and its packet queue with " << i->second.size() << " packets collected"); + while (!i->second.empty()) + { + delete[] i->second.front()->m_pcData; + delete i->second.front(); + i->second.pop(); + } + m_mBuffer.erase(i); + } +} + +void CRcvQueue::setNewEntry(CUDT *u) +{ + HLOGC(mglog.Debug, log << CUDTUnited::CONID(u->m_SocketID) << "setting socket PENDING FOR CONNECTION"); + CGuard listguard(m_IDLock); + m_vNewEntry.push_back(u); +} + +bool CRcvQueue::ifNewEntry() { return !(m_vNewEntry.empty()); } + +CUDT *CRcvQueue::getNewEntry() +{ + CGuard listguard(m_IDLock); + + if (m_vNewEntry.empty()) + return NULL; + + CUDT *u = (CUDT *)*(m_vNewEntry.begin()); + m_vNewEntry.erase(m_vNewEntry.begin()); + + return u; +} + +void CRcvQueue::storePkt(int32_t id, CPacket *pkt) +{ + CGuard bufferlock(m_PassLock); + + map >::iterator i = m_mBuffer.find(id); + + if (i == m_mBuffer.end()) + { + m_mBuffer[id].push(pkt); + pthread_cond_signal(&m_PassCond); + } + else + { + // avoid storing too many packets, in case of malfunction or attack + if (i->second.size() > 16) + return; + + i->second.push(pkt); + } +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/queue.h b/trunk/3rdparty/srt-1-fit/srtcore/queue.h new file mode 100644 index 000000000..6b93bf6c4 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/queue.h @@ -0,0 +1,548 @@ +/* + * 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 01/12/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + + +#ifndef __UDT_QUEUE_H__ +#define __UDT_QUEUE_H__ + +#include "channel.h" +#include "common.h" +#include "packet.h" +#include "netinet_any.h" +#include "utilities.h" +#include +#include +#include +#include + +class CUDT; + +struct CUnit +{ + CPacket m_Packet; // packet + enum Flag { FREE = 0, GOOD = 1, PASSACK = 2, DROPPED = 3 }; + Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped +}; + +class CUnitQueue +{ + +public: + + CUnitQueue(); + ~CUnitQueue(); + +public: // Storage size operations + + /// Initialize the unit queue. + /// @param [in] size queue size + /// @param [in] mss maximum segment size + /// @param [in] version IP version + /// @return 0: success, -1: failure. + + int init(int size, int mss, int version); + + /// Increase (double) the unit queue size. + /// @return 0: success, -1: failure. + + int increase(); + + /// Decrease (halve) the unit queue size. + /// @return 0: success, -1: failure. + + int shrink(); + +public: // Operations on units + + /// find an available unit for incoming packet. + /// @return Pointer to the available unit, NULL if not found. + + CUnit* getNextAvailUnit(); + + + void makeUnitFree(CUnit * unit); + + void makeUnitGood(CUnit * unit); + +public: + + inline int getIPversion() const { return m_iIPversion; } + + +private: + struct CQEntry + { + CUnit* m_pUnit; // unit queue + char* m_pBuffer; // data buffer + int m_iSize; // size of each queue + + CQEntry* m_pNext; + } + *m_pQEntry, // pointer to the first unit queue + *m_pCurrQueue, // pointer to the current available queue + *m_pLastQueue; // pointer to the last unit queue + + CUnit* m_pAvailUnit; // recent available unit + + int m_iSize; // total size of the unit queue, in number of packets + int m_iCount; // total number of valid packets in the queue + + int m_iMSS; // unit buffer size + int m_iIPversion; // IP version + +private: + CUnitQueue(const CUnitQueue&); + CUnitQueue& operator=(const CUnitQueue&); +}; + +struct CSNode +{ + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + uint64_t m_llTimeStamp_tk; // Time Stamp + + int m_iHeapLoc; // location on the heap, -1 means not on the heap +}; + +class CSndUList +{ +friend class CSndQueue; + +public: + CSndUList(); + ~CSndUList(); + +public: + + enum EReschedule { DONT_RESCHEDULE = 0, DO_RESCHEDULE = 1 }; + + static EReschedule rescheduleIf(bool cond) { return cond ? DO_RESCHEDULE : DONT_RESCHEDULE; } + + /// Update the timestamp of the UDT instance on the list. + /// @param [in] u pointer to the UDT instance + /// @param [in] reschedule if the timestamp should be rescheduled + + void update(const CUDT* u, EReschedule reschedule); + + /// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. + /// @param [out] addr destination address of the next packet + /// @param [out] pkt the next packet to be sent + /// @return 1 if successfully retrieved, -1 if no packet found. + + int pop(sockaddr*& addr, CPacket& pkt); + + /// Remove UDT instance from the list. + /// @param [in] u pointer to the UDT instance + + void remove(const CUDT* u); + + /// Retrieve the next scheduled processing time. + /// @return Scheduled processing time of the first UDT socket in the list. + + uint64_t getNextProcTime(); + +private: + + /// Doubles the size of the list. + /// + void realloc_(); + + /// Insert a new UDT instance into the list with realloc if required. + /// + /// @param [in] ts time stamp: next processing time + /// @param [in] u pointer to the UDT instance + void insert_(int64_t ts, const CUDT* u); + + /// Insert a new UDT instance into the list without realloc. + /// Should be called if there is a gauranteed space for the element. + /// + /// @param [in] ts time stamp: next processing time + /// @param [in] u pointer to the UDT instance + void insert_norealloc_(int64_t ts, const CUDT* u); + + void remove_(const CUDT* u); + +private: + CSNode** m_pHeap; // The heap array + int m_iArrayLength; // physical length of the array + int m_iLastEntry; // position of last entry on the heap array + + pthread_mutex_t m_ListLock; + + pthread_mutex_t* m_pWindowLock; + pthread_cond_t* m_pWindowCond; + + CTimer* m_pTimer; + +private: + CSndUList(const CSndUList&); + CSndUList& operator=(const CSndUList&); +}; + +struct CRNode +{ + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + uint64_t m_llTimeStamp_tk; // Time Stamp + + CRNode* m_pPrev; // previous link + CRNode* m_pNext; // next link + + bool m_bOnList; // if the node is already on the list +}; + +class CRcvUList +{ +public: + CRcvUList(); + ~CRcvUList(); + +public: + + /// Insert a new UDT instance to the list. + /// @param [in] u pointer to the UDT instance + + void insert(const CUDT* u); + + /// Remove the UDT instance from the list. + /// @param [in] u pointer to the UDT instance + + void remove(const CUDT* u); + + /// Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing. + /// @param [in] u pointer to the UDT instance + + void update(const CUDT* u); + +public: + CRNode* m_pUList; // the head node + +private: + CRNode* m_pLast; // the last node + +private: + CRcvUList(const CRcvUList&); + CRcvUList& operator=(const CRcvUList&); +}; + +class CHash +{ +public: + CHash(); + ~CHash(); + +public: + + /// Initialize the hash table. + /// @param [in] size hash table size + + void init(int size); + + /// Look for a UDT instance from the hash table. + /// @param [in] id socket ID + /// @return Pointer to a UDT instance, or NULL if not found. + + CUDT* lookup(int32_t id); + + /// Insert an entry to the hash table. + /// @param [in] id socket ID + /// @param [in] u pointer to the UDT instance + + void insert(int32_t id, CUDT* u); + + /// Remove an entry from the hash table. + /// @param [in] id socket ID + + void remove(int32_t id); + +private: + struct CBucket + { + int32_t m_iID; // Socket ID + CUDT* m_pUDT; // Socket instance + + CBucket* m_pNext; // next bucket + } **m_pBucket; // list of buckets (the hash table) + + int m_iHashSize; // size of hash table + +private: + CHash(const CHash&); + CHash& operator=(const CHash&); +}; + +class CRendezvousQueue +{ +public: + CRendezvousQueue(); + ~CRendezvousQueue(); + +public: + void insert(const SRTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl); + + // The should_lock parameter is given here to state as to whether + // the lock should be applied here. If called from some internals + // and the lock IS ALREADY APPLIED, use false here to prevent + // double locking and deadlock in result. + void remove(const SRTSOCKET& id, bool should_lock); + CUDT* retrieve(const sockaddr* addr, ref_t id); + + void updateConnStatus(EReadStatus rst, EConnectStatus, const CPacket& response); + +private: + struct CRL + { + SRTSOCKET m_iID; // UDT socket ID (self) + CUDT* m_pUDT; // UDT instance + int m_iIPversion; // IP version + sockaddr* m_pPeerAddr; // UDT sonnection peer address + uint64_t m_ullTTL; // the time that this request expires + }; + std::list m_lRendezvousID; // The sockets currently in rendezvous mode + + pthread_mutex_t m_RIDVectorLock; +}; + +class CSndQueue +{ +friend class CUDT; +friend class CUDTUnited; + +public: + CSndQueue(); + ~CSndQueue(); + +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 ""; } + + /// Initialize the sending queue. + /// @param [in] c UDP channel to be associated to the queue + /// @param [in] t Timer + + void init(CChannel* c, CTimer* t); + + /// Send out a packet to a given address. + /// @param [in] addr destination address + /// @param [in] packet packet to be sent out + /// @return Size of data sent out. + + int sendto(const sockaddr* addr, CPacket& packet); + +#ifdef SRT_ENABLE_IPOPTS + /// 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; +#endif + + int ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); } + int sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); } + + void setClosing() + { + m_bClosing = true; + } + +private: + static void* worker(void* param); + pthread_t m_WorkerThread; + + +private: + CSndUList* m_pSndUList; // List of UDT instances for data sending + CChannel* m_pChannel; // The UDP channel for data sending + CTimer* m_pTimer; // Timing facility + + pthread_mutex_t m_WindowLock; + pthread_cond_t m_WindowCond; + + volatile bool m_bClosing; // closing the worker + +#if defined(SRT_DEBUG_SNDQ_HIGHRATE)//>>debug high freq worker + uint64_t m_ullDbgPeriod; + uint64_t m_ullDbgTime; + struct { + unsigned long lIteration; // + unsigned long lSleepTo; //SleepTo + unsigned long lNotReadyPop; //Continue + unsigned long lSendTo; + unsigned long lNotReadyTs; + unsigned long lCondWait; //block on m_WindowCond + } m_WorkerStats; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + +private: + CSndQueue(const CSndQueue&); + CSndQueue& operator=(const CSndQueue&); +}; + +class CRcvQueue +{ +friend class CUDT; +friend class CUDTUnited; + +public: + CRcvQueue(); + ~CRcvQueue(); + +public: + + // XXX There's currently no way to access the socket ID set for + // whatever the queue is currently working. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } + + /// Initialize the receiving queue. + /// @param [in] size queue size + /// @param [in] mss maximum packet size + /// @param [in] version IP version + /// @param [in] hsize hash table size + /// @param [in] c UDP channel to be associated to the queue + /// @param [in] t timer + + void init(int size, int payload, int version, int hsize, CChannel* c, CTimer* t); + + /// Read a packet for a specific UDT socket id. + /// @param [in] id Socket ID + /// @param [out] packet received packet + /// @return Data size of the packet + + int recvfrom(int32_t id, ref_t packet); + + void setClosing() + { + m_bClosing = true; + } + +private: + static void* worker(void* param); + pthread_t m_WorkerThread; + // Subroutines of worker + EReadStatus worker_RetrieveUnit(ref_t id, ref_t unit, sockaddr* sa); + EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr* sa); + EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr* sa); + EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr* sa); + +private: + CUnitQueue m_UnitQueue; // The received packet queue + + CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue + CHash* m_pHash; // Hash table for UDT socket looking up + CChannel* m_pChannel; // UDP channel for receving packets + CTimer* m_pTimer; // shared timer with the snd queue + + int m_iPayloadSize; // packet payload size + + volatile bool m_bClosing; // closing the worker + +private: + int setListener(CUDT* u); + void removeListener(const CUDT* u); + + void registerConnector(const SRTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl); + void removeConnector(const SRTSOCKET& id, bool should_lock = true); + + void setNewEntry(CUDT* u); + bool ifNewEntry(); + CUDT* getNewEntry(); + + void storePkt(int32_t id, CPacket* pkt); + +private: + pthread_mutex_t m_LSLock; + CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity + CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode + + std::vector m_vNewEntry; // newly added entries, to be inserted + pthread_mutex_t m_IDLock; + + std::map > m_mBuffer; // temporary buffer for rendezvous connection request + pthread_mutex_t m_PassLock; + pthread_cond_t m_PassCond; + +private: + CRcvQueue(const CRcvQueue&); + CRcvQueue& operator=(const CRcvQueue&); +}; + +struct CMultiplexer +{ + CSndQueue* m_pSndQueue; // The sending queue + CRcvQueue* m_pRcvQueue; // The receiving queue + CChannel* m_pChannel; // The UDP channel for sending and receiving + CTimer* m_pTimer; // The timer + + int m_iPort; // The UDP port number of this multiplexer + int m_iIPversion; // IP version +#ifdef SRT_ENABLE_IPOPTS + int m_iIpTTL; + int m_iIpToS; +#endif + int m_iMSS; // Maximum Segment Size + int m_iRefCount; // number of UDT instances that are associated with this multiplexer + int m_iIpV6Only; // IPV6_V6ONLY option + bool m_bReusable; // if this one can be shared with others + + int m_iID; // multiplexer ID +}; + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt.h b/trunk/3rdparty/srt-1-fit/srtcore/srt.h new file mode 100644 index 000000000..0e566e859 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt.h @@ -0,0 +1,747 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef INC__SRTC_H +#define INC__SRTC_H + +#include "version.h" + +#include "platform_sys.h" + +#include +#include + +#include "srt4udt.h" +#include "logging_api.h" + +//////////////////////////////////////////////////////////////////////////////// + +//if compiling on VC6.0 or pre-WindowsXP systems +//use -DLEGACY_WIN32 + +//if compiling with MinGW, it only works on XP or above +//use -D_WIN32_WINNT=0x0501 + + +#ifdef _WIN32 + #ifndef __MINGW__ + // Explicitly define 32-bit and 64-bit numbers + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int32 uint32_t; + #ifndef LEGACY_WIN32 + typedef unsigned __int64 uint64_t; + #else + // VC 6.0 does not support unsigned __int64: may cause potential problems. + typedef __int64 uint64_t; + #endif + + #ifdef SRT_DYNAMIC + #ifdef SRT_EXPORTS + #define SRT_API __declspec(dllexport) + #else + #define SRT_API __declspec(dllimport) + #endif + #else + #define SRT_API + #endif + #else // __MINGW__ + #define SRT_API + #endif +#else + #define SRT_API __attribute__ ((visibility("default"))) +#endif + + +// For feature tests if you need. +// You can use these constants with SRTO_MINVERSION option. +#define SRT_VERSION_FEAT_HSv5 0x010300 + +// When compiling in C++17 mode, use the standard C++17 attributes +// (out of these, only [[deprecated]] is supported in C++14, so +// for all lesser standard use compiler-specific attributes). +#if defined(SRT_NO_DEPRECATED) + +#define SRT_ATR_UNUSED +#define SRT_ATR_DEPRECATED +#define SRT_ATR_NODISCARD + +#elif defined(__cplusplus) && __cplusplus > 201406 + +#define SRT_ATR_UNUSED [[maybe_unused]] +#define SRT_ATR_DEPRECATED [[deprecated]] +#define SRT_ATR_NODISCARD [[nodiscard]] + +// GNUG is GNU C/C++; this syntax is also supported by Clang +#elif defined( __GNUC__) +#define SRT_ATR_UNUSED __attribute__((unused)) +#define SRT_ATR_DEPRECATED __attribute__((deprecated)) +#define SRT_ATR_NODISCARD __attribute__((warn_unused_result)) +#else +#define SRT_ATR_UNUSED +#define SRT_ATR_DEPRECATED +#define SRT_ATR_NODISCARD +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int SRTSOCKET; // SRTSOCKET is a typedef to int anyway, and it's not even in UDT namespace :) + +#ifdef _WIN32 + #ifndef __MINGW__ + typedef SOCKET SYSSOCKET; + #else + typedef int SYSSOCKET; + #endif +#else + typedef int SYSSOCKET; +#endif + +typedef SYSSOCKET UDPSOCKET; + + +// Values returned by srt_getsockstate() +typedef enum SRT_SOCKSTATUS { + SRTS_INIT = 1, + SRTS_OPENED, + SRTS_LISTENING, + SRTS_CONNECTING, + SRTS_CONNECTED, + SRTS_BROKEN, + SRTS_CLOSING, + SRTS_CLOSED, + SRTS_NONEXIST +} SRT_SOCKSTATUS; + +// This is a duplicate enum. Must be kept in sync with the original UDT enum for +// backward compatibility until all compat is destroyed. +typedef enum SRT_SOCKOPT { + + SRTO_MSS = 0, // the Maximum Transfer Unit + SRTO_SNDSYN = 1, // if sending is blocking + SRTO_RCVSYN = 2, // if receiving is blocking + SRTO_ISN = 3, // Initial Sequence Number (valid only after srt_connect or srt_accept-ed sockets) + SRTO_FC = 4, // Flight flag size (window size) + SRTO_SNDBUF = 5, // maximum buffer in sending queue + SRTO_RCVBUF = 6, // UDT receiving buffer size + SRTO_LINGER = 7, // waiting for unsent data when closing + SRTO_UDP_SNDBUF = 8, // UDP sending buffer size + SRTO_UDP_RCVBUF = 9, // UDP receiving buffer size + // XXX Free space for 2 options + // after deprecated ones are removed + SRTO_RENDEZVOUS = 12, // rendezvous connection mode + SRTO_SNDTIMEO = 13, // send() timeout + SRTO_RCVTIMEO = 14, // recv() timeout + SRTO_REUSEADDR = 15, // reuse an existing port or create a new one + SRTO_MAXBW = 16, // maximum bandwidth (bytes per second) that the connection can use + SRTO_STATE = 17, // current socket state, see UDTSTATUS, read only + SRTO_EVENT = 18, // current available events associated with the socket + SRTO_SNDDATA = 19, // size of data in the sending buffer + SRTO_RCVDATA = 20, // size of data available for recv + SRTO_SENDER = 21, // Sender mode (independent of conn mode), for encryption, tsbpd handshake. + SRTO_TSBPDMODE = 22, // Enable/Disable TsbPd. Enable -> Tx set origin timestamp, Rx deliver packet at origin time + delay + SRTO_LATENCY = 23, // NOT RECOMMENDED. SET: to both SRTO_RCVLATENCY and SRTO_PEERLATENCY. GET: same as SRTO_RCVLATENCY. + SRTO_TSBPDDELAY = 23, // DEPRECATED. ALIAS: SRTO_LATENCY + SRTO_INPUTBW = 24, // Estimated input stream rate. + SRTO_OHEADBW, // MaxBW ceiling based on % over input stream rate. Applies when UDT_MAXBW=0 (auto). + SRTO_PASSPHRASE = 26, // Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto + SRTO_PBKEYLEN, // Crypto key len in bytes {16,24,32} Default: 16 (128-bit) + SRTO_KMSTATE, // Key Material exchange status (UDT_SRTKmState) + SRTO_IPTTL = 29, // IP Time To Live (passthru for system sockopt IPPROTO_IP/IP_TTL) + SRTO_IPTOS, // IP Type of Service (passthru for system sockopt IPPROTO_IP/IP_TOS) + SRTO_TLPKTDROP = 31, // Enable receiver pkt drop + SRTO_SNDDROPDELAY = 32, // Extra delay towards latency for sender TLPKTDROP decision (-1 to off) + SRTO_NAKREPORT = 33, // Enable receiver to send periodic NAK reports + SRTO_VERSION = 34, // Local SRT Version + SRTO_PEERVERSION, // Peer SRT Version (from SRT Handshake) + SRTO_CONNTIMEO = 36, // Connect timeout in msec. Ccaller default: 3000, rendezvous (x 10) + // deprecated: SRTO_TWOWAYDATA, SRTO_SNDPBKEYLEN, SRTO_RCVPBKEYLEN (@c below) + _DEPRECATED_SRTO_SNDPBKEYLEN = 38, // (needed to use inside the code without generating -Wswitch) + // + SRTO_SNDKMSTATE = 40, // (GET) the current state of the encryption at the peer side + SRTO_RCVKMSTATE, // (GET) the current state of the encryption at the agent side + SRTO_LOSSMAXTTL, // Maximum possible packet reorder tolerance (number of packets to receive after loss to send lossreport) + SRTO_RCVLATENCY, // TsbPd receiver delay (mSec) to absorb burst of missed packet retransmission + SRTO_PEERLATENCY, // Minimum value of the TsbPd receiver delay (mSec) for the opposite side (peer) + SRTO_MINVERSION, // Minimum SRT version needed for the peer (peers with less version will get connection reject) + SRTO_STREAMID, // A string set to a socket and passed to the listener's accepted socket + SRTO_CONGESTION, // Congestion controller type selection + SRTO_MESSAGEAPI, // In File mode, use message API (portions of data with boundaries) + SRTO_PAYLOADSIZE, // Maximum payload size sent in one UDP packet (0 if unlimited) + SRTO_TRANSTYPE = 50, // Transmission type (set of options required for given transmission type) + SRTO_KMREFRESHRATE, // After sending how many packets the encryption key should be flipped to the new key + SRTO_KMPREANNOUNCE, // How many packets before key flip the new key is annnounced and after key flip the old one decommissioned + SRTO_ENFORCEDENCRYPTION, // Connection to be rejected or quickly broken when one side encryption set or bad password + SRTO_IPV6ONLY, // IPV6_V6ONLY mode + SRTO_PEERIDLETIMEO, // Peer-idle timeout (max time of silence heard from peer) in [ms] + // (some space left) + SRTO_PACKETFILTER = 60 // Add and configure a packet filter +} SRT_SOCKOPT; + + +#ifdef __cplusplus + +typedef SRT_ATR_DEPRECATED SRT_SOCKOPT SRT_SOCKOPT_DEPRECATED; +#define SRT_DEPRECATED_OPTION(value) ((SRT_SOCKOPT_DEPRECATED)value) + +#else + +// deprecated enum labels are supported only since gcc 6, so in C there +// will be a whole deprecated enum type, as it's not an error in C to mix +// enum types +enum SRT_ATR_DEPRECATED SRT_SOCKOPT_DEPRECATED +{ + + // Dummy last option, as every entry ends with a comma + SRTO_DEPRECATED_END = 0 + +}; +#define SRT_DEPRECATED_OPTION(value) ((enum SRT_SOCKOPT_DEPRECATED)value) +#endif + +// DEPRECATED OPTIONS: + +// SRTO_TWOWAYDATA: not to be used. SRT connection is always bidirectional if +// both clients support HSv5 - that is, since version 1.3.0. This flag was +// introducted around 1.2.0 version when full bidirectional support was added, +// but the bidirectional feature was decided no to be enabled due to huge +// differences between bidirectional support (especially concerning encryption) +// with HSv4 and HSv5 (that is, HSv4 was decided to remain unidirectional only, +// even though partial support is already provided in this version). + +#define SRTO_TWOWAYDATA SRT_DEPRECATED_OPTION(37) + +// This has been deprecated a long time ago, treat this as never implemented. +// The value is also already reused for another option. +#define SRTO_TSBPDMAXLAG SRT_DEPRECATED_OPTION(32) + +// This option is a derivative from UDT; the mechanism that uses it is now +// settable by SRTO_CONGESTION, or more generally by SRTO_TRANSTYPE. The freed +// number has been reused for a read-only option SRTO_ISN. This option should +// have never been used anywhere, just for safety this is temporarily declared +// as deprecated. +#define SRTO_CC SRT_DEPRECATED_OPTION(3) + +// These two flags were derived from UDT, but they were never used. +// Probably it didn't make sense anyway. The maximum size of the message +// in File/Message mode is defined by SRTO_SNDBUF, and the MSGTTL is +// a parameter used in `srt_sendmsg` and `srt_sendmsg2`. +#define SRTO_MAXMSG SRT_DEPRECATED_OPTION(10) +#define SRTO_MSGTTL SRT_DEPRECATED_OPTION(11) + +// These flags come from an older experimental implementation of bidirectional +// encryption support, which were used two different SEKs, KEKs and passphrases +// per direction. The current implementation uses just one in both directions, +// so SRTO_PBKEYLEN should be used for both cases. +#define SRTO_SNDPBKEYLEN SRT_DEPRECATED_OPTION(38) +#define SRTO_RCVPBKEYLEN SRT_DEPRECATED_OPTION(39) + +// Keeping old name for compatibility (deprecated) +#define SRTO_SMOOTHER SRT_DEPRECATED_OPTION(47) +#define SRTO_STRICTENC SRT_DEPRECATED_OPTION(53) + +typedef enum SRT_TRANSTYPE +{ + SRTT_LIVE, + SRTT_FILE, + SRTT_INVALID +} SRT_TRANSTYPE; + +// These sizes should be used for Live mode. In Live mode you should not +// exceed the size that fits in a single MTU. + +// This is for MPEG TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE. +static const int SRT_LIVE_DEF_PLSIZE = 1316; // = 188*7, recommended for MPEG TS + +// This is the maximum payload size for Live mode, should you have a different +// payload type than MPEG TS. +static const int SRT_LIVE_MAX_PLSIZE = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16) + +// Latency for Live transmission: default is 120 +static const int SRT_LIVE_DEF_LATENCY_MS = 120; + +// Importrant note: please add new fields to this structure to the end and don't remove any existing fields +struct CBytePerfMon +{ + // global measurements + int64_t msTimeStamp; // time since the UDT entity is started, in milliseconds + int64_t pktSentTotal; // total number of sent data packets, including retransmissions + int64_t pktRecvTotal; // total number of received packets + int pktSndLossTotal; // total number of lost packets (sender side) + int pktRcvLossTotal; // total number of lost packets (receiver side) + int pktRetransTotal; // total number of retransmitted packets + int pktSentACKTotal; // total number of sent ACK packets + int pktRecvACKTotal; // total number of received ACK packets + int pktSentNAKTotal; // total number of sent NAK packets + int pktRecvNAKTotal; // total number of received NAK packets + int64_t usSndDurationTotal; // total time duration when UDT is sending data (idle time exclusive) + //>new + int pktSndDropTotal; // number of too-late-to-send dropped packets + int pktRcvDropTotal; // number of too-late-to play missing packets + int pktRcvUndecryptTotal; // number of undecrypted packets + uint64_t byteSentTotal; // total number of sent data bytes, including retransmissions + uint64_t byteRecvTotal; // total number of received bytes +#ifdef SRT_ENABLE_LOSTBYTESCOUNT + uint64_t byteRcvLossTotal; // total number of lost bytes +#endif + uint64_t byteRetransTotal; // total number of retransmitted bytes + uint64_t byteSndDropTotal; // number of too-late-to-send dropped bytes + uint64_t byteRcvDropTotal; // number of too-late-to play missing bytes (estimate based on average packet size) + uint64_t byteRcvUndecryptTotal; // number of undecrypted bytes + //< + + // local measurements + int64_t pktSent; // number of sent data packets, including retransmissions + int64_t pktRecv; // number of received packets + int pktSndLoss; // number of lost packets (sender side) + int pktRcvLoss; // number of lost packets (receiver side) + int pktRetrans; // number of retransmitted packets + int pktRcvRetrans; // number of retransmitted packets received + int pktSentACK; // number of sent ACK packets + int pktRecvACK; // number of received ACK packets + int pktSentNAK; // number of sent NAK packets + int pktRecvNAK; // number of received NAK packets + double mbpsSendRate; // sending rate in Mb/s + double mbpsRecvRate; // receiving rate in Mb/s + int64_t usSndDuration; // busy sending time (i.e., idle time exclusive) + int pktReorderDistance; // size of order discrepancy in received sequences + double pktRcvAvgBelatedTime; // average time of packet delay for belated packets (packets with sequence past the ACK) + int64_t pktRcvBelated; // number of received AND IGNORED packets due to having come too late + //>new + int pktSndDrop; // number of too-late-to-send dropped packets + int pktRcvDrop; // number of too-late-to play missing packets + int pktRcvUndecrypt; // number of undecrypted packets + uint64_t byteSent; // number of sent data bytes, including retransmissions + uint64_t byteRecv; // number of received bytes +#ifdef SRT_ENABLE_LOSTBYTESCOUNT + uint64_t byteRcvLoss; // number of retransmitted bytes +#endif + uint64_t byteRetrans; // number of retransmitted bytes + uint64_t byteSndDrop; // number of too-late-to-send dropped bytes + uint64_t byteRcvDrop; // number of too-late-to play missing bytes (estimate based on average packet size) + uint64_t byteRcvUndecrypt; // number of undecrypted bytes + //< + + // instant measurements + double usPktSndPeriod; // packet sending period, in microseconds + int pktFlowWindow; // flow window size, in number of packets + int pktCongestionWindow; // congestion window size, in number of packets + int pktFlightSize; // number of packets on flight + double msRTT; // RTT, in milliseconds + double mbpsBandwidth; // estimated bandwidth, in Mb/s + int byteAvailSndBuf; // available UDT sender buffer size + int byteAvailRcvBuf; // available UDT receiver buffer size + //>new + double mbpsMaxBW; // Transmit Bandwidth ceiling (Mbps) + int byteMSS; // MTU + + int pktSndBuf; // UnACKed packets in UDT sender + int byteSndBuf; // UnACKed bytes in UDT sender + int msSndBuf; // UnACKed timespan (msec) of UDT sender + int msSndTsbPdDelay; // Timestamp-based Packet Delivery Delay + + int pktRcvBuf; // Undelivered packets in UDT receiver + int byteRcvBuf; // Undelivered bytes of UDT receiver + int msRcvBuf; // Undelivered timespan (msec) of UDT receiver + int msRcvTsbPdDelay; // Timestamp-based Packet Delivery Delay + + int pktSndFilterExtraTotal; // number of control packets supplied by packet filter + int pktRcvFilterExtraTotal; // number of control packets received and not supplied back + int pktRcvFilterSupplyTotal; // number of packets that the filter supplied extra (e.g. FEC rebuilt) + int pktRcvFilterLossTotal; // number of packet loss not coverable by filter + + int pktSndFilterExtra; // number of control packets supplied by packet filter + int pktRcvFilterExtra; // number of control packets received and not supplied back + int pktRcvFilterSupply; // number of packets that the filter supplied extra (e.g. FEC rebuilt) + int pktRcvFilterLoss; // number of packet loss not coverable by filter + int pktReorderTolerance; // packet reorder tolerance value + //< +}; + +//////////////////////////////////////////////////////////////////////////////// + +// Error codes - define outside the CUDTException class +// because otherwise you'd have to use CUDTException::MJ_SUCCESS etc. +// in all throw CUDTException expressions. +enum CodeMajor +{ + MJ_UNKNOWN = -1, + MJ_SUCCESS = 0, + MJ_SETUP = 1, + MJ_CONNECTION = 2, + MJ_SYSTEMRES = 3, + MJ_FILESYSTEM = 4, + MJ_NOTSUP = 5, + MJ_AGAIN = 6, + MJ_PEERERROR = 7 +}; + +enum CodeMinor +{ + // These are "minor" error codes from various "major" categories + // MJ_SETUP + MN_NONE = 0, + MN_TIMEOUT = 1, + MN_REJECTED = 2, + MN_NORES = 3, + MN_SECURITY = 4, + // MJ_CONNECTION + MN_CONNLOST = 1, + MN_NOCONN = 2, + // MJ_SYSTEMRES + MN_THREAD = 1, + MN_MEMORY = 2, + // MJ_FILESYSTEM + MN_SEEKGFAIL = 1, + MN_READFAIL = 2, + MN_SEEKPFAIL = 3, + MN_WRITEFAIL = 4, + // MJ_NOTSUP + MN_ISBOUND = 1, + MN_ISCONNECTED = 2, + MN_INVAL = 3, + MN_SIDINVAL = 4, + MN_ISUNBOUND = 5, + MN_NOLISTEN = 6, + MN_ISRENDEZVOUS = 7, + MN_ISRENDUNBOUND = 8, + MN_INVALMSGAPI = 9, + MN_INVALBUFFERAPI = 10, + MN_BUSY = 11, + MN_XSIZE = 12, + MN_EIDINVAL = 13, + // MJ_AGAIN + MN_WRAVAIL = 1, + MN_RDAVAIL = 2, + MN_XMTIMEOUT = 3, + MN_CONGESTION = 4 +}; + +static const enum CodeMinor MN_ISSTREAM SRT_ATR_DEPRECATED = (enum CodeMinor)(9); +static const enum CodeMinor MN_ISDGRAM SRT_ATR_DEPRECATED = (enum CodeMinor)(10); + +// Stupid, but effective. This will be #undefined, so don't worry. +#define MJ(major) (1000 * MJ_##major) +#define MN(major, minor) (1000 * MJ_##major + MN_##minor) + +// Some better way to define it, and better for C language. +typedef enum SRT_ERRNO +{ + SRT_EUNKNOWN = -1, + SRT_SUCCESS = MJ_SUCCESS, + + SRT_ECONNSETUP = MJ(SETUP), + SRT_ENOSERVER = MN(SETUP, TIMEOUT), + SRT_ECONNREJ = MN(SETUP, REJECTED), + SRT_ESOCKFAIL = MN(SETUP, NORES), + SRT_ESECFAIL = MN(SETUP, SECURITY), + + SRT_ECONNFAIL = MJ(CONNECTION), + SRT_ECONNLOST = MN(CONNECTION, CONNLOST), + SRT_ENOCONN = MN(CONNECTION, NOCONN), + + SRT_ERESOURCE = MJ(SYSTEMRES), + SRT_ETHREAD = MN(SYSTEMRES, THREAD), + SRT_ENOBUF = MN(SYSTEMRES, MEMORY), + + SRT_EFILE = MJ(FILESYSTEM), + SRT_EINVRDOFF = MN(FILESYSTEM, SEEKGFAIL), + SRT_ERDPERM = MN(FILESYSTEM, READFAIL), + SRT_EINVWROFF = MN(FILESYSTEM, SEEKPFAIL), + SRT_EWRPERM = MN(FILESYSTEM, WRITEFAIL), + + SRT_EINVOP = MJ(NOTSUP), + SRT_EBOUNDSOCK = MN(NOTSUP, ISBOUND), + SRT_ECONNSOCK = MN(NOTSUP, ISCONNECTED), + SRT_EINVPARAM = MN(NOTSUP, INVAL), + SRT_EINVSOCK = MN(NOTSUP, SIDINVAL), + SRT_EUNBOUNDSOCK = MN(NOTSUP, ISUNBOUND), + SRT_ENOLISTEN = MN(NOTSUP, NOLISTEN), + SRT_ERDVNOSERV = MN(NOTSUP, ISRENDEZVOUS), + SRT_ERDVUNBOUND = MN(NOTSUP, ISRENDUNBOUND), + SRT_EINVALMSGAPI = MN(NOTSUP, INVALMSGAPI), + SRT_EINVALBUFFERAPI = MN(NOTSUP, INVALBUFFERAPI), + SRT_EDUPLISTEN = MN(NOTSUP, BUSY), + SRT_ELARGEMSG = MN(NOTSUP, XSIZE), + SRT_EINVPOLLID = MN(NOTSUP, EIDINVAL), + + SRT_EASYNCFAIL = MJ(AGAIN), + SRT_EASYNCSND = MN(AGAIN, WRAVAIL), + SRT_EASYNCRCV = MN(AGAIN, RDAVAIL), + SRT_ETIMEOUT = MN(AGAIN, XMTIMEOUT), + SRT_ECONGEST = MN(AGAIN, CONGESTION), + + SRT_EPEERERR = MJ(PEERERROR) +} SRT_ERRNO; + +static const SRT_ERRNO SRT_EISSTREAM SRT_ATR_DEPRECATED = (SRT_ERRNO) MN(NOTSUP, INVALMSGAPI); +static const SRT_ERRNO SRT_EISDGRAM SRT_ATR_DEPRECATED = (SRT_ERRNO) MN(NOTSUP, INVALBUFFERAPI); + +#undef MJ +#undef MN + +enum SRT_REJECT_REASON +{ + SRT_REJ_UNKNOWN, // initial set when in progress + SRT_REJ_SYSTEM, // broken due to system function error + SRT_REJ_PEER, // connection was rejected by peer + SRT_REJ_RESOURCE, // internal problem with resource allocation + SRT_REJ_ROGUE, // incorrect data in handshake messages + SRT_REJ_BACKLOG, // listener's backlog exceeded + SRT_REJ_IPE, // internal program error + SRT_REJ_CLOSE, // socket is closing + SRT_REJ_VERSION, // peer is older version than agent's minimum set + SRT_REJ_RDVCOOKIE, // rendezvous cookie collision + SRT_REJ_BADSECRET, // wrong password + SRT_REJ_UNSECURE, // password required or unexpected + SRT_REJ_MESSAGEAPI, // streamapi/messageapi collision + SRT_REJ_CONGESTION, // incompatible congestion-controller type + SRT_REJ_FILTER, // incompatible packet filter + + SRT_REJ__SIZE, +}; + +// Logging API - specialization for SRT. + +// Define logging functional areas for log selection. +// Use values greater than 0. Value 0 is reserved for LOGFA_GENERAL, +// which is considered always enabled. + +// Logger Functional Areas +// Note that 0 is "general". + +// Made by #define so that it's available also for C API. +#define SRT_LOGFA_GENERAL 0 +#define SRT_LOGFA_BSTATS 1 +#define SRT_LOGFA_CONTROL 2 +#define SRT_LOGFA_DATA 3 +#define SRT_LOGFA_TSBPD 4 +#define SRT_LOGFA_REXMIT 5 +#define SRT_LOGFA_HAICRYPT 6 +#define SRT_LOGFA_CONGEST 7 + +// To make a typical int32_t size, although still use std::bitset. +// C API will carry it over. +#define SRT_LOGFA_LASTNONE 31 + +enum SRT_KM_STATE +{ + SRT_KM_S_UNSECURED = 0, //No encryption + SRT_KM_S_SECURING = 1, //Stream encrypted, exchanging Keying Material + SRT_KM_S_SECURED = 2, //Stream encrypted, keying Material exchanged, decrypting ok. + SRT_KM_S_NOSECRET = 3, //Stream encrypted and no secret to decrypt Keying Material + SRT_KM_S_BADSECRET = 4 //Stream encrypted and wrong secret, cannot decrypt Keying Material +}; + +enum SRT_EPOLL_OPT +{ + SRT_EPOLL_OPT_NONE = 0x0, // fallback + // this values are defined same as linux epoll.h + // so that if system values are used by mistake, they should have the same effect + SRT_EPOLL_IN = 0x1, + SRT_EPOLL_OUT = 0x4, + SRT_EPOLL_ERR = 0x8, + SRT_EPOLL_ET = 1u << 31 +}; +// These are actually flags - use a bit container: +typedef int32_t SRT_EPOLL_T; + +enum SRT_EPOLL_FLAGS +{ + /// This allows the EID container to be empty when calling the waiting + /// function with infinite time. This means an infinite hangup, although + /// a socket can be added to this EID from a separate thread. + SRT_EPOLL_ENABLE_EMPTY = 1, + + /// This makes the waiting function check if there is output container + /// passed to it, and report an error if it isn't. By default it is allowed + /// that the output container is 0 size or NULL and therefore the readiness + /// state is reported only as a number of ready sockets from return value. + SRT_EPOLL_ENABLE_OUTPUTCHECK = 2 +}; + +#ifdef __cplusplus +// In C++ these enums cannot be treated as int and glued by operator |. +// Unless this operator is defined. +inline SRT_EPOLL_OPT operator|(SRT_EPOLL_OPT a1, SRT_EPOLL_OPT a2) +{ + return SRT_EPOLL_OPT( (int)a1 | (int)a2 ); +} + +inline bool operator&(int flags, SRT_EPOLL_OPT eflg) +{ + // Using an enum prevents treating int automatically as enum, + // requires explicit enum to be passed here, and minimizes the + // risk that the right side value will contain multiple flags. + return (flags & int(eflg)) != 0; +} +#endif + + + +typedef struct CBytePerfMon SRT_TRACEBSTATS; + +static const SRTSOCKET SRT_INVALID_SOCK = -1; +static const int SRT_ERROR = -1; + +// library initialization +SRT_API int srt_startup(void); +SRT_API int srt_cleanup(void); + +// +// Socket operations +// +SRT_API SRTSOCKET srt_socket (int af, int type, int protocol); +SRT_API SRTSOCKET srt_create_socket(); +SRT_API int srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API int srt_bind_peerof (SRTSOCKET u, UDPSOCKET udpsock); +SRT_API int srt_listen (SRTSOCKET u, int backlog); +SRT_API SRTSOCKET srt_accept (SRTSOCKET u, struct sockaddr* addr, int* addrlen); +typedef int srt_listen_callback_fn (void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid); +SRT_API int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); +SRT_API int srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); +SRT_API int srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, + const struct sockaddr* remote_name, int remote_namelen); +SRT_API int srt_close (SRTSOCKET u); +SRT_API int srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API int srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API int srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); +SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen); +SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); +SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); + + +// XXX Note that the srctime functionality doesn't work yet and needs fixing. +typedef struct SRT_MsgCtrl_ +{ + int flags; // Left for future + int msgttl; // TTL for a message, default -1 (no TTL limitation) + int inorder; // Whether a message is allowed to supersede partially lost one. Unused in stream and live mode. + int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame + uint64_t srctime; // source timestamp (usec), 0: use internal time + int32_t pktseq; // sequence number of the first packet in received message (unused for sending) + int32_t msgno; // message number (output value for both sending and receiving) +} SRT_MSGCTRL; + +// You are free to use either of these two methods to set SRT_MSGCTRL object +// to default values: either call srt_msgctrl_init(&obj) or obj = srt_msgctrl_default. +SRT_API void srt_msgctrl_init(SRT_MSGCTRL* mctrl); +SRT_API extern const SRT_MSGCTRL srt_msgctrl_default; + +// The send/receive functions. +// These functions have different names due to different sets of parameters +// to be supplied. Not all of them are needed or make sense in all modes: + +// Plain: supply only the buffer and its size. +// Msg: supply additionally +// - TTL (message is not delivered when exceeded) and +// - INORDER (when false, the message is allowed to be delivered in different +// order than when it was sent, when the later message is earlier ready to +// deliver) +// Msg2: Supply extra parameters in SRT_MSGCTRL. When receiving, these +// parameters will be filled, as needed. NULL is acceptable, in which case +// the defaults are used. + +// NOTE: srt_send and srt_recv have the last "..." left to allow ignore a +// deprecated and unused "flags" parameter. After confirming that all +// compat applications that pass useless 0 there are fixed, this will be +// removed. + +// +// Sending functions +// +SRT_API int srt_send (SRTSOCKET u, const char* buf, int len); +SRT_API int srt_sendmsg (SRTSOCKET u, const char* buf, int len, int ttl/* = -1*/, int inorder/* = false*/); +SRT_API int srt_sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL *mctrl); + +// +// Receiving functions +// +SRT_API int srt_recv (SRTSOCKET u, char* buf, int len); + +// srt_recvmsg is actually an alias to srt_recv, it stays under the old name for compat reasons. +SRT_API int srt_recvmsg (SRTSOCKET u, char* buf, int len); +SRT_API int srt_recvmsg2(SRTSOCKET u, char *buf, int len, SRT_MSGCTRL *mctrl); + + +// Special send/receive functions for files only. +#define SRT_DEFAULT_SENDFILE_BLOCK 364000 +#define SRT_DEFAULT_RECVFILE_BLOCK 7280000 +SRT_API int64_t srt_sendfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block); +SRT_API int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block); + + +// last error detection +SRT_API const char* srt_getlasterror_str(void); +SRT_API int srt_getlasterror(int* errno_loc); +SRT_API const char* srt_strerror(int code, int errnoval); +SRT_API void srt_clearlasterror(void); + +// performance track +// perfmon with Byte counters for better bitrate estimation. +SRT_API int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); +// permon with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes. +SRT_API int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); + +// Socket Status (for problem tracking) +SRT_API SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u); + +SRT_API int srt_epoll_create(void); +SRT_API int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); +SRT_API int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); +SRT_API int srt_epoll_remove_usock(int eid, SRTSOCKET u); +SRT_API int srt_epoll_remove_ssock(int eid, SYSSOCKET s); +SRT_API int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); +SRT_API int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); + +SRT_API int srt_epoll_wait(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, int64_t msTimeOut, + SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum); +typedef struct SRT_EPOLL_EVENT_ +{ + SRTSOCKET fd; + int events; // SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR +} SRT_EPOLL_EVENT; +SRT_API int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); + +SRT_API int32_t srt_epoll_set(int eid, int32_t flags); +SRT_API int srt_epoll_release(int eid); + +// Logging control + +SRT_API void srt_setloglevel(int ll); +SRT_API void srt_addlogfa(int fa); +SRT_API void srt_dellogfa(int fa); +SRT_API void srt_resetlogfa(const int* fara, size_t fara_size); +// This isn't predicted, will be only available in SRT C++ API. +// For the time being, until this API is ready, use UDT::setlogstream. +// SRT_API void srt_setlogstream(std::ostream& stream); +SRT_API void srt_setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler); +SRT_API void srt_setlogflags(int flags); + + +SRT_API int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); + +SRT_API enum SRT_REJECT_REASON srt_getrejectreason(SRTSOCKET sock); +SRT_API extern const char* const srt_rejectreason_msg []; +const char* srt_rejectreason_str(enum SRT_REJECT_REASON id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt4udt.h b/trunk/3rdparty/srt-1-fit/srtcore/srt4udt.h new file mode 100644 index 000000000..49f6d9f7a --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt4udt.h @@ -0,0 +1,71 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef SRT4UDT_H +#define SRT4UDT_H + +#ifndef INC__SRTC_H +#error "This is protected header, used by udt.h. This shouldn't be included directly" +#endif + +//undef SRT_ENABLE_ECN 1 /* Early Congestion Notification (for source bitrate control) */ + +//undef SRT_DEBUG_TSBPD_OUTJITTER 1 /* Packet Delivery histogram */ +//undef SRT_DEBUG_TSBPD_DRIFT 1 /* Debug Encoder-Decoder Drift) */ +//undef SRT_DEBUG_TSBPD_WRAP 1 /* Debug packet timestamp wraparound */ +//undef SRT_DEBUG_TLPKTDROP_DROPSEQ 1 +//undef SRT_DEBUG_SNDQ_HIGHRATE 1 + + +/* +* SRT_ENABLE_CONNTIMEO +* Option UDT_CONNTIMEO added to the API to set/get the connection timeout. +* The UDT hard coded default of 3000 msec is too small for some large RTT (satellite) use cases. +* The SRT handshake (2 exchanges) needs 2 times the RTT to complete with no packet loss. +*/ +#define SRT_ENABLE_CONNTIMEO 1 + +/* +* SRT_ENABLE_NOCWND +* Set the congestion window at its max (then disabling it) to prevent stopping transmission +* when too many packets are not acknowledged. +* The congestion windows is the maximum distance in pkts since the last acknowledged packets. +*/ +#define SRT_ENABLE_NOCWND 1 + +/* +* SRT_ENABLE_NAKREPORT +* Send periodic NAK report for more efficient retransmission instead of relying on ACK timeout +* to retransmit all non-ACKed packets, very inefficient with real-time and no congestion window. +*/ +#define SRT_ENABLE_NAKREPORT 1 + +#define SRT_ENABLE_RCVBUFSZ_MAVG 1 /* Recv buffer size moving average */ +#define SRT_ENABLE_SNDBUFSZ_MAVG 1 /* Send buffer size moving average */ +#define SRT_MAVG_SAMPLING_RATE 40 /* Max sampling rate */ + +#define SRT_ENABLE_LOSTBYTESCOUNT 1 + + +/* +* SRT_ENABLE_IPOPTS +* Enable IP TTL and ToS setting +*/ +#define SRT_ENABLE_IPOPTS 1 + + +#define SRT_ENABLE_CLOSE_SYNCH 1 + +#endif /* SRT4UDT_H */ diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp b/trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp new file mode 100644 index 000000000..56d875f7b --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp @@ -0,0 +1,318 @@ +/* + * 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. + *****************************************************************************/ + +#include +#include +#if __APPLE__ + #include "TargetConditionals.h" +#endif +#include "srt.h" +#include "common.h" +#include "core.h" +#include "utilities.h" + +using namespace std; + + +extern "C" { + +int srt_startup() { return CUDT::startup(); } +int srt_cleanup() { return CUDT::cleanup(); } + +SRTSOCKET srt_socket(int af, int type, int protocol) { return CUDT::socket(af, type, protocol); } +SRTSOCKET srt_create_socket() +{ + // XXX This must include rework around m_iIPVersion. This must be + // abandoned completely and all "IP VERSION" thing should rely on + // the exact specification in the 'sockaddr' objects passed to other functions, + // that is, the "current IP Version" remains undefined until any of + // srt_bind() or srt_connect() function is done. And when any of these + // functions are being called, the IP version is contained in the + // sockaddr object passed there. + + // Until this rework is done, srt_create_socket() will set the + // default AF_INET family. + + // Note that all arguments except the first one here are ignored. + return CUDT::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +} + +int srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); } +int srt_bind_peerof(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); } +int srt_listen(SRTSOCKET u, int backlog) { return CUDT::listen(u, backlog); } +SRTSOCKET srt_accept(SRTSOCKET u, struct sockaddr * addr, int * addrlen) { return CUDT::accept(u, addr, addrlen); } +int srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, 0); } +int srt_connect_debug(SRTSOCKET u, const struct sockaddr * name, int namelen, int forced_isn) { return CUDT::connect(u, name, namelen, forced_isn); } + +int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, + const struct sockaddr* remote_name, int remote_namelen) +{ + bool yes = 1; + CUDT::setsockopt(u, 0, UDT_RENDEZVOUS, &yes, sizeof yes); + + // Note: PORT is 16-bit and at the same location in both sockaddr_in and sockaddr_in6. + // Just as a safety precaution, check the structs. + if ( (local_name->sa_family != AF_INET && local_name->sa_family != AF_INET6) + || local_name->sa_family != remote_name->sa_family) + return SRT_EINVPARAM; + + sockaddr_in* local_sin = (sockaddr_in*)local_name; + sockaddr_in* remote_sin = (sockaddr_in*)remote_name; + + if (local_sin->sin_port != remote_sin->sin_port) + return SRT_EINVPARAM; + + int st = srt_bind(u, local_name, local_namelen); + if ( st != 0 ) + return st; + + return srt_connect(u, remote_name, remote_namelen); +} + +int srt_close(SRTSOCKET u) +{ + SRT_SOCKSTATUS st = srt_getsockstate(u); + + if ((st == SRTS_NONEXIST) || + (st == SRTS_CLOSED) || + (st == SRTS_CLOSING) ) + { + // It's closed already. Do nothing. + return 0; + } + + return CUDT::close(u); +} + +int srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } +int srt_getsockname(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getsockname(u, name, namelen); } +int srt_getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void * optval, int * optlen) +{ return CUDT::getsockopt(u, level, optname, optval, optlen); } +int srt_setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void * optval, int optlen) +{ return CUDT::setsockopt(u, level, optname, optval, optlen); } + +int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) +{ return CUDT::getsockopt(u, 0, opt, optval, optlen); } +int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) +{ return CUDT::setsockopt(u, 0, opt, optval, optlen); } + +int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); } +int srt_recv(SRTSOCKET u, char * buf, int len) { return CUDT::recv(u, buf, len, 0); } +int srt_sendmsg(SRTSOCKET u, const char * buf, int len, int ttl, int inorder) { return CUDT::sendmsg(u, buf, len, ttl, 0!= inorder); } +int srt_recvmsg(SRTSOCKET u, char * buf, int len) { uint64_t ign_srctime; return CUDT::recvmsg(u, buf, len, ign_srctime); } +int64_t srt_sendfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block) +{ + if (!path || !offset ) + { + return CUDT::setError(CUDTException(MJ_NOTSUP, MN_INVAL, 0)); + } + fstream ifs(path, ios::binary | ios::in); + if (!ifs) + { + return CUDT::setError(CUDTException(MJ_FILESYSTEM, MN_READFAIL, 0)); + } + int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block); + ifs.close(); + return ret; +} + +int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block) +{ + if (!path || !offset ) + { + return CUDT::setError(CUDTException(MJ_NOTSUP, MN_INVAL, 0)); + } + fstream ofs(path, ios::binary | ios::out); + if (!ofs) + { + return CUDT::setError(CUDTException(MJ_FILESYSTEM, MN_WRAVAIL, 0)); + } + int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block); + ofs.close(); + return ret; +} + +extern const SRT_MSGCTRL srt_msgctrl_default = { 0, -1, false, 0, 0, 0, 0 }; + +void srt_msgctrl_init(SRT_MSGCTRL* mctrl) +{ + *mctrl = srt_msgctrl_default; +} + +int srt_sendmsg2(SRTSOCKET u, const char * buf, int len, SRT_MSGCTRL *mctrl) +{ + // Allow NULL mctrl in the API, but not internally. + if (mctrl) + return CUDT::sendmsg2(u, buf, len, Ref(*mctrl)); + SRT_MSGCTRL mignore = srt_msgctrl_default; + return CUDT::sendmsg2(u, buf, len, Ref(mignore)); +} + +int srt_recvmsg2(SRTSOCKET u, char * buf, int len, SRT_MSGCTRL *mctrl) +{ + if (mctrl) + return CUDT::recvmsg2(u, buf, len, Ref(*mctrl)); + SRT_MSGCTRL mignore = srt_msgctrl_default; + return CUDT::recvmsg2(u, buf, len, Ref(mignore)); +} + +const char* srt_getlasterror_str() { return UDT::getlasterror().getErrorMessage(); } + +int srt_getlasterror(int* loc_errno) +{ + if ( loc_errno ) + *loc_errno = UDT::getlasterror().getErrno(); + return CUDT::getlasterror().getErrorCode(); +} + +const char* srt_strerror(int code, int err) +{ + static CUDTException e; + e = CUDTException(CodeMajor(code/1000), CodeMinor(code%1000), err); + return(e.getErrorMessage()); +} + + +void srt_clearlasterror() +{ + UDT::getlasterror().clear(); +} + +int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear) { return CUDT::bstats(u, perf, 0!= clear); } +int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous) { return CUDT::bstats(u, perf, 0!= clear, 0!= instantaneous); } + +SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u) { return SRT_SOCKSTATUS((int)CUDT::getsockstate(u)); } + +// event mechanism +int srt_epoll_create() { return CUDT::epoll_create(); } + +// You can use either SRT_EPOLL_* flags or EPOLL* flags from , both are the same. IN/OUT/ERR only. +// events == NULL accepted, in which case all flags are set. +int srt_epoll_add_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_add_usock(eid, u, events); } + +int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) +{ + int flag = 0; + + if (events) { + flag = *events; + } else { + flag = SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR; + } + + // call UDT native function + return CUDT::epoll_add_ssock(eid, s, &flag); +} + +int srt_epoll_remove_usock(int eid, SRTSOCKET u) { return CUDT::epoll_remove_usock(eid, u); } +int srt_epoll_remove_ssock(int eid, SYSSOCKET s) { return CUDT::epoll_remove_ssock(eid, s); } + +int srt_epoll_update_usock(int eid, SRTSOCKET u, const int * events) +{ + return CUDT::epoll_update_usock(eid, u, events); +} + +int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int * events) +{ + int flag = 0; + + if (events) { + flag = *events; + } else { + flag = SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR; + } + + // call UDT native function + return CUDT::epoll_update_ssock(eid, s, &flag); +} + +int srt_epoll_wait( + int eid, + SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, + int64_t msTimeOut, + SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum) + { + return UDT::epoll_wait2( + eid, + readfds, rnum, writefds, wnum, + msTimeOut, + lrfds, lrnum, lwfds, lwnum); +} + +int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut) +{ + return UDT::epoll_uwait( + eid, + fdsSet, + fdsSize, + msTimeOut); +} + +// use this function to set flags. Default flags are always "everything unset". +// Pass 0 here to clear everything, or nonzero to set a desired flag. +// Pass -1 to not change anything (but still get the current flag value). +int32_t srt_epoll_set(int eid, int32_t flags) { return CUDT::epoll_set(eid, flags); } + +int srt_epoll_release(int eid) { return CUDT::epoll_release(eid); } + +void srt_setloglevel(int ll) +{ + UDT::setloglevel(srt_logging::LogLevel::type(ll)); +} + +void srt_addlogfa(int fa) +{ + UDT::addlogfa(srt_logging::LogFA(fa)); +} + +void srt_dellogfa(int fa) +{ + UDT::dellogfa(srt_logging::LogFA(fa)); +} + +void srt_resetlogfa(const int* fara, size_t fara_size) +{ + UDT::resetlogfa(fara, fara_size); +} + +void srt_setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler) +{ + UDT::setloghandler(opaque, handler); +} + +void srt_setlogflags(int flags) +{ + UDT::setlogflags(flags); +} + +int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes) +{ + return CUDT::getsndbuffer(sock, blocks, bytes); +} + +enum SRT_REJECT_REASON srt_getrejectreason(SRTSOCKET sock) +{ + return CUDT::rejectReason(sock); +} + +int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +{ + if (!hook) + return CUDT::setError(CUDTException(MJ_NOTSUP, MN_INVAL)); + + return CUDT::installAcceptHook(lsn, hook, opaq); +} + +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c b/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c new file mode 100644 index 000000000..3db4a486b --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c @@ -0,0 +1,140 @@ +/* + * 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/. + * + */ + +// Implementation file for srt_compat.h +/***************************************************************************** +written by + Haivision Systems Inc. + *****************************************************************************/ + +// Prevents from misconfiguration through preprocessor. + +#include + +#include +#include +#include +#if defined(__unix__) && !defined(BSD) +#include +#endif + +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include +#endif + + +static const char* SysStrError_Fallback(int errnum, char* buf, size_t buflen) +{ +#if defined(_MSC_VER) && _MSC_VER < 1900 + _snprintf(buf, buflen - 1, "ERROR CODE %d", errnum); + buf[buflen - 1] = '\0'; +#else + snprintf(buf, buflen, "ERROR CODE %d", errnum); +#endif + return buf; +} + +// This function is a portable and thread-safe version of `strerror`. +// It requires a user-supplied buffer to store the message. The returned +// value is always equal to the given buffer pointer. If the system +// error message is longer than the given buflen, it will be trimmed. +// When the error code is incorrect for the given error message function, +// a fallback message will be returned, either as returned by the underlying +// function, or crafted by this function as a response to error in an +// underlying function. +extern const char * SysStrError(int errnum, char * buf, size_t buflen) +{ + if (buf == NULL || buflen < 4) // Required to put ??? into it as a fallback + { + errno = EFAULT; + return buf; + } + + buf[0] = '\0'; + +#if defined(_WIN32) + const char* lpMsgBuf; + + // Note: Intentionally the "fixed char size" types are used despite using + // character size dependent FormatMessage (instead of FormatMessageA) so that + // your compilation fails when you use wide characters. + // The problem is that when TCHAR != char, then the buffer written this way + // would have to be converted to ASCII, not just copied by strncpy. + FormatMessage(0 + | FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, // no lpSource + errnum, // dwMessageId (as controlled by FORMAT_MESSAGE_FROM_SYSTEM) + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + // This below parameter normally should contain a pointer to an allocated buffer, + // and this way it's LPTSTR. But when FORMAT_MESSAGE_ALLOCATE_BUFFER, then it is + // expected to be a the value of LPTSTR* type, converted to LPTSTR, that designates + // a pointer to a variable of type LPTSTR, to which the newly allocated buffer is + // assigned. This buffer should be freed afterwards using LocalFree(). + (LPSTR)&lpMsgBuf, + 0, NULL); + + if (lpMsgBuf) + { + strncpy(buf, lpMsgBuf, buflen-1); + buf[buflen-1] = 0; + LocalFree((HLOCAL)lpMsgBuf); + } + else + { + SysStrError_Fallback(errnum, buf, buflen); + } + + return buf; + +#elif (!defined(__GNU_LIBRARY__) && !defined(__GLIBC__) ) \ + || (( (_POSIX_C_SOURCE >= 200112L) || (_XOPEN_SOURCE >= 600)) && ! _GNU_SOURCE ) + // POSIX/XSI-compliant version. + // Overall general POSIX version: returns status. + // 0 for success, otherwise it's: + // - possibly -1 and the error code is in ::errno + // - possibly the error code itself + // The details of the errror are not interesting; simply + // craft a fallback message in this case. + if (strerror_r(errnum, buf, buflen) != 0) + { + return SysStrError_Fallback(errnum, buf, buflen); + } + return buf; +#else + // GLIBC is non-standard under these conditions. + // GNU version: returns the pointer to the message. + // This is either equal to the local buffer (buf) + // or some system-wide (constant) storage. To maintain + // stability of the API, this overall function shall + // always return the local buffer and the message in + // this buffer - so these cases should be distinguished + // and the internal storage copied to the buffer. + + char * gnu_buffer = strerror_r(errnum, buf, buflen); + if (!gnu_buffer) + { + // This should never happen, so just a paranoid check + return SysStrError_Fallback(errnum, buf, buflen); + } + + // If they are the same, the message is already copied + // (and it's usually a "fallback message" for an error case). + if (gnu_buffer != buf) + { + strncpy(buf, gnu_buffer, buflen-1); + buf[buflen-1] = 0; // guarantee what strncpy doesn't + } + + return buf; +#endif +} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.h b/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.h new file mode 100644 index 000000000..c05606bbd --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.h @@ -0,0 +1,105 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef HAISRT_COMPAT_H__ +#define HAISRT_COMPAT_H__ + +#include +#include + +#ifndef SRT_API +#ifdef _WIN32 + #ifndef __MINGW__ + #ifdef SRT_DYNAMIC + #ifdef SRT_EXPORTS + #define SRT_API __declspec(dllexport) + #else + #define SRT_API __declspec(dllimport) + #endif + #else + #define SRT_API + #endif + #else + #define SRT_API + #endif +#else + #define SRT_API __attribute__ ((visibility("default"))) +#endif +#endif + +#ifdef _WIN32 + // https://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx + // printf() Format for ssize_t + #if !defined(PRIzd) + #define PRIzd "Id" + #endif + // printf() Format for size_t + #if !defined(PRIzu) + #define PRIzu "Iu" + #endif +#else + // http://www.gnu.org/software/libc/manual/html_node/Integer-Conversions.html + // printf() Format for ssize_t + #if !defined(PRIzd) + #define PRIzd "zd" + #endif + // printf() Format for size_t + #if !defined(PRIzu) + #define PRIzu "zu" + #endif +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ensures that we store the error in the buffer and return the bufer. */ +SRT_API const char * SysStrError(int errnum, char * buf, size_t buflen); + +#ifdef __cplusplus +} // extern C + + +// Extra C++ stuff. Included only in C++ mode. + + +#include +inline std::string SysStrError(int errnum) +{ + char buf[1024]; + return SysStrError(errnum, buf, 1024); +} + +inline struct tm SysLocalTime(time_t tt) +{ + struct tm tms; + memset(&tms, 0, sizeof tms); +#ifdef _WIN32 + errno_t rr = localtime_s(&tms, &tt); + if (rr == 0) + return tms; +#else + tms = *localtime_r(&tt, &tms); +#endif + + return tms; +} + + +#endif // defined C++ + +#endif // HAISRT_COMPAT_H__ diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt_shared.rc b/trunk/3rdparty/srt-1-fit/srtcore/srt_shared.rc new file mode 100644 index 000000000..448192878 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt_shared.rc @@ -0,0 +1,45 @@ +// Microsoft Visual C++ generated resource script. +// +#include "version.h" +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO +#ifdef SRT_VERSION_BUILD + FILEVERSION SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH, SRT_VERSION_BUILD +#else + FILEVERSION SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH +#endif + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "CompanyName", "SRT Alliance" + VALUE "FileDescription", "SRT Local Build" + VALUE "InternalName", "srt.dll" + VALUE "LegalCopyright", "" + VALUE "OriginalFilename", "srt.dll" + VALUE "ProductName", "SRT" + VALUE "ProductVersion", SRT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + diff --git a/trunk/3rdparty/srt-1-fit/srtcore/threadname.h b/trunk/3rdparty/srt-1-fit/srtcore/threadname.h new file mode 100644 index 000000000..8fdcd9dfd --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/threadname.h @@ -0,0 +1,87 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef INC__THREADNAME_H +#define INC__THREADNAME_H + +#ifdef __linux__ + +#include + +class ThreadName +{ + char old_name[128]; + char new_name[128]; + bool good; + +public: + static const size_t BUFSIZE = 128; + + static bool get(char* namebuf) + { + return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1; + } + + static bool set(const char* name) + { + return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1; + } + + + ThreadName(const char* name) + { + if ( (good = get(old_name)) ) + { + snprintf(new_name, 127, "%s", name); + new_name[127] = 0; + prctl(PR_SET_NAME, (unsigned long)new_name, 0, 0); + } + } + + ~ThreadName() + { + if ( good ) + prctl(PR_SET_NAME, (unsigned long)old_name, 0, 0); + } +}; + +#else + +// Fake class, which does nothing. You can also take a look how +// this works in other systems that are not supported here and add +// the support. This is a fallback for systems that do not support +// thread names. + +class ThreadName +{ +public: + + static bool get(char*) { return false; } + static bool set(const char*) { return false; } + + ThreadName(const char*) + { + } + + ~ThreadName() // just to make it "non-trivially-destructible" for compatibility with normal version + { + } + +}; + + + +#endif +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/udt.h b/trunk/3rdparty/srt-1-fit/srtcore/udt.h new file mode 100644 index 000000000..77f903bd5 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/udt.h @@ -0,0 +1,413 @@ +/* + * 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 01/18/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +/* WARNING!!! + * Since now this file is a "C and C++ header". + * It should be then able to be interpreted by C compiler, so + * all C++-oriented things must be ifdef'd-out by __cplusplus. + * + * Mind also comments - to prevent any portability problems, + * B/C++ comments (// -> EOL) should not be used unless the + * area is under __cplusplus condition already. + * + * NOTE: this file contains _STRUCTURES_ that are common to C and C++, + * plus some functions and other functionalities ONLY FOR C++. This + * file doesn't contain _FUNCTIONS_ predicted to be used in C - see udtc.h + */ + +#ifndef __UDT_H__ +#define __UDT_H__ + +#include "srt.h" + +/* +* SRT_ENABLE_THREADCHECK (THIS IS SET IN MAKEFILE NOT HERE) +*/ +#if defined(SRT_ENABLE_THREADCHECK) +#include +#else +#define THREAD_STATE_INIT(name) +#define THREAD_EXIT() +#define THREAD_PAUSED() +#define THREAD_RESUMED() +#define INCREMENT_THREAD_ITERATIONS() +#endif + +/* Obsolete way to define MINGW */ +#ifndef __MINGW__ +#if defined(__MINGW32__) || defined(__MINGW64__) +#define __MINGW__ 1 +#endif +#endif + +#ifdef __cplusplus +#include +#include +#include +#include +#endif + + +// Legacy/backward/deprecated +#define UDT_API SRT_API + +//////////////////////////////////////////////////////////////////////////////// + +//if compiling on VC6.0 or pre-WindowsXP systems +//use -DLEGACY_WIN32 + +//if compiling with MinGW, it only works on XP or above +//use -D_WIN32_WINNT=0x0501 + + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +// This facility is used only for select() function. +// This is considered obsolete and the epoll() functionality rather should be used. +typedef std::set ud_set; +#define UD_CLR(u, uset) ((uset)->erase(u)) +#define UD_ISSET(u, uset) ((uset)->find(u) != (uset)->end()) +#define UD_SET(u, uset) ((uset)->insert(u)) +#define UD_ZERO(uset) ((uset)->clear()) +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Legacy names + +#define UDT_MSS SRTO_MSS +#define UDT_SNDSYN SRTO_SNDSYN +#define UDT_RCVSYN SRTO_RCVSYN +#define UDT_FC SRTO_FC +#define UDT_SNDBUF SRTO_SNDBUF +#define UDT_RCVBUF SRTO_RCVBUF +#define UDT_LINGER SRTO_LINGER +#define UDP_SNDBUF SRTO_UDP_SNDBUF +#define UDP_RCVBUF SRTO_UDP_RCVBUF +#define UDT_MAXMSG SRTO_MAXMSG +#define UDT_MSGTTL SRTO_MSGTTL +#define UDT_RENDEZVOUS SRTO_RENDEZVOUS +#define UDT_SNDTIMEO SRTO_SNDTIMEO +#define UDT_RCVTIMEO SRTO_RCVTIMEO +#define UDT_REUSEADDR SRTO_REUSEADDR +#define UDT_MAXBW SRTO_MAXBW +#define UDT_STATE SRTO_STATE +#define UDT_EVENT SRTO_EVENT +#define UDT_SNDDATA SRTO_SNDDATA +#define UDT_RCVDATA SRTO_RCVDATA +#define SRT_SENDER SRTO_SENDER +#define SRT_TSBPDMODE SRTO_TSBPDMODE +#define SRT_TSBPDDELAY SRTO_TSBPDDELAY +#define SRT_INPUTBW SRTO_INPUTBW +#define SRT_OHEADBW SRTO_OHEADBW +#define SRT_PASSPHRASE SRTO_PASSPHRASE +#define SRT_PBKEYLEN SRTO_PBKEYLEN +#define SRT_KMSTATE SRTO_KMSTATE +#define SRT_IPTTL SRTO_IPTTL +#define SRT_IPTOS SRTO_IPTOS +#define SRT_TLPKTDROP SRTO_TLPKTDROP +#define SRT_TSBPDMAXLAG SRTO_TSBPDMAXLAG +#define SRT_RCVNAKREPORT SRTO_NAKREPORT +#define SRT_CONNTIMEO SRTO_CONNTIMEO +#define SRT_SNDPBKEYLEN SRTO_SNDPBKEYLEN +#define SRT_RCVPBKEYLEN SRTO_RCVPBKEYLEN +#define SRT_SNDPEERKMSTATE SRTO_SNDPEERKMSTATE +#define SRT_RCVKMSTATE SRTO_RCVKMSTATE + +#define UDT_EPOLL_OPT SRT_EPOLL_OPT +#define UDT_EPOLL_IN SRT_EPOLL_IN +#define UDT_EPOLL_OUT SRT_EPOLL_OUT +#define UDT_EPOLL_ERR SRT_EPOLL_ERR + +/* Binary backward compatibility obsolete options */ +#define SRT_NAKREPORT SRT_RCVNAKREPORT + +#if !defined(SRT_DISABLE_LEGACY_UDTSTATUS) +#define UDTSTATUS SRT_SOCKSTATUS +#define INIT SRTS_INIT +#define OPENED SRTS_OPENED +#define LISTENING SRTS_LISTENING +#define CONNECTING SRTS_CONNECTING +#define CONNECTED SRTS_CONNECTED +#define BROKEN SRTS_BROKEN +#define CLOSING SRTS_CLOSING +#define CLOSED SRTS_CLOSED +#define NONEXIST SRTS_NONEXIST +#endif + +//////////////////////////////////////////////////////////////////////////////// + +struct CPerfMon +{ + // global measurements + int64_t msTimeStamp; // time since the UDT entity is started, in milliseconds + int64_t pktSentTotal; // total number of sent data packets, including retransmissions + int64_t pktRecvTotal; // total number of received packets + int pktSndLossTotal; // total number of lost packets (sender side) + int pktRcvLossTotal; // total number of lost packets (receiver side) + int pktRetransTotal; // total number of retransmitted packets + int pktRcvRetransTotal; // total number of retransmitted packets received + int pktSentACKTotal; // total number of sent ACK packets + int pktRecvACKTotal; // total number of received ACK packets + int pktSentNAKTotal; // total number of sent NAK packets + int pktRecvNAKTotal; // total number of received NAK packets + int64_t usSndDurationTotal; // total time duration when UDT is sending data (idle time exclusive) + + // local measurements + int64_t pktSent; // number of sent data packets, including retransmissions + int64_t pktRecv; // number of received packets + int pktSndLoss; // number of lost packets (sender side) + int pktRcvLoss; // number of lost packets (receiver side) + int pktRetrans; // number of retransmitted packets + int pktRcvRetrans; // number of retransmitted packets received + int pktSentACK; // number of sent ACK packets + int pktRecvACK; // number of received ACK packets + int pktSentNAK; // number of sent NAK packets + int pktRecvNAK; // number of received NAK packets + double mbpsSendRate; // sending rate in Mb/s + double mbpsRecvRate; // receiving rate in Mb/s + int64_t usSndDuration; // busy sending time (i.e., idle time exclusive) + int pktReorderDistance; // size of order discrepancy in received sequences + double pktRcvAvgBelatedTime; // average time of packet delay for belated packets (packets with sequence past the ACK) + int64_t pktRcvBelated; // number of received AND IGNORED packets due to having come too late + + // instant measurements + double usPktSndPeriod; // packet sending period, in microseconds + int pktFlowWindow; // flow window size, in number of packets + int pktCongestionWindow; // congestion window size, in number of packets + int pktFlightSize; // number of packets on flight + double msRTT; // RTT, in milliseconds + double mbpsBandwidth; // estimated bandwidth, in Mb/s + int byteAvailSndBuf; // available UDT sender buffer size + int byteAvailRcvBuf; // available UDT receiver buffer size +}; + +typedef SRTSOCKET UDTSOCKET; //legacy alias + +#ifdef __cplusplus + +// Class CUDTException exposed for C++ API. +// This is actually useless, unless you'd use a DIRECT C++ API, +// however there's no such API so far. The current C++ API for UDT/SRT +// is predicted to NEVER LET ANY EXCEPTION out of implementation, +// so it's useless to catch this exception anyway. + +class UDT_API CUDTException +{ +public: + + CUDTException(CodeMajor major = MJ_SUCCESS, CodeMinor minor = MN_NONE, int err = -1); + CUDTException(const CUDTException& e); + + ~CUDTException(); + + /// Get the description of the exception. + /// @return Text message for the exception description. + + const char* getErrorMessage(); + + /// Get the system errno for the exception. + /// @return errno. + + int getErrorCode() const; + + /// Get the system network errno for the exception. + /// @return errno. + + int getErrno() const; + /// Clear the error code. + + void clear(); + +private: + CodeMajor m_iMajor; // major exception categories + CodeMinor m_iMinor; // for specific error reasons + int m_iErrno; // errno returned by the system if there is any + std::string m_strMsg; // text error message + + std::string m_strAPI; // the name of UDT function that returns the error + std::string m_strDebug; // debug information, set to the original place that causes the error + +public: // Legacy Error Code + + static const int EUNKNOWN = SRT_EUNKNOWN; + static const int SUCCESS = SRT_SUCCESS; + static const int ECONNSETUP = SRT_ECONNSETUP; + static const int ENOSERVER = SRT_ENOSERVER; + static const int ECONNREJ = SRT_ECONNREJ; + static const int ESOCKFAIL = SRT_ESOCKFAIL; + static const int ESECFAIL = SRT_ESECFAIL; + static const int ECONNFAIL = SRT_ECONNFAIL; + static const int ECONNLOST = SRT_ECONNLOST; + static const int ENOCONN = SRT_ENOCONN; + static const int ERESOURCE = SRT_ERESOURCE; + static const int ETHREAD = SRT_ETHREAD; + static const int ENOBUF = SRT_ENOBUF; + static const int EFILE = SRT_EFILE; + static const int EINVRDOFF = SRT_EINVRDOFF; + static const int ERDPERM = SRT_ERDPERM; + static const int EINVWROFF = SRT_EINVWROFF; + static const int EWRPERM = SRT_EWRPERM; + static const int EINVOP = SRT_EINVOP; + static const int EBOUNDSOCK = SRT_EBOUNDSOCK; + static const int ECONNSOCK = SRT_ECONNSOCK; + static const int EINVPARAM = SRT_EINVPARAM; + static const int EINVSOCK = SRT_EINVSOCK; + static const int EUNBOUNDSOCK = SRT_EUNBOUNDSOCK; + static const int ESTREAMILL = SRT_EINVALMSGAPI; + static const int EDGRAMILL = SRT_EINVALBUFFERAPI; + static const int ENOLISTEN = SRT_ENOLISTEN; + static const int ERDVNOSERV = SRT_ERDVNOSERV; + static const int ERDVUNBOUND = SRT_ERDVUNBOUND; + static const int EINVALMSGAPI = SRT_EINVALMSGAPI; + static const int EINVALBUFFERAPI = SRT_EINVALBUFFERAPI; + static const int EDUPLISTEN = SRT_EDUPLISTEN; + static const int ELARGEMSG = SRT_ELARGEMSG; + static const int EINVPOLLID = SRT_EINVPOLLID; + static const int EASYNCFAIL = SRT_EASYNCFAIL; + static const int EASYNCSND = SRT_EASYNCSND; + static const int EASYNCRCV = SRT_EASYNCRCV; + static const int ETIMEOUT = SRT_ETIMEOUT; + static const int ECONGEST = SRT_ECONGEST; + static const int EPEERERR = SRT_EPEERERR; +}; + +namespace UDT +{ + +typedef CUDTException ERRORINFO; +//typedef UDT_SOCKOPT SOCKOPT; +typedef CPerfMon TRACEINFO; +typedef CBytePerfMon TRACEBSTATS; +typedef ud_set UDSET; + +UDT_API extern const SRTSOCKET INVALID_SOCK; +#undef ERROR +UDT_API extern const int ERROR; + +UDT_API int startup(); +UDT_API int cleanup(); +UDT_API UDTSOCKET socket(int af, int type, int protocol); +UDT_API int bind(UDTSOCKET u, const struct sockaddr* name, int namelen); +UDT_API int bind2(UDTSOCKET u, UDPSOCKET udpsock); +UDT_API int listen(UDTSOCKET u, int backlog); +UDT_API UDTSOCKET accept(UDTSOCKET u, struct sockaddr* addr, int* addrlen); +UDT_API int connect(UDTSOCKET u, const struct sockaddr* name, int namelen); +UDT_API int close(UDTSOCKET u); +UDT_API int getpeername(UDTSOCKET u, struct sockaddr* name, int* namelen); +UDT_API int getsockname(UDTSOCKET u, struct sockaddr* name, int* namelen); +UDT_API int getsockopt(UDTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); +UDT_API int setsockopt(UDTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); +UDT_API int send(UDTSOCKET u, const char* buf, int len, int flags); +UDT_API int recv(UDTSOCKET u, char* buf, int len, int flags); + +UDT_API int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false, uint64_t srctime = 0); +UDT_API int recvmsg(UDTSOCKET u, char* buf, int len, uint64_t& srctime); +UDT_API int recvmsg(UDTSOCKET u, char* buf, int len); + +UDT_API int64_t sendfile(UDTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000); +UDT_API int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000); +UDT_API int64_t sendfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 364000); +UDT_API int64_t recvfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 7280000); + +// select and selectEX are DEPRECATED; please use epoll. +UDT_API int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout); +UDT_API int selectEx(const std::vector& fds, std::vector* readfds, + std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); + +UDT_API int epoll_create(); +UDT_API int epoll_add_usock(int eid, UDTSOCKET u, const int* events = NULL); +UDT_API int epoll_add_ssock(int eid, SYSSOCKET s, const int* events = NULL); +UDT_API int epoll_remove_usock(int eid, UDTSOCKET u); +UDT_API int epoll_remove_ssock(int eid, SYSSOCKET s); +UDT_API int epoll_update_usock(int eid, UDTSOCKET u, const int* events = NULL); +UDT_API int epoll_update_ssock(int eid, SYSSOCKET s, const int* events = NULL); +UDT_API int epoll_wait(int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, + std::set* lrfds = NULL, std::set* wrfds = NULL); +UDT_API int epoll_wait2(int eid, UDTSOCKET* readfds, int* rnum, UDTSOCKET* writefds, int* wnum, int64_t msTimeOut, + SYSSOCKET* lrfds = NULL, int* lrnum = NULL, SYSSOCKET* lwfds = NULL, int* lwnum = NULL); +UDT_API int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); +UDT_API int epoll_release(int eid); +UDT_API ERRORINFO& getlasterror(); +UDT_API int getlasterror_code(); +UDT_API const char* getlasterror_desc(); +UDT_API int bstats(UDTSOCKET u, TRACEBSTATS* perf, bool clear = true); +UDT_API SRT_SOCKSTATUS getsockstate(UDTSOCKET u); + +// This is a C++ SRT API extension. This is not a part of legacy UDT API. +UDT_API void setloglevel(srt_logging::LogLevel::type ll); +UDT_API void addlogfa(srt_logging::LogFA fa); +UDT_API void dellogfa(srt_logging::LogFA fa); +UDT_API void resetlogfa(std::set fas); +UDT_API void resetlogfa(const int* fara, size_t fara_size); +UDT_API void setlogstream(std::ostream& stream); +UDT_API void setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler); +UDT_API void setlogflags(int flags); + +UDT_API bool setstreamid(UDTSOCKET u, const std::string& sid); +UDT_API std::string getstreamid(UDTSOCKET u); + +} // namespace UDT + +// This is a log configuration used inside SRT. +// Applications using SRT, if they want to use the logging mechanism +// are free to create their own logger configuration objects for their +// own logger FA objects, or create their own. The object of this type +// is required to initialize the logger FA object. +namespace srt_logging { struct LogConfig; } +UDT_API extern srt_logging::LogConfig srt_logger_config; + + +#endif /* __cplusplus */ + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/utilities.h b/trunk/3rdparty/srt-1-fit/srtcore/utilities.h new file mode 100755 index 000000000..d323a1651 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/utilities.h @@ -0,0 +1,969 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef INC__SRT_UTILITIES_H +#define INC__SRT_UTILITIES_H + + +#ifdef __GNUG__ +#define ATR_UNUSED __attribute__((unused)) +#define ATR_DEPRECATED __attribute__((deprecated)) +#else +#define ATR_UNUSED +#define ATR_DEPRECATED +#endif + +#if defined(__cplusplus) && __cplusplus > 199711L +#define HAVE_CXX11 1 + +// For gcc 4.7, claim C++11 is supported, as long as experimental C++0x is on, +// however it's only the "most required C++11 support". +#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 7 // 4.7 only! +#define ATR_NOEXCEPT +#define ATR_CONSTEXPR +#define ATR_OVERRIDE +#define ATR_FINAL +#else +#define HAVE_FULL_CXX11 1 +#define ATR_NOEXCEPT noexcept +#define ATR_CONSTEXPR constexpr +#define ATR_OVERRIDE override +#define ATR_FINAL final +#endif + +// Microsoft Visual Studio supports C++11, but not fully, +// and still did not change the value of __cplusplus. Treat +// this special way. +// _MSC_VER == 1800 means Microsoft Visual Studio 2013. +#elif defined(_MSC_VER) && _MSC_VER >= 1800 +#define HAVE_CXX11 1 +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 +#define HAVE_FULL_CXX11 1 +#define ATR_NOEXCEPT noexcept +#define ATR_CONSTEXPR constexpr +#define ATR_OVERRIDE override +#define ATR_FINAL final +#else +#define ATR_NOEXCEPT +#define ATR_CONSTEXPR +#define ATR_OVERRIDE +#define ATR_FINAL +#endif +#else +#define HAVE_CXX11 0 +#define ATR_NOEXCEPT // throw() - bad idea +#define ATR_CONSTEXPR +#define ATR_OVERRIDE +#define ATR_FINAL + +#endif + +#if !HAVE_CXX11 && defined(REQUIRE_CXX11) && REQUIRE_CXX11 == 1 +#error "The currently compiled application required C++11, but your compiler doesn't support it." +#endif + + +// Windows warning disabler +#define _CRT_SECURE_NO_WARNINGS 1 + +#include "platform_sys.h" + +// Happens that these are defined, undefine them in advance +#undef min +#undef max + +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_CXX11 +#include +#endif + +#include +#include +#include + +// -------------- UTILITIES ------------------------ + +// --- ENDIAN --- +// Copied from: https://gist.github.com/panzi/6856583 +// License: Public Domain. + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) + +# define __WINDOWS__ + +#endif + +#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) + +# include + +// GLIBC-2.8 and earlier does not provide these macros. +// See http://linux.die.net/man/3/endian +// From https://gist.github.com/panzi/6856583 +# if defined(__GLIBC__) \ + && ( !defined(__GLIBC_MINOR__) \ + || ((__GLIBC__ < 2) \ + || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 9))) ) +# include +# if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN) + +# define htole32(x) (x) +# define le32toh(x) (x) + +# elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN) + +# define htole16(x) ((((((uint16_t)(x)) >> 8))|((((uint16_t)(x)) << 8))) +# define le16toh(x) ((((((uint16_t)(x)) >> 8))|((((uint16_t)(x)) << 8))) + +# define htole32(x) (((uint32_t)htole16(((uint16_t)(((uint32_t)(x)) >> 16)))) | (((uint32_t)htole16(((uint16_t)(x)))) << 16)) +# define le32toh(x) (((uint32_t)le16toh(((uint16_t)(((uint32_t)(x)) >> 16)))) | (((uint32_t)le16toh(((uint16_t)(x)))) << 16)) + +# else +# error Byte Order not supported or not defined. +# endif +# endif + +#elif defined(__APPLE__) + +# include + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__OpenBSD__) + +# include + +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + +# include + +#ifndef be16toh +# define be16toh(x) betoh16(x) +#endif +#ifndef le16toh +# define le16toh(x) letoh16(x) +#endif + +#ifndef be32toh +# define be32toh(x) betoh32(x) +#endif +#ifndef le32toh +# define le32toh(x) letoh32(x) +#endif + +#ifndef be64toh +# define be64toh(x) betoh64(x) +#endif +#ifndef le64toh +# define le64toh(x) letoh64(x) +#endif + +#elif defined(__WINDOWS__) + +# include + +# if BYTE_ORDER == LITTLE_ENDIAN + +# define htobe16(x) htons(x) +# define htole16(x) (x) +# define be16toh(x) ntohs(x) +# define le16toh(x) (x) + +# define htobe32(x) htonl(x) +# define htole32(x) (x) +# define be32toh(x) ntohl(x) +# define le32toh(x) (x) + +# define htobe64(x) htonll(x) +# define htole64(x) (x) +# define be64toh(x) ntohll(x) +# define le64toh(x) (x) + +# elif BYTE_ORDER == BIG_ENDIAN + + /* that would be xbox 360 */ +# define htobe16(x) (x) +# define htole16(x) __builtin_bswap16(x) +# define be16toh(x) (x) +# define le16toh(x) __builtin_bswap16(x) + +# define htobe32(x) (x) +# define htole32(x) __builtin_bswap32(x) +# define be32toh(x) (x) +# define le32toh(x) __builtin_bswap32(x) + +# define htobe64(x) (x) +# define htole64(x) __builtin_bswap64(x) +# define be64toh(x) (x) +# define le64toh(x) __builtin_bswap64(x) + +# else + +# error byte order not supported + +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#else + +# error Endian: platform not supported + +#endif + +// Hardware <--> Network (big endian) convention +inline void HtoNLA(uint32_t* dst, const uint32_t* src, size_t size) +{ + for (size_t i = 0; i < size; ++ i) + dst[i] = htonl(src[i]); +} + +inline void NtoHLA(uint32_t* dst, const uint32_t* src, size_t size) +{ + for (size_t i = 0; i < size; ++ i) + dst[i] = ntohl(src[i]); +} + +// Hardware <--> Intel (little endian) convention +inline void HtoILA(uint32_t* dst, const uint32_t* src, size_t size) +{ + for (size_t i = 0; i < size; ++ i) + dst[i] = htole32(src[i]); +} + +inline void ItoHLA(uint32_t* dst, const uint32_t* src, size_t size) +{ + for (size_t i = 0; i < size; ++ i) + dst[i] = le32toh(src[i]); +} + +// Bit numbering utility. +// +// This is something that allows you to turn 32-bit integers into bit fields. +// Although bitfields are part of C++ language, they are not designed to be +// interchanged with 32-bit numbers, and any attempt to doing it (by placing +// inside a union, for example) is nonportable (order of bitfields inside +// same-covering 32-bit integer number is dependent on the endian), so they are +// popularly disregarded as useless. Instead the 32-bit numbers with bits +// individually selected is preferred, with usually manual playing around with +// & and | operators, as well as << and >>. This tool is designed to simplify +// the use of them. This can be used to qualify a range of bits inside a 32-bit +// number to be a separate number, you can "wrap" it by placing the integer +// value in the range of these bits, as well as "unwrap" (extract) it from +// the given place. For your own safety, use one prefix to all constants that +// concern bit ranges intended to be inside the same "bit container". +// +// Usage: typedef Bits MASKTYPE; // MASKTYPE is a name of your choice. +// +// With this defined, you can use the following members: +// - MASKTYPE::mask - to get the int32_t value with bimask (used bits set to 1, others to 0) +// - MASKTYPE::offset - to get the lowermost bit number, or number of bits to shift +// - MASKTYPE::wrap(int value) - to create a bitset where given value is encoded in given bits +// - MASKTYPE::unwrap(int bitset) - to extract an integer value from the bitset basing on mask definition +// (rightmost defaults to leftmost) +// REMEMBER: leftmost > rightmost because bit 0 is the LEAST significant one! + +template +struct BitsetMask +{ + static const bool correct = L >= R; + static const uint32_t value = (1u << L) | BitsetMask::value; +}; + +// This is kind-of functional programming. This describes a special case that is +// a "terminal case" in case when decreased L-1 (see above) reached == R. +template +struct BitsetMask +{ + static const bool correct = true; + static const uint32_t value = 1 << R; +}; + +// This is a trap for a case that BitsetMask::correct in the master template definition +// evaluates to false. This trap causes compile error and prevents from continuing +// recursive unwinding in wrong direction (and challenging the compiler's resistiveness +// for infinite loops). +template +struct BitsetMask +{ +}; + +template +struct Bits +{ + // DID YOU GET a kind-of error: 'mask' is not a member of 'Bits<3u, 5u, false>'? + // See the the above declaration of 'correct'! + static const uint32_t mask = BitsetMask::value; + static const uint32_t offset = R; + static const size_t size = L - R + 1; + + // Example: if our bitset mask is 00111100, this checks if given value fits in + // 00001111 mask (that is, does not exceed <0, 15>. + static bool fit(uint32_t value) { return (BitsetMask::value & value) == value; } + + /// 'wrap' gets some given value that should be placed in appropriate bit range and + /// returns a whole 32-bit word that has the value already at specified place. + /// To create a 32-bit container that contains already all values destined for different + /// bit ranges, simply use wrap() for each of them and bind them with | operator. + static uint32_t wrap(uint32_t baseval) { return (baseval << offset) & mask; } + + /// Extracts appropriate bit range and returns them as normal integer value. + static uint32_t unwrap(uint32_t bitset) { return (bitset & mask) >> offset; } + + template + static T unwrapt(uint32_t bitset) { return static_cast(unwrap(bitset)); } +}; + + +//inline int32_t Bit(size_t b) { return 1 << b; } +// XXX This would work only with 'constexpr', but this is +// available only in C++11. In C++03 this can be only done +// using a macro. +// +// Actually this can be expressed in C++11 using a better technique, +// such as user-defined literals: +// 2_bit --> 1 >> 2 + +#ifdef BIT +#undef BIT +#endif +#define BIT(x) (1 << (x)) + + +// ------------------------------------------------------------ +// This is something that reminds a structure consisting of fields +// of the same type, implemented as an array. It's parametrized +// by the type of fields and the type, which's values should be +// used for indexing (preferably an enum type). Whatever type is +// used for indexing, it is converted to size_t for indexing the +// actual array. +// +// The user should use it as an array: ds[DS_NAME], stating +// that DS_NAME is of enum type passed as 3rd parameter. +// However trying to do ds[0] would cause a compile error. +template +struct DynamicStruct +{ + FieldType inarray[NoOfFields]; + + void clear() + { + // As a standard library, it can be believed that this call + // can be optimized when FieldType is some integer. + std::fill(inarray, inarray + NoOfFields, FieldType()); + } + + FieldType operator[](IndexerType ix) const { return inarray[size_t(ix)]; } + FieldType& operator[](IndexerType ix) { return inarray[size_t(ix)]; } + + template + FieldType operator[](AnyOther ix) const + { + // If you can see a compile error here ('int' is not a class or struct, or + // that there's no definition of 'type' in given type), it means that you + // have used invalid data type passed to [] operator. See the definition + // of this type as DynamicStruct and see which type is required for indexing. + typename AnyOther::type wrong_usage_of_operator_index = AnyOther::type; + return inarray[size_t(ix)]; + } + + template + FieldType& operator[](AnyOther ix) + { + // If you can see a compile error here ('int' is not a class or struct, or + // that there's no definition of 'type' in given type), it means that you + // have used invalid data type passed to [] operator. See the definition + // of this type as DynamicStruct and see which type is required for indexing. + typename AnyOther::type wrong_usage_of_operator_index = AnyOther::type; + return inarray[size_t(ix)]; + } + + operator FieldType* () { return inarray; } + operator const FieldType* () const { return inarray; } + + char* raw() { return (char*)inarray; } +}; + + +// ------------------------------------------------------------ + + + +inline bool IsSet(int32_t bitset, int32_t flagset) +{ + return (bitset & flagset) == flagset; +} + +// Homecooked version of ref_t. It's a copy of std::reference_wrapper +// voided of unwanted properties and renamed to ref_t. + + +#if HAVE_CXX11 +#include +#endif + +template +class ref_t +{ + Type* m_data; + +public: + typedef Type type; + +#if HAVE_CXX11 + explicit ref_t(Type& __indata) + : m_data(std::addressof(__indata)) + { } +#else + explicit ref_t(Type& __indata) + : m_data((Type*)(&(char&)(__indata))) + { } +#endif + + ref_t(const ref_t& inref) + : m_data(inref.m_data) + { } + +#if HAVE_CXX11 + ref_t(const std::reference_wrapper& i): m_data(std::addressof(i.get())) {} +#endif + + Type& operator*() { return *m_data; } + + Type& get() const + { return *m_data; } + + Type operator->() const + { return *m_data; } +}; + +// This is required for Printable function if you have a container of pairs, +// but this function has a different definition for C++11 and C++03. +namespace srt_pair_op +{ + template + std::ostream& operator<<(std::ostream& s, const std::pair& v) + { + s << "{" << v.first << " " << v.second << "}"; + return s; + } +} + +#if HAVE_CXX11 + +// This alias was created so that 'Ref' (not 'ref') is used everywhere. +// Normally the C++11 'ref' fits perfectly here, however in C++03 mode +// it would have to be newly created. This would then cause a conflict +// between C++03 SRT and C++11 applications as well as between C++ standard +// library and SRT when SRT is compiled in C++11 mode (as it happens on +// Darwin/clang). +template +inline auto Ref(In& i) -> decltype(std::ref(i)) { return std::ref(i); } + +template +inline auto Move(In& i) -> decltype(std::move(i)) { return std::move(i); } + +// Gluing string of any type, wrapper for operator << + +template +inline Stream& Print(Stream& in) { return in;} + +template +inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args) +{ + sout << arg1; + return Print(sout, args...); +} + +template +inline std::string Sprint(Args&&... args) +{ + std::ostringstream sout; + Print(sout, args...); + return sout.str(); +} + +// We need to use UniquePtr, in the form of C++03 it will be a #define. +// Naturally will be used std::move() so that it can later painlessly +// switch to C++11. +template +using UniquePtr = std::unique_ptr; + +// Some utilities borrowed from tumux, as this is using options +// similar way. +template inline +std::string Printable(const Container& in, Value /*pseudoargument*/, Args&&... args) +{ + using namespace srt_pair_op; + std::ostringstream os; + Print(os, args...); + os << "[ "; + for (auto i: in) + os << Value(i) << " "; + os << "]"; + return os.str(); +} + +template inline +std::string Printable(const Container& in) +{ + using namespace srt_pair_op; + using Value = typename Container::value_type; + return Printable(in, Value()); +} + +template +auto map_get(Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type()) -> typename Map::mapped_type +{ + auto it = m.find(key); + return it == m.end() ? def : it->second; +} + +template +auto map_getp(Map& m, const Key& key) -> typename Map::mapped_type* +{ + auto it = m.find(key); + return it == m.end() ? nullptr : std::addressof(it->second); +} + +template +auto map_getp(const Map& m, const Key& key) -> typename Map::mapped_type const* +{ + auto it = m.find(key); + return it == m.end() ? nullptr : std::addressof(it->second); +} + + +#else + +template +ref_t Ref(Type& arg) +{ + return ref_t(arg); +} + +// The unique_ptr requires C++11, and the rvalue-reference feature, +// so here we're simulate the behavior using the old std::auto_ptr. + +// This is only to make a "move" call transparent and look ok towards +// the C++11 code. +template +std::auto_ptr_ref Move(const std::auto_ptr_ref& in) { return in; } + +// We need to provide also some fixes for this type that were not present in auto_ptr, +// but they are present in unique_ptr. + +// C++03 doesn't have a templated typedef, but still we need some things +// that can only function as a class. +template +class UniquePtr: public std::auto_ptr +{ + typedef std::auto_ptr Base; + +public: + + // This is a template - so method names must be declared explicitly + typedef typename Base::element_type element_type; + using Base::get; + using Base::reset; + + // All constructor declarations must be repeated. + // "Constructor delegation" is also only C++11 feature. + explicit UniquePtr(element_type* __p = 0) throw() : Base(__p) {} + UniquePtr(UniquePtr& __a) throw() : Base(__a) { } + template + UniquePtr(UniquePtr<_Tp1>& __a) throw() : Base(__a) {} + + UniquePtr& operator=(UniquePtr& __a) throw() { return Base::operator=(__a); } + template + UniquePtr& operator=(UniquePtr<_Tp1>& __a) throw() { return Base::operator=(__a); } + + // Good, now we need to add some parts of the API of unique_ptr. + + bool operator==(const UniquePtr& two) const { return get() == two.get(); } + bool operator!=(const UniquePtr& two) const { return get() != two.get(); } + + bool operator==(const element_type* two) const { return get() == two; } + bool operator!=(const element_type* two) const { return get() != two; } + + operator bool () { return 0!= get(); } +}; + +// A primitive one-argument version of Printable +template inline +std::string Printable(const Container& in) +{ + using namespace srt_pair_op; + typedef typename Container::value_type Value; + std::ostringstream os; + os << "[ "; + for (typename Container::const_iterator i = in.begin(); i != in.end(); ++i) + os << Value(*i) << " "; + os << "]"; + + return os.str(); +} + +template +typename Map::mapped_type map_get(Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type()) +{ + typename Map::iterator it = m.find(key); + return it == m.end() ? def : it->second; +} + +template +typename Map::mapped_type map_get(const Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type()) +{ + typename Map::const_iterator it = m.find(key); + return it == m.end() ? def : it->second; +} + +template +typename Map::mapped_type* map_getp(Map& m, const Key& key) +{ + typename Map::iterator it = m.find(key); + return it == m.end() ? (typename Map::mapped_type*)0 : &(it->second); +} + +template +typename Map::mapped_type const* map_getp(const Map& m, const Key& key) +{ + typename Map::const_iterator it = m.find(key); + return it == m.end() ? (typename Map::mapped_type*)0 : &(it->second); +} + +#endif + +template +struct CallbackHolder +{ + void* opaque; + Signature* fn; + + CallbackHolder(): opaque(NULL), fn(NULL) {} + + void set(void* o, Signature* f) + { + // Test if the pointer is a pointer to function. Don't let + // other type of pointers here. +#if HAVE_CXX11 + static_assert(std::is_function::value, "CallbackHolder is for functions only!"); +#else + // This is a poor-man's replacement, which should in most compilers + // generate a warning, if `Signature` resolves to a value type. + // This would make an illegal pointer cast from a value to a function type. + // Casting function-to-function, however, should not. Unfortunately + // newer compilers disallow that, too (when a signature differs), but + // then they should better use the C++11 way, much more reliable and safer. + void* (*testfn)(void*) ATR_UNUSED = (void*(*)(void*))f; +#endif + opaque = o; + fn = f; + } + + operator bool() { return fn != NULL; } +}; + +#define CALLBACK_CALL(holder,...) (*holder.fn)(holder.opaque, __VA_ARGS__) + +inline std::string FormatBinaryString(const uint8_t* bytes, size_t size) +{ + if ( size == 0 ) + return ""; + + //char buf[256]; + using namespace std; + + ostringstream os; + + // I know, it's funny to use sprintf and ostringstream simultaneously, + // but " %02X" in iostream is: << " " << hex << uppercase << setw(2) << setfill('0') << VALUE << setw(1) + // Too noisy. OTOH ostringstream solves the problem of memory allocation + // for a string of unpredictable size. + //sprintf(buf, "%02X", int(bytes[0])); + + os.fill('0'); + os.width(2); + os.setf(ios::basefield, ios::hex); + os.setf(ios::uppercase); + + //os << buf; + os << int(bytes[0]); + + + for (size_t i = 1; i < size; ++i) + { + //sprintf(buf, " %02X", int(bytes[i])); + //os << buf; + os << int(bytes[i]); + } + return os.str(); +} + + +/// This class is useful in every place where +/// the time drift should be traced. It's currently in use in every +/// solution that implements any kind of TSBPD. +template +class DriftTracer +{ + int64_t m_qDrift; + int64_t m_qOverdrift; + + int64_t m_qDriftSum; + unsigned m_uDriftSpan; + +public: + DriftTracer() + : m_qDrift(0) + , m_qOverdrift(0) + , m_qDriftSum(0) + , m_uDriftSpan(0) + {} + + bool update(int64_t driftval) + { + m_qDriftSum += driftval; + ++m_uDriftSpan; + + if (m_uDriftSpan < MAX_SPAN) + return false; + + if (CLEAR_ON_UPDATE) + m_qOverdrift = 0; + + // Calculate the median of all drift values. + // In most cases, the divisor should be == MAX_SPAN. + m_qDrift = m_qDriftSum / m_uDriftSpan; + + // And clear the collection + m_qDriftSum = 0; + m_uDriftSpan = 0; + + // In case of "overdrift", save the overdriven value in 'm_qOverdrift'. + // In clear mode, you should add this value to the time base when update() + // returns true. The drift value will be since now measured with the + // overdrift assumed to be added to the base. + if (std::abs(m_qDrift) > MAX_DRIFT) + { + m_qOverdrift = m_qDrift < 0 ? -MAX_DRIFT : MAX_DRIFT; + m_qDrift -= m_qOverdrift; + } + + // printDriftOffset(m_qOverdrift, m_qDrift); + + // Timebase is separate + // m_qTimeBase += m_qOverdrift; + + return true; + } + + // These values can be read at any time, however if you want + // to depend on the fact that they have been changed lately, + // you have to check the return value from update(). + // + // IMPORTANT: drift() can be called at any time, just remember + // that this value may look different than before only if the + // last update() returned true, which need not be important for you. + // + // CASE: CLEAR_ON_UPDATE = true + // overdrift() should be read only immediately after update() returned + // true. It will stay available with this value until the next time when + // update() returns true, in which case the value will be cleared. + // Therefore, after calling update() if it retuns true, you should read + // overdrift() immediately an make some use of it. Next valid overdrift + // will be then relative to every previous overdrift. + // + // CASE: CLEAR_ON_UPDATE = false + // overdrift() will start from 0, but it will always keep track on + // any changes in overdrift. By manipulating the MAX_DRIFT parameter + // you can decide how high the drift can go relatively to stay below + // overdrift. + int64_t drift() const { return m_qDrift; } + int64_t overdrift() const { return m_qOverdrift; } +}; + +template +struct MapProxy +{ + std::map& mp; + const KeyType& key; + + MapProxy(std::map& m, const KeyType& k): mp(m), key(k) {} + + void operator=(const ValueType& val) + { + mp[key] = val; + } + + typename std::map::iterator find() + { + return mp.find(key); + } + + typename std::map::const_iterator find() const + { + return mp.find(key); + } + + operator ValueType() const + { + typename std::map::const_iterator p = find(); + if (p == mp.end()) + return ""; + return p->second; + } + + ValueType deflt(const ValueType& defval) const + { + typename std::map::const_iterator p = find(); + if (p == mp.end()) + return defval; + return p->second; + } + + bool exists() const + { + return find() != mp.end(); + } +}; + +inline std::string BufferStamp(const char* mem, size_t size) +{ + using namespace std; + char spread[16]; + + int n = 16-size; + if (n > 0) + memset(spread+16-n, 0, n); + memcpy(spread, mem, min(size_t(16), size)); + + // Now prepare 4 cells for uint32_t. + union + { + uint32_t sum; + char cells[4]; + }; + memset(cells, 0, 4); + + for (size_t x = 0; x < 4; ++x) + for (size_t y = 0; y < 4; ++y) + { + cells[x] += spread[x+4*y]; + } + + // Convert to hex string + + ostringstream os; + + os << hex << uppercase << setfill('0') << setw(8) << sum; + + return os.str(); +} + +template +inline void Split(const std::string & str, char delimiter, OutputIterator tokens) +{ + if ( str.empty() ) + return; // May cause crash and won't extract anything anyway + + std::size_t start; + std::size_t end = -1; + + do + { + start = end + 1; + end = str.find(delimiter, start); + *tokens = str.substr( + start, + (end == std::string::npos) ? std::string::npos : end - start); + ++tokens; + } while (end != std::string::npos); +} + +inline std::string SelectNot(const std::string& unwanted, const std::string& s1, const std::string& s2) +{ + if (s1 == unwanted) + return s2; // might be unwanted, too, but then, there's nothing you can do anyway + if (s2 == unwanted) + return s1; + + // Both have wanted values, so now compare if they are same + if (s1 == s2) + return s1; // occasionally there's a winner + + // Irresolvable situation. + return std::string(); +} + +inline std::string SelectDefault(const std::string& checked, const std::string& def) +{ + if (checked == "") + return def; + return checked; +} + +template +inline size_t safe_advance(It& it, size_t num, It end) +{ + while ( it != end && num ) + { + --num; + ++it; + } + + return num; // will be effectively 0, if reached the required point, or >0, if end was by that number earlier +} + +// This is available only in C++17, dunno why not C++11 as it's pretty useful. +template inline +ATR_CONSTEXPR size_t Size(const V (&)[N]) ATR_NOEXCEPT { return N; } + +template +inline ValueType avg_iir(ValueType old_value, ValueType new_value) +{ + return (old_value*(DEPRLEN-1) + new_value)/DEPRLEN; +} + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/version.h.in b/trunk/3rdparty/srt-1-fit/srtcore/version.h.in new file mode 100644 index 000000000..d5ab88642 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/version.h.in @@ -0,0 +1,34 @@ +/* + * 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. + *****************************************************************************/ + +#ifndef INC__SRT_VERSION_H +#define INC__SRT_VERSION_H + +// To construct version value +#define SRT_MAKE_VERSION(major, minor, patch) \ + ((patch) + ((minor)*0x100) + ((major)*0x10000)) +#define SRT_MAKE_VERSION_VALUE SRT_MAKE_VERSION + +#define SRT_VERSION_MAJOR @SRT_VERSION_MAJOR@ +#define SRT_VERSION_MINOR @SRT_VERSION_MINOR@ +#define SRT_VERSION_PATCH @SRT_VERSION_PATCH@ +#cmakedefine SRT_VERSION_BUILD @APPVEYOR_BUILD_NUMBER_STRING@ + +#define SRT_VERSION_STRING "@SRT_VERSION@" +#define SRT_VERSION_VALUE \ + SRT_MAKE_VERSION_VALUE( \ + SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH ) + +#endif // INC__SRT_VERSION_H diff --git a/trunk/3rdparty/srt-1-fit/srtcore/window.cpp b/trunk/3rdparty/srt-1-fit/srtcore/window.cpp new file mode 100644 index 000000000..dfa1449e3 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/window.cpp @@ -0,0 +1,250 @@ +/* + * 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 01/22/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#include +#include +#include "common.h" +#include "window.h" +#include + +using namespace std; + +namespace ACKWindowTools +{ + +void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t ack) +{ + r_aSeq[r_iHead].iACKSeqNo = seq; + r_aSeq[r_iHead].iACK = ack; + r_aSeq[r_iHead].TimeStamp = CTimer::getTime(); + + r_iHead = (r_iHead + 1) % size; + + // overwrite the oldest ACK since it is not likely to be acknowledged + if (r_iHead == r_iTail) + r_iTail = (r_iTail + 1) % size; +} + +int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack) +{ + if (r_iHead >= r_iTail) + { + // Head has not exceeded the physical boundary of the window + + for (int i = r_iTail, n = r_iHead; i < n; ++ i) + { + // looking for indentical ACK Seq. No. + if (seq == r_aSeq[i].iACKSeqNo) + { + // return the Data ACK it carried + r_ack = r_aSeq[i].iACK; + + // calculate RTT + int rtt = int(CTimer::getTime() - r_aSeq[i].TimeStamp); + + if (i + 1 == r_iHead) + { + r_iTail = r_iHead = 0; + r_aSeq[0].iACKSeqNo = -1; + } + else + r_iTail = (i + 1) % size; + + return rtt; + } + } + + // Bad input, the ACK node has been overwritten + return -1; + } + + // Head has exceeded the physical window boundary, so it is behind tail + for (int j = r_iTail, n = r_iHead + size; j < n; ++ j) + { + // looking for indentical ACK seq. no. + if (seq == r_aSeq[j % size].iACKSeqNo) + { + // return Data ACK + j %= size; + r_ack = r_aSeq[j].iACK; + + // calculate RTT + int rtt = int(CTimer::getTime() - r_aSeq[j].TimeStamp); + + if (j == r_iHead) + { + r_iTail = r_iHead = 0; + r_aSeq[0].iACKSeqNo = -1; + } + else + r_iTail = (j + 1) % size; + + return rtt; + } + } + + // bad input, the ACK node has been overwritten + return -1; +} +} + +//////////////////////////////////////////////////////////////////////////////// + +void CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize) +{ + for (size_t i = 0; i < asize; ++ i) + r_pktWindow[i] = 1000000; //1 sec -> 1 pkt/sec + + for (size_t k = 0; k < psize; ++ k) + r_probeWindow[k] = 1000; //1 msec -> 1000 pkts/sec + + for (size_t i = 0; i < asize; ++ i) + r_bytesWindow[i] = CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i] +} + + +int CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, const int* abytes, size_t asize, int& bytesps) +{ + // get median value, but cannot change the original value order in the window + std::copy(window, window + asize, replica); + std::nth_element(replica, replica + (asize / 2), replica + asize); + //std::sort(replica, replica + asize); + int median = replica[asize / 2]; + + unsigned count = 0; + int sum = 0; + int upper = median << 3; + int lower = median >> 3; + + bytesps = 0; + unsigned long bytes = 0; + const int* bp = abytes; + // median filtering + const int* p = window; + for (int i = 0, n = asize; i < n; ++ i) + { + if ((*p < upper) && (*p > lower)) + { + ++ count; //packet counter + sum += *p; //usec counter + bytes += (unsigned long)*bp; //byte counter + } + ++ p; //advance packet pointer + ++ bp; //advance bytes pointer + } + + // claculate speed, or return 0 if not enough valid value + if (count > (asize >> 1)) + { + bytes += (CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received + bytesps = (unsigned long)ceil(1000000.0 / (double(sum) / double(bytes))); + return (int)ceil(1000000.0 / (sum / count)); + } + else + { + bytesps = 0; + return 0; + } +} + +int CPktTimeWindowTools::getBandwidth_in(const int* window, int* replica, size_t psize) +{ + // This calculation does more-less the following: + // + // 1. Having example window: + // - 50, 51, 100, 55, 80, 1000, 600, 1500, 1200, 10, 90 + // 2. This window is now sorted, but we only know the value in the middle: + // - 10, 50, 51, 55, 80, [[90]], 100, 600, 1000, 1200, 1500 + // 3. Now calculate: + // - lower: 90/8 = 11.25 + // - upper: 90*8 = 720 + // 4. Now calculate the arithmetic median from all these values, + // but drop those from outside the range: + // - 10, (11<) [ 50, 51, 55, 80, 90, 100, 600, ] (>720) 1000, 1200, 1500 + // 5. Calculate the median from the extracted range, + // NOTE: the median is actually repeated once, so size is +1. + // + // values = { 50, 51, 55, 80, 90, 100, 600 }; + // sum = 90 + accumulate(values); ==> 1026 + // median = sum/(1 + values.size()); ==> 147 + // + // For comparison: the overall arithmetic median from this window == 430 + // + // 6. Returned value = 1M/median + + // get median value, but cannot change the original value order in the window + std::copy(window, window + psize - 1, replica); + std::nth_element(replica, replica + (psize / 2), replica + psize - 1); + //std::sort(replica, replica + psize); <--- was used for debug, just leave it as a mark + int median = replica[psize / 2]; + + int count = 1; + int sum = median; + int upper = median << 3; // median*8 + int lower = median >> 3; // median/8 + + // median filtering + const int* p = window; + for (int i = 0, n = psize; i < n; ++ i) + { + if ((*p < upper) && (*p > lower)) + { + ++ count; + sum += *p; + } + ++ p; + } + + return (int)ceil(1000000.0 / (double(sum) / double(count))); +} + + diff --git a/trunk/3rdparty/srt-1-fit/srtcore/window.h b/trunk/3rdparty/srt-1-fit/srtcore/window.h new file mode 100644 index 000000000..418288372 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/window.h @@ -0,0 +1,352 @@ +/* + * 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 01/22/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef __UDT_WINDOW_H__ +#define __UDT_WINDOW_H__ + + +#ifndef _WIN32 + #include + #include +#endif +#include "udt.h" +#include "packet.h" + +namespace ACKWindowTools +{ + struct Seq + { + int32_t iACKSeqNo; // Seq. No. for the ACK packet + int32_t iACK; // Data Seq. No. carried by the ACK packet + uint64_t TimeStamp; // The timestamp when the ACK was sent + }; + + void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t ack); + int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack); +} + +template +class CACKWindow +{ +public: + CACKWindow() : + m_aSeq(), + m_iHead(0), + m_iTail(0) + { + m_aSeq[0].iACKSeqNo = -1; + } + + ~CACKWindow() {} + + /// Write an ACK record into the window. + /// @param [in] seq ACK seq. no. + /// @param [in] ack DATA ACK no. + + void store(int32_t seq, int32_t ack) + { + return ACKWindowTools::store(m_aSeq, SIZE, m_iHead, m_iTail, seq, ack); + } + + /// Search the ACK-2 "seq" in the window, find out the DATA "ack" and caluclate RTT . + /// @param [in] seq ACK-2 seq. no. + /// @param [out] ack the DATA ACK no. that matches the ACK-2 no. + /// @return RTT. + + int acknowledge(int32_t seq, int32_t& r_ack) + { + return ACKWindowTools::acknowledge(m_aSeq, SIZE, m_iHead, m_iTail, seq, r_ack); + } + +private: + + typedef ACKWindowTools::Seq Seq; + + Seq m_aSeq[SIZE]; + int m_iHead; // Pointer to the lastest ACK record + int m_iTail; // Pointer to the oldest ACK record + +private: + CACKWindow(const CACKWindow&); + CACKWindow& operator=(const CACKWindow&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CPktTimeWindowTools +{ +public: + static int getPktRcvSpeed_in(const int* window, int* replica, const int* bytes, size_t asize, int& bytesps); + static int getBandwidth_in(const int* window, int* replica, size_t psize); + + static void initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize); +}; + +template +class CPktTimeWindow: CPktTimeWindowTools +{ +public: + CPktTimeWindow(): + m_aPktWindow(), + m_aBytesWindow(), + m_iPktWindowPtr(0), + m_aProbeWindow(), + m_iProbeWindowPtr(0), + m_iLastSentTime(0), + m_iMinPktSndInt(1000000), + m_LastArrTime(), + m_CurrArrTime(), + m_ProbeTime(), + m_Probe1Sequence(-1) + { + pthread_mutex_init(&m_lockPktWindow, NULL); + pthread_mutex_init(&m_lockProbeWindow, NULL); + m_LastArrTime = CTimer::getTime(); + CPktTimeWindowTools::initializeWindowArrays(m_aPktWindow, m_aProbeWindow, m_aBytesWindow, ASIZE, PSIZE); + } + + ~CPktTimeWindow() + { + pthread_mutex_destroy(&m_lockPktWindow); + pthread_mutex_destroy(&m_lockProbeWindow); + } + + + /// read the minimum packet sending interval. + /// @return minimum packet sending interval (microseconds). + + int getMinPktSndInt() const { return m_iMinPktSndInt; } + + /// Calculate the packets arrival speed. + /// @return Packet arrival speed (packets per second). + + int getPktRcvSpeed(ref_t bytesps) const + { + // Lock access to the packet Window + CGuard cg(m_lockPktWindow); + + int pktReplica[ASIZE]; // packet information window (inter-packet time) + return getPktRcvSpeed_in(m_aPktWindow, pktReplica, m_aBytesWindow, ASIZE, *bytesps); + } + + int getPktRcvSpeed() const + { + int bytesps; + return getPktRcvSpeed(Ref(bytesps)); + } + + /// Estimate the bandwidth. + /// @return Estimated bandwidth (packets per second). + + int getBandwidth() const + { + // Lock access to the packet Window + CGuard cg(m_lockProbeWindow); + + int probeReplica[PSIZE]; + return getBandwidth_in(m_aProbeWindow, probeReplica, PSIZE); + } + + /// Record time information of a packet sending. + /// @param currtime timestamp of the packet sending. + + void onPktSent(int currtime) + { + int interval = currtime - m_iLastSentTime; + + if ((interval < m_iMinPktSndInt) && (interval > 0)) + m_iMinPktSndInt = interval; + + m_iLastSentTime = currtime; + } + + /// Record time information of an arrived packet. + + void onPktArrival(int pktsz = 0) + { + CGuard cg(m_lockPktWindow); + + m_CurrArrTime = CTimer::getTime(); + + // record the packet interval between the current and the last one + m_aPktWindow[m_iPktWindowPtr] = int(m_CurrArrTime - m_LastArrTime); + m_aBytesWindow[m_iPktWindowPtr] = pktsz; + + // the window is logically circular + ++ m_iPktWindowPtr; + if (m_iPktWindowPtr == ASIZE) + m_iPktWindowPtr = 0; + + // remember last packet arrival time + m_LastArrTime = m_CurrArrTime; + } + + /// Shortcut to test a packet for possible probe 1 or 2 + void probeArrival(const CPacket& pkt, bool unordered) + { + const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; + + // for probe1, we want 16th packet + if (inorder16 == 0) + { + probe1Arrival(pkt, unordered); + } + + if (unordered) + return; + + // for probe2, we want 17th packet + if (inorder16 == 1) + { + probe2Arrival(pkt); + } + } + + /// Record the arrival time of the first probing packet. + void probe1Arrival(const CPacket& pkt, bool unordered) + { + if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) + { + // Reset the starting probe into "undefined", when + // a packet has come as retransmitted before the + // measurement at arrival of 17th could be taken. + m_Probe1Sequence = -1; + return; + } + + m_ProbeTime = CTimer::getTime(); + m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken + } + + /// Record the arrival time of the second probing packet and the interval between packet pairs. + + void probe2Arrival(const CPacket& pkt) + { + // Reject probes that don't refer to the very next packet + // towards the one that was lately notified by probe1Arrival. + // Otherwise the result can be stupid. + + // Simply, in case when this wasn't called exactly for the + // expected packet pair, behave as if the 17th packet was lost. + + // no start point yet (or was reset) OR not very next packet + if (m_Probe1Sequence == -1 || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo) + return; + + // Grab the current time before trying to acquire + // a mutex. This might add extra delay and therefore + // screw up the measurement. + const uint64_t now = CTimer::getTime(); + + // Lock access to the packet Window + CGuard cg(m_lockProbeWindow); + + m_CurrArrTime = now; + + // Reset the starting probe to prevent checking if the + // measurement was already taken. + m_Probe1Sequence = -1; + + // record the probing packets interval + // Adjust the time for what a complete packet would have take + const int64_t timediff = m_CurrArrTime - m_ProbeTime; + const int64_t timediff_times_pl_size = timediff * CPacket::SRT_MAX_PAYLOAD_SIZE; + + // Let's take it simpler than it is coded here: + // (stating that a packet has never zero size) + // + // probe_case = (now - previous_packet_time) * SRT_MAX_PAYLOAD_SIZE / pktsz; + // + // Meaning: if the packet is fully packed, probe_case = timediff. + // Otherwise the timediff will be "converted" to a time that a fully packed packet "would take", + // provided the arrival time is proportional to the payload size and skipping + // the ETH+IP+UDP+SRT header part elliminates the constant packet delivery time influence. + // + const size_t pktsz = pkt.getLength(); + m_aProbeWindow[m_iProbeWindowPtr] = pktsz ? timediff_times_pl_size / pktsz : int(timediff); + + // OLD CODE BEFORE BSTATS: + // record the probing packets interval + // m_aProbeWindow[m_iProbeWindowPtr] = int(m_CurrArrTime - m_ProbeTime); + + // the window is logically circular + ++ m_iProbeWindowPtr; + if (m_iProbeWindowPtr == PSIZE) + m_iProbeWindowPtr = 0; + } + + +private: + int m_aPktWindow[ASIZE]; // packet information window (inter-packet time) + int m_aBytesWindow[ASIZE]; // + int m_iPktWindowPtr; // position pointer of the packet info. window. + mutable pthread_mutex_t m_lockPktWindow; // used to synchronize access to the packet window + + int m_aProbeWindow[PSIZE]; // record inter-packet time for probing packet pairs + int m_iProbeWindowPtr; // position pointer to the probing window + mutable pthread_mutex_t m_lockProbeWindow; // used to synchronize access to the probe window + + int m_iLastSentTime; // last packet sending time + int m_iMinPktSndInt; // Minimum packet sending interval + + uint64_t m_LastArrTime; // last packet arrival time + uint64_t m_CurrArrTime; // current packet arrival time + uint64_t m_ProbeTime; // arrival time of the first probing packet + int32_t m_Probe1Sequence; // sequence number for which the arrival time was notified + +private: + CPktTimeWindow(const CPktTimeWindow&); + CPktTimeWindow &operator=(const CPktTimeWindow&); +}; + + +#endif diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index 4b059b314..ae8ec91e7 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -102,6 +102,23 @@ function Ubuntu_prepare() fi fi + if [[ $SRS_SRT == YES ]]; then + echo "SRT enable, install depend tools" + tclsh <<< "exit" >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "Installing tcl." + require_sudoer "sudo apt-get install -y --force-yes tcl" + sudo apt-get install -y --force-yes tcl; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "The tcl is installed." + fi + + cmake --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "Installing cmake." + require_sudoer "sudo apt-get install -y --force-yes cmake" + sudo apt-get install -y --force-yes cmake; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "The cmake is installed." + fi + fi + pkg-config --version >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then echo "Installing pkg-config." require_sudoer "sudo apt-get install -y --force-yes pkg-config" @@ -181,6 +198,23 @@ function Centos_prepare() fi fi + if [[ $SRS_SRT == YES ]]; then + echo "SRT enable, install depend tools" + tclsh <<< "exit" >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "Installing tcl." + require_sudoer "sudo yum install -y tcl" + sudo yum install -y tcl; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "The tcl is installed." + fi + + cmake --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "Installing cmake." + require_sudoer "sudo yum install -y cmake" + sudo yum install -y cmake; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "The cmake is installed." + fi + fi + pkg-config --version --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then echo "Please install pkg-config"; exit -1; fi @@ -278,6 +312,23 @@ function OSX_prepare() fi fi + if [[ $SRS_SRT == YES ]]; then + echo "SRT enable, install depend tools" + tclsh <<< "exit" >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "Installing tcl." + echo "brew install tcl." + brew install tcl; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install tcl success" + fi + + cmake --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then + echo "Installing cmake." + echo "brew install cmake." + brew install cmake; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi + echo "install cmake success" + fi + fi + echo "OSX install tools success" return 0 } @@ -689,16 +740,34 @@ fi # SRT module, https://github.com/ossrs/srs/issues/1147#issuecomment-577469119 ##################################################################################### if [[ $SRS_SRT == YES ]]; then - if [[ -f /usr/local/lib64/libsrt.a && ! -f ${SRS_OBJS}/srt/lib/libsrt.a ]]; then - mkdir -p ${SRS_OBJS}/srt/lib && ln -sf /usr/local/lib64/libsrt.a ${SRS_OBJS}/srt/lib/libsrt.a - mkdir -p ${SRS_OBJS}/srt/include && ln -sf /usr/local/include/srt ${SRS_OBJS}/srt/include/ - fi - if [[ -f ${SRS_OBJS}/srt/lib/libsrt.a ]]; then - echo "libsrt-1.4.1 is ok."; + if [[ -f ${SRS_OBJS}/${SRS_PLATFORM}/srt/lib/libsrt.a ]]; then + echo "libsrt-1-fit is ok."; else - echo "no libsrt, please run in docker ossrs/srs:srt or build from source https://github.com/ossrs/srs/issues/1147#issuecomment-577469119"; - exit -1; + echo "Build srt-1-fit" + ( + if [[ ! -d ${SRS_OBJS}/${SRS_PLATFORM}/openssl/lib/pkgconfig ]]; then + echo "OpenSSL pkgconfig no found, build srt-1-fit failed."; + exit -1; + fi + # Always disable c++11 for libsrt, because only the srt-app requres it. + LIBSRT_OPTIONS="--disable-app --enable-shared=0 --enable-static --enable-c++11=0" + # Start build libsrt. + rm -rf ${SRS_OBJS}/${SRS_PLATFORM}/srt-1-fit && cd ${SRS_OBJS}/${SRS_PLATFORM} && + cp -R ../../3rdparty/srt-1-fit srt-1-fit && cd srt-1-fit && + PKG_CONFIG_PATH=../openssl/lib/pkgconfig ./configure --prefix=`pwd`/_release $LIBSRT_OPTIONS && + make ${SRS_JOBS} && make install && + cd .. && rm -rf srt && ln -sf srt-1-fit/_release srt && + # If exists lib64 of libsrt, link it to lib + if [[ -d srt/lib64 ]]; then + cd srt && ln -sf lib64 lib + fi + ) + ret=$?; if [[ $ret -ne 0 ]]; then echo "Build srt-1-fit failed, ret=$ret"; exit $ret; fi fi + # Always update the links. + (cd ${SRS_OBJS}/${SRS_PLATFORM} && rm -rf srt && ln -sf srt-1-fit/_release srt) + (cd ${SRS_OBJS} && rm -rf srt && ln -sf ${SRS_PLATFORM}/srt-1-fit/_release srt) + if [ ! -f ${SRS_OBJS}/srt/lib/libsrt.a ]; then echo "Build srt-1-fit failed."; exit -1; fi fi ##################################################################################### diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh index 5fc914038..55631a5fc 100755 --- a/trunk/auto/options.sh +++ b/trunk/auto/options.sh @@ -447,12 +447,13 @@ function apply_detail_options() { SRS_X86_X64=YES; opt="--x86-x64 $opt"; fi - # Enable c++11 for SRT. + # The SRT code in SRS requires c++11, although we build libsrt without c++11. + # TODO: FIXME: Remove c++11 code in SRT of SRS. if [[ $SRS_SRT == YES ]]; then SRS_CXX11=YES fi - # Enable FFmpeg fit for RTC to trancode audio from AAC to OPUS, if user has't disabled it. + # Enable FFmpeg fit for RTC to transcode audio from AAC to OPUS, if user enabled it. if [[ $SRS_RTC == YES && $SRS_FFMPEG_FIT == RESERVED ]]; then SRS_FFMPEG_FIT=YES fi diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 2b8e36dc9..275ce9568 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -520,10 +520,6 @@ rtc_server { # @see https://github.com/ossrs/srs/issues/307#issuecomment-612806318 # default: off merge_nalus off; - # Whether enable the perf stat at http://localhost:1985/api/v1/perf - # TODO: FIXME: We should enable it when refined. - # default: off - perf_stat off; # The black-hole to copy packet to, for debugging. # For example, when debugging Chrome publish stream, the received packets are encrypted cipher, # we can set the publisher black-hole, SRS will copy the plaintext packets to black-hole, and diff --git a/trunk/conf/srt.conf b/trunk/conf/srt.conf index f60ce5ded..396dccd3a 100644 --- a/trunk/conf/srt.conf +++ b/trunk/conf/srt.conf @@ -27,7 +27,13 @@ srt_server { # @doc https://github.com/ossrs/srs/issues/1147#issuecomment-577607026 vhost __defaultVhost__ { + http_remux { + enabled on; + mount [vhost]/[app]/[stream].flv; + } } + +# For SRT to use vhost. vhost srs.srt.com.cn { } diff --git a/trunk/conf/srt2rtc.conf b/trunk/conf/srt2rtc.conf new file mode 100644 index 000000000..ff8352ef2 --- /dev/null +++ b/trunk/conf/srt2rtc.conf @@ -0,0 +1,48 @@ + +listen 1935; +max_connections 1000; +daemon off; +srs_log_tank console; + +http_api { + enabled on; + listen 1985; +} + +http_server { + enabled on; + listen 8080; + dir ./objs/nginx/html; +} + +srt_server { + enabled on; + listen 10080; + maxbw 1000000000; + connect_timeout 4000; + peerlatency 300; + recvlatency 300; +} + +rtc_server { + enabled on; + # Listen at udp://8000 + listen 8000; + # + # The $CANDIDATE means fetch from env, if not configed, use * as default. + # + # The * means retrieving server IP automatically, from all network interfaces, + # @see https://github.com/ossrs/srs/wiki/v4_CN_RTCWiki#config-candidate + candidate $CANDIDATE; +} + +# @doc https://github.com/ossrs/srs/issues/1147#issuecomment-577607026 +vhost __defaultVhost__ { + rtc { + enabled on; + } + http_remux { + enabled on; + mount [vhost]/[app]/[stream].flv; + } +} diff --git a/trunk/configure b/trunk/configure index 4fbe83dfa..37a9398bd 100755 --- a/trunk/configure +++ b/trunk/configure @@ -162,8 +162,8 @@ fi # srt code path if [[ $SRS_SRT == YES ]]; then - LibSRTRoot="${SRS_WORKDIR}/src/srt"; LibSRTfile="${SRS_OBJS_DIR}/srt/lib/libsrt.a" - if [[ $SRS_SHARED_SRT == YES ]]; then LibSRTfile="-lsrt"; fi + SrsSRTRoot="${SRS_WORKDIR}/src/srt" + LibSRTRoot="${SRS_OBJS_DIR}/srt/include"; LibSRTfile="${SRS_OBJS_DIR}/srt/lib/libsrt.a" fi # For iconv on macOS only, CentOS seems ok. @@ -241,12 +241,12 @@ PROTOCOL_INCS="src/protocol"; MODULE_DIR=${PROTOCOL_INCS} . auto/modules.sh PROTOCOL_OBJS="${MODULE_OBJS[@]}" # #srt protocol features. -if [ $SRS_SRT = YES ]; then +if [[ $SRS_SRT == YES ]]; then MODULE_ID="SRT" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP") - ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) + ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot} ${LibSRTRoot}) MODULE_FILES=("srt_server" "srt_handle" "srt_conn" "srt_to_rtmp" "ts_demux" "srt_data") - SRT_INCS=${LibSRTRoot}; MODULE_DIR=${LibSRTRoot} . auto/modules.sh + SRT_INCS=(${LibSRTRoot} ${SrsSRTRoot}); MODULE_DIR=${SrsSRTRoot} . auto/modules.sh SRT_OBJS="${MODULE_OBJS[@]}" fi @@ -317,7 +317,8 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then ModuleLibIncs+=("${LibFfmpegRoot[*]}") fi if [[ $SRS_SRT == YES ]]; then - ModuleLibIncs+=("${LibSRTRoot[*]}") + ModuleLibIncs+=(${LibSRTRoot}) + ModuleLibIncs+=("${SrsSRTRoot[*]}") fi MODULE_FILES=("srs_main_server") SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh @@ -333,6 +334,10 @@ fi if [[ $SRS_FFMPEG_FIT == YES ]]; then ModuleLibIncs+=("${LibFfmpegRoot[*]}") fi +if [[ $SRS_SRT == YES ]]; then + ModuleLibIncs+=(${LibSRTRoot}) + ModuleLibIncs+=("${SrsSRTRoot[*]}") +fi MODULE_FILES=() DEFINES="" # add each modules for main @@ -379,6 +384,8 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then ModuleLibIncs+=("${LibFfmpegRoot[*]}") fi if [[ $SRS_SRT == YES ]]; then + ModuleLibIncs+=(${LibSRTRoot}) + ModuleLibIncs+=("${SrsSRTRoot[*]}") MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}" fi LINK_OPTIONS="${SrsLinkOptions}${SrsGprofLink}${SrsGperfLink}" @@ -424,7 +431,7 @@ if [ $SRS_UTEST = YES ]; then ModuleLibIncs+=("${LibFfmpegRoot[*]}") fi if [[ $SRS_SRT == YES ]]; then - ModuleLibIncs+=("${LibSRTRoot[*]}") + ModuleLibIncs+=("${SrsSRTRoot[*]}") fi ModuleLibFiles=(${LibSTfile} ${LibSSLfile}) if [[ $SRS_RTC == YES ]]; then diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 6c9bb2d38..d0b257163 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -3678,7 +3678,7 @@ srs_error_t SrsConfig::check_normal_config() for (int i = 0; conf && i < (int)conf->directives.size(); i++) { string n = conf->at(i)->name; if (n != "enabled" && n != "listen" && n != "dir" && n != "candidate" && n != "ecdsa" - && n != "encrypt" && n != "reuseport" && n != "merge_nalus" && n != "perf_stat" && n != "black_hole" + && n != "encrypt" && n != "reuseport" && n != "merge_nalus" && n != "black_hole" && n != "ip_family") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal rtc_server.%s", n.c_str()); } @@ -5033,23 +5033,6 @@ bool SrsConfig::get_rtc_server_merge_nalus() return SRS_CONF_PERFER_TRUE(conf->arg0()); } -bool SrsConfig::get_rtc_server_perf_stat() -{ - static bool DEFAULT = false; - - SrsConfDirective* conf = root->get("rtc_server"); - if (!conf) { - return DEFAULT; - } - - conf = conf->get("perf_stat"); - if (!conf || conf->arg0().empty()) { - return DEFAULT; - } - - return SRS_CONF_PERFER_FALSE(conf->arg0()); -} - bool SrsConfig::get_rtc_server_black_hole() { static bool DEFAULT = false; diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 13952d244..1a4a0239b 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -542,7 +542,6 @@ public: virtual bool get_rtc_server_encrypt(); virtual int get_rtc_server_reuseport(); virtual bool get_rtc_server_merge_nalus(); - virtual bool get_rtc_server_perf_stat(); public: virtual bool get_rtc_server_black_hole(); virtual std::string get_rtc_server_black_hole_addr(); diff --git a/trunk/src/app/srs_app_coworkers.cpp b/trunk/src/app/srs_app_coworkers.cpp index 71431d1ac..226976524 100644 --- a/trunk/src/app/srs_app_coworkers.cpp +++ b/trunk/src/app/srs_app_coworkers.cpp @@ -139,7 +139,7 @@ SrsRequest* SrsCoWorkers::find_stream_info(string vhost, string app, string stre return it->second; } -srs_error_t SrsCoWorkers::on_publish(SrsSource* s, SrsRequest* r) +srs_error_t SrsCoWorkers::on_publish(SrsLiveSource* s, SrsRequest* r) { srs_error_t err = srs_success; @@ -157,7 +157,7 @@ srs_error_t SrsCoWorkers::on_publish(SrsSource* s, SrsRequest* r) return err; } -void SrsCoWorkers::on_unpublish(SrsSource* s, SrsRequest* r) +void SrsCoWorkers::on_unpublish(SrsLiveSource* s, SrsRequest* r) { string url = r->get_stream_url(); diff --git a/trunk/src/app/srs_app_coworkers.hpp b/trunk/src/app/srs_app_coworkers.hpp index 43fd69656..af9c9a1ef 100644 --- a/trunk/src/app/srs_app_coworkers.hpp +++ b/trunk/src/app/srs_app_coworkers.hpp @@ -31,7 +31,7 @@ class SrsJsonAny; class SrsRequest; -class SrsSource; +class SrsLiveSource; // For origin cluster. class SrsCoWorkers @@ -50,8 +50,8 @@ public: private: virtual SrsRequest* find_stream_info(std::string vhost, std::string app, std::string stream); public: - virtual srs_error_t on_publish(SrsSource* s, SrsRequest* r); - virtual void on_unpublish(SrsSource* s, SrsRequest* r); + virtual srs_error_t on_publish(SrsLiveSource* s, SrsRequest* r); + virtual void on_unpublish(SrsLiveSource* s, SrsRequest* r); }; #endif diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index 579d5db30..8e078a1cd 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -29,7 +29,7 @@ #include #include -class SrsSource; +class SrsLiveSource; class SrsOriginHub; class SrsRequest; class SrsBuffer; diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index d2ff82931..0cb199314 100644 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -421,7 +421,7 @@ SrsEdgeIngester::~SrsEdgeIngester() srs_freep(trd); } -srs_error_t SrsEdgeIngester::initialize(SrsSource* s, SrsPlayEdge* e, SrsRequest* r) +srs_error_t SrsEdgeIngester::initialize(SrsLiveSource* s, SrsPlayEdge* e, SrsRequest* r) { source = s; edge = e; @@ -714,7 +714,7 @@ void SrsEdgeForwarder::set_queue_size(srs_utime_t queue_size) return queue->set_queue_size(queue_size); } -srs_error_t SrsEdgeForwarder::initialize(SrsSource* s, SrsPublishEdge* e, SrsRequest* r) +srs_error_t SrsEdgeForwarder::initialize(SrsLiveSource* s, SrsPublishEdge* e, SrsRequest* r) { source = s; edge = e; @@ -917,7 +917,7 @@ SrsPlayEdge::~SrsPlayEdge() srs_freep(ingester); } -srs_error_t SrsPlayEdge::initialize(SrsSource* source, SrsRequest* req) +srs_error_t SrsPlayEdge::initialize(SrsLiveSource* source, SrsRequest* req) { srs_error_t err = srs_success; @@ -997,7 +997,7 @@ void SrsPublishEdge::set_queue_size(srs_utime_t queue_size) return forwarder->set_queue_size(queue_size); } -srs_error_t SrsPublishEdge::initialize(SrsSource* source, SrsRequest* req) +srs_error_t SrsPublishEdge::initialize(SrsLiveSource* source, SrsRequest* req) { srs_error_t err = srs_success; diff --git a/trunk/src/app/srs_app_edge.hpp b/trunk/src/app/srs_app_edge.hpp index c34a92185..5b0c83e92 100644 --- a/trunk/src/app/srs_app_edge.hpp +++ b/trunk/src/app/srs_app_edge.hpp @@ -32,7 +32,7 @@ class SrsStSocket; class SrsRtmpServer; -class SrsSource; +class SrsLiveSource; class SrsRequest; class SrsPlayEdge; class SrsPublishEdge; @@ -153,7 +153,7 @@ public: class SrsEdgeIngester : public ISrsCoroutineHandler { private: - SrsSource* source; + SrsLiveSource* source; SrsPlayEdge* edge; SrsRequest* req; SrsCoroutine* trd; @@ -163,7 +163,7 @@ public: SrsEdgeIngester(); virtual ~SrsEdgeIngester(); public: - virtual srs_error_t initialize(SrsSource* s, SrsPlayEdge* e, SrsRequest* r); + virtual srs_error_t initialize(SrsLiveSource* s, SrsPlayEdge* e, SrsRequest* r); virtual srs_error_t start(); virtual void stop(); virtual std::string get_curr_origin(); @@ -181,7 +181,7 @@ private: class SrsEdgeForwarder : public ISrsCoroutineHandler { private: - SrsSource* source; + SrsLiveSource* source; SrsPublishEdge* edge; SrsRequest* req; SrsCoroutine* trd; @@ -200,7 +200,7 @@ public: public: virtual void set_queue_size(srs_utime_t queue_size); public: - virtual srs_error_t initialize(SrsSource* s, SrsPublishEdge* e, SrsRequest* r); + virtual srs_error_t initialize(SrsLiveSource* s, SrsPublishEdge* e, SrsRequest* r); virtual srs_error_t start(); virtual void stop(); // Interface ISrsReusableThread2Handler @@ -225,7 +225,7 @@ public: // Always use the req of source, // For we assume all client to edge is invalid, // if auth open, edge must valid it from origin, then service it. - virtual srs_error_t initialize(SrsSource* source, SrsRequest* req); + virtual srs_error_t initialize(SrsLiveSource* source, SrsRequest* req); // When client play stream on edge. virtual srs_error_t on_client_play(); // When all client stopped play, disconnect to origin. @@ -248,7 +248,7 @@ public: public: virtual void set_queue_size(srs_utime_t queue_size); public: - virtual srs_error_t initialize(SrsSource* source, SrsRequest* req); + virtual srs_error_t initialize(SrsLiveSource* source, SrsRequest* req); virtual bool can_publish(); // When client publish stream on edge. virtual srs_error_t on_client_publish(); diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index 25090ebfa..583deb197 100755 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -76,7 +76,7 @@ srs_error_t SrsForwarder::initialize(SrsRequest* r, string ep) srs_error_t err = srs_success; // it's ok to use the request object, - // SrsSource already copy it and never delete it. + // SrsLiveSource already copy it and never delete it. req = r; // the ep(endpoint) to forward to diff --git a/trunk/src/app/srs_app_forward.hpp b/trunk/src/app/srs_app_forward.hpp index 4f69edc40..75bf65e2b 100644 --- a/trunk/src/app/srs_app_forward.hpp +++ b/trunk/src/app/srs_app_forward.hpp @@ -37,7 +37,7 @@ class SrsMessageQueue; class SrsRtmpJitter; class SrsRtmpClient; class SrsRequest; -class SrsSource; +class SrsLiveSource; class SrsOriginHub; class SrsKbps; class SrsSimpleRtmpClient; diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index 4663d25f9..d99ef0544 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -1432,7 +1432,7 @@ srs_error_t SrsGb28181RtmpMuxer::initialize(SrsServer *s, SrsRequest* r) req = r; server = s; - if ((err = _srs_sources->fetch_or_create(req, (ISrsSourceHandler*)server, &source)) != srs_success) { + if ((err = _srs_sources->fetch_or_create(req, (ISrsLiveSourceHandler*)server, &source)) != srs_success) { return srs_error_wrap(err, "create source"); } diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index 4d3e37fa1..268fb895b 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -100,7 +100,7 @@ class SrsGb28181StreamChannel; class SrsGb28181SipSession; class SrsRtpJitterBuffer; class SrsServer; -class SrsSource; +class SrsLiveSource; class SrsRequest; class SrsResourceManager; class SrsGb28181Conn; @@ -326,7 +326,7 @@ private: std::string aac_specific_config; SrsRequest* req; - SrsSource* source; + SrsLiveSource* source; SrsServer* server; SrsRtpJitterBuffer *jitter_buffer; diff --git a/trunk/src/app/srs_app_hds.hpp b/trunk/src/app/srs_app_hds.hpp index 513b7e9a6..092847b14 100644 --- a/trunk/src/app/srs_app_hds.hpp +++ b/trunk/src/app/srs_app_hds.hpp @@ -33,7 +33,7 @@ class SrsRequest; class SrsSharedPtrMessage; class SrsHdsFragment; -class SrsSource; +class SrsLiveSource; // Mux RTMP to Adobe HDS streaming. class SrsHds diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index 3d00672a5..6f68a7e4f 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -41,7 +41,7 @@ class SrsRtmpJitter; class SrsTsContextWriter; class SrsRequest; class SrsPithyPrint; -class SrsSource; +class SrsLiveSource; class SrsOriginHub; class SrsFileWriter; class SrsSimpleStream; diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 729ce2171..aaeaeb405 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -533,12 +533,12 @@ srs_error_t SrsHttpServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage return http_static->mux.serve_http(w, r); } -srs_error_t SrsHttpServer::http_mount(SrsSource* s, SrsRequest* r) +srs_error_t SrsHttpServer::http_mount(SrsLiveSource* s, SrsRequest* r) { return http_stream->http_mount(s, r); } -void SrsHttpServer::http_unmount(SrsSource* s, SrsRequest* r) +void SrsHttpServer::http_unmount(SrsLiveSource* s, SrsRequest* r) { http_stream->http_unmount(s, r); } diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index 4331fefe8..6615f80e0 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -38,9 +38,9 @@ #include class SrsServer; -class SrsSource; +class SrsLiveSource; class SrsRequest; -class SrsConsumer; +class SrsLiveConsumer; class SrsStSocket; class SrsHttpParser; class ISrsHttpMessage; @@ -208,8 +208,8 @@ public: public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); public: - virtual srs_error_t http_mount(SrsSource* s, SrsRequest* r); - virtual void http_unmount(SrsSource* s, SrsRequest* r); + virtual srs_error_t http_mount(SrsLiveSource* s, SrsRequest* r); + virtual void http_unmount(SrsLiveSource* s, SrsRequest* r); }; #endif diff --git a/trunk/src/app/srs_app_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp index eb4e372e9..acdb4e19a 100755 --- a/trunk/src/app/srs_app_http_stream.cpp +++ b/trunk/src/app/srs_app_http_stream.cpp @@ -57,7 +57,7 @@ using namespace std; #include #include -SrsBufferCache::SrsBufferCache(SrsSource* s, SrsRequest* r) +SrsBufferCache::SrsBufferCache(SrsLiveSource* s, SrsRequest* r) { req = r->copy()->as_http(); source = s; @@ -76,7 +76,7 @@ SrsBufferCache::~SrsBufferCache() srs_freep(req); } -srs_error_t SrsBufferCache::update_auth(SrsSource* s, SrsRequest* r) +srs_error_t SrsBufferCache::update_auth(SrsLiveSource* s, SrsRequest* r) { srs_freep(req); req = r->copy(); @@ -96,7 +96,7 @@ srs_error_t SrsBufferCache::start() return err; } -srs_error_t SrsBufferCache::dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter) +srs_error_t SrsBufferCache::dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter) { srs_error_t err = srs_success; @@ -104,7 +104,7 @@ srs_error_t SrsBufferCache::dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgor return err; } - // the jitter is get from SrsSource, which means the time_jitter of vhost. + // the jitter is get from SrsLiveSource, which means the time_jitter of vhost. if ((err = queue->dump_packets(consumer, false, jitter)) != srs_success) { return srs_error_wrap(err, "dump packets"); } @@ -127,8 +127,8 @@ srs_error_t SrsBufferCache::cycle() // the stream cache will create consumer to cache stream, // which will trigger to fetch stream from origin for edge. - SrsConsumer* consumer = NULL; - SrsAutoFree(SrsConsumer, consumer); + SrsLiveConsumer* consumer = NULL; + SrsAutoFree(SrsLiveConsumer, consumer); if ((err = source->create_consumer(consumer)) != srs_success) { return srs_error_wrap(err, "create consumer"); } @@ -241,11 +241,11 @@ srs_error_t SrsTsStreamEncoder::write_metadata(int64_t /*timestamp*/, char* /*da bool SrsTsStreamEncoder::has_cache() { - // for ts stream, use gop cache of SrsSource is ok. + // for ts stream, use gop cache of SrsLiveSource is ok. return false; } -srs_error_t SrsTsStreamEncoder::dump_cache(SrsConsumer* /*consumer*/, SrsRtmpJitterAlgorithm /*jitter*/) +srs_error_t SrsTsStreamEncoder::dump_cache(SrsLiveConsumer* /*consumer*/, SrsRtmpJitterAlgorithm /*jitter*/) { // for ts stream, ignore cache. return srs_success; @@ -308,11 +308,11 @@ srs_error_t SrsFlvStreamEncoder::write_metadata(int64_t timestamp, char* data, i bool SrsFlvStreamEncoder::has_cache() { - // for flv stream, use gop cache of SrsSource is ok. + // for flv stream, use gop cache of SrsLiveSource is ok. return false; } -srs_error_t SrsFlvStreamEncoder::dump_cache(SrsConsumer* /*consumer*/, SrsRtmpJitterAlgorithm /*jitter*/) +srs_error_t SrsFlvStreamEncoder::dump_cache(SrsLiveConsumer* /*consumer*/, SrsRtmpJitterAlgorithm /*jitter*/) { // for flv stream, ignore cache. return srs_success; @@ -412,7 +412,7 @@ bool SrsAacStreamEncoder::has_cache() return true; } -srs_error_t SrsAacStreamEncoder::dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter) +srs_error_t SrsAacStreamEncoder::dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter) { srs_assert(cache); return cache->dump_cache(consumer, jitter); @@ -468,7 +468,7 @@ bool SrsMp3StreamEncoder::has_cache() return true; } -srs_error_t SrsMp3StreamEncoder::dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter) +srs_error_t SrsMp3StreamEncoder::dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter) { srs_assert(cache); return cache->dump_cache(consumer, jitter); @@ -515,7 +515,7 @@ srs_error_t SrsBufferWriter::writev(const iovec* iov, int iovcnt, ssize_t* pnwri return writer->writev(iov, iovcnt, pnwrite); } -SrsLiveStream::SrsLiveStream(SrsSource* s, SrsRequest* r, SrsBufferCache* c) +SrsLiveStream::SrsLiveStream(SrsLiveSource* s, SrsRequest* r, SrsBufferCache* c) { source = s; cache = c; @@ -527,7 +527,7 @@ SrsLiveStream::~SrsLiveStream() srs_freep(req); } -srs_error_t SrsLiveStream::update_auth(SrsSource* s, SrsRequest* r) +srs_error_t SrsLiveStream::update_auth(SrsLiveSource* s, SrsRequest* r) { source = s; @@ -585,8 +585,8 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess w->write_header(SRS_CONSTS_HTTP_OK); // create consumer of souce, ignore gop cache, use the audio gop cache. - SrsConsumer* consumer = NULL; - SrsAutoFree(SrsConsumer, consumer); + SrsLiveConsumer* consumer = NULL; + SrsAutoFree(SrsLiveConsumer, consumer); if ((err = source->create_consumer(consumer)) != srs_success) { return srs_error_wrap(err, "create consumer"); } @@ -893,7 +893,7 @@ srs_error_t SrsHttpStreamServer::initialize() } // TODO: FIXME: rename for HTTP FLV mount. -srs_error_t SrsHttpStreamServer::http_mount(SrsSource* s, SrsRequest* r) +srs_error_t SrsHttpStreamServer::http_mount(SrsLiveSource* s, SrsRequest* r) { srs_error_t err = srs_success; @@ -967,7 +967,7 @@ srs_error_t SrsHttpStreamServer::http_mount(SrsSource* s, SrsRequest* r) return err; } -void SrsHttpStreamServer::http_unmount(SrsSource* s, SrsRequest* r) +void SrsHttpStreamServer::http_unmount(SrsLiveSource* s, SrsRequest* r) { std::string sid = r->get_stream_url(); @@ -1000,7 +1000,7 @@ srs_error_t SrsHttpStreamServer::on_reload_vhost_http_remux_updated(string vhost return srs_error_wrap(err, "init flv entry"); } - // http mount need SrsRequest and SrsSource param, only create a mapping template entry + // http mount need SrsRequest and SrsLiveSource param, only create a mapping template entry // and do mount automatically on playing http flv if this stream is a new http_remux stream. return err; } @@ -1019,7 +1019,7 @@ srs_error_t SrsHttpStreamServer::on_reload_vhost_http_remux_updated(string vhost continue; } - SrsSource* source = entry->source; + SrsLiveSource* source = entry->source; if (_srs_config->get_vhost_http_remux_enabled(vhost)) { http_mount(source, req); } else { @@ -1127,7 +1127,7 @@ srs_error_t SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandle } } - SrsSource* s = NULL; + SrsLiveSource* s = NULL; if ((err = _srs_sources->fetch_or_create(r, server, &s)) != srs_success) { return srs_error_wrap(err, "source create"); } diff --git a/trunk/src/app/srs_app_http_stream.hpp b/trunk/src/app/srs_app_http_stream.hpp index de8a25d87..a1202f152 100755 --- a/trunk/src/app/srs_app_http_stream.hpp +++ b/trunk/src/app/srs_app_http_stream.hpp @@ -40,16 +40,16 @@ private: srs_utime_t fast_cache; private: SrsMessageQueue* queue; - SrsSource* source; + SrsLiveSource* source; SrsRequest* req; SrsCoroutine* trd; public: - SrsBufferCache(SrsSource* s, SrsRequest* r); + SrsBufferCache(SrsLiveSource* s, SrsRequest* r); virtual ~SrsBufferCache(); - virtual srs_error_t update_auth(SrsSource* s, SrsRequest* r); + virtual srs_error_t update_auth(SrsLiveSource* s, SrsRequest* r); public: virtual srs_error_t start(); - virtual srs_error_t dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter); + virtual srs_error_t dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter); // Interface ISrsEndlessThreadHandler. public: virtual srs_error_t cycle(); @@ -72,11 +72,11 @@ public: virtual srs_error_t write_metadata(int64_t timestamp, char* data, int size) = 0; public: // For some stream, for example, mp3 and aac, the audio stream, - // we use large gop cache in encoder, for the gop cache of SrsSource is ignore audio. - // @return true to use gop cache of encoder; otherwise, use SrsSource. + // we use large gop cache in encoder, for the gop cache of SrsLiveSource is ignore audio. + // @return true to use gop cache of encoder; otherwise, use SrsLiveSource. virtual bool has_cache() = 0; // Dumps the cache of encoder to consumer. - virtual srs_error_t dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter) = 0; + virtual srs_error_t dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter) = 0; }; // Transmux RTMP to HTTP Live Streaming. @@ -95,7 +95,7 @@ public: virtual srs_error_t write_metadata(int64_t timestamp, char* data, int size); public: virtual bool has_cache(); - virtual srs_error_t dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter); + virtual srs_error_t dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter); public: // Write the tags in a time. virtual srs_error_t write_tags(SrsSharedPtrMessage** msgs, int count); @@ -118,7 +118,7 @@ public: virtual srs_error_t write_metadata(int64_t timestamp, char* data, int size); public: virtual bool has_cache(); - virtual srs_error_t dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter); + virtual srs_error_t dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter); }; // Transmux RTMP with AAC stream to HTTP AAC Streaming. @@ -137,7 +137,7 @@ public: virtual srs_error_t write_metadata(int64_t timestamp, char* data, int size); public: virtual bool has_cache(); - virtual srs_error_t dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter); + virtual srs_error_t dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter); }; // Transmux RTMP with MP3 stream to HTTP MP3 Streaming. @@ -156,7 +156,7 @@ public: virtual srs_error_t write_metadata(int64_t timestamp, char* data, int size); public: virtual bool has_cache(); - virtual srs_error_t dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter); + virtual srs_error_t dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter); }; // Write stream to http response direclty. @@ -184,12 +184,12 @@ class SrsLiveStream : public ISrsHttpHandler { private: SrsRequest* req; - SrsSource* source; + SrsLiveSource* source; SrsBufferCache* cache; public: - SrsLiveStream(SrsSource* s, SrsRequest* r, SrsBufferCache* c); + SrsLiveStream(SrsLiveSource* s, SrsRequest* r, SrsBufferCache* c); virtual ~SrsLiveStream(); - virtual srs_error_t update_auth(SrsSource* s, SrsRequest* r); + virtual srs_error_t update_auth(SrsLiveSource* s, SrsRequest* r); public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); private: @@ -211,7 +211,7 @@ public: // We will free the request. SrsRequest* req; // Shared source. - SrsSource* source; + SrsLiveSource* source; public: // For template, the mount contains variables. // For concrete stream, the mount is url to access. @@ -249,8 +249,8 @@ public: virtual srs_error_t initialize(); public: // HTTP flv/ts/mp3/aac stream - virtual srs_error_t http_mount(SrsSource* s, SrsRequest* r); - virtual void http_unmount(SrsSource* s, SrsRequest* r); + virtual srs_error_t http_mount(SrsLiveSource* s, SrsRequest* r); + virtual void http_unmount(SrsLiveSource* s, SrsRequest* r); // Interface ISrsReloadHandler. public: virtual srs_error_t on_reload_vhost_added(std::string vhost); diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 5376df700..d1d3b2fd4 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -164,7 +164,7 @@ srs_error_t SrsRecvThread::do_cycle() return err; } -SrsQueueRecvThread::SrsQueueRecvThread(SrsConsumer* consumer, SrsRtmpServer* rtmp_sdk, srs_utime_t tm, SrsContextId parent_cid) +SrsQueueRecvThread::SrsQueueRecvThread(SrsLiveConsumer* consumer, SrsRtmpServer* rtmp_sdk, srs_utime_t tm, SrsContextId parent_cid) : trd(this, rtmp_sdk, tm, parent_cid) { _consumer = consumer; @@ -278,7 +278,7 @@ void SrsQueueRecvThread::on_stop() } SrsPublishRecvThread::SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, SrsRequest* _req, - int mr_sock_fd, srs_utime_t tm, SrsRtmpConn* conn, SrsSource* source, SrsContextId parent_cid) + int mr_sock_fd, srs_utime_t tm, SrsRtmpConn* conn, SrsLiveSource* source, SrsContextId parent_cid) : trd(this, rtmp_sdk, tm, parent_cid) { rtmp = rtmp_sdk; diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index 792b862c0..f9e166d73 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -37,9 +37,9 @@ class SrsRtmpServer; class SrsCommonMessage; class SrsRtmpConn; -class SrsSource; +class SrsLiveSource; class SrsRequest; -class SrsConsumer; +class SrsLiveConsumer; class SrsHttpConn; class SrsResponseOnlyHttpConn; @@ -114,10 +114,10 @@ private: SrsRtmpServer* rtmp; // The recv thread error code. srs_error_t recv_error; - SrsConsumer* _consumer; + SrsLiveConsumer* _consumer; public: // TODO: FIXME: Refine timeout in time unit. - SrsQueueRecvThread(SrsConsumer* consumer, SrsRtmpServer* rtmp_sdk, srs_utime_t tm, SrsContextId parent_cid); + SrsQueueRecvThread(SrsLiveConsumer* consumer, SrsRtmpServer* rtmp_sdk, srs_utime_t tm, SrsContextId parent_cid); virtual ~SrsQueueRecvThread(); public: virtual srs_error_t start(); @@ -164,7 +164,7 @@ private: srs_error_t recv_error; SrsRtmpConn* _conn; // The params for conn callback. - SrsSource* _source; + SrsLiveSource* _source; // The error timeout cond // @see https://github.com/ossrs/srs/issues/244 srs_cond_t error; @@ -173,7 +173,7 @@ private: SrsContextId ncid; public: SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, SrsRequest* _req, - int mr_sock_fd, srs_utime_t tm, SrsRtmpConn* conn, SrsSource* source, SrsContextId parent_cid); + int mr_sock_fd, srs_utime_t tm, SrsRtmpConn* conn, SrsLiveSource* source, SrsContextId parent_cid); virtual ~SrsPublishRecvThread(); public: // Wait for error for some timeout. diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index f5c6fffe2..fe6fcdb7f 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -463,7 +463,7 @@ srs_error_t SrsRtcPlayStream::initialize(SrsRequest* req, std::mapget_rtc_to_rtmp(req->vhost); if (rtc_to_rtmp) { - SrsSource *rtmp = NULL; + SrsLiveSource *rtmp = NULL; if ((err = _srs_sources->fetch_or_create(r, _srs_hybrid->srs()->instance(), &rtmp)) != srs_success) { return srs_error_wrap(err, "create source"); } @@ -1848,8 +1848,8 @@ srs_error_t SrsRtcConnection::add_publisher(SrsRtcUserConfig* ruc, SrsSdp& local SrsRequest* req = ruc->req_; - SrsRtcStreamDescription* stream_desc = new SrsRtcStreamDescription(); - SrsAutoFree(SrsRtcStreamDescription, stream_desc); + SrsRtcSourceDescription* stream_desc = new SrsRtcSourceDescription(); + SrsAutoFree(SrsRtcSourceDescription, stream_desc); // TODO: FIXME: Change to api of stream desc. if ((err = negotiate_publish_capability(ruc, stream_desc)) != srs_success) { @@ -1860,7 +1860,7 @@ srs_error_t SrsRtcConnection::add_publisher(SrsRtcUserConfig* ruc, SrsSdp& local return srs_error_wrap(err, "generate local sdp"); } - SrsRtcStream* source = NULL; + SrsRtcSource* source = NULL; if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) { return srs_error_wrap(err, "create source"); } @@ -1905,8 +1905,8 @@ srs_error_t SrsRtcConnection::add_player(SrsRtcUserConfig* ruc, SrsSdp& local_sd return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no play relations"); } - SrsRtcStreamDescription* stream_desc = new SrsRtcStreamDescription(); - SrsAutoFree(SrsRtcStreamDescription, stream_desc); + SrsRtcSourceDescription* stream_desc = new SrsRtcSourceDescription(); + SrsAutoFree(SrsRtcSourceDescription, stream_desc); std::map::iterator it = play_sub_relations.begin(); while (it != play_sub_relations.end()) { SrsRtcTrackDescription* track_desc = it->second; @@ -2676,7 +2676,7 @@ bool srs_sdp_has_h264_profile(const SrsSdp& sdp, const string& profile) return false; } -srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRtcUserConfig* ruc, SrsRtcStreamDescription* stream_desc) +srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRtcUserConfig* ruc, SrsRtcSourceDescription* stream_desc) { srs_error_t err = srs_success; @@ -2914,7 +2914,7 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRtcUserConfig* ruc return err; } -srs_error_t SrsRtcConnection::generate_publish_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc, bool unified_plan) +srs_error_t SrsRtcConnection::generate_publish_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan) { srs_error_t err = srs_success; @@ -3031,7 +3031,7 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRtcUserConfig* ruc, s // TODO: FIME: Should check packetization-mode=1 also. bool has_42e01f = srs_sdp_has_h264_profile(remote_sdp, "42e01f"); - SrsRtcStream* source = NULL; + SrsRtcSource* source = NULL; if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) { return srs_error_wrap(err, "fetch rtc source"); } @@ -3152,7 +3152,7 @@ srs_error_t SrsRtcConnection::fetch_source_capability(SrsRequest* req, std::map< bool nack_enabled = _srs_config->get_rtc_nack_enabled(req->vhost); bool twcc_enabled = _srs_config->get_rtc_twcc_enabled(req->vhost); - SrsRtcStream* source = NULL; + SrsRtcSource* source = NULL; if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) { return srs_error_wrap(err, "fetch rtc source"); } @@ -3249,7 +3249,7 @@ void video_track_generate_play_offer(SrsRtcTrackDescription* track, string mid, } } -srs_error_t SrsRtcConnection::generate_play_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc, bool unified_plan) +srs_error_t SrsRtcConnection::generate_play_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan) { srs_error_t err = srs_success; @@ -3443,7 +3443,7 @@ srs_error_t SrsRtcConnection::create_player(SrsRequest* req, std::map class SrsUdpMuxSocket; -class SrsConsumer; +class SrsLiveConsumer; class SrsStunPacket; class SrsRtcServer; class SrsRtcConnection; class SrsSharedPtrMessage; -class SrsRtcStream; +class SrsRtcSource; class SrsRtpPacket; class ISrsCodec; class SrsRtpNackForReceiver; @@ -214,7 +214,7 @@ public: // A RTC play stream, client pull and play stream from SRS. class SrsRtcPlayStream : public ISrsCoroutineHandler, public ISrsReloadHandler - , public ISrsRtcPLIWorkerHandler, public ISrsRtcStreamChangeCallback + , public ISrsRtcPLIWorkerHandler, public ISrsRtcSourceChangeCallback { private: SrsContextId cid_; @@ -223,7 +223,7 @@ private: SrsRtcPLIWorker* pli_worker_; private: SrsRequest* req_; - SrsRtcStream* source_; + SrsRtcSource* source_; // key: publish_ssrc, value: send track to process rtp/rtcp std::map audio_tracks_; std::map video_tracks_; @@ -252,9 +252,9 @@ public: virtual ~SrsRtcPlayStream(); public: srs_error_t initialize(SrsRequest* request, std::map sub_relations); -// Interface ISrsRtcStreamChangeCallback +// Interface ISrsRtcSourceChangeCallback public: - void on_stream_change(SrsRtcStreamDescription* desc); + void on_stream_change(SrsRtcSourceDescription* desc); // interface ISrsReloadHandler public: virtual srs_error_t on_reload_vhost_play(std::string vhost); @@ -335,7 +335,7 @@ private: SrsErrorPithyPrint* pli_epp; private: SrsRequest* req; - SrsRtcStream* source; + SrsRtcSource* source; // Simulators. int nn_simulate_nack_drop; private: @@ -353,7 +353,7 @@ public: SrsRtcPublishStream(SrsRtcConnection* session, const SrsContextId& cid); virtual ~SrsRtcPublishStream(); public: - srs_error_t initialize(SrsRequest* req, SrsRtcStreamDescription* stream_desc); + srs_error_t initialize(SrsRequest* req, SrsRtcSourceDescription* stream_desc); srs_error_t start(); // Directly set the status of track, generally for init to set the default value. void set_all_tracks_status(bool status); @@ -553,15 +553,15 @@ public: private: srs_error_t on_binding_request(SrsStunPacket* r); // publish media capabilitiy negotiate - srs_error_t negotiate_publish_capability(SrsRtcUserConfig* ruc, SrsRtcStreamDescription* stream_desc); - srs_error_t generate_publish_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc, bool unified_plan); + srs_error_t negotiate_publish_capability(SrsRtcUserConfig* ruc, SrsRtcSourceDescription* stream_desc); + srs_error_t generate_publish_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan); // play media capabilitiy negotiate //TODO: Use StreamDescription to negotiate and remove first negotiate_play_capability function srs_error_t negotiate_play_capability(SrsRtcUserConfig* ruc, std::map& sub_relations); srs_error_t fetch_source_capability(SrsRequest* req, std::map& sub_relations); - srs_error_t generate_play_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc, bool unified_plan); + srs_error_t generate_play_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan); srs_error_t create_player(SrsRequest* request, std::map sub_relations); - srs_error_t create_publisher(SrsRequest* request, SrsRtcStreamDescription* stream_desc); + srs_error_t create_publisher(SrsRequest* request, SrsRtcSourceDescription* stream_desc); }; class ISrsRtcHijacker diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp index c7a062376..a6a226803 100644 --- a/trunk/src/app/srs_app_rtc_server.cpp +++ b/trunk/src/app/srs_app_rtc_server.cpp @@ -469,7 +469,7 @@ srs_error_t SrsRtcServer::create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sd SrsRequest* req = ruc->req_; - SrsRtcStream* source = NULL; + SrsRtcSource* source = NULL; if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) { return srs_error_wrap(err, "create source"); } diff --git a/trunk/src/app/srs_app_rtc_server.hpp b/trunk/src/app/srs_app_rtc_server.hpp index d302ad64b..ae6af477e 100644 --- a/trunk/src/app/srs_app_rtc_server.hpp +++ b/trunk/src/app/srs_app_rtc_server.hpp @@ -40,7 +40,7 @@ class SrsHourGlass; class SrsRtcConnection; class SrsRequest; class SrsSdp; -class SrsRtcStream; +class SrsRtcSource; class SrsResourceManager; // The UDP black hole, for developer to use wireshark to catch plaintext packets. diff --git a/trunk/src/app/srs_app_rtc_source.cpp b/trunk/src/app/srs_app_rtc_source.cpp index b068b2a70..3ee15606c 100644 --- a/trunk/src/app/srs_app_rtc_source.cpp +++ b/trunk/src/app/srs_app_rtc_source.cpp @@ -152,15 +152,15 @@ SrsNtp SrsNtp::to_time_ms(uint64_t ntp) return srs_ntp; } -ISrsRtcStreamChangeCallback::ISrsRtcStreamChangeCallback() +ISrsRtcSourceChangeCallback::ISrsRtcSourceChangeCallback() { } -ISrsRtcStreamChangeCallback::~ISrsRtcStreamChangeCallback() +ISrsRtcSourceChangeCallback::~ISrsRtcSourceChangeCallback() { } -SrsRtcConsumer::SrsRtcConsumer(SrsRtcStream* s) +SrsRtcConsumer::SrsRtcConsumer(SrsRtcSource* s) { source = s; should_update_source_id = false; @@ -240,24 +240,24 @@ void SrsRtcConsumer::wait(int nb_msgs) srs_cond_wait(mw_wait); } -void SrsRtcConsumer::on_stream_change(SrsRtcStreamDescription* desc) +void SrsRtcConsumer::on_stream_change(SrsRtcSourceDescription* desc) { if (handler_) { handler_->on_stream_change(desc); } } -SrsRtcStreamManager::SrsRtcStreamManager() +SrsRtcSourceManager::SrsRtcSourceManager() { lock = srs_mutex_new(); } -SrsRtcStreamManager::~SrsRtcStreamManager() +SrsRtcSourceManager::~SrsRtcSourceManager() { srs_mutex_destroy(lock); } -srs_error_t SrsRtcStreamManager::fetch_or_create(SrsRequest* r, SrsRtcStream** pps) +srs_error_t SrsRtcSourceManager::fetch_or_create(SrsRequest* r, SrsRtcSource** pps) { srs_error_t err = srs_success; @@ -265,7 +265,7 @@ srs_error_t SrsRtcStreamManager::fetch_or_create(SrsRequest* r, SrsRtcStream** p // @bug https://github.com/ossrs/srs/issues/1230 SrsLocker(lock); - SrsRtcStream* source = NULL; + SrsRtcSource* source = NULL; if ((source = fetch(r)) != NULL) { *pps = source; return err; @@ -279,7 +279,7 @@ srs_error_t SrsRtcStreamManager::fetch_or_create(SrsRequest* r, SrsRtcStream** p srs_trace("new source, stream_url=%s", stream_url.c_str()); - source = new SrsRtcStream(); + source = new SrsRtcSource(); if ((err = source->initialize(r)) != srs_success) { return srs_error_wrap(err, "init source %s", r->get_stream_url().c_str()); } @@ -291,9 +291,9 @@ srs_error_t SrsRtcStreamManager::fetch_or_create(SrsRequest* r, SrsRtcStream** p return err; } -SrsRtcStream* SrsRtcStreamManager::fetch(SrsRequest* r) +SrsRtcSource* SrsRtcSourceManager::fetch(SrsRequest* r) { - SrsRtcStream* source = NULL; + SrsRtcSource* source = NULL; string stream_url = r->get_stream_url(); if (pool.find(stream_url) == pool.end()) { @@ -310,7 +310,7 @@ SrsRtcStream* SrsRtcStreamManager::fetch(SrsRequest* r) return source; } -SrsRtcStreamManager* _srs_rtc_sources = NULL; +SrsRtcSourceManager* _srs_rtc_sources = NULL; ISrsRtcPublishStream::ISrsRtcPublishStream() { @@ -320,11 +320,11 @@ ISrsRtcPublishStream::~ISrsRtcPublishStream() { } -ISrsRtcStreamEventHandler::ISrsRtcStreamEventHandler() +ISrsRtcSourceEventHandler::ISrsRtcSourceEventHandler() { } -ISrsRtcStreamEventHandler::~ISrsRtcStreamEventHandler() +ISrsRtcSourceEventHandler::~ISrsRtcSourceEventHandler() { } @@ -336,7 +336,7 @@ ISrsRtcSourceBridger::~ISrsRtcSourceBridger() { } -SrsRtcStream::SrsRtcStream() +SrsRtcSource::SrsRtcSource() { is_created_ = false; is_delivering_packets_ = false; @@ -350,7 +350,7 @@ SrsRtcStream::SrsRtcStream() pli_for_rtmp_ = pli_elapsed_ = 0; } -SrsRtcStream::~SrsRtcStream() +SrsRtcSource::~SrsRtcSource() { // never free the consumers, // for all consumers are auto free. @@ -361,7 +361,7 @@ SrsRtcStream::~SrsRtcStream() srs_freep(stream_desc_); } -srs_error_t SrsRtcStream::initialize(SrsRequest* r) +srs_error_t SrsRtcSource::initialize(SrsRequest* r) { srs_error_t err = srs_success; @@ -370,12 +370,12 @@ srs_error_t SrsRtcStream::initialize(SrsRequest* r) return err; } -void SrsRtcStream::update_auth(SrsRequest* r) +void SrsRtcSource::update_auth(SrsRequest* r) { req->update_auth(r); } -srs_error_t SrsRtcStream::on_source_changed() +srs_error_t SrsRtcSource::on_source_changed() { srs_error_t err = srs_success; @@ -408,23 +408,23 @@ srs_error_t SrsRtcStream::on_source_changed() return err; } -SrsContextId SrsRtcStream::source_id() +SrsContextId SrsRtcSource::source_id() { return _source_id; } -SrsContextId SrsRtcStream::pre_source_id() +SrsContextId SrsRtcSource::pre_source_id() { return _pre_source_id; } -void SrsRtcStream::set_bridger(ISrsRtcSourceBridger *bridger) +void SrsRtcSource::set_bridger(ISrsRtcSourceBridger *bridger) { srs_freep(bridger_); bridger_ = bridger; } -srs_error_t SrsRtcStream::create_consumer(SrsRtcConsumer*& consumer) +srs_error_t SrsRtcSource::create_consumer(SrsRtcConsumer*& consumer) { srs_error_t err = srs_success; @@ -436,7 +436,7 @@ srs_error_t SrsRtcStream::create_consumer(SrsRtcConsumer*& consumer) return err; } -srs_error_t SrsRtcStream::consumer_dumps(SrsRtcConsumer* consumer, bool ds, bool dm, bool dg) +srs_error_t SrsRtcSource::consumer_dumps(SrsRtcConsumer* consumer, bool ds, bool dm, bool dg) { srs_error_t err = srs_success; @@ -446,7 +446,7 @@ srs_error_t SrsRtcStream::consumer_dumps(SrsRtcConsumer* consumer, bool ds, bool return err; } -void SrsRtcStream::on_consumer_destroy(SrsRtcConsumer* consumer) +void SrsRtcSource::on_consumer_destroy(SrsRtcConsumer* consumer) { std::vector::iterator it; it = std::find(consumers.begin(), consumers.end(), consumer); @@ -457,26 +457,26 @@ void SrsRtcStream::on_consumer_destroy(SrsRtcConsumer* consumer) // When all consumers finished, notify publisher to handle it. if (publish_stream_ && consumers.empty()) { for (size_t i = 0; i < event_handlers_.size(); i++) { - ISrsRtcStreamEventHandler* h = event_handlers_.at(i); + ISrsRtcSourceEventHandler* h = event_handlers_.at(i); h->on_consumers_finished(); } } } -bool SrsRtcStream::can_publish() +bool SrsRtcSource::can_publish() { // TODO: FIXME: Should check the status of bridger. return !is_created_; } -void SrsRtcStream::set_stream_created() +void SrsRtcSource::set_stream_created() { srs_assert(!is_created_ && !is_delivering_packets_); is_created_ = true; } -srs_error_t SrsRtcStream::on_publish() +srs_error_t SrsRtcSource::on_publish() { srs_error_t err = srs_success; @@ -502,7 +502,7 @@ srs_error_t SrsRtcStream::on_publish() // The PLI interval for RTC2RTMP. pli_for_rtmp_ = _srs_config->get_rtc_pli_for_rtmp(req->vhost); - // @see SrsRtcStream::on_timer() + // @see SrsRtcSource::on_timer() _srs_hybrid->timer100ms()->subscribe(this); } @@ -511,7 +511,7 @@ srs_error_t SrsRtcStream::on_publish() return err; } -void SrsRtcStream::on_unpublish() +void SrsRtcSource::on_unpublish() { // ignore when already unpublished. if (!is_created_) { @@ -529,13 +529,13 @@ void SrsRtcStream::on_unpublish() _source_id = SrsContextId(); for (size_t i = 0; i < event_handlers_.size(); i++) { - ISrsRtcStreamEventHandler* h = event_handlers_.at(i); + ISrsRtcSourceEventHandler* h = event_handlers_.at(i); h->on_unpublish(); } //free bridger resource if (bridger_) { - // For SrsRtcStream::on_timer() + // For SrsRtcSource::on_timer() _srs_hybrid->timer100ms()->unsubscribe(this); bridger_->on_unpublish(); @@ -548,33 +548,33 @@ void SrsRtcStream::on_unpublish() // TODO: FIXME: Handle by statistic. } -void SrsRtcStream::subscribe(ISrsRtcStreamEventHandler* h) +void SrsRtcSource::subscribe(ISrsRtcSourceEventHandler* h) { if (std::find(event_handlers_.begin(), event_handlers_.end(), h) == event_handlers_.end()) { event_handlers_.push_back(h); } } -void SrsRtcStream::unsubscribe(ISrsRtcStreamEventHandler* h) +void SrsRtcSource::unsubscribe(ISrsRtcSourceEventHandler* h) { - std::vector::iterator it; + std::vector::iterator it; it = std::find(event_handlers_.begin(), event_handlers_.end(), h); if (it != event_handlers_.end()) { event_handlers_.erase(it); } } -ISrsRtcPublishStream* SrsRtcStream::publish_stream() +ISrsRtcPublishStream* SrsRtcSource::publish_stream() { return publish_stream_; } -void SrsRtcStream::set_publish_stream(ISrsRtcPublishStream* v) +void SrsRtcSource::set_publish_stream(ISrsRtcPublishStream* v) { publish_stream_ = v; } -srs_error_t SrsRtcStream::on_rtp(SrsRtpPacket* pkt) +srs_error_t SrsRtcSource::on_rtp(SrsRtpPacket* pkt) { srs_error_t err = srs_success; @@ -598,12 +598,12 @@ srs_error_t SrsRtcStream::on_rtp(SrsRtpPacket* pkt) return err; } -bool SrsRtcStream::has_stream_desc() +bool SrsRtcSource::has_stream_desc() { return stream_desc_; } -void SrsRtcStream::set_stream_desc(SrsRtcStreamDescription* stream_desc) +void SrsRtcSource::set_stream_desc(SrsRtcSourceDescription* stream_desc) { srs_freep(stream_desc_); @@ -612,7 +612,7 @@ void SrsRtcStream::set_stream_desc(SrsRtcStreamDescription* stream_desc) } } -std::vector SrsRtcStream::get_track_desc(std::string type, std::string media_name) +std::vector SrsRtcSource::get_track_desc(std::string type, std::string media_name) { std::vector track_descs; if (!stream_desc_) { @@ -636,7 +636,7 @@ std::vector SrsRtcStream::get_track_desc(std::string ty return track_descs; } -srs_error_t SrsRtcStream::on_timer(srs_utime_t interval) +srs_error_t SrsRtcSource::on_timer(srs_utime_t interval) { srs_error_t err = srs_success; @@ -662,7 +662,7 @@ srs_error_t SrsRtcStream::on_timer(srs_utime_t interval) } #ifdef SRS_FFMPEG_FIT -SrsRtcFromRtmpBridger::SrsRtcFromRtmpBridger(SrsRtcStream* source) +SrsRtcFromRtmpBridger::SrsRtcFromRtmpBridger(SrsRtcSource* source) { req = NULL; source_ = source; @@ -675,8 +675,8 @@ SrsRtcFromRtmpBridger::SrsRtcFromRtmpBridger(SrsRtcStream* source) audio_sequence = 0; video_sequence = 0; - SrsRtcStreamDescription* stream_desc = new SrsRtcStreamDescription(); - SrsAutoFree(SrsRtcStreamDescription, stream_desc); + SrsRtcSourceDescription* stream_desc = new SrsRtcSourceDescription(); + SrsAutoFree(SrsRtcSourceDescription, stream_desc); // audio track description if (true) { @@ -989,7 +989,7 @@ srs_error_t SrsRtcFromRtmpBridger::filter(SrsSharedPtrMessage* msg, SrsFormat* f return err; } -srs_error_t SrsRtcFromRtmpBridger::package_stap_a(SrsRtcStream* source, SrsSharedPtrMessage* msg, SrsRtpPacket* pkt) +srs_error_t SrsRtcFromRtmpBridger::package_stap_a(SrsRtcSource* source, SrsSharedPtrMessage* msg, SrsRtpPacket* pkt) { srs_error_t err = srs_success; @@ -1231,7 +1231,7 @@ srs_error_t SrsRtcFromRtmpBridger::consume_packets(vector& pkts) return err; } -SrsRtmpFromRtcBridger::SrsRtmpFromRtcBridger(SrsSource *src) +SrsRtmpFromRtcBridger::SrsRtmpFromRtcBridger(SrsLiveSource *src) { source_ = src; codec_ = NULL; @@ -2130,12 +2130,12 @@ SrsRtcTrackDescription* SrsRtcTrackDescription::copy() return cp; } -SrsRtcStreamDescription::SrsRtcStreamDescription() +SrsRtcSourceDescription::SrsRtcSourceDescription() { audio_track_desc_ = NULL; } -SrsRtcStreamDescription::~SrsRtcStreamDescription() +SrsRtcSourceDescription::~SrsRtcSourceDescription() { srs_freep(audio_track_desc_); @@ -2145,9 +2145,9 @@ SrsRtcStreamDescription::~SrsRtcStreamDescription() video_track_descs_.clear(); } -SrsRtcStreamDescription* SrsRtcStreamDescription::copy() +SrsRtcSourceDescription* SrsRtcSourceDescription::copy() { - SrsRtcStreamDescription* stream_desc = new SrsRtcStreamDescription(); + SrsRtcSourceDescription* stream_desc = new SrsRtcSourceDescription(); if (audio_track_desc_) { stream_desc->audio_track_desc_ = audio_track_desc_->copy(); @@ -2160,7 +2160,7 @@ SrsRtcStreamDescription* SrsRtcStreamDescription::copy() return stream_desc; } -SrsRtcTrackDescription* SrsRtcStreamDescription::find_track_description_by_ssrc(uint32_t ssrc) +SrsRtcTrackDescription* SrsRtcSourceDescription::find_track_description_by_ssrc(uint32_t ssrc) { if (audio_track_desc_ && audio_track_desc_->has_ssrc(ssrc)) { return audio_track_desc_; @@ -2328,7 +2328,7 @@ void SrsRtcAudioRecvTrack::on_before_decode_payload(SrsRtpPacket* pkt, SrsBuffer *ppt = SrsRtspPacketPayloadTypeRaw; } -srs_error_t SrsRtcAudioRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket* pkt) +srs_error_t SrsRtcAudioRecvTrack::on_rtp(SrsRtcSource* source, SrsRtpPacket* pkt) { srs_error_t err = srs_success; @@ -2384,7 +2384,7 @@ void SrsRtcVideoRecvTrack::on_before_decode_payload(SrsRtpPacket* pkt, SrsBuffer } } -srs_error_t SrsRtcVideoRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket* pkt) +srs_error_t SrsRtcVideoRecvTrack::on_rtp(SrsRtcSource* source, SrsRtpPacket* pkt) { srs_error_t err = srs_success; diff --git a/trunk/src/app/srs_app_rtc_source.hpp b/trunk/src/app/srs_app_rtc_source.hpp index 77cc74441..23fae5b34 100644 --- a/trunk/src/app/srs_app_rtc_source.hpp +++ b/trunk/src/app/srs_app_rtc_source.hpp @@ -43,12 +43,12 @@ class SrsMetaCache; class SrsSharedPtrMessage; class SrsCommonMessage; class SrsMessageArray; -class SrsRtcStream; +class SrsRtcSource; class SrsRtcFromRtmpBridger; class SrsAudioTranscoder; class SrsRtpPacket; class SrsSample; -class SrsRtcStreamDescription; +class SrsRtcSourceDescription; class SrsRtcTrackDescription; class SrsRtcConnection; class SrsRtpRingBuffer; @@ -74,20 +74,20 @@ public: }; // When RTC stream publish and re-publish. -class ISrsRtcStreamChangeCallback +class ISrsRtcSourceChangeCallback { public: - ISrsRtcStreamChangeCallback(); - virtual ~ISrsRtcStreamChangeCallback(); + ISrsRtcSourceChangeCallback(); + virtual ~ISrsRtcSourceChangeCallback(); public: - virtual void on_stream_change(SrsRtcStreamDescription* desc) = 0; + virtual void on_stream_change(SrsRtcSourceDescription* desc) = 0; }; // The RTC stream consumer, consume packets from RTC stream source. class SrsRtcConsumer { private: - SrsRtcStream* source; + SrsRtcSource* source; std::vector queue; // when source id changed, notice all consumers bool should_update_source_id; @@ -98,9 +98,9 @@ private: int mw_min_msgs; private: // The callback for stream change event. - ISrsRtcStreamChangeCallback* handler_; + ISrsRtcSourceChangeCallback* handler_; public: - SrsRtcConsumer(SrsRtcStream* s); + SrsRtcConsumer(SrsRtcSource* s); virtual ~SrsRtcConsumer(); public: // When source id changed, notice client to print. @@ -113,31 +113,31 @@ public: // Wait for at-least some messages incoming in queue. virtual void wait(int nb_msgs); public: - void set_handler(ISrsRtcStreamChangeCallback* h) { handler_ = h; } // SrsRtcConsumer::set_handler() - void on_stream_change(SrsRtcStreamDescription* desc); + void set_handler(ISrsRtcSourceChangeCallback* h) { handler_ = h; } // SrsRtcConsumer::set_handler() + void on_stream_change(SrsRtcSourceDescription* desc); }; -class SrsRtcStreamManager +class SrsRtcSourceManager { private: srs_mutex_t lock; - std::map pool; + std::map pool; public: - SrsRtcStreamManager(); - virtual ~SrsRtcStreamManager(); + SrsRtcSourceManager(); + virtual ~SrsRtcSourceManager(); public: // create source when fetch from cache failed. // @param r the client request. // @param pps the matched source, if success never be NULL. - virtual srs_error_t fetch_or_create(SrsRequest* r, SrsRtcStream** pps); + virtual srs_error_t fetch_or_create(SrsRequest* r, SrsRtcSource** pps); private: // Get the exists source, NULL when not exists. // update the request and return the exists source. - virtual SrsRtcStream* fetch(SrsRequest* r); + virtual SrsRtcSource* fetch(SrsRequest* r); }; // Global singleton instance. -extern SrsRtcStreamManager* _srs_rtc_sources; +extern SrsRtcSourceManager* _srs_rtc_sources; // A publish stream interface, for source to callback with. class ISrsRtcPublishStream @@ -150,11 +150,11 @@ public: virtual void request_keyframe(uint32_t ssrc) = 0; }; -class ISrsRtcStreamEventHandler +class ISrsRtcSourceEventHandler { public: - ISrsRtcStreamEventHandler(); - virtual ~ISrsRtcStreamEventHandler(); + ISrsRtcSourceEventHandler(); + virtual ~ISrsRtcSourceEventHandler(); public: // stream unpublish, sync API. virtual void on_unpublish() = 0; @@ -162,7 +162,7 @@ public: virtual void on_consumers_finished() = 0; }; -// SrsRtcStream bridge to SrsSource +// SrsRtcSource bridge to SrsLiveSource class ISrsRtcSourceBridger { public: @@ -175,7 +175,7 @@ public: }; // A Source is a stream, to publish and to play with, binding to SrsRtcPublishStream and SrsRtcPlayStream. -class SrsRtcStream : public ISrsFastTimer +class SrsRtcSource : public ISrsFastTimer { private: // For publish, it's the publish client id. @@ -188,7 +188,7 @@ private: SrsRequest* req; ISrsRtcPublishStream* publish_stream_; // Steam description for this steam. - SrsRtcStreamDescription* stream_desc_; + SrsRtcSourceDescription* stream_desc_; // The Source bridger, bridger stream to other source. ISrsRtcSourceBridger* bridger_; private: @@ -199,14 +199,14 @@ private: // Whether stream is delivering data, that is, DTLS is done. bool is_delivering_packets_; // Notify stream event to event handler - std::vector event_handlers_; + std::vector event_handlers_; private: // The PLI for RTC2RTMP. srs_utime_t pli_for_rtmp_; srs_utime_t pli_elapsed_; public: - SrsRtcStream(); - virtual ~SrsRtcStream(); + SrsRtcSource(); + virtual ~SrsRtcSource(); public: virtual srs_error_t initialize(SrsRequest* r); // Update the authentication information in request. @@ -241,8 +241,8 @@ public: virtual void on_unpublish(); public: // For event handler - void subscribe(ISrsRtcStreamEventHandler* h); - void unsubscribe(ISrsRtcStreamEventHandler* h); + void subscribe(ISrsRtcSourceEventHandler* h); + void unsubscribe(ISrsRtcSourceEventHandler* h); public: // Get and set the publisher, passed to consumer to process requests such as PLI. ISrsRtcPublishStream* publish_stream(); @@ -251,7 +251,7 @@ public: srs_error_t on_rtp(SrsRtpPacket* pkt); // Set and get stream description for souce bool has_stream_desc(); - void set_stream_desc(SrsRtcStreamDescription* stream_desc); + void set_stream_desc(SrsRtcSourceDescription* stream_desc); std::vector get_track_desc(std::string type, std::string media_type); // interface ISrsFastTimer private: @@ -259,11 +259,11 @@ private: }; #ifdef SRS_FFMPEG_FIT -class SrsRtcFromRtmpBridger : public ISrsSourceBridger +class SrsRtcFromRtmpBridger : public ISrsLiveSourceBridger { private: SrsRequest* req; - SrsRtcStream* source_; + SrsRtcSource* source_; // The format, codec information. SrsRtmpFormat* format; // The metadata cache. @@ -278,7 +278,7 @@ private: uint32_t audio_ssrc; uint32_t video_ssrc; public: - SrsRtcFromRtmpBridger(SrsRtcStream* source); + SrsRtcFromRtmpBridger(SrsRtcSource* source); virtual ~SrsRtcFromRtmpBridger(); public: virtual srs_error_t initialize(SrsRequest* r); @@ -292,7 +292,7 @@ public: virtual srs_error_t on_video(SrsSharedPtrMessage* msg); private: srs_error_t filter(SrsSharedPtrMessage* msg, SrsFormat* format, bool& has_idr, std::vector& samples); - srs_error_t package_stap_a(SrsRtcStream* source, SrsSharedPtrMessage* msg, SrsRtpPacket* pkt); + srs_error_t package_stap_a(SrsRtcSource* source, SrsSharedPtrMessage* msg, SrsRtpPacket* pkt); srs_error_t package_nalus(SrsSharedPtrMessage* msg, const std::vector& samples, std::vector& pkts); srs_error_t package_single_nalu(SrsSharedPtrMessage* msg, SrsSample* sample, std::vector& pkts); srs_error_t package_fu_a(SrsSharedPtrMessage* msg, SrsSample* sample, int fu_payload_size, std::vector& pkts); @@ -302,7 +302,7 @@ private: class SrsRtmpFromRtcBridger : public ISrsRtcSourceBridger { private: - SrsSource *source_; + SrsLiveSource *source_; SrsAudioTranscoder *codec_; bool is_first_audio; bool is_first_video; @@ -323,7 +323,7 @@ private: uint16_t lost_sn_; int64_t key_frame_ts_; public: - SrsRtmpFromRtcBridger(SrsSource *src); + SrsRtmpFromRtcBridger(SrsLiveSource *src); virtual ~SrsRtmpFromRtcBridger(); public: srs_error_t initialize(SrsRequest* r); @@ -504,7 +504,7 @@ public: SrsRtcTrackDescription* copy(); }; -class SrsRtcStreamDescription +class SrsRtcSourceDescription { public: // the id for this stream; @@ -513,11 +513,11 @@ public: SrsRtcTrackDescription* audio_track_desc_; std::vector video_track_descs_; public: - SrsRtcStreamDescription(); - virtual ~SrsRtcStreamDescription(); + SrsRtcSourceDescription(); + virtual ~SrsRtcSourceDescription(); public: - SrsRtcStreamDescription* copy(); + SrsRtcSourceDescription* copy(); SrsRtcTrackDescription* find_track_description_by_ssrc(uint32_t ssrc); }; @@ -556,7 +556,7 @@ public: // set to NULL, nack nerver copy it but set the pkt to NULL. srs_error_t on_nack(SrsRtpPacket** ppkt); public: - virtual srs_error_t on_rtp(SrsRtcStream* source, SrsRtpPacket* pkt) = 0; + virtual srs_error_t on_rtp(SrsRtcSource* source, SrsRtpPacket* pkt) = 0; virtual srs_error_t check_send_nacks() = 0; protected: virtual srs_error_t do_check_send_nacks(uint32_t& timeout_nacks); @@ -570,7 +570,7 @@ public: public: virtual void on_before_decode_payload(SrsRtpPacket* pkt, SrsBuffer* buf, ISrsRtpPayloader** ppayload, SrsRtspPacketPayloadType* ppt); public: - virtual srs_error_t on_rtp(SrsRtcStream* source, SrsRtpPacket* pkt); + virtual srs_error_t on_rtp(SrsRtcSource* source, SrsRtpPacket* pkt); virtual srs_error_t check_send_nacks(); }; @@ -582,7 +582,7 @@ public: public: virtual void on_before_decode_payload(SrsRtpPacket* pkt, SrsBuffer* buf, ISrsRtpPayloader** ppayload, SrsRtspPacketPayloadType* ppt); public: - virtual srs_error_t on_rtp(SrsRtcStream* source, SrsRtpPacket* pkt); + virtual srs_error_t on_rtp(SrsRtcSource* source, SrsRtpPacket* pkt); virtual srs_error_t check_send_nacks(); }; diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index b2dac498e..29947eaa2 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -526,7 +526,7 @@ srs_error_t SrsRtmpConn::stream_service_cycle() rtmp->set_send_timeout(SRS_CONSTS_RTMP_TIMEOUT); // find a source to serve. - SrsSource* source = NULL; + SrsLiveSource* source = NULL; if ((err = _srs_sources->fetch_or_create(req, server, &source)) != srs_success) { return srs_error_wrap(err, "rtmp: fetch source"); } @@ -621,7 +621,7 @@ srs_error_t SrsRtmpConn::check_vhost(bool try_default_vhost) return err; } -srs_error_t SrsRtmpConn::playing(SrsSource* source) +srs_error_t SrsRtmpConn::playing(SrsLiveSource* source) { srs_error_t err = srs_success; @@ -677,8 +677,8 @@ srs_error_t SrsRtmpConn::playing(SrsSource* source) set_sock_options(); // Create a consumer of source. - SrsConsumer* consumer = NULL; - SrsAutoFree(SrsConsumer, consumer); + SrsLiveConsumer* consumer = NULL; + SrsAutoFree(SrsLiveConsumer, consumer); if ((err = source->create_consumer(consumer)) != srs_success) { return srs_error_wrap(err, "rtmp: create consumer"); } @@ -709,7 +709,7 @@ srs_error_t SrsRtmpConn::playing(SrsSource* source) return err; } -srs_error_t SrsRtmpConn::do_playing(SrsSource* source, SrsConsumer* consumer, SrsQueueRecvThread* rtrd) +srs_error_t SrsRtmpConn::do_playing(SrsLiveSource* source, SrsLiveConsumer* consumer, SrsQueueRecvThread* rtrd) { srs_error_t err = srs_success; @@ -836,7 +836,7 @@ srs_error_t SrsRtmpConn::do_playing(SrsSource* source, SrsConsumer* consumer, Sr return err; } -srs_error_t SrsRtmpConn::publishing(SrsSource* source) +srs_error_t SrsRtmpConn::publishing(SrsLiveSource* source) { srs_error_t err = srs_success; @@ -875,7 +875,7 @@ srs_error_t SrsRtmpConn::publishing(SrsSource* source) return err; } -srs_error_t SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* rtrd) +srs_error_t SrsRtmpConn::do_publishing(SrsLiveSource* source, SrsPublishRecvThread* rtrd) { srs_error_t err = srs_success; @@ -955,7 +955,7 @@ srs_error_t SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* return err; } -srs_error_t SrsRtmpConn::acquire_publish(SrsSource* source) +srs_error_t SrsRtmpConn::acquire_publish(SrsLiveSource* source) { srs_error_t err = srs_success; @@ -963,7 +963,7 @@ srs_error_t SrsRtmpConn::acquire_publish(SrsSource* source) // Check whether RTC stream is busy. #ifdef SRS_RTC - SrsRtcStream *rtc = NULL; + SrsRtcSource *rtc = NULL; bool rtc_server_enabled = _srs_config->get_rtc_server_enabled(); bool rtc_enabled = _srs_config->get_rtc_enabled(req->vhost); if (rtc_server_enabled && rtc_enabled && !info->edge) { @@ -1003,7 +1003,7 @@ srs_error_t SrsRtmpConn::acquire_publish(SrsSource* source) } } -void SrsRtmpConn::release_publish(SrsSource* source) +void SrsRtmpConn::release_publish(SrsLiveSource* source) { // when edge, notice edge to change state. // when origin, notice all service to unpublish. @@ -1014,7 +1014,7 @@ void SrsRtmpConn::release_publish(SrsSource* source) } } -srs_error_t SrsRtmpConn::handle_publish_message(SrsSource* source, SrsCommonMessage* msg) +srs_error_t SrsRtmpConn::handle_publish_message(SrsLiveSource* source, SrsCommonMessage* msg) { srs_error_t err = srs_success; @@ -1055,7 +1055,7 @@ srs_error_t SrsRtmpConn::handle_publish_message(SrsSource* source, SrsCommonMess return err; } -srs_error_t SrsRtmpConn::process_publish_message(SrsSource* source, SrsCommonMessage* msg) +srs_error_t SrsRtmpConn::process_publish_message(SrsLiveSource* source, SrsCommonMessage* msg) { srs_error_t err = srs_success; @@ -1111,7 +1111,7 @@ srs_error_t SrsRtmpConn::process_publish_message(SrsSource* source, SrsCommonMes return err; } -srs_error_t SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg) +srs_error_t SrsRtmpConn::process_play_control_msg(SrsLiveConsumer* consumer, SrsCommonMessage* msg) { srs_error_t err = srs_success; diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index acfc15c70..b3325f13f 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -38,9 +38,9 @@ class SrsServer; class SrsRtmpServer; class SrsRequest; class SrsResponse; -class SrsSource; +class SrsLiveSource; class SrsRefer; -class SrsConsumer; +class SrsLiveConsumer; class SrsCommonMessage; class SrsStSocket; class SrsHttpHooks; @@ -161,15 +161,15 @@ private: // The stream(play/publish) service cycle, identify client first. virtual srs_error_t stream_service_cycle(); virtual srs_error_t check_vhost(bool try_default_vhost); - virtual srs_error_t playing(SrsSource* source); - virtual srs_error_t do_playing(SrsSource* source, SrsConsumer* consumer, SrsQueueRecvThread* trd); - virtual srs_error_t publishing(SrsSource* source); - virtual srs_error_t do_publishing(SrsSource* source, SrsPublishRecvThread* trd); - virtual srs_error_t acquire_publish(SrsSource* source); - virtual void release_publish(SrsSource* source); - virtual srs_error_t handle_publish_message(SrsSource* source, SrsCommonMessage* msg); - virtual srs_error_t process_publish_message(SrsSource* source, SrsCommonMessage* msg); - virtual srs_error_t process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg); + virtual srs_error_t playing(SrsLiveSource* source); + virtual srs_error_t do_playing(SrsLiveSource* source, SrsLiveConsumer* consumer, SrsQueueRecvThread* trd); + virtual srs_error_t publishing(SrsLiveSource* source); + virtual srs_error_t do_publishing(SrsLiveSource* source, SrsPublishRecvThread* trd); + virtual srs_error_t acquire_publish(SrsLiveSource* source); + virtual void release_publish(SrsLiveSource* source); + virtual srs_error_t handle_publish_message(SrsLiveSource* source, SrsCommonMessage* msg); + virtual srs_error_t process_publish_message(SrsLiveSource* source, SrsCommonMessage* msg); + virtual srs_error_t process_play_control_msg(SrsLiveConsumer* consumer, SrsCommonMessage* msg); virtual void set_sock_options(); private: virtual srs_error_t check_edge_token_traverse_auth(); diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index a29ab106d..8fc7f34be 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -1782,7 +1782,7 @@ srs_error_t SrsServer::on_reload_http_stream_updated() return err; } -srs_error_t SrsServer::on_publish(SrsSource* s, SrsRequest* r) +srs_error_t SrsServer::on_publish(SrsLiveSource* s, SrsRequest* r) { srs_error_t err = srs_success; @@ -1798,7 +1798,7 @@ srs_error_t SrsServer::on_publish(SrsSource* s, SrsRequest* r) return err; } -void SrsServer::on_unpublish(SrsSource* s, SrsRequest* r) +void SrsServer::on_unpublish(SrsLiveSource* s, SrsRequest* r) { http_server->http_unmount(s, r); diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 9694debe5..aa790e6e5 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -262,7 +262,7 @@ public: // TODO: FIXME: Rename to SrsLiveServer. // SRS RTMP server, initialize and listen, start connection service thread, destroy client. -class SrsServer : public ISrsReloadHandler, public ISrsSourceHandler +class SrsServer : public ISrsReloadHandler, public ISrsLiveSourceHandler , public ISrsResourceManager, public ISrsCoroutineHandler , public ISrsHourGlass { @@ -394,10 +394,10 @@ public: virtual srs_error_t on_reload_http_stream_enabled(); virtual srs_error_t on_reload_http_stream_disabled(); virtual srs_error_t on_reload_http_stream_updated(); -// Interface ISrsSourceHandler +// Interface ISrsLiveSourceHandler public: - virtual srs_error_t on_publish(SrsSource* s, SrsRequest* r); - virtual void on_unpublish(SrsSource* s, SrsRequest* r); + virtual srs_error_t on_publish(SrsLiveSource* s, SrsRequest* r); + virtual void on_unpublish(SrsLiveSource* s, SrsRequest* r); }; // The SRS server adapter, the master server. diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index ed737f1cc..1309dc178 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -329,7 +329,7 @@ srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** p return err; } -srs_error_t SrsMessageQueue::dump_packets(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag) +srs_error_t SrsMessageQueue::dump_packets(SrsLiveConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag) { srs_error_t err = srs_success; @@ -418,7 +418,7 @@ ISrsWakable::~ISrsWakable() { } -SrsConsumer::SrsConsumer(SrsSource* s) +SrsLiveConsumer::SrsLiveConsumer(SrsLiveSource* s) { source = s; paused = false; @@ -434,7 +434,7 @@ SrsConsumer::SrsConsumer(SrsSource* s) #endif } -SrsConsumer::~SrsConsumer() +SrsLiveConsumer::~SrsLiveConsumer() { source->on_consumer_destroy(this); srs_freep(jitter); @@ -445,22 +445,22 @@ SrsConsumer::~SrsConsumer() #endif } -void SrsConsumer::set_queue_size(srs_utime_t queue_size) +void SrsLiveConsumer::set_queue_size(srs_utime_t queue_size) { queue->set_queue_size(queue_size); } -void SrsConsumer::update_source_id() +void SrsLiveConsumer::update_source_id() { should_update_source_id = true; } -int64_t SrsConsumer::get_time() +int64_t SrsLiveConsumer::get_time() { return jitter->get_time(); } -srs_error_t SrsConsumer::enqueue(SrsSharedPtrMessage* shared_msg, bool atc, SrsRtmpJitterAlgorithm ag) +srs_error_t SrsLiveConsumer::enqueue(SrsSharedPtrMessage* shared_msg, bool atc, SrsRtmpJitterAlgorithm ag) { srs_error_t err = srs_success; @@ -504,7 +504,7 @@ srs_error_t SrsConsumer::enqueue(SrsSharedPtrMessage* shared_msg, bool atc, SrsR return err; } -srs_error_t SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count) +srs_error_t SrsLiveConsumer::dump_packets(SrsMessageArray* msgs, int& count) { srs_error_t err = srs_success; @@ -537,7 +537,7 @@ srs_error_t SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count) } #ifdef SRS_PERF_QUEUE_COND_WAIT -void SrsConsumer::wait(int nb_msgs, srs_utime_t msgs_duration) +void SrsLiveConsumer::wait(int nb_msgs, srs_utime_t msgs_duration) { if (paused) { srs_usleep(SRS_CONSTS_RTMP_PULSE); @@ -563,7 +563,7 @@ void SrsConsumer::wait(int nb_msgs, srs_utime_t msgs_duration) } #endif -srs_error_t SrsConsumer::on_play_client_pause(bool is_pause) +srs_error_t SrsLiveConsumer::on_play_client_pause(bool is_pause) { srs_error_t err = srs_success; @@ -573,7 +573,7 @@ srs_error_t SrsConsumer::on_play_client_pause(bool is_pause) return err; } -void SrsConsumer::wakeup() +void SrsLiveConsumer::wakeup() { #ifdef SRS_PERF_QUEUE_COND_WAIT if (mw_waiting) { @@ -681,7 +681,7 @@ void SrsGopCache::clear() audio_after_last_video_count = 0; } -srs_error_t SrsGopCache::dump(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm jitter_algorithm) +srs_error_t SrsGopCache::dump(SrsLiveConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm jitter_algorithm) { srs_error_t err = srs_success; @@ -719,11 +719,11 @@ bool SrsGopCache::pure_audio() return cached_video_count == 0; } -ISrsSourceHandler::ISrsSourceHandler() +ISrsLiveSourceHandler::ISrsLiveSourceHandler() { } -ISrsSourceHandler::~ISrsSourceHandler() +ISrsLiveSourceHandler::~ISrsLiveSourceHandler() { } @@ -861,7 +861,7 @@ SrsOriginHub::~SrsOriginHub() #endif } -srs_error_t SrsOriginHub::initialize(SrsSource* s, SrsRequest* r) +srs_error_t SrsOriginHub::initialize(SrsLiveSource* s, SrsRequest* r) { srs_error_t err = srs_success; @@ -1213,7 +1213,7 @@ srs_error_t SrsOriginHub::on_dvr_request_sh() // feed the dvr the metadata/sequence header, // when reload to start dvr, dvr will never get the sequence header in stream, - // use the SrsSource.on_dvr_request_sh to push the sequence header to DVR. + // use the SrsLiveSource.on_dvr_request_sh to push the sequence header to DVR. if (cache_metadata && (err = dvr->on_meta_data(cache_metadata)) != srs_success) { return srs_error_wrap(err, "dvr metadata"); } @@ -1328,7 +1328,7 @@ srs_error_t SrsOriginHub::on_reload_vhost_hls(string vhost) // when reload, we must fetch the sequence header from source cache. // notice the source to get the cached sequence header. // when reload to start hls, hls will never get the sequence header in stream, - // use the SrsSource.on_hls_start to push the sequence header to HLS. + // use the SrsLiveSource.on_hls_start to push the sequence header to HLS. SrsSharedPtrMessage* cache_sh_video = source->meta->vsh(); if (cache_sh_video) { if ((err = format->on_video(cache_sh_video)) != srs_success) { @@ -1561,7 +1561,7 @@ SrsFormat* SrsMetaCache::ash_format() return aformat; } -srs_error_t SrsMetaCache::dumps(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag, bool dm, bool ds) +srs_error_t SrsMetaCache::dumps(SrsLiveConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag, bool dm, bool ds) { srs_error_t err = srs_success; @@ -1684,26 +1684,26 @@ srs_error_t SrsMetaCache::update_vsh(SrsSharedPtrMessage* msg) return vformat->on_video(msg); } -SrsSourceManager* _srs_sources = NULL; +SrsLiveSourceManager* _srs_sources = NULL; -SrsSourceManager::SrsSourceManager() +SrsLiveSourceManager::SrsLiveSourceManager() { lock = srs_mutex_new(); timer_ = new SrsHourGlass("sources", this, 1 * SRS_UTIME_SECONDS); } -SrsSourceManager::~SrsSourceManager() +SrsLiveSourceManager::~SrsLiveSourceManager() { srs_mutex_destroy(lock); srs_freep(timer_); } -srs_error_t SrsSourceManager::initialize() +srs_error_t SrsLiveSourceManager::initialize() { return setup_ticks(); } -srs_error_t SrsSourceManager::fetch_or_create(SrsRequest* r, ISrsSourceHandler* h, SrsSource** pps) +srs_error_t SrsLiveSourceManager::fetch_or_create(SrsRequest* r, ISrsLiveSourceHandler* h, SrsLiveSource** pps) { srs_error_t err = srs_success; @@ -1712,7 +1712,7 @@ srs_error_t SrsSourceManager::fetch_or_create(SrsRequest* r, ISrsSourceHandler* // TODO: FIXME: Use smaller lock. SrsLocker(lock); - SrsSource* source = NULL; + SrsLiveSource* source = NULL; if ((source = fetch(r)) != NULL) { *pps = source; return err; @@ -1726,7 +1726,7 @@ srs_error_t SrsSourceManager::fetch_or_create(SrsRequest* r, ISrsSourceHandler* srs_trace("new source, stream_url=%s", stream_url.c_str()); - source = new SrsSource(); + source = new SrsLiveSource(); if ((err = source->initialize(r, h)) != srs_success) { err = srs_error_wrap(err, "init source %s", r->get_stream_url().c_str()); goto failed; @@ -1741,9 +1741,9 @@ failed: return err; } -SrsSource* SrsSourceManager::fetch(SrsRequest* r) +SrsLiveSource* SrsLiveSourceManager::fetch(SrsRequest* r) { - SrsSource* source = NULL; + SrsLiveSource* source = NULL; string stream_url = r->get_stream_url(); if (pool.find(stream_url) == pool.end()) { @@ -1760,17 +1760,17 @@ SrsSource* SrsSourceManager::fetch(SrsRequest* r) return source; } -void SrsSourceManager::dispose() +void SrsLiveSourceManager::dispose() { - std::map::iterator it; + std::map::iterator it; for (it = pool.begin(); it != pool.end(); ++it) { - SrsSource* source = it->second; + SrsLiveSource* source = it->second; source->dispose(); } return; } -srs_error_t SrsSourceManager::setup_ticks() +srs_error_t SrsLiveSourceManager::setup_ticks() { srs_error_t err = srs_success; @@ -1785,13 +1785,13 @@ srs_error_t SrsSourceManager::setup_ticks() return err; } -srs_error_t SrsSourceManager::notify(int event, srs_utime_t interval, srs_utime_t tick) +srs_error_t SrsLiveSourceManager::notify(int event, srs_utime_t interval, srs_utime_t tick) { srs_error_t err = srs_success; - std::map::iterator it; + std::map::iterator it; for (it = pool.begin(); it != pool.end();) { - SrsSource* source = it->second; + SrsLiveSource* source = it->second; // Do cycle source to cleanup components, such as hls dispose. if ((err = source->cycle()) != srs_success) { @@ -1826,25 +1826,25 @@ srs_error_t SrsSourceManager::notify(int event, srs_utime_t interval, srs_utime_ return err; } -void SrsSourceManager::destroy() +void SrsLiveSourceManager::destroy() { - std::map::iterator it; + std::map::iterator it; for (it = pool.begin(); it != pool.end(); ++it) { - SrsSource* source = it->second; + SrsLiveSource* source = it->second; srs_freep(source); } pool.clear(); } -ISrsSourceBridger::ISrsSourceBridger() +ISrsLiveSourceBridger::ISrsLiveSourceBridger() { } -ISrsSourceBridger::~ISrsSourceBridger() +ISrsLiveSourceBridger::~ISrsLiveSourceBridger() { } -SrsSource::SrsSource() +SrsLiveSource::SrsLiveSource() { req = NULL; jitter_algorithm = SrsRtmpJitterAlgorithmOFF; @@ -1870,7 +1870,7 @@ SrsSource::SrsSource() atc = false; } -SrsSource::~SrsSource() +SrsLiveSource::~SrsLiveSource() { _srs_config->unsubscribe(this); @@ -1890,14 +1890,14 @@ SrsSource::~SrsSource() srs_freep(bridger_); } -void SrsSource::dispose() +void SrsLiveSource::dispose() { hub->dispose(); meta->dispose(); gop_cache->dispose(); } -srs_error_t SrsSource::cycle() +srs_error_t SrsLiveSource::cycle() { srs_error_t err = hub->cycle(); if (err != srs_success) { @@ -1907,7 +1907,7 @@ srs_error_t SrsSource::cycle() return srs_success; } -bool SrsSource::expired() +bool SrsLiveSource::expired() { // unknown state? if (die_at == 0) { @@ -1932,7 +1932,7 @@ bool SrsSource::expired() return false; } -srs_error_t SrsSource::initialize(SrsRequest* r, ISrsSourceHandler* h) +srs_error_t SrsLiveSource::initialize(SrsRequest* r, ISrsLiveSourceHandler* h) { srs_error_t err = srs_success; @@ -1963,13 +1963,13 @@ srs_error_t SrsSource::initialize(SrsRequest* r, ISrsSourceHandler* h) return err; } -void SrsSource::set_bridger(ISrsSourceBridger* v) +void SrsLiveSource::set_bridger(ISrsLiveSourceBridger* v) { srs_freep(bridger_); bridger_ = v; } -srs_error_t SrsSource::on_reload_vhost_play(string vhost) +srs_error_t SrsLiveSource::on_reload_vhost_play(string vhost) { srs_error_t err = srs_success; @@ -2018,10 +2018,10 @@ srs_error_t SrsSource::on_reload_vhost_play(string vhost) srs_utime_t v = _srs_config->get_queue_length(req->vhost); if (true) { - std::vector::iterator it; + std::vector::iterator it; for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; + SrsLiveConsumer* consumer = *it; consumer->set_queue_size(v); } @@ -2052,7 +2052,7 @@ srs_error_t SrsSource::on_reload_vhost_play(string vhost) return err; } -srs_error_t SrsSource::on_source_id_changed(SrsContextId id) +srs_error_t SrsLiveSource::on_source_id_changed(SrsContextId id) { srs_error_t err = srs_success; @@ -2066,36 +2066,36 @@ srs_error_t SrsSource::on_source_id_changed(SrsContextId id) _source_id = id; // notice all consumer - std::vector::iterator it; + std::vector::iterator it; for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; + SrsLiveConsumer* consumer = *it; consumer->update_source_id(); } return err; } -SrsContextId SrsSource::source_id() +SrsContextId SrsLiveSource::source_id() { return _source_id; } -SrsContextId SrsSource::pre_source_id() +SrsContextId SrsLiveSource::pre_source_id() { return _pre_source_id; } -bool SrsSource::inactive() +bool SrsLiveSource::inactive() { return _can_publish; } -void SrsSource::update_auth(SrsRequest* r) +void SrsLiveSource::update_auth(SrsRequest* r) { req->update_auth(r); } -bool SrsSource::can_publish(bool is_edge) +bool SrsLiveSource::can_publish(bool is_edge) { // TODO: FIXME: Should check the status of bridger. @@ -2106,7 +2106,7 @@ bool SrsSource::can_publish(bool is_edge) return _can_publish; } -srs_error_t SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata) +srs_error_t SrsLiveSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata) { srs_error_t err = srs_success; @@ -2139,9 +2139,9 @@ srs_error_t SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* // copy to all consumer if (!drop_for_reduce) { - std::vector::iterator it; + std::vector::iterator it; for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; + SrsLiveConsumer* consumer = *it; if ((err = consumer->enqueue(meta->data(), atc, jitter_algorithm)) != srs_success) { return srs_error_wrap(err, "consume metadata"); } @@ -2152,7 +2152,7 @@ srs_error_t SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* return hub->on_meta_data(meta->data(), metadata); } -srs_error_t SrsSource::on_audio(SrsCommonMessage* shared_audio) +srs_error_t SrsLiveSource::on_audio(SrsCommonMessage* shared_audio) { srs_error_t err = srs_success; @@ -2197,7 +2197,7 @@ srs_error_t SrsSource::on_audio(SrsCommonMessage* shared_audio) return err; } -srs_error_t SrsSource::on_audio_imp(SrsSharedPtrMessage* msg) +srs_error_t SrsLiveSource::on_audio_imp(SrsSharedPtrMessage* msg) { srs_error_t err = srs_success; @@ -2226,7 +2226,7 @@ srs_error_t SrsSource::on_audio_imp(SrsSharedPtrMessage* msg) // copy to all consumer if (!drop_for_reduce) { for (int i = 0; i < (int)consumers.size(); i++) { - SrsConsumer* consumer = consumers.at(i); + SrsLiveConsumer* consumer = consumers.at(i); if ((err = consumer->enqueue(msg, atc, jitter_algorithm)) != srs_success) { return srs_error_wrap(err, "consume message"); } @@ -2265,7 +2265,7 @@ srs_error_t SrsSource::on_audio_imp(SrsSharedPtrMessage* msg) return err; } -srs_error_t SrsSource::on_video(SrsCommonMessage* shared_video) +srs_error_t SrsLiveSource::on_video(SrsCommonMessage* shared_video) { srs_error_t err = srs_success; @@ -2322,7 +2322,7 @@ srs_error_t SrsSource::on_video(SrsCommonMessage* shared_video) return err; } -srs_error_t SrsSource::on_video_imp(SrsSharedPtrMessage* msg) +srs_error_t SrsLiveSource::on_video_imp(SrsSharedPtrMessage* msg) { srs_error_t err = srs_success; @@ -2356,7 +2356,7 @@ srs_error_t SrsSource::on_video_imp(SrsSharedPtrMessage* msg) // copy to all consumer if (!drop_for_reduce) { for (int i = 0; i < (int)consumers.size(); i++) { - SrsConsumer* consumer = consumers.at(i); + SrsLiveConsumer* consumer = consumers.at(i); if ((err = consumer->enqueue(msg, atc, jitter_algorithm)) != srs_success) { return srs_error_wrap(err, "consume video"); } @@ -2386,7 +2386,7 @@ srs_error_t SrsSource::on_video_imp(SrsSharedPtrMessage* msg) return err; } -srs_error_t SrsSource::on_aggregate(SrsCommonMessage* msg) +srs_error_t SrsLiveSource::on_aggregate(SrsCommonMessage* msg) { srs_error_t err = srs_success; @@ -2476,7 +2476,7 @@ srs_error_t SrsSource::on_aggregate(SrsCommonMessage* msg) return err; } -srs_error_t SrsSource::on_publish() +srs_error_t SrsLiveSource::on_publish() { srs_error_t err = srs_success; @@ -2523,7 +2523,7 @@ srs_error_t SrsSource::on_publish() return err; } -void SrsSource::on_unpublish() +void SrsLiveSource::on_unpublish() { // ignore when already unpublished. if (_can_publish) { @@ -2569,11 +2569,11 @@ void SrsSource::on_unpublish() } } -srs_error_t SrsSource::create_consumer(SrsConsumer*& consumer) +srs_error_t SrsLiveSource::create_consumer(SrsLiveConsumer*& consumer) { srs_error_t err = srs_success; - consumer = new SrsConsumer(this); + consumer = new SrsLiveConsumer(this); consumers.push_back(consumer); // for edge, when play edge stream, check the state @@ -2587,7 +2587,7 @@ srs_error_t SrsSource::create_consumer(SrsConsumer*& consumer) return err; } -srs_error_t SrsSource::consumer_dumps(SrsConsumer* consumer, bool ds, bool dm, bool dg) +srs_error_t SrsLiveSource::consumer_dumps(SrsLiveConsumer* consumer, bool ds, bool dm, bool dg) { srs_error_t err = srs_success; @@ -2630,9 +2630,9 @@ srs_error_t SrsSource::consumer_dumps(SrsConsumer* consumer, bool ds, bool dm, b return err; } -void SrsSource::on_consumer_destroy(SrsConsumer* consumer) +void SrsLiveSource::on_consumer_destroy(SrsLiveConsumer* consumer) { - std::vector::iterator it; + std::vector::iterator it; it = std::find(consumers.begin(), consumers.end(), consumer); if (it != consumers.end()) { consumers.erase(it); @@ -2644,33 +2644,33 @@ void SrsSource::on_consumer_destroy(SrsConsumer* consumer) } } -void SrsSource::set_cache(bool enabled) +void SrsLiveSource::set_cache(bool enabled) { gop_cache->set(enabled); } -SrsRtmpJitterAlgorithm SrsSource::jitter() +SrsRtmpJitterAlgorithm SrsLiveSource::jitter() { return jitter_algorithm; } -srs_error_t SrsSource::on_edge_start_publish() +srs_error_t SrsLiveSource::on_edge_start_publish() { return publish_edge->on_client_publish(); } // TODO: FIXME: Use edge strategy pattern. -srs_error_t SrsSource::on_edge_proxy_publish(SrsCommonMessage* msg) +srs_error_t SrsLiveSource::on_edge_proxy_publish(SrsCommonMessage* msg) { return publish_edge->on_proxy_publish(msg); } -void SrsSource::on_edge_proxy_unpublish() +void SrsLiveSource::on_edge_proxy_unpublish() { publish_edge->on_proxy_unpublish(); } -string SrsSource::get_curr_origin() +string SrsLiveSource::get_curr_origin() { return play_edge->get_curr_origin(); } diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index aad6cd9f7..719f99a9b 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -38,10 +38,10 @@ class SrsFormat; class SrsRtmpFormat; -class SrsConsumer; +class SrsLiveConsumer; class SrsPlayEdge; class SrsPublishEdge; -class SrsSource; +class SrsLiveSource; class SrsCommonMessage; class SrsOnMetaDataPacket; class SrsSharedPtrMessage; @@ -157,8 +157,8 @@ public: // @max_count the max count to dequeue, must be positive. virtual srs_error_t dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count); // Dumps packets to consumer, use specified args. - // @remark the atc/tba/tbv/ag are same to SrsConsumer.enqueue(). - virtual srs_error_t dump_packets(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag); + // @remark the atc/tba/tbv/ag are same to SrsLiveConsumer.enqueue(). + virtual srs_error_t dump_packets(SrsLiveConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag); private: // Remove a gop from the front. // if no iframe found, clear it. @@ -182,12 +182,12 @@ public: virtual void wakeup() = 0; }; -// The consumer for SrsSource, that is a play client. -class SrsConsumer : public ISrsWakable +// The consumer for SrsLiveSource, that is a play client. +class SrsLiveConsumer : public ISrsWakable { private: SrsRtmpJitter* jitter; - SrsSource* source; + SrsLiveSource* source; SrsMessageQueue* queue; bool paused; // when source id changed, notice all consumers @@ -201,8 +201,8 @@ private: srs_utime_t mw_duration; #endif public: - SrsConsumer(SrsSource* s); - virtual ~SrsConsumer(); + SrsLiveConsumer(SrsLiveSource* s); + virtual ~SrsLiveConsumer(); public: // Set the size of queue. virtual void set_queue_size(srs_utime_t queue_size); @@ -279,7 +279,7 @@ public: // clear the gop cache. virtual void clear(); // dump the cached gop to consumer. - virtual srs_error_t dump(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm jitter_algorithm); + virtual srs_error_t dump(SrsLiveConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm jitter_algorithm); // used for atc to get the time of gop cache, // The atc will adjust the sequence header timestamp to gop cache. virtual bool empty(); @@ -294,16 +294,16 @@ public: // The handler to handle the event of srs source. // For example, the http flv streaming module handle the event and // mount http when rtmp start publishing. -class ISrsSourceHandler +class ISrsLiveSourceHandler { public: - ISrsSourceHandler(); - virtual ~ISrsSourceHandler(); + ISrsLiveSourceHandler(); + virtual ~ISrsLiveSourceHandler(); public: // when stream start publish, mount stream. - virtual srs_error_t on_publish(SrsSource* s, SrsRequest* r) = 0; + virtual srs_error_t on_publish(SrsLiveSource* s, SrsRequest* r) = 0; // when stream stop publish, unmount stream. - virtual void on_unpublish(SrsSource* s, SrsRequest* r) = 0; + virtual void on_unpublish(SrsLiveSource* s, SrsRequest* r) = 0; }; // The mix queue to correct the timestamp for mix_correct algorithm. @@ -328,7 +328,7 @@ public: class SrsOriginHub : public ISrsReloadHandler { private: - SrsSource* source; + SrsLiveSource* source; SrsRequest* req; bool is_active; private: @@ -356,7 +356,7 @@ public: public: // Initialize the hub with source and request. // @param r The request object, managed by source. - virtual srs_error_t initialize(SrsSource* s, SrsRequest* r); + virtual srs_error_t initialize(SrsLiveSource* s, SrsRequest* r); // Dispose the hub, release utilities resource, // For example, delete all HLS pieces. virtual void dispose(); @@ -433,7 +433,7 @@ public: // Dumps cached metadata to consumer. // @param dm Whether dumps the metadata. // @param ds Whether dumps the sequence header. - virtual srs_error_t dumps(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag, bool dm, bool ds); + virtual srs_error_t dumps(SrsLiveConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag, bool dm, bool ds); public: // Previous exists sequence header. virtual SrsSharedPtrMessage* previous_vsh(); @@ -451,26 +451,26 @@ public: }; // The source manager to create and refresh all stream sources. -class SrsSourceManager : public ISrsHourGlass +class SrsLiveSourceManager : public ISrsHourGlass { private: srs_mutex_t lock; - std::map pool; + std::map pool; SrsHourGlass* timer_; public: - SrsSourceManager(); - virtual ~SrsSourceManager(); + SrsLiveSourceManager(); + virtual ~SrsLiveSourceManager(); public: virtual srs_error_t initialize(); // create source when fetch from cache failed. // @param r the client request. // @param h the event handler for source. // @param pps the matched source, if success never be NULL. - virtual srs_error_t fetch_or_create(SrsRequest* r, ISrsSourceHandler* h, SrsSource** pps); + virtual srs_error_t fetch_or_create(SrsRequest* r, ISrsLiveSourceHandler* h, SrsLiveSource** pps); private: // Get the exists source, NULL when not exists. // update the request and return the exists source. - virtual SrsSource* fetch(SrsRequest* r); + virtual SrsLiveSource* fetch(SrsRequest* r); public: // dispose and cycle all sources. virtual void dispose(); @@ -485,14 +485,14 @@ public: }; // Global singleton instance. -extern SrsSourceManager* _srs_sources; +extern SrsLiveSourceManager* _srs_sources; -// For RTMP2RTC, bridge SrsSource to SrsRtcStream -class ISrsSourceBridger +// For RTMP2RTC, bridge SrsLiveSource to SrsRtcSource +class ISrsLiveSourceBridger { public: - ISrsSourceBridger(); - virtual ~ISrsSourceBridger(); + ISrsLiveSourceBridger(); + virtual ~ISrsLiveSourceBridger(); public: virtual srs_error_t on_publish() = 0; virtual srs_error_t on_audio(SrsSharedPtrMessage* audio) = 0; @@ -501,8 +501,7 @@ public: }; // The live streaming source. -// TODO: FIXME: Rename to SrsLiveStream. -class SrsSource : public ISrsReloadHandler +class SrsLiveSource : public ISrsReloadHandler { friend class SrsOriginHub; private: @@ -516,7 +515,7 @@ private: // deep copy of client request. SrsRequest* req; // To delivery stream to clients. - std::vector consumers; + std::vector consumers; // The time jitter algorithm for vhost. SrsRtmpJitterAlgorithm jitter_algorithm; // For play, whether use interlaced/mixed algorithm to correct timestamp. @@ -533,9 +532,9 @@ private: // The time of the packet we just got. int64_t last_packet_time; // The event handler. - ISrsSourceHandler* handler; + ISrsLiveSourceHandler* handler; // The source bridger for other source. - ISrsSourceBridger* bridger_; + ISrsLiveSourceBridger* bridger_; // The edge control service SrsPlayEdge* play_edge; SrsPublishEdge* publish_edge; @@ -552,8 +551,8 @@ private: // We will remove the source when source die. srs_utime_t die_at; public: - SrsSource(); - virtual ~SrsSource(); + SrsLiveSource(); + virtual ~SrsLiveSource(); public: virtual void dispose(); virtual srs_error_t cycle(); @@ -561,9 +560,9 @@ public: virtual bool expired(); public: // Initialize the hls with handlers. - virtual srs_error_t initialize(SrsRequest* r, ISrsSourceHandler* h); + virtual srs_error_t initialize(SrsRequest* r, ISrsLiveSourceHandler* h); // Bridge to other source, forward packets to it. - void set_bridger(ISrsSourceBridger* v); + void set_bridger(ISrsLiveSourceBridger* v); // Interface ISrsReloadHandler public: virtual srs_error_t on_reload_vhost_play(std::string vhost); @@ -601,13 +600,13 @@ public: public: // Create consumer // @param consumer, output the create consumer. - virtual srs_error_t create_consumer(SrsConsumer*& consumer); + virtual srs_error_t create_consumer(SrsLiveConsumer*& consumer); // Dumps packets in cache to consumer. // @param ds, whether dumps the sequence header. // @param dm, whether dumps the metadata. // @param dg, whether dumps the gop cache. - virtual srs_error_t consumer_dumps(SrsConsumer* consumer, bool ds = true, bool dm = true, bool dg = true); - virtual void on_consumer_destroy(SrsConsumer* consumer); + virtual srs_error_t consumer_dumps(SrsLiveConsumer* consumer, bool ds = true, bool dm = true, bool dg = true); + virtual void on_consumer_destroy(SrsLiveConsumer* consumer); virtual void set_cache(bool enabled); virtual SrsRtmpJitterAlgorithm jitter(); public: diff --git a/trunk/src/app/srs_app_threads.cpp b/trunk/src/app/srs_app_threads.cpp index a7dff9b82..8f4b52715 100644 --- a/trunk/src/app/srs_app_threads.cpp +++ b/trunk/src/app/srs_app_threads.cpp @@ -291,8 +291,8 @@ srs_error_t srs_thread_initialize() // The global objects which depends on ST. _srs_hybrid = new SrsHybridServer(); - _srs_rtc_sources = new SrsRtcStreamManager(); - _srs_sources = new SrsSourceManager(); + _srs_rtc_sources = new SrsRtcSourceManager(); + _srs_sources = new SrsLiveSourceManager(); _srs_stages = new SrsStageManager(); _srs_blackhole = new SrsRtcBlackhole(); _srs_rtc_manager = new SrsResourceManager("RTC", true); diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp index 47bad09fc..8f301a368 100644 --- a/trunk/src/core/srs_core_version4.hpp +++ b/trunk/src/core/srs_core_version4.hpp @@ -26,6 +26,6 @@ #define VERSION_MAJOR 4 #define VERSION_MINOR 0 -#define VERSION_REVISION 111 +#define VERSION_REVISION 115 #endif