mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-27 04:35:34 +08:00
Use sourcecode for libjuice
This commit is contained in:
7
thirdparty/libjuice/.clang-format
vendored
Normal file
7
thirdparty/libjuice/.clang-format
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: ForIndentation
|
||||
ColumnLimit: 100
|
||||
|
||||
45
thirdparty/libjuice/.clang-tidy
vendored
Normal file
45
thirdparty/libjuice/.clang-tidy
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
Checks: 'clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-clang-analyzer-security.insecureAPI.strcpy'
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: none
|
||||
CheckOptions:
|
||||
- key: llvm-else-after-return.WarnOnConditionVariables
|
||||
value: 'false'
|
||||
- key: modernize-loop-convert.MinConfidence
|
||||
value: reasonable
|
||||
- key: modernize-replace-auto-ptr.IncludeStyle
|
||||
value: llvm
|
||||
- key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons
|
||||
value: 'false'
|
||||
- key: google-readability-namespace-comments.ShortNamespaceLines
|
||||
value: '10'
|
||||
- key: cert-err33-c.CheckedFunctions
|
||||
value: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;'
|
||||
- key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
|
||||
value: 'false'
|
||||
- key: cert-dcl16-c.NewSuffixes
|
||||
value: 'L;LL;LU;LLU'
|
||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||
value: '1'
|
||||
- key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
|
||||
value: 'true'
|
||||
- key: google-readability-namespace-comments.SpacesBeforeComments
|
||||
value: '2'
|
||||
- key: modernize-loop-convert.MaxCopySize
|
||||
value: '16'
|
||||
- key: modernize-pass-by-value.IncludeStyle
|
||||
value: llvm
|
||||
- key: modernize-use-nullptr.NullMacros
|
||||
value: 'NULL'
|
||||
- key: llvm-qualified-auto.AddConstToQualified
|
||||
value: 'false'
|
||||
- key: modernize-loop-convert.NamingStyle
|
||||
value: CamelCase
|
||||
- key: llvm-else-after-return.WarnOnUnfixable
|
||||
value: 'false'
|
||||
- key: google-readability-function-size.StatementThreshold
|
||||
value: '800'
|
||||
...
|
||||
|
||||
11
thirdparty/libjuice/.editorconfig
vendored
Normal file
11
thirdparty/libjuice/.editorconfig
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
3
thirdparty/libjuice/.github/FUNDING.yml
vendored
Normal file
3
thirdparty/libjuice/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
github: ['paullouisageneau']
|
||||
custom: ['https://paypal.me/paullouisageneau']
|
||||
|
||||
43
thirdparty/libjuice/.github/workflows/build.yml
vendored
Normal file
43
thirdparty/libjuice/.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Build and test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install packages
|
||||
run: sudo apt update && sudo apt install nettle-dev clang-tidy
|
||||
- name: cmake
|
||||
run: cmake -B build -DUSE_NETTLE=1 -DWARNINGS_AS_ERRORS=1 -DCLANG_TIDY=ON
|
||||
- name: make
|
||||
run: (cd build; make)
|
||||
- name: test
|
||||
run: ./build/tests
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: cmake
|
||||
run: cmake -B build -DWARNINGS_AS_ERRORS=1 -DENABLE_LOCAL_ADDRESS_TRANSLATION=1
|
||||
- name: make
|
||||
run: (cd build; make)
|
||||
- name: test
|
||||
run: ./build/tests
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: cmake
|
||||
run: cmake -B build -G "NMake Makefiles" -DWARNINGS_AS_ERRORS=1
|
||||
- name: nmake
|
||||
run: |
|
||||
cd build
|
||||
nmake
|
||||
- name: test
|
||||
run: build/tests.exe
|
||||
|
||||
8
thirdparty/libjuice/.gitignore
vendored
Normal file
8
thirdparty/libjuice/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
build/
|
||||
*.d
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
compile_commands.json
|
||||
tests
|
||||
|
||||
249
thirdparty/libjuice/CMakeLists.txt
vendored
Normal file
249
thirdparty/libjuice/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
project(libjuice
|
||||
VERSION 1.2.3
|
||||
LANGUAGES C)
|
||||
set(PROJECT_DESCRIPTION "UDP Interactive Connectivity Establishment (ICE) library")
|
||||
|
||||
option(USE_NETTLE "Use Nettle for hash functions" OFF)
|
||||
option(NO_SERVER "Disable server support" OFF)
|
||||
option(NO_TESTS "Disable tests build" OFF)
|
||||
option(NO_EXPORT_HEADER "Disable export header" OFF)
|
||||
option(WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
|
||||
option(FUZZER "Enable oss-fuzz fuzzing" OFF)
|
||||
option(CLANG_TIDY "Enable clang-tidy" OFF)
|
||||
|
||||
# Mitigations
|
||||
option(DISABLE_CONSENT_FRESHNESS "Disable RFC 7675 Consent Freshness" OFF)
|
||||
option(ENABLE_LOCALHOST_ADDRESS "List localhost addresses in candidates" OFF)
|
||||
option(ENABLE_LOCAL_ADDRESS_TRANSLATION "Translate local addresses to localhost" OFF)
|
||||
|
||||
set(C_STANDARD 11)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-DNOMINMAX)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CLANG_TIDY)
|
||||
set(CMAKE_C_CLANG_TIDY clang-tidy)
|
||||
endif()
|
||||
|
||||
set(LIBJUICE_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/addr.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/agent.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/crc32.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/const_time.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/conn.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/conn_poll.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/conn_thread.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/conn_mux.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/base64.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/hash.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/hmac.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ice.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/juice.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/log.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/random.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/server.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/stun.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/timestamp.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/turn.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/udp.c
|
||||
)
|
||||
source_group("Source Files" FILES "${LIBJUICE_SOURCES}")
|
||||
|
||||
set(LIBJUICE_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/juice/juice.h
|
||||
)
|
||||
source_group("Header Files" FILES "${LIBJUICE_HEADERS}")
|
||||
|
||||
set(TESTS_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/main.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/crc32.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/base64.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/stun.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/gathering.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/connectivity.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/turn.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/thread.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/mux.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/notrickle.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/server.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/conflict.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/bind.c
|
||||
)
|
||||
source_group("Test Files" FILES "${TESTS_SOURCES}")
|
||||
|
||||
set(FUZZER_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fuzzer/fuzzer.c
|
||||
)
|
||||
source_group("Fuzzer Files" FILES "${FUZZER_SOURCES}")
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_library(juice SHARED EXCLUDE_FROM_ALL ${LIBJUICE_SOURCES})
|
||||
set_target_properties(juice PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
target_include_directories(juice PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
target_include_directories(juice PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/juice)
|
||||
target_include_directories(juice PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
target_compile_definitions(juice PRIVATE $<$<CONFIG:Release>:RELEASE=1>)
|
||||
target_link_libraries(juice PRIVATE Threads::Threads)
|
||||
|
||||
add_library(juice-static STATIC ${LIBJUICE_SOURCES})
|
||||
set_target_properties(juice-static PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
target_include_directories(juice-static PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
target_include_directories(juice-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/juice)
|
||||
target_include_directories(juice-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
target_compile_definitions(juice-static PRIVATE $<$<CONFIG:Release>:RELEASE=1>)
|
||||
target_compile_definitions(juice-static PUBLIC JUICE_STATIC)
|
||||
target_link_libraries(juice-static PRIVATE Threads::Threads)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(juice PRIVATE
|
||||
ws2_32 # winsock2
|
||||
bcrypt)
|
||||
target_link_libraries(juice-static PRIVATE
|
||||
ws2_32 # winsock2
|
||||
bcrypt)
|
||||
endif()
|
||||
|
||||
if(USE_NETTLE)
|
||||
find_package(Nettle REQUIRED)
|
||||
target_compile_definitions(juice PRIVATE USE_NETTLE=1)
|
||||
target_link_libraries(juice PRIVATE Nettle::Nettle)
|
||||
target_compile_definitions(juice-static PRIVATE USE_NETTLE=1)
|
||||
target_link_libraries(juice-static PRIVATE Nettle::Nettle)
|
||||
else()
|
||||
target_compile_definitions(juice PRIVATE USE_NETTLE=0)
|
||||
target_compile_definitions(juice-static PRIVATE USE_NETTLE=0)
|
||||
endif()
|
||||
|
||||
if(NO_SERVER)
|
||||
target_compile_definitions(juice PRIVATE NO_SERVER)
|
||||
target_compile_definitions(juice-static PRIVATE NO_SERVER)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
# This seems to be necessary on MacOS
|
||||
target_include_directories(juice PRIVATE /usr/local/include)
|
||||
target_include_directories(juice-static PRIVATE /usr/local/include)
|
||||
endif()
|
||||
|
||||
set_target_properties(juice PROPERTIES EXPORT_NAME LibJuice)
|
||||
add_library(LibJuice::LibJuice ALIAS juice)
|
||||
|
||||
set_target_properties(juice-static PROPERTIES EXPORT_NAME LibJuiceStatic)
|
||||
add_library(LibJuice::LibJuiceStatic ALIAS juice-static)
|
||||
|
||||
install(TARGETS juice-static EXPORT LibJuiceTargets
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
||||
install(FILES ${LIBJUICE_HEADERS} DESTINATION include/juice)
|
||||
|
||||
# Export Targets
|
||||
install(
|
||||
EXPORT LibJuiceTargets
|
||||
FILE LibJuiceTargets.cmake
|
||||
NAMESPACE LibJuice::
|
||||
DESTINATION lib/cmake/LibJuice
|
||||
)
|
||||
|
||||
# Export config
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibJuiceConfig.cmake
|
||||
DESTINATION lib/cmake/LibJuice
|
||||
)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_BINARY_DIR}/LibJuiceConfigVersion.cmake
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/LibJuiceConfigVersion.cmake
|
||||
DESTINATION lib/cmake/LibJuice)
|
||||
|
||||
if(NOT NO_EXPORT_HEADER AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.12")
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(juice
|
||||
EXPORT_MACRO_NAME JUICE_EXPORT
|
||||
NO_EXPORT_MACRO_NAME JUICE_NO_EXPORT
|
||||
DEPRECATED_MACRO_NAME JUICE_DEPRECATED
|
||||
STATIC_DEFINE JUICE_STATIC)
|
||||
target_include_directories(juice PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
|
||||
target_compile_definitions(juice PUBLIC -DJUICE_HAS_EXPORT_HEADER)
|
||||
set_target_properties(juice PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
install(FILES ${PROJECT_BINARY_DIR}/juice_export.h DESTINATION include/juice)
|
||||
else()
|
||||
target_compile_definitions(juice PRIVATE JUICE_EXPORTS)
|
||||
target_compile_definitions(juice-static PRIVATE JUICE_EXPORTS)
|
||||
endif()
|
||||
|
||||
if(NOT MSVC)
|
||||
target_compile_options(juice PRIVATE -Wall -Wextra)
|
||||
target_compile_options(juice-static PRIVATE -Wall -Wextra)
|
||||
endif()
|
||||
|
||||
if(WARNINGS_AS_ERRORS)
|
||||
if(MSVC)
|
||||
target_compile_options(juice PRIVATE /WX)
|
||||
target_compile_options(juice-static PRIVATE /WX)
|
||||
else()
|
||||
target_compile_options(juice PRIVATE -Werror)
|
||||
target_compile_options(juice-static PRIVATE -Werror)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(DISABLE_CONSENT_FRESHNESS)
|
||||
target_compile_definitions(juice PRIVATE JUICE_DISABLE_CONSENT_FRESHNESS=1)
|
||||
target_compile_definitions(juice-static PRIVATE JUICE_DISABLE_CONSENT_FRESHNESS=1)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LOCALHOST_ADDRESS)
|
||||
target_compile_definitions(juice PRIVATE JUICE_ENABLE_LOCALHOST_ADDRESS=1)
|
||||
target_compile_definitions(juice-static PRIVATE JUICE_ENABLE_LOCALHOST_ADDRESS=1)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LOCAL_ADDRESS_TRANSLATION)
|
||||
target_compile_definitions(juice PRIVATE JUICE_ENABLE_LOCAL_ADDRESS_TRANSLATION=1)
|
||||
target_compile_definitions(juice-static PRIVATE JUICE_ENABLE_LOCAL_ADDRESS_TRANSLATION=1)
|
||||
endif()
|
||||
|
||||
# Tests
|
||||
if(NOT NO_TESTS)
|
||||
add_executable(juice-tests ${TESTS_SOURCES})
|
||||
target_include_directories(juice-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
target_include_directories(juice-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/juice)
|
||||
|
||||
set_target_properties(juice-tests PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
OUTPUT_NAME tests)
|
||||
|
||||
set_target_properties(juice-tests PROPERTIES
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.github.paullouisageneau.libjuice.tests)
|
||||
|
||||
target_link_libraries(juice-tests juice Threads::Threads)
|
||||
endif()
|
||||
|
||||
# Fuzzer
|
||||
if(FUZZER)
|
||||
add_executable(stun-fuzzer ${FUZZER_SOURCES})
|
||||
target_include_directories(stun-fuzzer PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
target_include_directories(stun-fuzzer PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/juice)
|
||||
|
||||
set_target_properties(stun-fuzzer PROPERTIES OUTPUT_NAME fuzzer)
|
||||
target_link_libraries(stun-fuzzer juice-static ${LIB_FUZZING_ENGINE})
|
||||
endif()
|
||||
373
thirdparty/libjuice/LICENSE
vendored
Normal file
373
thirdparty/libjuice/LICENSE
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
77
thirdparty/libjuice/Makefile
vendored
Normal file
77
thirdparty/libjuice/Makefile
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
# libjuice
|
||||
|
||||
NAME=libjuice
|
||||
CC=$(CROSS)gcc
|
||||
AR=$(CROSS)ar
|
||||
RM=rm -f
|
||||
CFLAGS=-O2 -pthread -fPIC -Wno-address-of-packed-member
|
||||
LDFLAGS=-pthread
|
||||
LIBS=
|
||||
|
||||
INCLUDES=-Iinclude/juice
|
||||
LDLIBS=
|
||||
|
||||
USE_NETTLE ?= 0
|
||||
ifneq ($(USE_NETTLE), 0)
|
||||
CFLAGS+=-DUSE_NETTLE=1
|
||||
LIBS+=nettle
|
||||
else
|
||||
CFLAGS+=-DUSE_NETTLE=0
|
||||
endif
|
||||
|
||||
NO_SERVER ?= 0
|
||||
ifneq ($(NO_SERVER), 0)
|
||||
CFLAGS+=-DNO_SERVER
|
||||
endif
|
||||
|
||||
FORCE_M32 ?= 0
|
||||
ifneq ($(FORCE_M32), 0)
|
||||
CFLAGS+= -m32
|
||||
LDFLAGS+= -m32
|
||||
endif
|
||||
|
||||
CFLAGS+=-DJUICE_EXPORTS
|
||||
|
||||
ifneq ($(LIBS), "")
|
||||
INCLUDES+=$(if $(LIBS),$(shell pkg-config --cflags $(LIBS)),)
|
||||
LDLIBS+=$(if $(LIBS), $(shell pkg-config --libs $(LIBS)),)
|
||||
endif
|
||||
|
||||
SRCS=$(shell printf "%s " src/*.c)
|
||||
OBJS=$(subst .c,.o,$(SRCS))
|
||||
|
||||
TEST_SRCS=$(shell printf "%s " test/*.c)
|
||||
TEST_OBJS=$(subst .c,.o,$(TEST_SRCS))
|
||||
|
||||
all: $(NAME).a $(NAME).so tests
|
||||
|
||||
src/%.o: src/%.c
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -MMD -MP -o $@ -c $<
|
||||
|
||||
test/%.o: test/%.c
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -Iinclude -Isrc -MMD -MP -o $@ -c $<
|
||||
|
||||
-include $(subst .c,.d,$(SRCS))
|
||||
|
||||
$(NAME).a: $(OBJS)
|
||||
$(AR) crf $@ $(OBJS)
|
||||
|
||||
$(NAME).so: $(OBJS)
|
||||
$(CC) $(LDFLAGS) -shared -o $@ $(OBJS) $(LDLIBS)
|
||||
|
||||
tests: $(NAME).a $(TEST_OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(TEST_OBJS) $(LDLIBS) $(NAME).a
|
||||
|
||||
clean:
|
||||
-$(RM) include/juice/*.d *.d
|
||||
-$(RM) src/*.o src/*.d
|
||||
-$(RM) test/*.o test/*.d
|
||||
|
||||
dist-clean: clean
|
||||
-$(RM) $(NAME).a
|
||||
-$(RM) $(NAME).so
|
||||
-$(RM) tests
|
||||
-$(RM) include/*~
|
||||
-$(RM) src/*~
|
||||
-$(RM) test/*~
|
||||
|
||||
103
thirdparty/libjuice/README.md
vendored
Normal file
103
thirdparty/libjuice/README.md
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
# libjuice - UDP Interactive Connectivity Establishment
|
||||
|
||||
[](https://www.mozilla.org/en-US/MPL/2.0/)
|
||||
[](https://github.com/paullouisageneau/libjuice/actions/workflows/build.yml)
|
||||
[](https://repology.org/project/libjuice/versions)
|
||||
[](https://repology.org/project/libjuice/versions)
|
||||
[](https://gitter.im/libjuice/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://discord.gg/jXAP8jp3Nn)
|
||||
|
||||
libjuice :lemon::sweat_drops: (_JUICE is a UDP Interactive Connectivity Establishment library_) allows to open bidirectionnal User Datagram Protocol (UDP) streams with Network Address Translator (NAT) traversal.
|
||||
|
||||
The library is a simplified implementation of the Interactive Connectivity Establishment (ICE) protocol, client-side and server-side, written in C without dependencies for POSIX platforms (including GNU/Linux, Android, Apple macOS and iOS) and Microsoft Windows. The client supports only a single component over UDP per session in a standard single-gateway network topology, as this should be sufficient for the majority of use cases nowadays.
|
||||
|
||||
libjuice is licensed under MPL 2.0, see [LICENSE](https://github.com/paullouisageneau/libjuice/blob/master/LICENSE).
|
||||
|
||||
libjuice is available on [AUR](https://aur.archlinux.org/packages/libjuice/) and [vcpkg](https://vcpkg.info/port/libjuice). Bindings are available for [Rust](https://github.com/VollmondT/juice-rs).
|
||||
|
||||
For a STUN/TURN server application based on libjuice, see [Violet](https://github.com/paullouisageneau/violet).
|
||||
|
||||
## Compatibility
|
||||
|
||||
The library implements a simplified but fully compatible ICE agent ([RFC5245](https://www.rfc-editor.org/rfc/rfc5245.html) then [RFC8445](https://www.rfc-editor.org/rfc/rfc8445.html)) featuring:
|
||||
- STUN protocol ([RFC5389](https://www.rfc-editor.org/rfc/rfc5389.html) then [RFC8489](https://www.rfc-editor.org/rfc/rfc8489.html))
|
||||
- TURN relaying ([RFC5766](https://www.rfc-editor.org/rfc/rfc5766.html) then [RFC8656](https://www.rfc-editor.org/rfc/rfc8656.html))
|
||||
- Consent freshness ([RFC7675](https://www.rfc-editor.org/rfc/rfc7675.html))
|
||||
- SDP-based interface ([RFC8839](https://www.rfc-editor.org/rfc/rfc8839.html))
|
||||
- IPv4 and IPv6 dual-stack support
|
||||
- Optional multiplexing on a single UDP port
|
||||
|
||||
The limitations compared to a fully-featured ICE agent are:
|
||||
- Only UDP is supported as transport protocol and other protocols are ignored.
|
||||
- Only one component is supported, which is sufficient for WebRTC Data Channels and multiplexed RTP+RTCP.
|
||||
- Candidates are gathered without binding to each network interface, which behaves identically to the full implementation on most client systems.
|
||||
|
||||
It also implements a lightweight STUN/TURN server ([RFC8489](https://www.rfc-editor.org/rfc/rfc8489.html) and [RFC8656](https://www.rfc-editor.org/rfc/rfc8656.html)). The server can be disabled at compile-time with the `NO_SERVER` flag.
|
||||
|
||||
## Dependencies
|
||||
|
||||
None!
|
||||
|
||||
Optionally, [Nettle](https://www.lysator.liu.se/~nisse/nettle/) can provide SHA1 and SHA256 algorithms instead of the internal implementation.
|
||||
|
||||
## Building
|
||||
|
||||
### Clone repository
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/paullouisageneau/libjuice.git
|
||||
$ cd libjuice
|
||||
```
|
||||
|
||||
### Build with CMake
|
||||
|
||||
The CMake library targets `libjuice` and `libjuice-static` respectively correspond to the shared and static libraries. The default target will build the library and tests. It exports the targets with namespace `LibJuice::LibJuice` and `LibJuice::LibJuiceStatic` to link the library from another CMake project.
|
||||
|
||||
#### POSIX-compliant operating systems (including Linux and Apple macOS)
|
||||
|
||||
```bash
|
||||
$ cmake -B build
|
||||
$ cd build
|
||||
$ make -j2
|
||||
```
|
||||
|
||||
The option `USE_NETTLE` allows to use the Nettle library instead of the internal implementation for HMAC-SHA1:
|
||||
```bash
|
||||
$ cmake -B build -DUSE_NETTLE=1
|
||||
$ cd build
|
||||
$ make -j2
|
||||
```
|
||||
|
||||
#### Microsoft Windows with MinGW cross-compilation
|
||||
|
||||
```bash
|
||||
$ cmake -B build -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"
|
||||
$ cd build
|
||||
$ nmake
|
||||
```
|
||||
|
||||
### Build directly with Make (Linux only)
|
||||
|
||||
```bash
|
||||
$ make
|
||||
```
|
||||
|
||||
The option `USE_NETTLE` allows to use the Nettle library instead of the internal implementation for HMAC-SHA1:
|
||||
```bash
|
||||
$ make USE_NETTLE=1
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
See [test/connectivity.c](https://github.com/paullouisageneau/libjuice/blob/master/test/connectivity.c) for a complete local connection example.
|
||||
|
||||
See [test/server.c](https://github.com/paullouisageneau/libjuice/blob/master/test/server.c) for a server example.
|
||||
|
||||
2
thirdparty/libjuice/cmake/LibJuiceConfig.cmake
vendored
Normal file
2
thirdparty/libjuice/cmake/LibJuiceConfig.cmake
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/LibJuiceTargets.cmake")
|
||||
|
||||
142
thirdparty/libjuice/cmake/Modules/FindNettle.cmake
vendored
Normal file
142
thirdparty/libjuice/cmake/Modules/FindNettle.cmake
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
# Copyright (C) 2020 Dieter Baron and Thomas Klausner
|
||||
#
|
||||
# The authors can be contacted at <libzip@nih.at>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# 3. The names of the authors may not be used to endorse or promote
|
||||
# products derived from this software without specific prior
|
||||
# written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
|
||||
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindNettle
|
||||
-------
|
||||
|
||||
Finds the Nettle library.
|
||||
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module provides the following imported targets, if found:
|
||||
|
||||
``Nettle::Nettle``
|
||||
The Nettle library
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This will define the following variables:
|
||||
|
||||
``Nettle_FOUND``
|
||||
True if the system has the Nettle library.
|
||||
``Nettle_VERSION``
|
||||
The version of the Nettle library which was found.
|
||||
``Nettle_INCLUDE_DIRS``
|
||||
Include directories needed to use Nettle.
|
||||
``Nettle_LIBRARIES``
|
||||
Libraries needed to link to Nettle.
|
||||
|
||||
Cache Variables
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The following cache variables may also be set:
|
||||
|
||||
``Nettle_INCLUDE_DIR``
|
||||
The directory containing ``nettle/aes.h``.
|
||||
``Nettle_LIBRARY``
|
||||
The path to the Nettle library.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PC_Nettle QUIET nettle)
|
||||
|
||||
find_path(Nettle_INCLUDE_DIR
|
||||
NAMES nettle/aes.h nettle/md5.h nettle/pbkdf2.h nettle/ripemd160.h nettle/sha.h
|
||||
PATHS ${PC_Nettle_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(Nettle_LIBRARY
|
||||
NAMES nettle
|
||||
PATHS ${PC_Nettle_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
# Extract version information from the header file
|
||||
if(Nettle_INCLUDE_DIR)
|
||||
# This file only exists in nettle>=3.0
|
||||
if(EXISTS ${Nettle_INCLUDE_DIR}/nettle/version.h)
|
||||
file(STRINGS ${Nettle_INCLUDE_DIR}/nettle/version.h _ver_major_line
|
||||
REGEX "^#define NETTLE_VERSION_MAJOR *[0-9]+"
|
||||
LIMIT_COUNT 1)
|
||||
string(REGEX MATCH "[0-9]+"
|
||||
Nettle_MAJOR_VERSION "${_ver_major_line}")
|
||||
file(STRINGS ${Nettle_INCLUDE_DIR}/nettle/version.h _ver_minor_line
|
||||
REGEX "^#define NETTLE_VERSION_MINOR *[0-9]+"
|
||||
LIMIT_COUNT 1)
|
||||
string(REGEX MATCH "[0-9]+"
|
||||
Nettle_MINOR_VERSION "${_ver_minor_line}")
|
||||
set(Nettle_VERSION "${Nettle_MAJOR_VERSION}.${Nettle_MINOR_VERSION}")
|
||||
unset(_ver_major_line)
|
||||
unset(_ver_minor_line)
|
||||
else()
|
||||
if(PC_Nettle_VERSION)
|
||||
set(Nettle_VERSION ${PC_Nettle_VERSION})
|
||||
else()
|
||||
set(Nettle_VERSION "1.0")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Nettle
|
||||
FOUND_VAR Nettle_FOUND
|
||||
REQUIRED_VARS
|
||||
Nettle_LIBRARY
|
||||
Nettle_INCLUDE_DIR
|
||||
VERSION_VAR Nettle_VERSION
|
||||
)
|
||||
|
||||
if(Nettle_FOUND)
|
||||
set(Nettle_LIBRARIES ${Nettle_LIBRARY})
|
||||
set(Nettle_INCLUDE_DIRS ${Nettle_INCLUDE_DIR})
|
||||
set(Nettle_DEFINITIONS ${PC_Nettle_CFLAGS_OTHER})
|
||||
endif()
|
||||
|
||||
if(Nettle_FOUND AND NOT TARGET Nettle::Nettle)
|
||||
add_library(Nettle::Nettle UNKNOWN IMPORTED)
|
||||
set_target_properties(Nettle::Nettle PROPERTIES
|
||||
IMPORTED_LOCATION "${Nettle_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PC_Nettle_CFLAGS_OTHER}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Nettle_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
Nettle_INCLUDE_DIR
|
||||
Nettle_LIBRARY
|
||||
)
|
||||
|
||||
# compatibility variables
|
||||
set(Nettle_VERSION_STRING ${Nettle_VERSION})
|
||||
|
||||
26
thirdparty/libjuice/fuzzer/README.md
vendored
Normal file
26
thirdparty/libjuice/fuzzer/README.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
## Fuzzer
|
||||
|
||||
### Export Symbols
|
||||
```
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
export CFLAGS=-fsanitize=fuzzer-no-link,address
|
||||
export LIB_FUZZING_ENGINE=-fsanitize=fuzzer
|
||||
export LDFLAGS=-fsanitize=address
|
||||
```
|
||||
|
||||
### Build
|
||||
```
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Debug -DFUZZER=ON -DCMAKE_C_COMPILER=$CC \
|
||||
-DCMAKE_C_FLAGS=$CFLAGS -DCMAKE_EXE_LINKER_FLAGS=$CFLAGS \
|
||||
-DLIB_FUZZING_ENGINE=$LIB_FUZZING_ENGINE \
|
||||
../
|
||||
```
|
||||
|
||||
### Run
|
||||
```
|
||||
$ mkdir coverage
|
||||
$ ./fuzzer coverage/ ../fuzzer/input/
|
||||
```
|
||||
41
thirdparty/libjuice/fuzzer/fuzzer.c
vendored
Normal file
41
thirdparty/libjuice/fuzzer/fuzzer.c
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2022 0x34d (https://github.com/0x34d)
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "stun.h"
|
||||
|
||||
#define kMinInputLength 5
|
||||
#define kMaxInputLength 2048
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
stun_message_t msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
_juice_is_stun_datagram((void *)Data, Size);
|
||||
_juice_stun_read((void *)Data, Size, &msg);
|
||||
_juice_stun_check_integrity((void *)Data, Size, &msg, "VOkJxbRl1RmTxUk/WvJxBt");
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
thirdparty/libjuice/fuzzer/input/message1.raw
vendored
Normal file
BIN
thirdparty/libjuice/fuzzer/input/message1.raw
vendored
Normal file
Binary file not shown.
BIN
thirdparty/libjuice/fuzzer/input/message2.raw
vendored
Normal file
BIN
thirdparty/libjuice/fuzzer/input/message2.raw
vendored
Normal file
Binary file not shown.
352
thirdparty/libjuice/include/juice/juice.h
vendored
352
thirdparty/libjuice/include/juice/juice.h
vendored
@@ -1,176 +1,176 @@
|
||||
/**
|
||||
* Copyright (c) 2020-2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_H
|
||||
#define JUICE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef JUICE_HAS_EXPORT_HEADER
|
||||
#include "juice_export.h"
|
||||
#else // no export header
|
||||
#ifdef JUICE_STATIC
|
||||
#define JUICE_EXPORT
|
||||
#else // dynamic library
|
||||
#ifdef _WIN32
|
||||
#if defined(JUICE_EXPORTS) || defined(juice_EXPORTS)
|
||||
#define JUICE_EXPORT __declspec(dllexport) // building the library
|
||||
#else
|
||||
#define JUICE_EXPORT __declspec(dllimport) // using the library
|
||||
#endif
|
||||
#else // not WIN32
|
||||
#define JUICE_EXPORT
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define JUICE_ERR_SUCCESS 0
|
||||
#define JUICE_ERR_INVALID -1 // invalid argument
|
||||
#define JUICE_ERR_FAILED -2 // runtime error
|
||||
#define JUICE_ERR_NOT_AVAIL -3 // element not available
|
||||
|
||||
// ICE Agent
|
||||
|
||||
#define JUICE_MAX_ADDRESS_STRING_LEN 64
|
||||
#define JUICE_MAX_CANDIDATE_SDP_STRING_LEN 256
|
||||
#define JUICE_MAX_SDP_STRING_LEN 4096
|
||||
|
||||
typedef struct juice_agent juice_agent_t;
|
||||
|
||||
typedef enum juice_state {
|
||||
JUICE_STATE_DISCONNECTED = 0,
|
||||
JUICE_STATE_GATHERING,
|
||||
JUICE_STATE_CONNECTING,
|
||||
JUICE_STATE_CONNECTED,
|
||||
JUICE_STATE_COMPLETED,
|
||||
JUICE_STATE_FAILED
|
||||
} juice_state_t;
|
||||
|
||||
typedef void (*juice_cb_state_changed_t)(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
typedef void (*juice_cb_candidate_t)(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
typedef void (*juice_cb_gathering_done_t)(juice_agent_t *agent, void *user_ptr);
|
||||
typedef void (*juice_cb_recv_t)(juice_agent_t *agent, const char *data, size_t size,
|
||||
void *user_ptr);
|
||||
|
||||
typedef struct juice_turn_server {
|
||||
const char *host;
|
||||
const char *username;
|
||||
const char *password;
|
||||
uint16_t port;
|
||||
} juice_turn_server_t;
|
||||
|
||||
typedef enum juice_concurrency_mode {
|
||||
JUICE_CONCURRENCY_MODE_POLL = 0, // Connections share a single thread
|
||||
JUICE_CONCURRENCY_MODE_MUX, // Connections are multiplexed on a single UDP socket
|
||||
JUICE_CONCURRENCY_MODE_THREAD, // Each connection runs in its own thread
|
||||
} juice_concurrency_mode_t;
|
||||
|
||||
typedef struct juice_config {
|
||||
juice_concurrency_mode_t concurrency_mode;
|
||||
|
||||
const char *stun_server_host;
|
||||
uint16_t stun_server_port;
|
||||
|
||||
juice_turn_server_t *turn_servers;
|
||||
int turn_servers_count;
|
||||
|
||||
const char *bind_address;
|
||||
|
||||
uint16_t local_port_range_begin;
|
||||
uint16_t local_port_range_end;
|
||||
|
||||
juice_cb_state_changed_t cb_state_changed;
|
||||
juice_cb_candidate_t cb_candidate;
|
||||
juice_cb_gathering_done_t cb_gathering_done;
|
||||
juice_cb_recv_t cb_recv;
|
||||
|
||||
void *user_ptr;
|
||||
|
||||
} juice_config_t;
|
||||
|
||||
JUICE_EXPORT juice_agent_t *juice_create(const juice_config_t *config);
|
||||
JUICE_EXPORT void juice_destroy(juice_agent_t *agent);
|
||||
|
||||
JUICE_EXPORT int juice_gather_candidates(juice_agent_t *agent);
|
||||
JUICE_EXPORT int juice_get_local_description(juice_agent_t *agent, char *buffer, size_t size);
|
||||
JUICE_EXPORT int juice_set_remote_description(juice_agent_t *agent, const char *sdp);
|
||||
JUICE_EXPORT int juice_add_remote_candidate(juice_agent_t *agent, const char *sdp);
|
||||
JUICE_EXPORT int juice_set_remote_gathering_done(juice_agent_t *agent);
|
||||
JUICE_EXPORT int juice_send(juice_agent_t *agent, const char *data, size_t size);
|
||||
JUICE_EXPORT int juice_send_diffserv(juice_agent_t *agent, const char *data, size_t size, int ds);
|
||||
JUICE_EXPORT juice_state_t juice_get_state(juice_agent_t *agent);
|
||||
JUICE_EXPORT int juice_get_selected_candidates(juice_agent_t *agent, char *local, size_t local_size,
|
||||
char *remote, size_t remote_size);
|
||||
JUICE_EXPORT int juice_get_selected_addresses(juice_agent_t *agent, char *local, size_t local_size,
|
||||
char *remote, size_t remote_size);
|
||||
JUICE_EXPORT const char *juice_state_to_string(juice_state_t state);
|
||||
|
||||
// ICE server
|
||||
|
||||
typedef struct juice_server juice_server_t;
|
||||
|
||||
typedef struct juice_server_credentials {
|
||||
const char *username;
|
||||
const char *password;
|
||||
int allocations_quota;
|
||||
} juice_server_credentials_t;
|
||||
|
||||
typedef struct juice_server_config {
|
||||
juice_server_credentials_t *credentials;
|
||||
int credentials_count;
|
||||
|
||||
int max_allocations;
|
||||
int max_peers;
|
||||
|
||||
const char *bind_address;
|
||||
const char *external_address;
|
||||
uint16_t port;
|
||||
|
||||
uint16_t relay_port_range_begin;
|
||||
uint16_t relay_port_range_end;
|
||||
|
||||
const char *realm;
|
||||
|
||||
} juice_server_config_t;
|
||||
|
||||
JUICE_EXPORT juice_server_t *juice_server_create(const juice_server_config_t *config);
|
||||
JUICE_EXPORT void juice_server_destroy(juice_server_t *server);
|
||||
|
||||
JUICE_EXPORT uint16_t juice_server_get_port(juice_server_t *server);
|
||||
JUICE_EXPORT int juice_server_add_credentials(juice_server_t *server,
|
||||
const juice_server_credentials_t *credentials,
|
||||
unsigned long lifetime_ms);
|
||||
|
||||
// Logging
|
||||
|
||||
typedef enum juice_log_level {
|
||||
JUICE_LOG_LEVEL_VERBOSE = 0,
|
||||
JUICE_LOG_LEVEL_DEBUG,
|
||||
JUICE_LOG_LEVEL_INFO,
|
||||
JUICE_LOG_LEVEL_WARN,
|
||||
JUICE_LOG_LEVEL_ERROR,
|
||||
JUICE_LOG_LEVEL_FATAL,
|
||||
JUICE_LOG_LEVEL_NONE
|
||||
} juice_log_level_t;
|
||||
|
||||
typedef void (*juice_log_cb_t)(juice_log_level_t level, const char *message);
|
||||
|
||||
JUICE_EXPORT void juice_set_log_level(juice_log_level_t level);
|
||||
JUICE_EXPORT void juice_set_log_handler(juice_log_cb_t cb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/**
|
||||
* Copyright (c) 2020-2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_H
|
||||
#define JUICE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef JUICE_HAS_EXPORT_HEADER
|
||||
#include "juice_export.h"
|
||||
#else // no export header
|
||||
#ifdef JUICE_STATIC
|
||||
#define JUICE_EXPORT
|
||||
#else // dynamic library
|
||||
#ifdef _WIN32
|
||||
#if defined(JUICE_EXPORTS) || defined(juice_EXPORTS)
|
||||
#define JUICE_EXPORT __declspec(dllexport) // building the library
|
||||
#else
|
||||
#define JUICE_EXPORT __declspec(dllimport) // using the library
|
||||
#endif
|
||||
#else // not WIN32
|
||||
#define JUICE_EXPORT
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define JUICE_ERR_SUCCESS 0
|
||||
#define JUICE_ERR_INVALID -1 // invalid argument
|
||||
#define JUICE_ERR_FAILED -2 // runtime error
|
||||
#define JUICE_ERR_NOT_AVAIL -3 // element not available
|
||||
|
||||
// ICE Agent
|
||||
|
||||
#define JUICE_MAX_ADDRESS_STRING_LEN 64
|
||||
#define JUICE_MAX_CANDIDATE_SDP_STRING_LEN 256
|
||||
#define JUICE_MAX_SDP_STRING_LEN 4096
|
||||
|
||||
typedef struct juice_agent juice_agent_t;
|
||||
|
||||
typedef enum juice_state {
|
||||
JUICE_STATE_DISCONNECTED = 0,
|
||||
JUICE_STATE_GATHERING,
|
||||
JUICE_STATE_CONNECTING,
|
||||
JUICE_STATE_CONNECTED,
|
||||
JUICE_STATE_COMPLETED,
|
||||
JUICE_STATE_FAILED
|
||||
} juice_state_t;
|
||||
|
||||
typedef void (*juice_cb_state_changed_t)(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
typedef void (*juice_cb_candidate_t)(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
typedef void (*juice_cb_gathering_done_t)(juice_agent_t *agent, void *user_ptr);
|
||||
typedef void (*juice_cb_recv_t)(juice_agent_t *agent, const char *data, size_t size,
|
||||
void *user_ptr);
|
||||
|
||||
typedef struct juice_turn_server {
|
||||
const char *host;
|
||||
const char *username;
|
||||
const char *password;
|
||||
uint16_t port;
|
||||
} juice_turn_server_t;
|
||||
|
||||
typedef enum juice_concurrency_mode {
|
||||
JUICE_CONCURRENCY_MODE_POLL = 0, // Connections share a single thread
|
||||
JUICE_CONCURRENCY_MODE_MUX, // Connections are multiplexed on a single UDP socket
|
||||
JUICE_CONCURRENCY_MODE_THREAD, // Each connection runs in its own thread
|
||||
} juice_concurrency_mode_t;
|
||||
|
||||
typedef struct juice_config {
|
||||
juice_concurrency_mode_t concurrency_mode;
|
||||
|
||||
const char *stun_server_host;
|
||||
uint16_t stun_server_port;
|
||||
|
||||
juice_turn_server_t *turn_servers;
|
||||
int turn_servers_count;
|
||||
|
||||
const char *bind_address;
|
||||
|
||||
uint16_t local_port_range_begin;
|
||||
uint16_t local_port_range_end;
|
||||
|
||||
juice_cb_state_changed_t cb_state_changed;
|
||||
juice_cb_candidate_t cb_candidate;
|
||||
juice_cb_gathering_done_t cb_gathering_done;
|
||||
juice_cb_recv_t cb_recv;
|
||||
|
||||
void *user_ptr;
|
||||
|
||||
} juice_config_t;
|
||||
|
||||
JUICE_EXPORT juice_agent_t *juice_create(const juice_config_t *config);
|
||||
JUICE_EXPORT void juice_destroy(juice_agent_t *agent);
|
||||
|
||||
JUICE_EXPORT int juice_gather_candidates(juice_agent_t *agent);
|
||||
JUICE_EXPORT int juice_get_local_description(juice_agent_t *agent, char *buffer, size_t size);
|
||||
JUICE_EXPORT int juice_set_remote_description(juice_agent_t *agent, const char *sdp);
|
||||
JUICE_EXPORT int juice_add_remote_candidate(juice_agent_t *agent, const char *sdp);
|
||||
JUICE_EXPORT int juice_set_remote_gathering_done(juice_agent_t *agent);
|
||||
JUICE_EXPORT int juice_send(juice_agent_t *agent, const char *data, size_t size);
|
||||
JUICE_EXPORT int juice_send_diffserv(juice_agent_t *agent, const char *data, size_t size, int ds);
|
||||
JUICE_EXPORT juice_state_t juice_get_state(juice_agent_t *agent);
|
||||
JUICE_EXPORT int juice_get_selected_candidates(juice_agent_t *agent, char *local, size_t local_size,
|
||||
char *remote, size_t remote_size);
|
||||
JUICE_EXPORT int juice_get_selected_addresses(juice_agent_t *agent, char *local, size_t local_size,
|
||||
char *remote, size_t remote_size);
|
||||
JUICE_EXPORT const char *juice_state_to_string(juice_state_t state);
|
||||
|
||||
// ICE server
|
||||
|
||||
typedef struct juice_server juice_server_t;
|
||||
|
||||
typedef struct juice_server_credentials {
|
||||
const char *username;
|
||||
const char *password;
|
||||
int allocations_quota;
|
||||
} juice_server_credentials_t;
|
||||
|
||||
typedef struct juice_server_config {
|
||||
juice_server_credentials_t *credentials;
|
||||
int credentials_count;
|
||||
|
||||
int max_allocations;
|
||||
int max_peers;
|
||||
|
||||
const char *bind_address;
|
||||
const char *external_address;
|
||||
uint16_t port;
|
||||
|
||||
uint16_t relay_port_range_begin;
|
||||
uint16_t relay_port_range_end;
|
||||
|
||||
const char *realm;
|
||||
|
||||
} juice_server_config_t;
|
||||
|
||||
JUICE_EXPORT juice_server_t *juice_server_create(const juice_server_config_t *config);
|
||||
JUICE_EXPORT void juice_server_destroy(juice_server_t *server);
|
||||
|
||||
JUICE_EXPORT uint16_t juice_server_get_port(juice_server_t *server);
|
||||
JUICE_EXPORT int juice_server_add_credentials(juice_server_t *server,
|
||||
const juice_server_credentials_t *credentials,
|
||||
unsigned long lifetime_ms);
|
||||
|
||||
// Logging
|
||||
|
||||
typedef enum juice_log_level {
|
||||
JUICE_LOG_LEVEL_VERBOSE = 0,
|
||||
JUICE_LOG_LEVEL_DEBUG,
|
||||
JUICE_LOG_LEVEL_INFO,
|
||||
JUICE_LOG_LEVEL_WARN,
|
||||
JUICE_LOG_LEVEL_ERROR,
|
||||
JUICE_LOG_LEVEL_FATAL,
|
||||
JUICE_LOG_LEVEL_NONE
|
||||
} juice_log_level_t;
|
||||
|
||||
typedef void (*juice_log_cb_t)(juice_log_level_t level, const char *message);
|
||||
|
||||
JUICE_EXPORT void juice_set_log_level(juice_log_level_t level);
|
||||
JUICE_EXPORT void juice_set_log_handler(juice_log_cb_t cb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
BIN
thirdparty/libjuice/lib/juice.lib
vendored
BIN
thirdparty/libjuice/lib/juice.lib
vendored
Binary file not shown.
310
thirdparty/libjuice/src/addr.c
vendored
Normal file
310
thirdparty/libjuice/src/addr.c
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "addr.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
socklen_t addr_get_len(const struct sockaddr *sa) {
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
return sizeof(struct sockaddr_in);
|
||||
case AF_INET6:
|
||||
return sizeof(struct sockaddr_in6);
|
||||
default:
|
||||
JLOG_WARN("Unknown address family %hu", sa->sa_family);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t addr_get_port(const struct sockaddr *sa) {
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
return ntohs(((struct sockaddr_in *)sa)->sin_port);
|
||||
case AF_INET6:
|
||||
return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
|
||||
default:
|
||||
JLOG_WARN("Unknown address family %hu", sa->sa_family);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int addr_set_port(struct sockaddr *sa, uint16_t port) {
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
((struct sockaddr_in *)sa)->sin_port = htons(port);
|
||||
return 0;
|
||||
case AF_INET6:
|
||||
((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
|
||||
return 0;
|
||||
default:
|
||||
JLOG_WARN("Unknown address family %hu", sa->sa_family);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool addr_is_any(const struct sockaddr *sa) {
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET: {
|
||||
const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
|
||||
const uint8_t *b = (const uint8_t *)&sin->sin_addr;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
if (b[i] != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
|
||||
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
|
||||
const uint8_t *b = (const uint8_t *)&sin6->sin6_addr + 12;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
if (b[i] != 0)
|
||||
return false;
|
||||
} else {
|
||||
const uint8_t *b = (const uint8_t *)&sin6->sin6_addr;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (b[i] != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool addr_is_local(const struct sockaddr *sa) {
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET: {
|
||||
const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
|
||||
const uint8_t *b = (const uint8_t *)&sin->sin_addr;
|
||||
if (b[0] == 127) // loopback
|
||||
return true;
|
||||
if (b[0] == 169 && b[1] == 254) // link-local
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
|
||||
if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) {
|
||||
return true;
|
||||
}
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
|
||||
return true;
|
||||
}
|
||||
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
|
||||
const uint8_t *b = (const uint8_t *)&sin6->sin6_addr + 12;
|
||||
if (b[0] == 127) // loopback
|
||||
return true;
|
||||
if (b[0] == 169 && b[1] == 254) // link-local
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool addr_unmap_inet6_v4mapped(struct sockaddr *sa, socklen_t *len) {
|
||||
if (sa->sa_family != AF_INET6)
|
||||
return false;
|
||||
|
||||
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
|
||||
if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
|
||||
return false;
|
||||
|
||||
struct sockaddr_in6 copy = *sin6;
|
||||
sin6 = ©
|
||||
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
||||
memset(sin, 0, sizeof(*sin));
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_port = sin6->sin6_port;
|
||||
memcpy(&sin->sin_addr, ((const uint8_t *)&sin6->sin6_addr) + 12, 4);
|
||||
*len = sizeof(*sin);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addr_map_inet6_v4mapped(struct sockaddr_storage *ss, socklen_t *len) {
|
||||
if (ss->ss_family != AF_INET)
|
||||
return false;
|
||||
|
||||
const struct sockaddr_in *sin = (const struct sockaddr_in *)ss;
|
||||
struct sockaddr_in copy = *sin;
|
||||
sin = ©
|
||||
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
|
||||
memset(sin6, 0, sizeof(*sin6));
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_port = sin->sin_port;
|
||||
uint8_t *b = (uint8_t *)&sin6->sin6_addr;
|
||||
memset(b, 0, 10);
|
||||
memset(b + 10, 0xFF, 2);
|
||||
memcpy(b + 12, (const uint8_t *)&sin->sin_addr, 4);
|
||||
*len = sizeof(*sin6);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addr_is_equal(const struct sockaddr *a, const struct sockaddr *b, bool compare_ports) {
|
||||
if (a->sa_family != b->sa_family)
|
||||
return false;
|
||||
|
||||
switch (a->sa_family) {
|
||||
case AF_INET: {
|
||||
const struct sockaddr_in *ain = (const struct sockaddr_in *)a;
|
||||
const struct sockaddr_in *bin = (const struct sockaddr_in *)b;
|
||||
if (memcmp(&ain->sin_addr, &bin->sin_addr, 4) != 0)
|
||||
return false;
|
||||
if (compare_ports && ain->sin_port != bin->sin_port)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *ain6 = (const struct sockaddr_in6 *)a;
|
||||
const struct sockaddr_in6 *bin6 = (const struct sockaddr_in6 *)b;
|
||||
if (memcmp(&ain6->sin6_addr, &bin6->sin6_addr, 16) != 0)
|
||||
return false;
|
||||
if (compare_ports && ain6->sin6_port != bin6->sin6_port)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int addr_to_string(const struct sockaddr *sa, char *buffer, size_t size) {
|
||||
socklen_t salen = addr_get_len(sa);
|
||||
if (salen == 0)
|
||||
goto error;
|
||||
|
||||
char host[ADDR_MAX_NUMERICHOST_LEN];
|
||||
char service[ADDR_MAX_NUMERICSERV_LEN];
|
||||
if (getnameinfo(sa, salen, host, ADDR_MAX_NUMERICHOST_LEN, service, ADDR_MAX_NUMERICSERV_LEN,
|
||||
NI_NUMERICHOST | NI_NUMERICSERV | NI_DGRAM)) {
|
||||
JLOG_ERROR("getnameinfo failed, errno=%d", sockerrno);
|
||||
goto error;
|
||||
}
|
||||
|
||||
int len = snprintf(buffer, size, "%s:%s", host, service);
|
||||
if (len < 0 || (size_t)len >= size)
|
||||
goto error;
|
||||
|
||||
return len;
|
||||
|
||||
error:
|
||||
// Make sure we still write a valid null-terminated string
|
||||
snprintf(buffer, size, "?");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// djb2 hash function
|
||||
#define DJB2_INIT 5381
|
||||
static void djb2(unsigned long *hash, int i) {
|
||||
*hash = ((*hash << 5) + *hash) + i; // hash * 33 + i
|
||||
}
|
||||
|
||||
unsigned long addr_hash(const struct sockaddr *sa, bool with_port) {
|
||||
unsigned long hash = DJB2_INIT;
|
||||
|
||||
djb2(&hash, sa->sa_family);
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET: {
|
||||
const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
|
||||
const uint8_t *b = (const uint8_t *)&sin->sin_addr;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
djb2(&hash, b[i]);
|
||||
if (with_port) {
|
||||
djb2(&hash, sin->sin_port >> 8);
|
||||
djb2(&hash, sin->sin_port & 0xFF);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
|
||||
const uint8_t *b = (const uint8_t *)&sin6->sin6_addr;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
djb2(&hash, b[i]);
|
||||
if (with_port) {
|
||||
djb2(&hash, sin6->sin6_port >> 8);
|
||||
djb2(&hash, sin6->sin6_port & 0xFF);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
int addr_resolve(const char *hostname, const char *service, addr_record_t *records, size_t count) {
|
||||
addr_record_t *end = records + count;
|
||||
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
struct addrinfo *ai_list = NULL;
|
||||
if (getaddrinfo(hostname, service, &hints, &ai_list)) {
|
||||
JLOG_WARN("Address resolution failed for %s:%s", hostname, service);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
for (struct addrinfo *ai = ai_list; ai; ai = ai->ai_next) {
|
||||
if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
|
||||
++ret;
|
||||
if (records != end) {
|
||||
memcpy(&records->addr, ai->ai_addr, ai->ai_addrlen);
|
||||
records->len = (socklen_t)ai->ai_addrlen;
|
||||
++records;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(ai_list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool addr_is_numeric_hostname(const char *hostname) {
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
|
||||
struct addrinfo *ai_list = NULL;
|
||||
if (getaddrinfo(hostname, "9", &hints, &ai_list))
|
||||
return false;
|
||||
|
||||
freeaddrinfo(ai_list);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addr_record_is_equal(const addr_record_t *a, const addr_record_t *b, bool compare_ports) {
|
||||
return addr_is_equal((const struct sockaddr *)&a->addr, (const struct sockaddr *)&b->addr,
|
||||
compare_ports);
|
||||
}
|
||||
|
||||
int addr_record_to_string(const addr_record_t *record, char *buffer, size_t size) {
|
||||
return addr_to_string((const struct sockaddr *)&record->addr, buffer, size);
|
||||
}
|
||||
|
||||
unsigned long addr_record_hash(const addr_record_t *record, bool with_port) {
|
||||
return addr_hash((const struct sockaddr *)&record->addr, with_port);
|
||||
}
|
||||
45
thirdparty/libjuice/src/addr.h
vendored
Normal file
45
thirdparty/libjuice/src/addr.h
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_ADDR_H
|
||||
#define JUICE_ADDR_H
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// IPv6 max representation length is 45 plus 4 for potential zone index
|
||||
#define ADDR_MAX_NUMERICHOST_LEN 56 // 45 + 4 + 1 rounded up
|
||||
#define ADDR_MAX_NUMERICSERV_LEN 8 // 5 + 1 rounded up
|
||||
#define ADDR_MAX_STRING_LEN 64
|
||||
|
||||
socklen_t addr_get_len(const struct sockaddr *sa);
|
||||
uint16_t addr_get_port(const struct sockaddr *sa);
|
||||
int addr_set_port(struct sockaddr *sa, uint16_t port);
|
||||
bool addr_is_any(const struct sockaddr *sa);
|
||||
bool addr_is_local(const struct sockaddr *sa);
|
||||
bool addr_unmap_inet6_v4mapped(struct sockaddr *sa, socklen_t *len);
|
||||
bool addr_map_inet6_v4mapped(struct sockaddr_storage *ss, socklen_t *len);
|
||||
bool addr_is_equal(const struct sockaddr *a, const struct sockaddr *b, bool compare_ports);
|
||||
int addr_to_string(const struct sockaddr *sa, char *buffer, size_t size);
|
||||
unsigned long addr_hash(const struct sockaddr *sa, bool with_port);
|
||||
|
||||
typedef struct addr_record {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len;
|
||||
} addr_record_t;
|
||||
|
||||
int addr_resolve(const char *hostname, const char *service, addr_record_t *records, size_t count);
|
||||
bool addr_is_numeric_hostname(const char *hostname);
|
||||
|
||||
bool addr_record_is_equal(const addr_record_t *a, const addr_record_t *b, bool compare_ports);
|
||||
int addr_record_to_string(const addr_record_t *record, char *buffer, size_t size);
|
||||
unsigned long addr_record_hash(const addr_record_t *record, bool with_port);
|
||||
|
||||
#endif // JUICE_ADDR_H
|
||||
2514
thirdparty/libjuice/src/agent.c
vendored
Normal file
2514
thirdparty/libjuice/src/agent.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
227
thirdparty/libjuice/src/agent.h
vendored
Normal file
227
thirdparty/libjuice/src/agent.h
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_AGENT_H
|
||||
#define JUICE_AGENT_H
|
||||
|
||||
#include "addr.h"
|
||||
#include "conn.h"
|
||||
#include "ice.h"
|
||||
#include "juice.h"
|
||||
#include "stun.h"
|
||||
#include "thread.h"
|
||||
#include "timestamp.h"
|
||||
#include "turn.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// RFC 8445: Agents MUST NOT use an RTO value smaller than 500 ms.
|
||||
#define MIN_STUN_RETRANSMISSION_TIMEOUT 500 // msecs
|
||||
#define MAX_STUN_RETRANSMISSION_COUNT 5 // count (exponential backoff, will give ~30s)
|
||||
|
||||
// RFC 8445: ICE agents SHOULD use a default Ta value, 50 ms, but MAY use
|
||||
// another value based on the characteristics of the associated data.
|
||||
#define STUN_PACING_TIME 50 // msecs
|
||||
|
||||
// RFC 8445: Agents SHOULD use a Tr value of 15 seconds. Agents MAY use a bigger value but MUST NOT
|
||||
// use a value smaller than 15 seconds.
|
||||
#define STUN_KEEPALIVE_PERIOD 15000 // msecs
|
||||
|
||||
// Consent freshness
|
||||
// RFC 7675: Consent expires after 30 seconds.
|
||||
#define CONSENT_TIMEOUT 30000 // msecs
|
||||
|
||||
// RFC 7675: To prevent synchronization of consent checks, each interval MUST be randomized from
|
||||
// between 0.8 and 1.2 times the basic period. Implementations SHOULD set a default interval of 5
|
||||
// seconds, resulting in a period between checks of 4 to 6 seconds. Implementations MUST NOT set the
|
||||
// period between checks to less than 4 seconds.
|
||||
#define MIN_CONSENT_CHECK_PERIOD 4000 // msecs
|
||||
#define MAX_CONSENT_CHECK_PERIOD 6000 // msecs
|
||||
|
||||
// TURN refresh period
|
||||
#define TURN_LIFETIME 600000 // msecs, 10 min
|
||||
#define TURN_REFRESH_PERIOD (TURN_LIFETIME - 60000) // msecs, lifetime - 1 min
|
||||
|
||||
// ICE trickling timeout
|
||||
#define ICE_FAIL_TIMEOUT 30000 // msecs
|
||||
|
||||
// Max STUN and TURN server entries
|
||||
#define MAX_SERVER_ENTRIES_COUNT 2 // max STUN server entries
|
||||
#define MAX_RELAY_ENTRIES_COUNT 2 // max TURN server entries
|
||||
|
||||
// Max TURN redirections for ALTERNATE-SERVER mechanism
|
||||
#define MAX_TURN_REDIRECTIONS 1
|
||||
|
||||
// Compute max candidates and entries count
|
||||
#define MAX_STUN_SERVER_RECORDS_COUNT MAX_SERVER_ENTRIES_COUNT
|
||||
#define MAX_HOST_CANDIDATES_COUNT ((ICE_MAX_CANDIDATES_COUNT - MAX_STUN_SERVER_RECORDS_COUNT) / 2)
|
||||
#define MAX_PEER_REFLEXIVE_CANDIDATES_COUNT MAX_HOST_CANDIDATES_COUNT
|
||||
#define MAX_CANDIDATE_PAIRS_COUNT (ICE_MAX_CANDIDATES_COUNT * (1 + MAX_RELAY_ENTRIES_COUNT))
|
||||
#define MAX_STUN_ENTRIES_COUNT (MAX_CANDIDATE_PAIRS_COUNT + MAX_STUN_SERVER_RECORDS_COUNT)
|
||||
|
||||
#define AGENT_TURN_MAP_SIZE ICE_MAX_CANDIDATES_COUNT
|
||||
|
||||
typedef enum agent_mode {
|
||||
AGENT_MODE_UNKNOWN,
|
||||
AGENT_MODE_CONTROLLED,
|
||||
AGENT_MODE_CONTROLLING
|
||||
} agent_mode_t;
|
||||
|
||||
typedef enum agent_stun_entry_type {
|
||||
AGENT_STUN_ENTRY_TYPE_EMPTY,
|
||||
AGENT_STUN_ENTRY_TYPE_SERVER,
|
||||
AGENT_STUN_ENTRY_TYPE_RELAY,
|
||||
AGENT_STUN_ENTRY_TYPE_CHECK
|
||||
} agent_stun_entry_type_t;
|
||||
|
||||
typedef enum agent_stun_entry_state {
|
||||
AGENT_STUN_ENTRY_STATE_PENDING,
|
||||
AGENT_STUN_ENTRY_STATE_CANCELLED,
|
||||
AGENT_STUN_ENTRY_STATE_FAILED,
|
||||
AGENT_STUN_ENTRY_STATE_SUCCEEDED,
|
||||
AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE,
|
||||
AGENT_STUN_ENTRY_STATE_IDLE
|
||||
} agent_stun_entry_state_t;
|
||||
|
||||
typedef struct agent_turn_state {
|
||||
turn_map_t map;
|
||||
stun_credentials_t credentials;
|
||||
const char *password;
|
||||
} agent_turn_state_t;
|
||||
|
||||
typedef struct agent_stun_entry {
|
||||
agent_stun_entry_type_t type;
|
||||
agent_stun_entry_state_t state;
|
||||
agent_mode_t mode;
|
||||
ice_candidate_pair_t *pair;
|
||||
addr_record_t record;
|
||||
addr_record_t relayed;
|
||||
uint8_t transaction_id[STUN_TRANSACTION_ID_SIZE];
|
||||
timestamp_t next_transmission;
|
||||
timediff_t retransmission_timeout;
|
||||
int retransmissions;
|
||||
|
||||
// TURN
|
||||
agent_turn_state_t *turn;
|
||||
unsigned int turn_redirections;
|
||||
struct agent_stun_entry *relay_entry;
|
||||
|
||||
} agent_stun_entry_t;
|
||||
|
||||
struct juice_agent {
|
||||
juice_config_t config;
|
||||
juice_state_t state;
|
||||
agent_mode_t mode;
|
||||
|
||||
ice_description_t local;
|
||||
ice_description_t remote;
|
||||
|
||||
ice_candidate_pair_t candidate_pairs[MAX_CANDIDATE_PAIRS_COUNT];
|
||||
ice_candidate_pair_t *ordered_pairs[MAX_CANDIDATE_PAIRS_COUNT];
|
||||
ice_candidate_pair_t *selected_pair;
|
||||
int candidate_pairs_count;
|
||||
|
||||
agent_stun_entry_t entries[MAX_STUN_ENTRIES_COUNT];
|
||||
int entries_count;
|
||||
atomic_ptr(agent_stun_entry_t) selected_entry;
|
||||
|
||||
uint64_t ice_tiebreaker;
|
||||
timestamp_t fail_timestamp;
|
||||
bool gathering_done;
|
||||
|
||||
int conn_index;
|
||||
void *conn_impl;
|
||||
|
||||
thread_t resolver_thread;
|
||||
bool resolver_thread_started;
|
||||
};
|
||||
|
||||
juice_agent_t *agent_create(const juice_config_t *config);
|
||||
void agent_destroy(juice_agent_t *agent);
|
||||
|
||||
int agent_gather_candidates(juice_agent_t *agent);
|
||||
int agent_resolve_servers(juice_agent_t *agent);
|
||||
int agent_get_local_description(juice_agent_t *agent, char *buffer, size_t size);
|
||||
int agent_set_remote_description(juice_agent_t *agent, const char *sdp);
|
||||
int agent_add_remote_candidate(juice_agent_t *agent, const char *sdp);
|
||||
int agent_set_remote_gathering_done(juice_agent_t *agent);
|
||||
int agent_send(juice_agent_t *agent, const char *data, size_t size, int ds);
|
||||
int agent_direct_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds);
|
||||
int agent_relay_send(juice_agent_t *agent, agent_stun_entry_t *entry, const addr_record_t *dst,
|
||||
const char *data, size_t size, int ds);
|
||||
int agent_channel_send(juice_agent_t *agent, agent_stun_entry_t *entry, const addr_record_t *dst,
|
||||
const char *data, size_t size, int ds);
|
||||
juice_state_t agent_get_state(juice_agent_t *agent);
|
||||
int agent_get_selected_candidate_pair(juice_agent_t *agent, ice_candidate_t *local,
|
||||
ice_candidate_t *remote);
|
||||
|
||||
int agent_conn_recv(juice_agent_t *agent, char *buf, size_t len, const addr_record_t *src);
|
||||
int agent_conn_update(juice_agent_t *agent, timestamp_t *next_timestamp);
|
||||
int agent_conn_fail(juice_agent_t *agent);
|
||||
|
||||
int agent_input(juice_agent_t *agent, char *buf, size_t len, const addr_record_t *src,
|
||||
const addr_record_t *relayed); // relayed may be NULL
|
||||
int agent_bookkeeping(juice_agent_t *agent, timestamp_t *next_timestamp);
|
||||
void agent_change_state(juice_agent_t *agent, juice_state_t state);
|
||||
int agent_verify_stun_binding(juice_agent_t *agent, void *buf, size_t size,
|
||||
const stun_message_t *msg);
|
||||
int agent_verify_credentials(juice_agent_t *agent, const agent_stun_entry_t *entry, void *buf,
|
||||
size_t size, stun_message_t *msg);
|
||||
int agent_dispatch_stun(juice_agent_t *agent, void *buf, size_t size, stun_message_t *msg,
|
||||
const addr_record_t *src,
|
||||
const addr_record_t *relayed); // relayed may be NULL
|
||||
int agent_process_stun_binding(juice_agent_t *agent, const stun_message_t *msg,
|
||||
agent_stun_entry_t *entry, const addr_record_t *src,
|
||||
const addr_record_t *relayed); // relayed may be NULL
|
||||
int agent_send_stun_binding(juice_agent_t *agent, agent_stun_entry_t *entry, stun_class_t msg_class,
|
||||
unsigned int error_code, const uint8_t *transaction_id,
|
||||
const addr_record_t *mapped);
|
||||
int agent_process_turn_allocate(juice_agent_t *agent, const stun_message_t *msg,
|
||||
agent_stun_entry_t *entry);
|
||||
int agent_send_turn_allocate_request(juice_agent_t *agent, const agent_stun_entry_t *entry,
|
||||
stun_method_t method);
|
||||
int agent_process_turn_create_permission(juice_agent_t *agent, const stun_message_t *msg,
|
||||
agent_stun_entry_t *entry);
|
||||
int agent_send_turn_create_permission_request(juice_agent_t *agent, agent_stun_entry_t *entry,
|
||||
const addr_record_t *record, int ds);
|
||||
int agent_process_turn_channel_bind(juice_agent_t *agent, const stun_message_t *msg,
|
||||
agent_stun_entry_t *entry);
|
||||
int agent_send_turn_channel_bind_request(juice_agent_t *agent, agent_stun_entry_t *entry,
|
||||
const addr_record_t *record, int ds,
|
||||
uint16_t *out_channel); // out_channel may be NULL
|
||||
int agent_process_turn_data(juice_agent_t *agent, const stun_message_t *msg,
|
||||
agent_stun_entry_t *entry);
|
||||
int agent_process_channel_data(juice_agent_t *agent, agent_stun_entry_t *entry, char *buf,
|
||||
size_t len);
|
||||
|
||||
int agent_add_local_relayed_candidate(juice_agent_t *agent, const addr_record_t *record);
|
||||
int agent_add_local_reflexive_candidate(juice_agent_t *agent, ice_candidate_type_t type,
|
||||
const addr_record_t *record);
|
||||
int agent_add_remote_reflexive_candidate(juice_agent_t *agent, ice_candidate_type_t type,
|
||||
uint32_t priority, const addr_record_t *record);
|
||||
int agent_add_candidate_pair(juice_agent_t *agent, ice_candidate_t *local,
|
||||
ice_candidate_t *remote); // local may be NULL
|
||||
int agent_add_candidate_pairs_for_remote(juice_agent_t *agent, ice_candidate_t *remote);
|
||||
int agent_unfreeze_candidate_pair(juice_agent_t *agent, ice_candidate_pair_t *pair);
|
||||
|
||||
void agent_arm_keepalive(juice_agent_t *agent, agent_stun_entry_t *entry);
|
||||
void agent_arm_transmission(juice_agent_t *agent, agent_stun_entry_t *entry, timediff_t delay);
|
||||
void agent_update_gathering_done(juice_agent_t *agent);
|
||||
void agent_update_candidate_pairs(juice_agent_t *agent);
|
||||
void agent_update_ordered_pairs(juice_agent_t *agent);
|
||||
|
||||
agent_stun_entry_t *agent_find_entry_from_transaction_id(juice_agent_t *agent,
|
||||
const uint8_t *transaction_id);
|
||||
agent_stun_entry_t *
|
||||
agent_find_entry_from_record(juice_agent_t *agent, const addr_record_t *record,
|
||||
const addr_record_t *relayed); // relayed may be NULL
|
||||
void agent_translate_host_candidate_entry(juice_agent_t *agent, agent_stun_entry_t *entry);
|
||||
|
||||
#endif
|
||||
90
thirdparty/libjuice/src/base64.c
vendored
Normal file
90
thirdparty/libjuice/src/base64.c
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
int juice_base64_encode(const void *data, size_t size, char *out, size_t out_size) {
|
||||
static const char tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
if (out_size < 4 * ((size + 2) / 3) + 1)
|
||||
return -1;
|
||||
|
||||
const uint8_t *in = (const uint8_t *)data;
|
||||
char *w = out;
|
||||
while (size >= 3) {
|
||||
*w++ = tab[*in >> 2];
|
||||
*w++ = tab[((*in & 0x03) << 4) | (*(in + 1) >> 4)];
|
||||
*w++ = tab[((*(in + 1) & 0x0F) << 2) | (*(in + 2) >> 6)];
|
||||
*w++ = tab[*(in + 2) & 0x3F];
|
||||
in += 3;
|
||||
size -= 3;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
*w++ = tab[*in >> 2];
|
||||
if (size == 1) {
|
||||
*w++ = tab[(*in & 0x03) << 4];
|
||||
*w++ = '=';
|
||||
} else { // size == 2
|
||||
*w++ = tab[((*in & 0x03) << 4) | (*(in + 1) >> 4)];
|
||||
*w++ = tab[(*(in + 1) & 0x0F) << 2];
|
||||
}
|
||||
*w++ = '=';
|
||||
}
|
||||
|
||||
*w = '\0';
|
||||
return (int)(w - out);
|
||||
}
|
||||
|
||||
int juice_base64_decode(const char *str, void *out, size_t out_size) {
|
||||
const uint8_t *in = (const uint8_t *)str;
|
||||
uint8_t *w = (uint8_t *)out;
|
||||
while (*in && *in != '=') {
|
||||
uint8_t tab[4] = {0, 0, 0, 0};
|
||||
size_t size = 0;
|
||||
while (*in && size < 4) {
|
||||
uint8_t c = *in++;
|
||||
if (isspace(c))
|
||||
continue;
|
||||
if (c == '=')
|
||||
break;
|
||||
|
||||
if ('A' <= c && c <= 'Z')
|
||||
tab[size++] = c - 'A';
|
||||
else if ('a' <= c && c <= 'z')
|
||||
tab[size++] = c + 26 - 'a';
|
||||
else if ('0' <= c && c <= '9')
|
||||
tab[size++] = c + 52 - '0';
|
||||
else if (c == '+' || c == '-')
|
||||
tab[size++] = 62;
|
||||
else if (c == '/' || c == '_')
|
||||
tab[size++] = 63;
|
||||
else
|
||||
return -1; // Invalid character
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
if (out_size < size - 1)
|
||||
return -1;
|
||||
|
||||
out_size -= size - 1;
|
||||
|
||||
*w++ = (tab[0] << 2) | (tab[1] >> 4);
|
||||
if (size > 1) {
|
||||
*w++ = (tab[1] << 4) | (tab[2] >> 2);
|
||||
if (size > 2)
|
||||
*w++ = (tab[2] << 6) | tab[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (int)(w - (uint8_t *)out);
|
||||
}
|
||||
24
thirdparty/libjuice/src/base64.h
vendored
Normal file
24
thirdparty/libjuice/src/base64.h
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_BASE64_H
|
||||
#define JUICE_BASE64_H
|
||||
|
||||
#include "juice.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// RFC4648-compliant base64 encoder and decoder
|
||||
JUICE_EXPORT int juice_base64_encode(const void *data, size_t size, char *out, size_t out_size);
|
||||
JUICE_EXPORT int juice_base64_decode(const char *str, void *out, size_t out_size);
|
||||
|
||||
#define BASE64_ENCODE(data, size, out, out_size) juice_base64_encode(data, size, out, out_size)
|
||||
#define BASE64_DECODE(str, out, out_size) juice_base64_decode(str, out, out_size)
|
||||
|
||||
#endif // JUICE_BASE64_H
|
||||
249
thirdparty/libjuice/src/conn.c
vendored
Normal file
249
thirdparty/libjuice/src/conn.c
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "conn.h"
|
||||
#include "agent.h"
|
||||
#include "conn_mux.h"
|
||||
#include "conn_poll.h"
|
||||
#include "conn_thread.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define INITIAL_REGISTRY_SIZE 16
|
||||
|
||||
typedef struct conn_mode_entry {
|
||||
int (*registry_init_func)(conn_registry_t *registry, udp_socket_config_t *config);
|
||||
void (*registry_cleanup_func)(conn_registry_t *registry);
|
||||
|
||||
int (*init_func)(juice_agent_t *agent, struct conn_registry *registry,
|
||||
udp_socket_config_t *config);
|
||||
void (*cleanup_func)(juice_agent_t *agent);
|
||||
void (*lock_func)(juice_agent_t *agent);
|
||||
void (*unlock_func)(juice_agent_t *agent);
|
||||
int (*interrupt_func)(juice_agent_t *agent);
|
||||
int (*send_func)(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds);
|
||||
int (*get_addrs_func)(juice_agent_t *agent, addr_record_t *records, size_t size);
|
||||
|
||||
mutex_t mutex;
|
||||
conn_registry_t *registry;
|
||||
} conn_mode_entry_t;
|
||||
|
||||
#define MODE_ENTRIES_SIZE 3
|
||||
|
||||
static conn_mode_entry_t mode_entries[MODE_ENTRIES_SIZE] = {
|
||||
{conn_poll_registry_init, conn_poll_registry_cleanup, conn_poll_init, conn_poll_cleanup,
|
||||
conn_poll_lock, conn_poll_unlock, conn_poll_interrupt, conn_poll_send, conn_poll_get_addrs,
|
||||
MUTEX_INITIALIZER, NULL},
|
||||
{conn_mux_registry_init, conn_mux_registry_cleanup, conn_mux_init, conn_mux_cleanup,
|
||||
conn_mux_lock, conn_mux_unlock, conn_mux_interrupt, conn_mux_send, conn_mux_get_addrs,
|
||||
MUTEX_INITIALIZER, NULL},
|
||||
{NULL, NULL, conn_thread_init, conn_thread_cleanup, conn_thread_lock, conn_thread_unlock,
|
||||
conn_thread_interrupt, conn_thread_send, conn_thread_get_addrs, MUTEX_INITIALIZER, NULL}};
|
||||
|
||||
static conn_mode_entry_t *get_mode_entry(juice_agent_t *agent) {
|
||||
juice_concurrency_mode_t mode = agent->config.concurrency_mode;
|
||||
assert(mode >= 0 && mode < MODE_ENTRIES_SIZE);
|
||||
return mode_entries + (int)mode;
|
||||
}
|
||||
|
||||
static conn_registry_t *acquire_registry(conn_mode_entry_t *entry, udp_socket_config_t *config) {
|
||||
// entry must be locked
|
||||
conn_registry_t *registry = entry->registry;
|
||||
if (!registry) {
|
||||
if (!entry->registry_init_func)
|
||||
return NULL;
|
||||
|
||||
JLOG_DEBUG("Creating connections registry");
|
||||
|
||||
registry = calloc(1, sizeof(conn_registry_t));
|
||||
if (!registry) {
|
||||
JLOG_FATAL("Memory allocation failed for connections registry");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
registry->agents = malloc(INITIAL_REGISTRY_SIZE * sizeof(juice_agent_t *));
|
||||
if (!registry->agents) {
|
||||
JLOG_FATAL("Memory allocation failed for connections array");
|
||||
free(registry);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
registry->agents_size = INITIAL_REGISTRY_SIZE;
|
||||
registry->agents_count = 0;
|
||||
memset(registry->agents, 0, INITIAL_REGISTRY_SIZE * sizeof(juice_agent_t *));
|
||||
|
||||
mutex_init(®istry->mutex, MUTEX_RECURSIVE);
|
||||
mutex_lock(®istry->mutex);
|
||||
|
||||
if (entry->registry_init_func(registry, config)) {
|
||||
mutex_unlock(®istry->mutex);
|
||||
free(registry->agents);
|
||||
free(registry);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry->registry = registry;
|
||||
|
||||
} else {
|
||||
mutex_lock(®istry->mutex);
|
||||
}
|
||||
|
||||
// registry is locked
|
||||
return registry;
|
||||
}
|
||||
|
||||
static void release_registry(conn_mode_entry_t *entry) {
|
||||
// entry must be locked
|
||||
conn_registry_t *registry = entry->registry;
|
||||
if (!registry)
|
||||
return;
|
||||
|
||||
// registry must be locked
|
||||
|
||||
if (registry->agents_count == 0) {
|
||||
JLOG_DEBUG("No connection left, destroying connections registry");
|
||||
mutex_unlock(®istry->mutex);
|
||||
|
||||
if (entry->registry_cleanup_func)
|
||||
entry->registry_cleanup_func(registry);
|
||||
|
||||
free(registry->agents);
|
||||
free(registry);
|
||||
entry->registry = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
JLOG_VERBOSE("%d connection%s left", registry->agents_count,
|
||||
registry->agents_count >= 2 ? "s" : "");
|
||||
|
||||
mutex_unlock(®istry->mutex);
|
||||
}
|
||||
|
||||
int conn_create(juice_agent_t *agent, udp_socket_config_t *config) {
|
||||
conn_mode_entry_t *entry = get_mode_entry(agent);
|
||||
mutex_lock(&entry->mutex);
|
||||
conn_registry_t *registry = acquire_registry(entry, config); // locks the registry if created
|
||||
mutex_unlock(&entry->mutex);
|
||||
|
||||
JLOG_DEBUG("Creating connection");
|
||||
if (registry) {
|
||||
int i = 0;
|
||||
while (i < registry->agents_size && registry->agents[i])
|
||||
++i;
|
||||
|
||||
if (i == registry->agents_size) {
|
||||
int new_size = registry->agents_size * 2;
|
||||
JLOG_DEBUG("Reallocating connections array, new_size=%d", new_size);
|
||||
assert(new_size > 0);
|
||||
|
||||
juice_agent_t **new_agents =
|
||||
realloc(registry->agents, new_size * sizeof(juice_agent_t *));
|
||||
if (!new_agents) {
|
||||
JLOG_FATAL("Memory reallocation failed for connections array");
|
||||
mutex_unlock(®istry->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
registry->agents = new_agents;
|
||||
registry->agents_size = new_size;
|
||||
memset(registry->agents + i, 0, (new_size - i) * sizeof(juice_agent_t *));
|
||||
}
|
||||
|
||||
if (get_mode_entry(agent)->init_func(agent, registry, config)) {
|
||||
mutex_unlock(®istry->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
registry->agents[i] = agent;
|
||||
agent->conn_index = i;
|
||||
++registry->agents_count;
|
||||
|
||||
mutex_unlock(®istry->mutex);
|
||||
|
||||
} else {
|
||||
if (get_mode_entry(agent)->init_func(agent, NULL, config)) {
|
||||
mutex_unlock(®istry->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
agent->conn_index = -1;
|
||||
}
|
||||
|
||||
conn_interrupt(agent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conn_destroy(juice_agent_t *agent) {
|
||||
conn_mode_entry_t *entry = get_mode_entry(agent);
|
||||
mutex_lock(&entry->mutex);
|
||||
|
||||
JLOG_DEBUG("Destroying connection");
|
||||
conn_registry_t *registry = entry->registry;
|
||||
if (registry) {
|
||||
mutex_lock(®istry->mutex);
|
||||
|
||||
entry->cleanup_func(agent);
|
||||
|
||||
if (agent->conn_index >= 0) {
|
||||
int i = agent->conn_index;
|
||||
assert(registry->agents[i] == agent);
|
||||
registry->agents[i] = NULL;
|
||||
agent->conn_index = -1;
|
||||
}
|
||||
|
||||
assert(registry->agents_count > 0);
|
||||
--registry->agents_count;
|
||||
|
||||
release_registry(entry); // unlocks the registry
|
||||
|
||||
} else {
|
||||
entry->cleanup_func(agent);
|
||||
assert(agent->conn_index < 0);
|
||||
}
|
||||
|
||||
mutex_unlock(&entry->mutex);
|
||||
}
|
||||
|
||||
void conn_lock(juice_agent_t *agent) {
|
||||
if (!agent->conn_impl)
|
||||
return;
|
||||
|
||||
get_mode_entry(agent)->lock_func(agent);
|
||||
}
|
||||
|
||||
void conn_unlock(juice_agent_t *agent) {
|
||||
if (!agent->conn_impl)
|
||||
return;
|
||||
|
||||
get_mode_entry(agent)->unlock_func(agent);
|
||||
}
|
||||
|
||||
int conn_interrupt(juice_agent_t *agent) {
|
||||
if (!agent->conn_impl)
|
||||
return -1;
|
||||
|
||||
return get_mode_entry(agent)->interrupt_func(agent);
|
||||
}
|
||||
|
||||
int conn_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds) {
|
||||
if (!agent->conn_impl)
|
||||
return -1;
|
||||
|
||||
return get_mode_entry(agent)->send_func(agent, dst, data, size, ds);
|
||||
}
|
||||
|
||||
int conn_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size) {
|
||||
if (!agent->conn_impl)
|
||||
return -1;
|
||||
|
||||
return get_mode_entry(agent)->get_addrs_func(agent, records, size);
|
||||
}
|
||||
44
thirdparty/libjuice/src/conn.h
vendored
Normal file
44
thirdparty/libjuice/src/conn.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_CONN_H
|
||||
#define JUICE_CONN_H
|
||||
|
||||
#include "addr.h"
|
||||
#include "juice.h"
|
||||
#include "thread.h"
|
||||
#include "timestamp.h"
|
||||
#include "udp.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct juice_agent juice_agent_t;
|
||||
|
||||
// Generic connection interface for agents
|
||||
// This interface abstracts sockets and polling to allow for different concurrency modes.
|
||||
// See include/juice/juice.h for implemented concurrency modes
|
||||
|
||||
typedef struct conn_registry {
|
||||
void *impl;
|
||||
mutex_t mutex;
|
||||
juice_agent_t **agents;
|
||||
int agents_size;
|
||||
int agents_count;
|
||||
} conn_registry_t;
|
||||
|
||||
int conn_create(juice_agent_t *agent, udp_socket_config_t *config);
|
||||
void conn_destroy(juice_agent_t *agent);
|
||||
void conn_lock(juice_agent_t *agent);
|
||||
void conn_unlock(juice_agent_t *agent);
|
||||
int conn_interrupt(juice_agent_t *agent);
|
||||
int conn_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds);
|
||||
int conn_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size);
|
||||
|
||||
#endif
|
||||
540
thirdparty/libjuice/src/conn_mux.c
vendored
Normal file
540
thirdparty/libjuice/src/conn_mux.c
vendored
Normal file
@@ -0,0 +1,540 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "conn_mux.h"
|
||||
#include "agent.h"
|
||||
#include "log.h"
|
||||
#include "socket.h"
|
||||
#include "stun.h"
|
||||
#include "thread.h"
|
||||
#include "udp.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
#define INITIAL_MAP_SIZE 16
|
||||
|
||||
typedef enum map_entry_type {
|
||||
MAP_ENTRY_TYPE_EMPTY = 0,
|
||||
MAP_ENTRY_TYPE_DELETED,
|
||||
MAP_ENTRY_TYPE_FULL
|
||||
} map_entry_type_t;
|
||||
|
||||
typedef struct map_entry {
|
||||
map_entry_type_t type;
|
||||
juice_agent_t *agent;
|
||||
addr_record_t record;
|
||||
} map_entry_t;
|
||||
|
||||
typedef struct registry_impl {
|
||||
thread_t thread;
|
||||
socket_t sock;
|
||||
mutex_t send_mutex;
|
||||
int send_ds;
|
||||
map_entry_t *map;
|
||||
int map_size;
|
||||
int map_count;
|
||||
} registry_impl_t;
|
||||
|
||||
typedef struct conn_impl {
|
||||
conn_registry_t *registry;
|
||||
timestamp_t next_timestamp;
|
||||
bool finished;
|
||||
} conn_impl_t;
|
||||
|
||||
static bool is_ready(const juice_agent_t *agent) {
|
||||
if (!agent)
|
||||
return false;
|
||||
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
if (!conn_impl || conn_impl->finished)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static map_entry_t *find_map_entry(registry_impl_t *impl, const addr_record_t *record,
|
||||
bool allow_deleted);
|
||||
static int insert_map_entry(registry_impl_t *impl, const addr_record_t *record,
|
||||
juice_agent_t *agent);
|
||||
static int remove_map_entries(registry_impl_t *impl, juice_agent_t *agent);
|
||||
static int grow_map(registry_impl_t *impl, int new_size);
|
||||
|
||||
static map_entry_t *find_map_entry(registry_impl_t *impl, const addr_record_t *record,
|
||||
bool allow_deleted) {
|
||||
unsigned long key = addr_record_hash(record, false) % impl->map_size;
|
||||
unsigned long pos = key;
|
||||
while (true) {
|
||||
map_entry_t *entry = impl->map + pos;
|
||||
if (entry->type == MAP_ENTRY_TYPE_EMPTY ||
|
||||
addr_record_is_equal(&entry->record, record, true)) // compare ports
|
||||
break;
|
||||
|
||||
if (entry->type == MAP_ENTRY_TYPE_DELETED && allow_deleted)
|
||||
break;
|
||||
|
||||
pos = (pos + 1) % impl->map_size;
|
||||
if (pos == key)
|
||||
return NULL;
|
||||
}
|
||||
return impl->map + pos;
|
||||
}
|
||||
|
||||
static int insert_map_entry(registry_impl_t *impl, const addr_record_t *record,
|
||||
juice_agent_t *agent) {
|
||||
|
||||
map_entry_t *entry = find_map_entry(impl, record, true); // allow deleted
|
||||
if (!entry || (entry->type != MAP_ENTRY_TYPE_FULL && impl->map_count * 2 >= impl->map_size)) {
|
||||
grow_map(impl, impl->map_size * 2);
|
||||
return insert_map_entry(impl, record, agent);
|
||||
}
|
||||
|
||||
if (entry->type != MAP_ENTRY_TYPE_FULL)
|
||||
++impl->map_count;
|
||||
|
||||
entry->type = MAP_ENTRY_TYPE_FULL;
|
||||
entry->agent = agent;
|
||||
entry->record = *record;
|
||||
|
||||
JLOG_VERBOSE("Added map entry, count=%d", impl->map_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_map_entries(registry_impl_t *impl, juice_agent_t *agent) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < impl->map_size; ++i) {
|
||||
map_entry_t *entry = impl->map + i;
|
||||
if (entry->type == MAP_ENTRY_TYPE_FULL && entry->agent == agent) {
|
||||
entry->type = MAP_ENTRY_TYPE_DELETED;
|
||||
entry->agent = NULL;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
assert(impl->map_count >= count);
|
||||
impl->map_count -= count;
|
||||
|
||||
JLOG_VERBOSE("Removed %d map entries, count=%d", count, impl->map_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int grow_map(registry_impl_t *impl, int new_size) {
|
||||
if (new_size <= impl->map_size)
|
||||
return 0;
|
||||
|
||||
JLOG_DEBUG("Growing map, new_size=%d", new_size);
|
||||
|
||||
map_entry_t *new_map = calloc(1, new_size * sizeof(map_entry_t));
|
||||
if (!new_map) {
|
||||
JLOG_FATAL("Memory allocation failed for map");
|
||||
return -1;
|
||||
}
|
||||
|
||||
map_entry_t *old_map = impl->map;
|
||||
int old_size = impl->map_size;
|
||||
impl->map = new_map;
|
||||
impl->map_size = new_size;
|
||||
impl->map_count = 0;
|
||||
|
||||
for (int i = 0; i < old_size; ++i) {
|
||||
map_entry_t *old_entry = old_map + i;
|
||||
if (old_entry->type == MAP_ENTRY_TYPE_FULL)
|
||||
insert_map_entry(impl, &old_entry->record, old_entry->agent);
|
||||
}
|
||||
|
||||
free(old_map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_mux_prepare(conn_registry_t *registry, struct pollfd *pfd, timestamp_t *next_timestamp);
|
||||
int conn_mux_process(conn_registry_t *registry, struct pollfd *pfd);
|
||||
int conn_mux_recv(conn_registry_t *registry, char *buffer, size_t size, addr_record_t *src);
|
||||
void conn_mux_fail(conn_registry_t *registry);
|
||||
int conn_mux_run(conn_registry_t *registry);
|
||||
|
||||
static thread_return_t THREAD_CALL conn_mux_entry(void *arg) {
|
||||
conn_registry_t *registry = (conn_registry_t *)arg;
|
||||
conn_mux_run(registry);
|
||||
return (thread_return_t)0;
|
||||
}
|
||||
|
||||
int conn_mux_registry_init(conn_registry_t *registry, udp_socket_config_t *config) {
|
||||
(void)config;
|
||||
registry_impl_t *registry_impl = calloc(1, sizeof(registry_impl_t));
|
||||
if (!registry_impl) {
|
||||
JLOG_FATAL("Memory allocation failed for connections registry impl");
|
||||
return -1;
|
||||
}
|
||||
|
||||
registry_impl->map = calloc(INITIAL_MAP_SIZE, sizeof(map_entry_t));
|
||||
if (!registry_impl->map) {
|
||||
JLOG_FATAL("Memory allocation failed for map");
|
||||
free(registry_impl);
|
||||
return -1;
|
||||
}
|
||||
registry_impl->map_size = INITIAL_MAP_SIZE;
|
||||
registry_impl->map_count = 0;
|
||||
|
||||
registry_impl->sock = udp_create_socket(config);
|
||||
if (registry_impl->sock == INVALID_SOCKET) {
|
||||
JLOG_FATAL("UDP socket creation failed");
|
||||
free(registry_impl->map);
|
||||
free(registry_impl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mutex_init(®istry_impl->send_mutex, 0);
|
||||
registry->impl = registry_impl;
|
||||
|
||||
JLOG_DEBUG("Starting connections thread");
|
||||
int ret = thread_init(®istry_impl->thread, conn_mux_entry, registry);
|
||||
if (ret) {
|
||||
JLOG_FATAL("Thread creation failed, error=%d", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
mutex_destroy(®istry_impl->send_mutex);
|
||||
closesocket(registry_impl->sock);
|
||||
free(registry_impl->map);
|
||||
free(registry_impl);
|
||||
registry->impl = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void conn_mux_registry_cleanup(conn_registry_t *registry) {
|
||||
registry_impl_t *registry_impl = registry->impl;
|
||||
|
||||
JLOG_VERBOSE("Waiting for connections thread");
|
||||
thread_join(registry_impl->thread, NULL);
|
||||
|
||||
mutex_destroy(®istry_impl->send_mutex);
|
||||
closesocket(registry_impl->sock);
|
||||
free(registry_impl->map);
|
||||
free(registry->impl);
|
||||
registry->impl = NULL;
|
||||
}
|
||||
|
||||
int conn_mux_prepare(conn_registry_t *registry, struct pollfd *pfd, timestamp_t *next_timestamp) {
|
||||
timestamp_t now = current_timestamp();
|
||||
*next_timestamp = now + 60000;
|
||||
|
||||
mutex_lock(®istry->mutex);
|
||||
registry_impl_t *registry_impl = registry->impl;
|
||||
pfd->fd = registry_impl->sock;
|
||||
pfd->events = POLLIN;
|
||||
|
||||
for (int i = 0; i < registry->agents_size; ++i) {
|
||||
juice_agent_t *agent = registry->agents[i];
|
||||
if (is_ready(agent)) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
if (*next_timestamp > conn_impl->next_timestamp)
|
||||
*next_timestamp = conn_impl->next_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
int count = registry->agents_count;
|
||||
mutex_unlock(®istry->mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
static juice_agent_t *lookup_agent(conn_registry_t *registry, char *buf, size_t len,
|
||||
const addr_record_t *src) {
|
||||
JLOG_VERBOSE("Looking up agent from address");
|
||||
|
||||
registry_impl_t *registry_impl = registry->impl;
|
||||
map_entry_t *entry = find_map_entry(registry_impl, src, false);
|
||||
juice_agent_t *agent = entry && entry->type == MAP_ENTRY_TYPE_FULL ? entry->agent : NULL;
|
||||
if (agent) {
|
||||
JLOG_DEBUG("Found agent from address");
|
||||
return agent;
|
||||
}
|
||||
|
||||
if (!is_stun_datagram(buf, len)) {
|
||||
JLOG_INFO("Got non-STUN message from unknown source address");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JLOG_VERBOSE("Looking up agent from STUN message content");
|
||||
|
||||
stun_message_t msg;
|
||||
if (stun_read(buf, len, &msg) < 0) {
|
||||
JLOG_ERROR("STUN message reading failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (msg.msg_class == STUN_CLASS_REQUEST && msg.msg_method == STUN_METHOD_BINDING &&
|
||||
msg.has_integrity) {
|
||||
// Binding request from peer
|
||||
char username[STUN_MAX_USERNAME_LEN];
|
||||
strcpy(username, msg.credentials.username);
|
||||
char *separator = strchr(username, ':');
|
||||
if (!separator) {
|
||||
JLOG_WARN("STUN username invalid, username=\"%s\"", username);
|
||||
return NULL;
|
||||
}
|
||||
*separator = '\0';
|
||||
const char *local_ufrag = username;
|
||||
for (int i = 0; i < registry->agents_size; ++i) {
|
||||
agent = registry->agents[i];
|
||||
if (is_ready(agent)) {
|
||||
if (strcmp(local_ufrag, agent->local.ice_ufrag) == 0) {
|
||||
JLOG_DEBUG("Found agent from ICE ufrag");
|
||||
insert_map_entry(registry_impl, src, agent);
|
||||
return agent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!STUN_IS_RESPONSE(msg.msg_class)) {
|
||||
JLOG_INFO("Got unexpected STUN message from unknown source address");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < registry->agents_size; ++i) {
|
||||
agent = registry->agents[i];
|
||||
if (is_ready(agent)) {
|
||||
if (agent_find_entry_from_transaction_id(agent, msg.transaction_id)) {
|
||||
JLOG_DEBUG("Found agent from transaction ID");
|
||||
return agent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int conn_mux_process(conn_registry_t *registry, struct pollfd *pfd) {
|
||||
mutex_lock(®istry->mutex);
|
||||
|
||||
if (pfd->revents & POLLNVAL || pfd->revents & POLLERR) {
|
||||
JLOG_ERROR("Error when polling socket");
|
||||
conn_mux_fail(registry);
|
||||
mutex_unlock(®istry->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pfd->revents & POLLIN) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
addr_record_t src;
|
||||
int ret;
|
||||
while ((ret = conn_mux_recv(registry, buffer, BUFFER_SIZE, &src)) > 0) {
|
||||
if (JLOG_DEBUG_ENABLED) {
|
||||
char src_str[ADDR_MAX_STRING_LEN];
|
||||
addr_record_to_string(&src, src_str, ADDR_MAX_STRING_LEN);
|
||||
JLOG_DEBUG("Demultiplexing incoming datagram from %s", src_str);
|
||||
}
|
||||
|
||||
juice_agent_t *agent = lookup_agent(registry, buffer, (size_t)ret, &src);
|
||||
if (!agent || !is_ready(agent)) {
|
||||
JLOG_DEBUG("Agent not found for incoming datagram, dropping");
|
||||
continue;
|
||||
}
|
||||
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
if (agent_conn_recv(agent, buffer, (size_t)ret, &src) != 0) {
|
||||
JLOG_WARN("Agent receive failed");
|
||||
conn_impl->finished = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
conn_impl->next_timestamp = current_timestamp();
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
conn_mux_fail(registry);
|
||||
mutex_unlock(®istry->mutex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < registry->agents_size; ++i) {
|
||||
juice_agent_t *agent = registry->agents[i];
|
||||
if (is_ready(agent)) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
if (conn_impl->next_timestamp <= current_timestamp()) {
|
||||
if (agent_conn_update(agent, &conn_impl->next_timestamp) != 0) {
|
||||
JLOG_WARN("Agent update failed");
|
||||
conn_impl->finished = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(®istry->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_mux_recv(conn_registry_t *registry, char *buffer, size_t size, addr_record_t *src) {
|
||||
JLOG_VERBOSE("Receiving datagram");
|
||||
registry_impl_t *registry_impl = registry->impl;
|
||||
int len;
|
||||
while ((len = udp_recvfrom(registry_impl->sock, buffer, size, src)) == 0) {
|
||||
// Empty datagram (used to interrupt)
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK) {
|
||||
JLOG_VERBOSE("No more datagrams to receive");
|
||||
return 0;
|
||||
}
|
||||
JLOG_ERROR("recvfrom failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_unmap_inet6_v4mapped((struct sockaddr *)&src->addr, &src->len);
|
||||
return len; // len > 0
|
||||
}
|
||||
|
||||
void conn_mux_fail(conn_registry_t *registry) {
|
||||
for (int i = 0; i < registry->agents_size; ++i) {
|
||||
juice_agent_t *agent = registry->agents[i];
|
||||
if (is_ready(agent)) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
agent_conn_fail(agent);
|
||||
conn_impl->finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int conn_mux_run(conn_registry_t *registry) {
|
||||
struct pollfd pfd[1];
|
||||
timestamp_t next_timestamp;
|
||||
while (conn_mux_prepare(registry, pfd, &next_timestamp) > 0) {
|
||||
timediff_t timediff = next_timestamp - current_timestamp();
|
||||
if (timediff < 0)
|
||||
timediff = 0;
|
||||
|
||||
JLOG_VERBOSE("Entering poll for %d ms", (int)timediff);
|
||||
int ret = poll(pfd, 1, (int)timediff);
|
||||
JLOG_VERBOSE("Leaving poll");
|
||||
if (ret < 0) {
|
||||
if (sockerrno == SEINTR || sockerrno == SEAGAIN) {
|
||||
JLOG_VERBOSE("poll interrupted");
|
||||
continue;
|
||||
} else {
|
||||
JLOG_FATAL("poll failed, errno=%d", sockerrno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn_mux_process(registry, pfd) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
JLOG_DEBUG("Leaving connections thread");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_mux_init(juice_agent_t *agent, conn_registry_t *registry, udp_socket_config_t *config) {
|
||||
(void)config; // ignored, only the config from the first connection is used
|
||||
|
||||
conn_impl_t *conn_impl = calloc(1, sizeof(conn_impl_t));
|
||||
if (!conn_impl) {
|
||||
JLOG_FATAL("Memory allocation failed for connection impl");
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn_impl->registry = registry;
|
||||
agent->conn_impl = conn_impl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conn_mux_cleanup(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
conn_registry_t *registry = conn_impl->registry;
|
||||
|
||||
mutex_lock(®istry->mutex);
|
||||
registry_impl_t *registry_impl = registry->impl;
|
||||
remove_map_entries(registry_impl, agent);
|
||||
mutex_unlock(®istry->mutex);
|
||||
|
||||
conn_mux_interrupt(agent);
|
||||
|
||||
free(agent->conn_impl);
|
||||
agent->conn_impl = NULL;
|
||||
}
|
||||
|
||||
void conn_mux_lock(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
conn_registry_t *registry = conn_impl->registry;
|
||||
mutex_lock(®istry->mutex);
|
||||
}
|
||||
|
||||
void conn_mux_unlock(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
conn_registry_t *registry = conn_impl->registry;
|
||||
mutex_unlock(®istry->mutex);
|
||||
}
|
||||
|
||||
int conn_mux_interrupt(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
conn_registry_t *registry = conn_impl->registry;
|
||||
|
||||
mutex_lock(®istry->mutex);
|
||||
conn_impl->next_timestamp = current_timestamp();
|
||||
mutex_unlock(®istry->mutex);
|
||||
|
||||
JLOG_VERBOSE("Interrupting connections thread");
|
||||
|
||||
registry_impl_t *registry_impl = registry->impl;
|
||||
mutex_lock(®istry_impl->send_mutex);
|
||||
if (udp_sendto_self(registry_impl->sock, NULL, 0) < 0) {
|
||||
if (sockerrno != SEAGAIN && sockerrno != SEWOULDBLOCK) {
|
||||
JLOG_WARN("Failed to interrupt poll by triggering socket, errno=%d", sockerrno);
|
||||
}
|
||||
mutex_unlock(®istry_impl->send_mutex);
|
||||
return -1;
|
||||
}
|
||||
mutex_unlock(®istry_impl->send_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_mux_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
registry_impl_t *registry_impl = conn_impl->registry->impl;
|
||||
|
||||
mutex_lock(®istry_impl->send_mutex);
|
||||
|
||||
if (registry_impl->send_ds >= 0 && registry_impl->send_ds != ds) {
|
||||
JLOG_VERBOSE("Setting Differentiated Services field to 0x%X", ds);
|
||||
if (udp_set_diffserv(registry_impl->sock, ds) == 0)
|
||||
registry_impl->send_ds = ds;
|
||||
else
|
||||
registry_impl->send_ds = -1; // disable for next time
|
||||
}
|
||||
|
||||
JLOG_VERBOSE("Sending datagram, size=%d", size);
|
||||
|
||||
int ret = udp_sendto(registry_impl->sock, data, size, dst);
|
||||
if (ret < 0) {
|
||||
if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK)
|
||||
JLOG_INFO("Send failed, buffer is full");
|
||||
else if (sockerrno == SEMSGSIZE)
|
||||
JLOG_WARN("Send failed, datagram is too large");
|
||||
else
|
||||
JLOG_WARN("Send failed, errno=%d", sockerrno);
|
||||
}
|
||||
|
||||
mutex_unlock(®istry_impl->send_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int conn_mux_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
registry_impl_t *registry_impl = conn_impl->registry->impl;
|
||||
|
||||
return udp_get_addrs(registry_impl->sock, records, size);
|
||||
}
|
||||
32
thirdparty/libjuice/src/conn_mux.h
vendored
Normal file
32
thirdparty/libjuice/src/conn_mux.h
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_CONN_MUX_H
|
||||
#define JUICE_CONN_MUX_H
|
||||
|
||||
#include "addr.h"
|
||||
#include "conn.h"
|
||||
#include "thread.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int conn_mux_registry_init(conn_registry_t *registry, udp_socket_config_t *config);
|
||||
void conn_mux_registry_cleanup(conn_registry_t *registry);
|
||||
|
||||
int conn_mux_init(juice_agent_t *agent, conn_registry_t *registry, udp_socket_config_t *config);
|
||||
void conn_mux_cleanup(juice_agent_t *agent);
|
||||
void conn_mux_lock(juice_agent_t *agent);
|
||||
void conn_mux_unlock(juice_agent_t *agent);
|
||||
int conn_mux_interrupt(juice_agent_t *agent);
|
||||
int conn_mux_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds);
|
||||
int conn_mux_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size);
|
||||
|
||||
#endif
|
||||
433
thirdparty/libjuice/src/conn_poll.c
vendored
Normal file
433
thirdparty/libjuice/src/conn_poll.c
vendored
Normal file
@@ -0,0 +1,433 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "conn_poll.h"
|
||||
#include "agent.h"
|
||||
#include "log.h"
|
||||
#include "socket.h"
|
||||
#include "thread.h"
|
||||
#include "udp.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
typedef struct registry_impl {
|
||||
thread_t thread;
|
||||
#ifdef _WIN32
|
||||
socket_t interrupt_sock;
|
||||
#else
|
||||
int interrupt_pipe_out;
|
||||
int interrupt_pipe_in;
|
||||
#endif
|
||||
} registry_impl_t;
|
||||
|
||||
typedef enum conn_state { CONN_STATE_NEW = 0, CONN_STATE_READY, CONN_STATE_FINISHED } conn_state_t;
|
||||
|
||||
typedef struct conn_impl {
|
||||
conn_registry_t *registry;
|
||||
conn_state_t state;
|
||||
socket_t sock;
|
||||
mutex_t send_mutex;
|
||||
int send_ds;
|
||||
timestamp_t next_timestamp;
|
||||
} conn_impl_t;
|
||||
|
||||
typedef struct pfds_record {
|
||||
struct pollfd *pfds;
|
||||
nfds_t size;
|
||||
} pfds_record_t;
|
||||
|
||||
int conn_poll_prepare(conn_registry_t *registry, pfds_record_t *pfds, timestamp_t *next_timestamp);
|
||||
int conn_poll_process(conn_registry_t *registry, pfds_record_t *pfds);
|
||||
int conn_poll_recv(socket_t sock, char *buffer, size_t size, addr_record_t *src);
|
||||
int conn_poll_run(conn_registry_t *registry);
|
||||
|
||||
static thread_return_t THREAD_CALL conn_thread_entry(void *arg) {
|
||||
conn_registry_t *registry = (conn_registry_t *)arg;
|
||||
conn_poll_run(registry);
|
||||
return (thread_return_t)0;
|
||||
}
|
||||
|
||||
int conn_poll_registry_init(conn_registry_t *registry, udp_socket_config_t *config) {
|
||||
(void)config;
|
||||
registry_impl_t *registry_impl = calloc(1, sizeof(registry_impl_t));
|
||||
if (!registry_impl) {
|
||||
JLOG_FATAL("Memory allocation failed for connections registry impl");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
udp_socket_config_t interrupt_config;
|
||||
memset(&interrupt_config, 0, sizeof(interrupt_config));
|
||||
interrupt_config.bind_address = "localhost";
|
||||
registry_impl->interrupt_sock = udp_create_socket(&interrupt_config);
|
||||
if (registry_impl->interrupt_sock == INVALID_SOCKET) {
|
||||
JLOG_FATAL("Dummy socket creation failed");
|
||||
free(registry_impl);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
int pipefds[2];
|
||||
if (pipe(pipefds)) {
|
||||
JLOG_FATAL("Pipe creation failed");
|
||||
free(registry_impl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fcntl(pipefds[0], F_SETFL, O_NONBLOCK);
|
||||
fcntl(pipefds[1], F_SETFL, O_NONBLOCK);
|
||||
registry_impl->interrupt_pipe_out = pipefds[1]; // read
|
||||
registry_impl->interrupt_pipe_in = pipefds[0]; // write
|
||||
#endif
|
||||
|
||||
registry->impl = registry_impl;
|
||||
|
||||
JLOG_DEBUG("Starting connections thread");
|
||||
int ret = thread_init(®istry_impl->thread, conn_thread_entry, registry);
|
||||
if (ret) {
|
||||
JLOG_FATAL("Thread creation failed, error=%d", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
#ifndef _WIN32
|
||||
close(registry_impl->interrupt_pipe_out);
|
||||
close(registry_impl->interrupt_pipe_in);
|
||||
#endif
|
||||
free(registry_impl);
|
||||
registry->impl = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void conn_poll_registry_cleanup(conn_registry_t *registry) {
|
||||
registry_impl_t *registry_impl = registry->impl;
|
||||
|
||||
JLOG_VERBOSE("Waiting for connections thread");
|
||||
thread_join(registry_impl->thread, NULL);
|
||||
|
||||
#ifdef _WIN32
|
||||
closesocket(registry_impl->interrupt_sock);
|
||||
#else
|
||||
close(registry_impl->interrupt_pipe_out);
|
||||
close(registry_impl->interrupt_pipe_in);
|
||||
#endif
|
||||
free(registry->impl);
|
||||
registry->impl = NULL;
|
||||
}
|
||||
|
||||
int conn_poll_prepare(conn_registry_t *registry, pfds_record_t *pfds, timestamp_t *next_timestamp) {
|
||||
timestamp_t now = current_timestamp();
|
||||
*next_timestamp = now + 60000;
|
||||
|
||||
mutex_lock(®istry->mutex);
|
||||
nfds_t size = (nfds_t)(1 + registry->agents_size);
|
||||
if (pfds->size != size) {
|
||||
struct pollfd *new_pfds = realloc(pfds->pfds, sizeof(struct pollfd) * size);
|
||||
if (!new_pfds) {
|
||||
JLOG_FATAL("Memory allocation for poll file descriptors failed");
|
||||
goto error;
|
||||
}
|
||||
pfds->pfds = new_pfds;
|
||||
pfds->size = size;
|
||||
}
|
||||
|
||||
registry_impl_t *registry_impl = registry->impl;
|
||||
struct pollfd *interrupt_pfd = pfds->pfds;
|
||||
assert(interrupt_pfd);
|
||||
#ifdef _WIN32
|
||||
interrupt_pfd->fd = registry_impl->interrupt_sock;
|
||||
#else
|
||||
interrupt_pfd->fd = registry_impl->interrupt_pipe_in;
|
||||
#endif
|
||||
interrupt_pfd->events = POLLIN;
|
||||
|
||||
for (nfds_t i = 1; i < pfds->size; ++i) {
|
||||
struct pollfd *pfd = pfds->pfds + i;
|
||||
juice_agent_t *agent = registry->agents[i - 1];
|
||||
if (!agent) {
|
||||
pfd->fd = INVALID_SOCKET;
|
||||
pfd->events = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
if (!conn_impl ||
|
||||
(conn_impl->state != CONN_STATE_NEW && conn_impl->state != CONN_STATE_READY)) {
|
||||
pfd->fd = INVALID_SOCKET;
|
||||
pfd->events = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conn_impl->state == CONN_STATE_NEW)
|
||||
conn_impl->state = CONN_STATE_READY;
|
||||
|
||||
if (*next_timestamp > conn_impl->next_timestamp)
|
||||
*next_timestamp = conn_impl->next_timestamp;
|
||||
|
||||
pfd->fd = conn_impl->sock;
|
||||
pfd->events = POLLIN;
|
||||
}
|
||||
|
||||
int count = registry->agents_count;
|
||||
mutex_unlock(®istry->mutex);
|
||||
return count;
|
||||
|
||||
error:
|
||||
mutex_unlock(®istry->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int conn_poll_recv(socket_t sock, char *buffer, size_t size, addr_record_t *src) {
|
||||
JLOG_VERBOSE("Receiving datagram");
|
||||
int len;
|
||||
while ((len = udp_recvfrom(sock, buffer, size, src)) == 0) {
|
||||
// Empty datagram, ignore
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK) {
|
||||
JLOG_VERBOSE("No more datagrams to receive");
|
||||
return 0;
|
||||
}
|
||||
JLOG_ERROR("recvfrom failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_unmap_inet6_v4mapped((struct sockaddr *)&src->addr, &src->len);
|
||||
return len; // len > 0
|
||||
}
|
||||
|
||||
int conn_poll_process(conn_registry_t *registry, pfds_record_t *pfds) {
|
||||
struct pollfd *interrupt_pfd = pfds->pfds;
|
||||
if (interrupt_pfd->revents & POLLIN) {
|
||||
#ifdef _WIN32
|
||||
char dummy;
|
||||
addr_record_t src;
|
||||
while (udp_recvfrom(interrupt_pfd->fd, &dummy, 1, &src) >= 0) {
|
||||
// Ignore
|
||||
}
|
||||
#else
|
||||
char dummy;
|
||||
while (read(interrupt_pfd->fd, &dummy, 1) > 0) {
|
||||
// Ignore
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (nfds_t i = 1; i < pfds->size; ++i) {
|
||||
struct pollfd *pfd = pfds->pfds + i;
|
||||
if (pfd->fd == INVALID_SOCKET)
|
||||
continue;
|
||||
|
||||
mutex_lock(®istry->mutex);
|
||||
juice_agent_t *agent = registry->agents[i - 1];
|
||||
if (!agent)
|
||||
goto end;
|
||||
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
if (!conn_impl || conn_impl->sock != pfd->fd || conn_impl->state != CONN_STATE_READY)
|
||||
goto end;
|
||||
|
||||
if (pfd->revents & POLLNVAL || pfd->revents & POLLERR) {
|
||||
JLOG_WARN("Error when polling socket");
|
||||
agent_conn_fail(agent);
|
||||
conn_impl->state = CONN_STATE_FINISHED;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (pfd->revents & POLLIN) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
addr_record_t src;
|
||||
int ret = 0;
|
||||
int left = 1000; // limit for fairness between sockets
|
||||
while (left-- &&
|
||||
(ret = conn_poll_recv(conn_impl->sock, buffer, BUFFER_SIZE, &src)) > 0) {
|
||||
if (agent_conn_recv(agent, buffer, (size_t)ret, &src) != 0) {
|
||||
JLOG_WARN("Agent receive failed");
|
||||
conn_impl->state = CONN_STATE_FINISHED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conn_impl->state == CONN_STATE_FINISHED)
|
||||
goto end;
|
||||
|
||||
if (ret < 0) {
|
||||
agent_conn_fail(agent);
|
||||
conn_impl->state = CONN_STATE_FINISHED;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (agent_conn_update(agent, &conn_impl->next_timestamp) != 0) {
|
||||
JLOG_WARN("Agent update failed");
|
||||
conn_impl->state = CONN_STATE_FINISHED;
|
||||
goto end;
|
||||
}
|
||||
|
||||
} else if (conn_impl->next_timestamp <= current_timestamp()) {
|
||||
if (agent_conn_update(agent, &conn_impl->next_timestamp) != 0) {
|
||||
JLOG_WARN("Agent update failed");
|
||||
conn_impl->state = CONN_STATE_FINISHED;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
mutex_unlock(®istry->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_poll_run(conn_registry_t *registry) {
|
||||
pfds_record_t pfds;
|
||||
pfds.pfds = NULL;
|
||||
pfds.size = 0;
|
||||
timestamp_t next_timestamp = 0;
|
||||
int count;
|
||||
while ((count = conn_poll_prepare(registry, &pfds, &next_timestamp)) > 0) {
|
||||
timediff_t timediff = next_timestamp - current_timestamp();
|
||||
if (timediff < 0)
|
||||
timediff = 0;
|
||||
|
||||
JLOG_VERBOSE("Entering poll on %d sockets for %d ms", count, (int)timediff);
|
||||
int ret = poll(pfds.pfds, pfds.size, (int)timediff);
|
||||
JLOG_VERBOSE("Leaving poll");
|
||||
if (ret < 0) {
|
||||
#ifdef _WIN32
|
||||
if (ret == WSAENOTSOCK)
|
||||
continue; // prepare again as the fd has been removed
|
||||
#endif
|
||||
if (sockerrno == SEINTR || sockerrno == SEAGAIN) {
|
||||
JLOG_VERBOSE("poll interrupted");
|
||||
continue;
|
||||
} else {
|
||||
JLOG_FATAL("poll failed, errno=%d", sockerrno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn_poll_process(registry, &pfds) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
JLOG_DEBUG("Leaving connections thread");
|
||||
free(pfds.pfds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_poll_init(juice_agent_t *agent, conn_registry_t *registry, udp_socket_config_t *config) {
|
||||
conn_impl_t *conn_impl = calloc(1, sizeof(conn_impl_t));
|
||||
if (!conn_impl) {
|
||||
JLOG_FATAL("Memory allocation failed for connection impl");
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn_impl->sock = udp_create_socket(config);
|
||||
if (conn_impl->sock == INVALID_SOCKET) {
|
||||
JLOG_ERROR("UDP socket creation failed");
|
||||
free(conn_impl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mutex_init(&conn_impl->send_mutex, 0);
|
||||
conn_impl->registry = registry;
|
||||
|
||||
agent->conn_impl = conn_impl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conn_poll_cleanup(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
|
||||
conn_poll_interrupt(agent);
|
||||
|
||||
mutex_destroy(&conn_impl->send_mutex);
|
||||
closesocket(conn_impl->sock);
|
||||
free(agent->conn_impl);
|
||||
agent->conn_impl = NULL;
|
||||
}
|
||||
|
||||
void conn_poll_lock(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
conn_registry_t *registry = conn_impl->registry;
|
||||
mutex_lock(®istry->mutex);
|
||||
}
|
||||
|
||||
void conn_poll_unlock(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
conn_registry_t *registry = conn_impl->registry;
|
||||
mutex_unlock(®istry->mutex);
|
||||
}
|
||||
|
||||
int conn_poll_interrupt(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
conn_registry_t *registry = conn_impl->registry;
|
||||
registry_impl_t *registry_impl = registry->impl;
|
||||
|
||||
mutex_lock(®istry->mutex);
|
||||
conn_impl->next_timestamp = current_timestamp();
|
||||
mutex_unlock(®istry->mutex);
|
||||
|
||||
JLOG_VERBOSE("Interrupting connections thread");
|
||||
|
||||
#ifdef _WIN32
|
||||
if (udp_sendto_self(registry_impl->interrupt_sock, NULL, 0) < 0) {
|
||||
if (sockerrno != SEAGAIN && sockerrno != SEWOULDBLOCK) {
|
||||
JLOG_WARN("Failed to interrupt poll by triggering socket, errno=%d", sockerrno);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
char dummy = 0;
|
||||
if (write(registry_impl->interrupt_pipe_out, &dummy, 1) < 0 && errno != EAGAIN &&
|
||||
errno != EWOULDBLOCK) {
|
||||
JLOG_WARN("Failed to interrupt poll by writing to pipe, errno=%d", errno);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_poll_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
|
||||
mutex_lock(&conn_impl->send_mutex);
|
||||
|
||||
if (conn_impl->send_ds >= 0 && conn_impl->send_ds != ds) {
|
||||
JLOG_VERBOSE("Setting Differentiated Services field to 0x%X", ds);
|
||||
if (udp_set_diffserv(conn_impl->sock, ds) == 0)
|
||||
conn_impl->send_ds = ds;
|
||||
else
|
||||
conn_impl->send_ds = -1; // disable for next time
|
||||
}
|
||||
|
||||
JLOG_VERBOSE("Sending datagram, size=%d", size);
|
||||
|
||||
int ret = udp_sendto(conn_impl->sock, data, size, dst);
|
||||
if (ret < 0) {
|
||||
if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK)
|
||||
JLOG_INFO("Send failed, buffer is full");
|
||||
else if (sockerrno == SEMSGSIZE)
|
||||
JLOG_WARN("Send failed, datagram is too large");
|
||||
else
|
||||
JLOG_WARN("Send failed, errno=%d", sockerrno);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn_impl->send_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int conn_poll_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
|
||||
return udp_get_addrs(conn_impl->sock, records, size);
|
||||
}
|
||||
32
thirdparty/libjuice/src/conn_poll.h
vendored
Normal file
32
thirdparty/libjuice/src/conn_poll.h
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_CONN_POLL_H
|
||||
#define JUICE_CONN_POLL_H
|
||||
|
||||
#include "addr.h"
|
||||
#include "conn.h"
|
||||
#include "thread.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int conn_poll_registry_init(conn_registry_t *registry, udp_socket_config_t *config);
|
||||
void conn_poll_registry_cleanup(conn_registry_t *registry);
|
||||
|
||||
int conn_poll_init(juice_agent_t *agent, conn_registry_t *registry, udp_socket_config_t *config);
|
||||
void conn_poll_cleanup(juice_agent_t *agent);
|
||||
void conn_poll_lock(juice_agent_t *agent);
|
||||
void conn_poll_unlock(juice_agent_t *agent);
|
||||
int conn_poll_interrupt(juice_agent_t *agent);
|
||||
int conn_poll_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds);
|
||||
int conn_poll_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size);
|
||||
|
||||
#endif
|
||||
278
thirdparty/libjuice/src/conn_thread.c
vendored
Normal file
278
thirdparty/libjuice/src/conn_thread.c
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "conn_thread.h"
|
||||
#include "agent.h"
|
||||
#include "log.h"
|
||||
#include "socket.h"
|
||||
#include "thread.h"
|
||||
#include "udp.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
typedef struct conn_impl {
|
||||
thread_t thread;
|
||||
socket_t sock;
|
||||
mutex_t mutex;
|
||||
mutex_t send_mutex;
|
||||
int send_ds;
|
||||
timestamp_t next_timestamp;
|
||||
bool stopped;
|
||||
} conn_impl_t;
|
||||
|
||||
int conn_thread_run(juice_agent_t *agent);
|
||||
int conn_thread_prepare(juice_agent_t *agent, struct pollfd *pfd, timestamp_t *next_timestamp);
|
||||
int conn_thread_process(juice_agent_t *agent, struct pollfd *pfd);
|
||||
int conn_thread_recv(socket_t sock, char *buffer, size_t size, addr_record_t *src);
|
||||
|
||||
static thread_return_t THREAD_CALL conn_thread_entry(void *arg) {
|
||||
juice_agent_t *agent = (juice_agent_t *)arg;
|
||||
conn_thread_run(agent);
|
||||
return (thread_return_t)0;
|
||||
}
|
||||
|
||||
int conn_thread_prepare(juice_agent_t *agent, struct pollfd *pfd, timestamp_t *next_timestamp) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
mutex_lock(&conn_impl->mutex);
|
||||
if (conn_impl->stopped) {
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pfd->fd = conn_impl->sock;
|
||||
pfd->events = POLLIN;
|
||||
|
||||
*next_timestamp = conn_impl->next_timestamp;
|
||||
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int conn_thread_process(juice_agent_t *agent, struct pollfd *pfd) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
mutex_lock(&conn_impl->mutex);
|
||||
if (conn_impl->stopped) {
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pfd->revents & POLLNVAL || pfd->revents & POLLERR) {
|
||||
JLOG_ERROR("Error when polling socket");
|
||||
agent_conn_fail(agent);
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pfd->revents & POLLIN) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
addr_record_t src;
|
||||
int ret;
|
||||
while ((ret = conn_thread_recv(conn_impl->sock, buffer, BUFFER_SIZE, &src)) > 0) {
|
||||
if (agent_conn_recv(agent, buffer, (size_t)ret, &src) != 0) {
|
||||
JLOG_WARN("Agent receive failed");
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
agent_conn_fail(agent);
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (agent_conn_update(agent, &conn_impl->next_timestamp) != 0) {
|
||||
JLOG_WARN("Agent update failed");
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else if (conn_impl->next_timestamp <= current_timestamp()) {
|
||||
if (agent_conn_update(agent, &conn_impl->next_timestamp) != 0) {
|
||||
JLOG_WARN("Agent update failed");
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_thread_recv(socket_t sock, char *buffer, size_t size, addr_record_t *src) {
|
||||
JLOG_VERBOSE("Receiving datagram");
|
||||
int len;
|
||||
while ((len = udp_recvfrom(sock, buffer, size, src)) == 0) {
|
||||
// Empty datagram (used to interrupt)
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK) {
|
||||
JLOG_VERBOSE("No more datagrams to receive");
|
||||
return 0;
|
||||
}
|
||||
JLOG_ERROR("recvfrom failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_unmap_inet6_v4mapped((struct sockaddr *)&src->addr, &src->len);
|
||||
return len; // len > 0
|
||||
}
|
||||
|
||||
int conn_thread_run(juice_agent_t *agent) {
|
||||
struct pollfd pfd[1];
|
||||
timestamp_t next_timestamp;
|
||||
while (conn_thread_prepare(agent, pfd, &next_timestamp) > 0) {
|
||||
timediff_t timediff = next_timestamp - current_timestamp();
|
||||
if (timediff < 0)
|
||||
timediff = 0;
|
||||
|
||||
JLOG_VERBOSE("Entering poll for %d ms", (int)timediff);
|
||||
int ret = poll(pfd, 1, (int)timediff);
|
||||
JLOG_VERBOSE("Leaving poll");
|
||||
if (ret < 0) {
|
||||
if (sockerrno == SEINTR || sockerrno == SEAGAIN) {
|
||||
JLOG_VERBOSE("poll interrupted");
|
||||
continue;
|
||||
} else {
|
||||
JLOG_FATAL("poll failed, errno=%d", sockerrno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn_thread_process(agent, pfd) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
JLOG_DEBUG("Leaving connection thread");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_thread_init(juice_agent_t *agent, conn_registry_t *registry, udp_socket_config_t *config) {
|
||||
(void)registry;
|
||||
|
||||
conn_impl_t *conn_impl = calloc(1, sizeof(conn_impl_t));
|
||||
if (!conn_impl) {
|
||||
JLOG_FATAL("Memory allocation failed for connection impl");
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn_impl->sock = udp_create_socket(config);
|
||||
if (conn_impl->sock == INVALID_SOCKET) {
|
||||
JLOG_ERROR("UDP socket creation failed");
|
||||
free(conn_impl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mutex_init(&conn_impl->mutex, 0);
|
||||
mutex_init(&conn_impl->send_mutex, 0);
|
||||
|
||||
agent->conn_impl = conn_impl;
|
||||
|
||||
JLOG_DEBUG("Starting connection thread");
|
||||
int ret = thread_init(&conn_impl->thread, conn_thread_entry, agent);
|
||||
if (ret) {
|
||||
JLOG_FATAL("Thread creation failed, error=%d", ret);
|
||||
free(conn_impl);
|
||||
agent->conn_impl = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conn_thread_cleanup(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
|
||||
mutex_lock(&conn_impl->mutex);
|
||||
conn_impl->stopped = true;
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
|
||||
conn_thread_interrupt(agent);
|
||||
|
||||
JLOG_VERBOSE("Waiting for connection thread");
|
||||
thread_join(conn_impl->thread, NULL);
|
||||
|
||||
closesocket(conn_impl->sock);
|
||||
mutex_destroy(&conn_impl->mutex);
|
||||
mutex_destroy(&conn_impl->send_mutex);
|
||||
free(agent->conn_impl);
|
||||
agent->conn_impl = NULL;
|
||||
}
|
||||
|
||||
void conn_thread_lock(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
mutex_lock(&conn_impl->mutex);
|
||||
}
|
||||
|
||||
void conn_thread_unlock(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
}
|
||||
|
||||
int conn_thread_interrupt(juice_agent_t *agent) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
|
||||
mutex_lock(&conn_impl->mutex);
|
||||
conn_impl->next_timestamp = current_timestamp();
|
||||
mutex_unlock(&conn_impl->mutex);
|
||||
|
||||
JLOG_VERBOSE("Interrupting connection thread");
|
||||
|
||||
mutex_lock(&conn_impl->send_mutex);
|
||||
if (udp_sendto_self(conn_impl->sock, NULL, 0) < 0) {
|
||||
if (sockerrno != SEAGAIN && sockerrno != SEWOULDBLOCK) {
|
||||
JLOG_WARN("Failed to interrupt poll by triggering socket, errno=%d", sockerrno);
|
||||
}
|
||||
mutex_unlock(&conn_impl->send_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mutex_unlock(&conn_impl->send_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_thread_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
|
||||
mutex_lock(&conn_impl->send_mutex);
|
||||
|
||||
if (conn_impl->send_ds >= 0 && conn_impl->send_ds != ds) {
|
||||
JLOG_VERBOSE("Setting Differentiated Services field to 0x%X", ds);
|
||||
if (udp_set_diffserv(conn_impl->sock, ds) == 0)
|
||||
conn_impl->send_ds = ds;
|
||||
else
|
||||
conn_impl->send_ds = -1; // disable for next time
|
||||
}
|
||||
|
||||
JLOG_VERBOSE("Sending datagram, size=%d", size);
|
||||
|
||||
int ret = udp_sendto(conn_impl->sock, data, size, dst);
|
||||
if (ret < 0) {
|
||||
if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK)
|
||||
JLOG_INFO("Send failed, buffer is full");
|
||||
else if (sockerrno == SEMSGSIZE)
|
||||
JLOG_WARN("Send failed, datagram is too large");
|
||||
else
|
||||
JLOG_WARN("Send failed, errno=%d", sockerrno);
|
||||
}
|
||||
|
||||
mutex_unlock(&conn_impl->send_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int conn_thread_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size) {
|
||||
conn_impl_t *conn_impl = agent->conn_impl;
|
||||
|
||||
return udp_get_addrs(conn_impl->sock, records, size);
|
||||
}
|
||||
|
||||
32
thirdparty/libjuice/src/conn_thread.h
vendored
Normal file
32
thirdparty/libjuice/src/conn_thread.h
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_CONN_THREAD_H
|
||||
#define JUICE_CONN_THREAD_H
|
||||
|
||||
#include "addr.h"
|
||||
#include "conn.h"
|
||||
#include "thread.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int conn_thread_registry_init(conn_registry_t *registry, udp_socket_config_t *config);
|
||||
void conn_thread_registry_cleanup(conn_registry_t *registry);
|
||||
|
||||
int conn_thread_init(juice_agent_t *agent, conn_registry_t *registry, udp_socket_config_t *config);
|
||||
void conn_thread_cleanup(juice_agent_t *agent);
|
||||
void conn_thread_lock(juice_agent_t *agent);
|
||||
void conn_thread_unlock(juice_agent_t *agent);
|
||||
int conn_thread_interrupt(juice_agent_t *agent);
|
||||
int conn_thread_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size,
|
||||
int ds);
|
||||
int conn_thread_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size);
|
||||
|
||||
#endif
|
||||
34
thirdparty/libjuice/src/const_time.c
vendored
Normal file
34
thirdparty/libjuice/src/const_time.c
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "const_time.h"
|
||||
|
||||
int const_time_memcmp(const void *a, const void *b, size_t len) {
|
||||
const unsigned char *ca = a;
|
||||
const unsigned char *cb = b;
|
||||
unsigned char x = 0;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
x |= ca[i] ^ cb[i];
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
int const_time_strcmp(const void *a, const void *b) {
|
||||
const unsigned char *ca = a;
|
||||
const unsigned char *cb = b;
|
||||
unsigned char x = 0;
|
||||
size_t i = 0;
|
||||
for(;;) {
|
||||
x |= ca[i] ^ cb[i];
|
||||
if (!ca[i] || !cb[i])
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
18
thirdparty/libjuice/src/const_time.h
vendored
Normal file
18
thirdparty/libjuice/src/const_time.h
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_CONST_TIME_H
|
||||
#define JUICE_CONST_TIME_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int const_time_memcmp(const void *a, const void *b, size_t len);
|
||||
int const_time_strcmp(const void *a, const void *b);
|
||||
|
||||
#endif
|
||||
38
thirdparty/libjuice/src/crc32.c
vendored
Normal file
38
thirdparty/libjuice/src/crc32.c
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "crc32.h"
|
||||
|
||||
#define CRC32_REVERSED_POLY 0xEDB88320
|
||||
#define CRC32_INIT 0xFFFFFFFF
|
||||
#define CRC32_XOR 0xFFFFFFFF
|
||||
|
||||
static uint32_t crc32_byte(uint32_t crc) {
|
||||
for (int i = 0; i < 8; ++i)
|
||||
if (crc & 1)
|
||||
crc = (crc >> 1) ^ CRC32_REVERSED_POLY;
|
||||
else
|
||||
crc = (crc >> 1);
|
||||
return crc;
|
||||
}
|
||||
|
||||
static uint32_t crc32_table(const uint8_t *p, size_t size, uint32_t *table) {
|
||||
uint32_t crc = CRC32_INIT;
|
||||
while (size--)
|
||||
crc = table[(uint8_t)(crc & 0xFF) ^ *p++] ^ (crc >> 8);
|
||||
return crc ^ CRC32_XOR;
|
||||
}
|
||||
|
||||
JUICE_EXPORT uint32_t juice_crc32(const void *data, size_t size) {
|
||||
static uint32_t table[256] = {0};
|
||||
if (table[0] == 0)
|
||||
for (uint32_t i = 0; i < 256; ++i)
|
||||
table[i] = crc32_byte(i);
|
||||
|
||||
return crc32_table(data, size, table);
|
||||
}
|
||||
21
thirdparty/libjuice/src/crc32.h
vendored
Normal file
21
thirdparty/libjuice/src/crc32.h
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_CRC32_H
|
||||
#define JUICE_CRC32_H
|
||||
|
||||
#include "juice.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
JUICE_EXPORT uint32_t juice_crc32(const void *data, size_t size);
|
||||
|
||||
#define CRC32(data, size) juice_crc32(data, size)
|
||||
|
||||
#endif // JUICE_CRC32_H
|
||||
59
thirdparty/libjuice/src/hash.c
vendored
Normal file
59
thirdparty/libjuice/src/hash.c
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
#if USE_NETTLE
|
||||
#include <nettle/md5.h>
|
||||
#include <nettle/sha1.h>
|
||||
#include <nettle/sha2.h>
|
||||
#else
|
||||
#include "picohash.h"
|
||||
#endif
|
||||
|
||||
void hash_md5(const void *message, size_t size, void *digest) {
|
||||
#if USE_NETTLE
|
||||
struct md5_ctx ctx;
|
||||
md5_init(&ctx);
|
||||
md5_update(&ctx, size, message);
|
||||
md5_digest(&ctx, HASH_MD5_SIZE, digest);
|
||||
#else
|
||||
picohash_ctx_t ctx;
|
||||
picohash_init_md5(&ctx);
|
||||
picohash_update(&ctx, message, size);
|
||||
picohash_final(&ctx, digest);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hash_sha1(const void *message, size_t size, void *digest) {
|
||||
#if USE_NETTLE
|
||||
struct sha1_ctx ctx;
|
||||
sha1_init(&ctx);
|
||||
sha1_update(&ctx, size, message);
|
||||
sha1_digest(&ctx, HASH_SHA1_SIZE, digest);
|
||||
#else
|
||||
picohash_ctx_t ctx;
|
||||
picohash_init_sha1(&ctx);
|
||||
picohash_update(&ctx, message, size);
|
||||
picohash_final(&ctx, digest);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hash_sha256(const void *message, size_t size, void *digest) {
|
||||
#if USE_NETTLE
|
||||
struct sha256_ctx ctx;
|
||||
sha256_init(&ctx);
|
||||
sha256_update(&ctx, size, message);
|
||||
sha256_digest(&ctx, HASH_SHA256_SIZE, digest);
|
||||
#else
|
||||
picohash_ctx_t ctx;
|
||||
picohash_init_sha256(&ctx);
|
||||
picohash_update(&ctx, message, size);
|
||||
picohash_final(&ctx, digest);
|
||||
#endif
|
||||
}
|
||||
23
thirdparty/libjuice/src/hash.h
vendored
Normal file
23
thirdparty/libjuice/src/hash.h
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_HASH_H
|
||||
#define JUICE_HASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define HASH_MD5_SIZE 16
|
||||
#define HASH_SHA1_SIZE 24
|
||||
#define HASH_SHA256_SIZE 32
|
||||
|
||||
void hash_md5(const void *message, size_t size, void *digest);
|
||||
void hash_sha1(const void *message, size_t size, void *digest);
|
||||
void hash_sha256(const void *message, size_t size, void *digest);
|
||||
|
||||
#endif
|
||||
43
thirdparty/libjuice/src/hmac.c
vendored
Normal file
43
thirdparty/libjuice/src/hmac.c
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "hmac.h"
|
||||
|
||||
#if USE_NETTLE
|
||||
#include <nettle/hmac.h>
|
||||
#else
|
||||
#include "picohash.h"
|
||||
#endif
|
||||
|
||||
void hmac_sha1(const void *message, size_t size, const void *key, size_t key_size, void *digest) {
|
||||
#if USE_NETTLE
|
||||
struct hmac_sha1_ctx ctx;
|
||||
hmac_sha1_set_key(&ctx, key_size, key);
|
||||
hmac_sha1_update(&ctx, size, message);
|
||||
hmac_sha1_digest(&ctx, HMAC_SHA1_SIZE, digest);
|
||||
#else
|
||||
picohash_ctx_t ctx;
|
||||
picohash_init_hmac(&ctx, picohash_init_sha1, key, key_size);
|
||||
picohash_update(&ctx, message, size);
|
||||
picohash_final(&ctx, digest);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hmac_sha256(const void *message, size_t size, const void *key, size_t key_size, void *digest) {
|
||||
#if USE_NETTLE
|
||||
struct hmac_sha256_ctx ctx;
|
||||
hmac_sha256_set_key(&ctx, key_size, key);
|
||||
hmac_sha256_update(&ctx, size, message);
|
||||
hmac_sha256_digest(&ctx, HMAC_SHA256_SIZE, digest);
|
||||
#else
|
||||
picohash_ctx_t ctx;
|
||||
picohash_init_hmac(&ctx, picohash_init_sha256, key, key_size);
|
||||
picohash_update(&ctx, message, size);
|
||||
picohash_final(&ctx, digest);
|
||||
#endif
|
||||
}
|
||||
21
thirdparty/libjuice/src/hmac.h
vendored
Normal file
21
thirdparty/libjuice/src/hmac.h
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_HMAC_H
|
||||
#define JUICE_HMAC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define HMAC_SHA1_SIZE 20
|
||||
#define HMAC_SHA256_SIZE 32
|
||||
|
||||
void hmac_sha1(const void *message, size_t size, const void *key, size_t key_size, void *digest);
|
||||
void hmac_sha256(const void *message, size_t size, const void *key, size_t key_size, void *digest);
|
||||
|
||||
#endif
|
||||
408
thirdparty/libjuice/src/ice.c
vendored
Normal file
408
thirdparty/libjuice/src/ice.c
vendored
Normal file
@@ -0,0 +1,408 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "ice.h"
|
||||
#include "log.h"
|
||||
#include "random.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BUFFER_SIZE 1024
|
||||
|
||||
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
|
||||
|
||||
// See RFC4566 for SDP format: https://www.rfc-editor.org/rfc/rfc4566.html
|
||||
|
||||
static const char *skip_prefix(const char *str, const char *prefix) {
|
||||
size_t len = strlen(prefix);
|
||||
return strncmp(str, prefix, len) == 0 ? str + len : str;
|
||||
}
|
||||
|
||||
static bool match_prefix(const char *str, const char *prefix, const char **end) {
|
||||
*end = skip_prefix(str, prefix);
|
||||
return *end != str || !*prefix;
|
||||
}
|
||||
|
||||
static int parse_sdp_line(const char *line, ice_description_t *description) {
|
||||
const char *arg;
|
||||
if (match_prefix(line, "a=ice-ufrag:", &arg)) {
|
||||
sscanf(arg, "%256s", description->ice_ufrag);
|
||||
return 0;
|
||||
}
|
||||
if (match_prefix(line, "a=ice-pwd:", &arg)) {
|
||||
sscanf(arg, "%256s", description->ice_pwd);
|
||||
return 0;
|
||||
}
|
||||
if (match_prefix(line, "a=end-of-candidates:", &arg)) {
|
||||
description->finished = true;
|
||||
return 0;
|
||||
}
|
||||
ice_candidate_t candidate;
|
||||
if (ice_parse_candidate_sdp(line, &candidate) == 0) {
|
||||
ice_add_candidate(&candidate, description);
|
||||
return 0;
|
||||
}
|
||||
return ICE_PARSE_IGNORED;
|
||||
}
|
||||
|
||||
static int parse_sdp_candidate(const char *line, ice_candidate_t *candidate) {
|
||||
memset(candidate, 0, sizeof(*candidate));
|
||||
|
||||
line = skip_prefix(line, "a=");
|
||||
line = skip_prefix(line, "candidate:");
|
||||
|
||||
char transport[32 + 1];
|
||||
char type[32 + 1];
|
||||
if (sscanf(line, "%32s %d %32s %u %256s %32s typ %32s", candidate->foundation,
|
||||
&candidate->component, transport, &candidate->priority, candidate->hostname,
|
||||
candidate->service, type) != 7) {
|
||||
JLOG_WARN("Failed to parse candidate: %s", line);
|
||||
return ICE_PARSE_ERROR;
|
||||
}
|
||||
|
||||
for (int i = 0; transport[i]; ++i)
|
||||
transport[i] = toupper((unsigned char)transport[i]);
|
||||
|
||||
for (int i = 0; type[i]; ++i)
|
||||
type[i] = tolower((unsigned char)type[i]);
|
||||
|
||||
if (strcmp(type, "host") == 0)
|
||||
candidate->type = ICE_CANDIDATE_TYPE_HOST;
|
||||
else if (strcmp(type, "srflx") == 0)
|
||||
candidate->type = ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
|
||||
else if (strcmp(type, "relay") == 0)
|
||||
candidate->type = ICE_CANDIDATE_TYPE_RELAYED;
|
||||
else {
|
||||
JLOG_WARN("Ignoring candidate with unknown type \"%s\"", type);
|
||||
return ICE_PARSE_IGNORED;
|
||||
}
|
||||
|
||||
if (strcmp(transport, "UDP") != 0) {
|
||||
JLOG_WARN("Ignoring candidate with transport %s", transport);
|
||||
return ICE_PARSE_IGNORED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ice_parse_sdp(const char *sdp, ice_description_t *description) {
|
||||
memset(description, 0, sizeof(*description));
|
||||
description->candidates_count = 0;
|
||||
description->finished = false;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
size_t size = 0;
|
||||
while (*sdp) {
|
||||
if (*sdp == '\n') {
|
||||
if (size) {
|
||||
buffer[size++] = '\0';
|
||||
if(parse_sdp_line(buffer, description) == ICE_PARSE_ERROR)
|
||||
return ICE_PARSE_ERROR;
|
||||
|
||||
size = 0;
|
||||
}
|
||||
} else if (*sdp != '\r' && size + 1 < BUFFER_SIZE) {
|
||||
buffer[size++] = *sdp;
|
||||
}
|
||||
++sdp;
|
||||
}
|
||||
ice_sort_candidates(description);
|
||||
|
||||
JLOG_DEBUG("Parsed remote description: ufrag=\"%s\", pwd=\"%s\", candidates=%d",
|
||||
description->ice_ufrag, description->ice_pwd, description->candidates_count);
|
||||
|
||||
if (*description->ice_ufrag == '\0')
|
||||
return ICE_PARSE_MISSING_UFRAG;
|
||||
|
||||
if (*description->ice_pwd == '\0')
|
||||
return ICE_PARSE_MISSING_PWD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ice_parse_candidate_sdp(const char *line, ice_candidate_t *candidate) {
|
||||
const char *arg;
|
||||
if (match_prefix(line, "a=candidate:", &arg)) {
|
||||
int ret = parse_sdp_candidate(line, candidate);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ice_resolve_candidate(candidate, ICE_RESOLVE_MODE_SIMPLE);
|
||||
return 0;
|
||||
}
|
||||
return ICE_PARSE_ERROR;
|
||||
}
|
||||
|
||||
int ice_create_local_description(ice_description_t *description) {
|
||||
memset(description, 0, sizeof(*description));
|
||||
juice_random_str64(description->ice_ufrag, 4 + 1);
|
||||
juice_random_str64(description->ice_pwd, 22 + 1);
|
||||
description->candidates_count = 0;
|
||||
description->finished = false;
|
||||
JLOG_DEBUG("Created local description: ufrag=\"%s\", pwd=\"%s\"", description->ice_ufrag,
|
||||
description->ice_pwd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ice_create_local_candidate(ice_candidate_type_t type, int component, int index,
|
||||
const addr_record_t *record, ice_candidate_t *candidate) {
|
||||
memset(candidate, 0, sizeof(*candidate));
|
||||
candidate->type = type;
|
||||
candidate->component = component;
|
||||
candidate->resolved = *record;
|
||||
strcpy(candidate->foundation, "-");
|
||||
|
||||
candidate->priority = ice_compute_priority(candidate->type, candidate->resolved.addr.ss_family,
|
||||
candidate->component, index);
|
||||
|
||||
if (getnameinfo((struct sockaddr *)&record->addr, record->len, candidate->hostname, 256,
|
||||
candidate->service, 32, NI_NUMERICHOST | NI_NUMERICSERV | NI_DGRAM)) {
|
||||
JLOG_ERROR("getnameinfo failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ice_resolve_candidate(ice_candidate_t *candidate, ice_resolve_mode_t mode) {
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
if (mode != ICE_RESOLVE_MODE_LOOKUP)
|
||||
hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
|
||||
struct addrinfo *ai_list = NULL;
|
||||
if (getaddrinfo(candidate->hostname, candidate->service, &hints, &ai_list)) {
|
||||
JLOG_INFO("Failed to resolve address: %s:%s", candidate->hostname, candidate->service);
|
||||
candidate->resolved.len = 0;
|
||||
return -1;
|
||||
}
|
||||
for (struct addrinfo *ai = ai_list; ai; ai = ai->ai_next) {
|
||||
if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
|
||||
candidate->resolved.len = (socklen_t)ai->ai_addrlen;
|
||||
memcpy(&candidate->resolved.addr, ai->ai_addr, ai->ai_addrlen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(ai_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ice_add_candidate(ice_candidate_t *candidate, ice_description_t *description) {
|
||||
if (candidate->type == ICE_CANDIDATE_TYPE_UNKNOWN)
|
||||
return -1;
|
||||
|
||||
if (description->candidates_count >= ICE_MAX_CANDIDATES_COUNT) {
|
||||
JLOG_WARN("Description already has the maximum number of candidates");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(candidate->foundation, "-") == 0)
|
||||
snprintf(candidate->foundation, 32, "%u",
|
||||
(unsigned int)(description->candidates_count + 1));
|
||||
|
||||
ice_candidate_t *pos = description->candidates + description->candidates_count;
|
||||
*pos = *candidate;
|
||||
++description->candidates_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ice_sort_candidates(ice_description_t *description) {
|
||||
// In-place insertion sort
|
||||
ice_candidate_t *begin = description->candidates;
|
||||
ice_candidate_t *end = begin + description->candidates_count;
|
||||
ice_candidate_t *cur = begin;
|
||||
while (++cur < end) {
|
||||
uint32_t priority = cur->priority;
|
||||
ice_candidate_t *prev = cur;
|
||||
ice_candidate_t tmp = *prev;
|
||||
while (--prev >= begin && prev->priority < priority) {
|
||||
*(prev + 1) = *prev;
|
||||
}
|
||||
if (prev + 1 != cur)
|
||||
*(prev + 1) = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
ice_candidate_t *ice_find_candidate_from_addr(ice_description_t *description,
|
||||
const addr_record_t *record,
|
||||
ice_candidate_type_t type) {
|
||||
ice_candidate_t *cur = description->candidates;
|
||||
ice_candidate_t *end = cur + description->candidates_count;
|
||||
while (cur != end) {
|
||||
if ((type == ICE_CANDIDATE_TYPE_UNKNOWN || cur->type == type) &&
|
||||
addr_is_equal((struct sockaddr *)&record->addr, (struct sockaddr *)&cur->resolved.addr,
|
||||
true))
|
||||
return cur;
|
||||
++cur;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ice_generate_sdp(const ice_description_t *description, char *buffer, size_t size) {
|
||||
if (!*description->ice_ufrag || !*description->ice_pwd)
|
||||
return -1;
|
||||
|
||||
int len = 0;
|
||||
char *begin = buffer;
|
||||
char *end = begin + size;
|
||||
|
||||
// Round 0: description
|
||||
// Round i with i>0 and i<count+1: candidate i-1
|
||||
// Round count + 1: end-of-candidates and ice-options lines
|
||||
for (int i = 0; i < description->candidates_count + 2; ++i) {
|
||||
int ret;
|
||||
if (i == 0) {
|
||||
ret = snprintf(begin, end - begin, "a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",
|
||||
description->ice_ufrag, description->ice_pwd);
|
||||
} else if (i < description->candidates_count + 1) {
|
||||
const ice_candidate_t *candidate = description->candidates + i - 1;
|
||||
if (candidate->type == ICE_CANDIDATE_TYPE_UNKNOWN ||
|
||||
candidate->type == ICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
|
||||
continue;
|
||||
char tmp[BUFFER_SIZE];
|
||||
if (ice_generate_candidate_sdp(candidate, tmp, BUFFER_SIZE) < 0)
|
||||
continue;
|
||||
ret = snprintf(begin, end - begin, "%s\r\n", tmp);
|
||||
} else { // i == description->candidates_count + 1
|
||||
// RFC 8445 10. ICE Option: An agent compliant to this specification MUST inform the
|
||||
// peer about the compliance using the 'ice2' option.
|
||||
if (description->finished)
|
||||
ret = snprintf(begin, end - begin, "a=end-of-candidates\r\na=ice-options:ice2\r\n");
|
||||
else
|
||||
ret = snprintf(begin, end - begin, "a=ice-options:ice2,trickle\r\n");
|
||||
}
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
len += ret;
|
||||
|
||||
if (begin < end)
|
||||
begin += ret >= end - begin ? end - begin - 1 : ret;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int ice_generate_candidate_sdp(const ice_candidate_t *candidate, char *buffer, size_t size) {
|
||||
const char *type = NULL;
|
||||
const char *suffix = NULL;
|
||||
switch (candidate->type) {
|
||||
case ICE_CANDIDATE_TYPE_HOST:
|
||||
type = "host";
|
||||
break;
|
||||
case ICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
|
||||
type = "prflx";
|
||||
break;
|
||||
case ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
|
||||
type = "srflx";
|
||||
suffix = "raddr 0.0.0.0 rport 0"; // This is needed for compatibility with Firefox
|
||||
break;
|
||||
case ICE_CANDIDATE_TYPE_RELAYED:
|
||||
type = "relay";
|
||||
suffix = "raddr 0.0.0.0 rport 0"; // This is needed for compatibility with Firefox
|
||||
break;
|
||||
default:
|
||||
JLOG_ERROR("Unknown candidate type");
|
||||
return -1;
|
||||
}
|
||||
return snprintf(buffer, size, "a=candidate:%s %u UDP %u %s %s typ %s%s%s",
|
||||
candidate->foundation, candidate->component, candidate->priority,
|
||||
candidate->hostname, candidate->service, type, suffix ? " " : "",
|
||||
suffix ? suffix : "");
|
||||
}
|
||||
|
||||
int ice_create_candidate_pair(ice_candidate_t *local, ice_candidate_t *remote, bool is_controlling,
|
||||
ice_candidate_pair_t *pair) { // local or remote might be NULL
|
||||
if (local && remote && local->resolved.addr.ss_family != remote->resolved.addr.ss_family) {
|
||||
JLOG_ERROR("Mismatching candidates address families");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(pair, 0, sizeof(*pair));
|
||||
pair->local = local;
|
||||
pair->remote = remote;
|
||||
pair->state = ICE_CANDIDATE_PAIR_STATE_FROZEN;
|
||||
return ice_update_candidate_pair(pair, is_controlling);
|
||||
}
|
||||
|
||||
int ice_update_candidate_pair(ice_candidate_pair_t *pair, bool is_controlling) {
|
||||
// Compute pair priority according to RFC 8445, extended to support generic pairs missing local
|
||||
// or remote See https://www.rfc-editor.org/rfc/rfc8445.html#section-6.1.2.3
|
||||
if (!pair->local && !pair->remote)
|
||||
return 0;
|
||||
uint64_t local_priority =
|
||||
pair->local
|
||||
? pair->local->priority
|
||||
: ice_compute_priority(ICE_CANDIDATE_TYPE_HOST, pair->remote->resolved.addr.ss_family,
|
||||
pair->remote->component, 0);
|
||||
uint64_t remote_priority =
|
||||
pair->remote
|
||||
? pair->remote->priority
|
||||
: ice_compute_priority(ICE_CANDIDATE_TYPE_HOST, pair->local->resolved.addr.ss_family,
|
||||
pair->local->component, 0);
|
||||
uint64_t g = is_controlling ? local_priority : remote_priority;
|
||||
uint64_t d = is_controlling ? remote_priority : local_priority;
|
||||
uint64_t min = g < d ? g : d;
|
||||
uint64_t max = g > d ? g : d;
|
||||
pair->priority = (min << 32) + (max << 1) + (g > d ? 1 : 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ice_candidates_count(const ice_description_t *description, ice_candidate_type_t type) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < description->candidates_count; ++i) {
|
||||
const ice_candidate_t *candidate = description->candidates + i;
|
||||
if (candidate->type == type)
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t ice_compute_priority(ice_candidate_type_t type, int family, int component, int index) {
|
||||
// Compute candidate priority according to RFC 8445
|
||||
// See https://www.rfc-editor.org/rfc/rfc8445.html#section-5.1.2.1
|
||||
uint32_t p = 0;
|
||||
|
||||
switch (type) {
|
||||
case ICE_CANDIDATE_TYPE_HOST:
|
||||
p += ICE_CANDIDATE_PREF_HOST;
|
||||
break;
|
||||
case ICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
|
||||
p += ICE_CANDIDATE_PREF_PEER_REFLEXIVE;
|
||||
break;
|
||||
case ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
|
||||
p += ICE_CANDIDATE_PREF_SERVER_REFLEXIVE;
|
||||
break;
|
||||
case ICE_CANDIDATE_TYPE_RELAYED:
|
||||
p += ICE_CANDIDATE_PREF_RELAYED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p <<= 16;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
p += 32767;
|
||||
break;
|
||||
case AF_INET6:
|
||||
p += 65535;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p -= CLAMP(index, 0, 32767);
|
||||
p <<= 8;
|
||||
|
||||
p += 256 - CLAMP(component, 1, 256);
|
||||
return p;
|
||||
}
|
||||
103
thirdparty/libjuice/src/ice.h
vendored
Normal file
103
thirdparty/libjuice/src/ice.h
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_ICE_H
|
||||
#define JUICE_ICE_H
|
||||
|
||||
#include "addr.h"
|
||||
#include "juice.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define ICE_MAX_CANDIDATES_COUNT 20 // ~ 500B * 20 = 10KB
|
||||
|
||||
typedef enum ice_candidate_type {
|
||||
ICE_CANDIDATE_TYPE_UNKNOWN,
|
||||
ICE_CANDIDATE_TYPE_HOST,
|
||||
ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE,
|
||||
ICE_CANDIDATE_TYPE_PEER_REFLEXIVE,
|
||||
ICE_CANDIDATE_TYPE_RELAYED,
|
||||
} ice_candidate_type_t;
|
||||
|
||||
// RFC 8445: The RECOMMENDED values for type preferences are 126 for host candidates, 110 for
|
||||
// peer-reflexive candidates, 100 for server-reflexive candidates, and 0 for relayed candidates.
|
||||
#define ICE_CANDIDATE_PREF_HOST 126
|
||||
#define ICE_CANDIDATE_PREF_PEER_REFLEXIVE 110
|
||||
#define ICE_CANDIDATE_PREF_SERVER_REFLEXIVE 100
|
||||
#define ICE_CANDIDATE_PREF_RELAYED 0
|
||||
|
||||
typedef struct ice_candidate {
|
||||
ice_candidate_type_t type;
|
||||
uint32_t priority;
|
||||
int component;
|
||||
char foundation[32 + 1]; // 1 to 32 characters
|
||||
char transport[32 + 1];
|
||||
char hostname[256 + 1];
|
||||
char service[32 + 1];
|
||||
addr_record_t resolved;
|
||||
} ice_candidate_t;
|
||||
|
||||
typedef struct ice_description {
|
||||
char ice_ufrag[256 + 1]; // 4 to 256 characters
|
||||
char ice_pwd[256 + 1]; // 22 to 256 characters
|
||||
ice_candidate_t candidates[ICE_MAX_CANDIDATES_COUNT];
|
||||
int candidates_count;
|
||||
bool finished;
|
||||
} ice_description_t;
|
||||
|
||||
typedef enum ice_candidate_pair_state {
|
||||
ICE_CANDIDATE_PAIR_STATE_PENDING,
|
||||
ICE_CANDIDATE_PAIR_STATE_SUCCEEDED,
|
||||
ICE_CANDIDATE_PAIR_STATE_FAILED,
|
||||
ICE_CANDIDATE_PAIR_STATE_FROZEN,
|
||||
} ice_candidate_pair_state_t;
|
||||
|
||||
typedef struct ice_candidate_pair {
|
||||
ice_candidate_t *local;
|
||||
ice_candidate_t *remote;
|
||||
uint64_t priority;
|
||||
ice_candidate_pair_state_t state;
|
||||
bool nominated;
|
||||
bool nomination_requested;
|
||||
timestamp_t consent_expiry;
|
||||
} ice_candidate_pair_t;
|
||||
|
||||
typedef enum ice_resolve_mode {
|
||||
ICE_RESOLVE_MODE_SIMPLE,
|
||||
ICE_RESOLVE_MODE_LOOKUP,
|
||||
} ice_resolve_mode_t;
|
||||
|
||||
#define ICE_PARSE_ERROR -1
|
||||
#define ICE_PARSE_IGNORED -2
|
||||
#define ICE_PARSE_MISSING_UFRAG -3
|
||||
#define ICE_PARSE_MISSING_PWD -4
|
||||
|
||||
int ice_parse_sdp(const char *sdp, ice_description_t *description);
|
||||
int ice_parse_candidate_sdp(const char *line, ice_candidate_t *candidate);
|
||||
int ice_create_local_description(ice_description_t *description);
|
||||
int ice_create_local_candidate(ice_candidate_type_t type, int component, int index,
|
||||
const addr_record_t *record, ice_candidate_t *candidate);
|
||||
int ice_resolve_candidate(ice_candidate_t *candidate, ice_resolve_mode_t mode);
|
||||
int ice_add_candidate(ice_candidate_t *candidate, ice_description_t *description);
|
||||
void ice_sort_candidates(ice_description_t *description);
|
||||
ice_candidate_t *ice_find_candidate_from_addr(ice_description_t *description,
|
||||
const addr_record_t *record,
|
||||
ice_candidate_type_t type);
|
||||
int ice_generate_sdp(const ice_description_t *description, char *buffer, size_t size);
|
||||
int ice_generate_candidate_sdp(const ice_candidate_t *candidate, char *buffer, size_t size);
|
||||
int ice_create_candidate_pair(ice_candidate_t *local, ice_candidate_t *remote, bool is_controlling,
|
||||
ice_candidate_pair_t *pair); // local or remote might be NULL
|
||||
int ice_update_candidate_pair(ice_candidate_pair_t *pair, bool is_controlling);
|
||||
|
||||
int ice_candidates_count(const ice_description_t *description, ice_candidate_type_t type);
|
||||
|
||||
uint32_t ice_compute_priority(ice_candidate_type_t type, int family, int component, int index);
|
||||
|
||||
#endif
|
||||
207
thirdparty/libjuice/src/juice.c
vendored
Normal file
207
thirdparty/libjuice/src/juice.c
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice.h"
|
||||
#include "addr.h"
|
||||
#include "agent.h"
|
||||
#include "ice.h"
|
||||
|
||||
#ifndef NO_SERVER
|
||||
#include "server.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
JUICE_EXPORT juice_agent_t *juice_create(const juice_config_t *config) {
|
||||
if (!config)
|
||||
return NULL;
|
||||
|
||||
return agent_create(config);
|
||||
}
|
||||
|
||||
JUICE_EXPORT void juice_destroy(juice_agent_t *agent) {
|
||||
if (agent)
|
||||
agent_destroy(agent);
|
||||
}
|
||||
|
||||
JUICE_EXPORT int juice_gather_candidates(juice_agent_t *agent) {
|
||||
if (!agent)
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
if (agent_gather_candidates(agent) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
JUICE_EXPORT int juice_get_local_description(juice_agent_t *agent, char *buffer, size_t size) {
|
||||
if (!agent || (!buffer && size))
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
if (agent_get_local_description(agent, buffer, size) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
JUICE_EXPORT int juice_set_remote_description(juice_agent_t *agent, const char *sdp) {
|
||||
if (!agent || !sdp)
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
if (agent_set_remote_description(agent, sdp) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
JUICE_EXPORT int juice_add_remote_candidate(juice_agent_t *agent, const char *sdp) {
|
||||
if (!agent || !sdp)
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
if (agent_add_remote_candidate(agent, sdp) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
JUICE_EXPORT int juice_set_remote_gathering_done(juice_agent_t *agent) {
|
||||
if (!agent)
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
if (agent_set_remote_gathering_done(agent) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
JUICE_EXPORT int juice_send(juice_agent_t *agent, const char *data, size_t size) {
|
||||
if (!agent || (!data && size))
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
if (agent_send(agent, data, size, 0) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
JUICE_EXPORT int juice_send_diffserv(juice_agent_t *agent, const char *data, size_t size, int ds) {
|
||||
if (!agent || (!data && size))
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
if (agent_send(agent, data, size, ds) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
JUICE_EXPORT juice_state_t juice_get_state(juice_agent_t *agent) { return agent_get_state(agent); }
|
||||
|
||||
JUICE_EXPORT int juice_get_selected_candidates(juice_agent_t *agent, char *local, size_t local_size,
|
||||
char *remote, size_t remote_size) {
|
||||
if (!agent || (!local && local_size) || (!remote && remote_size))
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
ice_candidate_t local_cand, remote_cand;
|
||||
if (agent_get_selected_candidate_pair(agent, &local_cand, &remote_cand))
|
||||
return JUICE_ERR_NOT_AVAIL;
|
||||
|
||||
if (local_size && ice_generate_candidate_sdp(&local_cand, local, local_size) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
if (remote_size && ice_generate_candidate_sdp(&remote_cand, remote, remote_size) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
JUICE_EXPORT int juice_get_selected_addresses(juice_agent_t *agent, char *local, size_t local_size,
|
||||
char *remote, size_t remote_size) {
|
||||
if (!agent || (!local && local_size) || (!remote && remote_size))
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
ice_candidate_t local_cand, remote_cand;
|
||||
if (agent_get_selected_candidate_pair(agent, &local_cand, &remote_cand))
|
||||
return JUICE_ERR_NOT_AVAIL;
|
||||
|
||||
if (local_size && addr_record_to_string(&local_cand.resolved, local, local_size) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
if (remote_size && addr_record_to_string(&remote_cand.resolved, remote, remote_size) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
JUICE_EXPORT const char *juice_state_to_string(juice_state_t state) {
|
||||
switch (state) {
|
||||
case JUICE_STATE_DISCONNECTED:
|
||||
return "disconnected";
|
||||
case JUICE_STATE_GATHERING:
|
||||
return "gathering";
|
||||
case JUICE_STATE_CONNECTING:
|
||||
return "connecting";
|
||||
case JUICE_STATE_CONNECTED:
|
||||
return "connected";
|
||||
case JUICE_STATE_COMPLETED:
|
||||
return "completed";
|
||||
case JUICE_STATE_FAILED:
|
||||
return "failed";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
JUICE_EXPORT juice_server_t *juice_server_create(const juice_server_config_t *config) {
|
||||
#ifndef NO_SERVER
|
||||
if (!config)
|
||||
return NULL;
|
||||
|
||||
return server_create(config);
|
||||
#else
|
||||
(void)config;
|
||||
JLOG_FATAL("The library was compiled without server support");
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
JUICE_EXPORT void juice_server_destroy(juice_server_t *server) {
|
||||
#ifndef NO_SERVER
|
||||
if (server)
|
||||
server_destroy(server);
|
||||
#else
|
||||
(void)server;
|
||||
#endif
|
||||
}
|
||||
|
||||
JUICE_EXPORT uint16_t juice_server_get_port(juice_server_t *server) {
|
||||
#ifndef NO_SERVER
|
||||
return server ? server_get_port(server) : 0;
|
||||
#else
|
||||
(void)server;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
JUICE_EXPORT int juice_server_add_credentials(juice_server_t *server,
|
||||
const juice_server_credentials_t *credentials,
|
||||
unsigned long lifetime_ms) {
|
||||
#ifndef NO_SERVER
|
||||
if (!server || !credentials)
|
||||
return JUICE_ERR_INVALID;
|
||||
|
||||
if (server_add_credentials(server, credentials, (timediff_t)lifetime_ms) < 0)
|
||||
return JUICE_ERR_FAILED;
|
||||
|
||||
return JUICE_ERR_SUCCESS;
|
||||
#else
|
||||
(void)server;
|
||||
(void)credentials;
|
||||
(void)lifetime_ms;
|
||||
return JUICE_ERR_INVALID;
|
||||
#endif
|
||||
}
|
||||
129
thirdparty/libjuice/src/log.c
vendored
Normal file
129
thirdparty/libjuice/src/log.c
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "thread.h" // for mutexes and atomics
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
static const char *log_level_names[] = {"VERBOSE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
|
||||
|
||||
static const char *log_level_colors[] = {
|
||||
"\x1B[90m", // grey
|
||||
"\x1B[96m", // cyan
|
||||
"\x1B[39m", // default foreground
|
||||
"\x1B[93m", // yellow
|
||||
"\x1B[91m", // red
|
||||
"\x1B[97m\x1B[41m" // white on red
|
||||
};
|
||||
|
||||
static mutex_t log_mutex = MUTEX_INITIALIZER;
|
||||
static volatile juice_log_cb_t log_cb = NULL;
|
||||
static atomic(juice_log_level_t) log_level = ATOMIC_VAR_INIT(JUICE_LOG_LEVEL_WARN);
|
||||
|
||||
static bool use_color(void) {
|
||||
#ifdef _WIN32
|
||||
return false;
|
||||
#else
|
||||
return isatty(fileno(stdout)) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int get_localtime(const time_t *t, struct tm *buf) {
|
||||
#ifdef _WIN32
|
||||
// Windows does not have POSIX localtime_r...
|
||||
return localtime_s(buf, t) == 0 ? 0 : -1;
|
||||
#else // POSIX
|
||||
return localtime_r(t, buf) != NULL ? 0 : -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
JUICE_EXPORT void juice_set_log_level(juice_log_level_t level) { atomic_store(&log_level, level); }
|
||||
|
||||
JUICE_EXPORT void juice_set_log_handler(juice_log_cb_t cb) {
|
||||
mutex_lock(&log_mutex);
|
||||
log_cb = cb;
|
||||
mutex_unlock(&log_mutex);
|
||||
}
|
||||
|
||||
bool juice_log_is_enabled(juice_log_level_t level) {
|
||||
return level != JUICE_LOG_LEVEL_NONE && level >= atomic_load(&log_level);
|
||||
}
|
||||
|
||||
void juice_log_write(juice_log_level_t level, const char *file, int line, const char *fmt, ...) {
|
||||
if (!juice_log_is_enabled(level))
|
||||
return;
|
||||
|
||||
mutex_lock(&log_mutex);
|
||||
|
||||
#if !RELEASE
|
||||
const char *filename = file + strlen(file);
|
||||
while (filename != file && *filename != '/' && *filename != '\\')
|
||||
--filename;
|
||||
if (filename != file)
|
||||
++filename;
|
||||
#else
|
||||
(void)file;
|
||||
(void)line;
|
||||
#endif
|
||||
|
||||
if (log_cb) {
|
||||
char message[BUFFER_SIZE];
|
||||
int len = 0;
|
||||
#if !RELEASE
|
||||
len = snprintf(message, BUFFER_SIZE, "%s:%d: ", filename, line);
|
||||
if (len < 0)
|
||||
return;
|
||||
#endif
|
||||
if (len < BUFFER_SIZE) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(message + len, BUFFER_SIZE - len, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
log_cb(level, message);
|
||||
|
||||
} else {
|
||||
time_t t = time(NULL);
|
||||
struct tm lt;
|
||||
char buffer[16];
|
||||
if (get_localtime(&t, <) != 0 || strftime(buffer, 16, "%H:%M:%S", <) == 0)
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (use_color())
|
||||
fprintf(stdout, "%s", log_level_colors[level]);
|
||||
|
||||
fprintf(stdout, "%s %-7s ", buffer, log_level_names[level]);
|
||||
|
||||
#if !RELEASE
|
||||
fprintf(stdout, "%s:%d: ", filename, line);
|
||||
#endif
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (use_color())
|
||||
fprintf(stdout, "%s", "\x1B[0m\x1B[0K");
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
mutex_unlock(&log_mutex);
|
||||
}
|
||||
33
thirdparty/libjuice/src/log.h
vendored
Normal file
33
thirdparty/libjuice/src/log.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_LOG_H
|
||||
#define JUICE_LOG_H
|
||||
|
||||
#include "juice.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
bool juice_log_is_enabled(juice_log_level_t level);
|
||||
void juice_log_write(juice_log_level_t level, const char *file, int line, const char *fmt, ...);
|
||||
|
||||
#define JLOG_VERBOSE(...) juice_log_write(JUICE_LOG_LEVEL_VERBOSE, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define JLOG_DEBUG(...) juice_log_write(JUICE_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define JLOG_INFO(...) juice_log_write(JUICE_LOG_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define JLOG_WARN(...) juice_log_write(JUICE_LOG_LEVEL_WARN, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define JLOG_ERROR(...) juice_log_write(JUICE_LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define JLOG_FATAL(...) juice_log_write(JUICE_LOG_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define JLOG_VERBOSE_ENABLED juice_log_is_enabled(JUICE_LOG_LEVEL_VERBOSE)
|
||||
#define JLOG_DEBUG_ENABLED juice_log_is_enabled(JUICE_LOG_LEVEL_DEBUG)
|
||||
#define JLOG_INFO_ENABLED juice_log_is_enabled(JUICE_LOG_LEVEL_INFO)
|
||||
#define JLOG_WARN_ENABLED juice_log_is_enabled(JUICE_LOG_LEVEL_WARN)
|
||||
#define JLOG_ERROR_ENABLED juice_log_is_enabled(JUICE_LOG_LEVEL_ERROR)
|
||||
#define JLOG_FATAL_ENABLED juice_log_is_enabled(JUICE_LOG_LEVEL_FATAL)
|
||||
|
||||
#endif // JUICE_LOG_H
|
||||
741
thirdparty/libjuice/src/picohash.h
vendored
Normal file
741
thirdparty/libjuice/src/picohash.h
vendored
Normal file
@@ -0,0 +1,741 @@
|
||||
/**
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
#ifndef _picohash_h_
|
||||
#define _picohash_h_
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
/* assume Windows is little endian */
|
||||
#elif defined __BIG_ENDIAN__
|
||||
#define _PICOHASH_BIG_ENDIAN
|
||||
#elif defined __LITTLE_ENDIAN__
|
||||
/* override */
|
||||
#elif defined __BYTE_ORDER
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define _PICOHASH_BIG_ENDIAN
|
||||
#endif
|
||||
#else // ! defined __LITTLE_ENDIAN__
|
||||
#include <endian.h> // machine/endian.h
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define _PICOHASH_BIG_ENDIAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define PICOHASH_MD5_BLOCK_LENGTH 64
|
||||
#define PICOHASH_MD5_DIGEST_LENGTH 16
|
||||
|
||||
typedef struct {
|
||||
uint_fast32_t lo, hi;
|
||||
uint_fast32_t a, b, c, d;
|
||||
unsigned char buffer[64];
|
||||
uint_fast32_t block[PICOHASH_MD5_DIGEST_LENGTH];
|
||||
} _picohash_md5_ctx_t;
|
||||
|
||||
static void _picohash_md5_init(_picohash_md5_ctx_t *ctx);
|
||||
static void _picohash_md5_update(_picohash_md5_ctx_t *ctx, const void *data, size_t size);
|
||||
static void _picohash_md5_final(_picohash_md5_ctx_t *ctx, void *digest);
|
||||
|
||||
#define PICOHASH_SHA1_BLOCK_LENGTH 64
|
||||
#define PICOHASH_SHA1_DIGEST_LENGTH 20
|
||||
|
||||
typedef struct {
|
||||
uint32_t buffer[PICOHASH_SHA1_BLOCK_LENGTH / 4];
|
||||
uint32_t state[PICOHASH_SHA1_DIGEST_LENGTH / 4];
|
||||
uint64_t byteCount;
|
||||
uint8_t bufferOffset;
|
||||
} _picohash_sha1_ctx_t;
|
||||
|
||||
static void _picohash_sha1_init(_picohash_sha1_ctx_t *ctx);
|
||||
static void _picohash_sha1_update(_picohash_sha1_ctx_t *ctx, const void *input, size_t len);
|
||||
static void _picohash_sha1_final(_picohash_sha1_ctx_t *ctx, void *digest);
|
||||
|
||||
#define PICOHASH_SHA256_BLOCK_LENGTH 64
|
||||
#define PICOHASH_SHA256_DIGEST_LENGTH 32
|
||||
#define PICOHASH_SHA224_BLOCK_LENGTH PICOHASH_SHA256_BLOCK_LENGTH
|
||||
#define PICOHASH_SHA224_DIGEST_LENGTH 28
|
||||
|
||||
typedef struct {
|
||||
uint64_t length;
|
||||
uint32_t state[PICOHASH_SHA256_DIGEST_LENGTH / 4];
|
||||
uint32_t curlen;
|
||||
unsigned char buf[PICOHASH_SHA256_BLOCK_LENGTH];
|
||||
} _picohash_sha256_ctx_t;
|
||||
|
||||
static void _picohash_sha256_init(_picohash_sha256_ctx_t *ctx);
|
||||
static void _picohash_sha256_update(_picohash_sha256_ctx_t *ctx, const void *data, size_t len);
|
||||
static void _picohash_sha256_final(_picohash_sha256_ctx_t *ctx, void *digest);
|
||||
static void _picohash_sha224_init(_picohash_sha256_ctx_t *ctx);
|
||||
static void _picohash_sha224_final(_picohash_sha256_ctx_t *ctx, void *digest);
|
||||
|
||||
#define PICOHASH_MAX_BLOCK_LENGTH 64
|
||||
#define PICOHASH_MAX_DIGEST_LENGTH 32
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
_picohash_md5_ctx_t _md5;
|
||||
_picohash_sha1_ctx_t _sha1;
|
||||
_picohash_sha256_ctx_t _sha256;
|
||||
};
|
||||
size_t block_length;
|
||||
size_t digest_length;
|
||||
void (*_reset)(void *ctx);
|
||||
void (*_update)(void *ctx, const void *input, size_t len);
|
||||
void (*_final)(void *ctx, void *digest);
|
||||
struct {
|
||||
unsigned char key[PICOHASH_MAX_BLOCK_LENGTH];
|
||||
void (*hash_reset)(void *ctx);
|
||||
void (*hash_final)(void *ctx, void *digest);
|
||||
} _hmac;
|
||||
} picohash_ctx_t;
|
||||
|
||||
static void picohash_init_md5(picohash_ctx_t *ctx);
|
||||
static void picohash_init_sha1(picohash_ctx_t *ctx);
|
||||
static void picohash_init_sha224(picohash_ctx_t *ctx);
|
||||
static void picohash_init_sha256(picohash_ctx_t *ctx);
|
||||
static void picohash_update(picohash_ctx_t *ctx, const void *input, size_t len);
|
||||
static void picohash_final(picohash_ctx_t *ctx, void *digest);
|
||||
static void picohash_reset(picohash_ctx_t *ctx);
|
||||
|
||||
static void picohash_init_hmac(picohash_ctx_t *ctx, void (*initf)(picohash_ctx_t *), const void *key, size_t key_len);
|
||||
|
||||
/* following are private definitions */
|
||||
|
||||
/*
|
||||
* The basic MD5 functions.
|
||||
*
|
||||
* F is optimized compared to its RFC 1321 definition just like in Colin
|
||||
* Plumb's implementation.
|
||||
*/
|
||||
#define _PICOHASH_MD5_F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define _PICOHASH_MD5_G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
|
||||
#define _PICOHASH_MD5_H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define _PICOHASH_MD5_I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
|
||||
/*
|
||||
* The MD5 transformation for all four rounds.
|
||||
*/
|
||||
#define _PICOHASH_MD5_STEP(f, a, b, c, d, x, t, s) \
|
||||
(a) += f((b), (c), (d)) + (x) + (t); \
|
||||
(a) = (((a) << (s)) | (((a)&0xffffffff) >> (32 - (s)))); \
|
||||
(a) += (b);
|
||||
|
||||
/*
|
||||
* SET reads 4 input bytes in little-endian byte order and stores them
|
||||
* in a properly aligned word in host byte order.
|
||||
*
|
||||
* Paul-Louis Ageneau: Removed optimization for little-endian architectures
|
||||
* as it resulted in incorrect behavior when compiling with gcc optimizations.
|
||||
*/
|
||||
#define _PICOHASH_MD5_SET(n) \
|
||||
(ctx->block[(n)] = (uint_fast32_t)ptr[(n)*4] | ((uint_fast32_t)ptr[(n)*4 + 1] << 8) | ((uint_fast32_t)ptr[(n)*4 + 2] << 16) | \
|
||||
((uint_fast32_t)ptr[(n)*4 + 3] << 24))
|
||||
#define _PICOHASH_MD5_GET(n) (ctx->block[(n)])
|
||||
|
||||
/*
|
||||
* This processes one or more 64-byte data blocks, but does NOT update
|
||||
* the bit counters. There're no alignment requirements.
|
||||
*/
|
||||
static const void *_picohash_md5_body(_picohash_md5_ctx_t *ctx, const void *data, size_t size)
|
||||
{
|
||||
const unsigned char *ptr;
|
||||
uint_fast32_t a, b, c, d;
|
||||
uint_fast32_t saved_a, saved_b, saved_c, saved_d;
|
||||
|
||||
ptr = data;
|
||||
|
||||
a = ctx->a;
|
||||
b = ctx->b;
|
||||
c = ctx->c;
|
||||
d = ctx->d;
|
||||
|
||||
do {
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, a, b, c, d, _PICOHASH_MD5_SET(0), 0xd76aa478, 7)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, d, a, b, c, _PICOHASH_MD5_SET(1), 0xe8c7b756, 12)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, c, d, a, b, _PICOHASH_MD5_SET(2), 0x242070db, 17)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, b, c, d, a, _PICOHASH_MD5_SET(3), 0xc1bdceee, 22)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, a, b, c, d, _PICOHASH_MD5_SET(4), 0xf57c0faf, 7)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, d, a, b, c, _PICOHASH_MD5_SET(5), 0x4787c62a, 12)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, c, d, a, b, _PICOHASH_MD5_SET(6), 0xa8304613, 17)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, b, c, d, a, _PICOHASH_MD5_SET(7), 0xfd469501, 22)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, a, b, c, d, _PICOHASH_MD5_SET(8), 0x698098d8, 7)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, d, a, b, c, _PICOHASH_MD5_SET(9), 0x8b44f7af, 12)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, c, d, a, b, _PICOHASH_MD5_SET(10), 0xffff5bb1, 17)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, b, c, d, a, _PICOHASH_MD5_SET(11), 0x895cd7be, 22)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, a, b, c, d, _PICOHASH_MD5_SET(12), 0x6b901122, 7)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, d, a, b, c, _PICOHASH_MD5_SET(13), 0xfd987193, 12)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, c, d, a, b, _PICOHASH_MD5_SET(14), 0xa679438e, 17)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, b, c, d, a, _PICOHASH_MD5_SET(15), 0x49b40821, 22)
|
||||
|
||||
/* Round 2 */
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, a, b, c, d, _PICOHASH_MD5_GET(1), 0xf61e2562, 5)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, d, a, b, c, _PICOHASH_MD5_GET(6), 0xc040b340, 9)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, c, d, a, b, _PICOHASH_MD5_GET(11), 0x265e5a51, 14)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, b, c, d, a, _PICOHASH_MD5_GET(0), 0xe9b6c7aa, 20)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, a, b, c, d, _PICOHASH_MD5_GET(5), 0xd62f105d, 5)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, d, a, b, c, _PICOHASH_MD5_GET(10), 0x02441453, 9)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, c, d, a, b, _PICOHASH_MD5_GET(15), 0xd8a1e681, 14)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, b, c, d, a, _PICOHASH_MD5_GET(4), 0xe7d3fbc8, 20)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, a, b, c, d, _PICOHASH_MD5_GET(9), 0x21e1cde6, 5)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, d, a, b, c, _PICOHASH_MD5_GET(14), 0xc33707d6, 9)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, c, d, a, b, _PICOHASH_MD5_GET(3), 0xf4d50d87, 14)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, b, c, d, a, _PICOHASH_MD5_GET(8), 0x455a14ed, 20)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, a, b, c, d, _PICOHASH_MD5_GET(13), 0xa9e3e905, 5)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, d, a, b, c, _PICOHASH_MD5_GET(2), 0xfcefa3f8, 9)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, c, d, a, b, _PICOHASH_MD5_GET(7), 0x676f02d9, 14)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, b, c, d, a, _PICOHASH_MD5_GET(12), 0x8d2a4c8a, 20)
|
||||
|
||||
/* Round 3 */
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, a, b, c, d, _PICOHASH_MD5_GET(5), 0xfffa3942, 4)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, d, a, b, c, _PICOHASH_MD5_GET(8), 0x8771f681, 11)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, c, d, a, b, _PICOHASH_MD5_GET(11), 0x6d9d6122, 16)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, b, c, d, a, _PICOHASH_MD5_GET(14), 0xfde5380c, 23)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, a, b, c, d, _PICOHASH_MD5_GET(1), 0xa4beea44, 4)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, d, a, b, c, _PICOHASH_MD5_GET(4), 0x4bdecfa9, 11)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, c, d, a, b, _PICOHASH_MD5_GET(7), 0xf6bb4b60, 16)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, b, c, d, a, _PICOHASH_MD5_GET(10), 0xbebfbc70, 23)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, a, b, c, d, _PICOHASH_MD5_GET(13), 0x289b7ec6, 4)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, d, a, b, c, _PICOHASH_MD5_GET(0), 0xeaa127fa, 11)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, c, d, a, b, _PICOHASH_MD5_GET(3), 0xd4ef3085, 16)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, b, c, d, a, _PICOHASH_MD5_GET(6), 0x04881d05, 23)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, a, b, c, d, _PICOHASH_MD5_GET(9), 0xd9d4d039, 4)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, d, a, b, c, _PICOHASH_MD5_GET(12), 0xe6db99e5, 11)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, c, d, a, b, _PICOHASH_MD5_GET(15), 0x1fa27cf8, 16)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, b, c, d, a, _PICOHASH_MD5_GET(2), 0xc4ac5665, 23)
|
||||
|
||||
/* Round 4 */
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, a, b, c, d, _PICOHASH_MD5_GET(0), 0xf4292244, 6)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, d, a, b, c, _PICOHASH_MD5_GET(7), 0x432aff97, 10)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, c, d, a, b, _PICOHASH_MD5_GET(14), 0xab9423a7, 15)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, b, c, d, a, _PICOHASH_MD5_GET(5), 0xfc93a039, 21)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, a, b, c, d, _PICOHASH_MD5_GET(12), 0x655b59c3, 6)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, d, a, b, c, _PICOHASH_MD5_GET(3), 0x8f0ccc92, 10)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, c, d, a, b, _PICOHASH_MD5_GET(10), 0xffeff47d, 15)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, b, c, d, a, _PICOHASH_MD5_GET(1), 0x85845dd1, 21)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, a, b, c, d, _PICOHASH_MD5_GET(8), 0x6fa87e4f, 6)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, d, a, b, c, _PICOHASH_MD5_GET(15), 0xfe2ce6e0, 10)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, c, d, a, b, _PICOHASH_MD5_GET(6), 0xa3014314, 15)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, b, c, d, a, _PICOHASH_MD5_GET(13), 0x4e0811a1, 21)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, a, b, c, d, _PICOHASH_MD5_GET(4), 0xf7537e82, 6)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, d, a, b, c, _PICOHASH_MD5_GET(11), 0xbd3af235, 10)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, c, d, a, b, _PICOHASH_MD5_GET(2), 0x2ad7d2bb, 15)
|
||||
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, b, c, d, a, _PICOHASH_MD5_GET(9), 0xeb86d391, 21)
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
c += saved_c;
|
||||
d += saved_d;
|
||||
|
||||
ptr += 64;
|
||||
} while (size -= 64);
|
||||
|
||||
ctx->a = a;
|
||||
ctx->b = b;
|
||||
ctx->c = c;
|
||||
ctx->d = d;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
inline void _picohash_md5_init(_picohash_md5_ctx_t *ctx)
|
||||
{
|
||||
ctx->a = 0x67452301;
|
||||
ctx->b = 0xefcdab89;
|
||||
ctx->c = 0x98badcfe;
|
||||
ctx->d = 0x10325476;
|
||||
|
||||
ctx->lo = 0;
|
||||
ctx->hi = 0;
|
||||
}
|
||||
|
||||
inline void _picohash_md5_update(_picohash_md5_ctx_t *ctx, const void *data, size_t size)
|
||||
{
|
||||
uint_fast32_t saved_lo;
|
||||
unsigned long used, free;
|
||||
|
||||
saved_lo = ctx->lo;
|
||||
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
|
||||
ctx->hi++;
|
||||
ctx->hi += (uint_fast32_t)(size >> 29);
|
||||
|
||||
used = saved_lo & 0x3f;
|
||||
|
||||
if (used) {
|
||||
free = 64 - used;
|
||||
|
||||
if (size < free) {
|
||||
memcpy(&ctx->buffer[used], data, size);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ctx->buffer[used], data, free);
|
||||
data = (const unsigned char *)data + free;
|
||||
size -= free;
|
||||
_picohash_md5_body(ctx, ctx->buffer, 64);
|
||||
}
|
||||
|
||||
if (size >= 64) {
|
||||
data = _picohash_md5_body(ctx, data, size & ~(unsigned long)0x3f);
|
||||
size &= 0x3f;
|
||||
}
|
||||
|
||||
memcpy(ctx->buffer, data, size);
|
||||
}
|
||||
|
||||
inline void _picohash_md5_final(_picohash_md5_ctx_t *ctx, void *_digest)
|
||||
{
|
||||
unsigned char *digest = _digest;
|
||||
unsigned long used, free;
|
||||
|
||||
used = ctx->lo & 0x3f;
|
||||
|
||||
ctx->buffer[used++] = 0x80;
|
||||
|
||||
free = 64 - used;
|
||||
|
||||
if (free < 8) {
|
||||
memset(&ctx->buffer[used], 0, free);
|
||||
_picohash_md5_body(ctx, ctx->buffer, 64);
|
||||
used = 0;
|
||||
free = 64;
|
||||
}
|
||||
|
||||
memset(&ctx->buffer[used], 0, free - 8);
|
||||
|
||||
ctx->lo <<= 3;
|
||||
ctx->buffer[56] = ctx->lo;
|
||||
ctx->buffer[57] = ctx->lo >> 8;
|
||||
ctx->buffer[58] = ctx->lo >> 16;
|
||||
ctx->buffer[59] = ctx->lo >> 24;
|
||||
ctx->buffer[60] = ctx->hi;
|
||||
ctx->buffer[61] = ctx->hi >> 8;
|
||||
ctx->buffer[62] = ctx->hi >> 16;
|
||||
ctx->buffer[63] = ctx->hi >> 24;
|
||||
|
||||
_picohash_md5_body(ctx, ctx->buffer, 64);
|
||||
|
||||
digest[0] = ctx->a;
|
||||
digest[1] = ctx->a >> 8;
|
||||
digest[2] = ctx->a >> 16;
|
||||
digest[3] = ctx->a >> 24;
|
||||
digest[4] = ctx->b;
|
||||
digest[5] = ctx->b >> 8;
|
||||
digest[6] = ctx->b >> 16;
|
||||
digest[7] = ctx->b >> 24;
|
||||
digest[8] = ctx->c;
|
||||
digest[9] = ctx->c >> 8;
|
||||
digest[10] = ctx->c >> 16;
|
||||
digest[11] = ctx->c >> 24;
|
||||
digest[12] = ctx->d;
|
||||
digest[13] = ctx->d >> 8;
|
||||
digest[14] = ctx->d >> 16;
|
||||
digest[15] = ctx->d >> 24;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
|
||||
#define _PICOHASH_SHA1_K0 0x5a827999
|
||||
#define _PICOHASH_SHA1_K20 0x6ed9eba1
|
||||
#define _PICOHASH_SHA1_K40 0x8f1bbcdc
|
||||
#define _PICOHASH_SHA1_K60 0xca62c1d6
|
||||
|
||||
static inline uint32_t _picohash_sha1_rol32(uint32_t number, uint8_t bits)
|
||||
{
|
||||
return ((number << bits) | (number >> (32 - bits)));
|
||||
}
|
||||
|
||||
static inline void _picohash_sha1_hash_block(_picohash_sha1_ctx_t *s)
|
||||
{
|
||||
uint8_t i;
|
||||
uint32_t a, b, c, d, e, t;
|
||||
|
||||
a = s->state[0];
|
||||
b = s->state[1];
|
||||
c = s->state[2];
|
||||
d = s->state[3];
|
||||
e = s->state[4];
|
||||
for (i = 0; i < 80; i++) {
|
||||
if (i >= 16) {
|
||||
t = s->buffer[(i + 13) & 15] ^ s->buffer[(i + 8) & 15] ^ s->buffer[(i + 2) & 15] ^ s->buffer[i & 15];
|
||||
s->buffer[i & 15] = _picohash_sha1_rol32(t, 1);
|
||||
}
|
||||
if (i < 20) {
|
||||
t = (d ^ (b & (c ^ d))) + _PICOHASH_SHA1_K0;
|
||||
} else if (i < 40) {
|
||||
t = (b ^ c ^ d) + _PICOHASH_SHA1_K20;
|
||||
} else if (i < 60) {
|
||||
t = ((b & c) | (d & (b | c))) + _PICOHASH_SHA1_K40;
|
||||
} else {
|
||||
t = (b ^ c ^ d) + _PICOHASH_SHA1_K60;
|
||||
}
|
||||
t += _picohash_sha1_rol32(a, 5) + e + s->buffer[i & 15];
|
||||
e = d;
|
||||
d = c;
|
||||
c = _picohash_sha1_rol32(b, 30);
|
||||
b = a;
|
||||
a = t;
|
||||
}
|
||||
s->state[0] += a;
|
||||
s->state[1] += b;
|
||||
s->state[2] += c;
|
||||
s->state[3] += d;
|
||||
s->state[4] += e;
|
||||
}
|
||||
|
||||
static inline void _picohash_sha1_add_uncounted(_picohash_sha1_ctx_t *s, uint8_t data)
|
||||
{
|
||||
uint8_t *const b = (uint8_t *)s->buffer;
|
||||
#ifdef _PICOHASH_BIG_ENDIAN
|
||||
b[s->bufferOffset] = data;
|
||||
#else
|
||||
b[s->bufferOffset ^ 3] = data;
|
||||
#endif
|
||||
s->bufferOffset++;
|
||||
if (s->bufferOffset == PICOHASH_SHA1_BLOCK_LENGTH) {
|
||||
_picohash_sha1_hash_block(s);
|
||||
s->bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void _picohash_sha1_init(_picohash_sha1_ctx_t *s)
|
||||
{
|
||||
s->state[0] = 0x67452301;
|
||||
s->state[1] = 0xefcdab89;
|
||||
s->state[2] = 0x98badcfe;
|
||||
s->state[3] = 0x10325476;
|
||||
s->state[4] = 0xc3d2e1f0;
|
||||
s->byteCount = 0;
|
||||
s->bufferOffset = 0;
|
||||
}
|
||||
|
||||
inline void _picohash_sha1_update(_picohash_sha1_ctx_t *s, const void *_data, size_t len)
|
||||
{
|
||||
const uint8_t *data = _data;
|
||||
for (; len != 0; --len) {
|
||||
++s->byteCount;
|
||||
_picohash_sha1_add_uncounted(s, *data++);
|
||||
}
|
||||
}
|
||||
|
||||
inline void _picohash_sha1_final(_picohash_sha1_ctx_t *s, void *digest)
|
||||
{
|
||||
// Pad with 0x80 followed by 0x00 until the end of the block
|
||||
_picohash_sha1_add_uncounted(s, 0x80);
|
||||
while (s->bufferOffset != 56)
|
||||
_picohash_sha1_add_uncounted(s, 0x00);
|
||||
|
||||
// Append length in the last 8 bytes
|
||||
_picohash_sha1_add_uncounted(s, (uint8_t)(s->byteCount >> 53)); // Shifting to multiply by 8
|
||||
_picohash_sha1_add_uncounted(s, (uint8_t)(s->byteCount >> 45)); // as SHA-1 supports bitstreams
|
||||
_picohash_sha1_add_uncounted(s, (uint8_t)(s->byteCount >> 37)); // as well as byte.
|
||||
_picohash_sha1_add_uncounted(s, (uint8_t)(s->byteCount >> 29));
|
||||
_picohash_sha1_add_uncounted(s, (uint8_t)(s->byteCount >> 21));
|
||||
_picohash_sha1_add_uncounted(s, (uint8_t)(s->byteCount >> 13));
|
||||
_picohash_sha1_add_uncounted(s, (uint8_t)(s->byteCount >> 5));
|
||||
_picohash_sha1_add_uncounted(s, (uint8_t)(s->byteCount << 3));
|
||||
|
||||
#ifndef SHA_BIG_ENDIAN
|
||||
{ // Swap byte order back
|
||||
int i;
|
||||
for (i = 0; i < 5; i++) {
|
||||
s->state[i] = (((s->state[i]) << 24) & 0xff000000) | (((s->state[i]) << 8) & 0x00ff0000) |
|
||||
(((s->state[i]) >> 8) & 0x0000ff00) | (((s->state[i]) >> 24) & 0x000000ff);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
memcpy(digest, s->state, sizeof(s->state));
|
||||
}
|
||||
|
||||
#define _picohash_sha256_ch(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define _picohash_sha256_maj(x, y, z) (((x | y) & z) | (x & y))
|
||||
#define _picohash_sha256_s(x, y) \
|
||||
(((((uint32_t)(x)&0xFFFFFFFFUL) >> (uint32_t)((y)&31)) | ((uint32_t)(x) << (uint32_t)(32 - ((y)&31)))) & 0xFFFFFFFFUL)
|
||||
#define _picohash_sha256_r(x, n) (((x)&0xFFFFFFFFUL) >> (n))
|
||||
#define _picohash_sha256_sigma0(x) (_picohash_sha256_s(x, 2) ^ _picohash_sha256_s(x, 13) ^ _picohash_sha256_s(x, 22))
|
||||
#define _picohash_sha256_sigma1(x) (_picohash_sha256_s(x, 6) ^ _picohash_sha256_s(x, 11) ^ _picohash_sha256_s(x, 25))
|
||||
#define _picohash_sha256_gamma0(x) (_picohash_sha256_s(x, 7) ^ _picohash_sha256_s(x, 18) ^ _picohash_sha256_r(x, 3))
|
||||
#define _picohash_sha256_gamma1(x) (_picohash_sha256_s(x, 17) ^ _picohash_sha256_s(x, 19) ^ _picohash_sha256_r(x, 10))
|
||||
#define _picohash_sha256_rnd(a, b, c, d, e, f, g, h, i) \
|
||||
t0 = h + _picohash_sha256_sigma1(e) + _picohash_sha256_ch(e, f, g) + K[i] + W[i]; \
|
||||
t1 = _picohash_sha256_sigma0(a) + _picohash_sha256_maj(a, b, c); \
|
||||
d += t0; \
|
||||
h = t0 + t1;
|
||||
|
||||
static inline void _picohash_sha256_compress(_picohash_sha256_ctx_t *ctx, unsigned char *buf)
|
||||
{
|
||||
static const uint32_t K[64] = {
|
||||
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
|
||||
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
|
||||
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
|
||||
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
|
||||
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
|
||||
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
|
||||
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
|
||||
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL};
|
||||
uint32_t S[8], W[64], t, t0, t1;
|
||||
int i;
|
||||
|
||||
/* copy state into S */
|
||||
for (i = 0; i < 8; i++)
|
||||
S[i] = ctx->state[i];
|
||||
|
||||
/* copy the state into 512-bits into W[0..15] */
|
||||
for (i = 0; i < 16; i++)
|
||||
W[i] =
|
||||
(uint32_t)buf[4 * i] << 24 | (uint32_t)buf[4 * i + 1] << 16 | (uint32_t)buf[4 * i + 2] << 8 | (uint32_t)buf[4 * i + 3];
|
||||
|
||||
/* fill W[16..63] */
|
||||
for (i = 16; i < 64; i++)
|
||||
W[i] = _picohash_sha256_gamma1(W[i - 2]) + W[i - 7] + _picohash_sha256_gamma0(W[i - 15]) + W[i - 16];
|
||||
|
||||
/* Compress */
|
||||
for (i = 0; i < 64; ++i) {
|
||||
_picohash_sha256_rnd(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
|
||||
t = S[7];
|
||||
S[7] = S[6];
|
||||
S[6] = S[5];
|
||||
S[5] = S[4];
|
||||
S[4] = S[3];
|
||||
S[3] = S[2];
|
||||
S[2] = S[1];
|
||||
S[1] = S[0];
|
||||
S[0] = t;
|
||||
}
|
||||
|
||||
/* feedback */
|
||||
for (i = 0; i < 8; i++)
|
||||
ctx->state[i] = ctx->state[i] + S[i];
|
||||
}
|
||||
|
||||
static inline void _picohash_sha256_do_final(_picohash_sha256_ctx_t *ctx, void *digest, size_t len)
|
||||
{
|
||||
unsigned char *out = digest;
|
||||
size_t i;
|
||||
|
||||
/* increase the length of the message */
|
||||
ctx->length += ctx->curlen * 8;
|
||||
|
||||
/* append the '1' bit */
|
||||
ctx->buf[ctx->curlen++] = (unsigned char)0x80;
|
||||
|
||||
/* if the length is currently above 56 bytes we append zeros
|
||||
* then compress. Then we can fall back to padding zeros and length
|
||||
* encoding like normal.
|
||||
*/
|
||||
if (ctx->curlen > 56) {
|
||||
while (ctx->curlen < 64) {
|
||||
ctx->buf[ctx->curlen++] = (unsigned char)0;
|
||||
}
|
||||
_picohash_sha256_compress(ctx, ctx->buf);
|
||||
ctx->curlen = 0;
|
||||
}
|
||||
|
||||
/* pad upto 56 bytes of zeroes */
|
||||
while (ctx->curlen < 56) {
|
||||
ctx->buf[ctx->curlen++] = (unsigned char)0;
|
||||
}
|
||||
|
||||
/* store length */
|
||||
for (i = 0; i != 8; ++i)
|
||||
ctx->buf[56 + i] = (unsigned char)(ctx->length >> (56 - 8 * i));
|
||||
_picohash_sha256_compress(ctx, ctx->buf);
|
||||
|
||||
/* copy output */
|
||||
for (i = 0; i != len / 4; ++i) {
|
||||
out[i * 4] = ctx->state[i] >> 24;
|
||||
out[i * 4 + 1] = ctx->state[i] >> 16;
|
||||
out[i * 4 + 2] = ctx->state[i] >> 8;
|
||||
out[i * 4 + 3] = ctx->state[i];
|
||||
}
|
||||
}
|
||||
|
||||
inline void _picohash_sha256_init(_picohash_sha256_ctx_t *ctx)
|
||||
{
|
||||
ctx->curlen = 0;
|
||||
ctx->length = 0;
|
||||
ctx->state[0] = 0x6A09E667UL;
|
||||
ctx->state[1] = 0xBB67AE85UL;
|
||||
ctx->state[2] = 0x3C6EF372UL;
|
||||
ctx->state[3] = 0xA54FF53AUL;
|
||||
ctx->state[4] = 0x510E527FUL;
|
||||
ctx->state[5] = 0x9B05688CUL;
|
||||
ctx->state[6] = 0x1F83D9ABUL;
|
||||
ctx->state[7] = 0x5BE0CD19UL;
|
||||
}
|
||||
|
||||
inline void _picohash_sha256_update(_picohash_sha256_ctx_t *ctx, const void *data, size_t len)
|
||||
{
|
||||
const unsigned char *in = data;
|
||||
size_t n;
|
||||
|
||||
while (len > 0) {
|
||||
if (ctx->curlen == 0 && len >= PICOHASH_SHA256_BLOCK_LENGTH) {
|
||||
_picohash_sha256_compress(ctx, (unsigned char *)in);
|
||||
ctx->length += PICOHASH_SHA256_BLOCK_LENGTH * 8;
|
||||
in += PICOHASH_SHA256_BLOCK_LENGTH;
|
||||
len -= PICOHASH_SHA256_BLOCK_LENGTH;
|
||||
} else {
|
||||
n = PICOHASH_SHA256_BLOCK_LENGTH - ctx->curlen;
|
||||
if (n > len)
|
||||
n = len;
|
||||
memcpy(ctx->buf + ctx->curlen, in, n);
|
||||
ctx->curlen += (uint32_t)n;
|
||||
in += n;
|
||||
len -= n;
|
||||
if (ctx->curlen == 64) {
|
||||
_picohash_sha256_compress(ctx, ctx->buf);
|
||||
ctx->length += 8 * PICOHASH_SHA256_BLOCK_LENGTH;
|
||||
ctx->curlen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void _picohash_sha256_final(_picohash_sha256_ctx_t *ctx, void *digest)
|
||||
{
|
||||
_picohash_sha256_do_final(ctx, digest, PICOHASH_SHA256_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
inline void _picohash_sha224_init(_picohash_sha256_ctx_t *ctx)
|
||||
{
|
||||
ctx->curlen = 0;
|
||||
ctx->length = 0;
|
||||
ctx->state[0] = 0xc1059ed8UL;
|
||||
ctx->state[1] = 0x367cd507UL;
|
||||
ctx->state[2] = 0x3070dd17UL;
|
||||
ctx->state[3] = 0xf70e5939UL;
|
||||
ctx->state[4] = 0xffc00b31UL;
|
||||
ctx->state[5] = 0x68581511UL;
|
||||
ctx->state[6] = 0x64f98fa7UL;
|
||||
ctx->state[7] = 0xbefa4fa4UL;
|
||||
}
|
||||
|
||||
inline void _picohash_sha224_final(_picohash_sha256_ctx_t *ctx, void *digest)
|
||||
{
|
||||
_picohash_sha256_do_final(ctx, digest, PICOHASH_SHA224_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
inline void picohash_init_md5(picohash_ctx_t *ctx)
|
||||
{
|
||||
ctx->block_length = PICOHASH_MD5_BLOCK_LENGTH;
|
||||
ctx->digest_length = PICOHASH_MD5_DIGEST_LENGTH;
|
||||
ctx->_reset = (void *)_picohash_md5_init;
|
||||
ctx->_update = (void *)_picohash_md5_update;
|
||||
ctx->_final = (void *)_picohash_md5_final;
|
||||
|
||||
_picohash_md5_init(&ctx->_md5);
|
||||
}
|
||||
|
||||
inline void picohash_init_sha1(picohash_ctx_t *ctx)
|
||||
{
|
||||
ctx->block_length = PICOHASH_SHA1_BLOCK_LENGTH;
|
||||
ctx->digest_length = PICOHASH_SHA1_DIGEST_LENGTH;
|
||||
ctx->_reset = (void *)_picohash_sha1_init;
|
||||
ctx->_update = (void *)_picohash_sha1_update;
|
||||
ctx->_final = (void *)_picohash_sha1_final;
|
||||
_picohash_sha1_init(&ctx->_sha1);
|
||||
}
|
||||
|
||||
inline void picohash_init_sha224(picohash_ctx_t *ctx)
|
||||
{
|
||||
ctx->block_length = PICOHASH_SHA224_BLOCK_LENGTH;
|
||||
ctx->digest_length = PICOHASH_SHA224_DIGEST_LENGTH;
|
||||
ctx->_reset = (void *)_picohash_sha224_init;
|
||||
ctx->_update = (void *)_picohash_sha256_update;
|
||||
ctx->_final = (void *)_picohash_sha224_final;
|
||||
_picohash_sha224_init(&ctx->_sha256);
|
||||
}
|
||||
|
||||
inline void picohash_init_sha256(picohash_ctx_t *ctx)
|
||||
{
|
||||
ctx->block_length = PICOHASH_SHA256_BLOCK_LENGTH;
|
||||
ctx->digest_length = PICOHASH_SHA256_DIGEST_LENGTH;
|
||||
ctx->_reset = (void *)_picohash_sha256_init;
|
||||
ctx->_update = (void *)_picohash_sha256_update;
|
||||
ctx->_final = (void *)_picohash_sha256_final;
|
||||
_picohash_sha256_init(&ctx->_sha256);
|
||||
}
|
||||
|
||||
inline void picohash_update(picohash_ctx_t *ctx, const void *input, size_t len)
|
||||
{
|
||||
ctx->_update(ctx, input, len);
|
||||
}
|
||||
|
||||
inline void picohash_final(picohash_ctx_t *ctx, void *digest)
|
||||
{
|
||||
ctx->_final(ctx, digest);
|
||||
}
|
||||
|
||||
inline void picohash_reset(picohash_ctx_t *ctx)
|
||||
{
|
||||
ctx->_reset(ctx);
|
||||
}
|
||||
|
||||
static inline void _picohash_hmac_apply_key(picohash_ctx_t *ctx, unsigned char delta)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i != ctx->block_length; ++i)
|
||||
ctx->_hmac.key[i] ^= delta;
|
||||
picohash_update(ctx, ctx->_hmac.key, ctx->block_length);
|
||||
for (i = 0; i != ctx->block_length; ++i)
|
||||
ctx->_hmac.key[i] ^= delta;
|
||||
}
|
||||
|
||||
static void _picohash_hmac_final(picohash_ctx_t *ctx, void *digest)
|
||||
{
|
||||
unsigned char inner_digest[PICOHASH_MAX_DIGEST_LENGTH];
|
||||
|
||||
ctx->_hmac.hash_final(ctx, inner_digest);
|
||||
|
||||
ctx->_hmac.hash_reset(ctx);
|
||||
_picohash_hmac_apply_key(ctx, 0x5c);
|
||||
picohash_update(ctx, inner_digest, ctx->digest_length);
|
||||
memset(inner_digest, 0, ctx->digest_length);
|
||||
|
||||
ctx->_hmac.hash_final(ctx, digest);
|
||||
}
|
||||
|
||||
static inline void _picohash_hmac_reset(picohash_ctx_t *ctx)
|
||||
{
|
||||
ctx->_hmac.hash_reset(ctx);
|
||||
_picohash_hmac_apply_key(ctx, 0x36);
|
||||
}
|
||||
|
||||
inline void picohash_init_hmac(picohash_ctx_t *ctx, void (*initf)(picohash_ctx_t *), const void *key, size_t key_len)
|
||||
{
|
||||
initf(ctx);
|
||||
|
||||
memset(ctx->_hmac.key, 0, ctx->block_length);
|
||||
if (key_len > ctx->block_length) {
|
||||
/* hash the key if it is too long */
|
||||
picohash_update(ctx, key, key_len);
|
||||
picohash_final(ctx, ctx->_hmac.key);
|
||||
ctx->_hmac.hash_reset(ctx);
|
||||
} else {
|
||||
memcpy(ctx->_hmac.key, key, key_len);
|
||||
}
|
||||
|
||||
/* replace reset and final function */
|
||||
ctx->_hmac.hash_reset = ctx->_reset;
|
||||
ctx->_hmac.hash_final = ctx->_final;
|
||||
ctx->_reset = (void *)_picohash_hmac_reset;
|
||||
ctx->_final = (void *)_picohash_hmac_final;
|
||||
|
||||
/* start calculating the inner hash */
|
||||
_picohash_hmac_apply_key(ctx, 0x36);
|
||||
}
|
||||
|
||||
#endif
|
||||
126
thirdparty/libjuice/src/random.c
vendored
Normal file
126
thirdparty/libjuice/src/random.c
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "random.h"
|
||||
#include "log.h"
|
||||
#include "thread.h" // for mutexes
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
// getrandom() is not available in Android NDK API < 28 and needs glibc >= 2.25
|
||||
#if defined(__linux__) && !defined(__ANDROID__) && (!defined(__GLIBC__) || __GLIBC__ > 2 || __GLIBC_MINOR__ >= 25)
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/random.h>
|
||||
|
||||
static int random_bytes(void *buf, size_t size) {
|
||||
ssize_t ret = getrandom(buf, size, 0);
|
||||
if (ret < 0) {
|
||||
JLOG_WARN("getrandom failed, errno=%d", errno);
|
||||
return -1;
|
||||
}
|
||||
if ((size_t)ret < size) {
|
||||
JLOG_WARN("getrandom returned too few bytes, size=%zu, returned=%zu", size, (size_t)ret);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0601 // Windows 7
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
//
|
||||
#include <bcrypt.h>
|
||||
|
||||
static int random_bytes(void *buf, size_t size) {
|
||||
// Requires Windows 7 or later
|
||||
NTSTATUS status = BCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||||
return !status ? 0 : -1;
|
||||
}
|
||||
|
||||
#else
|
||||
static int random_bytes(void *buf, size_t size) {
|
||||
(void)buf;
|
||||
(void)size;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned int generate_seed() {
|
||||
#ifdef _WIN32
|
||||
return (unsigned int)GetTickCount();
|
||||
#else
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) == 0)
|
||||
return (unsigned int)(ts.tv_sec ^ ts.tv_nsec);
|
||||
else
|
||||
return (unsigned int)time(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void juice_random(void *buf, size_t size) {
|
||||
if (random_bytes(buf, size) == 0)
|
||||
return;
|
||||
|
||||
// rand() is not thread-safe
|
||||
static mutex_t rand_mutex = MUTEX_INITIALIZER;
|
||||
mutex_lock(&rand_mutex);
|
||||
|
||||
static bool srandom_called = false;
|
||||
#if defined(__linux__) || defined(__unix__) || defined(__APPLE__)
|
||||
#define random_func random
|
||||
#define srandom_func srandom
|
||||
if (!srandom_called)
|
||||
JLOG_DEBUG("Using random() for random bytes");
|
||||
#else
|
||||
#define random_func rand
|
||||
#define srandom_func srand
|
||||
if (!srandom_called)
|
||||
JLOG_WARN("Falling back on rand() for random bytes");
|
||||
#endif
|
||||
if (!srandom_called) {
|
||||
srandom_func(generate_seed());
|
||||
srandom_called = true;
|
||||
}
|
||||
// RAND_MAX is guaranteed to be at least 2^15 - 1
|
||||
uint8_t *bytes = buf;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
bytes[i] = (uint8_t)((random_func() & 0x7f80) >> 7);
|
||||
|
||||
mutex_unlock(&rand_mutex);
|
||||
}
|
||||
|
||||
void juice_random_str64(char *buf, size_t size) {
|
||||
static const char chars64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
size_t i = 0;
|
||||
for (i = 0; i + 1 < size; ++i) {
|
||||
uint8_t byte = 0;
|
||||
juice_random(&byte, 1);
|
||||
buf[i] = chars64[byte & 0x3F];
|
||||
}
|
||||
buf[i] = '\0';
|
||||
}
|
||||
|
||||
uint32_t juice_rand32(void) {
|
||||
uint32_t r = 0;
|
||||
juice_random(&r, sizeof(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
uint64_t juice_rand64(void) {
|
||||
uint64_t r = 0;
|
||||
juice_random(&r, sizeof(r));
|
||||
return r;
|
||||
}
|
||||
21
thirdparty/libjuice/src/random.h
vendored
Normal file
21
thirdparty/libjuice/src/random.h
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_RANDOM_H
|
||||
#define JUICE_RANDOM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void juice_random(void *buf, size_t size);
|
||||
void juice_random_str64(char *buf, size_t size);
|
||||
|
||||
uint32_t juice_rand32(void);
|
||||
uint64_t juice_rand64(void);
|
||||
|
||||
#endif // JUICE_RANDOM_H
|
||||
1143
thirdparty/libjuice/src/server.c
vendored
Normal file
1143
thirdparty/libjuice/src/server.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
123
thirdparty/libjuice/src/server.h
vendored
Normal file
123
thirdparty/libjuice/src/server.h
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_SERVER_H
|
||||
#define JUICE_SERVER_H
|
||||
|
||||
#ifndef NO_SERVER
|
||||
|
||||
#include "addr.h"
|
||||
#include "juice.h"
|
||||
#include "socket.h"
|
||||
#include "stun.h"
|
||||
#include "thread.h"
|
||||
#include "timestamp.h"
|
||||
#include "turn.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define SERVER_DEFAULT_REALM "libjuice"
|
||||
#define SERVER_DEFAULT_MAX_ALLOCATIONS 1000 // should be 1024-1 or less to be safe for poll()
|
||||
#define SERVER_DEFAULT_MAX_PEERS 16
|
||||
|
||||
#define SERVER_NONCE_KEY_SIZE 32
|
||||
|
||||
// RFC 8656: The server [...] SHOULD expire the nonce at least once every hour during the lifetime
|
||||
// of the allocation
|
||||
#define SERVER_NONCE_KEY_LIFETIME 600 * 1000 // 10 min
|
||||
|
||||
typedef enum server_turn_alloc_state {
|
||||
SERVER_TURN_ALLOC_EMPTY,
|
||||
SERVER_TURN_ALLOC_DELETED,
|
||||
SERVER_TURN_ALLOC_FULL
|
||||
} server_turn_alloc_state_t;
|
||||
|
||||
typedef struct server_turn_alloc {
|
||||
server_turn_alloc_state_t state;
|
||||
addr_record_t record;
|
||||
juice_server_credentials_t *credentials;
|
||||
uint8_t transaction_id[STUN_TRANSACTION_ID_SIZE];
|
||||
timestamp_t timestamp;
|
||||
socket_t sock;
|
||||
turn_map_t map;
|
||||
} server_turn_alloc_t;
|
||||
|
||||
typedef struct juice_credentials_list {
|
||||
struct juice_credentials_list *next;
|
||||
juice_server_credentials_t credentials;
|
||||
uint8_t userhash[USERHASH_SIZE];
|
||||
timestamp_t timestamp;
|
||||
} juice_credentials_list_t;
|
||||
|
||||
typedef struct juice_server {
|
||||
juice_server_config_t config; // Note config.credentials will be empty
|
||||
juice_credentials_list_t *credentials; // Credentials are stored in this list
|
||||
uint8_t nonce_key[SERVER_NONCE_KEY_SIZE];
|
||||
timestamp_t nonce_key_timestamp;
|
||||
socket_t sock;
|
||||
thread_t thread;
|
||||
mutex_t mutex;
|
||||
bool thread_stopped;
|
||||
server_turn_alloc_t *allocs;
|
||||
int allocs_count;
|
||||
} juice_server_t;
|
||||
|
||||
juice_server_t *server_create(const juice_server_config_t *config);
|
||||
void server_do_destroy(juice_server_t *server);
|
||||
void server_destroy(juice_server_t *server);
|
||||
|
||||
uint16_t server_get_port(juice_server_t *server);
|
||||
int server_add_credentials(juice_server_t *server, const juice_server_credentials_t *credentials,
|
||||
timediff_t lifetime);
|
||||
|
||||
juice_credentials_list_t *server_do_add_credentials(juice_server_t *server,
|
||||
const juice_server_credentials_t *credentials,
|
||||
timediff_t lifetime); // internal
|
||||
|
||||
void server_run(juice_server_t *server);
|
||||
int server_send(juice_server_t *agent, const addr_record_t *dst, const char *data, size_t size);
|
||||
int server_stun_send(juice_server_t *server, const addr_record_t *dst, const stun_message_t *msg,
|
||||
const char *password // password may be NULL
|
||||
);
|
||||
int server_recv(juice_server_t *server);
|
||||
int server_forward(juice_server_t *server, server_turn_alloc_t *alloc);
|
||||
int server_input(juice_server_t *agent, char *buf, size_t len, const addr_record_t *src);
|
||||
int server_interrupt(juice_server_t *server);
|
||||
int server_bookkeeping(juice_server_t *agent, timestamp_t *next_timestamp);
|
||||
|
||||
void server_get_nonce(juice_server_t *server, const addr_record_t *src, char *nonce);
|
||||
void server_prepare_credentials(juice_server_t *server, const addr_record_t *src,
|
||||
const juice_server_credentials_t *credentials, stun_message_t *msg);
|
||||
|
||||
int server_dispatch_stun(juice_server_t *server, void *buf, size_t size, stun_message_t *msg,
|
||||
const addr_record_t *src);
|
||||
int server_answer_stun_binding(juice_server_t *server, const uint8_t *transaction_id,
|
||||
const addr_record_t *src);
|
||||
int server_answer_stun_error(juice_server_t *server, const uint8_t *transaction_id,
|
||||
const addr_record_t *src, stun_method_t method, unsigned int code,
|
||||
const juice_server_credentials_t *credentials);
|
||||
|
||||
int server_process_stun_binding(juice_server_t *server, const stun_message_t *msg,
|
||||
const addr_record_t *src);
|
||||
int server_process_turn_allocate(juice_server_t *server, const stun_message_t *msg,
|
||||
const addr_record_t *src, juice_server_credentials_t *credentials);
|
||||
int server_process_turn_create_permission(juice_server_t *server, const stun_message_t *msg,
|
||||
const addr_record_t *src,
|
||||
const juice_server_credentials_t *credentials);
|
||||
int server_process_turn_channel_bind(juice_server_t *server, const stun_message_t *msg,
|
||||
const addr_record_t *src,
|
||||
const juice_server_credentials_t *credentials);
|
||||
int server_process_turn_send(juice_server_t *server, const stun_message_t *msg,
|
||||
const addr_record_t *src);
|
||||
int server_process_channel_data(juice_server_t *server, char *buf, size_t len,
|
||||
const addr_record_t *src);
|
||||
|
||||
#endif // ifndef NO_SERVER
|
||||
|
||||
#endif
|
||||
132
thirdparty/libjuice/src/socket.h
vendored
Normal file
132
thirdparty/libjuice/src/socket.h
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_SOCKET_H
|
||||
#define JUICE_SOCKET_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0601 // Windows 7
|
||||
#endif
|
||||
#ifndef __MSVCRT_VERSION__
|
||||
#define __MSVCRT_VERSION__ 0x0601
|
||||
#endif
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
//
|
||||
#include <iphlpapi.h>
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#ifndef IPV6_V6ONLY
|
||||
#define IPV6_V6ONLY 27
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NO_IFADDRS
|
||||
#define NO_PMTUDISC
|
||||
|
||||
typedef SOCKET socket_t;
|
||||
typedef SOCKADDR sockaddr;
|
||||
typedef ULONG ctl_t;
|
||||
typedef DWORD sockopt_t;
|
||||
#define sockerrno ((int)WSAGetLastError())
|
||||
#define IP_DONTFRAG IP_DONTFRAGMENT
|
||||
#define HOST_NAME_MAX 256
|
||||
|
||||
#define poll WSAPoll
|
||||
typedef ULONG nfds_t;
|
||||
|
||||
#define SEADDRINUSE WSAEADDRINUSE
|
||||
#define SEINTR WSAEINTR
|
||||
#define SEAGAIN WSAEWOULDBLOCK
|
||||
#define SEACCES WSAEACCES
|
||||
#define SEWOULDBLOCK WSAEWOULDBLOCK
|
||||
#define SEINPROGRESS WSAEINPROGRESS
|
||||
#define SECONNREFUSED WSAECONNREFUSED
|
||||
#define SECONNRESET WSAECONNRESET
|
||||
#define SENETRESET WSAENETRESET
|
||||
#define SEMSGSIZE WSAEMSGSIZE
|
||||
|
||||
#else // assume POSIX
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef __linux__
|
||||
#define NO_PMTUDISC
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define NO_IFADDRS
|
||||
#else
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
typedef int socket_t;
|
||||
typedef int ctl_t;
|
||||
typedef int sockopt_t;
|
||||
#define sockerrno errno
|
||||
#define INVALID_SOCKET -1
|
||||
#define ioctlsocket ioctl
|
||||
#define closesocket close
|
||||
|
||||
#define SEADDRINUSE EADDRINUSE
|
||||
#define SEINTR EINTR
|
||||
#define SEAGAIN EAGAIN
|
||||
#define SEACCES EACCES
|
||||
#define SEWOULDBLOCK EWOULDBLOCK
|
||||
#define SEINPROGRESS EINPROGRESS
|
||||
#define SECONNREFUSED ECONNREFUSED
|
||||
#define SECONNRESET ECONNRESET
|
||||
#define SENETRESET ENETRESET
|
||||
#define SEMSGSIZE EMSGSIZE
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#ifndef IN6_IS_ADDR_LOOPBACK
|
||||
#define IN6_IS_ADDR_LOOPBACK(a) \
|
||||
(((const uint32_t *)(a))[0] == 0 && ((const uint32_t *)(a))[1] == 0 && \
|
||||
((const uint32_t *)(a))[2] == 0 && ((const uint32_t *)(a))[3] == htonl(1))
|
||||
#endif
|
||||
|
||||
#ifndef IN6_IS_ADDR_LINKLOCAL
|
||||
#define IN6_IS_ADDR_LINKLOCAL(a) \
|
||||
((((const uint32_t *)(a))[0] & htonl(0xffc00000)) == htonl(0xfe800000))
|
||||
#endif
|
||||
|
||||
#ifndef IN6_IS_ADDR_SITELOCAL
|
||||
#define IN6_IS_ADDR_SITELOCAL(a) \
|
||||
((((const uint32_t *)(a))[0] & htonl(0xffc00000)) == htonl(0xfec00000))
|
||||
#endif
|
||||
|
||||
#ifndef IN6_IS_ADDR_V4MAPPED
|
||||
#define IN6_IS_ADDR_V4MAPPED(a) \
|
||||
((((const uint32_t *)(a))[0] == 0) && (((const uint32_t *)(a))[1] == 0) && \
|
||||
(((const uint32_t *)(a))[2] == htonl(0xFFFF)))
|
||||
#endif
|
||||
|
||||
#endif // JUICE_SOCKET_H
|
||||
1236
thirdparty/libjuice/src/stun.c
vendored
Normal file
1236
thirdparty/libjuice/src/stun.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
376
thirdparty/libjuice/src/stun.h
vendored
Normal file
376
thirdparty/libjuice/src/stun.h
vendored
Normal file
@@ -0,0 +1,376 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_STUN_H
|
||||
#define JUICE_STUN_H
|
||||
|
||||
#include "juice.h"
|
||||
|
||||
#include "addr.h"
|
||||
#include "hash.h"
|
||||
#include "hmac.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
/*
|
||||
* STUN message header (20 bytes)
|
||||
* See https://www.rfc-editor.org/rfc/rfc8489.html#section-5
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |0 0| STUN Message Type | Message Length |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Magic Cookie = 0x2112A442 |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | Transaction ID (96 bits) |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
#define STUN_TRANSACTION_ID_SIZE 12
|
||||
|
||||
struct stun_header {
|
||||
uint16_t type;
|
||||
uint16_t length;
|
||||
uint32_t magic;
|
||||
uint8_t transaction_id[STUN_TRANSACTION_ID_SIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* Format of STUN Message Type Field
|
||||
*
|
||||
* 0 1
|
||||
* 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* +--+--+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |M |M |M|M|M|C|M|M|M|C|M|M|M|M|
|
||||
* |11|10|9|8|7|1|6|5|4|0|3|2|1|0|
|
||||
* +--+--+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* Request: C=b00
|
||||
* Indication: C=b01
|
||||
* Response: C=b10 (success)
|
||||
* C=b11 (error)
|
||||
*/
|
||||
#define STUN_CLASS_MASK 0x0110
|
||||
|
||||
typedef enum stun_class {
|
||||
STUN_CLASS_REQUEST = 0x0000,
|
||||
STUN_CLASS_INDICATION = 0x0010,
|
||||
STUN_CLASS_RESP_SUCCESS = 0x0100,
|
||||
STUN_CLASS_RESP_ERROR = 0x0110
|
||||
} stun_class_t;
|
||||
|
||||
typedef enum stun_method {
|
||||
STUN_METHOD_BINDING = 0x0001,
|
||||
|
||||
// Methods for TURN
|
||||
// See https://www.rfc-editor.org/rfc/rfc8656.html#section-17
|
||||
STUN_METHOD_ALLOCATE = 0x003,
|
||||
STUN_METHOD_REFRESH = 0x004,
|
||||
STUN_METHOD_SEND = 0x006,
|
||||
STUN_METHOD_DATA = 0x007,
|
||||
STUN_METHOD_CREATE_PERMISSION = 0x008,
|
||||
STUN_METHOD_CHANNEL_BIND = 0x009
|
||||
} stun_method_t;
|
||||
|
||||
#define STUN_IS_RESPONSE(msg_class) (msg_class & 0x0100)
|
||||
|
||||
/*
|
||||
* STUN attribute header
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Type | Length |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Value (variable) ...
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
struct stun_attr {
|
||||
uint16_t type;
|
||||
uint16_t length;
|
||||
uint8_t value[];
|
||||
};
|
||||
|
||||
typedef enum stun_attr_type {
|
||||
// Comprehension-required
|
||||
STUN_ATTR_MAPPED_ADDRESS = 0x0001,
|
||||
STUN_ATTR_USERNAME = 0x0006,
|
||||
STUN_ATTR_MESSAGE_INTEGRITY = 0x0008,
|
||||
STUN_ATTR_ERROR_CODE = 0x0009,
|
||||
STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000A,
|
||||
STUN_ATTR_REALM = 0x0014,
|
||||
STUN_ATTR_NONCE = 0x0015,
|
||||
STUN_ATTR_MESSAGE_INTEGRITY_SHA256 = 0x001C,
|
||||
STUN_ATTR_PASSWORD_ALGORITHM = 0x001D,
|
||||
STUN_ATTR_USERHASH = 0x001E,
|
||||
STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020,
|
||||
STUN_ATTR_PRIORITY = 0x0024,
|
||||
STUN_ATTR_USE_CANDIDATE = 0x0025,
|
||||
|
||||
// Comprehension-optional
|
||||
STUN_ATTR_PASSWORD_ALGORITHMS = 0x8002,
|
||||
STUN_ATTR_ALTERNATE_DOMAIN = 0x8003,
|
||||
STUN_ATTR_SOFTWARE = 0x8022,
|
||||
STUN_ATTR_ALTERNATE_SERVER = 0x8023,
|
||||
STUN_ATTR_FINGERPRINT = 0x8028,
|
||||
STUN_ATTR_ICE_CONTROLLED = 0x8029,
|
||||
STUN_ATTR_ICE_CONTROLLING = 0x802A,
|
||||
|
||||
// Attributes for TURN
|
||||
// See https://www.rfc-editor.org/rfc/rfc8656.html#section-18
|
||||
STUN_ATTR_CHANNEL_NUMBER = 0x000C,
|
||||
STUN_ATTR_LIFETIME = 0x000D,
|
||||
STUN_ATTR_XOR_PEER_ADDRESS = 0x0012,
|
||||
STUN_ATTR_DATA = 0x0013,
|
||||
STUN_ATTR_XOR_RELAYED_ADDRESS = 0x0016,
|
||||
STUN_ATTR_EVEN_PORT = 0x0018,
|
||||
STUN_ATTR_REQUESTED_TRANSPORT = 0x0019,
|
||||
STUN_ATTR_DONT_FRAGMENT = 0x001A,
|
||||
STUN_ATTR_RESERVATION_TOKEN = 0x0022
|
||||
} stun_attr_type_t;
|
||||
|
||||
#define STUN_IS_OPTIONAL_ATTR(attr_type) (attr_type & 0x8000)
|
||||
|
||||
/*
|
||||
* STUN attribute value for MAPPED-ADDRESS or XOR-MAPPED-ADDRESS
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |X X X X X X X X| Family | Port or X-Port |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | Address or X-Address (32 bits or 128 bits) |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
struct stun_value_mapped_address {
|
||||
uint8_t padding;
|
||||
uint8_t family;
|
||||
uint16_t port;
|
||||
uint8_t address[];
|
||||
};
|
||||
|
||||
typedef enum stun_address_family {
|
||||
STUN_ADDRESS_FAMILY_IPV4 = 0x01,
|
||||
STUN_ADDRESS_FAMILY_IPV6 = 0x02,
|
||||
} stun_address_family_t;
|
||||
|
||||
/*
|
||||
* STUN attribute value for ERROR-CODE
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Reserved, should be 0 |Class| Number |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Reason Phrase (variable) ...
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
struct stun_value_error_code {
|
||||
uint16_t reserved;
|
||||
uint8_t code_class; // lower 3 bits only, higher bits are reserved
|
||||
uint8_t code_number;
|
||||
uint8_t reason[];
|
||||
};
|
||||
|
||||
#define STUN_ERROR_INTERNAL_VALIDATION_FAILED 599
|
||||
|
||||
/*
|
||||
* STUN attribute for CHANNEL-NUMBER
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Channel Number | RFFU = 0 |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
struct stun_value_channel_number {
|
||||
uint16_t channel_number;
|
||||
uint16_t reserved;
|
||||
};
|
||||
|
||||
/*
|
||||
* STUN attribute for EVEN-PORT
|
||||
*
|
||||
* 0
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |R| RFFU |
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
struct stun_value_even_port {
|
||||
uint8_t r;
|
||||
};
|
||||
|
||||
/*
|
||||
* STUN attribute for REQUESTED-TRANSPORT
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Protocol | RFFU |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
struct stun_value_requested_transport {
|
||||
uint8_t protocol;
|
||||
uint8_t reserved1;
|
||||
uint16_t reserved2;
|
||||
};
|
||||
|
||||
/*
|
||||
* STUN attribute value for PASSWORD-ALGORITHM and PASSWORD-ALGORITHMS
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Algorithm 1 | Algorithm 1 Parameters Length |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Algorithm 1 Parameters (variable)
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Algorithm 2 | Algorithm 2 Parameters Length |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Algorithm 2 Parameters (variable)
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | ...
|
||||
*/
|
||||
struct stun_value_password_algorithm {
|
||||
uint16_t algorithm;
|
||||
uint16_t parameters_length;
|
||||
uint8_t parameters[];
|
||||
};
|
||||
|
||||
typedef enum stun_password_algorithm {
|
||||
STUN_PASSWORD_ALGORITHM_UNSET = 0x0000,
|
||||
STUN_PASSWORD_ALGORITHM_MD5 = 0x0001,
|
||||
STUN_PASSWORD_ALGORITHM_SHA256 = 0x0002,
|
||||
} stun_password_algorithm_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
// The value of USERNAME is a variable-length value. It MUST contain a UTF-8 [RFC3629] encoded
|
||||
// sequence of less than 513 bytes [...]
|
||||
#define STUN_MAX_USERNAME_LEN 513 + 1
|
||||
|
||||
// The REALM attribute [...] MUST be a UTF-8 [RFC3629] encoded sequence of less than 128 characters
|
||||
// (which can be as long as 763 bytes)
|
||||
#define STUN_MAX_REALM_LEN 763 + 1
|
||||
|
||||
// The NONCE attribute may be present in requests and responses. It [...] MUST be less than 128
|
||||
// characters (which can be as long as 763 bytes)
|
||||
#define STUN_MAX_NONCE_LEN 763 + 1
|
||||
|
||||
// The value of SOFTWARE is variable length. It MUST be a UTF-8 [RFC3629] encoded sequence of less
|
||||
// than 128 characters (which can be as long as 763 bytes)
|
||||
#define STUN_MAX_SOFTWARE_LEN 763 + 1
|
||||
|
||||
// The reason phrase MUST be a UTF-8-encoded [RFC3629] sequence of fewer than 128 characters (which
|
||||
// can be as long as 509 bytes when encoding them or 763 bytes when decoding them).
|
||||
#define STUN_MAX_ERROR_REASON_LEN 763 + 1
|
||||
|
||||
#define STUN_MAX_PASSWORD_LEN STUN_MAX_USERNAME_LEN
|
||||
|
||||
// Nonce cookie prefix as specified in https://www.rfc-editor.org/rfc/rfc8489.html#section-9.2
|
||||
#define STUN_NONCE_COOKIE "obMatJos2"
|
||||
#define STUN_NONCE_COOKIE_LEN 9
|
||||
|
||||
// USERHASH is a SHA256 digest
|
||||
#define USERHASH_SIZE HASH_SHA256_SIZE
|
||||
|
||||
// STUN Security Feature bits as defined in https://www.rfc-editor.org/rfc/rfc8489.html#section-18.1
|
||||
// See errata about bit order: https://www.rfc-editor.org/errata_search.php?rfc=8489
|
||||
// Bits are assigned starting from the least significant side of the bit set, so Bit 0 is the rightmost bit, and Bit 23 is the leftmost bit.
|
||||
// Bit 0: Password algorithms
|
||||
// Bit 1: Username anonymity
|
||||
// Bit 2-23: Unassigned
|
||||
|
||||
#define STUN_SECURITY_PASSWORD_ALGORITHMS_BIT 0x01
|
||||
#define STUN_SECURITY_USERNAME_ANONYMITY_BIT 0x02
|
||||
|
||||
#define STUN_MAX_PASSWORD_ALGORITHMS_VALUE_SIZE 256
|
||||
|
||||
typedef struct stun_credentials {
|
||||
char username[STUN_MAX_USERNAME_LEN];
|
||||
char realm[STUN_MAX_REALM_LEN];
|
||||
char nonce[STUN_MAX_NONCE_LEN];
|
||||
uint8_t userhash[USERHASH_SIZE];
|
||||
bool enable_userhash;
|
||||
stun_password_algorithm_t password_algorithm;
|
||||
uint8_t password_algorithms_value[STUN_MAX_PASSWORD_ALGORITHMS_VALUE_SIZE];
|
||||
size_t password_algorithms_value_size;
|
||||
} stun_credentials_t;
|
||||
|
||||
typedef struct stun_message {
|
||||
stun_class_t msg_class;
|
||||
stun_method_t msg_method;
|
||||
uint8_t transaction_id[STUN_TRANSACTION_ID_SIZE];
|
||||
unsigned int error_code;
|
||||
uint32_t priority;
|
||||
uint64_t ice_controlling;
|
||||
uint64_t ice_controlled;
|
||||
bool use_candidate;
|
||||
addr_record_t mapped;
|
||||
|
||||
stun_credentials_t credentials;
|
||||
|
||||
// Only for reading
|
||||
bool has_integrity;
|
||||
bool has_fingerprint;
|
||||
|
||||
// TURN
|
||||
addr_record_t peer;
|
||||
addr_record_t relayed;
|
||||
addr_record_t alternate_server;
|
||||
const char *data;
|
||||
size_t data_size;
|
||||
uint32_t lifetime;
|
||||
uint16_t channel_number;
|
||||
bool lifetime_set;
|
||||
bool even_port;
|
||||
bool next_port;
|
||||
bool dont_fragment;
|
||||
bool requested_transport;
|
||||
uint64_t reservation_token;
|
||||
|
||||
} stun_message_t;
|
||||
|
||||
int stun_write(void *buf, size_t size, const stun_message_t *msg,
|
||||
const char *password); // password may be NULL
|
||||
int stun_write_header(void *buf, size_t size, stun_class_t class, stun_method_t method,
|
||||
const uint8_t *transaction_id);
|
||||
size_t stun_update_header_length(void *buf, size_t length);
|
||||
int stun_write_attr(void *buf, size_t size, uint16_t type, const void *value, size_t length);
|
||||
int stun_write_value_mapped_address(void *buf, size_t size, const struct sockaddr *addr,
|
||||
socklen_t addrlen, const uint8_t *mask);
|
||||
|
||||
bool is_stun_datagram(const void *data, size_t size);
|
||||
|
||||
int stun_read(void *data, size_t size, stun_message_t *msg);
|
||||
int stun_read_attr(const void *data, size_t size, stun_message_t *msg, uint8_t *begin,
|
||||
uint8_t *attr_begin, uint32_t *security_bits);
|
||||
int stun_read_value_mapped_address(const void *data, size_t size, addr_record_t *mapped,
|
||||
const uint8_t *mask);
|
||||
|
||||
bool stun_check_integrity(void *buf, size_t size, const stun_message_t *msg, const char *password);
|
||||
|
||||
void stun_compute_userhash(const char *username, const char *realm, uint8_t *out);
|
||||
void stun_prepend_nonce_cookie(char *nonce);
|
||||
void stun_process_credentials(const stun_credentials_t *credentials, stun_credentials_t *dst);
|
||||
|
||||
const char *stun_get_error_reason(unsigned int code);
|
||||
|
||||
// Export for tests
|
||||
JUICE_EXPORT bool _juice_is_stun_datagram(const void *data, size_t size);
|
||||
JUICE_EXPORT int _juice_stun_read(void *data, size_t size, stun_message_t *msg);
|
||||
JUICE_EXPORT bool _juice_stun_check_integrity(void *buf, size_t size, const stun_message_t *msg,
|
||||
const char *password);
|
||||
|
||||
#endif
|
||||
116
thirdparty/libjuice/src/thread.h
vendored
Normal file
116
thirdparty/libjuice/src/thread.h
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_THREAD_H
|
||||
#define JUICE_THREAD_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0601 // Windows 7
|
||||
#endif
|
||||
#ifndef __MSVCRT_VERSION__
|
||||
#define __MSVCRT_VERSION__ 0x0601
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef HANDLE mutex_t;
|
||||
typedef HANDLE thread_t;
|
||||
typedef DWORD thread_return_t;
|
||||
#define THREAD_CALL __stdcall
|
||||
|
||||
#define MUTEX_INITIALIZER NULL
|
||||
|
||||
#define MUTEX_PLAIN 0x0
|
||||
#define MUTEX_RECURSIVE 0x0 // mutexes are recursive on Windows
|
||||
|
||||
static inline int mutex_init_impl(mutex_t *m) {
|
||||
return ((*(m) = CreateMutex(NULL, FALSE, NULL)) != NULL ? 0 : (int)GetLastError());
|
||||
}
|
||||
|
||||
static inline int mutex_lock_impl(volatile mutex_t *m) {
|
||||
// Atomically initialize the mutex on first lock
|
||||
if (*(m) == NULL) {
|
||||
HANDLE cm = CreateMutex(NULL, FALSE, NULL);
|
||||
if (cm == NULL)
|
||||
return (int)GetLastError();
|
||||
if (InterlockedCompareExchangePointer(m, cm, NULL) != NULL)
|
||||
CloseHandle(cm);
|
||||
}
|
||||
return WaitForSingleObject(*m, INFINITE) != WAIT_FAILED ? 0 : (int)GetLastError();
|
||||
}
|
||||
|
||||
#define mutex_init(m, flags) mutex_init_impl(m)
|
||||
#define mutex_lock(m) mutex_lock_impl(m)
|
||||
#define mutex_unlock(m) (void)ReleaseMutex(*(m))
|
||||
#define mutex_destroy(m) (void)CloseHandle(*(m))
|
||||
|
||||
static inline void thread_join_impl(thread_t t, thread_return_t *res) {
|
||||
WaitForSingleObject(t, INFINITE);
|
||||
if (res)
|
||||
GetExitCodeThread(t, res);
|
||||
CloseHandle(t);
|
||||
}
|
||||
|
||||
#define thread_init(t, func, arg) \
|
||||
((*(t) = CreateThread(NULL, 0, func, arg, 0, NULL)) != NULL ? 0 : (int)GetLastError())
|
||||
#define thread_join(t, res) thread_join_impl(t, res)
|
||||
|
||||
#else // POSIX
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef pthread_mutex_t mutex_t;
|
||||
typedef pthread_t thread_t;
|
||||
typedef void *thread_return_t;
|
||||
#define THREAD_CALL
|
||||
|
||||
#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
|
||||
|
||||
#define MUTEX_PLAIN PTHREAD_MUTEX_NORMAL
|
||||
#define MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE
|
||||
|
||||
static inline int mutex_init_impl(mutex_t *m, int flags) {
|
||||
pthread_mutexattr_t mutexattr;
|
||||
pthread_mutexattr_init(&mutexattr);
|
||||
pthread_mutexattr_settype(&mutexattr, flags);
|
||||
int ret = pthread_mutex_init(m, &mutexattr);
|
||||
pthread_mutexattr_destroy(&mutexattr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define mutex_init(m, flags) mutex_init_impl(m, flags)
|
||||
#define mutex_lock(m) pthread_mutex_lock(m)
|
||||
#define mutex_unlock(m) (void)pthread_mutex_unlock(m)
|
||||
#define mutex_destroy(m) (void)pthread_mutex_destroy(m)
|
||||
|
||||
#define thread_init(t, func, arg) pthread_create(t, NULL, func, arg)
|
||||
#define thread_join(t, res) (void)pthread_join(t, res)
|
||||
|
||||
#endif // ifdef _WIN32
|
||||
|
||||
#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
|
||||
|
||||
#include <stdatomic.h>
|
||||
#define atomic(T) _Atomic(T)
|
||||
#define atomic_ptr(T) _Atomic(T*)
|
||||
|
||||
#else // no atomics
|
||||
|
||||
// Since we don't need compare-and-swap, just assume store and load are atomic
|
||||
#define atomic(T) volatile T
|
||||
#define atomic_ptr(T) T* volatile
|
||||
#define atomic_store(a, v) (void)(*(a) = (v))
|
||||
#define atomic_load(a) (*(a))
|
||||
#define ATOMIC_VAR_INIT(v) (v)
|
||||
|
||||
#endif // if atomics
|
||||
|
||||
#endif // JUICE_THREAD_H
|
||||
|
||||
45
thirdparty/libjuice/src/timestamp.c
vendored
Normal file
45
thirdparty/libjuice/src/timestamp.c
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "timestamp.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
|
||||
// clock_gettime() is not implemented on older versions of OS X (< 10.12)
|
||||
#if defined(__APPLE__) && !defined(CLOCK_MONOTONIC)
|
||||
#include <sys/time.h>
|
||||
#define CLOCK_MONOTONIC 0
|
||||
int clock_gettime(int clk_id, struct timespec *t) {
|
||||
(void)clk_id;
|
||||
|
||||
// gettimeofday() does not return monotonic time but it should be good enough.
|
||||
struct timeval now;
|
||||
if (gettimeofday(&now, NULL))
|
||||
return -1;
|
||||
|
||||
t->tv_sec = now.tv_sec;
|
||||
t->tv_nsec = now.tv_usec * 1000;
|
||||
return 0;
|
||||
}
|
||||
#endif // defined(__APPLE__) && !defined(CLOCK_MONOTONIC)
|
||||
|
||||
#endif
|
||||
|
||||
timestamp_t current_timestamp() {
|
||||
#ifdef _WIN32
|
||||
return (timestamp_t)GetTickCount();
|
||||
#else // POSIX
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts))
|
||||
return 0;
|
||||
return (timestamp_t)ts.tv_sec * 1000 + (timestamp_t)ts.tv_nsec / 1000000;
|
||||
#endif
|
||||
}
|
||||
20
thirdparty/libjuice/src/timestamp.h
vendored
Normal file
20
thirdparty/libjuice/src/timestamp.h
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_TIMESTAMP_H
|
||||
#define JUICE_TIMESTAMP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef int64_t timestamp_t;
|
||||
typedef timestamp_t timediff_t;
|
||||
|
||||
timestamp_t current_timestamp();
|
||||
|
||||
#endif
|
||||
495
thirdparty/libjuice/src/turn.c
vendored
Normal file
495
thirdparty/libjuice/src/turn.c
vendored
Normal file
@@ -0,0 +1,495 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "turn.h"
|
||||
#include "log.h"
|
||||
#include "random.h"
|
||||
#include "socket.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static bool memory_is_zero(const void *data, size_t size) {
|
||||
const char *d = data;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
if (d[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t random_channel_number() {
|
||||
/*
|
||||
* RFC 8656 12. Channels
|
||||
* The ChannelData message (see Section 12.4) starts with a two-byte
|
||||
* field that carries the channel number. The values of this field are
|
||||
* allocated as follows:
|
||||
*
|
||||
* +------------------------+--------------------------------------+
|
||||
* | 0x0000 through 0x3FFF: | These values can never be used for |
|
||||
* | | channel numbers. |
|
||||
* +------------------------+--------------------------------------+
|
||||
* | 0x4000 through 0x4FFF: | These values are the allowed channel |
|
||||
* | | numbers (4096 possible values). |
|
||||
* +------------------------+--------------------------------------+
|
||||
* | 0x5000 through 0xFFFF: | Reserved (For DTLS-SRTP multiplexing |
|
||||
* | | collision avoidance, see [RFC7983]). |
|
||||
* +------------------------+--------------------------------------+
|
||||
*/
|
||||
uint16_t r;
|
||||
juice_random(&r, 2);
|
||||
return 0x4000 | (r & 0x0FFF);
|
||||
}
|
||||
|
||||
bool is_channel_data(const void *data, size_t size) {
|
||||
// According RFC 8656, first byte in [64..79] is TURN Channel
|
||||
if (size == 0)
|
||||
return false;
|
||||
uint8_t b = *((const uint8_t *)data);
|
||||
return b >= 64 && b <= 79;
|
||||
}
|
||||
|
||||
bool is_valid_channel(uint16_t channel) { return channel >= 0x4000; }
|
||||
|
||||
int turn_wrap_channel_data(char *buffer, size_t size, const char *data, size_t data_size,
|
||||
uint16_t channel) {
|
||||
if (!is_valid_channel(channel)) {
|
||||
JLOG_WARN("Invalid channel number: 0x%hX", channel);
|
||||
return -1;
|
||||
}
|
||||
if (data_size >= 65536) {
|
||||
JLOG_WARN("ChannelData is too long, size=%zu", size);
|
||||
return -1;
|
||||
}
|
||||
if (size < sizeof(struct channel_data_header) + data_size) {
|
||||
JLOG_WARN("Buffer is too small to add ChannelData header, size=%zu, needed=%zu", size,
|
||||
sizeof(struct channel_data_header) + data_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memmove(buffer + sizeof(struct channel_data_header), data, data_size);
|
||||
struct channel_data_header *header = (struct channel_data_header *)buffer;
|
||||
header->channel_number = htons((uint16_t)channel);
|
||||
header->length = htons((uint16_t)data_size);
|
||||
return (int)(sizeof(struct channel_data_header) + data_size);
|
||||
}
|
||||
|
||||
static int find_ordered_channel_rec(turn_entry_t *const ordered_channels[], uint16_t channel,
|
||||
int begin, int end) {
|
||||
int d = end - begin;
|
||||
if (d <= 0)
|
||||
return begin;
|
||||
|
||||
int pivot = begin + d / 2;
|
||||
const turn_entry_t *entry = ordered_channels[pivot];
|
||||
if (channel < entry->channel)
|
||||
return find_ordered_channel_rec(ordered_channels, channel, begin, pivot);
|
||||
else if (channel > entry->channel)
|
||||
return find_ordered_channel_rec(ordered_channels, channel, pivot + 1, end);
|
||||
else
|
||||
return pivot;
|
||||
}
|
||||
|
||||
static int find_ordered_channel(const turn_map_t *map, uint16_t channel) {
|
||||
return find_ordered_channel_rec(map->ordered_channels, channel, 0, map->channels_count);
|
||||
}
|
||||
|
||||
static int find_ordered_transaction_id_rec(turn_entry_t *const ordered_transaction_ids[],
|
||||
const uint8_t *transaction_id, int begin, int end) {
|
||||
int d = end - begin;
|
||||
if (d <= 0)
|
||||
return begin;
|
||||
|
||||
int pivot = begin + d / 2;
|
||||
const turn_entry_t *entry = ordered_transaction_ids[pivot];
|
||||
int ret = memcmp(transaction_id, entry->transaction_id, STUN_TRANSACTION_ID_SIZE);
|
||||
if (ret < 0)
|
||||
return find_ordered_transaction_id_rec(ordered_transaction_ids, transaction_id, begin,
|
||||
pivot);
|
||||
else if (ret > 0)
|
||||
return find_ordered_transaction_id_rec(ordered_transaction_ids, transaction_id, pivot + 1,
|
||||
end);
|
||||
else
|
||||
return pivot;
|
||||
}
|
||||
|
||||
static int find_ordered_transaction_id(const turn_map_t *map, const uint8_t *transaction_id) {
|
||||
return find_ordered_transaction_id_rec(map->ordered_transaction_ids, transaction_id, 0,
|
||||
map->transaction_ids_count);
|
||||
}
|
||||
|
||||
static void remove_ordered_transaction_id(turn_map_t *map, const uint8_t *transaction_id) {
|
||||
int pos = find_ordered_transaction_id(map, transaction_id);
|
||||
if (pos < map->transaction_ids_count) {
|
||||
memmove(map->ordered_transaction_ids + pos, map->ordered_transaction_ids + pos + 1,
|
||||
(map->transaction_ids_count - (pos + 1)) * sizeof(turn_entry_t *));
|
||||
map->transaction_ids_count--;
|
||||
}
|
||||
}
|
||||
/*
|
||||
static void remove_ordered_channel(turn_map_t *map, uint16_t channel) {
|
||||
int pos = find_ordered_channel(map, channel);
|
||||
if (pos < map->channels_count) {
|
||||
memmove(map->ordered_channels + pos, map->ordered_channels + pos + 1,
|
||||
(map->channels_count - (pos + 1)) * sizeof(turn_entry_t *));
|
||||
map->channels_count--;
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_entry(turn_map_t *map, turn_entry_t *entry) {
|
||||
if (entry->type == TURN_ENTRY_TYPE_EMPTY || entry->type == TURN_ENTRY_TYPE_DELETED)
|
||||
return;
|
||||
|
||||
if (!memory_is_zero(entry->transaction_id, STUN_TRANSACTION_ID_SIZE))
|
||||
remove_ordered_transaction_id(map, entry->transaction_id);
|
||||
|
||||
if (entry->type == TURN_ENTRY_TYPE_CHANNEL && entry->channel)
|
||||
remove_ordered_channel(map, entry->channel);
|
||||
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
entry->type = TURN_ENTRY_TYPE_DELETED;
|
||||
}
|
||||
*/
|
||||
static turn_entry_t *find_entry(turn_map_t *map, const addr_record_t *record,
|
||||
turn_entry_type_t type, bool allow_deleted) {
|
||||
unsigned long key = (addr_record_hash(record, false) + (int)type) % map->map_size;
|
||||
unsigned long pos = key;
|
||||
while (true) {
|
||||
turn_entry_t *entry = map->map + pos;
|
||||
if (entry->type == TURN_ENTRY_TYPE_EMPTY ||
|
||||
(entry->type == type && addr_record_is_equal(&entry->record, record, false)))
|
||||
break;
|
||||
|
||||
if (allow_deleted && entry->type == TURN_ENTRY_TYPE_DELETED)
|
||||
break;
|
||||
|
||||
pos = (pos + 1) % map->map_size;
|
||||
if (pos == key) {
|
||||
JLOG_VERBOSE("TURN map is full");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return map->map + pos;
|
||||
}
|
||||
|
||||
static bool update_timestamp(turn_map_t *map, turn_entry_type_t type, const uint8_t *transaction_id,
|
||||
const addr_record_t *record, timediff_t duration) {
|
||||
turn_entry_t *entry;
|
||||
if (record) {
|
||||
entry = find_entry(map, record, type, true);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
if (entry->type == type) {
|
||||
if (memcmp(entry->transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE) == 0)
|
||||
return true;
|
||||
} else {
|
||||
entry->type = type;
|
||||
entry->record = *record;
|
||||
}
|
||||
|
||||
if (!memory_is_zero(entry->transaction_id, STUN_TRANSACTION_ID_SIZE))
|
||||
remove_ordered_transaction_id(map, entry->transaction_id);
|
||||
|
||||
memcpy(entry->transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE);
|
||||
|
||||
} else {
|
||||
int pos = find_ordered_transaction_id(map, transaction_id);
|
||||
if (pos == map->transaction_ids_count)
|
||||
return false;
|
||||
|
||||
entry = map->ordered_transaction_ids[pos];
|
||||
if (entry->type != type ||
|
||||
memcmp(entry->transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
entry->timestamp = current_timestamp() + duration;
|
||||
entry->fresh_transaction_id = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int turn_init_map(turn_map_t *map, int size) {
|
||||
memset(map, 0, sizeof(*map));
|
||||
|
||||
map->map_size = size * 2;
|
||||
map->channels_count = 0;
|
||||
map->transaction_ids_count = 0;
|
||||
|
||||
map->map = calloc(map->map_size, sizeof(turn_entry_t));
|
||||
map->ordered_channels = calloc(map->map_size, sizeof(turn_entry_t *));
|
||||
map->ordered_transaction_ids = calloc(map->map_size, sizeof(turn_entry_t *));
|
||||
|
||||
if (!map->map || !map->ordered_channels || !map->ordered_transaction_ids) {
|
||||
JLOG_ERROR("Failed to allocate TURN map of size %d", size);
|
||||
turn_destroy_map(map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void turn_destroy_map(turn_map_t *map) {
|
||||
free(map->map);
|
||||
free(map->ordered_channels);
|
||||
free(map->ordered_transaction_ids);
|
||||
}
|
||||
|
||||
bool turn_set_permission(turn_map_t *map, const uint8_t *transaction_id,
|
||||
const addr_record_t *record, timediff_t duration) {
|
||||
return update_timestamp(map, TURN_ENTRY_TYPE_PERMISSION, transaction_id, record, duration);
|
||||
}
|
||||
|
||||
bool turn_has_permission(turn_map_t *map, const addr_record_t *record) {
|
||||
turn_entry_t *entry = find_entry(map, record, TURN_ENTRY_TYPE_PERMISSION, false);
|
||||
if (!entry || entry->type != TURN_ENTRY_TYPE_PERMISSION)
|
||||
return false;
|
||||
|
||||
return current_timestamp() < entry->timestamp;
|
||||
}
|
||||
|
||||
bool turn_bind_channel(turn_map_t *map, const addr_record_t *record, const uint8_t *transaction_id,
|
||||
uint16_t channel, timediff_t duration) {
|
||||
if (!is_valid_channel(channel)) {
|
||||
JLOG_ERROR("Invalid channel number: 0x%hX", channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
turn_entry_t *entry = find_entry(map, record, TURN_ENTRY_TYPE_CHANNEL, true);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
if (entry->type == TURN_ENTRY_TYPE_CHANNEL && entry->channel) {
|
||||
if (entry->channel != channel) {
|
||||
JLOG_WARN("The record is already bound to a channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
entry->timestamp = current_timestamp() + duration;
|
||||
return true;
|
||||
}
|
||||
|
||||
int pos = find_ordered_channel(map, channel);
|
||||
if (pos < map->channels_count) {
|
||||
const turn_entry_t *other_entry = map->ordered_channels[pos];
|
||||
if (other_entry->channel == channel) {
|
||||
JLOG_WARN("The channel is already bound to a record");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->type != TURN_ENTRY_TYPE_CHANNEL) {
|
||||
entry->type = TURN_ENTRY_TYPE_CHANNEL;
|
||||
entry->record = *record;
|
||||
}
|
||||
|
||||
memmove(map->ordered_channels + pos + 1, map->ordered_channels + pos,
|
||||
(map->channels_count - pos) * sizeof(turn_entry_t *));
|
||||
map->ordered_channels[pos] = entry;
|
||||
map->channels_count++;
|
||||
|
||||
entry->channel = channel;
|
||||
entry->timestamp = current_timestamp() + duration;
|
||||
|
||||
if (transaction_id) {
|
||||
memcpy(entry->transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE);
|
||||
entry->fresh_transaction_id = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool turn_bind_random_channel(turn_map_t *map, const addr_record_t *record, uint16_t *channel,
|
||||
timediff_t duration) {
|
||||
uint16_t c;
|
||||
do {
|
||||
c = random_channel_number();
|
||||
} while (turn_find_channel(map, c, NULL));
|
||||
|
||||
if (!turn_bind_channel(map, record, NULL, c, duration))
|
||||
return false;
|
||||
|
||||
if (channel)
|
||||
*channel = c;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool turn_bind_current_channel(turn_map_t *map, const uint8_t *transaction_id,
|
||||
const addr_record_t *record, timediff_t duration) {
|
||||
return update_timestamp(map, TURN_ENTRY_TYPE_CHANNEL, transaction_id, record, duration);
|
||||
}
|
||||
|
||||
bool turn_get_channel(turn_map_t *map, const addr_record_t *record, uint16_t *channel) {
|
||||
turn_entry_t *entry = find_entry(map, record, TURN_ENTRY_TYPE_CHANNEL, false);
|
||||
if (!entry || entry->type != TURN_ENTRY_TYPE_CHANNEL)
|
||||
return false;
|
||||
|
||||
if (channel)
|
||||
*channel = entry->channel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool turn_get_bound_channel(turn_map_t *map, const addr_record_t *record, uint16_t *channel) {
|
||||
turn_entry_t *entry = find_entry(map, record, TURN_ENTRY_TYPE_CHANNEL, false);
|
||||
if (!entry || entry->type != TURN_ENTRY_TYPE_CHANNEL)
|
||||
return false;
|
||||
|
||||
if (!entry->channel || current_timestamp() >= entry->timestamp)
|
||||
return false;
|
||||
|
||||
if (channel)
|
||||
*channel = entry->channel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool turn_find_channel(turn_map_t *map, uint16_t channel, addr_record_t *record) {
|
||||
if (!is_valid_channel(channel)) {
|
||||
JLOG_WARN("Invalid channel number: 0x%hX", channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
int pos = find_ordered_channel(map, channel);
|
||||
if (pos == map->channels_count)
|
||||
return false;
|
||||
|
||||
const turn_entry_t *entry = map->ordered_channels[pos];
|
||||
if (entry->channel != channel)
|
||||
return false;
|
||||
|
||||
if (record)
|
||||
*record = entry->record;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool turn_find_bound_channel(turn_map_t *map, uint16_t channel, addr_record_t *record) {
|
||||
if (!is_valid_channel(channel)) {
|
||||
JLOG_WARN("Invalid channel number: 0x%hX", channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
int pos = find_ordered_channel(map, channel);
|
||||
if (pos == map->channels_count)
|
||||
return false;
|
||||
|
||||
const turn_entry_t *entry = map->ordered_channels[pos];
|
||||
if (entry->channel != channel || current_timestamp() >= entry->timestamp)
|
||||
return false;
|
||||
|
||||
if (record)
|
||||
*record = entry->record;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool set_transaction_id(turn_map_t *map, turn_entry_type_t type, const addr_record_t *record,
|
||||
const uint8_t *transaction_id) {
|
||||
if (type != TURN_ENTRY_TYPE_PERMISSION && type != TURN_ENTRY_TYPE_CHANNEL)
|
||||
return false;
|
||||
|
||||
turn_entry_t *entry = find_entry(map, record, type, true);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
if (entry->type == type && !memory_is_zero(entry->transaction_id, STUN_TRANSACTION_ID_SIZE))
|
||||
remove_ordered_transaction_id(map, entry->transaction_id);
|
||||
|
||||
int pos = find_ordered_transaction_id(map, transaction_id);
|
||||
memmove(map->ordered_transaction_ids + pos + 1, map->ordered_transaction_ids + pos,
|
||||
(map->transaction_ids_count - pos) * sizeof(turn_entry_t *));
|
||||
map->ordered_transaction_ids[pos] = entry;
|
||||
map->transaction_ids_count++;
|
||||
|
||||
if (entry->type != type) {
|
||||
entry->type = type;
|
||||
entry->record = *record;
|
||||
}
|
||||
|
||||
memcpy(entry->transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE);
|
||||
entry->fresh_transaction_id = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool find_transaction_id(turn_map_t *map, const uint8_t *transaction_id,
|
||||
addr_record_t *record) {
|
||||
int pos = find_ordered_transaction_id(map, transaction_id);
|
||||
if (pos == map->transaction_ids_count)
|
||||
return false;
|
||||
|
||||
const turn_entry_t *entry = map->ordered_transaction_ids[pos];
|
||||
if (memcmp(entry->transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE) != 0)
|
||||
return false;
|
||||
|
||||
if (record)
|
||||
*record = entry->record;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool set_random_transaction_id(turn_map_t *map, turn_entry_type_t type,
|
||||
const addr_record_t *record, uint8_t *transaction_id) {
|
||||
turn_entry_t *entry = find_entry(map, record, type, false);
|
||||
if (entry && entry->fresh_transaction_id) {
|
||||
if (transaction_id)
|
||||
memcpy(transaction_id, entry->transaction_id, STUN_TRANSACTION_ID_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t tid[STUN_TRANSACTION_ID_SIZE];
|
||||
do {
|
||||
juice_random(tid, STUN_TRANSACTION_ID_SIZE);
|
||||
} while (find_transaction_id(map, tid, NULL));
|
||||
|
||||
if (!set_transaction_id(map, type, record, tid))
|
||||
return false;
|
||||
|
||||
if (transaction_id)
|
||||
memcpy(transaction_id, tid, STUN_TRANSACTION_ID_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool turn_set_permission_transaction_id(turn_map_t *map, const addr_record_t *record,
|
||||
const uint8_t *transaction_id) {
|
||||
return set_transaction_id(map, TURN_ENTRY_TYPE_PERMISSION, record, transaction_id);
|
||||
}
|
||||
|
||||
bool turn_set_channel_transaction_id(turn_map_t *map, const addr_record_t *record,
|
||||
const uint8_t *transaction_id) {
|
||||
return set_transaction_id(map, TURN_ENTRY_TYPE_CHANNEL, record, transaction_id);
|
||||
}
|
||||
|
||||
bool turn_set_random_permission_transaction_id(turn_map_t *map, const addr_record_t *record,
|
||||
uint8_t *transaction_id) {
|
||||
return set_random_transaction_id(map, TURN_ENTRY_TYPE_PERMISSION, record, transaction_id);
|
||||
}
|
||||
|
||||
bool turn_set_random_channel_transaction_id(turn_map_t *map, const addr_record_t *record,
|
||||
uint8_t *transaction_id) {
|
||||
return set_random_transaction_id(map, TURN_ENTRY_TYPE_CHANNEL, record, transaction_id);
|
||||
}
|
||||
|
||||
bool turn_retrieve_transaction_id(turn_map_t *map, const uint8_t *transaction_id,
|
||||
addr_record_t *record) {
|
||||
int pos = find_ordered_transaction_id(map, transaction_id);
|
||||
if (pos == map->transaction_ids_count)
|
||||
return false;
|
||||
|
||||
turn_entry_t *entry = map->ordered_transaction_ids[pos];
|
||||
if (memcmp(entry->transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE) != 0)
|
||||
return false;
|
||||
|
||||
if (record)
|
||||
*record = entry->record;
|
||||
|
||||
entry->fresh_transaction_id = false;
|
||||
return true;
|
||||
}
|
||||
111
thirdparty/libjuice/src/turn.h
vendored
Normal file
111
thirdparty/libjuice/src/turn.h
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_TURN_H
|
||||
#define JUICE_TURN_H
|
||||
|
||||
#include "addr.h"
|
||||
#include "ice.h"
|
||||
#include "juice.h"
|
||||
#include "log.h"
|
||||
#include "stun.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
/*
|
||||
* TURN ChannelData Message
|
||||
* See https://www.rfc-editor.org/rfc/rfc8656.html#section-12.4
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Channel Number | Length |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* / Application Data /
|
||||
* / /
|
||||
* | |
|
||||
* | +-------------------------------+
|
||||
* | |
|
||||
* +-------------------------------+
|
||||
*/
|
||||
|
||||
struct channel_data_header {
|
||||
uint16_t channel_number;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
bool is_channel_data(const void *data, size_t size);
|
||||
bool is_valid_channel(uint16_t channel);
|
||||
|
||||
int turn_wrap_channel_data(char *buffer, size_t size, const char *data, size_t data_size,
|
||||
uint16_t channel);
|
||||
|
||||
// TURN state map
|
||||
|
||||
typedef enum turn_entry_type {
|
||||
TURN_ENTRY_TYPE_EMPTY = 0,
|
||||
TURN_ENTRY_TYPE_DELETED,
|
||||
TURN_ENTRY_TYPE_PERMISSION,
|
||||
TURN_ENTRY_TYPE_CHANNEL
|
||||
} turn_entry_type_t;
|
||||
|
||||
typedef struct turn_entry {
|
||||
turn_entry_type_t type;
|
||||
timestamp_t timestamp;
|
||||
addr_record_t record;
|
||||
uint8_t transaction_id[STUN_TRANSACTION_ID_SIZE];
|
||||
uint16_t channel;
|
||||
bool fresh_transaction_id;
|
||||
} turn_entry_t;
|
||||
|
||||
typedef struct turn_map {
|
||||
turn_entry_t *map;
|
||||
turn_entry_t **ordered_channels;
|
||||
turn_entry_t **ordered_transaction_ids;
|
||||
int map_size;
|
||||
int channels_count;
|
||||
int transaction_ids_count;
|
||||
} turn_map_t;
|
||||
|
||||
int turn_init_map(turn_map_t *map, int size);
|
||||
void turn_destroy_map(turn_map_t *map);
|
||||
|
||||
bool turn_set_permission(turn_map_t *map, const uint8_t *transaction_id,
|
||||
const addr_record_t *record, // record may be NULL
|
||||
timediff_t duration);
|
||||
bool turn_has_permission(turn_map_t *map, const addr_record_t *record);
|
||||
|
||||
bool turn_bind_channel(turn_map_t *map, const addr_record_t *record,
|
||||
const uint8_t *transaction_id, // transaction_id may be NULL
|
||||
uint16_t channel, timediff_t duration);
|
||||
bool turn_bind_random_channel(turn_map_t *map, const addr_record_t *record, uint16_t *channel,
|
||||
timediff_t duration);
|
||||
bool turn_bind_current_channel(turn_map_t *map, const uint8_t *transaction_id,
|
||||
const addr_record_t *record, // record may be NULL
|
||||
timediff_t duration);
|
||||
bool turn_get_channel(turn_map_t *map, const addr_record_t *record, uint16_t *channel);
|
||||
bool turn_get_bound_channel(turn_map_t *map, const addr_record_t *record, uint16_t *channel);
|
||||
bool turn_find_channel(turn_map_t *map, uint16_t channel, addr_record_t *record);
|
||||
bool turn_find_bound_channel(turn_map_t *map, uint16_t channel, addr_record_t *record);
|
||||
|
||||
bool turn_set_permission_transaction_id(turn_map_t *map, const addr_record_t *record,
|
||||
const uint8_t *transaction_id);
|
||||
bool turn_set_channel_transaction_id(turn_map_t *map, const addr_record_t *record,
|
||||
const uint8_t *transaction_id);
|
||||
bool turn_set_random_permission_transaction_id(turn_map_t *map, const addr_record_t *record,
|
||||
uint8_t *transaction_id);
|
||||
bool turn_set_random_channel_transaction_id(turn_map_t *map, const addr_record_t *record,
|
||||
uint8_t *transaction_id);
|
||||
bool turn_retrieve_transaction_id(turn_map_t *map, const uint8_t *transaction_id,
|
||||
addr_record_t *record);
|
||||
#endif
|
||||
604
thirdparty/libjuice/src/udp.c
vendored
Normal file
604
thirdparty/libjuice/src/udp.c
vendored
Normal file
@@ -0,0 +1,604 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "udp.h"
|
||||
#include "addr.h"
|
||||
#include "log.h"
|
||||
#include "random.h"
|
||||
#include "thread.h" // for mutexes
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
static struct addrinfo *find_family(struct addrinfo *ai_list, int family) {
|
||||
struct addrinfo *ai = ai_list;
|
||||
while (ai && ai->ai_family != family)
|
||||
ai = ai->ai_next;
|
||||
return ai;
|
||||
}
|
||||
|
||||
static uint16_t get_next_port_in_range(uint16_t begin, uint16_t end) {
|
||||
if (begin == 0)
|
||||
begin = 1024;
|
||||
if (end == 0)
|
||||
end = 0xFFFF;
|
||||
if (begin == end)
|
||||
return begin;
|
||||
|
||||
static volatile uint32_t count = 0;
|
||||
if (count == 0)
|
||||
count = juice_rand32();
|
||||
|
||||
static mutex_t mutex = MUTEX_INITIALIZER;
|
||||
mutex_lock(&mutex);
|
||||
uint32_t diff = end > begin ? end - begin : 0;
|
||||
uint16_t next = begin + count++ % (diff + 1);
|
||||
mutex_unlock(&mutex);
|
||||
return next;
|
||||
}
|
||||
|
||||
socket_t udp_create_socket(const udp_socket_config_t *config) {
|
||||
socket_t sock = INVALID_SOCKET;
|
||||
|
||||
// Obtain local Address
|
||||
struct addrinfo *ai_list = NULL;
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
|
||||
if (getaddrinfo(config->bind_address, "0", &hints, &ai_list) != 0) {
|
||||
JLOG_ERROR("getaddrinfo for binding address failed, errno=%d", sockerrno);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
// Create socket
|
||||
struct addrinfo *ai = NULL;
|
||||
const int families[2] = {AF_INET6, AF_INET}; // Prefer IPv6
|
||||
const char *names[2] = {"IPv6", "IPv4"};
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ai = find_family(ai_list, families[i]);
|
||||
if (!ai)
|
||||
continue;
|
||||
|
||||
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
JLOG_WARN("UDP socket creation for %s family failed, errno=%d", names[i], sockerrno);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (sock == INVALID_SOCKET) {
|
||||
JLOG_ERROR("UDP socket creation failed: no suitable address family");
|
||||
goto error;
|
||||
}
|
||||
|
||||
assert(ai != NULL);
|
||||
|
||||
// Listen on both IPv6 and IPv4
|
||||
const sockopt_t disabled = 0;
|
||||
if (ai->ai_family == AF_INET6)
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&disabled, sizeof(disabled));
|
||||
|
||||
// Set DF flag
|
||||
#ifndef NO_PMTUDISC
|
||||
const sockopt_t val = IP_PMTUDISC_DO;
|
||||
setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, (const char *)&val, sizeof(val));
|
||||
#ifdef IPV6_MTU_DISCOVER
|
||||
if (ai->ai_family == AF_INET6)
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, (const char *)&val, sizeof(val));
|
||||
#endif
|
||||
#else
|
||||
// It seems Mac OS lacks a way to set the DF flag...
|
||||
const sockopt_t enabled = 1;
|
||||
#ifdef IP_DONTFRAG
|
||||
setsockopt(sock, IPPROTO_IP, IP_DONTFRAG, (const char *)&enabled, sizeof(enabled));
|
||||
#endif
|
||||
#ifdef IPV6_DONTFRAG
|
||||
if (ai->ai_family == AF_INET6)
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_DONTFRAG, (const char *)&enabled, sizeof(enabled));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Set buffer size up to 1 MiB for performance
|
||||
const sockopt_t buffer_size = 1 * 1024 * 1024;
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char *)&buffer_size, sizeof(buffer_size));
|
||||
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char *)&buffer_size, sizeof(buffer_size));
|
||||
|
||||
ctl_t nbio = 1;
|
||||
if (ioctlsocket(sock, FIONBIO, &nbio)) {
|
||||
JLOG_ERROR("Setting non-blocking mode on UDP socket failed, errno=%d", sockerrno);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Bind it
|
||||
if (config->port_begin == 0 && config->port_end == 0) {
|
||||
if (bind(sock, ai->ai_addr, (socklen_t)ai->ai_addrlen) == 0) {
|
||||
JLOG_DEBUG("UDP socket bound to %s:%hu",
|
||||
config->bind_address ? config->bind_address : "any", udp_get_port(sock));
|
||||
freeaddrinfo(ai_list);
|
||||
return sock;
|
||||
}
|
||||
|
||||
JLOG_ERROR("UDP socket binding failed, errno=%d", sockerrno);
|
||||
|
||||
} else if (config->port_begin == config->port_end) {
|
||||
uint16_t port = config->port_begin;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen = (socklen_t)ai->ai_addrlen;
|
||||
memcpy(&addr, ai->ai_addr, addrlen);
|
||||
addr_set_port((struct sockaddr *)&addr, port);
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&addr, addrlen) == 0) {
|
||||
JLOG_DEBUG("UDP socket bound to %s:%hu",
|
||||
config->bind_address ? config->bind_address : "any", port);
|
||||
freeaddrinfo(ai_list);
|
||||
return sock;
|
||||
}
|
||||
|
||||
JLOG_ERROR("UDP socket binding failed on port %hu, errno=%d", port, sockerrno);
|
||||
|
||||
} else {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen = (socklen_t)ai->ai_addrlen;
|
||||
memcpy(&addr, ai->ai_addr, addrlen);
|
||||
|
||||
int retries = config->port_end - config->port_begin;
|
||||
do {
|
||||
uint16_t port = get_next_port_in_range(config->port_begin, config->port_end);
|
||||
addr_set_port((struct sockaddr *)&addr, port);
|
||||
if (bind(sock, (struct sockaddr *)&addr, addrlen) == 0) {
|
||||
JLOG_DEBUG("UDP socket bound to %s:%hu",
|
||||
config->bind_address ? config->bind_address : "any", port);
|
||||
freeaddrinfo(ai_list);
|
||||
return sock;
|
||||
}
|
||||
} while ((sockerrno == SEADDRINUSE || sockerrno == SEACCES) && retries-- > 0);
|
||||
|
||||
JLOG_ERROR("UDP socket binding failed on port range %s:[%hu,%hu], errno=%d",
|
||||
config->bind_address ? config->bind_address : "any", config->port_begin,
|
||||
config->port_end, sockerrno);
|
||||
}
|
||||
|
||||
error:
|
||||
freeaddrinfo(ai_list);
|
||||
if (sock != INVALID_SOCKET)
|
||||
closesocket(sock);
|
||||
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
int udp_recvfrom(socket_t sock, char *buffer, size_t size, addr_record_t *src) {
|
||||
while (true) {
|
||||
src->len = sizeof(src->addr);
|
||||
int len =
|
||||
recvfrom(sock, buffer, (socklen_t)size, 0, (struct sockaddr *)&src->addr, &src->len);
|
||||
if (len >= 0) {
|
||||
addr_unmap_inet6_v4mapped((struct sockaddr *)&src->addr, &src->len);
|
||||
|
||||
} else if (sockerrno == SECONNRESET || sockerrno == SENETRESET ||
|
||||
sockerrno == SECONNREFUSED) {
|
||||
// On Windows, if a UDP socket receives an ICMP port unreachable response after
|
||||
// sending a datagram, this error is stored, and the next call to recvfrom() returns
|
||||
// WSAECONNRESET (port unreachable) or WSAENETRESET (TTL expired).
|
||||
// Therefore, it may be ignored.
|
||||
JLOG_DEBUG("Ignoring %s returned by recvfrom",
|
||||
sockerrno == SECONNRESET
|
||||
? "ECONNRESET"
|
||||
: (sockerrno == SENETRESET ? "ENETRESET" : "ECONNREFUSED"));
|
||||
continue;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
int udp_sendto(socket_t sock, const char *data, size_t size, const addr_record_t *dst) {
|
||||
#ifndef __linux__
|
||||
addr_record_t tmp = *dst;
|
||||
addr_record_t name;
|
||||
name.len = sizeof(name.addr);
|
||||
if (getsockname(sock, (struct sockaddr *)&name.addr, &name.len) == 0) {
|
||||
if (name.addr.ss_family == AF_INET6)
|
||||
addr_map_inet6_v4mapped(&tmp.addr, &tmp.len);
|
||||
} else {
|
||||
JLOG_WARN("getsockname failed, errno=%d", sockerrno);
|
||||
}
|
||||
return sendto(sock, data, (socklen_t)size, 0, (const struct sockaddr *)&tmp.addr, tmp.len);
|
||||
#else
|
||||
return sendto(sock, data, size, 0, (const struct sockaddr *)&dst->addr, dst->len);
|
||||
#endif
|
||||
}
|
||||
|
||||
int udp_sendto_self(socket_t sock, const char *data, size_t size) {
|
||||
addr_record_t local;
|
||||
if (udp_get_local_addr(sock, AF_UNSPEC, &local) < 0)
|
||||
return -1;
|
||||
|
||||
int ret;
|
||||
#ifndef __linux__
|
||||
// We know local has the same address family as sock here
|
||||
ret = sendto(sock, data, (socklen_t)size, 0, (const struct sockaddr *)&local.addr, local.len);
|
||||
#else
|
||||
ret = sendto(sock, data, size, 0, (const struct sockaddr *)&local.addr, local.len);
|
||||
#endif
|
||||
if (ret >= 0 || local.addr.ss_family != AF_INET6)
|
||||
return ret;
|
||||
|
||||
// Fallback as IPv6 may be disabled on the loopback interface
|
||||
if (udp_get_local_addr(sock, AF_INET, &local) < 0)
|
||||
return -1;
|
||||
|
||||
#ifndef __linux__
|
||||
addr_map_inet6_v4mapped(&local.addr, &local.len);
|
||||
return sendto(sock, data, (socklen_t)size, 0, (const struct sockaddr *)&local.addr, local.len);
|
||||
#else
|
||||
return sendto(sock, data, size, 0, (const struct sockaddr *)&local.addr, local.len);
|
||||
#endif
|
||||
}
|
||||
|
||||
int udp_set_diffserv(socket_t sock, int ds) {
|
||||
#ifdef _WIN32
|
||||
// IP_TOS has been intentionally broken on Windows in favor of a convoluted proprietary
|
||||
// mechanism called qWave. Thank you Microsoft!
|
||||
// TODO: Investigate if DSCP can be still set directly without administrator flow configuration.
|
||||
(void)sock;
|
||||
(void)ds;
|
||||
JLOG_INFO("IP Differentiated Services are not supported on Windows");
|
||||
return -1;
|
||||
#else
|
||||
addr_record_t name;
|
||||
name.len = sizeof(name.addr);
|
||||
if (getsockname(sock, (struct sockaddr *)&name.addr, &name.len) < 0) {
|
||||
JLOG_WARN("getsockname failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (name.addr.ss_family) {
|
||||
case AF_INET:
|
||||
#ifdef IP_TOS
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_TOS, &ds, sizeof(ds)) < 0) {
|
||||
JLOG_WARN("Setting IP ToS failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
JLOG_INFO("Setting IP ToS is not supported");
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
case AF_INET6:
|
||||
#ifdef IPV6_TCLASS
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, &ds, sizeof(ds)) < 0) {
|
||||
JLOG_WARN("Setting IPv6 traffic class failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
#ifdef IP_TOS
|
||||
// Attempt to also set IP_TOS for IPv4, in case the system requires it
|
||||
setsockopt(sock, IPPROTO_IP, IP_TOS, &ds, sizeof(ds));
|
||||
#endif
|
||||
return 0;
|
||||
#else
|
||||
JLOG_INFO("Setting IPv6 traffic class is not supported");
|
||||
return -1;
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t udp_get_port(socket_t sock) {
|
||||
addr_record_t record;
|
||||
if (udp_get_bound_addr(sock, &record) < 0)
|
||||
return 0;
|
||||
return addr_get_port((struct sockaddr *)&record.addr);
|
||||
}
|
||||
|
||||
int udp_get_bound_addr(socket_t sock, addr_record_t *record) {
|
||||
record->len = sizeof(record->addr);
|
||||
if (getsockname(sock, (struct sockaddr *)&record->addr, &record->len)) {
|
||||
JLOG_WARN("getsockname failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udp_get_local_addr(socket_t sock, int family_hint, addr_record_t *record) {
|
||||
if (udp_get_bound_addr(sock, record) < 0)
|
||||
return -1;
|
||||
|
||||
// If the socket is bound to a particular address, return it
|
||||
if (!addr_is_any((struct sockaddr *)&record->addr)) {
|
||||
if (record->addr.ss_family == AF_INET && family_hint == AF_INET6)
|
||||
addr_map_inet6_v4mapped(&record->addr, &record->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (record->addr.ss_family == AF_INET6 && family_hint == AF_INET) {
|
||||
// Generate an IPv4 instead (socket is listening to any IPv4 or IPv6)
|
||||
|
||||
uint16_t port = addr_get_port((struct sockaddr *)&record->addr);
|
||||
if (port == 0)
|
||||
return -1;
|
||||
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)&record->addr;
|
||||
memset(sin, 0, sizeof(*sin));
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_port = htons(port);
|
||||
record->len = sizeof(*sin);
|
||||
}
|
||||
|
||||
switch (record->addr.ss_family) {
|
||||
case AF_INET: {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)&record->addr;
|
||||
const uint8_t localhost[4] = {127, 0, 0, 1};
|
||||
memcpy(&sin->sin_addr, localhost, 4);
|
||||
break;
|
||||
}
|
||||
case AF_INET6: {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&record->addr;
|
||||
uint8_t *b = (uint8_t *)&sin6->sin6_addr;
|
||||
memset(b, 0, 15);
|
||||
b[15] = 0x01; // localhost
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
|
||||
if (record->addr.ss_family == AF_INET && family_hint == AF_INET6)
|
||||
addr_map_inet6_v4mapped(&record->addr, &record->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Helper function to check if a similar address already exists in records
|
||||
// This function ignores the port
|
||||
static int has_duplicate_addr(struct sockaddr *addr, const addr_record_t *records, size_t count) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const addr_record_t *record = records + i;
|
||||
if (record->addr.ss_family == addr->sa_family) {
|
||||
switch (addr->sa_family) {
|
||||
case AF_INET: {
|
||||
// For IPv4, compare the whole address
|
||||
const struct sockaddr_in *rsin = (const struct sockaddr_in *)&record->addr;
|
||||
const struct sockaddr_in *asin = (const struct sockaddr_in *)addr;
|
||||
if (memcmp(&rsin->sin_addr, &asin->sin_addr, 4) == 0)
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case AF_INET6: {
|
||||
// For IPv6, compare the network part only
|
||||
const struct sockaddr_in6 *rsin6 = (const struct sockaddr_in6 *)&record->addr;
|
||||
const struct sockaddr_in6 *asin6 = (const struct sockaddr_in6 *)addr;
|
||||
if (memcmp(&rsin6->sin6_addr, &asin6->sin6_addr, 8) == 0) // compare first 64 bits
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && defined(NO_IFADDRS)
|
||||
// Helper function to get the IPv6 address of the default interface
|
||||
static int get_local_default_inet6(uint16_t port, struct sockaddr_in6 *result) {
|
||||
const char *dummy_host = "2001:db8::1"; // dummy public unreachable address
|
||||
const uint16_t dummy_port = 9; // discard port
|
||||
|
||||
struct sockaddr_in6 sin6;
|
||||
memset(&sin6, 0, sizeof(sin6));
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_port = htons(dummy_port);
|
||||
if (inet_pton(AF_INET6, dummy_host, &sin6.sin6_addr) != 1)
|
||||
return -1;
|
||||
|
||||
socket_t sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == INVALID_SOCKET)
|
||||
return -1;
|
||||
|
||||
if (connect(sock, (const struct sockaddr *)&sin6, sizeof(sin6)))
|
||||
goto error;
|
||||
|
||||
socklen_t result_len = sizeof(*result);
|
||||
if (getsockname(sock, (struct sockaddr *)result, &result_len))
|
||||
goto error;
|
||||
|
||||
if (result_len != sizeof(*result))
|
||||
goto error;
|
||||
|
||||
addr_set_port((struct sockaddr *)result, port);
|
||||
closesocket(sock);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
closesocket(sock);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int udp_get_addrs(socket_t sock, addr_record_t *records, size_t count) {
|
||||
addr_record_t bound;
|
||||
if (udp_get_bound_addr(sock, &bound) < 0) {
|
||||
JLOG_ERROR("Getting UDP bound address failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!addr_is_any((struct sockaddr *)&bound.addr)) {
|
||||
if (count > 0)
|
||||
records[0] = bound;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t port = addr_get_port((struct sockaddr *)&bound.addr);
|
||||
|
||||
// RFC 8445 5.1.1.1. Host Candidates:
|
||||
// Addresses from a loopback interface MUST NOT be included in the candidate addresses.
|
||||
// [...]
|
||||
// If gathering one or more host candidates that correspond to an IPv6 address that was
|
||||
// generated using a mechanism that prevents location tracking [RFC7721], host candidates
|
||||
// that correspond to IPv6 addresses that do allow location tracking, are configured on the
|
||||
// same interface, and are part of the same network prefix MUST NOT be gathered. Similarly,
|
||||
// when host candidates corresponding to an IPv6 address generated using a mechanism that
|
||||
// prevents location tracking are gathered, then host candidates corresponding to IPv6
|
||||
// link-local addresses [RFC4291] MUST NOT be gathered. The IPv6 default address selection
|
||||
// specification [RFC6724] specifies that temporary addresses [RFC4941] are to be preferred
|
||||
// over permanent addresses.
|
||||
|
||||
// IPv6 IIDs generated by modern systems are opaque so there is no way to reliably differentiate
|
||||
// privacy-enabled IPv6 addresses here. Therefore, we hope the preferred addresses are listed
|
||||
// first, and we never list link-local addresses.
|
||||
|
||||
addr_record_t *current = records;
|
||||
addr_record_t *end = records + count;
|
||||
int ret = 0;
|
||||
|
||||
#if JUICE_ENABLE_LOCALHOST_ADDRESS
|
||||
// Add localhost for test purposes
|
||||
addr_record_t local;
|
||||
if (bound.addr.ss_family == AF_INET6 && udp_get_local_addr(sock, AF_INET6, &local) == 0) {
|
||||
++ret;
|
||||
if (current != end) {
|
||||
*current = local;
|
||||
++current;
|
||||
}
|
||||
}
|
||||
if (udp_get_local_addr(sock, AF_INET, &local) == 0) {
|
||||
++ret;
|
||||
if (current != end) {
|
||||
*current = local;
|
||||
++current;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
char buf[4096];
|
||||
DWORD len = 0;
|
||||
if (WSAIoctl(sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, buf, sizeof(buf), &len, NULL, NULL)) {
|
||||
JLOG_ERROR("WSAIoctl with SIO_ADDRESS_LIST_QUERY failed, errno=%d", WSAGetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
SOCKET_ADDRESS_LIST *list = (SOCKET_ADDRESS_LIST *)buf;
|
||||
for (int i = 0; i < list->iAddressCount; ++i) {
|
||||
struct sockaddr *sa = list->Address[i].lpSockaddr;
|
||||
socklen_t len = list->Address[i].iSockaddrLength;
|
||||
if ((sa->sa_family == AF_INET ||
|
||||
(sa->sa_family == AF_INET6 && bound.addr.ss_family == AF_INET6)) &&
|
||||
!addr_is_local(sa)) {
|
||||
if (!has_duplicate_addr(sa, records, current - records)) {
|
||||
++ret;
|
||||
if (current != end) {
|
||||
memcpy(¤t->addr, sa, len);
|
||||
current->len = len;
|
||||
addr_unmap_inet6_v4mapped((struct sockaddr *)¤t->addr, ¤t->len);
|
||||
addr_set_port((struct sockaddr *)¤t->addr, port);
|
||||
++current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // POSIX
|
||||
#ifndef NO_IFADDRS
|
||||
struct ifaddrs *ifas;
|
||||
if (getifaddrs(&ifas)) {
|
||||
JLOG_ERROR("getifaddrs failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (struct ifaddrs *ifa = ifas; ifa; ifa = ifa->ifa_next) {
|
||||
unsigned int flags = ifa->ifa_flags;
|
||||
if (!(flags & IFF_UP) || (flags & IFF_LOOPBACK))
|
||||
continue;
|
||||
if (strcmp(ifa->ifa_name, "docker0") == 0)
|
||||
continue;
|
||||
|
||||
struct sockaddr *sa = ifa->ifa_addr;
|
||||
socklen_t len;
|
||||
if (sa &&
|
||||
(sa->sa_family == AF_INET ||
|
||||
(sa->sa_family == AF_INET6 && bound.addr.ss_family == AF_INET6)) &&
|
||||
!addr_is_local(sa) && (len = addr_get_len(sa)) > 0) {
|
||||
if (!has_duplicate_addr(sa, records, current - records)) {
|
||||
++ret;
|
||||
if (current != end) {
|
||||
memcpy(¤t->addr, sa, len);
|
||||
current->len = len;
|
||||
addr_set_port((struct sockaddr *)¤t->addr, port);
|
||||
++current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifas);
|
||||
|
||||
#else // NO_IFADDRS defined
|
||||
char buf[4096];
|
||||
struct ifconf ifc;
|
||||
memset(&ifc, 0, sizeof(ifc));
|
||||
ifc.ifc_len = sizeof(buf);
|
||||
ifc.ifc_buf = buf;
|
||||
|
||||
if (ioctlsocket(sock, SIOCGIFCONF, &ifc)) {
|
||||
JLOG_ERROR("ioctl for SIOCGIFCONF failed, errno=%d", sockerrno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ifconf_has_inet6 = false;
|
||||
int n = ifc.ifc_len / sizeof(struct ifreq);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
struct ifreq *ifr = ifc.ifc_req + i;
|
||||
struct sockaddr *sa = &ifr->ifr_addr;
|
||||
if (sa->sa_family == AF_INET6)
|
||||
ifconf_has_inet6 = true;
|
||||
|
||||
socklen_t len;
|
||||
if ((sa->sa_family == AF_INET ||
|
||||
(sa->sa_family == AF_INET6 && bound.addr.ss_family == AF_INET6)) &&
|
||||
!addr_is_local(sa) && (len = addr_get_len(sa)) > 0) {
|
||||
if (!has_duplicate_addr(sa, records, current - records)) {
|
||||
++ret;
|
||||
if (current != end) {
|
||||
memcpy(¤t->addr, sa, len);
|
||||
current->len = len;
|
||||
addr_set_port((struct sockaddr *)¤t->addr, port);
|
||||
++current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ifconf_has_inet6 && bound.addr.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 sin6;
|
||||
if (get_local_default_inet6(port, &sin6) == 0) {
|
||||
if (!addr_is_local((const struct sockaddr *)&sin6)) {
|
||||
++ret;
|
||||
if (current != end) {
|
||||
memcpy(¤t->addr, &sin6, sizeof(sin6));
|
||||
current->len = sizeof(sin6);
|
||||
++current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
33
thirdparty/libjuice/src/udp.h
vendored
Normal file
33
thirdparty/libjuice/src/udp.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef JUICE_UDP_H
|
||||
#define JUICE_UDP_H
|
||||
|
||||
#include "addr.h"
|
||||
#include "socket.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct udp_socket_config {
|
||||
const char *bind_address;
|
||||
uint16_t port_begin;
|
||||
uint16_t port_end;
|
||||
} udp_socket_config_t;
|
||||
|
||||
socket_t udp_create_socket(const udp_socket_config_t *config);
|
||||
int udp_recvfrom(socket_t sock, char *buffer, size_t size, addr_record_t *src);
|
||||
int udp_sendto(socket_t sock, const char *data, size_t size, const addr_record_t *dst);
|
||||
int udp_sendto_self(socket_t sock, const char *data, size_t size);
|
||||
int udp_set_diffserv(socket_t sock, int ds);
|
||||
uint16_t udp_get_port(socket_t sock);
|
||||
int udp_get_bound_addr(socket_t sock, addr_record_t *record);
|
||||
int udp_get_local_addr(socket_t sock, int family, addr_record_t *record); // family may be AF_UNSPEC
|
||||
int udp_get_addrs(socket_t sock, addr_record_t *records, size_t count);
|
||||
|
||||
#endif // JUICE_UDP_H
|
||||
45
thirdparty/libjuice/test/base64.c
vendored
Normal file
45
thirdparty/libjuice/test/base64.c
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BUFFER_SIZE 1024
|
||||
|
||||
int test_base64(void) {
|
||||
const char *str = "Man is distinguished, not only by his reason, but by this singular passion "
|
||||
"from other animals, which is a lust of the mind, that by a perseverance of "
|
||||
"delight in the continued and indefatigable generation of knowledge, exceeds "
|
||||
"the short vehemence of any carnal pleasure.";
|
||||
const char *expected =
|
||||
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIH"
|
||||
"Bhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBw"
|
||||
"ZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb2"
|
||||
"4gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4"
|
||||
"=";
|
||||
|
||||
char buffer1[BUFFER_SIZE];
|
||||
if (BASE64_ENCODE(str, strlen(str), buffer1, BUFFER_SIZE) <= 0)
|
||||
return -1;
|
||||
|
||||
if (strcmp(buffer1, expected) != 0)
|
||||
return -1;
|
||||
|
||||
char buffer2[BUFFER_SIZE];
|
||||
int len = BASE64_DECODE(buffer1, buffer2, BUFFER_SIZE);
|
||||
if (len <= 0)
|
||||
return -1;
|
||||
|
||||
buffer2[len] = '\0';
|
||||
if (strcmp(buffer2, str) != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
225
thirdparty/libjuice/test/bind.c
vendored
Normal file
225
thirdparty/libjuice/test/bind.c
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#else
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
#define BIND_ADDRESS "127.0.0.1"
|
||||
|
||||
static juice_agent_t *agent1;
|
||||
static juice_agent_t *agent2;
|
||||
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr);
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr);
|
||||
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
|
||||
int test_bind() {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
|
||||
|
||||
// Agent 1: Create agent
|
||||
juice_config_t config1;
|
||||
memset(&config1, 0, sizeof(config1));
|
||||
|
||||
config1.bind_address = BIND_ADDRESS;
|
||||
|
||||
config1.cb_state_changed = on_state_changed1;
|
||||
config1.cb_candidate = on_candidate1;
|
||||
config1.cb_gathering_done = on_gathering_done1;
|
||||
config1.cb_recv = on_recv1;
|
||||
config1.user_ptr = NULL;
|
||||
|
||||
agent1 = juice_create(&config1);
|
||||
|
||||
// Agent 2: Create agent
|
||||
juice_config_t config2;
|
||||
memset(&config2, 0, sizeof(config2));
|
||||
|
||||
config2.bind_address = BIND_ADDRESS;
|
||||
|
||||
config2.cb_state_changed = on_state_changed2;
|
||||
config2.cb_candidate = on_candidate2;
|
||||
config2.cb_gathering_done = on_gathering_done2;
|
||||
config2.cb_recv = on_recv2;
|
||||
config2.user_ptr = NULL;
|
||||
|
||||
agent2 = juice_create(&config2);
|
||||
|
||||
// Agent 1: Generate local description
|
||||
char sdp1[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent1, sdp1, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 1:\n%s\n", sdp1);
|
||||
|
||||
// Agent 2: Receive description from agent 1
|
||||
juice_set_remote_description(agent2, sdp1);
|
||||
|
||||
// Agent 2: Generate local description
|
||||
char sdp2[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent2, sdp2, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 2:\n%s\n", sdp2);
|
||||
|
||||
// Agent 1: Receive description from agent 2
|
||||
juice_set_remote_description(agent1, sdp2);
|
||||
|
||||
// Agent 1: Gather candidates (and send them to agent 2)
|
||||
juice_gather_candidates(agent1);
|
||||
sleep(2);
|
||||
|
||||
// Agent 2: Gather candidates (and send them to agent 1)
|
||||
juice_gather_candidates(agent2);
|
||||
sleep(2);
|
||||
|
||||
// -- Connection should be finished --
|
||||
bool success = true;
|
||||
/*
|
||||
// Check states
|
||||
juice_state_t state1 = juice_get_state(agent1);
|
||||
juice_state_t state2 = juice_get_state(agent2);
|
||||
bool success = (state1 == JUICE_STATE_COMPLETED && state2 == JUICE_STATE_COMPLETED);
|
||||
*/
|
||||
// Retrieve candidates
|
||||
char local[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
char remote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent1, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 1: %s\n", local);
|
||||
printf("Remote candidate 1: %s\n", remote);
|
||||
}
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent2, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 2: %s\n", local);
|
||||
printf("Remote candidate 2: %s\n", remote);
|
||||
}
|
||||
|
||||
// Retrieve addresses
|
||||
char localAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
char remoteAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
if (success &= (juice_get_selected_addresses(agent1, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 1: %s\n", localAddr);
|
||||
printf("Remote address 1: %s\n", remoteAddr);
|
||||
}
|
||||
if (success &= (juice_get_selected_addresses(agent2, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 2: %s\n", localAddr);
|
||||
printf("Remote address 2: %s\n", remoteAddr);
|
||||
}
|
||||
|
||||
// Agent 1: destroy
|
||||
juice_destroy(agent1);
|
||||
|
||||
// Agent 2: destroy
|
||||
juice_destroy(agent2);
|
||||
|
||||
if (success) {
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("Failure\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on state changed
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 1: %s\n", juice_state_to_string(state));
|
||||
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 1: on connected, send a message
|
||||
const char *message = "Hello from 1";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 2: on state changed
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 2: %s\n", juice_state_to_string(state));
|
||||
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 2: on connected, send a message
|
||||
const char *message = "Hello from 2";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on local candidate gathered
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 1: %s\n", sdp);
|
||||
|
||||
// Filter host candidates for the bind address
|
||||
if(!strstr(sdp, "host") || !strstr(sdp, BIND_ADDRESS))
|
||||
return;
|
||||
|
||||
// Agent 2: Receive it from agent 1
|
||||
juice_add_remote_candidate(agent2, sdp);
|
||||
}
|
||||
|
||||
// Agent 2: on local candidate gathered
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 2: %s\n", sdp);
|
||||
|
||||
// Filter host candidates for the bind address
|
||||
if(!strstr(sdp, "host") || !strstr(sdp, BIND_ADDRESS))
|
||||
return;
|
||||
|
||||
// Agent 1: Receive it from agent 2
|
||||
juice_add_remote_candidate(agent1, sdp);
|
||||
}
|
||||
|
||||
// Agent 1: on local candidates gathering done
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 1\n");
|
||||
juice_set_remote_gathering_done(agent2); // optional
|
||||
}
|
||||
|
||||
// Agent 2: on local candidates gathering done
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 2\n");
|
||||
juice_set_remote_gathering_done(agent1); // optional
|
||||
}
|
||||
|
||||
// Agent 1: on message received
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 1: %s\n", buffer);
|
||||
}
|
||||
|
||||
// Agent 2: on message received
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 2: %s\n", buffer);
|
||||
}
|
||||
203
thirdparty/libjuice/test/conflict.c
vendored
Normal file
203
thirdparty/libjuice/test/conflict.c
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#else
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
static juice_agent_t *agent1;
|
||||
static juice_agent_t *agent2;
|
||||
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr);
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr);
|
||||
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
|
||||
int test_conflict() {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
|
||||
|
||||
// Agent 1: Create agent
|
||||
juice_config_t config1;
|
||||
memset(&config1, 0, sizeof(config1));
|
||||
config1.cb_state_changed = on_state_changed1;
|
||||
config1.cb_candidate = on_candidate1;
|
||||
config1.cb_gathering_done = on_gathering_done1;
|
||||
config1.cb_recv = on_recv1;
|
||||
config1.user_ptr = NULL;
|
||||
|
||||
agent1 = juice_create(&config1);
|
||||
|
||||
// Agent 2: Create agent
|
||||
juice_config_t config2;
|
||||
memset(&config2, 0, sizeof(config2));
|
||||
config2.cb_state_changed = on_state_changed2;
|
||||
config2.cb_candidate = on_candidate2;
|
||||
config2.cb_gathering_done = on_gathering_done2;
|
||||
config2.cb_recv = on_recv2;
|
||||
config2.user_ptr = NULL;
|
||||
|
||||
agent2 = juice_create(&config2);
|
||||
|
||||
// Agent 1: Generate local description
|
||||
char sdp1[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent1, sdp1, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 1:\n%s\n", sdp1);
|
||||
|
||||
// Agent 2: Generate local description
|
||||
char sdp2[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent2, sdp2, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 2:\n%s\n", sdp2);
|
||||
|
||||
// Setting the remote description now on both agents will hint them both into controlling mode,
|
||||
// creating an ICE role conflict
|
||||
|
||||
// Agent 1: Receive description from agent 2
|
||||
juice_set_remote_description(agent1, sdp2);
|
||||
|
||||
// Agent 2: Receive description from agent 1
|
||||
juice_set_remote_description(agent2, sdp1);
|
||||
|
||||
// Agent 1: Gather candidates (and send them to agent 2)
|
||||
juice_gather_candidates(agent1);
|
||||
sleep(2);
|
||||
|
||||
// Agent 2: Gather candidates (and send them to agent 1)
|
||||
juice_gather_candidates(agent2);
|
||||
sleep(2);
|
||||
|
||||
// -- Connection should be finished --
|
||||
|
||||
// Check states
|
||||
juice_state_t state1 = juice_get_state(agent1);
|
||||
juice_state_t state2 = juice_get_state(agent2);
|
||||
bool success = (state1 == JUICE_STATE_COMPLETED && state2 == JUICE_STATE_COMPLETED);
|
||||
|
||||
// Retrieve candidates
|
||||
char local[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
char remote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent1, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 1: %s\n", local);
|
||||
printf("Remote candidate 1: %s\n", remote);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent2, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 2: %s\n", local);
|
||||
printf("Remote candidate 2: %s\n", remote);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
|
||||
// Agent 1: destroy
|
||||
juice_destroy(agent1);
|
||||
|
||||
// Agent 2: destroy
|
||||
juice_destroy(agent2);
|
||||
|
||||
if (success) {
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("Failure\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on state changed
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 1: %s\n", juice_state_to_string(state));
|
||||
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 1: on connected, send a message
|
||||
const char *message = "Hello from 1";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 2: on state changed
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 2: %s\n", juice_state_to_string(state));
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 2: on connected, send a message
|
||||
const char *message = "Hello from 2";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on local candidate gathered
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 1: %s\n", sdp);
|
||||
|
||||
// Agent 2: Receive it from agent 1
|
||||
juice_add_remote_candidate(agent2, sdp);
|
||||
}
|
||||
|
||||
// Agent 2: on local candidate gathered
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 2: %s\n", sdp);
|
||||
|
||||
// Agent 1: Receive it from agent 2
|
||||
juice_add_remote_candidate(agent1, sdp);
|
||||
}
|
||||
|
||||
// Agent 1: on local candidates gathering done
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 1\n");
|
||||
juice_set_remote_gathering_done(agent2); // optional
|
||||
}
|
||||
|
||||
// Agent 2: on local candidates gathering done
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 2\n");
|
||||
juice_set_remote_gathering_done(agent1); // optional
|
||||
}
|
||||
|
||||
// Agent 1: on message received
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 1: %s\n", buffer);
|
||||
}
|
||||
|
||||
// Agent 2: on message received
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 2: %s\n", buffer);
|
||||
}
|
||||
228
thirdparty/libjuice/test/connectivity.c
vendored
Normal file
228
thirdparty/libjuice/test/connectivity.c
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#else
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
static juice_agent_t *agent1;
|
||||
static juice_agent_t *agent2;
|
||||
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr);
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr);
|
||||
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
|
||||
int test_connectivity() {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
|
||||
|
||||
// Agent 1: Create agent
|
||||
juice_config_t config1;
|
||||
memset(&config1, 0, sizeof(config1));
|
||||
|
||||
// STUN server example
|
||||
config1.stun_server_host = "stun.l.google.com";
|
||||
config1.stun_server_port = 19302;
|
||||
|
||||
config1.cb_state_changed = on_state_changed1;
|
||||
config1.cb_candidate = on_candidate1;
|
||||
config1.cb_gathering_done = on_gathering_done1;
|
||||
config1.cb_recv = on_recv1;
|
||||
config1.user_ptr = NULL;
|
||||
|
||||
agent1 = juice_create(&config1);
|
||||
|
||||
// Agent 2: Create agent
|
||||
juice_config_t config2;
|
||||
memset(&config2, 0, sizeof(config2));
|
||||
|
||||
// STUN server example
|
||||
config2.stun_server_host = "stun.l.google.com";
|
||||
config2.stun_server_port = 19302;
|
||||
|
||||
// Port range example
|
||||
config2.local_port_range_begin = 60000;
|
||||
config2.local_port_range_end = 61000;
|
||||
|
||||
config2.cb_state_changed = on_state_changed2;
|
||||
config2.cb_candidate = on_candidate2;
|
||||
config2.cb_gathering_done = on_gathering_done2;
|
||||
config2.cb_recv = on_recv2;
|
||||
config2.user_ptr = NULL;
|
||||
|
||||
agent2 = juice_create(&config2);
|
||||
|
||||
// Agent 1: Generate local description
|
||||
char sdp1[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent1, sdp1, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 1:\n%s\n", sdp1);
|
||||
|
||||
// Agent 2: Receive description from agent 1
|
||||
juice_set_remote_description(agent2, sdp1);
|
||||
|
||||
// Agent 2: Generate local description
|
||||
char sdp2[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent2, sdp2, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 2:\n%s\n", sdp2);
|
||||
|
||||
// Agent 1: Receive description from agent 2
|
||||
juice_set_remote_description(agent1, sdp2);
|
||||
|
||||
// Agent 1: Gather candidates (and send them to agent 2)
|
||||
juice_gather_candidates(agent1);
|
||||
sleep(2);
|
||||
|
||||
// Agent 2: Gather candidates (and send them to agent 1)
|
||||
juice_gather_candidates(agent2);
|
||||
sleep(2);
|
||||
|
||||
// -- Connection should be finished --
|
||||
|
||||
// Check states
|
||||
juice_state_t state1 = juice_get_state(agent1);
|
||||
juice_state_t state2 = juice_get_state(agent2);
|
||||
bool success = (state1 == JUICE_STATE_COMPLETED && state2 == JUICE_STATE_COMPLETED);
|
||||
|
||||
// Retrieve candidates
|
||||
char local[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
char remote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent1, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 1: %s\n", local);
|
||||
printf("Remote candidate 1: %s\n", remote);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent2, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 2: %s\n", local);
|
||||
printf("Remote candidate 2: %s\n", remote);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
|
||||
// Retrieve addresses
|
||||
char localAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
char remoteAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
if (success &= (juice_get_selected_addresses(agent1, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 1: %s\n", localAddr);
|
||||
printf("Remote address 1: %s\n", remoteAddr);
|
||||
}
|
||||
if (success &= (juice_get_selected_addresses(agent2, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 2: %s\n", localAddr);
|
||||
printf("Remote address 2: %s\n", remoteAddr);
|
||||
}
|
||||
|
||||
// Agent 1: destroy
|
||||
juice_destroy(agent1);
|
||||
|
||||
// Agent 2: destroy
|
||||
juice_destroy(agent2);
|
||||
|
||||
if (success) {
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("Failure\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on state changed
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 1: %s\n", juice_state_to_string(state));
|
||||
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 1: on connected, send a message
|
||||
const char *message = "Hello from 1";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 2: on state changed
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 2: %s\n", juice_state_to_string(state));
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 2: on connected, send a message
|
||||
const char *message = "Hello from 2";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on local candidate gathered
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 1: %s\n", sdp);
|
||||
|
||||
// Agent 2: Receive it from agent 1
|
||||
juice_add_remote_candidate(agent2, sdp);
|
||||
}
|
||||
|
||||
// Agent 2: on local candidate gathered
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 2: %s\n", sdp);
|
||||
|
||||
// Agent 1: Receive it from agent 2
|
||||
juice_add_remote_candidate(agent1, sdp);
|
||||
}
|
||||
|
||||
// Agent 1: on local candidates gathering done
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 1\n");
|
||||
juice_set_remote_gathering_done(agent2); // optional
|
||||
}
|
||||
|
||||
// Agent 2: on local candidates gathering done
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 2\n");
|
||||
juice_set_remote_gathering_done(agent1); // optional
|
||||
}
|
||||
|
||||
// Agent 1: on message received
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 1: %s\n", buffer);
|
||||
}
|
||||
|
||||
// Agent 2: on message received
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 2: %s\n", buffer);
|
||||
}
|
||||
22
thirdparty/libjuice/test/crc32.c
vendored
Normal file
22
thirdparty/libjuice/test/crc32.c
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "crc32.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
int test_crc32(void) {
|
||||
const char *str = "The quick brown fox jumps over the lazy dog";
|
||||
uint32_t expected = 0x414fa339;
|
||||
|
||||
if (CRC32(str, strlen(str)) != expected)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
95
thirdparty/libjuice/test/gathering.c
vendored
Normal file
95
thirdparty/libjuice/test/gathering.c
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#else
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
static juice_agent_t *agent;
|
||||
static bool success = false;
|
||||
static bool done = false;
|
||||
|
||||
static void on_state_changed(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
static void on_candidate(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
static void on_gathering_done(juice_agent_t *agent, void *user_ptr);
|
||||
|
||||
int test_gathering() {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
|
||||
|
||||
// Create agent
|
||||
juice_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
|
||||
// STUN server example
|
||||
config.stun_server_host = "stun.l.google.com";
|
||||
config.stun_server_port = 19302;
|
||||
|
||||
config.cb_state_changed = on_state_changed;
|
||||
config.cb_candidate = on_candidate;
|
||||
config.cb_gathering_done = on_gathering_done;
|
||||
config.user_ptr = NULL;
|
||||
|
||||
agent = juice_create(&config);
|
||||
|
||||
// Generate local description
|
||||
char sdp[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent, sdp, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description:\n%s\n", sdp);
|
||||
|
||||
// Gather candidates
|
||||
juice_gather_candidates(agent);
|
||||
|
||||
// Wait until gathering done
|
||||
int secs = 10;
|
||||
while (secs-- && !done && !success)
|
||||
sleep(1);
|
||||
|
||||
// Destroy
|
||||
juice_destroy(agent);
|
||||
|
||||
if (success) {
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("Failure\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// On state changed
|
||||
static void on_state_changed(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State: %s\n", juice_state_to_string(state));
|
||||
}
|
||||
|
||||
// On local candidate gathered
|
||||
static void on_candidate(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate: %s\n", sdp);
|
||||
|
||||
// Success if a valid srflx candidate is emitted
|
||||
if (strstr(sdp, " typ srflx raddr 0.0.0.0 rport 0"))
|
||||
success = true;
|
||||
}
|
||||
|
||||
// On local candidates gathering done
|
||||
static void on_gathering_done(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done\n");
|
||||
|
||||
done = true;
|
||||
}
|
||||
110
thirdparty/libjuice/test/main.c
vendored
Normal file
110
thirdparty/libjuice/test/main.c
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int test_crc32(void);
|
||||
int test_base64(void);
|
||||
int test_stun(void);
|
||||
int test_connectivity(void);
|
||||
int test_thread(void);
|
||||
int test_mux(void);
|
||||
int test_notrickle(void);
|
||||
int test_gathering(void);
|
||||
int test_turn(void);
|
||||
int test_conflict(void);
|
||||
int test_bind(void);
|
||||
|
||||
#ifndef NO_SERVER
|
||||
int test_server(void);
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_WARN);
|
||||
|
||||
printf("\nRunning CRC32 implementation test...\n");
|
||||
if (test_crc32()) {
|
||||
fprintf(stderr, "CRC32 implementation test failed\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
printf("\nRunning base64 implementation test...\n");
|
||||
if (test_base64()) {
|
||||
fprintf(stderr, "base64 implementation test failed\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
printf("\nRunning STUN parsing implementation test...\n");
|
||||
if (test_stun()) {
|
||||
fprintf(stderr, "STUN parsing implementation test failed\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
printf("\nRunning candidates gathering test...\n");
|
||||
if (test_gathering()) {
|
||||
fprintf(stderr, "Candidates gathering test failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\nRunning connectivity test...\n");
|
||||
if (test_connectivity()) {
|
||||
fprintf(stderr, "Connectivity test failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Disabled as the Open Relay TURN server is unreliable
|
||||
/*
|
||||
printf("\nRunning TURN connectivity test...\n");
|
||||
if (test_turn()) {
|
||||
fprintf(stderr, "TURN connectivity test failed\n");
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
printf("\nRunning thread-mode connectivity test...\n");
|
||||
if (test_thread()) {
|
||||
fprintf(stderr, "Thread-mode connectivity test failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\nRunning mux-mode connectivity test...\n");
|
||||
if (test_mux()) {
|
||||
fprintf(stderr, "Mux-mode connectivity test failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\nRunning non-trickled connectivity test...\n");
|
||||
if (test_notrickle()) {
|
||||
fprintf(stderr, "Non-trickled connectivity test failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\nRunning connectivity test with role conflict...\n");
|
||||
if (test_conflict()) {
|
||||
fprintf(stderr, "Connectivity test with role conflict failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\nRunning connectivity test with bind address...\n");
|
||||
if (test_bind()) {
|
||||
fprintf(stderr, "Connectivity test with bind address failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef NO_SERVER
|
||||
printf("\nRunning server test...\n");
|
||||
if (test_server()) {
|
||||
fprintf(stderr, "Server test failed\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
226
thirdparty/libjuice/test/mux.c
vendored
Normal file
226
thirdparty/libjuice/test/mux.c
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#else
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
static juice_agent_t *agent1;
|
||||
static juice_agent_t *agent2;
|
||||
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr);
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr);
|
||||
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
|
||||
bool endswith(const char *str, const char *suffix) {
|
||||
size_t str_len = strlen(str);
|
||||
size_t suffix_len = strlen(suffix);
|
||||
return str_len >= suffix_len && memcmp(str + str_len - suffix_len, suffix, suffix_len) == 0;
|
||||
}
|
||||
|
||||
int test_mux() {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
|
||||
|
||||
// Agent 1: Create agent
|
||||
juice_config_t config1;
|
||||
memset(&config1, 0, sizeof(config1));
|
||||
config1.stun_server_host = "stun.l.google.com";
|
||||
config1.stun_server_port = 19302;
|
||||
config1.cb_state_changed = on_state_changed1;
|
||||
config1.cb_candidate = on_candidate1;
|
||||
config1.cb_gathering_done = on_gathering_done1;
|
||||
config1.cb_recv = on_recv1;
|
||||
config1.user_ptr = NULL;
|
||||
agent1 = juice_create(&config1);
|
||||
|
||||
// Agent 2: Create agent in mux mode on port 60000
|
||||
juice_config_t config2;
|
||||
memset(&config2, 0, sizeof(config2));
|
||||
config2.concurrency_mode = JUICE_CONCURRENCY_MODE_MUX;
|
||||
config2.local_port_range_begin = 60000;
|
||||
config2.local_port_range_end = 60000;
|
||||
config2.cb_state_changed = on_state_changed1;
|
||||
config2.cb_candidate = on_candidate1;
|
||||
config2.cb_gathering_done = on_gathering_done1;
|
||||
config2.cb_recv = on_recv1;
|
||||
config2.user_ptr = NULL;
|
||||
|
||||
agent2 = juice_create(&config2);
|
||||
|
||||
// Agent 1: Generate local description
|
||||
char sdp1[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent1, sdp1, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 1:\n%s\n", sdp1);
|
||||
|
||||
// Agent 2: Receive description from agent 1
|
||||
juice_set_remote_description(agent2, sdp1);
|
||||
|
||||
// Agent 2: Generate local description
|
||||
char sdp2[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent2, sdp2, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 2:\n%s\n", sdp2);
|
||||
|
||||
// Agent 1: Receive description from agent 2
|
||||
juice_set_remote_description(agent1, sdp2);
|
||||
|
||||
// Agent 1: Gather candidates (and send them to agent 2)
|
||||
juice_gather_candidates(agent1);
|
||||
sleep(2);
|
||||
|
||||
// Agent 2: Gather candidates (and send them to agent 1)
|
||||
juice_gather_candidates(agent2);
|
||||
sleep(2);
|
||||
|
||||
// -- Connection should be finished --
|
||||
|
||||
// Check states
|
||||
juice_state_t state1 = juice_get_state(agent1);
|
||||
juice_state_t state2 = juice_get_state(agent2);
|
||||
bool success = (state1 == JUICE_STATE_COMPLETED && state2 == JUICE_STATE_COMPLETED);
|
||||
|
||||
// Retrieve candidates
|
||||
char local[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
char remote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent1, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 1: %s\n", local);
|
||||
printf("Remote candidate 1: %s\n", remote);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent2, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 2: %s\n", local);
|
||||
printf("Remote candidate 2: %s\n", remote);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
|
||||
// Retrieve addresses
|
||||
char localAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
char remoteAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
if (success &= (juice_get_selected_addresses(agent1, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 1: %s\n", localAddr);
|
||||
printf("Remote address 1: %s\n", remoteAddr);
|
||||
success &= endswith(remoteAddr, ":60000");
|
||||
}
|
||||
if (success &= (juice_get_selected_addresses(agent2, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 2: %s\n", localAddr);
|
||||
printf("Remote address 2: %s\n", remoteAddr);
|
||||
success &= endswith(localAddr, ":60000");
|
||||
}
|
||||
|
||||
// Agent 1: destroy
|
||||
juice_destroy(agent1);
|
||||
|
||||
// Agent 2: destroy
|
||||
juice_destroy(agent2);
|
||||
|
||||
if (success) {
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("Failure\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on state changed
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 1: %s\n", juice_state_to_string(state));
|
||||
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 1: on connected, send a message
|
||||
const char *message = "Hello from 1";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 2: on state changed
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 2: %s\n", juice_state_to_string(state));
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 2: on connected, send a message
|
||||
const char *message = "Hello from 2";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on local candidate gathered
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 1: %s\n", sdp);
|
||||
|
||||
// Agent 2: Receive it from agent 1
|
||||
juice_add_remote_candidate(agent2, sdp);
|
||||
}
|
||||
|
||||
// Agent 2: on local candidate gathered
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 2: %s\n", sdp);
|
||||
|
||||
// Agent 1: Receive it from agent 2
|
||||
juice_add_remote_candidate(agent1, sdp);
|
||||
}
|
||||
|
||||
// Agent 1: on local candidates gathering done
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 1\n");
|
||||
juice_set_remote_gathering_done(agent2); // optional
|
||||
}
|
||||
|
||||
// Agent 2: on local candidates gathering done
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 2\n");
|
||||
juice_set_remote_gathering_done(agent1); // optional
|
||||
}
|
||||
|
||||
// Agent 1: on message received
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 1: %s\n", buffer);
|
||||
}
|
||||
|
||||
// Agent 2: on message received
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 2: %s\n", buffer);
|
||||
}
|
||||
201
thirdparty/libjuice/test/notrickle.c
vendored
Normal file
201
thirdparty/libjuice/test/notrickle.c
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#else
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
static juice_agent_t *agent1;
|
||||
static juice_agent_t *agent2;
|
||||
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr);
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr);
|
||||
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
|
||||
int test_notrickle() {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
|
||||
|
||||
// Agent 1: Create agent
|
||||
juice_config_t config1;
|
||||
memset(&config1, 0, sizeof(config1));
|
||||
|
||||
// STUN server example
|
||||
config1.stun_server_host = "stun.l.google.com";
|
||||
config1.stun_server_port = 19302;
|
||||
|
||||
config1.cb_state_changed = on_state_changed1;
|
||||
config1.cb_gathering_done = on_gathering_done1;
|
||||
config1.cb_recv = on_recv1;
|
||||
config1.user_ptr = NULL;
|
||||
|
||||
agent1 = juice_create(&config1);
|
||||
|
||||
// Agent 2: Create agent
|
||||
juice_config_t config2;
|
||||
memset(&config2, 0, sizeof(config2));
|
||||
|
||||
// STUN server example
|
||||
config2.stun_server_host = "stun.l.google.com";
|
||||
config2.stun_server_port = 19302;
|
||||
|
||||
config2.cb_state_changed = on_state_changed2;
|
||||
config2.cb_gathering_done = on_gathering_done2;
|
||||
config2.cb_recv = on_recv2;
|
||||
config2.user_ptr = NULL;
|
||||
|
||||
agent2 = juice_create(&config2);
|
||||
|
||||
// Agent 1: Gather candidates
|
||||
juice_gather_candidates(agent1);
|
||||
|
||||
sleep(4);
|
||||
|
||||
// -- Connection should be finished --
|
||||
|
||||
// Check states
|
||||
juice_state_t state1 = juice_get_state(agent1);
|
||||
juice_state_t state2 = juice_get_state(agent2);
|
||||
bool success = (state1 == JUICE_STATE_COMPLETED && state2 == JUICE_STATE_COMPLETED);
|
||||
|
||||
// Retrieve candidates
|
||||
char local[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
char remote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent1, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 1: %s\n", local);
|
||||
printf("Remote candidate 1: %s\n", remote);
|
||||
}
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent2, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 2: %s\n", local);
|
||||
printf("Remote candidate 2: %s\n", remote);
|
||||
}
|
||||
|
||||
// Retrieve addresses
|
||||
char localAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
char remoteAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
if (success &= (juice_get_selected_addresses(agent1, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 1: %s\n", localAddr);
|
||||
printf("Remote address 1: %s\n", remoteAddr);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
if (success &= (juice_get_selected_addresses(agent2, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 2: %s\n", localAddr);
|
||||
printf("Remote address 2: %s\n", remoteAddr);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
|
||||
// Agent 1: destroy
|
||||
juice_destroy(agent1);
|
||||
|
||||
// Agent 2: destroy
|
||||
juice_destroy(agent2);
|
||||
|
||||
if (success) {
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("Failure\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on state changed
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 1: %s\n", juice_state_to_string(state));
|
||||
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 1: on connected, send a message
|
||||
const char *message = "Hello from 1";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 2: on state changed
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 2: %s\n", juice_state_to_string(state));
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 2: on connected, send a message
|
||||
const char *message = "Hello from 2";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on local candidates gathering done
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 1\n");
|
||||
|
||||
// Agent 1: Generate local description
|
||||
char sdp1[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent1, sdp1, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 1:\n%s\n", sdp1);
|
||||
|
||||
// Agent 2: Receive description from agent 1
|
||||
juice_set_remote_description(agent2, sdp1);
|
||||
|
||||
// Agent 2: Gather candidates
|
||||
juice_gather_candidates(agent2);
|
||||
}
|
||||
|
||||
// Agent 2: on local candidates gathering done
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 2\n");
|
||||
|
||||
// Agent 2: Generate local description
|
||||
char sdp2[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent2, sdp2, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 2:\n%s\n", sdp2);
|
||||
|
||||
// Agent 1: Receive description from agent 2
|
||||
juice_set_remote_description(agent1, sdp2);
|
||||
}
|
||||
|
||||
// Agent 1: on message received
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 1: %s\n", buffer);
|
||||
}
|
||||
|
||||
// Agent 2: on message received
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 2: %s\n", buffer);
|
||||
}
|
||||
274
thirdparty/libjuice/test/server.c
vendored
Normal file
274
thirdparty/libjuice/test/server.c
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef NO_SERVER
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#else
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
#define TURN_USERNAME1 "server_test1"
|
||||
#define TURN_PASSWORD1 "79874638521694"
|
||||
|
||||
#define TURN_USERNAME2 "server_test2"
|
||||
#define TURN_PASSWORD2 "36512189907731"
|
||||
|
||||
static juice_server_t *server;
|
||||
static juice_agent_t *agent1;
|
||||
static juice_agent_t *agent2;
|
||||
static bool srflx_success = false;
|
||||
static bool relay_success = false;
|
||||
static bool success = false;
|
||||
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr);
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr);
|
||||
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
|
||||
int test_server() {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
|
||||
|
||||
// Create server
|
||||
juice_server_credentials_t credentials[1];
|
||||
memset(&credentials, 0, sizeof(credentials));
|
||||
credentials[0].username = TURN_USERNAME1;
|
||||
credentials[0].password = TURN_PASSWORD1;
|
||||
|
||||
juice_server_config_t server_config;
|
||||
memset(&server_config, 0, sizeof(server_config));
|
||||
server_config.port = 3478;
|
||||
server_config.credentials = credentials;
|
||||
server_config.credentials_count = 1;
|
||||
server_config.max_allocations = 100;
|
||||
server_config.realm = "Juice test server";
|
||||
server = juice_server_create(&server_config);
|
||||
|
||||
if(juice_server_get_port(server) != 3478) {
|
||||
printf("juice_server_get_port failed\n");
|
||||
juice_server_destroy(server);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Added credentials example
|
||||
juice_server_credentials_t added_credentials[1];
|
||||
memset(&added_credentials, 0, sizeof(added_credentials));
|
||||
added_credentials[0].username = TURN_USERNAME2;
|
||||
added_credentials[0].password = TURN_PASSWORD2;
|
||||
juice_server_add_credentials(server, added_credentials, 60000); // 60s
|
||||
|
||||
// Agent 1: Create agent
|
||||
juice_config_t config1;
|
||||
memset(&config1, 0, sizeof(config1));
|
||||
|
||||
// Set STUN server
|
||||
config1.stun_server_host = "localhost";
|
||||
config1.stun_server_port = 3478;
|
||||
|
||||
// Set TURN server
|
||||
juice_turn_server_t turn_server1;
|
||||
memset(&turn_server1, 0, sizeof(turn_server1));
|
||||
turn_server1.host = "localhost";
|
||||
turn_server1.port = 3478;
|
||||
turn_server1.username = TURN_USERNAME1;
|
||||
turn_server1.password = TURN_PASSWORD1;
|
||||
config1.turn_servers = &turn_server1;
|
||||
config1.turn_servers_count = 1;
|
||||
|
||||
config1.cb_state_changed = on_state_changed1;
|
||||
config1.cb_candidate = on_candidate1;
|
||||
config1.cb_gathering_done = on_gathering_done1;
|
||||
config1.cb_recv = on_recv1;
|
||||
config1.user_ptr = NULL;
|
||||
|
||||
agent1 = juice_create(&config1);
|
||||
|
||||
// Agent 2: Create agent
|
||||
juice_config_t config2;
|
||||
memset(&config2, 0, sizeof(config2));
|
||||
|
||||
// Set STUN server
|
||||
config2.stun_server_host = "localhost";
|
||||
config2.stun_server_port = 3478;
|
||||
|
||||
// Set TURN server
|
||||
juice_turn_server_t turn_server2;
|
||||
memset(&turn_server2, 0, sizeof(turn_server2));
|
||||
turn_server2.host = "localhost";
|
||||
turn_server2.port = 3478;
|
||||
turn_server2.username = TURN_USERNAME2;
|
||||
turn_server2.password = TURN_PASSWORD2;
|
||||
config2.turn_servers = &turn_server2;
|
||||
config2.turn_servers_count = 1;
|
||||
|
||||
config2.cb_state_changed = on_state_changed2;
|
||||
config2.cb_candidate = on_candidate2;
|
||||
config2.cb_gathering_done = on_gathering_done2;
|
||||
config2.cb_recv = on_recv2;
|
||||
config2.user_ptr = NULL;
|
||||
|
||||
agent2 = juice_create(&config2);
|
||||
|
||||
// Agent 1: Generate local description
|
||||
char sdp1[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent1, sdp1, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 1:\n%s\n", sdp1);
|
||||
|
||||
// Agent 2: Receive description from agent 1
|
||||
juice_set_remote_description(agent2, sdp1);
|
||||
|
||||
// Agent 2: Generate local description
|
||||
char sdp2[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent2, sdp2, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 2:\n%s\n", sdp2);
|
||||
|
||||
// Agent 1: Receive description from agent 2
|
||||
juice_set_remote_description(agent1, sdp2);
|
||||
|
||||
// Agent 1: Gather candidates (and send them to agent 2)
|
||||
juice_gather_candidates(agent1);
|
||||
sleep(2);
|
||||
|
||||
// Agent 2: Gather candidates (and send them to agent 1)
|
||||
juice_gather_candidates(agent2);
|
||||
sleep(2);
|
||||
|
||||
// -- Connection should be finished --
|
||||
|
||||
// Agent 1: destroy
|
||||
juice_destroy(agent1);
|
||||
|
||||
// Agent 2: destroy
|
||||
juice_destroy(agent2);
|
||||
|
||||
// Destroy server
|
||||
juice_server_destroy(server);
|
||||
|
||||
if (srflx_success && relay_success && success) {
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("Failure\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on state changed
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 1: %s\n", juice_state_to_string(state));
|
||||
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 1: on connected, send a message
|
||||
const char *message = "Hello from 1";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 2: on state changed
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 2: %s\n", juice_state_to_string(state));
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 2: on connected, send a message
|
||||
const char *message = "Hello from 2";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on local candidate gathered
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 1: %s\n", sdp);
|
||||
|
||||
// Success if a valid srflx candidate is emitted
|
||||
if (strstr(sdp, " typ srflx raddr 0.0.0.0 rport 0"))
|
||||
srflx_success = true;
|
||||
|
||||
// Success if a valid relay candidate is emitted
|
||||
if (strstr(sdp, " typ relay raddr 0.0.0.0 rport 0"))
|
||||
relay_success = true;
|
||||
|
||||
// Filter relayed candidates
|
||||
if (!strstr(sdp, "relay"))
|
||||
return;
|
||||
|
||||
// Agent 2: Receive it from agent 1
|
||||
juice_add_remote_candidate(agent2, sdp);
|
||||
}
|
||||
|
||||
// Agent 2: on local candidate gathered
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 2: %s\n", sdp);
|
||||
|
||||
// Success if a valid srflx candidate is emitted
|
||||
if (strstr(sdp, " typ srflx raddr 0.0.0.0 rport 0"))
|
||||
srflx_success = true;
|
||||
|
||||
// Success if a valid relay candidate is emitted
|
||||
if (strstr(sdp, " typ relay raddr 0.0.0.0 rport 0"))
|
||||
relay_success = true;
|
||||
|
||||
// Filter relayed candidates
|
||||
if (!strstr(sdp, "relay"))
|
||||
return;
|
||||
|
||||
// Agent 1: Receive it from agent 2
|
||||
juice_add_remote_candidate(agent1, sdp);
|
||||
}
|
||||
|
||||
// Agent 1: on local candidates gathering done
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 1\n");
|
||||
juice_set_remote_gathering_done(agent2); // optional
|
||||
}
|
||||
|
||||
// Agent 2: on local candidates gathering done
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 2\n");
|
||||
juice_set_remote_gathering_done(agent1); // optional
|
||||
}
|
||||
|
||||
// Agent 1: on message received
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 1: %s\n", buffer);
|
||||
success = true;
|
||||
}
|
||||
|
||||
// Agent 2: on message received
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 2: %s\n", buffer);
|
||||
success = true;
|
||||
}
|
||||
|
||||
#endif // ifndef NO_SERVER
|
||||
155
thirdparty/libjuice/test/stun.c
vendored
Normal file
155
thirdparty/libjuice/test/stun.c
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stun.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
int test_stun(void) {
|
||||
stun_message_t msg;
|
||||
|
||||
uint8_t message1[] = {
|
||||
0x00, 0x01, 0x00, 0x58, // Request type and message length
|
||||
0x21, 0x12, 0xa4, 0x42, // Magic cookie
|
||||
0xb7, 0xe7, 0xa7, 0x01, // Transaction ID
|
||||
0xbc, 0x34, 0xd6, 0x86, //
|
||||
0xfa, 0x87, 0xdf, 0xae, //
|
||||
0x80, 0x22, 0x00, 0x10, // SOFTWARE attribute header
|
||||
0x53, 0x54, 0x55, 0x4e, //
|
||||
0x20, 0x74, 0x65, 0x73, //
|
||||
0x74, 0x20, 0x63, 0x6c, //
|
||||
0x69, 0x65, 0x6e, 0x74, //
|
||||
0x00, 0x24, 0x00, 0x04, // PRIORITY attribute header
|
||||
0x6e, 0x00, 0x01, 0xff, //
|
||||
0x80, 0x29, 0x00, 0x08, // ICE-CONTROLLED attribute header
|
||||
0x93, 0x2f, 0xf9, 0xb1, //
|
||||
0x51, 0x26, 0x3b, 0x36, //
|
||||
0x00, 0x06, 0x00, 0x09, // USERNAME attribute header
|
||||
0x65, 0x76, 0x74, 0x6a, //
|
||||
0x3a, 0x68, 0x36, 0x76, //
|
||||
0x59, 0x20, 0x20, 0x20, //
|
||||
0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY attribute header
|
||||
0x9a, 0xea, 0xa7, 0x0c, //
|
||||
0xbf, 0xd8, 0xcb, 0x56, //
|
||||
0x78, 0x1e, 0xf2, 0xb5, //
|
||||
0xb2, 0xd3, 0xf2, 0x49, //
|
||||
0xc1, 0xb5, 0x71, 0xa2, //
|
||||
0x80, 0x28, 0x00, 0x04, // FINGERPRINT attribute header
|
||||
0xe5, 0x7a, 0x3b, 0xcf, //
|
||||
};
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
if (_juice_stun_read(message1, sizeof(message1), &msg) <= 0)
|
||||
return -1;
|
||||
|
||||
if(msg.msg_class != STUN_CLASS_REQUEST || msg.msg_method != STUN_METHOD_BINDING)
|
||||
return -1;
|
||||
|
||||
if (memcmp(msg.transaction_id, message1 + 8, 12) != 0)
|
||||
return -1;
|
||||
|
||||
if (msg.priority != 0x6e0001ff)
|
||||
return -1;
|
||||
|
||||
if (msg.ice_controlled != 0x932ff9b151263b36LL)
|
||||
return -1;
|
||||
|
||||
if (!msg.has_integrity)
|
||||
return -1;
|
||||
|
||||
if (!_juice_stun_check_integrity(message1, sizeof(message1), &msg, "VOkJxbRl1RmTxUk/WvJxBt"))
|
||||
return -1;
|
||||
|
||||
if(msg.error_code != 0)
|
||||
return -1;
|
||||
|
||||
// The test vector in RFC 8489 is completely wrong
|
||||
// See https://www.rfc-editor.org/errata_search.php?rfc=8489
|
||||
uint8_t message2[] = {
|
||||
0x00, 0x01, 0x00, 0x90, // Request type and message length
|
||||
0x21, 0x12, 0xa4, 0x42, // Magic cookie
|
||||
0x78, 0xad, 0x34, 0x33, // Transaction ID
|
||||
0xc6, 0xad, 0x72, 0xc0, //
|
||||
0x29, 0xda, 0x41, 0x2e, //
|
||||
0x00, 0x1e, 0x00, 0x20, // USERHASH attribute header
|
||||
0x4a, 0x3c, 0xf3, 0x8f, // Userhash value (32 bytes)
|
||||
0xef, 0x69, 0x92, 0xbd, //
|
||||
0xa9, 0x52, 0xc6, 0x78, //
|
||||
0x04, 0x17, 0xda, 0x0f, //
|
||||
0x24, 0x81, 0x94, 0x15, //
|
||||
0x56, 0x9e, 0x60, 0xb2, //
|
||||
0x05, 0xc4, 0x6e, 0x41, //
|
||||
0x40, 0x7f, 0x17, 0x04, //
|
||||
0x00, 0x15, 0x00, 0x29, // NONCE attribute header
|
||||
0x6f, 0x62, 0x4d, 0x61, // Nonce value and padding (3 bytes)
|
||||
0x74, 0x4a, 0x6f, 0x73, //
|
||||
0x32, 0x41, 0x41, 0x41, //
|
||||
0x43, 0x66, 0x2f, 0x2f, //
|
||||
0x34, 0x39, 0x39, 0x6b, //
|
||||
0x39, 0x35, 0x34, 0x64, //
|
||||
0x36, 0x4f, 0x4c, 0x33, //
|
||||
0x34, 0x6f, 0x4c, 0x39, //
|
||||
0x46, 0x53, 0x54, 0x76, //
|
||||
0x79, 0x36, 0x34, 0x73, //
|
||||
0x41, 0x00, 0x00, 0x00, //
|
||||
0x00, 0x14, 0x00, 0x0b, // REALM attribute header
|
||||
0x65, 0x78, 0x61, 0x6d, // Realm value (11 bytes) and padding (1 byte)
|
||||
0x70, 0x6c, 0x65, 0x2e, //
|
||||
0x6f, 0x72, 0x67, 0x00, //
|
||||
0x00, 0x1d, 0x00, 0x04, // PASSWORD-ALGORITHM attribute header
|
||||
0x00, 0x02, 0x00, 0x00, // PASSWORD-ALGORITHM value (4 bytes)
|
||||
0x00, 0x1c, 0x00, 0x20, // MESSAGE-INTEGRITY-SHA256 attribute header
|
||||
0xb5, 0xc7, 0xbf, 0x00, // HMAC-SHA256 value
|
||||
0x5b, 0x6c, 0x52, 0xa2, //
|
||||
0x1c, 0x51, 0xc5, 0xe8, //
|
||||
0x92, 0xf8, 0x19, 0x24, //
|
||||
0x13, 0x62, 0x96, 0xcb, //
|
||||
0x92, 0x7c, 0x43, 0x14, //
|
||||
0x93, 0x09, 0x27, 0x8c, //
|
||||
0xc6, 0x51, 0x8e, 0x65, //
|
||||
};
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
if (_juice_stun_read(message2, sizeof(message2), &msg) <= 0)
|
||||
return -1;
|
||||
|
||||
if(msg.msg_class != STUN_CLASS_REQUEST || msg.msg_method != STUN_METHOD_BINDING)
|
||||
return -1;
|
||||
|
||||
if (memcmp(msg.transaction_id, message2 + 8, 12) != 0)
|
||||
return -1;
|
||||
|
||||
if (!msg.credentials.enable_userhash)
|
||||
return -1;
|
||||
|
||||
if (memcmp(msg.credentials.userhash, message2 + 24, 32) != 0)
|
||||
return -1;
|
||||
|
||||
if (strcmp(msg.credentials.realm, "example.org") != 0)
|
||||
return -1;
|
||||
|
||||
if (strcmp(msg.credentials.nonce, "obMatJos2AAACf//499k954d6OL34oL9FSTvy64sA") != 0)
|
||||
return -1;
|
||||
|
||||
if (!msg.has_integrity)
|
||||
return -1;
|
||||
|
||||
// Username is "<U+30DE><U+30C8><U+30EA><U+30C3><U+30AF><U+30B9>" or "マトリックス"
|
||||
// aka "The Matrix" in Japanese
|
||||
strcpy(msg.credentials.username, "マトリックス");
|
||||
if (!_juice_stun_check_integrity(message2, sizeof(message2), &msg, "TheMatrIX"))
|
||||
return -1;
|
||||
|
||||
if(msg.error_code != STUN_ERROR_INTERNAL_VALIDATION_FAILED)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
220
thirdparty/libjuice/test/thread.c
vendored
Normal file
220
thirdparty/libjuice/test/thread.c
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#else
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
static juice_agent_t *agent1;
|
||||
static juice_agent_t *agent2;
|
||||
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr);
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr);
|
||||
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
|
||||
int test_thread() {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
|
||||
|
||||
// Agent 1: Create agent in thread concurrency mode
|
||||
juice_config_t config1;
|
||||
memset(&config1, 0, sizeof(config1));
|
||||
config1.concurrency_mode = JUICE_CONCURRENCY_MODE_THREAD;
|
||||
config1.stun_server_host = "stun.l.google.com";
|
||||
config1.stun_server_port = 19302;
|
||||
config1.cb_state_changed = on_state_changed1;
|
||||
config1.cb_candidate = on_candidate1;
|
||||
config1.cb_gathering_done = on_gathering_done1;
|
||||
config1.cb_recv = on_recv1;
|
||||
config1.user_ptr = NULL;
|
||||
|
||||
agent1 = juice_create(&config1);
|
||||
|
||||
// Agent 2: Create agent in thread concurrency mode
|
||||
juice_config_t config2;
|
||||
memset(&config2, 0, sizeof(config2));
|
||||
config2.concurrency_mode = JUICE_CONCURRENCY_MODE_THREAD;
|
||||
config2.stun_server_host = "stun.l.google.com";
|
||||
config2.stun_server_port = 19302;
|
||||
config2.cb_state_changed = on_state_changed2;
|
||||
config2.cb_candidate = on_candidate2;
|
||||
config2.cb_gathering_done = on_gathering_done2;
|
||||
config2.cb_recv = on_recv2;
|
||||
config2.user_ptr = NULL;
|
||||
|
||||
agent2 = juice_create(&config2);
|
||||
|
||||
// Agent 1: Generate local description
|
||||
char sdp1[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent1, sdp1, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 1:\n%s\n", sdp1);
|
||||
|
||||
// Agent 2: Receive description from agent 1
|
||||
juice_set_remote_description(agent2, sdp1);
|
||||
|
||||
// Agent 2: Generate local description
|
||||
char sdp2[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent2, sdp2, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 2:\n%s\n", sdp2);
|
||||
|
||||
// Agent 1: Receive description from agent 2
|
||||
juice_set_remote_description(agent1, sdp2);
|
||||
|
||||
// Agent 1: Gather candidates (and send them to agent 2)
|
||||
juice_gather_candidates(agent1);
|
||||
sleep(2);
|
||||
|
||||
// Agent 2: Gather candidates (and send them to agent 1)
|
||||
juice_gather_candidates(agent2);
|
||||
sleep(2);
|
||||
|
||||
// -- Connection should be finished --
|
||||
|
||||
// Check states
|
||||
juice_state_t state1 = juice_get_state(agent1);
|
||||
juice_state_t state2 = juice_get_state(agent2);
|
||||
bool success = (state1 == JUICE_STATE_COMPLETED && state2 == JUICE_STATE_COMPLETED);
|
||||
|
||||
// Retrieve candidates
|
||||
char local[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
char remote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent1, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 1: %s\n", local);
|
||||
printf("Remote candidate 1: %s\n", remote);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent2, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 2: %s\n", local);
|
||||
printf("Remote candidate 2: %s\n", remote);
|
||||
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
|
||||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
|
||||
success = false; // local connection should be possible
|
||||
}
|
||||
|
||||
// Retrieve addresses
|
||||
char localAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
char remoteAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
if (success &= (juice_get_selected_addresses(agent1, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 1: %s\n", localAddr);
|
||||
printf("Remote address 1: %s\n", remoteAddr);
|
||||
}
|
||||
if (success &= (juice_get_selected_addresses(agent2, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 2: %s\n", localAddr);
|
||||
printf("Remote address 2: %s\n", remoteAddr);
|
||||
}
|
||||
|
||||
// Agent 1: destroy
|
||||
juice_destroy(agent1);
|
||||
|
||||
// Agent 2: destroy
|
||||
juice_destroy(agent2);
|
||||
|
||||
if (success) {
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("Failure\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on state changed
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 1: %s\n", juice_state_to_string(state));
|
||||
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 1: on connected, send a message
|
||||
const char *message = "Hello from 1";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 2: on state changed
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 2: %s\n", juice_state_to_string(state));
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 2: on connected, send a message
|
||||
const char *message = "Hello from 2";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on local candidate gathered
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 1: %s\n", sdp);
|
||||
|
||||
// Agent 2: Receive it from agent 1
|
||||
juice_add_remote_candidate(agent2, sdp);
|
||||
}
|
||||
|
||||
// Agent 2: on local candidate gathered
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
printf("Candidate 2: %s\n", sdp);
|
||||
|
||||
// Agent 1: Receive it from agent 2
|
||||
juice_add_remote_candidate(agent1, sdp);
|
||||
}
|
||||
|
||||
// Agent 1: on local candidates gathering done
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 1\n");
|
||||
juice_set_remote_gathering_done(agent2); // optional
|
||||
}
|
||||
|
||||
// Agent 2: on local candidates gathering done
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 2\n");
|
||||
juice_set_remote_gathering_done(agent1); // optional
|
||||
}
|
||||
|
||||
// Agent 1: on message received
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 1: %s\n", buffer);
|
||||
}
|
||||
|
||||
// Agent 2: on message received
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 2: %s\n", buffer);
|
||||
}
|
||||
241
thirdparty/libjuice/test/turn.c
vendored
Normal file
241
thirdparty/libjuice/test/turn.c
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "juice/juice.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||
#else
|
||||
#include <unistd.h> // for sleep
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
static juice_agent_t *agent1;
|
||||
static juice_agent_t *agent2;
|
||||
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr);
|
||||
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr);
|
||||
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr);
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr);
|
||||
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr);
|
||||
|
||||
int test_turn() {
|
||||
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
|
||||
|
||||
// Agent 1: Create agent
|
||||
juice_config_t config1;
|
||||
memset(&config1, 0, sizeof(config1));
|
||||
|
||||
// STUN server example (use your own server in production)
|
||||
config1.stun_server_host = "openrelay.metered.ca";
|
||||
config1.stun_server_port = 80;
|
||||
|
||||
// TURN server example (use your own server in production)
|
||||
juice_turn_server_t turn_server;
|
||||
memset(&turn_server, 0, sizeof(turn_server));
|
||||
turn_server.host = "openrelay.metered.ca";
|
||||
turn_server.port = 80;
|
||||
turn_server.username = "openrelayproject";
|
||||
turn_server.password = "openrelayproject";
|
||||
config1.turn_servers = &turn_server;
|
||||
config1.turn_servers_count = 1;
|
||||
|
||||
config1.cb_state_changed = on_state_changed1;
|
||||
config1.cb_candidate = on_candidate1;
|
||||
config1.cb_gathering_done = on_gathering_done1;
|
||||
config1.cb_recv = on_recv1;
|
||||
config1.user_ptr = NULL;
|
||||
|
||||
agent1 = juice_create(&config1);
|
||||
|
||||
// Agent 2: Create agent
|
||||
juice_config_t config2;
|
||||
memset(&config2, 0, sizeof(config2));
|
||||
|
||||
// STUN server example (use your own server in production)
|
||||
config2.stun_server_host = "openrelay.metered.ca";
|
||||
config2.stun_server_port = 80;
|
||||
|
||||
config2.cb_state_changed = on_state_changed2;
|
||||
config2.cb_candidate = on_candidate2;
|
||||
config2.cb_gathering_done = on_gathering_done2;
|
||||
config2.cb_recv = on_recv2;
|
||||
config2.user_ptr = NULL;
|
||||
|
||||
agent2 = juice_create(&config2);
|
||||
|
||||
// Agent 1: Generate local description
|
||||
char sdp1[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent1, sdp1, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 1:\n%s\n", sdp1);
|
||||
|
||||
// Agent 2: Receive description from agent 1
|
||||
juice_set_remote_description(agent2, sdp1);
|
||||
|
||||
// Agent 2: Generate local description
|
||||
char sdp2[JUICE_MAX_SDP_STRING_LEN];
|
||||
juice_get_local_description(agent2, sdp2, JUICE_MAX_SDP_STRING_LEN);
|
||||
printf("Local description 2:\n%s\n", sdp2);
|
||||
|
||||
// Agent 1: Receive description from agent 2
|
||||
juice_set_remote_description(agent1, sdp2);
|
||||
|
||||
// Agent 1: Gather candidates (and send them to agent 2)
|
||||
juice_gather_candidates(agent1);
|
||||
sleep(2);
|
||||
|
||||
// Agent 2: Gather candidates (and send them to agent 1)
|
||||
juice_gather_candidates(agent2);
|
||||
sleep(2);
|
||||
|
||||
// -- Connection should be finished --
|
||||
|
||||
// Check states
|
||||
juice_state_t state1 = juice_get_state(agent1);
|
||||
juice_state_t state2 = juice_get_state(agent2);
|
||||
bool success = ((state1 == JUICE_STATE_COMPLETED || state1 == JUICE_STATE_CONNECTED) &&
|
||||
(state2 == JUICE_STATE_CONNECTED || state2 == JUICE_STATE_COMPLETED));
|
||||
|
||||
// Retrieve candidates
|
||||
char local[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
char remote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent1, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 1: %s\n", local);
|
||||
printf("Remote candidate 1: %s\n", remote);
|
||||
|
||||
success &= (strstr(local, "relay") != NULL);
|
||||
}
|
||||
if (success &=
|
||||
(juice_get_selected_candidates(agent2, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote,
|
||||
JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) {
|
||||
printf("Local candidate 2: %s\n", local);
|
||||
printf("Remote candidate 2: %s\n", remote);
|
||||
|
||||
success &= (strstr(remote, "relay") != NULL);
|
||||
}
|
||||
|
||||
// Retrieve addresses
|
||||
char localAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
char remoteAddr[JUICE_MAX_ADDRESS_STRING_LEN];
|
||||
if (success &= (juice_get_selected_addresses(agent1, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 1: %s\n", localAddr);
|
||||
printf("Remote address 1: %s\n", remoteAddr);
|
||||
}
|
||||
if (success &= (juice_get_selected_addresses(agent2, localAddr, JUICE_MAX_ADDRESS_STRING_LEN,
|
||||
remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) {
|
||||
printf("Local address 2: %s\n", localAddr);
|
||||
printf("Remote address 2: %s\n", remoteAddr);
|
||||
}
|
||||
|
||||
// Agent 1: destroy
|
||||
juice_destroy(agent1);
|
||||
|
||||
// Agent 2: destroy
|
||||
juice_destroy(agent2);
|
||||
|
||||
if (success) {
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("Failure\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on state changed
|
||||
static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 1: %s\n", juice_state_to_string(state));
|
||||
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 1: on connected, send a message
|
||||
const char *message = "Hello from 1";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 2: on state changed
|
||||
static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||
printf("State 2: %s\n", juice_state_to_string(state));
|
||||
if (state == JUICE_STATE_CONNECTED) {
|
||||
// Agent 2: on connected, send a message
|
||||
const char *message = "Hello from 2";
|
||||
juice_send(agent, message, strlen(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Agent 1: on local candidate gathered
|
||||
static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
// Filter relayed candidates
|
||||
if (!strstr(sdp, "relay"))
|
||||
return;
|
||||
|
||||
printf("Candidate 1: %s\n", sdp);
|
||||
|
||||
// Agent 2: Receive it from agent 1
|
||||
juice_add_remote_candidate(agent2, sdp);
|
||||
}
|
||||
|
||||
// Agent 2: on local candidate gathered
|
||||
static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
||||
// Filter server reflexive candidates
|
||||
if (!strstr(sdp, "srflx"))
|
||||
return;
|
||||
|
||||
printf("Candidate 2: %s\n", sdp);
|
||||
|
||||
// Agent 1: Receive it from agent 2
|
||||
juice_add_remote_candidate(agent1, sdp);
|
||||
}
|
||||
|
||||
// Agent 1: on local candidates gathering done
|
||||
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 1\n");
|
||||
juice_set_remote_gathering_done(agent2); // optional
|
||||
}
|
||||
|
||||
// Agent 2: on local candidates gathering done
|
||||
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) {
|
||||
printf("Gathering done 2\n");
|
||||
juice_set_remote_gathering_done(agent1); // optional
|
||||
}
|
||||
|
||||
// Agent 1: on message received
|
||||
static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 1: %s\n", buffer);
|
||||
}
|
||||
|
||||
// Agent 2: on message received
|
||||
static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (size > BUFFER_SIZE - 1)
|
||||
size = BUFFER_SIZE - 1;
|
||||
memcpy(buffer, data, size);
|
||||
buffer[size] = '\0';
|
||||
printf("Received 2: %s\n", buffer);
|
||||
}
|
||||
12
thirdparty/libjuice/xmake.lua
vendored
Normal file
12
thirdparty/libjuice/xmake.lua
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package("libjuice")
|
||||
add_deps("cmake")
|
||||
set_sourcedir(path.join(os.scriptdir(), ""))
|
||||
on_install(function (package)
|
||||
local configs = {}
|
||||
table.insert(configs, "-DNO_EXPORT_HEADER=ON -DNO_TESTS=ON")
|
||||
import("package.tools.cmake").install(package, configs)
|
||||
end)
|
||||
-- on_test(function (package)
|
||||
-- assert(package:has_cfuncs("juice_create", {includes = "juice/juice.h"}))
|
||||
-- end)
|
||||
package_end()
|
||||
1
thirdparty/xmake.lua
vendored
Normal file
1
thirdparty/xmake.lua
vendored
Normal file
@@ -0,0 +1 @@
|
||||
includes("libjuice")
|
||||
@@ -7,6 +7,7 @@ set_languages("c++17")
|
||||
add_rules("mode.release", "mode.debug")
|
||||
|
||||
add_requires("asio 1.24.0", "nlohmann_json", "spdlog 1.11.0")
|
||||
add_requires("libjuice", {system = false})
|
||||
|
||||
add_defines("JUICE_STATIC")
|
||||
add_defines("ASIO_STANDALONE","_WEBSOCKETPP_CPP11_INTERNAL_", "ASIO_HAS_STD_TYPE_TRAITS", "ASIO_HAS_STD_SHARED_PTR",
|
||||
@@ -17,15 +18,15 @@ add_links("ws2_32", "Bcrypt")
|
||||
add_cxflags("-MD")
|
||||
add_packages("spdlog")
|
||||
|
||||
includes("thirdparty")
|
||||
|
||||
target("ice")
|
||||
set_kind("static")
|
||||
add_deps("log", "ws")
|
||||
add_packages("asio", "nlohmann_json")
|
||||
add_packages("asio", "nlohmann_json", "libjuice")
|
||||
add_files("src/ice/*.cpp")
|
||||
add_links("juice")
|
||||
add_includedirs("src/ws")
|
||||
add_includedirs("thirdparty/libjuice/include", {public = true})
|
||||
add_linkdirs("thirdparty/libjuice/lib")
|
||||
|
||||
target("ws")
|
||||
set_kind("static")
|
||||
|
||||
Reference in New Issue
Block a user