mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 15:48:03 +00:00
Compare commits
89 Commits
Author | SHA1 | Date | |
---|---|---|---|
c675aedb83 | |||
e32d139056 | |||
a790161168 | |||
2697ef0d76 | |||
5044aedbec | |||
44c90c1cb4 | |||
be79c68540 | |||
226a927df1 | |||
4e1b9bb3c2 | |||
8bc016cc08 | |||
5afbe10d01 | |||
8df07ca68d | |||
884bd2316e | |||
dadecce709 | |||
103935bdd5 | |||
62e6954949 | |||
3ac2d155cc | |||
6d8788c2a1 | |||
603dd01b87 | |||
8091508428 | |||
b38f63f077 | |||
d87539937e | |||
eb09cadded | |||
79e0c62321 | |||
ef38777129 | |||
313f081061 | |||
6108b05e0d | |||
f68601b45f | |||
b94f2ab339 | |||
9c79c8516b | |||
ad03549f8c | |||
5cc8eb1ce6 | |||
d19ff754b2 | |||
fb0f903e2e | |||
6628297580 | |||
ccc05b9999 | |||
fb2f480f92 | |||
cca0ac859a | |||
9fbc7c6ea8 | |||
83d65c805a | |||
0a5cef331d | |||
7c1714f83c | |||
f3024d0552 | |||
bcd1972270 | |||
57501b1739 | |||
cfe8a0e9c6 | |||
10061b3d4b | |||
52959ee700 | |||
0e86d6e3f1 | |||
62675e4c21 | |||
eafe86e4c0 | |||
a2b2126930 | |||
b9a663de75 | |||
f5faeba7b0 | |||
98f02c5195 | |||
d8ab5b4820 | |||
b569e78c02 | |||
4eac16e053 | |||
bb13b4bd37 | |||
aabb435fc7 | |||
3d7764c1e9 | |||
37687c3cd6 | |||
63f10303e0 | |||
d656d739f3 | |||
dbc706b69d | |||
1985088f4f | |||
ccbaa8beda | |||
642d304af9 | |||
df2102497d | |||
ad676815bd | |||
4bd40799fd | |||
4d1d1fa6fe | |||
50f61b19aa | |||
b7dbe7cdd9 | |||
7bfd731ed3 | |||
4da8b8c9a3 | |||
0382067d92 | |||
325230404a | |||
2a721015f8 | |||
5752b17a6f | |||
878d15b746 | |||
26d240e3ba | |||
ca7fc8b26f | |||
35cb9ea4be | |||
ec42736a2f | |||
b4f7c506da | |||
a577bb9004 | |||
bfd858ab13 | |||
18fe326090 |
7
.github/workflows/build-gnutls.yml
vendored
7
.github/workflows/build-gnutls.yml
vendored
@ -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
|
||||||
|
4
.github/workflows/build-nice.yml
vendored
4
.github/workflows/build-nice.yml
vendored
@ -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
|
||||||
|
22
.github/workflows/build-openssl.yml
vendored
22
.github/workflows/build-openssl.yml
vendored
@ -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
2
.gitmodules
vendored
@ -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
|
||||||
|
@ -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
196
Jamfile
@ -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 ;
|
||||||
|
|
||||||
|
10
Makefile
10
Makefile
@ -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))
|
||||||
|
|
||||||
|
42
README.md
42
README.md
@ -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/libjuice
vendored
Submodule deps/libjuice updated: 833897ef91...92a2ed7d44
2
deps/plog
vendored
2
deps/plog
vendored
Submodule deps/plog updated: 47883f0609...afb6f6f0e8
2
deps/usrsctp
vendored
2
deps/usrsctp
vendored
Submodule deps/usrsctp updated: aa10d60bc2...fdc4d2b99b
@ -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)
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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];
|
||||||
|
@ -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; }
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
120
src/rtc.cpp
120
src/rtc.cpp
@ -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)
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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");
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
Reference in New Issue
Block a user