Compare commits

...

89 Commits

Author SHA1 Message Date
c675aedb83 Merge pull request #109 from paullouisageneau/fix-mid-handling
Fix mid handling in description
2020-07-01 00:17:41 +02:00
e32d139056 Moved data after non-data media in description 2020-07-01 00:13:12 +02:00
a790161168 Properly set remote data mid 2020-07-01 00:01:01 +02:00
2697ef0d76 Bumped version to 0.6.4 2020-06-29 21:09:02 +02:00
5044aedbec Updated libjuice to v0.4.4 2020-06-29 21:08:39 +02:00
44c90c1cb4 Set gnutls linking to shared in Jamfile 2020-06-29 21:05:34 +02:00
be79c68540 Merge pull request #108 from paullouisageneau/fix-srtp-transport
Fix optional SRTP transport
2020-06-29 21:03:44 +02:00
226a927df1 Implemented RTP and RTCP demultiplexing 2020-06-29 20:39:31 +02:00
4e1b9bb3c2 Fixed SRTP/DTLS demultiplexing 2020-06-29 09:48:42 +02:00
8bc016cc08 Added some logging 2020-06-29 09:48:42 +02:00
5afbe10d01 Changed to two SRTP sessions and introduced srtp_add_stream() 2020-06-29 09:48:42 +02:00
8df07ca68d Fixed serverSalt 2020-06-29 09:48:42 +02:00
884bd2316e Fixed error checking for OpenSSL 2020-06-29 09:48:42 +02:00
dadecce709 Fixed compilation for OpenSSL 2020-06-29 09:48:42 +02:00
103935bdd5 Introduced postCreation method to DTLS-SRTP 2020-06-29 09:48:42 +02:00
62e6954949 Use dedicated method to send media 2020-06-29 09:48:42 +02:00
3ac2d155cc Set DtlsSrtpTransport to bypass before handshake 2020-06-29 09:48:42 +02:00
6d8788c2a1 Updated libjuice 2020-06-29 09:47:53 +02:00
603dd01b87 Merge pull request #107 from Kyrio/fix-termux-include
Add the right header for IPPROTO_* on platforms such as Termux
2020-06-29 09:43:34 +02:00
8091508428 Add the right header for IPPROTO_* on some platforms 2020-06-29 01:08:34 +02:00
b38f63f077 Renamed PeerConnection::send() to sendMedia() 2020-06-26 18:02:07 +02:00
d87539937e Merge pull request #105 from paullouisageneau/fix-after-free-sctp-incoming
Fix use-after-free in SctpTransport::incoming()
2020-06-26 11:10:33 +02:00
eb09cadded Reordered SctpTransport::shutdown() and mLower->onRecv(nullptr) 2020-06-25 22:26:15 +02:00
79e0c62321 Merge pull request #103 from paullouisageneau/srtp-flag
Add USE_SRTP flag
2020-06-25 11:03:41 +02:00
ef38777129 Added USE_SRTP flag and renamed NO_WEBSOCKET flag 2020-06-25 10:59:12 +02:00
313f081061 Merge pull request #102 from paullouisageneau/fix-getaddrinfo-leak
Fix getaddrinfo() leak in Candidate::resolve()
2020-06-25 09:26:18 +02:00
6108b05e0d Fixed missing freeaddrinfo() on early exit 2020-06-25 09:20:05 +02:00
f68601b45f Updated libjuice 2020-06-24 15:30:15 +02:00
b94f2ab339 Bumped version to 0.6.3 2020-06-24 14:27:36 +02:00
9c79c8516b Updated libjuice to v0.4.3 2020-06-24 14:27:19 +02:00
ad03549f8c Updated libjuice 2020-06-24 13:38:56 +02:00
5cc8eb1ce6 Moved CleanupCertificateCache() to rtc::Cleanup() 2020-06-24 09:09:50 +02:00
d19ff754b2 Merge pull request #100 from paullouisageneau/fix-plog
Change plog to fork
2020-06-23 23:07:36 +02:00
fb0f903e2e Added includes after plog update 2020-06-23 23:03:09 +02:00
6628297580 Changed plog to fork https://github.com/paullouisageneau/plog 2020-06-23 22:51:43 +02:00
ccc05b9999 Added libjuice dependency on nettle if using gnutls 2020-06-23 19:18:35 +02:00
fb2f480f92 Change dir to build before cmake rather than using option -B 2020-06-23 15:03:28 +02:00
cca0ac859a Explicit . source directory for cmake 2020-06-23 12:21:24 +02:00
9fbc7c6ea8 Updated libjuice 2020-06-23 12:13:59 +02:00
83d65c805a Hint OpenSSL for libjuice cmake build 2020-06-23 09:28:27 +02:00
0a5cef331d Fixed target name usrsctp-static 2020-06-22 19:36:15 +02:00
7c1714f83c Changed MSVC build mode to Release and removed OpenSSL < 1.1 2020-06-22 18:55:19 +02:00
f3024d0552 Fixed compilation on MSVC 2020-06-22 14:59:34 +02:00
bcd1972270 Updated Readme 2020-06-22 12:14:29 +02:00
57501b1739 Updated Readme 2020-06-22 12:08:11 +02:00
cfe8a0e9c6 Fixed defines for MSVC 2020-06-21 20:21:00 +02:00
10061b3d4b Replaced dtls-id by tls-id according to draft-ietf-mmusic-dtls-sdp-32 2020-06-21 18:21:29 +02:00
52959ee700 Refactored media map access to prevent unused variables 2020-06-21 18:20:51 +02:00
0e86d6e3f1 Fixed cmake calls in Jamfile 2020-06-21 17:56:03 +02:00
62675e4c21 Merge pull request #97 from paullouisageneau/usrsctp-fix-notifications
Fix SCTP interleaved notifications
2020-06-21 17:51:12 +02:00
eafe86e4c0 Merge branch 'master' into usrsctp-fix-notifications 2020-06-21 17:35:08 +02:00
a2b2126930 Added comment about SCTP_FRAGMENT_INTERLEAVE 2020-06-21 17:32:55 +02:00
b9a663de75 Fixed compilation warning for MSVC 2020-06-20 23:35:58 +02:00
f5faeba7b0 Fixed compilation warning for MSVC 2020-06-20 23:29:47 +02:00
98f02c5195 Fixed Jamfile 2020-06-20 21:09:33 +02:00
d8ab5b4820 Added MSVC support to Jamfile 2020-06-20 15:54:26 +02:00
b569e78c02 Changed Jamfile to use cmake 2020-06-20 15:36:11 +02:00
4eac16e053 Updated usrsctp 2020-06-20 15:12:28 +02:00
bb13b4bd37 Renamed mPartialData to mPartialMessage 2020-06-20 12:46:03 +02:00
aabb435fc7 Separate SCTP partial notification and partial data 2020-06-20 12:40:21 +02:00
3d7764c1e9 Merge pull request #95 from paullouisageneau/msvc
Add workflow for MSVC
2020-06-20 11:21:44 +02:00
37687c3cd6 Removed finished flag 2020-06-20 11:16:49 +02:00
63f10303e0 Disable server verification on Windows 2020-06-20 11:05:22 +02:00
d656d739f3 Disable server verification if system CA certificates not found 2020-06-20 10:55:49 +02:00
dbc706b69d Fixed socket error code EINPROGRESS 2020-06-20 10:36:04 +02:00
1985088f4f Renamed socket errno constants 2020-06-20 10:10:22 +02:00
ccbaa8beda Updated libjuice 2020-06-20 10:09:30 +02:00
642d304af9 Second fix for EWOULDBLOCK on Windows 2020-06-19 19:12:22 +02:00
df2102497d Fixed compilation 2020-06-19 18:51:01 +02:00
ad676815bd Fixed EWOULDBLOCK errno on Windows 2020-06-19 18:48:19 +02:00
4bd40799fd Fixed compilation for MSVC 2020-06-19 18:33:55 +02:00
4d1d1fa6fe Renamed workflows 2020-06-19 18:20:07 +02:00
50f61b19aa Moved C++ tests and examples to static lib on Windows 2020-06-19 18:17:53 +02:00
b7dbe7cdd9 Added RTC_EXPORT directive on C API 2020-06-19 18:07:12 +02:00
7bfd731ed3 Fixed compilation with MSVC 2020-06-19 16:19:49 +02:00
4da8b8c9a3 Renamed build-ubuntu to build-linux 2020-06-19 15:31:13 +02:00
0382067d92 Fixed compilation with MSVC 2020-06-19 15:29:25 +02:00
325230404a Added build with MSVC on Windows 2020-06-19 14:03:54 +02:00
2a721015f8 Updated libjuice 2020-06-19 11:56:19 +02:00
5752b17a6f Merge pull request #94 from paullouisageneau/capi-log-callback
Optional log callback in C API
2020-06-17 23:54:12 +02:00
878d15b746 Added optional callback to C API rtcInitLogger() 2020-06-17 16:29:56 +02:00
26d240e3ba Prefixed callback types 2020-06-17 15:20:46 +02:00
ca7fc8b26f Merge pull request #93 from paullouisageneau/fix-setuserpointer
Fix setUserPointer() not replacing pointer
2020-06-15 21:25:46 +02:00
35cb9ea4be Fixed setUserPointer() not replacing pointer 2020-06-15 21:21:41 +02:00
ec42736a2f Merge pull request #92 from paullouisageneau/fix-clang-analyser
Prevent clang analyser inner pointer used after reallocation
2020-06-15 16:09:16 +02:00
b4f7c506da Added consistent checks on size parameter 2020-06-15 16:04:15 +02:00
a577bb9004 Fixed clang analyser inner pointer used after reallocation 2020-06-15 15:54:54 +02:00
bfd858ab13 Removed localhost candidates hack for macOS 2020-06-14 12:40:14 +02:00
18fe326090 Updated libjuice 2020-06-14 12:39:18 +02:00
45 changed files with 910 additions and 496 deletions

View File

@ -1,4 +1,4 @@
name: Build and test with GnuTLS name: Build with GnuTLS
on: on:
push: push:
branches: branches:
@ -7,7 +7,7 @@ on:
branches: branches:
- master - master
jobs: jobs:
build-ubuntu: build-linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -31,9 +31,6 @@ jobs:
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: cmake - name: cmake
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1 run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
env:
# hack to bypass EPERM issue on sendto()
CFLAGS: -DJUICE_ENABLE_ADDRS_LOCALHOST
- name: make - name: make
run: (cd build; make -j2) run: (cd build; make -j2)
- name: test - name: test

View File

@ -1,4 +1,4 @@
name: Build and test with libnice name: Build with libnice
on: on:
push: push:
branches: branches:
@ -7,7 +7,7 @@ on:
branches: branches:
- master - master
jobs: jobs:
build-ubuntu: build-linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

View File

@ -1,4 +1,4 @@
name: Build and test with OpenSSL name: Build with OpenSSL
on: on:
push: push:
branches: branches:
@ -7,7 +7,7 @@ on:
branches: branches:
- master - master
jobs: jobs:
build-ubuntu: build-linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -34,9 +34,23 @@ jobs:
env: env:
OPENSSL_ROOT_DIR: /usr/local/opt/openssl OPENSSL_ROOT_DIR: /usr/local/opt/openssl
OPENSSL_LIBRARIES: /usr/local/opt/openssl/lib OPENSSL_LIBRARIES: /usr/local/opt/openssl/lib
# hack to bypass EPERM issue on sendto()
CFLAGS: -DJUICE_ENABLE_ADDRS_LOCALHOST
- name: make - name: make
run: (cd build; make -j2) run: (cd build; make -j2)
- name: test - name: test
run: ./build/tests run: ./build/tests
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: ilammy/msvc-dev-cmd@v1
- name: submodules
run: git submodule update --init --recursive
- name: cmake
run: cmake -B build -G "NMake Makefiles" -DUSE_JUICE=1 -DUSE_GNUTLS=0
- name: nmake
run: |
cd build
nmake
- name: test
run: build/tests.exe

2
.gitmodules vendored
View File

@ -1,6 +1,6 @@
[submodule "deps/plog"] [submodule "deps/plog"]
path = deps/plog path = deps/plog
url = https://github.com/SergiusTheBest/plog url = https://github.com/paullouisageneau/plog
[submodule "usrsctp"] [submodule "usrsctp"]
path = deps/usrsctp path = deps/usrsctp
url = https://github.com/sctplab/usrsctp.git url = https://github.com/sctplab/usrsctp.git

View File

