diff --git a/.appveyor.yml b/.appveyor.yml index 96b785d93..1254c9c16 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,9 +3,9 @@ configuration: - Debug image: + - Visual Studio 2022 - Visual Studio 2019 - Visual Studio 2015 - - Visual Studio 2013 platform: - x64 @@ -14,12 +14,8 @@ platform: build_script: - ps: $VSIMG = $Env:APPVEYOR_BUILD_WORKER_IMAGE; $CNFG = $Env:CONFIGURATION # use a few differing arguments depending on VS version to exercise different options during builds - - ps: if ($VSIMG -match '2019' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -UNIT_TESTS ON -BONDING ON } - - ps: if ($VSIMG -match '2019' -and $CNFG -eq "Debug") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON } - - ps: if ($VSIMG -match '2015' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -UNIT_TESTS ON -BONDING ON} - - ps: if ($VSIMG -match '2015' -and $CNFG -eq "Debug") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS OFF } - - ps: if ($VSIMG -match '2013' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -CXX11 OFF -BUILD_APPS ON } - - ps: if ($VSIMG -match '2013' -and $CNFG -eq "Debug") { Exit-AppveyorBuild } # just skip 2013 debug build for speed + - ps: if ($CNFG -eq "Release") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -UNIT_TESTS ON -BONDING ON} + - ps: if ($CNFG -eq "Debug") { if ($VSIMG -match '2015') { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS OFF } else {.\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON }} test_script: - ps: if ( $Env:RUN_UNIT_TESTS ) { cd ./_build; ctest -E "TestIPv6.v6_calls_v4" --extra-verbose -C $Env:CONFIGURATION; cd ../ } diff --git a/CMakeLists.txt b/CMakeLists.txt index eef749f21..e5bc7e3bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ # cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) -set (SRT_VERSION 1.5.3) +set (SRT_VERSION 1.5.4) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") include(haiUtil) # needed for set_version_variables @@ -1383,7 +1383,6 @@ if (ENABLE_APPS) # 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) @@ -1421,7 +1420,6 @@ if (ENABLE_APPS) 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) diff --git a/README.md b/README.md index 8b263aebb..0bf810144 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ [![License: MPLv2.0][license-badge]](./LICENSE) [![Latest release][release-badge]][github releases] +[![Quality Gate Status][sonarcloud-badge]][sonarcloud-project] [![codecov][codecov-badge]][codecov-project] [![Build Status Linux and macOS][travis-badge]][travis] [![Build Status Windows][appveyor-badge]][appveyor] @@ -225,6 +226,9 @@ By contributing code to the SRT project, you agree to license your contribution [ConanCenter-package]: https://repology.org/project/srt/versions [ConanCenter-badge]: https://repology.org/badge/version-for-repo/conancenter/srt.svg +[sonarcloud-project]: https://sonarcloud.io/project/overview?id=srt +[sonarcloud-badge]: https://sonarcloud.io/api/project_badges/measure?project=srt&metric=alert_status + [codecov-project]: https://codecov.io/gh/haivision/srt [codecov-badge]: https://codecov.io/gh/haivision/srt/branch/master/graph/badge.svg diff --git a/apps/verbose.cpp b/apps/verbose.cpp index 58cf83fbe..4ea3b47dc 100644 --- a/apps/verbose.cpp +++ b/apps/verbose.cpp @@ -9,14 +9,13 @@ */ #include "verbose.hpp" +#include "sync.h" // srt::sync namespace Verbose { bool on = false; std::ostream* cverb = &std::cerr; -#if SRT_ENABLE_VERBOSE_LOCK - std::mutex vlock; -#endif + srt::sync::Mutex vlock; Log& Log::operator<<(LogNoEol) { @@ -28,19 +27,16 @@ namespace Verbose return *this; } -#if SRT_ENABLE_VERBOSE_LOCK Log& Log::operator<<(LogLock) { lockline = true; return *this; } -#endif Log::~Log() { if (on && !noeol) { -#if SRT_ENABLE_VERBOSE_LOCK if (lockline) { // Lock explicitly, as requested, and wait for the opportunity. @@ -48,7 +44,7 @@ namespace Verbose } else if (vlock.try_lock()) { - // Successfully locked, so unlock immediately, locking wasn't requeted. + // Successfully locked, so unlock immediately, locking wasn't requested. vlock.unlock(); } else @@ -62,15 +58,12 @@ namespace Verbose vlock.lock(); vlock.unlock(); } -#endif (*cverb) << std::endl; -#if SRT_ENABLE_VERBOSE_LOCK // If lockline is set, the lock was requested and WAS DONE, so unlock. // Otherwise locking WAS NOT DONE. if (lockline) vlock.unlock(); -#endif } } } diff --git a/apps/verbose.hpp b/apps/verbose.hpp index 879d54086..e3d20732a 100644 --- a/apps/verbose.hpp +++ b/apps/verbose.hpp @@ -12,9 +12,7 @@ #define INC_SRT_VERBOSE_HPP #include -#if SRT_ENABLE_VERBOSE_LOCK -#include -#endif +#include "atomic.h" namespace Verbose { @@ -23,22 +21,22 @@ extern bool on; extern std::ostream* cverb; struct LogNoEol { LogNoEol() {} }; -#if SRT_ENABLE_VERBOSE_LOCK struct LogLock { LogLock() {} }; -#endif class Log { bool noeol = false; -#if SRT_ENABLE_VERBOSE_LOCK - bool lockline = false; -#endif + srt::sync::atomic lockline; // Disallow creating dynamic objects - void* operator new(size_t); + void* operator new(size_t) = delete; public: + Log() {} + Log(const Log& ) {} + + template Log& operator<<(const V& arg) { @@ -50,9 +48,7 @@ class Log } Log& operator<<(LogNoEol); -#if SRT_ENABLE_VERBOSE_LOCK Log& operator<<(LogLock); -#endif ~Log(); }; @@ -80,7 +76,7 @@ inline void Print(Log& ) {} template inline void Print(Log& out, Arg1&& arg1, Args&&... args) { - out << std::forward(arg1); + out << arg1; Print(out, args...); } @@ -96,11 +92,16 @@ inline void Verb(Args&&... args) Verbose::Print(log, args...); } +template +inline void Verror(Args&&... args) +{ + Verbose::ErrLog log; + Verbose::Print(log, args...); +} + // Manipulator tags static const Verbose::LogNoEol VerbNoEOL; -#if SRT_ENABLE_VERBOSE_LOCK static const Verbose::LogLock VerbLock; -#endif #endif diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 54895fe15..74fbc506f 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -174,6 +174,8 @@ Since SRT v1.5.0. | [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.5.2 | The connection was rejected due to an unsupported or mismatching encryption mode | | | | | +See the full list in [Rejection Reason Codes](./rejection-codes.md). +

Error Codes

