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

SuqashSRS4: Build SRT native

This commit is contained in:
winlin 2021-05-16 16:14:00 +08:00
parent a1d7fe46c1
commit e3bca883e1
150 changed files with 45007 additions and 398 deletions

View file

@ -24,6 +24,14 @@ jobs:
- run: | - run: |
echo "Build and run SRS with GB28181" && echo "Build and run SRS with GB28181" &&
cd trunk && ./configure --gb28181=on && make cd trunk && ./configure --gb28181=on && make
build-c7-srt:
docker:
- image: ossrs/srs:dev
steps:
- checkout
- run: |
echo "Build and run SRS with SRT" &&
cd trunk && ./configure --srt=on && make
build-c8-baseline: build-c8-baseline:
docker: docker:
- image: ossrs/srs:dev8 - image: ossrs/srs:dev8
@ -32,14 +40,62 @@ jobs:
- run: | - run: |
echo "Build and run SRS for CentOS8" && echo "Build and run SRS for CentOS8" &&
cd trunk && ./configure && make cd trunk && ./configure && make
build-u20-baseline: build-c8-srt:
docker: docker:
- image: ossrs/srs:dev8 - image: ossrs/srs:dev8
steps:
- checkout
- run: |
echo "Build and run SRS with SRT for CentOS8" &&
cd trunk && ./configure --srt=on && make
build-u16-baseline:
docker:
- image: ossrs/srs:ubuntu16
steps:
- checkout
- run: |
echo "Build and run SRS for Ubuntu16" &&
cd trunk && ./configure && make
build-u16-srt:
docker:
- image: ossrs/srs:ubuntu16
steps:
- checkout
- run: |
echo "Build and run SRS with SRT for Ubuntu16" &&
cd trunk && ./configure --srt=on && make
build-u18-baseline:
docker:
- image: ossrs/srs:ubuntu18
steps:
- checkout
- run: |
echo "Build and run SRS for Ubuntu18" &&
cd trunk && ./configure && make
build-u18-srt:
docker:
- image: ossrs/srs:ubuntu18
steps:
- checkout
- run: |
echo "Build and run SRS with SRT for Ubuntu18" &&
cd trunk && ./configure --srt=on && make
build-u20-baseline:
docker:
- image: ossrs/srs:ubuntu20
steps: steps:
- checkout - checkout
- run: | - run: |
echo "Build and run SRS for Ubuntu20" && echo "Build and run SRS for Ubuntu20" &&
cd trunk && ./configure && make cd trunk && ./configure && make
build-u20-srt:
docker:
- image: ossrs/srs:ubuntu20
steps:
- checkout
- run: |
echo "Build and run SRS with SRT for Ubuntu20" &&
cd trunk && ./configure --srt=on && make
run-utest: run-utest:
docker: docker:
- image: ossrs/srs:dev - image: ossrs/srs:dev
@ -76,6 +132,13 @@ workflows:
- run-regression-test - run-regression-test
- build-c7-noasm - build-c7-noasm
- build-c7-gb28181 - build-c7-gb28181
- build-c7-srt
- build-c8-baseline - build-c8-baseline
- build-c8-srt
- build-u16-baseline
- build-u16-srt
- build-u18-baseline
- build-u18-srt
- build-u20-baseline - build-u20-baseline
- build-u20-srt
- build-c7-ansi-noff - build-c7-ansi-noff

View file

@ -182,6 +182,10 @@ The ports used by SRS:
## V4 changes ## V4 changes
* v4.0, 2021-05-15, SRT: Build SRT from source by SRS. 4.0.115
* v4.0, 2021-05-15, Rename SrsConsumer* to SrsLiveConsumer*. 4.0.114
* v4.0, 2021-05-15, Rename SrsRtcStream* to SrsRtcSource*. 4.0.113
* v4.0, 2021-05-15, Rename SrsSource* to SrsLiveSource*. 4.0.112
* v4.0, 2021-05-15, Rename SrsRtpPacket2 to SrsRtpPacket. 4.0.111 * v4.0, 2021-05-15, Rename SrsRtpPacket2 to SrsRtpPacket. 4.0.111
* v4.0, 2021-05-14, RTC: Remove [Object Cache Pool](https://github.com/ossrs/srs/commit/14bfc98122bba369572417c19ebb2a61b373fc45#commitcomment-47655008), no effect. 4.0.110 * v4.0, 2021-05-14, RTC: Remove [Object Cache Pool](https://github.com/ossrs/srs/commit/14bfc98122bba369572417c19ebb2a61b373fc45#commitcomment-47655008), no effect. 4.0.110
* v4.0, 2021-05-14, Change virtual public to public. 4.0.109 * v4.0, 2021-05-14, Change virtual public to public. 4.0.109

View file

@ -4,6 +4,10 @@ http-parser-2.1.zip
nginx-1.5.7.zip nginx-1.5.7.zip
* for srs to support hls streaming. * for srs to support hls streaming.
srt-1-fit
srt-1.4.1.tar.gz
* https://github.com/Haivision/srt/releases/tag/v1.4.1
openssl-1.1.1b.tar.gz openssl-1.1.1b.tar.gz
openssl-1.1.0e.zip openssl-1.1.0e.zip
openssl-OpenSSL_1_0_2u.tar.gz openssl-OpenSSL_1_0_2u.tar.gz

129
trunk/3rdparty/ffmpeg-4-fit/LICENSE.md vendored Normal file
View file

@ -0,0 +1,129 @@
# License
Most files in FFmpeg are under the GNU Lesser General Public License version 2.1
or later (LGPL v2.1+). Read the file `COPYING.LGPLv2.1` for details. Some other
files have MIT/X11/BSD-style licenses. In combination the LGPL v2.1+ applies to
FFmpeg.
Some optional parts of FFmpeg are licensed under the GNU General Public License
version 2 or later (GPL v2+). See the file `COPYING.GPLv2` for details. None of
these parts are used by default, you have to explicitly pass `--enable-gpl` to
configure to activate them. In this case, FFmpeg's license changes to GPL v2+.
Specifically, the GPL parts of FFmpeg are:
- libpostproc
- optional x86 optimization in the files
- `libavcodec/x86/flac_dsp_gpl.asm`
- `libavcodec/x86/idct_mmx.c`
- `libavfilter/x86/vf_removegrain.asm`
- the following building and testing tools
- `compat/solaris/make_sunver.pl`
- `doc/t2h.pm`
- `doc/texi2pod.pl`
- `libswresample/tests/swresample.c`
- `tests/checkasm/*`
- `tests/tiny_ssim.c`
- the following filters in libavfilter:
- `signature_lookup.c`
- `vf_blackframe.c`
- `vf_boxblur.c`
- `vf_colormatrix.c`
- `vf_cover_rect.c`
- `vf_cropdetect.c`
- `vf_delogo.c`
- `vf_eq.c`
- `vf_find_rect.c`
- `vf_fspp.c`
- `vf_histeq.c`
- `vf_hqdn3d.c`
- `vf_kerndeint.c`
- `vf_lensfun.c` (GPL version 3 or later)
- `vf_mcdeint.c`
- `vf_mpdecimate.c`
- `vf_nnedi.c`
- `vf_owdenoise.c`
- `vf_perspective.c`
- `vf_phase.c`
- `vf_pp.c`
- `vf_pp7.c`
- `vf_pullup.c`
- `vf_repeatfields.c`
- `vf_sab.c`
- `vf_signature.c`
- `vf_smartblur.c`
- `vf_spp.c`
- `vf_stereo3d.c`
- `vf_super2xsai.c`
- `vf_tinterlace.c`
- `vf_uspp.c`
- `vf_vaguedenoiser.c`
- `vsrc_mptestsrc.c`
Should you, for whatever reason, prefer to use version 3 of the (L)GPL, then
the configure parameter `--enable-version3` will activate this licensing option
for you. Read the file `COPYING.LGPLv3` or, if you have enabled GPL parts,
`COPYING.GPLv3` to learn the exact legal terms that apply in this case.
There are a handful of files under other licensing terms, namely:
* The files `libavcodec/jfdctfst.c`, `libavcodec/jfdctint_template.c` and
`libavcodec/jrevdct.c` are taken from libjpeg, see the top of the files for
licensing details. Specifically note that you must credit the IJG in the
documentation accompanying your program if you only distribute executables.
You must also indicate any changes including additions and deletions to
those three files in the documentation.
* `tests/reference.pnm` is under the expat license.
## External libraries
FFmpeg can be combined with a number of external libraries, which sometimes
affect the licensing of binaries resulting from the combination.
### Compatible libraries
The following libraries are under GPL version 2:
- avisynth
- frei0r
- libcdio
- libdavs2
- librubberband
- libvidstab
- libx264
- libx265
- libxavs
- libxavs2
- libxvid
When combining them with FFmpeg, FFmpeg needs to be licensed as GPL as well by
passing `--enable-gpl` to configure.
The following libraries are under LGPL version 3:
- gmp
- libaribb24
- liblensfun
When combining them with FFmpeg, use the configure option `--enable-version3` to
upgrade FFmpeg to the LGPL v3.
The VMAF, mbedTLS, RK MPI, OpenCORE and VisualOn libraries are under the Apache License
2.0. That license is incompatible with the LGPL v2.1 and the GPL v2, but not with
version 3 of those licenses. So to combine these libraries with FFmpeg, the
license version needs to be upgraded by passing `--enable-version3` to configure.
The smbclient library is under the GPL v3, to combine it with FFmpeg,
the options `--enable-gpl` and `--enable-version3` have to be passed to
configure to upgrade FFmpeg to the GPL v3.
### Incompatible libraries
There are certain libraries you can combine with FFmpeg whose licenses are not
compatible with the GPL and/or the LGPL. If you wish to enable these
libraries, even in circumstances that their license may be incompatible, pass
`--enable-nonfree` to configure. This will cause the resulting binary to be
unredistributable.
The Fraunhofer FDK AAC and OpenSSL libraries are under licenses which are
incompatible with the GPLv2 and v3. To the best of our knowledge, they are
compatible with the LGPL.

1031
trunk/3rdparty/srt-1-fit/CMakeLists.txt vendored Normal file

File diff suppressed because it is too large Load diff

373
trunk/3rdparty/srt-1-fit/LICENSE vendored Normal file
View file

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -0,0 +1,9 @@
// DO NOT DELETE
// This file is needed for Xcode to properly handle CMake OBJECT Libraries
// From docs (https://cmake.org/cmake/help/latest/command/add_library.html#object-libraries):
//
// ... Some native build systems (such as Xcode) may not like targets that have only object files,
// so consider adding at least one real source file to any target that references $<TARGET_OBJECTS:objlib>.
// Just a dummy symbol to avoid compiler warnings
int srt_object_lib_dummy = 0;

View file

@ -0,0 +1,10 @@
PUBLIC HEADERS
win/syslog_defs.h
#
# These are included by platform_sys.h header contained in ../srtcore/filelist.maf
#
win/unistd.h
SOURCES
win_time.cpp

View file

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

View file

@ -0,0 +1,45 @@
#ifndef INC__WINDOWS_SYSLOG_DEFS_H
#define INC__WINDOWS_SYSLOG_DEFS_H
#define LOG_EMERG 0
#define LOG_ALERT 1
#define LOG_CRIT 2
#define LOG_ERR 3
#define LOG_WARNING 4
#define LOG_NOTICE 5
#define LOG_INFO 6
#define LOG_DEBUG 7
#define LOG_PRIMASK 0x07
#define LOG_PRI(p) ((p) & LOG_PRIMASK)
#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri))
#define LOG_KERN (0<<3)
#define LOG_USER (1<<3)
#define LOG_MAIL (2<<3)
#define LOG_DAEMON (3<<3)
#define LOG_AUTH (4<<3)
#define LOG_SYSLOG (5<<3)
#define LOG_LPR (6<<3)
#define LOG_NEWS (7<<3)
#define LOG_UUCP (8<<3)
#define LOG_CRON (9<<3)
#define LOG_AUTHPRIV (10<<3)
#define LOG_FTP (11<<3)
/* Codes through 15 are reserved for system use */
#define LOG_LOCAL0 (16<<3)
#define LOG_LOCAL1 (17<<3)
#define LOG_LOCAL2 (18<<3)
#define LOG_LOCAL3 (19<<3)
#define LOG_LOCAL4 (20<<3)
#define LOG_LOCAL5 (21<<3)
#define LOG_LOCAL6 (22<<3)
#define LOG_LOCAL7 (23<<3)
#define LOG_NFACILITIES 24
#define LOG_FACMASK 0x03f8
#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3)
#endif

View file

@ -0,0 +1,52 @@
#ifndef _UNISTD_H
#define _UNISTD_H 1
/* This file intended to serve as a drop-in replacement for
* unistd.h on Windows
* Please add functionality as neeeded
*/
#include <stdlib.h>
#include <io.h>
//#include <getopt.h> /* getopt at: https://gist.github.com/ashelly/7776712*/
#include <process.h> /* for getpid() and the exec..() family */
#include <direct.h> /* for _getcwd() and _chdir() */
#define srandom srand
#define random rand
/* Values for the second argument to access.
These may be OR'd together. */
#define R_OK 4 /* Test for read permission. */
#define W_OK 2 /* Test for write permission. */
//#define X_OK 1 /* execute permission - unsupported in windows*/
#define F_OK 0 /* Test for existence. */
#define access _access
#define dup2 _dup2
#define execve _execve
#define ftruncate _chsize
#define unlink _unlink
#define fileno _fileno
#define getcwd _getcwd
#define chdir _chdir
#define isatty _isatty
#define lseek _lseek
/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */
#define ssize_t int
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
/* should be in some equivalent to <sys/types.h> */
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#endif /* unistd.h */

View file

@ -0,0 +1,56 @@
#ifndef INC__WIN_WINTIME
#define INC__WIN_WINTIME
#include <winsock2.h>
#include <windows.h>
// HACK: This include is a workaround for a bug in the MinGW headers
// where pthread.h, which defines _POSIX_THREAD_SAFE_FUNCTIONS,
// has to be included before time.h so that time.h defines
// localtime_r correctly
#include <pthread.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(_MSC_VER)
#define SRTCOMPAT_WINTIME_STATIC_INLINE_DECL static inline
#else
// NOTE: MVC Does not like static inline for C functions in some versions.
// so just use static for MVC.
#define SRTCOMPAT_WINTIME_STATIC_INLINE_DECL static
#endif
#ifndef _TIMEZONE_DEFINED /* also in sys/time.h */
#define _TIMEZONE_DEFINED
struct timezone
{
int tz_minuteswest; /* minutes W of Greenwich */
int tz_dsttime; /* type of dst correction */
};
#endif
void SRTCompat_timeradd(
struct timeval *a, struct timeval *b, struct timeval *result);
SRTCOMPAT_WINTIME_STATIC_INLINE_DECL void timeradd(
struct timeval *a, struct timeval *b, struct timeval *result)
{
SRTCompat_timeradd(a, b, result);
}
int SRTCompat_gettimeofday(
struct timeval* tp, struct timezone* tz);
SRTCOMPAT_WINTIME_STATIC_INLINE_DECL int gettimeofday(
struct timeval* tp, struct timezone* tz)
{
return SRTCompat_gettimeofday(tp, tz);
}
#undef SRTCOMPAT_WINTIME_STATIC_INLINE_DECL
#ifdef __cplusplus
}
#endif
#endif // INC__WIN_WINTIME

View file

@ -0,0 +1,65 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#include "win/wintime.h"
#include <sys/timeb.h>
void SRTCompat_timeradd(struct timeval *a, struct timeval *b, struct timeval *result)
{
result->tv_sec = a->tv_sec + b->tv_sec;
result->tv_usec = a->tv_usec + b->tv_usec;
if (result->tv_usec >= 1000000)
{
result->tv_sec++;
result->tv_usec -= 1000000;
}
}
int SRTCompat_gettimeofday(struct timeval* tp, struct timezone* tz)
{
static LARGE_INTEGER tickFrequency, epochOffset;
// For our first call, use "ftime()", so that we get a time with a proper epoch.
// For subsequent calls, use "QueryPerformanceCount()", because it's more fine-grain.
static int isFirstCall = 1;
LARGE_INTEGER tickNow;
QueryPerformanceCounter(&tickNow);
if (isFirstCall)
{
struct timeb tb;
ftime(&tb);
tp->tv_sec = (long)tb.time;
tp->tv_usec = 1000*tb.millitm;
// Also get our counter frequency:
QueryPerformanceFrequency(&tickFrequency);
// And compute an offset to add to subsequent counter times, so we get a proper epoch:
epochOffset.QuadPart = tb.time*tickFrequency.QuadPart + (tb.millitm*tickFrequency.QuadPart)/1000 - tickNow.QuadPart;
isFirstCall = 0; // for next time
}
else
{
// Adjust our counter time so that we get a proper epoch:
tickNow.QuadPart += epochOffset.QuadPart;
tp->tv_sec = (long) (tickNow.QuadPart / tickFrequency.QuadPart);
tp->tv_usec = (long) (((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart);
}
return 0;
}

249
trunk/3rdparty/srt-1-fit/configure vendored Executable file
View file

@ -0,0 +1,249 @@
#!/usr/bin/tclsh
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2018 Haivision Systems Inc.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This is a general-purpose configure script, which is a user-friendly
# wrapper to call the "cmake" command.
# There are two options that are handled specifically:
#
# --help: show the list of official options
# --prefix: alias to --cmake-install-prefix
# The processing done automatically on all options by default is:
# Every option like:
# --long-c++-option
# --cmake-special-option=ON
# Turns into:
# -DLONG_CXX_OPTION=1
# -DCMAKE_SPECIAL_OPTION=ON
#
# In the configuration file, "configure-data.tcl", you can add
# special processing for options and define explicit options
# in the "::options" dictionary. Explicit options (in contrast
# to "blind" options) have additional properties:
#
# - only those options are mentioned with --help
# - you can pass a value for this option without = character
# - you can specify --disable-option instead of --enable-option=0
#
# In "configure-data.tcl", beside ::options, you can define "preprocess" and
# "postprocess" procedures. In "preprocess", use ::optval array to modify the
# list of options to be processed further. Additionally in "postprocess"
# procedure you can influence directly the options for "cmake" command in
# ::cmakeopt variable (modifying ::optval in "postprocess" is useless).
# The idea is that CMakeLists.txt contains things that are highly
# customizable, but no system or option autodetection AWA "sensible
# defaults" are provided. This is done by this script.
set here [file dirname $argv0]
set options ""
set toolchain_changers ""
source $here/configure-data.tcl
# Update alias with default alias
dict set alias --prefix --cmake-install-prefix=
proc resolve opt {
set type arg
set pos [string first $opt =]
if { $pos == -1 } {
set type bool
set mark ""
} else {
set type arg
set mark [string range $opt $pos+1 end]
set opt [string range $opt 0 $pos-1]
}
set var [string toupper [string map {- _ + x} $opt]]
return [list --$opt $var $type $mark]
}
# Check if a --disable option has its --enable counterpart. If so,
# then just invert the option.
proc resolve_disablers {} {
set enablers ""
set optkeys_len [llength $::optkeys]
for {set pos 0} {$pos < $optkeys_len} {incr pos} {
set opt [lindex $::optkeys $pos]
if { [string match --disable-* $opt] } {
set inverted enable-[string range $opt 10 end]
if { $inverted in [dict keys $::options] } {
lset ::optkeys $pos --$inverted
set val $::optval($opt)
unset ::optval($opt)
if { $val == "" || ![string is boolean $val] } {
set ::optval(--$inverted) 0
} else {
set ::optval(--$inverted) [expr {!$val}]
}
puts "NOTE: $opt changed into --$inverted=$::optval(--$inverted)"
}
}
}
}
foreach {o desc} $options {
lassign [resolve $o] optname optvar opttype optmark
set opt($optname) [list $optvar $opttype $optmark]
set info($optname) $desc
}
if { $argv == "--help" } {
puts stderr "Usage: ./configure \[options\]"
puts stderr "OPTIONS:"
foreach o [lsort [array names opt]] {
lassign $opt($o) unu type mark
set imark ""
if { $mark != "" } {
set imark "=$mark"
}
puts stderr "\t$o$imark - $info($o)"
}
puts stderr "NOTE1: Option list may be incomplete. Refer to variables in CMakeLists.txt"
puts stderr "NOTE2: Non-internal options turn e.g. --enable-c++11 into cmake -DENABLE_CXX11=1"
puts stderr "NOTE3: You can use --disable-x instead of --enable-x=0 for the above options."
exit 1
}
if { [info proc init] != "" } {
init
}
#parray opt
set saveopt ""
set optkeys ""
set dryrun 0
set type ""
foreach a $argv {
if { [info exists val] } { unset val }
if { $saveopt != "" } {
set optval($saveopt) $a
set saveopt ""
continue
}
if { [string range $a 0 1] != "--" } {
error "Unexpected argument '$a'. Options must start with --"
}
if { $a == "--dryrun" } {
set dryrun 1
continue
}
set type ""
if { [string first = $a] != -1 } {
lassign [split $a =] a val
}
if { [dict exists $::alias $a] } {
set aname [dict get $::alias $a]
if { [string first = $aname] != -1 } {
lassign [split $aname =] a aval
set type arg
}
}
if { ![info exists opt($a)] } {
#puts stderr "WARNING: Unknown option: $a"
# But still, simply turn the option to assign-based use.
lassign [resolve [string range $a 2 end]] oname var
if { ![info exists val] && $type == "" } {
set type bool
}
} else {
lassign $opt($a) var type
}
if { $type == "bool" } {
if { ![info exists val] } {
set val 1
}
set optval($a) $val
} elseif { [info exists val] } {
set optval($a) $val
} else {
set saveopt $a
}
lappend optkeys $a
}
if { $saveopt != "" } {
error "Extra unhandled argument: $saveopt"
}
set cmakeopt ""
resolve_disablers
if { [info proc preprocess] != "" } {
preprocess
}
# Check if there were new values added not added to optkeys
foreach a [array names optval] {
if { $a ni $optkeys } {
lappend optkeys $a
}
}
foreach a $optkeys {
if { ![info exists optval($a)] } {
continue ;# user action might have removed it.
}
if { ![info exists opt($a)] } {
#puts stderr "WARNING: Unknown option: $a"
# But still, simply turn the option to assign-based use.
lassign [resolve [string range $a 2 end]] oname var
if { ![info exists val] && $type == "" } {
set type bool
}
} else {
lassign $opt($a) var type
}
set val $optval($a)
lappend cmakeopt "-D$var=$val"
}
if { [info proc postprocess] != "" } {
postprocess
}
#puts "VARSPEC: $cmakeopt"
set cmd [list cmake $here {*}$cmakeopt]
puts "Running: $cmd"
if { !$dryrun} {
if { [catch {exec 2>@stderr >@stdout {*}$cmd} result] } {
puts "CONFIGURE: cmake reported error: $result"
}
} else {
puts "(not really - dry run)"
}

View file

@ -0,0 +1,361 @@
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2018 Haivision Systems Inc.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# API description:
# Expected variables:
# - options: dictionary "option-name" : "description"
# if there's '=' in option name, it expects an argument. Otherwise it's boolean.
# - alias: optional, you can make shortcuts to longer named options. Remember to use = in target name.
#
# Optional procedures:
# - preprocess: run before command-line arguments ($argv) are reviewed
# - postprocess: run after options are reviewed and all data filled in
#
# Available variables in postprocess:
#
# - optval (array): contains all option names with their assigned values
# - cmakeopt (scalar): a list of all options for "cmake" command line
# Options processed here internally, not passed to cmake
set internal_options {
with-compiler-prefix=<prefix> "set C/C++ toolchains <prefix>gcc and <prefix>g++"
with-compiler-type=<name> "compiler type: gcc(default), cc, others simply add ++ for C++"
with-srt-name=<name> "Override srt library name"
with-haicrypt-name=<name> "Override haicrypt library name (if compiled separately)"
}
# Options that refer directly to variables used in CMakeLists.txt
set cmake_options {
cygwin-use-posix "Should the POSIX API be used for cygwin. Ignored if the system isn't cygwin. (default: OFF)"
enable-encryption "Should encryption features be enabled (default: ON)"
enable-c++11 "Should the c++11 parts (srt-live-transmit) be enabled (default: ON)"
enable-apps "Should the Support Applications be Built? (default: ON)"
enable-testing "Should developer testing applications be built (default: OFF)"
enable-c++-deps "Extra library dependencies in srt.pc for C language (default: OFF)"
enable-heavy-logging "Should heavy debug logging be enabled (default: OFF)"
enable-logging "Should logging be enabled (default: ON)"
enable-debug=<0,1,2> "Enable debug mode (0=disabled, 1=debug, 2=rel-with-debug)"
enable-haicrypt-logging "Should logging in haicrypt be enabled (default: OFF)"
enable-inet-pton "Set to OFF to prevent usage of inet_pton when building against modern SDKs (default: ON)"
enable-code-coverage "Enable code coverage reporting (default: OFF)"
enable-monotonic-clock "Enforced clock_gettime with monotonic clock on GC CV /temporary fix for #729/ (default: OFF)"
enable-profile "Should instrument the code for profiling. Ignored for non-GNU compiler. (default: OFF)"
enable-relative-libpath "Should applications contain relative library paths, like ../lib (default: OFF)"
enable-shared "Should libsrt be built as a shared library (default: ON)"
enable-static "Should libsrt be built as a static library (default: ON)"
enable-suflip "Should suflip tool be built (default: OFF)"
enable-getnameinfo "In-logs sockaddr-to-string should do rev-dns (default: OFF)"
enable-unittests "Enable unit tests (default: OFF)"
enable-thread-check "Enable #include <threadcheck.h> that implements THREAD_* macros"
openssl-crypto-library=<filepath> "Path to a library."
openssl-include-dir=<path> "Path to a file."
openssl-ssl-library=<filepath> "Path to a library."
pkg-config-executable=<filepath> "pkg-config executable"
pthread-include-dir=<path> "Path to a file."
pthread-library=<filepath> "Path to a library."
use-busy-waiting "Enable more accurate sending times at a cost of potentially higher CPU load (default: OFF)"
use-gnustl "Get c++ library/headers from the gnustl.pc"
use-enclib "Encryption library to be used: openssl(default), gnutls, mbedtls"
use-gnutls "DEPRECATED. Use USE_ENCLIB=openssl|gnutls|mbedtls instead"
use-openssl-pc "Use pkg-config to find OpenSSL libraries (default: ON)"
use-static-libstdc++ "Should use static rather than shared libstdc++ (default: OFF)"
}
set options $internal_options$cmake_options
# Just example. Available in the system.
set alias {
--prefix --cmake-install-prefix=
}
proc pkg-config args {
return [string trim [exec pkg-config {*}$args]]
}
proc flagval v {
set out ""
foreach o $v {
lappend out [string trim [string range $o 2 en]]
}
return $out
}
set haicrypt_name ""
set srt_name ""
proc preprocess {} {
# Prepare windows basic path info
set ::CYGWIN 0
set e [catch {exec uname -o} res]
# We have Cygwin, if uname -o returns "cygwin" and does not fail.
if { !$e && $res == "Cygwin" } {
set ::CYGWIN 1
puts "CYGWIN DETECTED"
}
set ::HAVE_LINUX [expr {$::tcl_platform(os) == "Linux"}]
set ::HAVE_DARWIN [expr {$::tcl_platform(os) == "Darwin"}]
set ::CYGWIN_USE_POSIX 0
if { "--cygwin-use-posix" in $::optkeys } {
set ::CYGWIN_USE_POSIX 1
}
set ::HAVE_WINDOWS 0
if { $::tcl_platform(platform) == "windows" } {
puts "WINDOWS PLATFORM detected"
set ::HAVE_WINDOWS 1
}
if { $::CYGWIN && !$::CYGWIN_USE_POSIX } {
puts "CYGWIN - MINGW enforced"
# Make Cygwin tools see it right, to compile for MinGW
if { "--with-compiler-prefix" ni $::optkeys } {
set ::optval(--with-compiler-prefix) /bin/x86_64-w64-mingw32-
}
# Extract drive C: information
set drive_path [exec mount -p | tail -1 | cut {-d } -f 1]
set ::DRIVE_C $drive_path/c
set ::HAVE_WINDOWS 1
} else {
# Don't check for Windows, non-Windows parts will not use it.
set ::DRIVE_C C:
}
# Alias to old name --with-gnutls, which enforces using gnutls instead of openssl
if { [info exists ::optval(--with-gnutls)] } {
unset ::optval(--with-gnutls)
set ::optval(--use-enclib) gnutls
puts "WARNING: --with-gnutls is a deprecated alias to --use-enclib=gnutls, please use the latter one"
}
# Alias to old name --use-gnutls, which enforces using gnutls instead of openssl
if { [info exists ::optval(--use-gnutls)] } {
unset ::optval(--use-gnutls)
set ::optval(--use-enclib) gnutls
puts "WARNING: --use-gnutls is a deprecated alias to --use-enclib=gnutls, please use the latter one"
}
if { [info exists ::optval(--with-target-path)] } {
set ::target_path $::optval(--with-target-path)
unset ::optval(--with-target-path)
puts "NOTE: Explicit target path: $::target_path"
}
if { "--with-srt-name" in $::optkeys } {
set ::srt_name $::optval(--with-srt-name)
unset ::optval(--with-srt-name)
}
if { "--with-haicrypt-name" in $::optkeys } {
set ::haicrypt_name $::optval(--with-haicrypt-name)
unset ::optval(--with-haicrypt-name)
}
}
proc GetCompilerCommand {} {
# Expect that the compiler was set through:
# --with-compiler-prefix
# --cmake-c[++]-compiler
# (cmake-toolchain-file will set things up without the need to check things here)
if { [info exists ::optval(--with-compiler-prefix)] } {
set prefix $::optval(--with-compiler-prefix)
return ${prefix}gcc
}
if { [info exists ::optval(--cmake-c-compiler)] } {
return $::optval(--cmake-c-compiler)
}
if { [info exists ::optval(--cmake-c++-compiler)] } {
return $::optval(--cmake-c++-compiler)
}
if { [info exists ::optval(--cmake-cxx-compiler)] } {
return $::optval(--cmake-cxx-compiler)
}
puts "NOTE: Cannot obtain compiler, assuming toolchain file will do what's necessary"
return ""
}
proc postprocess {} {
set iscross 0
# Check if there was any option that changed the toolchain. If so, don't apply any autodetection-based toolchain change.
set all_options [array names ::optval]
set toolchain_changed no
foreach changer {
--with-compiler-prefix
--cmake-c-compiler
--cmake-c++-compiler
--cmake-cxx-compiler
--cmake-toolchain-file
} {
if { $changer in $all_options } {
puts "NOTE: toolchain changed by '$changer' option"
set toolchain_changed yes
break
}
}
set cygwin_posix 0
if { "--cygwin-use-posix" in $all_options } {
# Will enforce OpenSSL autodetection
set cygwin_posix 1
}
if { $toolchain_changed } {
# Check characteristics of the compiler - in particular, whether the target is different
# than the current target.
set compiler_path ""
set cmd [GetCompilerCommand]
if { $cmd != "" } {
set gcc_version [exec $cmd -v 2>@1]
set target ""
set compiler_path [file dirname $cmd]
foreach l [split $gcc_version \n] {
if { [string match Target:* $l] } {
set target [lindex $l 1] ;# [0]Target: [1]x86_64-some-things-further
set target_platform [lindex [split $target -] 0] ;# [0]x86_64 [1]redhat [2]linux
break
}
}
if { $target_platform == "" } {
puts "NOTE: can't obtain target from gcc -v: $l"
} else {
if { $target_platform != $::tcl_platform(machine) } {
puts "NOTE: foreign target type detected ($target)" ;# - setting CROSSCOMPILING flag"
#lappend ::cmakeopt "-DHAVE_CROSSCOMPILER=1"
set iscross 1
}
}
}
}
if { $::srt_name != "" } {
lappend ::cmakeopt "-DTARGET_srt=$::srt_name"
}
if { $::haicrypt_name != "" } {
lappend ::cmakeopt "-DTARGET_haicrypt=$::haicrypt_name"
}
set have_openssl 0
if { [lsearch -glob $::optkeys --openssl*] != -1 } {
set have_openssl 1
}
set have_gnutls 0
if { [lsearch -glob $::optkeys --use-gnutls] != -1 } {
set have_gnutls 1
}
if { $have_openssl && $have_gnutls } {
puts "NOTE: SSL library is exclusively selectable. Thus, --use-gnutls option will be ignored"
set have_gnutls 0
}
if { $have_gnutls } {
lappend ::cmakeopt "-DUSE_GNUTLS=ON"
}
if {$iscross} {
proc check-target-path {path} {
puts "Checking path '$path'"
if { [file isdir $path]
&& [file isdir $path/bin]
&& [file isdir $path/include]
&& ([file isdir $path/lib] || [file isdir $path/lib64]) } {
return yes
}
return no
}
if { ![info exists ::target_path] } {
# Try to autodetect the target path by having the basic 3 directories.
set target_path ""
set compiler_prefix [file dirname $compiler_path] ;# strip 'bin' directory
puts "NOTE: no --with-target-path found, will try to autodetect at $compiler_path"
foreach path [list $compiler_path $compiler_prefix/$target] {
if { [check-target-path $path] } {
set target_path $path
puts "NOTE: target path detected: $target_path"
break
}
}
if { $target_path == "" } {
puts "ERROR: Can't determine compiler's platform files root path (using compiler command path). Specify --with-target-path."
exit 1
}
} else {
set target_path $::target_path
# Still, check if correct.
if { ![check-target-path $target_path] } {
puts "ERROR: path in --with-target-path does not contain typical subdirectories"
exit 1
}
puts "NOTE: Using explicit target path: $target_path"
}
# Add this for cmake, should it need for something
lappend ::cmakeopt "-DCMAKE_PREFIX_PATH=$target_path"
# Add explicitly the path for pkg-config
# which lib
if { [file isdir $target_path/lib64/pkgconfig] } {
set ::env(PKG_CONFIG_PATH) $target_path/lib64/pkgconfig
puts "PKG_CONFIG_PATH: Found pkgconfig in lib64 for '$target_path' - using it"
} elseif { [file isdir $target_path/lib/pkgconfig] } {
set ::env(PKG_CONFIG_PATH) $target_path/lib/pkgconfig
puts "PKG_CONFIG_PATH: Found pkgconfig in lib for '$target_path' - using it"
} else {
puts "PKG_CONFIG_PATH: NOT changed, no pkgconfig in '$target_path'"
}
# Otherwise don't set PKG_CONFIG_PATH and we'll see.
}
if { $::HAVE_DARWIN && !$toolchain_changed} {
if { $have_gnutls } {
# Use gnutls explicitly, as found in brew
set er [catch {exec brew info gnutls} res]
if { $er } {
error "Cannot find gnutls in brew"
}
} else {
# ON Darwin there's a problem with linking against the Mac-provided OpenSSL.
# This must use brew-provided OpenSSL.
#
if { !$have_openssl } {
set er [catch {exec brew info openssl} res]
if { $er } {
error "You must have OpenSSL installed from 'brew' tool. The standard Mac version is inappropriate."
}
lappend ::cmakeopt "-DOPENSSL_INCLUDE_DIR=/usr/local/opt/openssl/include"
lappend ::cmakeopt "-DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib/libcrypto.a"
}
}
}
}

View file

@ -0,0 +1,22 @@
#ifndef INC__CRYSPR_CONFIG_H
#define INC__CRYSPR_CONFIG_H
// Size of the single block for encryption.
// This might need tweaking for particular implementation library.
#define CRYSPR_AESBLKSZ 16 /* 128-bit */
#if defined(USE_OPENSSL)
#include "cryspr-openssl.h"
#define cryspr4SRT() crysprOpenSSL()
#elif defined(USE_GNUTLS)
#include "cryspr-gnutls.h"
#define cryspr4SRT() crysprGnuTLS()
#elif defined(USE_MBEDTLS)
#include "cryspr-mbedtls.h"
#define cryspr4SRT() crysprMbedtls()
#else
#error Cryspr implementation not selected. Please define USE_* + OPENSSL/GNUTLS/MBEDTLS.
#endif
#endif

View file

@ -0,0 +1,178 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#include "hcrypt.h"
#include <string.h>
typedef struct tag_crysprGnuTLS_AES_cb {
CRYSPR_cb ccb; /* CRYSPR control block */
/* Add other cryptolib specific data here */
} crysprGnuTLS_cb;
int crysprGnuTLS_Prng(unsigned char *rn, int len)
{
return(gnutls_rnd(GNUTLS_RND_KEY,(rn),(len)) < 0 ? -1 : 0);
}
int crysprGnuTLS_AES_SetKey(
bool bEncrypt, /* true:encrypt key, false:decrypt key*/
const unsigned char *kstr, /* key string */
size_t kstr_len, /* kstr length in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
{
if (bEncrypt) { /* Encrypt key */
if (!(kstr_len == 16 || kstr_len == 24 || kstr_len == 32)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) bad length\n");
return -1;
}
aes_set_encrypt_key (aes_key, kstr_len, kstr);
} else { /* Decrypt key */
if (!(kstr_len == 16 || kstr_len == 24 || kstr_len == 32)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_decrypt_key(kek) bad length\n");
return -1;
}
aes_set_decrypt_key (aes_key, kstr_len, kstr);
}
return(0);
}
int crysprGnuTLS_AES_EcbCipher( /* AES Electronic Codebook cipher*/
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */
const unsigned char *indata,/* src (clear text)*/
size_t inlen, /* length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen) /* dst len */
{
int nblk = inlen/CRYSPR_AESBLKSZ;
int nmore = inlen%CRYSPR_AESBLKSZ;
int i;
if (bEncrypt) {
/* Encrypt packet payload, block by block, in output buffer */
for (i=0; i<nblk; i++){
aes_encrypt(aes_key, CRYSPR_AESBLKSZ, &out_txt[(i*CRYSPR_AESBLKSZ)], &indata[(i*CRYSPR_AESBLKSZ)]);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
unsigned char intxt[CRYSPR_AESBLKSZ];
memcpy(intxt, &indata[(nblk*CRYSPR_AESBLKSZ)], nmore);
memset(intxt+nmore, 0, CRYSPR_AESBLKSZ-nmore);
aes_encrypt(aes_key, CRYSPR_AESBLKSZ, &out_txt[(nblk*CRYSPR_AESBLKSZ)], intxt);
nblk++;
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
} else { /* Decrypt */
for (i=0; i<nblk; i++){
aes_decrypt(aes_key, CRYSPR_AESBLKSZ, &out_txt[(i*CRYSPR_AESBLKSZ)], &indata[(i*CRYSPR_AESBLKSZ)]);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
//shall not happens in decrypt
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
}
return 0;
}
int crysprGnuTLS_AES_CtrCipher( /* AES-CTR128 Encryption */
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* src length */
unsigned char *out_txt) /* dest buffer[inlen] */
{
(void)bEncrypt; /* CTR mode encrypt for both encryption and decryption */
ctr_crypt (aes_key, /* ctx */
(nettle_cipher_func*)aes_encrypt, /* nettle_cipher_func */
CRYSPR_AESBLKSZ, /* cipher blocksize */
iv, /* iv */
inlen, /* length */
out_txt, /* dest */
indata); /* src */
return 0;
}
#ifdef CRYSPR_HAS_PBKDF2
/*
* Password-based Key Derivation Function
*/
int crysprGnuTLS_KmPbkdf2(
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key buffer[key_len]*/
{
(void)cryspr_cb;
pbkdf2_hmac_sha1(passwd_len,(const uint8_t *)passwd,itr,salt_len,salt,key_len,out);
return(0);
}
#endif /* CRYSPR_HAS_PBKDF2 */
static CRYSPR_methods crysprGnuTLS_methods;
CRYSPR_methods *crysprGnuTLS(void)
{
if(NULL == crysprGnuTLS_methods.open) {
crysprInit(&crysprGnuTLS_methods); /* Set default methods */
/* CryptoLib Primitive API */
crysprGnuTLS_methods.prng = crysprGnuTLS_Prng;
crysprGnuTLS_methods.aes_set_key = crysprGnuTLS_AES_SetKey;
#if CRYSPR_HAS_AESCTR
crysprGnuTLS_methods.aes_ctr_cipher = crysprGnuTLS_AES_CtrCipher;
#endif
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
/* AES-ECB only required if cryspr has no AES-CTR or no AES KeyWrap */
crysprGnuTLS_methods.aes_ecb_cipher = crysprGnuTLS_AES_EcbCipher;
#endif
#if !CRYSPR_HAS_PBKDF2
crysprGnuTLS_methods.sha1_msg_digest= crysprGnuTLS_SHA1_MsgDigest; //Onl required if using generic KmPbkdf2
#endif
//--Crypto Session (Top API)
// crysprGnuTLS_methods.open =
// crysprGnuTLS_methods.close =
//--Keying material (km) encryption
#if CRYSPR_HAS_PBKDF2
crysprGnuTLS_methods.km_pbkdf2 = crysprGnuTLS_KmPbkdf2;
#endif
// crysprGnuTLS_methods.km_setkey =
// crysprGnuTLS_methods.km_wrap =
// crysprGnuTLS_methods.km_unwrap =
//--Media stream (ms) encryption
// crysprGnuTLS_methods.ms_setkey =
// crysprGnuTLS_methods.ms_encrypt =
// crysprGnuTLS_methods.ms_decrypt =
}
return(&crysprGnuTLS_methods);
}

View file

@ -0,0 +1,61 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#ifndef CRYSPR_GNUTLS_H
#define CRYSPR_GNUTLS_H
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h> //gnutls_rnd()
#include <nettle/aes.h> //has AES cipher
#include <nettle/ctr.h> //has CTR cipher mode
#include <nettle/pbkdf2.h> //has Password-based Key Derivation Function 2
//#include <nettle/sha1.h> //No need for sha1 since we have pbkdf2
/* Define CRYSPR_HAS_AESCTR to 1 if this CRYSPR has AESCTR cipher mode
if not set it 0 to use enable CTR cipher mode implementation using ECB cipher mode
and provide the aes_ecb_cipher method.
*/
#define CRYSPR_HAS_AESCTR 1
/* Define CRYSPR_HAS_AESKWRAP to 1 if this CRYSPR has AES Key Wrap
if not set to 0 to enable default/fallback crysprFallback_AES_WrapKey/crysprFallback_AES_UnwrapKey methods
and provide the aes_ecb_cipher method .
*/
#define CRYSPR_HAS_AESKWRAP 0
/* Define CRYSPR_HAS_PBKDF2 to 1 if this CRYSPR has SHA1-HMAC Password-based Key Derivaion Function 2
if not set to 0 to enable not-yet-implemented/fallback crysprFallback.km_pbkdf2 method
and provide the sha1_msg_digest method.
*/
#define CRYSPR_HAS_PBKDF2 1
/*
#define CRYSPR_AESCTX to the CRYSPR specifix AES key context object.
This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK
It is set from hte keystring through CRYSPR_methods.aes_set_key and passed
to CRYSPR_methods.aes_XXX.
*/
typedef struct aes_ctx CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */
struct tag_CRYSPR_methods *crysprGnuTLS(void);
#endif /* CRYSPR_GNUTLS_H */

View file

@ -0,0 +1,235 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#include "hcrypt.h"
#include <string.h>
#include <mbedtls/aes.h>
#include <mbedtls/md.h>
#include <mbedtls/pkcs5.h>
#include <mbedtls/entropy.h>
// Static members of cryspr::mbedtls class.
static mbedtls_ctr_drbg_context crysprMbedtls_ctr_drbg;
static mbedtls_entropy_context crysprMbedtls_entropy;
static mbedtls_md_context_t crysprMbedtls_mdctx;
typedef struct tag_crysprGnuTLS_AES_cb {
CRYSPR_cb ccb; /* CRYSPR control block */
/* Add other cryptolib specific data here */
} crysprMbedtls_cb;
int crysprMbedtls_Prng(unsigned char *rn, int len)
{
int ret = mbedtls_ctr_drbg_random( &crysprMbedtls_ctr_drbg, rn, len );
if (ret != 0)
{
return -1;
}
return 0;
}
int crysprMbedtls_AES_SetKey(
bool bEncrypt, /* true:encrypt key, false:decrypt key*/
const unsigned char *kstr, /* key string */
size_t kstr_len, /* kstr length in bytes (16, 24, or 32 bytes, for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
{
if (!(kstr_len == 16 || kstr_len == 24 || kstr_len == 32)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) bad length\n");
return -1;
}
int ret;
// mbedtls uses the "bits" convention (128, 192, 254), just like openssl.
// kstr_len is in "bytes" convention (16, 24, 32).
if (bEncrypt) { /* Encrypt key */
ret = mbedtls_aes_setkey_enc(aes_key, kstr, kstr_len*8);
} else { /* Decrypt key */
ret = mbedtls_aes_setkey_dec(aes_key, kstr, kstr_len*8);
}
return ret == 0 ? 0 : -1;
}
int crysprMbedtls_AES_EcbCipher( /* AES Electronic Codebook cipher*/
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */
const unsigned char *indata,/* src (clear text)*/
size_t inlen, /* length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen) /* dst len */
{
int nblk = inlen/CRYSPR_AESBLKSZ;
int nmore = inlen%CRYSPR_AESBLKSZ;
int i;
if (bEncrypt) {
/* Encrypt packet payload, block by block, in output buffer */
for (i = 0; i < nblk; i++) {
// NOTE: CRYSPR_AESBLKSZ is implicitly the ONLY POSSIBLE
// size of the block.
mbedtls_aes_crypt_ecb(aes_key, MBEDTLS_AES_ENCRYPT,
&indata[(i*CRYSPR_AESBLKSZ)],
&out_txt[(i*CRYSPR_AESBLKSZ)]);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
unsigned char intxt[CRYSPR_AESBLKSZ];
memcpy(intxt, &indata[(nblk*CRYSPR_AESBLKSZ)], nmore);
memset(intxt+nmore, 0, CRYSPR_AESBLKSZ-nmore);
mbedtls_aes_crypt_ecb(aes_key, MBEDTLS_AES_ENCRYPT,
intxt,
&out_txt[(nblk*CRYSPR_AESBLKSZ)]);
nblk++;
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
} else { /* Decrypt */
for (i=0; i<nblk; i++){
mbedtls_aes_crypt_ecb(aes_key, MBEDTLS_AES_DECRYPT,
&indata[(i*CRYSPR_AESBLKSZ)],
&out_txt[(i*CRYSPR_AESBLKSZ)]);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
//shall not happens in decrypt
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
}
return 0;
}
int crysprMbedtls_AES_CtrCipher( /* AES-CTR128 Encryption */
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* src length */
unsigned char *out_txt) /* dest buffer[inlen] */
{
unsigned char ctr[CRYSPR_AESBLKSZ];
size_t blk_ofs = 0;
(void)bEncrypt; /* CTR mode encrypt for both encryption and decryption */
memset(&ctr[0], 0, sizeof(ctr));
mbedtls_aes_crypt_ctr(aes_key,
inlen,
&blk_ofs,
iv,
ctr,
indata,
out_txt);
return 0;
}
/*
* Password-based Key Derivation Function
*/
int crysprMbedtls_KmPbkdf2(
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key buffer[key_len]*/
{
(void)cryspr_cb;
int ret = mbedtls_pkcs5_pbkdf2_hmac(&crysprMbedtls_mdctx,
(unsigned char*)passwd, passwd_len, salt, salt_len,
itr, key_len, out);
if (ret == 0)
return 0;
// XXX report error, log?
return -1;
}
static CRYSPR_methods crysprMbedtls_methods;
CRYSPR_methods *crysprMbedtls(void)
{
if (crysprMbedtls_methods.open)
return(&crysprMbedtls_methods);
crysprInit(&crysprMbedtls_methods); /* Set default methods */
/* CryptoLib Primitive API */
crysprMbedtls_methods.prng = crysprMbedtls_Prng;
crysprMbedtls_methods.aes_set_key = crysprMbedtls_AES_SetKey;
#if CRYSPR_HAS_AESCTR
crysprMbedtls_methods.aes_ctr_cipher = crysprMbedtls_AES_CtrCipher;
#endif
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
/* AES-ECB only required if cryspr has no AES-CTR or no AES KeyWrap */
crysprMbedtls_methods.aes_ecb_cipher = crysprMbedtls_AES_EcbCipher;
#endif
#if !CRYSPR_HAS_PBKDF2
crysprMbedtls_methods.sha1_msg_digest= crysprMbedtls_SHA1_MsgDigest; //Onl required if using generic KmPbkdf2
#endif
//--Crypto Session (Top API)
// crysprMbedtls_methods.open =
// crysprMbedtls_methods.close =
//--Keying material (km) encryption
crysprMbedtls_methods.km_pbkdf2 = crysprMbedtls_KmPbkdf2;
// crysprMbedtls_methods.km_setkey =
// crysprMbedtls_methods.km_wrap =
// crysprMbedtls_methods.km_unwrap =
//--Media stream (ms) encryption
// crysprMbedtls_methods.ms_setkey =
// crysprMbedtls_methods.ms_encrypt =
// crysprMbedtls_methods.ms_decrypt =
// Initialize extra static data
mbedtls_entropy_init( &crysprMbedtls_entropy );
mbedtls_ctr_drbg_init( &crysprMbedtls_ctr_drbg );
int ret;
if ( (ret = mbedtls_ctr_drbg_seed( &crysprMbedtls_ctr_drbg, mbedtls_entropy_func,
&crysprMbedtls_entropy, NULL, 0)) != 0 )
{
HCRYPT_LOG(LOG_CRIT, "crysprMbedtls: STATIC INIT FAILED on mbedtls_ctr_drbg_init: -0x%04x", -ret);
return NULL;
}
// Ok, mbedtls with all flexibility you couldn't make it more complicated.
mbedtls_md_init(&crysprMbedtls_mdctx);
const mbedtls_md_info_t* ifo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
const int yes_use_hmac = 1;
mbedtls_md_setup(&crysprMbedtls_mdctx, ifo, yes_use_hmac);
return(&crysprMbedtls_methods);
}

View file

@ -0,0 +1,63 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#ifndef CRYSPR_GNUTLS_H
#define CRYSPR_GNUTLS_H
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/aes.h>
/* Define CRYSPR_HAS_AESCTR to 1 if this CRYSPR has AESCTR cipher mode
if not set it 0 to use enable CTR cipher mode implementation using ECB cipher mode
and provide the aes_ecb_cipher method.
*/
#define CRYSPR_HAS_AESCTR 1
/* Define CRYSPR_HAS_AESKWRAP to 1 if this CRYSPR has AES Key Wrap
if not set to 0 to enable default/fallback crysprFallback_AES_WrapKey/crysprFallback_AES_UnwrapKey methods
and provide the aes_ecb_cipher method .
*/
#define CRYSPR_HAS_AESKWRAP 0
/* Define CRYSPR_HAS_PBKDF2 to 1 if this CRYSPR has SHA1-HMAC Password-based Key Derivaion Function 2
if not set to 0 to enable not-yet-implemented/fallback crysprFallback.km_pbkdf2 method
and provide the sha1_msg_digest method.
*/
#define CRYSPR_HAS_PBKDF2 1
// mbedtls uses in the enc/dec functions 16-byte blocks
// for xcryption. This is not marked by any constant. See
// e.g. <mbedtls/aes.h>, mbedtls_aes_crypt_ecb signature.
#if CRYSPR_AESBLKSZ != 16
#error mbedtls requires AES single block size 16 bytes, implicitly.
#endif
/*
#define CRYSPR_AESCTX to the CRYSPR specifix AES key context object.
This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK
It is set from hte keystring through CRYSPR_methods.aes_set_key and passed
to CRYSPR_methods.aes_XXX.
*/
typedef struct mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */
struct tag_CRYSPR_methods *crysprMbedtls(void);
#endif /* CRYSPR_GNUTLS_H */

View file

@ -0,0 +1,218 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-26 (jdube)
OpenSSL CRYSPR/4SRT (CRYypto Service PRovider for SRT).
*****************************************************************************/
#include "hcrypt.h"
#include <string.h>
typedef struct tag_crysprOpenSSL_AES_cb {
CRYSPR_cb ccb;
/* Add cryptolib specific data here */
} crysprOpenSSL_cb;
int crysprOpenSSL_Prng(unsigned char *rn, int len)
{
return(RAND_bytes(rn, len) <= 0 ? -1 : 0);
}
int crysprOpenSSL_AES_SetKey(
bool bEncrypt, /* true Enxcrypt key, false: decrypt */
const unsigned char *kstr, /* key sttring*/
size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* CRYpto Service PRovider AES Key context */
{
if (bEncrypt) { /* Encrypt key */
if (AES_set_encrypt_key(kstr, kstr_len * 8, aes_key)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) failed\n");
return(-1);
}
} else { /* Decrypt key */
if (AES_set_decrypt_key(kstr, kstr_len * 8, aes_key)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_decrypt_key(kek) failed\n");
return(-1);
}
}
return(0);
}
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
int crysprOpenSSL_AES_EcbCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CRYpto Service PRovider AES Key context */
const unsigned char *indata,/* src (clear text if encrypt, cipher text otherwise)*/
size_t inlen, /* indata length */
unsigned char *out_txt, /* dst (cipher text if encrypt, clear text otherwise) */
size_t *outlen) /* in/out dst len */
{
int nblk = inlen/CRYSPR_AESBLKSZ;
int nmore = inlen%CRYSPR_AESBLKSZ;
size_t outsiz = (outlen ? *outlen : 0);
int i;
if (outsiz % CRYSPR_AESBLKSZ) return(-1); /* output buf size must be a multiple of AES block size (16) */
if (bEncrypt) {
if (outsiz > 16 && outsiz < (nblk+nmore)*CRYSPR_AESBLKSZ) return(-1); /* output buf size must have room for PKCS7 padding */
/* Encrypt packet payload, block by block, in output buffer */
for (i=0; i<nblk; i++){
AES_ecb_encrypt(&indata[(i*CRYSPR_AESBLKSZ)],
&out_txt[(i*CRYSPR_AESBLKSZ)], aes_key, AES_ENCRYPT);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
unsigned char intxt[CRYSPR_AESBLKSZ];
/* PKCS7 padding: padding value is number of bytes padded */
memcpy(intxt, &indata[(nblk*CRYSPR_AESBLKSZ)], nmore);
memset(intxt+nmore, CRYSPR_AESBLKSZ-nmore, CRYSPR_AESBLKSZ-nmore);
AES_ecb_encrypt(intxt, &out_txt[(nblk*CRYSPR_AESBLKSZ)], aes_key, AES_ENCRYPT);
nblk++;
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
} else { /* Decrypt */
for (i=0; i<nblk; i++){
AES_ecb_encrypt(&indata[(i*CRYSPR_AESBLKSZ)],
&out_txt[(i*CRYSPR_AESBLKSZ)], aes_key, AES_DECRYPT);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
//shall not happens in decrypt
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
}
return 0;
}
#endif /* !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP) */
int crysprOpenSSL_AES_CtrCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CRYpto Service PRovider AES Key context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* length */
unsigned char *out_txt) /* dest */
{
unsigned char ctr[CRYSPR_AESBLKSZ];
unsigned blk_ofs = 0;
(void)bEncrypt; /* CTR mode encrypt for both encryption and decryption */
memset(&ctr[0], 0, sizeof(ctr));
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL))
CRYPTO_ctr128_encrypt(indata, out_txt,
inlen, aes_key, iv, ctr, &blk_ofs, (block128_f) AES_encrypt);
#else
AES_ctr128_encrypt(indata, out_txt,
inlen, aes_key, iv, ctr, &blk_ofs);
#endif
return 0;
}
/*
* Password-based Key Derivation Function
*/
int crysprOpenSSL_KmPbkdf2(
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key */
{
(void)cryspr_cb;
int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd,passwd_len,salt,salt_len,itr,key_len,out);
return(rc == 1? 0 : -1);
}
#if CRYSPR_HAS_AESKWRAP
int crysprOpenSSL_KmWrap(CRYSPR_cb *cryspr_cb,
unsigned char *wrap,
const unsigned char *sek,
unsigned int seklen)
{
crysprOpenSSL_cb *aes_data = (crysprOpenSSL_cb *)cryspr_cb;
AES_KEY *kek = &aes_data->ccb.aes_kek; //key encrypting key
return(((seklen + HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_wrap_key(kek, NULL, wrap, sek, seklen)) ? 0 : -1);
}
int crysprOpenSSL_KmUnwrap(
CRYSPR_cb *cryspr_cb,
unsigned char *sek, //Stream encrypting key
const unsigned char *wrap,
unsigned int wraplen)
{
crysprOpenSSL_cb *aes_data = (crysprOpenSSL_cb *)cryspr_cb;
AES_KEY *kek = &aes_data->ccb.aes_kek; //key encrypting key
return(((wraplen - HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_unwrap_key(kek, NULL, sek, wrap, wraplen)) ? 0 : -1);
}
#endif /*CRYSPR_HAS_AESKWRAP*/
static CRYSPR_methods crysprOpenSSL_methods;
CRYSPR_methods *crysprOpenSSL(void)
{
if(NULL == crysprOpenSSL_methods.open) {
crysprInit(&crysprOpenSSL_methods); //Default/fallback methods
crysprOpenSSL_methods.prng = crysprOpenSSL_Prng;
//--CryptoLib Primitive API-----------------------------------------------
crysprOpenSSL_methods.aes_set_key = crysprOpenSSL_AES_SetKey;
#if CRYSPR_HAS_AESCTR
crysprOpenSSL_methods.aes_ctr_cipher = crysprOpenSSL_AES_CtrCipher;
#endif
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
/* AES-ECB only required if cryspr has no AES-CTR and no AES KeyWrap */
/* OpenSSL has both AESCTR and AESKWRP and the AESECB wrapper is only used
to test the falback methods */
crysprOpenSSL_methods.aes_ecb_cipher = crysprOpenSSL_AES_EcbCipher;
#endif
#if !CRYSPR_HAS_PBKDF2
crysprOpenSSL_methods.sha1_msg_digest= NULL; //Required to use eventual default/fallback KmPbkdf2
#endif
//--Crypto Session API-----------------------------------------
// crysprOpenSSL_methods.open =
// crysprOpenSSL_methods.close =
//--Keying material (km) encryption
#if CRYSPR_HAS_PBKDF2
crysprOpenSSL_methods.km_pbkdf2 = crysprOpenSSL_KmPbkdf2;
#else
#error There is no default/fallback method for PBKDF2
#endif
// crysprOpenSSL_methods.km_setkey =
#if CRYSPR_HAS_AESKWRAP
crysprOpenSSL_methods.km_wrap = crysprOpenSSL_KmWrap;
crysprOpenSSL_methods.km_unwrap = crysprOpenSSL_KmUnwrap;
#endif
//--Media stream (ms) encryption
// crysprOpenSSL_methods.ms_setkey =
// crysprOpenSSL_methods.ms_encrypt =
// crysprOpenSSL_methods.ms_decrypt =
}
return(&crysprOpenSSL_methods);
}

View file

@ -0,0 +1,65 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-26 (jdube)
OpenSSL Direct AES CRYSPR/4SRT (CRYypto Service PRovider for SRT).
*****************************************************************************/
#ifndef CRYSPR_OPENSSL_H
#define CRYSPR_OPENSSL_H
#include <openssl/evp.h> /* PKCS5_xxx() */
#include <openssl/aes.h> /* AES_xxx() */
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL))
# include <openssl/modes.h> /* CRYPTO_xxx() */
#endif
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */
/* Define CRYSPR_HAS_AESCTR to 1 if this CRYSPR has AESCTR cipher mode
if not set it 0 to use enable CTR cipher mode implementation using ECB cipher mode
and provide the aes_ecb_cipher method.
*/
#define CRYSPR_HAS_AESCTR 1
/* Define CRYSPR_HAS_AESKWRAP to 1 if this CRYSPR has AES Key Wrap
if not set to 0 to enable default/fallback crysprFallback_AES_WrapKey/crysprFallback_AES_UnwrapKey methods
and provide the aes_ecb_cipher method .
*/
#if (OPENSSL_VERSION_NUMBER < 0x0090808fL) //0.9.8h
#define CRYSPR_HAS_AESKWRAP 0
#else
#define CRYSPR_HAS_AESKWRAP 1
#endif
/* Define CRYSPR_HAS_PBKDF2 to 1 if this CRYSPR has SHA1-HMAC Password-based Key Derivaion Function 2
if not set to 0 to enable not-yet-implemented/fallback crysprFallback.km_pbkdf2 method
and provide the sha1_msg_digest method.
*/
#define CRYSPR_HAS_PBKDF2 1 /* Define to 1 if CRYSPR has Password-based Key Derivaion Function 2 */
/*
#define CRYSPR_AESCTX to the CRYSPR specifix AES key context object.
This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK
It is set from hte keystring through CRYSPR_methods.aes_set_key and passed
to CRYSPR_methods.aes_*.
*/
typedef AES_KEY CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */
struct tag_CRYSPR_methods *crysprOpenSSL(void);
#endif /* CRYSPR_OPENSSL_H */

View file

@ -0,0 +1,709 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-28 (jdube)
CRYSPR/4SRT Initial implementation.
*****************************************************************************/
#include "hcrypt.h"
#include "cryspr.h"
#include <stdlib.h>
#include <string.h>
int crysprStub_Prng(unsigned char *rn, int len)
{
(void)rn;
(void)len;
return(0);
}
int crysprStub_AES_SetKey(
bool bEncrypt, /* true Enxcrypt key, false: decrypt */
const unsigned char *kstr, /* key sttring*/
size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
{
(void)bEncrypt;
(void)kstr;
(void)kstr_len;
(void)aes_key;
return(0);
}
int crysprStub_AES_EcbCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* AES context */
const unsigned char *indata,/* src (clear text)*/
size_t inlen, /* length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen) /* dst len */
{
(void)bEncrypt;
(void)aes_key;
(void)indata;
(void)inlen;
(void)out_txt;
(void)outlen;
return -1;
}
int crysprStub_AES_CtrCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* AES context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* length */
unsigned char *out_txt) /* dest */
{
(void)bEncrypt;
(void)aes_key;
(void)iv;
(void)indata;
(void)inlen;
(void)out_txt;
return(-1);
}
unsigned char *crysprStub_SHA1_MsgDigest(
const unsigned char *m, /* in: message */
size_t m_len, /* message length */
unsigned char *md) /* out: message digest buffer *160 bytes */
{
(void)m;
(void)m_len;
(void)md;
return(NULL);//return md;
}
/*
* Password-based Key Derivation Function
*/
int crysprStub_KmPbkdf2(
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key */
{
(void)cryspr_cb;
(void)passwd;
(void)passwd_len;
(void)salt;
(void)salt_len;
(void)itr;
(void)key_len;
(void)out;
/* >>Todo:
* develop PBKDF2 using SHA1 primitive cryspr_cb->cryspr->sha1_msg_digest() for cryptolibs not providing it
*/
return(-1);
}
static int crysprFallback_KmSetKey(CRYSPR_cb *cryspr_cb, bool bWrap, const unsigned char *kek, size_t kek_len)
{
CRYSPR_AESCTX *aes_kek = &cryspr_cb->aes_kek;
if (cryspr_cb->cryspr->aes_set_key(bWrap, kek, kek_len, aes_kek)) {
HCRYPT_LOG(LOG_ERR, "AES_set_%s_key(kek) failed\n", bWrap? "encrypt": "decrypt");
return(-1);
}
return(0);
}
/*
* AES_wrap_key()/AES_unwrap_key() introduced in openssl 0.9.8h
* Here is an implementation using AES native API for cryspr not providing it.
*/
static const unsigned char default_iv[] = {
0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
};
int crysprFallback_AES_WrapKey(CRYSPR_cb *cryspr_cb,
unsigned char *out,
const unsigned char *in,
unsigned int inlen)
{
unsigned char *A, B[16], *R;
const unsigned char *iv = default_iv;
unsigned int i, j, t;
if ((inlen & 0x7) || (inlen < 8))
return -1;
A = B;
t = 1;
memcpy(out + 8, in, inlen);
memcpy(A, iv, 8);
for (j = 0; j < 6; j++)
{
R = out + 8;
for (i = 0; i < inlen; i += 8, t++, R += 8)
{
memcpy(B + 8, R, 8);
{
size_t outlen = 16;
cryspr_cb->cryspr->aes_ecb_cipher(true, &cryspr_cb->aes_kek, B, 16, B, &outlen);
}
A[7] ^= (unsigned char)(t & 0xff);
if (t > 0xff)
{
A[6] ^= (unsigned char)((t >> 8) & 0xff);
A[5] ^= (unsigned char)((t >> 16) & 0xff);
A[4] ^= (unsigned char)((t >> 24) & 0xff);
}
memcpy(R, B + 8, 8);
}
}
memcpy(out, A, 8);
return 0;
}
int crysprFallback_AES_UnwrapKey(CRYSPR_cb *cryspr_cb,
unsigned char *out,
const unsigned char *in,
unsigned int inlen)
{
unsigned char *A, B[16], *R;
const unsigned char *iv = default_iv;
unsigned int i, j, t;
inlen -= 8;
if (inlen & 0x7)
return -1;
if (inlen < 8)
return -1;
A = B;
t = 6 * (inlen >> 3);
memcpy(A, in, 8);
memcpy(out, in + 8, inlen);
for (j = 0; j < 6; j++)
{
R = out + inlen - 8;
for (i = 0; i < inlen; i += 8, t--, R -= 8)
{
A[7] ^= (unsigned char)(t & 0xff);
if (t > 0xff)
{
A[6] ^= (unsigned char)((t >> 8) & 0xff);
A[5] ^= (unsigned char)((t >> 16) & 0xff);
A[4] ^= (unsigned char)((t >> 24) & 0xff);
}
memcpy(B + 8, R, 8);
{
size_t outlen = 16;
cryspr_cb->cryspr->aes_ecb_cipher(false, &cryspr_cb->aes_kek, B, 16, B, &outlen);
}
memcpy(R, B + 8, 8);
}
}
if (memcmp(A, iv, 8))
{
memset(out, 0, inlen);
return -1;
}
return 0;
}
static unsigned char *_crysprFallback_GetOutbuf(CRYSPR_cb *cryspr_cb, size_t pfx_len, size_t out_len)
{
unsigned char *out_buf;
if ((pfx_len + out_len) > (cryspr_cb->outbuf_siz - cryspr_cb->outbuf_ofs)) {
/* Not enough room left, circle buffers */
cryspr_cb->outbuf_ofs = 0;
}
out_buf = &cryspr_cb->outbuf[cryspr_cb->outbuf_ofs];
cryspr_cb->outbuf_ofs += (pfx_len + out_len);
return(out_buf);
}
static CRYSPR_cb *crysprFallback_Open(CRYSPR_methods *cryspr, size_t max_len)
{
CRYSPR_cb *cryspr_cb;
unsigned char *membuf;
size_t memsiz, padded_len = hcryptMsg_PaddedLen(max_len, 128/8);
HCRYPT_LOG(LOG_DEBUG, "%s", "Using OpenSSL AES\n");
memsiz = sizeof(*cryspr_cb) + (CRYSPR_OUTMSGMAX * padded_len);
#if !CRYSPR_HAS_AESCTR
memsiz += HCRYPT_CTR_STREAM_SZ;
#endif /* !CRYSPR_HAS_AESCTR */
cryspr_cb = malloc(memsiz);
if (NULL == cryspr_cb) {
HCRYPT_LOG(LOG_ERR, "malloc(%zd) failed\n", memsiz);
return(NULL);
}
membuf = (unsigned char *)cryspr_cb;
membuf += sizeof(*cryspr_cb);
#if !CRYSPR_HAS_AESCTR
cryspr_cb->ctr_stream = membuf;
membuf += HCRYPT_CTR_STREAM_SZ;
cryspr_cb->ctr_stream_siz = HCRYPT_CTR_STREAM_SZ;
cryspr_cb->ctr_stream_len = 0;
#endif /* !CRYSPR_HAS_AESCTR */
cryspr_cb->outbuf = membuf;
cryspr_cb->outbuf_siz = CRYSPR_OUTMSGMAX * padded_len;
cryspr_cb->outbuf_ofs = 0;
// membuf += cryspr_cb->outbuf_siz;
cryspr_cb->cryspr=(CRYSPR_methods *)cryspr;
return(cryspr_cb);
}
static int crysprFallback_Close(CRYSPR_cb *cryspr_cb)
{
if (NULL != cryspr_cb) {
free(cryspr_cb);
}
return(0);
}
static int crysprFallback_MsSetKey(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, const unsigned char *key, size_t key_len)
{
CRYSPR_AESCTX *aes_sek = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)]; /* Ctx tells if it's for odd or even key */
if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) /* Encrypt key */
|| (ctx->mode == HCRYPT_CTX_MODE_AESCTR)) { /* CTR mode decrypts using encryption methods */
if (cryspr_cb->cryspr->aes_set_key(true, key, key_len, aes_sek)) {
HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_encrypt_key(sek) failed\n");
return(-1);
}
} else { /* Decrypt key */
if (cryspr_cb->cryspr->aes_set_key(false, key, key_len, aes_sek)) {
HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_decrypt_key(sek) failed\n");
return(-1);
}
}
return(0);
}
#if !CRYSPR_HAS_AESCTR
static int _crysprFallback_AES_SetCtrStream(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, size_t len, unsigned char *iv)
{
/* Counter stream:
* 0 1 2 3 4 5 nblk
* +---+---+---+---+---+---+---+---+
* |blk|blk|blk|blk|blk|blk|...|blk|
* +---+---+---+---+---+---+---+---+
*/
/* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
unsigned char ctr[HCRYPT_CTR_BLK_SZ];
unsigned nblk;
ASSERT(NULL != cryspr_cb);
ASSERT(NULL != ctx);
memcpy(ctr, iv, HCRYPT_CTR_BLK_SZ);
nblk = (len + (HCRYPT_CTR_BLK_SZ-1))/HCRYPT_CTR_BLK_SZ;
if ((nblk * HCRYPT_CTR_BLK_SZ) <= cryspr_cb->ctr_stream_siz) {
unsigned blk;
unsigned char *csp = &cryspr_cb->ctr_stream[0];
for(blk = 0; blk < nblk; blk++) {
memcpy(csp, ctr, HCRYPT_CTR_BLK_SZ);
csp += HCRYPT_CTR_BLK_SZ;
if (0 == ++(ctr[HCRYPT_CTR_BLK_SZ-1])) ++(ctr[HCRYPT_CTR_BLK_SZ-2]);
}
cryspr_cb->ctr_stream_len = nblk * HCRYPT_CTR_BLK_SZ;
} else {
HCRYPT_LOG(LOG_ERR, "packet too long(%zd)\n", len);
return(-1);
}
return(0);
}
#endif
static int crysprFallback_MsEncrypt(
CRYSPR_cb *cryspr_cb,
hcrypt_Ctx *ctx,
hcrypt_DataDesc *in_data, int nbin ATR_UNUSED,
void *out_p[], size_t out_len_p[], int *nbout_p)
{
unsigned char *out_msg;
size_t out_len = 0; //payload size
int pfx_len;
ASSERT(NULL != ctx);
ASSERT(NULL != cryspr_cb);
ASSERT((NULL != in_data) || (1 == nbin)); //Only one in_data[] supported
/*
* Get message prefix length
* to reserve room for unencrypted message header in output buffer
*/
pfx_len = ctx->msg_info->pfx_len;
/* Get buffer room from the internal circular output buffer */
out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, in_data[0].len);
if (NULL != out_msg) {
switch(ctx->mode) {
case HCRYPT_CTX_MODE_AESCTR: /* Counter mode */
{
#if CRYSPR_HAS_AESCTR
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
unsigned char iv[CRYSPR_AESBLKSZ];
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
cryspr_cb->cryspr->aes_ctr_cipher(true, aes_key, iv, in_data[0].payload, in_data[0].len,
&out_msg[pfx_len]);
#else /*CRYSPR_HAS_AESCTR*/
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
unsigned char iv[CRYSPR_AESBLKSZ];
int iret = 0;
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
/* Create CtrStream. May be longer than in_len (next cryspr block size boundary) */
iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv);
if (iret) {
return(iret);
}
/* Reserve output buffer for cryspr */
out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, cryspr_cb->ctr_stream_len);
/* Create KeyStream (encrypt CtrStream) */
iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key,
cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len,
&out_msg[pfx_len], &out_len);
if (iret) {
HCRYPT_LOG(LOG_ERR, "%s", "hcOpenSSL_AES_ecb_cipher(encrypt, failed\n");
return(iret);
}
#endif/*CRYSPR_HAS_AESCTR*/
/* Prepend packet prefix (clear text) in output buffer */
memcpy(out_msg, in_data[0].pfx, pfx_len);
/* CTR mode output length is same as input, no padding */
out_len = in_data[0].len;
break;
}
case HCRYPT_CTX_MODE_CLRTXT: /* Clear text mode (transparent mode for tests) */
memcpy(&out_msg[pfx_len], in_data[0].payload, in_data[0].len);
memcpy(out_msg, in_data[0].pfx, pfx_len);
out_len = in_data[0].len;
break;
default:
/* Unsupported cipher mode */
return(-1);
}
} else {
/* input data too big */
return(-1);
}
if (out_len > 0) {
/* Encrypted messages have been produced */
if (NULL == out_p) {
/*
* Application did not provided output buffer,
* so copy encrypted message back in input buffer
*/
memcpy(in_data[0].pfx, out_msg, pfx_len);
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
/* XOR KeyStream with input text directly in input buffer */
hcrypt_XorStream(in_data[0].payload, &out_msg[pfx_len], out_len);
}else{
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
}
#else /* CRYSPR_HAS_AESCTR */
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
#endif /* CRYSPR_HAS_AESCTR */
} else {
/* Copy header in output buffer if needed */
if (pfx_len > 0) memcpy(out_msg, in_data[0].pfx, pfx_len);
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
hcrypt_XorStream(&out_msg[pfx_len], in_data[0].payload, out_len);
}
#endif /* CRYSPR_HAS_AESCTR */
out_p[0] = out_msg;
out_len_p[0] = pfx_len + out_len;
*nbout_p = 1;
}
} else {
/*
* Nothing out
* This is not an error for implementations using deferred/async processing
* with co-processor, DSP, crypto hardware, etc.
* Submitted input data could be returned encrypted in a next call.
*/
if (nbout_p != NULL) *nbout_p = 0;
return(-1);
}
return(0);
}
static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx,
hcrypt_DataDesc *in_data, int nbin ATR_UNUSED, void *out_p[], size_t out_len_p[], int *nbout_p)
{
unsigned char *out_txt;
size_t out_len;
int iret = 0;
ASSERT(NULL != cryspr_cb);
ASSERT(NULL != ctx);
ASSERT((NULL != in_data) || (1 == nbin)); //Only one in_data[] supported
/* Reserve output buffer (w/no header) */
out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, in_data[0].len);
if (NULL != out_txt) {
switch(ctx->mode) {
case HCRYPT_CTX_MODE_AESCTR:
{
#if CRYSPR_HAS_AESCTR
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
unsigned char iv[CRYSPR_AESBLKSZ];
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
cryspr_cb->cryspr->aes_ctr_cipher(false, aes_key, iv, in_data[0].payload, in_data[0].len,
out_txt);
out_len = in_data[0].len;
#else /*CRYSPR_HAS_AESCTR*/
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
unsigned char iv[CRYSPR_AESBLKSZ];
int iret = 0;
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
/* Create CtrStream. May be longer than in_len (next cipher block size boundary) */
iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv);
if (iret) {
return(iret);
}
/* Reserve output buffer for cryspr */
out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, cryspr_cb->ctr_stream_len);
/* Create KeyStream (encrypt CtrStream) */
iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key,
cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len,
out_txt, &out_len);
if (iret) {
HCRYPT_LOG(LOG_ERR, "%s", "crysprNatural_AES_ecb_cipher(encrypt failed\n");
return(iret);
}
#endif /*CRYSPR_HAS_AESCTR*/
break;
}
case HCRYPT_CTX_MODE_CLRTXT:
memcpy(out_txt, in_data[0].payload, in_data[0].len);
out_len = in_data[0].len;
break;
default:
return(-1);
}
} else {
return(-1);
}
if (out_len > 0) {
if (NULL == out_p) {
/*
* Application did not provided output buffer,
* so copy encrypted message back in input buffer
*/
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
/* XOR KeyStream with input text directly in input buffer */
hcrypt_XorStream(in_data[0].payload, out_txt, out_len);
}else{
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, out_txt, out_len);
}
#else /* CRYSPR_HAS_AESCTR */
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, out_txt, out_len);
#endif /* CRYSPR_HAS_AESCTR */
} else {
/* Copy header in output buffer if needed */
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
hcrypt_XorStream(out_txt, in_data[0].payload, out_len);
}
#endif /* CRYSPR_HAS_AESCTR */
out_p[0] = out_txt;
out_len_p[0] = out_len;
*nbout_p = 1;
}
iret = 0;
} else {
if (NULL != nbout_p) *nbout_p = 0;
iret = -1;
}
#if 0
{ /* Debug decryption errors */
static int nberr = 0;
if (out_txt[0] != 0x47){
if ((++nberr == 1)
|| ((nberr > 500) && (0 == ((((unsigned char *)&MSmsg->pki)[2] & 0x0F)|((unsigned char *)&MSmsg->pki)[3])))) {
HCRYPT_LOG(LOG_DEBUG, "keyindex=%d\n", hcryptCtx_GetKeyIndex(ctx));
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
HCRYPT_PRINTKEY(ctx->salt, ctx->salt_len, "salt");
}
} else {
nberr = 0;
}
}
#endif
return(iret);
}
CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr)
{
/* CryptoLib Primitive API */
cryspr->prng = crysprStub_Prng;
cryspr->aes_set_key = crysprStub_AES_SetKey;
cryspr->aes_ecb_cipher = crysprStub_AES_EcbCipher;
cryspr->aes_ctr_cipher = crysprStub_AES_CtrCipher;
cryspr->sha1_msg_digest = crysprStub_SHA1_MsgDigest;
/* Crypto Session API */
cryspr->open = crysprFallback_Open;
cryspr->close = crysprFallback_Close;
//Keying material (km) encryption
cryspr->km_pbkdf2 = crysprStub_KmPbkdf2;
cryspr->km_setkey = crysprFallback_KmSetKey;
cryspr->km_wrap = crysprFallback_AES_WrapKey;
cryspr->km_unwrap = crysprFallback_AES_UnwrapKey;
//Media stream (ms) encryption
cryspr->ms_setkey = crysprFallback_MsSetKey;
cryspr->ms_encrypt = crysprFallback_MsEncrypt;
cryspr->ms_decrypt = crysprFallback_MsDecrypt;
return(cryspr);
}
HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance(void)
{
return((HaiCrypt_Cryspr)cryspr4SRT());
}

View file

@ -0,0 +1,203 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-28 (jdube)
CRYSPR/4SRT Initial implementation.
*****************************************************************************/
#ifndef CRYSPR_H
#define CRYSPR_H
#include <stdbool.h>
#include <sys/types.h>
#if !defined(HAISRT_VERSION_INT)
#include "haicrypt.h"
#include "hcrypt_msg.h"
#else
// Included by haisrt.h or similar
#include "haisrt/haicrypt.h"
#include "haisrt/hcrypt_msg.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include "cryspr-config.h"
typedef struct tag_CRYSPR_cb {
CRYSPR_AESCTX aes_kek; /* Key Encrypting Key (KEK) */
CRYSPR_AESCTX aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */
struct tag_CRYSPR_methods *cryspr;
#if !CRYSPR_HAS_AESCTR
/* Reserve room to build the counter stream ourself */
#define HCRYPT_CTR_BLK_SZ CRYSPR_AESBLKSZ
#define HCRYPT_CTR_STREAM_SZ 2048
unsigned char * ctr_stream;
size_t ctr_stream_len; /* Content size */
size_t ctr_stream_siz; /* Allocated length */
#endif /* !CRYSPR_HAS_AESCTR */
#define CRYSPR_OUTMSGMAX 6
uint8_t * outbuf; /* output circle buffer */
size_t outbuf_ofs; /* write offset in circle buffer */
size_t outbuf_siz; /* circle buffer size */
} CRYSPR_cb;
typedef struct tag_CRYSPR_methods {
/*
* prng:
* Pseudo-Random Number Generator
*/
int (*prng)(
unsigned char *rn, /* out: pseudo random number */
int rn_len);
int (*aes_set_key)(
bool bEncrypt, /* true Enxcrypt key, false: decrypt */
const unsigned char *kstr,/* key string*/
size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aeskey); /* Cryptolib Specific AES key context */
int (*aes_ecb_cipher)(
bool bEncrypt, /* true:encrypt false:decrypt */
CRYSPR_AESCTX *aes_key, /* ctx */
const unsigned char *indata, /* src (clear text)*/
size_t inlen, /* src length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen); /* dst length */
int (*aes_ctr_cipher)(
bool bEncrypt, /* true:encrypt false:decrypt (don't care with CTR) */
CRYSPR_AESCTX *aes_key, /* ctx */
unsigned char *iv, /* iv */
const unsigned char *indata, /* src (clear text) */
size_t inlen, /* src length */
unsigned char *out_txt);/* dest */
unsigned char *(*sha1_msg_digest)(
const unsigned char *m, /* in: message */
size_t m_len, /* message length */
unsigned char *md); /* out: message digest buffer *160 bytes */
/*
* open:
* Create a cipher instance
* Allocate output buffers
*/
CRYSPR_cb *(*open)(
struct tag_CRYSPR_methods *cryspr,
size_t max_len); /* Maximum packet length that will be encrypted/decrypted */
/*
* close:
* Release any cipher resources
*/
int (*close)(
CRYSPR_cb *cryspr_data); /* Cipher handle, internal data */
/*
* pbkdf2_hmac_sha1
* Password-based Key Derivation Function 2
*/
int (*km_pbkdf2)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t out_len, /* key_len */
unsigned char *out); /* derived key */
/*
* km_setkey:
* Set the Key Encypting Key for Wrap (Encryption) or UnWrap (Decryption).
* Context (ctx) tells if it's for Wrap or Unwrap
* A Context flags (ctx->flags) also tells if this is for wrap(encryption) or unwrap(decryption) context (HCRYPT_CTX_F_ENCRYPT)
*/
int (*km_setkey)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
bool bWrap, /* True: Wrap KEK, False: Unwrap KEK */
const unsigned char *kek, size_t kek_len); /* KEK: Key Encrypting Key */
/*
* km_wrap:
* wrap media stream key
*/
int (*km_wrap)(CRYSPR_cb *cryspr_cb,
unsigned char *wrap,
const unsigned char *sek,
unsigned int seklen);
/*
* km_unwrap:
* wrap media stream key
*/
int (*km_unwrap)(CRYSPR_cb *cryspr_cb,
unsigned char *sek,
const unsigned char *wrap,
unsigned int wraplen);
/*
* setkey:
* Set the Odd or Even, Encryption or Decryption key.
* Context (ctx) tells if it's for Odd or Even key (hcryptCtx_GetKeyIndex(ctx))
* A Context flags (ctx->flags) also tells if this is an encryption or decryption context (HCRYPT_CTX_F_ENCRYPT)
*/
int (*ms_setkey)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
hcrypt_Ctx *ctx, /* HaiCrypt Context (cipher, keys, Odd/Even, etc..) */
const unsigned char *key, size_t kwelen); /* New Key */
/*
* encrypt:
* Submit a list of nbin clear transport packets (hcrypt_DataDesc *in_data) to encryption
* returns *nbout encrypted data packets of length out_len_p[] into out_p[]
*
* If cipher implements deferred encryption (co-processor, async encryption),
* it may return no encrypted packets, or encrypted packets for clear text packets of a previous call.
*/
int (*ms_encrypt)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
hcrypt_Ctx *ctx, /* HaiCrypt Context (cipher, keys, Odd/Even, etc..) */
hcrypt_DataDesc *in_data, int nbin, /* Clear text transport packets: header and payload */
void *out_p[], size_t out_len_p[], int *nbout); /* Encrypted packets */
/*
* decrypt:
* Submit a list of nbin encrypted transport packets (hcrypt_DataDesc *in_data) to decryption
* returns *nbout clear text data packets of length out_len_p[] into out_p[]
*
* If cipher implements deferred decryption (co-processor, async encryption),
* it may return no decrypted packets, or decrypted packets for encrypted packets of a previous call.
*/
int (*ms_decrypt)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
hcrypt_Ctx *ctx, /* HaiCrypt Context (cipher, keys, Odd/Even, etc..) */
hcrypt_DataDesc *in_data, int nbin, /* Clear text transport packets: header and payload */
void *out_p[], size_t out_len_p[], int *nbout); /* Encrypted packets */
} CRYSPR_methods;
CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr);
#ifdef __cplusplus
}
#endif
#endif /* CRYSPR_H */

View file

@ -0,0 +1,27 @@
# This file is currently reserved for future refactoring, when all headers
# are going to be moved here. This is the list of headers considered to be
# attached to the installation package. Once possible, please move the below
# header files from ../include back to this directory.
PUBLIC HEADERS
haicrypt.h
hcrypt_ctx.h
hcrypt_msg.h
PRIVATE HEADERS
hcrypt.h
cryspr.h
cryspr-gnutls.h
haicrypt_log.h
SOURCES
cryspr.c
cryspr-gnutls.c
hcrypt.c
hcrypt_ctx_rx.c
hcrypt_ctx_tx.c
hcrypt_rx.c
hcrypt_sa.c
hcrypt_tx.c
hcrypt_xpt_srt.c
hcrypt_xpt_sta.c
haicrypt_log.cpp

View file

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

View file

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

View file

@ -0,0 +1,138 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#ifndef HAICRYPT_H
#define HAICRYPT_H
#include <sys/types.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// setup exports
#if defined _WIN32 && !defined __MINGW__
#ifdef HAICRYPT_DYNAMIC
#ifdef HAICRYPT_EXPORTS
#define HAICRYPT_API __declspec(dllexport)
#else
#define HAICRYPT_API __declspec(dllimport)
#endif
#else
#define HAICRYPT_API
#endif
#else
#define HAICRYPT_API
#endif
typedef void *HaiCrypt_Cryspr;
HAICRYPT_API HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance (void); /* Return a default cryspr instance */
#define HAICRYPT_CIPHER_BLK_SZ 16 /* AES Block Size */
#define HAICRYPT_PWD_MAX_SZ 80 /* MAX password (for Password-based Key Derivation) */
#define HAICRYPT_KEY_MAX_SZ 32 /* MAX key */
#define HAICRYPT_SECRET_MAX_SZ (HAICRYPT_PWD_MAX_SZ > HAICRYPT_KEY_MAX_SZ ? HAICRYPT_PWD_MAX_SZ : HAICRYPT_KEY_MAX_SZ)
#define HAICRYPT_SALT_SZ 16
#define HAICRYPT_WRAPKEY_SIGN_SZ 8 /* RFC3394 AES KeyWrap signature size */
#define HAICRYPT_PBKDF2_SALT_LEN 8 /* PKCS#5 PBKDF2 Password based key derivation salt length */
#define HAICRYPT_PBKDF2_ITER_CNT 2048 /* PKCS#5 PBKDF2 Password based key derivation iteration count */
#define HAICRYPT_TS_PKT_SZ 188 /* Transport Stream packet size */
typedef struct {
#define HAICRYPT_SECTYP_UNDEF 0
#define HAICRYPT_SECTYP_PRESHARED 1 /* Preshared KEK */
#define HAICRYPT_SECTYP_PASSPHRASE 2 /* Password */
unsigned typ;
size_t len;
unsigned char str[HAICRYPT_SECRET_MAX_SZ];
}HaiCrypt_Secret;
typedef struct {
#define HAICRYPT_CFG_F_TX 0x01 /* !TX -> RX */
#define HAICRYPT_CFG_F_CRYPTO 0x02 /* Perform crypto Tx:Encrypt Rx:Decrypt */
#define HAICRYPT_CFG_F_FEC 0x04 /* Do Forward Error Correction */
unsigned flags;
HaiCrypt_Secret secret; /* Security Association */
HaiCrypt_Cryspr cryspr; /* CRYSPR implementation */
#define HAICRYPT_DEF_KEY_LENGTH 16 /* default key length (bytes) */
size_t key_len; /* SEK length (bytes) */
#define HAICRYPT_DEF_DATA_MAX_LENGTH 1500 /* default packet data length (bytes) */
size_t data_max_len; /* Maximum data_len passed to HaiCrypt (bytes) */
#define HAICRYPT_XPT_STANDALONE 0
#define HAICRYPT_XPT_SRT 1
int xport;
#define HAICRYPT_DEF_KM_TX_PERIOD 1000 /* Keying Material Default Tx Period (msec) */
unsigned int km_tx_period_ms; /* Keying Material Tx period (msec) */
#define HAICRYPT_DEF_KM_REFRESH_RATE 0x1000000 /* Keying Material Default Refresh Rate (pkts) */
unsigned int km_refresh_rate_pkt; /* Keying Material Refresh Rate (pkts) */
#define HAICRYPT_DEF_KM_PRE_ANNOUNCE 0x1000 /* Keying Material Default Pre/Post Announce (pkts) */
unsigned int km_pre_announce_pkt; /* Keying Material Pre/Post Announce (pkts) */
}HaiCrypt_Cfg;
typedef enum HaiCrypt_CryptoDir { HAICRYPT_CRYPTO_DIR_RX, HAICRYPT_CRYPTO_DIR_TX } HaiCrypt_CryptoDir;
//typedef void *HaiCrypt_Handle;
// internally it will be correctly interpreted,
// for the outsider it's just some kinda incomplete type
// but still if you use any kinda pointer instead, you'll get complaints
typedef struct hcrypt_Session_str* HaiCrypt_Handle;
HAICRYPT_API int HaiCrypt_SetLogLevel(int level, int logfa);
HAICRYPT_API int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc);
HAICRYPT_API int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handle *phhc);
HAICRYPT_API int HaiCrypt_Close(HaiCrypt_Handle hhc);
HAICRYPT_API int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_p);
HAICRYPT_API int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout);
HAICRYPT_API int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout);
HAICRYPT_API int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc);
HAICRYPT_API int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout);
HAICRYPT_API int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
HAICRYPT_API int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
/* Status values */
#define HAICRYPT_ERROR -1
#define HAICRYPT_ERROR_WRONG_SECRET -2
#define HAICRYPT_OK 0
#ifdef __cplusplus
}
#endif
#endif /* HAICRYPT_H */

View file

@ -0,0 +1,120 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#if ENABLE_HAICRYPT_LOGGING
#include "hcrypt.h"
#include "haicrypt.h"
#include "../srtcore/srt.h"
#include "../srtcore/logging.h"
extern srt_logging::LogConfig srt_logger_config;
// LOGFA symbol defined in srt.h
srt_logging::Logger hclog(SRT_LOGFA_HAICRYPT, srt_logger_config, "SRT.k");
extern "C" {
int HaiCrypt_SetLogLevel(int level, int logfa)
{
srt_setloglevel(level);
if (logfa != SRT_LOGFA_GENERAL) // General can't be turned on or off
{
srt_addlogfa(logfa);
}
return 0;
}
// HaiCrypt will be using its own FA, which will be turned off by default.
// Templates made C way.
// It's tempting to use the HAICRYPT_DEFINE_LOG_DISPATCHER macro here because it would provide the
// exact signature that is needed here, the problem is though that this would expand the LOGLEVEL
// parameter, which is also a macro, into the value that the macro designates, which would generate
// the HaiCrypt_LogF_0 instead of HaiCrypt_LogF_LOG_DEBUG, for example.
#define HAICRYPT_DEFINE_LOG_DISPATCHER(LOGLEVEL, dispatcher) \
int HaiCrypt_LogF_##LOGLEVEL ( const char* file, int line, const char* function, const char* format, ...) \
{ \
va_list ap; \
va_start(ap, format); \
srt_logging::LogDispatcher& lg = hclog.dispatcher; \
if (!lg.CheckEnabled()) return -1; \
lg().setloc(file, line, function).vform(format, ap); \
va_end(ap); \
return 0; \
}
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_DEBUG, Debug);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_NOTICE, Note);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_INFO, Note);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_WARNING, Warn);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_ERR, Error);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_CRIT, Fatal);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_ALERT, Fatal);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_EMERG, Fatal);
static void DumpCfgFlags(int flags, std::ostream& out)
{
static struct { int flg; const char* desc; } flgtable [] = {
#define HCRYPTF(name) { HAICRYPT_CFG_F_##name, #name }
HCRYPTF(TX),
HCRYPTF(CRYPTO),
HCRYPTF(FEC)
#undef HCRYPTF
};
size_t flgtable_size = sizeof(flgtable)/sizeof(flgtable[0]);
size_t i;
out << "{";
const char* sep = "";
const char* sep_bar = " | ";
for (i = 0; i < flgtable_size; ++i)
{
if ( (flgtable[i].flg & flags) != 0 )
{
out << sep << flgtable[i].desc;
sep = sep_bar;
}
}
out << "}";
}
void HaiCrypt_DumpConfig(const HaiCrypt_Cfg* cfg)
{
std::ostringstream cfg_flags;
DumpCfgFlags(cfg->flags, cfg_flags);
LOGC(hclog.Debug, log << "CFG DUMP: flags=" << cfg_flags.str()
<< " xport=" << (cfg->xport == HAICRYPT_XPT_SRT ? "SRT" : "INVALID")
<< " cipher="
<< (cfg->cipher == HaiCryptCipher_OpenSSL_EVP_CTR() ? "OSSL-EVP-CTR":
cfg->cipher == HaiCryptCipher_OpenSSL_AES() ? "OSSL-AES":
// This below is used as the only one when Nettle is used. When OpenSSL
// is used, one of the above will trigger, and the one below will then never trigger.
cfg->cipher == HaiCryptCipher_Get_Instance() ? "Nettle-AES":
"UNKNOWN")
<< " key_len=" << cfg->key_len << " data_max_len=" << cfg->data_max_len);
LOGC(hclog.Debug, log << "CFG DUMP: txperiod="
<< cfg->km_tx_period_ms << "ms kmrefresh=" << cfg->km_refresh_rate_pkt
<< " kmpreannounce=" << cfg->km_pre_announce_pkt
<< " secret "
<< "{tp=" << (cfg->secret.typ == 1 ? "PSK" : cfg->secret.typ == 2 ? "PWD" : "???")
<< " len=" << cfg->secret.len << " pwd=" << cfg->secret.str << "}");
}
} // extern "C"
#endif // Block for the whole file

View file

@ -0,0 +1,34 @@
#ifndef IMC__HAICRYPT_LOG_H
#define IMC__HAICRYPT_LOG_H
#ifdef __cplusplus
extern "C" {
#endif
#define HAICRYPT_DECLARE_LOG_DISPATCHER(LOGLEVEL) \
int HaiCrypt_LogF_##LOGLEVEL ( const char* file, int line, const char* function, const char* format, ...)
// Now declare all dispatcher functions
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_DEBUG);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_NOTICE);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_INFO);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_WARNING);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_ERR);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_CRIT);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_ALERT);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_EMERG);
#define HCRYPT_LOG_INIT()
#define HCRYPT_LOG_EXIT()
#define HCRYPT_LOG(lvl, fmt, ...) HaiCrypt_LogF_##lvl (__FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__)
#if ENABLE_HAICRYPT_LOGGING == 2
#define HCRYPT_DEV 1
#endif
#ifdef __cplusplus
}
#endif
#endif // macroguard

View file

@ -0,0 +1,342 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <stdio.h> /* snprintf */
#include <stdlib.h> /* NULL, malloc, free */
#include <string.h> /* memcpy, memset */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/time.h> /* timerclear */
#endif
#include "hcrypt.h"
#if ENABLE_HAICRYPT_LOGGING
void HaiCrypt_DumpConfig(const HaiCrypt_Cfg* cfg);
#else
#define HaiCrypt_DumpConfig(x) (void)0
#endif
static hcrypt_Session* sHaiCrypt_PrepareHandle(const HaiCrypt_Cfg* cfg, HaiCrypt_CryptoDir tx)
{
hcrypt_Session *crypto;
unsigned char *mem_buf;
size_t mem_siz, inbuf_siz;
HaiCrypt_DumpConfig(cfg);
HCRYPT_PRINTKEY(cfg->secret.str, cfg->secret.len, "cfgkey");
inbuf_siz = 0;
inbuf_siz = hcryptMsg_PaddedLen(cfg->data_max_len, 128/8);
/* Allocate crypto session control struct */
mem_siz = sizeof(hcrypt_Session) // structure
+ inbuf_siz;
crypto = malloc(mem_siz);
if (NULL == crypto) {
HCRYPT_LOG(LOG_ERR, "%s\n", "malloc failed");
return NULL;
}
mem_buf = (unsigned char *)crypto;
mem_buf += sizeof(*crypto);
memset(crypto, 0, sizeof(*crypto));
if (inbuf_siz) {
crypto->inbuf = mem_buf;
crypto->inbuf_siz = inbuf_siz;
}
crypto->cryspr = cfg->cryspr;
crypto->cfg.data_max_len = cfg->data_max_len;
/* Setup transport packet info */
switch (cfg->xport) {
case HAICRYPT_XPT_STANDALONE:
crypto->se = HCRYPT_SE_TSUDP;
crypto->msg_info = hcryptMsg_STA_MsgInfo();
break;
case HAICRYPT_XPT_SRT:
crypto->se = HCRYPT_SE_TSSRT;
crypto->msg_info = hcryptMsg_SRT_MsgInfo();
break;
default:
HCRYPT_LOG(LOG_ERR, "invalid xport: %d\n", cfg->xport);
free(crypto);
return NULL;
}
timerclear(&crypto->km.tx_last);
crypto->km.tx_period.tv_sec = cfg->km_tx_period_ms / 1000;
crypto->km.tx_period.tv_usec = (cfg->km_tx_period_ms % 1000) * 1000;
crypto->km.refresh_rate = cfg->km_refresh_rate_pkt;
crypto->km.pre_announce = cfg->km_pre_announce_pkt;
/* Indentify each context */
crypto->ctx_pair[0].flags = HCRYPT_MSG_F_eSEK | (tx ? HCRYPT_CTX_F_ENCRYPT : 0);
crypto->ctx_pair[1].flags = HCRYPT_MSG_F_oSEK | (tx ? HCRYPT_CTX_F_ENCRYPT : 0);
/* Point to each other */
crypto->ctx_pair[0].alt = &crypto->ctx_pair[1];
crypto->ctx_pair[1].alt = &crypto->ctx_pair[0];
crypto->cryspr_cb = crypto->cryspr->open(crypto->cryspr, cfg->data_max_len);
if (NULL == crypto->cryspr_cb) {
free(crypto);
return NULL;
}
return crypto;
}
int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc)
{
ASSERT(cfg != NULL);
ASSERT(phhc != NULL);
hcrypt_Session *crypto;
HaiCrypt_CryptoDir tx = (HaiCrypt_CryptoDir)(HAICRYPT_CFG_F_TX & cfg->flags);
*phhc = NULL;
HCRYPT_LOG_INIT();
//Test log
HCRYPT_LOG(LOG_INFO, "creating crypto context(flags=0x%x)\n", cfg->flags);
if (!(HAICRYPT_CFG_F_CRYPTO & cfg->flags)) {
HCRYPT_LOG(LOG_INFO, "no supported flags set (0x%x)\n", cfg->flags);
return(-1);
} else if ((16 != cfg->key_len) /* SEK length */
&& (24 != cfg->key_len)
&& (32 != cfg->key_len)) {
HCRYPT_LOG(LOG_ERR, "invalid key length (%d). Expected: 16, 24, 32\n", (int)cfg->key_len);
return(-1);
} else if ((HAICRYPT_SECTYP_PASSPHRASE == cfg->secret.typ)
&& ((0 == cfg->secret.len) || (sizeof(cfg->secret.str) < cfg->secret.len))) { /* KEK length */
HCRYPT_LOG(LOG_ERR, "invalid secret passphrase length (%d)\n", (int)cfg->secret.len);
return(-1);
} else if ((HAICRYPT_SECTYP_PRESHARED == cfg->secret.typ)
&& (cfg->key_len > cfg->secret.len)) {
HCRYPT_LOG(LOG_ERR, "preshared secret length (%d) smaller than key length (%d)\n",
(int)cfg->secret.len, (int)cfg->key_len);
return(-1);
} else if (NULL == cfg->cryspr) {
HCRYPT_LOG(LOG_ERR, "%s\n", "no cryspr specified");
return(-1);
} else if (0 == cfg->data_max_len) {
HCRYPT_LOG(LOG_ERR, "%s\n", "no data_max_len specified");
return(-1);
}
crypto = sHaiCrypt_PrepareHandle(cfg, tx);
if (!crypto)
return -1;
if (tx) { /* Encoder */
/* Configure initial context */
if (hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[0], cfg)
|| hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[1], cfg)) {
free(crypto);
return(-1);
}
/* Generate keys for first (default) context */
if (hcryptCtx_Tx_Rekey(crypto, &crypto->ctx_pair[0])) {
free(crypto);
return(-1);
}
crypto->ctx = &crypto->ctx_pair[0];
crypto->ctx->flags |= (HCRYPT_CTX_F_ANNOUNCE | HCRYPT_CTX_F_TTSEND);
crypto->ctx->status = HCRYPT_CTX_S_ACTIVE;
} else { /* Decoder */
/* Configure contexts */
if (hcryptCtx_Rx_Init(crypto, &crypto->ctx_pair[0], cfg)
|| hcryptCtx_Rx_Init(crypto, &crypto->ctx_pair[1], cfg)) {
free(crypto);
return(-1);
}
}
*phhc = (void *)crypto;
return(0);
}
int HaiCrypt_ExtractConfig(HaiCrypt_Handle hhcSrc, HaiCrypt_Cfg* pcfg)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhcSrc;
hcrypt_Ctx* ctx = crypto->ctx;
if (!ctx)
{
// Fall back to the first of the pair;
// Should this be not initialized, ignore it.
ctx = &crypto->ctx_pair[0];
// We assume that when ctx != NULL, it is active or keyed anyway.
if (ctx->status != HCRYPT_CTX_S_KEYED && ctx->status != HCRYPT_CTX_S_ACTIVE)
return -1;
}
pcfg->flags = HAICRYPT_CFG_F_CRYPTO;
if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) == HCRYPT_CTX_F_ENCRYPT)
pcfg->flags |= HAICRYPT_CFG_F_TX;
/* Set this explicitly - this use of this library is SRT only. */
pcfg->xport = HAICRYPT_XPT_SRT;
pcfg->cryspr = crypto->cryspr;
pcfg->key_len = ctx->cfg.key_len;
if (pcfg->key_len == 0) // not initialized - usual in RX
{
pcfg->key_len = ctx->sek_len;
}
pcfg->data_max_len = crypto->cfg.data_max_len;
pcfg->km_tx_period_ms = 0;//No HaiCrypt KM inject period, handled in SRT;
pcfg->km_refresh_rate_pkt = crypto->km.refresh_rate;
pcfg->km_pre_announce_pkt = crypto->km.pre_announce;
/* As SRT is using only the PASSPHRASE type, never PRESHARED,
* this is so assumed here, although there are completely no
* premises as to which is currently used by the hhcSrc.
*/
pcfg->secret.typ = HAICRYPT_SECTYP_PASSPHRASE;
pcfg->secret.len = ctx->cfg.pwd_len;
memcpy(pcfg->secret.str, ctx->cfg.pwd, pcfg->secret.len);
return 0;
}
int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handle *phhc)
{
hcrypt_Session *cryptoSrc = (hcrypt_Session *)hhcSrc;
hcrypt_Session *cryptoClone;
unsigned char *mem_buf;
size_t mem_siz, inbuf_siz;
*phhc = NULL;
ASSERT(NULL != hhcSrc);
HCRYPT_LOG(LOG_INFO, "%s\n", "creating CLONED crypto context");
if (tx) {
HaiCrypt_Cfg crypto_config;
HaiCrypt_ExtractConfig(hhcSrc, &crypto_config);
/*
* Just invert the direction written in flags and use the
* standard way of creating the context, as you already have a config.
*/
crypto_config.flags |= HAICRYPT_CFG_F_TX;
cryptoClone = sHaiCrypt_PrepareHandle(&crypto_config, tx);
if (!cryptoClone)
return -1;
/* Configure initial context */
if (hcryptCtx_Tx_Init(cryptoClone, &cryptoClone->ctx_pair[0], &crypto_config)
|| hcryptCtx_Tx_Init(cryptoClone, &cryptoClone->ctx_pair[1], &crypto_config)) {
free(cryptoClone);
return(-1);
}
/* Clone keys for first (default) context from the source RX crypto */
if (hcryptCtx_Tx_CloneKey(cryptoClone, &cryptoClone->ctx_pair[0], cryptoSrc)) {
free(cryptoClone);
return(-1);
}
cryptoClone->ctx = &cryptoClone->ctx_pair[0];
cryptoClone->ctx->flags |= (HCRYPT_CTX_F_ANNOUNCE | HCRYPT_CTX_F_TTSEND);
cryptoClone->ctx->status = HCRYPT_CTX_S_ACTIVE;
} else { /* Receiver */
/*
* If cryspr has no special input buffer alignment requirement,
* handle it in the crypto session.
*/
inbuf_siz = cryptoSrc->inbuf_siz ;
/* Allocate crypto session control struct */
mem_siz = sizeof(hcrypt_Session) // structure
+ inbuf_siz;
cryptoClone = malloc(mem_siz);
if (NULL == cryptoClone) {
HCRYPT_LOG(LOG_ERR, "%s\n", "malloc failed");
return(-1);
}
mem_buf = (unsigned char *)cryptoClone;
mem_buf += sizeof(*cryptoClone);
memcpy(cryptoClone, cryptoSrc, sizeof(*cryptoClone));
if (inbuf_siz) {
cryptoClone->inbuf = mem_buf;
mem_buf += inbuf_siz;
}
timerclear(&cryptoClone->km.tx_last);
/* Adjust pointers pointing into cryproSrc after copy
msg_info and crysprs are extern statics so this is ok*/
cryptoClone->ctx_pair[0].alt = &cryptoClone->ctx_pair[1];
cryptoClone->ctx_pair[1].alt = &cryptoClone->ctx_pair[0];
/* create a new cryspr (OpenSSL) context */
cryptoClone->cryspr_cb = cryptoClone->cryspr->open(cryptoClone->cryspr, cryptoClone->cfg.data_max_len);
if (NULL == cryptoClone->cryspr_cb) {
//shred
free(cryptoClone);
return(-1);
}
/* Configure contexts */
if (hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[0], NULL)
|| hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[1], NULL)) {
free(cryptoClone);
return(-1);
}
/* Clear salt to force later regeneration of KEK as AES decrypting key,
copyed one is encrypting key */
cryptoClone->ctx_pair[0].flags &= ~HCRYPT_CTX_F_ENCRYPT;
cryptoClone->ctx_pair[1].flags &= ~HCRYPT_CTX_F_ENCRYPT;
memset(cryptoClone->ctx_pair[0].salt, 0, sizeof(cryptoClone->ctx_pair[0].salt));
cryptoClone->ctx_pair[0].salt_len = 0;
}
*phhc = (void *)cryptoClone;
return(0);
}
int HaiCrypt_Close(HaiCrypt_Handle hhc)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
int rc = -1;
if (crypto) {
if (crypto->cryspr && crypto->cryspr->close) crypto->cryspr->close(crypto->cryspr_cb);
free(crypto);
rc = 0;
}
HCRYPT_LOG_EXIT();
return rc;
}

View file

@ -0,0 +1,169 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
2014-03-26 (jsantiago)
OS-X Build.
2014-03-27 (jdube)
Remove dependency on internal Crypto API.
2016-07-22 (jsantiago)
MINGW-W64 Build.
*****************************************************************************/
#ifndef HCRYPT_H
#define HCRYPT_H
#include <sys/types.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#if defined(_MSC_VER)
#pragma warning(disable:4267)
#pragma warning(disable:4018)
#endif
#else
#include <sys/time.h>
#endif
#ifdef __GNUC__
#define ATR_UNUSED __attribute__((unused))
#else
#define ATR_UNUSED
#endif
#include "haicrypt.h"
#include "hcrypt_msg.h"
#include "hcrypt_ctx.h"
#include "cryspr.h"
//#define HCRYPT_DEV 1 /* Development: should not be defined in committed code */
#ifdef HAICRYPT_SUPPORT_CRYPTO_API
/* See CRYPTOFEC_OBJECT in session structure */
#define CRYPTO_API_SERVER 1 /* Enable handler's structures */
#include "crypto_api.h"
#endif /* HAICRYPT_SUPPORT_CRYPTO_API */
typedef struct hcrypt_Session_str {
#ifdef HAICRYPT_SUPPORT_CRYPTO_API
/*
* Resv matches internal upper layer handle (crypto_api)
* They are not used in HaiCrypt.
* This make 3 layers using the same handle.
* To get rid of this dependency for a portable HaiCrypt,
* revise caller (crypto_hc.c) to allocate its own buffer.
*/
CRYPTOFEC_OBJECT resv; /* See above comment */
#endif /* HAICRYPT_SUPPORT_CRYPTO_API */
hcrypt_Ctx ctx_pair[2]; /* Even(0)/Odd(1) crypto contexts */
hcrypt_Ctx * ctx; /* Current context */
CRYSPR_methods * cryspr;
CRYSPR_cb * cryspr_cb;
unsigned char * inbuf; /* allocated if cipher has no getinbuf() func */
size_t inbuf_siz;
int se; /* Stream Encapsulation (HCRYPT_SE_xxx) */
hcrypt_MsgInfo * msg_info;
struct {
size_t data_max_len;
}cfg;
struct {
struct timeval tx_period; /* Keying Material tx period (milliseconds) */
struct timeval tx_last; /* Keying Material last tx time */
unsigned int refresh_rate; /* SEK use period */
unsigned int pre_announce; /* Pre/Post next/old SEK announce */
}km;
} hcrypt_Session;
#if ENABLE_HAICRYPT_LOGGING
#include "haicrypt_log.h"
#else
#define HCRYPT_LOG_INIT()
#define HCRYPT_LOG_EXIT()
#define HCRYPT_LOG(lvl, fmt, ...)
#endif
#ifdef HCRYPT_DEV
#define HCRYPT_PRINTKEY(key, len, tag) HCRYPT_LOG(LOG_DEBUG, \
"%s[%d]=0x%02x%02x..%02x%02x\n", tag, len, \
(key)[0], (key)[1], (key)[(len)-2], (key)[(len)-1])
#else /* HCRYPT_DEV */
#define HCRYPT_PRINTKEY(key,len,tag)
#endif /* HCRYPT_DEV */
#ifndef ASSERT
#include <assert.h>
#define ASSERT(c) assert(c)
#endif
/* HaiCrypt-TP CTR mode IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
#define hcrypt_SetCtrIV(pki, nonce, iv) do { \
memset(&(iv)[0], 0, 128/8); \
memcpy(&(iv)[10], (pki), HCRYPT_PKI_SZ); \
hcrypt_XorStream(&(iv)[0], (nonce), 112/8); \
} while(0)
#define hcrypt_XorStream(dst, strm, len) do { \
int __XORSTREAMi; \
for (__XORSTREAMi = 0 \
;__XORSTREAMi < (int)(len) \
;__XORSTREAMi += 1) { \
(dst)[__XORSTREAMi] ^= (strm)[__XORSTREAMi]; \
} \
} while(0)
int hcryptCtx_SetSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Secret *secret);
int hcryptCtx_GenSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx);
int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg);
int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx);
int hcryptCtx_Tx_CloneKey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const hcrypt_Session* cryptoSrc);
int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto);
int hcryptCtx_Tx_PreSwitch(hcrypt_Session *crypto);
int hcryptCtx_Tx_Switch(hcrypt_Session *crypto);
int hcryptCtx_Tx_PostSwitch(hcrypt_Session *crypto);
int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *alt_sek);
int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto);
int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto, void *out_p[], size_t out_len_p[], int maxout);
int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg);
int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *msg, size_t msg_len);
#endif /* HCRYPT_H */

View file

@ -0,0 +1,97 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#ifndef HCRYPT_CTX_H
#define HCRYPT_CTX_H
#include <stdbool.h>
#include <sys/types.h>
#include "hcrypt.h"
#if !defined(HAISRT_VERSION_INT)
#include "haicrypt.h"
#include "hcrypt_msg.h"
#else
// Included by haisrt.h or similar
#include "haisrt/haicrypt.h"
#include "haisrt/hcrypt_msg.h"
#endif
typedef struct {
unsigned char *pfx; //Prefix described by transport msg info (in ctx)
unsigned char *payload;
size_t len; //Payload size
}hcrypt_DataDesc;
typedef struct tag_hcrypt_Ctx {
struct tag_hcrypt_Ctx * alt; /* Alternative ctx (even/odd) */
#define HCRYPT_CTX_F_MSG 0x00FF /* Aligned wiht message header flags */
#define HCRYPT_CTX_F_eSEK HCRYPT_MSG_F_eSEK
#define HCRYPT_CTX_F_oSEK HCRYPT_MSG_F_oSEK
#define HCRYPT_CTX_F_xSEK HCRYPT_MSG_F_xSEK
#define HCRYPT_CTX_F_ENCRYPT 0x0100 /* 0:decrypt 1:encrypt */
#define HCRYPT_CTX_F_ANNOUNCE 0x0200 /* Announce KM */
#define HCRYPT_CTX_F_TTSEND 0x0400 /* time to send */
unsigned flags;
#define hcryptCtx_GetKeyFlags(ctx) ((ctx)->flags & HCRYPT_CTX_F_xSEK)
#define hcryptCtx_GetKeyIndex(ctx) (((ctx)->flags & HCRYPT_CTX_F_xSEK)>>1)
#define HCRYPT_CTX_S_INIT 1
#define HCRYPT_CTX_S_SARDY 2 /* Security Association (KEK) ready */
#define HCRYPT_CTX_S_KEYED 3 /* Media Stream Encrypting Key (SEK) ready */
#define HCRYPT_CTX_S_ACTIVE 4 /* Announced and in use */
#define HCRYPT_CTX_S_DEPRECATED 5 /* Still announced but no longer used */
unsigned status;
#define HCRYPT_CTX_MODE_CLRTXT 0 /* NULL cipher (for tests) */
#define HCRYPT_CTX_MODE_AESECB 1 /* Electronic Code Book mode */
#define HCRYPT_CTX_MODE_AESCTR 2 /* Counter mode */
#define HCRYPT_CTX_MODE_AESCBC 3 /* Cipher-block chaining mode */
unsigned mode;
struct {
size_t key_len;
size_t pwd_len;
char pwd[HAICRYPT_PWD_MAX_SZ];
} cfg;
size_t salt_len;
unsigned char salt[HAICRYPT_SALT_SZ];
size_t sek_len;
unsigned char sek[HAICRYPT_KEY_MAX_SZ];
hcrypt_MsgInfo * msg_info; /* Transport message handler */
unsigned pkt_cnt; /* Key usage counter */
#define HCRYPT_CTX_MAX_KM_PFX_SZ 16
size_t KMmsg_len;
unsigned char KMmsg_cache[HCRYPT_CTX_MAX_KM_PFX_SZ + HCRYPT_MSG_KM_MAX_SZ];
#define HCRYPT_CTX_MAX_MS_PFX_SZ 16
unsigned char MSpfx_cache[HCRYPT_CTX_MAX_MS_PFX_SZ];
} hcrypt_Ctx;
#endif /* HCRYPT_CTX_H */

View file

@ -0,0 +1,199 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memcpy */
#include "hcrypt.h"
int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg)
{
ctx->mode = HCRYPT_CTX_MODE_AESCTR;
ctx->status = HCRYPT_CTX_S_INIT;
ctx->msg_info = crypto->msg_info;
if (cfg && hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) {
return(-1);
}
ctx->status = HCRYPT_CTX_S_SARDY;
return(0);
}
int hcryptCtx_Rx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *sek, size_t sek_len)
{
if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, sek, sek_len)) {
HCRYPT_LOG(LOG_ERR, "cryspr setkey[%d](sek) failed\n", hcryptCtx_GetKeyIndex(ctx));
return(-1);
}
memcpy(ctx->sek, sek, sek_len);
ctx->sek_len = sek_len;
HCRYPT_LOG(LOG_INFO, "updated context[%d]\n", hcryptCtx_GetKeyIndex(ctx));
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
ctx->status = HCRYPT_CTX_S_KEYED;
return(0);
}
/* Parse Keying Material message */
int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t msg_len)
{
size_t sek_len, salt_len;
unsigned char seks[HAICRYPT_KEY_MAX_SZ * 2];
int sek_cnt;
size_t kek_len = 0;
hcrypt_Ctx *ctx;
int do_pbkdf = 0;
if (NULL == crypto) {
HCRYPT_LOG(LOG_ERR, "Rx_ParseKM: invalid params: crypto=%p\n", crypto);
return(-1);
}
/* Validate message content */
{
if (msg_len <= HCRYPT_MSG_KM_OFS_SALT) {
HCRYPT_LOG(LOG_WARNING, "KMmsg length too small (%zd)\n", msg_len);
return(-1);
}
salt_len = hcryptMsg_KM_GetSaltLen(km_msg);
sek_len = hcryptMsg_KM_GetSekLen(km_msg);
if ((salt_len > HAICRYPT_SALT_SZ)
|| (sek_len > HAICRYPT_KEY_MAX_SZ)) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported salt/key length\n");
return(-1);
}
if ((16 != sek_len)
&& (24 != sek_len)
&& (32 != sek_len)) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported key length\n");
return(-1);
}
if (hcryptMsg_KM_HasBothSek(km_msg)) {
sek_cnt = 2;
} else {
sek_cnt = 1;
}
if (msg_len != (HCRYPT_MSG_KM_OFS_SALT + salt_len + (sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) {
HCRYPT_LOG(LOG_WARNING, "KMmsg length inconsistent (%zd,%zd,%zd)\n",
salt_len, sek_len, msg_len);
return(-1);
}
/* Check options support */
if ((HCRYPT_CIPHER_AES_CTR != km_msg[HCRYPT_MSG_KM_OFS_CIPHER])
|| (HCRYPT_AUTH_NONE != km_msg[HCRYPT_MSG_KM_OFS_AUTH])) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported option\n");
return(-1);
}
if (crypto->se != km_msg[HCRYPT_MSG_KM_OFS_SE]) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg invalid SE\n");
return(-1);
}
/* Check KEKI here and pick right key */
//>>todo
/*
* We support no key exchange,
* KEK is preshared or derived from a passphrase
*/
}
/* Pick the context updated by this KMmsg */
if (hcryptMsg_KM_HasBothSek(km_msg) && (NULL != crypto->ctx)) {
ctx = crypto->ctx->alt; /* 2 SEK KM, start with inactive ctx */
} else {
ctx = &crypto->ctx_pair[hcryptMsg_KM_GetKeyIndex(km_msg)];
}
if (NULL == ctx) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg invalid flags (no SEK)\n");
return(-1);
}
/* Check Salt and get if new */
if ((salt_len != ctx->salt_len)
|| (0 != memcmp(ctx->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len))) {
/* Salt changed (or 1st KMmsg received) */
memcpy(ctx->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len);
ctx->salt_len = salt_len;
do_pbkdf = 1; /* Impact on password derived kek */
}
/* Check SEK length and get if new */
if (sek_len != ctx->sek_len) {
/* Key length changed or 1st KMmsg received */
ctx->sek_len = sek_len;
do_pbkdf = 1; /* Impact on password derived kek */
}
/*
* Regenerate KEK if it is password derived
* and Salt or SEK length changed
*/
if (ctx->cfg.pwd_len && do_pbkdf) {
if (hcryptCtx_GenSecret(crypto, ctx)) {
return(-1);
}
ctx->status = HCRYPT_CTX_S_SARDY;
kek_len = sek_len; /* KEK changed */
}
/* Unwrap SEK(s) and set in context */
if (0 > crypto->cryspr->km_unwrap(crypto->cryspr_cb, seks,
&km_msg[HCRYPT_MSG_KM_OFS_SALT + salt_len],
(sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) {
HCRYPT_LOG(LOG_WARNING, "%s", "unwrap key failed\n");
return(-2); //Report unmatched shared secret
}
/*
* First SEK in KMmsg is eSEK if both SEK present
*/
hcryptCtx_Rx_Rekey(crypto, ctx,
((2 == sek_cnt) && (ctx->flags & HCRYPT_MSG_F_oSEK)) ? &seks[sek_len] : &seks[0],
sek_len);
/*
* Refresh KMmsg cache to detect Keying Material changes
*/
ctx->KMmsg_len = msg_len;
memcpy(ctx->KMmsg_cache, km_msg, msg_len);
/* update other (alternate) context if both SEK provided */
if (2 == sek_cnt) {
hcrypt_Ctx *alt = ctx->alt;
memcpy(alt->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len);
alt->salt_len = salt_len;
if (kek_len) { /* New or changed KEK */
// memcpy(&alt->aes_kek, &ctx->aes_kek, sizeof(alt->aes_kek));
alt->status = HCRYPT_CTX_S_SARDY;
}
hcryptCtx_Rx_Rekey(crypto, alt,
((2 == sek_cnt) && (alt->flags & HCRYPT_MSG_F_oSEK)) ? &seks[sek_len] : &seks[0],
sek_len);
alt->KMmsg_len = msg_len;
memcpy(alt->KMmsg_cache, km_msg, msg_len);
}
return(0);
}

View file

@ -0,0 +1,420 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memcpy */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <win/wintime.h>
#else
#include <sys/time.h>
#endif
#include "hcrypt.h"
int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg)
{
ctx->cfg.key_len = cfg->key_len;
ctx->mode = HCRYPT_CTX_MODE_AESCTR;
ctx->status = HCRYPT_CTX_S_INIT;
ctx->msg_info = crypto->msg_info;
if (hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) {
return(-1);
}
return(0);
}
int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx)
{
int iret;
ASSERT(HCRYPT_CTX_S_SARDY <= ctx->status);
/* Generate Salt */
ctx->salt_len = HAICRYPT_SALT_SZ;
if (0 > (iret = crypto->cryspr->prng(ctx->salt, ctx->salt_len))) {
HCRYPT_LOG(LOG_ERR, "PRNG(salt[%zd]) failed\n", ctx->salt_len);
return(iret);
}
/* Generate SEK */
ctx->sek_len = ctx->cfg.key_len;
if (0 > (iret = crypto->cryspr->prng(ctx->sek, ctx->sek_len))) {
HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", ctx->sek_len);
return(iret);
}
/* Set SEK in cryspr */
if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, ctx->sek, ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "cryspr setkey(sek[%zd]) failed\n", ctx->sek_len);
return(-1);
}
HCRYPT_LOG(LOG_NOTICE, "rekeyed crypto context[%d]\n", (ctx->flags & HCRYPT_CTX_F_xSEK)/2);
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
/* Regenerate KEK if Password-based (uses newly generated salt and sek_len) */
if ((0 < ctx->cfg.pwd_len)
&& (0 > (iret = hcryptCtx_GenSecret(crypto, ctx)))) {
return(iret);
}
/* Assemble the new Keying Material message */
if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, ctx, NULL))) {
return(iret);
}
if ((HCRYPT_CTX_S_KEYED <= ctx->alt->status)
&& hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) {
/*
* previous context KM announced in alternate (odd/even) KM,
* reassemble it without our KM
*/
hcryptCtx_Tx_AsmKM(crypto, ctx->alt, NULL);
}
/* Initialize the Media Stream message prefix cache */
ctx->msg_info->resetCache(ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, ctx->flags & HCRYPT_CTX_F_xSEK);
ctx->pkt_cnt = 1;
ctx->status = HCRYPT_CTX_S_KEYED;
return(0);
}
int hcryptCtx_Tx_CloneKey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const hcrypt_Session* cryptoSrc)
{
int iret;
ASSERT(HCRYPT_CTX_S_SARDY <= ctx->status);
const hcrypt_Ctx* ctxSrc = cryptoSrc->ctx;
if (!ctxSrc)
{
/* Probbly the context is not yet completely initialized, so
* use blindly the first context from the pair
*/
ctxSrc = &cryptoSrc->ctx_pair[0];
}
/* Copy SALT (instead of generating) */
ctx->salt_len = ctxSrc->salt_len;
memcpy(ctx->salt, ctxSrc->salt, ctx->salt_len);
/* Copy SEK */
ctx->sek_len = ctxSrc->sek_len;
memcpy(ctx->sek, ctxSrc->sek, ctx->sek_len);
/* Set SEK in cryspr */
if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, ctx->sek, ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "cryspr setkey(sek[%zd]) failed\n", ctx->sek_len);
return(-1);
}
HCRYPT_LOG(LOG_NOTICE, "clone-keyed crypto context[%d]\n", (ctx->flags & HCRYPT_CTX_F_xSEK)/2);
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
/* Regenerate KEK if Password-based (uses newly generated salt and sek_len) */
/* (note for CloneKey imp: it's expected that the same passphrase-salt pair
shall generate the same KEK. GenSecret also prints the KEK */
if ((0 < ctx->cfg.pwd_len)
&& (0 > (iret = hcryptCtx_GenSecret(crypto, ctx)))) {
return(iret);
}
/* Assemble the new Keying Material message */
if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, ctx, NULL))) {
return(iret);
}
if ((HCRYPT_CTX_S_KEYED <= ctx->alt->status)
&& hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) {
/*
* previous context KM announced in alternate (odd/even) KM,
* reassemble it without our KM
*/
hcryptCtx_Tx_AsmKM(crypto, ctx->alt, NULL);
}
/* Initialize the Media Stream message prefix cache */
ctx->msg_info->resetCache(ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, ctx->flags & HCRYPT_CTX_F_xSEK);
ctx->pkt_cnt = 1;
ctx->status = HCRYPT_CTX_S_KEYED;
return(0);
}
/*
* Refresh the alternate context from the current.
* Regenerates the SEK but keep the salt, doing so also
* preserve the KEK generated from secret password and salt.
*/
int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
hcrypt_Ctx *new_ctx;
int iret;
ASSERT(NULL != ctx);
ASSERT(HCRYPT_CTX_S_ACTIVE == ctx->status);
/* Pick the alternative (inactive) context */
new_ctx = ctx->alt;
ASSERT(HCRYPT_CTX_S_SARDY <= new_ctx->status);
/* Keep same KEK, configuration, and salt */
// memcpy(&new_ctx->aes_kek, &ctx->aes_kek, sizeof(new_ctx->aes_kek));
memcpy(&new_ctx->cfg, &ctx->cfg, sizeof(new_ctx->cfg));
new_ctx->salt_len = ctx->salt_len;
memcpy(new_ctx->salt, ctx->salt, HAICRYPT_SALT_SZ);
/* Generate new SEK */
new_ctx->sek_len = new_ctx->cfg.key_len;
HCRYPT_LOG(LOG_DEBUG, "refresh/generate SEK. salt_len=%d sek_len=%d\n", (int)new_ctx->salt_len, (int)new_ctx->sek_len);
if (0 > crypto->cryspr->prng(new_ctx->sek, new_ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", new_ctx->sek_len);
return(-1);
}
/* Cryspr's dependent key */
if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, new_ctx, new_ctx->sek, new_ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "refresh cryspr setkey(sek[%d]) failed\n", new_ctx->sek_len);
return(-1);
}
HCRYPT_PRINTKEY(new_ctx->sek, new_ctx->sek_len, "sek");
/* Assemble the new KMmsg with new and current SEK */
if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, new_ctx, ctx->sek))) {
return(iret);
}
/* Initialize the message prefix cache */
new_ctx->msg_info->resetCache(new_ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, new_ctx->flags & HCRYPT_MSG_F_xSEK);
new_ctx->pkt_cnt = 0;
new_ctx->status = HCRYPT_CTX_S_KEYED;
return(0);
}
/*
* Prepare context switch
* both odd & even keys announced
*/
int hcryptCtx_Tx_PreSwitch(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
ASSERT(NULL != ctx);
ASSERT(HCRYPT_CTX_S_ACTIVE == ctx->status);
ASSERT(HCRYPT_CTX_S_KEYED == ctx->alt->status);
ctx->alt->flags |= HCRYPT_CTX_F_ANNOUNCE;
ctx->alt->flags |= HCRYPT_CTX_F_TTSEND; //Send now
/* Stop announcing current context if next one contains its key */
if (hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) {
ctx->flags &= ~HCRYPT_CTX_F_ANNOUNCE;
}
return(0);
}
int hcryptCtx_Tx_Switch(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
ASSERT(HCRYPT_CTX_S_KEYED <= ctx->alt->status);
ctx->status = HCRYPT_CTX_S_DEPRECATED;
ctx->alt->status = HCRYPT_CTX_S_ACTIVE;
ctx->alt->flags |= HCRYPT_CTX_F_ANNOUNCE; // Already cleared if new KM has both SEK
crypto->ctx = ctx->alt;
return(0);
}
int hcryptCtx_Tx_PostSwitch(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
hcrypt_Ctx *old_ctx = ctx->alt;
/* Stop announcing old context (if announced) */
old_ctx->flags &= ~HCRYPT_CTX_F_ANNOUNCE;
old_ctx->status = HCRYPT_CTX_S_SARDY;
/* If current context KM announce both, reassemble it */
if (hcryptMsg_KM_HasBothSek(ctx->KMmsg_cache)) {
hcryptCtx_Tx_AsmKM(crypto, ctx, NULL);
}
return(0);
}
/* Assemble Keying Material message */
int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *alt_sek)
{
unsigned char *km_msg;
size_t msg_len;
int sek_cnt = (NULL == alt_sek ? 1 : 2);
unsigned char sek_buf[HAICRYPT_KEY_MAX_SZ * 2];
unsigned char *seks;
if (NULL == ctx) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context undefined\n");
return(-1);
}
msg_len = HCRYPT_MSG_KM_OFS_SALT
+ ctx->salt_len
+ (ctx->sek_len * sek_cnt)
+ HAICRYPT_WRAPKEY_SIGN_SZ;
km_msg = &ctx->KMmsg_cache[0];
ctx->KMmsg_len = 0;
memset(km_msg, 0, msg_len);
ctx->msg_info->resetCache(km_msg, HCRYPT_MSG_PT_KM,
2 == sek_cnt ? HCRYPT_MSG_F_xSEK : (ctx->flags & HCRYPT_MSG_F_xSEK));
/* crypto->KMmsg_cache[4..7]: KEKI=0 */
km_msg[HCRYPT_MSG_KM_OFS_CIPHER] = HCRYPT_CIPHER_AES_CTR;
km_msg[HCRYPT_MSG_KM_OFS_AUTH] = HCRYPT_AUTH_NONE;
km_msg[HCRYPT_MSG_KM_OFS_SE] = crypto->se;
hcryptMsg_KM_SetSaltLen(km_msg, ctx->salt_len);
hcryptMsg_KM_SetSekLen(km_msg, ctx->sek_len);
memcpy(&km_msg[HCRYPT_MSG_KM_OFS_SALT], ctx->salt, ctx->salt_len);
if (2 == sek_cnt) {
/* Even SEK first in dual SEK KMmsg */
if (HCRYPT_MSG_F_eSEK & ctx->flags) {
memcpy(&sek_buf[0], ctx->sek, ctx->sek_len);
memcpy(&sek_buf[ctx->sek_len], alt_sek, ctx->sek_len);
} else {
memcpy(&sek_buf[0], alt_sek, ctx->sek_len);
memcpy(&sek_buf[ctx->sek_len], ctx->sek, ctx->sek_len);
}
seks = sek_buf;
} else {
seks = ctx->sek;
}
if (0 > crypto->cryspr->km_wrap(crypto->cryspr_cb,
&km_msg[HCRYPT_MSG_KM_OFS_SALT + ctx->salt_len],
seks, sek_cnt * ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "%s", "wrap key failed\n");
return(-1);
}
ctx->KMmsg_len = msg_len;
return(0);
}
int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
ASSERT(NULL != ctx);
HCRYPT_LOG(LOG_DEBUG, "KM[%d] KEY STATUS: pkt_cnt=%u against ref.rate=%u and pre.announce=%u\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2,
ctx->pkt_cnt, crypto->km.refresh_rate, crypto->km.pre_announce);
if ((ctx->pkt_cnt > crypto->km.refresh_rate)
|| (ctx->pkt_cnt == 0)) { //rolled over
/*
* End of crypto period for current SEK,
* switch to other (even/odd) SEK
*/
HCRYPT_LOG(LOG_INFO, "KM[%d] Activated\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);
hcryptCtx_Tx_Switch(crypto);
} else
if ((ctx->pkt_cnt > (crypto->km.refresh_rate - crypto->km.pre_announce))
&& !(ctx->alt->flags & HCRYPT_CTX_F_ANNOUNCE)) {
/*
* End of crypto period approach for this SEK,
* prepare next SEK for announcement
*/
hcryptCtx_Tx_Refresh(crypto);
HCRYPT_LOG(LOG_INFO, "KM[%d] Pre-announced\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);
hcryptCtx_Tx_PreSwitch(crypto);
} else
if ((ctx->alt->status == HCRYPT_CTX_S_DEPRECATED)
&& (ctx->pkt_cnt > crypto->km.pre_announce)) {
/*
* Deprecated SEK is no longer needed (for late packets),
* decommission it
*/
HCRYPT_LOG(LOG_INFO, "KM[%d] Deprecated\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);
hcryptCtx_Tx_PostSwitch(crypto);
}
/* Check if it is time to send Keying Material */
if (timerisset(&crypto->km.tx_period)) { /* tx_period=0.0 -> out-of-stream Keying Material distribution */
struct timeval now, nxt_tx;
gettimeofday(&now, NULL);
timeradd(&crypto->km.tx_last, &crypto->km.tx_period, &nxt_tx);
if (timercmp(&now, &nxt_tx, >)) {
if (crypto->ctx_pair[0].flags & HCRYPT_CTX_F_ANNOUNCE) crypto->ctx_pair[0].flags |= HCRYPT_CTX_F_TTSEND;
if (crypto->ctx_pair[1].flags & HCRYPT_CTX_F_ANNOUNCE) crypto->ctx_pair[1].flags |= HCRYPT_CTX_F_TTSEND;
}
}
return(0);
}
int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto,
void *out_p[], size_t out_len_p[], int maxout ATR_UNUSED)
{
int i, nbout = 0;
ASSERT(maxout >= 2);
for (i=0; i<2; i++) {
if (crypto->ctx_pair[i].flags & HCRYPT_CTX_F_TTSEND) { /* Time To Send */
HCRYPT_LOG(LOG_DEBUG, "Send KMmsg[%d] len=%zd\n", i,
crypto->ctx_pair[i].KMmsg_len);
/* Send Keying Material */
out_p[nbout] = crypto->ctx_pair[i].KMmsg_cache;
out_len_p[nbout] = crypto->ctx_pair[i].KMmsg_len;
nbout++;
crypto->ctx_pair[i].flags &= ~HCRYPT_CTX_F_TTSEND;
}
}
if (nbout) {
struct timeval now;
gettimeofday(&now, NULL);
crypto->km.tx_last = now;
}
return(nbout);
}

View file

@ -0,0 +1,155 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#ifndef HCRYPT_MSG_H
#define HCRYPT_MSG_H
/*
* HaiCrypt Transport Message Header info
*/
#ifndef HCRYPT_DSP
#include <sys/types.h>
typedef uint32_t hcrypt_Pki;
#endif /* HCRYPT_DSP */
#define HCRYPT_MSG_VERSION 1 /* Current HaiCrypt version */
#define HCRYPT_MSG_SIGN (('H'-'@')<<10 | ('A'-'@')<<5 | ('I'-'@')) /* Haivision PnP Mfr ID 'HAI' */
#define HCRYPT_PKI_SZ 4 /* Packet Index size (CTR mode cipher) */
#define HCRYPT_MSG_PT_MS 1 /* Media stream */
#define HCRYPT_MSG_PT_KM 2 /* Keying Material */
#define HCRYPT_MSG_PT_RESV7 7 /* Reserved to dicriminate MPEG-TS packet (SyncByte=0x47) */
#define HCRYPT_MSG_F_eSEK 0x01 /* Even Stream Encrypting Key */
#define HCRYPT_MSG_F_oSEK 0x02 /* Odd Stream Encrypting Key */
#define HCRYPT_MSG_F_xSEK 0x03 /* Both Stream Encrypting Keys */
typedef struct {
int hdr_len; // data and control common prefix portion
int pfx_len; // Message Prefix len. Also payload offset
unsigned (*getKeyFlags)(unsigned char *msg);
hcrypt_Pki (*getPki)(unsigned char *msg, int nwko);
void (*setPki)(unsigned char *msg, hcrypt_Pki);
void (*resetCache)(unsigned char *pfx_cache, unsigned pkt_type, unsigned flags);
void (*indexMsg)(unsigned char *msg, unsigned char *pfx_cache);
int (*parseMsg)(unsigned char *msg);
}hcrypt_MsgInfo;
#define hcryptMsg_GetKeyIndex(mi,msg) ((mi)->getKeyFlags(msg)>>1)
#define hcryptMsg_GetPki(mi,msg,nwko) ((mi)->getPki(msg,nwko))
#define hcryptMsg_SetPki(mi,msg,pki) (mi)->setPki(msg, pki)
#define hcryptMsg_HasEvenSek(mi,msg) ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_eSEK)
#define hcryptMsg_HasOddSek(mi,msg) ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_oSEK)
#define hcryptMsg_HasBothSek(mi,msg) (HCRYPT_MSG_F_xSEK == ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_xSEK))
#define hcryptMsg_HasNoSek(mi,msg) (0 == ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_xSEK))
#define hcryptMsg_PaddedLen(len, fact) ((((len)+(fact)-1)/(fact))*(fact))
/*
* HaiCrypt KMmsg (Keying Material):
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x00 |0|Vers | PT | Sign | resv |KF |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x04 | KEKI |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x08 | Cipher | Auth | SE | Resv1 |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x0C | Resv2 | Slen/4 | Klen/4 |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x10 | Salt |
* | ... |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* | Wrap |
* | ... |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
#define HCRYPT_MSG_KM_OFS_VERSION 0
#define HCRYPT_MSG_KM_OFS_PT 0
#define HCRYPT_MSG_KM_OFS_SIGN 1
#define HCRYPT_MSG_KM_OFS_KFLGS 3
#define HCRYPT_MSG_KM_RSH_KFLGS 0 /* Right shift (in byte) */
#define HCRYPT_MSG_KM_OFS_KEKI 4
#define HCRYPT_MSG_KM_OFS_CIPHER 8
#define HCRYPT_MSG_KM_OFS_AUTH 9
#define HCRYPT_MSG_KM_OFS_SE 10
#define HCRYPT_MSG_KM_OFS_RESV2 12
#define HCRYPT_MSG_KM_OFS_SLEN 14
#define HCRYPT_MSG_KM_OFS_KLEN 15
#define HCRYPT_MSG_KM_OFS_SALT 16
#define HCRYPT_MSG_KM_MAX_SZ (0 \
+ HCRYPT_MSG_KM_OFS_SALT \
+ HAICRYPT_SALT_SZ \
+ (HAICRYPT_KEY_MAX_SZ * 2) \
+ HAICRYPT_WRAPKEY_SIGN_SZ)
#define HCRYPT_CIPHER_NONE 0
#define HCRYPT_CIPHER_AES_ECB 1
#define HCRYPT_CIPHER_AES_CTR 2
#define HCRYPT_CIPHER_AES_CBC 3
#define HCRYPT_AUTH_NONE 0
#define HCRYPT_SE_TSUDP 1
hcrypt_MsgInfo * hcryptMsg_STA_MsgInfo(void);
#define HCRYPT_SE_TSSRT 2
hcrypt_MsgInfo * hcryptMsg_SRT_MsgInfo(void);
#define hcryptMsg_KM_GetVersion(msg) (((msg)[HCRYPT_MSG_KM_OFS_VERSION]>>4)& 0xF)
#define hcryptMsg_KM_GetPktType(msg) (((msg)[HCRYPT_MSG_KM_OFS_PT]) & 0xF)
#define hcryptMsg_KM_GetSign(msg) (((msg)[HCRYPT_MSG_KM_OFS_SIGN]<<8) | (msg)[HCRYPT_MSG_KM_OFS_SIGN+1])
#define hcryptMsg_KM_GetKeyIndex(msg) (((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_xSEK)>>1)
#define hcryptMsg_KM_HasEvenSek(msg) ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_eSEK)
#define hcryptMsg_KM_HasOddSek(msg) ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_oSEK)
#define hcryptMsg_KM_HasBothSek(msg) (HCRYPT_MSG_F_xSEK == ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_xSEK))
#define hcryptMsg_KM_HasNoSek(msg) (0 == ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_xSEK))
#define hcryptMsg_KM_GetCipher(msg) ((msg)[HCRYPT_MSG_KM_OFS_CIPHER])
#define hcryptMsg_KM_GetAuth(msg) ((msg)[HCRYPT_MSG_KM_OFS_AUTH])
#define hcryptMsg_KM_GetSE(msg) ((msg)[HCRYPT_MSG_KM_OFS_SE])
#define hcryptMsg_KM_GetSaltLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_SLEN] * 4)
#define hcryptMsg_KM_GetSekLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_KLEN] * 4)
#define hcryptMsg_KM_SetSaltLen(msg,len)do {(msg)[HCRYPT_MSG_KM_OFS_SLEN] = (len)/4;} while(0)
#define hcryptMsg_KM_SetSekLen(msg,len) do {(msg)[HCRYPT_MSG_KM_OFS_KLEN] = (len)/4;} while(0)
#endif /* HCRYPT_MSG_H */

View file

@ -0,0 +1,146 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <stdlib.h> /* NULL */
#include <string.h> /* memcmp */
#include "hcrypt.h"
int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc,
unsigned char *in_pfx, unsigned char *data, size_t data_len)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx;
int nb = -1;
if ((NULL == crypto)
|| (NULL == data)) {
HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n");
return(nb);
}
ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_pfx)];
ASSERT(NULL != ctx); /* Header check should prevent this error */
ASSERT(NULL != crypto->cryspr); /* Header check should prevent this error */
crypto->ctx = ctx; /* Context of last received msg */
if (NULL == crypto->cryspr->ms_decrypt) {
HCRYPT_LOG(LOG_ERR, "%s", "cryspr had no decryptor\n");
} else if (ctx->status >= HCRYPT_CTX_S_KEYED) {
hcrypt_DataDesc indata;
indata.pfx = in_pfx;
indata.payload = data;
indata.len = data_len;
if (0 > (nb = crypto->cryspr->ms_decrypt(crypto->cryspr_cb, ctx, &indata, 1, NULL, NULL, NULL))) {
HCRYPT_LOG(LOG_ERR, "%s", "ms_decrypt failed\n");
} else {
nb = indata.len;
}
} else { /* No key received yet */
nb = 0;
}
return(nb);
}
int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc,
unsigned char *in_msg, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx;
int nbout = maxout;
int msg_type;
if ((NULL == crypto)
|| (NULL == in_msg)) {
HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n");
return(-1);
}
/* Validate HaiCrypt message */
if (0 > (msg_type = crypto->msg_info->parseMsg(in_msg))) {
return(-1);
}
switch(msg_type) {
case HCRYPT_MSG_PT_MS: /* MSmsg */
ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_msg)];
if ((NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n");
return(-1);
}
ASSERT(NULL != ctx); /* Header check should prevent this error */
ASSERT(NULL != crypto->cryspr); /* Header check should prevent this error */
crypto->ctx = ctx; /* Context of last received msg */
if (NULL == crypto->cryspr->ms_decrypt) {
HCRYPT_LOG(LOG_ERR, "%s", "cryspr had no decryptor\n");
nbout = -1;
} else if (ctx->status >= HCRYPT_CTX_S_KEYED) {
hcrypt_DataDesc indata;
indata.pfx = in_msg;
indata.payload = &in_msg[crypto->msg_info->pfx_len];
indata.len = in_len - crypto->msg_info->pfx_len;
if (crypto->cryspr->ms_decrypt(crypto->cryspr_cb, ctx, &indata, 1, out_p, out_len_p, &nbout)) {
HCRYPT_LOG(LOG_ERR, "%s", "ms_decrypt failed\n");
nbout = -1;
}
} else { /* No key received yet */
nbout = 0;
}
break;
case HCRYPT_MSG_PT_KM: /* KMmsg */
/* Even or Both SEKs check with even context */
ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_msg)];
ASSERT(NULL != ctx); /* Header check should prevent this error */
if ((ctx->status < HCRYPT_CTX_S_KEYED) /* No key deciphered yet */
|| (in_len != ctx->KMmsg_len) /* or not same size */
|| (0 != memcmp(ctx->KMmsg_cache, in_msg, in_len))) { /* or different */
nbout = hcryptCtx_Rx_ParseKM(crypto, in_msg, in_len);
//-2: unmatched shared secret
//-1: other failures
//0: success
} else {
nbout = 0;
}
if (NULL != out_p) out_p[0] = NULL;
if (NULL != out_len_p) out_len_p[0] = 0;
break;
default:
HCRYPT_LOG(LOG_WARNING, "%s", "unknown packet type\n");
nbout = 0;
break;
}
return(nbout);
}

View file

@ -0,0 +1,96 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
*****************************************************************************/
/*
* For now:
* Pre-shared or password derived KEK (Key Encrypting Key)
* Future:
* Certificate-based association
*/
#include <string.h> /* memcpy */
#include "hcrypt.h"
int hcryptCtx_SetSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Secret *secret)
{
int iret;
(void)crypto;
switch(secret->typ) {
case HAICRYPT_SECTYP_PRESHARED:
ASSERT(secret->len <= HAICRYPT_KEY_MAX_SZ);
ctx->cfg.pwd_len = 0;
/* KEK: Key Encrypting Key */
if (0 > (iret = crypto->cryspr->km_setkey(crypto->cryspr_cb,
(HCRYPT_CTX_F_ENCRYPT & ctx->flags ? true : false),
secret->str, secret->len))) {
HCRYPT_LOG(LOG_ERR, "km_setkey(pdkek[%zd]) failed (rc=%d)\n", secret->len, iret);
return(-1);
}
ctx->status = HCRYPT_CTX_S_SARDY;
break;
case HAICRYPT_SECTYP_PASSPHRASE:
ASSERT(secret->len <= sizeof(ctx->cfg.pwd));
memcpy(ctx->cfg.pwd, secret->str, secret->len);
ctx->cfg.pwd_len = secret->len;
/* KEK will be derived from password with Salt */
ctx->status = HCRYPT_CTX_S_SARDY;
break;
default:
HCRYPT_LOG(LOG_ERR, "Unknown secret type %d\n",
secret->typ);
return(-1);
}
return(0);
}
int hcryptCtx_GenSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx)
{
/*
* KEK need same length as the key it protects (SEK)
* KEK = PBKDF2(Pwd, LSB(64, Salt), Iter, Klen)
*/
unsigned char kek[HAICRYPT_KEY_MAX_SZ];
size_t kek_len = ctx->sek_len;
size_t pbkdf_salt_len = (ctx->salt_len >= HAICRYPT_PBKDF2_SALT_LEN
? HAICRYPT_PBKDF2_SALT_LEN
: ctx->salt_len);
int iret = 0;
(void)crypto;
iret = crypto->cryspr->km_pbkdf2(crypto->cryspr_cb, ctx->cfg.pwd, ctx->cfg.pwd_len,
&ctx->salt[ctx->salt_len - pbkdf_salt_len], pbkdf_salt_len,
HAICRYPT_PBKDF2_ITER_CNT, kek_len, kek);
if(iret) {
HCRYPT_LOG(LOG_ERR, "km_pbkdf2() failed (rc=%d)\n", iret);
return(-1);
}
HCRYPT_PRINTKEY(ctx->cfg.pwd, ctx->cfg.pwd_len, "pwd");
HCRYPT_PRINTKEY(kek, kek_len, "kek");
/* KEK: Key Encrypting Key */
if (0 > (iret = crypto->cryspr->km_setkey(crypto->cryspr_cb, (HCRYPT_CTX_F_ENCRYPT & ctx->flags ? true : false), kek, kek_len))) {
HCRYPT_LOG(LOG_ERR, "km_setkey(pdkek[%zd]) failed (rc=%d)\n", kek_len, iret);
return(-1);
}
return(0);
}

View file

@ -0,0 +1,174 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <sys/types.h>
#include <stdlib.h> /* NULL */
#include <string.h> /* memcpy */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdint.h>
#else
#include <arpa/inet.h> /* htonl */
#endif
#include "hcrypt.h"
int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_pp)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
ASSERT(NULL != crypto);
ASSERT(NULL != crypto->cryspr);
int pad_factor = (HCRYPT_CTX_MODE_AESECB == crypto->ctx->mode ? 128/8 : 1);
#ifndef _WIN32
ASSERT(crypto->inbuf != NULL);
#endif
size_t in_len = crypto->msg_info->pfx_len + hcryptMsg_PaddedLen(data_len, pad_factor);
*in_pp = crypto->inbuf;
if (in_len > crypto->inbuf_siz) {
*in_pp = NULL;
return(-1);
}
return(crypto->msg_info->pfx_len);
}
int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = NULL;
int nbout = 0;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))
|| (NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "ManageKeys: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
/* Manage Key Material (refresh, announce, decommission) */
hcryptCtx_Tx_ManageKM(crypto);
if (NULL == (ctx = crypto->ctx)) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
return(-1);
}
ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE);
nbout = hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout);
return(nbout);
}
int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = NULL;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))){
HCRYPT_LOG(LOG_ERR, "GetKeyFlags: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
return(hcryptCtx_GetKeyFlags(ctx));
}
int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc,
unsigned char *in_pfx, unsigned char *in_data, size_t in_len)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = NULL;
int nbout = 0;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))){
HCRYPT_LOG(LOG_ERR, "Tx_Data: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
/* Get/Set packet index */
ctx->msg_info->indexMsg(in_pfx, ctx->MSpfx_cache);
/* Encrypt */
{
hcrypt_DataDesc indata;
indata.pfx = in_pfx;
indata.payload = in_data;
indata.len = in_len;
if (0 > (nbout = crypto->cryspr->ms_encrypt(crypto->cryspr_cb, ctx, &indata, 1, NULL, NULL, NULL))) {
HCRYPT_LOG(LOG_ERR, "%s", "ms_encrypt failed\n");
return(nbout);
}
}
ctx->pkt_cnt++;
return(nbout);
}
int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc,
unsigned char *in_msg, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = NULL;
int nb, nbout = 0;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))
|| (NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "Tx_Process: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
/* Manage Key Material (refresh, announce, decommission) */
hcryptCtx_Tx_ManageKM(crypto);
if (NULL == (ctx = crypto->ctx)) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
return(-1);
}
ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE);
nbout += hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout);
/* Get packet index */
ctx->msg_info->indexMsg(in_msg, ctx->MSpfx_cache);
/* Encrypt */
nb = maxout - nbout;
{
hcrypt_DataDesc indata;
indata.pfx = in_msg;
indata.payload = &in_msg[ctx->msg_info->pfx_len];
indata.len = in_len - ctx->msg_info->pfx_len;
if (crypto->cryspr->ms_encrypt(crypto->cryspr_cb, ctx, &indata, 1, &out_p[nbout], &out_len_p[nbout], &nb)) {
HCRYPT_LOG(LOG_ERR, "%s", "ms_encrypt failed\n");
return(nbout);
}
}
nbout += nb;
ctx->pkt_cnt++;
return(nbout);
}

View file

@ -0,0 +1,224 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-07-11 (jdube)
HaiCrypt initial implementation.
*****************************************************************************/
#include <string.h> /* memcpy */
#include <stdio.h>
#include <haicrypt.h>
#include "hcrypt.h"
#ifndef _WIN32
/* RFC6070 PBKDF2 Tests Vectors */
static struct TestVector {
size_t pwd_len;
const char *pwd;
size_t salt_len;
const unsigned char *salt;
int cnt;
size_t dk_len;
unsigned char dk[32];
} tv[] = {
{ /* 1 */
.pwd_len = 8, .pwd = "password",
.salt_len = 4, .salt = (unsigned char *)"salt",
.cnt = 1,
.dk_len = 20,
.dk = {
0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
0x2f, 0xe0, 0x37, 0xa6
}
},
{ /* 2 */
.pwd_len = 8, .pwd = "password",
.salt_len = 4, .salt = (unsigned char *)"salt",
.cnt = 2,
.dk_len = 20,
.dk = {
0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
0xd8, 0xde, 0x89, 0x57
}
},
{ /* 3 */
.pwd_len = 8, .pwd = "password",
.salt_len = 4, .salt = (unsigned char *)"salt",
.cnt = 4096,
.dk_len = 20,
.dk = {
0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
0x65, 0xa4, 0x29, 0xc1
}
},
{ /* 4 */
.pwd_len = 8, .pwd = "password",
.salt_len = 4, .salt = (unsigned char *)"salt",
.cnt = 16777216,
.dk_len = 20,
.dk = {
0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
0x26, 0x34, 0xe9, 0x84
}
},
{ /* 5 */
.pwd_len = 24, .pwd = "passwordPASSWORDpassword",
.salt_len = 36, .salt = (unsigned char *)"saltSALTsaltSALTsaltSALTsaltSALTsalt",
.cnt = 4096,
.dk_len = 25,
.dk = {
0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
0x38
}
},
{ /* 6 */
.pwd_len = 9, .pwd = "pass\0word",
.salt_len = 5, .salt = (unsigned char *)"sa\0lt",
.cnt = 4096,
.dk_len = 16,
.dk = {
0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3
}
},
};
#include <sys/time.h>
static int hc_ut_pbkdf2(unsigned verbose)
{
int i;
int nbt = sizeof(tv)/sizeof(tv[0]);
int nbe = 0;
unsigned char dk[32];
struct timeval tstart, tstop, tdiff;
for (i=0; i<nbt; i++) {
if (verbose) {
printf("PBKDF2 test vector %d", i+1);
fflush(stdout);
gettimeofday(&tstart, NULL);
}
hcrypt_pbkdf2_hmac_sha1(tv[i].pwd, tv[i].pwd_len,
tv[i].salt, tv[i].salt_len,
tv[i].cnt, tv[i].dk_len, dk);
if (verbose) {
gettimeofday(&tstop, NULL);
timersub(&tstop, &tstart, &tdiff);
}
if(memcmp(dk, tv[i].dk, tv[i].dk_len)) {
if (verbose) {
printf(": failed in %lu.%06lu sec\n", tdiff.tv_sec, (unsigned long)tdiff.tv_usec);
} else {
printf("PBKDF2 test vector %d: failed\n", i+1);
}
nbe++;
} else if (verbose) {
printf(": passed in %lu.%06lu sec\n", tdiff.tv_sec, (unsigned long)tdiff.tv_usec);
}
}
return(nbe);
}
int hc_ut_encrypt_ctr_speed(void)
{
static HaiCrypt_Secret secret = {
.typ = HAICRYPT_SECTYP_PASSPHRASE,
.len = 12,
.str = "000000000000"
};
HaiCrypt_Cfg crypto_cfg;
HaiCrypt_Handle hcrypto;
struct timeval tstart, tstop, tdiff;
unsigned char pkt[1500];
int nbe = 0;
int i;
#ifdef HAICRYPT_USE_OPENSSL_EVP_CBC
HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP_CBC(void); /* OpenSSL EVP interface CBC mode*/
#endif
memset(&crypto_cfg, 0, sizeof(crypto_cfg));
crypto_cfg.flags = HAICRYPT_CFG_F_CRYPTO | HAICRYPT_CFG_F_TX;
crypto_cfg.xport = HAICRYPT_XPT_SRT;
#ifdef HAICRYPT_USE_OPENSSL_EVP_CBC
crypto_cfg.cipher = HaiCryptCipher_OpenSSL_EVP_CBC();
#else
crypto_cfg.cipher = HaiCryptCipher_Get_Instance();
#endif
crypto_cfg.key_len = (size_t)128/8;
crypto_cfg.data_max_len = HAICRYPT_DEF_DATA_MAX_LENGTH; //MTU
crypto_cfg.km_tx_period_ms = 0;//No HaiCrypt KM inject period, handled in SRT;
crypto_cfg.km_refresh_rate_pkt = HAICRYPT_DEF_KM_REFRESH_RATE;
crypto_cfg.km_pre_announce_pkt = 0x10000; //HAICRYPT_DEF_KM_PRE_ANNOUNCE;
memcpy(&crypto_cfg.secret, &secret, sizeof(crypto_cfg.secret));
if (HaiCrypt_Create(&crypto_cfg, &hcrypto)) {
fprintf(stderr, "haicrypt: HaiCrypt_Create failed\n");
return(1);
}
for (i=0; i<1500; i++) {
pkt[i] = i & 0xff;
}
#define UT_NBPKTS 100000L
#define UT_PKTSZ (7*188)
gettimeofday(&tstart, NULL);
for (i=0; i<UT_NBPKTS; i++) {
if (0 > HaiCrypt_Tx_Data(hcrypto, &pkt[0], &pkt[16], UT_PKTSZ)) nbe++;
if (0 == (i % 1000)) {
printf("\b\b\b\b\b\b%6d", i);
fflush(stdout);
}
}
gettimeofday(&tstop, NULL);
timersub(&tstop, &tstart, &tdiff);
printf("\nhaicrypt: encrypted %ld packets in %lu.%06lu sec (%ld.%03ld kbps)\n",
UT_NBPKTS, tdiff.tv_sec, (unsigned long)tdiff.tv_usec,
(((UT_NBPKTS * UT_PKTSZ*10)/((tdiff.tv_sec*10) + (tdiff.tv_usec/100))) / 1000),
(((UT_NBPKTS * UT_PKTSZ*10)/((tdiff.tv_sec*10) + (tdiff.tv_usec/100))) % 1000));
HaiCrypt_Close(hcrypto);
return(nbe);
}
int main(int argc, char *argv[])
{
int nbe = 0;
(void)argc;
(void)argv;
nbe += hc_ut_encrypt_ctr_speed();
nbe += hc_ut_pbkdf2(1);
printf("haicrypt unit test %s: %d errors found\n", nbe ? "failed" : "passed", nbe);
return(nbe);
}
#endif // _WIN32

View file

@ -0,0 +1,171 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memset, memcpy */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h> /* htonl, ntohl */
#endif
#include "hcrypt.h"
/*
* HaiCrypt SRT (Secure Reliable Transport) Media Stream (MS) Msg Prefix:
* This is UDT data header with Crypto Key Flags (KF) added.
* Header is in 32bit host order words in the context of the functions of this handler.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x00 |0| Packet Sequence Number (pki) |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x04 |FF |o|KF | Message Number |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x08 | Time Stamp |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x0C | Destination Socket ID) |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* | Payload... |
*/
/*
* HaiCrypt Standalone Transport Keying Material (KM) Msg header kept in SRT
* Message and cache maintained in network order
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x00 |0|Vers | PT | Sign | resv |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* ... .
*/
#define HCRYPT_MSG_SRT_HDR_SZ 16
#define HCRYPT_MSG_SRT_PFX_SZ 16
#define HCRYPT_MSG_SRT_OFS_PKI 0
#define HCRYPT_MSG_SRT_OFS_MSGNO 4
#define HCRYPT_MSG_SRT_SHF_KFLGS 27 //shift
static hcrypt_MsgInfo _hcMsg_SRT_MsgInfo;
static unsigned hcryptMsg_SRT_GetKeyFlags(unsigned char *msg)
{
uint32_t msgno;
memcpy(&msgno, &msg[HCRYPT_MSG_SRT_OFS_MSGNO], sizeof(msgno)); //header is in host order
return((unsigned)((msgno >> HCRYPT_MSG_SRT_SHF_KFLGS) & HCRYPT_MSG_F_xSEK));
}
static hcrypt_Pki hcryptMsg_SRT_GetPki(unsigned char *msg, int nwkorder)
{
hcrypt_Pki pki;
memcpy(&pki, &msg[HCRYPT_MSG_SRT_OFS_PKI], sizeof(pki)); //header is in host order
return (nwkorder ? htonl(pki) : pki);
}
static void hcryptMsg_SRT_SetPki(unsigned char *msg, hcrypt_Pki pki)
{
memcpy(&msg[HCRYPT_MSG_SRT_OFS_PKI], &pki, sizeof(pki)); //header is in host order
}
static void hcryptMsg_SRT_ResetCache(unsigned char *pfx_cache, unsigned pkt_type, unsigned kflgs)
{
switch(pkt_type) {
case HCRYPT_MSG_PT_MS: /* Media Stream */
/* Nothing to do, header filled by protocol */
break;
case HCRYPT_MSG_PT_KM: /* Keying Material */
pfx_cache[HCRYPT_MSG_KM_OFS_VERSION] = (unsigned char)((HCRYPT_MSG_VERSION << 4) | pkt_type); // version || PT
pfx_cache[HCRYPT_MSG_KM_OFS_SIGN] = (unsigned char)((HCRYPT_MSG_SIGN >> 8) & 0xFF); // Haivision PnP Mfr ID
pfx_cache[HCRYPT_MSG_KM_OFS_SIGN+1] = (unsigned char)(HCRYPT_MSG_SIGN & 0xFF);
pfx_cache[HCRYPT_MSG_KM_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx
break;
default:
break;
}
}
static void hcryptMsg_SRT_IndexMsg(unsigned char *msg, unsigned char *pfx_cache)
{
(void)msg;
(void)pfx_cache;
return; //nothing to do, header and index maintained by SRT
}
static int hcryptMsg_SRT_ParseMsg(unsigned char *msg)
{
int rc;
if ((HCRYPT_MSG_VERSION == hcryptMsg_KM_GetVersion(msg)) /* Version 1 */
&& (HCRYPT_MSG_PT_KM == hcryptMsg_KM_GetPktType(msg)) /* Keying Material */
&& (HCRYPT_MSG_SIGN == hcryptMsg_KM_GetSign(msg))) { /* 'HAI' PnP Mfr ID */
rc = HCRYPT_MSG_PT_KM;
} else {
//Assume it's data.
//SRT does not call this for MS msg
rc = HCRYPT_MSG_PT_MS;
}
switch(rc) {
case HCRYPT_MSG_PT_MS:
if (hcryptMsg_HasNoSek(&_hcMsg_SRT_MsgInfo, msg)
|| hcryptMsg_HasBothSek(&_hcMsg_SRT_MsgInfo, msg)) {
HCRYPT_LOG(LOG_ERR, "invalid MS msg flgs: %02x\n",
hcryptMsg_GetKeyIndex(&_hcMsg_SRT_MsgInfo, msg));
return(-1);
}
break;
case HCRYPT_MSG_PT_KM:
if (HCRYPT_SE_TSSRT != hcryptMsg_KM_GetSE(msg)) { //Check Stream Encapsulation (SE)
HCRYPT_LOG(LOG_ERR, "invalid KM msg SE: %d\n",
hcryptMsg_KM_GetSE(msg));
return(-1);
}
if (hcryptMsg_KM_HasNoSek(msg)) {
HCRYPT_LOG(LOG_ERR, "invalid KM msg flgs: %02x\n",
hcryptMsg_KM_GetKeyIndex(msg));
return(-1);
}
break;
default:
HCRYPT_LOG(LOG_ERR, "invalid pkt type: %d\n", rc);
rc = 0; /* unknown packet type */
break;
}
return(rc); /* -1: error, 0: unknown: >0: PT */
}
static hcrypt_MsgInfo _hcMsg_SRT_MsgInfo;
hcrypt_MsgInfo *hcryptMsg_SRT_MsgInfo(void)
{
_hcMsg_SRT_MsgInfo.hdr_len = HCRYPT_MSG_SRT_HDR_SZ;
_hcMsg_SRT_MsgInfo.pfx_len = HCRYPT_MSG_SRT_PFX_SZ;
_hcMsg_SRT_MsgInfo.getKeyFlags = hcryptMsg_SRT_GetKeyFlags;
_hcMsg_SRT_MsgInfo.getPki = hcryptMsg_SRT_GetPki;
_hcMsg_SRT_MsgInfo.setPki = hcryptMsg_SRT_SetPki;
_hcMsg_SRT_MsgInfo.resetCache = hcryptMsg_SRT_ResetCache;
_hcMsg_SRT_MsgInfo.indexMsg = hcryptMsg_SRT_IndexMsg;
_hcMsg_SRT_MsgInfo.parseMsg = hcryptMsg_SRT_ParseMsg;
return(&_hcMsg_SRT_MsgInfo);
}

View file

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

11
trunk/3rdparty/srt-1-fit/nuget.config vendored Normal file
View file

@ -0,0 +1,11 @@
<configuration>
<config>
<add key="dependencyVersion" value="Highest" />
<add key="globalPackagesFolder" value="c:\packages" />
<add key="repositoryPath" value="packages" />
</config>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<!--<add key="Local Source" value="D:\Data\Dev\LocalNuget" />-->
</packageSources>
</configuration>

View file

@ -0,0 +1,115 @@
# original file from obs-studio:
# https://github.com/obsproject/obs-studio
# /cmake/Modules/FindMbedTLS.cmake
#
# Once done these will be defined:
#
# LIBMBEDTLS_FOUND
# LIBMBEDTLS_INCLUDE_DIRS
# LIBMBEDTLS_LIBRARIES
#
# For use in OBS:
#
# MBEDTLS_INCLUDE_DIR
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_check_modules(_MBEDTLS QUIET mbedtls)
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_lib_suffix 64)
else()
set(_lib_suffix 32)
endif()
# If we're on MacOS or Linux, please try to statically-link mbedtls.
if(STATIC_MBEDTLS AND (APPLE OR UNIX))
set(_MBEDTLS_LIBRARIES libmbedtls.a)
set(_MBEDCRYPTO_LIBRARIES libmbedcrypto.a)
set(_MBEDX509_LIBRARIES libmbedx509.a)
endif()
find_path(MBEDTLS_INCLUDE_DIR
NAMES mbedtls/ssl.h
HINTS
${MBEDTLS_PREFIX}
PATHS
/usr/include /usr/local/include /opt/local/include /sw/include
PATH_SUFFIXES
include)
find_library(MBEDTLS_LIB
NAMES ${_MBEDTLS_LIBRARIES} mbedtls libmbedtls
HINTS
${MBEDTLS_PREFIX}
PATHS
/usr/lib /usr/local/lib /opt/local/lib /sw/lib
PATH_SUFFIXES
lib${_lib_suffix} lib
libs${_lib_suffix} libs
bin${_lib_suffix} bin
../lib${_lib_suffix} ../lib
../libs${_lib_suffix} ../libs
../bin${_lib_suffix} ../bin)
find_library(MBEDCRYPTO_LIB
NAMES ${_MBEDCRYPTO_LIBRARIES} mbedcrypto libmbedcrypto
HINTS
${MBEDTLS_PREFIX}
PATHS
/usr/lib /usr/local/lib /opt/local/lib /sw/lib
PATH_SUFFIXES
lib${_lib_suffix} lib
libs${_lib_suffix} libs
bin${_lib_suffix} bin
../lib${_lib_suffix} ../lib
../libs${_lib_suffix} ../libs
../bin${_lib_suffix} ../bin)
find_library(MBEDX509_LIB
NAMES ${_MBEDX509_LIBRARIES} mbedx509 libmbedx509
HINTS
${MBEDTLS_PREFIX}
PATHS
/usr/lib /usr/local/lib /opt/local/lib /sw/lib
PATH_SUFFIXES
lib${_lib_suffix} lib
libs${_lib_suffix} libs
bin${_lib_suffix} bin
../lib${_lib_suffix} ../lib
../libs${_lib_suffix} ../libs
../bin${_lib_suffix} ../bin)
# Sometimes mbedtls is split between three libs, and sometimes it isn't.
# If it isn't, let's check if the symbols we need are all in MBEDTLS_LIB.
if(MBEDTLS_LIB AND NOT MBEDCRYPTO_LIB AND NOT MBEDX509_LIB)
set(CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_LIB})
set(CMAKE_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR})
check_symbol_exists(mbedtls_x509_crt_init "mbedtls/x509_crt.h" MBEDTLS_INCLUDES_X509)
check_symbol_exists(mbedtls_sha256_init "mbedtls/sha256.h" MBEDTLS_INCLUDES_CRYPTO)
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
endif()
# If we find all three libraries, then go ahead.
if(MBEDTLS_LIB AND MBEDCRYPTO_LIB AND MBEDX509_LIB)
set(LIBMBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR})
set(LIBMBEDTLS_LIBRARIES ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB} ${MBEDX509_LIB})
set(MBEDTLS_INCLUDE_DIRS ${LIBMBEDTLS_INCLUDE_DIRS})
set(MBEDTLS_LIBRARIES ${LIBMBEDTLS_LIBRARIES})
# Otherwise, if we find MBEDTLS_LIB, and it has both CRYPTO and x509
# within the single lib (i.e. a windows build environment), then also
# feel free to go ahead.
elseif(MBEDTLS_LIB AND MBEDTLS_INCLUDES_CRYPTO AND MBEDTLS_INCLUDES_X509)
set(LIBMBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR})
set(LIBMBEDTLS_LIBRARIES ${MBEDTLS_LIB})
set(MBEDTLS_INCLUDE_DIRS ${LIBMBEDTLS_INCLUDE_DIRS})
set(MBEDTLS_LIBRARIES ${LIBMBEDTLS_LIBRARIES})
endif()
# Now we've accounted for the 3-vs-1 library case:
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Libmbedtls DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS)
mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS)

56
trunk/3rdparty/srt-1-fit/scripts/check-deps vendored Executable file
View file

@ -0,0 +1,56 @@
#!/bin/bash
# Now Check if Tcl is installed, run it if so. The backslash extends the comment and hides the line below against Tcl interpreter \
exec tclsh "$0" "$@" || echo "Please install 'tcl' package first - it's required to run any other scripts here." && exit 1
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2018 Haivision Systems Inc.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
if { [catch {package require Tcl 8.5}] } {
puts stderr "Tcl version at least 8.5 required, please upgrade"
exit 1
}
set ok 1
if { [catch {exec pkg-config --exists openssl}] } {
set ok 0
puts "Openssl: NOT INSTALLED, please install (libssl-dev\[el\], openssl-dev\[el\] etc.)"
} else {
puts "Openssl: found version [exec pkg-config --modversion openssl] -- ok"
}
set nothave [catch {set cmake [exec cmake --version]}]
if { $nothave } {
puts "CMake version >= 2.8 required - please install cmake"
set ok 0
} else {
set cmakel1 [lindex [split $cmake \n] 0]
set cv [lindex $cmakel1 end]
if { [package vcompare $cv 2.8] == -1 } {
puts "CMake version >= 2.8 required - please upgrade cmake"
set ok 0
} else {
puts "Cmake version $cv -- ok."
}
}
# May others also apply
if { $ok } {
puts "All dependencies satisfied, you should be good to go."
exit 0
}
puts "Please fix the above findings before compiling"
exit 1

View file

@ -0,0 +1,35 @@
rem Create empty directories for package bundle
@echo off
IF "%PLATFORM%"=="x86" (
SET FOLDER_PLATFORM="32"
) ELSE IF "%PLATFORM%"=="x64" (
SET FOLDER_PLATFORM="64"
) ELSE (
echo "Platform %PLATFORM% is not supported"
exit 1
)
md %APPVEYOR_BUILD_FOLDER%\package
md %APPVEYOR_BUILD_FOLDER%\package\include
md %APPVEYOR_BUILD_FOLDER%\package\include\win
md %APPVEYOR_BUILD_FOLDER%\package\bin
md %APPVEYOR_BUILD_FOLDER%\package\lib
md %APPVEYOR_BUILD_FOLDER%\package\openssl-win%FOLDER_PLATFORM%
rem Gather SRT includes, binaries and libs
copy %APPVEYOR_BUILD_FOLDER%\version.h %APPVEYOR_BUILD_FOLDER%\package\include\
copy %APPVEYOR_BUILD_FOLDER%\srtcore\*.h %APPVEYOR_BUILD_FOLDER%\package\include\
copy %APPVEYOR_BUILD_FOLDER%\haicrypt\*.h %APPVEYOR_BUILD_FOLDER%\package\include\
copy %APPVEYOR_BUILD_FOLDER%\common\*.h %APPVEYOR_BUILD_FOLDER%\package\include\
copy %APPVEYOR_BUILD_FOLDER%\common\win\*.h %APPVEYOR_BUILD_FOLDER%\package\include\win\
copy %APPVEYOR_BUILD_FOLDER%\%CONFIGURATION%\*.exe %APPVEYOR_BUILD_FOLDER%\package\bin\
copy %APPVEYOR_BUILD_FOLDER%\%CONFIGURATION%\*.dll %APPVEYOR_BUILD_FOLDER%\package\bin\
copy %APPVEYOR_BUILD_FOLDER%\%CONFIGURATION%\*.lib %APPVEYOR_BUILD_FOLDER%\package\lib\
IF "%CONFIGURATION%"=="Debug" (
copy %APPVEYOR_BUILD_FOLDER%\%CONFIGURATION%\*.pdb %APPVEYOR_BUILD_FOLDER%\package\bin\
)
rem gather 3rd party openssl elements
(robocopy c:\openssl-win%FOLDER_PLATFORM%\ %APPVEYOR_BUILD_FOLDER%\package\openssl-win%FOLDER_PLATFORM% /s /e /np) ^& IF %ERRORLEVEL% GTR 1 exit %ERRORLEVEL%
exit 0

View file

@ -0,0 +1,115 @@
#!/usr/bin/tclsh
set cachefile [lindex $argv 0]
if { $cachefile == "" } {
puts stderr "Usage: [file tail $argv0] <existing CMakeCache.txt file>"
exit 1
}
set struct {
name
type
value
description
}
set fd [open $cachefile r]
set cached ""
set dbase ""
while {[gets $fd line] != -1 } {
set line [string trim $line]
# Hash comment
if { [string index $line 0] == "#" } {
continue
}
# empty line
if { $line == "" } {
set cached ""
continue
}
if { [string range $line 0 1] == "//" } {
set linepart [string range $line 2 end]
# Variable description. Add to cache.
if { $cached != "" && [string index $cached end] != " " && [string index $linepart 0] != " " } {
append cached " "
}
append cached $linepart
}
# Possibly a variable
if [string is alpha [string index $line 0]] {
# Note: this skips variables starting grom underscore.
if { [string range $line 0 5] == "CMAKE_" } {
# Skip variables with CMAKE_ prefix, they are internal.
continue
}
lassign [split $line =] vartype value
lassign [split $vartype :] var type
# Store the variable now
set storage [list $var $type $value $cached]
set cached ""
lappend dbase $storage
continue
}
#puts stderr "Ignored line: $line"
# Ignored.
}
# Now look over the stored variables
set lenlimit 80
foreach stor $dbase {
lassign $stor {*}$struct
if { [string length $description] > $lenlimit } {
set description [string range $description 0 $lenlimit-2]...
}
if { $type in {STATIC INTERNAL} } {
continue
}
# Check special case of CXX to turn back to c++.
set pos [string first CXX $name]
if { $pos != -1 } {
# Check around, actually after XX should be no letter.
if { $pos+3 >= [string length $name] || ![string is alpha [string index $name $pos+3]] } {
set name [string replace $name $pos $pos+2 C++]
}
}
set optname [string tolower [string map {_ -} $name]]
# Variables of type bool are just empty.
# Variables of other types must have =<value> added.
# Lowercase cmake type will be used here.
set optassign ""
set def ""
if { $type != "BOOL" } {
set optassign "=<[string tolower $type]>"
} else {
# Supply default for boolean option
set def " (default: $value)"
}
puts " $optname$optassign \"$description$def\""
}

View file

@ -0,0 +1,19 @@
# code copied from https://crascit.com/2015/07/25/cmake-gtest/
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(
googletest
SOURCE_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-src"
BINARY_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-build"
GIT_REPOSITORY
https://github.com/google/googletest.git
GIT_TAG release-1.8.1
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View file

@ -0,0 +1,32 @@
# the following code to fetch googletest
# is inspired by and adapted after https://crascit.com/2015/07/25/cmake-gtest/
# download and unpack googletest at configure time
macro(fetch_googletest _download_module_path _download_root)
set(GOOGLETEST_DOWNLOAD_ROOT ${_download_root})
configure_file(
${_download_module_path}/googletest-download.cmake
${_download_root}/CMakeLists.txt
@ONLY
)
unset(GOOGLETEST_DOWNLOAD_ROOT)
execute_process(
COMMAND
"${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
WORKING_DIRECTORY
${_download_root}
)
execute_process(
COMMAND
"${CMAKE_COMMAND}" --build .
WORKING_DIRECTORY
${_download_root}
)
# adds the targers: gtest, gtest_main, gmock, gmock_main
add_subdirectory(
${_download_root}/googletest-src
${_download_root}/googletest-build
)
endmacro()

View file

@ -0,0 +1,291 @@
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2018 Haivision Systems Inc.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
include(CheckCXXSourceCompiles)
# Useful for combinging paths
function(adddirname prefix lst out_lst)
set(output)
foreach(item ${lst})
list(APPEND output "${prefix}/${item}")
endforeach()
set(${out_lst} ${${out_lst}} ${output} PARENT_SCOPE)
endfunction()
# Splits a version formed as "major.minor.patch" recorded in variable 'prefix'
# and writes it into variables started with 'prefix' and ended with _MAJOR, _MINOR and _PATCH.
MACRO(set_version_variables prefix value)
string(REPLACE "." ";" VERSION_LIST ${value})
list(GET VERSION_LIST 0 ${prefix}_MAJOR)
list(GET VERSION_LIST 1 ${prefix}_MINOR)
list(GET VERSION_LIST 2 ${prefix}_PATCH)
set(${prefix}_DEFINESTR "")
ENDMACRO(set_version_variables)
# Sets given variable to 1, if the condition that follows it is satisfied.
# Otherwise set it to 0.
MACRO(set_if varname)
IF(${ARGN})
SET(${varname} 1)
ELSE(${ARGN})
SET(${varname} 0)
ENDIF(${ARGN})
ENDMACRO(set_if)
FUNCTION(join_arguments outvar)
set (output)
foreach (i ${ARGN})
set(output "${output} ${i}")
endforeach()
set (${outvar} ${output} PARENT_SCOPE)
ENDFUNCTION()
# LEGACY. PLEASE DON'T USE ANYMORE.
MACRO(MafRead maffile)
message(WARNING "MafRead is deprecated. Please use MafReadDir instead")
# ARGN contains the extra "section-variable" pairs
# If empty, return nothing
set (MAFREAD_TAGS
SOURCES # source files
PUBLIC_HEADERS # installable headers for include
PROTECTED_HEADERS # installable headers used by other headers
PRIVATE_HEADERS # non-installable headers
)
cmake_parse_arguments(MAFREAD_VAR "" "${MAFREAD_TAGS}" "" ${ARGN})
# Arguments for these tags are variables to be filled
# with the contents of particular section.
# While reading the file, extract the section.
# Section is recognized by either first uppercase character or space.
# @c http://cmake.org/pipermail/cmake/2007-May/014222.html
FILE(READ ${maffile} MAFREAD_CONTENTS)
STRING(REGEX REPLACE ";" "\\\\;" MAFREAD_CONTENTS "${MAFREAD_CONTENTS}")
STRING(REGEX REPLACE "\n" ";" MAFREAD_CONTENTS "${MAFREAD_CONTENTS}")
#message("DEBUG: MAF FILE CONTENTS: ${MAFREAD_CONTENTS}")
#message("DEBUG: PASSED VARIABLES:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# message("DEBUG: ${DEBUG_VAR}=${MAFREAD_VAR_${DEBUG_VAR}}")
#endforeach()
# The unnamed section becomes SOURCES
set (MAFREAD_VARIABLE ${MAFREAD_VAR_SOURCES})
set (MAFREAD_UNASSIGNED "")
FOREACH(MAFREAD_LINE ${MAFREAD_CONTENTS})
# Test what this line is
string(STRIP ${MAFREAD_LINE} MAFREAD_OLINE)
string(SUBSTRING ${MAFREAD_OLINE} 0 1 MAFREAD_FIRST)
#message("DEBUG: LINE='${MAFREAD_LINE}' FIRST='${MAFREAD_FIRST}'")
# The 'continue' command is cmake 3.2 - very late discovery
if (MAFREAD_FIRST STREQUAL "")
#message("DEBUG: ... skipped: empty")
elseif (MAFREAD_FIRST STREQUAL "#")
#message("DEBUG: ... skipped: comment")
else()
# Will be skipped if the line was a comment/empty
string(REGEX MATCH "[ A-Z]" MAFREAD_SECMARK ${MAFREAD_FIRST})
if (MAFREAD_SECMARK STREQUAL "")
# This isn't a section, it's a list element.
#message("DEBUG: ITEM: ${MAFREAD_OLINE} --> ${MAFREAD_VARIABLE}")
LIST(APPEND ${MAFREAD_VARIABLE} ${MAFREAD_OLINE})
else()
# It's a section - change the running variable
# Make it section name
STRING(REPLACE " " "_" MAFREAD_SECNAME ${MAFREAD_OLINE})
set(MAFREAD_VARIABLE ${MAFREAD_VAR_${MAFREAD_SECNAME}})
if (MAFREAD_VARIABLE STREQUAL "")
set(MAFREAD_VARIABLE MAFREAD_UNASSIGNED)
endif()
#message("DEBUG: NEW SECTION: '${MAFREAD_SECNAME}' --> VARIABLE: '${MAFREAD_VARIABLE}'")
endif()
endif()
ENDFOREACH()
# Final debug report
#set (ALL_VARS "")
#message("DEBUG: extracted variables:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# list(APPEND ALL_VARS ${MAFREAD_VAR_${DEBUG_VAR}})
#endforeach()
#list(REMOVE_DUPLICATES ALL_VARS)
#foreach(DEBUG_VAR ${ALL_VARS})
# message("DEBUG: --> ${DEBUG_VAR} = ${${DEBUG_VAR}}")
#endforeach()
ENDMACRO(MafRead)
# New version of MafRead macro, which automatically adds directory
# prefix. This should also resolve each relative path.
MACRO(MafReadDir directory maffile)
# ARGN contains the extra "section-variable" pairs
# If empty, return nothing
set (MAFREAD_TAGS
SOURCES # source files
PUBLIC_HEADERS # installable headers for include
PROTECTED_HEADERS # installable headers used by other headers
PRIVATE_HEADERS # non-installable headers
SOURCES_WIN32_SHARED # windows specific SOURCES
PRIVATE_HEADERS_WIN32_SHARED # windows specific PRIVATE_HEADERS
OPTIONS
)
cmake_parse_arguments(MAFREAD_VAR "" "${MAFREAD_TAGS}" "" ${ARGN})
# Arguments for these tags are variables to be filled
# with the contents of particular section.
# While reading the file, extract the section.
# Section is recognized by either first uppercase character or space.
# @c http://cmake.org/pipermail/cmake/2007-May/014222.html
FILE(READ ${directory}/${maffile} MAFREAD_CONTENTS)
STRING(REGEX REPLACE ";" "\\\\;" MAFREAD_CONTENTS "${MAFREAD_CONTENTS}")
STRING(REGEX REPLACE "\n" ";" MAFREAD_CONTENTS "${MAFREAD_CONTENTS}")
# Once correctly read, declare this file as dependency of the build file.
# Normally you should use cmake_configure_depends(), but this is
# available only since 3.0 version.
configure_file(${directory}/${maffile} dummy_${maffile}.cmake.out)
file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/dummy_${maffile}.cmake.out)
#message("DEBUG: MAF FILE CONTENTS: ${MAFREAD_CONTENTS}")
#message("DEBUG: PASSED VARIABLES:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# message("DEBUG: ${DEBUG_VAR}=${MAFREAD_VAR_${DEBUG_VAR}}")
#endforeach()
# The unnamed section becomes SOURCES
set (MAFREAD_VARIABLE ${MAFREAD_VAR_SOURCES})
set (MAFREAD_UNASSIGNED "")
# Default section type. Another is 'flags'.
set (MAFREAD_SECTION_TYPE file)
FOREACH(MAFREAD_LINE ${MAFREAD_CONTENTS})
# Test what this line is
string(STRIP ${MAFREAD_LINE} MAFREAD_OLINE)
string(SUBSTRING ${MAFREAD_OLINE} 0 1 MAFREAD_FIRST)
#message("DEBUG: LINE='${MAFREAD_LINE}' FIRST='${MAFREAD_FIRST}'")
# The 'continue' command is cmake 3.2 - very late discovery
if (MAFREAD_FIRST STREQUAL "")
#message("DEBUG: ... skipped: empty")
elseif (MAFREAD_FIRST STREQUAL "#")
#message("DEBUG: ... skipped: comment")
else()
# Will be skipped if the line was a comment/empty
string(REGEX MATCH "[ A-Z-]" MAFREAD_SECMARK ${MAFREAD_FIRST})
if (MAFREAD_SECMARK STREQUAL "")
# This isn't a section, it's a list element.
#message("DEBUG: ITEM: ${MAFREAD_OLINE} --> ${MAFREAD_VARIABLE}")
if (${MAFREAD_SECTION_TYPE} STREQUAL file)
get_filename_component(MAFREAD_OLINE ${directory}/${MAFREAD_OLINE} ABSOLUTE)
endif()
LIST(APPEND ${MAFREAD_VARIABLE} ${MAFREAD_OLINE})
else()
# It's a section - change the running variable
# Make it section name
STRING(REPLACE " " "_" MAFREAD_SECNAME ${MAFREAD_OLINE})
# The cmake's version of 'if (MAFREAD_SECNAME[0] == '-')' - sigh...
string(SUBSTRING ${MAFREAD_SECNAME} 0 1 MAFREAD_SECNAME0)
if (${MAFREAD_SECNAME0} STREQUAL "-")
set (MAFREAD_SECTION_TYPE option)
string(SUBSTRING ${MAFREAD_SECNAME} 1 -1 MAFREAD_SECNAME)
else()
set (MAFREAD_SECTION_TYPE file)
endif()
set(MAFREAD_VARIABLE ${MAFREAD_VAR_${MAFREAD_SECNAME}})
if (MAFREAD_VARIABLE STREQUAL "")
set(MAFREAD_VARIABLE MAFREAD_UNASSIGNED)
endif()
#message("DEBUG: NEW SECTION: '${MAFREAD_SECNAME}' --> VARIABLE: '${MAFREAD_VARIABLE}'")
endif()
endif()
ENDFOREACH()
# Final debug report
#set (ALL_VARS "")
#message("DEBUG: extracted variables:")
#foreach(DEBUG_VAR ${MAFREAD_TAGS})
# list(APPEND ALL_VARS ${MAFREAD_VAR_${DEBUG_VAR}})
#endforeach()
#list(REMOVE_DUPLICATES ALL_VARS)
#foreach(DEBUG_VAR ${ALL_VARS})
# message("DEBUG: --> ${DEBUG_VAR} = ${${DEBUG_VAR}}")
#endforeach()
ENDMACRO(MafReadDir)
# NOTE: This is historical only. Not in use.
# It should be a similar interface to mafread.tcl like
# the above MafRead macro.
MACRO(GetMafHeaders directory outvar)
EXECUTE_PROCESS(
COMMAND ${CMAKE_MODULE_PATH}/mafread.tcl
${CMAKE_SOURCE_DIR}/${directory}/HEADERS.maf
"PUBLIC HEADERS"
"PROTECTED HEADERS"
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE ${outvar}
)
SEPARATE_ARGUMENTS(${outvar})
adddirname(${CMAKE_SOURCE_DIR}/${directory} "${${outvar}}" ${outvar})
ENDMACRO(GetMafHeaders)
function (getVarsWith _prefix _varResult)
get_cmake_property(_vars VARIABLES)
string (REGEX MATCHALL "(^|;)${_prefix}[A-Za-z0-9_]*" _matchedVars "${_vars}")
set (${_varResult} ${_matchedVars} PARENT_SCOPE)
endfunction()
function (check_testcode_compiles testcode libraries _successful)
set (save_required_libraries ${CMAKE_REQUIRED_LIBRARIES})
set (CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES} ${libraries}")
check_cxx_source_compiles("${testcode}" ${_successful})
set (${_successful} ${${_successful}} PARENT_SCOPE)
set (CMAKE_REQUIRED_LIBRARIES ${save_required_libraries})
endfunction()
function (test_requires_clock_gettime _result)
# This function tests if clock_gettime can be used
# - at all
# - with or without librt
# Result will be:
# rt (if librt required)
# "" (if no extra libraries required)
# -- killed by FATAL_ERROR if clock_gettime is not available
set (code "
#include <time.h>
int main() {
timespec res\;
int result = clock_gettime(CLOCK_MONOTONIC, &res)\;
return result == 0\;
}
")
check_testcode_compiles(${code} "" HAVE_CLOCK_GETTIME_IN)
if (HAVE_CLOCK_GETTIME_IN)
message(STATUS "Checked clock_gettime(): no extra libs needed")
set (${_result} "" PARENT_SCOPE)
return()
endif()
check_testcode_compiles(${code} "rt" HAVE_CLOCK_GETTIME_LIBRT)
if (HAVE_CLOCK_GETTIME_LIBRT)
message(STATUS "Checked clock_gettime(): requires -lrt")
set (${_result} "-lrt" PARENT_SCOPE)
return()
endif()
message(FATAL_ERROR "clock_gettime() is not available on this system")
endfunction()

View file

@ -0,0 +1,173 @@
# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake
# files which are included with CMake 2.8.4
# It has been altered for iOS development
# Options:
#
# IOS_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64
# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders
# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch.
# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch.
#
# IOS_ARCH = arm64 (default for OS), armv7, armv7s, i386 (default for SIMULATOR), x86_64 (default for SIMULATOR64)
#
# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder
# By default this location is automatcially chosen based on the IOS_PLATFORM value above.
# If set manually, it will override the default location and force the user of a particular Developer Platform
#
# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder
# By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value.
# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path.
# If set manually, this will force the use of a specific SDK version
#
# IOS_DISABLE_BITCODE - set to 1 if you want to disable bitcode generation
# Standard settings
set (CMAKE_SYSTEM_NAME Darwin)
set (CMAKE_SYSTEM_VERSION 1)
set (UNIX True)
set (APPLE True)
set (IOS True)
# Required as of cmake 2.8.10
set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE)
# Determine the cmake host system version so we know where to find the iOS SDKs
find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin)
if (CMAKE_UNAME)
exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION)
string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}")
endif (CMAKE_UNAME)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(CMAKE_AR ar CACHE FILEPATH "" FORCE)
set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ")
set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}")
set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
if (NOT DEFINED IOS_DISABLE_BITCODE)
set (EMBED_OPTIONS "-fembed-bitcode")
endif(NOT DEFINED IOS_DISABLE_BITCODE)
if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR ENABLE_DEBUG)
set(IOS_DEBUG_OPTIONS "-glldb -gmodules")
else()
set(IOS_DEBUG_OPTIONS "-fvisibility=hidden -fvisibility-inlines-hidden")
endif()
set (CMAKE_C_FLAGS_INIT "${IOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}")
set (CMAKE_CXX_FLAGS_INIT "${IOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}")
set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_C_LINK_FLAGS}")
set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_CXX_LINK_FLAGS}")
set (CMAKE_PLATFORM_HAS_INSTALLNAME 1)
set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib")
set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle")
set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,")
set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,")
set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a")
# Specify install_name_tool and pkg-config since it outside of SDK path and therefore can't be found by CMake
if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool)
endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
if (NOT DEFINED PKG_CONFIG_EXECUTABLE)
find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config)
if (DEFINED PKG_CONFIG_EXECUTABLE)
execute_process(COMMAND pkg-config --version OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING)
endif(DEFINED PKG_CONFIG_EXECUTABLE)
endif(NOT DEFINED PKG_CONFIG_EXECUTABLE)
# fffio Specify path to install shared library on device
set (CMAKE_INSTALL_NAME_DIR "@executable_path/Frameworks")
set (CMAKE_BUILD_WITH_INSTALL_NAME_DIR TRUE)
# Setup iOS platform unless specified manually with IOS_PLATFORM
if (NOT DEFINED IOS_PLATFORM)
set (IOS_PLATFORM "OS")
endif (NOT DEFINED IOS_PLATFORM)
set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform")
# Check the platform selection and setup for developer root
if (${IOS_PLATFORM} STREQUAL OS)
set (IOS_PLATFORM_LOCATION "iPhoneOS.platform")
# This causes the installers to properly locate the output libraries
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos")
elseif (${IOS_PLATFORM} STREQUAL SIMULATOR)
set (SIMULATOR true)
set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
# This causes the installers to properly locate the output libraries
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
elseif (${IOS_PLATFORM} STREQUAL SIMULATOR64)
set (SIMULATOR true)
set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
# This causes the installers to properly locate the output libraries
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
else (${IOS_PLATFORM} STREQUAL OS)
message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR")
endif (${IOS_PLATFORM} STREQUAL OS)
# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT
if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
exec_program(/usr/bin/xcode-select ARGS -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR)
set (CMAKE_IOS_DEVELOPER_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform")
# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT
if (NOT DEFINED CMAKE_IOS_SDK_ROOT)
file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*")
if (_CMAKE_IOS_SDKS)
list (SORT _CMAKE_IOS_SDKS)
list (REVERSE _CMAKE_IOS_SDKS)
list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT)
else (_CMAKE_IOS_SDKS)
message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.")
endif (_CMAKE_IOS_SDKS)
message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}")
endif (NOT DEFINED CMAKE_IOS_SDK_ROOT)
set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK")
# Set the sysroot default to the most recent SDK
set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support")
# set the architecture for iOS
if (NOT DEFINED IOS_ARCH)
if (${IOS_PLATFORM} STREQUAL OS)
set (IOS_ARCH arm64)
elseif (${IOS_PLATFORM} STREQUAL SIMULATOR)
set (IOS_ARCH i386)
elseif (${IOS_PLATFORM} STREQUAL SIMULATOR64)
set (IOS_ARCH x86_64)
endif (${IOS_PLATFORM} STREQUAL OS)
endif(NOT DEFINED IOS_ARCH)
set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS")
# Set the find root to the iOS developer roots and to user defined paths
set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root")
# default to searching for frameworks first
set (CMAKE_FIND_FRAMEWORK FIRST)
# set up the default search directories for frameworks
set (CMAKE_SYSTEM_FRAMEWORK_PATH
${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks
${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks
${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks
)
# only search the iOS sdks, not the remainder of the host filesystem
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

58
trunk/3rdparty/srt-1-fit/scripts/mafread.tcl vendored Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/tclsh
proc is-section line {
return [regexp {^[A-Z ]+$} $line]
}
# First argument is Manifest file, others are sections.
set sections [lassign $argv maffile]
if { $sections == "" } {
puts stderr "Usage: [file tail $argv0] <MAF file> <section name>"
exit 1
}
# NOTE: If the file doesn't exist, simply print nothing.
# If there's no manifest file under this name, it means that
# there are no files that satisfy given manifest and section.
if { [catch {set fd [open $maffile r]}] } {
exit
}
set extracted ""
set insection 0
while { [gets $fd line] >= 0 } {
set oline [string trim $line]
if { $oline == "" } {
continue
}
if { [string index $oline 0] == "#" } {
continue
}
if { !$insection } {
# An opportunity to see if this is a section name
if { ![is-section $line] } {
continue
}
# If it is, then check if this is OUR section
if { $oline in $sections } {
set insection 1
continue
}
} else {
# We are inside the interesting section, so collect filenames
# Check if this is a next section name - if it is, stop reading.
if { [is-section $line] } {
continue
}
# Otherwise read the current filename
lappend extracted $oline
}
}
puts $extracted

View file

@ -0,0 +1,47 @@
# Script for reading generated version values and updating metadata properties
#read major / minor version values from version.h (generated by cmake via version.h.in)
$majorVer=99
$minorVer=99
$patchVer=0
$buildNum=0
#define regular expressions to be used when checking for #define statements
$versionSniffingRegex = "(\s*#define\s+(\S+)\s+)(\d+)"
#read generated file, load values from this with regular expression
Get-Content ".\version.h" | Where-Object { $_ -match $versionSniffingRegex } | ForEach-Object {
switch ($Matches[2])
{
"SRT_VERSION_MAJOR" { $majorVer = $Matches[3] }
"SRT_VERSION_MINOR" { $minorVer = $Matches[3] }
"SRT_VERSION_PATCH" { $patchVer = $Matches[3] }
"SRT_VERSION_BUILD" { $buildNum = $Matches[3] }
}
}
$FileDescriptionBranchCommitValue = "SRT Local Build"
if($Env:APPVEYOR){
#make AppVeyor update with this new version number
Update-AppveyorBuild -Version "$majorVer.$minorVer.$patchVer.$buildNum"
$FileDescriptionBranchCommitValue = "$Env:APPVEYOR_REPO_NAME - $($Env:APPVEYOR_REPO_BRANCH) ($($Env:APPVEYOR_REPO_COMMIT.substring(0,8)))"
}
#find C++ resource files and update file description with branch / commit details
$FileDescriptionStringRegex = '(\bVALUE\s+\"FileDescription\"\s*\,\s*\")([^\"]*\\\")*[^\"]*(\")'
Get-ChildItem -Path "./srtcore/srt_shared.rc" | ForEach-Object {
$fileName = $_
Write-Host "Processing metadata changes for file: $fileName"
$FileLines = Get-Content -path $fileName
for($i=0;$i -lt $FileLines.Count;$i++)
{
$FileLines[$i] = $FileLines[$i] -Replace $FileDescriptionStringRegex, "`${1}$FileDescriptionBranchCommitValue`${3}"
}
[System.IO.File]::WriteAllLines($fileName.FullName, $FileLines)
}

31
trunk/3rdparty/srt-1-fit/scripts/srt-ffplay vendored Executable file
View file

@ -0,0 +1,31 @@
#!/bin/bash
#
# SRT - Secure, Reliable, Transport
# Copyright (c) 2018 Haivision Systems Inc.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
FFPLAY=`type -p ffplay || echo none`
if [[ $FFPLAY == "none" ]]; then
echo >&2 "ERROR: ffplay not available to call. Please install ffplay first."
exit 1
fi
DIRNAME=`dirname $0`
if [[ ! -x $DIRNAME/srt-live-transmit ]]; then
echo >&2 "ERROR: you need 'srt-live-transmit' tool from SRT package in the same directory as this script."
exit 1
fi
SRCLOC=$1
if [[ -z $SRCLOC ]]; then
echo >&2 "Usage: `basename $0` <source URL>"
exit 1
fi
$DIRNAME/srt-live-transmit "$1" file://con/ | ffplay -

View file

@ -0,0 +1,12 @@
prefix=@INSTALLDIR@
exec_prefix=${prefix}
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: srt
Description: SRT library set
Version: @SRT_VERSION@
Libs: -L${libdir} -l@TARGET_srt@ @IFNEEDED_LINK_HAICRYPT@ @IFNEEDED_SRTBASE@ @IFNEEDED_SRT_LDFLAGS@
Libs.private: @SRT_LIBS_PRIVATE@
Cflags: -I${includedir} -I${includedir}/srt
Requires.private: @SSL_REQUIRED_MODULES@

View file

@ -0,0 +1,110 @@
#!/usr/bin/tclsh
set read_running 0
set write_running 0
set read_eof 0
set theend 0
set nread 0
set nwritten 0
proc ReadBack {fd} {
if { !$::write_running } {
puts stderr "ERROR: connection closed unexpectedly!"
set ::theend 1
return
}
set r [read $fd 4096]
if {$r == ""} {
if {[eof $fd]} {
puts stderr "EOF on socket"
set ::read_running 0
return
}
# --- puts stderr "SPURIOUS, not reading"
return
}
# --- puts stderr "REPRINTING [string bytelength $r] bytes"
puts -nonewline stdout $r
incr ::nwritten [string bytelength $r]
# --- puts stderr "DONE"
set remain [expr {$::nread - $::nwritten}]
if { $::read_eof } {
puts stderr "Finishing... read=$::nread written=$::nwritten diff=[expr {$::nwritten - $::nread}] - [expr {100.0*$remain/$::nread}]%"
}
# Nothing more to read
if {$remain == 0} {
puts stderr "NOTHING MORE TO BE WRITTEN - exitting"
set ::theend 1
return
}
after idle "ReadBack $fd"
}
proc SendToSocket {fd} {
global theend
if { !$::write_running } {
# --- puts stderr "SERVER DOWN, not reading"
fileevent stdin readable {}
return
}
if { $::read_eof } {
# Don't read, already EOF.
}
# --- puts stderr "READING cin"
set r [read stdin 4096]
if {$r == ""} {
if {[eof stdin]} {
if {!$::read_eof} {
puts stderr "EOF, setting server off"
set ::read_eof 1
}
# Just enough when the next SendToSocket will
# not be scheduled.
return
}
# --- puts stderr "SPURIOUS, not reading"
return
}
# --- puts stderr "SENDING [string bytelength $r] bytes"
# Set blocking for a short moment of sending
# in order to prevent losing data that must wait
fconfigure $fd -blocking yes
puts -nonewline $fd $r
incr ::nread [string bytelength $r]
fconfigure $fd -blocking no
# --- if {[fblocked stdin]} {
# --- # Nothing more to read
# --- return
# --- }
after idle "SendToSocket $fd"
}
set fd [socket {*}$argv]
fconfigure $fd -encoding binary -translation binary -blocking no -buffering none
fileevent $fd readable "ReadBack $fd"
fconfigure stdin -encoding binary -translation binary -blocking no
fconfigure stdout -encoding binary -translation binary
fileevent stdin readable "SendToSocket $fd"
# --- puts stderr "READY, sending"
set read_running 1
set write_running 1
vwait theend
close $fd

View file

@ -0,0 +1,51 @@
#!/usr/bin/tclsh
proc SpawnEchoServer {fd host port} {
fconfigure $fd -encoding binary -translation binary -blocking no -buffering none
fileevent $fd readable "EchoBack $fd"
# --- puts stderr "Connected: [fconfigure $fd -peername]"
}
proc EchoBack {fd} {
# --- puts stderr "READ-READY"
while 1 {
# --- puts stderr "READING 4096"
set r [read $fd 4096]
if {$r == ""} {
if {[eof $fd]} {
# --- puts stderr "EOF. Closing"
close $fd
return
}
# --- puts stderr "SPURIOUS, giving up read"
return
}
# Set blocking for a short moment of sending
# in order to prevent losing data that must wait
# --- puts stderr "SENDING [string bytelength $r] bytes"
fconfigure $fd -blocking yes
puts -nonewline $fd $r
fconfigure $fd -blocking no
if {[fblocked $fd]} {
# --- puts stderr "NO MORE DATA"
# Nothing more to read
return
}
# --- puts stderr "AGAIN"
}
}
socket -server SpawnEchoServer $argv
puts stderr "SERVER READY"
vwait tk

View file

@ -0,0 +1,360 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 02/21/2013
modified by
Haivision Systems Inc.
*****************************************************************************/
#include "core.h"
#include "ccc.h"
#include <cmath>
#include <cstring>
CCC::CCC():
m_iSYNInterval(CUDT::m_iSYNInterval),
m_dPktSndPeriod(1.0),
m_dCWndSize(16.0),
m_iBandwidth(),
m_dMaxCWndSize(),
m_iMSS(),
m_iSndCurrSeqNo(),
m_iRcvRate(),
m_iRTT(),
m_pcParam(NULL),
m_iPSize(0),
m_UDT(),
m_iACKPeriod(0),
m_iACKInterval(0),
m_bUserDefinedRTO(false),
m_iRTO(-1),
m_PerfInfo()
{
}
CCC::~CCC()
{
delete [] m_pcParam;
}
void CCC::setACKTimer(int msINT)
{
m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT;
}
void CCC::setACKInterval(int pktINT)
{
m_iACKInterval = pktINT;
}
void CCC::setRTO(int usRTO)
{
m_bUserDefinedRTO = true;
m_iRTO = usRTO;
}
void CCC::sendCustomMsg(CPacket& pkt) const
{
CUDT* u = CUDT::getUDTHandle(m_UDT);
if (NULL != u)
{
pkt.m_iID = u->m_PeerID;
#ifdef SRT_ENABLE_CTRLTSTAMP
pkt.m_iTimeStamp = int(CTimer::getTime() - u->m_StartTime);
#endif
u->m_pSndQueue->sendto(u->m_pPeerAddr, pkt);
}
}
const CPerfMon* CCC::getPerfInfo()
{
try
{
CUDT* u = CUDT::getUDTHandle(m_UDT);
if (NULL != u)
u->sample(&m_PerfInfo, false);
}
catch (...)
{
return NULL;
}
return &m_PerfInfo;
}
void CCC::setMSS(int mss)
{
m_iMSS = mss;
}
void CCC::setBandwidth(int bw)
{
m_iBandwidth = bw;
}
void CCC::setSndCurrSeqNo(int32_t seqno)
{
m_iSndCurrSeqNo = seqno;
}
void CCC::setRcvRate(int rcvrate)
{
m_iRcvRate = rcvrate;
}
void CCC::setMaxCWndSize(int cwnd)
{
m_dMaxCWndSize = cwnd;
}
void CCC::setRTT(int rtt)
{
m_iRTT = rtt;
}
void CCC::setUserParam(const char* param, int size)
{
delete [] m_pcParam;
m_pcParam = new char[size];
memcpy(m_pcParam, param, size);
m_iPSize = size;
}
//
CUDTCC::CUDTCC():
m_iRCInterval(),
m_LastRCTime(),
m_bSlowStart(),
m_iLastAck(),
m_bLoss(),
m_iLastDecSeq(),
m_dLastDecPeriod(),
m_iNAKCount(),
m_iDecRandom(),
m_iAvgNAKNum(),
m_iDecCount()
{
}
void CUDTCC::init()
{
m_iRCInterval = m_iSYNInterval;
m_LastRCTime = CTimer::getTime();
setACKTimer(m_iRCInterval);
m_bSlowStart = true;
m_iLastAck = m_iSndCurrSeqNo;
m_bLoss = false;
m_iLastDecSeq = CSeqNo::decseq(m_iLastAck);
m_dLastDecPeriod = 1;
m_iAvgNAKNum = 0;
m_iNAKCount = 0;
m_iDecRandom = 1;
m_dCWndSize = 16;
m_dPktSndPeriod = 1;
}
void CUDTCC::onACK(int32_t ack)
{
int64_t B = 0;
double inc = 0;
// Note: 1/24/2012
// The minimum increase parameter is increased from "1.0 / m_iMSS" to 0.01
// because the original was too small and caused sending rate to stay at low level
// for long time.
const double min_inc = 0.01;
uint64_t currtime = CTimer::getTime();
if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval)
return;
m_LastRCTime = currtime;
#ifdef SRT_ENABLE_BSTATS
//m_iRcvRate is bytes/sec
if (m_bSlowStart)
{
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
m_iLastAck = ack;
if (m_dCWndSize > m_dMaxCWndSize)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
else
m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize;
}
}
else
m_dCWndSize = ((m_iRcvRate + m_iMSS -1) / m_iMSS) / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
#else
if (m_bSlowStart)
{
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
m_iLastAck = ack;
if (m_dCWndSize > m_dMaxCWndSize)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
else
m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize;
}
}
else
m_dCWndSize = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
#endif
// During Slow Start, no rate increase
if (m_bSlowStart)
return;
if (m_bLoss)
{
m_bLoss = false;
return;
}
//m_iBandwidth is pkts/sec
B = (int64_t)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod);
if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B))
B = m_iBandwidth / 9;
if (B <= 0)
inc = min_inc;
else
{
// inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS)
// Beta = 1.5 * 10^(-6)
inc = pow(10.0, ceil(log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS;
if (inc < min_inc)
inc = min_inc;
}
m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval);
}
void CUDTCC::onLoss(const int32_t* losslist, int)
{
//Slow Start stopped, if it hasn't yet
if (m_bSlowStart)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
{
// Set the sending rate to the receiving rate.
#ifdef SRT_ENABLE_BSTATS
//Need average packet size here for better send period
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
#else
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
#endif
return;
}
// If no receiving rate is observed, we have to compute the sending
// rate according to the current window size, and decrease it
// using the method below.
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
}
m_bLoss = true;
if (CSeqNo::seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0)
{
m_dLastDecPeriod = m_dPktSndPeriod;
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125);
m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125);
m_iNAKCount = 1;
m_iDecCount = 1;
m_iLastDecSeq = m_iSndCurrSeqNo;
// remove global synchronization using randomization
srand(m_iLastDecSeq);
m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX));
if (m_iDecRandom < 1)
m_iDecRandom = 1;
}
else if ((m_iDecCount ++ < 5) && (0 == (++ m_iNAKCount % m_iDecRandom)))
{
// 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125);
m_iLastDecSeq = m_iSndCurrSeqNo;
}
}
void CUDTCC::onTimeout()
{
if (m_bSlowStart)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
#ifdef SRT_ENABLE_BSTATS
// Need average packet size here
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
#else
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
#endif
else
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
}
else
{
/*
m_dLastDecPeriod = m_dPktSndPeriod;
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2);
m_iLastDecSeq = m_iLastAck;
*/
}
}

View file

@ -0,0 +1,219 @@
/*****************************************************************************
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 02/28/2012
*****************************************************************************/
#ifndef __UDT_CCC_H__
#define __UDT_CCC_H__
#include "udt.h"
#include "packet.h"
class UDT_API CCC
{
friend class CUDT;
public:
CCC();
virtual ~CCC();
private:
CCC(const CCC&);
CCC& operator=(const CCC&) {return *this;}
public:
/// Callback function to be called (only) at the start of a UDT connection.
/// note that this is different from CCC(), which is always called.
virtual void init() {}
/// Callback function to be called when a UDT connection is closed.
virtual void close() {}
/// Callback function to be called when an ACK packet is received.
/// @param [in] ackno the data sequence number acknowledged by this ACK.
virtual void onACK(int32_t) {}
/// Callback function to be called when a loss report is received.
/// @param [in] losslist list of sequence number of packets, in the format describled in packet.cpp.
/// @param [in] size length of the loss list.
virtual void onLoss(const int32_t*, int) {}
/// Callback function to be called when a timeout event occurs.
virtual void onTimeout() {}
/// Callback function to be called when a data is sent.
/// @param [in] seqno the data sequence number.
/// @param [in] size the payload size.
virtual void onPktSent(const CPacket*) {}
/// Callback function to be called when a data is received.
/// @param [in] seqno the data sequence number.
/// @param [in] size the payload size.
virtual void onPktReceived(const CPacket*) {}
/// Callback function to Process a user defined packet.
/// @param [in] pkt the user defined packet.
virtual void processCustomMsg(const CPacket*) {}
protected:
/// Set periodical acknowldging and the ACK period.
/// @param [in] msINT the period to send an ACK.
void setACKTimer(int msINT);
/// Set packet-based acknowldging and the number of packets to send an ACK.
/// @param [in] pktINT the number of packets to send an ACK.
void setACKInterval(int pktINT);
/// Set RTO value.
/// @param [in] msRTO RTO in macroseconds.
void setRTO(int usRTO);
/// Send a user defined control packet.
/// @param [in] pkt user defined packet.
void sendCustomMsg(CPacket& pkt) const;
/// retrieve performance information.
/// @return Pointer to a performance info structure.
const CPerfMon* getPerfInfo();
/// Set user defined parameters.
/// @param [in] param the paramters in one buffer.
/// @param [in] size the size of the buffer.
void setUserParam(const char* param, int size);
private:
void setMSS(int mss);
void setMaxCWndSize(int cwnd);
void setBandwidth(int bw);
void setSndCurrSeqNo(int32_t seqno);
void setRcvRate(int rcvrate);
void setRTT(int rtt);
protected:
const int32_t& m_iSYNInterval; // UDT constant parameter, SYN
double m_dPktSndPeriod; // Packet sending period, in microseconds
double m_dCWndSize; // Congestion window size, in packets
int m_iBandwidth; // estimated bandwidth, packets per second
double m_dMaxCWndSize; // maximum cwnd size, in packets
int m_iMSS; // Maximum Packet Size, including all packet headers
int32_t m_iSndCurrSeqNo; // current maximum seq no sent out
int m_iRcvRate; // packet arrive rate at receiver side, packets per second
int m_iRTT; // current estimated RTT, microsecond
char* m_pcParam; // user defined parameter
int m_iPSize; // size of m_pcParam
private:
UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to
int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds
int m_iACKInterval; // How many packets to send one ACK, in packets
bool m_bUserDefinedRTO; // if the RTO value is defined by users
int m_iRTO; // RTO value, microseconds
CPerfMon m_PerfInfo; // protocol statistics information
};
class CCCVirtualFactory
{
public:
virtual ~CCCVirtualFactory() {}
virtual CCC* create() = 0;
virtual CCCVirtualFactory* clone() = 0;
};
template <class T>
class CCCFactory: public CCCVirtualFactory
{
public:
virtual ~CCCFactory() {}
virtual CCC* create() {return new T;}
virtual CCCVirtualFactory* clone() {return new CCCFactory<T>;}
};
class CUDTCC: public CCC
{
public:
CUDTCC();
public:
virtual void init();
virtual void onACK(int32_t);
virtual void onLoss(const int32_t*, int);
virtual void onTimeout();
private:
int m_iRCInterval; // UDT Rate control interval
uint64_t m_LastRCTime; // last rate increase time
bool m_bSlowStart; // if in slow start phase
int32_t m_iLastAck; // last ACKed seq no
bool m_bLoss; // if loss happened since last rate increase
int32_t m_iLastDecSeq; // max pkt seq no sent out when last decrease happened
double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened
int m_iNAKCount; // NAK counter
int m_iDecRandom; // random threshold on decrease by number of loss events
int m_iAvgNAKNum; // average number of NAKs per congestion
int m_iDecCount; // number of decreases in a congestion epoch
};
#endif

View file

@ -0,0 +1,75 @@
SRT Core
========
These files are contents of the SRT library. Beside files that are used exclusively
and internally by the library, this directory also contains:
- common files: usually header files, which can be used also by other projects,
even if they don't link against SRT
- public and protected header files - header files for the library, which will
be picked up from here
Which header files are public, protected and private, it's defined in the manifest
file together with all source files that the SRT library comprises of: `filelist.maf`.
Common files
============
This directory holds the files that may be used separately by both SRT library
itself and the internal applications.
Source files are added to SRT library, so apps don't have to use them. However
these source files might be used by some internal applications that do not
link against SRT library.
Header files contained here might be required by internal applications no
matter if they link against SRT or not. They are here because simultaneously
they are used also by the SRT library.
Utilities
=========
1. threadname.h
This is a utility that is useful for debugging and it allows a thread to be given
a name. This name is used in the logging messages, as well as you can see it also
inside the debugger.
This is currently supported only on Linux; some more portable and more reliable
way is needed.
2. utilities.h
A set of various reusable components, all defined as C++ classes or C++ inline
functions.
3. `netinet_any.h`
This defines a `sockaddr_any` type, which simplifies dealing with the BSD socket API
using `sockaddr`, `sockaddr_in` and `sockaddr_in6` structures.
Compat and portability
======================
1. `srt_compat.h`
This part contains some portability problem resolutions, including:
- `strerror` in a version that is both portable and thread safe
- `localtime` in a version that is both portable and thread safe
2. win directory
This contains various header files that are used on Windows platform only.
They provide various facilities available OOTB on POSIX systems.
3. `platform_sys.h`
This is a file that is responsible to include whatever system include
files must be included for whatever system API must be provided for
the needs of SRT library. This is a part of public headers.

3245
trunk/3rdparty/srt-1-fit/srtcore/api.cpp vendored Normal file

File diff suppressed because it is too large Load diff

300
trunk/3rdparty/srt-1-fit/srtcore/api.h vendored Normal file
View file

@ -0,0 +1,300 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 09/28/2010
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_API_H__
#define __UDT_API_H__
#include <map>
#include <vector>
#include <string>
#include "netinet_any.h"
#include "udt.h"
#include "packet.h"
#include "queue.h"
#include "cache.h"
#include "epoll.h"
#include "handshake.h"
class CUDT;
class CUDTSocket
{
public:
CUDTSocket();
~CUDTSocket();
SRT_SOCKSTATUS m_Status; //< current socket state
/// Time when the socket is closed.
/// When the socket is closed, it is not removed immediately from the list
/// of sockets in order to prevent other methods from accessing invalid address.
/// A timer is started and the socket will be removed after approximately
/// 1 second (see CUDTUnited::checkBrokenSockets()).
uint64_t m_ClosureTimeStamp;
int m_iIPversion; //< IP version
sockaddr* m_pSelfAddr; //< pointer to the local address of the socket
sockaddr* m_pPeerAddr; //< pointer to the peer address of the socket
SRTSOCKET m_SocketID; //< socket ID
SRTSOCKET m_ListenSocket; //< ID of the listener socket; 0 means this is an independent socket
SRTSOCKET m_PeerID; //< peer socket ID
int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port
CUDT* m_pUDT; //< pointer to the UDT entity
std::set<SRTSOCKET>* m_pQueuedSockets; //< set of connections waiting for accept()
std::set<SRTSOCKET>* m_pAcceptSockets; //< set of accept()ed connections
pthread_cond_t m_AcceptCond; //< used to block "accept" call
pthread_mutex_t m_AcceptLock; //< mutex associated to m_AcceptCond
unsigned int m_uiBackLog; //< maximum number of connections in queue
int m_iMuxID; //< multiplexer ID
pthread_mutex_t m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect
static int64_t getPeerSpec(SRTSOCKET id, int32_t isn)
{
return (id << 30) + isn;
}
int64_t getPeerSpec()
{
return getPeerSpec(m_PeerID, m_iISN);
}
private:
CUDTSocket(const CUDTSocket&);
CUDTSocket& operator=(const CUDTSocket&);
};
////////////////////////////////////////////////////////////////////////////////
class CUDTUnited
{
friend class CUDT;
friend class CRendezvousQueue;
public:
CUDTUnited();
~CUDTUnited();
public:
static std::string CONID(SRTSOCKET sock);
/// initialize the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int startup();
/// release the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int cleanup();
/// Create a new UDT socket.
/// @param [in] af IP version, IPv4 (AF_INET) or IPv6 (AF_INET6).
/// @param [in] type (ignored)
/// @return The new UDT socket ID, or INVALID_SOCK.
SRTSOCKET newSocket(int af, int );
/// Create a new UDT connection.
/// @param [in] listen the listening UDT socket;
/// @param [in] peer peer address.
/// @param [in,out] hs handshake information from peer side (in), negotiated value (out);
/// @return If the new connection is successfully created: 1 success, 0 already exist, -1 error.
int newConnection(const SRTSOCKET listen, const sockaddr* peer, CHandShake* hs, const CPacket& hspkt,
ref_t<SRT_REJECT_REASON> r_error);
int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq);
/// look up the UDT entity according to its ID.
/// @param [in] u the UDT socket ID.
/// @return Pointer to the UDT entity.
CUDT* lookup(const SRTSOCKET u);
/// Check the status of the UDT socket.
/// @param [in] u the UDT socket ID.
/// @return UDT socket status, or NONEXIST if not found.
SRT_SOCKSTATUS getStatus(const SRTSOCKET u);
// socket APIs
int bind(const SRTSOCKET u, const sockaddr* name, int namelen);
int bind(const SRTSOCKET u, UDPSOCKET udpsock);
int listen(const SRTSOCKET u, int backlog);
SRTSOCKET accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen);
int connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn);
int close(const SRTSOCKET u);
int getpeername(const SRTSOCKET u, sockaddr* name, int* namelen);
int getsockname(const SRTSOCKET u, sockaddr* name, int* namelen);
int select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout);
int selectEx(const std::vector<SRTSOCKET>& fds, std::vector<SRTSOCKET>* readfds, std::vector<SRTSOCKET>* writefds, std::vector<SRTSOCKET>* exceptfds, int64_t msTimeOut);
int epoll_create();
int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_remove_usock(const int eid, const SRTSOCKET u);
int epoll_remove_ssock(const int eid, const SYSSOCKET s);
int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds = NULL, std::set<SYSSOCKET>* lwfds = NULL);
int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
int32_t epoll_set(const int eid, int32_t flags);
int epoll_release(const int eid);
/// record the UDT exception.
/// @param [in] e pointer to a UDT exception instance.
void setError(CUDTException* e);
/// look up the most recent UDT exception.
/// @return pointer to a UDT exception instance.
CUDTException* getError();
private:
// void init();
private:
std::map<SRTSOCKET, CUDTSocket*> m_Sockets; // stores all the socket structures
pthread_mutex_t m_ControlLock; // used to synchronize UDT API
pthread_mutex_t m_IDLock; // used to synchronize ID generation
SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID
std::map<int64_t, std::set<SRTSOCKET> > m_PeerRec;// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn
private:
pthread_key_t m_TLSError; // thread local error record (last error)
static void TLSDestroy(void* e) {if (NULL != e) delete (CUDTException*)e;}
private:
CUDTSocket* locate(const SRTSOCKET u);
CUDTSocket* locate(const sockaddr* peer, const SRTSOCKET id, int32_t isn);
void updateMux(CUDTSocket* s, const sockaddr* addr = NULL, const UDPSOCKET* = NULL);
void updateListenerMux(CUDTSocket* s, const CUDTSocket* ls);
private:
std::map<int, CMultiplexer> m_mMultiplexer; // UDP multiplexer
pthread_mutex_t m_MultiplexerLock;
private:
CCache<CInfoBlock>* m_pCache; // UDT network information cache
private:
volatile bool m_bClosing;
pthread_mutex_t m_GCStopLock;
pthread_cond_t m_GCStopCond;
pthread_mutex_t m_InitLock;
int m_iInstanceCount; // number of startup() called by application
bool m_bGCStatus; // if the GC thread is working (true)
pthread_t m_GCThread;
static void* garbageCollect(void*);
std::map<SRTSOCKET, CUDTSocket*> m_ClosedSockets; // temporarily store closed sockets
void checkBrokenSockets();
void removeSocket(const SRTSOCKET u);
CEPoll m_EPoll; // handling epoll data structures and events
private:
CUDTUnited(const CUDTUnited&);
CUDTUnited& operator=(const CUDTUnited&);
};
// Debug support
inline std::string SockaddrToString(const sockaddr* sadr)
{
void* addr =
sadr->sa_family == AF_INET ?
(void*)&((sockaddr_in*)sadr)->sin_addr
: sadr->sa_family == AF_INET6 ?
(void*)&((sockaddr_in6*)sadr)->sin6_addr
: 0;
// (cast to (void*) is required because otherwise the 2-3 arguments
// of ?: operator would have different types, which isn't allowed in C++.
if ( !addr )
return "unknown:0";
std::ostringstream output;
char hostbuf[1024];
int flags;
#if ENABLE_GETNAMEINFO
flags = NI_NAMEREQD;
#else
flags = NI_NUMERICHOST | NI_NUMERICSERV;
#endif
if (!getnameinfo(sadr, sizeof(*sadr), hostbuf, 1024, NULL, 0, flags))
{
output << hostbuf;
}
output << ":" << ntohs(((sockaddr_in*)sadr)->sin_port); // TRICK: sin_port and sin6_port have the same offset and size
return output.str();
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,510 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 05/05/2009
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_BUFFER_H__
#define __UDT_BUFFER_H__
#include "udt.h"
#include "list.h"
#include "queue.h"
#include "utilities.h"
#include <fstream>
class CSndBuffer
{
public:
// XXX There's currently no way to access the socket ID set for
// whatever the buffer is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
CSndBuffer(int size = 32, int mss = 1500);
~CSndBuffer();
public:
/// Insert a user buffer into the sending list.
/// @param [in] data pointer to the user data block.
/// @param [in] len size of the block.
/// @param [in] ttl time to live in milliseconds
/// @param [in] order if the block should be delivered in order, for DGRAM only
void addBuffer(const char* data, int len, int ttl, bool order, uint64_t srctime, ref_t<int32_t> r_msgno);
/// Read a block of data from file and insert it into the sending list.
/// @param [in] ifs input file stream.
/// @param [in] len size of the block.
/// @return actual size of data added from the file.
int addBufferFromFile(std::fstream& ifs, int len);
/// Find data position to pack a DATA packet from the furthest reading point.
/// @param [out] data the pointer to the data position.
/// @param [out] msgno message number of the packet.
/// @param [out] origintime origin time stamp of the message
/// @param [in] kflags Odd|Even crypto key flag
/// @return Actual length of data read.
int readData(char** data, int32_t& msgno, uint64_t& origintime, int kflgs);
/// Find data position to pack a DATA packet for a retransmission.
/// @param [out] data the pointer to the data position.
/// @param [in] offset offset from the last ACK point.
/// @param [out] msgno message number of the packet.
/// @param [out] origintime origin time stamp of the message
/// @param [out] msglen length of the message
/// @return Actual length of data read.
int readData(char** data, const int offset, int32_t& msgno, uint64_t& origintime, int& msglen);
/// Update the ACK point and may release/unmap/return the user data according to the flag.
/// @param [in] offset number of packets acknowledged.
void ackData(int offset);
/// Read size of data still in the sending list.
/// @return Current size of the data in the sending list.
int getCurrBufSize() const;
int dropLateData(int &bytes, uint64_t latetime);
#ifdef SRT_ENABLE_SNDBUFSZ_MAVG
void updAvgBufSize(uint64_t time);
int getAvgBufSize(ref_t<int> bytes, ref_t<int> timespan);
#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */
int getCurrBufSize(ref_t<int> bytes, ref_t<int> timespan);
uint64_t getInRatePeriod() const { return m_InRatePeriod; }
/// Retrieve input bitrate in bytes per second
int getInputRate() const { return m_iInRateBps; }
/// Update input rate calculation.
/// @param [in] time current time in microseconds
/// @param [in] pkts number of packets newly added to the buffer
/// @param [in] bytes number of payload bytes in those newly added packets
///
/// @return Current size of the data in the sending list.
void updateInputRate(uint64_t time, int pkts = 0, int bytes = 0);
void resetInputRateSmpPeriod(bool disable = false)
{
setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US);
}
private:
void increase();
void setInputRateSmpPeriod(int period);
private: // Constants
static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms
static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms
static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload
static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE;
private:
pthread_mutex_t m_BufLock; // used to synchronize buffer operation
struct Block
{
char* m_pcData; // pointer to the data block
int m_iLength; // length of the block
int32_t m_iMsgNoBitset; // message number
uint64_t m_ullOriginTime_us; // original request time
uint64_t m_ullSourceTime_us;
int m_iTTL; // time to live (milliseconds)
Block* m_pNext; // next block
int32_t getMsgSeq()
{
// NOTE: this extracts message ID with regard to REXMIT flag.
// This is valid only for message ID that IS GENERATED in this instance,
// not provided by the peer. This can be otherwise sent to the peer - it doesn't matter
// for the peer that it uses LESS bits to represent the message.
return m_iMsgNoBitset & MSGNO_SEQ::mask;
}
} *m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock;
// m_pBlock: The head pointer
// m_pFirstBlock: The first block
// m_pCurrBlock: The current block
// m_pLastBlock: The last block (if first == last, buffer is empty)
struct Buffer
{
char* m_pcData; // buffer
int m_iSize; // size
Buffer* m_pNext; // next buffer
} *m_pBuffer; // physical buffer
int32_t m_iNextMsgNo; // next message number
int m_iSize; // buffer size (number of packets)
int m_iMSS; // maximum seqment/packet size
int m_iCount; // number of used blocks
int m_iBytesCount; // number of payload bytes in queue
uint64_t m_ullLastOriginTime_us;
#ifdef SRT_ENABLE_SNDBUFSZ_MAVG
uint64_t m_LastSamplingTime;
int m_iCountMAvg;
int m_iBytesCountMAvg;
int m_TimespanMAvg;
#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */
int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime
int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime
uint64_t m_InRateStartTime;
uint64_t m_InRatePeriod; // usec
int m_iInRateBps; // Input Rate in Bytes/sec
int m_iAvgPayloadSz; // Average packet payload size
private:
CSndBuffer(const CSndBuffer&);
CSndBuffer& operator=(const CSndBuffer&);
};
////////////////////////////////////////////////////////////////////////////////
class CRcvBuffer
{
public:
// XXX There's currently no way to access the socket ID set for
// whatever the queue is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
/// Construct the buffer.
/// @param [in] queue CUnitQueue that actually holds the units (packets)
/// @param [in] bufsize_pkts in units (packets)
CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = 65536);
~CRcvBuffer();
public:
/// Write data into the buffer.
/// @param [in] unit pointer to a data unit containing new packet
/// @param [in] offset offset from last ACK point.
/// @return 0 is success, -1 if data is repeated.
int addData(CUnit* unit, int offset);
/// Read data into a user buffer.
/// @param [in] data pointer to user buffer.
/// @param [in] len length of user buffer.
/// @return size of data read.
int readBuffer(char* data, int len);
/// Read data directly into file.
/// @param [in] file C++ file stream.
/// @param [in] len expected length of data to write into the file.
/// @return size of data read.
int readBufferToFile(std::fstream& ofs, int len);
/// Update the ACK point of the buffer.
/// @param [in] len number of units to be acknowledged.
/// @return 1 if a user buffer is fulfilled, otherwise 0.
void ackData(int len);
/// Query how many buffer space left for data receiving.
/// Actually only acknowledged packets, that are still in the buffer,
/// are considered to take buffer space.
///
/// @return size of available buffer space (including user buffer) for data receiving.
/// Not counting unacknowledged packets.
int getAvailBufSize() const;
/// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now).
/// @return size of valid (continous) data for reading.
int getRcvDataSize() const;
/// Query how many data was received and acknowledged.
/// @param [out] bytes bytes
/// @param [out] spantime spantime
/// @return size in pkts of acked data.
int getRcvDataSize(int &bytes, int &spantime);
#if SRT_ENABLE_RCVBUFSZ_MAVG
/// Query a 1 sec moving average of how many data was received and acknowledged.
/// @param [out] bytes bytes
/// @param [out] spantime spantime
/// @return size in pkts of acked data.
int getRcvAvgDataSize(int &bytes, int &spantime);
/// Query how many data of the receive buffer is acknowledged.
/// @param [in] now current time in us.
/// @return none.
void updRcvAvgDataSize(uint64_t now);
#endif /* SRT_ENABLE_RCVBUFSZ_MAVG */
/// Query the received average payload size.
/// @return size (bytes) of payload size
int getRcvAvgPayloadSize() const;
/// Mark the message to be dropped from the message list.
/// @param [in] msgno message number.
/// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the msgno value)
void dropMsg(int32_t msgno, bool using_rexmit_flag);
/// read a message.
/// @param [out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @return actuall size of data read.
int readMsg(char* data, int len);
/// read a message.
/// @param [out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
/// @return actuall size of data read.
int readMsg(char* data, int len, ref_t<SRT_MSGCTRL> mctrl);
/// Query if data is ready to read (tsbpdtime <= now if TsbPD is active).
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
/// of next packet in recv buffer, ready or not.
/// @param [out] curpktseq Sequence number of the packet if there is one ready to play
/// @return true if ready to play, false otherwise (tsbpdtime may be !0 in
/// both cases).
bool isRcvDataReady(ref_t<uint64_t> tsbpdtime, ref_t<int32_t> curpktseq);
bool isRcvDataReady();
bool isRcvDataAvailable()
{
return m_iLastAckPos != m_iStartPos;
}
CPacket* getRcvReadyPacket();
/// Set TimeStamp-Based Packet Delivery Rx Mode
/// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay
/// @param [in] delay aggreed TsbPD delay
/// @return 0
int setRcvTsbPdMode(uint64_t timebase, uint32_t delay);
/// Add packet timestamp for drift caclculation and compensation
/// @param [in] timestamp packet time stamp
/// @param [ref] lock Mutex that should be locked for the operation
void addRcvTsbPdDriftSample(uint32_t timestamp, pthread_mutex_t& lock);
#ifdef SRT_DEBUG_TSBPD_DRIFT
void printDriftHistogram(int64_t iDrift);
void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg);
#endif
/// Get information on the 1st message in queue.
// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none
/// @param [out] passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app)
/// @param [out] skipseqno -1 or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets.
/// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true
/// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE:
/// IF skipseqno != -1, packet ready to play preceeded by missing packets.;
/// IF skipseqno == -1, no missing packet but 1st not ready to play.
bool getRcvFirstMsg(ref_t<uint64_t> tsbpdtime, ref_t<bool> passack, ref_t<int32_t> skipseqno, ref_t<int32_t> curpktseq);
/// Update the ACK point of the buffer.
/// @param [in] len size of data to be skip & acknowledged.
void skipData(int len);
#if ENABLE_HEAVY_LOGGING
void reportBufferStats(); // Heavy logging Debug only
#endif
private:
/// Adjust receive queue to 1st ready to play message (tsbpdtime < now).
// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none
/// @retval true 1st packet ready to play without discontinuity (no hole)
/// @retval false tsbpdtime = 0: no packet ready to play
bool getRcvReadyMsg(ref_t<uint64_t> tsbpdtime, ref_t<int32_t> curpktseq);
public:
// (This is exposed as used publicly in logs)
/// Get packet delivery local time base (adjusted for wrap around)
/// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min
/// @return local delivery time (usec)
uint64_t getTsbPdTimeBase(uint32_t timestamp_us);
/// Get packet local delivery time
/// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min
/// @return local delivery time (usec)
public:
uint64_t getPktTsbPdTime(uint32_t timestamp);
int debugGetSize() const;
bool empty() const;
// Required by PacketFilter facility to use as a storage
// for provided packets
CUnitQueue* getUnitQueue()
{
return m_pUnitQueue;
}
private:
/// thread safe bytes counter of the Recv & Ack buffer
/// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true)
/// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer.
/// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer
void countBytes(int pkts, int bytes, bool acked = false);
private:
bool scanMsg(ref_t<int> start, ref_t<int> end, ref_t<bool> passack);
private:
CUnit** m_pUnit; // pointer to the protocol buffer (array of CUnit* items)
const int m_iSize; // size of the array of CUnit* items
CUnitQueue* m_pUnitQueue; // the shared unit queue
int m_iStartPos; // the head position for I/O (inclusive)
int m_iLastAckPos; // the last ACKed position (exclusive)
// EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1
int m_iMaxPos; // the furthest data position
int m_iNotch; // the starting read point of the first unit
pthread_mutex_t m_BytesCountLock; // used to protect counters operations
int m_iBytesCount; // Number of payload bytes in the buffer
int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer
int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer
int m_iAvgPayloadSz; // Average payload size for dropped bytes estimation
bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode
uint32_t m_uTsbPdDelay; // aggreed delay
uint64_t m_ullTsbPdTimeBase; // localtime base for TsbPd mode
// Note: m_ullTsbPdTimeBase cumulates values from:
// 1. Initial SRT_CMD_HSREQ packet returned value diff to current time:
// == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception
// 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected
// += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8).
// 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively
// from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE
// once the value of average drift exceeds this value in whatever direction.
// += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE
//
// XXX Application-supplied timestamps won't work therefore. This requires separate
// calculation of all these things above.
bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around
static const uint32_t TSBPD_WRAP_PERIOD = (30*1000000); //30 seconds (in usec)
static const int TSBPD_DRIFT_MAX_VALUE = 5000; // Max drift (usec) above which TsbPD Time Offset is adjusted
static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; // Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation
//int m_iTsbPdDrift; // recent drift in the packet time stamp
//int64_t m_TsbPdDriftSum; // Sum of sampled drift
//int m_iTsbPdDriftNbSamples; // Number of samples in sum and histogram
DriftTracer<TSBPD_DRIFT_MAX_SAMPLES, TSBPD_DRIFT_MAX_VALUE> m_DriftTracer;
#ifdef SRT_ENABLE_RCVBUFSZ_MAVG
uint64_t m_LastSamplingTime;
int m_TimespanMAvg;
int m_iCountMAvg;
int m_iBytesCountMAvg;
#endif /* SRT_ENABLE_RCVBUFSZ_MAVG */
#ifdef SRT_DEBUG_TSBPD_DRIFT
int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment)
int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment)
static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram
#endif /* SRT_DEBUG_TSBPD_DRIFT */
#ifdef SRT_DEBUG_TSBPD_OUTJITTER
unsigned long m_ulPdHisto[4][10];
#endif /* SRT_DEBUG_TSBPD_OUTJITTER */
private:
CRcvBuffer();
CRcvBuffer(const CRcvBuffer&);
CRcvBuffer& operator=(const CRcvBuffer&);
};
#endif

View file

@ -0,0 +1,120 @@
/*****************************************************************************
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 05/05/2009
*****************************************************************************/
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include <cstring>
#include "cache.h"
#include "core.h"
using namespace std;
CInfoBlock& CInfoBlock::operator=(const CInfoBlock& obj)
{
std::copy(obj.m_piIP, obj.m_piIP + 4, m_piIP);
m_iIPversion = obj.m_iIPversion;
m_ullTimeStamp = obj.m_ullTimeStamp;
m_iRTT = obj.m_iRTT;
m_iBandwidth = obj.m_iBandwidth;
m_iLossRate = obj.m_iLossRate;
m_iReorderDistance = obj.m_iReorderDistance;
m_dInterval = obj.m_dInterval;
m_dCWnd = obj.m_dCWnd;
return *this;
}
bool CInfoBlock::operator==(const CInfoBlock& obj)
{
if (m_iIPversion != obj.m_iIPversion)
return false;
else if (m_iIPversion == AF_INET)
return (m_piIP[0] == obj.m_piIP[0]);
for (int i = 0; i < 4; ++ i)
{
if (m_piIP[i] != obj.m_piIP[i])
return false;
}
return true;
}
CInfoBlock* CInfoBlock::clone()
{
CInfoBlock* obj = new CInfoBlock;
std::copy(m_piIP, m_piIP + 4, obj->m_piIP);
obj->m_iIPversion = m_iIPversion;
obj->m_ullTimeStamp = m_ullTimeStamp;
obj->m_iRTT = m_iRTT;
obj->m_iBandwidth = m_iBandwidth;
obj->m_iLossRate = m_iLossRate;
obj->m_iReorderDistance = m_iReorderDistance;
obj->m_dInterval = m_dInterval;
obj->m_dCWnd = m_dCWnd;
return obj;
}
int CInfoBlock::getKey()
{
if (m_iIPversion == AF_INET)
return m_piIP[0];
return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3];
}
void CInfoBlock::convert(const sockaddr* addr, int ver, uint32_t ip[])
{
if (ver == AF_INET)
{
ip[0] = ((sockaddr_in*)addr)->sin_addr.s_addr;
ip[1] = ip[2] = ip[3] = 0;
}
else
{
memcpy((char*)ip, (char*)((sockaddr_in6*)addr)->sin6_addr.s6_addr, 16);
}
}

266
trunk/3rdparty/srt-1-fit/srtcore/cache.h vendored Normal file
View file

@ -0,0 +1,266 @@
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 01/27/2011
*****************************************************************************/
#ifndef __UDT_CACHE_H__
#define __UDT_CACHE_H__
#include <list>
#include <vector>
#include "common.h"
#include "udt.h"
class CCacheItem
{
public:
virtual ~CCacheItem() {}
public:
virtual CCacheItem& operator=(const CCacheItem&) = 0;
// The "==" operator SHOULD only compare key values.
virtual bool operator==(const CCacheItem&) = 0;
/// get a deep copy clone of the current item
/// @return Pointer to the new item, or NULL if failed.
virtual CCacheItem* clone() = 0;
/// get a random key value between 0 and MAX_INT to be used for the hash in cache
/// @return A random hash key.
virtual int getKey() = 0;
// If there is any shared resources between the cache item and its clone,
// the shared resource should be released by this function.
virtual void release() {}
};
template<typename T> class CCache
{
public:
CCache(int size = 1024):
m_iMaxSize(size),
m_iHashSize(size * 3),
m_iCurrSize(0)
{
m_vHashPtr.resize(m_iHashSize);
CGuard::createMutex(m_Lock);
}
~CCache()
{
clear();
CGuard::releaseMutex(m_Lock);
}
public:
/// find the matching item in the cache.
/// @param [in,out] data storage for the retrieved item; initially it must carry the key information
/// @return 0 if found a match, otherwise -1.
int lookup(T* data)
{
CGuard cacheguard(m_Lock);
int key = data->getKey();
if (key < 0)
return -1;
if (key >= m_iMaxSize)
key %= m_iHashSize;
const ItemPtrList& item_list = m_vHashPtr[key];
for (typename ItemPtrList::const_iterator i = item_list.begin(); i != item_list.end(); ++ i)
{
if (*data == ***i)
{
// copy the cached info
*data = ***i;
return 0;
}
}
return -1;
}
/// update an item in the cache, or insert one if it doesn't exist; oldest item may be removed
/// @param [in] data the new item to updated/inserted to the cache
/// @return 0 if success, otherwise -1.
int update(T* data)
{
CGuard cacheguard(m_Lock);
int key = data->getKey();
if (key < 0)
return -1;
if (key >= m_iMaxSize)
key %= m_iHashSize;
T* curr = NULL;
ItemPtrList& item_list = m_vHashPtr[key];
for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i)
{
if (*data == ***i)
{
// update the existing entry with the new value
***i = *data;
curr = **i;
// remove the current entry
m_StorageList.erase(*i);
item_list.erase(i);
// re-insert to the front
m_StorageList.push_front(curr);
item_list.push_front(m_StorageList.begin());
return 0;
}
}
// create new entry and insert to front
curr = data->clone();
m_StorageList.push_front(curr);
item_list.push_front(m_StorageList.begin());
++ m_iCurrSize;
if (m_iCurrSize >= m_iMaxSize)
{
// Cache overflow, remove oldest entry.
T* last_data = m_StorageList.back();
int last_key = last_data->getKey() % m_iHashSize;
ItemPtrList& last_item_list = m_vHashPtr[last_key];
for (typename ItemPtrList::iterator i = last_item_list.begin(); i != last_item_list.end(); ++ i)
{
if (*last_data == ***i)
{
last_item_list.erase(i);
break;
}
}
last_data->release();
delete last_data;
m_StorageList.pop_back();
-- m_iCurrSize;
}
return 0;
}
/// Specify the cache size (i.e., max number of items).
/// @param [in] size max cache size.
void setSizeLimit(int size)
{
m_iMaxSize = size;
m_iHashSize = size * 3;
m_vHashPtr.resize(m_iHashSize);
}
/// Clear all entries in the cache, restore to initialization state.
void clear()
{
for (typename std::list<T*>::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++ i)
{
(*i)->release();
delete *i;
}
m_StorageList.clear();
for (typename std::vector<ItemPtrList>::iterator i = m_vHashPtr.begin(); i != m_vHashPtr.end(); ++ i)
i->clear();
m_iCurrSize = 0;
}
private:
std::list<T*> m_StorageList;
typedef typename std::list<T*>::iterator ItemPtr;
typedef std::list<ItemPtr> ItemPtrList;
std::vector<ItemPtrList> m_vHashPtr;
int m_iMaxSize;
int m_iHashSize;
int m_iCurrSize;
pthread_mutex_t m_Lock;
private:
CCache(const CCache&);
CCache& operator=(const CCache&);
};
class CInfoBlock
{
public:
uint32_t m_piIP[4]; // IP address, machine read only, not human readable format
int m_iIPversion; // IP version
uint64_t m_ullTimeStamp; // last update time
int m_iRTT; // RTT
int m_iBandwidth; // estimated bandwidth
int m_iLossRate; // average loss rate
int m_iReorderDistance; // packet reordering distance
double m_dInterval; // inter-packet time, congestion control
double m_dCWnd; // congestion window size, congestion control
public:
virtual ~CInfoBlock() {}
virtual CInfoBlock& operator=(const CInfoBlock& obj);
virtual bool operator==(const CInfoBlock& obj);
virtual CInfoBlock* clone();
virtual int getKey();
virtual void release() {}
public:
/// convert sockaddr structure to an integer array
/// @param [in] addr network address
/// @param [in] ver IP version
/// @param [out] ip the result machine readable IP address in integer array
static void convert(const sockaddr* addr, int ver, uint32_t ip[]);
};
#endif

View file

@ -0,0 +1,765 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
****************************************************************************/
/****************************************************************************
written by
Yunhong Gu, last updated 01/27/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef _WIN32
#if __APPLE__
#include "TargetConditionals.h"
#endif
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <cstdio>
#include <cerrno>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#endif
#include <iostream>
#include <iomanip> // Logging
#include <srt_compat.h>
#include <csignal>
#include "channel.h"
#include "packet.h"
#include "api.h" // SockaddrToString - possibly move it to somewhere else
#include "logging.h"
#include "utilities.h"
#ifdef _WIN32
typedef int socklen_t;
#endif
#ifndef _WIN32
#define NET_ERROR errno
#else
#define NET_ERROR WSAGetLastError()
#endif
using namespace std;
using namespace srt_logging;
CChannel::CChannel():
m_iIPversion(AF_INET),
m_iSockAddrSize(sizeof(sockaddr_in)),
m_iSocket(),
#ifdef SRT_ENABLE_IPOPTS
m_iIpTTL(-1), /* IPv4 TTL or IPv6 HOPs [1..255] (-1:undefined) */
m_iIpToS(-1), /* IPv4 Type of Service or IPv6 Traffic Class [0x00..0xff] (-1:undefined) */
#endif
m_iSndBufSize(65536),
m_iRcvBufSize(65536),
m_iIpV6Only(-1)
{
}
CChannel::CChannel(int version):
m_iIPversion(version),
m_iSocket(),
#ifdef SRT_ENABLE_IPOPTS
m_iIpTTL(-1),
m_iIpToS(-1),
#endif
m_iSndBufSize(65536),
m_iRcvBufSize(65536),
m_iIpV6Only(-1),
m_BindAddr(version)
{
SRT_ASSERT(version == AF_INET || version == AF_INET6);
m_iSockAddrSize = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
}
CChannel::~CChannel()
{
}
void CChannel::open(const sockaddr* addr)
{
// construct an socket
m_iSocket = ::socket(m_iIPversion, SOCK_DGRAM, 0);
#ifdef _WIN32
if (INVALID_SOCKET == m_iSocket)
#else
if (m_iSocket < 0)
#endif
throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR);
if ((m_iIpV6Only != -1) && (m_iIPversion == AF_INET6)) // (not an error if it fails)
::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)(&m_iIpV6Only), sizeof(m_iIpV6Only));
if (NULL != addr)
{
socklen_t namelen = m_iSockAddrSize;
if (0 != ::bind(m_iSocket, addr, namelen))
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
memcpy(&m_BindAddr, addr, namelen);
m_BindAddr.len = namelen;
}
else
{
//sendto or WSASendTo will also automatically bind the socket
addrinfo hints;
addrinfo* res;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = m_iIPversion;
hints.ai_socktype = SOCK_DGRAM;
if (0 != ::getaddrinfo(NULL, "0", &hints, &res))
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
// On Windows ai_addrlen has type size_t (unsigned), while bind takes int.
if (0 != ::bind(m_iSocket, res->ai_addr, (socklen_t)res->ai_addrlen))
{
::freeaddrinfo(res);
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
}
memcpy(&m_BindAddr, res->ai_addr, res->ai_addrlen);
m_BindAddr.len = (socklen_t) res->ai_addrlen;
::freeaddrinfo(res);
}
HLOGC(mglog.Debug, log << "CHANNEL: Bound to local address: " << SockaddrToString(&m_BindAddr));
setUDPSockOpt();
}
void CChannel::attach(UDPSOCKET udpsock)
{
m_iSocket = udpsock;
setUDPSockOpt();
}
void CChannel::setUDPSockOpt()
{
#if defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
// BSD system will fail setsockopt if the requested buffer size exceeds system maximum value
int maxsize = 64000;
if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int)))
::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&maxsize, sizeof(int));
if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int)))
::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&maxsize, sizeof(int));
#else
// for other systems, if requested is greated than maximum, the maximum value will be automactally used
if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) ||
(0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int))))
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
#endif
SRT_ASSERT(m_iIPversion == AF_INET || m_iIPversion == AF_INET6);
#ifdef SRT_ENABLE_IPOPTS
if (-1 != m_iIpTTL)
{
if (m_iIPversion == AF_INET)
{
if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_iIpTTL, sizeof(m_iIpTTL)))
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
}
else
{
// If IPv6 address is unspecified, set BOTH IP_TTL and IPV6_UNICAST_HOPS.
// For specified IPv6 address, set IPV6_UNICAST_HOPS ONLY UNLESS it's an IPv4-mapped-IPv6
if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr))
{
if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char*)&m_iIpTTL, sizeof(m_iIpTTL)))
{
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
}
}
// For specified IPv6 address, set IP_TTL ONLY WHEN it's an IPv4-mapped-IPv6
if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr))
{
if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_iIpTTL, sizeof(m_iIpTTL)))
{
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
}
}
}
}
if (-1 != m_iIpToS)
{
if (m_iIPversion == AF_INET)
{
if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_iIpToS, sizeof(m_iIpToS)))
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
}
else
{
// If IPv6 address is unspecified, set BOTH IP_TOS and IPV6_TCLASS.
#ifdef IPV6_TCLASS
// For specified IPv6 address, set IPV6_TCLASS ONLY UNLESS it's an IPv4-mapped-IPv6
if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr))
{
if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*)&m_iIpToS, sizeof(m_iIpToS)))
{
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
}
}
#endif
// For specified IPv6 address, set IP_TOS ONLY WHEN it's an IPv4-mapped-IPv6
if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr))
{
if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_iIpToS, sizeof(m_iIpToS)))
{
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
}
}
}
}
#endif
#ifdef UNIX
// Set non-blocking I/O
// UNIX does not support SO_RCVTIMEO
int opts = ::fcntl(m_iSocket, F_GETFL);
if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK))
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
#elif defined(_WIN32)
u_long nonBlocking = 1;
if (0 != ioctlsocket (m_iSocket, FIONBIO, &nonBlocking))
throw CUDTException (MJ_SETUP, MN_NORES, NET_ERROR);
#else
timeval tv;
tv.tv_sec = 0;
#if defined (BSD) || defined (OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
// Known BSD bug as the day I wrote this code.
// A small time out value will cause the socket to block forever.
tv.tv_usec = 10000;
#else
tv.tv_usec = 100;
#endif
// Set receiving time-out value
if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(timeval)))
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
#endif
}
void CChannel::close() const
{
#ifndef _WIN32
::close(m_iSocket);
#else
::closesocket(m_iSocket);
#endif
}
int CChannel::getSndBufSize()
{
socklen_t size = sizeof(socklen_t);
::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char *)&m_iSndBufSize, &size);
return m_iSndBufSize;
}
int CChannel::getRcvBufSize()
{
socklen_t size = sizeof(socklen_t);
::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char *)&m_iRcvBufSize, &size);
return m_iRcvBufSize;
}
void CChannel::setSndBufSize(int size)
{
m_iSndBufSize = size;
}
void CChannel::setRcvBufSize(int size)
{
m_iRcvBufSize = size;
}
void CChannel::setIpV6Only(int ipV6Only)
{
m_iIpV6Only = ipV6Only;
}
#ifdef SRT_ENABLE_IPOPTS
int CChannel::getIpTTL() const
{
socklen_t size = sizeof(m_iIpTTL);
if (m_iIPversion == AF_INET)
{
::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (char *)&m_iIpTTL, &size);
}
else
{
::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&m_iIpTTL, &size);
}
return m_iIpTTL;
}
int CChannel::getIpToS() const
{
socklen_t size = sizeof(m_iIpToS);
if (m_iIPversion == AF_INET)
{
::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (char *)&m_iIpToS, &size);
}
else
{
#ifdef IPV6_TCLASS
::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (char *)&m_iIpToS, &size);
#endif
}
return m_iIpToS;
}
void CChannel::setIpTTL(int ttl)
{
m_iIpTTL = ttl;
}
void CChannel::setIpToS(int tos)
{
m_iIpToS = tos;
}
#endif
int CChannel::ioctlQuery(int SRT_ATR_UNUSED type) const
{
#ifdef unix
int value = 0;
int res = ::ioctl(m_iSocket, type, &value);
if ( res != -1 )
return value;
#endif
return -1;
}
int CChannel::sockoptQuery(int SRT_ATR_UNUSED level, int SRT_ATR_UNUSED option) const
{
#ifdef unix
int value = 0;
socklen_t len = sizeof (int);
int res = ::getsockopt(m_iSocket, level, option, &value, &len);
if ( res != -1 )
return value;
#endif
return -1;
}
void CChannel::getSockAddr(sockaddr* addr) const
{
socklen_t namelen = m_iSockAddrSize;
::getsockname(m_iSocket, addr, &namelen);
}
void CChannel::getPeerAddr(sockaddr* addr) const
{
socklen_t namelen = m_iSockAddrSize;
::getpeername(m_iSocket, addr, &namelen);
}
int CChannel::sendto(const sockaddr* addr, CPacket& packet) const
{
#if ENABLE_HEAVY_LOGGING
std::ostringstream spec;
if (packet.isControl())
{
spec << " type=CONTROL"
<< " cmd=" << MessageTypeStr(packet.getType(), packet.getExtendedType())
<< " arg=" << packet.header(SRT_PH_MSGNO);
}
else
{
spec << " type=DATA"
<< " %" << packet.getSeqNo()
<< " msgno=" << MSGNO_SEQ::unwrap(packet.m_iMsgNo)
<< packet.MessageFlagStr()
<< " !" << BufferStamp(packet.m_pcData, packet.getLength());
}
LOGC(mglog.Debug, log << "CChannel::sendto: SENDING NOW DST=" << SockaddrToString(addr)
<< " target=@" << packet.m_iID
<< " size=" << packet.getLength()
<< " pkt.ts=" << FormatTime(packet.m_iTimeStamp)
<< spec.str());
#endif
#ifdef SRT_TEST_FAKE_LOSS
#define FAKELOSS_STRING_0(x) #x
#define FAKELOSS_STRING(x) FAKELOSS_STRING_0(x)
const char* fakeloss_text = FAKELOSS_STRING(SRT_TEST_FAKE_LOSS);
#undef FAKELOSS_STRING
#undef FAKELOSS_WRAP
static int dcounter = 0;
static int flwcounter = 0;
struct FakelossConfig
{
pair<int,int> config;
FakelossConfig(const char* f)
{
vector<string> out;
Split(f, '+', back_inserter(out));
config.first = atoi(out[0].c_str());
config.second = out.size() > 1 ? atoi(out[1].c_str()) : 8;
}
};
static FakelossConfig fakeloss = fakeloss_text;
if (!packet.isControl())
{
if (dcounter == 0)
{
timeval tv;
gettimeofday(&tv, 0);
srand(tv.tv_usec & 0xFFFF);
}
++dcounter;
if (flwcounter)
{
// This is a counter of how many packets in a row shall be lost
--flwcounter;
HLOGC(mglog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (" << flwcounter << " more to drop)");
return packet.getLength(); // fake successful sendinf
}
if (dcounter > 8)
{
// Make a random number in the range between 8 and 24
int rnd = rand() % 16 + SRT_TEST_FAKE_LOSS;
if (dcounter > rnd)
{
dcounter = 1;
HLOGC(mglog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (will drop " << fakeloss.config.first << " more)");
flwcounter = fakeloss.config.first;
return packet.getLength(); // fake successful sendinf
}
}
}
#endif
// convert control information into network order
// XXX USE HtoNLA!
if (packet.isControl())
for (ptrdiff_t i = 0, n = packet.getLength() / 4; i < n; ++i)
*((uint32_t *)packet.m_pcData + i) = htonl(*((uint32_t *)packet.m_pcData + i));
// convert packet header into network order
//for (int j = 0; j < 4; ++ j)
// packet.m_nHeader[j] = htonl(packet.m_nHeader[j]);
uint32_t* p = packet.m_nHeader;
for (int j = 0; j < 4; ++ j)
{
*p = htonl(*p);
++ p;
}
#ifndef _WIN32
msghdr mh;
mh.msg_name = (sockaddr*)addr;
mh.msg_namelen = m_iSockAddrSize;
mh.msg_iov = (iovec*)packet.m_PacketVector;
mh.msg_iovlen = 2;
mh.msg_control = NULL;
mh.msg_controllen = 0;
mh.msg_flags = 0;
int res = ::sendmsg(m_iSocket, &mh, 0);
#else
DWORD size = (DWORD) (CPacket::HDR_SIZE + packet.getLength());
int addrsize = m_iSockAddrSize;
int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr, addrsize, NULL, NULL);
res = (0 == res) ? size : -1;
#endif
// convert back into local host order
//for (int k = 0; k < 4; ++ k)
// packet.m_nHeader[k] = ntohl(packet.m_nHeader[k]);
p = packet.m_nHeader;
for (int k = 0; k < 4; ++ k)
{
*p = ntohl(*p);
++ p;
}
if (packet.isControl())
{
for (ptrdiff_t l = 0, n = packet.getLength() / 4; l < n; ++ l)
*((uint32_t *)packet.m_pcData + l) = ntohl(*((uint32_t *)packet.m_pcData + l));
}
return res;
}
EReadStatus CChannel::recvfrom(sockaddr* addr, CPacket& packet) const
{
EReadStatus status = RST_OK;
int msg_flags = 0;
int recv_size = -1;
#if defined(UNIX) || defined(_WIN32)
fd_set set;
timeval tv;
FD_ZERO(&set);
FD_SET(m_iSocket, &set);
tv.tv_sec = 0;
tv.tv_usec = 10000;
const int select_ret = ::select((int) m_iSocket + 1, &set, NULL, &set, &tv);
#else
const int select_ret = 1; // the socket is expected to be in the blocking mode itself
#endif
if (select_ret == 0) // timeout
{
packet.setLength(-1);
return RST_AGAIN;
}
#ifndef _WIN32
if (select_ret > 0)
{
msghdr mh;
mh.msg_name = addr;
mh.msg_namelen = m_iSockAddrSize;
mh.msg_iov = packet.m_PacketVector;
mh.msg_iovlen = 2;
mh.msg_control = NULL;
mh.msg_controllen = 0;
mh.msg_flags = 0;
recv_size = ::recvmsg(m_iSocket, &mh, 0);
msg_flags = mh.msg_flags;
}
// Note that there are exactly four groups of possible errors
// reported by recvmsg():
// 1. Temporary error, can't get the data, but you can try again.
// Codes: EAGAIN/EWOULDBLOCK, EINTR, ECONNREFUSED
// Return: RST_AGAIN.
//
// 2. Problems that should never happen due to unused configurations.
// Codes: ECONNREFUSED, ENOTCONN
// Return: RST_ERROR, just formally treat this as IPE.
//
// 3. Unexpected runtime errors:
// Codes: EINVAL, EFAULT, ENOMEM, ENOTSOCK
// Return: RST_ERROR. Except ENOMEM, this can only be an IPE. ENOMEM
// should make the program stop as lacking memory will kill the program anyway soon.
//
// 4. Expected socket closed in the meantime by another thread.
// Codes: EBADF
// Return: RST_ERROR. This will simply make the worker thread exit, which is
// expected to happen after CChannel::close() is called by another thread.
// We do not handle <= SOCKET_ERROR as they are handled further by checking the recv_size
if (select_ret == -1 || recv_size == -1)
{
const int err = NET_ERROR;
if (err == EAGAIN || err == EINTR || err == ECONNREFUSED) // For EAGAIN, this isn't an error, just a useless call.
{
status = RST_AGAIN;
}
else
{
HLOGC(mglog.Debug, log << CONID() << "(sys)recvmsg: " << SysStrError(err) << " [" << err << "]");
status = RST_ERROR;
}
goto Return_error;
}
#else
// XXX REFACTORING NEEDED!
// This procedure uses the WSARecvFrom function that just reads
// into one buffer. On Windows, the equivalent for recvmsg, WSARecvMsg
// uses the equivalent of msghdr - WSAMSG, which has different field
// names and also uses the equivalet of iovec - WSABUF, which has different
// field names and layout. It is important that this code be translated
// to the "proper" solution, however this requires that CPacket::m_PacketVector
// also uses the "platform independent" (or, better, platform-suitable) type
// which can be appropriate for the appropriate system function, not just iovec
// (see a specifically provided definition for iovec for windows in packet.h).
//
// For the time being, the msg_flags variable is defined in both cases
// so that it can be checked independently, however it won't have any other
// value one Windows than 0, unless this procedure below is rewritten
// to use WSARecvMsg().
int recv_ret = SOCKET_ERROR;
DWORD flag = 0;
if (select_ret > 0) // the total number of socket handles that are ready
{
DWORD size = (DWORD) (CPacket::HDR_SIZE + packet.getLength());
int addrsize = m_iSockAddrSize;
recv_ret = ::WSARecvFrom(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, &flag, addr, &addrsize, NULL, NULL);
if (recv_ret == 0)
recv_size = size;
}
// We do not handle <= SOCKET_ERROR as they are handled further by checking the recv_size
if (select_ret == SOCKET_ERROR || recv_ret == SOCKET_ERROR) // == SOCKET_ERROR
{
recv_size = -1;
// On Windows this is a little bit more complicated, so simply treat every error
// as an "again" situation. This should still be probably fixed, but it needs more
// thorough research. For example, the problem usually reported from here is
// WSAETIMEDOUT, which isn't mentioned in the documentation of WSARecvFrom at all.
//
// These below errors are treated as "fatal", all others are treated as "again".
static const int fatals [] =
{
WSAEFAULT,
WSAEINVAL,
WSAENETDOWN,
WSANOTINITIALISED,
WSA_OPERATION_ABORTED
};
static const int* fatals_end = fatals + Size(fatals);
const int err = NET_ERROR;
if (std::find(fatals, fatals_end, err) != fatals_end)
{
HLOGC(mglog.Debug, log << CONID() << "(sys)WSARecvFrom: " << SysStrError(err) << " [" << err << "]");
status = RST_ERROR;
}
else
{
status = RST_AGAIN;
}
goto Return_error;
}
// Not sure if this problem has ever occurred on Windows, just a sanity check.
if (flag & MSG_PARTIAL)
msg_flags = 1;
#endif
// Sanity check for a case when it didn't fill in even the header
if (size_t(recv_size) < CPacket::HDR_SIZE)
{
status = RST_AGAIN;
HLOGC(mglog.Debug, log << CONID() << "POSSIBLE ATTACK: received too short packet with " << recv_size << " bytes");
goto Return_error;
}
// Fix for an issue with Linux Kernel found during tests at Tencent.
//
// There was a bug in older Linux Kernel which caused that when the internal
// buffer was depleted during reading from the network, not the whole buffer
// was copied from the packet, EVEN THOUGH THE GIVEN BUFFER WAS OF ENOUGH SIZE.
// It was still very kind of the buggy procedure, though, that at least
// they inform the caller about that this has happened by setting MSG_TRUNC
// flag.
//
// Normally this flag should be set only if there was too small buffer given
// by the caller, so as this code knows that the size is enough, it never
// predicted this to happen. Just for a case then when you run this on a buggy
// system that suffers of this problem, the fix for this case is left here.
//
// When this happens, then you have at best a fragment of the buffer and it's
// useless anyway. This is solved by dropping the packet and fake that no
// packet was received, so the packet will be then retransmitted.
if ( msg_flags != 0 )
{
HLOGC(mglog.Debug, log << CONID() << "NET ERROR: packet size=" << recv_size
<< " msg_flags=0x" << hex << msg_flags << ", possibly MSG_TRUNC (0x" << hex << int(MSG_TRUNC) << ")");
status = RST_AGAIN;
goto Return_error;
}
packet.setLength(recv_size - CPacket::HDR_SIZE);
// convert back into local host order
// XXX use NtoHLA().
//for (int i = 0; i < 4; ++ i)
// packet.m_nHeader[i] = ntohl(packet.m_nHeader[i]);
{
uint32_t* p = packet.m_nHeader;
for (size_t i = 0; i < SRT_PH__SIZE; ++ i)
{
*p = ntohl(*p);
++ p;
}
}
if (packet.isControl())
{
for (size_t j = 0, n = packet.getLength() / sizeof (uint32_t); j < n; ++ j)
*((uint32_t *)packet.m_pcData + j) = ntohl(*((uint32_t *)packet.m_pcData + j));
}
return RST_OK;
Return_error:
packet.setLength(-1);
return status;
}

View file

@ -0,0 +1,187 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 01/27/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_CHANNEL_H__
#define __UDT_CHANNEL_H__
#include "udt.h"
#include "packet.h"
#include "netinet_any.h"
class CChannel
{
public:
// XXX There's currently no way to access the socket ID set for
// whatever the channel is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
CChannel();
CChannel(int version);
~CChannel();
/// Open a UDP channel.
/// @param [in] addr The local address that UDP will use.
void open(const sockaddr* addr = NULL);
/// Open a UDP channel based on an existing UDP socket.
/// @param [in] udpsock UDP socket descriptor.
void attach(UDPSOCKET udpsock);
/// Disconnect and close the UDP entity.
void close() const;
/// Get the UDP sending buffer size.
/// @return Current UDP sending buffer size.
int getSndBufSize();
/// Get the UDP receiving buffer size.
/// @return Current UDP receiving buffer size.
int getRcvBufSize();
/// Set the UDP sending buffer size.
/// @param [in] size expected UDP sending buffer size.
void setSndBufSize(int size);
/// Set the UDP receiving buffer size.
/// @param [in] size expected UDP receiving buffer size.
void setRcvBufSize(int size);
/// Set the IPV6ONLY option.
/// @param [in] IPV6ONLY value.
void setIpV6Only(int ipV6Only);
/// Query the socket address that the channel is using.
/// @param [out] addr pointer to store the returned socket address.
void getSockAddr(sockaddr* addr) const;
/// Query the peer side socket address that the channel is connect to.
/// @param [out] addr pointer to store the returned socket address.
void getPeerAddr(sockaddr* addr) const;
/// Send a packet to the given address.
/// @param [in] addr pointer to the destination address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data sent.
int sendto(const sockaddr* addr, CPacket& packet) const;
/// Receive a packet from the channel and record the source address.
/// @param [in] addr pointer to the source address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data received.
EReadStatus recvfrom(sockaddr* addr, CPacket& packet) const;
#ifdef SRT_ENABLE_IPOPTS
/// Set the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return none.
void setIpTTL(int ttl);
/// Set the IP Type of Service.
/// @param [in] tos IP Type of Service.
void setIpToS(int tos);
/// Get the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return TTL.
int getIpTTL() const;
/// Get the IP Type of Service.
/// @return ToS.
int getIpToS() const;
#endif
int ioctlQuery(int type) const;
int sockoptQuery(int level, int option) const;
const sockaddr* bindAddress() { return &m_BindAddr; }
const sockaddr_any& bindAddressAny() { return m_BindAddr; }
private:
void setUDPSockOpt();
private:
const int m_iIPversion; // IP version
int m_iSockAddrSize; // socket address structure size (pre-defined to avoid run-time test)
UDPSOCKET m_iSocket; // socket descriptor
#ifdef SRT_ENABLE_IPOPTS
int m_iIpTTL;
int m_iIpToS;
#endif
int m_iSndBufSize; // UDP sending buffer size
int m_iRcvBufSize; // UDP receiving buffer size
int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set)
sockaddr_any m_BindAddr;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,850 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 08/01/2009
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_COMMON_H__
#define __UDT_COMMON_H__
#define _CRT_SECURE_NO_WARNINGS 1 // silences windows complaints for sscanf
#include <cstdlib>
#ifndef _WIN32
#include <sys/time.h>
#include <sys/uio.h>
#else
// #include <winsock2.h>
//#include <windows.h>
#endif
#include <pthread.h>
#include "udt.h"
#include "utilities.h"
#ifdef _DEBUG
#include <assert.h>
#define SRT_ASSERT(cond) assert(cond)
#else
#define SRT_ASSERT(cond)
#endif
enum UDTSockType
{
UDT_UNDEFINED = 0, // initial trap representation
UDT_STREAM = 1,
UDT_DGRAM
};
/// The message types used by UDT protocol. This is a part of UDT
/// protocol and should never be changed.
enum UDTMessageType
{
UMSG_HANDSHAKE = 0, //< Connection Handshake. Control: see @a CHandShake.
UMSG_KEEPALIVE = 1, //< Keep-alive.
UMSG_ACK = 2, //< Acknowledgement. Control: past-the-end sequence number up to which packets have been received.
UMSG_LOSSREPORT = 3, //< Negative Acknowledgement (NAK). Control: Loss list.
UMSG_CGWARNING = 4, //< Congestion warning.
UMSG_SHUTDOWN = 5, //< Shutdown.
UMSG_ACKACK = 6, //< Acknowledgement of Acknowledgement. Add info: The ACK sequence number
UMSG_DROPREQ = 7, //< Message Drop Request. Add info: Message ID. Control Info: (first, last) number of the message.
UMSG_PEERERROR = 8, //< Signal from the Peer side. Add info: Error code.
// ... add extra code types here
UMSG_END_OF_TYPES,
UMSG_EXT = 0x7FFF //< For the use of user-defined control packets.
};
// This side's role is: INITIATOR prepares the environment first, and sends
// appropriate information to the peer. The peer must be RESPONDER and be ready
// to receive it. It's important for the encryption: the INITIATOR side generates
// the KM, and sends it to RESPONDER. RESPONDER awaits KM received from the
// INITIATOR. Note that in bidirectional mode - that is always with HSv5 - the
// INITIATOR creates both sending and receiving contexts, then sends the key to
// RESPONDER, which creates both sending and receiving contexts, using the same
// key received from INITIATOR.
//
// The method of selection:
//
// In HSv4, it's always data sender (the party that sets SRTO_SENDER flag on the
// socket) INITIATOR, and receiver - RESPONDER. The HSREQ and KMREQ are done
// AFTER the UDT connection is done using UMSG_EXT extension messages. As this
// is unidirectional, the INITIATOR prepares the sending context only, the
// RESPONDER - receiving context only.
//
// In HSv5, for caller-listener configuration, it's simple: caller is INITIATOR,
// listener is RESPONDER. In case of rendezvous the parties are equivalent,
// so the role is resolved by "cookie contest". Rendezvous sockets both know
// each other's cookie generated during the URQ_WAVEAHAND handshake phase.
// The cookies are simply compared as integer numbers; the party which's cookie
// is a greater number becomes an INITIATOR, and the other party becomes a
// RESPONDER.
//
// The case of a draw - that both occasionally have baked identical cookies -
// is treated as an extremely rare and virtually impossible case, so this
// results in connection rejected.
enum HandshakeSide
{
HSD_DRAW,
HSD_INITIATOR, //< Side that initiates HSREQ/KMREQ. HSv4: data sender, HSv5: connecting socket or winner rendezvous socket
HSD_RESPONDER //< Side that expects HSREQ/KMREQ from the peer. HSv4: data receiver, HSv5: accepted socket or loser rendezvous socket
};
// For debug
std::string MessageTypeStr(UDTMessageType mt, uint32_t extt = 0);
////////////////////////////////////////////////////////////////////////////////
// Commonly used by various reading facilities
enum EReadStatus
{
RST_OK = 0, //< A new portion of data has been received
RST_AGAIN, //< Nothing has been received, try again
RST_ERROR = -1 //< Irrecoverable error, please close descriptor and stop reading.
};
enum EConnectStatus
{
CONN_ACCEPT = 0, //< Received final handshake that confirms connection established
CONN_REJECT = -1, //< Error during processing handshake.
CONN_CONTINUE = 1, //< induction->conclusion phase
CONN_RENDEZVOUS = 2, //< pass to a separate rendezvous processing (HSv5 only)
CONN_CONFUSED = 3, //< listener thinks it's connected, but caller missed conclusion
CONN_RUNNING = 10, //< no connection in progress, already connected
CONN_AGAIN = -2 //< No data was read, don't change any state.
};
std::string ConnectStatusStr(EConnectStatus est);
const int64_t BW_INFINITE = 1000000000/8; //Infinite=> 1 Gbps
enum ETransmissionEvent
{
TEV_INIT, // --> After creation, and after any parameters were updated.
TEV_ACK, // --> When handling UMSG_ACK - older CCC:onAck()
TEV_ACKACK, // --> UDT does only RTT sync, can be read from CUDT::RTT().
TEV_LOSSREPORT, // --> When handling UMSG_LOSSREPORT - older CCC::onLoss()
TEV_CHECKTIMER, // --> See TEV_CHT_REXMIT
TEV_SEND, // --> When the packet is scheduled for sending - older CCC::onPktSent
TEV_RECEIVE, // --> When a data packet was received - older CCC::onPktReceived
TEV_CUSTOM, // --> probably dead call - older CCC::processCustomMsg
TEV__SIZE
};
std::string TransmissionEventStr(ETransmissionEvent ev);
// Special parameter for TEV_CHECKTIMER
enum ECheckTimerStage
{
TEV_CHT_INIT, // --> UDT: just update parameters, don't call any CCC::*
TEV_CHT_FASTREXMIT, // --> not available on UDT
TEV_CHT_REXMIT // --> CCC::onTimeout() in UDT
};
enum EInitEvent
{
TEV_INIT_RESET = 0,
TEV_INIT_INPUTBW,
TEV_INIT_OHEADBW
};
class CPacket;
// XXX Use some more standard less hand-crafted solution, if possible
// XXX Consider creating a mapping between TEV_* values and associated types,
// so that the type is compiler-enforced when calling updateCC() and when
// connecting signals to slots.
struct EventVariant
{
enum Type {UNDEFINED, PACKET, ARRAY, ACK, STAGE, INIT} type;
union U
{
CPacket* packet;
int32_t ack;
struct
{
int32_t* ptr;
size_t len;
} array;
ECheckTimerStage stage;
EInitEvent init;
} u;
EventVariant()
{
type = UNDEFINED;
memset(&u, 0, sizeof u);
}
template<Type t>
struct VariantFor;
template <Type tp, typename Arg>
void Assign(Arg arg)
{
type = tp;
(u.*(VariantFor<tp>::field())) = arg;
//(u.*field) = arg;
}
void operator=(CPacket* arg) { Assign<PACKET>(arg); };
void operator=(int32_t arg) { Assign<ACK>(arg); };
void operator=(ECheckTimerStage arg) { Assign<STAGE>(arg); };
void operator=(EInitEvent arg) { Assign<INIT>(arg); };
// Note: UNDEFINED and ARRAY don't have assignment operator.
// For ARRAY you'll use 'set' function. For UNDEFINED there's nothing.
template <class T>
EventVariant(T arg)
{
*this = arg;
}
const int32_t* get_ptr() const
{
return u.array.ptr;
}
size_t get_len()
{
return u.array.len;
}
void set(int32_t* ptr, size_t len)
{
type = ARRAY;
u.array.ptr = ptr;
u.array.len = len;
}
EventVariant(int32_t* ptr, size_t len)
{
set(ptr, len);
}
template<Type T>
typename VariantFor<T>::type get()
{
return u.*(VariantFor<T>::field());
}
};
/*
Maybe later.
This had to be a solution for automatic extraction of the
type hidden in particular EventArg for particular event so
that it's not runtime-mistaken.
In order that this make sense there would be required an array
indexed by event id (just like a slot array m_Slots in CUDT),
where the "type distiller" function would be extracted and then
combined with the user-connected slot function this would call
it already with correct type. Note that also the ConnectSignal
function would have to get the signal id by template parameter,
not function parameter. For example:
m_parent->ConnectSignal<TEV_ACK>(SSLOT(updateOnSent));
in which updateOnSent would have to receive an appropriate type.
This has a disadvantage that you can't connect multiple signals
with different argument types to the same slot, you'd have to
make slot wrappers to translate arguments.
It seems that a better idea would be to create binders that would
translate the argument from EventArg to the correct type according
to the rules imposed by particular event id. But I'd not make it
until there's a green light on C++11 for SRT, so maybe in a far future.
template <ETransmissionEvent type>
class EventArgType;
#define MAP_EVENT_TYPE(tev, tp) template<> class EventArgType<tev> { typedef tp type; }
*/
// The 'type' field wouldn't be even necessary if we
template<> struct EventVariant::VariantFor<EventVariant::PACKET>
{
typedef CPacket* type;
static type U::*field() {return &U::packet;}
};
template<> struct EventVariant::VariantFor<EventVariant::ACK>
{
typedef int32_t type;
static type U::*field() { return &U::ack; }
};
template<> struct EventVariant::VariantFor<EventVariant::STAGE>
{
typedef ECheckTimerStage type;
static type U::*field() { return &U::stage; }
};
template<> struct EventVariant::VariantFor<EventVariant::INIT>
{
typedef EInitEvent type;
static type U::*field() { return &U::init; }
};
// Using a hand-crafted solution because there's a non-backward-compatible
// change between C++03 and others on the way up to C++17 (and we want this
// code to be compliant with all C++ standards):
//
// - there's std::mem_fun in C++03 - deprecated in C++11, removed in C++17
// - std::function in C++11 would be perfect, but not in C++03
// This can be changed in future to use C++11 way, but only after C++03
// compatibility is finally abaondoned. Until then, this stays with a custom
// class.
class EventSlotBase
{
public:
virtual void emit(ETransmissionEvent tev, EventVariant var) = 0;
typedef void dispatcher_t(void* opaque, ETransmissionEvent tev, EventVariant var);
virtual ~EventSlotBase() {}
};
class SimpleEventSlot: public EventSlotBase
{
public:
void* opaque;
dispatcher_t* dispatcher;
SimpleEventSlot(void* op, dispatcher_t* disp): opaque(op), dispatcher(disp) {}
void emit(ETransmissionEvent tev, EventVariant var) ATR_OVERRIDE
{
(*dispatcher)(opaque, tev, var);
}
};
template <class Class>
class ObjectEventSlot: public EventSlotBase
{
public:
typedef void (Class::*method_ptr_t)(ETransmissionEvent tev, EventVariant var);
method_ptr_t pm;
Class* po;
ObjectEventSlot(Class* o, method_ptr_t m): pm(m), po(o) {}
void emit(ETransmissionEvent tev, EventVariant var) ATR_OVERRIDE
{
(po->*pm)(tev, var);
}
};
struct EventSlot
{
mutable EventSlotBase* slot;
// Create empty slot. Calls are ignored.
EventSlot(): slot(0) {}
// "Stealing" copy constructor, following the auto_ptr method.
// This isn't very nice, but no other way to do it in C++03
// without rvalue-reference and move.
EventSlot(const EventSlot& victim)
{
slot = victim.slot; // Should MOVE.
victim.slot = 0;
}
EventSlot(void* op, EventSlotBase::dispatcher_t* disp)
{
slot = new SimpleEventSlot(op, disp);
}
template <class ObjectClass>
EventSlot(ObjectClass* obj, typename ObjectEventSlot<ObjectClass>::method_ptr_t method)
{
slot = new ObjectEventSlot<ObjectClass>(obj, method);
}
void emit(ETransmissionEvent tev, EventVariant var)
{
if (!slot)
return;
slot->emit(tev, var);
}
~EventSlot()
{
if (slot)
delete slot;
}
};
// Old UDT library specific classes, moved from utilities as utilities
// should now be general-purpose.
class CTimer
{
public:
CTimer();
~CTimer();
public:
/// Sleep for "interval_tk" CCs.
/// @param [in] interval_tk CCs to sleep.
void sleep(uint64_t interval_tk);
/// Seelp until CC "nexttime_tk".
/// @param [in] nexttime_tk next time the caller is waken up.
void sleepto(uint64_t nexttime_tk);
/// Stop the sleep() or sleepto() methods.
void interrupt();
/// trigger the clock for a tick, for better granuality in no_busy_waiting timer.
void tick();
public:
/// Read the CPU clock cycle into x.
/// @param [out] x to record cpu clock cycles.
static void rdtsc(uint64_t &x);
/// return the CPU frequency.
/// @return CPU frequency.
static uint64_t getCPUFrequency();
/// check the current time, 64bit, in microseconds.
/// @return current time in microseconds.
static uint64_t getTime();
/// trigger an event such as new connection, close, new data, etc. for "select" call.
static void triggerEvent();
enum EWait {WT_EVENT, WT_ERROR, WT_TIMEOUT};
/// wait for an event to br triggered by "triggerEvent".
/// @retval WT_EVENT The event has happened
/// @retval WT_TIMEOUT The event hasn't happened, the function exited due to timeout
/// @retval WT_ERROR The function has exit due to an error
static EWait waitForEvent();
/// sleep for a short interval. exact sleep time does not matter
static void sleep();
/// Wait for condition with timeout
/// @param [in] cond Condition variable to wait for
/// @param [in] mutex locked mutex associated with the condition variable
/// @param [in] delay timeout in microseconds
/// @retval 0 Wait was successfull
/// @retval ETIMEDOUT The wait timed out
static int condTimedWaitUS(pthread_cond_t* cond, pthread_mutex_t* mutex, uint64_t delay);
private:
uint64_t getTimeInMicroSec();
private:
uint64_t m_ullSchedTime_tk; // next schedulled time
pthread_cond_t m_TickCond;
pthread_mutex_t m_TickLock;
static pthread_cond_t m_EventCond;
static pthread_mutex_t m_EventLock;
private:
static uint64_t s_ullCPUFrequency; // CPU frequency : clock cycles per microsecond
static uint64_t readCPUFrequency();
static bool m_bUseMicroSecond; // No higher resolution timer available, use gettimeofday().
};
////////////////////////////////////////////////////////////////////////////////
class CGuard
{
public:
/// Constructs CGuard, which locks the given mutex for
/// the scope where this object exists.
/// @param lock Mutex to lock
/// @param if_condition If this is false, CGuard will do completely nothing
CGuard(pthread_mutex_t& lock, bool if_condition = true);
~CGuard();
public:
static int enterCS(pthread_mutex_t& lock);
static int leaveCS(pthread_mutex_t& lock);
static void createMutex(pthread_mutex_t& lock);
static void releaseMutex(pthread_mutex_t& lock);
static void createCond(pthread_cond_t& cond);
static void releaseCond(pthread_cond_t& cond);
void forceUnlock();
private:
pthread_mutex_t& m_Mutex; // Alias name of the mutex to be protected
int m_iLocked; // Locking status
CGuard& operator=(const CGuard&);
};
class InvertedGuard
{
pthread_mutex_t* m_pMutex;
public:
InvertedGuard(pthread_mutex_t* smutex): m_pMutex(smutex)
{
if ( !smutex )
return;
CGuard::leaveCS(*smutex);
}
~InvertedGuard()
{
if ( !m_pMutex )
return;
CGuard::enterCS(*m_pMutex);
}
};
////////////////////////////////////////////////////////////////////////////////
// UDT Sequence Number 0 - (2^31 - 1)
// seqcmp: compare two seq#, considering the wraping
// seqlen: length from the 1st to the 2nd seq#, including both
// seqoff: offset from the 2nd to the 1st seq#
// incseq: increase the seq# by 1
// decseq: decrease the seq# by 1
// incseq: increase the seq# by a given offset
class CSeqNo
{
public:
/// This behaves like seq1 - seq2, in comparison to numbers,
/// and with the statement that only the sign of the result matters.
/// That is, it returns a negative value if seq1 < seq2,
/// positive if seq1 > seq2, and zero if they are equal.
/// The only correct application of this function is when you
/// compare two values and it works faster than seqoff. However
/// the result's meaning is only in its sign. DO NOT USE THE
/// VALUE for any other purpose. It is not meant to be the
/// distance between two sequence numbers.
///
/// Example: to check if (seq1 %> seq2): seqcmp(seq1, seq2) > 0.
inline static int seqcmp(int32_t seq1, int32_t seq2)
{return (abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1);}
/// This function measures a length of the range from seq1 to seq2,
/// WITH A PRECONDITION that certainly @a seq1 is earlier than @a seq2.
/// This can also include an enormously large distance between them,
/// that is, exceeding the m_iSeqNoTH value (can be also used to test
/// if this distance is larger). Prior to calling this function the
/// caller must be certain that @a seq2 is a sequence coming from a
/// later time than @a seq1, and still, of course, this distance didn't
/// exceed m_iMaxSeqNo.
inline static int seqlen(int32_t seq1, int32_t seq2)
{return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);}
/// This behaves like seq2 - seq1, with the precondition that the true
/// distance between two sequence numbers never exceeds m_iSeqNoTH.
/// That is, if the difference in numeric values of these two arguments
/// exceeds m_iSeqNoTH, it is treated as if the later of these two
/// sequence numbers has overflown and actually a segment of the
/// MAX+1 value should be added to it to get the proper result.
///
/// Note: this function does more calculations than seqcmp, so it should
/// be used if you need the exact distance between two sequences. If
/// you are only interested with their relationship, use seqcmp.
inline static int seqoff(int32_t seq1, int32_t seq2)
{
if (abs(seq1 - seq2) < m_iSeqNoTH)
return seq2 - seq1;
if (seq1 < seq2)
return seq2 - seq1 - m_iMaxSeqNo - 1;
return seq2 - seq1 + m_iMaxSeqNo + 1;
}
inline static int32_t incseq(int32_t seq)
{return (seq == m_iMaxSeqNo) ? 0 : seq + 1;}
inline static int32_t decseq(int32_t seq)
{return (seq == 0) ? m_iMaxSeqNo : seq - 1;}
inline static int32_t incseq(int32_t seq, int32_t inc)
{return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;}
// m_iMaxSeqNo >= inc + sec --- inc + sec <= m_iMaxSeqNo
// if inc + sec > m_iMaxSeqNo then return seq + inc - (m_iMaxSeqNo+1)
inline static int32_t decseq(int32_t seq, int32_t dec)
{
// Check if seq - dec < 0, but before it would have happened
if ( seq < dec )
{
int32_t left = dec - seq; // This is so many that is left after dragging dec to 0
// So now decrement the (m_iMaxSeqNo+1) by "left"
return m_iMaxSeqNo - left + 1;
}
return seq - dec;
}
public:
static const int32_t m_iSeqNoTH = 0x3FFFFFFF; // threshold for comparing seq. no.
static const int32_t m_iMaxSeqNo = 0x7FFFFFFF; // maximum sequence number used in UDT
};
////////////////////////////////////////////////////////////////////////////////
// UDT ACK Sub-sequence Number: 0 - (2^31 - 1)
class CAckNo
{
public:
inline static int32_t incack(int32_t ackno)
{return (ackno == m_iMaxAckSeqNo) ? 0 : ackno + 1;}
public:
static const int32_t m_iMaxAckSeqNo = 0x7FFFFFFF; // maximum ACK sub-sequence number used in UDT
};
////////////////////////////////////////////////////////////////////////////////
struct CIPAddress
{
static bool ipcmp(const struct sockaddr* addr1, const struct sockaddr* addr2, int ver = AF_INET);
static void ntop(const struct sockaddr* addr, uint32_t ip[4], int ver = AF_INET);
static void pton(struct sockaddr* addr, const uint32_t ip[4], int ver = AF_INET);
static std::string show(const struct sockaddr* adr);
};
////////////////////////////////////////////////////////////////////////////////
struct CMD5
{
static void compute(const char* input, unsigned char result[16]);
};
// Debug stats
template <size_t SIZE>
class StatsLossRecords
{
int32_t initseq;
std::bitset<SIZE> array;
public:
StatsLossRecords(): initseq(-1) {}
// To check if this structure still keeps record of that sequence.
// This is to check if the information about this not being found
// is still reliable.
bool exists(int32_t seq)
{
return initseq != -1 && CSeqNo::seqcmp(seq, initseq) >= 0;
}
int32_t base() { return initseq; }
void clear()
{
initseq = -1;
array.reset();
}
void add(int32_t lo, int32_t hi)
{
int32_t end = CSeqNo::incseq(hi);
for (int32_t i = lo; i != end; i = CSeqNo::incseq(i))
add(i);
}
void add(int32_t seq)
{
if ( array.none() )
{
// May happen it wasn't initialized. Set it as initial loss sequence.
initseq = seq;
array[0] = true;
return;
}
// Calculate the distance between this seq and the oldest one.
int seqdiff = CSeqNo::seqoff(initseq, seq);
if ( seqdiff > int(SIZE) )
{
// Size exceeded. Drop the oldest sequences.
// First calculate how many must be removed.
size_t toremove = seqdiff - SIZE;
// Now, since that position, find the nearest 1
while ( !array[toremove] && toremove <= SIZE )
++toremove;
// All have to be dropped, so simply reset the array
if ( toremove == SIZE )
{
initseq = seq;
array[0] = true;
return;
}
// Now do the shift of the first found 1 to position 0
// and its index add to initseq
initseq += toremove;
seqdiff -= toremove;
array >>= toremove;
}
// Now set appropriate bit that represents this seq
array[seqdiff] = true;
}
StatsLossRecords& operator << (int32_t seq)
{
add(seq);
return *this;
}
void remove(int32_t seq)
{
// Check if is in range. If not, ignore.
int seqdiff = CSeqNo::seqoff(initseq, seq);
if ( seqdiff < 0 )
return; // already out of array
if ( seqdiff > SIZE )
return; // never was added!
array[seqdiff] = true;
}
bool find(int32_t seq) const
{
int seqdiff = CSeqNo::seqoff(initseq, seq);
if ( seqdiff < 0 )
return false; // already out of array
if ( size_t(seqdiff) > SIZE )
return false; // never was added!
return array[seqdiff];
}
#if HAVE_CXX11
std::string to_string() const
{
std::string out;
for (size_t i = 0; i < SIZE; ++i)
{
if ( array[i] )
out += std::to_string(initseq+i) + " ";
}
return out;
}
#endif
};
// Version parsing
inline ATR_CONSTEXPR uint32_t SrtVersion(int major, int minor, int patch)
{
return patch + minor*0x100 + major*0x10000;
}
inline int32_t SrtParseVersion(const char* v)
{
int major, minor, patch;
int result = sscanf(v, "%d.%d.%d", &major, &minor, &patch);
if (result != 3)
{
return 0;
}
return major*0x10000 + minor*0x100 + patch;
}
inline std::string SrtVersionString(int version)
{
int patch = version % 0x100;
int minor = (version/0x100)%0x100;
int major = version/0x10000;
char buf[20];
sprintf(buf, "%d.%d.%d", major, minor, patch);
return buf;
}
#endif

View file

@ -0,0 +1,643 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
// This is a controversial thing, so temporarily blocking
//#define SRT_ENABLE_SYSTEMBUFFER_TRACE
#ifdef SRT_ENABLE_SYSTEMBUFFER_TRACE
#if defined(unix)
// XXX will be nonportable
#include <sys/ioctl.h>
#endif
#endif
#include <string>
#include <cmath>
#include "common.h"
#include "core.h"
#include "queue.h"
#include "packet.h"
#include "congctl.h"
#include "logging.h"
using namespace std;
using namespace srt_logging;
SrtCongestionControlBase::SrtCongestionControlBase(CUDT* parent)
{
m_parent = parent;
m_dMaxCWndSize = m_parent->flowWindowSize();
// RcvRate (deliveryRate()), RTT and Bandwidth can be read directly from CUDT when needed.
m_dCWndSize = 1000;
m_dPktSndPeriod = 1;
}
void SrtCongestion::Check()
{
if (!congctl)
throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
}
// Useful macro to shorthand passing a method as argument
// Requires "Me" name by which a class refers to itself
#define SSLOT(method) EventSlot(this, &Me:: method)
class LiveCC: public SrtCongestionControlBase
{
int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec)
size_t m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit
size_t m_zMaxPayloadSize;
// NAKREPORT stuff.
int m_iMinNakInterval_us; // Minimum NAK Report Period (usec)
int m_iNakReportAccel; // NAK Report Period (RTT) accelerator
typedef LiveCC Me; // required for SSLOT macro
public:
LiveCC(CUDT* parent)
: SrtCongestionControlBase(parent)
{
m_llSndMaxBW = BW_INFINITE; // 1 Gbbps in Bytes/sec BW_INFINITE
m_zMaxPayloadSize = parent->OPT_PayloadSize();
if ( m_zMaxPayloadSize == 0 )
m_zMaxPayloadSize = parent->maxPayloadSize();
m_zSndAvgPayloadSize = m_zMaxPayloadSize;
m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec)
m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator
HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize);
updatePktSndPeriod();
// NOTE: TEV_SEND gets dispatched from Sending thread, all others
// from receiving thread.
parent->ConnectSignal(TEV_SEND, SSLOT(updatePayloadSize));
/*
* Readjust the max SndPeriod onACK (and onTimeout)
*/
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(updatePktSndPeriod_onTimer));
parent->ConnectSignal(TEV_ACK, SSLOT(updatePktSndPeriod_onAck));
}
bool checkTransArgs(SrtCongestion::TransAPI api, SrtCongestion::TransDir dir, const char* , size_t size, int , bool ) ATR_OVERRIDE
{
if (api != SrtCongestion::STA_MESSAGE)
{
LOGC(cclog.Error, log << "LiveCC: invalid API use. Only sendmsg/recvmsg allowed.");
return false;
}
if (dir == SrtCongestion::STAD_SEND)
{
// For sending, check if the size of data doesn't exceed the maximum live packet size.
if (size > m_zMaxPayloadSize)
{
LOGC(cclog.Error, log << "LiveCC: payload size: " << size << " exceeds maximum allowed " << m_zMaxPayloadSize);
return false;
}
}
else
{
// For receiving, check if the buffer has enough space to keep the payload.
if (size < m_zMaxPayloadSize)
{
LOGC(cclog.Error, log << "LiveCC: buffer size: " << size << " is too small for the maximum possible " << m_zMaxPayloadSize);
return false;
}
}
return true;
}
// XXX You can decide here if the not-fully-packed packet should require immediate ACK or not.
// bool needsQuickACK(const CPacket& pkt) ATR_OVERRIDE
virtual int64_t sndBandwidth() ATR_OVERRIDE { return m_llSndMaxBW; }
private:
// SLOTS:
// TEV_SEND -> CPacket*.
void updatePayloadSize(ETransmissionEvent, EventVariant var)
{
const CPacket& packet = *var.get<EventVariant::PACKET>();
// XXX NOTE: TEV_SEND is sent from CSndQueue::worker thread, which is
// different to threads running any other events (TEV_CHECKTIMER and TEV_ACK).
// The m_zSndAvgPayloadSize field is however left unguarded because
// there's no other modifier of this field.
// Worst case scenario, the procedure running in CRcvQueue::worker
// thread will pick up a "slightly outdated" average value from this
// field - this is insignificant.
m_zSndAvgPayloadSize = avg_iir<128, size_t>(m_zSndAvgPayloadSize, packet.getLength());
HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize);
}
void updatePktSndPeriod_onTimer(ETransmissionEvent , EventVariant var)
{
if ( var.get<EventVariant::STAGE>() != TEV_CHT_INIT )
updatePktSndPeriod();
}
void updatePktSndPeriod_onAck(ETransmissionEvent , EventVariant )
{
updatePktSndPeriod();
}
void updatePktSndPeriod()
{
// packet = payload + header
const double pktsize = (double) m_zSndAvgPayloadSize + CPacket::SRT_DATA_HDR_SIZE;
m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW);
HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod
<< " (pktsize=" << pktsize << ", bw=" << m_llSndMaxBW);
}
void setMaxBW(int64_t maxbw)
{
m_llSndMaxBW = maxbw > 0 ? maxbw : BW_INFINITE;
updatePktSndPeriod();
#ifdef SRT_ENABLE_NOCWND
/*
* UDT default flow control should not trigger under normal SRT operation
* UDT stops sending if the number of packets in transit (not acknowledged)
* is larger than the congestion window.
* Up to SRT 1.0.6, this value was set at 1000 pkts, which may be insufficient
* for satellite links with ~1000 msec RTT and high bit rate.
*/
// XXX Consider making this a socket option.
m_dCWndSize = m_dMaxCWndSize;
#else
m_dCWndSize = 1000;
#endif
}
void updateBandwidth(int64_t maxbw, int64_t bw) ATR_OVERRIDE
{
// bw is the bandwidth calculated with regard to the
// SRTO_INPUTBW and SRTO_OHEADBW parameters. The maxbw
// value simply represents the SRTO_MAXBW setting.
if (maxbw)
{
setMaxBW(maxbw);
return;
}
if (bw == 0)
{
return;
}
setMaxBW(bw);
}
SrtCongestion::RexmitMethod rexmitMethod() ATR_OVERRIDE
{
return SrtCongestion::SRM_FASTREXMIT;
}
uint64_t updateNAKInterval(uint64_t nakint_tk, int /*rcv_speed*/, size_t /*loss_length*/) ATR_OVERRIDE
{
/*
* duB:
* The RTT accounts for the time for the last NAK to reach sender and start resending lost pkts.
* The rcv_speed add the time to resend all the pkts in the loss list.
*
* For realtime Transport Stream content, pkts/sec is not a good indication of time to transmit
* since packets are not filled to m_iMSS and packet size average is lower than (7*188)
* for low bit rates.
* If NAK report is lost, another cycle (RTT) is requred which is bad for low latency so we
* accelerate the NAK Reports frequency, at the cost of possible duplicate resend.
* Finally, the UDT4 native minimum NAK interval (m_ullMinNakInt_tk) is 300 ms which is too high
* (~10 i30 video frames) to maintain low latency.
*/
// Note: this value will still be reshaped to defined minimum,
// as per minNAKInterval.
return nakint_tk / m_iNakReportAccel;
}
uint64_t minNAKInterval() ATR_OVERRIDE
{
return m_iMinNakInterval_us * CTimer::getCPUFrequency();
}
};
class FileCC : public SrtCongestionControlBase
{
typedef FileCC Me; // Required by SSLOT macro
// Fields from CUDTCC
int m_iRCInterval; // UDT Rate control interval
uint64_t m_LastRCTime; // last rate increase time
bool m_bSlowStart; // if in slow start phase
int32_t m_iLastAck; // last ACKed seq no
bool m_bLoss; // if loss happened since last rate increase
int32_t m_iLastDecSeq; // max pkt seq no sent out when last decrease happened
double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened
int m_iNAKCount; // NAK counter
int m_iDecRandom; // random threshold on decrease by number of loss events
int m_iAvgNAKNum; // average number of NAKs per congestion
int m_iDecCount; // number of decreases in a congestion epoch
int64_t m_maxSR;
public:
FileCC(CUDT* parent)
: SrtCongestionControlBase(parent)
, m_iRCInterval(CUDT::COMM_SYN_INTERVAL_US)
, m_LastRCTime(CTimer::getTime())
, m_bSlowStart(true)
, m_iLastAck(parent->sndSeqNo())
, m_bLoss(false)
, m_iLastDecSeq(CSeqNo::decseq(m_iLastAck))
, m_dLastDecPeriod(1)
, m_iNAKCount(0)
, m_iDecRandom(1)
, m_iAvgNAKNum(0)
, m_iDecCount(0)
, m_maxSR(0)
{
// Note that this function is called at the moment of
// calling m_Smoother.configure(this). It is placed more less
// at the same position as the series-of-parameter-setting-then-init
// in the original UDT code. So, old CUDTCC::init() can be moved
// to constructor.
// SmotherBase
m_dCWndSize = 16;
m_dPktSndPeriod = 1;
parent->ConnectSignal(TEV_ACK, SSLOT(updateSndPeriod));
parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(slowdownSndPeriod));
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(speedupToWindowSize));
HLOGC(cclog.Debug, log << "Creating FileCC");
}
bool checkTransArgs(SrtCongestion::TransAPI, SrtCongestion::TransDir, const char*, size_t, int, bool) ATR_OVERRIDE
{
// XXX
// The FileCC has currently no restrictions, although it should be
// rather required that the "message" mode or "buffer" mode be used on both sides the same.
// This must be somehow checked separately.
return true;
}
bool needsQuickACK(const CPacket& pkt) ATR_OVERRIDE
{
// For FileCC, treat non-full-buffer situation as an end-of-message situation;
// request ACK to be sent immediately.
if (pkt.getLength() < m_parent->maxPayloadSize())
{
// This is not a regular fixed size packet...
// an irregular sized packet usually indicates the end of a message, so send an ACK immediately
return true;
}
return false;
}
void updateBandwidth(int64_t maxbw, int64_t) ATR_OVERRIDE
{
if (maxbw != 0)
{
m_maxSR = maxbw;
HLOGC(cclog.Debug, log << "FileCC: updated BW: " << m_maxSR);
}
}
private:
// SLOTS
void updateSndPeriod(ETransmissionEvent, EventVariant arg)
{
const int ack = arg.get<EventVariant::ACK>();
const uint64_t currtime = CTimer::getTime();
if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval)
return;
m_LastRCTime = currtime;
if (m_bSlowStart)
{
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
m_iLastAck = ack;
if (m_dCWndSize > m_dMaxCWndSize)
{
m_bSlowStart = false;
if (m_parent->deliveryRate() > 0)
{
m_dPktSndPeriod = 1000000.0 / m_parent->deliveryRate();
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:ENDED) wndsize="
<< m_dCWndSize << "/" << m_dMaxCWndSize
<< " sndperiod=" << m_dPktSndPeriod << "us = 1M/("
<< m_parent->deliveryRate() << " pkts/s)");
}
else
{
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:ENDED) wndsize="
<< m_dCWndSize << "/" << m_dMaxCWndSize
<< " sndperiod=" << m_dPktSndPeriod << "us = wndsize/(RTT+RCIV) RTT="
<< m_parent->RTT() << " RCIV=" << m_iRCInterval);
}
}
else
{
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:KEPT) wndsize="
<< m_dCWndSize << "/" << m_dMaxCWndSize
<< " sndperiod=" << m_dPktSndPeriod << "us");
}
}
else
{
m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->RTT() + m_iRCInterval) + 16;
HLOGC(cclog.Debug, log << "FileCC: UPD (speed mode) wndsize="
<< m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->RTT()
<< " sndperiod=" << m_dPktSndPeriod << "us. deliverRate = "
<< m_parent->deliveryRate() << " pkts/s)");
}
if (!m_bSlowStart)
{
if (m_bLoss)
{
m_bLoss = false;
}
// During Slow Start, no rate increase
else
{
double inc = 0;
const int loss_bw = 2 * (1000000 / m_dLastDecPeriod); // 2 times last loss point
const int bw_pktps = min(loss_bw, m_parent->bandwidth());
int64_t B = (int64_t)(bw_pktps - 1000000.0 / m_dPktSndPeriod);
if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((bw_pktps / 9) < B))
B = bw_pktps / 9;
if (B <= 0)
inc = 1.0 / m_parent->MSS();
else
{
// inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS)
// Beta = 1.5 * 10^(-6)
inc = pow(10.0, ceil(log10(B * m_parent->MSS() * 8.0))) * 0.0000015 / m_parent->MSS();
inc = max(inc, 1.0 / m_parent->MSS());
}
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:OFF) loss_bw=" << loss_bw
<< " bandwidth=" << m_parent->bandwidth() << " inc=" << inc
<< " m_dPktSndPeriod=" << m_dPktSndPeriod
<< "->" << (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval));
m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval);
}
}
#if ENABLE_HEAVY_LOGGING
// Try to do reverse-calculation for m_dPktSndPeriod, as per minSP below
// sndperiod = mega / (maxbw / MSS)
// 1/sndperiod = (maxbw/MSS) / mega
// mega/sndperiod = maxbw/MSS
// maxbw = (MSS*mega)/sndperiod
uint64_t usedbw = (m_parent->MSS() * 1000000.0) / m_dPktSndPeriod;
#if defined(unix) && defined (SRT_ENABLE_SYSTEMBUFFER_TRACE)
// Check the outgoing system queue level
int udp_buffer_size = m_parent->sndQueue()->sockoptQuery(SOL_SOCKET, SO_SNDBUF);
int udp_buffer_level = m_parent->sndQueue()->ioctlQuery(TIOCOUTQ);
int udp_buffer_free = udp_buffer_size - udp_buffer_level;
#else
int udp_buffer_free = -1;
#endif
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:"
<< (m_bSlowStart ? "ON" : "OFF") << ") wndsize=" << m_dCWndSize
<< " sndperiod=" << m_dPktSndPeriod << "us BANDWIDTH USED:" << usedbw << " (limit: " << m_maxSR << ")"
" SYSTEM BUFFER LEFT: " << udp_buffer_free);
#endif
//set maximum transfer rate
if (m_maxSR)
{
double minSP = 1000000.0 / (double(m_maxSR) / m_parent->MSS());
if (m_dPktSndPeriod < minSP)
{
m_dPktSndPeriod = minSP;
HLOGC(cclog.Debug, log << "FileCC: BW limited to " << m_maxSR
<< " - SLOWDOWN sndperiod=" << m_dPktSndPeriod << "us");
}
}
}
// When a lossreport has been received, it might be due to having
// reached the available bandwidth limit. Slowdown to avoid further losses.
void slowdownSndPeriod(ETransmissionEvent, EventVariant arg)
{
const int32_t* losslist = arg.get_ptr();
size_t losslist_size = arg.get_len();
// Sanity check. Should be impossible that TEV_LOSSREPORT event
// is called with a nonempty loss list.
if (losslist_size == 0)
{
LOGC(cclog.Error, log << "IPE: FileCC: empty loss list!");
return;
}
//Slow Start stopped, if it hasn't yet
if (m_bSlowStart)
{
m_bSlowStart = false;
if (m_parent->deliveryRate() > 0)
{
m_dPktSndPeriod = 1000000.0 / m_parent->deliveryRate();
HLOGC(cclog.Debug, log << "FileCC: LOSS, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS mega/rate (rate="
<< m_parent->deliveryRate() << ")");
}
else
{
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
HLOGC(cclog.Debug, log << "FileCC: LOSS, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (RTT="
<< m_parent->RTT() << " RCIV=" << m_iRCInterval << ")");
}
}
m_bLoss = true;
// TODO: const int pktsInFlight = CSeqNo::seqoff(m_iLastAck, m_parent->sndSeqNo());
const int pktsInFlight = m_parent->RTT() / m_dPktSndPeriod;
const int numPktsLost = m_parent->sndLossLength();
const int lost_pcent_x10 = pktsInFlight > 0 ? (numPktsLost * 1000) / pktsInFlight : 0;
HLOGC(cclog.Debug, log << "FileCC: LOSS: "
<< "sent=" << CSeqNo::seqlen(m_iLastAck, m_parent->sndSeqNo()) << ", inFlight=" << pktsInFlight
<< ", lost=" << numPktsLost << " ("
<< lost_pcent_x10 / 10 << "." << lost_pcent_x10 % 10 << "\%)");
if (lost_pcent_x10 < 20) // 2.0%
{
HLOGC(cclog.Debug, log << "FileCC: LOSS: m_dLastDecPeriod=" << m_dLastDecPeriod << "->" << m_dPktSndPeriod);
m_dLastDecPeriod = m_dPktSndPeriod;
return;
}
// In contradiction to UDT, TEV_LOSSREPORT will be reported also when
// the lossreport is being sent again, periodically, as a result of
// NAKREPORT feature. You should make sure that NAKREPORT is off when
// using FileCC, so relying on SRTO_TRANSTYPE rather than
// just SRTO_CONGESTION is recommended.
int32_t lossbegin = SEQNO_VALUE::unwrap(losslist[0]);
if (CSeqNo::seqcmp(lossbegin, m_iLastDecSeq) > 0)
{
m_dLastDecPeriod = m_dPktSndPeriod;
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.03);
const double loss_share_factor = 0.03;
m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * (1 - loss_share_factor) + m_iNAKCount * loss_share_factor);
m_iNAKCount = 1;
m_iDecCount = 1;
m_iLastDecSeq = m_parent->sndSeqNo();
// remove global synchronization using randomization
srand(m_iLastDecSeq);
m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX));
if (m_iDecRandom < 1)
m_iDecRandom = 1;
HLOGC(cclog.Debug, log << "FileCC: LOSS:NEW lseqno=" << lossbegin
<< ", lastsentseqno=" << m_iLastDecSeq
<< ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin)
<< ", rand=" << m_iDecRandom
<< " avg NAK:" << m_iAvgNAKNum
<< ", sndperiod=" << m_dPktSndPeriod << "us");
}
else if ((m_iDecCount++ < 5) && (0 == (++m_iNAKCount % m_iDecRandom)))
{
// 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.03);
m_iLastDecSeq = m_parent->sndSeqNo();
HLOGC(cclog.Debug, log << "FileCC: LOSS:PERIOD lseqno=" << lossbegin
<< ", lastsentseqno=" << m_iLastDecSeq
<< ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin)
<< ", deccnt=" << m_iDecCount
<< ", decrnd=" << m_iDecRandom
<< ", sndperiod=" << m_dPktSndPeriod << "us");
}
else
{
HLOGC(cclog.Debug, log << "FileCC: LOSS:STILL lseqno=" << lossbegin
<< ", lastsentseqno=" << m_iLastDecSeq
<< ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin)
<< ", deccnt=" << m_iDecCount
<< ", decrnd=" << m_iDecRandom
<< ", sndperiod=" << m_dPktSndPeriod << "us");
}
}
void speedupToWindowSize(ETransmissionEvent, EventVariant arg)
{
ECheckTimerStage stg = arg.get<EventVariant::STAGE>();
// TEV_INIT is in the beginning of checkTimers(), used
// only to synchronize back the values (which is done in updateCC
// after emitting the signal).
if (stg == TEV_CHT_INIT)
return;
if (m_bSlowStart)
{
m_bSlowStart = false;
if (m_parent->deliveryRate() > 0)
{
m_dPktSndPeriod = 1000000.0 / m_parent->deliveryRate();
HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS mega/rate (rate="
<< m_parent->deliveryRate() << ")");
}
else
{
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize="
<< setprecision(6) << m_dCWndSize << " RTT=" << m_parent->RTT() << " RCIV=" << m_iRCInterval << ")");
}
}
else
{
// XXX This code is a copy of legacy CUDTCC::onTimeout() body.
// This part was commented out there already.
/*
m_dLastDecPeriod = m_dPktSndPeriod;
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2);
m_iLastDecSeq = m_iLastAck;
*/
}
}
SrtCongestion::RexmitMethod rexmitMethod() ATR_OVERRIDE
{
return SrtCongestion::SRM_LATEREXMIT;
}
};
#undef SSLOT
template <class Target>
struct Creator
{
static SrtCongestionControlBase* Create(CUDT* parent) { return new Target(parent); }
};
SrtCongestion::NamePtr SrtCongestion::congctls[N_CONTROLLERS] =
{
{"live", Creator<LiveCC>::Create },
{"file", Creator<FileCC>::Create }
};
bool SrtCongestion::configure(CUDT* parent)
{
if (selector == N_CONTROLLERS)
return false;
// Found a congctl, so call the creation function
congctl = (*congctls[selector].second)(parent);
// The congctl should have pinned in all events
// that are of its interest. It's stated that
// it's ready after creation.
return !!congctl;
}
SrtCongestion::~SrtCongestion()
{
delete congctl;
congctl = 0;
}

View file

@ -0,0 +1,206 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#ifndef INC__CONGCTL_H
#define INC__CONGCTL_H
#include <map>
#include <string>
#include <utility>
class CUDT;
class SrtCongestionControlBase;
typedef SrtCongestionControlBase* srtcc_create_t(CUDT* parent);
class SrtCongestion
{
// Temporarily changed to linear searching, until this is exposed
// for a user-defined controller.
// Note that this is a pointer to function :)
static const size_t N_CONTROLLERS = 2;
// The first/second is to mimic the map.
typedef struct { const char* first; srtcc_create_t* second; } NamePtr;
static NamePtr congctls[N_CONTROLLERS];
// This is a congctl container.
SrtCongestionControlBase* congctl;
size_t selector;
void Check();
public:
// If you predict to allow something to be done on controller also
// before it is configured, call this first. If you need it configured,
// you can rely on Check().
bool ready() { return congctl; }
SrtCongestionControlBase* operator->() { Check(); return congctl; }
// In the beginning it's uninitialized
SrtCongestion(): congctl(), selector(N_CONTROLLERS) {}
struct IsName
{
std::string n;
IsName(std::string nn): n(nn) {}
bool operator()(NamePtr np) { return n == np.first; }
};
// You can call select() multiple times, until finally
// the 'configure' method is called.
bool select(const std::string& name)
{
NamePtr* end = congctls+N_CONTROLLERS;
NamePtr* try_selector = std::find_if(congctls, end, IsName(name));
if (try_selector == end)
return false;
selector = try_selector - congctls;
return true;
}
std::string selected_name()
{
if (selector == N_CONTROLLERS)
return "";
return congctls[selector].first;
}
// Copy constructor - important when listener-spawning
// Things being done:
// 1. The congctl is individual, so don't copy it. Set NULL.
// 2. The selected name is copied so that it's configured correctly.
SrtCongestion(const SrtCongestion& source): congctl(), selector(source.selector) {}
// This function will be called by the parent CUDT
// in appropriate time. It should select appropriate
// congctl basing on the value in selector, then
// pin oneself in into CUDT for receiving event signals.
bool configure(CUDT* parent);
// Will delete the pinned in congctl object.
// This must be defined in *.cpp file due to virtual
// destruction.
~SrtCongestion();
enum RexmitMethod
{
SRM_LATEREXMIT,
SRM_FASTREXMIT
};
enum TransAPI
{
STA_MESSAGE = 0x1, // sendmsg/recvmsg functions
STA_BUFFER = 0x2, // send/recv functions
STA_FILE = 0x3, // sendfile/recvfile functions
};
enum TransDir
{
STAD_RECV = 0,
STAD_SEND = 1
};
};
class SrtCongestionControlBase
{
protected:
// Here can be some common fields
CUDT* m_parent;
double m_dPktSndPeriod;
double m_dCWndSize;
//int m_iBandwidth; // NOT REQUIRED. Use m_parent->bandwidth() instead.
double m_dMaxCWndSize;
//int m_iMSS; // NOT REQUIRED. Use m_parent->MSS() instead.
//int32_t m_iSndCurrSeqNo; // NOT REQUIRED. Use m_parent->sndSeqNo().
//int m_iRcvRate; // NOT REQUIRED. Use m_parent->deliveryRate() instead.
//int m_RTT; // NOT REQUIRED. Use m_parent->RTT() instead.
//char* m_pcParam; // Used to access m_llMaxBw. Use m_parent->maxBandwidth() instead.
// Constructor in protected section so that this class is semi-abstract.
SrtCongestionControlBase(CUDT* parent);
public:
// This could be also made abstract, but this causes a linkage
// problem in C++: this would constitute the first virtual method,
// and C++ compiler uses the location of the first virtual method as the
// file to which it also emits the virtual call table. When this is
// abstract, there would have to be simultaneously either defined
// an empty method in congctl.cpp file (obviously never called),
// or simply left empty body here.
virtual ~SrtCongestionControlBase() { }
// All these functions that return values interesting for processing
// by CUDT can be overridden. Normally they should refer to the fields
// and these fields should keep the values as a state.
virtual double pktSndPeriod_us() { return m_dPktSndPeriod; }
virtual double cgWindowSize() { return m_dCWndSize; }
virtual double cgWindowMaxSize() { return m_dMaxCWndSize; }
virtual int64_t sndBandwidth() { return 0; }
// If user-defined, will return nonzero value.
// If not, it will be internally calculated.
virtual int RTO() { return 0; }
// Maximum number of packets to trigger ACK sending.
// Specifies the number of packets to receive before sending the ACK.
// Used by CUDT together with ACKTimeout_us() to trigger ACK packet sending.
virtual int ACKMaxPackets() const { return 0; }
// Periodical interval to send an ACK, in microseconds.
// If user-defined, this value will be used to calculate
// the next ACK time every time ACK is considered to be sent (see CUDT::checkTimers).
// Otherwise this will be calculated internally in CUDT, normally taken
// from CUDT::COMM_SYN_INTERVAL_US.
virtual int ACKTimeout_us() const { return 0; }
// Called when the settings concerning m_llMaxBW were changed.
// Arg 1: value of CUDT::m_llMaxBW
// Arg 2: value calculated out of CUDT::m_llInputBW and CUDT::m_iOverheadBW.
virtual void updateBandwidth(int64_t, int64_t) {}
virtual bool needsQuickACK(const CPacket&)
{
return false;
}
// Particular controller is allowed to agree or disagree on the use of particular API.
virtual bool checkTransArgs(SrtCongestion::TransAPI , SrtCongestion::TransDir , const char* /*buffer*/, size_t /*size*/, int /*ttl*/, bool /*inorder*/)
{
return true;
}
virtual SrtCongestion::RexmitMethod rexmitMethod() = 0; // Implementation enforced.
virtual uint64_t updateNAKInterval(uint64_t nakint_tk, int rcv_speed, size_t loss_length)
{
if (rcv_speed > 0)
nakint_tk += (loss_length * uint64_t(1000000) / rcv_speed) * CTimer::getCPUFrequency();
return nakint_tk;
}
virtual uint64_t minNAKInterval()
{
return 0; // Leave default
}
};
#endif

9705
trunk/3rdparty/srt-1-fit/srtcore/core.cpp vendored Normal file

File diff suppressed because it is too large Load diff

873
trunk/3rdparty/srt-1-fit/srtcore/core.h vendored Normal file
View file

@ -0,0 +1,873 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 02/28/2012
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_CORE_H__
#define __UDT_CORE_H__
#include <deque>
#include <sstream>
#include "srt.h"
#include "common.h"
#include "list.h"
#include "buffer.h"
#include "window.h"
#include "packet.h"
#include "channel.h"
#include "api.h"
#include "cache.h"
#include "queue.h"
#include "handshake.h"
#include "congctl.h"
#include "packetfilter.h"
#include "utilities.h"
#include <haicrypt.h>
namespace srt_logging
{
extern Logger
glog,
// blog,
mglog,
dlog,
tslog,
rxlog,
cclog;
}
// XXX Utility function - to be moved to utilities.h?
template <class T>
inline T CountIIR(T base, T newval, double factor)
{
if ( base == 0.0 )
return newval;
T diff = newval - base;
return base+T(diff*factor);
}
// XXX Probably a better rework for that can be done - this can be
// turned into a serializable structure, just like it's for CHandShake.
enum AckDataItem
{
ACKD_RCVLASTACK = 0,
ACKD_RTT = 1,
ACKD_RTTVAR = 2,
ACKD_BUFFERLEFT = 3,
ACKD_TOTAL_SIZE_SMALL = 4,
// Extra fields existing in UDT (not always sent)
ACKD_RCVSPEED = 4, // length would be 16
ACKD_BANDWIDTH = 5,
ACKD_TOTAL_SIZE_UDTBASE = 6, // length = 24
// Extra stats for SRT
ACKD_RCVRATE = 6,
ACKD_TOTAL_SIZE_VER101 = 7, // length = 28
ACKD_XMRATE = 7, // XXX This is a weird compat stuff. Version 1.1.3 defines it as ACKD_BANDWIDTH*m_iMaxSRTPayloadSize when set. Never got.
// XXX NOTE: field number 7 may be used for something in future, need to confirm destruction of all !compat 1.0.2 version
ACKD_TOTAL_SIZE_VER102 = 8, // 32
// FEATURE BLOCKED. Probably not to be restored.
// ACKD_ACKBITMAP = 8,
ACKD_TOTAL_SIZE = ACKD_TOTAL_SIZE_VER102 // length = 32 (or more)
};
const size_t ACKD_FIELD_SIZE = sizeof(int32_t);
// For HSv4 legacy handshake
#define SRT_MAX_HSRETRY 10 /* Maximum SRT handshake retry */
enum SeqPairItems
{
SEQ_BEGIN = 0, SEQ_END = 1, SEQ_SIZE = 2
};
// Extended SRT Congestion control class - only an incomplete definition required
class CCryptoControl;
// XXX REFACTOR: The 'CUDT' class is to be merged with 'CUDTSocket'.
// There's no reason for separating them, there's no case of having them
// anyhow managed separately. After this is done, with a small help with
// separating the internal abnormal path management (exceptions) from the
// API (return values), through CUDTUnited, this class may become in future
// an officially exposed C++ API.
class CUDT
{
friend class CUDTSocket;
friend class CUDTUnited;
friend class CCC;
friend struct CUDTComp;
friend class CCache<CInfoBlock>;
friend class CRendezvousQueue;
friend class CSndQueue;
friend class CRcvQueue;
friend class CSndUList;
friend class CRcvUList;
friend class PacketFilter;
private: // constructor and desctructor
void construct();
void clearData();
CUDT();
CUDT(const CUDT& ancestor);
const CUDT& operator=(const CUDT&) {return *this;}
~CUDT();
public: //API
static int startup();
static int cleanup();
static SRTSOCKET socket(int af, int type = SOCK_STREAM, int protocol = 0);
static int bind(SRTSOCKET u, const sockaddr* name, int namelen);
static int bind(SRTSOCKET u, UDPSOCKET udpsock);
static int listen(SRTSOCKET u, int backlog);
static SRTSOCKET accept(SRTSOCKET u, sockaddr* addr, int* addrlen);
static int connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn);
static int close(SRTSOCKET u);
static int getpeername(SRTSOCKET u, sockaddr* name, int* namelen);
static int getsockname(SRTSOCKET u, sockaddr* name, int* namelen);
static int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen);
static int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen);
static int send(SRTSOCKET u, const char* buf, int len, int flags);
static int recv(SRTSOCKET u, char* buf, int len, int flags);
static int sendmsg(SRTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false, uint64_t srctime = 0);
static int recvmsg(SRTSOCKET u, char* buf, int len, uint64_t& srctime);
static int sendmsg2(SRTSOCKET u, const char* buf, int len, ref_t<SRT_MSGCTRL> mctrl);
static int recvmsg2(SRTSOCKET u, char* buf, int len, ref_t<SRT_MSGCTRL> mctrl);
static int64_t sendfile(SRTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = SRT_DEFAULT_SENDFILE_BLOCK);
static int64_t recvfile(SRTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = SRT_DEFAULT_RECVFILE_BLOCK);
static int select(int nfds, ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout);
static int selectEx(const std::vector<SRTSOCKET>& fds, std::vector<SRTSOCKET>* readfds, std::vector<SRTSOCKET>* writefds, std::vector<SRTSOCKET>* exceptfds, int64_t msTimeOut);
static int epoll_create();
static int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
static int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
static int epoll_remove_usock(const int eid, const SRTSOCKET u);
static int epoll_remove_ssock(const int eid, const SYSSOCKET s);
static int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
static int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
static int epoll_wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds = NULL, std::set<SYSSOCKET>* wrfds = NULL);
static int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
static int32_t epoll_set(const int eid, int32_t flags);
static int epoll_release(const int eid);
static CUDTException& getlasterror();
static int bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true, bool instantaneous = false);
static SRT_SOCKSTATUS getsockstate(SRTSOCKET u);
static bool setstreamid(SRTSOCKET u, const std::string& sid);
static std::string getstreamid(SRTSOCKET u);
static int getsndbuffer(SRTSOCKET u, size_t* blocks, size_t* bytes);
static SRT_REJECT_REASON rejectReason(SRTSOCKET s);
static int setError(const CUDTException& e)
{
s_UDTUnited.setError(new CUDTException(e));
return SRT_ERROR;
}
public: // internal API
static const SRTSOCKET INVALID_SOCK = -1; // invalid socket descriptor
static const int ERROR = -1; // socket api error returned value
static const int HS_VERSION_UDT4 = 4;
static const int HS_VERSION_SRT1 = 5;
// Parameters
//
// Note: use notation with X*1000*1000* ... instead of million zeros in a row.
// In C++17 there is a possible notation of 5'000'000 for convenience, but that's
// something only for a far future.
static const int COMM_RESPONSE_TIMEOUT_MS = 5*1000; // 5 seconds
static const int COMM_RESPONSE_MAX_EXP = 16;
static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000;
static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000;
static const int32_t COMM_SYN_INTERVAL_US = 10*1000;
int handshakeVersion()
{
return m_ConnRes.m_iVersion;
}
std::string CONID() const
{
#if ENABLE_LOGGING
std::ostringstream os;
os << "%" << m_SocketID << ":";
return os.str();
#else
return "";
#endif
}
SRTSOCKET socketID() { return m_SocketID; }
static CUDT* getUDTHandle(SRTSOCKET u);
static std::vector<SRTSOCKET> existingSockets();
void addressAndSend(CPacket& pkt);
void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, int srtlen_in = 0);
bool isTsbPd() { return m_bOPT_TsbPd; }
int RTT() { return m_iRTT; }
int32_t sndSeqNo() { return m_iSndCurrSeqNo; }
int32_t rcvSeqNo() { return m_iRcvCurrSeqNo; }
int flowWindowSize() { return m_iFlowWindowSize; }
int32_t deliveryRate() { return m_iDeliveryRate; }
int bandwidth() { return m_iBandwidth; }
int64_t maxBandwidth() { return m_llMaxBW; }
int MSS() { return m_iMSS; }
size_t maxPayloadSize() { return m_iMaxSRTPayloadSize; }
size_t OPT_PayloadSize() { return m_zOPT_ExpPayloadSize; }
uint64_t minNAKInterval() { return m_ullMinNakInt_tk; }
int32_t ISN() { return m_iISN; }
int sndLossLength() { return m_pSndLossList->getLossLength(); }
// XXX See CUDT::tsbpd() to see how to implement it. This should
// do the same as TLPKTDROP feature when skipping packets that are agreed
// to be lost. Note that this is predicted to be called with TSBPD off.
// This is to be exposed for the application so that it can require this
// sequence to be skipped, if that packet has been otherwise arrived through
// a different channel.
void skipIncoming(int32_t seq);
void ConnectSignal(ETransmissionEvent tev, EventSlot sl);
void DisconnectSignal(ETransmissionEvent tev);
private:
/// initialize a UDT entity and bind to a local address.
void open();
/// Start listening to any connection request.
void setListenState();
/// Connect to a UDT entity listening at address "peer".
/// @param peer [in] The address of the listening UDT entity.
void startConnect(const sockaddr* peer, int32_t forced_isn);
/// Process the response handshake packet. Failure reasons can be:
/// * Socket is not in connecting state
/// * Response @a pkt is not a handshake control message
/// * Rendezvous socket has once processed a regular handshake
/// @param pkt [in] handshake packet.
/// @retval 0 Connection successful
/// @retval 1 Connection in progress (m_ConnReq turned into RESPONSE)
/// @retval -1 Connection failed
SRT_ATR_NODISCARD EConnectStatus processConnectResponse(const CPacket& pkt, CUDTException* eout, bool synchro) ATR_NOEXCEPT;
// This function works in case of HSv5 rendezvous. It changes the state
// according to the present state and received message type, as well as the
// INITIATOR/RESPONDER side resolved through cookieContest().
// The resulting data are:
// - rsptype: handshake message type that should be sent back to the peer (nothing if URQ_DONE)
// - needs_extension: the HSREQ/KMREQ or HSRSP/KMRSP extensions should be attached to the handshake message.
// - RETURNED VALUE: if true, it means a URQ_CONCLUSION message was received with HSRSP/KMRSP extensions and needs HSRSP/KMRSP.
void rendezvousSwitchState(ref_t<UDTRequestType> rsptype, ref_t<bool> needs_extension, ref_t<bool> needs_hsrsp);
void cookieContest();
/// Interpret the incoming handshake packet in order to perform appropriate
/// rendezvous FSM state transition if needed, and craft the response, serialized
/// into the packet to be next sent.
/// @param reqpkt Packet to be written with handshake data
/// @param response incoming handshake response packet to be interpreted
/// @param serv_addr incoming packet's address
/// @param synchro True when this function was called in blocking mode
/// @param rst Current read status to know if the HS packet was freshly received from the peer, or this is only a periodic update (RST_AGAIN)
SRT_ATR_NODISCARD EConnectStatus processRendezvous(ref_t<CPacket> reqpkt, const CPacket &response, const sockaddr* serv_addr, bool synchro, EReadStatus);
SRT_ATR_NODISCARD bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout);
SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket& response, bool rendezvous, CUDTException* eout, bool synchro);
void applyResponseSettings();
SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT;
SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr* serv_addr);
void checkUpdateCryptoKeyLen(const char* loghdr, int32_t typefield);
SRT_ATR_NODISCARD size_t fillSrtHandshake_HSREQ(uint32_t* srtdata, size_t srtlen, int hs_version);
SRT_ATR_NODISCARD size_t fillSrtHandshake_HSRSP(uint32_t* srtdata, size_t srtlen, int hs_version);
SRT_ATR_NODISCARD size_t fillSrtHandshake(uint32_t* srtdata, size_t srtlen, int msgtype, int hs_version);
SRT_ATR_NODISCARD bool createSrtHandshake(ref_t<CPacket> reqpkt, ref_t<CHandShake> hs,
int srths_cmd, int srtkm_cmd, const uint32_t* data, size_t datalen);
SRT_ATR_NODISCARD size_t prepareSrtHsMsg(int cmd, uint32_t* srtdata, size_t size);
SRT_ATR_NODISCARD bool processSrtMsg(const CPacket *ctrlpkt);
SRT_ATR_NODISCARD int processSrtMsg_HSREQ(const uint32_t* srtdata, size_t len, uint32_t ts, int hsv);
SRT_ATR_NODISCARD int processSrtMsg_HSRSP(const uint32_t* srtdata, size_t len, uint32_t ts, int hsv);
SRT_ATR_NODISCARD bool interpretSrtHandshake(const CHandShake& hs, const CPacket& hspkt, uint32_t* out_data, size_t* out_len);
SRT_ATR_NODISCARD bool checkApplyFilterConfig(const std::string& cs);
void updateAfterSrtHandshake(int srt_cmd, int hsv);
void updateSrtRcvSettings();
void updateSrtSndSettings();
void checkNeedDrop(ref_t<bool> bCongestion);
/// Connect to a UDT entity listening at address "peer", which has sent "hs" request.
/// @param peer [in] The address of the listening UDT entity.
/// @param hs [in/out] The handshake information sent by the peer side (in), negotiated value (out).
void acceptAndRespond(const sockaddr* peer, CHandShake* hs, const CPacket& hspkt);
bool runAcceptHook(CUDT* acore, const sockaddr* peer, const CHandShake* hs, const CPacket& hspkt);
/// Close the opened UDT entity.
bool close();
/// Request UDT to send out a data block "data" with size of "len".
/// @param data [in] The address of the application data to be sent.
/// @param len [in] The size of the data block.
/// @return Actual size of data sent.
SRT_ATR_NODISCARD int send(const char* data, int len)
{
return sendmsg(data, len, -1, false, 0);
}
/// Request UDT to receive data to a memory block "data" with size of "len".
/// @param data [out] data received.
/// @param len [in] The desired size of data to be received.
/// @return Actual size of data received.
SRT_ATR_NODISCARD int recv(char* data, int len);
/// send a message of a memory block "data" with size of "len".
/// @param data [out] data received.
/// @param len [in] The desired size of data to be received.
/// @param ttl [in] the time-to-live of the message.
/// @param inorder [in] if the message should be delivered in order.
/// @param srctime [in] Time when the data were ready to send.
/// @return Actual size of data sent.
SRT_ATR_NODISCARD int sendmsg(const char* data, int len, int ttl, bool inorder, uint64_t srctime);
/// Receive a message to buffer "data".
/// @param data [out] data received.
/// @param len [in] size of the buffer.
/// @return Actual size of data received.
SRT_ATR_NODISCARD int sendmsg2(const char* data, int len, ref_t<SRT_MSGCTRL> m);
SRT_ATR_NODISCARD int recvmsg(char* data, int len, uint64_t& srctime);
SRT_ATR_NODISCARD int recvmsg2(char* data, int len, ref_t<SRT_MSGCTRL> m);
SRT_ATR_NODISCARD int receiveMessage(char* data, int len, ref_t<SRT_MSGCTRL> m);
SRT_ATR_NODISCARD int receiveBuffer(char* data, int len);
/// Request UDT to send out a file described as "fd", starting from "offset", with size of "size".
/// @param ifs [in] The input file stream.
/// @param offset [in, out] From where to read and send data; output is the new offset when the call returns.
/// @param size [in] How many data to be sent.
/// @param block [in] size of block per read from disk
/// @return Actual size of data sent.
SRT_ATR_NODISCARD int64_t sendfile(std::fstream& ifs, int64_t& offset, int64_t size, int block = 366000);
/// Request UDT to receive data into a file described as "fd", starting from "offset", with expected size of "size".
/// @param ofs [out] The output file stream.
/// @param offset [in, out] From where to write data; output is the new offset when the call returns.
/// @param size [in] How many data to be received.
/// @param block [in] size of block per write to disk
/// @return Actual size of data received.
SRT_ATR_NODISCARD int64_t recvfile(std::fstream& ofs, int64_t& offset, int64_t size, int block = 7320000);
/// Configure UDT options.
/// @param optName [in] The enum name of a UDT option.
/// @param optval [in] The value to be set.
/// @param optlen [in] size of "optval".
void setOpt(SRT_SOCKOPT optName, const void* optval, int optlen);
/// Read UDT options.
/// @param optName [in] The enum name of a UDT option.
/// @param optval [in] The value to be returned.
/// @param optlen [out] size of "optval".
void getOpt(SRT_SOCKOPT optName, void* optval, int& optlen);
/// read the performance data with bytes counters since bstats()
///
/// @param perf [in, out] pointer to a CPerfMon structure to record the performance data.
/// @param clear [in] flag to decide if the local performance trace should be cleared.
/// @param instantaneous [in] flag to request instantaneous data
/// instead of moving averages.
void bstats(CBytePerfMon* perf, bool clear = true, bool instantaneous = false);
/// Mark sequence contained in the given packet as not lost. This
/// removes the loss record from both current receiver loss list and
/// the receiver fresh loss list.
void unlose(const CPacket& oldpacket);
void dropFromLossLists(int32_t from, int32_t to);
void considerLegacySrtHandshake(uint64_t timebase);
void checkSndTimers(Whether2RegenKm regen = DONT_REGEN_KM);
void handshakeDone()
{
m_iSndHsRetryCnt = 0;
}
int64_t withOverhead(int64_t basebw)
{
return (basebw * (100 + m_iOverheadBW))/100;
}
static double Bps2Mbps(int64_t basebw)
{
return double(basebw) * 8.0/1000000.0;
}
bool stillConnected()
{
// Still connected is when:
// - no "broken" condition appeared (security, protocol error, response timeout)
return !m_bBroken
// - still connected (no one called srt_close())
&& m_bConnected
// - isn't currently closing (srt_close() called, response timeout, shutdown)
&& !m_bClosing;
}
int sndSpaceLeft()
{
return sndBuffersLeft() * m_iMaxSRTPayloadSize;
}
int sndBuffersLeft()
{
return m_iSndBufSize - m_pSndBuffer->getCurrBufSize();
}
// TSBPD thread main function.
static void* tsbpd(void* param);
static CUDTUnited s_UDTUnited; // UDT global management base
private: // Identification
SRTSOCKET m_SocketID; // UDT socket number
// XXX Deprecated field. In any place where it's used, UDT_DGRAM is
// the only allowed value. The functionality of distinguishing the transmission
// method is now in m_CongCtl.
UDTSockType m_iSockType; // Type of the UDT connection (SOCK_STREAM or SOCK_DGRAM)
SRTSOCKET m_PeerID; // peer id, for multiplexer
int m_iMaxSRTPayloadSize; // Maximum/regular payload size, in bytes
size_t m_zOPT_ExpPayloadSize; // Expected average payload size (user option)
// Options
int m_iMSS; // Maximum Segment Size, in bytes
bool m_bSynSending; // Sending syncronization mode
bool m_bSynRecving; // Receiving syncronization mode
int m_iFlightFlagSize; // Maximum number of packets in flight from the peer side
int m_iSndBufSize; // Maximum UDT sender buffer size
int m_iRcvBufSize; // Maximum UDT receiver buffer size
linger m_Linger; // Linger information on close
int m_iUDPSndBufSize; // UDP sending buffer size
int m_iUDPRcvBufSize; // UDP receiving buffer size
int m_iIPversion; // IP version
bool m_bRendezvous; // Rendezvous connection mode
#ifdef SRT_ENABLE_CONNTIMEO
int m_iConnTimeOut; // connect timeout in milliseconds
#endif
int m_iSndTimeOut; // sending timeout in milliseconds
int m_iRcvTimeOut; // receiving timeout in milliseconds
bool m_bReuseAddr; // reuse an exiting port or not, for UDP multiplexer
int64_t m_llMaxBW; // maximum data transfer rate (threshold)
#ifdef SRT_ENABLE_IPOPTS
int m_iIpTTL;
int m_iIpToS;
#endif
// These fields keep the options for encryption
// (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is
// created later and takes values from these.
HaiCrypt_Secret m_CryptoSecret;
int m_iSndCryptoKeyLen;
// XXX Consider removing. The m_bDataSender stays here
// in order to maintain the HS side selection in HSv4.
bool m_bDataSender;
// HSv4 (legacy handshake) support)
uint64_t m_ullSndHsLastTime_us; //Last SRT handshake request time
int m_iSndHsRetryCnt; //SRT handshake retries left
bool m_bMessageAPI;
bool m_bOPT_TsbPd; // Whether AGENT will do TSBPD Rx (whether peer does, is not agent's problem)
int m_iOPT_TsbPdDelay; // Agent's Rx latency
int m_iOPT_PeerTsbPdDelay; // Peer's Rx latency for the traffic made by Agent's Tx.
bool m_bOPT_TLPktDrop; // Whether Agent WILL DO TLPKTDROP on Rx.
int m_iOPT_SndDropDelay; // Extra delay when deciding to snd-drop for TLPKTDROP, -1 to off
bool m_bOPT_StrictEncryption; // Off by default. When on, any connection other than nopw-nopw & pw1-pw1 is rejected.
std::string m_sStreamName;
int m_iOPT_PeerIdleTimeout; // Timeout for hearing anything from the peer.
int m_iTsbPdDelay_ms; // Rx delay to absorb burst in milliseconds
int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst in milliseconds
bool m_bTLPktDrop; // Enable Too-late Packet Drop
int64_t m_llInputBW; // Input stream rate (bytes/sec)
int m_iOverheadBW; // Percent above input stream rate (applies if m_llMaxBW == 0)
bool m_bRcvNakReport; // Enable Receiver Periodic NAK Reports
int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set)
private:
UniquePtr<CCryptoControl> m_pCryptoControl; // congestion control SRT class (small data extension)
CCache<CInfoBlock>* m_pCache; // network information cache
// Congestion control
std::vector<EventSlot> m_Slots[TEV__SIZE];
SrtCongestion m_CongCtl;
// Packet filtering
PacketFilter m_PacketFilter;
std::string m_OPT_PktFilterConfigString;
SRT_ARQLevel m_PktFilterRexmitLevel;
std::string m_sPeerPktFilterConfigString;
// Attached tool function
void EmitSignal(ETransmissionEvent tev, EventVariant var);
// Internal state
volatile bool m_bListening; // If the UDT entit is listening to connection
volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed
volatile bool m_bConnected; // Whether the connection is on or off
volatile bool m_bClosing; // If the UDT entity is closing
volatile bool m_bShutdown; // If the peer side has shutdown the connection
volatile bool m_bBroken; // If the connection has been broken
volatile bool m_bPeerHealth; // If the peer status is normal
volatile SRT_REJECT_REASON m_RejectReason;
bool m_bOpened; // If the UDT entity has been opened
int m_iBrokenCounter; // a counter (number of GC checks) to let the GC tag this socket as disconnected
int m_iEXPCount; // Expiration counter
int m_iBandwidth; // Estimated bandwidth, number of packets per second
int m_iRTT; // RTT, in microseconds
int m_iRTTVar; // RTT variance
int m_iDeliveryRate; // Packet arrival rate at the receiver side
int m_iByteDeliveryRate; // Byte arrival rate at the receiver side
uint64_t m_ullLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer)
CHandShake m_ConnReq; // connection request
CHandShake m_ConnRes; // connection response
CHandShake::RendezvousState m_RdvState; // HSv5 rendezvous state
HandshakeSide m_SrtHsSide; // HSv5 rendezvous handshake side resolved from cookie contest (DRAW if not yet resolved)
int64_t m_llLastReqTime; // last time when a connection request is sent
private: // Sending related data
CSndBuffer* m_pSndBuffer; // Sender buffer
CSndLossList* m_pSndLossList; // Sender loss list
CPktTimeWindow<16, 16> m_SndTimeWindow; // Packet sending time window
volatile uint64_t m_ullInterval_tk; // Inter-packet time, in CPU clock cycles
uint64_t m_ullTimeDiff_tk; // aggregate difference in inter-packet time
volatile int m_iFlowWindowSize; // Flow control window size
volatile double m_dCongestionWindow; // congestion window size
volatile int32_t m_iSndLastFullAck; // Last full ACK received
volatile int32_t m_iSndLastAck; // Last ACK received
volatile int32_t m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list
volatile int32_t m_iSndCurrSeqNo; // The largest sequence number that has been sent
int32_t m_iLastDecSeq; // Sequence number sent last decrease occurs
int32_t m_iSndLastAck2; // Last ACK2 sent back
uint64_t m_ullSndLastAck2Time; // The time when last ACK2 was sent back
int32_t m_iISN; // Initial Sequence Number
bool m_bPeerTsbPd; // Peer accept TimeStamp-Based Rx mode
bool m_bPeerTLPktDrop; // Enable sender late packet dropping
bool m_bPeerNakReport; // Sender's peer (receiver) issues Periodic NAK Reports
bool m_bPeerRexmitFlag; // Receiver supports rexmit flag in payload packets
int32_t m_iReXmitCount; // Re-Transmit Count since last ACK
private: // Receiving related data
CRcvBuffer* m_pRcvBuffer; //< Receiver buffer
CRcvLossList* m_pRcvLossList; //< Receiver loss list
std::deque<CRcvFreshLoss> m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for.
int m_iReorderTolerance; //< Current value of dynamic reorder tolerance
int m_iMaxReorderTolerance; //< Maximum allowed value for dynamic reorder tolerance
int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came <TTL-2 time, resets with every increased reorder tolerance
int m_iConsecOrderedDelivery; //< Increases with every packet coming in order or retransmitted, resets with every out-of-order packet
CACKWindow<1024> m_ACKWindow; //< ACK history window
CPktTimeWindow<16, 64> m_RcvTimeWindow; //< Packet arrival time window
int32_t m_iRcvLastAck; //< Last sent ACK
#ifdef ENABLE_LOGGING
int32_t m_iDebugPrevLastAck;
#endif
int32_t m_iRcvLastSkipAck; // Last dropped sequence ACK
uint64_t m_ullLastAckTime_tk; // Timestamp of last ACK
int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged
int32_t m_iAckSeqNo; // Last ACK sequence number
int32_t m_iRcvCurrSeqNo; // Largest received sequence number
int32_t m_iRcvCurrPhySeqNo; // Same as m_iRcvCurrSeqNo, but physical only (disregarding a filter)
uint64_t m_ullLastWarningTime; // Last time that a warning message is sent
int32_t m_iPeerISN; // Initial Sequence Number of the peer side
uint64_t m_ullRcvPeerStartTime;
uint32_t m_lSrtVersion;
uint32_t m_lMinimumPeerSrtVersion;
uint32_t m_lPeerSrtVersion;
uint32_t m_lPeerSrtFlags;
bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets
pthread_t m_RcvTsbPdThread; // Rcv TsbPD Thread handle
pthread_cond_t m_RcvTsbPdCond;
bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent
CallbackHolder<srt_listen_callback_fn> m_cbAcceptHook;
// FORWARDER
public:
static int installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq)
{
return s_UDTUnited.installAcceptHook(lsn, hook, opaq);
}
private:
void installAcceptHook(srt_listen_callback_fn* hook, void* opaq)
{
m_cbAcceptHook.set(opaq, hook);
}
private: // synchronization: mutexes and conditions
pthread_mutex_t m_ConnectionLock; // used to synchronize connection operation
pthread_cond_t m_SendBlockCond; // used to block "send" call
pthread_mutex_t m_SendBlockLock; // lock associated to m_SendBlockCond
pthread_mutex_t m_RcvBufferLock; // Protects the state of the m_pRcvBuffer
// Protects access to m_iSndCurrSeqNo, m_iSndLastAck
pthread_mutex_t m_RecvAckLock; // Protects the state changes while processing incomming ACK (UDT_EPOLL_OUT)
pthread_cond_t m_RecvDataCond; // used to block "recv" when there is no data
pthread_mutex_t m_RecvDataLock; // lock associated to m_RecvDataCond
pthread_mutex_t m_SendLock; // used to synchronize "send" call
pthread_mutex_t m_RecvLock; // used to synchronize "recv" call
pthread_mutex_t m_RcvLossLock; // Protects the receiver loss list (access: CRcvQueue::worker, CUDT::tsbpd)
pthread_mutex_t m_StatsLock; // used to synchronize access to trace statistics
void initSynch();
void destroySynch();
void releaseSynch();
private: // Common connection Congestion Control setup
SRT_REJECT_REASON setupCC();
void updateCC(ETransmissionEvent, EventVariant arg);
bool createCrypter(HandshakeSide side, bool bidi);
private: // Generation and processing of packets
void sendCtrl(UDTMessageType pkttype, const void* lparam = NULL, void* rparam = NULL, int size = 0);
void processCtrl(CPacket& ctrlpkt);
void sendLossReport(const std::vector< std::pair<int32_t, int32_t> >& losslist);
void processCtrlAck(const CPacket& ctrlpkt, const uint64_t currtime_tk);
///
/// @param ackdata_seqno sequence number of a data packet being acknowledged
void updateSndLossListOnACK(int32_t ackdata_seqno);
/// Pack a packet from a list of lost packets.
///
/// @param packet [in, out] a packet structure to fill
/// @param origintime [in, out] origin timestamp of the packet
///
/// @return payload size on success, <=0 on failure
int packLostData(CPacket& packet, uint64_t& origintime);
int packData(CPacket& packet, uint64_t& ts);
int processData(CUnit* unit);
void processClose();
SRT_REJECT_REASON processConnectRequest(const sockaddr* addr, CPacket& packet);
static void addLossRecord(std::vector<int32_t>& lossrecord, int32_t lo, int32_t hi);
int32_t bake(const sockaddr* addr, int32_t previous_cookie = 0, int correction = 0);
private: // Trace
struct CoreStats
{
uint64_t startTime; // timestamp when the UDT entity is started
int64_t sentTotal; // total number of sent data packets, including retransmissions
int64_t recvTotal; // total number of received packets
int sndLossTotal; // total number of lost packets (sender side)
int rcvLossTotal; // total number of lost packets (receiver side)
int retransTotal; // total number of retransmitted packets
int sentACKTotal; // total number of sent ACK packets
int recvACKTotal; // total number of received ACK packets
int sentNAKTotal; // total number of sent NAK packets
int recvNAKTotal; // total number of received NAK packets
int sndDropTotal;
int rcvDropTotal;
uint64_t bytesSentTotal; // total number of bytes sent, including retransmissions
uint64_t bytesRecvTotal; // total number of received bytes
uint64_t rcvBytesLossTotal; // total number of loss bytes (estimate)
uint64_t bytesRetransTotal; // total number of retransmitted bytes
uint64_t sndBytesDropTotal;
uint64_t rcvBytesDropTotal;
int m_rcvUndecryptTotal;
uint64_t m_rcvBytesUndecryptTotal;
int sndFilterExtraTotal;
int rcvFilterExtraTotal;
int rcvFilterSupplyTotal;
int rcvFilterLossTotal;
int64_t m_sndDurationTotal; // total real time for sending
uint64_t lastSampleTime; // last performance sample time
int64_t traceSent; // number of packets sent in the last trace interval
int64_t traceRecv; // number of packets received in the last trace interval
int traceSndLoss; // number of lost packets in the last trace interval (sender side)
int traceRcvLoss; // number of lost packets in the last trace interval (receiver side)
int traceRetrans; // number of retransmitted packets in the last trace interval
int sentACK; // number of ACKs sent in the last trace interval
int recvACK; // number of ACKs received in the last trace interval
int sentNAK; // number of NAKs sent in the last trace interval
int recvNAK; // number of NAKs received in the last trace interval
int traceSndDrop;
int traceRcvDrop;
int traceRcvRetrans;
int traceReorderDistance;
double traceBelatedTime;
int64_t traceRcvBelated;
uint64_t traceBytesSent; // number of bytes sent in the last trace interval
uint64_t traceBytesRecv; // number of bytes sent in the last trace interval
uint64_t traceRcvBytesLoss; // number of bytes bytes lost in the last trace interval (estimate)
uint64_t traceBytesRetrans; // number of bytes retransmitted in the last trace interval
uint64_t traceSndBytesDrop;
uint64_t traceRcvBytesDrop;
int traceRcvUndecrypt;
uint64_t traceRcvBytesUndecrypt;
int sndFilterExtra;
int rcvFilterExtra;
int rcvFilterSupply;
int rcvFilterLoss;
int64_t sndDuration; // real time for sending
int64_t sndDurationCounter; // timers to record the sending duration
} m_stats;
public:
static const int SELF_CLOCK_INTERVAL = 64; // ACK interval for self-clocking
static const int SEND_LITE_ACK = sizeof(int32_t); // special size for ack containing only ack seq
static const int PACKETPAIR_MASK = 0xF;
static const size_t MAX_SID_LENGTH = 512;
private: // Timers
uint64_t m_ullCPUFrequency; // CPU clock frequency, used for Timer, ticks per microsecond
uint64_t m_ullNextACKTime_tk; // Next ACK time, in CPU clock cycles, same below
uint64_t m_ullNextNAKTime_tk; // Next NAK time
volatile uint64_t m_ullACKInt_tk; // ACK interval
volatile uint64_t m_ullNAKInt_tk; // NAK interval
volatile uint64_t m_ullLastRspTime_tk; // time stamp of last response from the peer
volatile uint64_t m_ullLastRspAckTime_tk; // time stamp of last ACK from the peer, protect with m_RecvAckLock
volatile uint64_t m_ullLastSndTime_tk; // time stamp of last data/ctrl sent (in system ticks)
uint64_t m_ullMinNakInt_tk; // NAK timeout lower bound; too small value can cause unnecessary retransmission
uint64_t m_ullMinExpInt_tk; // timeout lower bound threshold: too small timeout can cause problem
int m_iPktCount; // packet counter for ACK
int m_iLightACKCount; // light ACK counter
uint64_t m_ullTargetTime_tk; // scheduled time of next packet sending
void checkTimers();
void checkACKTimer (uint64_t currtime_tk);
void checkNAKTimer(uint64_t currtime_tk);
bool checkExpTimer (uint64_t currtime_tk); // returns true if the connection is expired
void checkRexmitTimer(uint64_t currtime_tk);
public: // For the use of CCryptoControl
// HaiCrypt configuration
unsigned int m_uKmRefreshRatePkt;
unsigned int m_uKmPreAnnouncePkt;
private: // for UDP multiplexer
CSndQueue* m_pSndQueue; // packet sending queue
CRcvQueue* m_pRcvQueue; // packet receiving queue
sockaddr* m_pPeerAddr; // peer address
uint32_t m_piSelfIP[4]; // local UDP IP address
CSNode* m_pSNode; // node information for UDT list used in snd queue
CRNode* m_pRNode; // node information for UDT list used in rcv queue
public: // For SrtCongestion
const CSndQueue* sndQueue() { return m_pSndQueue; }
const CRcvQueue* rcvQueue() { return m_pRcvQueue; }
private: // for epoll
std::set<int> m_sPollID; // set of epoll ID to trigger
void addEPoll(const int eid);
void removeEPoll(const int eid);
};
#endif

View file

@ -0,0 +1,887 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#include <cstring>
#include <string>
#include <sstream>
#include <iterator>
#include "udt.h"
#include "utilities.h"
#include <haicrypt.h>
#include "crypto.h"
#include "logging.h"
#include "core.h"
using namespace srt_logging;
#define SRT_MAX_KMRETRY 10
//#define SRT_CMD_KMREQ 3 /* HaiCryptTP SRT Keying Material */
//#define SRT_CMD_KMRSP 4 /* HaiCryptTP SRT Keying Material ACK */
#define SRT_CMD_KMREQ_SZ HCRYPT_MSG_KM_MAX_SZ /* */
#if SRT_CMD_KMREQ_SZ > SRT_CMD_MAXSZ
#error SRT_CMD_MAXSZ too small
#endif
/* Key Material Request (Network Order)
See HaiCryptTP SRT (hcrypt_xpt_srt.c)
*/
// 10* HAICRYPT_DEF_KM_PRE_ANNOUNCE
const int SRT_CRYPT_KM_PRE_ANNOUNCE = 0x10000;
#if ENABLE_LOGGING
std::string KmStateStr(SRT_KM_STATE state)
{
switch (state)
{
#define TAKE(val) case SRT_KM_S_##val : return #val
TAKE(UNSECURED);
TAKE(SECURED);
TAKE(SECURING);
TAKE(NOSECRET);
TAKE(BADSECRET);
#undef TAKE
default:
{
char buf[256];
sprintf(buf, "??? (%d)", state);
return buf;
}
}
}
std::string CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srtlen)
{
std::ostringstream os;
os << hdr << ": cmd=" << cmd << "(" << (cmd == SRT_CMD_KMREQ ? "KMREQ":"KMRSP") <<") len="
<< size_t(srtlen*sizeof(int32_t)) << " KmState: SND="
<< KmStateStr(m_SndKmState)
<< " RCV=" << KmStateStr(m_RcvKmState);
return os.str();
}
#endif
void CCryptoControl::updateKmState(int cmd, size_t srtlen SRT_ATR_UNUSED)
{
if (cmd == SRT_CMD_KMREQ)
{
if ( SRT_KM_S_UNSECURED == m_SndKmState)
{
m_SndKmState = SRT_KM_S_SECURING;
}
LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
}
else
{
LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
}
}
void CCryptoControl::createFakeSndContext()
{
if (!m_iSndKmKeyLen)
m_iSndKmKeyLen = 16;
if (!createCryptoCtx(Ref(m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX))
{
HLOGC(mglog.Debug, log << "Error: Can't create fake crypto context for sending - sending will return ERROR!");
m_hSndCrypto = 0;
}
}
int CCryptoControl::processSrtMsg_KMREQ(
const uint32_t* srtdata SRT_ATR_UNUSED,
size_t bytelen SRT_ATR_UNUSED,
uint32_t* srtdata_out, ref_t<size_t> r_srtlen, int hsv SRT_ATR_UNUSED)
{
size_t& srtlen = *r_srtlen;
//Receiver
/* All 32-bit msg fields swapped on reception
* But HaiCrypt expect network order message
* Re-swap to cancel it.
*/
#ifdef SRT_ENABLE_ENCRYPTION
srtlen = bytelen/sizeof(srtdata[SRT_KMR_KMSTATE]);
HtoNLA(srtdata_out, srtdata, srtlen);
unsigned char* kmdata = reinterpret_cast<unsigned char*>(srtdata_out);
std::vector<unsigned char> kmcopy(kmdata, kmdata + bytelen);
// The side that has received KMREQ is always an HSD_RESPONDER, regardless of
// what has called this function. The HSv5 handshake only enforces bidirectional
// connection.
bool bidirectional = hsv > CUDT::HS_VERSION_UDT4;
// Local macro to return rejection appropriately.
// CHANGED. The first version made HSv5 reject the connection.
// This isn't well handled by applications, so the connection is
// still established, but unable to handle any transport.
//#define KMREQ_RESULT_REJECTION() if (bidirectional) { return SRT_CMD_NONE; } else { srtlen = 1; goto HSv4_ErrorReport; }
#define KMREQ_RESULT_REJECTION() { srtlen = 1; goto HSv4_ErrorReport; }
int rc = HAICRYPT_OK; // needed before 'goto' run from KMREQ_RESULT_REJECTION macro
bool SRT_ATR_UNUSED wasb4 = false;
size_t sek_len = 0;
// What we have to do:
// If encryption is on (we know that by having m_KmSecret nonempty), create
// the crypto context (if bidirectional, create for both sending and receiving).
// Both crypto contexts should be set with the same length of the key.
// The problem with interpretinting this should be reported as SRT_CMD_NONE,
// should be appropriately handled by the caller, as it expects that this
// function normally return SRT_CMD_KMRSP.
if ( bytelen <= HCRYPT_MSG_KM_OFS_SALT ) //Sanity on message
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: size of the KM (" << bytelen << ") is too small, must be >" << HCRYPT_MSG_KM_OFS_SALT);
m_RcvKmState = SRT_KM_S_BADSECRET;
KMREQ_RESULT_REJECTION();
}
HLOGC(mglog.Debug, log << "KMREQ: getting SEK and creating receiver crypto");
sek_len = hcryptMsg_KM_GetSekLen(kmdata);
if ( sek_len == 0 )
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Received SEK is empty - REJECTING!");
m_RcvKmState = SRT_KM_S_BADSECRET;
KMREQ_RESULT_REJECTION();
}
// Write the key length
m_iRcvKmKeyLen = sek_len;
// Overwrite the key length anyway - it doesn't make sense to somehow
// keep the original setting because it will only make KMX impossible.
#if ENABLE_HEAVY_LOGGING
if (m_iSndKmKeyLen != m_iRcvKmKeyLen)
{
LOGC(mglog.Debug, log << "processSrtMsg_KMREQ: Agent's PBKEYLEN=" << m_iSndKmKeyLen
<< " overwritten by Peer's PBKEYLEN=" << m_iRcvKmKeyLen);
}
#endif
m_iSndKmKeyLen = m_iRcvKmKeyLen;
// This is checked only now so that the SRTO_PBKEYLEN return always the correct value,
// even if encryption is not possible because Agent didn't set a password, or supplied
// a wrong password.
if (m_KmSecret.len == 0) //We have a shared secret <==> encryption is on
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Agent does not declare encryption - won't decrypt incoming packets!");
m_RcvKmState = SRT_KM_S_NOSECRET;
KMREQ_RESULT_REJECTION();
}
wasb4 = m_hRcvCrypto;
if (!createCryptoCtx(Ref(m_hRcvCrypto), m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX))
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Can't create RCV CRYPTO CTX - must reject...");
m_RcvKmState = SRT_KM_S_NOSECRET;
KMREQ_RESULT_REJECTION();
}
if (!wasb4)
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: created RX ENC with KeyLen=" << m_iRcvKmKeyLen);
}
// We have both sides set with password, so both are pending for security
m_RcvKmState = SRT_KM_S_SECURING;
// m_SndKmState is set to SECURING or UNSECURED in init(),
// or it might have been set to SECURED, NOSECRET or BADSECRET in the previous
// handshake iteration (handshakes may be sent multiple times for the same connection).
rc = HaiCrypt_Rx_Process(m_hRcvCrypto, kmdata, bytelen, NULL, NULL, 0);
switch(rc >= 0 ? HAICRYPT_OK : rc)
{
case HAICRYPT_OK:
m_RcvKmState = SRT_KM_S_SECURED;
HLOGC(mglog.Debug, log << "KMREQ/rcv: (snd) Rx process successful - SECURED.");
//Send back the whole message to confirm
break;
case HAICRYPT_ERROR_WRONG_SECRET: //Unmatched shared secret to decrypt wrapped key
m_RcvKmState = m_SndKmState = SRT_KM_S_BADSECRET;
//Send status KMRSP message to tel error
srtlen = 1;
LOGC(mglog.Error, log << "KMREQ/rcv: (snd) Rx process failure - BADSECRET");
break;
case HAICRYPT_ERROR: //Other errors
default:
m_RcvKmState = m_SndKmState = SRT_KM_S_NOSECRET;
srtlen = 1;
LOGC(mglog.Error, log << "KMREQ/rcv: (snd) Rx process failure (IPE) - NOSECRET");
break;
}
LOGP(mglog.Note, FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen));
// Since now, when CCryptoControl::decrypt() encounters an error, it will print it, ONCE,
// until the next KMREQ is received as a key regeneration.
m_bErrorReported = false;
if (srtlen == 1)
goto HSv4_ErrorReport;
// Configure the sender context also, if it succeeded to configure the
// receiver context and we are using bidirectional mode.
if ( bidirectional )
{
// Note: 'bidirectional' means that we want a bidirectional key update,
// which happens only and exclusively with HSv5 handshake - not when the
// usual key update through UMSG_EXT+SRT_CMD_KMREQ was done (which is used
// in HSv4 versions also to initialize the first key, unlike HSv5).
if (m_RcvKmState == SRT_KM_S_SECURED)
{
if (m_SndKmState == SRT_KM_S_SECURING && !m_hSndCrypto)
{
m_iSndKmKeyLen = m_iRcvKmKeyLen;
if (HaiCrypt_Clone(m_hRcvCrypto, HAICRYPT_CRYPTO_DIR_TX, &m_hSndCrypto) != HAICRYPT_OK)
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Can't create SND CRYPTO CTX - WILL NOT SEND-ENCRYPT correctly!");
if (hasPassphrase())
m_SndKmState = SRT_KM_S_BADSECRET;
else
m_SndKmState = SRT_KM_S_NOSECRET;
}
else
{
m_SndKmState = SRT_KM_S_SECURED;
}
LOGC(mglog.Note, log << FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen)
<< " SndKeyLen=" << m_iSndKmKeyLen
<< " TX CRYPTO CTX CLONED FROM RX"
);
// Write the KM message into the field from which it will be next sent.
memcpy(m_SndKmMsg[0].Msg, kmdata, bytelen);
m_SndKmMsg[0].MsgLen = bytelen;
m_SndKmMsg[0].iPeerRetry = 0; // Don't start sending them upon connection :)
}
else
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: NOT cloning RX to TX crypto: already in "
<< KmStateStr(m_SndKmState) << " state");
}
}
else
{
HLOGP(mglog.Debug, "processSrtMsg_KMREQ: NOT SECURED - not replaying failed security association to TX CRYPTO CTX");
}
}
else
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: NOT REPLAYING the key update to TX CRYPTO CTX.");
}
return SRT_CMD_KMRSP;
HSv4_ErrorReport:
if (bidirectional && hasPassphrase())
{
// If the Forward KMX process has failed, the reverse-KMX process was not done at all.
// This will lead to incorrect object configuration and will fail to properly declare
// the transmission state.
// Create the "fake crypto" with the passphrsae you currently have.
createFakeSndContext();
}
#undef KMREQ_RESULT_REJECTION
#else
// It's ok that this is reported as error because this happens in a scenario,
// when non-encryption-enabled SRT application is contacted by encryption-enabled SRT
// application which tries to make a security association.
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Encryption not enabled at compile time - must reject...");
m_RcvKmState = SRT_KM_S_NOSECRET;
#endif
srtlen = 1;
srtdata_out[SRT_KMR_KMSTATE] = m_RcvKmState;
return SRT_CMD_KMRSP;
}
int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int /* XXX unused? hsv*/)
{
/* All 32-bit msg fields (if present) swapped on reception
* But HaiCrypt expect network order message
* Re-swap to cancel it.
*/
uint32_t srtd[SRTDATA_MAXSIZE];
size_t srtlen = len/sizeof(uint32_t);
HtoNLA(srtd, srtdata, srtlen);
int retstatus = -1;
// Unused?
//bool bidirectional = hsv > CUDT::HS_VERSION_UDT4;
// Since now, when CCryptoControl::decrypt() encounters an error, it will print it, ONCE,
// until the next KMREQ is received as a key regeneration.
m_bErrorReported = false;
if (srtlen == 1) // Error report. Set accordingly.
{
SRT_KM_STATE peerstate = SRT_KM_STATE(srtd[SRT_KMR_KMSTATE]); /* Bad or no passphrase */
m_SndKmMsg[0].iPeerRetry = 0;
m_SndKmMsg[1].iPeerRetry = 0;
switch (peerstate)
{
case SRT_KM_S_BADSECRET:
m_SndKmState = m_RcvKmState = SRT_KM_S_BADSECRET;
retstatus = -1;
break;
// Default embraces two cases:
// NOSECRET: this KMRSP was sent by secured Peer, but Agent supplied no password.
// UNSECURED: this KMRSP was sent by unsecure Peer because Agent sent KMREQ.
case SRT_KM_S_NOSECRET:
// This means that the peer did not set the password, while Agent did.
m_RcvKmState = SRT_KM_S_UNSECURED;
m_SndKmState = SRT_KM_S_NOSECRET;
retstatus = -1;
break;
case SRT_KM_S_UNSECURED:
// This means that KMRSP was sent without KMREQ, to inform the Agent,
// that the Peer, unlike Agent, does use password. Agent can send then,
// but can't decrypt what Peer would send.
m_RcvKmState = SRT_KM_S_NOSECRET;
m_SndKmState = SRT_KM_S_UNSECURED;
retstatus = 0;
break;
default:
LOGC(mglog.Fatal, log << "processSrtMsg_KMRSP: IPE: unknown peer error state: "
<< KmStateStr(peerstate) << " (" << int(peerstate) << ")");
m_RcvKmState = SRT_KM_S_NOSECRET;
m_SndKmState = SRT_KM_S_NOSECRET;
retstatus = -1; //This is IPE
break;
}
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: received failure report. STATE: " << KmStateStr(m_RcvKmState));
}
else
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: received key response len=" << len);
// XXX INSECURE << ": [" << FormatBinaryString((uint8_t*)srtd, len) << "]";
bool key1 = getKmMsg_acceptResponse(0, srtd, len);
bool key2 = true;
if ( !key1 )
key2 = getKmMsg_acceptResponse(1, srtd, len); // <--- NOTE SEQUENCING!
if (key1 || key2)
{
m_SndKmState = m_RcvKmState = SRT_KM_S_SECURED;
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: KM response matches " << (key1 ? "EVEN" : "ODD") << " key");
retstatus = 1;
}
else
{
retstatus = -1;
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: IPE??? KM response key matches no key");
/* XXX INSECURE
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: KM response: [" << FormatBinaryString((uint8_t*)srtd, len)
<< "] matches no key 0=[" << FormatBinaryString((uint8_t*)m_SndKmMsg[0].Msg, m_SndKmMsg[0].MsgLen)
<< "] 1=[" << FormatBinaryString((uint8_t*)m_SndKmMsg[1].Msg, m_SndKmMsg[1].MsgLen) << "]");
*/
m_SndKmState = m_RcvKmState = SRT_KM_S_BADSECRET;
}
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
<< "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry);
}
LOGP(mglog.Note, FormatKmMessage("processSrtMsg_KMRSP", SRT_CMD_KMRSP, len));
return retstatus;
}
void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED)
{
if ( !m_hSndCrypto || m_SndKmState == SRT_KM_S_UNSECURED)
{
HLOGC(mglog.Debug, log << "sendKeysToPeer: NOT sending/regenerating keys: "
<< (m_hSndCrypto ? "CONNECTION UNSECURED" : "NO TX CRYPTO CTX created"));
return;
}
#ifdef SRT_ENABLE_ENCRYPTION
uint64_t now = 0;
/*
* Crypto Key Distribution to peer:
* If...
* - we want encryption; and
* - we have not tried more than CSRTCC_MAXRETRY times (peer may not be SRT); and
* - and did not get answer back from peer; and
* - last sent Keying Material req should have been replied (RTT*1.5 elapsed);
* then (re-)send handshake request.
*/
if ( ((m_SndKmMsg[0].iPeerRetry > 0) || (m_SndKmMsg[1].iPeerRetry > 0))
&& ((m_SndKmLastTime + ((m_parent->RTT() * 3)/2)) <= (now = CTimer::getTime())))
{
for (int ki = 0; ki < 2; ki++)
{
if (m_SndKmMsg[ki].iPeerRetry > 0 && m_SndKmMsg[ki].MsgLen > 0)
{
m_SndKmMsg[ki].iPeerRetry--;
HLOGC(mglog.Debug, log << "sendKeysToPeer: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
<< " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry);
m_SndKmLastTime = now;
m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t));
}
}
}
if (now == 0)
{
HLOGC(mglog.Debug, log << "sendKeysToPeer: NO KEYS RESENT, will " <<
(regen ? "" : "NOT ") << "regenerate.");
}
if (regen)
regenCryptoKm(
true, // send UMSG_EXT + SRT_CMD_KMREQ to the peer, if regenerated the key
false // Do not apply the regenerated key to the to the receiver context
); // regenerate and send
#endif
}
#ifdef SRT_ENABLE_ENCRYPTION
void CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional)
{
if (!m_hSndCrypto)
return;
void *out_p[2];
size_t out_len_p[2];
int nbo = HaiCrypt_Tx_ManageKeys(m_hSndCrypto, out_p, out_len_p, 2);
int sent = 0;
HLOGC(mglog.Debug, log << "regenCryptoKm: regenerating crypto keys nbo=" << nbo <<
" THEN=" << (sendit ? "SEND" : "KEEP") << " DIR=" << (bidirectional ? "BOTH" : "SENDER"));
for (int i = 0; i < nbo && i < 2; i++)
{
/*
* New connection keying material
* or regenerated after crypto_cfg.km_refresh_rate_pkt packets .
* Send to peer
*/
// XXX Need to make it clearer and less hardcoded values
int kix = hcryptMsg_KM_GetKeyIndex((unsigned char *)(out_p[i]));
int ki = kix & 0x1;
if ((out_len_p[i] != m_SndKmMsg[ki].MsgLen)
|| (0 != memcmp(out_p[i], m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen)))
{
uint8_t* oldkey SRT_ATR_UNUSED = m_SndKmMsg[ki].Msg;
HLOGC(mglog.Debug, log << "new key[" << ki << "] index=" << kix
<< " OLD=[" << m_SndKmMsg[ki].MsgLen << "]"
<< FormatBinaryString(m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen)
<< " NEW=[" << out_len_p[i] << "]"
<< FormatBinaryString((const uint8_t*)out_p[i], out_len_p[i]));
/* New Keying material, send to peer */
memcpy(m_SndKmMsg[ki].Msg, out_p[i], out_len_p[i]);
m_SndKmMsg[ki].MsgLen = out_len_p[i];
m_SndKmMsg[ki].iPeerRetry = SRT_MAX_KMRETRY;
if (bidirectional && !sendit)
{
// "Send" this key also to myself, just to be applied to the receiver crypto,
// exactly the same way how this key is interpreted on the peer side into its receiver crypto
int rc = HaiCrypt_Rx_Process(m_hRcvCrypto, m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen, NULL, NULL, 0);
if ( rc < 0 )
{
LOGC(mglog.Fatal, log << "regenCryptoKm: IPE: applying key generated in snd crypto into rcv crypto: failed code=" << rc);
// The party won't be able to decrypt incoming data!
// Not sure if anything has to be reported.
}
}
if (sendit)
{
HLOGC(mglog.Debug, log << "regenCryptoKm: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
<< " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry);
m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t));
sent++;
}
}
else if (out_len_p[i] == 0)
{
HLOGC(mglog.Debug, log << "no key[" << ki << "] index=" << kix << ": not generated");
}
else
{
HLOGC(mglog.Debug, log << "no key[" << ki << "] index=" << kix << ": key unchanged");
}
}
HLOGC(mglog.Debug, log << "regenCryptoKm: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
<< "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry);
if (sent)
m_SndKmLastTime = CTimer::getTime();
}
#endif
CCryptoControl::CCryptoControl(CUDT* parent, SRTSOCKET id):
m_parent(parent), // should be initialized in createCC()
m_SocketID(id),
m_iSndKmKeyLen(0),
m_iRcvKmKeyLen(0),
m_SndKmState(SRT_KM_S_UNSECURED),
m_RcvKmState(SRT_KM_S_UNSECURED),
m_KmRefreshRatePkt(0),
m_KmPreAnnouncePkt(0),
m_bErrorReported(false)
{
m_KmSecret.len = 0;
//send
m_SndKmLastTime = 0;
m_SndKmMsg[0].MsgLen = 0;
m_SndKmMsg[0].iPeerRetry = 0;
m_SndKmMsg[1].MsgLen = 0;
m_SndKmMsg[1].iPeerRetry = 0;
m_hSndCrypto = NULL;
//recv
m_hRcvCrypto = NULL;
}
bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
{
// NOTE: initiator creates m_hSndCrypto. When bidirectional,
// it creates also m_hRcvCrypto with the same key length.
// Acceptor creates nothing - it will create appropriate
// contexts when receiving KMREQ from the initiator.
HLOGC(mglog.Debug, log << "CCryptoControl::init: HS SIDE:"
<< (side == HSD_INITIATOR ? "INITIATOR" : "RESPONDER")
<< " DIRECTION:" << (bidirectional ? "BOTH" : (side == HSD_INITIATOR) ? "SENDER" : "RECEIVER"));
// Set UNSECURED state as default
m_RcvKmState = SRT_KM_S_UNSECURED;
// Set security-pending state, if a password was set.
m_SndKmState = hasPassphrase() ? SRT_KM_S_SECURING : SRT_KM_S_UNSECURED;
m_KmPreAnnouncePkt = m_parent->m_uKmPreAnnouncePkt;
m_KmRefreshRatePkt = m_parent->m_uKmRefreshRatePkt;
if ( side == HSD_INITIATOR )
{
if (hasPassphrase())
{
#ifdef SRT_ENABLE_ENCRYPTION
if (m_iSndKmKeyLen == 0)
{
HLOGC(mglog.Debug, log << "CCryptoControl::init: PBKEYLEN still 0, setting default 16");
m_iSndKmKeyLen = 16;
}
bool ok = createCryptoCtx(Ref(m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX);
HLOGC(mglog.Debug, log << "CCryptoControl::init: creating SND crypto context: " << ok);
if (ok && bidirectional)
{
m_iRcvKmKeyLen = m_iSndKmKeyLen;
int st = HaiCrypt_Clone(m_hSndCrypto, HAICRYPT_CRYPTO_DIR_RX, &m_hRcvCrypto);
HLOGC(mglog.Debug, log << "CCryptoControl::init: creating CLONED RCV crypto context: status=" << st);
ok = st == 0;
}
// Note: this is sanity check, it should never happen.
if (!ok)
{
m_SndKmState = SRT_KM_S_NOSECRET; // wanted to secure, but error occurred.
if (bidirectional)
m_RcvKmState = SRT_KM_S_NOSECRET;
return false;
}
regenCryptoKm(
false, // Do not send the key (will be attached it to the HSv5 handshake)
bidirectional // replicate the key to the receiver context, if bidirectional
);
#else
LOGC(mglog.Error, log << "CCryptoControl::init: encryption not supported");
return true;
#endif
}
else
{
HLOGC(mglog.Debug, log << "CCryptoControl::init: CAN'T CREATE crypto: key length for SND = " << m_iSndKmKeyLen);
}
}
else
{
HLOGC(mglog.Debug, log << "CCryptoControl::init: NOT creating crypto contexts - will be created upon reception of KMREQ");
}
return true;
}
void CCryptoControl::close()
{
/* Wipeout secrets */
memset(&m_KmSecret, 0, sizeof(m_KmSecret));
}
std::string CCryptoControl::CONID() const
{
if ( m_SocketID == 0 )
return "";
std::ostringstream os;
os << "%" << m_SocketID << ":";
return os.str();
}
#if ENABLE_HEAVY_LOGGING
static std::string CryptoFlags(int flg)
{
using namespace std;
vector<string> f;
if (flg & HAICRYPT_CFG_F_CRYPTO)
f.push_back("crypto");
if (flg & HAICRYPT_CFG_F_TX)
f.push_back("TX");
if (flg & HAICRYPT_CFG_F_FEC)
f.push_back("fec");
ostringstream os;
copy(f.begin(), f.end(), ostream_iterator<string>(os, "|"));
return os.str();
}
#endif
#ifdef SRT_ENABLE_ENCRYPTION
bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle> hCrypto, size_t keylen, HaiCrypt_CryptoDir cdir)
{
if (*hCrypto)
{
// XXX You can check here if the existing handle represents
// a correctly defined crypto. But this doesn't seem to be
// necessary - the whole CCryptoControl facility seems to be valid only
// within the frames of one connection.
return true;
}
if ((m_KmSecret.len <= 0) || (keylen <= 0))
{
LOGC(mglog.Error, log << CONID() << "cryptoCtx: missing secret (" << m_KmSecret.len << ") or key length (" << keylen << ")");
return false;
}
HaiCrypt_Cfg crypto_cfg;
memset(&crypto_cfg, 0, sizeof(crypto_cfg));
#if 0//test key refresh (fast rate)
m_KmRefreshRatePkt = 2000;
m_KmPreAnnouncePkt = 500;
#endif
crypto_cfg.flags = HAICRYPT_CFG_F_CRYPTO | (cdir == HAICRYPT_CRYPTO_DIR_TX ? HAICRYPT_CFG_F_TX : 0);
crypto_cfg.xport = HAICRYPT_XPT_SRT;
crypto_cfg.cryspr = HaiCryptCryspr_Get_Instance();
crypto_cfg.key_len = (size_t)keylen;
crypto_cfg.data_max_len = HAICRYPT_DEF_DATA_MAX_LENGTH; //MTU
crypto_cfg.km_tx_period_ms = 0;//No HaiCrypt KM inject period, handled in SRT;
crypto_cfg.km_refresh_rate_pkt = m_KmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : m_KmRefreshRatePkt;
crypto_cfg.km_pre_announce_pkt = m_KmPreAnnouncePkt == 0 ? SRT_CRYPT_KM_PRE_ANNOUNCE : m_KmPreAnnouncePkt;
crypto_cfg.secret = m_KmSecret;
//memcpy(&crypto_cfg.secret, &m_KmSecret, sizeof(crypto_cfg.secret));
HLOGC(mglog.Debug, log << "CRYPTO CFG: flags=" << CryptoFlags(crypto_cfg.flags) << " xport=" << crypto_cfg.xport << " cryspr=" << crypto_cfg.cryspr
<< " keylen=" << crypto_cfg.key_len << " passphrase_length=" << crypto_cfg.secret.len);
if (HaiCrypt_Create(&crypto_cfg, &hCrypto.get()) != HAICRYPT_OK)
{
LOGC(mglog.Error, log << CONID() << "cryptoCtx: could not create " << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " crypto ctx");
return false;
}
HLOGC(mglog.Debug, log << CONID() << "cryptoCtx: CREATED crypto for dir=" << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " keylen=" << keylen);
return true;
}
#else
bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle>, size_t, HaiCrypt_CryptoDir)
{
return false;
}
#endif
EncryptionStatus CCryptoControl::encrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
{
#ifdef SRT_ENABLE_ENCRYPTION
// Encryption not enabled - do nothing.
if ( getSndCryptoFlags() == EK_NOENC )
return ENCS_CLEAR;
CPacket& packet = *r_packet;
int rc = HaiCrypt_Tx_Data(m_hSndCrypto, (uint8_t*)packet.getHeader(), (uint8_t*)packet.m_pcData, packet.getLength());
if (rc < 0)
{
return ENCS_FAILED;
}
else if ( rc > 0 )
{
// XXX what happens if the encryption is said to be "succeeded",
// but the length is 0? Shouldn't this be treated as unwanted?
packet.setLength(rc);
}
return ENCS_CLEAR;
#else
return ENCS_NOTSUP;
#endif
}
EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
{
#ifdef SRT_ENABLE_ENCRYPTION
CPacket& packet = *r_packet;
if (packet.getMsgCryptoFlags() == EK_NOENC)
{
HLOGC(mglog.Debug, log << "CPacket::decrypt: packet not encrypted");
return ENCS_CLEAR; // not encrypted, no need do decrypt, no flags to be modified
}
if (m_RcvKmState == SRT_KM_S_UNSECURED)
{
if (m_KmSecret.len != 0)
{
// We were unaware that the peer has set password,
// but now here we are.
m_RcvKmState = SRT_KM_S_SECURING;
LOGC(mglog.Note, log << "SECURITY UPDATE: Peer has surprised Agent with encryption, but KMX is pending - current packet size="
<< packet.getLength() << " dropped");
return ENCS_FAILED;
}
else
{
// Peer has set a password, but Agent did not,
// which means that it will be unable to decrypt
// sent payloads anyway.
m_RcvKmState = SRT_KM_S_NOSECRET;
LOGP(mglog.Error, "SECURITY FAILURE: Agent has no PW, but Peer sender has declared one, can't decrypt");
// This only informs about the state change; it will be also caught by the condition below
}
}
if (m_RcvKmState != SRT_KM_S_SECURED)
{
// If not "secured", it means that it won't be able to decrypt packets,
// so there's no point to even try to send them to HaiCrypt_Rx_Data.
// Actually the current conditions concerning m_hRcvCrypto are such that this object
// is cretaed in case of SRT_KM_S_BADSECRET, so it will simply fail to decrypt,
// but with SRT_KM_S_NOSECRET m_hRcvCrypto is not even created (is NULL), which
// will then cause an error to be reported, misleadingly. Simply don't try to
// decrypt anything as long as you are not sure that the connection is secured.
// This problem will occur every time a packet comes in, it's worth reporting,
// but not with every single packet arriving. Print it once and turn off the flag;
// it will be restored at the next attempt of KMX.
if (!m_bErrorReported)
{
m_bErrorReported = true;
LOGC(mglog.Error, log << "SECURITY STATUS: " << KmStateStr(m_RcvKmState) << " - can't decrypt packet.");
}
HLOGC(mglog.Debug, log << "Packet still not decrypted, status=" << KmStateStr(m_RcvKmState)
<< " - dropping size=" << packet.getLength());
return ENCS_FAILED;
}
int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, (uint8_t *)packet.getHeader(), (uint8_t *)packet.m_pcData, packet.getLength());
if ( rc <= 0 )
{
LOGC(mglog.Error, log << "decrypt ERROR (IPE): HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption");
// -1: decryption failure
// 0: key not received yet
return ENCS_FAILED;
}
// Otherwise: rc == decrypted text length.
packet.setLength(rc); /* In case clr txt size is different from cipher txt */
// Decryption succeeded. Update flags.
packet.setMsgCryptoFlags(EK_NOENC);
HLOGC(mglog.Debug, log << "decrypt: successfully decrypted, resulting length=" << rc);
return ENCS_CLEAR;
#else
return ENCS_NOTSUP;
#endif
}
CCryptoControl::~CCryptoControl()
{
#ifdef SRT_ENABLE_ENCRYPTION
if (m_hSndCrypto)
{
HaiCrypt_Close(m_hSndCrypto);
}
if (m_hRcvCrypto)
{
HaiCrypt_Close(m_hRcvCrypto);
}
#endif
}
std::string SrtFlagString(int32_t flags)
{
#define LEN(arr) (sizeof (arr)/(sizeof ((arr)[0])))
std::string output;
static std::string namera[] = { "TSBPD-snd", "TSBPD-rcv", "haicrypt", "TLPktDrop", "NAKReport", "ReXmitFlag", "StreamAPI" };
size_t i = 0;
for ( ; i < LEN(namera); ++i )
{
if ( (flags & 1) == 1 )
{
output += "+" + namera[i] + " ";
}
else
{
output += "-" + namera[i] + " ";
}
flags >>= 1;
//if ( flags == 0 )
// break;
}
#undef LEN
if ( flags != 0 )
{
output += "+unknown";
}
return output;
}

View file

@ -0,0 +1,268 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__CRYPTO_H
#define INC__CRYPTO_H
#include <cstring>
#include <string>
// UDT
#include "udt.h"
#include "packet.h"
#include "utilities.h"
#include "logging.h"
#include <haicrypt.h>
#include <hcrypt_msg.h>
#if ENABLE_LOGGING
std::string KmStateStr(SRT_KM_STATE state);
namespace srt_logging
{
extern Logger mglog;
}
#endif
// For KMREQ/KMRSP. Only one field is used.
const size_t SRT_KMR_KMSTATE = 0;
#define SRT_CMD_MAXSZ HCRYPT_MSG_KM_MAX_SZ /* Maximum SRT custom messages payload size (bytes) */
const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(int32_t);
enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1};
class CCryptoControl
{
//public:
class CUDT* m_parent;
SRTSOCKET m_SocketID;
size_t m_iSndKmKeyLen; //Key length
size_t m_iRcvKmKeyLen; //Key length from rx KM
// Temporarily allow these to be accessed.
public:
SRT_KM_STATE m_SndKmState; //Sender Km State (imposed by agent)
SRT_KM_STATE m_RcvKmState; //Receiver Km State (informed by peer)
private:
// Partial haicrypt configuration, consider
// putting the whole HaiCrypt_Cfg object here.
int m_KmRefreshRatePkt;
int m_KmPreAnnouncePkt;
HaiCrypt_Secret m_KmSecret; //Key material shared secret
// Sender
uint64_t m_SndKmLastTime;
struct {
unsigned char Msg[HCRYPT_MSG_KM_MAX_SZ];
size_t MsgLen;
int iPeerRetry;
} m_SndKmMsg[2];
HaiCrypt_Handle m_hSndCrypto;
// Receiver
HaiCrypt_Handle m_hRcvCrypto;
bool m_bErrorReported;
public:
bool sendingAllowed()
{
// This function is called to state as to whether the
// crypter allows the packet to be sent over the link.
// This is possible in two cases:
// - when Agent didn't set a password, no matter the crypto state
if (m_KmSecret.len == 0)
return true;
// - when Agent did set a password and the crypto state is SECURED.
if (m_KmSecret.len > 0 && m_SndKmState == SRT_KM_S_SECURED
// && m_iRcvPeerKmState == SRT_KM_S_SECURED ?
)
return true;
return false;
}
bool hasPassphrase() const
{
return m_KmSecret.len > 0;
}
private:
#ifdef SRT_ENABLE_ENCRYPTION
void regenCryptoKm(bool sendit, bool bidirectional);
#endif
public:
size_t KeyLen() { return m_iSndKmKeyLen; }
// Needed for CUDT
void updateKmState(int cmd, size_t srtlen);
// Detailed processing
int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, uint32_t* srtdata_out, ref_t<size_t> r_srtlen, int hsv);
// This returns:
// 1 - the given payload is the same as the currently used key
// 0 - there's no key in agent or the payload is error message with agent NOSECRET.
// -1 - the payload is error message with other state or it doesn't match the key
int processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int hsv);
void createFakeSndContext();
const unsigned char* getKmMsg_data(size_t ki) const { return m_SndKmMsg[ki].Msg; }
size_t getKmMsg_size(size_t ki) const { return m_SndKmMsg[ki].MsgLen; }
/// Check if the key stored at @c ki shall be sent. When during the handshake,
/// it only matters if the KM message for that index is recorded at all.
/// Otherwise returns true only if also the retry counter didn't expire.
///
/// @param ki Key index (0 or 1)
/// @param runtime True, if this happens as a key update
/// during transmission (otherwise it's during the handshake)
/// @return Whether the KM message at given index needs to be sent.
bool getKmMsg_needSend(size_t ki, bool runtime) const
{
if (runtime)
return (m_SndKmMsg[ki].iPeerRetry > 0 && m_SndKmMsg[ki].MsgLen > 0);
else
return m_SndKmMsg[ki].MsgLen > 0;
}
/// Mark the key as already sent. When no 'runtime' (during the handshake)
/// it actually does nothing so that this will be retried as long as the handshake
/// itself is being retried. Otherwise this is during transmission and will expire
/// after several retries.
///
/// @param ki Key index (0 or 1)
/// @param runtime True, if this happens as a key update
/// during transmission (otherwise it's during the handshake)
void getKmMsg_markSent(size_t ki, bool runtime)
{
#if ENABLE_LOGGING
using srt_logging::mglog;
#endif
m_SndKmLastTime = CTimer::getTime();
if (runtime)
{
m_SndKmMsg[ki].iPeerRetry--;
HLOGC(mglog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " retry=" << m_SndKmMsg[ki].iPeerRetry);
}
else
{
HLOGC(mglog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " STILL IN USE.");
}
}
/// Check if the response returned by KMRSP matches the recorded KM message.
/// When it is, set also the retry counter to 0 to prevent further retries.
///
/// @param ki KM message index (0 or 1)
/// @param srtmsg Message received through KMRSP
/// @param bytesize Size of the message
/// @return True if the message is identical to the recorded KM message at given index.
bool getKmMsg_acceptResponse(size_t ki, const uint32_t* srtmsg, size_t bytesize)
{
if ( m_SndKmMsg[ki].MsgLen == bytesize
&& 0 == memcmp(m_SndKmMsg[ki].Msg, srtmsg, m_SndKmMsg[ki].MsgLen))
{
m_SndKmMsg[ki].iPeerRetry = 0;
return true;
}
return false;
}
CCryptoControl(CUDT* parent, SRTSOCKET id);
// DEBUG PURPOSES:
std::string CONID() const;
std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen);
bool init(HandshakeSide, bool);
void close();
// This function is used in:
// - HSv4 (initial key material exchange - in HSv5 it's attached to handshake)
// - case of key regeneration, which should be then exchanged again
void sendKeysToPeer(Whether2RegenKm regen);
void setCryptoSecret(const HaiCrypt_Secret& secret)
{
m_KmSecret = secret;
//memcpy(&m_KmSecret, &secret, sizeof(m_KmSecret));
}
void setCryptoKeylen(size_t keylen)
{
m_iSndKmKeyLen = keylen;
m_iRcvKmKeyLen = keylen;
}
bool createCryptoCtx(ref_t<HaiCrypt_Handle> rh, size_t keylen, HaiCrypt_CryptoDir tx);
int getSndCryptoFlags() const
{
#ifdef SRT_ENABLE_ENCRYPTION
return(m_hSndCrypto ?
HaiCrypt_Tx_GetKeyFlags(m_hSndCrypto) :
// When encryption isn't on, check if it was required
// If it was, return -1 as flags, which means that
// encryption was requested and not possible.
hasPassphrase() ? -1 :
0);
#else
return 0;
#endif
}
bool isSndEncryptionOK() const
{
// Similar to this above, just quickly check if the encryption
// is required and possible, or not possible
if (!hasPassphrase())
return true; // no encryption required
if (m_hSndCrypto)
return true; // encryption is required and possible
return false;
}
/// Encrypts the packet. If encryption is not turned on, it
/// does nothing. If the encryption is not correctly configured,
/// the encryption will fail.
/// XXX Encryption flags in the PH_MSGNO
/// field in the header must be correctly set before calling.
EncryptionStatus encrypt(ref_t<CPacket> r_packet);
/// Decrypts the packet. If the packet has ENCKEYSPEC part
/// in PH_MSGNO set to EK_NOENC, it does nothing. It decrypts
/// only if the encryption correctly configured, otherwise it
/// fails. After successful decryption, the ENCKEYSPEC part
// in PH_MSGNO is set to EK_NOENC.
EncryptionStatus decrypt(ref_t<CPacket> r_packet);
~CCryptoControl();
};
#endif // SRT_CONGESTION_CONTROL_H

View file

@ -0,0 +1,727 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 01/01/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifdef LINUX
#include <sys/epoll.h>
#include <unistd.h>
#endif
#if __APPLE__
#include "TargetConditionals.h"
#endif
#if defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#if defined(__ANDROID__) || defined(ANDROID)
#include <sys/select.h>
#endif
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <iterator>
#include "common.h"
#include "epoll.h"
#include "logging.h"
#include "udt.h"
using namespace std;
namespace srt_logging
{
extern Logger mglog;
}
using namespace srt_logging;
#if ENABLE_HEAVY_LOGGING
#define IF_DIRNAME(tested, flag, name) (tested & flag ? name : "")
#endif
CEPoll::CEPoll():
m_iIDSeed(0)
{
CGuard::createMutex(m_EPollLock);
}
CEPoll::~CEPoll()
{
CGuard::releaseMutex(m_EPollLock);
}
int CEPoll::create()
{
CGuard pg(m_EPollLock);
if (++ m_iIDSeed >= 0x7FFFFFFF)
m_iIDSeed = 0;
// Check if an item already exists. Should not ever happen.
if (m_mPolls.find(m_iIDSeed) != m_mPolls.end())
throw CUDTException(MJ_SETUP, MN_NONE);
int localid = 0;
#ifdef LINUX
localid = epoll_create(1024);
/* Possible reasons of -1 error:
EMFILE: The per-user limit on the number of epoll instances imposed by /proc/sys/fs/epoll/max_user_instances was encountered.
ENFILE: The system limit on the total number of open files has been reached.
ENOMEM: There was insufficient memory to create the kernel object.
*/
if (localid < 0)
throw CUDTException(MJ_SETUP, MN_NONE, errno);
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
localid = kqueue();
if (localid < 0)
throw CUDTException(MJ_SETUP, MN_NONE, errno);
#else
// on Solaris, use /dev/poll
// on Windows, select
#endif
pair<map<int, CEPollDesc>::iterator, bool> res = m_mPolls.insert(make_pair(m_iIDSeed, CEPollDesc(m_iIDSeed, localid)));
if (!res.second) // Insertion failed (no memory?)
throw CUDTException(MJ_SETUP, MN_NONE);
return m_iIDSeed;
}
int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
{
CGuard pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
#ifdef LINUX
epoll_event ev;
memset(&ev, 0, sizeof(epoll_event));
if (NULL == events)
ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
else
{
ev.events = 0;
if (*events & UDT_EPOLL_IN)
ev.events |= EPOLLIN;
if (*events & UDT_EPOLL_OUT)
ev.events |= EPOLLOUT;
if (*events & UDT_EPOLL_ERR)
ev.events |= EPOLLERR;
}
ev.data.fd = s;
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_ADD, s, &ev) < 0)
throw CUDTException();
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
struct kevent ke[2];
int num = 0;
if (NULL == events)
{
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
}
else
{
if (*events & UDT_EPOLL_IN)
{
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
}
if (*events & UDT_EPOLL_OUT)
{
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
}
}
if (kevent(p->second.m_iLocalID, ke, num, NULL, 0, NULL) < 0)
throw CUDTException();
#else
#ifdef _MSC_VER
// Microsoft Visual Studio doesn't support the #warning directive - nonstandard anyway.
// Use #pragma message with the same text.
// All other compilers should be ok :)
#pragma message("WARNING: Unsupported system for epoll. The epoll_add_ssock() API call won't work on this platform.")
#else
#warning "Unsupported system for epoll. The epoll_add_ssock() API call won't work on this platform."
#endif
#endif
p->second.m_sLocals.insert(s);
return 0;
}
int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
{
CGuard pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
#ifdef LINUX
epoll_event ev; // ev is ignored, for compatibility with old Linux kernel only.
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_DEL, s, &ev) < 0)
throw CUDTException();
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
struct kevent ke;
//
// Since I don't know what was set before
// Just clear out both read and write
//
EV_SET(&ke, s, EVFILT_READ, EV_DELETE, 0, 0, NULL);
kevent(p->second.m_iLocalID, &ke, 1, NULL, 0, NULL);
EV_SET(&ke, s, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
kevent(p->second.m_iLocalID, &ke, 1, NULL, 0, NULL);
#endif
p->second.m_sLocals.erase(s);
return 0;
}
// Need this to atomically modify polled events (ex: remove write/keep read)
int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
{
CGuard pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
CEPollDesc& d = p->second;
int32_t evts = events ? *events : uint32_t(SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR);
bool edgeTriggered = evts & SRT_EPOLL_ET;
evts &= ~SRT_EPOLL_ET;
if (evts)
{
pair<CEPollDesc::ewatch_t::iterator, bool> iter_new = d.addWatch(u, evts, edgeTriggered);
CEPollDesc::Wait& wait = iter_new.first->second;
if (!iter_new.second)
{
// The object exists. We only are certain about the `u`
// parameter, but others are probably unchanged. Change them
// forcefully and take out notices that are no longer valid.
const int removable = wait.watch & ~evts;
// Check if there are any events that would be removed.
// If there are no removed events watched (for example, when
// only new events are being added to existing socket),
// there's nothing to remove, but might be something to update.
if (removable)
{
d.removeExcessEvents(wait, evts);
}
// Update the watch configuration, including edge
wait.watch = evts;
if (edgeTriggered)
wait.edge = evts;
// Now it should look exactly like newly added
// and the state is also updated
}
const int newstate = wait.watch & wait.state;
if (newstate)
{
d.addEventNotice(wait, u, newstate);
}
}
else if (edgeTriggered)
{
// Specified only SRT_EPOLL_ET flag, but no event flag. Error.
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
else
{
// Update with no events means to remove subscription
d.removeSubscription(u);
}
return 0;
}
int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
{
CGuard pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
#ifdef LINUX
epoll_event ev;
memset(&ev, 0, sizeof(epoll_event));
if (NULL == events)
ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
else
{
ev.events = 0;
if (*events & UDT_EPOLL_IN)
ev.events |= EPOLLIN;
if (*events & UDT_EPOLL_OUT)
ev.events |= EPOLLOUT;
if (*events & UDT_EPOLL_ERR)
ev.events |= EPOLLERR;
}
ev.data.fd = s;
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_MOD, s, &ev) < 0)
throw CUDTException();
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
struct kevent ke[2];
int num = 0;
//
// Since I don't know what was set before
// Just clear out both read and write
//
EV_SET(&ke[0], s, EVFILT_READ, EV_DELETE, 0, 0, NULL);
kevent(p->second.m_iLocalID, ke, 1, NULL, 0, NULL);
EV_SET(&ke[0], s, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
kevent(p->second.m_iLocalID, ke, 1, NULL, 0, NULL);
if (NULL == events)
{
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
}
else
{
if (*events & UDT_EPOLL_IN)
{
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
}
if (*events & UDT_EPOLL_OUT)
{
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
}
}
if (kevent(p->second.m_iLocalID, ke, num, NULL, 0, NULL) < 0)
throw CUDTException();
#endif
// Assuming add is used if not inserted
// p->second.m_sLocals.insert(s);
return 0;
}
int CEPoll::setflags(const int eid, int32_t flags)
{
CGuard pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
CEPollDesc& ed = p->second;
int32_t oflags = ed.flags();
if (flags == -1)
return oflags;
if (flags == 0)
{
ed.clr_flags(~int32_t());
}
else
{
ed.set_flags(flags);
}
return oflags;
}
int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut)
{
// It is allowed to call this function witn fdsSize == 0
// and therefore also NULL fdsSet. This will then only report
// the number of ready sockets, just without information which.
if (fdsSize < 0 || (fdsSize > 0 && !fdsSet))
throw CUDTException(MJ_NOTSUP, MN_INVAL);
int64_t entertime = CTimer::getTime();
while (true)
{
{
CGuard pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
CEPollDesc& ed = p->second;
if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty())
{
// Empty EID is not allowed, report error.
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK) && (fdsSet == NULL || fdsSize == 0))
{
// Empty EID is not allowed, report error.
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
if (!ed.m_sLocals.empty())
{
// XXX Add error log
// uwait should not be used with EIDs subscribed to system sockets
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
int total = 0; // This is a list, so count it during iteration
CEPollDesc::enotice_t::iterator i = ed.enotice_begin();
while (i != ed.enotice_end())
{
int pos = total; // previous past-the-end position
++total;
if (total > fdsSize)
break;
fdsSet[pos] = *i;
ed.checkEdge(i++); // NOTE: potentially deletes `i`
}
if (total)
return total;
}
if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000)))
break; // official wait does: throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
CTimer::waitForEvent();
}
return 0;
}
int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* lwfds)
{
// if all fields is NULL and waiting time is infinite, then this would be a deadlock
if (!readfds && !writefds && !lrfds && !lwfds && (msTimeOut < 0))
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
// Clear these sets in case the app forget to do it.
if (readfds) readfds->clear();
if (writefds) writefds->clear();
if (lrfds) lrfds->clear();
if (lwfds) lwfds->clear();
int total = 0;
int64_t entertime = CTimer::getTime();
HLOGC(mglog.Debug, log << "CEPoll::wait: START for eid=" << eid);
while (true)
{
{
CGuard epollock(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
{
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
}
CEPollDesc& ed = p->second;
if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty() && ed.m_sLocals.empty())
{
// Empty EID is not allowed, report error.
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK))
{
// Empty report is not allowed, report error.
if (!ed.m_sLocals.empty() && (!lrfds || !lwfds))
throw CUDTException(MJ_NOTSUP, MN_INVAL);
if (!ed.watch_empty() && (!readfds || !writefds))
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
IF_HEAVY_LOGGING(int total_noticed = 0);
IF_HEAVY_LOGGING(ostringstream debug_sockets);
// Sockets with exceptions are returned to both read and write sets.
for (CEPollDesc::enotice_t::iterator it = ed.enotice_begin(), it_next = it; it != ed.enotice_end(); it = it_next)
{
++it_next;
IF_HEAVY_LOGGING(++total_noticed);
if (readfds && ((it->events & UDT_EPOLL_IN) || (it->events & UDT_EPOLL_ERR)))
{
if (readfds->insert(it->fd).second)
++total;
}
if (writefds && ((it->events & UDT_EPOLL_OUT) || (it->events & UDT_EPOLL_ERR)))
{
if (writefds->insert(it->fd).second)
++total;
}
IF_HEAVY_LOGGING(debug_sockets << " " << it->fd << ":"
<< IF_DIRNAME(it->events, SRT_EPOLL_IN, "R")
<< IF_DIRNAME(it->events, SRT_EPOLL_OUT, "W")
<< IF_DIRNAME(it->events, SRT_EPOLL_ERR, "E"));
if (ed.checkEdge(it)) // NOTE: potentially erases 'it'.
{
IF_HEAVY_LOGGING(debug_sockets << "!");
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: REPORTED " << total << "/" << total_noticed
<< debug_sockets.str());
if (lrfds || lwfds)
{
#ifdef LINUX
const int max_events = ed.m_sLocals.size();
epoll_event ev[max_events];
int nfds = ::epoll_wait(ed.m_iLocalID, ev, max_events, 0);
IF_HEAVY_LOGGING(const int prev_total = total);
for (int i = 0; i < nfds; ++ i)
{
if ((NULL != lrfds) && (ev[i].events & EPOLLIN))
{
lrfds->insert(ev[i].data.fd);
++ total;
}
if ((NULL != lwfds) && (ev[i].events & EPOLLOUT))
{
lwfds->insert(ev[i].data.fd);
++ total;
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: LINUX: picking up " << (total - prev_total) << " ready fds.");
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
struct timespec tmout = {0, 0};
const int max_events = ed.m_sLocals.size();
struct kevent ke[max_events];
int nfds = kevent(ed.m_iLocalID, NULL, 0, ke, max_events, &tmout);
IF_HEAVY_LOGGING(const int prev_total = total);
for (int i = 0; i < nfds; ++ i)
{
if ((NULL != lrfds) && (ke[i].filter == EVFILT_READ))
{
lrfds->insert(ke[i].ident);
++ total;
}
if ((NULL != lwfds) && (ke[i].filter == EVFILT_WRITE))
{
lwfds->insert(ke[i].ident);
++ total;
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: Darwin/BSD: picking up " << (total - prev_total) << " ready fds.");
#else
//currently "select" is used for all non-Linux platforms.
//faster approaches can be applied for specific systems in the future.
//"select" has a limitation on the number of sockets
int max_fd = 0;
fd_set rqreadfds;
fd_set rqwritefds;
FD_ZERO(&rqreadfds);
FD_ZERO(&rqwritefds);
for (set<SYSSOCKET>::const_iterator i = ed.m_sLocals.begin(); i != ed.m_sLocals.end(); ++ i)
{
if (lrfds)
FD_SET(*i, &rqreadfds);
if (lwfds)
FD_SET(*i, &rqwritefds);
if ((int)*i > max_fd)
max_fd = *i;
}
IF_HEAVY_LOGGING(const int prev_total = total);
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
if (::select(max_fd + 1, &rqreadfds, &rqwritefds, NULL, &tv) > 0)
{
for (set<SYSSOCKET>::const_iterator i = ed.m_sLocals.begin(); i != ed.m_sLocals.end(); ++ i)
{
if (lrfds && FD_ISSET(*i, &rqreadfds))
{
lrfds->insert(*i);
++ total;
}
if (lwfds && FD_ISSET(*i, &rqwritefds))
{
lwfds->insert(*i);
++ total;
}
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: select(otherSYS): picking up " << (total - prev_total) << " ready fds.");
#endif
}
} // END-LOCK: m_EPollLock
HLOGC(mglog.Debug, log << "CEPoll::wait: Total of " << total << " READY SOCKETS");
if (total > 0)
return total;
if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000)))
{
HLOGP(mglog.Debug, "... not waiting longer - timeout");
throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
}
CTimer::EWait wt ATR_UNUSED = CTimer::waitForEvent();
HLOGC(mglog.Debug, log << "CEPoll::wait: EVENT WAITING: "
<< (wt == CTimer::WT_TIMEOUT ? "CHECKPOINT" : wt == CTimer::WT_EVENT ? "TRIGGERED" : "ERROR"));
}
return 0;
}
int CEPoll::release(const int eid)
{
CGuard pg(m_EPollLock);
map<int, CEPollDesc>::iterator i = m_mPolls.find(eid);
if (i == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
#ifdef LINUX
// release local/system epoll descriptor
::close(i->second.m_iLocalID);
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
::close(i->second.m_iLocalID);
#endif
m_mPolls.erase(i);
return 0;
}
int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int events, const bool enable)
{
vector<int> lost;
CGuard pg(m_EPollLock);
for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i)
{
map<int, CEPollDesc>::iterator p = m_mPolls.find(*i);
if (p == m_mPolls.end())
{
// EID invalid, though still present in the socket's subscriber list
// (dangling in the socket). Postpone to fix the subscruption and continue.
lost.push_back(*i);
continue;
}
CEPollDesc& ed = p->second;
// Check if this EID is subscribed for this socket.
CEPollDesc::Wait* pwait = ed.watch_find(uid);
if (!pwait)
{
// As this is mapped in the socket's data, it should be impossible.
continue;
}
// compute new states
// New state to be set into the permanent state
const int newstate = enable ? pwait->state | events // SET event bits if enable
: pwait->state & (~events); // CLEAR event bits
// compute states changes!
int changes = pwait->state ^ newstate; // oldState XOR newState
if (!changes)
continue; // no changes!
// assign new state
pwait->state = newstate;
// filter change relating what is watching
changes &= pwait->watch;
if (!changes)
continue; // no change watching
// set events changes!
// This function will update the notice object associated with
// the given events, that is:
// - if enable, it will set event flags, possibly in a new notice object
// - if !enable, it will clear event flags, possibly remove notice if resulted in 0
ed.updateEventNotice(*pwait, uid, events, enable);
}
for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i)
eids.erase(*i);
return 0;
}

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

@ -0,0 +1,395 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 08/20/2010
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_EPOLL_H__
#define __UDT_EPOLL_H__
#include <map>
#include <set>
#include <list>
#include "udt.h"
struct CEPollDesc
{
const int m_iID; // epoll ID
struct Wait;
struct Notice: public SRT_EPOLL_EVENT
{
Wait* parent;
Notice(Wait* p, SRTSOCKET sock, int ev): parent(p)
{
fd = sock;
events = ev;
}
};
/// The type for `m_USockEventNotice`, the pair contains:
/// * The back-pointer to the subscriber object for which this event notice serves
/// * The events currently being on
typedef std::list<Notice> enotice_t;
struct Wait
{
/// Events the subscriber is interested with. Only those will be
/// regarded when updating event flags.
int watch;
/// Which events should be edge-triggered. When the event isn't
/// mentioned in `watch`, this bit flag is disregarded. Otherwise
/// it means that the event is to be waited for persistent state
/// if this flag is not present here, and for edge trigger, if
/// the flag is present here.
int edge;
/// The current persistent state. This is usually duplicated in
/// a dedicated state object in `m_USockEventNotice`, however the state
/// here will stay forever as is, regardless of the edge/persistent
/// subscription mode for the event.
int state;
/// The iterator to `m_USockEventNotice` container that contains the
/// event notice object for this subscription, or the value from
/// `nullNotice()` if there is no such object.
enotice_t::iterator notit;
Wait(int sub, bool etr, enotice_t::iterator i)
:watch(sub)
,edge(etr ? sub : 0)
,state(0)
,notit(i)
{
}
int edgeOnly() { return edge & watch; }
};
typedef std::map<SRTSOCKET, Wait> ewatch_t;
private:
/// Sockets that are subscribed for events in this eid.
ewatch_t m_USockWatchState;
/// Objects representing changes in SRT sockets.
/// Objects are removed from here when an event is registerred as edge-triggered.
/// Otherwise it is removed only when all events as per subscription
/// are no longer on.
enotice_t m_USockEventNotice;
// Special behavior
int32_t m_Flags;
enotice_t::iterator nullNotice() { return m_USockEventNotice.end(); }
public:
CEPollDesc(int id, int localID)
: m_iID(id)
, m_Flags(0)
, m_iLocalID(localID)
{
}
static const int32_t EF_NOCHECK_EMPTY = 1 << 0;
static const int32_t EF_CHECK_REP = 1 << 1;
int32_t flags() { return m_Flags; }
bool flags(int32_t f) { return (m_Flags & f) != 0; }
void set_flags(int32_t flg) { m_Flags |= flg; }
void clr_flags(int32_t flg) { m_Flags &= ~flg; }
// Container accessors for ewatch_t.
bool watch_empty() { return m_USockWatchState.empty(); }
Wait* watch_find(SRTSOCKET sock)
{
ewatch_t::iterator i = m_USockWatchState.find(sock);
if (i == m_USockWatchState.end())
return NULL;
return &i->second;
}
// Container accessors for enotice_t.
enotice_t::iterator enotice_begin() { return m_USockEventNotice.begin(); }
enotice_t::iterator enotice_end() { return m_USockEventNotice.end(); }
const int m_iLocalID; // local system epoll ID
std::set<SYSSOCKET> m_sLocals; // set of local (non-UDT) descriptors
std::pair<ewatch_t::iterator, bool> addWatch(SRTSOCKET sock, int32_t events, bool edgeTrg)
{
return m_USockWatchState.insert(std::make_pair(sock, Wait(events, edgeTrg, nullNotice())));
}
void addEventNotice(Wait& wait, SRTSOCKET sock, int events)
{
// `events` contains bits to be set, so:
//
// 1. If no notice object exists, add it exactly with `events`.
// 2. If it exists, only set the bits from `events`.
// ASSUME: 'events' is not 0, that is, we have some readiness
if (wait.notit == nullNotice()) // No notice object
{
// Add new event notice and bind to the wait object.
m_USockEventNotice.push_back(Notice(&wait, sock, events));
wait.notit = --m_USockEventNotice.end();
return;
}
// We have an existing event notice, so update it
wait.notit->events |= events;
}
// This function only updates the corresponding event notice object
// according to the change in the events.
void updateEventNotice(Wait& wait, SRTSOCKET sock, int events, bool enable)
{
if (enable)
{
addEventNotice(wait, sock, events);
}
else
{
removeExcessEvents(wait, ~events);
}
}
void removeSubscription(SRTSOCKET u)
{
std::map<SRTSOCKET, Wait>::iterator i = m_USockWatchState.find(u);
if (i == m_USockWatchState.end())
return;
if (i->second.notit != nullNotice())
{
m_USockEventNotice.erase(i->second.notit);
// NOTE: no need to update the Wait::notit field
// because the Wait object is about to be removed anyway.
}
m_USockWatchState.erase(i);
}
void removeExistingNotices(Wait& wait)
{
m_USockEventNotice.erase(wait.notit);
wait.notit = nullNotice();
}
void removeEvents(Wait& wait)
{
if (wait.notit == nullNotice())
return;
removeExistingNotices(wait);
}
// This function removes notices referring to
// events that are NOT present in @a nevts, but
// may be among subscriptions and therefore potentially
// have an associated notice.
void removeExcessEvents(Wait& wait, int nevts)
{
// Update the event notice, should it exist
// If the watch points to a null notice, there's simply
// no notice there, so nothing to update or prospectively
// remove - but may be something to add.
if (wait.notit == nullNotice())
return;
// `events` contains bits to be cleared.
// 1. If there is no notice event, do nothing - clear already.
// 2. If there is a notice event, update by clearing the bits
// 2.1. If this made resulting state to be 0, also remove the notice.
const int newstate = wait.notit->events & nevts;
if (newstate)
{
wait.notit->events = newstate;
}
else
{
// If the new state is full 0 (no events),
// then remove the corresponding notice object
removeExistingNotices(wait);
}
}
bool checkEdge(enotice_t::iterator i)
{
// This function should check if this event was subscribed
// as edge-triggered, and if so, clear the event from the notice.
// Update events and check edge mode at the subscriber
i->events &= ~i->parent->edgeOnly();
if(!i->events)
{
removeExistingNotices(*i->parent);
return true;
}
return false;
}
};
class CEPoll
{
friend class CUDT;
friend class CRendezvousQueue;
public:
CEPoll();
~CEPoll();
public: // for CUDTUnited API
/// create a new EPoll.
/// @return new EPoll ID if success, otherwise an error number.
int create();
/// add a UDT socket to an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT Socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int add_usock(const int eid, const SRTSOCKET& u, const int* events = NULL) { return update_usock(eid, u, events); }
/// add a system socket to an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] s system Socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL);
/// remove a UDT socket event from an EPoll; socket will be removed if no events to watch.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @return 0 if success, otherwise an error number.
int remove_usock(const int eid, const SRTSOCKET& u) { static const int Null(0); return update_usock(eid, u, &Null);}
/// remove a system socket event from an EPoll; socket will be removed if no events to watch.
/// @param [in] eid EPoll ID.
/// @param [in] s system socket ID.
/// @return 0 if success, otherwise an error number.
int remove_ssock(const int eid, const SYSSOCKET& s);
/// update a UDT socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int update_usock(const int eid, const SRTSOCKET& u, const int* events);
/// update a system socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL);
/// wait for EPoll events or timeout.
/// @param [in] eid EPoll ID.
/// @param [out] readfds UDT sockets available for reading.
/// @param [out] writefds UDT sockets available for writing.
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @param [out] lrfds system file descriptors for reading.
/// @param [out] lwfds system file descriptors for writing.
/// @return number of sockets available for IO.
int wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds, std::set<SYSSOCKET>* lwfds);
/// wait for EPoll events or timeout optimized with explicit EPOLL_ERR event and the edge mode option.
/// @param [in] eid EPoll ID.
/// @param [out] fdsSet array of user socket events (SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR).
/// @param [int] fdsSize of fds array
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @return total of available events in the epoll system (can be greater than fdsSize)
int uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
/// close and release an EPoll.
/// @param [in] eid EPoll ID.
/// @return 0 if success, otherwise an error number.
int release(const int eid);
public: // for CUDT to acknowledge IO status
/// Update events available for a UDT socket.
/// @param [in] uid UDT socket ID.
/// @param [in] eids EPoll IDs to be set
/// @param [in] events Combination of events to update
/// @param [in] enable true -> enable, otherwise disable
/// @return 0 if success, otherwise an error number
int update_events(const SRTSOCKET& uid, std::set<int>& eids, int events, bool enable);
int setflags(const int eid, int32_t flags);
private:
int m_iIDSeed; // seed to generate a new ID
pthread_mutex_t m_SeedLock;
std::map<int, CEPollDesc> m_mPolls; // all epolls
pthread_mutex_t m_EPollLock;
};
#endif

2253
trunk/3rdparty/srt-1-fit/srtcore/fec.cpp vendored Normal file

File diff suppressed because it is too large Load diff

248
trunk/3rdparty/srt-1-fit/srtcore/fec.h vendored Normal file
View file

@ -0,0 +1,248 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#ifndef INC__SRT_FEC_H
#define INC__SRT_FEC_H
#include <string>
#include <map>
#include <vector>
#include <deque>
#include "packetfilter_api.h"
class FECFilterBuiltin: public SrtPacketFilterBase
{
SrtFilterConfig cfg;
size_t m_number_cols;
size_t m_number_rows;
// Configuration
SRT_ARQLevel m_fallback_level;
bool m_cols_only;
bool m_arrangement_staircase;
public:
size_t numberCols() const { return m_number_cols; }
size_t numberRows() const { return m_number_rows; }
size_t sizeCol() const { return m_number_rows; }
size_t sizeRow() const { return m_number_cols; }
struct Group
{
int32_t base; //< Sequence of the first packet in the group
size_t step; //< by how many packets the sequence should increase to get the next packet
size_t drop; //< by how much the sequence should increase to get to the next series
size_t collected; //< how many packets were taken to collect the clip
Group(): base(CSeqNo::m_iMaxSeqNo), step(0), drop(0), collected(0)
{
}
uint16_t length_clip;
uint8_t flag_clip;
uint32_t timestamp_clip;
std::vector<char> payload_clip;
// This is mutable because it's an intermediate buffer for
// the purpose of output.
//mutable vector<char> output_buffer;
enum Type
{
HORIZ, // Horizontal, recursive
VERT, // Vertical, recursive
// NOTE: HORIZ/VERT are defined as 0/1 so that not-inversion
// can flip between them.
SINGLE // Horizontal-only with no recursion
};
};
struct RcvGroup: Group
{
bool fec;
bool dismissed;
RcvGroup(): fec(false), dismissed(false) {}
#if ENABLE_HEAVY_LOGGING
std::string DisplayStats()
{
if (base == CSeqNo::m_iMaxSeqNo)
return "UNINITIALIZED!!!";
std::ostringstream os;
os << "base=" << base << " step=" << step << " drop=" << drop << " collected=" << collected
<< " " << (fec ? "+" : "-") << "FEC " << (dismissed ? "DISMISSED" : "active");
return os.str();
}
#endif
};
private:
// Row Groups: every item represents a single row group and collects clips for one row.
// Col Groups: every item represents a signel column group and collect clips for packets represented in one column
struct Send
{
// We need only ONE horizontal group. Simply after the group
// is closed (last packet supplied), and the FEC packet extracted,
// the group is no longer in use.
Group row;
std::vector<Group> cols;
} snd;
struct Receive
{
SRTSOCKET id;
bool order_required;
Receive(std::vector<SrtPacket>& provided): id(SRT_INVALID_SOCK), order_required(false), rebuilt(provided)
{
}
// In reception we need to keep as many horizontal groups as required
// for possible later tracking. A horizontal group should be dismissed
// when the size of this container exceeds the `m_number_rows` (size of the column).
//
// The 'std::deque' type is used here for a trial implementation. A desired solution
// would be a kind of a ring buffer where new groups are added and old (exceeding
// the size) automatically dismissed.
std::deque<RcvGroup> rowq;
// Base index at the oldest column platform determines
// the base index of the queue. Meaning, first you need
// to determnine the column index, where the index 0 is
// the fistmost element of this queue. After determining
// the column index, there must be also a second factor
// deteremined - which column series it is. So, this can
// start by extracting the base sequence of the element
// at the index column. This is the series 0. Now, the
// distance between these two sequences, divided by
// rowsize*colsize should return %index-in-column,
// /number-series. The latter multiplied by the row size
// is the offset between the firstmost column and the
// searched column.
std::deque<RcvGroup> colq;
// This keeps the value of "packet received or not".
// The sequence number of the first cell is rowq[0].base.
// When dropping a row,
// - the firstmost element of rowq is removed
// - the length of one row is removed from this std::vector
int32_t cell_base;
std::deque<bool> cells;
// Note this function will automatically extend the container
// with empty cells if the index exceeds the size, HOWEVER
// the caller must make sure that this index isn't any "crazy",
// that is, it fits somehow in reasonable ranges.
bool CellAt(size_t index)
{
if (index >= cells.size())
{
// Cells not prepared for this sequence yet,
// so extend in advance.
cells.resize(index+1, false);
return false; // It wasn't marked, anyway.
}
return cells[index];
}
typedef SrtPacket PrivPacket;
std::vector<PrivPacket>& rebuilt;
} rcv;
void ConfigureGroup(Group& g, int32_t seqno, size_t gstep, size_t drop);
template <class Container>
void ConfigureColumns(Container& which, int32_t isn);
void ResetGroup(Group& g);
// Universal
void ClipData(Group& g, uint16_t length_net, uint8_t kflg,
uint32_t timestamp_hw, const char* payload, size_t payload_size);
void ClipPacket(Group& g, const CPacket& pkt);
// Sending
bool CheckGroupClose(Group& g, size_t pos, size_t size);
void PackControl(const Group& g, signed char groupix, SrtPacket& pkt, int32_t seqno);
// Receiving
void CheckLargeDrop(int32_t seqno);
int ExtendRows(int rowx);
int ExtendColumns(int colgx);
void MarkCellReceived(int32_t seq);
bool HangHorizontal(const CPacket& pkt, bool fec_ctl, loss_seqs_t& irrecover);
bool HangVertical(const CPacket& pkt, signed char fec_colx, loss_seqs_t& irrecover);
void ClipControlPacket(Group& g, const CPacket& pkt);
void ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt);
void RcvRebuild(Group& g, int32_t seqno, Group::Type tp);
int32_t RcvGetLossSeqHoriz(Group& g);
int32_t RcvGetLossSeqVert(Group& g);
static void TranslateLossRecords(const std::set<int32_t>& loss, loss_seqs_t& irrecover);
void RcvCheckDismissColumn(int32_t seqno, int colgx, loss_seqs_t& irrecover);
int RcvGetRowGroupIndex(int32_t seq);
int RcvGetColumnGroupIndex(int32_t seq);
void CollectIrrecoverRow(RcvGroup& g, loss_seqs_t& irrecover) const;
bool IsLost(int32_t seq) const;
public:
FECFilterBuiltin(const SrtFilterInitializer& init, std::vector<SrtPacket>& provided, const std::string& confstr);
// Sender side
// This function creates and stores the FEC control packet with
// a prediction to be immediately sent. This is called in the function
// that normally is prepared for extracting a data packet from the sender
// buffer and send it over the channel.
virtual bool packControlPacket(SrtPacket& r_packet, int32_t seq) ATR_OVERRIDE;
// This is called at the moment when the sender queue decided to pick up
// a new packet from the scheduled packets. This should be then used to
// continue filling the group, possibly followed by final calculating the
// FEC control packet ready to send.
virtual void feedSource(CPacket& r_packet) ATR_OVERRIDE;
// Receiver side
// This function is called at the moment when a new data packet has
// arrived (no matter if subsequent or recovered). The 'state' value
// defines the configured level of loss state required to send the
// loss report.
virtual bool receive(const CPacket& pkt, loss_seqs_t& loss_seqs) ATR_OVERRIDE;
// Configuration
// This is the size that is needed extra by packets operated by this corrector.
// It should be subtracted from a current maximum value for SRTO_PAYLOADSIZE
// The default FEC uses extra space only for FEC/CTL packet.
// The timestamp clip is placed in the timestamp field in the header.
// The payload contains:
// - the length clip
// - the flag spec
// - the payload clip
// The payload clip takes simply the current length of SRTO_PAYLOADSIZE.
// So extra 4 bytes are needed, 2 for flags, 2 for length clip.
static const size_t EXTRA_SIZE = 4;
virtual SRT_ARQLevel arqLevel() ATR_OVERRIDE { return m_fallback_level; }
};
#endif

View file

@ -0,0 +1,57 @@
SOURCES
api.cpp
buffer.cpp
cache.cpp
channel.cpp
common.cpp
core.cpp
crypto.cpp
epoll.cpp
fec.cpp
handshake.cpp
list.cpp
md5.cpp
packet.cpp
packetfilter.cpp
queue.cpp
congctl.cpp
srt_c_api.cpp
window.cpp
srt_compat.c
PUBLIC HEADERS
srt.h
logging_api.h
PROTECTED HEADERS
platform_sys.h
udt.h
srt4udt.h
PRIVATE HEADERS
api.h
buffer.h
cache.h
channel.h
common.h
core.h
crypto.h
epoll.h
handshake.h
list.h
logging.h
md5.h
netinet_any.h
packet.h
queue.h
congctl.h
srt4udt.h
srt_compat.h
threadname.h
utilities.h
window.h
SOURCES WIN32 SHARED
srt_shared.rc

View file

@ -0,0 +1,260 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <cstring>
#include <string>
#include <sstream>
#include <iterator>
#include <algorithm>
#include "udt.h"
#include "core.h"
#include "handshake.h"
#include "utilities.h"
using namespace std;
CHandShake::CHandShake():
m_iVersion(0),
m_iType(0), // Universal: UDT_UNDEFINED or no flags
m_iISN(0),
m_iMSS(0),
m_iFlightFlagSize(0),
m_iReqType(URQ_WAVEAHAND),
m_iID(0),
m_iCookie(0),
m_extension(false)
{
for (int i = 0; i < 4; ++ i)
m_piPeerIP[i] = 0;
}
int CHandShake::store_to(char* buf, ref_t<size_t> r_size)
{
size_t& size = *r_size;
if (size < m_iContentSize)
return -1;
int32_t* p = reinterpret_cast<int32_t*>(buf);
*p++ = m_iVersion;
*p++ = m_iType;
*p++ = m_iISN;
*p++ = m_iMSS;
*p++ = m_iFlightFlagSize;
*p++ = int32_t(m_iReqType);
*p++ = m_iID;
*p++ = m_iCookie;
for (int i = 0; i < 4; ++ i)
*p++ = m_piPeerIP[i];
size = m_iContentSize;
return 0;
}
int CHandShake::load_from(const char* buf, size_t size)
{
if (size < m_iContentSize)
return -1;
const int32_t* p = reinterpret_cast<const int32_t*>(buf);
m_iVersion = *p++;
m_iType = *p++;
m_iISN = *p++;
m_iMSS = *p++;
m_iFlightFlagSize = *p++;
m_iReqType = UDTRequestType(*p++);
m_iID = *p++;
m_iCookie = *p++;
for (int i = 0; i < 4; ++ i)
m_piPeerIP[i] = *p++;
return 0;
}
#ifdef ENABLE_LOGGING
const char* srt_rejectreason_name [] = {
"UNKNOWN",
"SYSTEM",
"PEER",
"RESOURCE",
"ROGUE",
"BACKLOG",
"IPE",
"CLOSE",
"VERSION",
"RDVCOOKIE",
"BADSECRET",
"UNSECURE",
"MESSAGEAPI",
"CONGESTION",
"FILTER",
};
std::string RequestTypeStr(UDTRequestType rq)
{
if (rq >= URQ_FAILURE_TYPES)
{
SRT_REJECT_REASON rej = RejectReasonForURQ(rq);
int id = rej;
return std::string("ERROR:") + srt_rejectreason_name[id];
}
switch ( rq )
{
case URQ_INDUCTION: return "induction";
case URQ_WAVEAHAND: return "waveahand";
case URQ_CONCLUSION: return "conclusion";
case URQ_AGREEMENT: return "agreement";
default: return "INVALID";
}
}
string CHandShake::RdvStateStr(CHandShake::RendezvousState s)
{
switch (s)
{
case RDV_WAVING: return "waving";
case RDV_ATTENTION: return "attention";
case RDV_FINE: return "fine";
case RDV_INITIATED: return "initiated";
case RDV_CONNECTED: return "connected";
default: ;
}
return "invalid";
}
#endif
string CHandShake::show()
{
ostringstream so;
so << "version=" << m_iVersion << " type=" << hex << m_iType << dec
<< " ISN=" << m_iISN << " MSS=" << m_iMSS << " FLW=" << m_iFlightFlagSize
<< " reqtype=" << RequestTypeStr(m_iReqType) << " srcID=" << m_iID
<< " cookie=" << hex << m_iCookie << dec
<< " srcIP=";
const unsigned char* p = (const unsigned char*)m_piPeerIP;
const unsigned char* pe = p + 4 * (sizeof(uint32_t));
copy(p, pe, ostream_iterator<unsigned>(so, "."));
// XXX HS version symbols should be probably declared inside
// CHandShake, not CUDT.
if ( m_iVersion > CUDT::HS_VERSION_UDT4 )
{
so << "EXT: ";
if (m_iType == 0) // no flags at all
so << "none";
else
so << ExtensionFlagStr(m_iType);
}
return so.str();
}
string CHandShake::ExtensionFlagStr(int32_t fl)
{
std::ostringstream out;
if ( fl & HS_EXT_HSREQ )
out << " hsx";
if ( fl & HS_EXT_KMREQ )
out << " kmx";
if ( fl & HS_EXT_CONFIG )
out << " config";
int kl = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(fl) << 6;
if (kl != 0)
{
out << " AES-" << kl;
}
else
{
out << " no-pbklen";
}
return out.str();
}
// XXX This code isn't currently used. Left here because it can
// be used in future, should any refactoring for the "manual word placement"
// code be done.
bool SrtHSRequest::serialize(char* buf, size_t size) const
{
if (size < SRT_HS_SIZE)
return false;
int32_t* p = reinterpret_cast<int32_t*>(buf);
*p++ = m_iSrtVersion;
*p++ = m_iSrtFlags;
*p++ = m_iSrtTsbpd;
*p++ = 0; // SURPRISE! Seriously, use (something) if this "reserved" is going to be used for something.
return true;
}
bool SrtHSRequest::deserialize(const char* buf, size_t size)
{
m_iSrtVersion = 0; // just to let users recognize if it succeeded or not.
if (size < SRT_HS_SIZE)
return false;
const int32_t* p = reinterpret_cast<const int32_t*>(buf);
m_iSrtVersion = (*p++);
m_iSrtFlags = (*p++);
m_iSrtTsbpd = (*p++);
m_iSrtReserved = (*p++);
return true;
}

View file

@ -0,0 +1,342 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef INC__HANDSHAKE_H
#define INC__HANDSHAKE_H
#include "crypto.h"
#include "utilities.h"
typedef Bits<31, 16> HS_CMDSPEC_CMD;
typedef Bits<15, 0> HS_CMDSPEC_SIZE;
// NOTE: Some of these flags represent CAPABILITIES, that is,
// as long as these flags are defined, they must be always set
// (unless they are deprecated).
enum SrtOptions
{
SRT_OPT_TSBPDSND = BIT(0), /* Timestamp-based Packet delivery real-time data sender */
SRT_OPT_TSBPDRCV = BIT(1), /* Timestamp-based Packet delivery real-time data receiver */
SRT_OPT_HAICRYPT = BIT(2), /* CAPABILITY: HaiCrypt AES-128/192/256-CTR */
SRT_OPT_TLPKTDROP = BIT(3), /* Drop real-time data packets too late to be processed in time */
SRT_OPT_NAKREPORT = BIT(4), /* Periodic NAK report */
SRT_OPT_REXMITFLG = BIT(5), // CAPABILITY: One bit in payload packet msgno is "retransmitted" flag
// (this flag can be reused for something else, when pre-1.2.0 versions are all abandoned)
SRT_OPT_STREAM = BIT(6), // STREAM MODE (not MESSAGE mode)
SRT_OPT_FILTERCAP = BIT(7), // CAPABILITY: Packet filter supported
};
inline int SrtVersionCapabilities()
{
// NOTE: SRT_OPT_REXMITFLG is not included here because
// SRT is prepared to handle also peers that don't have this
// capability, so a listener responding to a peer that doesn't
// support it should NOT set this flag.
//
// This state will remain until this backward compatibility is
// decided to be broken, in which case this flag will be always
// set, and clients that do not support this capability will be
// rejected.
return SRT_OPT_HAICRYPT | SRT_OPT_FILTERCAP;
}
std::string SrtFlagString(int32_t flags);
const int SRT_CMD_REJECT = 0, // REJECT is only a symbol for return type
SRT_CMD_HSREQ = 1,
SRT_CMD_HSRSP = 2,
SRT_CMD_KMREQ = 3,
SRT_CMD_KMRSP = 4,
SRT_CMD_SID = 5,
SRT_CMD_CONGESTION = 6,
SRT_CMD_FILTER = 7,
SRT_CMD_NONE = -1; // for cases when {no pong for ping is required} | {no extension block found}
enum SrtDataStruct
{
SRT_HS_VERSION = 0,
SRT_HS_FLAGS,
SRT_HS_LATENCY,
// Keep it always last
SRT_HS__SIZE
};
// For HSv5 the lo and hi part is used for particular side's latency
typedef Bits<31, 16> SRT_HS_LATENCY_RCV;
typedef Bits<15, 0> SRT_HS_LATENCY_SND;
// For HSv4 only the lower part is used.
typedef Bits<15, 0> SRT_HS_LATENCY_LEG;
// XXX These structures are currently unused. The code can be changed
// so that these are used instead of manual tailoring of the messages.
struct SrtHandshakeExtension
{
protected:
uint32_t m_SrtCommand; // Used only in extension
public:
SrtHandshakeExtension(int cmd)
{
m_SrtCommand = cmd;
}
void setCommand(int cmd)
{
m_SrtCommand = cmd;
}
};
struct SrtHSRequest: public SrtHandshakeExtension
{
typedef Bits<31, 16> SRT_HSTYPE_ENCFLAGS;
typedef Bits<15, 0> SRT_HSTYPE_HSFLAGS;
// For translating PBKEYLEN into crypto flags
// This value is 16, 24, 32; after cutting off
// the leftmost 3 bits, it is 2, 3, 4.
typedef Bits<5, 3> SRT_PBKEYLEN_BITS;
// This value fits ins SRT_HSTYPE_HSFLAGS.
// .... HAIVISIOn
static const int32_t SRT_MAGIC_CODE = 0x4A17;
static int32_t wrapFlags(bool withmagic, int crypto_keylen)
{
int32_t base = withmagic ? SRT_MAGIC_CODE : 0;
return base | SRT_HSTYPE_ENCFLAGS::wrap( SRT_PBKEYLEN_BITS::unwrap(crypto_keylen) );
}
private:
friend class CHandShake;
static const size_t SRT_HS_SIZE = 4*sizeof(uint32_t); // 4 existing fields
static const size_t SRT_EXT_HS_SIZE = 2*sizeof(uint32_t) + SRT_HS_SIZE; // SRT magic and SRT HS type, used only in UDT HS ext
typedef Bits<15, 0> SRT_TSBPD_DELAY;
uint32_t m_iSrtVersion;
uint32_t m_iSrtFlags;
uint32_t m_iSrtTsbpd;
uint32_t m_iSrtReserved;
public:
SrtHSRequest(): SrtHandshakeExtension(SRT_CMD_HSREQ), m_iSrtVersion(), m_iSrtFlags(), m_iSrtTsbpd(), m_iSrtReserved() {}
void setVersion(uint32_t v) { m_iSrtVersion = v; }
uint32_t version() const { return m_iSrtVersion; }
void setFlag(SrtOptions opt) { m_iSrtFlags |= uint32_t(opt); }
void clearFlag(SrtOptions opt) { m_iSrtFlags &= ~opt; }
uint32_t flags() const { return m_iSrtFlags; }
void setTsbPdDelay(uint16_t delay) { m_iSrtTsbpd |= SRT_TSBPD_DELAY::wrap(delay); }
// Unknown what the 1-16 bits have to be used for.
uint16_t tsbPdDelay() const
{
return SRT_TSBPD_DELAY::unwrap(m_iSrtTsbpd);
}
size_t size() const { return SRT_EXT_HS_SIZE; }
bool serialize(char* p, size_t size) const;
bool deserialize(const char* mem, size_t size);
};
struct SrtKMRequest: public SrtHandshakeExtension
{
uint32_t m_iKmState;
char m_aKey[1]; // dynamic size
};
////////////////////////////////////////////////////////////////////////////////
enum UDTRequestType
{
URQ_INDUCTION_TYPES = 0, // XXX used to check in one place. Consdr rm.
URQ_INDUCTION = 1, // First part for client-server connection
URQ_WAVEAHAND = 0, // First part for rendezvous connection
URQ_CONCLUSION = -1, // Second part of handshake negotiation
URQ_AGREEMENT = -2, // Extra (last) step for rendezvous only
URQ_DONE = -3, // Special value used only in state-switching, to state that nothing should be sent in response
// Note: the client-server connection uses:
// --> INDUCTION (empty)
// <-- INDUCTION (cookie)
// --> CONCLUSION (cookie)
// <-- CONCLUSION (ok)
// The rendezvous HSv4 (legacy):
// --> WAVEAHAND (effective only if peer is also connecting)
// <-- CONCLUSION (empty) (consider yourself connected upon reception)
// --> AGREEMENT (sent as a response for conclusion, requires no response)
// The rendezvous HSv5 (using SRT extensions):
// --> WAVEAHAND (with cookie)
// --- (selecting INITIATOR/RESPONDER by cookie contest - comparing one another's cookie)
// <-- CONCLUSION (without extensions, if RESPONDER, with extensions, if INITIATOR)
// --> CONCLUSION (with response extensions, if RESPONDER)
// <-- AGREEMENT (sent exclusively by INITIATOR upon reception of CONCLUSIOn with response extensions)
// Errors reported by the peer, also used as useless error codes
// in handshake processing functions.
URQ_FAILURE_TYPES = 1000
// NOTE: codes above 1000 are reserved for failure codes for
// rejection reason, as per `SRT_REJECT_REASON` enum. DO NOT
// add any new values here.
};
inline UDTRequestType URQFailure(SRT_REJECT_REASON reason)
{
return UDTRequestType(URQ_FAILURE_TYPES + int(reason));
}
inline SRT_REJECT_REASON RejectReasonForURQ(UDTRequestType req)
{
if (req < URQ_FAILURE_TYPES || req - URQ_FAILURE_TYPES >= SRT_REJ__SIZE)
return SRT_REJ_UNKNOWN;
return SRT_REJECT_REASON(req - URQ_FAILURE_TYPES);
}
// DEPRECATED values. Use URQFailure(SRT_REJECT_REASON).
const UDTRequestType URQ_ERROR_REJECT SRT_ATR_DEPRECATED = (UDTRequestType)1002; // == 1000 + SRT_REJ_PEER
const UDTRequestType URQ_ERROR_INVALID SRT_ATR_DEPRECATED = (UDTRequestType)1004; // == 1000 + SRT_REJ_ROGUE
// XXX Change all uses of that field to UDTRequestType when possible
#if ENABLE_LOGGING
std::string RequestTypeStr(UDTRequestType);
#else
inline std::string RequestTypeStr(UDTRequestType) { return ""; }
#endif
class CHandShake
{
public:
CHandShake();
int store_to(char* buf, ref_t<size_t> size);
int load_from(const char* buf, size_t size);
public:
// This is the size of SERIALIZED handshake.
// Might be defined as simply sizeof(CHandShake), but the
// enum values would have to be forced as int32_t, which is only
// available in C++11. Theoretically they are all 32-bit, but
// such a statement is not reliable and not portable.
static const size_t m_iContentSize = 48; // Size of hand shake data
// Extension flags
static const int32_t HS_EXT_HSREQ = BIT(0);
static const int32_t HS_EXT_KMREQ = BIT(1);
static const int32_t HS_EXT_CONFIG = BIT(2);
static std::string ExtensionFlagStr(int32_t fl);
// Applicable only when m_iVersion == HS_VERSION_SRT1
int32_t flags() { return m_iType; }
public:
int32_t m_iVersion; // UDT version (HS_VERSION_* symbols)
int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags
int32_t m_iISN; // random initial sequence number
int32_t m_iMSS; // maximum segment size
int32_t m_iFlightFlagSize; // flow control window size
UDTRequestType m_iReqType; // handshake stage
int32_t m_iID; // socket ID
int32_t m_iCookie; // cookie
uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to
bool m_extension;
std::string show();
// The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way).
//
// The WAVING state is the very initial state of the rendezvous connection and restored after the
// connection is closed.
// The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible
// situations are:
// - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE
// - "parallel arrangement" both parties transit to ATTENTION
//
// Parallel arrangement is a "virtually impossible" case, in which both parties must send the first
// URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same
// time, on machines with exactly the same performance and all things preceding the message sending
// have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if
// the clients have started at different times, the one who started first sends a message and the
// system of the receiver buffers this message even before the client binds the port for enough long
// time so that it outlasts also the possible second, repeated waveahand.
enum RendezvousState
{
RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events.
RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer.
RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION].
RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE].
RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state.
RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT.
};
#if ENABLE_LOGGING
static std::string RdvStateStr(RendezvousState s);
#else
static std::string RdvStateStr(RendezvousState) { return ""; }
#endif
};
#endif

View file

@ -0,0 +1,769 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 01/22/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
#include "list.h"
#include "packet.h"
CSndLossList::CSndLossList(int size):
m_caSeq(),
m_iHead(-1),
m_iLength(0),
m_iSize(size),
m_iLastInsertPos(-1),
m_ListLock()
{
m_caSeq = new Seq[size];
// -1 means there is no data in the node
for (int i = 0; i < size; ++ i)
{
m_caSeq[i].data1 = -1;
m_caSeq[i].data2 = -1;
}
// sender list needs mutex protection
pthread_mutex_init(&m_ListLock, 0);
}
CSndLossList::~CSndLossList()
{
delete [] m_caSeq;
pthread_mutex_destroy(&m_ListLock);
}
int CSndLossList::insert(int32_t seqno1, int32_t seqno2)
{
CGuard listguard(m_ListLock);
if (0 == m_iLength)
{
// insert data into an empty list
m_iHead = 0;
m_caSeq[m_iHead].data1 = seqno1;
if (seqno2 != seqno1)
m_caSeq[m_iHead].data2 = seqno2;
m_caSeq[m_iHead].next = -1;
m_iLastInsertPos = m_iHead;
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
return m_iLength;
}
// otherwise find the position where the data can be inserted
int origlen = m_iLength;
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno1);
int loc = (m_iHead + offset + m_iSize) % m_iSize;
if (offset < 0)
{
// Insert data prior to the head pointer
m_caSeq[loc].data1 = seqno1;
if (seqno2 != seqno1)
m_caSeq[loc].data2 = seqno2;
// new node becomes head
m_caSeq[loc].next = m_iHead;
m_iHead = loc;
m_iLastInsertPos = loc;
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
}
else if (offset > 0)
{
if (seqno1 == m_caSeq[loc].data1)
{
m_iLastInsertPos = loc;
// first seqno is equivlent, compare the second
if (-1 == m_caSeq[loc].data2)
{
if (seqno2 != seqno1)
{
m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1;
m_caSeq[loc].data2 = seqno2;
}
}
else if (CSeqNo::seqcmp(seqno2, m_caSeq[loc].data2) > 0)
{
// new seq pair is longer than old pair, e.g., insert [3, 7] to [3, 5], becomes [3, 7]
m_iLength += CSeqNo::seqlen(m_caSeq[loc].data2, seqno2) - 1;
m_caSeq[loc].data2 = seqno2;
}
else
// Do nothing if it is already there
return 0;
}
else
{
// searching the prior node
int i;
if ((-1 != m_iLastInsertPos) && (CSeqNo::seqcmp(m_caSeq[m_iLastInsertPos].data1, seqno1) < 0))
i = m_iLastInsertPos;
else
i = m_iHead;
while ((-1 != m_caSeq[i].next) && (CSeqNo::seqcmp(m_caSeq[m_caSeq[i].next].data1, seqno1) < 0))
i = m_caSeq[i].next;
if ((-1 == m_caSeq[i].data2) || (CSeqNo::seqcmp(m_caSeq[i].data2, seqno1) < 0))
{
m_iLastInsertPos = loc;
// no overlap, create new node
m_caSeq[loc].data1 = seqno1;
if (seqno2 != seqno1)
m_caSeq[loc].data2 = seqno2;
m_caSeq[loc].next = m_caSeq[i].next;
m_caSeq[i].next = loc;
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
}
else
{
m_iLastInsertPos = i;
// overlap, coalesce with prior node, insert(3, 7) to [2, 5], ... becomes [2, 7]
if (CSeqNo::seqcmp(m_caSeq[i].data2, seqno2) < 0)
{
m_iLength += CSeqNo::seqlen(m_caSeq[i].data2, seqno2) - 1;
m_caSeq[i].data2 = seqno2;
loc = i;
}
else
return 0;
}
}
}
else
{
m_iLastInsertPos = m_iHead;
// insert to head node
if (seqno2 != seqno1)
{
if (-1 == m_caSeq[loc].data2)
{
m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1;
m_caSeq[loc].data2 = seqno2;
}
else if (CSeqNo::seqcmp(seqno2, m_caSeq[loc].data2) > 0)
{
m_iLength += CSeqNo::seqlen(m_caSeq[loc].data2, seqno2) - 1;
m_caSeq[loc].data2 = seqno2;
}
else
return 0;
}
else
return 0;
}
// coalesce with next node. E.g., [3, 7], ..., [6, 9] becomes [3, 9]
while ((-1 != m_caSeq[loc].next) && (-1 != m_caSeq[loc].data2))
{
const int i = m_caSeq[loc].next;
if (CSeqNo::seqcmp(m_caSeq[i].data1, CSeqNo::incseq(m_caSeq[loc].data2)) > 0)
break;
// coalesce if there is overlap
if (-1 != m_caSeq[i].data2)
{
if (CSeqNo::seqcmp(m_caSeq[i].data2, m_caSeq[loc].data2) > 0)
{
if (CSeqNo::seqcmp(m_caSeq[loc].data2, m_caSeq[i].data1) >= 0)
m_iLength -= CSeqNo::seqlen(m_caSeq[i].data1, m_caSeq[loc].data2);
m_caSeq[loc].data2 = m_caSeq[i].data2;
}
else
m_iLength -= CSeqNo::seqlen(m_caSeq[i].data1, m_caSeq[i].data2);
}
else
{
if (m_caSeq[i].data1 == CSeqNo::incseq(m_caSeq[loc].data2))
m_caSeq[loc].data2 = m_caSeq[i].data1;
else
m_iLength--;
}
m_caSeq[i].data1 = -1;
m_caSeq[i].data2 = -1;
m_caSeq[loc].next = m_caSeq[i].next;
}
return m_iLength - origlen;
}
void CSndLossList::remove(int32_t seqno)
{
CGuard listguard(m_ListLock);
if (0 == m_iLength)
return;
// Remove all from the head pointer to a node with a larger seq. no. or the list is empty
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno);
int loc = (m_iHead + offset + m_iSize) % m_iSize;
if (0 == offset)
{
// It is the head. Remove the head and point to the next node
loc = (loc + 1) % m_iSize;
if (-1 == m_caSeq[m_iHead].data2)
loc = m_caSeq[m_iHead].next;
else
{
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[m_iHead].data2, CSeqNo::incseq(seqno)) > 0)
m_caSeq[loc].data2 = m_caSeq[m_iHead].data2;
m_caSeq[m_iHead].data2 = -1;
m_caSeq[loc].next = m_caSeq[m_iHead].next;
}
m_caSeq[m_iHead].data1 = -1;
if (m_iLastInsertPos == m_iHead)
m_iLastInsertPos = -1;
m_iHead = loc;
m_iLength --;
}
else if (offset > 0)
{
int h = m_iHead;
if (seqno == m_caSeq[loc].data1)
{
// target node is not empty, remove part/all of the seqno in the node.
int temp = loc;
loc = (loc + 1) % m_iSize;
if (-1 == m_caSeq[temp].data2)
m_iHead = m_caSeq[temp].next;
else
{
// remove part, e.g., [3, 7] becomes [], [4, 7] after remove(3)
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[temp].data2, m_caSeq[loc].data1) > 0)
m_caSeq[loc].data2 = m_caSeq[temp].data2;
m_iHead = loc;
m_caSeq[loc].next = m_caSeq[temp].next;
m_caSeq[temp].next = loc;
m_caSeq[temp].data2 = -1;
}
}
else
{
// target node is empty, check prior node
int i = m_iHead;
while ((-1 != m_caSeq[i].next) && (CSeqNo::seqcmp(m_caSeq[m_caSeq[i].next].data1, seqno) < 0))
i = m_caSeq[i].next;
loc = (loc + 1) % m_iSize;
if (-1 == m_caSeq[i].data2)
m_iHead = m_caSeq[i].next;
else if (CSeqNo::seqcmp(m_caSeq[i].data2, seqno) > 0)
{
// remove part/all seqno in the prior node
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[i].data2, m_caSeq[loc].data1) > 0)
m_caSeq[loc].data2 = m_caSeq[i].data2;
m_caSeq[i].data2 = seqno;
m_caSeq[loc].next = m_caSeq[i].next;
m_caSeq[i].next = loc;
m_iHead = loc;
}
else
m_iHead = m_caSeq[i].next;
}
// Remove all nodes prior to the new head
while (h != m_iHead)
{
if (m_caSeq[h].data2 != -1)
{
m_iLength -= CSeqNo::seqlen(m_caSeq[h].data1, m_caSeq[h].data2);
m_caSeq[h].data2 = -1;
}
else
m_iLength --;
m_caSeq[h].data1 = -1;
if (m_iLastInsertPos == h)
m_iLastInsertPos = -1;
h = m_caSeq[h].next;
}
}
}
int CSndLossList::getLossLength() const
{
CGuard listguard(m_ListLock);
return m_iLength;
}
int32_t CSndLossList::popLostSeq()
{
CGuard listguard(m_ListLock);
if (0 == m_iLength)
return -1;
if (m_iLastInsertPos == m_iHead)
m_iLastInsertPos = -1;
// return the first loss seq. no.
int32_t seqno = m_caSeq[m_iHead].data1;
// head moves to the next node
if (-1 == m_caSeq[m_iHead].data2)
{
//[3, -1] becomes [], and head moves to next node in the list
m_caSeq[m_iHead].data1 = -1;
m_iHead = m_caSeq[m_iHead].next;
}
else
{
// shift to next node, e.g., [3, 7] becomes [], [4, 7]
int loc = (m_iHead + 1) % m_iSize;
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[m_iHead].data2, m_caSeq[loc].data1) > 0)
m_caSeq[loc].data2 = m_caSeq[m_iHead].data2;
m_caSeq[m_iHead].data1 = -1;
m_caSeq[m_iHead].data2 = -1;
m_caSeq[loc].next = m_caSeq[m_iHead].next;
m_iHead = loc;
}
m_iLength --;
return seqno;
}
////////////////////////////////////////////////////////////////////////////////
CRcvLossList::CRcvLossList(int size):
m_caSeq(),
m_iHead(-1),
m_iTail(-1),
m_iLength(0),
m_iSize(size)
{
m_caSeq = new Seq[m_iSize];
// -1 means there is no data in the node
for (int i = 0; i < size; ++ i)
{
m_caSeq[i].data1 = -1;
m_caSeq[i].data2 = -1;
}
}
CRcvLossList::~CRcvLossList()
{
delete [] m_caSeq;
}
void CRcvLossList::insert(int32_t seqno1, int32_t seqno2)
{
// Data to be inserted must be larger than all those in the list
// guaranteed by the UDT receiver
if (0 == m_iLength)
{
// insert data into an empty list
m_iHead = 0;
m_iTail = 0;
m_caSeq[m_iHead].data1 = seqno1;
if (seqno2 != seqno1)
m_caSeq[m_iHead].data2 = seqno2;
m_caSeq[m_iHead].next = -1;
m_caSeq[m_iHead].prior = -1;
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
return;
}
// otherwise searching for the position where the node should be
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno1);
int loc = (m_iHead + offset) % m_iSize;
if ((-1 != m_caSeq[m_iTail].data2) && (CSeqNo::incseq(m_caSeq[m_iTail].data2) == seqno1))
{
// coalesce with prior node, e.g., [2, 5], [6, 7] becomes [2, 7]
loc = m_iTail;
m_caSeq[loc].data2 = seqno2;
}
else
{
// create new node
m_caSeq[loc].data1 = seqno1;
if (seqno2 != seqno1)
m_caSeq[loc].data2 = seqno2;
m_caSeq[m_iTail].next = loc;
m_caSeq[loc].prior = m_iTail;
m_caSeq[loc].next = -1;
m_iTail = loc;
}
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
}
bool CRcvLossList::remove(int32_t seqno)
{
if (0 == m_iLength)
return false;
// locate the position of "seqno" in the list
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno);
if (offset < 0)
return false;
int loc = (m_iHead + offset) % m_iSize;
if (seqno == m_caSeq[loc].data1)
{
// This is a seq. no. that starts the loss sequence
if (-1 == m_caSeq[loc].data2)
{
// there is only 1 loss in the sequence, delete it from the node
if (m_iHead == loc)
{
m_iHead = m_caSeq[m_iHead].next;
if (-1 != m_iHead)
m_caSeq[m_iHead].prior = -1;
}
else
{
m_caSeq[m_caSeq[loc].prior].next = m_caSeq[loc].next;
if (-1 != m_caSeq[loc].next)
m_caSeq[m_caSeq[loc].next].prior = m_caSeq[loc].prior;
else
m_iTail = m_caSeq[loc].prior;
}
m_caSeq[loc].data1 = -1;
}
else
{
// there are more than 1 loss in the sequence
// move the node to the next and update the starter as the next loss inSeqNo(seqno)
// find next node
int i = (loc + 1) % m_iSize;
// remove the "seqno" and change the starter as next seq. no.
m_caSeq[i].data1 = CSeqNo::incseq(m_caSeq[loc].data1);
// process the sequence end
if (CSeqNo::seqcmp(m_caSeq[loc].data2, CSeqNo::incseq(m_caSeq[loc].data1)) > 0)
m_caSeq[i].data2 = m_caSeq[loc].data2;
// remove the current node
m_caSeq[loc].data1 = -1;
m_caSeq[loc].data2 = -1;
// update list pointer
m_caSeq[i].next = m_caSeq[loc].next;
m_caSeq[i].prior = m_caSeq[loc].prior;
if (m_iHead == loc)
m_iHead = i;
else
m_caSeq[m_caSeq[i].prior].next = i;
if (m_iTail == loc)
m_iTail = i;
else
m_caSeq[m_caSeq[i].next].prior = i;
}
m_iLength --;
return true;
}
// There is no loss sequence in the current position
// the "seqno" may be contained in a previous node
// searching previous node
int i = (loc - 1 + m_iSize) % m_iSize;
while (-1 == m_caSeq[i].data1)
i = (i - 1 + m_iSize) % m_iSize;
// not contained in this node, return
if ((-1 == m_caSeq[i].data2) || (CSeqNo::seqcmp(seqno, m_caSeq[i].data2) > 0))
return false;
if (seqno == m_caSeq[i].data2)
{
// it is the sequence end
if (seqno == CSeqNo::incseq(m_caSeq[i].data1))
m_caSeq[i].data2 = -1;
else
m_caSeq[i].data2 = CSeqNo::decseq(seqno);
}
else
{
// split the sequence
// construct the second sequence from CSeqNo::incseq(seqno) to the original sequence end
// located at "loc + 1"
loc = (loc + 1) % m_iSize;
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[i].data2, m_caSeq[loc].data1) > 0)
m_caSeq[loc].data2 = m_caSeq[i].data2;
// the first (original) sequence is between the original sequence start to CSeqNo::decseq(seqno)
if (seqno == CSeqNo::incseq(m_caSeq[i].data1))
m_caSeq[i].data2 = -1;
else
m_caSeq[i].data2 = CSeqNo::decseq(seqno);
// update the list pointer
m_caSeq[loc].next = m_caSeq[i].next;
m_caSeq[i].next = loc;
m_caSeq[loc].prior = i;
if (m_iTail == i)
m_iTail = loc;
else
m_caSeq[m_caSeq[loc].next].prior = loc;
}
m_iLength --;
return true;
}
bool CRcvLossList::remove(int32_t seqno1, int32_t seqno2)
{
if (seqno1 <= seqno2)
{
for (int32_t i = seqno1; i <= seqno2; ++ i)
remove(i);
}
else
{
for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++ j)
remove(j);
for (int32_t k = 0; k <= seqno2; ++ k)
remove(k);
}
return true;
}
bool CRcvLossList::find(int32_t seqno1, int32_t seqno2) const
{
if (0 == m_iLength)
return false;
int p = m_iHead;
while (-1 != p)
{
if ((CSeqNo::seqcmp(m_caSeq[p].data1, seqno1) == 0) ||
((CSeqNo::seqcmp(m_caSeq[p].data1, seqno1) > 0) && (CSeqNo::seqcmp(m_caSeq[p].data1, seqno2) <= 0)) ||
((CSeqNo::seqcmp(m_caSeq[p].data1, seqno1) < 0) && (m_caSeq[p].data2 != -1) && CSeqNo::seqcmp(m_caSeq[p].data2, seqno1) >= 0))
return true;
p = m_caSeq[p].next;
}
return false;
}
int CRcvLossList::getLossLength() const
{
return m_iLength;
}
int CRcvLossList::getFirstLostSeq() const
{
if (0 == m_iLength)
return -1;
return m_caSeq[m_iHead].data1;
}
void CRcvLossList::getLossArray(int32_t* array, int& len, int limit)
{
len = 0;
int i = m_iHead;
while ((len < limit - 1) && (-1 != i))
{
array[len] = m_caSeq[i].data1;
if (-1 != m_caSeq[i].data2)
{
// there are more than 1 loss in the sequence
array[len] |= LOSSDATA_SEQNO_RANGE_FIRST;
++ len;
array[len] = m_caSeq[i].data2;
}
++ len;
i = m_caSeq[i].next;
}
}
CRcvFreshLoss::CRcvFreshLoss(int32_t seqlo, int32_t seqhi, int initial_age): ttl(initial_age)
{
CTimer::rdtsc(timestamp);
seq[0] = seqlo;
seq[1] = seqhi;
}
CRcvFreshLoss::Emod CRcvFreshLoss::revoke(int32_t sequence)
{
int32_t diffbegin = CSeqNo::seqcmp(sequence, seq[0]);
int32_t diffend = CSeqNo::seqcmp(sequence, seq[1]);
if ( diffbegin < 0 || diffend > 0 )
{
return NONE; // not within the range at all.
}
if ( diffbegin == 0 )
{
if ( diffend == 0 ) // exactly at begin and end
{
return DELETE;
}
// only exactly at begin. Shrink the range
seq[0] = CSeqNo::incseq(seq[0]);
return STRIPPED;
}
if ( diffend == 0 ) // exactly at end
{
seq[1] = CSeqNo::decseq(seq[1]);
return STRIPPED;
}
return SPLIT;
}
CRcvFreshLoss::Emod CRcvFreshLoss::revoke(int32_t lo, int32_t hi)
{
// This should only if the range lo-hi is anyhow covered by seq[0]-seq[1].
// Note: if the checked item contains sequences that are OLDER
// than the oldest sequence in this range, they should be deleted,
// even though this wasn't explicitly requested.
// LOHI: <lo, hi>
// ITEM: <lo, hi> <--- delete
// If the sequence range is older than the range to be revoked,
// delete it anyway.
if ( CSeqNo::seqcmp(lo, seq[1]) > 0 )
return DELETE;
// LOHI: <lo, hi>
// ITEM: <lo, hi> <-- NOTFOUND
// This element is newer than the given sequence, so match failed.
if ( CSeqNo::seqcmp(hi, seq[0]) < 0 )
return NONE;
// LOHI: <lo, hi>
// ITEM: <lo, ! hi>
// RESULT: <lo, hi>
// 2. If the 'hi' is in the middle (less than seq[1]), delete partially.
// That is, take care of this range for itself and return STRIPPED.
if ( CSeqNo::seqcmp(hi, seq[1]) < 0 )
{
seq[0] = CSeqNo::incseq(hi);
return STRIPPED;
}
// LOHI: <lo, hi>
// ITEM: <lo, ! hi>
// RESULT: DELETE.
// 3. Otherwise delete the record, even if this was covering only part of this range.
// This is not possible that the sequences OLDER THAN THIS are not required to be
// revoken together with this one.
return DELETE;
}

250
trunk/3rdparty/srt-1-fit/srtcore/list.h vendored Normal file
View file

@ -0,0 +1,250 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 01/22/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_LIST_H__
#define __UDT_LIST_H__
#include "udt.h"
#include "common.h"
class CSndLossList
{
public:
CSndLossList(int size = 1024);
~CSndLossList();
/// Insert a seq. no. into the sender loss list.
/// @param [in] seqno1 sequence number starts.
/// @param [in] seqno2 sequence number ends.
/// @return number of packets that are not in the list previously.
int insert(int32_t seqno1, int32_t seqno2);
/// Remove ALL the seq. no. that are not greater than the parameter.
/// @param [in] seqno sequence number.
void remove(int32_t seqno);
/// Read the loss length.
/// @return The length of the list.
int getLossLength() const;
/// Read the first (smallest) loss seq. no. in the list and remove it.
/// @return The seq. no. or -1 if the list is empty.
int32_t popLostSeq();
private:
struct Seq
{
int32_t data1; // sequence number starts
int32_t data2; // seqnence number ends
int next; // next node in the list
}* m_caSeq;
int m_iHead; // first node
int m_iLength; // loss length
int m_iSize; // size of the static array
int m_iLastInsertPos; // position of last insert node
mutable pthread_mutex_t m_ListLock; // used to synchronize list operation
private:
CSndLossList(const CSndLossList&);
CSndLossList& operator=(const CSndLossList&);
};
////////////////////////////////////////////////////////////////////////////////
class CRcvLossList
{
public:
CRcvLossList(int size = 1024);
~CRcvLossList();
/// Insert a series of loss seq. no. between "seqno1" and "seqno2" into the receiver's loss list.
/// @param [in] seqno1 sequence number starts.
/// @param [in] seqno2 seqeunce number ends.
void insert(int32_t seqno1, int32_t seqno2);
/// Remove a loss seq. no. from the receiver's loss list.
/// @param [in] seqno sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
bool remove(int32_t seqno);
/// Remove all packets between seqno1 and seqno2.
/// @param [in] seqno1 start sequence number.
/// @param [in] seqno2 end sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
bool remove(int32_t seqno1, int32_t seqno2);
/// Find if there is any lost packets whose sequence number falling seqno1 and seqno2.
/// @param [in] seqno1 start sequence number.
/// @param [in] seqno2 end sequence number.
/// @return True if found; otherwise false.
bool find(int32_t seqno1, int32_t seqno2) const;
/// Read the loss length.
/// @return the length of the list.
int getLossLength() const;
/// Read the first (smallest) seq. no. in the list.
/// @return the sequence number or -1 if the list is empty.
int getFirstLostSeq() const;
/// Get a encoded loss array for NAK report.
/// @param [out] array the result list of seq. no. to be included in NAK.
/// @param [out] len physical length of the result array.
/// @param [in] limit maximum length of the array.
void getLossArray(int32_t* array, int& len, int limit);
private:
struct Seq
{
int32_t data1; // sequence number starts
int32_t data2; // sequence number ends
int next; // next node in the list
int prior; // prior node in the list;
}* m_caSeq;
int m_iHead; // first node in the list
int m_iTail; // last node in the list;
int m_iLength; // loss length
int m_iSize; // size of the static array
private:
CRcvLossList(const CRcvLossList&);
CRcvLossList& operator=(const CRcvLossList&);
public:
struct iterator
{
int32_t head;
Seq* seq;
iterator(Seq* str, int32_t v): head(v), seq(str) {}
iterator next() const
{
if ( head == -1 )
return *this; // should report error, but we can only throw exception, so simply ignore it.
return iterator(seq, seq[head].next);
}
iterator& operator++()
{
*this = next();
return *this;
}
iterator operator++(int)
{
iterator old (seq, head);
*this = next();
return old;
}
bool operator==(const iterator& second) const
{
// Ignore seq - should be the same and this is only a sanity check.
return head == second.head;
}
bool operator!=(const iterator& second) const { return !(*this == second); }
std::pair<int32_t, int32_t> operator*()
{
return std::make_pair(seq[head].data1, seq[head].data2);
}
};
iterator begin() { return iterator(m_caSeq, m_iHead); }
iterator end() { return iterator(m_caSeq, -1); }
};
struct CRcvFreshLoss
{
int32_t seq[2];
int ttl;
uint64_t timestamp;
CRcvFreshLoss(int32_t seqlo, int32_t seqhi, int initial_ttl);
// Don't WTF when looking at this. The Windows system headers define
// a publicly visible preprocessor macro with that name. REALLY!
#ifdef DELETE
#undef DELETE
#endif
enum Emod {
NONE, //< the given sequence was not found in this range
STRIPPED, //< it was equal to first or last, already taken care of
SPLIT, //< found in the middle, you have to split this range into two
DELETE //< This was a range of one element exactly equal to sequence. Simply delete it.
};
Emod revoke(int32_t sequence);
Emod revoke(int32_t lo, int32_t hi);
};
#endif

View file

@ -0,0 +1,481 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__SRT_LOGGING_H
#define INC__SRT_LOGGING_H
#include <iostream>
#include <iomanip>
#include <set>
#include <sstream>
#include <cstdarg>
#ifdef _WIN32
#include "win/wintime.h"
#include <sys/timeb.h>
#else
#include <sys/time.h>
#endif
#include <pthread.h>
#if HAVE_CXX11
#include <mutex>
#endif
#include "srt.h"
#include "utilities.h"
#include "threadname.h"
#include "logging_api.h"
#include "srt_compat.h"
#ifdef __GNUC__
#define PRINTF_LIKE __attribute__((format(printf,2,3)))
#else
#define PRINTF_LIKE
#endif
#if ENABLE_LOGGING
// GENERAL NOTE: All logger functions ADD THEIR OWN \n (EOL). Don't add any your own EOL character.
// The logging system may not add the EOL character, if appropriate flag was set in log settings.
// Anyway, treat the whole contents of eventually formatted message as exactly one line.
// LOGC uses an iostream-like syntax, using the special 'log' symbol.
// This symbol isn't visible outside the log macro parameters.
// Usage: LOGC(mglog.Debug, log << param1 << param2 << param3);
#define LOGC(logdes, args) if (logdes.CheckEnabled()) { srt_logging::LogDispatcher::Proxy log(logdes); log.setloc(__FILE__, __LINE__, __FUNCTION__); args; }
// LOGF uses printf-like style formatting.
// Usage: LOGF(mglog.Debug, "%s: %d", param1.c_str(), int(param2));
#define LOGF(logdes, ...) if (logdes.CheckEnabled()) logdes().setloc(__FILE__, __LINE__, __FUNCTION__).form(__VA_ARGS__)
// LOGP is C++11 only OR with only one string argument.
// Usage: LOGP(mglog.Debug, param1, param2, param3);
#define LOGP(logdes, ...) if (logdes.CheckEnabled()) logdes.printloc(__FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__)
#if ENABLE_HEAVY_LOGGING
#define HLOGC LOGC
#define HLOGP LOGP
#define HLOGF LOGF
#define IF_HEAVY_LOGGING(instr) instr
#else
#define HLOGC(...)
#define HLOGF(...)
#define HLOGP(...)
#define IF_HEAVY_LOGGING(instr) (void)0
#endif
#else
#define LOGC(...)
#define LOGF(...)
#define LOGP(...)
#define HLOGC(...)
#define HLOGF(...)
#define HLOGP(...)
#define IF_HEAVY_LOGGING(instr) (void)0
#endif
namespace srt_logging
{
struct LogConfig
{
typedef std::bitset<SRT_LOGFA_LASTNONE+1> fa_bitset_t;
fa_bitset_t enabled_fa; // NOTE: assumed atomic reading
LogLevel::type max_level; // NOTE: assumed atomic reading
std::ostream* log_stream;
SRT_LOG_HANDLER_FN* loghandler_fn;
void* loghandler_opaque;
pthread_mutex_t mutex;
int flags;
LogConfig(const fa_bitset_t& initial_fa):
enabled_fa(initial_fa),
max_level(LogLevel::warning),
log_stream(&std::cerr)
{
pthread_mutex_init(&mutex, 0);
}
LogConfig(const fa_bitset_t& efa, LogLevel::type l, std::ostream* ls):
enabled_fa(efa), max_level(l), log_stream(ls)
{
pthread_mutex_init(&mutex, 0);
}
~LogConfig()
{
pthread_mutex_destroy(&mutex);
}
void lock() { pthread_mutex_lock(&mutex); }
void unlock() { pthread_mutex_unlock(&mutex); }
};
// The LogDispatcher class represents the object that is responsible for
// a decision whether to log something or not, and if so, print the log.
struct SRT_API LogDispatcher
{
private:
int fa;
LogLevel::type level;
static const size_t MAX_PREFIX_SIZE = 32;
char prefix[MAX_PREFIX_SIZE+1];
LogConfig* src_config;
pthread_mutex_t mutex;
bool isset(int flg) { return (src_config->flags & flg) != 0; }
public:
LogDispatcher(int functional_area, LogLevel::type log_level, const char* your_pfx,
const char* logger_pfx /*[[nullable]]*/, LogConfig& config):
fa(functional_area),
level(log_level),
src_config(&config)
{
// XXX stpcpy desired, but not enough portable
// Composing the exact prefix is not critical, so simply
// cut the prefix, if the length is exceeded
// See Logger::Logger; we know this has normally 2 characters,
// except !!FATAL!!, which has 9. Still less than 32.
strcpy(prefix, your_pfx);
// If the size of the FA name together with severity exceeds the size,
// just skip the former.
if (logger_pfx && strlen(prefix) + strlen(logger_pfx) + 1 < MAX_PREFIX_SIZE)
{
strcat(prefix, ":");
strcat(prefix, logger_pfx);
}
pthread_mutex_init(&mutex, 0);
}
~LogDispatcher()
{
pthread_mutex_destroy(&mutex);
}
bool CheckEnabled();
void CreateLogLinePrefix(std::ostringstream&);
void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl);
// log.Debug("This is the ", nth, " time"); <--- C++11 only.
// log.Debug() << "This is the " << nth << " time"; <--- C++03 available.
#if HAVE_CXX11
template <class... Args>
void PrintLogLine(const char* file, int line, const std::string& area, Args&&... args);
template<class Arg1, class... Args>
void operator()(Arg1&& arg1, Args&&... args)
{
PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg1, args...);
}
template<class Arg1, class... Args>
void printloc(const char* file, int line, const std::string& area, Arg1&& arg1, Args&&... args)
{
PrintLogLine(file, line, area, arg1, args...);
}
#else
template <class Arg>
void PrintLogLine(const char* file, int line, const std::string& area, const Arg& arg);
// For C++03 (older) standard provide only with one argument.
template <class Arg>
void operator()(const Arg& arg)
{
PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg);
}
void printloc(const char* file, int line, const std::string& area, const std::string& arg1)
{
PrintLogLine(file, line, area, arg1);
}
#endif
#if ENABLE_LOGGING
struct Proxy;
friend struct Proxy;
Proxy operator()();
#else
// Dummy proxy that does nothing
struct DummyProxy
{
DummyProxy(LogDispatcher&)
{
}
template <class T>
DummyProxy& operator<<(const T& ) // predicted for temporary objects
{
return *this;
}
DummyProxy& form(const char*, ...)
{
return *this;
}
DummyProxy& setloc(const char* , int , std::string)
{
return *this;
}
};
DummyProxy operator()()
{
return DummyProxy(*this);
}
#endif
};
#if ENABLE_LOGGING
struct LogDispatcher::Proxy
{
LogDispatcher& that;
std::ostringstream os;
// Cache the 'enabled' state in the beginning. If the logging
// becomes enabled or disabled in the middle of the log, we don't
// want it to be partially printed anyway.
bool that_enabled;
int flags;
// CACHE!!!
const char* i_file;
int i_line;
std::string area;
Proxy& setloc(const char* f, int l, std::string a)
{
i_file = f;
i_line = l;
area = a;
return *this;
}
// Left for future. Not sure if it's more convenient
// to use this to translate __PRETTY_FUNCTION__ to
// something short, or just let's leave __FUNCTION__
// or better __func__.
std::string ExtractName(std::string pretty_function);
Proxy(LogDispatcher& guy);
// Copy constructor is needed due to noncopyable ostringstream.
// This is used only in creation of the default object, so just
// use the default values, just copy the location cache.
Proxy(const Proxy& p): that(p.that), area(p.area)
{
i_file = p.i_file;
i_line = p.i_line;
that_enabled = false;
flags = p.flags;
}
template <class T>
Proxy& operator<<(const T& arg) // predicted for temporary objects
{
if ( that_enabled )
{
os << arg;
}
return *this;
}
~Proxy()
{
if ( that_enabled )
{
if ( (flags & SRT_LOGF_DISABLE_EOL) == 0 )
os << std::endl;
that.SendLogLine(i_file, i_line, area, os.str());
}
// Needed in destructor?
//os.clear();
//os.str("");
}
Proxy& form(const char* fmts, ...) PRINTF_LIKE
{
if ( !that_enabled )
return *this;
if ( !fmts || fmts[0] == '\0' )
return *this;
va_list ap;
va_start(ap, fmts);
vform(fmts, ap);
va_end(ap);
return *this;
}
Proxy& vform(const char* fmts, va_list ap)
{
char buf[512];
vsprintf(buf, fmts, ap);
size_t len = strlen(buf);
if ( buf[len-1] == '\n' )
{
// Remove EOL character, should it happen to be at the end.
// The EOL will be added at the end anyway.
buf[len-1] = '\0';
}
os << buf;
return *this;
}
};
#endif
class Logger
{
int m_fa;
LogConfig& m_config;
public:
LogDispatcher Debug;
LogDispatcher Note;
LogDispatcher Warn;
LogDispatcher Error;
LogDispatcher Fatal;
Logger(int functional_area, LogConfig& config, const char* logger_pfx = NULL):
m_fa(functional_area),
m_config(config),
Debug ( m_fa, LogLevel::debug, " D", logger_pfx, m_config ),
Note ( m_fa, LogLevel::note, ".N", logger_pfx, m_config ),
Warn ( m_fa, LogLevel::warning, "!W", logger_pfx, m_config ),
Error ( m_fa, LogLevel::error, "*E", logger_pfx, m_config ),
Fatal ( m_fa, LogLevel::fatal, "!!FATAL!!", logger_pfx, m_config )
{
}
};
inline bool LogDispatcher::CheckEnabled()
{
// Don't use enabler caching. Check enabled state every time.
// These assume to be atomically read, so the lock is not needed
// (note that writing to this field is still mutex-protected).
// It's also no problem if the level was changed at the moment
// when the enabler check is tested here. Worst case, the log
// will be printed just a moment after it was turned off.
const LogConfig* config = src_config; // to enforce using const operator[]
int configured_enabled_fa = config->enabled_fa[fa];
int configured_maxlevel = config->max_level;
return configured_enabled_fa && level <= configured_maxlevel;
}
SRT_API std::string FormatTime(uint64_t time);
#if HAVE_CXX11
//extern std::mutex Debug_mutex;
inline void PrintArgs(std::ostream&) {}
template <class Arg1, class... Args>
inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args)
{
serr << arg1;
PrintArgs(serr, args...);
}
template <class... Args>
inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, Args&&... args ATR_UNUSED)
{
#ifdef ENABLE_LOGGING
std::ostringstream serr;
CreateLogLinePrefix(serr);
PrintArgs(serr, args...);
if ( !isset(SRT_LOGF_DISABLE_EOL) )
serr << std::endl;
// Not sure, but it wasn't ever used.
SendLogLine(file, line, area, serr.str());
#endif
}
#else
template <class Arg>
inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, const Arg& arg ATR_UNUSED)
{
#ifdef ENABLE_LOGGING
std::ostringstream serr;
CreateLogLinePrefix(serr);
serr << arg;
if ( !isset(SRT_LOGF_DISABLE_EOL) )
serr << std::endl;
// Not sure, but it wasn't ever used.
SendLogLine(file, line, area, serr.str());
#endif
}
#endif
// SendLogLine can be compiled normally. It's intermediately used by:
// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING
// - PrintLogLine, which has empty body when !ENABLE_LOGGING
inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg)
{
src_config->lock();
if ( src_config->loghandler_fn )
{
(*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str());
}
else if ( src_config->log_stream )
{
(*src_config->log_stream) << msg;
(*src_config->log_stream).flush();
}
src_config->unlock();
}
}
#endif

View file

@ -0,0 +1,108 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__SRT_LOGGING_API_H
#define INC__SRT_LOGGING_API_H
// These are required for access functions:
// - adding FA (requires set)
// - setting a log stream (requires iostream)
#ifdef __cplusplus
#include <set>
#include <iostream>
#endif
#include <pthread.h>
#ifdef _WIN32
#include "win/syslog_defs.h"
#else
#include <syslog.h>
#endif
// Syslog is included so that it provides log level names.
// Haivision log standard requires the same names plus extra one:
#ifndef LOG_DEBUG_TRACE
#define LOG_DEBUG_TRACE 8
#endif
// It's unused anyway, just for the record.
#define SRT_LOG_LEVEL_MIN LOG_CRIT
#define SRT_LOG_LEVEL_MAX LOG_DEBUG
// Flags
#define SRT_LOGF_DISABLE_TIME 1
#define SRT_LOGF_DISABLE_THREADNAME 2
#define SRT_LOGF_DISABLE_SEVERITY 4
#define SRT_LOGF_DISABLE_EOL 8
// Handler type.
typedef void SRT_LOG_HANDLER_FN(void* opaque, int level, const char* file, int line, const char* area, const char* message);
#ifdef __cplusplus
namespace srt_logging
{
struct LogFA
{
private:
int value;
public:
operator int() const { return value; }
LogFA(int v): value(v)
{
// Generally this was what it has to be used for.
// Unfortunately it couldn't be agreed with the
//logging_fa_all.insert(v);
}
};
const LogFA LOGFA_GENERAL = 0;
namespace LogLevel
{
// There are 3 general levels:
// A. fatal - this means the application WILL crash.
// B. unexpected:
// - error: this was unexpected for the library
// - warning: this was expected by the library, but may be harmful for the application
// C. expected:
// - note: a significant, but rarely occurring event
// - debug: may occur even very often and enabling it can harm performance
enum type
{
fatal = LOG_CRIT,
// Fatal vs. Error: with Error, you can still continue.
error = LOG_ERR,
// Error vs. Warning: Warning isn't considered a problem for the library.
warning = LOG_WARNING,
// Warning vs. Note: Note means something unusual, but completely correct behavior.
note = LOG_NOTICE,
// Note vs. Debug: Debug may occur even multiple times in a millisecond.
// (Well, worth noting that Error and Warning potentially also can).
debug = LOG_DEBUG
};
}
class Logger;
}
#endif
#endif

381
trunk/3rdparty/srt-1-fit/srtcore/md5.cpp vendored Normal file
View file

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

91
trunk/3rdparty/srt-1-fit/srtcore/md5.h vendored Normal file
View file

@ -0,0 +1,91 @@
/*
Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
L. Peter Deutsch
ghost@aladdin.com
*/
/* $Id: md5.h,v 1.2 2007/12/24 05:58:37 lilyco Exp $ */
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.h is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Removed support for non-ANSI compilers; removed
references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>.
1999-05-03 lpd Original version.
*/
#ifndef md5_INCLUDED
# define md5_INCLUDED
/*
* This package supports both compile-time and run-time determination of CPU
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
* defined as non-zero, the code will be compiled to run only on big-endian
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
* run on either big- or little-endian CPUs, but will run slightly less
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
*/
typedef unsigned char md5_byte_t; /* 8-bit byte */
typedef unsigned int md5_word_t; /* 32-bit word */
/* Define the state of the MD5 Algorithm. */
typedef struct md5_state_s {
md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */
} md5_state_t;
#ifdef __cplusplus
extern "C"
{
#endif
/* Initialize the algorithm. */
void md5_init(md5_state_t *pms);
/* Append a string to the message. */
void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
/* Finish the message and return the digest. */
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
#ifdef __cplusplus
} /* end extern "C" */
#endif
#endif /* md5_INCLUDED */

View file

@ -0,0 +1,133 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__NETINET_ANY_H
#define INC__NETINET_ANY_H
#include <cstring>
#include "platform_sys.h"
// This structure should replace every use of sockaddr and its currently
// used specializations, sockaddr_in and sockaddr_in6. This is to simplify
// the use of the original BSD API that relies on type-violating type casts.
// You can use the instances of sockaddr_any in every place where sockaddr is
// required.
struct sockaddr_any
{
union
{
sockaddr_in sin;
sockaddr_in6 sin6;
sockaddr sa;
};
socklen_t len;
sockaddr_any(int domain = AF_INET)
{
memset(this, 0, sizeof *this);
sa.sa_family = domain;
len = size();
}
socklen_t size() const
{
switch (sa.sa_family)
{
case AF_INET: return socklen_t(sizeof sin);
case AF_INET6: return socklen_t(sizeof sin6);
default: return 0; // fallback, impossible
}
}
int family() const { return sa.sa_family; }
// port is in exactly the same location in both sin and sin6
// and has the same size. This is actually yet another common
// field, just not mentioned in the sockaddr structure.
uint16_t& r_port() { return sin.sin_port; }
uint16_t r_port() const { return sin.sin_port; }
int hport() const { return ntohs(sin.sin_port); }
void hport(int value)
{
// Port is fortunately located at the same position
// in both sockaddr_in and sockaddr_in6 and has the
// same size.
sin.sin_port = htons(value);
}
sockaddr* get() { return &sa; }
sockaddr* operator&() { return &sa; }
const sockaddr* get() const { return &sa; }
const sockaddr* operator&() const { return &sa; }
template <int> struct TypeMap;
template <int af_domain>
typename TypeMap<af_domain>::type& get();
struct Equal
{
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
{
return memcmp(&c1, &c2, sizeof(c1)) == 0;
}
};
struct EqualAddress
{
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
{
if ( c1.sa.sa_family == AF_INET )
{
return c1.sin.sin_addr.s_addr == c2.sin.sin_addr.s_addr;
}
if ( c1.sa.sa_family == AF_INET6 )
{
return memcmp(&c1.sin6.sin6_addr, &c2.sin6.sin6_addr, sizeof (in6_addr)) == 0;
}
return false;
}
};
bool equal_address(const sockaddr_any& rhs) const
{
return EqualAddress()(*this, rhs);
}
struct Less
{
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
{
return memcmp(&c1, &c2, sizeof(c1)) < 0;
}
};
};
template<> struct sockaddr_any::TypeMap<AF_INET> { typedef sockaddr_in type; };
template<> struct sockaddr_any::TypeMap<AF_INET6> { typedef sockaddr_in6 type; };
template <>
inline sockaddr_any::TypeMap<AF_INET>::type& sockaddr_any::get<AF_INET>() { return sin; }
template <>
inline sockaddr_any::TypeMap<AF_INET6>::type& sockaddr_any::get<AF_INET6>() { return sin6; }
#endif

View file

@ -0,0 +1,494 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 02/12/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Packet Header |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// ~ Data / Control Information Field ~
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0| Sequence Number |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |ff |o|kf |r| Message Number |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Time Stamp |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Destination Socket ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// bit 0:
// 0: Data Packet
// 1: Control Packet
// bit ff:
// 11: solo message packet
// 10: first packet of a message
// 01: last packet of a message
// bit o:
// 0: in order delivery not required
// 1: in order delivery required
// bit kf: HaiCrypt Key Flags
// 00: not encrypted
// 01: encrypted with even key
// 10: encrypted with odd key
// bit r: retransmission flag (set to 1 if this packet was sent again)
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |1| Type | Reserved |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Additional Info |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Time Stamp |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Destination Socket ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// bit 1-15: Message type -- see @a UDTMessageType
// 0: Protocol Connection Handshake (UMSG_HANDSHAKE}
// Add. Info: Undefined
// Control Info: Handshake information (see @a CHandShake)
// 1: Keep-alive (UMSG_KEEPALIVE)
// Add. Info: Undefined
// Control Info: None
// 2: Acknowledgement (UMSG_ACK)
// Add. Info: The ACK sequence number
// Control Info: The sequence number to which (but not include) all the previous packets have beed received
// Optional: RTT
// RTT Variance
// available receiver buffer size (in bytes)
// advertised flow window size (number of packets)
// estimated bandwidth (number of packets per second)
// 3: Negative Acknowledgement (UMSG_LOSSREPORT)
// Add. Info: Undefined
// Control Info: Loss list (see loss list coding below)
// 4: Congestion/Delay Warning (UMSG_CGWARNING)
// Add. Info: Undefined
// Control Info: None
// 5: Shutdown (UMSG_SHUTDOWN)
// Add. Info: Undefined
// Control Info: None
// 6: Acknowledgement of Acknowledement (UMSG_ACKACK)
// Add. Info: The ACK sequence number
// Control Info: None
// 7: Message Drop Request (UMSG_DROPREQ)
// Add. Info: Message ID
// Control Info: first sequence number of the message
// last seqeunce number of the message
// 8: Error Signal from the Peer Side (UMSG_PEERERROR)
// Add. Info: Error code
// Control Info: None
// 0x7FFF: Explained by bits 16 - 31 (UMSG_EXT)
//
// bit 16 - 31:
// This space is used for future expansion or user defined control packets.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |1| Sequence Number a (first) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0| Sequence Number b (last) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0| Sequence Number (single) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Loss List Field Coding:
// For any consectutive lost seqeunce numbers that the differnece between
// the last and first is more than 1, only record the first (a) and the
// the last (b) sequence numbers in the loss list field, and modify the
// the first bit of a to 1.
// For any single loss or consectutive loss less than 2 packets, use
// the original sequence numbers in the field.
#include <cstring>
#include "packet.h"
#include "logging.h"
namespace srt_logging
{
extern Logger mglog;
}
using namespace srt_logging;
// Set up the aliases in the constructure
CPacket::CPacket():
__pad(),
m_data_owned(false),
m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])),
m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])),
m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])),
m_iID((int32_t&)(m_nHeader[SRT_PH_ID])),
m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef()))
{
m_nHeader.clear();
// The part at PV_HEADER will be always set to a builtin buffer
// containing SRT header.
m_PacketVector[PV_HEADER].set(m_nHeader.raw(), HDR_SIZE);
// The part at PV_DATA is zero-initialized. It should be
// set (through m_pcData and setLength()) to some externally
// provided buffer before calling CChannel::sendto().
m_PacketVector[PV_DATA].set(NULL, 0);
}
void CPacket::allocate(size_t alloc_buffer_size)
{
m_PacketVector[PV_DATA].set(new char[alloc_buffer_size], alloc_buffer_size);
m_data_owned = true;
}
void CPacket::deallocate()
{
if (m_data_owned)
delete [] (char*)m_PacketVector[PV_DATA].data();
m_PacketVector[PV_DATA].set(NULL, 0);
}
CPacket::~CPacket()
{
// PV_HEADER is always owned, PV_DATA may use a "borrowed" buffer.
// Delete the internal buffer only if it was declared as owned.
if (m_data_owned)
delete[](char*)m_PacketVector[PV_DATA].data();
}
size_t CPacket::getLength() const
{
return m_PacketVector[PV_DATA].size();
}
void CPacket::setLength(size_t len)
{
m_PacketVector[PV_DATA].setLength(len);
}
void CPacket::pack(UDTMessageType pkttype, const void* lparam, void* rparam, int size)
{
// Set (bit-0 = 1) and (bit-1~15 = type)
setControl(pkttype);
// Set additional information and control information field
switch (pkttype)
{
case UMSG_ACK: //0010 - Acknowledgement (ACK)
// ACK packet seq. no.
if (NULL != lparam)
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
// data ACK seq. no.
// optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated link capacity (packets per second)
m_PacketVector[PV_DATA].set(rparam, size);
break;
case UMSG_ACKACK: //0110 - Acknowledgement of Acknowledgement (ACK-2)
// ACK packet seq. no.
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
break;
case UMSG_LOSSREPORT: //0011 - Loss Report (NAK)
// loss list
m_PacketVector[PV_DATA].set(rparam, size);
break;
case UMSG_CGWARNING: //0100 - Congestion Warning
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
break;
case UMSG_KEEPALIVE: //0001 - Keep-alive
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
break;
case UMSG_HANDSHAKE: //0000 - Handshake
// control info filed is handshake info
m_PacketVector[PV_DATA].set(rparam, size);
break;
case UMSG_SHUTDOWN: //0101 - Shutdown
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
break;
case UMSG_DROPREQ: //0111 - Message Drop Request
// msg id
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
//first seq no, last seq no
m_PacketVector[PV_DATA].set(rparam, size);
break;
case UMSG_PEERERROR: //1000 - Error Signal from the Peer Side
// Error type
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
break;
case UMSG_EXT: //0x7FFF - Reserved for user defined control packets
// for extended control packet
// "lparam" contains the extended type information for bit 16 - 31
// "rparam" is the control information
m_nHeader[SRT_PH_SEQNO] |= *(int32_t *)lparam;
if (NULL != rparam)
{
m_PacketVector[PV_DATA].set(rparam, size);
}
else
{
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
}
break;
default:
break;
}
}
IOVector* CPacket::getPacketVector()
{
return m_PacketVector;
}
UDTMessageType CPacket::getType() const
{
return UDTMessageType(SEQNO_MSGTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]));
}
int CPacket::getExtendedType() const
{
return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
}
int32_t CPacket::getAckSeqNo() const
{
// read additional information field
// This field is used only in UMSG_ACK and UMSG_ACKACK,
// so 'getAckSeqNo' symbolically defines the only use of it
// in case of CONTROL PACKET.
return m_nHeader[SRT_PH_MSGNO];
}
uint16_t CPacket::getControlFlags() const
{
// This returns exactly the "extended type" value,
// which is not used at all in case when the standard
// type message is interpreted. This can be used to pass
// additional special flags.
return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
}
PacketBoundary CPacket::getMsgBoundary() const
{
return PacketBoundary(MSGNO_PACKET_BOUNDARY::unwrap(m_nHeader[SRT_PH_MSGNO]));
}
bool CPacket::getMsgOrderFlag() const
{
return 0!= MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]);
}
int32_t CPacket::getMsgSeq(bool has_rexmit) const
{
if ( has_rexmit )
{
return MSGNO_SEQ::unwrap(m_nHeader[SRT_PH_MSGNO]);
}
else
{
return MSGNO_SEQ_OLD::unwrap(m_nHeader[SRT_PH_MSGNO]);
}
}
bool CPacket::getRexmitFlag() const
{
// return false; //
return 0 != MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]);
}
EncryptionKeySpec CPacket::getMsgCryptoFlags() const
{
return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(m_nHeader[SRT_PH_MSGNO]));
}
// This is required as the encryption/decryption happens in place.
// This is required to clear off the flags after decryption or set
// crypto flags after encrypting a packet.
void CPacket::setMsgCryptoFlags(EncryptionKeySpec spec)
{
int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask;
m_nHeader[SRT_PH_MSGNO] = clr_msgno | EncryptionKeyBits(spec);
}
/*
Leaving old code for historical reasons. This is moved to CSRTCC.
EncryptionStatus CPacket::encrypt(HaiCrypt_Handle hcrypto)
{
if ( !hcrypto )
{
LOGC(mglog.Error, log << "IPE: NULL crypto passed to CPacket::encrypt!");
return ENCS_FAILED;
}
int rc = HaiCrypt_Tx_Data(hcrypto, (uint8_t *)m_nHeader.raw(), (uint8_t *)m_pcData, m_PacketVector[PV_DATA].iov_len);
if ( rc < 0 )
{
// -1: encryption failure
// 0: key not received yet
return ENCS_FAILED;
} else if (rc > 0) {
m_PacketVector[PV_DATA].iov_len = rc;
}
return ENCS_CLEAR;
}
EncryptionStatus CPacket::decrypt(HaiCrypt_Handle hcrypto)
{
if (getMsgCryptoFlags() == EK_NOENC)
{
//HLOGC(mglog.Debug, log << "CPacket::decrypt: packet not encrypted");
return ENCS_CLEAR; // not encrypted, no need do decrypt, no flags to be modified
}
if (!hcrypto)
{
LOGC(mglog.Error, log << "IPE: NULL crypto passed to CPacket::decrypt!");
return ENCS_FAILED; // "invalid argument" (leave encryption flags untouched)
}
int rc = HaiCrypt_Rx_Data(hcrypto, (uint8_t *)m_nHeader.raw(), (uint8_t *)m_pcData, m_PacketVector[PV_DATA].iov_len);
if ( rc <= 0 )
{
// -1: decryption failure
// 0: key not received yet
return ENCS_FAILED;
}
// Otherwise: rc == decrypted text length.
m_PacketVector[PV_DATA].iov_len = rc; // In case clr txt size is different from cipher txt
// Decryption succeeded. Update flags.
m_nHeader[SRT_PH_MSGNO] &= ~MSGNO_ENCKEYSPEC::mask; // sets EK_NOENC to ENCKEYSPEC bits.
return ENCS_CLEAR;
}
*/
uint32_t CPacket::getMsgTimeStamp() const
{
// SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests
return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK;
}
CPacket* CPacket::clone() const
{
CPacket* pkt = new CPacket;
memcpy(pkt->m_nHeader, m_nHeader, HDR_SIZE);
pkt->m_pcData = new char[m_PacketVector[PV_DATA].size()];
memcpy(pkt->m_pcData, m_pcData, m_PacketVector[PV_DATA].size());
pkt->m_PacketVector[PV_DATA].setLength(m_PacketVector[PV_DATA].size());
return pkt;
}
// Useful for debugging
std::string PacketMessageFlagStr(uint32_t msgno_field)
{
using namespace std;
stringstream out;
static const char* const boundary [] = { "PB_SUBSEQUENT", "PB_LAST", "PB_FIRST", "PB_SOLO" };
static const char* const order [] = { "ORD_RELAXED", "ORD_REQUIRED" };
static const char* const crypto [] = { "EK_NOENC", "EK_EVEN", "EK_ODD", "EK*ERROR" };
static const char* const rexmit [] = { "SN_ORIGINAL", "SN_REXMIT" };
out << boundary[MSGNO_PACKET_BOUNDARY::unwrap(msgno_field)] << " ";
out << order[MSGNO_PACKET_INORDER::unwrap(msgno_field)] << " ";
out << crypto[MSGNO_ENCKEYSPEC::unwrap(msgno_field)] << " ";
out << rexmit[MSGNO_REXMIT::unwrap(msgno_field)];
return out.str();
}

View file

@ -0,0 +1,421 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 01/02/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_PACKET_H__
#define __UDT_PACKET_H__
#include "udt.h"
#include "common.h"
#include "utilities.h"
#include "packetfilter_api.h"
//////////////////////////////////////////////////////////////////////////////
// The purpose of the IOVector class is to proide a platform-independet interface
// to the WSABUF on Windows and iovec on Linux, that can be easilly converted
// to the native structure for use in WSARecvFrom() and recvmsg(...) functions
class IOVector
#ifdef _WIN32
: public WSABUF
#else
: public iovec
#endif
{
public:
inline void set(void *buffer, size_t length)
{
#ifdef _WIN32
len = (ULONG)length;
buf = (CHAR*)buffer;
#else
iov_base = (void*)buffer;
iov_len = length;
#endif
}
inline char*& dataRef()
{
#ifdef _WIN32
return buf;
#else
return (char*&) iov_base;
#endif
}
inline char* data()
{
#ifdef _WIN32
return buf;
#else
return (char*)iov_base;
#endif
}
inline size_t size() const
{
#ifdef _WIN32
return (size_t) len;
#else
return iov_len;
#endif
}
inline void setLength(size_t length)
{
#ifdef _WIN32
len = length;
#else
iov_len = length;
#endif
}
};
/// To define packets in order in the buffer. This is public due to being used in buffer.
enum PacketBoundary
{
PB_SUBSEQUENT = 0, // 00
/// 01: last packet of a message
PB_LAST = 1, // 01
/// 10: first packet of a message
PB_FIRST = 2, // 10
/// 11: solo message packet
PB_SOLO = 3, // 11
};
// Breakdown of the PM_SEQNO field in the header:
// C| X X ... X, where:
typedef Bits<31> SEQNO_CONTROL;
// 1|T T T T T T T T T T T T T T T|E E...E
typedef Bits<30, 16> SEQNO_MSGTYPE;
typedef Bits<15, 0> SEQNO_EXTTYPE;
// 0|S S ... S
typedef Bits<30, 0> SEQNO_VALUE;
// This bit cannot be used by SEQNO anyway, so it's additionally used
// in LOSSREPORT data specification to define that this value is the
// BEGIN value for a SEQNO range (to distinguish it from a SOLO loss SEQNO value).
const int32_t LOSSDATA_SEQNO_RANGE_FIRST = SEQNO_CONTROL::mask;
// Just cosmetics for readability.
const int32_t LOSSDATA_SEQNO_RANGE_LAST = 0, LOSSDATA_SEQNO_SOLO = 0;
inline int32_t CreateControlSeqNo(UDTMessageType type)
{
return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(size_t(type));
}
inline int32_t CreateControlExtSeqNo(int exttype)
{
return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(size_t(UMSG_EXT)) | SEQNO_EXTTYPE::wrap(exttype);
}
// MSGNO breakdown: B B|O|K K|R|M M M M M M M M M M...M
typedef Bits<31, 30> MSGNO_PACKET_BOUNDARY;
typedef Bits<29> MSGNO_PACKET_INORDER;
typedef Bits<28, 27> MSGNO_ENCKEYSPEC;
#if 1 // can block rexmit flag
// New bit breakdown - rexmit flag supported.
typedef Bits<26> MSGNO_REXMIT;
typedef Bits<25, 0> MSGNO_SEQ;
// Old bit breakdown - no rexmit flag
typedef Bits<26, 0> MSGNO_SEQ_OLD;
// This symbol is for older SRT version, where the peer does not support the MSGNO_REXMIT flag.
// The message should be extracted as PMASK_MSGNO_SEQ, if REXMIT is supported, and PMASK_MSGNO_SEQ_OLD otherwise.
const uint32_t PACKET_SND_NORMAL = 0, PACKET_SND_REXMIT = MSGNO_REXMIT::mask;
#else
// Old bit breakdown - no rexmit flag
typedef Bits<26, 0> MSGNO_SEQ;
#endif
// constexpr in C++11 !
inline int32_t PacketBoundaryBits(PacketBoundary o) { return MSGNO_PACKET_BOUNDARY::wrap(int32_t(o)); }
enum EncryptionKeySpec
{
EK_NOENC = 0,
EK_EVEN = 1,
EK_ODD = 2
};
enum EncryptionStatus
{
ENCS_CLEAR = 0,
ENCS_FAILED = -1,
ENCS_NOTSUP = -2
};
const int32_t PMASK_MSGNO_ENCKEYSPEC = MSGNO_ENCKEYSPEC::mask;
inline int32_t EncryptionKeyBits(EncryptionKeySpec f)
{
return MSGNO_ENCKEYSPEC::wrap(int32_t(f));
}
inline EncryptionKeySpec GetEncryptionKeySpec(int32_t msgno)
{
return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(msgno));
}
const int32_t PUMASK_SEQNO_PROBE = 0xF;
std::string PacketMessageFlagStr(uint32_t msgno_field);
class CChannel;
class CPacket
{
friend class CChannel;
friend class CSndQueue;
friend class CRcvQueue;
public:
CPacket();
~CPacket();
void allocate(size_t size);
void deallocate();
/// Get the payload or the control information field length.
/// @return the payload or the control information field length.
size_t getLength() const;
/// Set the payload or the control information field length.
/// @param len [in] the payload or the control information field length.
void setLength(size_t len);
/// Pack a Control packet.
/// @param pkttype [in] packet type filed.
/// @param lparam [in] pointer to the first data structure, explained by the packet type.
/// @param rparam [in] pointer to the second data structure, explained by the packet type.
/// @param size [in] size of rparam, in number of bytes;
void pack(UDTMessageType pkttype, const void* lparam = NULL, void* rparam = NULL, int size = 0);
/// Read the packet vector.
/// @return Pointer to the packet vector.
IOVector* getPacketVector();
uint32_t* getHeader() { return m_nHeader; }
/// Read the packet flag.
/// @return packet flag (0 or 1).
// XXX DEPRECATED. Use isControl() instead
ATR_DEPRECATED
int getFlag() const
{
return isControl() ? 1 : 0;
}
/// Read the packet type.
/// @return packet type filed (000 ~ 111).
UDTMessageType getType() const;
bool isControl(UDTMessageType type) const
{
return isControl() && type == getType();
}
bool isControl() const
{
// read bit 0
return 0!= SEQNO_CONTROL::unwrap(m_nHeader[SRT_PH_SEQNO]);
}
void setControl(UDTMessageType type)
{
m_nHeader[SRT_PH_SEQNO] = SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(type);
}
/// Read the extended packet type.
/// @return extended packet type filed (0x000 ~ 0xFFF).
int getExtendedType() const;
/// Read the ACK-2 seq. no.
/// @return packet header field (bit 16~31).
int32_t getAckSeqNo() const;
uint16_t getControlFlags() const;
// Note: this will return a "singular" value, if the packet
// contains the control message
int32_t getSeqNo() const
{
return m_nHeader[SRT_PH_SEQNO];
}
/// Read the message boundary flag bit.
/// @return packet header field [1] (bit 0~1).
PacketBoundary getMsgBoundary() const;
/// Read the message inorder delivery flag bit.
/// @return packet header field [1] (bit 2).
bool getMsgOrderFlag() const;
/// Read the rexmit flag (true if the packet was sent due to retransmission).
/// If the peer does not support retransmission flag, the current agent cannot use it as well
/// (because the peer will understand this bit as a part of MSGNO field).
bool getRexmitFlag() const;
/// Read the message sequence number.
/// @return packet header field [1]
int32_t getMsgSeq(bool has_rexmit = true) const;
/// Read the message crypto key bits.
/// @return packet header field [1] (bit 3~4).
EncryptionKeySpec getMsgCryptoFlags() const;
void setMsgCryptoFlags(EncryptionKeySpec spec);
/// Read the message time stamp.
/// @return packet header field [2] (bit 0~31, bit 0-26 if SRT_DEBUG_TSBPD_WRAP).
uint32_t getMsgTimeStamp() const;
#ifdef SRT_DEBUG_TSBPD_WRAP //Receiver
static const uint32_t MAX_TIMESTAMP = 0x07FFFFFF; //27 bit fast wraparound for tests (~2m15s)
#else
static const uint32_t MAX_TIMESTAMP = 0xFFFFFFFF; //Full 32 bit (01h11m35s)
#endif
protected:
static const uint32_t TIMESTAMP_MASK = MAX_TIMESTAMP; // this value to be also used as a mask
public:
/// Clone this packet.
/// @return Pointer to the new packet.
CPacket* clone() const;
enum PacketVectorFields
{
PV_HEADER = 0,
PV_DATA = 1,
PV_SIZE = 2
};
protected:
// Length in bytes
// DynamicStruct is the same as array of given type and size, just it
// enforces that you index it using a symbol from symbolic enum type, not by a bare integer.
typedef DynamicStruct<uint32_t, SRT_PH__SIZE, SrtPktHeaderFields> HEADER_TYPE;
HEADER_TYPE m_nHeader; //< The 128-bit header field
// XXX NOTE: iovec here is not portable. On Windows there's a different
// (although similar) structure defined, which means that this way the
// Windows function that is an equivalent of `recvmsg` cannot be used.
// For example, something like that:
// class IoVector: public iovec { public: size_t size() { return iov_len; } char* data() { return iov_base; } };
// class IoVector: public WSAMSG { public: size_t size() { return len; } char* data() { return buf; } };
IOVector m_PacketVector[PV_SIZE]; //< The 2-demension vector of UDT packet [header, data]
int32_t __pad;
bool m_data_owned;
protected:
CPacket& operator=(const CPacket&);
CPacket (const CPacket&);
public:
int32_t& m_iSeqNo; // alias: sequence number
int32_t& m_iMsgNo; // alias: message number
int32_t& m_iTimeStamp; // alias: timestamp
int32_t& m_iID; // alias: socket ID
char*& m_pcData; // alias: data/control information
//static const int m_iPktHdrSize; // packet header size
static const size_t HDR_SIZE = sizeof(HEADER_TYPE); // packet header size = SRT_PH__SIZE * sizeof(uint32_t)
// Used in many computations
// Actually this can be also calculated as: sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr).
static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }.
static const size_t SRT_DATA_HDR_SIZE = UDP_HDR_SIZE + HDR_SIZE;
// Some well known data
static const size_t ETH_MAX_MTU_SIZE = 1500;
// And derived
static const size_t SRT_MAX_PAYLOAD_SIZE = ETH_MAX_MTU_SIZE - SRT_DATA_HDR_SIZE;
// Packet interface
char* data() { return m_pcData; }
const char* data() const { return m_pcData; }
size_t size() const { return getLength(); }
uint32_t header(SrtPktHeaderFields field) const { return m_nHeader[field]; }
std::string MessageFlagStr()
#if ENABLE_LOGGING
{ return PacketMessageFlagStr(m_nHeader[SRT_PH_MSGNO]); }
#else
{ return ""; }
#endif
};
#endif

View file

@ -0,0 +1,294 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#include <string>
#include <map>
#include <vector>
#include <deque>
#include <iterator>
#include "packetfilter.h"
#include "packetfilter_builtin.h"
#include "core.h"
#include "packet.h"
#include "logging.h"
using namespace std;
using namespace srt_logging;
bool ParseFilterConfig(std::string s, SrtFilterConfig& out)
{
vector<string> parts;
Split(s, ',', back_inserter(parts));
out.type = parts[0];
PacketFilter::Factory* fac = PacketFilter::find(out.type);
if (!fac)
return false;
for (vector<string>::iterator i = parts.begin()+1; i != parts.end(); ++i)
{
vector<string> keyval;
Split(*i, ':', back_inserter(keyval));
if (keyval.size() != 2)
return false;
out.parameters[keyval[0]] = keyval[1];
}
// Extract characteristic data
out.extra_size = fac->ExtraSize();
return true;
}
struct SortBySequence
{
bool operator()(const CUnit* u1, const CUnit* u2)
{
int32_t s1 = u1->m_Packet.getSeqNo();
int32_t s2 = u2->m_Packet.getSeqNo();
return CSeqNo::seqcmp(s1, s2) < 0;
}
};
void PacketFilter::receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming, ref_t<loss_seqs_t> r_loss_seqs)
{
const CPacket& rpkt = unit->m_Packet;
if (m_filter->receive(rpkt, *r_loss_seqs))
{
// For the sake of rebuilding MARK THIS UNIT GOOD, otherwise the
// unit factory will supply it from getNextAvailUnit() as if it were not in use.
unit->m_iFlag = CUnit::GOOD;
HLOGC(mglog.Debug, log << "FILTER: PASSTHRU current packet %" << unit->m_Packet.getSeqNo());
r_incoming.get().push_back(unit);
}
else
{
// Packet not to be passthru, update stats
CGuard lg(m_parent->m_StatsLock);
++m_parent->m_stats.rcvFilterExtra;
++m_parent->m_stats.rcvFilterExtraTotal;
}
// r_loss_seqs enters empty into this function and can be only filled here.
for (loss_seqs_t::iterator i = r_loss_seqs.get().begin();
i != r_loss_seqs.get().end(); ++i)
{
// Sequences here are low-high, if there happens any negative distance
// here, simply skip and report IPE.
int dist = CSeqNo::seqoff(i->first, i->second) + 1;
if (dist > 0)
{
CGuard lg(m_parent->m_StatsLock);
m_parent->m_stats.rcvFilterLoss += dist;
m_parent->m_stats.rcvFilterLossTotal += dist;
}
else
{
LOGC(mglog.Error, log << "FILTER: IPE: loss record: invalid loss: %"
<< i->first << " - %" << i->second);
}
}
// Pack first recovered packets, if any.
if (!m_provided.empty())
{
HLOGC(mglog.Debug, log << "FILTER: inserting REBUILT packets (" << m_provided.size() << "):");
size_t nsupply = m_provided.size();
InsertRebuilt(*r_incoming, m_unitq);
CGuard lg(m_parent->m_StatsLock);
m_parent->m_stats.rcvFilterSupply += nsupply;
m_parent->m_stats.rcvFilterSupplyTotal += nsupply;
}
// Now that all units have been filled as they should be,
// SET THEM ALL FREE. This is because now it's up to the
// buffer to decide as to whether it wants them or not.
// Wanted units will be set GOOD flag, unwanted will remain
// with FREE and therefore will be returned at the next
// call to getNextAvailUnit().
unit->m_iFlag = CUnit::FREE;
vector<CUnit*>& inco = *r_incoming;
for (vector<CUnit*>::iterator i = inco.begin(); i != inco.end(); ++i)
{
CUnit* u = *i;
u->m_iFlag = CUnit::FREE;
}
// Packets must be sorted by sequence number, ascending, in order
// not to challenge the SRT's contiguity checker.
sort(inco.begin(), inco.end(), SortBySequence());
// For now, report immediately the irrecoverable packets
// from the row.
// Later, the `irrecover_row` or `irrecover_col` will be
// reported only, depending on level settings. For example,
// with default LATELY level, packets will be reported as
// irrecoverable only when they are irrecoverable in the
// vertical group.
// With "always", do not report any losses, SRT will simply check
// them itself.
return;
}
bool PacketFilter::packControlPacket(ref_t<CPacket> r_packet, int32_t seq, int kflg)
{
bool have = m_filter->packControlPacket(m_sndctlpkt, seq);
if (!have)
return false;
// Now this should be repacked back to CPacket.
// The header must be copied, it's always part of CPacket.
uint32_t* hdr = r_packet.get().getHeader();
memcpy(hdr, m_sndctlpkt.hdr, SRT_PH__SIZE * sizeof(*hdr));
// The buffer can be assigned.
r_packet.get().m_pcData = m_sndctlpkt.buffer;
r_packet.get().setLength(m_sndctlpkt.length);
// This sets only the Packet Boundary flags, while all other things:
// - Order
// - Rexmit
// - Crypto
// - Message Number
// will be set to 0/false
r_packet.get().m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
// ... and then fix only the Crypto flags
r_packet.get().setMsgCryptoFlags(EncryptionKeySpec(kflg));
// Don't set the ID, it will be later set for any kind of packet.
// Write the timestamp clip into the timestamp field.
return true;
}
void PacketFilter::InsertRebuilt(vector<CUnit*>& incoming, CUnitQueue* uq)
{
if (m_provided.empty())
return;
for (vector<SrtPacket>::iterator i = m_provided.begin(); i != m_provided.end(); ++i)
{
CUnit* u = uq->getNextAvailUnit();
if (!u)
{
LOGC(mglog.Error, log << "FILTER: LOCAL STORAGE DEPLETED. Can't return rebuilt packets.");
break;
}
// LOCK the unit as GOOD because otherwise the next
// call to getNextAvailUnit will return THE SAME UNIT.
u->m_iFlag = CUnit::GOOD;
// After returning from this function, all units will be
// set back to FREE so that the buffer can decide whether
// it wants them or not.
CPacket& packet = u->m_Packet;
memcpy(packet.getHeader(), i->hdr, CPacket::HDR_SIZE);
memcpy(packet.m_pcData, i->buffer, i->length);
packet.setLength(i->length);
HLOGC(mglog.Debug, log << "FILTER: PROVIDING rebuilt packet %" << packet.getSeqNo());
incoming.push_back(u);
}
m_provided.clear();
}
bool PacketFilter::IsBuiltin(const string& s)
{
return builtin_filters.count(s);
}
std::set<std::string> PacketFilter::builtin_filters;
PacketFilter::filters_map_t PacketFilter::filters;
PacketFilter::Factory::~Factory()
{
}
void PacketFilter::globalInit()
{
// Add here builtin packet filters and mark them
// as builtin. This will disallow users to register
// external filters with the same name.
filters["fec"] = new Creator<FECFilterBuiltin>;
builtin_filters.insert("fec");
}
bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr)
{
m_parent = parent;
SrtFilterConfig cfg;
if (!ParseFilterConfig(confstr, cfg))
return false;
// Extract the "type" key from parameters, or use
// builtin if lacking.
filters_map_t::iterator selector = filters.find(cfg.type);
if (selector == filters.end())
return false;
SrtFilterInitializer init;
init.socket_id = parent->socketID();
init.snd_isn = parent->sndSeqNo();
init.rcv_isn = parent->rcvSeqNo();
init.payload_size = parent->OPT_PayloadSize();
// Found a filter, so call the creation function
m_filter = selector->second->Create(init, m_provided, confstr);
if (!m_filter)
return false;
m_unitq = uq;
// The filter should have pinned in all events
// that are of its interest. It's stated that
// it's ready after creation.
return true;
}
bool PacketFilter::correctConfig(const SrtFilterConfig& conf)
{
const string* pname = map_getp(conf.parameters, "type");
if (!pname)
return true; // default, parameters ignored
if (*pname == "adaptive")
return true;
filters_map_t::iterator x = filters.find(*pname);
if (x == filters.end())
return false;
return true;
}
PacketFilter::~PacketFilter()
{
delete m_filter;
}

View file

@ -0,0 +1,198 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#ifndef INC__PACKETFILTER_H
#define INC__PACKETFILTER_H
#include <cstdlib>
#include <map>
#include <string>
#include "packet.h"
#include "queue.h"
#include "utilities.h"
#include "packetfilter_api.h"
class PacketFilter
{
friend class SrtPacketFilterBase;
public:
typedef std::vector< std::pair<int32_t, int32_t> > loss_seqs_t;
typedef SrtPacketFilterBase* filter_create_t(const SrtFilterInitializer& init, std::vector<SrtPacket>&, const std::string& config);
private:
friend bool ParseFilterConfig(std::string s, SrtFilterConfig& out);
class Factory
{
public:
virtual SrtPacketFilterBase* Create(const SrtFilterInitializer& init, std::vector<SrtPacket>& provided, const std::string& confstr) = 0;
// Characteristic data
virtual size_t ExtraSize() = 0;
virtual ~Factory();
};
template <class Target>
class Creator: public Factory
{
virtual SrtPacketFilterBase* Create(const SrtFilterInitializer& init,
std::vector<SrtPacket>& provided,
const std::string& confstr) ATR_OVERRIDE
{ return new Target(init, provided, confstr); }
// Import the extra size data
virtual size_t ExtraSize() ATR_OVERRIDE { return Target::EXTRA_SIZE; }
public:
Creator() {}
virtual ~Creator() {}
};
// We need a private wrapper for the auto-pointer, can't use
// std::unique_ptr here due to no C++11.
struct ManagedPtr
{
Factory* f;
mutable bool owns;
// Accept whatever
ManagedPtr(Factory* ff): f(ff), owns(true) {}
ManagedPtr(): f(NULL), owns(false) {}
~ManagedPtr()
{
if (owns)
delete f;
}
void copy_internal(const ManagedPtr& other)
{
other.owns = false;
f = other.f;
owns = true;
}
ManagedPtr(const ManagedPtr& other)
{
copy_internal(other);
}
void operator=(const ManagedPtr& other)
{
if (owns)
delete f;
copy_internal(other);
}
Factory* operator->() { return f; }
Factory* get() { return f; }
};
// The list of builtin names that are reserved.
static std::set<std::string> builtin_filters;
// Temporarily changed to linear searching, until this is exposed
// for a user-defined filter.
typedef std::map<std::string, ManagedPtr> filters_map_t;
static filters_map_t filters;
// This is a filter container.
SrtPacketFilterBase* m_filter;
void Check()
{
#if ENABLE_DEBUG
if (!m_filter)
abort();
#endif
// Don't do any check for now.
}
public:
static void globalInit();
static bool IsBuiltin(const std::string&);
template <class NewFilter>
static bool add(const std::string& name)
{
if (IsBuiltin(name))
return false;
filters[name] = new Creator<NewFilter>;
return true;
}
static Factory* find(const std::string& type)
{
filters_map_t::iterator i = filters.find(type);
if (i == filters.end())
return NULL; // No matter what to return - this is "undefined behavior" to be prevented
return i->second.get();
}
// Filter is optional, so this check should be done always
// manually.
bool installed() const { return m_filter; }
operator bool() const { return installed(); }
SrtPacketFilterBase* operator->() { Check(); return m_filter; }
// In the beginning it's initialized as first, builtin default.
// Still, it will be created only when requested.
PacketFilter(): m_filter(), m_parent(), m_sndctlpkt(0), m_unitq() {}
// Copy constructor - important when listener-spawning
// Things being done:
// 1. The filter is individual, so don't copy it. Set NULL.
// 2. This will be configued anyway basing on possibly a new rule set.
PacketFilter(const PacketFilter& source SRT_ATR_UNUSED): m_filter(), m_sndctlpkt(0), m_unitq() {}
// This function will be called by the parent CUDT
// in appropriate time. It should select appropriate
// filter basing on the value in selector, then
// pin oneself in into CUDT for receiving event signals.
bool configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr);
static bool correctConfig(const SrtFilterConfig& c);
// Will delete the pinned in filter object.
// This must be defined in *.cpp file due to virtual
// destruction.
~PacketFilter();
// Simple wrappers
void feedSource(ref_t<CPacket> r_packet);
SRT_ARQLevel arqLevel();
bool packControlPacket(ref_t<CPacket> r_packet, int32_t seq, int kflg);
void receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming, ref_t<loss_seqs_t> r_loss_seqs);
protected:
void InsertRebuilt(std::vector<CUnit*>& incoming, CUnitQueue* uq);
CUDT* m_parent;
// Sender part
SrtPacket m_sndctlpkt;
// Receiver part
CUnitQueue* m_unitq;
std::vector<SrtPacket> m_provided;
};
inline void PacketFilter::feedSource(ref_t<CPacket> r_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource(*r_packet); }
inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_filter->arqLevel(); }
#endif

View file

@ -0,0 +1,140 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#ifndef INC__PACKETFILTER_API_H
#define INC__PACKETFILTER_API_H
enum SrtPktHeaderFields
{
SRT_PH_SEQNO = 0, //< sequence number
SRT_PH_MSGNO = 1, //< message number
SRT_PH_TIMESTAMP = 2, //< time stamp
SRT_PH_ID = 3, //< socket ID
// Must be the last value - this is size of all, not a field id
SRT_PH__SIZE
};
enum SRT_ARQLevel
{
SRT_ARQ_NEVER, //< Never send LOSSREPORT
SRT_ARQ_ONREQ, //< Only record the loss, but report only those that are returned in receive()
SRT_ARQ_ALWAYS, //< always send LOSSREPORT immediately after detecting a loss
};
struct SrtFilterConfig
{
std::string type;
std::map<std::string, std::string> parameters;
size_t extra_size; // needed for filter option check against payload size
};
struct SrtFilterInitializer
{
SRTSOCKET socket_id;
int32_t snd_isn;
int32_t rcv_isn;
size_t payload_size;
};
struct SrtPacket
{
uint32_t hdr[SRT_PH__SIZE];
char buffer[SRT_LIVE_MAX_PLSIZE];
size_t length;
SrtPacket(size_t size): length(size)
{
memset(hdr, 0, sizeof(hdr));
}
uint32_t header(SrtPktHeaderFields field) { return hdr[field]; }
char* data() { return buffer; }
const char* data() const { return buffer; }
size_t size() const { return length; }
};
bool ParseFilterConfig(std::string s, SrtFilterConfig& out);
class SrtPacketFilterBase
{
SrtFilterInitializer initParams;
protected:
SRTSOCKET socketID() const { return initParams.socket_id; }
int32_t sndISN() const { return initParams.snd_isn; }
int32_t rcvISN() const { return initParams.rcv_isn; }
size_t payloadSize() const { return initParams.payload_size; }
friend class PacketFilter;
// Beside the size of the rows, special values:
// 0: if you have 0 specified for rows, there are only columns
// -1: Only during the handshake, use the value specified by peer.
// -N: The N value still specifies the size, but in particular
// dimension there is no filter control packet formed nor expected.
public:
typedef std::vector< std::pair<int32_t, int32_t> > loss_seqs_t;
protected:
SrtPacketFilterBase(const SrtFilterInitializer& i): initParams(i)
{
}
// Sender side
/// This function creates and stores the filter control packet with
/// a prediction to be immediately sent. This is called in the function
/// that normally is prepared for extracting a data packet from the sender
/// buffer and send it over the channel. The returned value informs the
/// caller whether the control packet was available and therefore provided.
/// @param [OUT] packet Target place where the packet should be stored
/// @param [IN] seq Sequence number of the packet last requested for sending
/// @return true if the control packet has been provided
virtual bool packControlPacket(SrtPacket& packet, int32_t seq) = 0;
/// This is called at the moment when the sender queue decided to pick up
/// a new packet from the scheduled packets. This should be then used to
/// continue filling the group, possibly followed by final calculating the
/// control packet ready to send. The packet received by this function is
/// potentially allowed to be modified.
/// @param [INOUT] packet The packet about to send
virtual void feedSource(CPacket& packet) = 0;
// Receiver side
// This function is called at the moment when a new data packet has
// arrived (no matter if subsequent or recovered). The 'state' value
// defines the configured level of loss state required to send the
// loss report.
virtual bool receive(const CPacket& pkt, loss_seqs_t& loss_seqs) = 0;
// Backward configuration.
// This should have some stable value after the configuration is parsed,
// and it should be a stable value set ONCE, after the filter module is ready.
virtual SRT_ARQLevel arqLevel() = 0;
virtual ~SrtPacketFilterBase()
{
}
};
#endif

View file

@ -0,0 +1,18 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#ifndef INC__PACKETFILTER_BUILTIN_H
#define INC__PACKETFILTER_BUILTIN_H
// Integration header
#include "fec.h"
#endif

View file

@ -0,0 +1,34 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#ifndef INC__PLATFORM_SYS_H
#define INC__PLATFORM_SYS_H
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <ws2ipdef.h>
#include <windows.h>
#include <stdint.h>
#include <inttypes.h>
#if defined(_MSC_VER)
#pragma warning(disable:4251)
#endif
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#endif
#endif

1684
trunk/3rdparty/srt-1-fit/srtcore/queue.cpp vendored Normal file

File diff suppressed because it is too large Load diff

548
trunk/3rdparty/srt-1-fit/srtcore/queue.h vendored Normal file
View file

@ -0,0 +1,548 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 01/12/2011
modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_QUEUE_H__
#define __UDT_QUEUE_H__
#include "channel.h"
#include "common.h"
#include "packet.h"
#include "netinet_any.h"
#include "utilities.h"
#include <list>
#include <map>
#include <queue>
#include <vector>
class CUDT;
struct CUnit
{
CPacket m_Packet; // packet
enum Flag { FREE = 0, GOOD = 1, PASSACK = 2, DROPPED = 3 };
Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped
};
class CUnitQueue
{
public:
CUnitQueue();
~CUnitQueue();
public: // Storage size operations
/// Initialize the unit queue.
/// @param [in] size queue size
/// @param [in] mss maximum segment size
/// @param [in] version IP version
/// @return 0: success, -1: failure.
int init(int size, int mss, int version);
/// Increase (double) the unit queue size.
/// @return 0: success, -1: failure.
int increase();
/// Decrease (halve) the unit queue size.
/// @return 0: success, -1: failure.
int shrink();
public: // Operations on units
/// find an available unit for incoming packet.
/// @return Pointer to the available unit, NULL if not found.
CUnit* getNextAvailUnit();
void makeUnitFree(CUnit * unit);
void makeUnitGood(CUnit * unit);
public:
inline int getIPversion() const { return m_iIPversion; }
private:
struct CQEntry
{
CUnit* m_pUnit; // unit queue
char* m_pBuffer; // data buffer
int m_iSize; // size of each queue
CQEntry* m_pNext;
}
*m_pQEntry, // pointer to the first unit queue
*m_pCurrQueue, // pointer to the current available queue
*m_pLastQueue; // pointer to the last unit queue
CUnit* m_pAvailUnit; // recent available unit
int m_iSize; // total size of the unit queue, in number of packets
int m_iCount; // total number of valid packets in the queue
int m_iMSS; // unit buffer size
int m_iIPversion; // IP version
private:
CUnitQueue(const CUnitQueue&);
CUnitQueue& operator=(const CUnitQueue&);
};
struct CSNode
{
CUDT* m_pUDT; // Pointer to the instance of CUDT socket
uint64_t m_llTimeStamp_tk; // Time Stamp
int m_iHeapLoc; // location on the heap, -1 means not on the heap
};
class CSndUList
{
friend class CSndQueue;
public:
CSndUList();
~CSndUList();
public:
enum EReschedule { DONT_RESCHEDULE = 0, DO_RESCHEDULE = 1 };
static EReschedule rescheduleIf(bool cond) { return cond ? DO_RESCHEDULE : DONT_RESCHEDULE; }
/// Update the timestamp of the UDT instance on the list.
/// @param [in] u pointer to the UDT instance
/// @param [in] reschedule if the timestamp should be rescheduled
void update(const CUDT* u, EReschedule reschedule);
/// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue.
/// @param [out] addr destination address of the next packet
/// @param [out] pkt the next packet to be sent
/// @return 1 if successfully retrieved, -1 if no packet found.
int pop(sockaddr*& addr, CPacket& pkt);
/// Remove UDT instance from the list.
/// @param [in] u pointer to the UDT instance
void remove(const CUDT* u);
/// Retrieve the next scheduled processing time.
/// @return Scheduled processing time of the first UDT socket in the list.
uint64_t getNextProcTime();
private:
/// Doubles the size of the list.
///
void realloc_();
/// Insert a new UDT instance into the list with realloc if required.
///
/// @param [in] ts time stamp: next processing time
/// @param [in] u pointer to the UDT instance
void insert_(int64_t ts, const CUDT* u);
/// Insert a new UDT instance into the list without realloc.
/// Should be called if there is a gauranteed space for the element.
///
/// @param [in] ts time stamp: next processing time
/// @param [in] u pointer to the UDT instance
void insert_norealloc_(int64_t ts, const CUDT* u);
void remove_(const CUDT* u);
private:
CSNode** m_pHeap; // The heap array
int m_iArrayLength; // physical length of the array
int m_iLastEntry; // position of last entry on the heap array
pthread_mutex_t m_ListLock;
pthread_mutex_t* m_pWindowLock;
pthread_cond_t* m_pWindowCond;
CTimer* m_pTimer;
private:
CSndUList(const CSndUList&);
CSndUList& operator=(const CSndUList&);
};
struct CRNode
{
CUDT* m_pUDT; // Pointer to the instance of CUDT socket
uint64_t m_llTimeStamp_tk; // Time Stamp
CRNode* m_pPrev; // previous link
CRNode* m_pNext; // next link
bool m_bOnList; // if the node is already on the list
};
class CRcvUList
{
public:
CRcvUList();
~CRcvUList();
public:
/// Insert a new UDT instance to the list.
/// @param [in] u pointer to the UDT instance
void insert(const CUDT* u);
/// Remove the UDT instance from the list.
/// @param [in] u pointer to the UDT instance
void remove(const CUDT* u);
/// Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing.
/// @param [in] u pointer to the UDT instance
void update(const CUDT* u);
public:
CRNode* m_pUList; // the head node
private:
CRNode* m_pLast; // the last node
private:
CRcvUList(const CRcvUList&);
CRcvUList& operator=(const CRcvUList&);
};
class CHash
{
public:
CHash();
~CHash();
public:
/// Initialize the hash table.
/// @param [in] size hash table size
void init(int size);
/// Look for a UDT instance from the hash table.
/// @param [in] id socket ID
/// @return Pointer to a UDT instance, or NULL if not found.
CUDT* lookup(int32_t id);
/// Insert an entry to the hash table.
/// @param [in] id socket ID
/// @param [in] u pointer to the UDT instance
void insert(int32_t id, CUDT* u);
/// Remove an entry from the hash table.
/// @param [in] id socket ID
void remove(int32_t id);
private:
struct CBucket
{
int32_t m_iID; // Socket ID
CUDT* m_pUDT; // Socket instance
CBucket* m_pNext; // next bucket
} **m_pBucket; // list of buckets (the hash table)
int m_iHashSize; // size of hash table
private:
CHash(const CHash&);
CHash& operator=(const CHash&);
};
class CRendezvousQueue
{
public:
CRendezvousQueue();
~CRendezvousQueue();
public:
void insert(const SRTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl);
// The should_lock parameter is given here to state as to whether
// the lock should be applied here. If called from some internals
// and the lock IS ALREADY APPLIED, use false here to prevent
// double locking and deadlock in result.
void remove(const SRTSOCKET& id, bool should_lock);
CUDT* retrieve(const sockaddr* addr, ref_t<SRTSOCKET> id);
void updateConnStatus(EReadStatus rst, EConnectStatus, const CPacket& response);
private:
struct CRL
{
SRTSOCKET m_iID; // UDT socket ID (self)
CUDT* m_pUDT; // UDT instance
int m_iIPversion; // IP version
sockaddr* m_pPeerAddr; // UDT sonnection peer address
uint64_t m_ullTTL; // the time that this request expires
};
std::list<CRL> m_lRendezvousID; // The sockets currently in rendezvous mode
pthread_mutex_t m_RIDVectorLock;
};
class CSndQueue
{
friend class CUDT;
friend class CUDTUnited;
public:
CSndQueue();
~CSndQueue();
public:
// XXX There's currently no way to access the socket ID set for
// whatever the queue is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
/// Initialize the sending queue.
/// @param [in] c UDP channel to be associated to the queue
/// @param [in] t Timer
void init(CChannel* c, CTimer* t);
/// Send out a packet to a given address.
/// @param [in] addr destination address
/// @param [in] packet packet to be sent out
/// @return Size of data sent out.
int sendto(const sockaddr* addr, CPacket& packet);
#ifdef SRT_ENABLE_IPOPTS
/// Get the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return TTL.
int getIpTTL() const;
/// Get the IP Type of Service.
/// @return ToS.
int getIpToS() const;
#endif
int ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); }
int sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); }
void setClosing()
{
m_bClosing = true;
}
private:
static void* worker(void* param);
pthread_t m_WorkerThread;
private:
CSndUList* m_pSndUList; // List of UDT instances for data sending
CChannel* m_pChannel; // The UDP channel for data sending
CTimer* m_pTimer; // Timing facility
pthread_mutex_t m_WindowLock;
pthread_cond_t m_WindowCond;
volatile bool m_bClosing; // closing the worker
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)//>>debug high freq worker
uint64_t m_ullDbgPeriod;
uint64_t m_ullDbgTime;
struct {
unsigned long lIteration; //
unsigned long lSleepTo; //SleepTo
unsigned long lNotReadyPop; //Continue
unsigned long lSendTo;
unsigned long lNotReadyTs;
unsigned long lCondWait; //block on m_WindowCond
} m_WorkerStats;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
private:
CSndQueue(const CSndQueue&);
CSndQueue& operator=(const CSndQueue&);
};
class CRcvQueue
{
friend class CUDT;
friend class CUDTUnited;
public:
CRcvQueue();
~CRcvQueue();
public:
// XXX There's currently no way to access the socket ID set for
// whatever the queue is currently working. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
/// Initialize the receiving queue.
/// @param [in] size queue size
/// @param [in] mss maximum packet size
/// @param [in] version IP version
/// @param [in] hsize hash table size
/// @param [in] c UDP channel to be associated to the queue
/// @param [in] t timer
void init(int size, int payload, int version, int hsize, CChannel* c, CTimer* t);
/// Read a packet for a specific UDT socket id.
/// @param [in] id Socket ID
/// @param [out] packet received packet
/// @return Data size of the packet
int recvfrom(int32_t id, ref_t<CPacket> packet);
void setClosing()
{
m_bClosing = true;
}
private:
static void* worker(void* param);
pthread_t m_WorkerThread;
// Subroutines of worker
EReadStatus worker_RetrieveUnit(ref_t<int32_t> id, ref_t<CUnit*> unit, sockaddr* sa);
EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr* sa);
EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr* sa);
EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr* sa);
private:
CUnitQueue m_UnitQueue; // The received packet queue
CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue
CHash* m_pHash; // Hash table for UDT socket looking up
CChannel* m_pChannel; // UDP channel for receving packets
CTimer* m_pTimer; // shared timer with the snd queue
int m_iPayloadSize; // packet payload size
volatile bool m_bClosing; // closing the worker
private:
int setListener(CUDT* u);
void removeListener(const CUDT* u);
void registerConnector(const SRTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl);
void removeConnector(const SRTSOCKET& id, bool should_lock = true);
void setNewEntry(CUDT* u);
bool ifNewEntry();
CUDT* getNewEntry();
void storePkt(int32_t id, CPacket* pkt);
private:
pthread_mutex_t m_LSLock;
CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity
CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode
std::vector<CUDT*> m_vNewEntry; // newly added entries, to be inserted
pthread_mutex_t m_IDLock;
std::map<int32_t, std::queue<CPacket*> > m_mBuffer; // temporary buffer for rendezvous connection request
pthread_mutex_t m_PassLock;
pthread_cond_t m_PassCond;
private:
CRcvQueue(const CRcvQueue&);
CRcvQueue& operator=(const CRcvQueue&);
};
struct CMultiplexer
{
CSndQueue* m_pSndQueue; // The sending queue
CRcvQueue* m_pRcvQueue; // The receiving queue
CChannel* m_pChannel; // The UDP channel for sending and receiving
CTimer* m_pTimer; // The timer
int m_iPort; // The UDP port number of this multiplexer
int m_iIPversion; // IP version
#ifdef SRT_ENABLE_IPOPTS
int m_iIpTTL;
int m_iIpToS;
#endif
int m_iMSS; // Maximum Segment Size
int m_iRefCount; // number of UDT instances that are associated with this multiplexer
int m_iIpV6Only; // IPV6_V6ONLY option
bool m_bReusable; // if this one can be shared with others
int m_iID; // multiplexer ID
};
#endif

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

@ -0,0 +1,747 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__SRTC_H
#define INC__SRTC_H
#include "version.h"
#include "platform_sys.h"
#include <string.h>
#include <stdlib.h>
#include "srt4udt.h"
#include "logging_api.h"
////////////////////////////////////////////////////////////////////////////////
//if compiling on VC6.0 or pre-WindowsXP systems
//use -DLEGACY_WIN32
//if compiling with MinGW, it only works on XP or above
//use -D_WIN32_WINNT=0x0501
#ifdef _WIN32
#ifndef __MINGW__
// Explicitly define 32-bit and 64-bit numbers
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int32 uint32_t;
#ifndef LEGACY_WIN32
typedef unsigned __int64 uint64_t;
#else
// VC 6.0 does not support unsigned __int64: may cause potential problems.
typedef __int64 uint64_t;
#endif
#ifdef SRT_DYNAMIC
#ifdef SRT_EXPORTS
#define SRT_API __declspec(dllexport)
#else
#define SRT_API __declspec(dllimport)
#endif
#else
#define SRT_API
#endif
#else // __MINGW__
#define SRT_API
#endif
#else
#define SRT_API __attribute__ ((visibility("default")))
#endif
// For feature tests if you need.
// You can use these constants with SRTO_MINVERSION option.
#define SRT_VERSION_FEAT_HSv5 0x010300
// When compiling in C++17 mode, use the standard C++17 attributes
// (out of these, only [[deprecated]] is supported in C++14, so
// for all lesser standard use compiler-specific attributes).
#if defined(SRT_NO_DEPRECATED)
#define SRT_ATR_UNUSED
#define SRT_ATR_DEPRECATED
#define SRT_ATR_NODISCARD
#elif defined(__cplusplus) && __cplusplus > 201406
#define SRT_ATR_UNUSED [[maybe_unused]]
#define SRT_ATR_DEPRECATED [[deprecated]]
#define SRT_ATR_NODISCARD [[nodiscard]]
// GNUG is GNU C/C++; this syntax is also supported by Clang
#elif defined( __GNUC__)
#define SRT_ATR_UNUSED __attribute__((unused))
#define SRT_ATR_DEPRECATED __attribute__((deprecated))
#define SRT_ATR_NODISCARD __attribute__((warn_unused_result))
#else
#define SRT_ATR_UNUSED
#define SRT_ATR_DEPRECATED
#define SRT_ATR_NODISCARD
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef int SRTSOCKET; // SRTSOCKET is a typedef to int anyway, and it's not even in UDT namespace :)
#ifdef _WIN32
#ifndef __MINGW__
typedef SOCKET SYSSOCKET;
#else
typedef int SYSSOCKET;
#endif
#else
typedef int SYSSOCKET;
#endif
typedef SYSSOCKET UDPSOCKET;
// Values returned by srt_getsockstate()
typedef enum SRT_SOCKSTATUS {
SRTS_INIT = 1,
SRTS_OPENED,
SRTS_LISTENING,
SRTS_CONNECTING,
SRTS_CONNECTED,
SRTS_BROKEN,
SRTS_CLOSING,
SRTS_CLOSED,
SRTS_NONEXIST
} SRT_SOCKSTATUS;
// This is a duplicate enum. Must be kept in sync with the original UDT enum for
// backward compatibility until all compat is destroyed.
typedef enum SRT_SOCKOPT {
SRTO_MSS = 0, // the Maximum Transfer Unit
SRTO_SNDSYN = 1, // if sending is blocking
SRTO_RCVSYN = 2, // if receiving is blocking
SRTO_ISN = 3, // Initial Sequence Number (valid only after srt_connect or srt_accept-ed sockets)
SRTO_FC = 4, // Flight flag size (window size)
SRTO_SNDBUF = 5, // maximum buffer in sending queue
SRTO_RCVBUF = 6, // UDT receiving buffer size
SRTO_LINGER = 7, // waiting for unsent data when closing
SRTO_UDP_SNDBUF = 8, // UDP sending buffer size
SRTO_UDP_RCVBUF = 9, // UDP receiving buffer size
// XXX Free space for 2 options
// after deprecated ones are removed
SRTO_RENDEZVOUS = 12, // rendezvous connection mode
SRTO_SNDTIMEO = 13, // send() timeout
SRTO_RCVTIMEO = 14, // recv() timeout
SRTO_REUSEADDR = 15, // reuse an existing port or create a new one
SRTO_MAXBW = 16, // maximum bandwidth (bytes per second) that the connection can use
SRTO_STATE = 17, // current socket state, see UDTSTATUS, read only
SRTO_EVENT = 18, // current available events associated with the socket
SRTO_SNDDATA = 19, // size of data in the sending buffer
SRTO_RCVDATA = 20, // size of data available for recv
SRTO_SENDER = 21, // Sender mode (independent of conn mode), for encryption, tsbpd handshake.
SRTO_TSBPDMODE = 22, // Enable/Disable TsbPd. Enable -> Tx set origin timestamp, Rx deliver packet at origin time + delay
SRTO_LATENCY = 23, // NOT RECOMMENDED. SET: to both SRTO_RCVLATENCY and SRTO_PEERLATENCY. GET: same as SRTO_RCVLATENCY.
SRTO_TSBPDDELAY = 23, // DEPRECATED. ALIAS: SRTO_LATENCY
SRTO_INPUTBW = 24, // Estimated input stream rate.
SRTO_OHEADBW, // MaxBW ceiling based on % over input stream rate. Applies when UDT_MAXBW=0 (auto).
SRTO_PASSPHRASE = 26, // Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto
SRTO_PBKEYLEN, // Crypto key len in bytes {16,24,32} Default: 16 (128-bit)
SRTO_KMSTATE, // Key Material exchange status (UDT_SRTKmState)
SRTO_IPTTL = 29, // IP Time To Live (passthru for system sockopt IPPROTO_IP/IP_TTL)
SRTO_IPTOS, // IP Type of Service (passthru for system sockopt IPPROTO_IP/IP_TOS)
SRTO_TLPKTDROP = 31, // Enable receiver pkt drop
SRTO_SNDDROPDELAY = 32, // Extra delay towards latency for sender TLPKTDROP decision (-1 to off)
SRTO_NAKREPORT = 33, // Enable receiver to send periodic NAK reports
SRTO_VERSION = 34, // Local SRT Version
SRTO_PEERVERSION, // Peer SRT Version (from SRT Handshake)
SRTO_CONNTIMEO = 36, // Connect timeout in msec. Ccaller default: 3000, rendezvous (x 10)
// deprecated: SRTO_TWOWAYDATA, SRTO_SNDPBKEYLEN, SRTO_RCVPBKEYLEN (@c below)
_DEPRECATED_SRTO_SNDPBKEYLEN = 38, // (needed to use inside the code without generating -Wswitch)
//
SRTO_SNDKMSTATE = 40, // (GET) the current state of the encryption at the peer side
SRTO_RCVKMSTATE, // (GET) the current state of the encryption at the agent side
SRTO_LOSSMAXTTL, // Maximum possible packet reorder tolerance (number of packets to receive after loss to send lossreport)
SRTO_RCVLATENCY, // TsbPd receiver delay (mSec) to absorb burst of missed packet retransmission
SRTO_PEERLATENCY, // Minimum value of the TsbPd receiver delay (mSec) for the opposite side (peer)
SRTO_MINVERSION, // Minimum SRT version needed for the peer (peers with less version will get connection reject)
SRTO_STREAMID, // A string set to a socket and passed to the listener's accepted socket
SRTO_CONGESTION, // Congestion controller type selection
SRTO_MESSAGEAPI, // In File mode, use message API (portions of data with boundaries)
SRTO_PAYLOADSIZE, // Maximum payload size sent in one UDP packet (0 if unlimited)
SRTO_TRANSTYPE = 50, // Transmission type (set of options required for given transmission type)
SRTO_KMREFRESHRATE, // After sending how many packets the encryption key should be flipped to the new key
SRTO_KMPREANNOUNCE, // How many packets before key flip the new key is annnounced and after key flip the old one decommissioned
SRTO_ENFORCEDENCRYPTION, // Connection to be rejected or quickly broken when one side encryption set or bad password
SRTO_IPV6ONLY, // IPV6_V6ONLY mode
SRTO_PEERIDLETIMEO, // Peer-idle timeout (max time of silence heard from peer) in [ms]
// (some space left)
SRTO_PACKETFILTER = 60 // Add and configure a packet filter
} SRT_SOCKOPT;
#ifdef __cplusplus
typedef SRT_ATR_DEPRECATED SRT_SOCKOPT SRT_SOCKOPT_DEPRECATED;
#define SRT_DEPRECATED_OPTION(value) ((SRT_SOCKOPT_DEPRECATED)value)
#else
// deprecated enum labels are supported only since gcc 6, so in C there
// will be a whole deprecated enum type, as it's not an error in C to mix
// enum types
enum SRT_ATR_DEPRECATED SRT_SOCKOPT_DEPRECATED
{
// Dummy last option, as every entry ends with a comma
SRTO_DEPRECATED_END = 0
};
#define SRT_DEPRECATED_OPTION(value) ((enum SRT_SOCKOPT_DEPRECATED)value)
#endif
// DEPRECATED OPTIONS:
// SRTO_TWOWAYDATA: not to be used. SRT connection is always bidirectional if
// both clients support HSv5 - that is, since version 1.3.0. This flag was
// introducted around 1.2.0 version when full bidirectional support was added,
// but the bidirectional feature was decided no to be enabled due to huge
// differences between bidirectional support (especially concerning encryption)
// with HSv4 and HSv5 (that is, HSv4 was decided to remain unidirectional only,
// even though partial support is already provided in this version).
#define SRTO_TWOWAYDATA SRT_DEPRECATED_OPTION(37)
// This has been deprecated a long time ago, treat this as never implemented.
// The value is also already reused for another option.
#define SRTO_TSBPDMAXLAG SRT_DEPRECATED_OPTION(32)
// This option is a derivative from UDT; the mechanism that uses it is now
// settable by SRTO_CONGESTION, or more generally by SRTO_TRANSTYPE. The freed
// number has been reused for a read-only option SRTO_ISN. This option should
// have never been used anywhere, just for safety this is temporarily declared
// as deprecated.
#define SRTO_CC SRT_DEPRECATED_OPTION(3)
// These two flags were derived from UDT, but they were never used.
// Probably it didn't make sense anyway. The maximum size of the message
// in File/Message mode is defined by SRTO_SNDBUF, and the MSGTTL is
// a parameter used in `srt_sendmsg` and `srt_sendmsg2`.
#define SRTO_MAXMSG SRT_DEPRECATED_OPTION(10)
#define SRTO_MSGTTL SRT_DEPRECATED_OPTION(11)
// These flags come from an older experimental implementation of bidirectional
// encryption support, which were used two different SEKs, KEKs and passphrases
// per direction. The current implementation uses just one in both directions,
// so SRTO_PBKEYLEN should be used for both cases.
#define SRTO_SNDPBKEYLEN SRT_DEPRECATED_OPTION(38)
#define SRTO_RCVPBKEYLEN SRT_DEPRECATED_OPTION(39)
// Keeping old name for compatibility (deprecated)
#define SRTO_SMOOTHER SRT_DEPRECATED_OPTION(47)
#define SRTO_STRICTENC SRT_DEPRECATED_OPTION(53)
typedef enum SRT_TRANSTYPE
{
SRTT_LIVE,
SRTT_FILE,
SRTT_INVALID
} SRT_TRANSTYPE;
// These sizes should be used for Live mode. In Live mode you should not
// exceed the size that fits in a single MTU.
// This is for MPEG TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE.
static const int SRT_LIVE_DEF_PLSIZE = 1316; // = 188*7, recommended for MPEG TS
// This is the maximum payload size for Live mode, should you have a different
// payload type than MPEG TS.
static const int SRT_LIVE_MAX_PLSIZE = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16)
// Latency for Live transmission: default is 120
static const int SRT_LIVE_DEF_LATENCY_MS = 120;
// Importrant note: please add new fields to this structure to the end and don't remove any existing fields
struct CBytePerfMon
{
// global measurements
int64_t msTimeStamp; // time since the UDT entity is started, in milliseconds
int64_t pktSentTotal; // total number of sent data packets, including retransmissions
int64_t pktRecvTotal; // total number of received packets
int pktSndLossTotal; // total number of lost packets (sender side)
int pktRcvLossTotal; // total number of lost packets (receiver side)
int pktRetransTotal; // total number of retransmitted packets
int pktSentACKTotal; // total number of sent ACK packets
int pktRecvACKTotal; // total number of received ACK packets
int pktSentNAKTotal; // total number of sent NAK packets
int pktRecvNAKTotal; // total number of received NAK packets
int64_t usSndDurationTotal; // total time duration when UDT is sending data (idle time exclusive)
//>new
int pktSndDropTotal; // number of too-late-to-send dropped packets
int pktRcvDropTotal; // number of too-late-to play missing packets
int pktRcvUndecryptTotal; // number of undecrypted packets
uint64_t byteSentTotal; // total number of sent data bytes, including retransmissions
uint64_t byteRecvTotal; // total number of received bytes
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
uint64_t byteRcvLossTotal; // total number of lost bytes
#endif
uint64_t byteRetransTotal; // total number of retransmitted bytes
uint64_t byteSndDropTotal; // number of too-late-to-send dropped bytes
uint64_t byteRcvDropTotal; // number of too-late-to play missing bytes (estimate based on average packet size)
uint64_t byteRcvUndecryptTotal; // number of undecrypted bytes
//<
// local measurements
int64_t pktSent; // number of sent data packets, including retransmissions
int64_t pktRecv; // number of received packets
int pktSndLoss; // number of lost packets (sender side)
int pktRcvLoss; // number of lost packets (receiver side)
int pktRetrans; // number of retransmitted packets
int pktRcvRetrans; // number of retransmitted packets received
int pktSentACK; // number of sent ACK packets
int pktRecvACK; // number of received ACK packets
int pktSentNAK; // number of sent NAK packets
int pktRecvNAK; // number of received NAK packets
double mbpsSendRate; // sending rate in Mb/s
double mbpsRecvRate; // receiving rate in Mb/s
int64_t usSndDuration; // busy sending time (i.e., idle time exclusive)
int pktReorderDistance; // size of order discrepancy in received sequences
double pktRcvAvgBelatedTime; // average time of packet delay for belated packets (packets with sequence past the ACK)
int64_t pktRcvBelated; // number of received AND IGNORED packets due to having come too late
//>new
int pktSndDrop; // number of too-late-to-send dropped packets
int pktRcvDrop; // number of too-late-to play missing packets
int pktRcvUndecrypt; // number of undecrypted packets
uint64_t byteSent; // number of sent data bytes, including retransmissions
uint64_t byteRecv; // number of received bytes
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
uint64_t byteRcvLoss; // number of retransmitted bytes
#endif
uint64_t byteRetrans; // number of retransmitted bytes
uint64_t byteSndDrop; // number of too-late-to-send dropped bytes
uint64_t byteRcvDrop; // number of too-late-to play missing bytes (estimate based on average packet size)
uint64_t byteRcvUndecrypt; // number of undecrypted bytes
//<
// instant measurements
double usPktSndPeriod; // packet sending period, in microseconds
int pktFlowWindow; // flow window size, in number of packets
int pktCongestionWindow; // congestion window size, in number of packets
int pktFlightSize; // number of packets on flight
double msRTT; // RTT, in milliseconds
double mbpsBandwidth; // estimated bandwidth, in Mb/s
int byteAvailSndBuf; // available UDT sender buffer size
int byteAvailRcvBuf; // available UDT receiver buffer size
//>new
double mbpsMaxBW; // Transmit Bandwidth ceiling (Mbps)
int byteMSS; // MTU
int pktSndBuf; // UnACKed packets in UDT sender
int byteSndBuf; // UnACKed bytes in UDT sender
int msSndBuf; // UnACKed timespan (msec) of UDT sender
int msSndTsbPdDelay; // Timestamp-based Packet Delivery Delay
int pktRcvBuf; // Undelivered packets in UDT receiver
int byteRcvBuf; // Undelivered bytes of UDT receiver
int msRcvBuf; // Undelivered timespan (msec) of UDT receiver
int msRcvTsbPdDelay; // Timestamp-based Packet Delivery Delay
int pktSndFilterExtraTotal; // number of control packets supplied by packet filter
int pktRcvFilterExtraTotal; // number of control packets received and not supplied back
int pktRcvFilterSupplyTotal; // number of packets that the filter supplied extra (e.g. FEC rebuilt)
int pktRcvFilterLossTotal; // number of packet loss not coverable by filter
int pktSndFilterExtra; // number of control packets supplied by packet filter
int pktRcvFilterExtra; // number of control packets received and not supplied back
int pktRcvFilterSupply; // number of packets that the filter supplied extra (e.g. FEC rebuilt)
int pktRcvFilterLoss; // number of packet loss not coverable by filter
int pktReorderTolerance; // packet reorder tolerance value
//<
};
////////////////////////////////////////////////////////////////////////////////
// Error codes - define outside the CUDTException class
// because otherwise you'd have to use CUDTException::MJ_SUCCESS etc.
// in all throw CUDTException expressions.
enum CodeMajor
{
MJ_UNKNOWN = -1,
MJ_SUCCESS = 0,
MJ_SETUP = 1,
MJ_CONNECTION = 2,
MJ_SYSTEMRES = 3,
MJ_FILESYSTEM = 4,
MJ_NOTSUP = 5,
MJ_AGAIN = 6,
MJ_PEERERROR = 7
};
enum CodeMinor
{
// These are "minor" error codes from various "major" categories
// MJ_SETUP
MN_NONE = 0,
MN_TIMEOUT = 1,
MN_REJECTED = 2,
MN_NORES = 3,
MN_SECURITY = 4,
// MJ_CONNECTION
MN_CONNLOST = 1,
MN_NOCONN = 2,
// MJ_SYSTEMRES
MN_THREAD = 1,
MN_MEMORY = 2,
// MJ_FILESYSTEM
MN_SEEKGFAIL = 1,
MN_READFAIL = 2,
MN_SEEKPFAIL = 3,
MN_WRITEFAIL = 4,
// MJ_NOTSUP
MN_ISBOUND = 1,
MN_ISCONNECTED = 2,
MN_INVAL = 3,
MN_SIDINVAL = 4,
MN_ISUNBOUND = 5,
MN_NOLISTEN = 6,
MN_ISRENDEZVOUS = 7,
MN_ISRENDUNBOUND = 8,
MN_INVALMSGAPI = 9,
MN_INVALBUFFERAPI = 10,
MN_BUSY = 11,
MN_XSIZE = 12,
MN_EIDINVAL = 13,
// MJ_AGAIN
MN_WRAVAIL = 1,
MN_RDAVAIL = 2,
MN_XMTIMEOUT = 3,
MN_CONGESTION = 4
};
static const enum CodeMinor MN_ISSTREAM SRT_ATR_DEPRECATED = (enum CodeMinor)(9);
static const enum CodeMinor MN_ISDGRAM SRT_ATR_DEPRECATED = (enum CodeMinor)(10);
// Stupid, but effective. This will be #undefined, so don't worry.
#define MJ(major) (1000 * MJ_##major)
#define MN(major, minor) (1000 * MJ_##major + MN_##minor)
// Some better way to define it, and better for C language.
typedef enum SRT_ERRNO
{
SRT_EUNKNOWN = -1,
SRT_SUCCESS = MJ_SUCCESS,
SRT_ECONNSETUP = MJ(SETUP),
SRT_ENOSERVER = MN(SETUP, TIMEOUT),
SRT_ECONNREJ = MN(SETUP, REJECTED),
SRT_ESOCKFAIL = MN(SETUP, NORES),
SRT_ESECFAIL = MN(SETUP, SECURITY),
SRT_ECONNFAIL = MJ(CONNECTION),
SRT_ECONNLOST = MN(CONNECTION, CONNLOST),
SRT_ENOCONN = MN(CONNECTION, NOCONN),
SRT_ERESOURCE = MJ(SYSTEMRES),
SRT_ETHREAD = MN(SYSTEMRES, THREAD),
SRT_ENOBUF = MN(SYSTEMRES, MEMORY),
SRT_EFILE = MJ(FILESYSTEM),
SRT_EINVRDOFF = MN(FILESYSTEM, SEEKGFAIL),
SRT_ERDPERM = MN(FILESYSTEM, READFAIL),
SRT_EINVWROFF = MN(FILESYSTEM, SEEKPFAIL),
SRT_EWRPERM = MN(FILESYSTEM, WRITEFAIL),
SRT_EINVOP = MJ(NOTSUP),
SRT_EBOUNDSOCK = MN(NOTSUP, ISBOUND),
SRT_ECONNSOCK = MN(NOTSUP, ISCONNECTED),
SRT_EINVPARAM = MN(NOTSUP, INVAL),
SRT_EINVSOCK = MN(NOTSUP, SIDINVAL),
SRT_EUNBOUNDSOCK = MN(NOTSUP, ISUNBOUND),
SRT_ENOLISTEN = MN(NOTSUP, NOLISTEN),
SRT_ERDVNOSERV = MN(NOTSUP, ISRENDEZVOUS),
SRT_ERDVUNBOUND = MN(NOTSUP, ISRENDUNBOUND),
SRT_EINVALMSGAPI = MN(NOTSUP, INVALMSGAPI),
SRT_EINVALBUFFERAPI = MN(NOTSUP, INVALBUFFERAPI),
SRT_EDUPLISTEN = MN(NOTSUP, BUSY),
SRT_ELARGEMSG = MN(NOTSUP, XSIZE),
SRT_EINVPOLLID = MN(NOTSUP, EIDINVAL),
SRT_EASYNCFAIL = MJ(AGAIN),
SRT_EASYNCSND = MN(AGAIN, WRAVAIL),
SRT_EASYNCRCV = MN(AGAIN, RDAVAIL),
SRT_ETIMEOUT = MN(AGAIN, XMTIMEOUT),
SRT_ECONGEST = MN(AGAIN, CONGESTION),
SRT_EPEERERR = MJ(PEERERROR)
} SRT_ERRNO;
static const SRT_ERRNO SRT_EISSTREAM SRT_ATR_DEPRECATED = (SRT_ERRNO) MN(NOTSUP, INVALMSGAPI);
static const SRT_ERRNO SRT_EISDGRAM SRT_ATR_DEPRECATED = (SRT_ERRNO) MN(NOTSUP, INVALBUFFERAPI);
#undef MJ
#undef MN
enum SRT_REJECT_REASON
{
SRT_REJ_UNKNOWN, // initial set when in progress
SRT_REJ_SYSTEM, // broken due to system function error
SRT_REJ_PEER, // connection was rejected by peer
SRT_REJ_RESOURCE, // internal problem with resource allocation
SRT_REJ_ROGUE, // incorrect data in handshake messages
SRT_REJ_BACKLOG, // listener's backlog exceeded
SRT_REJ_IPE, // internal program error
SRT_REJ_CLOSE, // socket is closing
SRT_REJ_VERSION, // peer is older version than agent's minimum set
SRT_REJ_RDVCOOKIE, // rendezvous cookie collision
SRT_REJ_BADSECRET, // wrong password
SRT_REJ_UNSECURE, // password required or unexpected
SRT_REJ_MESSAGEAPI, // streamapi/messageapi collision
SRT_REJ_CONGESTION, // incompatible congestion-controller type
SRT_REJ_FILTER, // incompatible packet filter
SRT_REJ__SIZE,
};
// Logging API - specialization for SRT.
// Define logging functional areas for log selection.
// Use values greater than 0. Value 0 is reserved for LOGFA_GENERAL,
// which is considered always enabled.
// Logger Functional Areas
// Note that 0 is "general".
// Made by #define so that it's available also for C API.
#define SRT_LOGFA_GENERAL 0
#define SRT_LOGFA_BSTATS 1
#define SRT_LOGFA_CONTROL 2
#define SRT_LOGFA_DATA 3
#define SRT_LOGFA_TSBPD 4
#define SRT_LOGFA_REXMIT 5
#define SRT_LOGFA_HAICRYPT 6
#define SRT_LOGFA_CONGEST 7
// To make a typical int32_t size, although still use std::bitset.
// C API will carry it over.
#define SRT_LOGFA_LASTNONE 31
enum SRT_KM_STATE
{
SRT_KM_S_UNSECURED = 0, //No encryption
SRT_KM_S_SECURING = 1, //Stream encrypted, exchanging Keying Material
SRT_KM_S_SECURED = 2, //Stream encrypted, keying Material exchanged, decrypting ok.
SRT_KM_S_NOSECRET = 3, //Stream encrypted and no secret to decrypt Keying Material
SRT_KM_S_BADSECRET = 4 //Stream encrypted and wrong secret, cannot decrypt Keying Material
};
enum SRT_EPOLL_OPT
{
SRT_EPOLL_OPT_NONE = 0x0, // fallback
// this values are defined same as linux epoll.h
// so that if system values are used by mistake, they should have the same effect
SRT_EPOLL_IN = 0x1,
SRT_EPOLL_OUT = 0x4,
SRT_EPOLL_ERR = 0x8,
SRT_EPOLL_ET = 1u << 31
};
// These are actually flags - use a bit container:
typedef int32_t SRT_EPOLL_T;
enum SRT_EPOLL_FLAGS
{
/// This allows the EID container to be empty when calling the waiting
/// function with infinite time. This means an infinite hangup, although
/// a socket can be added to this EID from a separate thread.
SRT_EPOLL_ENABLE_EMPTY = 1,
/// This makes the waiting function check if there is output container
/// passed to it, and report an error if it isn't. By default it is allowed
/// that the output container is 0 size or NULL and therefore the readiness
/// state is reported only as a number of ready sockets from return value.
SRT_EPOLL_ENABLE_OUTPUTCHECK = 2
};
#ifdef __cplusplus
// In C++ these enums cannot be treated as int and glued by operator |.
// Unless this operator is defined.
inline SRT_EPOLL_OPT operator|(SRT_EPOLL_OPT a1, SRT_EPOLL_OPT a2)
{
return SRT_EPOLL_OPT( (int)a1 | (int)a2 );
}
inline bool operator&(int flags, SRT_EPOLL_OPT eflg)
{
// Using an enum prevents treating int automatically as enum,
// requires explicit enum to be passed here, and minimizes the
// risk that the right side value will contain multiple flags.
return (flags & int(eflg)) != 0;
}
#endif
typedef struct CBytePerfMon SRT_TRACEBSTATS;
static const SRTSOCKET SRT_INVALID_SOCK = -1;
static const int SRT_ERROR = -1;
// library initialization
SRT_API int srt_startup(void);
SRT_API int srt_cleanup(void);
//
// Socket operations
//
SRT_API SRTSOCKET srt_socket (int af, int type, int protocol);
SRT_API SRTSOCKET srt_create_socket();
SRT_API int srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen);
SRT_API int srt_bind_peerof (SRTSOCKET u, UDPSOCKET udpsock);
SRT_API int srt_listen (SRTSOCKET u, int backlog);
SRT_API SRTSOCKET srt_accept (SRTSOCKET u, struct sockaddr* addr, int* addrlen);
typedef int srt_listen_callback_fn (void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid);
SRT_API int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque);
SRT_API int srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen);
SRT_API int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn);
SRT_API int srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen,
const struct sockaddr* remote_name, int remote_namelen);
SRT_API int srt_close (SRTSOCKET u);
SRT_API int srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen);
SRT_API int srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen);
SRT_API int srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen);
SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen);
SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen);
SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen);
// XXX Note that the srctime functionality doesn't work yet and needs fixing.
typedef struct SRT_MsgCtrl_
{
int flags; // Left for future
int msgttl; // TTL for a message, default -1 (no TTL limitation)
int inorder; // Whether a message is allowed to supersede partially lost one. Unused in stream and live mode.
int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame
uint64_t srctime; // source timestamp (usec), 0: use internal time
int32_t pktseq; // sequence number of the first packet in received message (unused for sending)
int32_t msgno; // message number (output value for both sending and receiving)
} SRT_MSGCTRL;
// You are free to use either of these two methods to set SRT_MSGCTRL object
// to default values: either call srt_msgctrl_init(&obj) or obj = srt_msgctrl_default.
SRT_API void srt_msgctrl_init(SRT_MSGCTRL* mctrl);
SRT_API extern const SRT_MSGCTRL srt_msgctrl_default;
// The send/receive functions.
// These functions have different names due to different sets of parameters
// to be supplied. Not all of them are needed or make sense in all modes:
// Plain: supply only the buffer and its size.
// Msg: supply additionally
// - TTL (message is not delivered when exceeded) and
// - INORDER (when false, the message is allowed to be delivered in different
// order than when it was sent, when the later message is earlier ready to
// deliver)
// Msg2: Supply extra parameters in SRT_MSGCTRL. When receiving, these
// parameters will be filled, as needed. NULL is acceptable, in which case
// the defaults are used.
// NOTE: srt_send and srt_recv have the last "..." left to allow ignore a
// deprecated and unused "flags" parameter. After confirming that all
// compat applications that pass useless 0 there are fixed, this will be
// removed.
//
// Sending functions
//
SRT_API int srt_send (SRTSOCKET u, const char* buf, int len);
SRT_API int srt_sendmsg (SRTSOCKET u, const char* buf, int len, int ttl/* = -1*/, int inorder/* = false*/);
SRT_API int srt_sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL *mctrl);
//
// Receiving functions
//
SRT_API int srt_recv (SRTSOCKET u, char* buf, int len);
// srt_recvmsg is actually an alias to srt_recv, it stays under the old name for compat reasons.
SRT_API int srt_recvmsg (SRTSOCKET u, char* buf, int len);
SRT_API int srt_recvmsg2(SRTSOCKET u, char *buf, int len, SRT_MSGCTRL *mctrl);
// Special send/receive functions for files only.
#define SRT_DEFAULT_SENDFILE_BLOCK 364000
#define SRT_DEFAULT_RECVFILE_BLOCK 7280000
SRT_API int64_t srt_sendfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block);
SRT_API int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block);
// last error detection
SRT_API const char* srt_getlasterror_str(void);
SRT_API int srt_getlasterror(int* errno_loc);
SRT_API const char* srt_strerror(int code, int errnoval);
SRT_API void srt_clearlasterror(void);
// performance track
// perfmon with Byte counters for better bitrate estimation.
SRT_API int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear);
// permon with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes.
SRT_API int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous);
// Socket Status (for problem tracking)
SRT_API SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u);
SRT_API int srt_epoll_create(void);
SRT_API int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events);
SRT_API int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events);
SRT_API int srt_epoll_remove_usock(int eid, SRTSOCKET u);
SRT_API int srt_epoll_remove_ssock(int eid, SYSSOCKET s);
SRT_API int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events);
SRT_API int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events);
SRT_API int srt_epoll_wait(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, int64_t msTimeOut,
SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum);
typedef struct SRT_EPOLL_EVENT_
{
SRTSOCKET fd;
int events; // SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR
} SRT_EPOLL_EVENT;
SRT_API int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
SRT_API int32_t srt_epoll_set(int eid, int32_t flags);
SRT_API int srt_epoll_release(int eid);
// Logging control
SRT_API void srt_setloglevel(int ll);
SRT_API void srt_addlogfa(int fa);
SRT_API void srt_dellogfa(int fa);
SRT_API void srt_resetlogfa(const int* fara, size_t fara_size);
// This isn't predicted, will be only available in SRT C++ API.
// For the time being, until this API is ready, use UDT::setlogstream.
// SRT_API void srt_setlogstream(std::ostream& stream);
SRT_API void srt_setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler);
SRT_API void srt_setlogflags(int flags);
SRT_API int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes);
SRT_API enum SRT_REJECT_REASON srt_getrejectreason(SRTSOCKET sock);
SRT_API extern const char* const srt_rejectreason_msg [];
const char* srt_rejectreason_str(enum SRT_REJECT_REASON id);
#ifdef __cplusplus
}
#endif
#endif

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