@ -1,12 +1,13 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.7)
project(libdatachannel project(libdatachannel
DESCRIPTION "WebRTC DataChannels Library" DESCRIPTION "WebRTC Data Channels Library"
VERSION 0.6.2 VERSION 0.6.4
LANGUAGES CXX) LANGUAGES CXX)
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF) option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
option(USE_JUICE "Use libjuice instead of libnice" OFF) option(USE_JUICE "Use libjuice instead of libnice" OFF)
option(RTC_ENABLE_WEBSOCKET "Build WebSocket support" ON) option(USE_SRTP "Enable SRTP for media support" OFF)
option(NO_WEBSOCKET "Disable WebSocket support" OFF)
if(USE_GNUTLS) if(USE_GNUTLS)
option(USE_NETTLE "Use Nettle instead of OpenSSL in libjuice" ON) option(USE_NETTLE "Use Nettle instead of OpenSSL in libjuice" ON)
@ -21,6 +22,8 @@ if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN) add_definitions(-DWIN32_LEAN_AND_MEAN)
if (MSVC) if (MSVC)
add_definitions(-DNOMINMAX) add_definitions(-DNOMINMAX)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
endif() endif()
endif() endif()
@ -80,7 +83,6 @@ set(TESTS_SOURCES
set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(SRTP)
set(CMAKE_POLICY_DEFAULT_CMP0048 NEW) set(CMAKE_POLICY_DEFAULT_CMP0048 NEW)
add_subdirectory(deps/plog) add_subdirectory(deps/plog)
@ -97,7 +99,14 @@ endif()
add_library(Usrsctp::Usrsctp ALIAS usrsctp) add_library(Usrsctp::Usrsctp ALIAS usrsctp)
add_library(Usrsctp::UsrsctpStatic ALIAS usrsctp-static) add_library(Usrsctp::UsrsctpStatic ALIAS usrsctp-static)
if (RTC_ENABLE_WEBSOCKET) if (NO_WEBSOCKET)
add_library(datachannel SHARED
${LIBDATACHANNEL_SOURCES})
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
${LIBDATACHANNEL_SOURCES})
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=0)
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=0)
else()
add_library(datachannel SHARED add_library(datachannel SHARED
${LIBDATACHANNEL_SOURCES} ${LIBDATACHANNEL_SOURCES}
${LIBDATACHANNEL_WEBSOCKET_SOURCES}) ${LIBDATACHANNEL_WEBSOCKET_SOURCES})
@ -106,13 +115,6 @@ if (RTC_ENABLE_WEBSOCKET)
${LIBDATACHANNEL_WEBSOCKET_SOURCES}) ${LIBDATACHANNEL_WEBSOCKET_SOURCES})
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=1) target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=1)
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=1) target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=1)
else()
add_library(datachannel SHARED
${LIBDATACHANNEL_SOURCES})
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL
${LIBDATACHANNEL_SOURCES})
target_compile_definitions(datachannel PUBLIC RTC_ENABLE_WEBSOCKET=0)
target_compile_definitions(datachannel-static PUBLIC RTC_ENABLE_WEBSOCKET=0)
endif() endif()
set_target_properties(datachannel PROPERTIES set_target_properties(datachannel PROPERTIES
@ -139,7 +141,8 @@ if(WIN32)
target_link_libraries(datachannel-static PRIVATE wsock32 ws2_32) # winsock2 target_link_libraries(datachannel-static PRIVATE wsock32 ws2_32) # winsock2
endif() endif()
if(SRTP_FOUND) if(USE_SRTP)
find_package(SRTP REQUIRED)
if(NOT TARGET SRTP::SRTP) if(NOT TARGET SRTP::SRTP)
add_library(SRTP::SRTP UNKNOWN IMPORTED) add_library(SRTP::SRTP UNKNOWN IMPORTED)
set_target_properties(SRTP::SRTP PROPERTIES set_target_properties(SRTP::SRTP PROPERTIES
@ -205,7 +208,11 @@ set_target_properties(datachannel-tests PROPERTIES
CXX_STANDARD 17) CXX_STANDARD 17)
set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests) set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel-tests datachannel nlohmann_json::nlohmann_json) if(WIN32)
target_link_libraries(datachannel-tests datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-tests datachannel)
endif()
# Benchmark # Benchmark
add_executable(datachannel-benchmark test/benchmark.cpp) add_executable(datachannel-benchmark test/benchmark.cpp)
@ -215,7 +222,11 @@ set_target_properties(datachannel-benchmark PROPERTIES
set_target_properties(datachannel-benchmark PROPERTIES OUTPUT_NAME benchmark) set_target_properties(datachannel-benchmark PROPERTIES OUTPUT_NAME benchmark)
target_compile_definitions(datachannel-benchmark PRIVATE BENCHMARK_MAIN=1) target_compile_definitions(datachannel-benchmark PRIVATE BENCHMARK_MAIN=1)
target_include_directories(datachannel-benchmark PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(datachannel-benchmark PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel-benchmark datachannel) if(WIN32)
target_link_libraries(datachannel-benchmark datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-benchmark datachannel)
endif()
# Examples # Examples
set(JSON_BuildTests OFF CACHE INTERNAL "") set(JSON_BuildTests OFF CACHE INTERNAL "")

196
Jamfile
View File

@ -3,6 +3,12 @@ import feature : feature ;
project libdatachannel ; project libdatachannel ;
path-constant CWD : . ; path-constant CWD : . ;
feature gnutls : off on : composite propagated ;
feature.compose <gnutls>off
: <define>USE_GNUTLS=0 ;
feature.compose <gnutls>on
: <define>USE_GNUTLS=1 ;
lib libdatachannel lib libdatachannel
: # sources : # sources
[ glob ./src/*.cpp ] [ glob ./src/*.cpp ]
@ -12,25 +18,25 @@ lib libdatachannel
<define>USE_JUICE=1 <define>USE_JUICE=1
<define>RTC_ENABLE_MEDIA=0 <define>RTC_ENABLE_MEDIA=0
<define>RTC_ENABLE_WEBSOCKET=0 <define>RTC_ENABLE_WEBSOCKET=0
<toolset>msvc:<define>WIN32_LEAN_AND_MEAN
<toolset>msvc:<define>NOMINMAX
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
<toolset>msvc:<define>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
<library>/libdatachannel//usrsctp <library>/libdatachannel//usrsctp
<library>/libdatachannel//juice <library>/libdatachannel//juice
<library>/libdatachannel//plog <library>/libdatachannel//plog
<gnutls>on:<library>gnutls/<link>shared
<gnutls>off:<library>ssl
<gnutls>off:<library>crypto
: # default build : # default build
<link>static <link>static
: # usage requirements : # usage requirements
<include>./include <include>./include
<library>/libdatachannel//plog <library>/libdatachannel//plog
<cxxflags>-pthread <toolset>gcc:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
<toolset>gcc:<cxxflags>"-Wno-pedantic -Wno-unused-parameter -Wno-unused-variable" <toolset>clang:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
<toolset>clang:<cxxflags>"-Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
; ;
feature gnutls : off on : composite propagated ;
feature.compose <gnutls>off
: <define>USE_GNUTLS=0 ;
feature.compose <gnutls>on
: <define>USE_GNUTLS=1 ;
alias plog alias plog
: # no sources : # no sources
: # no build requirements : # no build requirements
@ -45,7 +51,16 @@ alias usrsctp
: # no default build : # no default build
: # usage requirements : # usage requirements
<include>./deps/usrsctp/usrsctplib <include>./deps/usrsctp/usrsctplib
<library>libusrsctp.a <library>libusrsctp.a
;
alias usrsctp
: # no sources
: <toolset>msvc
: # no default build
: # usage requirements
<include>./deps/usrsctp/usrsctplib
<library>usrsctp.lib
; ;
alias juice alias juice
@ -54,35 +69,168 @@ alias juice
: # no default build : # no default build
: # usage requirements : # usage requirements
<include>./deps/libjuice/include <include>./deps/libjuice/include
<library>libjuice.a <library>libjuice-static.a
<gnutls>on:<library>nettle
;
alias juice
: # no sources
: <toolset>msvc
: # no default build
: # usage requirements
<include>./deps/libjuice/include
<library>juice-static.lib
; ;
make libusrsctp.a : : @make_libusrsctp ; make libusrsctp.a : : @make_libusrsctp ;
make usrsctp.lib : : @make_libusrsctp_msvc ;
actions make_libusrsctp actions make_libusrsctp
{ {
(cd $(CWD)/deps/usrsctp && \ (cd $(CWD)/deps/usrsctp && mkdir build && cd build && cmake -DCMAKE_C_FLAGS="-fPIC" .. && make -j2 usrsctp-static)
./bootstrap && \ cp $(CWD)/deps/usrsctp/build/usrsctplib/libusrsctp.a $(<)
./configure --enable-static --disable-debug CFLAGS="-fPIC -Wno-address-of-packed-member" && \ }
make) actions make_libusrsctp_msvc
cp $(CWD)/deps/usrsctp/usrsctplib/.libs/libusrsctp.a $(<) {
SET OLDD=%CD%
cd $(CWD)/deps/usrsctp
mkdir build
cd build
cmake -G "Visual Studio 16 2019" ..
cd build
msbuild usrsctplib.sln /property:Configuration=Release
cd %OLDD%
cp $(CWD)/deps/usrsctp/build/usrsctplib/Release/usrsctp.lib $(<)
} }
make libjuice.a : : @make_libjuice ; make libjuice-static.a : : @make_libjuice ;
make juice-static.lib : : @make_libjuice_msvc ;
rule make_libjuice ( targets * : sources * : properties * ) rule make_libjuice ( targets * : sources * : properties * )
{ {
if <crypto>gnutls in $(properties) if <gnutls>on in $(properties)
{ {
MAKEOPTS on $(targets) = "USE_NETTLE=1" ; CMAKEOPTS on $(targets) = "-DUSE_NETTLE=1" ;
} }
else else {
{ local OPENSSL_INCLUDE = [ feature.get-values <openssl-include> : $(properties) ] ;
MAKEOPTS on $(targets) = "USE_NETTLE=0" ;
if <target-os>darwin in $(properties) && $(OPENSSL_INCLUDE) = ""
{
# on macOS, default to pick up openssl from the homebrew installation
# brew install openssl
OPENSSL_INCLUDE = /usr/local/opt/openssl/include ;
}
CMAKEOPTS on $(targets) = "-DUSE_NETTLE=0" ;
if $(OPENSSL_INCLUDE) != ""
{
CMAKEOPTS on $(targets) += " -DOPENSSL_ROOT_DIR=$(OPENSSL_INCLUDE)/.." ;
}
} }
} }
actions make_libjuice actions make_libjuice
{ {
(cd $(CWD)/deps/libjuice && make $(MAKEOPTS)) (cd $(CWD)/deps/libjuice && mkdir build && cd build && cmake -DCMAKE_C_FLAGS="-fPIC" $(CMAKEOPTS) .. && make -j2 juice-static)
cp $(CWD)/deps/libjuice/libjuice.a $(<) cp $(CWD)/deps/libjuice/build/libjuice-static.a $(<)
}
rule make_libjuice_msvc ( targets * : sources * : properties * )
{
if <gnutls>on in $(properties)
{
CMAKEOPTS on $(targets) = "-DUSE_NETTLE=1" ;
}
else
{
CMAKEOPTS on $(targets) = "-DUSE_NETTLE=0" ;
}
}
actions make_libjuice_msvc
{
SET OLDD=%CD%
cd $(CWD)/deps/libjuice
mkdir build
cd build
cmake -G "Visual Studio 16 2019" $(CMAKEOPTS) ..
msbuild libjuice.sln /property:Configuration=Release
cd %OLDD%
cp $(CWD)/deps/libjuice/build/Release/juice-static.lib $(<)
} }
# the search path to pick up the openssl libraries from. This is the <search>
# property of those libraries
rule openssl-lib-path ( properties * )
{
local OPENSSL_LIB = [ feature.get-values <openssl-lib> : $(properties) ] ;
if <target-os>darwin in $(properties) && $(OPENSSL_LIB) = ""
{
# on macOS, default to pick up openssl from the homebrew installation
# brew install openssl
OPENSSL_LIB = /usr/local/opt/openssl/lib ;
}
else if <target-os>windows in $(properties) && $(OPENSSL_LIB) = ""
{
# on windows, assume openssl is installed to c:\OpenSSL-Win32
if <address-model>64 in $(properties)
{ OPENSSL_LIB = c:\\OpenSSL-Win64\\lib ; }
else
{ OPENSSL_LIB = c:\\OpenSSL-Win32\\lib ; }
}
local result ;
result += <search>$(OPENSSL_LIB) ;
return $(result) ;
}
# the include path to pick up openssl headers from. This is the
# usage-requirement for the openssl-related libraries
rule openssl-include-path ( properties * )
{
local OPENSSL_INCLUDE = [ feature.get-values <openssl-include> : $(properties) ] ;
if <target-os>darwin in $(properties) && $(OPENSSL_INCLUDE) = ""
{
# on macOS, default to pick up openssl from the homebrew installation
# brew install openssl
OPENSSL_INCLUDE = /usr/local/opt/openssl/include ;
}
else if <target-os>windows in $(properties) && $(OPENSSL_INCLUDE) = ""
{
# on windows, assume openssl is installed to c:\OpenSSL-Win32
if <address-model>64 in $(properties)
{ OPENSSL_INCLUDE = c:\\OpenSSL-Win64\\include ; }
else
{ OPENSSL_INCLUDE = c:\\OpenSSL-Win32\\include ; }
}
local result ;
result += <include>$(OPENSSL_INCLUDE) ;
return $(result) ;
}
# libraries for OpenSSL on Windows
lib advapi32 : : <name>advapi32 ;
lib user32 : : <name>user32 ;
lib shell32 : : <name>shell32 ;
lib gdi32 : : <name>gdi32 ;
lib bcrypt : : <name>bcrypt ;
lib z : : <link>shared <name>z ;
alias ssl-deps : advapi32 user32 shell32 gdi32 ;
# OpenSSL on Windows
lib crypto : ssl-deps : <toolset>msvc <openssl-version>1.1 <name>libcrypto
<conditional>@openssl-lib-path : : <conditional>@openssl-include-path ;
lib ssl : ssl-deps : <toolset>msvc <openssl-version>1.1 <name>libssl <use>crypto
<conditional>@openssl-lib-path : : <conditional>@openssl-include-path ;
# OpenSSL on other platforms
lib crypto : : <name>crypto <use>z <conditional>@openssl-lib-path : :
<conditional>@openssl-include-path ;
lib ssl : : <name>ssl <use>crypto <conditional>@openssl-lib-path : :
<conditional>@openssl-include-path ;
# GnuTLS
lib gnutls : : <link>shared <name>gnutls ;
lib nettle : : <link>shared <name>nettle ;

View File

@ -38,22 +38,22 @@ else
LIBS+=glib-2.0 gobject-2.0 nice LIBS+=glib-2.0 gobject-2.0 nice
endif endif
RTC_ENABLE_MEDIA ?= 0 USE_SRTP ?= 0
ifneq ($(RTC_ENABLE_MEDIA), 0) ifneq ($(USE_SRTP), 0)
CPPFLAGS+=-DRTC_ENABLE_MEDIA=1 CPPFLAGS+=-DRTC_ENABLE_MEDIA=1
LIBS+=srtp LIBS+=srtp
else else
CPPFLAGS+=-DRTC_ENABLE_MEDIA=0 CPPFLAGS+=-DRTC_ENABLE_MEDIA=0
endif endif
RTC_ENABLE_WEBSOCKET ?= 1
ifneq ($(RTC_ENABLE_WEBSOCKET), 0) NO_WEBSOCKET ?= 0
ifeq ($(NO_WEBSOCKET), 0)
CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=1 CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=1
else else
CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=0 CPPFLAGS+=-DRTC_ENABLE_WEBSOCKET=0
endif endif
INCLUDES+=$(shell pkg-config --cflags $(LIBS)) INCLUDES+=$(shell pkg-config --cflags $(LIBS))
LDLIBS+=$(LOCALLIBS) $(shell pkg-config --libs $(LIBS)) LDLIBS+=$(LOCALLIBS) $(shell pkg-config --libs $(LIBS))

View File

@ -44,31 +44,55 @@ Features:
## Dependencies ## Dependencies
Dependencies:
- GnuTLS: https://www.gnutls.org/ or OpenSSL: https://www.openssl.org/ - GnuTLS: https://www.gnutls.org/ or OpenSSL: https://www.openssl.org/
Optional: Optional dependencies:
- libnice: https://nice.freedesktop.org/ (substituable with libjuice) - libnice: https://nice.freedesktop.org/ (substituable with libjuice)
- libSRTP: https://github.com/cisco/libsrtp - libSRTP: https://github.com/cisco/libsrtp (only necessary for media transport)
Submodules: Submodules:
- libjuice: https://github.com/paullouisageneau/libjuice - libjuice: https://github.com/paullouisageneau/libjuice
- usrsctp: https://github.com/sctplab/usrsctp - usrsctp: https://github.com/sctplab/usrsctp
## Building ## Building
### Building with CMake (preferred)
### Clone repository and submodules
```bash ```bash
$ git clone https://github.com/paullouisageneau/libdatachannel.git
$ cd libdatachannel
$ git submodule update --init --recursive $ git submodule update --init --recursive
$ mkdir build
$ cd build
$ cmake -DUSE_JUICE=1 -DUSE_GNUTLS=1 ..
$ make
``` ```
### Building directly with Make ### Building with CMake
The CMake library targets `libdatachannel` and `libdatachannel-static` respectively correspond to the shared and static libraries. On Windows, the DLL resulting from the shared library build only exposes the C API, use the static library for the C++ API. The default target will build tests and examples.
#### POSIX-compliant operating systems (including Linux and Apple macOS)
```bash
$ cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
$ cd build
$ make -j2
```
#### Microsoft Windows with MinGW cross-compilation
```bash
$ cmake -B build -DUSE_JUICE=1 -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake # replace with your toolchain file
$ cd build
$ make -j2
```
#### Microsoft Windows with Microsoft Visual C++
```bash
$ cmake -B build -G "NMake Makefiles" -DUSE_JUICE=1
$ cd build
$ nmake
```
### Building directly with Make (Linux only)
```bash ```bash
$ git submodule update --init --recursive
$ make USE_JUICE=1 USE_GNUTLS=1 $ make USE_JUICE=1 USE_GNUTLS=1
``` ```

2
deps/libjuice vendored

2
deps/plog vendored

Submodule deps/plog updated: 47883f0609...afb6f6f0e8

2
deps/usrsctp vendored

View File

@ -7,5 +7,11 @@ add_executable(datachannel-client main.cpp)
set_target_properties(datachannel-client PROPERTIES set_target_properties(datachannel-client PROPERTIES
CXX_STANDARD 17 CXX_STANDARD 17
OUTPUT_NAME client) OUTPUT_NAME client)
if(WIN32)
target_link_libraries(datachannel-client datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-client datachannel)
endif()
target_link_libraries(datachannel-client datachannel nlohmann_json) target_link_libraries(datachannel-client datachannel nlohmann_json)

View File

@ -212,7 +212,7 @@ string randomId(size_t length) {
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
string id(length, '0'); string id(length, '0');
default_random_engine rng(random_device{}()); default_random_engine rng(random_device{}());
uniform_int_distribution<int> dist(0, characters.size() - 1); uniform_int_distribution<int> dist(0, int(characters.size() - 1));
generate(id.begin(), id.end(), [&]() { return characters.at(dist(rng)); }); generate(id.begin(), id.end(), [&]() { return characters.at(dist(rng)); });
return id; return id;
} }

View File

@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.5.1)
project(offerer C) project(offerer C)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_C_FLAGS "-Wall -g -O2")
add_executable(datachannel-copy-paste-capi-offerer offerer.c) add_executable(datachannel-copy-paste-capi-offerer offerer.c)
set_target_properties(datachannel-copy-paste-capi-offerer PROPERTIES set_target_properties(datachannel-copy-paste-capi-offerer PROPERTIES
@ -13,3 +12,4 @@ add_executable(datachannel-copy-paste-capi-answerer answerer.c)
set_target_properties(datachannel-copy-paste-capi-answerer PROPERTIES set_target_properties(datachannel-copy-paste-capi-answerer PROPERTIES
OUTPUT_NAME answerer) OUTPUT_NAME answerer)
target_link_libraries(datachannel-copy-paste-capi-answerer datachannel) target_link_libraries(datachannel-copy-paste-capi-answerer datachannel)

View File

@ -41,39 +41,31 @@ typedef struct {
bool connected; bool connected;
} Peer; } Peer;
Peer *peer = NULL;
static void dataChannelCallback(int dc, void *ptr); static void dataChannelCallback(int dc, void *ptr);
static void descriptionCallback(const char *sdp, const char *type, void *ptr); static void descriptionCallback(const char *sdp, const char *type, void *ptr);
static void candidateCallback(const char *cand, const char *mid, void *ptr); static void candidateCallback(const char *cand, const char *mid, void *ptr);
static void stateChangeCallback(rtcState state, void *ptr); static void stateChangeCallback(rtcState state, void *ptr);
static void gatheringStateCallback(rtcGatheringState state, void *ptr); static void gatheringStateCallback(rtcGatheringState state, void *ptr);
static void closedCallback(void *ptr); static void closedCallback(void *ptr);
static void messageCallback(const char *message, int size, void *ptr); static void messageCallback(const char *message, int size, void *ptr);
static void deletePeer(Peer *peer); static void deletePeer(Peer *peer);
int all_space(const char *str);
char* state_print(rtcState state); char* state_print(rtcState state);
char* rtcGatheringState_print(rtcState state); char *rtcGatheringState_print(rtcGatheringState state);
int all_space(const char *str);
int main(int argc, char **argv) { int main(int argc, char **argv) {
rtcInitLogger(RTC_LOG_DEBUG); rtcInitLogger(RTC_LOG_DEBUG, NULL);
// Create peer // Create peer
rtcConfiguration config; rtcConfiguration config;
memset(&config, 0, sizeof(config)); memset(&config, 0, sizeof(config));
Peer *peer = (Peer *)malloc(sizeof(Peer)); Peer *peer = (Peer *)malloc(sizeof(Peer));
if (!peer) { if (!peer) {
fprintf(stderr, "Error allocating memory for peer\n"); fprintf(stderr, "Error allocating memory for peer\n");
return -1; return -1;
} }
memset(peer, 0, sizeof(Peer)); memset(peer, 0, sizeof(Peer));
@ -259,59 +251,59 @@ static void dataChannelCallback(int dc, void *ptr) {
printf("DataChannel %s: Received with label \"%s\"\n", "answerer", buffer); printf("DataChannel %s: Received with label \"%s\"\n", "answerer", buffer);
} }
char *state_print(rtcState state) {
char *str = NULL;
switch (state) {
case RTC_NEW:
str = "RTC_NEW";
break;
case RTC_CONNECTING:
str = "RTC_CONNECTING";
break;
case RTC_CONNECTED:
str = "RTC_CONNECTED";
break;
case RTC_DISCONNECTED:
str = "RTC_DISCONNECTED";
break;
case RTC_FAILED:
str = "RTC_FAILED";
break;
case RTC_CLOSED:
str = "RTC_CLOSED";
break;
default:
break;
}
return str;
}
char *rtcGatheringState_print(rtcGatheringState state) {
char *str = NULL;
switch (state) {
case RTC_GATHERING_NEW:
str = "RTC_GATHERING_NEW";
break;
case RTC_GATHERING_INPROGRESS:
str = "RTC_GATHERING_INPROGRESS";
break;
case RTC_GATHERING_COMPLETE:
str = "RTC_GATHERING_COMPLETE";
break;
default:
break;
}
return str;
}
int all_space(const char *str) { int all_space(const char *str) {
while (*str) { while (*str) {
if (!isspace(*str++)) { if (!isspace(*str++)) {
return 0; return 0;
} }
} }
return 1; return 1;
}
char* state_print(rtcState state) {
char *str = NULL;
switch (state) {
case RTC_NEW:
str = "RTC_NEW";
break;
case RTC_CONNECTING:
str = "RTC_CONNECTING";
break;
case RTC_CONNECTED:
str = "RTC_CONNECTED";
break;
case RTC_DISCONNECTED:
str = "RTC_DISCONNECTED";
break;
case RTC_FAILED:
str = "RTC_FAILED";
break;
case RTC_CLOSED:
str = "RTC_CLOSED";
break;
default:
break;
}
return str;
}
char* rtcGatheringState_print(rtcState state) {
char* str = NULL;
switch (state) {
case RTC_GATHERING_NEW:
str = "RTC_GATHERING_NEW";
break;
case RTC_GATHERING_INPROGRESS:
str = "RTC_GATHERING_INPROGRESS";
break;
case RTC_GATHERING_COMPLETE:
str = "RTC_GATHERING_COMPLETE";
break;
default:
break;
}
return str;
} }

View File

@ -1,10 +1,11 @@
// Simple POSIX getline() implementation // Simple POSIX getline() implementation
// This code is public domain // This code is public domain
#include "malloc.h" #include <malloc.h>
#include "stdio.h" #include <stddef.h>
#include <stdio.h>
ssize_t getline(char **lineptr, size_t *n, FILE *stream) { int getline(char **lineptr, size_t *n, FILE *stream) {
if (!lineptr || !stream || !n) if (!lineptr || !stream || !n)
return -1; return -1;
@ -20,7 +21,7 @@ ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
*n = 128; *n = 128;
} }
size_t pos = 0; int pos = 0;
while(c != EOF) { while(c != EOF) {
if (pos + 1 >= *n) { if (pos + 1 >= *n) {
size_t new_size = *n + (*n >> 2); size_t new_size = *n + (*n >> 2);
@ -45,4 +46,3 @@ ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
(*lineptr)[pos] = '\0'; (*lineptr)[pos] = '\0';
return pos; return pos;
} }

View File

@ -33,9 +33,6 @@ static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#include <unistd.h> // for sleep #include <unistd.h> // for sleep
#endif #endif
char* state_print(rtcState state);
char* rtcGatheringState_print(rtcState state);
typedef struct { typedef struct {
rtcState state; rtcState state;
rtcGatheringState gatheringState; rtcGatheringState gatheringState;
@ -44,37 +41,31 @@ typedef struct {
bool connected; bool connected;
} Peer; } Peer;
Peer *peer = NULL;
static void descriptionCallback(const char *sdp, const char *type, void *ptr); static void descriptionCallback(const char *sdp, const char *type, void *ptr);
static void candidateCallback(const char *cand, const char *mid, void *ptr); static void candidateCallback(const char *cand, const char *mid, void *ptr);
static void stateChangeCallback(rtcState state, void *ptr); static void stateChangeCallback(rtcState state, void *ptr);
static void gatheringStateCallback(rtcGatheringState state, void *ptr); static void gatheringStateCallback(rtcGatheringState state, void *ptr);
static void openCallback(void *ptr); static void openCallback(void *ptr);
static void closedCallback(void *ptr); static void closedCallback(void *ptr);
static void messageCallback(const char *message, int size, void *ptr); static void messageCallback(const char *message, int size, void *ptr);
static void deletePeer(Peer *peer); static void deletePeer(Peer *peer);
char *state_print(rtcState state);
char *rtcGatheringState_print(rtcGatheringState state);
int all_space(const char *str); int all_space(const char *str);
int main(int argc, char **argv){ int main(int argc, char **argv){
rtcInitLogger(RTC_LOG_DEBUG); rtcInitLogger(RTC_LOG_DEBUG, NULL);
// Create peer // Create peer
rtcConfiguration config; rtcConfiguration config;
memset(&config, 0, sizeof(config)); memset(&config, 0, sizeof(config));
Peer *peer = (Peer *)malloc(sizeof(Peer)); Peer *peer = (Peer *)malloc(sizeof(Peer));
if (!peer) { if (!peer) {
fprintf(stderr, "Error allocating memory for peer\n"); fprintf(stderr, "Error allocating memory for peer\n");
return -1; return -1;
} }
memset(peer, 0, sizeof(Peer)); memset(peer, 0, sizeof(Peer));
@ -263,60 +254,59 @@ static void deletePeer(Peer *peer) {
} }
} }
char *state_print(rtcState state) {
char *str = NULL;
switch (state) {
case RTC_NEW:
str = "RTC_NEW";
break;
case RTC_CONNECTING:
str = "RTC_CONNECTING";
break;
case RTC_CONNECTED:
str = "RTC_CONNECTED";
break;
case RTC_DISCONNECTED:
str = "RTC_DISCONNECTED";
break;
case RTC_FAILED:
str = "RTC_FAILED";
break;
case RTC_CLOSED:
str = "RTC_CLOSED";
break;
default:
break;
}
return str;
}
char *rtcGatheringState_print(rtcGatheringState state) {
char *str = NULL;
switch (state) {
case RTC_GATHERING_NEW:
str = "RTC_GATHERING_NEW";
break;
case RTC_GATHERING_INPROGRESS:
str = "RTC_GATHERING_INPROGRESS";
break;
case RTC_GATHERING_COMPLETE:
str = "RTC_GATHERING_COMPLETE";
break;
default:
break;
}
return str;
}
int all_space(const char *str) { int all_space(const char *str) {
while (*str) { while (*str) {
if (!isspace(*str++)) { if (!isspace(*str++)) {
return 0; return 0;
} }
} }
return 1; return 1;
}
char* state_print(rtcState state) {
char *str = NULL;
switch (state) {
case RTC_NEW:
str = "RTC_NEW";
break;
case RTC_CONNECTING:
str = "RTC_CONNECTING";
break;
case RTC_CONNECTED:
str = "RTC_CONNECTED";
break;
case RTC_DISCONNECTED:
str = "RTC_DISCONNECTED";
break;
case RTC_FAILED:
str = "RTC_FAILED";
break;
case RTC_CLOSED:
str = "RTC_CLOSED";
break;
default:
break;
}
return str;
}
char* rtcGatheringState_print(rtcState state) {
char* str = NULL;
switch (state) {
case RTC_GATHERING_NEW:
str = "RTC_GATHERING_NEW";
break;
case RTC_GATHERING_INPROGRESS:
str = "RTC_GATHERING_INPROGRESS";
break;
case RTC_GATHERING_COMPLETE:
str = "RTC_GATHERING_COMPLETE";
break;
default:
break;
}
return str;
} }

View File

@ -4,11 +4,19 @@ add_executable(datachannel-copy-paste-offerer offerer.cpp)
set_target_properties(datachannel-copy-paste-offerer PROPERTIES set_target_properties(datachannel-copy-paste-offerer PROPERTIES
CXX_STANDARD 17 CXX_STANDARD 17
OUTPUT_NAME offerer) OUTPUT_NAME offerer)
target_link_libraries(datachannel-copy-paste-offerer datachannel) if(WIN32)
target_link_libraries(datachannel-copy-paste-offerer datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-copy-paste-offerer datachannel)
endif()
add_executable(datachannel-copy-paste-answerer answerer.cpp) add_executable(datachannel-copy-paste-answerer answerer.cpp)
set_target_properties(datachannel-copy-paste-answerer PROPERTIES set_target_properties(datachannel-copy-paste-answerer PROPERTIES
CXX_STANDARD 17 CXX_STANDARD 17
OUTPUT_NAME answerer) OUTPUT_NAME answerer)
target_link_libraries(datachannel-copy-paste-answerer datachannel) if(WIN32)
target_link_libraries(datachannel-copy-paste-answerer datachannel-static) # DLL exports only the C API
else()
target_link_libraries(datachannel-copy-paste-answerer datachannel)
endif()

View File

@ -108,8 +108,8 @@ template <typename Iterator> bool DataChannel::sendBuffer(Iterator first, Iterat
auto message = std::make_shared<Message>(size); auto message = std::make_shared<Message>(size);
auto pos = message->begin(); auto pos = message->begin();
for (Iterator it = first; it != last; ++it) { for (Iterator it = first; it != last; ++it) {
auto [bytes, size] = to_bytes(*it); auto [bytes, len] = to_bytes(*it);
pos = std::copy(bytes, bytes + size, pos); pos = std::copy(bytes, bytes + len, pos);
} }
return outgoing(message); return outgoing(message);
} }

View File

@ -49,6 +49,7 @@ public:
bool ended() const; bool ended() const;
void hintType(Type type); void hintType(Type type);
void setDataMid(string mid);
void setFingerprint(string fingerprint); void setFingerprint(string fingerprint);
void setSctpPort(uint16_t port); void setSctpPort(uint16_t port);
void setMaxMessageSize(size_t size); void setMaxMessageSize(size_t size);

View File

@ -101,7 +101,7 @@ public:
// Media // Media
bool hasMedia() const; bool hasMedia() const;
void sendMedia(const binary &packet); void sendMedia(const binary &packet);
void send(const byte *packet, size_t size); void sendMedia(const byte *packet, size_t size);
void onMedia(std::function<void(const binary &packet)> callback); void onMedia(std::function<void(const binary &packet)> callback);

View File

@ -23,9 +23,11 @@
extern "C" { extern "C" {
#endif #endif
#include <stdint.h> #ifdef _WIN32
#define RTC_EXPORT __declspec(dllexport)
// libdatachannel C API #else
#define RTC_EXPORT
#endif
#ifndef RTC_ENABLE_MEDIA #ifndef RTC_ENABLE_MEDIA
#define RTC_ENABLE_MEDIA 1 #define RTC_ENABLE_MEDIA 1
@ -35,6 +37,10 @@ extern "C" {
#define RTC_ENABLE_WEBSOCKET 1 #define RTC_ENABLE_WEBSOCKET 1
#endif #endif
#include <stdint.h>
// libdatachannel C API
typedef enum { typedef enum {
RTC_NEW = 0, RTC_NEW = 0,
RTC_CONNECTING = 1, RTC_CONNECTING = 1,
@ -71,71 +77,72 @@ typedef struct {
uint16_t portRangeEnd; uint16_t portRangeEnd;
} rtcConfiguration; } rtcConfiguration;
typedef void (*dataChannelCallbackFunc)(int dc, void *ptr); typedef void (*rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
typedef void (*descriptionCallbackFunc)(const char *sdp, const char *type, void *ptr); typedef void (*rtcDataChannelCallbackFunc)(int dc, void *ptr);
typedef void (*candidateCallbackFunc)(const char *cand, const char *mid, void *ptr); typedef void (*rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
typedef void (*stateChangeCallbackFunc)(rtcState state, void *ptr); typedef void (*rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
typedef void (*gatheringStateCallbackFunc)(rtcGatheringState state, void *ptr); typedef void (*rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
typedef void (*openCallbackFunc)(void *ptr); typedef void (*rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
typedef void (*closedCallbackFunc)(void *ptr); typedef void (*rtcOpenCallbackFunc)(void *ptr);
typedef void (*errorCallbackFunc)(const char *error, void *ptr); typedef void (*rtcClosedCallbackFunc)(void *ptr);
typedef void (*messageCallbackFunc)(const char *message, int size, void *ptr); typedef void (*rtcErrorCallbackFunc)(const char *error, void *ptr);
typedef void (*bufferedAmountLowCallbackFunc)(void *ptr); typedef void (*rtcMessageCallbackFunc)(const char *message, int size, void *ptr);
typedef void (*availableCallbackFunc)(void *ptr); typedef void (*rtcBufferedAmountLowCallbackFunc)(void *ptr);
typedef void (*rtcAvailableCallbackFunc)(void *ptr);
// Log // Log
void rtcInitLogger(rtcLogLevel level); RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb); // NULL cb to log to stdout
// User pointer // User pointer
void rtcSetUserPointer(int id, void *ptr); RTC_EXPORT void rtcSetUserPointer(int id, void *ptr);
// PeerConnection // PeerConnection
int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id RTC_EXPORT int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id
int rtcDeletePeerConnection(int pc); RTC_EXPORT int rtcDeletePeerConnection(int pc);
int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb); RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb); RTC_EXPORT int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb);
int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb); RTC_EXPORT int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb);
int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb); RTC_EXPORT int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb);
int rtcSetGatheringStateChangeCallback(int pc, gatheringStateCallbackFunc cb); RTC_EXPORT int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb);
int rtcSetRemoteDescription(int pc, const char *sdp, const char *type); RTC_EXPORT int rtcSetRemoteDescription(int pc, const char *sdp, const char *type);
int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid); RTC_EXPORT int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid);
int rtcGetLocalAddress(int pc, char *buffer, int size); RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
int rtcGetRemoteAddress(int pc, char *buffer, int size); RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
// DataChannel // DataChannel
int rtcCreateDataChannel(int pc, const char *label); // returns dc id RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id
int rtcDeleteDataChannel(int dc); RTC_EXPORT int rtcDeleteDataChannel(int dc);
int rtcGetDataChannelLabel(int dc, char *buffer, int size); RTC_EXPORT int rtcGetDataChannelLabel(int dc, char *buffer, int size);
// WebSocket // WebSocket
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET
int rtcCreateWebSocket(const char *url); // returns ws id RTC_EXPORT int rtcCreateWebSocket(const char *url); // returns ws id
int rtcDeleteWebsocket(int ws); RTC_EXPORT int rtcDeleteWebsocket(int ws);
#endif #endif
// DataChannel and WebSocket common API // DataChannel and WebSocket common API
int rtcSetOpenCallback(int id, openCallbackFunc cb); RTC_EXPORT int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb);
int rtcSetClosedCallback(int id, closedCallbackFunc cb); RTC_EXPORT int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb);
int rtcSetErrorCallback(int id, errorCallbackFunc cb); RTC_EXPORT int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb);
int rtcSetMessageCallback(int id, messageCallbackFunc cb); RTC_EXPORT int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb);
int rtcSendMessage(int id, const char *data, int size); RTC_EXPORT int rtcSendMessage(int id, const char *data, int size);
int rtcGetBufferedAmount(int id); // total size buffered to send RTC_EXPORT int rtcGetBufferedAmount(int id); // total size buffered to send
int rtcSetBufferedAmountLowThreshold(int id, int amount); RTC_EXPORT int rtcSetBufferedAmountLowThreshold(int id, int amount);
int rtcSetBufferedAmountLowCallback(int id, bufferedAmountLowCallbackFunc cb); RTC_EXPORT int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb);
// DataChannel and WebSocket common extended API // DataChannel and WebSocket common extended API
int rtcGetAvailableAmount(int id); // total size available to receive RTC_EXPORT int rtcGetAvailableAmount(int id); // total size available to receive
int rtcSetAvailableCallback(int id, availableCallbackFunc cb); RTC_EXPORT int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb);
int rtcReceiveMessage(int id, char *buffer, int *size); RTC_EXPORT int rtcReceiveMessage(int id, char *buffer, int *size);
// Optional preload and cleanup // Optional preload and cleanup
void rtcPreload(); RTC_EXPORT void rtcPreload(void);
void rtcCleanup(); RTC_EXPORT void rtcCleanup(void);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

View File

@ -42,7 +42,7 @@ string to_base64(const binary &data) {
i += 3; i += 3;
} }
int left = data.size() - i; int left = int(data.size() - i);
if (left) { if (left) {
auto d0 = to_integer<uint8_t>(data[i]); auto d0 = to_integer<uint8_t>(data[i]);
out += tab[d0 >> 2]; out += tab[d0 >> 2];

View File

@ -28,6 +28,7 @@
#else #else
#include <netdb.h> #include <netdb.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h>
#endif #endif
#include <sys/types.h> #include <sys/types.h>
@ -98,8 +99,8 @@ bool Candidate::resolve(ResolveMode mode) {
// Rewrite the candidate // Rewrite the candidate
char nodebuffer[MAX_NUMERICNODE_LEN]; char nodebuffer[MAX_NUMERICNODE_LEN];
char servbuffer[MAX_NUMERICSERV_LEN]; char servbuffer[MAX_NUMERICSERV_LEN];
if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN, if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
servbuffer, MAX_NUMERICSERV_LEN, MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
NI_NUMERICHOST | NI_NUMERICSERV) == 0) { NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
const char sp{' '}; const char sp{' '};
std::ostringstream oss; std::ostringstream oss;
@ -107,8 +108,9 @@ bool Candidate::resolve(ResolveMode mode) {
oss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type; oss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type;
oss << left; oss << left;
mCandidate = oss.str(); mCandidate = oss.str();
mIsResolved = true;
PLOG_VERBOSE << "Resolved candidate: " << mCandidate; PLOG_VERBOSE << "Resolved candidate: " << mCandidate;
return mIsResolved = true; break;
} }
} }
} }
@ -116,7 +118,7 @@ bool Candidate::resolve(ResolveMode mode) {
freeaddrinfo(result); freeaddrinfo(result);
} }
return false; return mIsResolved;
} }
bool Candidate::isResolved() const { return mIsResolved; } bool Candidate::isResolved() const { return mIsResolved; }

View File

@ -132,14 +132,14 @@ namespace rtc {
Certificate::Certificate(string crt_pem, string key_pem) { Certificate::Certificate(string crt_pem, string key_pem) {
BIO *bio = BIO_new(BIO_s_mem()); BIO *bio = BIO_new(BIO_s_mem());
BIO_write(bio, crt_pem.data(), crt_pem.size()); BIO_write(bio, crt_pem.data(), int(crt_pem.size()));
mX509 = shared_ptr<X509>(PEM_read_bio_X509(bio, nullptr, 0, 0), X509_free); mX509 = shared_ptr<X509>(PEM_read_bio_X509(bio, nullptr, 0, 0), X509_free);
BIO_free(bio); BIO_free(bio);
if (!mX509) if (!mX509)
throw std::invalid_argument("Unable to import certificate PEM"); throw std::invalid_argument("Unable to import certificate PEM");
bio = BIO_new(BIO_s_mem()); bio = BIO_new(BIO_s_mem());
BIO_write(bio, key_pem.data(), key_pem.size()); BIO_write(bio, key_pem.data(), int(key_pem.size()));
mPKey = shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio, nullptr, 0, 0), EVP_PKEY_free); mPKey = shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio, nullptr, 0, 0), EVP_PKEY_free);
BIO_free(bio); BIO_free(bio);
if (!mPKey) if (!mPKey)
@ -233,8 +233,8 @@ namespace {
// Helper function roughly equivalent to std::async with policy std::launch::async // Helper function roughly equivalent to std::async with policy std::launch::async
// since std::async might be unreliable on some platforms (e.g. Mingw32 on Windows) // since std::async might be unreliable on some platforms (e.g. Mingw32 on Windows)
template <class F, class... Args> template <class F, class... Args>
std::future<std::result_of_t<std::decay_t<F>(std::decay_t<Args>...)>> thread_call(F &&f, std::future<std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>>
Args &&... args) { thread_call(F &&f, Args &&... args) {
using R = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>; using R = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>;
std::packaged_task<R()> task(std::bind(f, std::forward<Args>(args)...)); std::packaged_task<R()> task(std::bind(f, std::forward<Args>(args)...));
std::future<R> future = task.get_future(); std::future<R> future = task.get_future();

View File

@ -186,8 +186,8 @@ void DataChannel::open(shared_ptr<SctpTransport> transport) {
open.channelType = mReliability->type; open.channelType = mReliability->type;
open.priority = htons(0); open.priority = htons(0);
open.reliabilityParameter = htonl(reliabilityParameter); open.reliabilityParameter = htonl(reliabilityParameter);
open.labelLength = htons(mLabel.size()); open.labelLength = htons(uint16_t(mLabel.size()));
open.protocolLength = htons(mProtocol.size()); open.protocolLength = htons(uint16_t(mProtocol.size()));
auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage)); auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
std::copy(mLabel.begin(), mLabel.end(), end); std::copy(mLabel.begin(), mLabel.end(), end);

View File

@ -26,6 +26,7 @@
using std::size_t; using std::size_t;
using std::string; using std::string;
using std::chrono::system_clock;
namespace { namespace {
@ -54,7 +55,7 @@ Description::Description(const string &sdp, Type type, Role role)
mData.mid = "data"; mData.mid = "data";
hintType(type); hintType(type);
auto seed = std::chrono::system_clock::now().time_since_epoch().count(); auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
std::default_random_engine generator(seed); std::default_random_engine generator(seed);
std::uniform_int_distribution<uint32_t> uniform; std::uniform_int_distribution<uint32_t> uniform;
mSessionId = std::to_string(uniform(generator)); mSessionId = std::to_string(uniform(generator));
@ -112,7 +113,8 @@ Description::Description(const string &sdp, Type type, Role role)
if (match_prefix(value, "sha-256 ")) { if (match_prefix(value, "sha-256 ")) {
mFingerprint = value.substr(8); mFingerprint = value.substr(8);
std::transform(mFingerprint->begin(), mFingerprint->end(), std::transform(mFingerprint->begin(), mFingerprint->end(),
mFingerprint->begin(), [](char c) { return std::toupper(c); }); mFingerprint->begin(),
[](char c) { return char(std::toupper(c)); });
} else { } else {
PLOG_WARNING << "Unknown SDP fingerprint type: " << value; PLOG_WARNING << "Unknown SDP fingerprint type: " << value;
} }
@ -161,6 +163,8 @@ void Description::hintType(Type type) {
} }
} }
void Description::setDataMid(string mid) { mData.mid = mid; }
void Description::setFingerprint(string fingerprint) { void Description::setFingerprint(string fingerprint) {
mFingerprint.emplace(std::move(fingerprint)); mFingerprint.emplace(std::move(fingerprint));
} }
@ -185,11 +189,11 @@ std::vector<Candidate> Description::extractCandidates() {
bool Description::hasMedia() const { return !mMedia.empty(); } bool Description::hasMedia() const { return !mMedia.empty(); }
void Description::addMedia(const Description &source) { void Description::addMedia(const Description &source) {
for (auto [mid, media] : source.mMedia) for (auto p : source.mMedia)
if (mid != mData.mid) if (p.first != mData.mid)
mMedia.emplace(mid, media); mMedia.emplace(std::move(p));
else else
PLOG_WARNING << "Media mid \"" << mid << "\" is the same as data mid, ignoring"; PLOG_WARNING << "Media mid \"" << p.first << "\" is the same as data mid, ignoring";
} }
Description::operator string() const { return generateSdp("\r\n"); } Description::operator string() const { return generateSdp("\r\n"); }
@ -210,10 +214,32 @@ string Description::generateSdp(const string &eol) const {
// see Negotiating Media Multiplexing Using the Session Description Protocol // see Negotiating Media Multiplexing Using the Session Description Protocol
// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54
sdp << "a=group:BUNDLE"; sdp << "a=group:BUNDLE";
for (const auto &[mid, _] : mMedia) for (const auto &m : mMedia)
sdp << " " << mid; sdp << " " << m.first; // mid
sdp << " " << mData.mid << eol; sdp << " " << mData.mid << eol;
sdp << "a=msid-semantic: WMS" << eol;
// Non-data media
if (!mMedia.empty()) {
// Lip-sync
sdp << "a=group:LS";
for (const auto &m : mMedia)
sdp << " " << m.first; // mid
sdp << eol;
// Descriptions and attributes
for (const auto &m : mMedia) {
const auto &media = m.second;
sdp << "m=" << media.type << ' ' << 0 << ' ' << media.description << eol;
sdp << "c=IN IP4 0.0.0.0" << eol;
sdp << "a=bundle-only" << eol;
sdp << "a=mid:" << media.mid << eol;
for (const auto &attr : media.attributes)
sdp << "a=" << attr << eol;
}
}
// Data // Data
const string dataDescription = "UDP/DTLS/SCTP webrtc-datachannel"; const string dataDescription = "UDP/DTLS/SCTP webrtc-datachannel";
sdp << "m=application" << ' ' << (!mMedia.empty() ? 0 : 9) << ' ' << dataDescription << eol; sdp << "m=application" << ' ' << (!mMedia.empty() ? 0 : 9) << ' ' << dataDescription << eol;
@ -226,31 +252,16 @@ string Description::generateSdp(const string &eol) const {
if (mData.maxMessageSize) if (mData.maxMessageSize)
sdp << "a=max-message-size:" << *mData.maxMessageSize << eol; sdp << "a=max-message-size:" << *mData.maxMessageSize << eol;
// Non-data media
if (!mMedia.empty()) {
// Lip-sync
sdp << "a=group:LS";
for (const auto &[mid, _] : mMedia)
sdp << " " << mid;
sdp << eol;
// Descriptions and attributes
for (const auto &[_, media] : mMedia) {
sdp << "m=" << media.type << ' ' << 0 << ' ' << media.description << eol;
sdp << "c=IN IP4 0.0.0.0" << eol;
sdp << "a=bundle-only" << eol;
sdp << "a=mid:" << media.mid << eol;
for (const auto &attr : media.attributes)
sdp << "a=" << attr << eol;
}
}
// Common // Common
sdp << "a=ice-options:trickle" << eol; if (!mEnded)
sdp << "a=ice-options:trickle" << eol;
sdp << "a=ice-ufrag:" << mIceUfrag << eol; sdp << "a=ice-ufrag:" << mIceUfrag << eol;
sdp << "a=ice-pwd:" << mIcePwd << eol; sdp << "a=ice-pwd:" << mIcePwd << eol;
sdp << "a=setup:" << roleToString(mRole) << eol; sdp << "a=setup:" << roleToString(mRole) << eol;
sdp << "a=dtls-id:1" << eol; sdp << "a=tls-id:1" << eol;
if (mFingerprint) if (mFingerprint)
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol; sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;

View File

@ -43,50 +43,86 @@ DtlsSrtpTransport::DtlsSrtpTransport(std::shared_ptr<IceTransport> lower,
std::move(stateChangeCallback)), std::move(stateChangeCallback)),
mSrtpRecvCallback(std::move(srtpRecvCallback)) { // distinct from Transport recv callback mSrtpRecvCallback(std::move(srtpRecvCallback)) { // distinct from Transport recv callback
PLOG_DEBUG << "Initializing SRTP transport"; PLOG_DEBUG << "Initializing DTLS-SRTP transport";
#if USE_GNUTLS if (srtp_err_status_t err = srtp_create(&mSrtpIn, nullptr)) {
PLOG_DEBUG << "Initializing DTLS-SRTP transport (GnuTLS)"; throw std::runtime_error("SRTP create failed, status=" + to_string(static_cast<int>(err)));
gnutls::check(gnutls_srtp_set_profile(mSession, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80), }
"Failed to set SRTP profile"); if (srtp_err_status_t err = srtp_create(&mSrtpOut, nullptr)) {
#else srtp_dealloc(mSrtpIn);
PLOG_DEBUG << "Initializing DTLS-SRTP transport (OpenSSL)"; throw std::runtime_error("SRTP create failed, status=" + to_string(static_cast<int>(err)));
openssl::check(SSL_set_tlsext_use_srtp(mSsl, "SRTP_AES128_CM_SHA1_80"), }
"Failed to set SRTP profile");
#endif
} }
DtlsSrtpTransport::~DtlsSrtpTransport() { DtlsSrtpTransport::~DtlsSrtpTransport() {
stop(); stop();
if (mCreated) srtp_dealloc(mSrtpIn);
srtp_dealloc(mSrtp); srtp_dealloc(mSrtpOut);
} }
bool DtlsSrtpTransport::send(message_ptr message) { bool DtlsSrtpTransport::sendMedia(message_ptr message) {
if (!message) if (!message)
return false; return false;
if (!mInitDone) {
PLOG_WARNING << "SRTP media sent before keys are derived";
return false;
}
int size = message->size(); int size = message->size();
PLOG_VERBOSE << "Send size=" << size; PLOG_VERBOSE << "Send size=" << size;
// srtp_protect() assumes that it can write SRTP_MAX_TRAILER_LEN (for the authentication tag) // The RTP header has a minimum size of 12 bytes
// into the location in memory immediately following the RTP packet. if (size < 12)
throw std::runtime_error("RTP/RTCP packet too short");
// srtp_protect() and srtp_protect_rtcp() assume that they can write SRTP_MAX_TRAILER_LEN (for
// the authentication tag) into the location in memory immediately following the RTP packet.
message->resize(size + SRTP_MAX_TRAILER_LEN); message->resize(size + SRTP_MAX_TRAILER_LEN);
if (srtp_err_status_t err = srtp_protect(mSrtp, message->data(), &size)) {
if (err == srtp_err_status_replay_fail) uint8_t value2 = to_integer<uint8_t>(*(message->begin() + 1)) & 0x7F;
throw std::runtime_error("SRTP packet is a replay"); PLOG_VERBOSE << "Demultiplexing SRTCP and SRTP with RTP payload type, value="
else << unsigned(value2);
throw std::runtime_error("SRTP protect error, status=" +
to_string(static_cast<int>(err))); // RFC 5761 Multiplexing RTP and RTCP 4. Distinguishable RTP and RTCP Packets
// It is RECOMMENDED to follow the guidelines in the RTP/AVP profile for the choice of RTP
// payload type values, with the additional restriction that payload type values in the
// range 64-95 MUST NOT be used. Specifically, dynamic RTP payload types SHOULD be chosen in
// the range 96-127 where possible. Values below 64 MAY be used if that is insufficient
// [...]
if (value2 >= 64 && value2 <= 95) { // Range 64-95 (inclusive) MUST be RTCP
if (srtp_err_status_t err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)) {
if (err == srtp_err_status_replay_fail)
throw std::runtime_error("SRTCP packet is a replay");
else
throw std::runtime_error("SRTCP protect error, status=" +
to_string(static_cast<int>(err)));
}
PLOG_VERBOSE << "Protected SRTCP packet, size=" << size;
} else {
if (srtp_err_status_t err = srtp_protect(mSrtpOut, message->data(), &size)) {
if (err == srtp_err_status_replay_fail)
throw std::runtime_error("SRTP packet is a replay");
else
throw std::runtime_error("SRTP protect error, status=" +
to_string(static_cast<int>(err)));
}
PLOG_VERBOSE << "Protected SRTP packet, size=" << size;
} }
PLOG_VERBOSE << "Protected SRTP packet, size=" << size;
message->resize(size); message->resize(size);
outgoing(message); outgoing(message);
return true; return true;
} }
void DtlsSrtpTransport::incoming(message_ptr message) { void DtlsSrtpTransport::incoming(message_ptr message) {
if (!mInitDone) {
// Bypas
DtlsTransport::incoming(message);
return;
}
int size = message->size(); int size = message->size();
if (size == 0) if (size == 0)
return; return;
@ -95,49 +131,80 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
// The process for demultiplexing a packet is as follows. The receiver looks at the first byte // The process for demultiplexing a packet is as follows. The receiver looks at the first byte
// of the packet. [...] If the value is in between 128 and 191 (inclusive), then the packet is // of the packet. [...] If the value is in between 128 and 191 (inclusive), then the packet is
// RTP (or RTCP [...]). If the value is between 20 and 63 (inclusive), the packet is DTLS. // RTP (or RTCP [...]). If the value is between 20 and 63 (inclusive), the packet is DTLS.
uint8_t value = to_integer<uint8_t>(*message->begin()); uint8_t value1 = to_integer<uint8_t>(*message->begin());
PLOG_VERBOSE << "Demultiplexing DTLS and SRTP/SRTCP with first byte, value="
<< unsigned(value1);
if (value >= 128 && value <= 192) { if (value1 >= 20 && value1 <= 63) {
PLOG_VERBOSE << "Incoming DTLS packet, size=" << size; PLOG_VERBOSE << "Incoming DTLS packet, size=" << size;
DtlsTransport::incoming(message); DtlsTransport::incoming(message);
} else if (value >= 20 && value <= 64) {
PLOG_VERBOSE << "Incoming SRTP packet, size=" << size;
if (srtp_err_status_t err = srtp_unprotect(mSrtp, message->data(), &size)) { } else if (value1 >= 128 && value1 <= 191) {
if (err == srtp_err_status_replay_fail) // The RTP header has a minimum size of 12 bytes
PLOG_WARNING << "Incoming SRTP packet is a replay"; if (size < 12) {
else PLOG_WARNING << "Incoming SRTP/SRTCP packet too short, size=" << size;
PLOG_WARNING << "SRTP unprotect error, status=" << err;
return; return;
} }
PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size;
uint8_t value2 = to_integer<uint8_t>(*(message->begin() + 1)) & 0x7F;
PLOG_VERBOSE << "Demultiplexing SRTCP and SRTP with RTP payload type, value="
<< unsigned(value2);
// See RFC 5761 reference above
if (value2 >= 64 && value2 <= 95) { // Range 64-95 (inclusive) MUST be RTCP
PLOG_VERBOSE << "Incoming SRTCP packet, size=" << size;
if (srtp_err_status_t err = srtp_unprotect_rtcp(mSrtpIn, message->data(), &size)) {
if (err == srtp_err_status_replay_fail)
PLOG_WARNING << "Incoming SRTCP packet is a replay";
else
PLOG_WARNING << "SRTCP unprotect error, status=" << err;
return;
}
PLOG_VERBOSE << "Unprotected SRTCP packet, size=" << size;
} else {
PLOG_VERBOSE << "Incoming SRTP packet, size=" << size;
if (srtp_err_status_t err = srtp_unprotect(mSrtpIn, message->data(), &size)) {
if (err == srtp_err_status_replay_fail)
PLOG_WARNING << "Incoming SRTP packet is a replay";
else
PLOG_WARNING << "SRTP unprotect error, status=" << err;
return;
}
PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size;
}
message->resize(size); message->resize(size);
mSrtpRecvCallback(message); mSrtpRecvCallback(message);
} else { } else {
PLOG_WARNING << "Unknown packet type, value=" << value << ", size=" << size; PLOG_WARNING << "Unknown packet type, value=" << unsigned(value1) << ", size=" << size;
} }
} }
void DtlsSrtpTransport::postCreation() {
#if USE_GNUTLS
PLOG_DEBUG << "Setting SRTP profile (GnuTLS)";
gnutls::check(gnutls_srtp_set_profile(mSession, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80),
"Failed to set SRTP profile");
#else
PLOG_DEBUG << "Setting SRTP profile (OpenSSL)";
// returns 0 on success, 1 on error
if (SSL_set_tlsext_use_srtp(mSsl, "SRTP_AES128_CM_SHA1_80"), "Failed to set SRTP profile")
throw std::runtime_error("Failed to set SRTP profile: " + openssl::error_string(ERR_get_error()));
#endif
}
void DtlsSrtpTransport::postHandshake() { void DtlsSrtpTransport::postHandshake() {
if (mCreated) if (mInitDone)
return; return;
srtp_policy_t inbound = {};
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
inbound.ssrc.type = ssrc_any_inbound;
srtp_policy_t outbound = {};
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
outbound.ssrc.type = ssrc_any_outbound;
const size_t materialLen = SRTP_AES_ICM_128_KEY_LEN_WSALT * 2; const size_t materialLen = SRTP_AES_ICM_128_KEY_LEN_WSALT * 2;
unsigned char material[materialLen]; unsigned char material[materialLen];
const unsigned char *clientKey, *clientSalt, *serverKey, *serverSalt; const unsigned char *clientKey, *clientSalt, *serverKey, *serverSalt;
#if USE_GNUTLS #if USE_GNUTLS
PLOG_INFO << "Deriving SRTP keying material (GnuTLS)";
gnutls_datum_t clientKeyDatum, clientSaltDatum, serverKeyDatum, serverSaltDatum; gnutls_datum_t clientKeyDatum, clientSaltDatum, serverKeyDatum, serverSaltDatum;
gnutls::check(gnutls_srtp_get_keys(mSession, material, materialLen, &clientKeyDatum, gnutls::check(gnutls_srtp_get_keys(mSession, material, materialLen, &clientKeyDatum,
&clientSaltDatum, &serverKeyDatum, &serverSaltDatum), &clientSaltDatum, &serverKeyDatum, &serverSaltDatum),
@ -160,18 +227,23 @@ void DtlsSrtpTransport::postHandshake() {
serverKey = reinterpret_cast<const unsigned char *>(serverKeyDatum.data); serverKey = reinterpret_cast<const unsigned char *>(serverKeyDatum.data);
serverSalt = reinterpret_cast<const unsigned char *>(serverSaltDatum.data); serverSalt = reinterpret_cast<const unsigned char *>(serverSaltDatum.data);
#else #else
// This provides the client write master key, the server write master key, the client write PLOG_INFO << "Deriving SRTP keying material (OpenSSL)";
// master salt and the server write master salt in that order.
// The extractor provides the client write master key, the server write master key, the client
// write master salt and the server write master salt in that order.
const string label = "EXTRACTOR-dtls_srtp"; const string label = "EXTRACTOR-dtls_srtp";
openssl::check(SSL_export_keying_material(mSsl, material, materialLen, label.c_str(),
label.size(), nullptr, 0, 0), // returns 1 on success, 0 or -1 on failure (OpenSSL API is a complete mess...)
"Failed to derive SRTP keys"); if (SSL_export_keying_material(mSsl, material, materialLen, label.c_str(), label.size(),
nullptr, 0, 0) <= 0)
throw std::runtime_error("Failed to derive SRTP keys: " +
openssl::error_string(ERR_get_error()));
clientKey = material; clientKey = material;
clientSalt = clientKey + SRTP_AES_128_KEY_LEN; clientSalt = clientKey + SRTP_AES_128_KEY_LEN;
serverKey = material + SRTP_AES_ICM_128_KEY_LEN_WSALT; serverKey = material + SRTP_AES_ICM_128_KEY_LEN_WSALT;
serverSalt = serverSalt + SRTP_AES_128_KEY_LEN; serverSalt = serverKey + SRTP_AES_128_KEY_LEN;
#endif #endif
unsigned char clientSessionKey[SRTP_AES_ICM_128_KEY_LEN_WSALT]; unsigned char clientSessionKey[SRTP_AES_ICM_128_KEY_LEN_WSALT];
@ -182,22 +254,31 @@ void DtlsSrtpTransport::postHandshake() {
std::memcpy(serverSessionKey, serverKey, SRTP_AES_128_KEY_LEN); std::memcpy(serverSessionKey, serverKey, SRTP_AES_128_KEY_LEN);
std::memcpy(serverSessionKey + SRTP_AES_128_KEY_LEN, serverSalt, SRTP_SALT_LEN); std::memcpy(serverSessionKey + SRTP_AES_128_KEY_LEN, serverSalt, SRTP_SALT_LEN);
if (mIsClient) { srtp_policy_t inbound = {};
inbound.key = serverSessionKey; srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
outbound.key = clientSessionKey; srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
} else { inbound.ssrc.type = ssrc_any_inbound;
inbound.key = clientSessionKey; inbound.ssrc.value = 0;
outbound.key = serverSessionKey; inbound.key = mIsClient ? serverSessionKey : clientSessionKey;
} inbound.next = nullptr;
srtp_policy_t *policies = &inbound; if (srtp_err_status_t err = srtp_add_stream(mSrtpIn, &inbound))
inbound.next = &outbound; throw std::runtime_error("SRTP add inbound stream failed, status=" +
to_string(static_cast<int>(err)));
srtp_policy_t outbound = {};
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
outbound.ssrc.type = ssrc_any_outbound;
outbound.ssrc.value = 0;
outbound.key = mIsClient ? clientSessionKey : serverSessionKey;
outbound.next = nullptr; outbound.next = nullptr;
if (srtp_err_status_t err = srtp_create(&mSrtp, policies)) if (srtp_err_status_t err = srtp_add_stream(mSrtpOut, &outbound))
throw std::runtime_error("SRTP create failed, status=" + to_string(static_cast<int>(err))); throw std::runtime_error("SRTP add outbound stream failed, status=" +
to_string(static_cast<int>(err)));
mCreated = true; mInitDone = true;
} }
} // namespace rtc } // namespace rtc

View File

@ -38,16 +38,17 @@ public:
state_callback stateChangeCallback); state_callback stateChangeCallback);
~DtlsSrtpTransport(); ~DtlsSrtpTransport();
bool send(message_ptr message) override; bool sendMedia(message_ptr message);
private: private:
void incoming(message_ptr message) override; void incoming(message_ptr message) override;
void postCreation() override;
void postHandshake() override; void postHandshake() override;
message_callback mSrtpRecvCallback; message_callback mSrtpRecvCallback;
srtp_t mSrtp; srtp_t mSrtpIn, mSrtpOut;
bool mCreated = false; bool mInitDone = false;
}; };
} // namespace rtc } // namespace rtc

View File

@ -24,6 +24,14 @@
#include <exception> #include <exception>
#include <iostream> #include <iostream>
#if !USE_GNUTLS
#ifdef _WIN32
#include <winsock2.h> // for timeval
#else
#include <sys/time.h> // for timeval
#endif
#endif
using namespace std::chrono; using namespace std::chrono;
using std::shared_ptr; using std::shared_ptr;
@ -77,6 +85,8 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
gnutls_transport_set_pull_function(mSession, ReadCallback); gnutls_transport_set_pull_function(mSession, ReadCallback);
gnutls_transport_set_pull_timeout_function(mSession, TimeoutCallback); gnutls_transport_set_pull_timeout_function(mSession, TimeoutCallback);
postCreation();
mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this); mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
registerIncoming(); registerIncoming();
@ -129,6 +139,10 @@ void DtlsTransport::incoming(message_ptr message) {
mIncomingQueue.push(message); mIncomingQueue.push(message);
} }
void DtlsTransport::postCreation() {
// Dummy
}
void DtlsTransport::postHandshake() { void DtlsTransport::postHandshake() {
// Dummy // Dummy
} }
@ -300,7 +314,8 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)"; PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
try { try {
if (!(mCtx = SSL_CTX_new(DTLS_method()))) mCtx = SSL_CTX_new(DTLS_method());
if (!mCtx)
throw std::runtime_error("Failed to create SSL context"); throw std::runtime_error("Failed to create SSL context");
openssl::check(SSL_CTX_set_cipher_list(mCtx, "ALL:!LOW:!EXP:!RC4:!MD5:@STRENGTH"), openssl::check(SSL_CTX_set_cipher_list(mCtx, "ALL:!LOW:!EXP:!RC4:!MD5:@STRENGTH"),
@ -324,7 +339,8 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
openssl::check(SSL_CTX_check_private_key(mCtx), "SSL local private key check failed"); openssl::check(SSL_CTX_check_private_key(mCtx), "SSL local private key check failed");
if (!(mSsl = SSL_new(mCtx))) mSsl = SSL_new(mCtx);
if (!mSsl)
throw std::runtime_error("Failed to create SSL instance"); throw std::runtime_error("Failed to create SSL instance");
SSL_set_ex_data(mSsl, TransportExIndex, this); SSL_set_ex_data(mSsl, TransportExIndex, this);
@ -334,7 +350,9 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
else else
SSL_set_accept_state(mSsl); SSL_set_accept_state(mSsl);
if (!(mInBio = BIO_new(BIO_s_mem())) || !(mOutBio = BIO_new(BioMethods))) mInBio = BIO_new(BIO_s_mem());
mOutBio = BIO_new(BioMethods);
if (!mInBio || !mOutBio)
throw std::runtime_error("Failed to create BIO"); throw std::runtime_error("Failed to create BIO");
BIO_set_mem_eof_return(mInBio, BIO_EOF); BIO_set_mem_eof_return(mInBio, BIO_EOF);
@ -382,7 +400,7 @@ bool DtlsTransport::send(message_ptr message) {
PLOG_VERBOSE << "Send size=" << message->size(); PLOG_VERBOSE << "Send size=" << message->size();
int ret = SSL_write(mSsl, message->data(), message->size()); int ret = SSL_write(mSsl, message->data(), int(message->size()));
return openssl::check(mSsl, ret); return openssl::check(mSsl, ret);
} }
@ -396,6 +414,10 @@ void DtlsTransport::incoming(message_ptr message) {
mIncomingQueue.push(message); mIncomingQueue.push(message);
} }
void DtlsTransport::postCreation() {
// Dummy
}
void DtlsTransport::postHandshake() { void DtlsTransport::postHandshake() {
// Dummy // Dummy
} }
@ -416,11 +438,11 @@ void DtlsTransport::runRecvLoop() {
// Process pending messages // Process pending messages
while (!mIncomingQueue.empty()) { while (!mIncomingQueue.empty()) {
auto message = *mIncomingQueue.pop(); auto message = *mIncomingQueue.pop();
BIO_write(mInBio, message->data(), message->size()); BIO_write(mInBio, message->data(), int(message->size()));
if (state() == State::Connecting) { if (state() == State::Connecting) {
// Continue the handshake // Continue the handshake
int ret = SSL_do_handshake(mSsl); ret = SSL_do_handshake(mSsl);
if (!openssl::check(mSsl, ret, "Handshake failed")) if (!openssl::check(mSsl, ret, "Handshake failed"))
break; break;
@ -434,7 +456,7 @@ void DtlsTransport::runRecvLoop() {
postHandshake(); postHandshake();
} }
} else { } else {
int ret = SSL_read(mSsl, buffer, bufferSize); ret = SSL_read(mSsl, buffer, bufferSize);
if (!openssl::check(mSsl, ret)) if (!openssl::check(mSsl, ret))
break; break;
@ -447,7 +469,7 @@ void DtlsTransport::runRecvLoop() {
std::optional<milliseconds> duration; std::optional<milliseconds> duration;
if (state() == State::Connecting) { if (state() == State::Connecting) {
// Warning: This function breaks the usual return value convention // Warning: This function breaks the usual return value convention
int ret = DTLSv1_handle_timeout(mSsl); ret = DTLSv1_handle_timeout(mSsl);
if (ret < 0) { if (ret < 0) {
throw std::runtime_error("Handshake timeout"); // write BIO can't fail throw std::runtime_error("Handshake timeout"); // write BIO can't fail
} else if (ret > 0) { } else if (ret > 0) {
@ -486,7 +508,7 @@ void DtlsTransport::runRecvLoop() {
} }
} }
int DtlsTransport::CertificateCallback(int preverify_ok, X509_STORE_CTX *ctx) { int DtlsTransport::CertificateCallback(int /*preverify_ok*/, X509_STORE_CTX *ctx) {
SSL *ssl = SSL *ssl =
static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
DtlsTransport *t = DtlsTransport *t =
@ -535,7 +557,7 @@ int DtlsTransport::BioMethodWrite(BIO *bio, const char *in, int inl) {
return inl; // can't fail return inl; // can't fail
} }
long DtlsTransport::BioMethodCtrl(BIO *bio, int cmd, long num, void *ptr) { long DtlsTransport::BioMethodCtrl(BIO * /*bio*/, int cmd, long /*num*/, void * /*ptr*/) {
switch (cmd) { switch (cmd) {
case BIO_CTRL_FLUSH: case BIO_CTRL_FLUSH:
return 1; return 1;

View File

@ -52,6 +52,7 @@ public:
protected: protected:
virtual void incoming(message_ptr message) override; virtual void incoming(message_ptr message) override;
virtual void postCreation();
virtual void postHandshake(); virtual void postHandshake();
void runRecvLoop(); void runRecvLoop();

View File

@ -40,6 +40,7 @@ using namespace std::chrono_literals;
using std::shared_ptr; using std::shared_ptr;
using std::weak_ptr; using std::weak_ptr;
using std::chrono::system_clock;
#if USE_JUICE #if USE_JUICE
@ -69,7 +70,7 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
// Randomize servers order // Randomize servers order
std::vector<IceServer> servers = config.iceServers; std::vector<IceServer> servers = config.iceServers;
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed)); std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
// Pick a STUN server (TURN support is not implemented in libjuice yet) // Pick a STUN server (TURN support is not implemented in libjuice yet)
@ -82,7 +83,7 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
mStunHostname = server.hostname; mStunHostname = server.hostname;
mStunService = server.service; mStunService = server.service;
jconfig.stun_server_host = mStunHostname.c_str(); jconfig.stun_server_host = mStunHostname.c_str();
jconfig.stun_server_port = std::stoul(mStunService); jconfig.stun_server_port = uint16_t(std::stoul(mStunService));
break; break;
} }
} }
@ -206,7 +207,7 @@ void IceTransport::processCandidate(const string &candidate) {
void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); } void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); }
void IceTransport::StateChangeCallback(juice_agent_t *agent, juice_state_t state, void *user_ptr) { void IceTransport::StateChangeCallback(juice_agent_t *, juice_state_t state, void *user_ptr) {
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr); auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
try { try {
iceTransport->processStateChange(static_cast<unsigned int>(state)); iceTransport->processStateChange(static_cast<unsigned int>(state));
@ -215,7 +216,7 @@ void IceTransport::StateChangeCallback(juice_agent_t *agent, juice_state_t state
} }
} }
void IceTransport::CandidateCallback(juice_agent_t *agent, const char *sdp, void *user_ptr) { void IceTransport::CandidateCallback(juice_agent_t *, const char *sdp, void *user_ptr) {
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr); auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
try { try {
iceTransport->processCandidate(sdp); iceTransport->processCandidate(sdp);
@ -224,7 +225,7 @@ void IceTransport::CandidateCallback(juice_agent_t *agent, const char *sdp, void
} }
} }
void IceTransport::GatheringDoneCallback(juice_agent_t *agent, void *user_ptr) { void IceTransport::GatheringDoneCallback(juice_agent_t *, void *user_ptr) {
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr); auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
try { try {
iceTransport->processGatheringDone(); iceTransport->processGatheringDone();
@ -233,8 +234,7 @@ void IceTransport::GatheringDoneCallback(juice_agent_t *agent, void *user_ptr) {
} }
} }
void IceTransport::RecvCallback(juice_agent_t *agent, const char *data, size_t size, void IceTransport::RecvCallback(juice_agent_t *, const char *data, size_t size, void *user_ptr) {
void *user_ptr) {
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr); auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
try { try {
PLOG_VERBOSE << "Incoming size=" << size; PLOG_VERBOSE << "Incoming size=" << size;
@ -337,7 +337,7 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
// Randomize order // Randomize order
std::vector<IceServer> servers = config.iceServers; std::vector<IceServer> servers = config.iceServers;
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed)); std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
// Add one STUN server // Add one STUN server

View File

@ -60,7 +60,10 @@ void Init::Preload() {
make_certificate().wait(); // preload certificate make_certificate().wait(); // preload certificate
} }
void Init::Cleanup() { Global.reset(); } void Init::Cleanup() {
Global.reset();
CleanupCertificateCache();
}
Init::Init() { Init::Init() {
#ifdef _WIN32 #ifdef _WIN32
@ -86,7 +89,6 @@ Init::Init() {
} }
Init::~Init() { Init::~Init() {
CleanupCertificateCache();
SctpTransport::Cleanup(); SctpTransport::Cleanup();
DtlsTransport::Cleanup(); DtlsTransport::Cleanup();
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET

View File

@ -19,6 +19,8 @@
#include "log.hpp" #include "log.hpp"
#include "plog/Appenders/ColorConsoleAppender.h" #include "plog/Appenders/ColorConsoleAppender.h"
#include "plog/Formatters/TxtFormatter.h"
#include "plog/Init.h"
#include "plog/Log.h" #include "plog/Log.h"
#include "plog/Logger.h" #include "plog/Logger.h"

View File

@ -227,7 +227,7 @@ void PeerConnection::sendMedia(const binary &packet) {
outgoingMedia(make_message(packet.begin(), packet.end(), Message::Binary)); outgoingMedia(make_message(packet.begin(), packet.end(), Message::Binary));
} }
void PeerConnection::send(const byte *packet, size_t size) { void PeerConnection::sendMedia(const byte *packet, size_t size) {
outgoingMedia(make_message(packet, packet + size, Message::Binary)); outgoingMedia(make_message(packet, packet + size, Message::Binary));
} }
@ -244,7 +244,7 @@ void PeerConnection::outgoingMedia(message_ptr message) {
if (!transport) if (!transport)
throw std::runtime_error("PeerConnection is not open"); throw std::runtime_error("PeerConnection is not open");
std::dynamic_pointer_cast<DtlsSrtpTransport>(transport)->send(message); std::dynamic_pointer_cast<DtlsSrtpTransport>(transport)->sendMedia(message);
#else #else
PLOG_WARNING << "Ignoring sent media (not compiled with SRTP support)"; PLOG_WARNING << "Ignoring sent media (not compiled with SRTP support)";
#endif #endif
@ -483,7 +483,7 @@ void PeerConnection::forwardMessage(message_ptr message) {
return; return;
} }
auto channel = findDataChannel(message->stream); auto channel = findDataChannel(uint16_t(message->stream));
auto iceTransport = std::atomic_load(&mIceTransport); auto iceTransport = std::atomic_load(&mIceTransport);
auto sctpTransport = std::atomic_load(&mSctpTransport); auto sctpTransport = std::atomic_load(&mSctpTransport);
@ -594,14 +594,20 @@ void PeerConnection::remoteCloseDataChannels() {
void PeerConnection::processLocalDescription(Description description) { void PeerConnection::processLocalDescription(Description description) {
std::optional<uint16_t> remoteSctpPort; std::optional<uint16_t> remoteSctpPort;
if (auto remote = remoteDescription()) std::optional<string> remoteDataMid;
remoteSctpPort = remote->sctpPort(); if (auto remote = remoteDescription()) {
remoteDataMid = remote->dataMid();
remoteSctpPort = remote->sctpPort();
}
auto certificate = mCertificate.get(); // wait for certificate if not ready auto certificate = mCertificate.get(); // wait for certificate if not ready
{ {
std::lock_guard lock(mLocalDescriptionMutex); std::lock_guard lock(mLocalDescriptionMutex);
mLocalDescription.emplace(std::move(description)); mLocalDescription.emplace(std::move(description));
if (remoteDataMid)
mLocalDescription->setDataMid(*remoteDataMid);
mLocalDescription->setFingerprint(certificate->fingerprint()); mLocalDescription->setFingerprint(certificate->fingerprint());
mLocalDescription->setSctpPort(remoteSctpPort.value_or(DEFAULT_SCTP_PORT)); mLocalDescription->setSctpPort(remoteSctpPort.value_or(DEFAULT_SCTP_PORT));
mLocalDescription->setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE); mLocalDescription->setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
@ -658,13 +664,14 @@ void PeerConnection::resetCallbacks() {
mGatheringStateChangeCallback = nullptr; mGatheringStateChangeCallback = nullptr;
} }
bool PeerConnection::getSelectedCandidatePair(CandidateInfo *local, CandidateInfo *remote) { bool PeerConnection::getSelectedCandidatePair([[maybe_unused]] CandidateInfo *local,
#if not USE_JUICE [[maybe_unused]] CandidateInfo *remote) {
#if USE_JUICE
PLOG_WARNING << "getSelectedCandidatePair() is not implemented for libjuice";
return false;
#else
auto iceTransport = std::atomic_load(&mIceTransport); auto iceTransport = std::atomic_load(&mIceTransport);
return iceTransport->getSelectedCandidatePair(local, remote); return iceTransport->getSelectedCandidatePair(local, remote);
#else
PLOG_WARNING << "getSelectedCandidatePair is not implemented for libjuice";
return false;
#endif #endif
} }

View File

@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2019 Paul-Louis Ageneau * Copyright (c) 2019-2020 Paul-Louis Ageneau
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -18,14 +18,16 @@
#include "include.hpp" #include "include.hpp"
#include "datachannel.hpp" #include "rtc.h"
#include "peerconnection.hpp"
#include "datachannel.hpp"
#include "log.hpp"
#include "peerconnection.hpp"
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET
#include "websocket.hpp" #include "websocket.hpp"
#endif #endif
#include <rtc.h> #include "plog/Formatters/FuncMessageFormatter.h"
#include <exception> #include <exception>
#include <mutex> #include <mutex>
@ -33,6 +35,11 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#ifdef _WIN32
#include <codecvt>
#include <locale>
#endif
using namespace rtc; using namespace rtc;
using std::shared_ptr; using std::shared_ptr;
using std::string; using std::string;
@ -57,7 +64,7 @@ void *getUserPointer(int id) {
void setUserPointer(int i, void *ptr) { void setUserPointer(int i, void *ptr) {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
if (ptr) if (ptr)
userPointerMap.insert(std::make_pair(i, ptr)); userPointerMap[i] = ptr;
else else
userPointerMap.erase(i); userPointerMap.erase(i);
} }
@ -143,7 +150,7 @@ shared_ptr<Channel> getChannel(int id) {
template <typename F> int wrap(F func) { template <typename F> int wrap(F func) {
try { try {
return func(); return int(func());
} catch (const std::invalid_argument &e) { } catch (const std::invalid_argument &e) {
PLOG_ERROR << e.what(); PLOG_ERROR << e.what();
@ -160,9 +167,48 @@ template <typename F> int wrap(F func) {
return RTC_ERR_SUCCESS; \ return RTC_ERR_SUCCESS; \
}) })
class plog_appender : public plog::IAppender {
public:
plog_appender(rtcLogCallbackFunc cb = nullptr) { set_callback(cb); }
void set_callback(rtcLogCallbackFunc cb) {
std::lock_guard lock(mutex);
callback = cb;
}
void write(const plog::Record &record) override {
plog::Severity severity = record.getSeverity();
auto formatted = plog::FuncMessageFormatter::format(record);
formatted.pop_back(); // remove newline
#ifdef _WIN32
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
std::string str = converter.to_bytes(formatted);
#else
std::string str = formatted;
#endif
std::lock_guard lock(mutex);
if (callback)
callback(static_cast<rtcLogLevel>(record.getSeverity()), str.c_str());
else
std::cout << plog::severityToString(severity) << " " << str << std::endl;
}
private:
rtcLogCallbackFunc callback;
};
} // namespace } // namespace
void rtcInitLogger(rtcLogLevel level) { InitLogger(static_cast<LogLevel>(level)); } void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
static std::optional<plog_appender> appender;
if (appender)
appender->set_callback(cb);
else if (cb)
appender.emplace(plog_appender(cb));
InitLogger(static_cast<plog::Severity>(level), appender ? &appender.value() : nullptr);
}
void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); } void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
@ -241,7 +287,7 @@ int rtcDeleteWebsocket(int ws) {
} }
#endif #endif
int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb) { int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb) {
return WRAP({ return WRAP({
auto peerConnection = getPeerConnection(pc); auto peerConnection = getPeerConnection(pc);
if (cb) if (cb)
@ -256,7 +302,7 @@ int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb) {
}); });
} }
int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb) { int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) {
return WRAP({ return WRAP({
auto peerConnection = getPeerConnection(pc); auto peerConnection = getPeerConnection(pc);
if (cb) if (cb)
@ -268,7 +314,7 @@ int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb) {
}); });
} }
int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb) { int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb) {
return WRAP({ return WRAP({
auto peerConnection = getPeerConnection(pc); auto peerConnection = getPeerConnection(pc);
if (cb) if (cb)
@ -280,7 +326,7 @@ int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb) {
}); });
} }
int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb) { int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb) {
return WRAP({ return WRAP({
auto peerConnection = getPeerConnection(pc); auto peerConnection = getPeerConnection(pc);
if (cb) if (cb)
@ -292,7 +338,7 @@ int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb) {
}); });
} }
int rtcSetGatheringStateChangeCallback(int pc, gatheringStateCallbackFunc cb) { int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb) {
return WRAP({ return WRAP({
auto peerConnection = getPeerConnection(pc); auto peerConnection = getPeerConnection(pc);
if (cb) if (cb)
@ -333,9 +379,13 @@ int rtcGetLocalAddress(int pc, char *buffer, int size) {
if (!buffer) if (!buffer)
throw std::invalid_argument("Unexpected null pointer"); throw std::invalid_argument("Unexpected null pointer");
if (size <= 0)
return 0;
if (auto addr = peerConnection->localAddress()) { if (auto addr = peerConnection->localAddress()) {
size = std::min(size_t(size - 1), addr->size()); const char *data = addr->data();
std::copy(addr->data(), addr->data() + size, buffer); size = std::min(size - 1, int(addr->size()));
std::copy(data, data + size, buffer);
buffer[size] = '\0'; buffer[size] = '\0';
return size + 1; return size + 1;
} }
@ -349,11 +399,15 @@ int rtcGetRemoteAddress(int pc, char *buffer, int size) {
if (!buffer) if (!buffer)
throw std::invalid_argument("Unexpected null pointer"); throw std::invalid_argument("Unexpected null pointer");
if (size <= 0)
return 0;
if (auto addr = peerConnection->remoteAddress()) { if (auto addr = peerConnection->remoteAddress()) {
size = std::min(size_t(size - 1), addr->size()); const char *data = addr->data();
std::copy(addr->data(), addr->data() + size, buffer); size = std::min(size - 1, int(addr->size()));
std::copy(data, data + size, buffer);
buffer[size] = '\0'; buffer[size] = '\0';
return size + 1; return int(size + 1);
} }
}); });
} }
@ -365,19 +419,19 @@ int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
if (!buffer) if (!buffer)
throw std::invalid_argument("Unexpected null pointer"); throw std::invalid_argument("Unexpected null pointer");
if (size >= 0) { if (size <= 0)
string label = dataChannel->label();
size = std::min(size_t(size - 1), label.size());
std::copy(label.data(), label.data() + size, buffer);
buffer[size] = '\0';
return size + 1;
} else {
return 0; return 0;
}
string label = dataChannel->label();
const char *data = label.data();
size = std::min(size - 1, int(label.size()));
std::copy(data, data + size, buffer);
buffer[size] = '\0';
return int(size + 1);
}); });
} }
int rtcSetOpenCallback(int id, openCallbackFunc cb) { int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb) {
return WRAP({ return WRAP({
auto channel = getChannel(id); auto channel = getChannel(id);
if (cb) if (cb)
@ -387,7 +441,7 @@ int rtcSetOpenCallback(int id, openCallbackFunc cb) {
}); });
} }
int rtcSetClosedCallback(int id, closedCallbackFunc cb) { int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb) {
return WRAP({ return WRAP({
auto channel = getChannel(id); auto channel = getChannel(id);
if (cb) if (cb)
@ -397,7 +451,7 @@ int rtcSetClosedCallback(int id, closedCallbackFunc cb) {
}); });
} }
int rtcSetErrorCallback(int id, errorCallbackFunc cb) { int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb) {
return WRAP({ return WRAP({
auto channel = getChannel(id); auto channel = getChannel(id);
if (cb) if (cb)
@ -408,13 +462,13 @@ int rtcSetErrorCallback(int id, errorCallbackFunc cb) {
}); });
} }
int rtcSetMessageCallback(int id, messageCallbackFunc cb) { int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb) {
return WRAP({ return WRAP({
auto channel = getChannel(id); auto channel = getChannel(id);
if (cb) if (cb)
channel->onMessage( channel->onMessage(
[id, cb](const binary &b) { [id, cb](const binary &b) {
cb(reinterpret_cast<const char *>(b.data()), b.size(), getUserPointer(id)); cb(reinterpret_cast<const char *>(b.data()), int(b.size()), getUserPointer(id));
}, },
[id, cb](const string &s) { cb(s.c_str(), -1, getUserPointer(id)); }); [id, cb](const string &s) { cb(s.c_str(), -1, getUserPointer(id)); });
else else
@ -435,7 +489,7 @@ int rtcSendMessage(int id, const char *data, int size) {
return size; return size;
} else { } else {
string str(data); string str(data);
int len = str.size(); int len = int(str.size());
channel->send(std::move(str)); channel->send(std::move(str));
return len; return len;
} }
@ -456,7 +510,7 @@ int rtcSetBufferedAmountLowThreshold(int id, int amount) {
}); });
} }
int rtcSetBufferedAmountLowCallback(int id, bufferedAmountLowCallbackFunc cb) { int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb) {
return WRAP({ return WRAP({
auto channel = getChannel(id); auto channel = getChannel(id);
if (cb) if (cb)
@ -470,7 +524,7 @@ int rtcGetAvailableAmount(int id) {
return WRAP({ return int(getChannel(id)->availableAmount()); }); return WRAP({ return int(getChannel(id)->availableAmount()); });
} }
int rtcSetAvailableCallback(int id, availableCallbackFunc cb) { int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb) {
return WRAP({ return WRAP({
auto channel = getChannel(id); auto channel = getChannel(id);
if (cb) if (cb)

View File

@ -194,6 +194,11 @@ SctpTransport::~SctpTransport() {
} }
bool SctpTransport::stop() { bool SctpTransport::stop() {
// Transport::stop() will unregister incoming() from the lower layer, therefore we need to make
// sure the thread from lower layers is not blocked in incoming() by the WrittenOnce condition.
mWrittenOnce = true;
mWrittenCondition.notify_all();
if (!Transport::stop()) if (!Transport::stop())
return false; return false;
@ -262,7 +267,7 @@ bool SctpTransport::send(message_ptr message) {
return true; return true;
mSendQueue.push(message); mSendQueue.push(message);
updateBufferedAmount(message->stream, message_size_func(message)); updateBufferedAmount(uint16_t(message->stream), long(message_size_func(message)));
return false; return false;
} }
@ -302,7 +307,7 @@ bool SctpTransport::trySendQueue() {
if (!trySendMessage(message)) if (!trySendMessage(message))
return false; return false;
mSendQueue.pop(); mSendQueue.pop();
updateBufferedAmount(message->stream, -message_size_func(message)); updateBufferedAmount(uint16_t(message->stream), -long(message_size_func(message)));
} }
return true; return true;
} }
@ -324,7 +329,7 @@ bool SctpTransport::trySendMessage(message_ptr message) {
ppid = PPID_CONTROL; ppid = PPID_CONTROL;
break; break;
case Message::Reset: case Message::Reset:
sendReset(message->stream); sendReset(uint16_t(message->stream));
return true; return true;
default: default:
// Ignore // Ignore
@ -448,32 +453,46 @@ bool SctpTransport::safeFlush() {
} }
} }
int SctpTransport::handleRecv(struct socket *sock, union sctp_sockstore addr, const byte *data, int SctpTransport::handleRecv(struct socket * /*sock*/, union sctp_sockstore /*addr*/,
size_t len, struct sctp_rcvinfo info, int flags) { const byte *data, size_t len, struct sctp_rcvinfo info, int flags) {
try { try {
PLOG_VERBOSE << "Handle recv, len=" << len; PLOG_VERBOSE << "Handle recv, len=" << len;
if (!len) if (!len)
return -1; return 0; // Ignore
// This is valid because SCTP_FRAGMENT_INTERLEAVE is set to level 0 // SCTP_FRAGMENT_INTERLEAVE does not seem to work as expected for messages > 64KB,
// so partial messages and notifications may not be interleaved. // therefore partial notifications and messages need to be handled separately.
if (flags & MSG_EOR) { if (flags & MSG_NOTIFICATION) {
if (!mPartialRecv.empty()) { // SCTP event notification
mPartialRecv.insert(mPartialRecv.end(), data, data + len); if (flags & MSG_EOR) {
data = mPartialRecv.data(); if (!mPartialNotification.empty()) {
len = mPartialRecv.size(); mPartialNotification.insert(mPartialNotification.end(), data, data + len);
} data = mPartialNotification.data();
// Message/Notification is complete, process it len = mPartialNotification.size();
if (flags & MSG_NOTIFICATION) }
// Notification is complete, process it
processNotification(reinterpret_cast<const union sctp_notification *>(data), len); processNotification(reinterpret_cast<const union sctp_notification *>(data), len);
else mPartialNotification.clear();
processData(data, len, info.rcv_sid, PayloadId(htonl(info.rcv_ppid))); } else {
mPartialNotification.insert(mPartialNotification.end(), data, data + len);
}
mPartialRecv.clear();
} else { } else {
// Message/Notification is not complete // SCTP message
mPartialRecv.insert(mPartialRecv.end(), data, data + len); if (flags & MSG_EOR) {
if (!mPartialMessage.empty()) {
mPartialMessage.insert(mPartialMessage.end(), data, data + len);
data = mPartialMessage.data();
len = mPartialMessage.size();
}
// Message is complete, process it
processData(data, len, info.rcv_sid, PayloadId(htonl(info.rcv_ppid)));
mPartialMessage.clear();
} else {
mPartialMessage.insert(mPartialMessage.end(), data, data + len);
}
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
PLOG_ERROR << "SCTP recv: " << e.what(); PLOG_ERROR << "SCTP recv: " << e.what();
return -1; return -1;
@ -486,7 +505,7 @@ int SctpTransport::handleSend(size_t free) {
return safeFlush() ? 0 : -1; return safeFlush() ? 0 : -1;
} }
int SctpTransport::handleWrite(byte *data, size_t len, uint8_t tos, uint8_t set_df) { int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t /*set_df*/) {
try { try {
PLOG_VERBOSE << "Handle write, len=" << len; PLOG_VERBOSE << "Handle write, len=" << len;

View File

@ -99,7 +99,8 @@ private:
std::atomic<bool> mWritten = false; // written outside lock std::atomic<bool> mWritten = false; // written outside lock
std::atomic<bool> mWrittenOnce = false; // same std::atomic<bool> mWrittenOnce = false; // same
binary mPartialRecv, mPartialStringData, mPartialBinaryData; binary mPartialMessage, mPartialNotification;
binary mPartialStringData, mPartialBinaryData;
// Stats // Stats
std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0; std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0;

View File

@ -21,6 +21,7 @@
#if RTC_ENABLE_WEBSOCKET #if RTC_ENABLE_WEBSOCKET
#include <exception> #include <exception>
#ifndef _WIN32 #ifndef _WIN32
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
@ -150,7 +151,7 @@ void TcpTransport::connect(const string &hostname, const string &service) {
for (auto p = result; p; p = p->ai_next) { for (auto p = result; p; p = p->ai_next) {
try { try {
connect(p->ai_addr, p->ai_addrlen); connect(p->ai_addr, socklen_t(p->ai_addrlen));
PLOG_INFO << "Connected to " << hostname << ":" << service; PLOG_INFO << "Connected to " << hostname << ":" << service;
freeaddrinfo(result); freeaddrinfo(result);
@ -201,7 +202,7 @@ void TcpTransport::connect(const sockaddr *addr, socklen_t addrlen) {
// Initiate connection // Initiate connection
int ret = ::connect(mSock, addr, addrlen); int ret = ::connect(mSock, addr, addrlen);
if (ret < 0 && errno != EINPROGRESS) { if (ret < 0 && sockerrno != SEINPROGRESS && sockerrno != SEWOULDBLOCK) {
std::ostringstream msg; std::ostringstream msg;
msg << "TCP connection to " << node << ":" << serv << " failed, errno=" << sockerrno; msg << "TCP connection to " << node << ":" << serv << " failed, errno=" << sockerrno;
throw std::runtime_error(msg.str()); throw std::runtime_error(msg.str());
@ -271,14 +272,14 @@ bool TcpTransport::trySendMessage(message_ptr &message) {
auto data = reinterpret_cast<const char *>(message->data()); auto data = reinterpret_cast<const char *>(message->data());
auto size = message->size(); auto size = message->size();
while (size) { while (size) {
#if defined(__APPLE__) or defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32)
int flags = 0; int flags = 0;
#else #else
int flags = MSG_NOSIGNAL; int flags = MSG_NOSIGNAL;
#endif #endif
int len = ::send(mSock, data, size, flags); int len = ::send(mSock, data, int(size), flags);
if (len < 0) { if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK) {
message = make_message(message->end() - size, message->end()); message = make_message(message->end() - size, message->end());
return false; return false;
} else { } else {
@ -335,7 +336,7 @@ void TcpTransport::runLoop() {
char buffer[bufferSize]; char buffer[bufferSize];
int len = ::recv(mSock, buffer, bufferSize, 0); int len = ::recv(mSock, buffer, bufferSize, 0);
if (len < 0) { if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK) {
continue; continue;
} else { } else {
throw std::runtime_error("Connection lost"); throw std::runtime_error("Connection lost");

View File

@ -48,6 +48,11 @@ gnutls_datum_t make_datum(char *data, size_t size);
#else // USE_GNUTLS==0 #else // USE_GNUTLS==0
#ifdef _WIN32
// Include winsock2.h header first since OpenSSL may include winsock.h
#include <winsock2.h>
#endif
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/bio.h> #include <openssl/bio.h>

View File

@ -269,9 +269,19 @@ TlsTransport::TlsTransport(shared_ptr<TcpTransport> lower, string host, state_ca
SSL_CTX_set_quiet_shutdown(mCtx, 1); SSL_CTX_set_quiet_shutdown(mCtx, 1);
SSL_CTX_set_info_callback(mCtx, InfoCallback); SSL_CTX_set_info_callback(mCtx, InfoCallback);
SSL_CTX_set_default_verify_paths(mCtx); // SSL_CTX_set_default_verify_paths() does nothing on Windows
SSL_CTX_set_verify(mCtx, SSL_VERIFY_PEER, NULL); #ifndef _WIN32
SSL_CTX_set_verify_depth(mCtx, 4); if (SSL_CTX_set_default_verify_paths(mCtx)) {
#else
if (false) {
#endif
PLOG_INFO << "SSL root CA certificates available, server verification enabled";
SSL_CTX_set_verify(mCtx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify_depth(mCtx, 4);
} else {
PLOG_WARNING << "SSL root CA certificates unavailable, server verification disabled";
SSL_CTX_set_verify(mCtx, SSL_VERIFY_NONE, NULL);
}
if (!(mSsl = SSL_new(mCtx))) if (!(mSsl = SSL_new(mCtx)))
throw std::runtime_error("Failed to create SSL instance"); throw std::runtime_error("Failed to create SSL instance");
@ -337,7 +347,7 @@ bool TlsTransport::send(message_ptr message) {
if (message->size() == 0) if (message->size() == 0)
return true; return true;
int ret = SSL_write(mSsl, message->data(), message->size()); int ret = SSL_write(mSsl, message->data(), int(message->size()));
if (!openssl::check(mSsl, ret)) if (!openssl::check(mSsl, ret))
return false; return false;
@ -393,7 +403,7 @@ void TlsTransport::runRecvLoop() {
message_ptr message = *next; message_ptr message = *next;
if (message->size() > 0) if (message->size() > 0)
BIO_write(mInBio, message->data(), message->size()); // Input BIO_write(mInBio, message->data(), int(message->size())); // Input
else else
recv(message); // Pass zero-sized messages through recv(message); // Pass zero-sized messages through
} }

View File

@ -41,12 +41,17 @@ public:
virtual ~Transport() { virtual ~Transport() {
stop(); stop();
if (mLower)
mLower->onRecv(nullptr); // doing it on stop could cause a deadlock
} }
virtual bool stop() { virtual bool stop() {
return !mShutdown.exchange(true); if (mShutdown.exchange(true))
return false;
// We don't want incoming() to be called by the lower layer anymore
if (mLower)
mLower->onRecv(nullptr);
return true;
} }
void registerIncoming() { void registerIncoming() {

View File

@ -50,7 +50,7 @@ using std::to_integer;
using std::to_string; using std::to_string;
using random_bytes_engine = using random_bytes_engine =
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned char>; std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned short>;
WsTransport::WsTransport(std::shared_ptr<Transport> lower, string host, string path, WsTransport::WsTransport(std::shared_ptr<Transport> lower, string host, string path,
message_callback recvCallback, state_callback stateCallback) message_callback recvCallback, state_callback stateCallback)
@ -145,12 +145,12 @@ void WsTransport::close() {
bool WsTransport::sendHttpRequest() { bool WsTransport::sendHttpRequest() {
changeState(State::Connecting); changeState(State::Connecting);
auto seed = system_clock::now().time_since_epoch().count(); auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
random_bytes_engine generator(seed); random_bytes_engine generator(seed);
binary key(16); binary key(16);
auto k = reinterpret_cast<uint8_t *>(key.data()); auto k = reinterpret_cast<uint8_t *>(key.data());
std::generate(k, k + key.size(), generator); std::generate(k, k + key.size(), [&]() { return uint8_t(generator()); });
const string request = "GET " + mPath + const string request = "GET " + mPath +
" HTTP/1.1\r\n" " HTTP/1.1\r\n"
@ -283,7 +283,7 @@ size_t WsTransport::readFrame(byte *buffer, size_t size, Frame &frame) {
cur += 4; cur += 4;
} }
if (end - cur < frame.length) if (size_t(end - cur) < frame.length)
return 0; return 0;
frame.payload = cur; frame.payload = cur;
@ -292,7 +292,7 @@ size_t WsTransport::readFrame(byte *buffer, size_t size, Frame &frame) {
frame.payload[i] ^= maskingKey[i % 4]; frame.payload[i] ^= maskingKey[i % 4];
cur += frame.length; cur += frame.length;
return cur - buffer; return size_t(cur - buffer);
} }
void WsTransport::recvFrame(const Frame &frame) { void WsTransport::recvFrame(const Frame &frame) {
@ -378,13 +378,13 @@ bool WsTransport::sendFrame(const Frame &frame) {
} }
if (frame.mask) { if (frame.mask) {
auto seed = system_clock::now().time_since_epoch().count(); auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
random_bytes_engine generator(seed); random_bytes_engine generator(seed);
byte *maskingKey = reinterpret_cast<byte *>(cur); byte *maskingKey = reinterpret_cast<byte *>(cur);
auto u = reinterpret_cast<uint8_t *>(maskingKey); auto u = reinterpret_cast<uint8_t *>(maskingKey);
std::generate(u, u + 4, generator); std::generate(u, u + 4, [&]() { return uint8_t(generator()); });
cur += 4; cur += 4;
for (size_t i = 0; i < frame.length; ++i) for (size_t i = 0; i < frame.length; ++i)

View File

@ -95,13 +95,12 @@ size_t benchmark(milliseconds duration) {
fill(messageData.begin(), messageData.end(), byte(0xFF)); fill(messageData.begin(), messageData.end(), byte(0xFF));
atomic<size_t> receivedSize = 0; atomic<size_t> receivedSize = 0;
atomic<bool> finished = false;
steady_clock::time_point startTime, openTime, receivedTime, endTime; steady_clock::time_point startTime, openTime, receivedTime, endTime;
shared_ptr<DataChannel> dc2; shared_ptr<DataChannel> dc2;
pc2->onDataChannel( pc2->onDataChannel(
[&dc2, &finished, &receivedSize, &receivedTime, &endTime](shared_ptr<DataChannel> dc) { [&dc2, &receivedSize, &receivedTime](shared_ptr<DataChannel> dc) {
dc->onMessage([&receivedTime, &receivedSize](const variant<binary, string> &message) { dc->onMessage([&receivedTime, &receivedSize](const variant<binary, string> &message) {
if (holds_alternative<binary>(message)) { if (holds_alternative<binary>(message)) {
const auto &bin = get<binary>(message); const auto &bin = get<binary>(message);
@ -111,11 +110,7 @@ size_t benchmark(milliseconds duration) {
} }
}); });
dc->onClosed([&finished, &endTime]() { dc->onClosed([]() { cout << "DataChannel closed." << endl; });
cout << "DataChannel closed." << endl;
endTime = steady_clock::now();
finished = true;
});
std::atomic_store(&dc2, dc); std::atomic_store(&dc2, dc);
}); });
@ -157,11 +152,9 @@ size_t benchmark(milliseconds duration) {
cout << "Received: " << receivedSize.load() / 1000 << " KB" << endl; cout << "Received: " << receivedSize.load() / 1000 << " KB" << endl;
} }
if (auto adc2 = std::atomic_load(&dc2)) { dc1->close();
dc1->close();
while (!finished && adc2->isOpen()) endTime = steady_clock::now();
this_thread::sleep_for(100ms);
}
auto connectDuration = duration_cast<milliseconds>(openTime - startTime); auto connectDuration = duration_cast<milliseconds>(openTime - startTime);
auto transferDuration = duration_cast<milliseconds>(endTime - receivedTime); auto transferDuration = duration_cast<milliseconds>(endTime - receivedTime);

View File

@ -18,7 +18,6 @@
#include <rtc/rtc.h> #include <rtc/rtc.h>
#include <cstdbool>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@ -136,7 +135,7 @@ static void deletePeer(Peer *peer) {
int test_capi_main() { int test_capi_main() {
int attempts; int attempts;
rtcInitLogger(RTC_LOG_DEBUG); rtcInitLogger(RTC_LOG_DEBUG, nullptr);
// Create peer 1 // Create peer 1
rtcConfiguration config1; rtcConfiguration config1;