| *Error Code* | *Description* | diff --git a/docs/API/rejection-codes.md b/docs/API/rejection-codes.md new file mode 100644 index 000000000..0b8418b5f --- /dev/null +++ b/docs/API/rejection-codes.md @@ -0,0 +1,473 @@ +# SRT Rejection Codes + +This document provides an overview of the rejection (error) codes used by and supported within SRT and SRT-based applications. For information on other types of error codes refer to the [API Socket Options](./docs/API/API-socket-options.md) document. + +[:arrow_down:   Jump to list of rejection codes](#api-function-rejection-codes) + + +## Summary of Rejection Codes + +Rejection codes are used in the SRT API, and are transferred on the wire as a part of a Handshake packet (refer to the `Handshake Type` field of the [Handshake](./docs/features/handshake.md) packet). +The rejection codes are divided into several ranges: + + - SRT internal + - Predefined application level codes + - User defined (custom) codes + +In the SRT API these ranges are marked with the following constants (preprocessor definitions): + + - `SRT_REJC_INTERNAL` = 0 + - `SRT_REJC_PREDEFINED` = 1000 + - `SRT_REJC_USERDEFINED` = 2000 + +When transferred on the wire, the API value is incremented by 1000 to become the `Handshake Type` field value. In the following sections the values of rejection reason codes are given in accordance with the API values. + + +### SRT Internal Rejection Codes + +Defined in [**srt.h**](srtcore/srt.h), these codes provide the reason why a connection is rejected by SRT. They cover the reserved range 0 - 999 (below `SRT_REJC_PREDEFINED`). These codes cannot be used by applications to report a rejection reason. + +Naming: `SRT_REJ_*` + + - `SRT_REJ_UNKNOWN` = 0 + - `SRT_REJ_SYSTEM` = 1 + - ... + - `SRT_REJ_CRYPTO` = 17 +See [the list below](#api-function-rejection-codes) for details. + +### Extended Rejection Codes + +As defined in [**access_control.h**](srtcore/access_control.h), these are standard server error codes including those adopted from HTTP. They provide the reason why an application rejects a connection. The value is expected to be set by an application via the listener callback if it wants to reject an incoming connection request. These codes cover the reserved range 1000 - 1999 (`SRT_REJC_PREDEFINED` - `SRT_REJC_USERDEFINED`). + + +Subranges (1000 + value): + + - **0 - 99**: Reserved for unique SRT-specific codes (unused by HTTP) + - **100 - 399**: Info, Success, and Redirection in HTTP (unused by SRT) + - **400 - 599**: Client and server errors in HTTP (adopted by SRT) + +Naming: `SRT_REJX_*` + +Example: + + - `SRT_REJX_KEY_NOTSUP` (1001): The key used in the StreamID keyed string is not supported by the service. + - `SRT_REJX_BAD_REQUEST` (1400) + - ... + +### User Defined Rejection Codes + +These codes can be freely defined by an application. They can be custom codes, not adopted by other vendors. For example, `2005: “Makito license expired”`. They cover the reserved range 2000 - 2999 (higher than `SRT_REJC_USERDEFINED`). + + +## API Function Rejection Codes + +SRT's API function rejection codes refer to system-level error conditions caused by SRT-specific settings or operating conditions. They are uninfluenced by application-related events, and applications are not permitted to use or simulate these codes. + +The table below lists the rejection codes as defined in [**srt.h**](srtcore/srt.h) (click the *Rejection Reason* link to view a complete description). + +| *Code* | *Rejection Reason* | *Since* | *Description* | +|:------:|:-------------------------------------------------- |:-------:|:-------------------------------------------------------------------------------------------------------------- | +| 0 | [SRT_REJ_UNKNOWN](#SRT_REJ_UNKNOWN) | 1.3.4 | Fallback value for cases where connection is not rejected. | +| 1 | [SRT_REJ_SYSTEM](#SRT_REJ_SYSTEM) | 1.3.4 | System function reported a failure. | +| 2 | [SRT_REJ_PEER](#SRT_REJ_PEER) | 1.3.4 | Connection rejected by peer, with no additional details. | +| 3 | [SRT_REJ_RESOURCE](#SRT_REJ_RESOURCE) | 1.3.4 | Problem with resource allocation (usually memory). | +| 4 | [SRT_REJ_ROGUE](#SRT_REJ_ROGUE) | 1.3.4 | Data sent by one party cannot be interpreted. | +| 5 | [SRT_REJ_BACKLOG](#SRT_REJ_BACKLOG) | 1.3.4 | Listener's backlog has been exceeded. | +| 6 | [SRT_REJ_IPE](#SRT_REJ_IPE) | 1.3.4 | Internal Program Error. | +| 7 | [SRT_REJ_CLOSE](#SRT_REJ_CLOSE) | 1.3.4 | Listener socket received a request as it is being closed. | +| 8 | [SRT_REJ_VERSION](#SRT_REJ_VERSION) | 1.3.4 | Minimum version requirement for a connection not satisfied by one party. | +| 9 | [SRT_REJ_RDVCOOKIE](#SRT_REJ_RDVCOOKIE) | 1.3.4 | Rendezvous cookie collision. | +| 10 | [SRT_REJ_BADSECRET](#SRT_REJ_BADSECRET) | 1.3.4 | Both parties have defined connection passphrases that differ. | +| 11 | [SRT_REJ_UNSECURE](#SRT_REJ_UNSECURE) | 1.3.4 | Only one party has set up a connection password. | +| 12 | [SRT_REJ_MESSAGEAPI](#SRT_REJ_MESSAGEAPI) | 1.3.4 | [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag is different on both connection parties. | +| 13 | [SRT_REJ_CONGESTION](#SRT_REJ_CONGESTION) | 1.3.4 | Incompatible congestion-controller type. | +| 14 | [SRT_REJ_FILTER](#SRT_REJ_FILTER) | 1.3.4 | [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option is different on both connection parties. | +| 15 | [SRT_REJ_GROUP](#SRT_REJ_GROUP) | 1.4.2 | Group type or group settings are incompatible between connection parties. | +| 16 | [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | 1.4.2 | Connection not rejected, but timed out. | +| 17 | [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.5.2 | Connection rejected due to unsupported or mismatching encryption mode. | +| | | | | + + +## Access Control Rejection Codes + +SRT's access control rejection codes are intended for use by applications to forcefully reject connections in SRT listener callbacks. They are intended only as a guide to promote standardization. If they are used in an application, a description of their specific implementation should be published (the descriptions in this documentation are not definitive). + +The table below lists the rejection codes as defined in [**access_control.h**](srtcore/access_control.h) (click the *Rejection Reason* link to view a complete description). + +| *Code* | *Rejection Reason* | *Since* | *Description* | +|:------:|:------------------------------------------------- |:-------:|:-------------------------------------------------------------------------------------------------------------- | +| 1000 | [SRT_REJX_FALLBACK](#SRT_REJX_FALLBACK) | 1.4.2 | Callback handler has interrupted an incoming connection. | +| 1001 | [SRT_REJX_KEY_NOTSUP](#SRT_REJX_KEY_NOTSUP) | 1.4.2 | Key specified in StreamID string not supported by application. | +| 1002 | [SRT_REJX_FILEPATH](#SRT_REJX_FILEPATH) | 1.4.2 | Resource type designates file where path has wrong syntax or is not found. | +| 1003 | [SRT_REJX_HOSTNOTFOUND](#SRT_REJX_HOSTNOTFOUND) | 1.4.2 | The host specified in the `h` key cannot be identified. | +| 1400 | [SRT_REJX_BAD_REQUEST](#SRT_REJX_BAD_REQUEST) | 1.4.2 | General syntax error. | +| 1401 | [SRT_REJX_UNAUTHORIZED](#SRT_REJX_UNAUTHORIZED) | 1.4.2 | Authentication failed; client unauthorized to access the resource. | +| 1402 | [SRT_REJX_OVERLOAD](#SRT_REJX_OVERLOAD) | 1.4.2 | Server load too heavy to process request, or credit limit exceeded. | +| 1403 | [SRT_REJX_FORBIDDEN](#SRT_REJX_FORBIDDEN) | 1.4.2 | Access denied to the resource for any reason. | +| 1404 | [SRT_REJX_NOTFOUND](#SRT_REJX_NOTFOUND) | 1.4.2 | Resource specified by `r` and `h` keys cannot be found. | +| 1405 | [SRT_REJX_BAD_MODE](#SRT_REJX_BAD_MODE) | 1.4.2 | Mode specified in the `m` key in StreamID is not supported for this request. | +| 1406 | [SRT_REJX_UNACCEPTABLE](#SRT_REJX_UNACCEPTABLE) | 1.4.2 | Unavailable parameters in `StreamID`, or `m=publish` data format not supported. | +| 1409 | [SRT_REJX_CONFLICT](#SRT_REJX_CONFLICT) | 1.4.2 | Resource specified by `r` and `h` keys is locked for modification. | +| 1415 | [SRT_REJX_NOTSUP_MEDIA](#SRT_REJX_NOTSUP_MEDIA) | 1.4.2 | Media type not supported by the application. | +| 1423 | [SRT_REJX_LOCKED](#SRT_REJX_LOCKED) | 1.4.2 | Resource is locked against any access. | +| 1424 | [SRT_REJX_FAILED_DEPEND](#SRT_REJX_FAILED_DEPEND) | 1.4.2 | Dependent entity for the request is not present. | +| 1500 | [SRT_REJX_ISE](#SRT_REJX_ISE) | 1.4.2 | Internal server error. | +| 1501 | [SRT_REJX_UNIMPLEMENTED](#SRT_REJX_UNIMPLEMENTED) | 1.4.2 | Request not supported by current version of the service. | +| 1502 | [SRT_REJX_GW](#SRT_REJX_GW) | 1.4.2 | Target endpoint rejected connection from gateway server | +| 1503 | [SRT_REJX_DOWN](#SRT_REJX_DOWN) | 1.4.2 | Service is down for maintenance. | +| 1505 | [SRT_REJX_VERSION](#SRT_REJX_VERSION) | 1.4.2 | SRT application version not supported. | +| 1507 | [SRT_REJX_NOROOM](#SRT_REJX_NOROOM) | 1.4.2 | Data stream cannot be archived due to lack of storage space. | +| | | | | + + +**NOTE**: SRT rejection codes follow this prefix convention: + + - `SRT_REJ`: standard rejection codes from SRT API functions (0 - 99) + - `SRT_REJC`: mark the border values between ranges. + - `SRT_REJX`: extended rejection codes (code values above 1000).above)?* + + +## API Function Rejection Reasons + + +#### SRT_REJ_UNKNOWN + +A fallback value for cases when there was no connection rejected. + + +#### SRT_REJ_SYSTEM + +One system function reported a failure. Usually this means some system +error or lack of system resources to complete the task. + + +#### SRT_REJ_PEER + +The connection has been rejected by the peer, but no further details are available. +This usually means that the peer doesn't support rejection reason reporting. + + +#### SRT_REJ_RESOURCE + +A problem with resource allocation (usually memory). + + +#### SRT_REJ_ROGUE + +The data sent by one party to another cannot be properly interpreted. This +should not happen during normal usage, unless it's a bug, or some weird +events are happening on the network. + + +#### SRT_REJ_BACKLOG + +The listener's backlog has exceeded its queue limit (there are many other callers +waiting for the opportunity to be connected and the "wait queue" has +reached its limit). + + +#### SRT_REJ_IPE + +Internal Program Error. This should not happen during normal usage. It +usually indicates a bug in the software (although this can be reported by both +local and foreign hosts). + + +#### SRT_REJ_CLOSE + +The listener socket was able to receive the request, but is currently +being closed. It's likely that the next request will result in a timeout. + + +#### SRT_REJ_VERSION + +One party in the connection has set up a minimum version that is required for +that connection, but the other party doesn't satisfy this requirement. + + +#### SRT_REJ_RDVCOOKIE + +Rendezvous cookie collision. Normally, the probability that this will happen is +negligible. However, it *can* result from a misconfiguration when, in attempting +to make a rendezvous connection, both parties try to bind to the same IP address, +or both are local addresses of the same host. In such a case the sent handshake +packets are returned to the same host as if they were sent by the peer (i.e. a +party is sending to itself). In such situations, this reject reason will be +reported for every attempt. + + +#### SRT_REJ_BADSECRET + +Both parties have defined a passphrase for a connection, but they differ. + + +#### SRT_REJ_UNSECURE + +Only one connection party has set up a password. See also the +[`SRTO_ENFORCEDENCRYPTION`](API-socket-options.md#SRTO_ENFORCEDENCRYPTION) flag. + + +#### SRT_REJ_MESSAGEAPI + +The value of the [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) +flag is different on both parties in a connection. + + +#### SRT_REJ_CONGESTION + +The [`SRTO_CONGESTION`](API-socket-options.md#SRTO_CONGESTION)option has +been set up differently on both parties in a connection. + + +#### SRT_REJ_FILTER + +The [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option +has been set differently on both parties in a connection. + + +#### SRT_REJ_GROUP + +The group type or some group settings are incompatible between connection parties. +While every connection within a bonding group may have different target addresses, +they should all designate the same endpoint and the same SRT application. If this +condition isn't satisfied, then the peer will respond with a different peer group +ID for the connection that is trying to contact a machine/application that is +completely different from the existing connections in the bonding group. + + +#### SRT_REJ_TIMEOUT + +The connection wasn't rejected, but it timed out. This code is always sent on +a connection timeout, but this is the only way to get this state in non-blocking +mode (see [`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN)). + +There may also be server and user rejection codes, as defined by the +`SRT_REJC_INTERNAL`, `SRT_REJC_PREDEFINED`, and `SRT_REJC_USERDEFINED` +constants. Note that the number space from the value of `SRT_REJC_PREDEFINED` +and above is reserved for "predefined codes" (`SRT_REJC_PREDEFINED` value plus +adopted HTTP codes). Values above `SRT_REJC_USERDEFINED` are freely defined by +the application. + +#### SRT_REJ_CRYPTO + +The connection was rejected due to a mismatch in crypto modes. See `SRTO_CRYPTOMODE`. + +[:arrow_up:   Back to top](#srt-rejection-codes) + + +## Access Control Rejection Reasons + +An SRT listener callback handler can decide to reject an incoming connection. +Under normal circumstances, the rejection code is predefined as `SRT_REJ_RESOURCE`. +The handler can, however, set its own rejection code. There are two numbered spaces +intended for this purpose (as the range below `SRT_REJC_PREDEFINED` is reserved +for internal codes): + +- `SRT_REJC_PREDEFINED` and above: These are predefined errors. Errors from this +range (that is, below `SRT_REJC_USERDEFINED`) have their definitions provided in +the `access_control.h` public header file. The intention is that applications +using these codes understand the situations they describe in a standard way. + +- `SRT_REJC_USERDEFINED` and above: These are errors that are freely defined by +the application. Codes from this range can be only understood if each application +knows the code definitions of the other. These codes should be used only after +making sure that the applications at either end of a connection understand them. + +The intention here is for the predefined codes to be consistent with the HTTP +standard codes. Such code can be set by using the [`srt_setrejectreason`](docs/api/api-functions.md#srt-setrejectreason) function. + +The SRT-specific codes are: + +#### SRT_REJX_FALLBACK + +This code should be set by the callback handler in the beginning in case +the application needs to be informed that the callback handler +actually has interpreted the incoming connection, but hasn't set a +more appropriate code describing the situation. + +#### SRT_REJX_KEY_NOTSUP + +Indicates there was a key specified in the StreamID string that this application +doesn't support. Note that it's not obligatory for the application to +react this way - it may chose to ignore unknown keys completely, or +to have some keys in the ignore list (which it won't interpret, but tolerate) +while rejecting any others. It is also up to the application +to decide to return this specific error, or more generally report +the syntax error with `SRT_REJX_BAD_REQUEST`. + +#### SRT_REJX_FILEPATH + +The resource type designates a file, and the path either has the wrong syntax +or is not found. In the case where `t=file`, the path should be specified under +the `r` key, and the file specified there must be able to be saved this way. +It's up to the application to decide how to treat this path, how to parse it, +and what this path specifically means. For the `r` key, the application should +at least handle the single filename, and have storage space available to save +it (provided a file of the same name does not already exist there). The +application should decide whether and how to handle all other situations (like +directory path, special markers in the path to be interpreted by the +application, etc.), or to report this error. + +#### SRT_REJX_HOSTNOTFOUND + +The host specified in the `h` key cannot be identified. The `h` key is +generally for a situation when you have multiple DNS names for a host, +so an application may want to extract the name from the URI and set it +to the `h` key so that the application can distinguish the request also by +the target host name. The application may, however, limit the number of +recognized services by host name to some predefined names and not +handle the others, even if this is properly resolved by DNS. In this +case it should report this error. + +The other error codes are HTTP codes adapted for SRT: + +#### SRT_REJX_BAD_REQUEST + +General syntax error. This can be reported in any case when parsing +the StreamID contents failed, or it cannot be properly interpreted. + +#### SRT_REJX_UNAUTHORIZED + +Authentication failed, which makes the client unauthorized to access the +resource. This error, however, confirms that the syntax is correct and +the resource has been properly identified. Note that this cannot be +reported when you use a simple user-password authentication +method because in this case the password is verified only after the +listener callback handler accepts the connection. This error is rather +intended to be reported in the case of `t=auth` when the authentication +process has generated some valid session ID, but then the session +connection has specified a resource that is not within the frames +of that authentication. + +#### SRT_REJX_OVERLOAD + +The server is too heavily loaded to process the request, or the credit limit +for accessing the service and the resource has been exceeded. +In HTTP the description mentions payment for a service, but +it is also used by some services for general "credit" management +for a client. In SRT it should be used when the service is doing +any kind of credit management to limit access to selected clients +that "have" enough credit, even if the credit is something the client +can recharge itself, or that can be granted depending on available +service resources. + +#### SRT_REJX_FORBIDDEN + +Access denied to the resource for any reason. This error is +independent of an authorization or authentication error (as reported +by `SRT_REJX_UNAUTHORIZED`). The application can decide which +is more appropriate. This error is usually intended for +a resource that should only be accessed after a successful +authorization over a separate auth-only connection, where the query +in StreamID has correctly specified the resource identity and mode, +but the session ID (in the `s` key) is either (a) not specified, or +(b) specifies a valid session, but the authorization region for this +session does not include the specified resource. + +#### SRT_REJX_NOTFOUND + +The resource specified in the `r` key (in combination with the `h` key) +is not found at this time. This error should be only reported if the +information about resource accessibility is allowed to be publicly +visible. Otherwise, the application might report authorization +errors. + +#### SRT_REJX_BAD_MODE + +The mode specified in the `m` key in StreamID is not supported for this request. +This may apply to read-only or write-only resources, as well when interactive +(bidirectional) access is not valid for a resource. + +#### SRT_REJX_UNACCEPTABLE + +Applies when the parameters specified in StreamID cannot be satisfied for the +requested resource, or when `m=publish` but the data format is not acceptable. +This is a general error reporting an unsupported format for data that appears to +be wrong when sending, or a restriction on the data format (as specified in the +details of the resource specification) such that it cannot be provided +when receiving. + +#### SRT_REJX_CONFLICT + +The resource being accessed (as specified by `r` and `h` keys) is locked for +modification. This error should only be reported for `m=publish` when the +resource being accessed is read-only because another client (not necessarily +connected through SRT): + +- is currently publishing into this resource +- has reserved this resource ID for publishing + +Note that this error should be reported when there is no other reason for +having a problem accessing the resource. + +#### SRT_REJX_NOTSUP_MEDIA + +The media type is not supported by the application. The media type is +specified in the `t` key. The currently standard types are +`stream`, `file` and `auth`. An application may extend this list, and +is not obliged to support all of the standard types. + +#### SRT_REJX_LOCKED + +The resource being accessed is locked against any access. This is similar to +`SRT_REJX_CONFLICT`, but in this case the resource is locked for reading +and writing. This is for when the resource should be shown as existing and +available to the client, but access is temporarily blocked. + +#### SRT_REJX_FAILED_DEPEND + +The dependent entity for the request is not present. In this case the +dependent entity is the session, which should be specified in the `s` +key. This means that the specified session ID is nonexistent, or it +has already expired. + +#### SRT_REJX_ISE + +Internal server error. This is for a general case when a request has +been correctly verified, with no related problems found, but an +unexpected error occurs after the processing of the request has started. + +#### SRT_REJX_UNIMPLEMENTED + +The request was correctly recognized, but the current software version +of the service (be it SRT or any other software component) doesn't +support it. This should be reported for a case where some features to +be specified in the StreamID request are supposed to be supported in a +predictable future, but the current version of the server does not +support it, or the support for this feature in this version has been +temporarily blocked. This shouldn't be reported for existing features that are +being deprecated, or older features that are no longer supported +(for this case the general `SRT_REJX_BAD_REQUEST` is more appropriate). + +#### SRT_REJX_GW + +The server acts as a gateway and the target endpoint rejected the +connection. The reason the connection was rejected is unspecified. +The gateway cannot forward the original rejection code from the +target endpoint because this would suggest the error was on the +gateway itself. Use this error with some other mechanism to report +the original target error, if possible. + +#### SRT_REJX_DOWN + +The service is down for maintenance. This can only be reported +when the service has been temporarily replaced by a stub that is only +reporting this error, while the real service is down for maintenance. + +#### SRT_REJX_VERSION + +Application version not supported. This can refer to an application feature +that is unsupported (possibly from an older SRT version), or to a feature +that is no longer supported because of backward compatibility requirements. + +#### SRT_REJX_NOROOM + +The data stream cannot be archived due to a lack of storage space. This is +reported when a request to send a file or a live stream to be archived is +unsuccessful. Note that the length of a file transmission is usually +pre-declared, so this error can be reported early. It can also be reported when +the stream is of undefined length, and there is no more storage space +available. + + +[:arrow_up:   Back to top](#srt-rejection-codes) diff --git a/docs/README.md b/docs/README.md index 9e3bdb198..8c07ee515 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,6 +7,7 @@ | [SRT API](API/API.md) | [API](API/) | [API.md](API/API.md) | Detailed description of the SRT C API. | | [SRT API Functions](API/API-functions.md) | [API](API/) | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions. | | [SRT API Socket Options](API/API-socket-options.md) | [API](API/) | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API. | +| [SRT Rejection Codes](API/rejections-codes.md) | [API](API/) | [rejection-codes.md](API/rejection-codes.md) | The list of SRT rejections codes. | | [SRT Statistics](API/statistics.md) | [API](API/) | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics. | | [Configuration Guidelines](API/configuration-guidelines.md) | [API](API/) | [configuration-guidelines.md](API/configuration-guidelines.md) | How to configure SRT buffers. | | | | | | diff --git a/docs/features/access-control.md b/docs/features/access-control.md index 99fe891fa..6375802b2 100644 --- a/docs/features/access-control.md +++ b/docs/features/access-control.md @@ -154,198 +154,7 @@ standard codes. Therefore the following sub-ranges are used: Such a code can be set by using the `srt_setrejectreason` function. -The SRT-specific codes are: - -#### SRT_REJX_FALLBACK - -This code should be set by the callback handler in the beginning in case -the application needs to be informed that the callback handler -actually has interpreted the incoming connection, but hasn't set a -more appropriate code describing the situation. - -#### SRT_REJX_KEY_NOTSUP - -Indicates there was a key specified in the StreamID string that this application -doesn't support. Note that it's not obligatory for the application to -react this way - it may chose to ignore unknown keys completely, or -to have some keys in the ignore list (which it won't interpret, but tolerate) -while rejecting any others. It is also up to the application -to decide to return this specific error, or more generally report -the syntax error with `SRT_REJX_BAD_REQUEST`. - -#### SRT_REJX_FILEPATH - -The resource type designates a file, and the path either has the wrong syntax -or is not found. In the case where `t=file`, the path should be specified under -the `r` key, and the file specified there must be able to be saved this way. -It's up to the application to decide how to treat this path, how to parse it, -and what this path specifically means. For the `r` key, the application should -at least handle the single filename, and have storage space available to save -it (provided a file of the same name does not already exist there). The -application should decide whether and how to handle all other situations (like -directory path, special markers in the path to be interpreted by the -application, etc.), or to report this error. - -#### SRT_REJX_HOSTNOTFOUND - -The host specified in the `h` key cannot be identified. The `h` key is -generally for a situation when you have multiple DNS names for a host, -so an application may want to extract the name from the URI and set it -to `h` key so that the application can distinguish the request also by -the target host name. The application may however limit the number of -recognized services by host name to some predefined names and not -handle the others, even if this is properly resolved by DNS. In this -case it should report this error. - -The other error codes are HTTP codes adopted for SRT: - -#### SRT_REJX_BAD_REQUEST - -General syntax error. This can be reported in any case when parsing -the StreamID contents failed, or it cannot be properly interpreted. - -#### SRT_REJX_UNAUTHORIZED - -Authentication failed, which makes the client unauthorized to access the -resource. This error, however, confirms that the syntax is correct and -the resource has been properly identified. Note that this cannot be -reported when you use a simple user-password authentication -method because in this case the password is verified only after the -listener callback handler accepts the connection. This error is rather -intended to be reported in case of `t=auth` when the authentication -process has generated some valid session ID, but then the session -connection has specified a resource that is not within the frames -of that authentication. - -#### SRT_REJX_OVERLOAD - -The server is too heavily loaded to process your request, or you -have exceeded credits for accessing the service and the resource. -In HTTP the description mentions payment for a service, but -it is also used by some services for general "credit" management -for a client. In SRT it should be used when your service is doing -any kind of credit management to limit access to selected clients -that "have" enough credit, even if the credit is something the client -can recharge itself, or that can be granted depending on available -service resources. - -#### SRT_REJX_FORBIDDEN - -Access denied to the resource for any reason. This error is -independent of an authorization or authentication error (as reported -by `SRT_REJX_UNAUTHORIZED`). The application can decide which -is more appropriate. This error is usually intended for -a resource that should only be accessed after a successful -authorization over a separate auth-only connection, where the query -in StreamID has correctly specified the resource identity and mode, -but the session ID (in the `s` key) is either (a) not specified, or (b) does -specify a valid session, but the authorization region for this -session does not embrace the specified resource. - -#### SRT_REJX_NOTFOUND - -The resource specified in the `r` key (in combination with the `h` key) -is not found at this time. This error should be only reported if the -information about resource accessibility is allowed to be publicly -visible. Otherwise the application might report authorization -errors. - -#### SRT_REJX_BAD_MODE - -The mode specified in the `m` key in StreamID is not supported for this request. -This may apply to read-only or write-only resources, as well for when interactive -(bidirectional) access is not valid for a resource. - -#### SRT_REJX_UNACCEPTABLE - -Applies when the parameters specified in StreamID cannot be satisfied for the -requested resource, or when `m=publish` but the data format is not acceptable. -This is a general error reporting an unsupported format for data that appears to -be wrong when sending, or a restriction on the data format (as specified in the -details of the resource specification) such that it cannot be provided -when receiving. - -#### SRT_REJX_CONFLICT - -The resource being accessed (as specified by `r` and `h` keys) is locked for -modification. This error should only be reported for `m=publish` when the -resource being accessed is read-only because another client (not necessarily -connected through SRT): - -- is currently publishing into this resource -- has reserved this resource ID for publishing - -Note that this error should be reported when there is no other reason for -having a problem accessing the resource. - -#### SRT_REJX_NOTSUP_MEDIA - -The media type is not supported by the application. The media type is -specified in the `t` key. The currently standard types are -`stream`, `file` and `auth`. An application may extend this list, and -is not obliged to support all of the standard types. - -#### SRT_REJX_LOCKED - -The resource being accessed is locked against any access. This is similar to -`SRT_REJX_CONFLICT`, but in this case the resource is locked for reading -and writing. This is for when the resource should be shown as existing and -available to the client, but access is temporarily blocked. - -#### SRT_REJX_FAILED_DEPEND - -The dependent entity for the request is not present. In this case the -dependent entity is the session, which should be specified in the `s` -key. This means that the specified session ID is nonexistent or it -has already expired. - -#### SRT_REJX_ISE - -Internal server error. This is for a general case when a request has -been correctly verified, with no related problems found, but an -unexpected error occurs after the processing of the request has started. - -#### SRT_REJX_UNIMPLEMENTED - -The request was correctly recognized, but the current software version -of the service (be it SRT or any other software component) doesn't -support it. This should be reported for a case, when some features to -be specified in the StreamID request are supposed to be supported in a -predictable future, but the current version of the server does not -support it, or the support for this feature in this version has been -temporarily blocked. This shouldn't be reported for existing features that are -being deprecated, or older features that are no longer supported -(for this case the general `SRT_REJX_BAD_REQUEST` is more appropriate). - -#### SRT_REJX_GW - -The server acts as a gateway and the target endpoint rejected the -connection. The reason the connection was rejected is unspecified. -The gateway cannot forward the original rejection code from the -target endpoint because this would suggest the error was on the -gateway itself. Use this error with some other mechanism to report -the original target error, if possible. - -#### SRT_REJX_DOWN - -The service is down for maintenance. This can only be reported -when the service has been temporarily replaced by a stub that is only -reporting this error, while the real service is down for maintenance. - -#### SRT_REJX_VERSION - -Application version not supported. This can refer to an application feature -that is unsupported (possibly from an older SRT version), or to a feature -that is no longer supported because of backward compatibility requirements. - -#### SRT_REJX_NOROOM - -The data stream cannot be archived due to a lack of storage space. This is -reported when a request to send a file or a live stream to be archived is -unsuccessful. Note that the length of a file transmission is usually -pre-declared, so this error can be reported early. It can also be reported when -the stream is of undefined length, and there is no more storage space -available. +See the list of rejection codes in the [Rejection Codes](../API/rejection-codes.md) document. ## Example diff --git a/examples/recvlive.cpp b/examples/recvlive.cpp index 81c2b1ef7..e14420a66 100644 --- a/examples/recvlive.cpp +++ b/examples/recvlive.cpp @@ -147,7 +147,7 @@ int main(int argc, char* argv[]) clientservice, sizeof(clientservice), NI_NUMERICHOST|NI_NUMERICSERV); cout << "new connection: " << clienthost << ":" << clientservice << endl; - int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; + events = SRT_EPOLL_IN | SRT_EPOLL_ERR; if (SRT_ERROR == srt_epoll_add_usock(epid, fhandle, &events)) { cout << "srt_epoll_add_usock: " << srt_getlasterror_str() << endl; diff --git a/examples/sendmsg.cpp b/examples/sendmsg.cpp index 53a0f66a5..af551849b 100644 --- a/examples/sendmsg.cpp +++ b/examples/sendmsg.cpp @@ -150,25 +150,25 @@ int main(int argc, char* argv[]) } else { - int lpos = 0; + int lpos2 = 0; int nparsed = 0; if (line[0] == '+') { - nparsed = sscanf(line.c_str(), "+%d %d %n%*s", &ttl, &id, &lpos); + nparsed = sscanf(line.c_str(), "+%d %d %n%*s", &ttl, &id, &lpos2); if (nparsed != 2) { - cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos2 << ")\n"; status = SRT_ERROR; break; } } else { - nparsed = sscanf(line.c_str(), "%d %n%*s", &id, &lpos); + nparsed = sscanf(line.c_str(), "%d %n%*s", &id, &lpos2); if (nparsed != 1) { - cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos2 << ")\n"; status = SRT_ERROR; break; } diff --git a/haicrypt/cryspr.c b/haicrypt/cryspr.c index 2e3a68a59..a078aad73 100644 --- a/haicrypt/cryspr.c +++ b/haicrypt/cryspr.c @@ -17,6 +17,10 @@ written by CRYSPR/4SRT Initial implementation. *****************************************************************************/ +#ifndef _WIN32 +#include /* htonl */ +#endif + #include "hcrypt.h" #include "cryspr.h" @@ -429,6 +433,8 @@ static int crysprFallback_MsEncrypt( /* Auth tag produced by AES GCM. */ unsigned char tag[HAICRYPT_AUTHTAG_MAX]; + /* Additional authenticated data used by AES-GCM. */ + unsigned char aad[HAICRYPT_AAD_MAX]; /* * Get buffer room from the internal circular output buffer. @@ -452,33 +458,30 @@ static int crysprFallback_MsEncrypt( /* 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); - if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) { - const int iret = cryspr_cb->cryspr->aes_gcm_cipher(true, aes_key, iv, in_data[0].pfx, pfx_len, in_data[0].payload, in_data[0].len, + const bool old_aead = ctx->use_gcm_153; // SRT v1.5.2 to v1.5.3. + if (old_aead) + { + hcrypt_SetCtrIV((unsigned char*)&pki, ctx->salt, iv); + memcpy(aad, in_data[0].pfx, sizeof(aad)); + } + else + { + hcrypt_SetGcmIV((unsigned char*)&pki, ctx->salt, iv); + + for (size_t i = 0; i < sizeof(aad) / 4; ++i) + *((uint32_t*)aad + i) = htonl(*((uint32_t*)in_data[0].pfx + i)); + } + + const int iret = cryspr_cb->cryspr->aes_gcm_cipher(true, aes_key, iv, aad, sizeof(aad), in_data[0].payload, in_data[0].len, &out_msg[pfx_len], tag); if (iret) { return(iret); } } else { + hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv); #if CRYSPR_HAS_AESCTR cryspr_cb->cryspr->aes_ctr_cipher(true, aes_key, iv, in_data[0].payload, in_data[0].len, &out_msg[pfx_len]); @@ -599,28 +602,26 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, /* 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); - if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) { + /* Additional authenticated data used by AES-GCM. */ + unsigned char aad[HAICRYPT_AAD_MAX]; + const bool old_aead = ctx->use_gcm_153; // SRT v1.5.2 to v1.5.3. + if (old_aead) + { + hcrypt_SetCtrIV((unsigned char*)&pki, ctx->salt, iv); + memcpy(aad, in_data[0].pfx, sizeof(aad)); + } + else + { + hcrypt_SetGcmIV((unsigned char*)&pki, ctx->salt, iv); + + for (size_t i = 0; i < sizeof(aad) / 4; ++i) + *((uint32_t*)aad + i) = htonl(*((uint32_t*)in_data[0].pfx + i)); + } + unsigned char* tag = in_data[0].payload + in_data[0].len - HAICRYPT_AUTHTAG_MAX; - int liret = cryspr_cb->cryspr->aes_gcm_cipher(false, aes_key, iv, in_data[0].pfx, ctx->msg_info->pfx_len, in_data[0].payload, in_data[0].len - HAICRYPT_AUTHTAG_MAX, + int liret = cryspr_cb->cryspr->aes_gcm_cipher(false, aes_key, iv, aad, sizeof(aad), in_data[0].payload, in_data[0].len - HAICRYPT_AUTHTAG_MAX, out_txt, tag); if (liret) { return(liret); @@ -628,6 +629,7 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, out_len = in_data[0].len - HAICRYPT_AUTHTAG_MAX; } else { + hcrypt_SetCtrIV((unsigned char*)&pki, ctx->salt, iv); #if CRYSPR_HAS_AESCTR cryspr_cb->cryspr->aes_ctr_cipher(false, aes_key, iv, in_data[0].payload, in_data[0].len, out_txt); diff --git a/haicrypt/haicrypt.h b/haicrypt/haicrypt.h index b6e83ad7a..da0ad3493 100644 --- a/haicrypt/haicrypt.h +++ b/haicrypt/haicrypt.h @@ -37,6 +37,7 @@ HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance (void); /* Return a default crys #define HAICRYPT_KEY_MAX_SZ 32 /* MAX key */ #define HAICRYPT_SECRET_MAX_SZ (HAICRYPT_PWD_MAX_SZ > HAICRYPT_KEY_MAX_SZ ? HAICRYPT_PWD_MAX_SZ : HAICRYPT_KEY_MAX_SZ) #define HAICRYPT_AUTHTAG_MAX 16 /* maximum length of the auth tag (e.g. GCM) */ +#define HAICRYPT_AAD_MAX 16 /* maximum length of the additional authenticated data (GCM mode) */ #define HAICRYPT_SALT_SZ 16 @@ -96,6 +97,7 @@ typedef struct hcrypt_Session_str* HaiCrypt_Handle; int HaiCrypt_SetLogLevel(int level, int logfa); int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc); +int HaiCrypt_UpdateGcm153(HaiCrypt_Handle hhc, unsigned use_gcm_153); int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handle *phhc); int HaiCrypt_Close(HaiCrypt_Handle hhc); int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_p); diff --git a/haicrypt/hcrypt.c b/haicrypt/hcrypt.c index dc3f06801..a2b81d832 100644 --- a/haicrypt/hcrypt.c +++ b/haicrypt/hcrypt.c @@ -178,6 +178,18 @@ int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc) return(0); } +int HaiCrypt_UpdateGcm153(HaiCrypt_Handle hhc, unsigned use_gcm_153) +{ + ASSERT(hhc != NULL); + hcrypt_Session* crypto = hhc; + if (!crypto) + return (-1); + + crypto->ctx_pair[0].use_gcm_153 = use_gcm_153; + crypto->ctx_pair[1].use_gcm_153 = use_gcm_153; + return (0); +} + int HaiCrypt_ExtractConfig(HaiCrypt_Handle hhcSrc, HaiCrypt_Cfg* pcfg) { hcrypt_Session *crypto = (hcrypt_Session *)hhcSrc; diff --git a/haicrypt/hcrypt.h b/haicrypt/hcrypt.h index 34e744e8a..e28a29777 100644 --- a/haicrypt/hcrypt.h +++ b/haicrypt/hcrypt.h @@ -135,6 +135,25 @@ typedef struct hcrypt_Session_str { hcrypt_XorStream(&(iv)[0], (nonce), 112/8); \ } while(0) +/* HaiCrypt-TP GCM mode IV (96-bit) - SRT 1.5.4: + * 0 1 2 3 4 5 6 7 8 9 10 11 + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * | 0s | pki | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * XOR + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * | nonce + + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * + * pki (32-bit): packet index + * nonce (96-bit): number used once (salt) + */ +#define hcrypt_SetGcmIV(pki, nonce, iv) do { \ + memset(&(iv)[0], 0, 96/8); \ + memcpy(&(iv)[8], (pki), HCRYPT_PKI_SZ); \ + hcrypt_XorStream(&(iv)[0], (nonce), 96/8); \ + } while(0) + #define hcrypt_XorStream(dst, strm, len) do { \ int __XORSTREAMi; \ for (__XORSTREAMi = 0 \ diff --git a/haicrypt/hcrypt_ctx.h b/haicrypt/hcrypt_ctx.h index 3a46fd40f..0d962c430 100644 --- a/haicrypt/hcrypt_ctx.h +++ b/haicrypt/hcrypt_ctx.h @@ -70,6 +70,7 @@ typedef struct tag_hcrypt_Ctx { #define HCRYPT_CTX_MODE_AESCBC 3 /* Cipher-block chaining mode */ #define HCRYPT_CTX_MODE_AESGCM 4 /* AES GCM authenticated encryption */ unsigned mode; + bool use_gcm_153; /* AES-GCM compatibility mode (SRT v1.5.3 and earlier) */ struct { size_t key_len; diff --git a/haicrypt/hcrypt_ctx_rx.c b/haicrypt/hcrypt_ctx_rx.c index 9fcba6c30..2b67490d3 100644 --- a/haicrypt/hcrypt_ctx_rx.c +++ b/haicrypt/hcrypt_ctx_rx.c @@ -28,8 +28,8 @@ int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cf ctx->mode = (cfg->flags & HAICRYPT_CFG_F_GCM) ? HCRYPT_CTX_MODE_AESGCM : HCRYPT_CTX_MODE_AESCTR; } ctx->status = HCRYPT_CTX_S_INIT; - ctx->msg_info = crypto->msg_info; + ctx->use_gcm_153 = false; // Default initialization. if (cfg && hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) { return(-1); diff --git a/haicrypt/hcrypt_ctx_tx.c b/haicrypt/hcrypt_ctx_tx.c index 9ed5ba36c..e66c9497b 100644 --- a/haicrypt/hcrypt_ctx_tx.c +++ b/haicrypt/hcrypt_ctx_tx.c @@ -35,7 +35,7 @@ int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cf ctx->mode = (cfg->flags & HAICRYPT_CFG_F_GCM) ? HCRYPT_CTX_MODE_AESGCM : HCRYPT_CTX_MODE_AESCTR; ctx->status = HCRYPT_CTX_S_INIT; - + ctx->use_gcm_153 = false; // Default initialization. ctx->msg_info = crypto->msg_info; if (hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) { @@ -184,7 +184,6 @@ int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto) 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; diff --git a/scripts/build-windows.ps1 b/scripts/build-windows.ps1 index 4db3fe627..66586158e 100644 --- a/scripts/build-windows.ps1 +++ b/scripts/build-windows.ps1 @@ -1,238 +1,235 @@ -################################################################################ -# Windows SRT Build Script -#============================ -# Usable on a Windows PC with Powershell and Visual studio, -# or called by CI systems like AppVeyor -# -# By default produces a VS2019 64-bit Release binary using C++11 threads, without -# encryption or unit tests enabled, but including test apps. -# Before enabling any encryption options, install OpenSSL or set VCKPG flag to build -################################################################################ - -param ( - [Parameter()][String]$VS_VERSION = "2019", - [Parameter()][String]$CONFIGURATION = "Release", - [Parameter()][String]$DEVENV_PLATFORM = "x64", - [Parameter()][String]$ENABLE_ENCRYPTION = "OFF", - [Parameter()][String]$STATIC_LINK_SSL = "OFF", - [Parameter()][String]$CXX11 = "ON", - [Parameter()][String]$BUILD_APPS = "ON", - [Parameter()][String]$UNIT_TESTS = "OFF", - [Parameter()][String]$BUILD_DIR = "_build", - [Parameter()][String]$VCPKG_OPENSSL = "OFF", - [Parameter()][String]$BONDING = "OFF" -) - -# cmake can be optionally installed (useful when running interactively on a developer station). -# The URL for automatic download is defined later in the script, but it should be possible to just vary the -# specific version set below and the URL should be stable enough to still work - you have been warned. -$cmakeVersion = "3.23.2" - -# make all errors trigger a script stop, rather than just carry on -$ErrorActionPreference = "Stop" - -$projectRoot = Join-Path $PSScriptRoot "/.." -Resolve - -# if running within AppVeyor, use environment variables to set params instead of passed-in values -if ( $Env:APPVEYOR ) { - if ( $Env:PLATFORM -eq 'x86' ) { $DEVENV_PLATFORM = 'Win32' } else { $DEVENV_PLATFORM = 'x64' } - if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2019' ) { $VS_VERSION='2019' } - if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2015' ) { $VS_VERSION='2015' } - if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2013' ) { $VS_VERSION='2013' } - - #if not statically linking OpenSSL, set flag to gather the specific openssl package from the build server into package - if ( $STATIC_LINK_SSL -eq 'OFF' ) { $Env:GATHER_SSL_INTO_PACKAGE = $true } - - #if unit tests are on, set flag to actually execute ctest step - if ( $UNIT_TESTS -eq 'ON' ) { $Env:RUN_UNIT_TESTS = $true } - - $CONFIGURATION = $Env:CONFIGURATION - - #appveyor has many openssl installations - place the latest one in the default location unless VS2013 - if( $VS_VERSION -ne '2013' ) { - Remove-Item -Path "C:\OpenSSL-Win32" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null - Remove-Item -Path "C:\OpenSSL-Win64" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null - Copy-Item -Path "C:\OpenSSL-v111-Win32" "C:\OpenSSL-Win32" -Recurse | Out-Null - Copy-Item -Path "C:\OpenSSL-v111-Win64" "C:\OpenSSL-Win64" -Recurse | Out-Null - } -} - -# persist VS_VERSION so it can be used in an artifact name later -$Env:VS_VERSION = $VS_VERSION - -# select the appropriate cmake generator string given the environment -if ( $VS_VERSION -eq '2019' ) { $CMAKE_GENERATOR = 'Visual Studio 16 2019'; $MSBUILDVER = "16.0"; } -if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015'; $MSBUILDVER = "14.0"; } -if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015 Win64'; $MSBUILDVER = "14.0"; } -if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013'; $MSBUILDVER = "12.0"; } -if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013 Win64'; $MSBUILDVER = "12.0"; } - -# clear any previous build and create & enter the build directory -$buildDir = Join-Path "$projectRoot" "$BUILD_DIR" -Write-Output "Creating (or cleaning if already existing) the folder $buildDir for project files and outputs" -Remove-Item -Path $buildDir -Recurse -Force -ErrorAction SilentlyContinue | Out-Null -New-Item -ItemType Directory -Path $buildDir -ErrorAction SilentlyContinue | Out-Null -Push-Location $buildDir - -# check cmake is installed -if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) { - $installCmake = Read-Host "Unable to find cmake in your PATH - would you like to download and install automatically? [yes/no]" - - if ( $installCmake -eq "y" -or $installCmake -eq "yes" ) { - # download cmake and run MSI for user - $client = New-Object System.Net.WebClient - $tempDownloadFile = New-TemporaryFile - - $cmakeUrl = "https://github.com/Kitware/CMake/releases/download/v$cmakeVersion/cmake-$cmakeVersion-win64-x64.msi" - $cmakeMsiFile = "$tempDownloadFile.cmake-$cmakeVersion-win64-x64.msi" - Write-Output "Downloading cmake from $cmakeUrl (temporary file location $cmakeMsiFile)" - Write-Output "Note: select the option to add cmake to path for this script to operate" - $client.DownloadFile("$cmakeUrl", "$cmakeMsiFile") - Start-Process $cmakeMsiFile -Wait - Remove-Item $cmakeMsiFile - Write-Output "Cmake should have installed, this script will now exit because of path updates - please now re-run this script" - throw - } - else{ - Write-Output "Quitting because cmake is required" - throw - } -} - -# get pthreads from nuget if CXX11 is not enabled -if ( $CXX11 -eq "OFF" ) { - # get pthreads (this is legacy, and is only available in nuget for VS2015 and VS2013) - if ( $VS_VERSION -gt 2015 ) { - Write-Output "Pthreads is not recommended for use beyond VS2015 and is not supported by this build script - aborting build" - throw - } - if ( $DEVENV_PLATFORM -eq 'Win32' ) { - nuget install cinegy.pthreads-win32-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages - } - else { - nuget install cinegy.pthreads-win64-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages - } -} - -# check to see if static SSL linking was requested, and enable encryption if not already ON -if ( $STATIC_LINK_SSL -eq "ON" ) { - if ( $ENABLE_ENCRYPTION -eq "OFF" ) { - # requesting a static link implicitly requires encryption support - Write-Output "Static linking to OpenSSL requested, will force encryption feature ON" - $ENABLE_ENCRYPTION = "ON" - } -} - -# check to see if VCPKG is marked to provide OpenSSL, and enable encryption if not already ON -if ( $VCPKG_OPENSSL -eq "ON" ) { - if ( $ENABLE_ENCRYPTION -eq "OFF" ) { - # requesting VCPKG to provide OpenSSL requires encryption support - Write-Output "VCPKG compilation of OpenSSL requested, will force encryption feature ON" - $ENABLE_ENCRYPTION = "ON" - } -} - -# build the cmake command flags from arguments -$cmakeFlags = "-DCMAKE_BUILD_TYPE=$CONFIGURATION " + - "-DENABLE_STDCXX_SYNC=$CXX11 " + - "-DENABLE_APPS=$BUILD_APPS " + - "-DENABLE_ENCRYPTION=$ENABLE_ENCRYPTION " + - "-DENABLE_BONDING=$BONDING " + - "-DENABLE_UNITTESTS=$UNIT_TESTS" - -# if VCPKG is flagged to provide OpenSSL, checkout VCPKG and install package -if ( $VCPKG_OPENSSL -eq 'ON' ) { - Push-Location $projectRoot - Write-Output "Cloning VCPKG into: $(Get-Location)" - if (Test-Path -Path ".\vcpkg") { - Set-Location .\vcpkg - git pull - } else { - git clone https://github.com/microsoft/vcpkg - Set-Location .\vcpkg - } - - .\bootstrap-vcpkg.bat - - if($DEVENV_PLATFORM -EQ "x64"){ - if($STATIC_LINK_SSL -EQ "ON"){ - .\vcpkg install openssl:x64-windows-static - $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x64-windows-static" - } - else{ - .\vcpkg install openssl:x64-windows - } - } - else{ - if($STATIC_LINK_SSL -EQ "ON"){ - .\vcpkg install openssl:x86-windows-static - $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x86-windows-static" - } - else{ - .\vcpkg install openssl:x86-windows - } - } - - .\vcpkg integrate install - Pop-Location - $cmakeFlags += " -DCMAKE_TOOLCHAIN_FILE=$projectRoot\vcpkg\scripts\buildsystems\vcpkg.cmake" -} -else { - $cmakeFlags += " -DOPENSSL_USE_STATIC_LIBS=$STATIC_LINK_SSL " -} - -# cmake uses a flag for architecture from vs2019, so add that as a suffix -if ( $VS_VERSION -eq '2019' ) { - $cmakeFlags += " -A `"$DEVENV_PLATFORM`"" -} - -# fire cmake to build project files -$execVar = "cmake ../ -G`"$CMAKE_GENERATOR`" $cmakeFlags" -Write-Output $execVar - -# Reset reaction to Continue for cmake as it sometimes tends to print -# things on stderr, which is understood by PowerShell as error. The -# exit code from cmake will be checked anyway. -$ErrorActionPreference = "Continue" -Invoke-Expression "& $execVar" - -# check build ran OK, exit if cmake failed -if( $LASTEXITCODE -ne 0 ) { - Write-Output "Non-zero exit code from cmake: $LASTEXITCODE" - throw -} - -$ErrorActionPreference = "Stop" - -# run the set-version-metadata script to inject build numbers into appveyors console and the resulting DLL -. $PSScriptRoot/set-version-metadata.ps1 - -# look for msbuild -$msBuildPath = Get-Command "msbuild.exe" -ErrorAction SilentlyContinue -if ( $null -eq $msBuildPath ) { - # no mbsuild in the path, so try to locate with 'vswhere' - $vsWherePath = Get-Command "vswhere.exe" -ErrorAction SilentlyContinue - if ( $null -eq $vsWherePath ) { - # no vswhere in the path, so check the Microsoft published location (true since VS2017 Update 2) - $vsWherePath = Get-Command "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -ErrorAction SilentlyContinue - if ( $null -eq $vsWherePath ) { - Write-Output "Cannot find vswhere (used to locate msbuild). Please install VS2017 update 2 (or later) or add vswhere to your path and try again" - throw - } - } - $msBuildPath = & $vsWherePath -products * -version $MSBUILDVER -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1 - if ( $null -eq $msBuildPath ) { - Write-Output "vswhere.exe cannot find msbuild for the specified Visual Studio version - please check the installation" - throw - } -} - -& $msBuildPath SRT.sln -m /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM - -# return to the directory previously occupied before running the script -Pop-Location - -# if msbuild returned non-zero, throw to cause failure in CI -if( $LASTEXITCODE -ne 0 ) { - throw -} +################################################################################ +# Windows SRT Build Script +#============================ +# Usable on a Windows PC with Powershell and Visual studio, +# or called by CI systems like AppVeyor +# +# By default produces a VS2019 64-bit Release binary using C++11 threads, without +# encryption or unit tests enabled, but including test apps. +# Before enabling any encryption options, install OpenSSL or set VCKPG flag to build +################################################################################ + +param ( + [Parameter()][String]$VS_VERSION = "2022", + [Parameter()][String]$CONFIGURATION = "Release", + [Parameter()][String]$DEVENV_PLATFORM = "x64", + [Parameter()][String]$ENABLE_ENCRYPTION = "OFF", + [Parameter()][String]$STATIC_LINK_SSL = "OFF", + [Parameter()][String]$CXX11 = "ON", + [Parameter()][String]$BUILD_APPS = "ON", + [Parameter()][String]$UNIT_TESTS = "OFF", + [Parameter()][String]$BUILD_DIR = "_build", + [Parameter()][String]$VCPKG_OPENSSL = "OFF", + [Parameter()][String]$BONDING = "OFF" +) + +# cmake can be optionally installed (useful when running interactively on a developer station). +# The URL for automatic download is defined later in the script, but it should be possible to just vary the +# specific version set below and the URL should be stable enough to still work - you have been warned. +$cmakeVersion = "3.23.2" + +# make all errors trigger a script stop, rather than just carry on +$ErrorActionPreference = "Stop" + +$projectRoot = Join-Path $PSScriptRoot "/.." -Resolve + +# if running within AppVeyor, use environment variables to set params instead of passed-in values +if ( $Env:APPVEYOR ) { + if ( $Env:PLATFORM -eq 'x86' ) { $DEVENV_PLATFORM = 'Win32' } else { $DEVENV_PLATFORM = 'x64' } + if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2022' ) { $VS_VERSION='2022' } + if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2019' ) { $VS_VERSION='2019' } + if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2015' ) { $VS_VERSION='2015' } + + #if not statically linking OpenSSL, set flag to gather the specific openssl package from the build server into package + if ( $STATIC_LINK_SSL -eq 'OFF' ) { $Env:GATHER_SSL_INTO_PACKAGE = $true } + + #if unit tests are on, set flag to actually execute ctest step + if ( $UNIT_TESTS -eq 'ON' ) { $Env:RUN_UNIT_TESTS = $true } + + $CONFIGURATION = $Env:CONFIGURATION + + #appveyor has many openssl installations - place the latest one in the default location unless VS2013 + Remove-Item -Path "C:\OpenSSL-Win32" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + Remove-Item -Path "C:\OpenSSL-Win64" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + Copy-Item -Path "C:\OpenSSL-v111-Win32" "C:\OpenSSL-Win32" -Recurse | Out-Null + Copy-Item -Path "C:\OpenSSL-v111-Win64" "C:\OpenSSL-Win64" -Recurse | Out-Null +} + +# persist VS_VERSION so it can be used in an artifact name later +$Env:VS_VERSION = $VS_VERSION + +# select the appropriate cmake generator string given the environment +if ( $VS_VERSION -eq '2022' ) { $CMAKE_GENERATOR = 'Visual Studio 17 2022'; $MSBUILDVER = "17.10"; } +if ( $VS_VERSION -eq '2019' ) { $CMAKE_GENERATOR = 'Visual Studio 16 2019'; $MSBUILDVER = "16.0"; } +if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015'; $MSBUILDVER = "14.0"; } +if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015 Win64'; $MSBUILDVER = "14.0"; } + +# clear any previous build and create & enter the build directory +$buildDir = Join-Path "$projectRoot" "$BUILD_DIR" +Write-Output "Creating (or cleaning if already existing) the folder $buildDir for project files and outputs" +Remove-Item -Path $buildDir -Recurse -Force -ErrorAction SilentlyContinue | Out-Null +New-Item -ItemType Directory -Path $buildDir -ErrorAction SilentlyContinue | Out-Null +Push-Location $buildDir + +# check cmake is installed +if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) { + $installCmake = Read-Host "Unable to find cmake in your PATH - would you like to download and install automatically? [yes/no]" + + if ( $installCmake -eq "y" -or $installCmake -eq "yes" ) { + # download cmake and run MSI for user + $client = New-Object System.Net.WebClient + $tempDownloadFile = New-TemporaryFile + + $cmakeUrl = "https://github.com/Kitware/CMake/releases/download/v$cmakeVersion/cmake-$cmakeVersion-win64-x64.msi" + $cmakeMsiFile = "$tempDownloadFile.cmake-$cmakeVersion-win64-x64.msi" + Write-Output "Downloading cmake from $cmakeUrl (temporary file location $cmakeMsiFile)" + Write-Output "Note: select the option to add cmake to path for this script to operate" + $client.DownloadFile("$cmakeUrl", "$cmakeMsiFile") + Start-Process $cmakeMsiFile -Wait + Remove-Item $cmakeMsiFile + Write-Output "Cmake should have installed, this script will now exit because of path updates - please now re-run this script" + throw + } + else{ + Write-Output "Quitting because cmake is required" + throw + } +} + +# get pthreads from nuget if CXX11 is not enabled +if ( $CXX11 -eq "OFF" ) { + # get pthreads (this is legacy, and is only available in nuget for VS2015 and VS2013) + if ( $VS_VERSION -gt 2015 ) { + Write-Output "Pthreads is not recommended for use beyond VS2015 and is not supported by this build script - aborting build" + throw + } + if ( $DEVENV_PLATFORM -eq 'Win32' ) { + nuget install cinegy.pthreads-win32-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages + } + else { + nuget install cinegy.pthreads-win64-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages + } +} + +# check to see if static SSL linking was requested, and enable encryption if not already ON +if ( $STATIC_LINK_SSL -eq "ON" ) { + if ( $ENABLE_ENCRYPTION -eq "OFF" ) { + # requesting a static link implicitly requires encryption support + Write-Output "Static linking to OpenSSL requested, will force encryption feature ON" + $ENABLE_ENCRYPTION = "ON" + } +} + +# check to see if VCPKG is marked to provide OpenSSL, and enable encryption if not already ON +if ( $VCPKG_OPENSSL -eq "ON" ) { + if ( $ENABLE_ENCRYPTION -eq "OFF" ) { + # requesting VCPKG to provide OpenSSL requires encryption support + Write-Output "VCPKG compilation of OpenSSL requested, will force encryption feature ON" + $ENABLE_ENCRYPTION = "ON" + } +} + +# build the cmake command flags from arguments +$cmakeFlags = "-DCMAKE_BUILD_TYPE=$CONFIGURATION " + + "-DENABLE_STDCXX_SYNC=$CXX11 " + + "-DENABLE_APPS=$BUILD_APPS " + + "-DENABLE_ENCRYPTION=$ENABLE_ENCRYPTION " + + "-DENABLE_BONDING=$BONDING " + + "-DENABLE_UNITTESTS=$UNIT_TESTS" + +# if VCPKG is flagged to provide OpenSSL, checkout VCPKG and install package +if ( $VCPKG_OPENSSL -eq 'ON' ) { + Push-Location $projectRoot + Write-Output "Cloning VCPKG into: $(Get-Location)" + if (Test-Path -Path ".\vcpkg") { + Set-Location .\vcpkg + git pull + } else { + git clone https://github.com/microsoft/vcpkg + Set-Location .\vcpkg + } + + .\bootstrap-vcpkg.bat + + if($DEVENV_PLATFORM -EQ "x64"){ + if($STATIC_LINK_SSL -EQ "ON"){ + .\vcpkg install openssl:x64-windows-static + $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x64-windows-static" + } + else{ + .\vcpkg install openssl:x64-windows + } + } + else{ + if($STATIC_LINK_SSL -EQ "ON"){ + .\vcpkg install openssl:x86-windows-static + $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x86-windows-static" + } + else{ + .\vcpkg install openssl:x86-windows + } + } + + .\vcpkg integrate install + Pop-Location + $cmakeFlags += " -DCMAKE_TOOLCHAIN_FILE=$projectRoot\vcpkg\scripts\buildsystems\vcpkg.cmake" +} +else { + $cmakeFlags += " -DOPENSSL_USE_STATIC_LIBS=$STATIC_LINK_SSL " +} + +# cmake uses a flag for architecture from vs2019, so add that as a suffix +if ( $VS_VERSION -ge '2019' ) { + $cmakeFlags += " -A `"$DEVENV_PLATFORM`"" +} + +# fire cmake to build project files +$execVar = "cmake ../ -G`"$CMAKE_GENERATOR`" $cmakeFlags" +Write-Output $execVar + +# Reset reaction to Continue for cmake as it sometimes tends to print +# things on stderr, which is understood by PowerShell as error. The +# exit code from cmake will be checked anyway. +$ErrorActionPreference = "Continue" +Invoke-Expression "& $execVar" + +# check build ran OK, exit if cmake failed +if( $LASTEXITCODE -ne 0 ) { + Write-Output "Non-zero exit code from cmake: $LASTEXITCODE" + throw +} + +$ErrorActionPreference = "Stop" + +# run the set-version-metadata script to inject build numbers into appveyors console and the resulting DLL +. $PSScriptRoot/set-version-metadata.ps1 + +# look for msbuild +$msBuildPath = Get-Command "msbuild.exe" -ErrorAction SilentlyContinue +if ( $null -eq $msBuildPath ) { + # no mbsuild in the path, so try to locate with 'vswhere' + $vsWherePath = Get-Command "vswhere.exe" -ErrorAction SilentlyContinue + if ( $null -eq $vsWherePath ) { + # no vswhere in the path, so check the Microsoft published location (true since VS2017 Update 2) + $vsWherePath = Get-Command "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -ErrorAction SilentlyContinue + if ( $null -eq $vsWherePath ) { + Write-Output "Cannot find vswhere (used to locate msbuild). Please install VS2017 update 2 (or later) or add vswhere to your path and try again" + throw + } + } + $msBuildPath = & $vsWherePath -products * -version $MSBUILDVER -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1 + if ( $null -eq $msBuildPath ) { + Write-Output "vswhere.exe cannot find msbuild for the specified Visual Studio version - please check the installation" + throw + } +} + +& $msBuildPath SRT.sln -m /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM + +# return to the directory previously occupied before running the script +Pop-Location + +# if msbuild returned non-zero, throw to cause failure in CI +if( $LASTEXITCODE -ne 0 ) { + throw +} diff --git a/scripts/release-notes/README.md b/scripts/release-notes/README.md index d6c4adbfc..d34b07bfa 100644 --- a/scripts/release-notes/README.md +++ b/scripts/release-notes/README.md @@ -11,7 +11,7 @@ git log --pretty=format:"%h|%s|%an|%ae" v1.4.0...HEAD > commits.csv Use the produced `commits.csv` file as an input to the script: ```shell -python scripts/release-notes/generate-release-notes.py commits.csv +python scripts/release-notes/generate_release_notes.py commits.csv ``` The script produces `release-notes.md` as an output. diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 61bb5ad1f..561dc419c 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -238,6 +238,8 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) int srt::CUDTUnited::startup() { ScopedLock gcinit(m_InitLock); + if (m_bGCStatus) + return 1; if (m_iInstanceCount++ > 0) return 1; @@ -256,9 +258,6 @@ int srt::CUDTUnited::startup() PacketFilter::globalInit(); - if (m_bGCStatus) - return 1; - m_bClosing = false; if (!StartThread(m_GCThread, garbageCollect, this, "SRT:GC")) @@ -540,6 +539,8 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, try { + // Protect the config of the listener socket from a data race. + ScopedLock lck(ls->core().m_ConnectionLock); ns = new CUDTSocket(*ls); // No need to check the peer, this is the address from which the request has come. ns->m_PeerAddr = peer; @@ -1915,11 +1916,28 @@ int srt::CUDTUnited::close(const SRTSOCKET u) return 0; } #endif - CUDTSocket* s = locateSocket(u); - if (!s) - throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); +#if ENABLE_HEAVY_LOGGING + // Wrapping the log into a destructor so that it + // is printed AFTER the destructor of SocketKeeper. + struct ScopedExitLog + { + const CUDTSocket* const ps; + ScopedExitLog(const CUDTSocket* p): ps(p){} + ~ScopedExitLog() + { + if (ps) // Could be not acquired by SocketKeeper, occasionally + { + HLOGC(smlog.Debug, log << "CUDTUnited::close/end: @" << ps->m_SocketID << " busy=" << ps->isStillBusy()); + } + } + }; +#endif + + SocketKeeper k(*this, u, ERH_THROW); + IF_HEAVY_LOGGING(ScopedExitLog slog(k.socket)); + HLOGC(smlog.Debug, log << "CUDTUnited::close/begin: @" << u << " busy=" << k.socket->isStillBusy()); - return close(s); + return close(k.socket); } #if ENABLE_BONDING @@ -2324,7 +2342,7 @@ int srt::CUDTUnited::selectEx(const vector& fds, if (readfds) { - if ((s->core().m_bConnected && s->core().m_pRcvBuffer->isRcvDataReady()) || + if ((s->core().m_bConnected && s->core().isRcvBufferReady()) || (s->core().m_bListening && (s->m_QueuedSockets.size() > 0))) { readfds->push_back(s->m_SocketID); @@ -2548,6 +2566,45 @@ srt::CUDTGroup* srt::CUDTUnited::acquireSocketsGroup(CUDTSocket* s) } #endif +srt::CUDTSocket* srt::CUDTUnited::locateAcquireSocket(SRTSOCKET u, ErrorHandling erh) +{ + ScopedLock cg(m_GlobControlLock); + + CUDTSocket* s = locateSocket_LOCKED(u); + if (!s) + { + if (erh == ERH_THROW) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + return NULL; + } + + s->apiAcquire(); + return s; +} + +bool srt::CUDTUnited::acquireSocket(CUDTSocket* s) +{ + // Note that before using this function you must be certain + // that the socket isn't broken already and it still has at least + // one more GC cycle to live. In other words, you must be certain + // that this pointer passed here isn't dangling and was obtained + // directly from m_Sockets, or even better, has been acquired + // by some other functionality already, which is only about to + // be released earlier than you need. + ScopedLock cg(m_GlobControlLock); + s->apiAcquire(); + // Keep the lock so that no one changes anything in the meantime. + // If the socket m_Status == SRTS_CLOSED (set by setClosed()), then + // this socket is no longer present in the m_Sockets container + if (s->m_Status >= SRTS_BROKEN) + { + s->apiRelease(); + return false; + } + + return true; +} + srt::CUDTSocket* srt::CUDTUnited::locatePeer(const sockaddr_any& peer, const SRTSOCKET id, int32_t isn) { ScopedLock cg(m_GlobControlLock); @@ -2614,7 +2671,7 @@ void srt::CUDTUnited::checkBrokenSockets() if (s->m_Status == SRTS_LISTENING) { - const steady_clock::duration elapsed = steady_clock::now() - s->m_tsClosureTimeStamp; + const steady_clock::duration elapsed = steady_clock::now() - s->m_tsClosureTimeStamp.load(); // A listening socket should wait an extra 3 seconds // in case a client is connecting. if (elapsed < milliseconds_from(CUDT::COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS)) @@ -2673,6 +2730,20 @@ void srt::CUDTUnited::checkBrokenSockets() for (sockets_t::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++j) { CUDTSocket* ps = j->second; + + // NOTE: There is still a hypothetical risk here that ps + // was made busy while the socket was already moved to m_ClosedSocket, + // if the socket was acquired through CUDTUnited::acquireSocket (that is, + // busy flag acquisition was done through the CUDTSocket* pointer rather + // than through the numeric ID). Therefore this way of busy acquisition + // should be done only if at the moment of acquisition there are certainly + // other conditions applying on the socket that prevent it from being deleted. + if (ps->isStillBusy()) + { + HLOGC(smlog.Debug, log << "checkBrokenSockets: @" << ps->m_SocketID << " is still busy, SKIPPING THIS CYCLE."); + continue; + } + CUDT& u = ps->core(); // HLOGC(smlog.Debug, log << "checking CLOSED socket: " << j->first); @@ -2692,7 +2763,7 @@ void srt::CUDTUnited::checkBrokenSockets() // timeout 1 second to destroy a socket AND it has been removed from // RcvUList const steady_clock::time_point now = steady_clock::now(); - const steady_clock::duration closed_ago = now - ps->m_tsClosureTimeStamp; + const steady_clock::duration closed_ago = now - ps->m_tsClosureTimeStamp.load(); if (closed_ago > seconds_from(1)) { CRNode* rnode = u.m_pRNode; @@ -2742,6 +2813,14 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) if (rn && rn->m_bOnList) return; + if (s->isStillBusy()) + { + HLOGC(smlog.Debug, log << "@" << s->m_SocketID << " is still busy, NOT deleting"); + return; + } + + LOGC(smlog.Note, log << "@" << s->m_SocketID << " busy=" << s->isStillBusy()); + #if ENABLE_BONDING if (s->m_GroupOf) { @@ -3392,8 +3471,7 @@ int srt::CUDT::cleanup() SRTSOCKET srt::CUDT::socket() { - if (!uglobal().m_bGCStatus) - uglobal().startup(); + uglobal().startup(); try { @@ -3443,8 +3521,7 @@ srt::CUDTGroup& srt::CUDTUnited::newGroup(const int type) SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) { // Doing the same lazy-startup as with srt_create_socket() - if (!uglobal().m_bGCStatus) - uglobal().startup(); + uglobal().startup(); try { diff --git a/srtcore/api.h b/srtcore/api.h index fb62b7f57..582ba8aa7 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -123,6 +123,18 @@ class CUDTSocket void construct(); +private: + srt::sync::atomic m_iBusy; +public: + void apiAcquire() { ++m_iBusy; } + void apiRelease() { --m_iBusy; } + + int isStillBusy() const + { + return m_iBusy; + } + + // Controversial whether it should stand. This lock is mainly // for API things connected to this socket, while status is also // set as atomic to allow multi-thread access. @@ -134,7 +146,8 @@ class CUDTSocket /// of sockets in order to prevent other methods from accessing invalid address. /// A timer is started and the socket will be removed after approximately /// 1 second (see CUDTUnited::checkBrokenSockets()). - sync::steady_clock::time_point m_tsClosureTimeStamp; + //sync::steady_clock::time_point m_tsClosureTimeStamp; + sync::AtomicClock m_tsClosureTimeStamp; sockaddr_any m_SelfAddr; //< local address of the socket sockaddr_any m_PeerAddr; //< peer address of the socket @@ -334,6 +347,7 @@ class CUDTUnited int epoll_release(const int eid); #if ENABLE_BONDING + SRT_ATR_NODISCARD SRT_TSA_NEEDS_LOCKED(m_GlobControlLock) CUDTGroup& addGroup(SRTSOCKET id, SRT_GROUP_TYPE type) { @@ -359,6 +373,7 @@ class CUDTUnited SRT_TSA_NEEDS_LOCKED(m_GlobControlLock) void deleteGroup_LOCKED(CUDTGroup* g); + SRT_ATR_NODISCARD SRT_TSA_NEEDS_LOCKED(m_GlobControlLock) CUDTGroup* findPeerGroup_LOCKED(SRTSOCKET peergroup) { @@ -467,8 +482,52 @@ class CUDTUnited } } }; - #endif + + CUDTSocket* locateAcquireSocket(SRTSOCKET u, ErrorHandling erh = ERH_RETURN); + bool acquireSocket(CUDTSocket* s); + +public: + struct SocketKeeper + { + CUDTSocket* socket; + + SocketKeeper(): socket(NULL) {} + + // This is intended for API functions to lock the socket's existence + // for the lifetime of their call. + SocketKeeper(CUDTUnited& glob, SRTSOCKET id, ErrorHandling erh = ERH_RETURN) { socket = glob.locateAcquireSocket(id, erh); } + + // This is intended for TSBPD thread that should lock the socket's + // existence until it exits. + SocketKeeper(CUDTUnited& glob, CUDTSocket* s) + { + acquire(glob, s); + } + + // Note: acquire doesn't check if the keeper already keeps anything. + // This is only for a use together with an empty constructor. + bool acquire(CUDTUnited& glob, CUDTSocket* s) + { + if (s == NULL) + return false; + const bool caught = glob.acquireSocket(s); + socket = caught ? s : NULL; + return caught; + } + + ~SocketKeeper() + { + if (socket) + { + SRT_ASSERT(socket->isStillBusy() > 0); + socket->apiRelease(); + } + } + }; + +private: + void updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* = NULL); bool updateListenerMux(CUDTSocket* s, const CUDTSocket* ls); diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 2ec42487d..b7d289baa 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -218,9 +218,10 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) return std::make_pair(0, 0); } - m_iMaxPosOff -= len; - if (m_iMaxPosOff < 0) - m_iMaxPosOff = 0; + int newmax = m_iMaxPosOff - len; + if (newmax < 0) + newmax = 0; + m_iMaxPosOff = newmax; int iNumDropped = 0; // Number of dropped packets that were missing. int iNumDiscarded = 0; // The number of dropped packets that existed in the buffer. @@ -760,6 +761,12 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no return unreadableInfo; } +int32_t CRcvBuffer::getFirstNonreadSeqNo() const +{ + const int offset = offPos(m_iStartPos, m_iFirstNonreadPos); + return CSeqNo::incseq(m_iStartSeqNo, offset); +} + void CRcvBuffer::countBytes(int pkts, int bytes) { ScopedLock lock(m_BytesCountLock); @@ -770,7 +777,7 @@ void CRcvBuffer::countBytes(int pkts, int bytes) if (!m_uAvgPayloadSz) m_uAvgPayloadSz = bytes; else - m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); + m_uAvgPayloadSz = avg_iir<100, unsigned>(m_uAvgPayloadSz, bytes); } } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index f783ac2a2..eb3385ea6 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -187,6 +187,11 @@ class CRcvBuffer PacketInfo getFirstReadablePacketInfo(time_point time_now) const; + /// @brief Get the sequence number of the first packet that can't be read + /// (either because it is missing, or because it is a part of a bigger message + /// that is not fully available yet). + int32_t getFirstNonreadSeqNo() const; + /// Get information on packets available to be read. /// @returns a pair of sequence numbers (first available; first unavailable). /// @@ -353,10 +358,15 @@ class CRcvBuffer const size_t m_szSize; // size of the array of units (buffer) CUnitQueue* m_pUnitQueue; // the shared unit queue - int m_iStartSeqNo; + // ATOMIC because getStartSeqNo() may be called from other thread + // than CUDT's receiver worker thread. Even if it's not a problem + // if this value is a bit outdated, it must be read solid. + sync::atomic m_iStartSeqNo; int m_iStartPos; // the head position for I/O (inclusive) int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) - int m_iMaxPosOff; // the furthest data position + + // ATOMIC: sometimes this value is checked for buffer emptiness + sync::atomic m_iMaxPosOff; // the furthest data position int m_iNotch; // the starting read point of the first unit size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false @@ -403,7 +413,7 @@ class CRcvBuffer mutable sync::Mutex m_BytesCountLock; // used to protect counters operations int m_iBytesCount; // Number of payload bytes in the buffer int m_iPktsCount; // Number of payload bytes in the buffer - unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation + sync::atomic m_uAvgPayloadSz; // Average payload size for dropped bytes estimation }; } // namespace srt diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 3f7a77be6..7473e3824 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -234,7 +234,7 @@ void CSndRateEstimator::addSample(const time_point& ts, int pkts, size_t bytes) } else { - m_iRateBps = sum.m_iBytesCount * 1000 / (iNumPeriods * SAMPLE_DURATION_MS); + m_iRateBps = (sum.m_iBytesCount + CPacket::HDR_SIZE * sum.m_iPktsCount) * 1000 / (iNumPeriods * SAMPLE_DURATION_MS); } HLOGC(bslog.Note, @@ -260,7 +260,8 @@ void CSndRateEstimator::addSample(const time_point& ts, int pkts, size_t bytes) int CSndRateEstimator::getCurrentRate() const { SRT_ASSERT(m_iCurSampleIdx >= 0 && m_iCurSampleIdx < NUM_PERIODS); - return (int) avg_iir<16, unsigned long long>(m_iRateBps, m_Samples[m_iCurSampleIdx].m_iBytesCount * 1000 / SAMPLE_DURATION_MS); + const Sample& s = m_Samples[m_iCurSampleIdx]; + return (int) avg_iir<16, unsigned long long>(m_iRateBps, (CPacket::HDR_SIZE * s.m_iPktsCount + s.m_iBytesCount) * 1000 / SAMPLE_DURATION_MS); } int CSndRateEstimator::incSampleIdx(int val, int inc) const diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index aacbd8310..88479827d 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -141,10 +141,11 @@ class CSndRateEstimator /// @param [in] bytes number of payload bytes in the sample. void addSample(const time_point& time, int pkts = 0, size_t bytes = 0); - /// Retrieve estimated bitrate in bytes per second + /// Retrieve estimated bitrate in bytes per second with 16-byte packet header. int getRate() const { return m_iRateBps; } - /// Retrieve estimated bitrate in bytes per second inluding the current sampling interval. + /// Retrieve estimated bitrate in bytes per second (with 16-byte packet header) + /// including the current sampling interval. int getCurrentRate() const; private: @@ -191,10 +192,10 @@ class CSndRateEstimator Sample m_Samples[NUM_PERIODS]; - time_point m_tsFirstSampleTime; //< Start time of the first sameple. + time_point m_tsFirstSampleTime; //< Start time of the first sample. int m_iFirstSampleIdx; //< Index of the first sample. int m_iCurSampleIdx; //< Index of the current sample being collected. - int m_iRateBps; // Input Rate in Bytes/sec + int m_iRateBps; //< Rate in Bytes/sec. }; } // namespace srt diff --git a/srtcore/cache.cpp b/srtcore/cache.cpp index 4cda9a70f..2c6f624c4 100644 --- a/srtcore/cache.cpp +++ b/srtcore/cache.cpp @@ -38,7 +38,6 @@ written by Yunhong Gu, last updated 05/05/2009 *****************************************************************************/ - #include "platform_sys.h" #include @@ -49,70 +48,70 @@ using namespace std; srt::CInfoBlock& srt::CInfoBlock::copyFrom(const CInfoBlock& obj) { - std::copy(obj.m_piIP, obj.m_piIP + 4, m_piIP); - m_iIPversion = obj.m_iIPversion; - m_ullTimeStamp = obj.m_ullTimeStamp; - m_iSRTT = obj.m_iSRTT; - m_iBandwidth = obj.m_iBandwidth; - m_iLossRate = obj.m_iLossRate; - m_iReorderDistance = obj.m_iReorderDistance; - m_dInterval = obj.m_dInterval; - m_dCWnd = obj.m_dCWnd; - - return *this; + std::copy(obj.m_piIP, obj.m_piIP + 4, m_piIP); + m_iIPversion = obj.m_iIPversion; + m_ullTimeStamp = obj.m_ullTimeStamp; + m_iSRTT = obj.m_iSRTT; + m_iBandwidth = obj.m_iBandwidth; + m_iLossRate = obj.m_iLossRate; + m_iReorderDistance = obj.m_iReorderDistance; + m_dInterval = obj.m_dInterval; + m_dCWnd = obj.m_dCWnd; + + return *this; } bool srt::CInfoBlock::operator==(const CInfoBlock& obj) const { - if (m_iIPversion != obj.m_iIPversion) - return false; + if (m_iIPversion != obj.m_iIPversion) + return false; - else if (m_iIPversion == AF_INET) - return (m_piIP[0] == obj.m_piIP[0]); + 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; - } + for (int i = 0; i < 4; ++i) + { + if (m_piIP[i] != obj.m_piIP[i]) + return false; + } - return true; + return true; } srt::CInfoBlock* srt::CInfoBlock::clone() { - CInfoBlock* obj = new CInfoBlock; - - std::copy(m_piIP, m_piIP + 4, obj->m_piIP); - obj->m_iIPversion = m_iIPversion; - obj->m_ullTimeStamp = m_ullTimeStamp; - obj->m_iSRTT = m_iSRTT; - obj->m_iBandwidth = m_iBandwidth; - obj->m_iLossRate = m_iLossRate; - obj->m_iReorderDistance = m_iReorderDistance; - obj->m_dInterval = m_dInterval; - obj->m_dCWnd = m_dCWnd; - - return obj; + 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_iSRTT = m_iSRTT; + obj->m_iBandwidth = m_iBandwidth; + obj->m_iLossRate = m_iLossRate; + obj->m_iReorderDistance = m_iReorderDistance; + obj->m_dInterval = m_dInterval; + obj->m_dCWnd = m_dCWnd; + + return obj; } int srt::CInfoBlock::getKey() { - if (m_iIPversion == AF_INET) - return m_piIP[0]; + if (m_iIPversion == AF_INET) + return m_piIP[0]; - return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3]; + return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3]; } void srt::CInfoBlock::convert(const sockaddr_any& addr, uint32_t aw_ip[4]) { - if (addr.family() == AF_INET) - { - aw_ip[0] = addr.sin.sin_addr.s_addr; - aw_ip[1] = aw_ip[2] = aw_ip[3] = 0; - } - else - { - memcpy((aw_ip), addr.sin6.sin6_addr.s6_addr, sizeof addr.sin6.sin6_addr.s6_addr); - } + if (addr.family() == AF_INET) + { + aw_ip[0] = addr.sin.sin_addr.s_addr; + aw_ip[1] = aw_ip[2] = aw_ip[3] = 0; + } + else + { + memcpy((aw_ip), addr.sin6.sin6_addr.s6_addr, sizeof addr.sin6.sin6_addr.s6_addr); + } } diff --git a/srtcore/cache.h b/srtcore/cache.h index d5a037633..d7f586c20 100644 --- a/srtcore/cache.h +++ b/srtcore/cache.h @@ -54,220 +54,215 @@ namespace srt class CCacheItem { public: - virtual ~CCacheItem() {} + virtual ~CCacheItem() {} public: - virtual CCacheItem& operator=(const CCacheItem&) = 0; + virtual CCacheItem& operator=(const CCacheItem&) = 0; - // The "==" operator SHOULD only compare key values. - virtual bool 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. + /// get a deep copy clone of the current item + /// @return Pointer to the new item, or NULL if failed. - virtual CCacheItem* clone() = 0; + 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. + /// 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; + 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() {} + // 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 +template +class CCache { public: - CCache(int size = 1024): - m_iMaxSize(size), - m_iHashSize(size * 3), - m_iCurrSize(0) - { - m_vHashPtr.resize(m_iHashSize); - // Exception: -> CUDTUnited ctor - srt::sync::setupMutex(m_Lock, "Cache"); - } - - ~CCache() - { - clear(); - } + CCache(int size = 1024) + : m_iMaxSize(size) + , m_iHashSize(size * 3) + , m_iCurrSize(0) + { + m_vHashPtr.resize(m_iHashSize); + // Exception: -> CUDTUnited ctor + srt::sync::setupMutex(m_Lock, "Cache"); + } + + ~CCache() { clear(); } 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) - { - srt::sync::ScopedLock 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) - { - srt::sync::ScopedLock 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) + /// 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) + { + srt::sync::ScopedLock 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) { - last_item_list.erase(i); - break; + // copy the cached info + *data = ***i; + return 0; } - } + } - last_data->release(); - delete last_data; - m_StorageList.pop_back(); - -- m_iCurrSize; - } + return -1; + } - return 0; - } + /// 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. -private: + int update(T* data) + { + srt::sync::ScopedLock 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; + } - /// Specify the cache size (i.e., max number of items). - /// Private or else must be protected by a lock. - /// @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. - /// Private or else must be protected by a lock. - 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; - } + return 0; + } private: - std::list m_StorageList; - typedef typename std::list::iterator ItemPtr; - typedef std::list ItemPtrList; - std::vector m_vHashPtr; + /// Specify the cache size (i.e., max number of items). + /// Private or else must be protected by a lock. + /// @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. + /// Private or else must be protected by a lock. + 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; + } - int m_iMaxSize; - int m_iHashSize; - int m_iCurrSize; +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; - srt::sync::Mutex m_Lock; + srt::sync::Mutex m_Lock; private: - CCache(const CCache&); - CCache& operator=(const CCache&); + 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; // Address family: AF_INET or AF_INET6. - uint64_t m_ullTimeStamp; // Last update time. - int m_iSRTT; // Smoothed RTT. - int m_iBandwidth; // Estimated link bandwidth. - int m_iLossRate; // Average loss rate. - int m_iReorderDistance; // Packet reordering distance. - double m_dInterval; // Inter-packet time (Congestion Control). - double m_dCWnd; // Congestion window size (Congestion Control). + uint32_t m_piIP[4]; // IP address, machine read only, not human readable format. + int m_iIPversion; // Address family: AF_INET or AF_INET6. + uint64_t m_ullTimeStamp; // Last update time. + int m_iSRTT; // Smoothed RTT. + int m_iBandwidth; // Estimated link bandwidth. + int m_iLossRate; // Average loss rate. + int m_iReorderDistance; // Packet reordering distance. + double m_dInterval; // Inter-packet time (Congestion Control). + double m_dCWnd; // Congestion window size (Congestion Control). public: - CInfoBlock() {} // NOTE: leaves uninitialized - CInfoBlock& copyFrom(const CInfoBlock& obj); - CInfoBlock(const CInfoBlock& src) { copyFrom(src); } - CInfoBlock& operator=(const CInfoBlock& src) { return copyFrom(src); } - bool operator==(const CInfoBlock& obj) const; - CInfoBlock* clone(); - int getKey(); - void release() {} + CInfoBlock() {} // NOTE: leaves uninitialized + CInfoBlock& copyFrom(const CInfoBlock& obj); + CInfoBlock(const CInfoBlock& src) { copyFrom(src); } + CInfoBlock& operator=(const CInfoBlock& src) { return copyFrom(src); } + bool operator==(const CInfoBlock& obj) const; + CInfoBlock* clone(); + int getKey(); + 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 - /// 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_any& addr, uint32_t ip[4]); + static void convert(const sockaddr_any& addr, uint32_t ip[4]); }; } // namespace srt diff --git a/srtcore/core.cpp b/srtcore/core.cpp index e8d68e2a5..c6cde1545 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -608,9 +608,8 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) case SRTO_RCVDATA: if (m_pRcvBuffer) { - enterCS(m_RecvLock); + ScopedLock lck(m_RcvBufferLock); *(int32_t *)optval = m_pRcvBuffer->getRcvDataSize(); - leaveCS(m_RecvLock); } else *(int32_t *)optval = 0; @@ -2032,7 +2031,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) { uint32_t srtdata_out[SRTDATA_MAXSIZE]; size_t len_out = 0; - res = m_pCryptoControl->processSrtMsg_KMREQ(srtdata, len, CUDT::HS_VERSION_UDT4, + res = m_pCryptoControl->processSrtMsg_KMREQ(srtdata, len, CUDT::HS_VERSION_UDT4, m_uPeerSrtVersion, (srtdata_out), (len_out)); if (res == SRT_CMD_KMRSP) { @@ -2069,7 +2068,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) case SRT_CMD_KMRSP: { // KMRSP doesn't expect any following action - m_pCryptoControl->processSrtMsg_KMRSP(srtdata, len, CUDT::HS_VERSION_UDT4); + m_pCryptoControl->processSrtMsg_KMRSP(srtdata, len, m_uPeerSrtVersion); return true; // nothing to do } @@ -2659,7 +2658,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; } - int res = m_pCryptoControl->processSrtMsg_KMREQ(begin + 1, bytelen, HS_VERSION_SRT1, + int res = m_pCryptoControl->processSrtMsg_KMREQ(begin + 1, bytelen, HS_VERSION_SRT1, m_uPeerSrtVersion, (out_data), (*pw_len)); if (res != SRT_CMD_KMRSP) { @@ -2706,7 +2705,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, } else if (cmd == SRT_CMD_KMRSP) { - int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, HS_VERSION_SRT1); + int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, m_uPeerSrtVersion); if (m_config.bEnforcedEnc && res == -1) { if (m_pCryptoControl->m_SndKmState == SRT_KM_S_BADSECRET) @@ -4743,6 +4742,7 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; HLOGC(cnlog.Debug, log << CONID() << "applyResponseSettings: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + // NOTE: m_RcvAckLock required, but this is here allowed because not all threads run yet m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; const int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; @@ -5466,6 +5466,7 @@ void * srt::CUDT::tsbpd(void* param) rxready = true; if (info.seq_gap) { + // XXX TSA: Requires lock on m_RcvBufferLock (locked already by enterCS) const int iDropCnt SRT_ATR_UNUSED = self->rcvDropTooLateUpTo(info.seqno); #if ENABLE_BONDING shall_update_group = true; @@ -5862,6 +5863,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& } #if ENABLE_BONDING + m_ConnectionLock.unlock(); // The socket and the group are only linked to each other after interpretSrtHandshake(..) has been called. // Keep the group alive for the lifetime of this function, // and do it BEFORE acquiring m_ConnectionLock to avoid @@ -5869,6 +5871,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // This will check if a socket belongs to a group and if so // it will remember this group and keep it alive here. CUDTUnited::GroupKeeper group_keeper(uglobal(), m_parent); + m_ConnectionLock.lock(); #endif if (!prepareBuffers(NULL)) @@ -5983,7 +5986,7 @@ bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::st #endif bool is_suppressed = IsSet(m_LogSlowDownExpired, BIT(logid)); - bool isnow = (m_tsLogSlowDown.load() + milliseconds_from(SRT_LOG_SLOWDOWN_FREQ_MS)) <= tnow; + const bool isnow = (m_tsLogSlowDown[logid].load() + milliseconds_from(SRT_LOG_SLOWDOWN_FREQ_MS)) <= tnow; if (isnow) { // Theoretically this should prevent other calls of this function to take @@ -5995,11 +5998,11 @@ bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::st // Note: it may happen that two threads could intermix one another between // the check and setting up, but this will at worst case set the slightly // later time again. - m_tsLogSlowDown.store(tnow); + m_tsLogSlowDown[logid].store(tnow); is_suppressed = false; - int supr = m_aSuppressedMsg[logid]; + const int supr = m_aSuppressedMsg[logid]; if (supr > 0) w_why = Sprint("++SUPPRESSED: ", supr); @@ -6007,7 +6010,7 @@ bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::st } else { - w_why = Sprint("Too early - last one was ", FormatDuration(tnow - m_tsLogSlowDown.load())); + w_why = Sprint("Too early - last one was ", FormatDuration(tnow - m_tsLogSlowDown[logid].load())); // Set YOUR OWN bit, atomically. m_LogSlowDownExpired |= uint8_t(BIT(logid)); ++m_aSuppressedMsg[logid]; @@ -6041,13 +6044,15 @@ bool srt::CUDT::createCrypter(HandshakeSide side, bool bidirectional) // they have outdated values. m_pCryptoControl->setCryptoSecret(m_config.CryptoSecret); + const bool useGcm153 = m_uPeerSrtVersion <= SrtVersion(1, 5, 3); + if (bidirectional || m_config.bDataSender) { HLOGC(rslog.Debug, log << CONID() << "createCrypter: setting RCV/SND KeyLen=" << m_config.iSndCryptoKeyLen); m_pCryptoControl->setCryptoKeylen(m_config.iSndCryptoKeyLen); } - return m_pCryptoControl->init(side, m_config, bidirectional); + return m_pCryptoControl->init(side, m_config, bidirectional, useGcm153); } SRT_REJECT_REASON srt::CUDT::setupCC() @@ -7140,7 +7145,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ do { - if (stillConnected() && !timeout && !m_pRcvBuffer->isRcvDataReady(steady_clock::now())) + if (stillConnected() && !timeout && !isRcvBufferReady()) { /* Kick TsbPd thread to schedule next wakeup (if running) */ if (m_bTsbPd) @@ -7783,7 +7788,7 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) // - m_dCWndSize m_tdSendInterval = microseconds_from((int64_t)m_CongCtl->pktSndPeriod_us()); const double cgwindow = m_CongCtl->cgWindowSize(); - m_iCongestionWindow = cgwindow; + m_iCongestionWindow = (int) cgwindow; #if ENABLE_HEAVY_LOGGING HLOGC(rslog.Debug, log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) @@ -8044,6 +8049,27 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp // [[using locked(m_RcvBufferLock)]] bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) { + if (m_config.bTSBPD || !m_config.bMessageAPI) + { + // The getFirstNonreadSeqNo() function retuens the sequence number of the first packet + // that cannot be read. In cases when a message can consist of several data packets, + // an existing packet of partially available message also cannot be read. + // If TSBPD mode is enabled, a message must consist of a single data packet only. + w_seq = m_pRcvBuffer->getFirstNonreadSeqNo(); + + const int32_t iNextSeqNo = CSeqNo::incseq(m_iRcvCurrSeqNo); + SRT_ASSERT(CSeqNo::seqcmp(w_seq, iNextSeqNo) <= 0); + w_log_reason = iNextSeqNo != w_seq ? "first lost" : "expected next"; + + if (CSeqNo::seqcmp(w_seq, iNextSeqNo) > 0) + { + LOGC(xtlog.Error, log << "IPE: NONCONT-SEQUENCE: RCV buffer first non-read %" << w_seq << ", RCV latest seqno %" << m_iRcvCurrSeqNo); + w_seq = iNextSeqNo; + } + + return true; + } + { ScopedLock losslock (m_RcvLossLock); const int32_t seq = m_pRcvLossList->getFirstLostSeq(); @@ -8091,6 +8117,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // since the last full ACK. It should unblock the sender to proceed further. const bool bNeedFullAck = (m_bBufferWasFull && getAvailRcvBufferSizeNoLock() > 0); int32_t ack; // First unacknowledged packet sequence number (acknowledge up to ack). + if (!getFirstNoncontSequence((ack), (reason))) return nbsent; @@ -8687,10 +8714,21 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr { if (ctrlpkt.getAckSeqNo() > (m_iAckSeqNo - static_cast(ACK_WND_SIZE)) && ctrlpkt.getAckSeqNo() <= m_iAckSeqNo) { - LOGC(inlog.Note, - log << CONID() << "ACKACK out of order, skipping RTT calculation " - << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iSRTT << ")"); + string why; + if (frequentLogAllowed(FREQLOGFA_ACKACK_OUTOFORDER, tsArrival, (why))) + { + LOGC(inlog.Note, + log << CONID() << "ACKACK out of order, skipping RTT calculation " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iSRTT << ")." << why); + } +#if SRT_ENABLE_FREQUENT_LOG_TRACE + else + { + LOGC(qrlog.Note, log << "SUPPRESSED: ACKACK out of order LOG: " << why); + } +#endif + return; } @@ -8747,7 +8785,7 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr if (m_config.bDriftTracer) { #if ENABLE_BONDING - ScopedLock glock(uglobal().m_GlobControlLock); + ScopedLock glock(uglobal().m_GlobControlLock); // XXX not too excessive? const bool drift_updated = #endif m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); @@ -9224,6 +9262,7 @@ void srt::CUDT::updateSrtRcvSettings() // (unlike in normal situation when reading directly from socket), however // its time to play shall be properly defined. ScopedLock lock(m_RecvLock); + // XXX ALSO: m_RcvBufferLock ??? // NOTE: remember to also update synchronizeWithGroup() if more settings are updated here. m_pRcvBuffer->setPeerRexmitFlag(m_bPeerRexmitFlag); @@ -10722,7 +10761,7 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) { int bufseq; { - ScopedLock lg (m_RcvBufferLock); + ScopedLock lg (source->m_RcvBufferLock); bufseq = source->m_pRcvBuffer->getStartSeqNo(); } ScopedLock lg (m_RecvLock); @@ -11285,6 +11324,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) return conn; packet.setLength(m_iMaxSRTPayloadSize); + // XXX REQUIRES LOCK ON acpu->m_ConnectionLock. + // Check clashes with m_LSLock! if (!acpu->createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (packet), (hs))) @@ -11732,17 +11773,20 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) // Bound to one call because this requires locking pg->updateFailedLink(); } + // Sockets that never succeeded to connect must be deleted + // explicitly, otherwise they will never be deleted. OTOH + // the socket can be on the path of deletion already, so + // this only makes sure that the socket will be deleted, + // one way or another. + if (pending_broken) + { + // XXX This somehow can cause a deadlock + // uglobal()->close(m_parent); + LOGC(smlog.Debug, log << "updateBrokenConnection...: BROKEN SOCKET @" << m_SocketID << " - CLOSING, to be removed from group."); + m_parent->setBrokenClosed(); + } } - // Sockets that never succeeded to connect must be deleted - // explicitly, otherwise they will never be deleted. - if (pending_broken) - { - // XXX This somehow can cause a deadlock - // uglobal()->close(m_parent); - LOGC(smlog.Debug, log << "updateBrokenConnection...: BROKEN SOCKET @" << m_SocketID << " - CLOSING, to be removed from group."); - m_parent->setBrokenClosed(); - } #endif } diff --git a/srtcore/core.h b/srtcore/core.h index a836cf214..e86c85ddf 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -571,7 +571,11 @@ class CUDT // Note: This is an "interpret" function, which should treat the tp as // "possibly group type" that might be out of the existing values. SRT_ATR_NODISCARD bool interpretGroup(const int32_t grpdata[], size_t data_size, int hsreq_type_cmd); - SRT_ATR_NODISCARD SRTSOCKET makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE tp, uint32_t link_flags); + SRT_ATR_NODISCARD + // XXX These below can't be set because the header file has no access to these field definitions + //SRT_TSA_NEEDS_LOCKED(CUDTUnited::m_GlobControlLock) + //SRT_TSA_NEEDS_NONLOCKED(CUDTGroup::m_GroupLock) + SRTSOCKET makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE tp, uint32_t link_flags); void synchronizeWithGroup(CUDTGroup* grp); #endif @@ -705,6 +709,8 @@ class CUDT /// removes the loss record from both current receiver loss list and /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); + + SRT_TSA_NEEDS_NONLOCKED(m_RcvLossLock) // will scope-lock it inside void dropFromLossLists(int32_t from, int32_t to); SRT_TSA_NEEDS_LOCKED(m_RcvBufferLock) @@ -781,6 +787,8 @@ class CUDT /// @param seqno [in] The sequence number of the first packets following those to be dropped. /// @param reason A reason for dropping (see @a DropReason). /// @return The number of packets dropped. + SRT_TSA_NEEDS_LOCKED(m_RcvBufferLock) + SRT_TSA_NEEDS_NONLOCKED(m_RcvLossLock) int rcvDropTooLateUpTo(int seqno, DropReason reason = DROP_TOO_LATE); static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); @@ -964,13 +972,15 @@ class CUDT int32_t m_iReXmitCount; // Re-Transmit Count since last ACK static const size_t - MAX_FREQLOGFA = 2, FREQLOGFA_ENCRYPTION_FAILURE = 0, - FREQLOGFA_RCV_DROPPED = 1; - atomic_time_point m_tsLogSlowDown; // The last time a log message from the "slow down" group was shown. - // The "slow down" group of logs are those that can be printed too often otherwise, but can't be turned off (warnings and errors). - // Currently only used by decryption failure message, therefore no mutex protection needed. - sync::atomic m_LogSlowDownExpired; // Can't use bitset because atomic + FREQLOGFA_RCV_DROPPED = 1, + FREQLOGFA_ACKACK_OUTOFORDER = 2, + MAX_FREQLOGFA = 3; + + atomic_time_point m_tsLogSlowDown[MAX_FREQLOGFA]; // The last time a log message from the "slow down" group was shown. + // The "slow down" group of logs are those that can be printed too often otherwise, but can't be turned off (warnings and errors). + // Currently only used by decryption failure message, therefore no mutex protection needed. + sync::atomic m_LogSlowDownExpired; // Can't use bitset because atomic sync::atomic m_aSuppressedMsg[MAX_FREQLOGFA]; /// @brief Check if a frequent log can be shown. @@ -979,6 +989,7 @@ class CUDT bool frequentLogAllowed(size_t logid, const time_point& tnow, std::string& why); private: // Receiving related data + SRT_TSA_PT_GUARDED_BY(m_RcvBufferLock) CRcvBuffer* m_pRcvBuffer; //< Receiver buffer SRT_TSA_PT_GUARDED_BY(m_RcvLossLock) CRcvLossList* m_pRcvLossList; //< Receiver loss list @@ -1009,6 +1020,7 @@ class CUDT bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets bool m_bGroupTsbPd; // TSBPD should be used for GROUP RECEIVER instead + SRT_TSA_GUARDED_BY(m_RcvTsbPdStartupLock) sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock bool m_bTsbPdNeedsWakeup; // Signal TsbPd thread to wake up on RCV buffer state change. @@ -1143,6 +1155,8 @@ class CUDT /// @retval false Nothing was extracted for sending, @a nexttime should be ignored bool packData(CPacket& packet, time_point& nexttime, sockaddr_any& src_addr); + /// Also excludes srt::CUDTUnited::m_GlobControlLock. + SRT_TSA_NEEDS_NONLOCKED(m_RcvTsbPdStartupLock, m_StatsLock, m_RecvLock, m_RcvLossLock, m_RcvBufferLock) int processData(CUnit* unit); /// This function passes the incoming packet to the initial processing @@ -1158,6 +1172,7 @@ class CUDT /// @return 0 The call was successful (regardless if the packet was accepted or not). /// @return -1 The call has failed: no space left in the buffer. /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). + SRT_TSA_NEEDS_LOCKED(m_RcvBufferLock) int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); /// Get the packet's TSBPD time - @@ -1166,8 +1181,10 @@ class CUDT /// and shall not be used when ENABLE_BONDING=0. time_point getPktTsbPdTime(void* grp, const CPacket& packet); + SRT_TSA_NEEDS_NONLOCKED(m_RcvTsbPdStartupLock) /// Checks and spawns the TSBPD thread if required. int checkLazySpawnTsbPdThread(); + void processClose(); /// Process the request after receiving the handshake from caller. diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index fdd643f22..e7fe0be89 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -138,7 +138,7 @@ void srt::CCryptoControl::createFakeSndContext() int srt::CCryptoControl::processSrtMsg_KMREQ( const uint32_t* srtdata SRT_ATR_UNUSED, size_t bytelen SRT_ATR_UNUSED, - int hsv SRT_ATR_UNUSED, + int hsv SRT_ATR_UNUSED, unsigned srtv SRT_ATR_UNUSED, uint32_t pw_srtdata_out[], size_t& w_srtlen) { //Receiver @@ -172,6 +172,8 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( (m_iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO && kmdata[HCRYPT_MSG_KM_OFS_CIPHER] == HCRYPT_CIPHER_AES_GCM) || (m_iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM); + m_bUseGcm153 = srtv <= SrtVersion(1, 5, 3); + // 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). @@ -331,6 +333,13 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: NOT REPLAYING the key update to TX CRYPTO CTX."); } +#ifdef SRT_ENABLE_ENCRYPTION + if (m_hRcvCrypto != NULL) + HaiCrypt_UpdateGcm153(m_hRcvCrypto, m_bUseGcm153); + if (m_hSndCrypto != NULL) + HaiCrypt_UpdateGcm153(m_hSndCrypto, m_bUseGcm153); +#endif + return SRT_CMD_KMRSP; HSv4_ErrorReport: @@ -359,7 +368,7 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( return SRT_CMD_KMRSP; } -int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int /* XXX unused? hsv*/) +int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, unsigned srtv) { /* All 32-bit msg fields (if present) swapped on reception * But HaiCrypt expect network order message @@ -371,9 +380,6 @@ int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len 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; @@ -459,6 +465,14 @@ int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len } HLOGC(cnlog.Debug, log << "processSrtMsg_KMRSP: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry << "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry); + + m_bUseGcm153 = srtv <= SrtVersion(1, 5, 3); +#ifdef SRT_ENABLE_ENCRYPTION + if (m_hRcvCrypto != NULL) + HaiCrypt_UpdateGcm153(m_hRcvCrypto, m_bUseGcm153); + if (m_hSndCrypto != NULL) + HaiCrypt_UpdateGcm153(m_hSndCrypto, m_bUseGcm153); +#endif } LOGP(cnlog.Note, FormatKmMessage("processSrtMsg_KMRSP", SRT_CMD_KMRSP, len)); @@ -593,6 +607,7 @@ srt::CCryptoControl::CCryptoControl(SRTSOCKET id) , m_KmRefreshRatePkt(0) , m_KmPreAnnouncePkt(0) , m_iCryptoMode(CSrtConfig::CIPHER_MODE_AUTO) + , m_bUseGcm153(false) , m_bErrorReported(false) { m_KmSecret.len = 0; @@ -606,7 +621,7 @@ srt::CCryptoControl::CCryptoControl(SRTSOCKET id) m_hRcvCrypto = NULL; } -bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool bidirectional SRT_ATR_UNUSED) +bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool bidirectional SRT_ATR_UNUSED, bool bUseGcm153 SRT_ATR_UNUSED) { // NOTE: initiator creates m_hSndCrypto. When bidirectional, // it creates also m_hRcvCrypto with the same key length. @@ -620,6 +635,7 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b // Set UNSECURED state as default m_RcvKmState = SRT_KM_S_UNSECURED; m_iCryptoMode = cfg.iCryptoMode; + m_bUseGcm153 = bUseGcm153; #ifdef SRT_ENABLE_ENCRYPTION if (!cfg.bTSBPD && m_iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO) @@ -807,7 +823,7 @@ srt::EncryptionStatus srt::CCryptoControl::encrypt(CPacket& w_packet SRT_ATR_UNU { return ENCS_FAILED; } - else if ( rc > 0 ) + 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? diff --git a/srtcore/crypto.h b/srtcore/crypto.h index 9b3387d6e..4fca16eb3 100644 --- a/srtcore/crypto.h +++ b/srtcore/crypto.h @@ -68,6 +68,7 @@ class CCryptoControl int m_KmRefreshRatePkt; int m_KmPreAnnouncePkt; int m_iCryptoMode; + bool m_bUseGcm153; // Older AES-GCM version existed up to SRT v1.5.3. HaiCrypt_Secret m_KmSecret; //Key material shared secret // Sender @@ -127,15 +128,18 @@ class CCryptoControl // Needed for CUDT void updateKmState(int cmd, size_t srtlen); - // Detailed processing - int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, int hsv, + /// Process the KM request message. + /// @param srtv peer's SRT version. + int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, int hsv, unsigned srtv, uint32_t srtdata_out[], size_t&); - // 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); + /// Process the KM response message. + /// @param srtv peer's SRT version. + /// @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, unsigned srtv); void createFakeSndContext(); const unsigned char* getKmMsg_data(size_t ki) const { return m_SndKmMsg[ki].Msg; } @@ -207,7 +211,7 @@ class CCryptoControl std::string CONID() const; std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen); - bool init(HandshakeSide, const CSrtConfig&, bool); + bool init(HandshakeSide, const CSrtConfig&, bool bidir, bool bUseGcm153); SRT_TSA_NEEDS_NONLOCKED(m_mtxLock) void close(); diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 7c77f2e20..d5a687ffa 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -758,12 +758,15 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) w_optlen = sizeof(uint32_t); return; + case SRTO_KMSTATE: + *(uint32_t*)pw_optval = getGroupEncryptionState(); + w_optlen = sizeof(uint32_t); + return; + // Write-only options for security reasons or // options that refer to a socket state, that // makes no sense for a group. case SRTO_PASSPHRASE: - case SRTO_KMSTATE: - case SRTO_PBKEYLEN: case SRTO_KMPREANNOUNCE: case SRTO_KMREFRESHRATE: case SRTO_BINDTODEVICE: @@ -775,6 +778,24 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) default:; // pass on } + bool is_set_on_socket = false; + { + // Can't have m_GroupLock locked while calling getOpt on a member socket + // because the call will acquire m_ControlLock leading to a lock-order-inversion. + enterCS(m_GroupLock); + gli_t gi = m_Group.begin(); + CUDTSocket* const ps = (gi != m_Group.end()) ? gi->ps : NULL; + CUDTUnited::SocketKeeper sk(CUDT::uglobal(), ps); + leaveCS(m_GroupLock); + if (sk.socket) + { + // Return the value from the first member socket, if any is present + // Note: Will throw exception if the request is wrong. + sk.socket->core().getOpt(optname, (pw_optval), (w_optlen)); + is_set_on_socket = true; + } + } + // Check if the option is in the storage, which means that // it was modified on the group. @@ -783,12 +804,18 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) if (i == m_config.end()) { + // Already written to the target variable. + if (is_set_on_socket) + return; + // Not found, see the defaults if (!getOptDefault(optname, (pw_optval), (w_optlen))) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); return; } + // NOTE: even if is_set_on_socket, if it was also found in the group + // settings, overwrite with the value from the group. // Found, return the value from the storage. // Check the size first. @@ -799,6 +826,52 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) memcpy((pw_optval), &i->value[0], i->value.size()); } +SRT_KM_STATE CUDTGroup::getGroupEncryptionState() +{ + multiset kmstates; + { + ScopedLock lk (m_GroupLock); + + // First check the container. If empty, return UNSECURED + if (m_Group.empty()) + return SRT_KM_S_UNSECURED; + + for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + { + CCryptoControl* cc = gi->ps->core().m_pCryptoControl.get(); + if (!cc) + continue; + SRT_KM_STATE gst = cc->m_RcvKmState; + // A fix to NOSECRET is because this is the state when agent has set + // no password, but peer did, and ENFORCEDENCRYPTION=false allowed + // this connection to be established. UNSECURED can't be taken in this + // case because this would suggest that BOTH are unsecured, that is, + // we have established an unsecured connection (which ain't true). + if (gst == SRT_KM_S_UNSECURED && cc->m_SndKmState == SRT_KM_S_NOSECRET) + gst = SRT_KM_S_NOSECRET; + kmstates.insert(gst); + } + } + + // Criteria are: + // 1. UNSECURED, if no member sockets, or at least one UNSECURED found. + // 2. SECURED, if at least one SECURED found (cut off the previous criteria). + // 3. BADSECRET otherwise, although return NOSECRET if no BADSECRET is found. + + if (kmstates.count(SRT_KM_S_UNSECURED)) + return SRT_KM_S_UNSECURED; + + // Now we have UNSECURED ruled out. Remaining may be NOSECRET, BADSECRET or SECURED. + // NOTE: SECURING is an intermediate state for HSv4 and can't occur in groups. + if (kmstates.count(SRT_KM_S_SECURED)) + return SRT_KM_S_SECURED; + + if (kmstates.count(SRT_KM_S_BADSECRET)) + return SRT_KM_S_BADSECRET; + + return SRT_KM_S_NOSECRET; +} + SRT_SOCKSTATUS CUDTGroup::getStatus() { typedef vector > states_t; diff --git a/srtcore/group.h b/srtcore/group.h index 247423da3..56f1456f8 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -313,6 +313,8 @@ class CUDTGroup void send_CheckValidSockets(); + SRT_KM_STATE getGroupEncryptionState(); + public: int recv(char* buf, int len, SRT_MSGCTRL& w_mc); diff --git a/srtcore/logging.h b/srtcore/logging.h index 26e5a7bd7..c8d120e66 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -112,7 +112,7 @@ struct LogConfig std::ostream* log_stream; SRT_LOG_HANDLER_FN* loghandler_fn; void* loghandler_opaque; - srt::sync::Mutex mutex; + mutable srt::sync::Mutex mutex; int flags; LogConfig(const fa_bitset_t& efa, @@ -132,10 +132,10 @@ struct LogConfig } SRT_TSA_WILL_LOCK(mutex) - void lock() { mutex.lock(); } + void lock() const { mutex.lock(); } SRT_TSA_WILL_UNLOCK(mutex) - void unlock() { mutex.unlock(); } + void unlock() const { mutex.unlock(); } }; // The LogDispatcher class represents the object that is responsible for @@ -424,8 +424,10 @@ inline bool LogDispatcher::CheckEnabled() // 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[] + config->lock(); int configured_enabled_fa = config->enabled_fa[fa]; int configured_maxlevel = config->max_level; + config->unlock(); return configured_enabled_fa && level <= configured_maxlevel; } diff --git a/srtcore/packet.h b/srtcore/packet.h index 5094247b5..5a6d6eb15 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -74,6 +74,8 @@ class IOVector #endif { public: + IOVector() { set(NULL, 0); } + inline void set(void* buffer, size_t length) { #ifdef _WIN32 diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 98999a81f..6cb4faeb1 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -444,7 +444,7 @@ int srt::CSndQueue::sockoptQuery(int level, int type) const } #if ENABLE_LOGGING -int srt::CSndQueue::m_counter = 0; +srt::sync::atomic srt::CSndQueue::m_counter(0); #endif void srt::CSndQueue::init(CChannel* c, CTimer* t) @@ -504,11 +504,9 @@ void* srt::CSndQueue::worker(void* param) { CSndQueue* self = (CSndQueue*)param; -#if ENABLE_LOGGING - THREAD_STATE_INIT(("SRT:SndQ:w" + Sprint(m_counter)).c_str()); -#else - THREAD_STATE_INIT("SRT:SndQ:worker"); -#endif + std::string thname; + ThreadName::get(thname); + THREAD_STATE_INIT(thname.c_str()); #if defined(SRT_DEBUG_SNDQ_HIGHRATE) #define IF_DEBUG_HIGHRATE(statement) statement @@ -576,6 +574,13 @@ void* srt::CSndQueue::worker(void* param) continue; } + CUDTUnited::SocketKeeper sk (CUDT::uglobal(), u->id()); + if (!sk.socket) + { + HLOGC(qslog.Debug, log << "Socket to be processed was deleted in the meantime, not packing"); + continue; + } + // pack a packet from the socket CPacket pkt; steady_clock::time_point next_send_time; @@ -931,6 +936,16 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst EReadStatus read_st = rst; EConnectStatus conn_st = cst; + CUDTUnited::SocketKeeper sk (CUDT::uglobal(), i->id); + if (!sk.socket) + { + // Socket deleted already, so stop this and proceed to the next loop. + LOGC(cnlog.Error, log << "updateConnStatus: IPE: socket @" << i->id << " already closed, proceed to only removal from lists"); + toRemove.push_back(*i); + continue; + } + + if (cst != CONN_RENDEZVOUS && dest_id != 0) { if (i->id != dest_id) @@ -976,14 +991,22 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst for (vector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) { HLOGC(cnlog.Debug, log << "updateConnStatus: COMPLETING dep objects update on failed @" << i->id); - // + remove(i->id); + + CUDTUnited::SocketKeeper sk (CUDT::uglobal(), i->id); + if (!sk.socket) + { + // This actually shall never happen, so it's a kind of paranoid check. + LOGC(cnlog.Error, log << "updateConnStatus: IPE: socket @" << i->id << " already closed, NOT ACCESSING its contents"); + continue; + } + // Setting m_bConnecting to false, and need to remove the socket from the rendezvous queue // because the next CUDT::close will not remove it from the queue when m_bConnecting = false, // and may crash on next pass. // // TODO: maybe lock i->u->m_ConnectionLock? i->u->m_bConnecting = false; - remove(i->u->m_SocketID); // DO NOT close the socket here because in this case it might be // unable to get status from at the right moment. Also only member @@ -994,6 +1017,11 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst CUDT::uglobal().m_EPoll.update_events( i->u->m_SocketID, i->u->m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + // Make sure that the socket wasn't deleted in the meantime. + // Skip this part if it was. Note also that if the socket was + // decided to be deleted, it's already moved to m_ClosedSockets + // and should have been therefore already processed for deletion. + i->u->completeBrokenConnectionDependencies(i->errorcode); } @@ -1129,8 +1157,6 @@ srt::CRcvQueue::CRcvQueue() , m_iIPversion() , m_szPayloadSize() , m_bClosing(false) - , m_LSLock() - , m_pListener(NULL) , m_pRendezvousQueue(NULL) , m_vNewEntry() , m_IDLock() @@ -1208,11 +1234,9 @@ void* srt::CRcvQueue::worker(void* param) sockaddr_any sa(self->getIPversion()); int32_t id = 0; -#if ENABLE_LOGGING - THREAD_STATE_INIT(("SRT:RcvQ:w" + Sprint(m_counter)).c_str()); -#else - THREAD_STATE_INIT("SRT:RcvQ:worker"); -#endif + std::string thname; + ThreadName::get(thname); + THREAD_STATE_INIT(thname.c_str()); CUnit* unit = 0; EConnectStatus cst = CONN_AGAIN; @@ -1404,11 +1428,13 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, int listener_ret = SRT_REJ_UNKNOWN; bool have_listener = false; { - ScopedLock cg(m_LSLock); - if (m_pListener) + SharedLock shl(m_pListener); + CUDT* pListener = m_pListener.getPtrNoLock(); + + if (pListener) { - LOGC(cnlog.Debug, log << "PASSING request from: " << addr.str() << " to listener:" << m_pListener->socketID()); - listener_ret = m_pListener->processConnectRequest(addr, unit->m_Packet); + LOGC(cnlog.Debug, log << "PASSING request from: " << addr.str() << " to listener:" << pListener->socketID()); + listener_ret = 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 @@ -1446,6 +1472,12 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CU HLOGC(cnlog.Debug, log << "worker_ProcessAddressedPacket: resending to QUEUED socket @" << id); return worker_TryAsyncRend_OrStore(id, unit, addr); } + // Although we don´t have an exclusive passing here, + // we can count on that when the socket was once present in the hash, + // it will not be deleted for at least one GC cycle. But we still need + // to maintain the object existence until it's in use. + // Note that here we are out of any locks, so m_GlobControlLock can be locked. + CUDTUnited::SocketKeeper sk (CUDT::uglobal(), u->m_parent); // Found associated CUDT - process this as control or data packet // addressed to an associated socket. @@ -1690,21 +1722,15 @@ int srt::CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) int srt::CRcvQueue::setListener(CUDT* u) { - ScopedLock lslock(m_LSLock); - - if (NULL != m_pListener) + if (!m_pListener.set(u)) return -1; - m_pListener = u; return 0; } void srt::CRcvQueue::removeListener(const CUDT* u) { - ScopedLock lslock(m_LSLock); - - if (u == m_pListener) - m_pListener = NULL; + m_pListener.clearIf(u); } void srt::CRcvQueue::registerConnector(const SRTSOCKET& id, diff --git a/srtcore/queue.h b/srtcore/queue.h index dd68a7721..132b670b3 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -469,7 +469,7 @@ class CSndQueue private: #if ENABLE_LOGGING - static int m_counter; + static srt::sync::atomic m_counter; #endif CSndQueue(const CSndQueue&); @@ -554,9 +554,8 @@ class CRcvQueue void storePktClone(int32_t id, const CPacket& pkt); private: - sync::Mutex m_LSLock; - CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity - CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode + sync::CSharedObjectPtr 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 sync::Mutex m_IDLock; diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 117fee2cd..b67c05246 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -217,6 +217,7 @@ used by SRT library internally. #define SRT_TSA_ASSERT_SHARED_CAPABILITY(x) SRT_TSA_EXPR(assert_shared_capability(x)) #define SRT_TSA_RETURN_CAPABILITY(x) SRT_TSA_EXPR(lock_returned(x)) #define SRT_TSA_DISABLED SRT_TSA_EXPR(no_thread_safety_analysis) +// The caller must not hold the given capabilities. #endif // not _MSC_VER #endif // INC_SRT_ATTR_DEFS_H diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index a7cebb909..bfe153657 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -357,3 +357,98 @@ int srt::sync::genRandomInt(int minVal, int maxVal) #endif // HAVE_CXX11 } + +//////////////////////////////////////////////////////////////////////////////// +// +// Shared Mutex +// +//////////////////////////////////////////////////////////////////////////////// + +srt::sync::SharedMutex::SharedMutex() + : m_LockWriteCond() + , m_LockReadCond() + , m_Mutex() + , m_iCountRead(0) + , m_bWriterLocked(false) +{ + setupCond(m_LockReadCond, "SharedMutex::m_pLockReadCond"); + setupCond(m_LockWriteCond, "SharedMutex::m_pLockWriteCond"); + setupMutex(m_Mutex, "SharedMutex::m_pMutex"); +} + +srt::sync::SharedMutex::~SharedMutex() +{ + releaseMutex(m_Mutex); + releaseCond(m_LockWriteCond); + releaseCond(m_LockReadCond); +} + +void srt::sync::SharedMutex::lock() +{ + UniqueLock l1(m_Mutex); + while (m_bWriterLocked) + m_LockWriteCond.wait(l1); + + m_bWriterLocked = true; + + while (m_iCountRead) + m_LockReadCond.wait(l1); +} + +bool srt::sync::SharedMutex::try_lock() +{ + UniqueLock l1(m_Mutex); + if (m_bWriterLocked || m_iCountRead > 0) + return false; + + m_bWriterLocked = true; + return true; +} + +void srt::sync::SharedMutex::unlock() +{ + ScopedLock lk(m_Mutex); + m_bWriterLocked = false; + + m_LockWriteCond.notify_all(); +} + +void srt::sync::SharedMutex::lock_shared() +{ + UniqueLock lk(m_Mutex); + while (m_bWriterLocked) + m_LockWriteCond.wait(lk); + + m_iCountRead++; +} + +bool srt::sync::SharedMutex::try_lock_shared() +{ + UniqueLock lk(m_Mutex); + if (m_bWriterLocked) + return false; + + m_iCountRead++; + return true; +} + +void srt::sync::SharedMutex::unlock_shared() +{ + ScopedLock lk(m_Mutex); + + m_iCountRead--; + + SRT_ASSERT(m_iCountRead >= 0); + if (m_iCountRead < 0) + m_iCountRead = 0; + + if (m_bWriterLocked && m_iCountRead == 0) + m_LockReadCond.notify_one(); + +} + +int srt::sync::SharedMutex::getReaderCount() const +{ + ScopedLock lk(m_Mutex); + return m_iCountRead; +} \ No newline at end of file diff --git a/srtcore/sync.h b/srtcore/sync.h index 562404849..8f08e3914 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -343,15 +343,19 @@ class SRT_TSA_CAPABILITY("mutex") Mutex pthread_mutex_t m_mutex; }; -/// A pthread version of std::chrono::scoped_lock (or lock_guard for C++11) +/// A pthread version of std::scoped_lock (or lock_guard for C++11). class SRT_TSA_SCOPED_CAPABILITY ScopedLock { public: SRT_TSA_WILL_LOCK(m) - explicit ScopedLock(Mutex& m); + explicit ScopedLock(Mutex& m) + : m_mutex(m) + { + m_mutex.lock(); + } SRT_TSA_WILL_UNLOCK() - ~ScopedLock(); + ~ScopedLock() { m_mutex.unlock(); } private: Mutex& m_mutex; @@ -491,6 +495,122 @@ class Condition inline void setupCond(Condition& cv, const char*) { cv.init(); } inline void releaseCond(Condition& cv) { cv.destroy(); } +/////////////////////////////////////////////////////////////////////////////// +// +// Shared Mutex section +// +/////////////////////////////////////////////////////////////////////////////// + +/// Implementation of a read-write mutex. +/// This allows multiple readers at a time, or a single writer. +/// TODO: The class can be improved if needed to give writer a preference +/// by adding additional m_iWritersWaiting member variable (counter). +/// TODO: The m_iCountRead could be made atomic to make unlok_shared() faster and lock-free. +class SRT_TSA_CAPABILITY("mutex") SharedMutex +{ +public: + SharedMutex(); + ~SharedMutex(); + +public: + /// Acquire the lock for writting purposes. Only one thread can acquire this lock at a time + /// Once it is locked, no reader can acquire it + void lock(); + bool try_lock(); + void unlock(); + + /// Acquire the lock if no writter already has it. For read purpose only + /// Several readers can lock this at the same time. + void lock_shared(); + bool try_lock_shared(); + void unlock_shared(); + + int getReaderCount() const; + +protected: + Condition m_LockWriteCond; + Condition m_LockReadCond; + + mutable Mutex m_Mutex; + + int m_iCountRead; + bool m_bWriterLocked; +}; + +/// A version of std::scoped_lock (or lock_guard for C++11). +/// We could have used the srt::sync::ScopedLock making it a template-based class. +/// But in that case all usages would have to be specificed like ScopedLock in C++03. +class SRT_TSA_SCOPED_CAPABILITY ExclusiveLock +{ +public: + SRT_TSA_WILL_LOCK(m) + explicit ExclusiveLock(SharedMutex& m) + : m_mutex(m) + { + m_mutex.lock(); + } + + SRT_TSA_WILL_UNLOCK(m_mutex) + ~ExclusiveLock() { m_mutex.unlock(); } + +private: + SharedMutex& m_mutex; +}; + +/// A reduced implementation of the std::shared_lock functionality (available in C++14). +class SRT_TSA_SCOPED_CAPABILITY SharedLock +{ +public: + SRT_TSA_WILL_LOCK_SHARED(m) + explicit SharedLock(SharedMutex& m) + : m_mtx(m) + { + m_mtx.lock_shared(); + } + + SRT_TSA_WILL_UNLOCK_SHARED(m_mtx) + ~SharedLock() { m_mtx.unlock_shared(); } + +private: + SharedMutex& m_mtx; +}; + +/// A class template for a shared object. It is a wrapper around a pointer to an object +/// and a shared mutex. It allows multiple readers to access the object at the same time, +/// but only one writer can access the object at a time. +template +class CSharedObjectPtr : public SharedMutex +{ +public: + CSharedObjectPtr() + : m_pObj(NULL) + { + } + + bool set(T* pObj) + { + ExclusiveLock lock(*this); + if (m_pObj) + return false; + m_pObj = pObj; + return true; + } + + bool clearIf(const T* pObj) + { + ExclusiveLock lock(*this); + if (m_pObj != pObj) + return false; + m_pObj = NULL; + return true; + } + + T* getPtrNoLock() const { return m_pObj; } + +private: + T* m_pObj; +}; + /////////////////////////////////////////////////////////////////////////////// // // Event (CV) section @@ -679,21 +799,21 @@ class SRT_TSA_SCOPED_CAPABILITY CUniqueSync: public CSync UniqueLock& locker() { return m_ulock; } - SRT_TSA_WILL_LOCK(this->m_ulock.mutex()) + SRT_TSA_WILL_LOCK(mut) CUniqueSync(Mutex& mut, Condition& cnd) : CSync(cnd, m_ulock) , m_ulock(mut) { } - SRT_TSA_WILL_LOCK(this->m_ulock.mutex()) + SRT_TSA_WILL_LOCK(m_ulock.mutex()) CUniqueSync(CEvent& event) : CSync(event.cond(), m_ulock) , m_ulock(event.mutex()) { } - SRT_TSA_WILL_UNLOCK(this->m_ulock.mutex()) + SRT_TSA_WILL_UNLOCK(m_ulock.mutex()) ~CUniqueSync() {} // These functions can be used safely because diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 8cb475ea7..8d7561a19 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -230,18 +230,6 @@ bool srt::sync::Mutex::try_lock() return (pthread_mutex_trylock(&m_mutex) == 0); } -srt::sync::ScopedLock::ScopedLock(Mutex& m) - : m_mutex(m) -{ - m_mutex.lock(); -} - -srt::sync::ScopedLock::~ScopedLock() -{ - m_mutex.unlock(); -} - - srt::sync::UniqueLock::UniqueLock(Mutex& m) : m_Mutex(m) { diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index 241d26d99..f40a52eb5 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -349,6 +349,14 @@ TEST(Bonding, Options) string pass = "longenoughpassword"; // passphrase should be ok. EXPECT_NE(srt_setsockflag(grp, SRTO_PASSPHRASE, pass.c_str(), pass.size()), SRT_ERROR); + + uint32_t val = 16; + EXPECT_NE(srt_setsockflag(grp, SRTO_PBKEYLEN, &val, sizeof val), SRT_ERROR); + +#ifdef ENABLE_AEAD_API_PREVIEW + val = 1; + EXPECT_NE(srt_setsockflag(grp, SRTO_CRYPTOMODE, &val, sizeof val), SRT_ERROR); +#endif #endif int lat = 500; @@ -421,14 +429,14 @@ TEST(Bonding, Options) int revlat = -1; int optsize = sizeof revlat; EXPECT_NE(srt_getsockflag(grp, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); - EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(optsize, (int) sizeof revlat); EXPECT_EQ(revlat, 500); revlat = -1; optsize = sizeof revlat; // Expect the same value set on the member socket EXPECT_NE(srt_getsockflag(member, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); - EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(optsize, (int) sizeof revlat); EXPECT_EQ(revlat, 500); // Individual socket option modified on group @@ -443,9 +451,28 @@ TEST(Bonding, Options) // But getting the option value should be equal to the group setting EXPECT_NE(srt_getsockflag(grp, SRTO_OHEADBW, &ohead, &optsize), SRT_ERROR); - EXPECT_EQ(optsize, sizeof ohead); + EXPECT_EQ(optsize, (int) sizeof ohead); EXPECT_EQ(ohead, 12); +#if SRT_ENABLE_ENCRYPTION + + uint32_t kms = -1; + + EXPECT_NE(srt_getsockflag(grp, SRTO_KMSTATE, &kms, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, (int) sizeof kms); + EXPECT_EQ(kms, int(SRT_KM_S_SECURED)); + + EXPECT_NE(srt_getsockflag(grp, SRTO_PBKEYLEN, &kms, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, (int) sizeof kms); + EXPECT_EQ(kms, 16); + +#ifdef ENABLE_AEAD_API_PREVIEW + EXPECT_NE(srt_getsockflag(grp, SRTO_CRYPTOMODE, &kms, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof kms); + EXPECT_EQ(kms, 1); +#endif +#endif + // We're done, the thread can close connection and exit { // Make sure that the thread reached the wait() call. diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 554c6aae5..511c2dcb1 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -236,7 +236,7 @@ TEST_F(CRcvBufferReadMsg, OnePacketGap) { const size_t msg_bytelen = m_payload_sz; EXPECT_TRUE(rcv_buffer.isRcvDataReady()); - EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen); + EXPECT_EQ(readMessage(buff.data(), buff.size()), (int) msg_bytelen); EXPECT_TRUE(verifyPayload(buff.data(), msg_bytelen, CSeqNo::incseq(m_init_seqno, pktno))); } EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); @@ -317,7 +317,7 @@ TEST_F(CRcvBufferReadMsg, PacketDropByMsgNoSeqNo) // Let's say SND does not have the very first packet of the message, // therefore seqnolo of the msg drop request starts with the second packet of the message. - EXPECT_EQ(rcv_buffer.dropMessage(CSeqNo::incseq(m_init_seqno), CSeqNo::incseq(m_init_seqno, msg_len_pkts - 1), msgno, CRcvBuffer::KEEP_EXISTING), msg_len_pkts); + EXPECT_EQ(rcv_buffer.dropMessage(CSeqNo::incseq(m_init_seqno), CSeqNo::incseq(m_init_seqno, msg_len_pkts - 1), msgno, CRcvBuffer::KEEP_EXISTING), (int) msg_len_pkts); EXPECT_FALSE(hasAvailablePackets()); EXPECT_FALSE(rcv_buffer.isRcvDataReady()); @@ -343,7 +343,7 @@ TEST_F(CRcvBufferReadMsg, OnePacket) EXPECT_TRUE(hasAvailablePackets()); const int res2 = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res2, msg_bytelen); + EXPECT_EQ(res2, (int) msg_bytelen); EXPECT_TRUE(verifyPayload(buff.data(), res2, m_init_seqno)); EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } @@ -419,7 +419,7 @@ TEST_F(CRcvBufferReadMsg, MsgAcked) EXPECT_TRUE(hasAvailablePackets()); const int res = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -478,7 +478,7 @@ TEST_F(CRcvBufferReadMsg, MsgHalfAck) EXPECT_TRUE(hasAvailablePackets()); const int res = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -502,7 +502,7 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgNoACK) const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; const int res = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -529,7 +529,7 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; const int res = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -598,7 +598,7 @@ TEST_F(CRcvBufferReadMsg, LongMsgReadReady) EXPECT_TRUE(hasAvailablePackets()); const int res = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -621,7 +621,7 @@ TEST_F(CRcvBufferReadMsg, MsgOutOfOrderDrop) const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; int res = m_rcv_buffer->readMessage(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, msg_seqno + int(i))); @@ -660,7 +660,7 @@ TEST_F(CRcvBufferReadMsg, MsgOutOfOrderAfterInOrder) for (int msg_i = 0; msg_i < 3; ++msg_i) { EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); - EXPECT_EQ(m_rcv_buffer->readMessage(buff.data(), buff.size()), msg_bytelen); + EXPECT_EQ(m_rcv_buffer->readMessage(buff.data(), buff.size()), (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, int(m_init_seqno + msg_i * msg_pkts + i))); @@ -706,7 +706,7 @@ TEST_F(CRcvBufferReadMsg, OnePacketTSBPD) // Read out the first message const int read_len = m_rcv_buffer->readMessage(buff.data(), buff.size()); - EXPECT_EQ(read_len, msg_bytelen); + EXPECT_EQ(read_len, (int) msg_bytelen); EXPECT_TRUE(verifyPayload(buff.data(), read_len, m_init_seqno)); // Check the state after a packet was read @@ -768,7 +768,7 @@ TEST_F(CRcvBufferReadMsg, TSBPDGapBeforeValid) const size_t msg_bytelen = m_payload_sz; array buff; - EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen); + EXPECT_EQ(readMessage(buff.data(), buff.size()), (int) msg_bytelen); EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno)); EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index dca7595b8..ec52d9dd8 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -262,7 +262,7 @@ TEST(TestConnectionAPI, Accept) { // Do extra checks only if you know that this was returned. EXPECT_EQ(ready[0].fd, caller_sock); - EXPECT_EQ(ready[0].events & SRT_EPOLL_ERR, 0); + EXPECT_EQ(ready[0].events & SRT_EPOLL_ERR, 0u); } srt_close(caller_sock); srt_close(listener_sock); diff --git a/test/test_crypto.cpp b/test/test_crypto.cpp index f4fa7f614..47b18dd1a 100644 --- a/test/test_crypto.cpp +++ b/test/test_crypto.cpp @@ -44,7 +44,7 @@ namespace srt m_crypt.setCryptoKeylen(cfg.iSndCryptoKeyLen); cfg.iCryptoMode = CSrtConfig::CIPHER_MODE_AES_GCM; - EXPECT_EQ(m_crypt.init(HSD_INITIATOR, cfg, true), HaiCrypt_IsAESGCM_Supported() != 0); + EXPECT_TRUE(m_crypt.init(HSD_INITIATOR, cfg, true, HaiCrypt_IsAESGCM_Supported())); const unsigned char* kmmsg = m_crypt.getKmMsg_data(0); const size_t km_len = m_crypt.getKmMsg_size(0); @@ -53,7 +53,7 @@ namespace srt std::array km_nworder; NtoHLA(km_nworder.data(), reinterpret_cast(kmmsg), km_len); - m_crypt.processSrtMsg_KMREQ(km_nworder.data(), km_len, 5, kmout, kmout_len); + m_crypt.processSrtMsg_KMREQ(km_nworder.data(), km_len, 5, SrtVersion(1, 5, 3), kmout, kmout_len); } void TearDown() override diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 185b54e98..ac57e5265 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -544,7 +544,7 @@ TEST(TestFEC, ConnectionMess) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); - ASSERT_NE(a, SRT_ERROR); + ASSERT_NE(a, SRT_ERROR) << srt_getlasterror_str(); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config diff --git a/test/test_losslist_rcv.cpp b/test/test_losslist_rcv.cpp index 9ab4f382b..94fea6ceb 100644 --- a/test/test_losslist_rcv.cpp +++ b/test/test_losslist_rcv.cpp @@ -81,7 +81,7 @@ TEST(CRcvFreshLossListTest, CheckFreshLossList) CRcvFreshLoss (45, 80, 100) }; - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); // Ok, now let's do element removal @@ -90,7 +90,7 @@ TEST(CRcvFreshLossListTest, CheckFreshLossList) EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 10); - EXPECT_EQ(floss.size(), 5); + EXPECT_EQ(floss.size(), 5u); // Now we expect to have [10-15] [25-25] [27-35]... // After revoking 25 it should have removed it. @@ -99,41 +99,41 @@ TEST(CRcvFreshLossListTest, CheckFreshLossList) rm = CRcvFreshLoss::removeOne((floss), 27, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 10); - EXPECT_EQ(floss.size(), 5); + EXPECT_EQ(floss.size(), 5u); // STRIP rm = CRcvFreshLoss::removeOne((floss), 28, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 10); - EXPECT_EQ(floss.size(), 5); + EXPECT_EQ(floss.size(), 5u); // DELETE rm = CRcvFreshLoss::removeOne((floss), 25, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 10); - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); // SPLIT rm = CRcvFreshLoss::removeOne((floss), 50, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 100); - EXPECT_EQ(floss.size(), 5); + EXPECT_EQ(floss.size(), 5u); // DELETE rm = CRcvFreshLoss::removeOne((floss), 30, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 3); - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); // Remove nonexistent sequence, but existing before. rm = CRcvFreshLoss::removeOne((floss), 25, NULL); EXPECT_EQ(rm, false); - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); // Remove nonexistent sequence that didn't exist before. rm = CRcvFreshLoss::removeOne((floss), 31, &had_ttl); EXPECT_EQ(rm, false); EXPECT_EQ(had_ttl, 0); - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); } diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp index d00628e63..df4aec487 100644 --- a/test/test_reuseaddr.cpp +++ b/test/test_reuseaddr.cpp @@ -355,7 +355,7 @@ class ReuseAddr : public srt::Test sockaddr_any showacp = (sockaddr*)&scl; std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; - int epoll_in = SRT_EPOLL_IN; + epoll_in = SRT_EPOLL_IN; srt_epoll_add_usock(server_pollid, accepted_sock, &epoll_in); // wait for input char buffer[1316]; diff --git a/test/test_snd_rate_estimator.cpp b/test/test_snd_rate_estimator.cpp index e3d512424..c658c659c 100644 --- a/test/test_snd_rate_estimator.cpp +++ b/test/test_snd_rate_estimator.cpp @@ -3,6 +3,7 @@ #include "gtest/gtest.h" #include "buffer_tools.h" #include "sync.h" +#include "packet.h" using namespace srt; using namespace std; @@ -53,6 +54,7 @@ TEST_F(CSndRateEstFixture, Empty) TEST_F(CSndRateEstFixture, CBRSending) { + const size_t hdrBytes = srt::CPacket::HDR_SIZE; // Generate CBR sending for 2.1 seconds to wrap the buffer around. for (int i = 0; i < 2100; ++i) { @@ -61,7 +63,7 @@ TEST_F(CSndRateEstFixture, CBRSending) const auto rate = m_rateEst.getRate(); if (i >= 100) - EXPECT_EQ(rate, 1316000) << "i=" << i; + EXPECT_EQ(rate, 1316000 + 1000 * hdrBytes) << "i=" << i; else EXPECT_EQ(rate, 0) << "i=" << i; } @@ -72,6 +74,7 @@ TEST_F(CSndRateEstFixture, CBRSending) // only for one sampling period. TEST_F(CSndRateEstFixture, CBRSendingAfterPause) { + const size_t hdrBytes = srt::CPacket::HDR_SIZE; // Send 100 packets with 1000 bytes each for (int i = 0; i < 3100; ++i) { @@ -82,7 +85,7 @@ TEST_F(CSndRateEstFixture, CBRSendingAfterPause) const auto rate = m_rateEst.getRate(); if (i >= 100 && !(i >= 2000 && i < 2100)) - EXPECT_EQ(rate, 1316000) << "i=" << i; + EXPECT_EQ(rate, 1316000 + 1000 * hdrBytes) << "i=" << i; else EXPECT_EQ(rate, 0) << "i=" << i; } @@ -92,6 +95,7 @@ TEST_F(CSndRateEstFixture, CBRSendingAfterPause) // Those empty samples should be included in bitrate estimation. TEST_F(CSndRateEstFixture, CBRSendingShortPause) { + const size_t hdrBytes = srt::CPacket::HDR_SIZE; // Send 100 packets with 1000 bytes each for (int i = 0; i < 3100; ++i) { @@ -102,9 +106,9 @@ TEST_F(CSndRateEstFixture, CBRSendingShortPause) const auto rate = m_rateEst.getRate(); if (i >= 1500 && i < 2000) - EXPECT_EQ(rate, 658000) << "i=" << i; + EXPECT_EQ(rate, 658000 + 500 * hdrBytes) << "i=" << i; else if (i >= 100) - EXPECT_EQ(rate, 1316000) << "i=" << i; + EXPECT_EQ(rate, 1316000 + 1000 * hdrBytes) << "i=" << i; else EXPECT_EQ(rate, 0) << "i=" << i; } diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 271d84186..a4c90af37 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -245,7 +245,7 @@ void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueTy << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); EXPECT_EQ(opt_val, value) << desc << ": Wrong " << entry.optname << " value " << opt_val; - EXPECT_EQ(opt_len, entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; + EXPECT_EQ(opt_len, (int) entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; } typedef char const* strptr; @@ -258,7 +258,7 @@ void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); EXPECT_EQ(strncmp(opt_val, value, min(opt_len, (int)entry.opt_len)), 0) << desc << ": Wrong " << entry.optname << " value " << opt_val; - EXPECT_EQ(opt_len, entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; + EXPECT_EQ(opt_len, (int) entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; } template diff --git a/test/test_sync.cpp b/test/test_sync.cpp index e0454a581..d06b5e5f5 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -159,7 +159,7 @@ TEST(SyncRandom, GenRandomInt) { const int rand_val = genRandomInt(0, int(mn.size()) - 1); ASSERT_GE(rand_val, 0); - ASSERT_LT(rand_val, mn.size()); + ASSERT_LT(rand_val, (int) mn.size()); ++mn[rand_val]; } @@ -445,7 +445,7 @@ TEST(SyncEvent, WaitForTwoNotifyOne) using future_t = decltype(async(launch::async, wait_async, &cond, &mutex, timeout, 0)); - future_t future_result[2] = { + std::array future_result = { async(launch::async, wait_async, &cond, &mutex, timeout, 0), async(launch::async, wait_async, &cond, &mutex, timeout, 1) }; @@ -462,9 +462,9 @@ TEST(SyncEvent, WaitForTwoNotifyOne) using wait_t = decltype(future_t().wait_for(chrono::microseconds(0))); - wait_t wait_state[2] = { - move(future_result[0].wait_for(chrono::microseconds(500))), - move(future_result[1].wait_for(chrono::microseconds(500))) + std::array wait_state = { + future_result[0].wait_for(chrono::microseconds(500)), + future_result[1].wait_for(chrono::microseconds(500)) }; cerr << "SyncEvent::WaitForTwoNotifyOne: NOTIFICATION came from " << notified_clients.size() @@ -609,6 +609,91 @@ TEST(SyncThread, Joinable) EXPECT_FALSE(foo.joinable()); } +/*****************************************************************************/ +/* + * SharedMutex + */ + /*****************************************************************************/ +TEST(SharedMutex, LockWriteRead) +{ + SharedMutex mut; + + mut.lock(); + EXPECT_FALSE(mut.try_lock_shared()); + +} + +TEST(SharedMutex, LockReadWrite) +{ + SharedMutex mut; + + mut.lock_shared(); + EXPECT_FALSE(mut.try_lock()); + +} + +TEST(SharedMutex, LockReadTwice) +{ + SharedMutex mut; + + mut.lock_shared(); + mut.lock_shared(); + EXPECT_TRUE(mut.try_lock_shared()); +} + +TEST(SharedMutex, LockWriteTwice) +{ + SharedMutex mut; + + mut.lock(); + EXPECT_FALSE(mut.try_lock()); +} + +TEST(SharedMutex, LockUnlockWrite) +{ + SharedMutex mut; + mut.lock(); + EXPECT_FALSE(mut.try_lock()); + mut.unlock(); + EXPECT_TRUE(mut.try_lock()); +} + +TEST(SharedMutex, LockUnlockRead) +{ + SharedMutex mut; + + mut.lock_shared(); + EXPECT_FALSE(mut.try_lock()); + + mut.unlock_shared(); + EXPECT_TRUE(mut.try_lock()); +} + +TEST(SharedMutex, LockedReadCount) +{ + SharedMutex mut; + int count = 0; + + mut.lock_shared(); + count++; + ASSERT_EQ(mut.getReaderCount(), count); + + mut.lock_shared(); + count++; + ASSERT_EQ(mut.getReaderCount(), count); + + mut.unlock_shared(); + count--; + ASSERT_EQ(mut.getReaderCount(), count); + + mut.unlock_shared(); + count--; + ASSERT_EQ(mut.getReaderCount(), count); + + EXPECT_TRUE(mut.try_lock()); +} + + /*****************************************************************************/ /* * FormatTime diff --git a/test/test_utilities.cpp b/test/test_utilities.cpp index 1d2c1aa59..28c14044e 100644 --- a/test/test_utilities.cpp +++ b/test/test_utilities.cpp @@ -160,11 +160,11 @@ TEST(CircularBuffer, Overall) ASSERT_EQ(output.d, 22.1); IF_HEAVY_LOGGING(cerr << "Pushing 1 aslong there is capacity:\n"); - int i = 0; + IF_HEAVY_LOGGING(int i = 0); while (buf.push(1) != -1) { IF_HEAVY_LOGGING(cerr << "Pushed, begin=" << buf.m_xBegin << " end=" << buf.m_xEnd << endl); - ++i; + IF_HEAVY_LOGGING(++i); } IF_HEAVY_LOGGING(cerr << "Done " << i << " operations, buffer:\n"); IF_HEAVY_LOGGING(ShowCircularBuffer(buf)); diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 17f4020dc..1811220c8 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -268,7 +268,7 @@ bool CheckMediaSpec(const string& prefix, const vector& spec, string& w_ for (string& a: adrs) w_outspec += a + ","; - Verb() << "NOTE: " << prefix << " specification set as: " << (w_outspec); + Verb("NOTE: ", prefix, " specification set as: ", (w_outspec)); return true; } @@ -294,7 +294,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr int type; int size = sizeof type; srt_getsockflag(acpsock, SRTO_GROUPCONNECT, &type, &size); - Verb() << "listener: @" << acpsock << " - accepting " << (type ? "GROUP" : "SINGLE") << VerbNoEOL; + Verb("listener: @", acpsock, " - accepting ", (type ? "GROUP" : "SINGLE"), VerbNoEOL); if (type != 0) { SRT_GROUP_TYPE gt; @@ -302,12 +302,12 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { if (gt < Size(gtypes)) - Verb() << " type=" << gtypes[gt] << VerbNoEOL; + Verb(" type=", gtypes[gt], VerbNoEOL); else - Verb() << " type=" << int(gt) << VerbNoEOL; + Verb(" type=", int(gt), VerbNoEOL); } } - Verb() << " connection"; + Verb(" connection"); return 0; } @@ -317,7 +317,7 @@ extern "C" int SrtUserPasswordHook(void* , SRTSOCKET acpsock, int hsv, const soc { if (hsv < 5) { - Verb() << "SrtUserPasswordHook: HS version 4 doesn't support extended handshake"; + Verb("SrtUserPasswordHook: HS version 4 doesn't support extended handshake"); return -1; } @@ -818,7 +818,7 @@ int main( int argc, char** argv ) return 0; } - Verb() << "MEDIA CREATION FAILED: " << x.what() << " - exiting."; + Verb("MEDIA CREATION FAILED: ", x.what(), " - exiting."); // Don't speak anything when no -v option. // (the "requested interrupt" will be printed anyway) @@ -847,10 +847,10 @@ int main( int argc, char** argv ) if (transmit_use_sourcetime && src->uri.type() != UriParser::SRT) { - Verb() << "WARNING: -st option is effective only if the target type is SRT"; + Verb("WARNING: -st option is effective only if the target type is SRT"); } - Verb() << "STARTING TRANSMISSION: '" << source_spec << "' --> '" << target_spec << "'"; + Verb("STARTING TRANSMISSION: '", source_spec, "' --> '", target_spec, "'"); // After the time has been spent in the creation // (including waiting for connection) @@ -876,41 +876,41 @@ int main( int argc, char** argv ) { if (stoptime == 0 && timeout != -1 ) { - Verb() << "[." << VerbNoEOL; + Verb("[.", VerbNoEOL); alarm(timeout); } else { alarm(0); } - Verb() << " << ... " << VerbNoEOL; + Verb(", ... ", VerbNoEOL); g_interrupt_reason = "reading"; const MediaPacket& data = src->Read(chunk); - Verb() << " << " << data.payload.size() << " -> " << VerbNoEOL; + Verb(", ", data.payload.size(), " -> ", VerbNoEOL); if ( data.payload.empty() && src->End() ) { - Verb() << "EOS"; + Verb("EOS"); break; } g_interrupt_reason = "writing"; tar->Write(data); if (stoptime == 0 && timeout != -1 ) { - Verb() << ".] " << VerbNoEOL; + Verb(".] ", VerbNoEOL); alarm(0); } if ( tar->Broken() ) { - Verb() << " OUTPUT broken"; + Verb(" OUTPUT broken"); break; } - Verb() << "sent"; + Verb("sent"); if (::transmit_int_state) { - Verror() << "\n (interrupted on request)"; + Verror("\n (interrupted on request)"); break; } @@ -922,7 +922,7 @@ int main( int argc, char** argv ) int remain = int(stoptime - final_delay - elapsed); if (remain < 0) { - Verror() << "\n (interrupted on timeout: elapsed " << elapsed << "s) - waiting " << final_delay << "s for cleanup"; + Verror("\n (interrupted on timeout: elapsed ", elapsed, "s) - waiting ", final_delay, "s for cleanup"); this_thread::sleep_for(chrono::seconds(final_delay)); break; } @@ -934,17 +934,17 @@ int main( int argc, char** argv ) if (!skip_flushing) { - Verror() << "(DEBUG) EOF when reading file. Looping until the sending bufer depletes.\n"; + Verror("(DEBUG) EOF when reading file. Looping until the sending bufer depletes.\n"); for (;;) { size_t still = tar->Still(); if (still == 0) { - Verror() << "(DEBUG) DEPLETED. Done.\n"; + Verror("(DEBUG) DEPLETED. Done.\n"); break; } - Verror() << "(DEBUG)... still " << still << " bytes (sleep 1s)\n"; + Verror("(DEBUG)... still ", still, " bytes (sleep 1s)\n"); this_thread::sleep_for(chrono::seconds(1)); } } @@ -952,16 +952,16 @@ int main( int argc, char** argv ) if (stoptime != 0 && ::timer_state) { - Verror() << "Exit on timeout."; + Verror("Exit on timeout."); } else if (::transmit_int_state) { - Verror() << "Exit on interrupt."; + Verror("Exit on interrupt."); // Do nothing. } else { - Verror() << "STD EXCEPTION: " << x.what(); + Verror("STD EXCEPTION: ", x.what()); } if ( crashonx ) @@ -969,7 +969,7 @@ int main( int argc, char** argv ) if (final_delay > 0) { - Verror() << "Waiting " << final_delay << "s for possible cleanup..."; + Verror("Waiting ", final_delay, "s for possible cleanup..."); this_thread::sleep_for(chrono::seconds(final_delay)); } if (stoptime != 0 && ::timer_state) @@ -979,7 +979,7 @@ int main( int argc, char** argv ) } catch (...) { - Verror() << "UNKNOWN type of EXCEPTION"; + Verror("UNKNOWN type of EXCEPTION"); if ( crashonx ) throw; diff --git a/testing/srt-test-mpbond.cpp b/testing/srt-test-mpbond.cpp index bf9739675..60d91df2c 100644 --- a/testing/srt-test-mpbond.cpp +++ b/testing/srt-test-mpbond.cpp @@ -209,7 +209,6 @@ int main( int argc, char** argv ) return 1; } - auto s = new SrtSource; unique_ptr src; unique_ptr tar; @@ -222,6 +221,7 @@ int main( int argc, char** argv ) Verb() << "SRT -> " << outspec; tar = Target::Create(outspec); + auto s = new SrtSource; s->Acquire(conngrp); src.reset(s); } diff --git a/testing/srt-test-multiplex.cpp b/testing/srt-test-multiplex.cpp index 62c7fbd3d..deb36554c 100644 --- a/testing/srt-test-multiplex.cpp +++ b/testing/srt-test-multiplex.cpp @@ -403,7 +403,7 @@ void Stall() ++i_next; if (i->has_quit) { - Verb() << "Found QUIT mediumpair: " << i->name << " - removing from base"; + Verb("Found QUIT mediumpair: ", i->name, " - removing from base"); i->Stop(); g_media_base.media.erase(i); } @@ -411,7 +411,7 @@ void Stall() if (g_media_base.media.empty()) { - Verb() << "All media have quit. Marking exit."; + Verb("All media have quit. Marking exit."); break; } } diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index 45d657128..912af555e 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -320,7 +320,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin Verb() << "Setting up output: " << spec; unique_ptr m { new TargetMedium }; m->Setup(Target::Create(spec)); - m_output_media.push_back(move(m)); + m_output_media.push_back(std::move(m)); } @@ -367,9 +367,9 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin Verb() << "SRT set up as input source and the first output target"; // Add SRT medium to output targets, and keep input medium empty. - unique_ptr m { new TargetMedium }; - m->Setup(m_srt_relay.get()); - m_output_media.push_back(move(m)); + unique_ptr med { new TargetMedium }; + med->Setup(m_srt_relay.get()); + m_output_media.push_back(std::move(med)); } else { diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 2d6635288..e6f5c871e 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -348,7 +348,7 @@ void SrtCommon::InitParameters(string host, string path, map par) } cc.token = token++; - m_group_nodes.push_back(move(cc)); + m_group_nodes.push_back(std::move(cc)); } par.erase("type"); @@ -1059,8 +1059,8 @@ void SrtCommon::OpenGroupClient() if (!extras.empty()) { Verb() << "?" << extras[0] << VerbNoEOL; - for (size_t i = 1; i < extras.size(); ++i) - Verb() << "&" << extras[i] << VerbNoEOL; + for (size_t ii = 1; ii < extras.size(); ++ii) + Verb() << "&" << extras[ii] << VerbNoEOL; } Verb(); @@ -1130,15 +1130,15 @@ void SrtCommon::OpenGroupClient() // spread the setting on all sockets. ConfigurePost(m_sock); - for (size_t i = 0; i < targets.size(); ++i) + for (size_t j = 0; j < targets.size(); ++j) { // As m_group_nodes is simply transformed into 'targets', // one index can be used to index them all. You don't // have to check if they have equal addresses because they // are equal by definition. - if (targets[i].id != -1 && targets[i].errorcode == SRT_SUCCESS) + if (targets[j].id != -1 && targets[j].errorcode == SRT_SUCCESS) { - m_group_nodes[i].socket = targets[i].id; + m_group_nodes[j].socket = targets[j].id; } } @@ -1159,9 +1159,9 @@ void SrtCommon::OpenGroupClient() } m_group_data.resize(size); - for (size_t i = 0; i < m_group_nodes.size(); ++i) + for (size_t j = 0; j < m_group_nodes.size(); ++j) { - SRTSOCKET insock = m_group_nodes[i].socket; + SRTSOCKET insock = m_group_nodes[j].socket; if (insock == -1) { Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; @@ -1194,11 +1194,11 @@ void SrtCommon::OpenGroupClient() NULL, NULL) != -1) { Verb() << "[C]" << VerbNoEOL; - for (int i = 0; i < len1; ++i) - Verb() << " " << ready_conn[i] << VerbNoEOL; + for (int ii = 0; ii < len1; ++ii) + Verb() << " " << ready_conn[ii] << VerbNoEOL; Verb() << "[E]" << VerbNoEOL; - for (int i = 0; i < len2; ++i) - Verb() << " " << ready_err[i] << VerbNoEOL; + for (int ii = 0; ii < len2; ++ii) + Verb() << " " << ready_err[ii] << VerbNoEOL; Verb() << ""; @@ -3042,7 +3042,7 @@ extern unique_ptr CreateMedium(const string& uri) } if (ptr) - ptr->uri = move(u); + ptr->uri = std::move(u); return ptr; }