diff --git a/src/ice/libnice/ice_agent.cpp b/src/ice/ice_agent.cpp similarity index 100% rename from src/ice/libnice/ice_agent.cpp rename to src/ice/ice_agent.cpp diff --git a/src/ice/libnice/ice_agent.h b/src/ice/ice_agent.h similarity index 99% rename from src/ice/libnice/ice_agent.h rename to src/ice/ice_agent.h index 3f96aa4..d02166e 100644 --- a/src/ice/libnice/ice_agent.h +++ b/src/ice/ice_agent.h @@ -5,6 +5,7 @@ #include #include "gio/gnetworking.h" +#include "glib.h" #include "nice/agent.h" #define NICE_MAX_SDP_STRING_LEN 4096 diff --git a/src/ice/libjuice/ice_agent.cpp b/src/ice/libjuice/ice_agent.cpp deleted file mode 100644 index 8fc35e2..0000000 --- a/src/ice/libjuice/ice_agent.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "ice_agent.h" - -#include - -#include - -#include "log.h" - -IceAgent::IceAgent(bool offer_peer, std::string &stun_ip, uint16_t stun_port, - std::string &turn_ip, uint16_t turn_port, - std::string &turn_username, std::string &turn_password) - : stun_ip_(stun_ip), - stun_port_(stun_port), - turn_ip_(turn_ip), - turn_port_(turn_port), - turn_username_(turn_username), - turn_password_(turn_password) {} - -IceAgent::~IceAgent() {} - -int IceAgent::CreateIceAgent(juice_cb_state_changed_t on_state_changed, - juice_cb_candidate_t on_candidate, - juice_cb_gathering_done_t on_gathering_done, - juice_cb_recv_t on_recv, void *user_ptr) { - // juice_set_log_level(JUICE_LOG_LEVEL_DEBUG); - - juice_set_log_handler([](juice_log_level_t level, const char *message) { - if (JUICE_LOG_LEVEL_VERBOSE == level) { - LOG_INFO("{}", message); - } else if (JUICE_LOG_LEVEL_DEBUG == level) { - LOG_INFO("{}", message); - } else if (JUICE_LOG_LEVEL_INFO == level) { - LOG_INFO("{}", message); - } else if (JUICE_LOG_LEVEL_WARN == level) { - LOG_WARN("{}", message); - } else if (JUICE_LOG_LEVEL_ERROR == level) { - LOG_ERROR("{}", message); - } else if (JUICE_LOG_LEVEL_FATAL == level) { - LOG_FATAL("{}", message); - } else if (JUICE_LOG_LEVEL_NONE == level) { - LOG_INFO("{}", message); - } - }); - - memset(&config_, 0, sizeof(config_)); - - config_.stun_server_host = stun_ip_.c_str(); - config_.stun_server_port = stun_port_; - - if (!turn_ip_.empty() && -1 != turn_port_ && !turn_username_.empty() && - !turn_password_.empty()) { - memset(&turn_server_, 0, sizeof(turn_server_)); - turn_server_.host = turn_ip_.c_str(); - turn_server_.port = turn_port_; - turn_server_.username = turn_username_.c_str(); - turn_server_.password = turn_password_.c_str(); - config_.turn_servers = &turn_server_; - config_.turn_servers_count = 1; - } - - config_.cb_state_changed = on_state_changed; - config_.cb_candidate = on_candidate; - config_.cb_gathering_done = on_gathering_done; - config_.cb_recv = on_recv; - config_.user_ptr = user_ptr; - - config_.local_port_range_begin = 40000; - config_.local_port_range_end = 50000; - - agent_ = juice_create(&config_); - - LOG_INFO("Juice agent init finish"); - - return 0; -} - -int IceAgent::DestoryIceAgent() { - juice_destroy(agent_); - return 0; -} - -char *IceAgent::GenerateLocalSdp() { - if (nullptr == agent_) { - LOG_INFO("agent_ is nullptr"); - return nullptr; - } - - juice_get_local_description(agent_, local_sdp_, JUICE_MAX_SDP_STRING_LEN); - // LOG_INFO("Generate local sdp:[\n{}]", local_sdp_); - - return local_sdp_; -} - -int IceAgent::SetRemoteSdp(const char *remote_sdp) { - return juice_set_remote_description(agent_, remote_sdp); -} - -int IceAgent::GatherCandidates() { return juice_gather_candidates(agent_); } - -juice_state_t IceAgent::GetIceState() { - state_ = juice_get_state(agent_); - - return state_; -} - -bool IceAgent::GetSelectedCandidates() { - char local[JUICE_MAX_CANDIDATE_SDP_STRING_LEN]; - char remote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN]; - - bool success = state_ == JUICE_STATE_COMPLETED; - if (success &= (juice_get_selected_candidates( - agent_, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote, - JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) { - LOG_INFO("Local candidate 1: {}", local); - LOG_INFO("Remote candidate 1: {}", 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 - } - - return success; -} - -bool IceAgent::GetSelectedAddresses() { - char localAddr[JUICE_MAX_ADDRESS_STRING_LEN]; - char remoteAddr[JUICE_MAX_ADDRESS_STRING_LEN]; - - bool success = state_ == JUICE_STATE_COMPLETED; - if (success &= (juice_get_selected_addresses( - agent_, localAddr, JUICE_MAX_ADDRESS_STRING_LEN, - remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) { - LOG_INFO("Local address 1: {}", localAddr); - LOG_INFO("Remote address 1: {}", remoteAddr); - } - - return success; -} - -int IceAgent::AddRemoteCandidates(const char *remote_candidates) { - return juice_add_remote_candidate(agent_, remote_candidates); -} - -int IceAgent::SetRemoteGatheringDone() { - return juice_set_remote_gathering_done(agent_); -} - -int IceAgent::Send(const char *data, size_t size) { - if (juice_state_t::JUICE_STATE_COMPLETED != juice_get_state(agent_)) { - return -1; - } - return juice_send(agent_, data, size); -} \ No newline at end of file diff --git a/src/ice/libjuice/ice_agent.h b/src/ice/libjuice/ice_agent.h deleted file mode 100644 index 21252a7..0000000 --- a/src/ice/libjuice/ice_agent.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef _ICE_AGENT_H_ -#define _ICE_AGENT_H_ - -#include - -#include "juice/juice.h" -#include "nice/agent.h" - -class IceAgent { - public: - IceAgent(bool offer_peer, std::string& stun_ip, uint16_t stun_port, - std::string& turn_ip, uint16_t turn_port, std::string& turn_username, - std::string& turn_password); - ~IceAgent(); - - int CreateIceAgent(juice_cb_state_changed_t on_state_changed, - juice_cb_candidate_t on_candidate, - juice_cb_gathering_done_t on_gathering_done, - juice_cb_recv_t on_recv, void* user_ptr); - - int DestoryIceAgent(); - - char* GenerateLocalSdp(); - - int SetRemoteSdp(const char* remote_sdp); - - int GatherCandidates(); - - juice_state_t GetIceState(); - - bool GetSelectedCandidates(); - - bool GetSelectedAddresses(); - - int AddRemoteCandidates(const char* remote_candidates); - - int SetRemoteGatheringDone(); - - int Send(const char* data, size_t size); - - private: - std::string stun_ip_ = ""; - uint16_t stun_port_ = 0; - std::string turn_ip_ = ""; - uint16_t turn_port_ = 0; - std::string turn_username_ = ""; - std::string turn_password_ = ""; - juice_agent_t* agent_ = nullptr; - char local_sdp_[JUICE_MAX_SDP_STRING_LEN]; - juice_state_t state_; - juice_config_t config_; - juice_turn_server_t turn_server_; -}; - -#endif \ No newline at end of file diff --git a/src/transmission/ice_transmission.cpp b/src/transmission/ice_transmission.cpp index c3e6585..6db970e 100644 --- a/src/transmission/ice_transmission.cpp +++ b/src/transmission/ice_transmission.cpp @@ -115,7 +115,6 @@ int IceTransmission::InitIceTransmission(std::string &stun_ip, int stun_port, std::make_unique(offer_peer_, stun_ip, stun_port, turn_ip, turn_port, turn_username, turn_password); -#ifdef USE_NICE ice_agent_->CreateIceAgent( [](NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState state, gpointer user_ptr) { @@ -171,66 +170,6 @@ int IceTransmission::InitIceTransmission(std::string &stun_ip, int stun_port, } }, this); -#else - ice_agent_->CreateIceAgent( - [](juice_agent_t *agent, juice_state_t state, void *user_ptr) { - if (user_ptr) { - IceTransmission *ice_transmission_obj = - static_cast(user_ptr); - LOG_INFO("[{}->{}] state_change: {}", ice_transmission_obj->user_id_, - ice_transmission_obj->remote_user_id_, - juice_state_to_string(state)); - ice_transmission_obj->state_ = state; - ice_transmission_obj->on_ice_status_change_( - juice_state_to_string(state)); - } else { - LOG_INFO("state_change: {}", juice_state_to_string(state)); - } - }, - [](juice_agent_t *agent, const char *sdp, void *user_ptr) { - LOG_INFO("candadite: {}", sdp); - // trickle - // static_cast(user_ptr)->SendOfferLocalCandidate(sdp); - }, - [](juice_agent_t *agent, void *user_ptr) { - // non-trickle - if (user_ptr) { - IceTransmission *ice_transmission_obj = - static_cast(user_ptr); - LOG_INFO("[{}] gather_done", ice_transmission_obj->user_id_); - - if (ice_transmission_obj->offer_peer_) { - ice_transmission_obj->GetLocalSdp(); - ice_transmission_obj->SendOffer(); - - } else { - ice_transmission_obj->CreateAnswer(); - ice_transmission_obj->SendAnswer(); - } - } - }, - [](juice_agent_t *agent, const char *buffer, size_t size, - void *user_ptr) { - if (user_ptr) { - IceTransmission *ice_transmission_obj = - static_cast(user_ptr); - if (ice_transmission_obj) { - if (ice_transmission_obj->CheckIsVideoPacket(buffer, size)) { - RtpPacket packet((uint8_t *)buffer, size); - ice_transmission_obj->rtp_video_receiver_->InsertRtpPacket( - packet); - } else if (ice_transmission_obj->CheckIsDataPacket(buffer, size)) { - RtpPacket packet((uint8_t *)buffer, size); - ice_transmission_obj->rtp_data_receiver_->InsertRtpPacket(packet); - } else if (ice_transmission_obj->CheckIsRtcpPacket(buffer, size)) { - // LOG_ERROR("Rtcp packet [{}]", (uint8_t)(buffer[1])); - } - } - } - }, - this); -#endif return 0; } @@ -316,11 +255,7 @@ int IceTransmission::SendAnswer() { } int IceTransmission::SendData(DATA_TYPE type, const char *data, size_t size) { -#ifdef USE_NICE if (NiceComponentState::NICE_COMPONENT_STATE_READY == state_) { -#else - if (juice_state_t::JUICE_STATE_COMPLETED == state_) { -#endif std::vector packets; if (DATA_TYPE::VIDEO == type) { diff --git a/src/transmission/ice_transmission.h b/src/transmission/ice_transmission.h index d473150..f062dfb 100644 --- a/src/transmission/ice_transmission.h +++ b/src/transmission/ice_transmission.h @@ -4,6 +4,7 @@ #include #include "congestion_control.h" +#include "ice_agent.h" #include "ringbuffer.h" #include "rtp_codec.h" #include "rtp_data_receiver.h" @@ -13,14 +14,6 @@ #include "rtp_video_sender.h" #include "ws_transmission.h" -#define USE_NICE 1 - -#ifdef USE_NICE -#include "libnice/ice_agent.h" -#else -#include "libjuice/ice_agent.h" -#endif - class IceTransmission { public: typedef enum { VIDEO = 96, AUDIO = 97, DATA = 127 } DATA_TYPE; @@ -106,11 +99,7 @@ class IceTransmission { std::string remote_user_id_ = ""; bool offer_peer_ = true; std::string remote_ice_username_ = ""; -#ifdef USE_NICE NiceComponentState state_ = NICE_COMPONENT_STATE_DISCONNECTED; -#else - juice_state_t state_ = JUICE_STATE_DISCONNECTED; -#endif private: std::unique_ptr video_rtp_codec_ = nullptr; diff --git a/thirdparty/libjuice/.clang-format b/thirdparty/libjuice/.clang-format deleted file mode 100644 index ce57268..0000000 --- a/thirdparty/libjuice/.clang-format +++ /dev/null @@ -1,7 +0,0 @@ ---- -BasedOnStyle: LLVM -IndentWidth: 4 -TabWidth: 4 -UseTab: ForIndentation -ColumnLimit: 100 - diff --git a/thirdparty/libjuice/.clang-tidy b/thirdparty/libjuice/.clang-tidy deleted file mode 100644 index b9df5c7..0000000 --- a/thirdparty/libjuice/.clang-tidy +++ /dev/null @@ -1,45 +0,0 @@ ---- -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' -... - diff --git a/thirdparty/libjuice/.editorconfig b/thirdparty/libjuice/.editorconfig deleted file mode 100644 index d080c5f..0000000 --- a/thirdparty/libjuice/.editorconfig +++ /dev/null @@ -1,11 +0,0 @@ -# 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 - diff --git a/thirdparty/libjuice/.github/FUNDING.yml b/thirdparty/libjuice/.github/FUNDING.yml deleted file mode 100644 index 9b11c07..0000000 --- a/thirdparty/libjuice/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -github: ['paullouisageneau'] -custom: ['https://paypal.me/paullouisageneau'] - diff --git a/thirdparty/libjuice/.github/workflows/build.yml b/thirdparty/libjuice/.github/workflows/build.yml deleted file mode 100644 index 179aeeb..0000000 --- a/thirdparty/libjuice/.github/workflows/build.yml +++ /dev/null @@ -1,43 +0,0 @@ -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 - diff --git a/thirdparty/libjuice/.gitignore b/thirdparty/libjuice/.gitignore deleted file mode 100644 index d855c3d..0000000 --- a/thirdparty/libjuice/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -build/ -*.d -*.o -*.a -*.so -compile_commands.json -tests - diff --git a/thirdparty/libjuice/CMakeLists.txt b/thirdparty/libjuice/CMakeLists.txt deleted file mode 100644 index 733efde..0000000 --- a/thirdparty/libjuice/CMakeLists.txt +++ /dev/null @@ -1,249 +0,0 @@ -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 - $ - $) -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 $<$: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 - $ - $) -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 $<$: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 $) - 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() diff --git a/thirdparty/libjuice/LICENSE b/thirdparty/libjuice/LICENSE deleted file mode 100644 index 14e2f77..0000000 --- a/thirdparty/libjuice/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -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. diff --git a/thirdparty/libjuice/Makefile b/thirdparty/libjuice/Makefile deleted file mode 100644 index 986f594..0000000 --- a/thirdparty/libjuice/Makefile +++ /dev/null @@ -1,77 +0,0 @@ -# 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/*~ - diff --git a/thirdparty/libjuice/README.md b/thirdparty/libjuice/README.md deleted file mode 100644 index 8875d46..0000000 --- a/thirdparty/libjuice/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# libjuice - UDP Interactive Connectivity Establishment - -[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-blue.svg)](https://www.mozilla.org/en-US/MPL/2.0/) -[![Build and test](https://github.com/paullouisageneau/libjuice/actions/workflows/build.yml/badge.svg)](https://github.com/paullouisageneau/libjuice/actions/workflows/build.yml) -[![AUR package](https://repology.org/badge/version-for-repo/aur/libjuice.svg)](https://repology.org/project/libjuice/versions) -[![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/libjuice.svg)](https://repology.org/project/libjuice/versions) -[![Gitter](https://badges.gitter.im/libjuice/community.svg)](https://gitter.im/libjuice/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Discord](https://img.shields.io/discord/903257095539925006?logo=discord)](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. - diff --git a/thirdparty/libjuice/cmake/LibJuiceConfig.cmake b/thirdparty/libjuice/cmake/LibJuiceConfig.cmake deleted file mode 100644 index a0c3537..0000000 --- a/thirdparty/libjuice/cmake/LibJuiceConfig.cmake +++ /dev/null @@ -1,2 +0,0 @@ -include("${CMAKE_CURRENT_LIST_DIR}/LibJuiceTargets.cmake") - diff --git a/thirdparty/libjuice/cmake/Modules/FindNettle.cmake b/thirdparty/libjuice/cmake/Modules/FindNettle.cmake deleted file mode 100644 index 80d59bb..0000000 --- a/thirdparty/libjuice/cmake/Modules/FindNettle.cmake +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (C) 2020 Dieter Baron and Thomas Klausner -# -# The authors can be contacted 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}) - diff --git a/thirdparty/libjuice/fuzzer/README.md b/thirdparty/libjuice/fuzzer/README.md deleted file mode 100644 index 496f616..0000000 --- a/thirdparty/libjuice/fuzzer/README.md +++ /dev/null @@ -1,26 +0,0 @@ -## 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/ -``` diff --git a/thirdparty/libjuice/fuzzer/fuzzer.c b/thirdparty/libjuice/fuzzer/fuzzer.c deleted file mode 100644 index 60858bf..0000000 --- a/thirdparty/libjuice/fuzzer/fuzzer.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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 -#include - -#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; -} diff --git a/thirdparty/libjuice/fuzzer/input/message1.raw b/thirdparty/libjuice/fuzzer/input/message1.raw deleted file mode 100644 index c0a79fa..0000000 Binary files a/thirdparty/libjuice/fuzzer/input/message1.raw and /dev/null differ diff --git a/thirdparty/libjuice/fuzzer/input/message2.raw b/thirdparty/libjuice/fuzzer/input/message2.raw deleted file mode 100644 index 9947796..0000000 Binary files a/thirdparty/libjuice/fuzzer/input/message2.raw and /dev/null differ diff --git a/thirdparty/libjuice/include/juice/juice.h b/thirdparty/libjuice/include/juice/juice.h deleted file mode 100644 index 06bcc13..0000000 --- a/thirdparty/libjuice/include/juice/juice.h +++ /dev/null @@ -1,176 +0,0 @@ -/** - * 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 -#include -#include - -#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 diff --git a/thirdparty/libjuice/src/addr.c b/thirdparty/libjuice/src/addr.c deleted file mode 100644 index a8b2fab..0000000 --- a/thirdparty/libjuice/src/addr.c +++ /dev/null @@ -1,310 +0,0 @@ -/** - * 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 -#include - -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); -} diff --git a/thirdparty/libjuice/src/addr.h b/thirdparty/libjuice/src/addr.h deleted file mode 100644 index d4bdb74..0000000 --- a/thirdparty/libjuice/src/addr.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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 -#include - -// 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 diff --git a/thirdparty/libjuice/src/agent.c b/thirdparty/libjuice/src/agent.c deleted file mode 100644 index 07aaecf..0000000 --- a/thirdparty/libjuice/src/agent.c +++ /dev/null @@ -1,2514 +0,0 @@ -/** - * 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 "agent.h" -#include "ice.h" -#include "juice.h" -#include "log.h" -#include "random.h" -#include "stun.h" -#include "turn.h" -#include "udp.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -// RFC 8656: The Permission Lifetime MUST be 300 seconds (= 5 minutes) -#define PERMISSION_LIFETIME 300000 // ms - -// RFC 8656: Channel bindings last for 10 minutes unless refreshed -#define BIND_LIFETIME 600000 // ms - -#define BUFFER_SIZE 4096 -#define DEFAULT_MAX_RECORDS_COUNT 8 - -static char *alloc_string_copy(const char *orig, bool *alloc_failed) { - if (!orig) - return NULL; - - char *copy = malloc(strlen(orig) + 1); - if (!copy) { - if (alloc_failed) - *alloc_failed = true; - - return NULL; - } - strcpy(copy, orig); - return copy; -} - -juice_agent_t *agent_create(const juice_config_t *config) { - JLOG_VERBOSE("Creating agent"); - -#ifdef _WIN32 - WSADATA wsaData; - if (WSAStartup(MAKEWORD(2, 2), &wsaData)) { - JLOG_FATAL("WSAStartup failed"); - return NULL; - } -#endif - - juice_agent_t *agent = calloc(1, sizeof(juice_agent_t)); - if (!agent) { - JLOG_FATAL("Memory allocation for agent failed"); - return NULL; - } - - bool alloc_failed = false; - agent->config.concurrency_mode = config->concurrency_mode; - agent->config.stun_server_host = alloc_string_copy(config->stun_server_host, &alloc_failed); - agent->config.stun_server_port = config->stun_server_port; - agent->config.bind_address = alloc_string_copy(config->bind_address, &alloc_failed); - agent->config.local_port_range_begin = config->local_port_range_begin; - agent->config.local_port_range_end = config->local_port_range_end; - agent->config.cb_state_changed = config->cb_state_changed; - agent->config.cb_candidate = config->cb_candidate; - agent->config.cb_gathering_done = config->cb_gathering_done; - agent->config.cb_recv = config->cb_recv; - agent->config.user_ptr = config->user_ptr; - if (alloc_failed) { - JLOG_FATAL("Memory allocation for configuration copy failed"); - goto error; - } - - if (config->turn_servers_count <= 0) { - agent->config.turn_servers = NULL; - agent->config.turn_servers_count = 0; - } else { - agent->config.turn_servers = - calloc(config->turn_servers_count, sizeof(juice_turn_server_t)); - if (!agent->config.turn_servers) { - JLOG_FATAL("Memory allocation for TURN servers copy failed"); - goto error; - } - agent->config.turn_servers_count = config->turn_servers_count; - for (int i = 0; i < config->turn_servers_count; ++i) { - agent->config.turn_servers[i].host = - alloc_string_copy(config->turn_servers[i].host, &alloc_failed); - agent->config.turn_servers[i].username = - alloc_string_copy(config->turn_servers[i].username, &alloc_failed); - agent->config.turn_servers[i].password = - alloc_string_copy(config->turn_servers[i].password, &alloc_failed); - agent->config.turn_servers[i].port = config->turn_servers[i].port; - if (alloc_failed) { - JLOG_FATAL("Memory allocation for TURN server configuration copy failed"); - goto error; - } - } - } - - agent->state = JUICE_STATE_DISCONNECTED; - agent->mode = AGENT_MODE_UNKNOWN; - agent->selected_entry = ATOMIC_VAR_INIT(NULL); - - agent->conn_index = -1; - agent->conn_impl = NULL; - - ice_create_local_description(&agent->local); - - // RFC 8445: 16.1. Attributes - // The content of the [ICE-CONTROLLED/ICE-CONTROLLING] attribute is a 64-bit - // unsigned integer in network byte order, which contains a random number. - // The number is used for solving role conflicts, when it is referred to as - // the "tiebreaker value". An ICE agent MUST use the same number for - // all Binding requests, for all streams, within an ICE session, unless - // it has received a 487 response, in which case it MUST change the - // number. - juice_random(&agent->ice_tiebreaker, sizeof(agent->ice_tiebreaker)); - - return agent; - -error: - agent_destroy(agent); - return NULL; -} - -void agent_destroy(juice_agent_t *agent) { - JLOG_DEBUG("Destroying agent"); - - if (agent->resolver_thread_started) { - JLOG_VERBOSE("Waiting for resolver thread"); - thread_join(agent->resolver_thread, NULL); - } - - if (agent->conn_impl) { - conn_destroy(agent); - } - - // Free credentials in entries - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->turn) { - turn_destroy_map(&entry->turn->map); - free(entry->turn); - } - } - - // Free strings in config - free((void *)agent->config.stun_server_host); - for (int i = 0; i < agent->config.turn_servers_count; ++i) { - juice_turn_server_t *turn_server = agent->config.turn_servers + i; - free((void *)turn_server->host); - free((void *)turn_server->username); - free((void *)turn_server->password); - } - free(agent->config.turn_servers); - free((void *)agent->config.bind_address); - free(agent); - -#ifdef _WIN32 - WSACleanup(); -#endif - - JLOG_VERBOSE("Destroyed agent"); -} - -static bool has_nonnumeric_server_hostnames(const juice_config_t *config) { - if (config->stun_server_host && !addr_is_numeric_hostname(config->stun_server_host)) - return true; - - for (int i = 0; i < config->turn_servers_count; ++i) { - juice_turn_server_t *turn_server = config->turn_servers + i; - if (turn_server->host && !addr_is_numeric_hostname(turn_server->host)) - return true; - } - - return false; -} - -static thread_return_t THREAD_CALL resolver_thread_entry(void *arg) { - agent_resolve_servers((juice_agent_t *)arg); - return (thread_return_t)0; -} - -int agent_gather_candidates(juice_agent_t *agent) { - JLOG_VERBOSE("Gathering candidates"); - if (agent->conn_impl) { - JLOG_WARN("Candidates gathering already started"); - return 0; - } - - if (agent->mode == AGENT_MODE_UNKNOWN) { - JLOG_DEBUG("Assuming controlling mode"); - agent->mode = AGENT_MODE_CONTROLLING; - } - - agent_change_state(agent, JUICE_STATE_GATHERING); - - udp_socket_config_t socket_config; - memset(&socket_config, 0, sizeof(socket_config)); - socket_config.bind_address = agent->config.bind_address; - socket_config.port_begin = agent->config.local_port_range_begin; - socket_config.port_end = agent->config.local_port_range_end; - - if (conn_create(agent, &socket_config)) { - JLOG_FATAL("Connection creation for agent failed"); - return -1; - } - - addr_record_t records[ICE_MAX_CANDIDATES_COUNT - 1]; - int records_count = conn_get_addrs(agent, records, ICE_MAX_CANDIDATES_COUNT - 1); - if (records_count < 0) { - JLOG_ERROR("Failed to gather local host candidates"); - records_count = 0; - } else if (records_count == 0) { - JLOG_WARN("No local host candidates gathered"); - } else if (records_count > ICE_MAX_CANDIDATES_COUNT - 1) - records_count = ICE_MAX_CANDIDATES_COUNT - 1; - - conn_lock(agent); - - JLOG_VERBOSE("Adding %d local host candidates", records_count); - for (int i = 0; i < records_count; ++i) { - ice_candidate_t candidate; - if (ice_create_local_candidate(ICE_CANDIDATE_TYPE_HOST, 1, agent->local.candidates_count, - records + i, &candidate)) { - JLOG_ERROR("Failed to create host candidate"); - continue; - } - if (agent->local.candidates_count >= MAX_HOST_CANDIDATES_COUNT) { - JLOG_WARN("Local description already has the maximum number of host candidates"); - break; - } - if (ice_add_candidate(&candidate, &agent->local)) { - JLOG_ERROR("Failed to add candidate to local description"); - continue; - } - } - - ice_sort_candidates(&agent->local); - - for (int i = 0; i < agent->entries_count; ++i) - agent_translate_host_candidate_entry(agent, agent->entries + i); - - char buffer[BUFFER_SIZE]; - for (int i = 0; i < agent->local.candidates_count; ++i) { - ice_candidate_t *candidate = agent->local.candidates + i; - if (candidate->type != ICE_CANDIDATE_TYPE_HOST) - continue; - - if (ice_generate_candidate_sdp(candidate, buffer, BUFFER_SIZE) < 0) { - JLOG_ERROR("Failed to generate SDP for local candidate"); - continue; - } - - JLOG_DEBUG("Gathered host candidate: %s", buffer); - - if (agent->config.cb_candidate) - agent->config.cb_candidate(agent, buffer, agent->config.user_ptr); - } - - agent_change_state(agent, JUICE_STATE_CONNECTING); - conn_unlock(agent); - conn_interrupt(agent); - - if (has_nonnumeric_server_hostnames(&agent->config)) { - // Resolve server hostnames in a separate thread as it may block - JLOG_DEBUG("Starting resolver thread for servers"); - int ret = thread_init(&agent->resolver_thread, resolver_thread_entry, agent); - if (ret) { - JLOG_FATAL("Thread creation failed, error=%d", ret); - agent_update_gathering_done(agent); - return -1; - } - agent->resolver_thread_started = true; - } else { - JLOG_DEBUG("Resolving servers synchronously"); - if (agent_resolve_servers(agent) < 0) - return -1; - } - - return 0; -} - -int agent_resolve_servers(juice_agent_t *agent) { - conn_lock(agent); - - // TURN server resolution - juice_concurrency_mode_t mode = agent->config.concurrency_mode; - if (mode == JUICE_CONCURRENCY_MODE_MUX) { - if (agent->config.turn_servers_count > 0) - JLOG_WARN("TURN servers are not supported in mux mode"); - - } else if (agent->config.turn_servers_count > 0) { - int count = 0; - for (int i = 0; i < agent->config.turn_servers_count; ++i) { - if (count >= MAX_RELAY_ENTRIES_COUNT) - break; - - juice_turn_server_t *turn_server = agent->config.turn_servers + i; - if (!turn_server->host) - continue; - - if (!turn_server->port) - turn_server->port = 3478; // default TURN port - - char service[8]; - snprintf(service, 8, "%hu", turn_server->port); - addr_record_t records[DEFAULT_MAX_RECORDS_COUNT]; - int records_count = - addr_resolve(turn_server->host, service, records, DEFAULT_MAX_RECORDS_COUNT); - if (records_count > 0) { - if (records_count > DEFAULT_MAX_RECORDS_COUNT) - records_count = DEFAULT_MAX_RECORDS_COUNT; - - JLOG_INFO("Using TURN server %s:%s", turn_server->host, service); - - addr_record_t *record = NULL; - for (int j = 0; j < records_count; ++j) { - int family = records[j].addr.ss_family; - // Prefer IPv4 for TURN - if (family == AF_INET) { - record = records + j; - break; - } - if (family == AF_INET6 && !record) - record = records + j; - } - if (record) { - // Ignore duplicate TURN servers as they will cause conflicts - bool is_duplicate = false; - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->type == AGENT_STUN_ENTRY_TYPE_RELAY && - addr_record_is_equal(&entry->record, record, true)) { - is_duplicate = true; - break; - } - } - if (is_duplicate) { - JLOG_INFO("Duplicate TURN server, ignoring"); - continue; - } - - JLOG_VERBOSE("Registering STUN entry %d for relay request", - agent->entries_count); - agent_stun_entry_t *entry = agent->entries + agent->entries_count; - entry->type = AGENT_STUN_ENTRY_TYPE_RELAY; - entry->state = AGENT_STUN_ENTRY_STATE_PENDING; - entry->pair = NULL; - entry->record = *record; - entry->turn_redirections = 0; - entry->turn = calloc(1, sizeof(agent_turn_state_t)); - if (!entry->turn) { - JLOG_ERROR("Memory allocation for TURN state failed"); - break; - } - if (turn_init_map(&entry->turn->map, AGENT_TURN_MAP_SIZE) < 0) { - free(entry->turn); - break; - } - snprintf(entry->turn->credentials.username, STUN_MAX_USERNAME_LEN, "%s", - turn_server->username); - entry->turn->password = turn_server->password; - juice_random(entry->transaction_id, STUN_TRANSACTION_ID_SIZE); - ++agent->entries_count; - - agent_arm_transmission(agent, entry, STUN_PACING_TIME * i); - - ++count; - } - } else { - JLOG_ERROR("TURN address resolution failed"); - } - } - } - - // STUN server resolution - // The entry is added after so the TURN server address will be matched in priority - if (agent->config.stun_server_host) { - if (!agent->config.stun_server_port) - agent->config.stun_server_port = 3478; // default STUN port - - char service[8]; - snprintf(service, 8, "%hu", agent->config.stun_server_port); - addr_record_t records[MAX_STUN_SERVER_RECORDS_COUNT]; - int records_count = addr_resolve(agent->config.stun_server_host, service, records, - MAX_STUN_SERVER_RECORDS_COUNT); - if (records_count > 0) { - if (records_count > MAX_STUN_SERVER_RECORDS_COUNT) - records_count = MAX_STUN_SERVER_RECORDS_COUNT; - - JLOG_INFO("Using STUN server %s:%s", agent->config.stun_server_host, service); - - for (int i = 0; i < records_count; ++i) { - if (i >= MAX_SERVER_ENTRIES_COUNT) - break; - JLOG_VERBOSE("Registering STUN entry %d for server request", agent->entries_count); - agent_stun_entry_t *entry = agent->entries + agent->entries_count; - entry->type = AGENT_STUN_ENTRY_TYPE_SERVER; - entry->state = AGENT_STUN_ENTRY_STATE_PENDING; - entry->pair = NULL; - entry->record = records[i]; - juice_random(entry->transaction_id, STUN_TRANSACTION_ID_SIZE); - ++agent->entries_count; - - agent_arm_transmission(agent, entry, STUN_PACING_TIME * i); - } - } else { - JLOG_ERROR("STUN server address resolution failed"); - } - } - - agent_update_gathering_done(agent); - conn_unlock(agent); - conn_interrupt(agent); - return 0; -} - -int agent_get_local_description(juice_agent_t *agent, char *buffer, size_t size) { - conn_lock(agent); - if (ice_generate_sdp(&agent->local, buffer, size) < 0) { - JLOG_ERROR("Failed to generate local SDP description"); - conn_unlock(agent); - return -1; - } - JLOG_VERBOSE("Generated local SDP description: %s", buffer); - - if (agent->mode == AGENT_MODE_UNKNOWN) { - JLOG_DEBUG("Assuming controlling mode"); - agent->mode = AGENT_MODE_CONTROLLING; - } - conn_unlock(agent); - return 0; -} - -int agent_set_remote_description(juice_agent_t *agent, const char *sdp) { - conn_lock(agent); - JLOG_VERBOSE("Setting remote SDP description: %s", sdp); - - ice_description_t remote; - int ret = ice_parse_sdp(sdp, &remote); - if (ret < 0) { - if (ret == ICE_PARSE_MISSING_UFRAG) - JLOG_ERROR("Missing ICE user fragment in remote description"); - else if (ret == ICE_PARSE_MISSING_PWD) - JLOG_ERROR("Missing ICE password in remote description"); - else - JLOG_ERROR("Failed to parse remote SDP description"); - - conn_unlock(agent); - return -1; - } - - if (*agent->remote.ice_ufrag) { - // There is already a remote description - if (strcmp(agent->remote.ice_ufrag, remote.ice_ufrag) == 0 || - strcmp(agent->remote.ice_pwd, remote.ice_pwd) == 0) { - JLOG_DEBUG("Remote description is already set, ignoring"); - conn_unlock(agent); - return 0; - } - - JLOG_WARN("ICE restart is unsupported"); - conn_unlock(agent); - return -1; - } - - agent->remote = remote; - - if (agent->mode == AGENT_MODE_UNKNOWN) { - JLOG_DEBUG("Assuming controlled mode"); - agent->mode = AGENT_MODE_CONTROLLED; - } - - // There is only one component, therefore we can unfreeze already existing pairs now - JLOG_DEBUG("Unfreezing %d existing candidate pairs", (int)agent->candidate_pairs_count); - for (int i = 0; i < agent->candidate_pairs_count; ++i) { - agent_unfreeze_candidate_pair(agent, agent->candidate_pairs + i); - } - JLOG_DEBUG("Adding %d candidates from remote description", (int)agent->remote.candidates_count); - for (int i = 0; i < agent->remote.candidates_count; ++i) { - ice_candidate_t *remote = agent->remote.candidates + i; - if (agent_add_candidate_pairs_for_remote(agent, remote)) - JLOG_WARN("Failed to add candidate pair from remote description"); - } - - conn_unlock(agent); - conn_interrupt(agent); - return 0; -} - -int agent_add_remote_candidate(juice_agent_t *agent, const char *sdp) { - conn_lock(agent); - JLOG_VERBOSE("Adding remote candidate: %s", sdp); - ice_candidate_t candidate; - int ret = ice_parse_candidate_sdp(sdp, &candidate); - if (ret < 0) { - if (ret == ICE_PARSE_IGNORED) - JLOG_DEBUG("Ignored SDP candidate: %s", sdp); - else if (ret == ICE_PARSE_ERROR) - JLOG_ERROR("Failed to parse remote SDP candidate: %s", sdp); - - conn_unlock(agent); - return -1; - } - if (ice_add_candidate(&candidate, &agent->remote)) { - JLOG_ERROR("Failed to add candidate to remote description"); - conn_unlock(agent); - return -1; - } - ice_candidate_t *remote = agent->remote.candidates + agent->remote.candidates_count - 1; - ret = agent_add_candidate_pairs_for_remote(agent, remote); - - conn_unlock(agent); - conn_interrupt(agent); - return ret; -} - -int agent_set_remote_gathering_done(juice_agent_t *agent) { - conn_lock(agent); - agent->remote.finished = true; - agent->fail_timestamp = 0; // So the bookkeeping will recompute it and fail - conn_unlock(agent); - return 0; -} - -int agent_send(juice_agent_t *agent, const char *data, size_t size, int ds) { - // Try not to lock in the send path - agent_stun_entry_t *selected_entry = atomic_load(&agent->selected_entry); - if (!selected_entry) { - JLOG_ERROR("Send while ICE is not connected"); - return -1; - } - - if (selected_entry->relay_entry) { - // The datagram should be sent through the relay, use a channel to minimize overhead - conn_lock(agent); // We have to lock - int ret = agent_channel_send(agent, selected_entry->relay_entry, &selected_entry->record, - data, size, ds); - conn_unlock(agent); - return ret; - } - - return agent_direct_send(agent, &selected_entry->record, data, size, ds); -} - -int agent_direct_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size, - int ds) { - return conn_send(agent, dst, data, size, 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) { - if (!entry->turn) { - JLOG_ERROR("Missing TURN state on relay entry"); - return -1; - } - - JLOG_VERBOSE("Sending datagram via TURN Send Indication, size=%d", size); - - // Send CreatePermission if necessary - if (!turn_has_permission(&entry->turn->map, dst)) - if (agent_send_turn_create_permission_request(agent, entry, dst, ds)) - return -1; - - // Send the data in a TURN Send indication - stun_message_t msg; - memset(&msg, 0, sizeof(msg)); - msg.msg_class = STUN_CLASS_INDICATION; - msg.msg_method = STUN_METHOD_SEND; - juice_random(msg.transaction_id, STUN_TRANSACTION_ID_SIZE); - msg.peer = *dst; - msg.data = data; - msg.data_size = size; - - char buffer[BUFFER_SIZE]; - size = stun_write(buffer, BUFFER_SIZE, &msg, NULL); // no password - if (size <= 0) { - JLOG_ERROR("STUN message write failed"); - return -1; - } - - return agent_direct_send(agent, &entry->record, buffer, size, ds); -} - -int agent_channel_send(juice_agent_t *agent, agent_stun_entry_t *entry, const addr_record_t *record, - const char *data, size_t size, int ds) { - if (!entry->turn) { - JLOG_ERROR("Missing TURN state on relay entry"); - return -1; - } - - // Send ChannelBind if necessary - uint16_t channel; - if (!turn_get_bound_channel(&entry->turn->map, record, &channel)) - if (agent_send_turn_channel_bind_request(agent, entry, record, ds, &channel) < 0) - return -1; - - JLOG_VERBOSE("Sending datagram via TURN ChannelData, channel=0x%hX, size=%d", channel, size); - - // Send the data wrapped as ChannelData - char buffer[BUFFER_SIZE]; - int len = turn_wrap_channel_data(buffer, BUFFER_SIZE, data, size, channel); - if (len <= 0) { - JLOG_ERROR("TURN ChannelData wrapping failed"); - return -1; - } - - return agent_direct_send(agent, &entry->record, buffer, len, ds); -} - -juice_state_t agent_get_state(juice_agent_t *agent) { - conn_lock(agent); - juice_state_t state = agent->state; - conn_unlock(agent); - return state; -} - -int agent_get_selected_candidate_pair(juice_agent_t *agent, ice_candidate_t *local, - ice_candidate_t *remote) { - conn_lock(agent); - ice_candidate_pair_t *pair = agent->selected_pair; - if (!pair) { - conn_unlock(agent); - return -1; - } - - if (local) - *local = pair->local ? *pair->local : agent->local.candidates[0]; - if (remote) - *remote = *pair->remote; - - conn_unlock(agent); - return 0; -} - -int agent_conn_update(juice_agent_t *agent, timestamp_t *next_timestamp) { - return agent_bookkeeping(agent, next_timestamp); -} - -int agent_conn_recv(juice_agent_t *agent, char *buf, size_t len, const addr_record_t *src) { - agent_input(agent, buf, len, src, NULL); - return 0; // ignore errors -} - -int agent_conn_fail(juice_agent_t *agent) { - agent_change_state(agent, JUICE_STATE_FAILED); - atomic_store(&agent->selected_entry, NULL); // disallow sending - return 0; -} - -int agent_input(juice_agent_t *agent, char *buf, size_t len, const addr_record_t *src, - const addr_record_t *relayed) { - JLOG_VERBOSE("Received datagram, size=%d", len); - - if (agent->state == JUICE_STATE_DISCONNECTED || agent->state == JUICE_STATE_GATHERING) - return 0; - - if (is_stun_datagram(buf, len)) { - if (JLOG_DEBUG_ENABLED) { - char src_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(src, src_str, ADDR_MAX_STRING_LEN); - if (relayed) { - char relayed_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(relayed, relayed_str, ADDR_MAX_STRING_LEN); - JLOG_DEBUG("Received STUN datagram from %s relayed via %s", src_str, relayed_str); - } else { - JLOG_DEBUG("Received STUN datagram from %s", src_str); - } - } - stun_message_t msg; - if (stun_read(buf, len, &msg) < 0) { - JLOG_ERROR("STUN message reading failed"); - return -1; - } - return agent_dispatch_stun(agent, buf, len, &msg, src, relayed); - } - - if (JLOG_DEBUG_ENABLED) { - char src_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(src, src_str, ADDR_MAX_STRING_LEN); - if (relayed) { - char relayed_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(relayed, relayed_str, ADDR_MAX_STRING_LEN); - JLOG_DEBUG("Received non-STUN datagram from %s relayed via %s", src_str, relayed_str); - } else { - JLOG_DEBUG("Received non-STUN datagram from %s", src_str); - } - } - agent_stun_entry_t *entry = agent_find_entry_from_record(agent, src, relayed); - if (!entry) { - JLOG_WARN("Received a datagram from unknown address, ignoring"); - return -1; - } - switch (entry->type) { - case AGENT_STUN_ENTRY_TYPE_RELAY: - if (is_channel_data(buf, len)) { - JLOG_DEBUG("Received ChannelData datagram"); - return agent_process_channel_data(agent, entry, buf, len); - } - break; - - case AGENT_STUN_ENTRY_TYPE_CHECK: - JLOG_DEBUG("Received application datagram"); - if (agent->config.cb_recv) - agent->config.cb_recv(agent, buf, len, agent->config.user_ptr); - return 0; - - default: - break; - } - - JLOG_WARN("Received unexpected non-STUN datagram, ignoring"); - return -1; -} - -int agent_bookkeeping(juice_agent_t *agent, timestamp_t *next_timestamp) { - JLOG_VERBOSE("Bookkeeping..."); - - timestamp_t now = current_timestamp(); - *next_timestamp = now + 6000000; - - if (agent->state == JUICE_STATE_DISCONNECTED || agent->state == JUICE_STATE_GATHERING) - return 0; - - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - - // STUN requests transmission or retransmission - if (entry->state == AGENT_STUN_ENTRY_STATE_PENDING) { - if (entry->next_transmission > now) - continue; - - if (entry->retransmissions >= 0) { - if (JLOG_DEBUG_ENABLED) { - char record_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(&entry->record, record_str, ADDR_MAX_STRING_LEN); - JLOG_DEBUG("STUN entry %d: Sending request to %s (%d retransmission%s left)", i, - record_str, entry->retransmissions, - entry->retransmissions >= 2 ? "s" : ""); - } - int ret; - switch (entry->type) { - case AGENT_STUN_ENTRY_TYPE_RELAY: - ret = agent_send_turn_allocate_request(agent, entry, STUN_METHOD_ALLOCATE); - break; - - default: - ret = agent_send_stun_binding(agent, entry, STUN_CLASS_REQUEST, 0, NULL, NULL); - break; - } - - if (ret >= 0) { - --entry->retransmissions; - entry->next_transmission = now + entry->retransmission_timeout; - entry->retransmission_timeout *= 2; - continue; - } - } - - // Failure sending or end of retransmissions - JLOG_DEBUG("STUN entry %d: Failed", i); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - entry->next_transmission = 0; - - switch (entry->type) { - case AGENT_STUN_ENTRY_TYPE_RELAY: - JLOG_INFO("TURN allocation failed"); - agent_update_gathering_done(agent); - break; - - case AGENT_STUN_ENTRY_TYPE_SERVER: - JLOG_INFO("STUN server binding failed"); - agent_update_gathering_done(agent); - break; - - default: - if (entry->pair) { - JLOG_DEBUG("Candidate pair check failed"); - entry->pair->state = ICE_CANDIDATE_PAIR_STATE_FAILED; - } - break; - } - } - // STUN keepalives - else if (entry->state == AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE) { -#if JUICE_DISABLE_CONSENT_FRESHNESS - // No expiration -#else - // Consent freshness expiration - if (entry->pair && entry->pair->consent_expiry <= now) { - JLOG_INFO("STUN entry %d: Consent expired for candidate pair", i); - entry->pair->state = ICE_CANDIDATE_PAIR_STATE_FAILED; - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - entry->next_transmission = 0; - continue; - } -#endif - - if (entry->next_transmission > now) - continue; - - JLOG_DEBUG("STUN entry %d: Sending keepalive", i); - - juice_random(entry->transaction_id, STUN_TRANSACTION_ID_SIZE); - - int ret; - switch (entry->type) { - case AGENT_STUN_ENTRY_TYPE_RELAY: - // RFC 8445 5.1.1.4. Keeping Candidates Alive: - // Refreshes for allocations are done using the Refresh transaction, as described in - // [RFC5766] - ret = agent_send_turn_allocate_request(agent, entry, STUN_METHOD_REFRESH); - break; - case AGENT_STUN_ENTRY_TYPE_SERVER: - // RFC 8445 5.1.1.4. Keeping Candidates Alive: - // For server-reflexive candidates learned through a Binding request, the bindings - // MUST be kept alive by additional Binding requests to the server. - ret = agent_send_stun_binding(agent, entry, STUN_CLASS_REQUEST, 0, NULL, NULL); - break; - default: -#if JUICE_DISABLE_CONSENT_FRESHNESS - // RFC 8445 11. Keepalives: - // All endpoints MUST send keepalives for each data session. [...] STUN keepalives - // MUST be used when an ICE agent is a full ICE implementation and is communicating - // with a peer that supports ICE (lite or full). [...] When STUN is being used for - // keepalives, a STUN Binding Indication is used [RFC5389]. - ret = agent_send_stun_binding(agent, entry, STUN_CLASS_INDICATION, 0, NULL, NULL); -#else - // RFC 7675 4. Design Considerations: - // STUN binding requests sent for consent freshness also serve the keepalive purpose - // (i.e., to keep NAT bindings alive). Because of that, dedicated keepalives (e.g., - // STUN Binding Indications) are not sent on candidate pairs where consent requests - // are sent, in accordance with Section 20.2.3 of [RFC5245]. - ret = agent_send_stun_binding(agent, entry, STUN_CLASS_REQUEST, 0, NULL, NULL); -#endif - break; - } - - if (ret < 0) { - JLOG_WARN("Sending keepalive failed"); - agent_arm_transmission(agent, entry, STUN_KEEPALIVE_PERIOD); - continue; - } - - agent_arm_keepalive(agent, entry); - - } else { - // Entry does not transmit, unset next transmission - entry->next_transmission = 0; - } - } - - if (agent->candidate_pairs_count == 0) - goto finally; - - int pending_count = 0; - ice_candidate_pair_t *nominated_pair = NULL; - ice_candidate_pair_t *selected_pair = NULL; - for (int i = 0; i < agent->candidate_pairs_count; ++i) { - ice_candidate_pair_t *pair = agent->ordered_pairs[i]; - if (pair->nominated) { - // RFC 8445 8.1.1. Nominating Pairs: - // If more than one candidate pair is nominated by the controlling agent, and if the - // controlled agent accepts multiple nominations requests, the agents MUST produce the - // selected pairs and use the pairs with the highest priority. - if (!nominated_pair) { - nominated_pair = pair; - selected_pair = pair; - } - } else if (pair->state == ICE_CANDIDATE_PAIR_STATE_SUCCEEDED) { - if (!selected_pair) - selected_pair = pair; - } else if (pair->state == ICE_CANDIDATE_PAIR_STATE_PENDING) { - if (agent->mode == AGENT_MODE_CONTROLLING && selected_pair) { - // A higher-priority pair will be used, we can stop checking. - // Entries will be synchronized after the current loop. - JLOG_VERBOSE("Cancelling check for lower-priority pair"); - pair->state = ICE_CANDIDATE_PAIR_STATE_FROZEN; - } else { - ++pending_count; - } - } - } - - // Cancel entries of frozen pairs - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->pair && entry->pair->state == ICE_CANDIDATE_PAIR_STATE_FROZEN && - entry->state != AGENT_STUN_ENTRY_STATE_IDLE && - entry->state != AGENT_STUN_ENTRY_STATE_CANCELLED) { - JLOG_DEBUG("STUN entry %d: Cancelled", i); - entry->state = AGENT_STUN_ENTRY_STATE_CANCELLED; - entry->next_transmission = 0; - } - } - - if (nominated_pair && nominated_pair->state == ICE_CANDIDATE_PAIR_STATE_FAILED) { - JLOG_WARN("Lost connectivity"); - agent_change_state(agent, JUICE_STATE_FAILED); - atomic_store(&agent->selected_entry, NULL); // disallow sending - return 0; - } - - if (selected_pair) { - // Succeeded - // Change selected entry if this is a new selected pair - if (agent->selected_pair != selected_pair) { - JLOG_DEBUG(selected_pair->nominated ? "New selected and nominated pair" - : "New selected pair"); - agent->selected_pair = selected_pair; - - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->pair == selected_pair) { - atomic_store(&agent->selected_entry, entry); - break; - } - } - } - - bool selected_pair_has_relay = - (selected_pair->local && selected_pair->local->type == ICE_CANDIDATE_TYPE_RELAYED) || - (selected_pair->remote && selected_pair->remote->type == ICE_CANDIDATE_TYPE_RELAYED); - - if (selected_pair->nominated || - (agent->mode == AGENT_MODE_CONTROLLING && !selected_pair_has_relay)) { - // Limit retransmissions of still pending entries - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->pair != selected_pair && - entry->state == AGENT_STUN_ENTRY_STATE_PENDING && entry->retransmissions > 1) - entry->retransmissions = 1; - } - } - - if (nominated_pair) { - // Completed - // Do not allow direct transition from connecting to completed - if (agent->state == JUICE_STATE_CONNECTING) - agent_change_state(agent, JUICE_STATE_CONNECTED); - - // Actually transition to finished only if controlled or if nothing is pending anymore - if (agent->mode == AGENT_MODE_CONTROLLED || pending_count == 0) - agent_change_state(agent, JUICE_STATE_COMPLETED); - - agent_stun_entry_t *nominated_entry = NULL; - agent_stun_entry_t *relay_entry = NULL; - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->pair && entry->pair == nominated_pair) { - nominated_entry = entry; - relay_entry = nominated_entry->relay_entry; - break; - } - } - - // Enable keepalive for the entry of the nominated pair - if (nominated_entry && - nominated_entry->state != AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE) { - nominated_entry->state = AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE; - agent_arm_keepalive(agent, nominated_entry); - } - - // If the entry of the nominated candidate is relayed locally, we need also to - // refresh the corresponding TURN session regularly - if (relay_entry && relay_entry->state != AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE) { - relay_entry->state = AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE; - agent_arm_keepalive(agent, relay_entry); - } - - // Disable keepalives for other entries - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry != nominated_entry && entry != relay_entry && - entry->state == AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE) - entry->state = AGENT_STUN_ENTRY_STATE_SUCCEEDED; - } - - } else { - // Connected - agent_change_state(agent, JUICE_STATE_CONNECTED); - - // RFC 8445 8.1.1. Nominating Pairs: - // Once the controlling agent has successfully nominated a candidate pair, the agent - // MUST NOT nominate another pair for same component of the data stream within the ICE - // session. - // For this reason, we wait until no pair is pending so the selected pair won't change. - if (agent->mode == AGENT_MODE_CONTROLLING && pending_count == 0 && selected_pair && - !selected_pair->nomination_requested) { - // Nominate selected - JLOG_DEBUG("Requesting pair nomination (controlling)"); - selected_pair->nomination_requested = true; - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->pair && entry->pair == selected_pair) { - entry->state = AGENT_STUN_ENTRY_STATE_PENDING; // we don't want keepalives - agent_arm_transmission(agent, entry, 0); // transmit now - break; - } - } - } - } - } else if (pending_count == 0) { - // Failed - if (!agent->fail_timestamp) - agent->fail_timestamp = now + (agent->remote.finished ? 0 : ICE_FAIL_TIMEOUT); - - if (agent->fail_timestamp && now >= agent->fail_timestamp) { - agent_change_state(agent, JUICE_STATE_FAILED); - atomic_store(&agent->selected_entry, NULL); // disallow sending - return 0; - } - - if (*next_timestamp > agent->fail_timestamp) - *next_timestamp = agent->fail_timestamp; - } - -finally: - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->next_transmission && *next_timestamp > entry->next_transmission) - *next_timestamp = entry->next_transmission; - - if (entry->state == AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE && entry->pair && - *next_timestamp > entry->pair->consent_expiry) - *next_timestamp = selected_pair->consent_expiry; - } - - return 0; -} - -void agent_change_state(juice_agent_t *agent, juice_state_t state) { - if (state != agent->state) { - JLOG_INFO("Changing state to %s", juice_state_to_string(state)); - agent->state = state; - if (agent->config.cb_state_changed) - agent->config.cb_state_changed(agent, state, agent->config.user_ptr); - } -} - -int agent_verify_stun_binding(juice_agent_t *agent, void *buf, size_t size, - const stun_message_t *msg) { - if (msg->msg_method != STUN_METHOD_BINDING) - return -1; - - if (msg->msg_class == STUN_CLASS_INDICATION || msg->msg_class == STUN_CLASS_RESP_ERROR) - return 0; - - if (!msg->has_integrity) { - JLOG_WARN("Missing integrity in STUN message"); - return -1; - } - - // Check username (The USERNAME attribute is not present in responses) - if (msg->msg_class == STUN_CLASS_REQUEST) { - 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 -1; - } - *separator = '\0'; - const char *local_ufrag = username; - const char *remote_ufrag = separator + 1; - if (strcmp(local_ufrag, agent->local.ice_ufrag) != 0) { - JLOG_WARN("STUN local ufrag check failed, expected=\"%s\", actual=\"%s\"", - agent->local.ice_ufrag, local_ufrag); - return -1; - } - // RFC 8445 7.3. STUN Server Procedures: - // It is possible (and in fact very likely) that the initiating agent will receive a Binding - // request prior to receiving the candidates from its peer. If this happens, the agent MUST - // immediately generate a response. - if (*agent->remote.ice_ufrag != '\0' && - strcmp(remote_ufrag, agent->remote.ice_ufrag) != 0) { - JLOG_WARN("STUN remote ufrag check failed, expected=\"%s\", actual=\"%s\"", - agent->remote.ice_ufrag, remote_ufrag); - return -1; - } - } - // Check password - const char *password = - msg->msg_class == STUN_CLASS_REQUEST ? agent->local.ice_pwd : agent->remote.ice_pwd; - if (*password == '\0') { - JLOG_WARN("STUN integrity check failed, unknown password"); - return -1; - } - if (!stun_check_integrity(buf, size, msg, password)) { - JLOG_WARN("STUN integrity check failed, password=\"%s\"", password); - return -1; - } - return 0; -} - -int agent_verify_credentials(juice_agent_t *agent, const agent_stun_entry_t *entry, void *buf, - size_t size, stun_message_t *msg) { - (void)agent; - - // RFC 8489: If the response is an error response with an error code of 400 (Bad Request) and - // does not contain either the MESSAGE-INTEGRITY or MESSAGE-INTEGRITY-SHA256 attribute, then the - // response MUST be discarded, as if it were never received. This means that retransmits, if - // applicable, will continue. - if (msg->msg_class == STUN_CLASS_INDICATION || - (msg->msg_class == STUN_CLASS_RESP_ERROR && msg->error_code != 400)) - return 0; - - if (!msg->has_integrity) { - JLOG_WARN("Missing integrity in STUN message"); - return -1; - } - if (!entry->turn) { - JLOG_WARN("No credentials for entry"); - return -1; - } - stun_credentials_t *credentials = &entry->turn->credentials; - const char *password = entry->turn->password; - - // Prepare credentials - strcpy(msg->credentials.realm, credentials->realm); - strcpy(msg->credentials.nonce, credentials->nonce); - strcpy(msg->credentials.username, credentials->username); - - // Check credentials - if (!stun_check_integrity(buf, size, msg, password)) { - JLOG_WARN("STUN integrity check failed"); - return -1; - } - return 0; -} - -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) { - if (msg->msg_method == STUN_METHOD_BINDING && msg->has_integrity) { - JLOG_VERBOSE("STUN message is from the remote peer"); - // Verify the message now - if (agent_verify_stun_binding(agent, buf, size, msg)) { - JLOG_WARN("STUN message verification failed"); - return -1; - } - if (!relayed) { - if (agent_add_remote_reflexive_candidate(agent, ICE_CANDIDATE_TYPE_PEER_REFLEXIVE, - msg->priority, src)) { - JLOG_WARN("Failed to add remote peer reflexive candidate from STUN message"); - } - } - } - - agent_stun_entry_t *entry = NULL; - if (STUN_IS_RESPONSE(msg->msg_class)) { - JLOG_VERBOSE("STUN message is a response, looking for transaction ID"); - entry = agent_find_entry_from_transaction_id(agent, msg->transaction_id); - if (!entry) { - JLOG_WARN("No STUN entry matching transaction ID, ignoring"); - return -1; - } - } else { - JLOG_VERBOSE("STUN message is a request or indication, looking for remote address"); - entry = agent_find_entry_from_record(agent, src, relayed); - if (entry) { - JLOG_VERBOSE("Found STUN entry matching remote address"); - } else { - // This may happen normally, for instance when there is no space left for reflexive - // candidates - JLOG_DEBUG("No STUN entry matching remote address, ignoring"); - return 0; - } - } - - switch (msg->msg_method) { - case STUN_METHOD_BINDING: - // Message was verified earlier, no need to re-verify - if (entry->type == AGENT_STUN_ENTRY_TYPE_CHECK && !msg->has_integrity && - (msg->msg_class == STUN_CLASS_REQUEST || msg->msg_class == STUN_CLASS_RESP_SUCCESS)) { - JLOG_WARN("Missing integrity in STUN Binding message from remote peer, ignoring"); - return -1; - } - return agent_process_stun_binding(agent, msg, entry, src, relayed); - - case STUN_METHOD_ALLOCATE: - case STUN_METHOD_REFRESH: - if (agent_verify_credentials(agent, entry, buf, size, msg)) { - JLOG_WARN("Ignoring invalid TURN Allocate message"); - return -1; - } - return agent_process_turn_allocate(agent, msg, entry); - - case STUN_METHOD_CREATE_PERMISSION: - if (agent_verify_credentials(agent, entry, buf, size, msg)) { - JLOG_WARN("Ignoring invalid TURN CreatePermission message"); - return -1; - } - return agent_process_turn_create_permission(agent, msg, entry); - - case STUN_METHOD_CHANNEL_BIND: - if (agent_verify_credentials(agent, entry, buf, size, msg)) { - JLOG_WARN("Ignoring invalid TURN ChannelBind message"); - return -1; - } - return agent_process_turn_channel_bind(agent, msg, entry); - - case STUN_METHOD_DATA: - return agent_process_turn_data(agent, msg, entry); - - default: - JLOG_WARN("Unknown STUN method 0x%X, ignoring", msg->msg_method); - return -1; - } -} - -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) { - - switch (msg->msg_class) { - case STUN_CLASS_REQUEST: { - JLOG_DEBUG("Received STUN Binding request"); - if (entry->type != AGENT_STUN_ENTRY_TYPE_CHECK) - return -1; - - ice_candidate_pair_t *pair = entry->pair; - if (msg->ice_controlling == msg->ice_controlled) { - JLOG_WARN("Controlling and controlled attributes mismatch in request"); - agent_send_stun_binding(agent, entry, STUN_CLASS_RESP_ERROR, 400, msg->transaction_id, - NULL); - return -1; - } - // RFC8445 7.3.1.1. Detecting and Repairing Role Conflicts: - // If the agent is in the controlling role, and the ICE-CONTROLLING attribute is present in - // the request: - // * If the agent's tiebreaker value is larger than or equal to the contents of the - // ICE-CONTROLLING attribute, the agent generates a Binding error response and includes an - // ERROR-CODE attribute with a value of 487 (Role Conflict) but retains its role. - // * If the agent's tiebreaker value is less than the contents of the ICE-CONTROLLING - // attribute, the agent switches to the controlled role. - if (agent->mode == AGENT_MODE_CONTROLLING && msg->ice_controlling) { - JLOG_WARN("ICE role conflict (both controlling)"); - if (agent->ice_tiebreaker >= msg->ice_controlling) { - JLOG_DEBUG("Asking remote peer to switch roles"); - agent_send_stun_binding(agent, entry, STUN_CLASS_RESP_ERROR, 487, - msg->transaction_id, NULL); - } else { - JLOG_DEBUG("Switching to controlled role"); - agent->mode = AGENT_MODE_CONTROLLED; - agent_update_candidate_pairs(agent); - } - break; - } - // If the agent is in the controlled role, and the ICE-CONTROLLED attribute is present in - // the request: - // * If the agent's tiebreaker value is larger than or equal to the contents of the - // ICE-CONTROLLED attribute, the agent switches to the controlling role. - // * If the agent's tiebreaker value is less than the contents of the ICE-CONTROLLED - // attribute, the agent generates a Binding error response and includes an ERROR-CODE - // attribute with a value of 487 (Role Conflict) but retains its role. - if (msg->ice_controlled && agent->mode == AGENT_MODE_CONTROLLED) { - JLOG_WARN("ICE role conflict (both controlled)"); - if (agent->ice_tiebreaker >= msg->ice_controlling) { - JLOG_DEBUG("Switching to controlling role"); - agent->mode = AGENT_MODE_CONTROLLING; - agent_update_candidate_pairs(agent); - } else { - JLOG_DEBUG("Asking remote peer to switch roles"); - agent_send_stun_binding(agent, entry, STUN_CLASS_RESP_ERROR, 487, - msg->transaction_id, NULL); - } - break; - } - if (msg->use_candidate) { - if (!msg->ice_controlling) { - JLOG_WARN("STUN message use_candidate missing ice_controlling attribute"); - agent_send_stun_binding(agent, entry, STUN_CLASS_RESP_ERROR, 400, - msg->transaction_id, NULL); - return -1; - } - // RFC 8445 7.3.1.5. Updating the Nominated Flag: - // If the state of this pair is Succeeded, it means that the check previously sent by - // this pair produced a successful response and generated a valid pair. The agent sets - // the nominated flag value of the valid pair to true. - if (pair->state == ICE_CANDIDATE_PAIR_STATE_SUCCEEDED) { - JLOG_DEBUG("Got a nominated pair (controlled)"); - pair->nominated = true; - } else if (!pair->nomination_requested) { - JLOG_DEBUG("Pair nomination requested (controlled)"); - pair->nomination_requested = true; - } - } - // Response - if (agent_send_stun_binding(agent, entry, STUN_CLASS_RESP_SUCCESS, 0, msg->transaction_id, - src)) { - JLOG_ERROR("Failed to send STUN Binding response"); - return -1; - } - // Triggered check - // RFC 8445: If the state of that pair is Succeeded, nothing further is done. If the state - // of that pair is In-Progress, [...] the agent MUST [...] trigger a new connectivity check - // of the pair. [...] If the state of that pair is Waiting, Frozen, or Failed, the agent - // MUST [...] trigger a new connectivity check of the pair. - if (pair->state != ICE_CANDIDATE_PAIR_STATE_SUCCEEDED && *agent->remote.ice_ufrag != '\0') { - JLOG_DEBUG("Triggered pair check"); - pair->state = ICE_CANDIDATE_PAIR_STATE_PENDING; - entry->state = AGENT_STUN_ENTRY_STATE_PENDING; - agent_arm_transmission(agent, entry, STUN_PACING_TIME); - } - break; - } - case STUN_CLASS_RESP_SUCCESS: { - JLOG_DEBUG("Received STUN Binding success response from %s", - entry->type == AGENT_STUN_ENTRY_TYPE_CHECK ? "peer" : "server"); - - if (entry->type == AGENT_STUN_ENTRY_TYPE_SERVER) - JLOG_INFO("STUN server binding successful"); - - if (entry->state != AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE) { - entry->state = AGENT_STUN_ENTRY_STATE_SUCCEEDED; - entry->next_transmission = 0; - } - - if (!agent->selected_pair || !agent->selected_pair->nominated) { - // We want to send keepalives now - entry->state = AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE; - agent_arm_keepalive(agent, entry); - } - - if (msg->mapped.len && !relayed) { - JLOG_VERBOSE("Response has mapped address"); - - if (JLOG_INFO_ENABLED && entry->type != AGENT_STUN_ENTRY_TYPE_CHECK) { - char mapped_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(&msg->mapped, mapped_str, ADDR_MAX_STRING_LEN); - JLOG_INFO("Got STUN mapped address %s from server", mapped_str); - } - - ice_candidate_type_t type = (entry->type == AGENT_STUN_ENTRY_TYPE_CHECK) - ? ICE_CANDIDATE_TYPE_PEER_REFLEXIVE - : ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE; - if (agent_add_local_reflexive_candidate(agent, type, &msg->mapped)) { - JLOG_WARN("Failed to add local peer reflexive candidate from STUN mapped address"); - } - } - - if (entry->type == AGENT_STUN_ENTRY_TYPE_CHECK) { - // 7.2.5.2.1. Non-Symmetric Transport Addresses: - // The ICE agent MUST check that the source and destination transport addresses in the - // Binding request and response are symmetric. [...] If the addresses are not symmetric, - // the agent MUST set the candidate pair state to Failed. - if (!addr_record_is_equal(src, &entry->record, true)) { - JLOG_DEBUG( - "Candidate pair check failed (non-symmetric source address in response)"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - break; - } - - ice_candidate_pair_t *pair = entry->pair; - if (!pair) { - JLOG_ERROR("STUN entry for candidate pair checking has no candidate pair"); - return -1; - } - - if (pair->state != ICE_CANDIDATE_PAIR_STATE_SUCCEEDED) { - JLOG_DEBUG("Candidate pair check succeeded"); - pair->state = ICE_CANDIDATE_PAIR_STATE_SUCCEEDED; - } - - if (!pair->local && msg->mapped.len) - pair->local = ice_find_candidate_from_addr(&agent->local, &msg->mapped, - ICE_CANDIDATE_TYPE_UNKNOWN); - - // Update consent timestamp - pair->consent_expiry = current_timestamp() + CONSENT_TIMEOUT; - - // RFC 8445 7.3.1.5. Updating the Nominated Flag: - // [...] once the check is sent and if it generates a successful response, and - // generates a valid pair, the agent sets the nominated flag of the pair to true. - if (pair->nomination_requested) { - JLOG_DEBUG("Got a nominated pair (%s)", - agent->mode == AGENT_MODE_CONTROLLING ? "controlling" : "controlled"); - pair->nominated = true; - } - } else if (entry->type == AGENT_STUN_ENTRY_TYPE_SERVER) { - agent_update_gathering_done(agent); - } - break; - } - case STUN_CLASS_RESP_ERROR: { - if (msg->error_code != STUN_ERROR_INTERNAL_VALIDATION_FAILED) { - if (msg->error_code == 487) - JLOG_DEBUG("Got STUN Binding error response, code=%u", - (unsigned int)msg->error_code); - else - JLOG_WARN("Got STUN Binding error response, code=%u", - (unsigned int)msg->error_code); - } - - if (entry->type == AGENT_STUN_ENTRY_TYPE_CHECK) { - if (msg->error_code == 487) { - if (entry->mode == agent->mode) { - // RFC 8445 7.2.5.1. Role Conflict: - // If the Binding request generates a 487 (Role Conflict) error response, and if - // the ICE agent included an ICE-CONTROLLED attribute in the request, the agent - // MUST switch to the controlling role. If the agent included an ICE-CONTROLLING - // attribute in the request, the agent MUST switch to the controlled role. Once - // the agent has switched its role, the agent MUST [...] set the candidate pair - // state to Waiting [and] change the tiebreaker value. - JLOG_WARN("ICE role conflict"); - JLOG_DEBUG("Switching roles to %s as requested", - entry->mode == AGENT_MODE_CONTROLLING ? "controlled" - : "controlling"); - agent->mode = entry->mode == AGENT_MODE_CONTROLLING ? AGENT_MODE_CONTROLLED - : AGENT_MODE_CONTROLLING; - agent_update_candidate_pairs(agent); - - juice_random(&agent->ice_tiebreaker, sizeof(agent->ice_tiebreaker)); - if (entry->state != AGENT_STUN_ENTRY_STATE_IDLE) { // Check might not be started - entry->state = AGENT_STUN_ENTRY_STATE_PENDING; - agent_arm_transmission(agent, entry, 0); - } - } else { - JLOG_DEBUG("Already switched roles to %s as requested", - agent->mode == AGENT_MODE_CONTROLLING ? "controlling" - : "controlled"); - } - } else { - // 7.2.5.2.4. Unrecoverable STUN Response: - // If the Binding request generates a STUN error response that is unrecoverable - // [RFC5389], the ICE agent SHOULD set the candidate pair state to Failed. - JLOG_DEBUG("Chandidate pair check failed (unrecoverable error)"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - } - } else if (entry->type == AGENT_STUN_ENTRY_TYPE_SERVER) { - JLOG_INFO("STUN server binding failed (unrecoverable error)"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - agent_update_gathering_done(agent); - } - break; - } - case STUN_CLASS_INDICATION: { - JLOG_VERBOSE("Received STUN Binding indication"); - break; - } - default: { - JLOG_WARN("Got STUN unexpected binding message, class=%u", (unsigned int)msg->msg_class); - return -1; - } - } - return 0; -} - -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) { - // Send STUN Binding - JLOG_DEBUG("Sending STUN Binding %s", - msg_class == STUN_CLASS_REQUEST - ? "request" - : (msg_class == STUN_CLASS_INDICATION ? "indication" : "response")); - - stun_message_t msg; - memset(&msg, 0, sizeof(msg)); - msg.msg_class = msg_class; - msg.msg_method = STUN_METHOD_BINDING; - - if ((msg_class == STUN_CLASS_RESP_SUCCESS || msg_class == STUN_CLASS_RESP_ERROR) && - !transaction_id) { - JLOG_ERROR("No transaction ID specified for STUN response"); - return -1; - } - - if (transaction_id) - memcpy(msg.transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE); - else if (msg_class == STUN_CLASS_INDICATION) - juice_random(msg.transaction_id, STUN_TRANSACTION_ID_SIZE); - else - memcpy(msg.transaction_id, entry->transaction_id, STUN_TRANSACTION_ID_SIZE); - - const char *password = NULL; - if (entry->type == AGENT_STUN_ENTRY_TYPE_CHECK) { - // RFC 8445 7.2.2. Forming Credentials: - // A connectivity-check Binding request MUST utilize the STUN short-term credential - // mechanism. The username for the credential is formed by concatenating the username - // fragment provided by the peer with the username fragment of the ICE agent sending the - // request, separated by a colon (":"). The password is equal to the password provided by - // the peer. - switch (msg_class) { - case STUN_CLASS_REQUEST: { - if (*agent->remote.ice_ufrag == '\0' || *agent->remote.ice_pwd == '\0') { - JLOG_DEBUG("Missing remote ICE credentials, dropping STUN binding request"); - return 0; - } - snprintf(msg.credentials.username, STUN_MAX_USERNAME_LEN, "%s:%s", - agent->remote.ice_ufrag, agent->local.ice_ufrag); - password = agent->remote.ice_pwd; - msg.ice_controlling = agent->mode == AGENT_MODE_CONTROLLING ? agent->ice_tiebreaker : 0; - msg.ice_controlled = agent->mode == AGENT_MODE_CONTROLLED ? agent->ice_tiebreaker : 0; - - // RFC 8445 7.1.1. PRIORITY - // The PRIORITY attribute MUST be included in a Binding request and be set to the value - // computed by the algorithm in Section 5.1.2 for the local candidate, but with the - // candidate type preference of peer-reflexive candidates. - int family = entry->record.addr.ss_family; - int index = entry->pair && entry->pair->local - ? (int)(entry->pair->local - agent->local.candidates) - : 0; - msg.priority = - ice_compute_priority(ICE_CANDIDATE_TYPE_PEER_REFLEXIVE, family, 1, index); - - // RFC 8445 8.1.1. Nominating Pairs: - // Once the controlling agent has picked a valid pair for nomination, it repeats the - // connectivity check that produced this valid pair [...], this time with the - // USE-CANDIDATE attribute. - msg.use_candidate = agent->mode == AGENT_MODE_CONTROLLING && entry->pair && - entry->pair->nomination_requested; - - entry->mode = agent->mode; // save current mode in case of conflict - break; - } - case STUN_CLASS_RESP_SUCCESS: - case STUN_CLASS_RESP_ERROR: { - password = agent->local.ice_pwd; - msg.error_code = error_code; - if (mapped) - msg.mapped = *mapped; - - break; - } - case STUN_CLASS_INDICATION: { - // RFC8445 11. Keepalives: - // When STUN is being used for keepalives, a STUN Binding Indication is used. The - // Indication MUST NOT utilize any authentication mechanism. It SHOULD contain the - // FINGERPRINT attribute to aid in demultiplexing, but it SHOULD NOT contain any other - // attributes. - } - } - } - - char buffer[BUFFER_SIZE]; - int size = stun_write(buffer, BUFFER_SIZE, &msg, password); - if (size <= 0) { - JLOG_ERROR("STUN message write failed"); - return -1; - } - - if (entry->relay_entry) { - // The datagram must be sent through the relay - JLOG_DEBUG("Sending STUN message via relay"); - int ret; - if (agent->state == JUICE_STATE_COMPLETED) - ret = agent_channel_send(agent, entry->relay_entry, &entry->record, buffer, size, 0); - else - ret = agent_relay_send(agent, entry->relay_entry, &entry->record, buffer, size, 0); - - if (ret < 0) { - JLOG_WARN("STUN message send via relay failed"); - return -1; - } - return 0; - } - - // Direct send - if (agent_direct_send(agent, &entry->record, buffer, size, 0) < 0) { - JLOG_WARN("STUN message send failed"); - return -1; - } - return 0; -} - -int agent_process_turn_allocate(juice_agent_t *agent, const stun_message_t *msg, - agent_stun_entry_t *entry) { - if (msg->msg_method != STUN_METHOD_ALLOCATE && msg->msg_method != STUN_METHOD_REFRESH) - return -1; - - if (entry->type != AGENT_STUN_ENTRY_TYPE_RELAY) { - JLOG_WARN("Received TURN %s message for a non-relay entry, ignoring", - msg->msg_method == STUN_METHOD_ALLOCATE ? "Allocate" : "Refresh"); - return -1; - } - if (!entry->turn) { - JLOG_ERROR("Missing TURN state on relay entry"); - return -1; - } - - switch (msg->msg_class) { - case STUN_CLASS_RESP_SUCCESS: { - JLOG_DEBUG("Received TURN %s success response", - msg->msg_method == STUN_METHOD_ALLOCATE ? "Allocate" : "Refresh"); - - if (msg->msg_method == STUN_METHOD_REFRESH) { - JLOG_DEBUG("TURN refresh successful"); - // There is nothing to do - break; - } - - JLOG_DEBUG("TURN allocate successful"); - - if (!msg->relayed.len) { - JLOG_ERROR("Expected relayed address in TURN Allocate response"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - return -1; - } - - if (entry->state != AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE) { - entry->state = AGENT_STUN_ENTRY_STATE_SUCCEEDED; - entry->next_transmission = 0; - } - - if (!agent->selected_pair || !agent->selected_pair->nominated) { - // We want to send refresh requests for keepalive now - entry->state = AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE; - agent_arm_keepalive(agent, entry); - } - - if (msg->mapped.len) { - JLOG_VERBOSE("Response has mapped address"); - - if (JLOG_INFO_ENABLED) { - char mapped_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(&msg->mapped, mapped_str, ADDR_MAX_STRING_LEN); - JLOG_INFO("Got STUN mapped address %s from TURN server", mapped_str); - } - - if (agent_add_local_reflexive_candidate(agent, ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE, - &msg->mapped)) { - JLOG_WARN("Failed to add local peer reflexive candidate from TURN mapped address"); - } - } - - entry->relayed = msg->relayed; - if (agent_add_local_relayed_candidate(agent, &msg->relayed)) { - JLOG_WARN("Failed to add local relayed candidate from TURN relayed address"); - return -1; - } - - if (JLOG_INFO_ENABLED) { - char relayed_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(&entry->relayed, relayed_str, ADDR_MAX_STRING_LEN); - JLOG_INFO("Allocated TURN relayed address %s", relayed_str); - } - - agent_update_gathering_done(agent); - break; - } - case STUN_CLASS_RESP_ERROR: { - if (msg->error_code == 401) { // Unauthorized - JLOG_DEBUG("Got TURN %s Unauthorized response", - msg->msg_method == STUN_METHOD_ALLOCATE ? "Allocate" : "Refresh"); - if (*entry->turn->credentials.realm != '\0') { - JLOG_ERROR("TURN authentication failed"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - agent_update_gathering_done(agent); - return -1; - } - if (*msg->credentials.realm == '\0' || *msg->credentials.nonce == '\0') { - JLOG_ERROR("Expected realm and nonce in TURN error response"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - agent_update_gathering_done(agent); - return -1; - } - - stun_process_credentials(&msg->credentials, &entry->turn->credentials); - - // Resend request when possible - agent_arm_transmission(agent, entry, 0); - - } else if (msg->error_code == 438) { // Stale Nonce - JLOG_DEBUG("Got TURN %s Stale Nonce response", - msg->msg_method == STUN_METHOD_ALLOCATE ? "Allocate" : "Refresh"); - if (*msg->credentials.realm == '\0' || *msg->credentials.nonce == '\0') { - JLOG_ERROR("Expected realm and nonce in TURN error response"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - agent_update_gathering_done(agent); - return -1; - } - - stun_process_credentials(&msg->credentials, &entry->turn->credentials); - - // Resend request when possible - agent_arm_transmission(agent, entry, 0); - - } else if (msg->msg_method == STUN_METHOD_ALLOCATE && - msg->error_code == 300) { // Try Alternate - // RFC 8489 10. ALTERNATE-SERVER Mechanism: - // A client using this extension handles a 300 (Try Alternate) error code as follows. - // The client looks for an ALTERNATE-SERVER attribute in the error response. If one is - // found, then the client considers the current transaction as failed and reattempts the - // request with the server specified in the attribute, using the same transport protocol - // used for the previous request. - if (!msg->alternate_server.len || - addr_record_is_equal(&msg->alternate_server, &entry->record, true)) { - JLOG_ERROR("Expected alternate server in TURN Allocate 300 Try Alternate response"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - agent_update_gathering_done(agent); - return -1; - } - // Prevent infinite redirection loop - if (entry->turn_redirections >= MAX_TURN_REDIRECTIONS) { - JLOG_ERROR("Too many redirections for TURN Allocate"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - agent_update_gathering_done(agent); - return -1; - } - - if (JLOG_INFO_ENABLED) { - char alternate_server_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(&msg->alternate_server, alternate_server_str, - ADDR_MAX_STRING_LEN); - JLOG_INFO("Trying alternate TURN server %s", alternate_server_str); - } - - // Change record and resend request when possible - ++entry->turn_redirections; - entry->record = msg->alternate_server; - agent_arm_transmission(agent, entry, 0); - - } else { - if (msg->error_code != STUN_ERROR_INTERNAL_VALIDATION_FAILED) - JLOG_WARN("Got TURN %s error response, code=%u", - msg->msg_method == STUN_METHOD_ALLOCATE ? "Allocate" : "Refresh", - (unsigned int)msg->error_code); - - JLOG_INFO("TURN allocation failed"); - entry->state = AGENT_STUN_ENTRY_STATE_FAILED; - agent_update_gathering_done(agent); - } - break; - } - default: { - JLOG_WARN("Got unexpected TURN %s message, class=%u", - msg->msg_method == STUN_METHOD_ALLOCATE ? "Allocate" : "Refresh", - (unsigned int)msg->msg_class); - return -1; - } - } - return 0; -} - -int agent_send_turn_allocate_request(juice_agent_t *agent, const agent_stun_entry_t *entry, - stun_method_t method) { - if (method != STUN_METHOD_ALLOCATE && method != STUN_METHOD_REFRESH) - return -1; - - JLOG_DEBUG("Sending TURN %s request", method == STUN_METHOD_ALLOCATE ? "Allocate" : "Refresh"); - - if (entry->type != AGENT_STUN_ENTRY_TYPE_RELAY) { - JLOG_ERROR("Attempted to send a TURN %s request for a non-relay entry", - method == STUN_METHOD_ALLOCATE ? "Allocate" : "Refresh"); - return -1; - } - if (!entry->turn) { - JLOG_ERROR("Missing TURN state on relay entry"); - return -1; - } - - stun_message_t msg; - memset(&msg, 0, sizeof(msg)); - msg.msg_class = STUN_CLASS_REQUEST; - msg.msg_method = method; - memcpy(msg.transaction_id, entry->transaction_id, STUN_TRANSACTION_ID_SIZE); - - msg.credentials = entry->turn->credentials; - msg.lifetime = TURN_LIFETIME / 1000; // seconds - - // Include allocation attributes in Allocate request only - if (method == STUN_METHOD_ALLOCATE) { - msg.requested_transport = true; - } - - const char *password = *msg.credentials.nonce != '\0' ? entry->turn->password : NULL; - - char buffer[BUFFER_SIZE]; - int size = stun_write(buffer, BUFFER_SIZE, &msg, password); - if (size <= 0) { - JLOG_ERROR("STUN message write failed"); - return -1; - } - if (agent_direct_send(agent, &entry->record, buffer, size, 0) < 0) { - JLOG_WARN("STUN message send failed"); - return -1; - } - return 0; -} - -int agent_process_turn_create_permission(juice_agent_t *agent, const stun_message_t *msg, - agent_stun_entry_t *entry) { - (void)(agent); - if (entry->type != AGENT_STUN_ENTRY_TYPE_RELAY) { - JLOG_WARN("Received TURN CreatePermission message for a non-relay entry, ignoring"); - return -1; - } - if (!entry->turn) { - JLOG_ERROR("Missing TURN state on relay entry"); - return -1; - } - - switch (msg->msg_class) { - case STUN_CLASS_RESP_SUCCESS: { - JLOG_DEBUG("Received TURN CreatePermission success response"); - if (!turn_set_permission(&entry->turn->map, msg->transaction_id, NULL, - PERMISSION_LIFETIME / 2)) - JLOG_WARN("Transaction ID from TURN CreatePermission response does not match"); - break; - } - case STUN_CLASS_RESP_ERROR: { - if (msg->error_code == 438) { // Stale Nonce - JLOG_DEBUG("Got TURN CreatePermission Stale Nonce response"); - if (*msg->credentials.realm == '\0' || *msg->credentials.nonce == '\0') { - JLOG_ERROR("Expected realm and nonce in TURN error response"); - return -1; - } - - stun_process_credentials(&msg->credentials, &entry->turn->credentials); - - // Resend - addr_record_t record; - if (turn_retrieve_transaction_id(&entry->turn->map, msg->transaction_id, &record)) - agent_send_turn_create_permission_request(agent, entry, &record, 0); - - } else if (msg->error_code != STUN_ERROR_INTERNAL_VALIDATION_FAILED) { - JLOG_WARN("Got TURN CreatePermission error response, code=%u", - (unsigned int)msg->error_code); - } - break; - } - default: { - JLOG_WARN("Got unexpected TURN CreatePermission message, class=%u", - (unsigned int)msg->msg_class); - return -1; - } - } - return 0; -} - -int agent_send_turn_create_permission_request(juice_agent_t *agent, agent_stun_entry_t *entry, - const addr_record_t *record, int ds) { - if (JLOG_DEBUG_ENABLED) { - char record_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(record, record_str, ADDR_MAX_STRING_LEN); - JLOG_DEBUG("Sending TURN CreatePermission request for %s", record_str); - } - - if (entry->type != AGENT_STUN_ENTRY_TYPE_RELAY) { - JLOG_ERROR("Attempted to send a TURN CreatePermission request for a non-relay entry"); - return -1; - } - if (!entry->turn) { - JLOG_ERROR("Missing TURN state on relay entry"); - return -1; - } - const stun_credentials_t *credentials = &entry->turn->credentials; - - if (*credentials->realm == '\0' || *credentials->nonce == '\0') { - JLOG_ERROR("Missing realm and nonce to send TURN CreatePermission request"); - return -1; - } - - stun_message_t msg; - memset(&msg, 0, sizeof(msg)); - msg.msg_class = STUN_CLASS_REQUEST; - msg.msg_method = STUN_METHOD_CREATE_PERMISSION; - if (!turn_set_random_permission_transaction_id(&entry->turn->map, record, msg.transaction_id)) - return -1; - - msg.credentials = entry->turn->credentials; - msg.peer = *record; - - char buffer[BUFFER_SIZE]; - int size = stun_write(buffer, BUFFER_SIZE, &msg, entry->turn->password); - if (size <= 0) { - JLOG_ERROR("STUN message write failed"); - return -1; - } - if (agent_direct_send(agent, &entry->record, buffer, size, ds) < 0) { - JLOG_WARN("STUN message send failed"); - return -1; - } - return 0; -} - -int agent_process_turn_channel_bind(juice_agent_t *agent, const stun_message_t *msg, - agent_stun_entry_t *entry) { - (void)agent; - if (entry->type != AGENT_STUN_ENTRY_TYPE_RELAY) { - JLOG_WARN("Received TURN ChannelBind message for a non-relay entry, ignoring"); - return -1; - } - if (!entry->turn) { - JLOG_ERROR("Missing TURN state on relay entry"); - return -1; - } - - switch (msg->msg_class) { - case STUN_CLASS_RESP_SUCCESS: { - JLOG_DEBUG("Received TURN ChannelBind success response"); - if (!turn_bind_current_channel(&entry->turn->map, msg->transaction_id, NULL, - BIND_LIFETIME / 2)) - JLOG_WARN("Transaction ID from TURN ChannelBind response does not match"); - break; - } - case STUN_CLASS_RESP_ERROR: { - if (msg->error_code == 438) { // Stale Nonce - JLOG_DEBUG("Got TURN ChannelBind Stale Nonce response"); - if (*msg->credentials.realm == '\0' || *msg->credentials.nonce == '\0') { - JLOG_ERROR("Expected realm and nonce in TURN error response"); - return -1; - } - - stun_process_credentials(&msg->credentials, &entry->turn->credentials); - - // Resend - addr_record_t record; - if (turn_retrieve_transaction_id(&entry->turn->map, msg->transaction_id, &record)) - agent_send_turn_channel_bind_request(agent, entry, &record, 0, NULL); - - } else if (msg->error_code != STUN_ERROR_INTERNAL_VALIDATION_FAILED) { - JLOG_WARN("Got TURN ChannelBind error response, code=%u", - (unsigned int)msg->error_code); - } - break; - } - default: { - JLOG_WARN("Got STUN unexpected ChannelBind message, class=%u", - (unsigned int)msg->msg_class); - return -1; - } - } - return 0; -} - -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) { - if (JLOG_DEBUG_ENABLED) { - char record_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(record, record_str, ADDR_MAX_STRING_LEN); - JLOG_DEBUG("Sending TURN ChannelBind request for %s", record_str); - } - - if (entry->type != AGENT_STUN_ENTRY_TYPE_RELAY) { - JLOG_ERROR("Attempted to send a TURN ChannelBind request for a non-relay entry"); - return -1; - } - if (!entry->turn) { - JLOG_ERROR("Missing TURN state on relay entry"); - return -1; - } - const stun_credentials_t *credentials = &entry->turn->credentials; - const char *password = entry->turn->password; - - if (*credentials->realm == '\0' || *credentials->nonce == '\0') { - JLOG_ERROR("Missing realm and nonce to send TURN ChannelBind request"); - return -1; - } - - uint16_t channel; - if (!turn_get_channel(&entry->turn->map, record, &channel)) - if (!turn_bind_random_channel(&entry->turn->map, record, &channel, 0)) - return -1; - - stun_message_t msg; - memset(&msg, 0, sizeof(msg)); - msg.msg_class = STUN_CLASS_REQUEST; - msg.msg_method = STUN_METHOD_CHANNEL_BIND; - if (!turn_set_random_channel_transaction_id(&entry->turn->map, record, msg.transaction_id)) - return -1; - - msg.credentials = entry->turn->credentials; - msg.channel_number = channel; - msg.peer = *record; - - if (out_channel) - *out_channel = channel; - - char buffer[BUFFER_SIZE]; - int size = stun_write(buffer, BUFFER_SIZE, &msg, password); - if (size <= 0) { - JLOG_ERROR("STUN message write failed"); - return -1; - } - if (agent_direct_send(agent, &entry->record, buffer, size, ds) < 0) { - JLOG_WARN("STUN message send failed"); - return -1; - } - return 0; -} - -int agent_process_turn_data(juice_agent_t *agent, const stun_message_t *msg, - agent_stun_entry_t *entry) { - if (entry->type != AGENT_STUN_ENTRY_TYPE_RELAY) { - JLOG_WARN("Received TURN Data message for a non-relay entry, ignoring"); - return -1; - } - if (msg->msg_class != STUN_CLASS_INDICATION) { - JLOG_WARN("Received non-indication TURN Data message, ignoring"); - return -1; - } - - JLOG_DEBUG("Received TURN Data indication"); - if (!msg->data) { - JLOG_WARN("Missing data in TURN Data indication"); - return -1; - } - if (!msg->peer.len) { - JLOG_WARN("Missing peer address in TURN Data indication"); - return -1; - } - return agent_input(agent, (char *)msg->data, msg->data_size, &msg->peer, &entry->relayed); -} - -int agent_process_channel_data(juice_agent_t *agent, agent_stun_entry_t *entry, char *buf, - size_t len) { - if (len < sizeof(struct channel_data_header)) { - JLOG_WARN("ChannelData is too short"); - return -1; - } - - const struct channel_data_header *header = (const struct channel_data_header *)buf; - buf += sizeof(struct channel_data_header); - len -= sizeof(struct channel_data_header); - uint16_t channel = ntohs(header->channel_number); - uint16_t length = ntohs(header->length); - JLOG_VERBOSE("Received ChannelData, channel=0x%hX, length=%hu", channel, length); - if (length > len) { - JLOG_WARN("ChannelData has invalid length"); - return -1; - } - - addr_record_t src; - if (!turn_find_channel(&entry->turn->map, channel, &src)) { - JLOG_WARN("Channel not found"); - return -1; - } - - return agent_input(agent, buf, length, &src, &entry->relayed); -} - -int agent_add_local_relayed_candidate(juice_agent_t *agent, const addr_record_t *record) { - if (ice_find_candidate_from_addr(&agent->local, record, ICE_CANDIDATE_TYPE_RELAYED)) { - JLOG_VERBOSE("The relayed local candidate already exists"); - return 0; - } - ice_candidate_t candidate; - if (ice_create_local_candidate(ICE_CANDIDATE_TYPE_RELAYED, 1, agent->local.candidates_count, - record, &candidate)) { - JLOG_ERROR("Failed to create relayed candidate"); - return -1; - } - if (ice_add_candidate(&candidate, &agent->local)) { - JLOG_ERROR("Failed to add candidate to local description"); - return -1; - } - - char buffer[BUFFER_SIZE]; - if (ice_generate_candidate_sdp(&candidate, buffer, BUFFER_SIZE) < 0) { - JLOG_ERROR("Failed to generate SDP for local candidate"); - return -1; - } - JLOG_DEBUG("Gathered relayed candidate: %s", buffer); - - // Relayed candidates must be differenciated, so match them with already known remote candidates - ice_candidate_t *local = agent->local.candidates + agent->local.candidates_count - 1; - for (int i = 0; i < agent->remote.candidates_count; ++i) { - ice_candidate_t *remote = agent->remote.candidates + i; - if (local->resolved.addr.ss_family == remote->resolved.addr.ss_family) - agent_add_candidate_pair(agent, local, remote); - } - - if (agent->config.cb_candidate) - agent->config.cb_candidate(agent, buffer, agent->config.user_ptr); - - return 0; -} - -int agent_add_local_reflexive_candidate(juice_agent_t *agent, ice_candidate_type_t type, - const addr_record_t *record) { - if (type != ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE && type != ICE_CANDIDATE_TYPE_PEER_REFLEXIVE) { - JLOG_ERROR("Invalid type for local reflexive candidate"); - return -1; - } - int family = record->addr.ss_family; - if (ice_find_candidate_from_addr(&agent->local, record, - family == AF_INET6 ? ICE_CANDIDATE_TYPE_UNKNOWN : type)) { - JLOG_VERBOSE("A local candidate exists for the mapped address"); - return 0; - } - ice_candidate_t candidate; - if (ice_create_local_candidate(type, 1, agent->local.candidates_count, record, &candidate)) { - JLOG_ERROR("Failed to create reflexive candidate"); - return -1; - } - if (candidate.type == ICE_CANDIDATE_TYPE_PEER_REFLEXIVE && - ice_candidates_count(&agent->local, ICE_CANDIDATE_TYPE_PEER_REFLEXIVE) >= - MAX_PEER_REFLEXIVE_CANDIDATES_COUNT) { - JLOG_INFO( - "Local description has the maximum number of peer reflexive candidates, ignoring"); - return 0; - } - if (ice_add_candidate(&candidate, &agent->local)) { - JLOG_ERROR("Failed to add candidate to local description"); - return -1; - } - - char buffer[BUFFER_SIZE]; - if (ice_generate_candidate_sdp(&candidate, buffer, BUFFER_SIZE) < 0) { - JLOG_ERROR("Failed to generate SDP for local candidate"); - return -1; - } - JLOG_DEBUG("Gathered reflexive candidate: %s", buffer); - - if (type != ICE_CANDIDATE_TYPE_PEER_REFLEXIVE && agent->config.cb_candidate) - agent->config.cb_candidate(agent, buffer, agent->config.user_ptr); - - return 0; -} - -int agent_add_remote_reflexive_candidate(juice_agent_t *agent, ice_candidate_type_t type, - uint32_t priority, const addr_record_t *record) { - if (type != ICE_CANDIDATE_TYPE_PEER_REFLEXIVE) { - JLOG_ERROR("Invalid type for remote reflexive candidate"); - return -1; - } - if (ice_find_candidate_from_addr(&agent->remote, record, ICE_CANDIDATE_TYPE_UNKNOWN)) { - JLOG_VERBOSE("A remote candidate exists for the remote address"); - return 0; - } - ice_candidate_t candidate; - if (ice_create_local_candidate(type, 1, agent->local.candidates_count, record, &candidate)) { - JLOG_ERROR("Failed to create reflexive candidate"); - return -1; - } - if (ice_candidates_count(&agent->remote, ICE_CANDIDATE_TYPE_PEER_REFLEXIVE) >= - MAX_PEER_REFLEXIVE_CANDIDATES_COUNT) { - JLOG_INFO( - "Remote description has the maximum number of peer reflexive candidates, ignoring"); - return 0; - } - if (ice_add_candidate(&candidate, &agent->remote)) { - JLOG_ERROR("Failed to add candidate to remote description"); - return -1; - } - - JLOG_DEBUG("Obtained a new remote reflexive candidate, priority=%lu", (unsigned long)priority); - - ice_candidate_t *remote = agent->remote.candidates + agent->remote.candidates_count - 1; - remote->priority = priority; - - return agent_add_candidate_pairs_for_remote(agent, remote); -} - -int agent_add_candidate_pair(juice_agent_t *agent, ice_candidate_t *local, // local may be NULL - ice_candidate_t *remote) { - ice_candidate_pair_t pair; - bool is_controlling = agent->mode == AGENT_MODE_CONTROLLING; - if (ice_create_candidate_pair(local, remote, is_controlling, &pair)) { - JLOG_ERROR("Failed to create candidate pair"); - return -1; - } - - if (agent->candidate_pairs_count >= MAX_CANDIDATE_PAIRS_COUNT) { - JLOG_WARN("Session already has the maximum number of candidate pairs"); - return -1; - } - - JLOG_VERBOSE("Adding new candidate pair, priority=%" PRIu64, pair.priority); - - // Add pair - ice_candidate_pair_t *pos = agent->candidate_pairs + agent->candidate_pairs_count; - *pos = pair; - ++agent->candidate_pairs_count; - - agent_update_ordered_pairs(agent); - - if (agent->entries_count == MAX_STUN_ENTRIES_COUNT) { - JLOG_WARN("No free STUN entry left for candidate pair checking"); - return -1; - } - - agent_stun_entry_t *relay_entry = NULL; - if (local && local->type == ICE_CANDIDATE_TYPE_RELAYED) { - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *other_entry = agent->entries + i; - if (other_entry->type == AGENT_STUN_ENTRY_TYPE_RELAY && - addr_record_is_equal(&other_entry->relayed, &local->resolved, true)) { - relay_entry = other_entry; - break; - } - } - if (!relay_entry) { - JLOG_ERROR("Relay entry not found"); - return -1; - } - } - - JLOG_VERBOSE("Registering STUN entry %d for candidate pair checking", agent->entries_count); - agent_stun_entry_t *entry = agent->entries + agent->entries_count; - entry->type = AGENT_STUN_ENTRY_TYPE_CHECK; - entry->state = AGENT_STUN_ENTRY_STATE_IDLE; - entry->mode = AGENT_MODE_UNKNOWN; - entry->pair = pos; - entry->record = pos->remote->resolved; - entry->relay_entry = relay_entry; - juice_random(entry->transaction_id, STUN_TRANSACTION_ID_SIZE); - ++agent->entries_count; - - if (remote->type == ICE_CANDIDATE_TYPE_HOST) - agent_translate_host_candidate_entry(agent, entry); - - if (agent->mode == AGENT_MODE_CONTROLLING) { - for (int i = 0; i < agent->candidate_pairs_count; ++i) { - ice_candidate_pair_t *ordered_pair = agent->ordered_pairs[i]; - if (ordered_pair == pos) { - JLOG_VERBOSE("Candidate pair has priority"); - break; - } - if (ordered_pair->state == ICE_CANDIDATE_PAIR_STATE_SUCCEEDED) { - // We found a succeeded pair with higher priority, ignore this one - JLOG_VERBOSE("Candidate pair doesn't have priority, keeping it frozen"); - return 0; - } - } - } - - // There is only one component, therefore we can unfreeze the pair and schedule it when possible - if (*agent->remote.ice_ufrag != '\0') { - JLOG_VERBOSE("Unfreezing the new candidate pair"); - agent_unfreeze_candidate_pair(agent, pos); - } - - return 0; -} - -int agent_add_candidate_pairs_for_remote(juice_agent_t *agent, ice_candidate_t *remote) { - // Here is the trick: local non-relayed candidates are undifferentiated for sending. - // Therefore, we don't need to match remote candidates with local ones. - if (agent_add_candidate_pair(agent, NULL, remote)) - return -1; - - // However, we need still to differenciate local relayed candidates - for (int i = 0; i < agent->local.candidates_count; ++i) { - ice_candidate_t *local = agent->local.candidates + i; - if (local->type == ICE_CANDIDATE_TYPE_RELAYED && - local->resolved.addr.ss_family == remote->resolved.addr.ss_family) - if (agent_add_candidate_pair(agent, local, remote)) - return -1; - } - - return 0; -} - -int agent_unfreeze_candidate_pair(juice_agent_t *agent, ice_candidate_pair_t *pair) { - if (pair->state != ICE_CANDIDATE_PAIR_STATE_FROZEN) - return 0; - - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->pair == pair) { - pair->state = ICE_CANDIDATE_PAIR_STATE_PENDING; - entry->state = AGENT_STUN_ENTRY_STATE_PENDING; - agent_arm_transmission(agent, entry, 0); // transmit now - return 0; - } - } - - JLOG_WARN("Unable to unfreeze the pair: no matching entry"); - return -1; -} - -void agent_arm_keepalive(juice_agent_t *agent, agent_stun_entry_t *entry) { - if (entry->state == AGENT_STUN_ENTRY_STATE_SUCCEEDED) - entry->state = AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE; - - if (entry->state != AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE) - return; - - timediff_t period; - switch (entry->type) { - case AGENT_STUN_ENTRY_TYPE_RELAY: - period = agent->remote.candidates_count > 0 ? TURN_REFRESH_PERIOD : STUN_KEEPALIVE_PERIOD; - break; - case AGENT_STUN_ENTRY_TYPE_SERVER: - period = STUN_KEEPALIVE_PERIOD; - break; - default: -#if JUICE_DISABLE_CONSENT_FRESHNESS - period = STUN_KEEPALIVE_PERIOD; -#else - period = MIN_CONSENT_CHECK_PERIOD + - juice_rand32() % (MAX_CONSENT_CHECK_PERIOD - MIN_CONSENT_CHECK_PERIOD + 1); -#endif - break; - } - - agent_arm_transmission(agent, entry, period); -} - -void agent_arm_transmission(juice_agent_t *agent, agent_stun_entry_t *entry, timediff_t delay) { - if (entry->state != AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE) - entry->state = AGENT_STUN_ENTRY_STATE_PENDING; - - // Arm transmission - entry->next_transmission = current_timestamp() + delay; - - if (entry->state == AGENT_STUN_ENTRY_STATE_PENDING) { - bool limit = agent->selected_pair && - (agent->selected_pair->nominated || (agent->selected_pair != entry->pair && - agent->mode == AGENT_MODE_CONTROLLING)); - entry->retransmissions = limit ? 1 : MAX_STUN_RETRANSMISSION_COUNT; - entry->retransmission_timeout = MIN_STUN_RETRANSMISSION_TIMEOUT; - } - - // Find a time slot - agent_stun_entry_t *other = agent->entries; - while (other != agent->entries + agent->entries_count) { - if (other != entry) { - timestamp_t other_transmission = other->next_transmission; - timediff_t timediff = entry->next_transmission - other_transmission; - if (other_transmission && abs((int)timediff) < STUN_PACING_TIME) { - entry->next_transmission = other_transmission + STUN_PACING_TIME; - other = agent->entries; - continue; - } - } - ++other; - } -} - -void agent_update_gathering_done(juice_agent_t *agent) { - JLOG_VERBOSE("Updating gathering status"); - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->type != AGENT_STUN_ENTRY_TYPE_CHECK && - entry->state == AGENT_STUN_ENTRY_STATE_PENDING) { - JLOG_VERBOSE("STUN server or relay entry %d is still pending", i); - return; - } - } - if (!agent->gathering_done) { - JLOG_INFO("Candidate gathering done"); - agent->local.finished = true; - agent->gathering_done = true; - - if (agent->config.cb_gathering_done) - agent->config.cb_gathering_done(agent, agent->config.user_ptr); - } -} - -void agent_update_candidate_pairs(juice_agent_t *agent) { - bool is_controlling = agent->mode == AGENT_MODE_CONTROLLING; - for (int i = 0; i < agent->candidate_pairs_count; ++i) { - ice_candidate_pair_t *pair = agent->candidate_pairs + i; - ice_update_candidate_pair(pair, is_controlling); - } - agent_update_ordered_pairs(agent); -} - -void agent_update_ordered_pairs(juice_agent_t *agent) { - JLOG_VERBOSE("Updating ordered candidate pairs"); - for (int i = 0; i < agent->candidate_pairs_count; ++i) { - ice_candidate_pair_t **begin = agent->ordered_pairs; - ice_candidate_pair_t **end = begin + i; - ice_candidate_pair_t **prev = end; - uint64_t priority = agent->candidate_pairs[i].priority; - while (--prev >= begin && (*prev)->priority < priority) - *(prev + 1) = *prev; - - *(prev + 1) = agent->candidate_pairs + i; - } -} - -static inline bool pair_is_relayed(const ice_candidate_pair_t *pair) { - return pair->local && pair->local->type == ICE_CANDIDATE_TYPE_RELAYED; -} - -static inline bool entry_is_relayed(const agent_stun_entry_t *entry) { - return entry->pair && pair_is_relayed(entry->pair); -} - -agent_stun_entry_t *agent_find_entry_from_transaction_id(juice_agent_t *agent, - const uint8_t *transaction_id) { - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (memcmp(transaction_id, entry->transaction_id, STUN_TRANSACTION_ID_SIZE) == 0) { - JLOG_VERBOSE("STUN entry %d matching incoming transaction ID", i); - return entry; - } - if (entry->turn) { - if (turn_retrieve_transaction_id(&entry->turn->map, transaction_id, NULL)) { - JLOG_VERBOSE("STUN entry %d matching incoming transaction ID (TURN)", i); - return entry; - } - } - } - return NULL; -} - -agent_stun_entry_t *agent_find_entry_from_record(juice_agent_t *agent, const addr_record_t *record, - const addr_record_t *relayed) { - agent_stun_entry_t *selected_entry = atomic_load(&agent->selected_entry); - - if (agent->state == JUICE_STATE_COMPLETED && selected_entry) { - // As an optimization, try to match the selected entry first - if (relayed) { - if (entry_is_relayed(selected_entry) && - addr_record_is_equal(&selected_entry->pair->local->resolved, relayed, true) && - addr_record_is_equal(&selected_entry->record, record, true)) { - JLOG_DEBUG("STUN selected entry matching incoming relayed address"); - return selected_entry; - } - } else { - if (!entry_is_relayed(selected_entry) && - addr_record_is_equal(&selected_entry->record, record, true)) { - JLOG_DEBUG("STUN selected entry matching incoming address"); - return selected_entry; - } - } - } - - if (relayed) { - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry_is_relayed(entry) && - addr_record_is_equal(&entry->pair->local->resolved, relayed, true) && - addr_record_is_equal(&entry->record, record, true)) { - JLOG_DEBUG("STUN entry %d matching incoming relayed address", i); - return entry; - } - } - } else { - // Try to match pairs by priority first - ice_candidate_pair_t *matching_pair = NULL; - for (int i = 0; i < agent->candidate_pairs_count; ++i) { - ice_candidate_pair_t *pair = agent->ordered_pairs[i]; - if (!pair_is_relayed(pair) && - addr_record_is_equal(&pair->remote->resolved, record, true)) { - matching_pair = pair; - break; - } - } - - if (matching_pair) { - // Just find the corresponding entry - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (entry->pair == matching_pair) { - JLOG_DEBUG("STUN entry %d pair matching incoming address", i); - return entry; - } - } - } - - // Try to match entries directly - for (int i = 0; i < agent->entries_count; ++i) { - agent_stun_entry_t *entry = agent->entries + i; - if (!entry_is_relayed(entry) && addr_record_is_equal(&entry->record, record, true)) { - JLOG_DEBUG("STUN entry %d matching incoming address", i); - return entry; - } - } - } - return NULL; -} - -void agent_translate_host_candidate_entry(juice_agent_t *agent, agent_stun_entry_t *entry) { - if (!entry->pair || entry->pair->remote->type != ICE_CANDIDATE_TYPE_HOST) - return; - -#if JUICE_ENABLE_LOCAL_ADDRESS_TRANSLATION - for (int i = 0; i < agent->local.candidates_count; ++i) { - ice_candidate_t *candidate = agent->local.candidates + i; - if (candidate->type != ICE_CANDIDATE_TYPE_HOST) - continue; - - if (addr_record_is_equal(&candidate->resolved, &entry->record, false)) { - JLOG_DEBUG("Entry remote address matches local candidate, translating to localhost"); - struct sockaddr_storage *addr = &entry->record.addr; - switch (addr->ss_family) { - case AF_INET6: { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; - memset(&sin6->sin6_addr, 0, 16); - *((uint8_t *)&sin6->sin6_addr + 15) = 0x01; - break; - } - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)addr; - const uint8_t localhost[4] = {127, 0, 0, 1}; - memcpy(&sin->sin_addr, localhost, 4); - break; - } - default: - // Ignore - break; - } - break; - } - } -#else - (void)agent; -#endif -} diff --git a/thirdparty/libjuice/src/agent.h b/thirdparty/libjuice/src/agent.h deleted file mode 100644 index 71e1b06..0000000 --- a/thirdparty/libjuice/src/agent.h +++ /dev/null @@ -1,228 +0,0 @@ -/** - * 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 -#include - -// 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) -// #define MAX_STUN_RETRANSMISSION_COUNT 2 // 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 diff --git a/thirdparty/libjuice/src/base64.c b/thirdparty/libjuice/src/base64.c deleted file mode 100644 index 206cc6a..0000000 --- a/thirdparty/libjuice/src/base64.c +++ /dev/null @@ -1,90 +0,0 @@ -/** - * 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 -#include - -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); -} diff --git a/thirdparty/libjuice/src/base64.h b/thirdparty/libjuice/src/base64.h deleted file mode 100644 index 21ebf20..0000000 --- a/thirdparty/libjuice/src/base64.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 -#include - -// 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 diff --git a/thirdparty/libjuice/src/conn.c b/thirdparty/libjuice/src/conn.c deleted file mode 100644 index 4d7893a..0000000 --- a/thirdparty/libjuice/src/conn.c +++ /dev/null @@ -1,249 +0,0 @@ -/** - * 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 -#include - -#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); -} diff --git a/thirdparty/libjuice/src/conn.h b/thirdparty/libjuice/src/conn.h deleted file mode 100644 index 4aec7d0..0000000 --- a/thirdparty/libjuice/src/conn.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 -#include - -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 diff --git a/thirdparty/libjuice/src/conn_mux.c b/thirdparty/libjuice/src/conn_mux.c deleted file mode 100644 index 56101ab..0000000 --- a/thirdparty/libjuice/src/conn_mux.c +++ /dev/null @@ -1,540 +0,0 @@ -/** - * 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 -#include - -#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); -} diff --git a/thirdparty/libjuice/src/conn_mux.h b/thirdparty/libjuice/src/conn_mux.h deleted file mode 100644 index 9519f06..0000000 --- a/thirdparty/libjuice/src/conn_mux.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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 -#include - -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 diff --git a/thirdparty/libjuice/src/conn_poll.c b/thirdparty/libjuice/src/conn_poll.c deleted file mode 100644 index 2f5c3ec..0000000 --- a/thirdparty/libjuice/src/conn_poll.c +++ /dev/null @@ -1,433 +0,0 @@ -/** - * 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 -#include - -#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); -} diff --git a/thirdparty/libjuice/src/conn_poll.h b/thirdparty/libjuice/src/conn_poll.h deleted file mode 100644 index b3a162a..0000000 --- a/thirdparty/libjuice/src/conn_poll.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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 -#include - -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 diff --git a/thirdparty/libjuice/src/conn_thread.c b/thirdparty/libjuice/src/conn_thread.c deleted file mode 100644 index 4da821e..0000000 --- a/thirdparty/libjuice/src/conn_thread.c +++ /dev/null @@ -1,278 +0,0 @@ -/** - * 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 -#include - -#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); -} - diff --git a/thirdparty/libjuice/src/conn_thread.h b/thirdparty/libjuice/src/conn_thread.h deleted file mode 100644 index ceb23a4..0000000 --- a/thirdparty/libjuice/src/conn_thread.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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 -#include - -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 diff --git a/thirdparty/libjuice/src/const_time.c b/thirdparty/libjuice/src/const_time.c deleted file mode 100644 index 5529ca2..0000000 --- a/thirdparty/libjuice/src/const_time.c +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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; -} diff --git a/thirdparty/libjuice/src/const_time.h b/thirdparty/libjuice/src/const_time.h deleted file mode 100644 index d654a73..0000000 --- a/thirdparty/libjuice/src/const_time.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 -#include - -int const_time_memcmp(const void *a, const void *b, size_t len); -int const_time_strcmp(const void *a, const void *b); - -#endif diff --git a/thirdparty/libjuice/src/crc32.c b/thirdparty/libjuice/src/crc32.c deleted file mode 100644 index 61cff0a..0000000 --- a/thirdparty/libjuice/src/crc32.c +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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); -} diff --git a/thirdparty/libjuice/src/crc32.h b/thirdparty/libjuice/src/crc32.h deleted file mode 100644 index 9567209..0000000 --- a/thirdparty/libjuice/src/crc32.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 -#include - -JUICE_EXPORT uint32_t juice_crc32(const void *data, size_t size); - -#define CRC32(data, size) juice_crc32(data, size) - -#endif // JUICE_CRC32_H diff --git a/thirdparty/libjuice/src/hash.c b/thirdparty/libjuice/src/hash.c deleted file mode 100644 index 3f969e6..0000000 --- a/thirdparty/libjuice/src/hash.c +++ /dev/null @@ -1,59 +0,0 @@ -/** - * 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 -#include -#include -#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 -} diff --git a/thirdparty/libjuice/src/hash.h b/thirdparty/libjuice/src/hash.h deleted file mode 100644 index 31d392b..0000000 --- a/thirdparty/libjuice/src/hash.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 -#include - -#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 diff --git a/thirdparty/libjuice/src/hmac.c b/thirdparty/libjuice/src/hmac.c deleted file mode 100644 index 179b02f..0000000 --- a/thirdparty/libjuice/src/hmac.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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 -#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 -} diff --git a/thirdparty/libjuice/src/hmac.h b/thirdparty/libjuice/src/hmac.h deleted file mode 100644 index 2eedfc0..0000000 --- a/thirdparty/libjuice/src/hmac.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 -#include - -#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 diff --git a/thirdparty/libjuice/src/ice.c b/thirdparty/libjuice/src/ice.c deleted file mode 100644 index 859032e..0000000 --- a/thirdparty/libjuice/src/ice.c +++ /dev/null @@ -1,408 +0,0 @@ -/** - * 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 -#include -#include -#include -#include - -#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 icandidates_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; -} diff --git a/thirdparty/libjuice/src/ice.h b/thirdparty/libjuice/src/ice.h deleted file mode 100644 index ea17aaf..0000000 --- a/thirdparty/libjuice/src/ice.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * 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 -#include - -#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 diff --git a/thirdparty/libjuice/src/juice.c b/thirdparty/libjuice/src/juice.c deleted file mode 100644 index 6af80c9..0000000 --- a/thirdparty/libjuice/src/juice.c +++ /dev/null @@ -1,207 +0,0 @@ -/** - * 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 - -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 -} diff --git a/thirdparty/libjuice/src/log.c b/thirdparty/libjuice/src/log.c deleted file mode 100644 index bd6dba7..0000000 --- a/thirdparty/libjuice/src/log.c +++ /dev/null @@ -1,129 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifndef _WIN32 -#include -#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); -} diff --git a/thirdparty/libjuice/src/log.h b/thirdparty/libjuice/src/log.h deleted file mode 100644 index 4ac34a6..0000000 --- a/thirdparty/libjuice/src/log.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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 - -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 diff --git a/thirdparty/libjuice/src/picohash.h b/thirdparty/libjuice/src/picohash.h deleted file mode 100644 index 4a58374..0000000 --- a/thirdparty/libjuice/src/picohash.h +++ /dev/null @@ -1,741 +0,0 @@ -/** - * - * 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 -#include -#include - -#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 // 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 diff --git a/thirdparty/libjuice/src/random.c b/thirdparty/libjuice/src/random.c deleted file mode 100644 index 46bd8f8..0000000 --- a/thirdparty/libjuice/src/random.c +++ /dev/null @@ -1,126 +0,0 @@ -/** - * 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 -#include -#include - -// 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 -#include - -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 -// -#include - -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; -} diff --git a/thirdparty/libjuice/src/random.h b/thirdparty/libjuice/src/random.h deleted file mode 100644 index 2985d87..0000000 --- a/thirdparty/libjuice/src/random.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 -#include - -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 diff --git a/thirdparty/libjuice/src/server.c b/thirdparty/libjuice/src/server.c deleted file mode 100644 index 3dcfaf5..0000000 --- a/thirdparty/libjuice/src/server.c +++ /dev/null @@ -1,1143 +0,0 @@ -/** - * 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 "server.h" -#include "const_time.h" -#include "hmac.h" -#include "ice.h" -#include "juice.h" -#include "log.h" -#include "random.h" -#include "stun.h" -#include "turn.h" -#include "udp.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -#define ALLOCATION_LIFETIME 600000 // ms - -// RFC 8656: The Permission Lifetime MUST be 300 seconds (= 5 minutes) -#define PERMISSION_LIFETIME 300000 // ms - -// RFC 8656: Channel bindings last for 10 minutes unless refreshed -#define BIND_LIFETIME 600000 // ms - -#define MAX_RELAYED_RECORDS_COUNT 8 -#define BUFFER_SIZE 4096 - -static char *alloc_string_copy(const char *orig, bool *alloc_failed) { - if (!orig) - return NULL; - - char *copy = malloc(strlen(orig) + 1); - if (!copy) { - if (alloc_failed) - *alloc_failed = true; - - return NULL; - } - strcpy(copy, orig); - return copy; -} - -static server_turn_alloc_t *find_allocation(server_turn_alloc_t allocs[], int size, - const addr_record_t *record, bool allow_deleted) { - unsigned long key = addr_record_hash(record, true) % size; - unsigned long pos = key; - while (!(allocs[pos].state == SERVER_TURN_ALLOC_EMPTY || - (allow_deleted && allocs[pos].state == SERVER_TURN_ALLOC_DELETED) || - addr_record_is_equal(&allocs[pos].record, record, true))) { - pos = (pos + 1) % size; - if (pos == key) { - JLOG_VERBOSE("TURN allocation map is full"); - return NULL; - } - } - return allocs + pos; -} - -static void delete_allocation(server_turn_alloc_t *alloc) { - if (alloc->state != SERVER_TURN_ALLOC_FULL) - return; - - ++alloc->credentials->allocations_quota; - - alloc->state = SERVER_TURN_ALLOC_DELETED; - turn_destroy_map(&alloc->map); - closesocket(alloc->sock); - alloc->sock = INVALID_SOCKET; - alloc->credentials = NULL; -} - -static thread_return_t THREAD_CALL server_thread_entry(void *arg) { - server_run((juice_server_t *)arg); - return (thread_return_t)0; -} - -juice_server_t *server_create(const juice_server_config_t *config) { - JLOG_VERBOSE("Creating server"); - -#ifdef _WIN32 - WSADATA wsaData; - if (WSAStartup(MAKEWORD(2, 2), &wsaData)) { - JLOG_FATAL("WSAStartup failed"); - return NULL; - } -#endif - - juice_server_t *server = calloc(1, sizeof(juice_server_t)); - if (!server) { - JLOG_FATAL("Memory allocation for server data failed"); - return NULL; - } - - udp_socket_config_t socket_config; - memset(&socket_config, 0, sizeof(socket_config)); - socket_config.bind_address = config->bind_address; - socket_config.port_begin = config->port; - socket_config.port_end = config->port; - - server->sock = udp_create_socket(&socket_config); - if (server->sock == INVALID_SOCKET) { - JLOG_FATAL("Server socket opening failed"); - free(server); - return NULL; - } - - mutex_init(&server->mutex, MUTEX_RECURSIVE); - - bool alloc_failed = false; - server->config.max_allocations = - config->max_allocations > 0 ? config->max_allocations : SERVER_DEFAULT_MAX_ALLOCATIONS; - server->config.max_peers = config->max_peers; - server->config.bind_address = alloc_string_copy(config->bind_address, &alloc_failed); - server->config.external_address = alloc_string_copy(config->external_address, &alloc_failed); - server->config.port = config->port; - server->config.relay_port_range_begin = config->relay_port_range_begin; - server->config.relay_port_range_end = config->relay_port_range_end; - server->config.realm = alloc_string_copy( - config->realm && *config->realm != '\0' ? config->realm : SERVER_DEFAULT_REALM, - &alloc_failed); - if (alloc_failed) { - JLOG_FATAL("Memory allocation for server configuration failed"); - goto error; - } - - // Don't copy credentials but process them - server->config.credentials = NULL; - server->config.credentials_count = 0; - if (config->credentials_count <= 0) { - // TURN disabled - JLOG_INFO("TURN relaying disabled, STUN-only mode"); - server->allocs = NULL; - server->allocs_count = 0; - - } else { - // TURN enabled - server->allocs = calloc(server->config.max_allocations, sizeof(server_turn_alloc_t)); - if (!server->allocs) { - JLOG_FATAL("Memory allocation for TURN allocation table failed"); - goto error; - } - server->allocs_count = (int)server->config.max_allocations; - - for (int i = 0; i < config->credentials_count; ++i) { - juice_server_credentials_t *credentials = config->credentials + i; - if (server->config.max_allocations < credentials->allocations_quota) - server->config.max_allocations = credentials->allocations_quota; - - if (!server_do_add_credentials(server, credentials, 0)) { // never expires - JLOG_FATAL("Failed to add TURN credentials"); - goto error; - } - } - - juice_credentials_list_t *node = server->credentials; - while (node) { - juice_server_credentials_t *credentials = &node->credentials; - if (credentials->allocations_quota == 0) // unlimited - credentials->allocations_quota = server->config.max_allocations; - - node = node->next; - } - } - - server->config.port = udp_get_port(server->sock); - server->nonce_key_timestamp = 0; - if (server->config.max_peers == 0) - server->config.max_peers = SERVER_DEFAULT_MAX_PEERS; - - if (server->config.bind_address) - JLOG_INFO("Created server on %s:%hu", server->config.bind_address, server->config.port); - else - JLOG_INFO("Created server on port %hu", server->config.port); - - int ret = thread_init(&server->thread, server_thread_entry, server); - if (ret) { - JLOG_FATAL("Thread creation failed, error=%d", ret); - goto error; - } - - return server; - -error: - server_do_destroy(server); - return NULL; -} - -void server_do_destroy(juice_server_t *server) { - JLOG_DEBUG("Destroying server"); - - closesocket(server->sock); - mutex_destroy(&server->mutex); - - server_turn_alloc_t *end = server->allocs + server->allocs_count; - for (server_turn_alloc_t *alloc = server->allocs; alloc < end; ++alloc) { - delete_allocation(alloc); - } - free((void *)server->allocs); - - juice_credentials_list_t *node = server->credentials; - while (node) { - juice_credentials_list_t *prev = node; - node = node->next; - free((void *)prev->credentials.username); - free((void *)prev->credentials.password); - free(prev); - } - - free((void *)server->config.bind_address); - free((void *)server->config.external_address); - free((void *)server->config.realm); - free(server); - -#ifdef _WIN32 - WSACleanup(); -#endif - JLOG_VERBOSE("Destroyed server"); -} - -void server_destroy(juice_server_t *server) { - mutex_lock(&server->mutex); - - JLOG_VERBOSE("Waiting for server thread"); - server->thread_stopped = true; - mutex_unlock(&server->mutex); - server_interrupt(server); - thread_join(server->thread, NULL); - - server_do_destroy(server); -} - -uint16_t server_get_port(juice_server_t *server) { - mutex_lock(&server->mutex); - uint16_t port = server->config.port; // updated at creation - mutex_unlock(&server->mutex); - return port; -} - -int server_add_credentials(juice_server_t *server, const juice_server_credentials_t *credentials, - timediff_t lifetime) { - mutex_lock(&server->mutex); - - if (server->config.max_allocations < credentials->allocations_quota) - server->config.max_allocations = credentials->allocations_quota; - - if (server->allocs_count < (int)server->config.max_allocations) { - if (server->allocs_count == 0) - JLOG_INFO("Enabling TURN relaying"); - - server_turn_alloc_t *reallocated = - realloc(server->allocs, server->config.max_allocations * sizeof(server_turn_alloc_t)); - if (!reallocated) { - JLOG_ERROR("Memory allocation for TURN allocation table failed"); - mutex_unlock(&server->mutex); - return -1; - } - memset(reallocated + server->allocs_count, 0, - ((int)server->config.max_allocations - server->allocs_count) * - sizeof(server_turn_alloc_t)); - server->allocs_count = (int)server->config.max_allocations; - server->allocs = reallocated; - } - - juice_credentials_list_t *node = server_do_add_credentials(server, credentials, lifetime); - if (!node) { - mutex_unlock(&server->mutex); - return -1; - } - - if (node->credentials.allocations_quota == 0) // unlimited - node->credentials.allocations_quota = server->config.max_allocations; - - mutex_unlock(&server->mutex); - return 0; -} - -juice_credentials_list_t *server_do_add_credentials(juice_server_t *server, - const juice_server_credentials_t *credentials, - timediff_t lifetime) { - juice_credentials_list_t *node = calloc(1, sizeof(juice_credentials_list_t)); - if (!node) { - JLOG_ERROR("Memory allocation for TURN credentials failed"); - goto error; - } - - bool alloc_failed = false; - node->credentials.username = - alloc_string_copy(credentials->username ? credentials->username : "", &alloc_failed); - node->credentials.password = - alloc_string_copy(credentials->password ? credentials->password : "", &alloc_failed); - node->credentials.allocations_quota = credentials->allocations_quota; - if (alloc_failed) { - JLOG_ERROR("Memory allocation for TURN credentials failed"); - goto error; - } - - stun_compute_userhash(node->credentials.username, server->config.realm, node->userhash); - - if (lifetime > 0) - node->timestamp = current_timestamp() + lifetime; - else - node->timestamp = 0; // never expires - - node->next = server->credentials; - server->credentials = node; - return server->credentials; - -error: - if (node) { - free((void *)node->credentials.username); - free((void *)node->credentials.password); - free(node); - } - return NULL; -} - -void server_run(juice_server_t *server) { - mutex_lock(&server->mutex); - nfds_t nfd = 0; - struct pollfd *pfd = NULL; - - // Main loop - timestamp_t next_timestamp; - while (server_bookkeeping(server, &next_timestamp) == 0) { - timediff_t timediff = next_timestamp - current_timestamp(); - if (timediff < 0) - timediff = 0; - - if (!pfd || nfd != (nfds_t)(1 + server->allocs_count)) { - free(pfd); - nfd = (nfds_t)(1 + server->allocs_count); - pfd = calloc(nfd, sizeof(struct pollfd)); - if (!pfd) { - JLOG_FATAL("Memory allocation for poll descriptors failed"); - break; - } - } - - pfd[0].fd = server->sock; - pfd[0].events = POLLIN; - - for (int i = 0; i < server->allocs_count; ++i) { - server_turn_alloc_t *alloc = server->allocs + i; - if (alloc->state == SERVER_TURN_ALLOC_FULL) { - pfd[1 + i].fd = alloc->sock; - pfd[1 + i].events = POLLIN; - } else { - pfd[1 + i].fd = -1; // ignore - } - } - - JLOG_VERBOSE("Entering poll for %d ms", (int)timediff); - mutex_unlock(&server->mutex); - int ret = poll(pfd, nfd, (int)timediff); - mutex_lock(&server->mutex); - 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 (server->thread_stopped) { - JLOG_VERBOSE("Server destruction requested"); - break; - } - - if (pfd[0].revents & POLLNVAL || pfd[0].revents & POLLERR) { - JLOG_FATAL("Error when polling server socket"); - break; - } - - if (pfd[0].revents & POLLIN) { - if (server_recv(server) < 0) - break; - } - - for (int i = 0; i < server->allocs_count; ++i) { - server_turn_alloc_t *alloc = server->allocs + i; - if (alloc->state == SERVER_TURN_ALLOC_FULL && pfd[1 + i].revents & POLLIN) - server_forward(server, alloc); - } - } - - JLOG_DEBUG("Leaving server thread"); - free(pfd); - mutex_unlock(&server->mutex); -} - -int server_send(juice_server_t *server, const addr_record_t *dst, const char *data, size_t size) { - JLOG_VERBOSE("Sending datagram, size=%d", size); - - int ret = udp_sendto(server->sock, data, size, dst); - if (ret < 0 && sockerrno != SEAGAIN && sockerrno != SEWOULDBLOCK) - JLOG_WARN("Send failed, errno=%d", sockerrno); - - return ret; -} - -int server_stun_send(juice_server_t *server, const addr_record_t *dst, const stun_message_t *msg, - const char *password) { - char buffer[BUFFER_SIZE]; - int size = stun_write(buffer, BUFFER_SIZE, msg, password); - if (size <= 0) { - JLOG_ERROR("STUN message write failed"); - return -1; - } - - if (server_send(server, dst, buffer, size) < 0) { - JLOG_WARN("STUN message send failed, errno=%d", sockerrno); - return -1; - } - return 0; -} - -int server_recv(juice_server_t *server) { - JLOG_VERBOSE("Receiving datagrams"); - while (true) { - char buffer[BUFFER_SIZE]; - addr_record_t record; - int len = udp_recvfrom(server->sock, buffer, BUFFER_SIZE, &record); - if (len < 0) { - if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK) { - JLOG_VERBOSE("No more datagrams to receive"); - break; - } - JLOG_ERROR("recvfrom failed, errno=%d", sockerrno); - return -1; - } - if (len == 0) { - // Empty datagram (used to interrupt) - continue; - } - - addr_unmap_inet6_v4mapped((struct sockaddr *)&record.addr, &record.len); - server_input(server, buffer, len, &record); - } - - return 0; -} - -int server_forward(juice_server_t *server, server_turn_alloc_t *alloc) { - JLOG_VERBOSE("Forwarding datagrams"); - while (true) { - char buffer[BUFFER_SIZE]; - addr_record_t record; - int len = udp_recvfrom(alloc->sock, buffer, BUFFER_SIZE, &record); - if (len < 0) { - if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK) { - break; - } - JLOG_WARN("recvfrom failed, errno=%d", sockerrno); - return -1; - } - addr_unmap_inet6_v4mapped((struct sockaddr *)&record.addr, &record.len); - - uint16_t channel; - if (turn_get_bound_channel(&alloc->map, &record, &channel)) { - // Use ChannelData - len = turn_wrap_channel_data(buffer, BUFFER_SIZE, buffer, len, channel); - if (len <= 0) { - JLOG_ERROR("TURN ChannelData wrapping failed"); - return -1; - } - - JLOG_VERBOSE("Forwarding as ChannelData, size=%d", len); - - int ret = udp_sendto(server->sock, buffer, len, &alloc->record); - if (ret < 0 && sockerrno != SEAGAIN && sockerrno != SEWOULDBLOCK) - JLOG_WARN("Send failed, errno=%d", sockerrno); - - return ret; - - } else { - // Use TURN Data indication - JLOG_VERBOSE("Forwarding as TURN Data indication"); - - stun_message_t msg; - memset(&msg, 0, sizeof(msg)); - msg.msg_class = STUN_CLASS_INDICATION; - msg.msg_method = STUN_METHOD_DATA; - msg.peer = record; - msg.data = buffer; - msg.data_size = len; - juice_random(msg.transaction_id, STUN_TRANSACTION_ID_SIZE); - - return server_stun_send(server, &alloc->record, &msg, NULL); - } - } - - return 0; -} - -int server_input(juice_server_t *server, char *buf, size_t len, const addr_record_t *src) { - JLOG_VERBOSE("Received datagram, size=%d", len); - - if (is_stun_datagram(buf, len)) { - if (JLOG_DEBUG_ENABLED) { - char src_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(src, src_str, ADDR_MAX_STRING_LEN); - JLOG_DEBUG("Received STUN datagram from %s", src_str); - } - stun_message_t msg; - if (stun_read(buf, len, &msg) < 0) { - JLOG_ERROR("STUN message reading failed"); - return -1; - } - return server_dispatch_stun(server, buf, len, &msg, src); - } - - if (is_channel_data(buf, len)) { - if (JLOG_DEBUG_ENABLED) { - char src_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(src, src_str, ADDR_MAX_STRING_LEN); - JLOG_DEBUG("Received ChannelData datagram from %s", src_str); - } - return server_process_channel_data(server, buf, len, src); - } - - if (JLOG_WARN_ENABLED) { - char src_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(src, src_str, ADDR_MAX_STRING_LEN); - JLOG_WARN("Received unexpected non-STUN datagram from %s, ignoring", src_str); - } - return -1; -} - -int server_interrupt(juice_server_t *server) { - JLOG_VERBOSE("Interrupting server thread"); - mutex_lock(&server->mutex); - if (server->sock == INVALID_SOCKET) { - mutex_unlock(&server->mutex); - return -1; - } - - if (udp_sendto_self(server->sock, NULL, 0) < 0) { - if (sockerrno != SEAGAIN && sockerrno != SEWOULDBLOCK) { - JLOG_WARN("Failed to interrupt thread by triggering socket, errno=%d", sockerrno); - mutex_unlock(&server->mutex); - return -1; - } - } - - mutex_unlock(&server->mutex); - return 0; -} - -int server_bookkeeping(juice_server_t *server, timestamp_t *next_timestamp) { - timestamp_t now = current_timestamp(); - *next_timestamp = now + 60000; - - // Handle allocations - for (int i = 0; i < server->allocs_count; ++i) { - server_turn_alloc_t *alloc = server->allocs + i; - if (alloc->state != SERVER_TURN_ALLOC_FULL) - continue; - - if (alloc->timestamp <= now) { - JLOG_DEBUG("Allocation timed out"); - delete_allocation(alloc); - continue; - } - - if (alloc->timestamp < *next_timestamp) - *next_timestamp = alloc->timestamp; - } - - // Handle credentials - juice_credentials_list_t **pnode = &server->credentials; // We are deleting some elements - while (*pnode) { - if ((*pnode)->timestamp && (*pnode)->timestamp <= now) { - JLOG_DEBUG("Credentials timed out"); - juice_credentials_list_t *next = (*pnode)->next; - free((void *)(*pnode)->credentials.username); - free((void *)(*pnode)->credentials.password); - free((*pnode)); - *pnode = next; - continue; - } - - pnode = &(*pnode)->next; - } - - return 0; -} - -void server_get_nonce(juice_server_t *server, const addr_record_t *src, char *nonce) { - timestamp_t now = current_timestamp(); - if (now >= server->nonce_key_timestamp) { - juice_random(server->nonce_key, SERVER_NONCE_KEY_SIZE); - server->nonce_key_timestamp = now + SERVER_NONCE_KEY_LIFETIME; - } - - uint8_t digest[HMAC_SHA256_SIZE]; - hmac_sha256(&src->addr, src->len, server->nonce_key, SERVER_NONCE_KEY_SIZE, digest); - - size_t len = HMAC_SHA256_SIZE; - if (len > STUN_MAX_NONCE_LEN) - len = STUN_MAX_NONCE_LEN; - - // RFC 4648 base64url character table - const char *table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - for (size_t i = 0; i < len; ++i) - nonce[i] = table[digest[i] % 64]; - - nonce[len] = '\0'; - - stun_prepend_nonce_cookie(nonce); -} - -void server_prepare_credentials(juice_server_t *server, const addr_record_t *src, - const juice_server_credentials_t *credentials, - stun_message_t *msg) { - snprintf(msg->credentials.realm, STUN_MAX_REALM_LEN, "%s", server->config.realm); - server_get_nonce(server, src, msg->credentials.nonce); - - if (credentials) - snprintf(msg->credentials.username, STUN_MAX_USERNAME_LEN, "%s", credentials->username); -} - -int server_dispatch_stun(juice_server_t *server, void *buf, size_t size, stun_message_t *msg, - const addr_record_t *src) { - - if (!(msg->msg_class == STUN_CLASS_REQUEST || - (msg->msg_class == STUN_CLASS_INDICATION && - (msg->msg_method == STUN_METHOD_BINDING || msg->msg_method == STUN_METHOD_SEND)))) { - JLOG_WARN("Unexpected STUN message, class=0x%X, method=0x%X", msg->msg_class, - msg->msg_method); - return -1; - } - - if (server->allocs_count == 0 && msg->msg_method != STUN_METHOD_BINDING) { - // TURN support is disabled - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 400, // Bad request - NULL); - } - - if (msg->error_code == STUN_ERROR_INTERNAL_VALIDATION_FAILED) { - if (msg->msg_class == STUN_CLASS_REQUEST) { - JLOG_WARN("Invalid STUN message, answering bad request error response"); - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 400, // Bad request - NULL); - } else { - JLOG_WARN("Invalid STUN message, dropping"); - return -1; - } - } - - juice_server_credentials_t *credentials = NULL; - if (msg->msg_method != STUN_METHOD_BINDING && msg->msg_class != STUN_CLASS_INDICATION) { - if (!msg->has_integrity || // - *msg->credentials.realm == '\0' || *msg->credentials.nonce == '\0' || - (*msg->credentials.username == '\0' && !msg->credentials.enable_userhash)) { - JLOG_DEBUG("Answering STUN unauthorized error response"); - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 401, // Unauthorized - NULL); // No username - } - - char nonce[STUN_MAX_NONCE_LEN]; - server_get_nonce(server, src, nonce); - if (strcmp(msg->credentials.nonce, nonce) != 0 || - strcmp(msg->credentials.realm, server->config.realm) != 0) { - JLOG_DEBUG("Answering STUN stale nonce error response"); - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 438, // Stale nonce - NULL); // No username - } - - timestamp_t now = current_timestamp(); - if (msg->credentials.enable_userhash) { - juice_credentials_list_t *node = server->credentials; - while (node) { - if ((!node->timestamp || node->timestamp > now) && - const_time_memcmp(node->userhash, msg->credentials.userhash, USERHASH_SIZE) == - 0) { - credentials = &node->credentials; - } - node = node->next; - } - - if (credentials) - snprintf(msg->credentials.username, STUN_MAX_USERNAME_LEN, "%s", - credentials->username); - else - JLOG_WARN("No credentials for userhash"); - - } else { - juice_credentials_list_t *node = server->credentials; - while (node) { - if ((!node->timestamp || node->timestamp > now) && - const_time_strcmp(node->credentials.username, msg->credentials.username) == 0) { - credentials = &node->credentials; - } - node = node->next; - } - - if (!credentials) - JLOG_WARN("No credentials for username \"%s\"", msg->credentials.username); - } - if (!credentials) { - server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 401, // Unauthorized - NULL); // No username - return -1; - } - - // Check credentials - if (!stun_check_integrity(buf, size, msg, credentials->password)) { - JLOG_WARN("STUN authentication failed for username \"%s\"", msg->credentials.username); - server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 401, // Unauthorized - NULL); // No username - return -1; - } - } - - switch (msg->msg_method) { - case STUN_METHOD_BINDING: - return server_process_stun_binding(server, msg, src); - - case STUN_METHOD_ALLOCATE: - case STUN_METHOD_REFRESH: - return server_process_turn_allocate(server, msg, src, credentials); - - case STUN_METHOD_CREATE_PERMISSION: - return server_process_turn_create_permission(server, msg, src, credentials); - - case STUN_METHOD_CHANNEL_BIND: - return server_process_turn_channel_bind(server, msg, src, credentials); - - case STUN_METHOD_SEND: - return server_process_turn_send(server, msg, src); - - default: - JLOG_WARN("Unknown STUN method 0x%X, ignoring", msg->msg_method); - return -1; - } -} - -int server_answer_stun_binding(juice_server_t *server, const uint8_t *transaction_id, - const addr_record_t *src) { - JLOG_DEBUG("Answering STUN Binding request"); - - stun_message_t ans; - memset(&ans, 0, sizeof(ans)); - ans.msg_class = STUN_CLASS_RESP_SUCCESS; - ans.msg_method = STUN_METHOD_BINDING; - ans.mapped = *src; - memcpy(ans.transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE); - - char buffer[BUFFER_SIZE]; - int size = stun_write(buffer, BUFFER_SIZE, &ans, NULL); - if (size <= 0) { - JLOG_ERROR("STUN message write failed"); - return -1; - } - - if (server_send(server, src, buffer, size) < 0) { - JLOG_WARN("STUN message send failed, errno=%d", sockerrno); - return -1; - } - - return 0; -} - -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) { - JLOG_DEBUG("Answering STUN error response with code %u", code); - - stun_message_t ans; - memset(&ans, 0, sizeof(ans)); - ans.msg_class = STUN_CLASS_RESP_ERROR; - ans.msg_method = method; - ans.error_code = code; - memcpy(ans.transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE); - - if (method != STUN_METHOD_BINDING) - server_prepare_credentials(server, src, credentials, &ans); - - return server_stun_send(server, src, &ans, credentials ? credentials->password : NULL); -} - -int server_process_stun_binding(juice_server_t *server, const stun_message_t *msg, - const addr_record_t *src) { - if (JLOG_INFO_ENABLED) { - char src_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(src, src_str, ADDR_MAX_STRING_LEN); - JLOG_INFO("Got STUN binding from client %s", src_str); - } - - return server_answer_stun_binding(server, msg->transaction_id, 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) { - if (msg->msg_class != STUN_CLASS_REQUEST) - return -1; - - if (msg->msg_method != STUN_METHOD_ALLOCATE && msg->msg_method != STUN_METHOD_REFRESH) - return -1; - - JLOG_DEBUG("Processing TURN Allocate request"); - - server_turn_alloc_t *alloc = find_allocation(server->allocs, server->allocs_count, src, true); - if (!alloc) { - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 486, // Allocation quota reached - credentials); - } - - if (alloc->state == SERVER_TURN_ALLOC_FULL) { - // Allocation exists - if (msg->msg_method == STUN_METHOD_ALLOCATE && - memcmp(alloc->transaction_id, msg->transaction_id, STUN_TRANSACTION_ID_SIZE) != 0) { - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 437, // Allocation mismatch - credentials); - } - - if (alloc->credentials != credentials) { - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 441, // Wrong credentials - credentials); - } - } else { - // Allocation does not exist - if (msg->msg_method == STUN_METHOD_REFRESH) { - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 437, // Allocation mismatch - credentials); - } - - if (credentials->allocations_quota <= 0) { - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 486, // Allocation quota reached - credentials); - } - - udp_socket_config_t socket_config; - memset(&socket_config, 0, sizeof(socket_config)); - socket_config.bind_address = server->config.bind_address; - socket_config.port_begin = server->config.relay_port_range_begin; - socket_config.port_end = server->config.relay_port_range_end; - alloc->sock = udp_create_socket(&socket_config); - if (alloc->sock == INVALID_SOCKET) { - server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, 500, - credentials); - return -1; - } - if (turn_init_map(&alloc->map, server->config.max_peers) < 0) { - closesocket(alloc->sock); - alloc->sock = INVALID_SOCKET; - server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, 500, - credentials); - return -1; - } - - alloc->state = SERVER_TURN_ALLOC_FULL; - alloc->record = *src; - alloc->credentials = credentials; - - --credentials->allocations_quota; - } - - uint32_t lifetime = ALLOCATION_LIFETIME / 1000; - if (msg->lifetime_set && msg->lifetime < lifetime) - lifetime = msg->lifetime; - - alloc->timestamp = current_timestamp() + lifetime * 1000; - memcpy(alloc->transaction_id, msg->transaction_id, STUN_TRANSACTION_ID_SIZE); - - addr_record_t records[MAX_RELAYED_RECORDS_COUNT]; - const addr_record_t *relayed = NULL; - if (lifetime == 0) { - delete_allocation(alloc); - - } else { - int count = 0; - if (server->config.external_address) { - char service[8]; - snprintf(service, 8, "%hu", udp_get_port(alloc->sock)); - count = addr_resolve(server->config.external_address, service, records, - MAX_RELAYED_RECORDS_COUNT); - if (count <= 0) { - JLOG_ERROR("Specified external address is invalid"); - goto error; - } - } else { - count = udp_get_addrs(alloc->sock, records, MAX_RELAYED_RECORDS_COUNT); - if (count <= 0) { - JLOG_ERROR("No local address found"); - goto error; - } - } - - if (count > MAX_RELAYED_RECORDS_COUNT) - count = MAX_RELAYED_RECORDS_COUNT; - - for (int i = 0; i < count; ++i) { - const addr_record_t *record = records + i; - if (record->addr.ss_family == AF_INET || !relayed) { - relayed = record; - if (record->addr.ss_family == AF_INET) - break; - } - } - - if (!relayed) { - JLOG_ERROR("No advertisable relayed address found"); - goto error; - } - - if (JLOG_INFO_ENABLED) { - char src_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(src, src_str, ADDR_MAX_STRING_LEN); - char relayed_str[ADDR_MAX_STRING_LEN]; - addr_record_to_string(relayed, relayed_str, ADDR_MAX_STRING_LEN); - JLOG_INFO("Allocated TURN relayed address %s for client %s", relayed_str, src_str); - } - } - - stun_message_t ans; - memset(&ans, 0, sizeof(ans)); - ans.msg_class = STUN_CLASS_RESP_SUCCESS; - ans.msg_method = msg->msg_method; - ans.lifetime = lifetime; - ans.lifetime_set = true; - ans.mapped = *src; - if (relayed) - ans.relayed = *relayed; - memcpy(ans.transaction_id, msg->transaction_id, STUN_TRANSACTION_ID_SIZE); - - server_prepare_credentials(server, src, credentials, &ans); - - return server_stun_send(server, src, &ans, credentials->password); - -error: - delete_allocation(alloc); - server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, 500, credentials); - return -1; -} - -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) { - if (msg->msg_class != STUN_CLASS_REQUEST) - return -1; - - JLOG_DEBUG("Processing STUN CreatePermission request"); - - if (!msg->peer.len) { - JLOG_WARN("Missing peer address in TURN CreatePermission request"); - return -1; - } - - server_turn_alloc_t *alloc = find_allocation(server->allocs, server->allocs_count, src, false); - if (!alloc || alloc->state != SERVER_TURN_ALLOC_FULL) { - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 437, // Allocation mismatch - credentials); - } - if (alloc->credentials != credentials) { - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 441, // Wrong credentials - credentials); - } - - if (!turn_set_permission(&alloc->map, msg->transaction_id, &msg->peer, PERMISSION_LIFETIME)) { - server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, 500, - credentials); - return -1; - } - - stun_message_t ans; - memset(&ans, 0, sizeof(ans)); - ans.msg_class = STUN_CLASS_RESP_SUCCESS; - ans.msg_method = STUN_METHOD_CREATE_PERMISSION; - memcpy(ans.transaction_id, msg->transaction_id, STUN_TRANSACTION_ID_SIZE); - - server_prepare_credentials(server, src, credentials, &ans); - - return server_stun_send(server, src, &ans, credentials->password); -} - -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) { - if (msg->msg_class != STUN_CLASS_REQUEST) - return -1; - - JLOG_DEBUG("Processing STUN ChannelBind request"); - - if (!msg->peer.len) { - JLOG_WARN("Missing peer address in TURN ChannelBind request"); - return -1; - } - if (!msg->channel_number) { - JLOG_WARN("Missing channel number in TURN ChannelBind request"); - return -1; - } - - server_turn_alloc_t *alloc = find_allocation(server->allocs, server->allocs_count, src, false); - if (!alloc || alloc->state != SERVER_TURN_ALLOC_FULL) { - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 437, // Allocation mismatch - credentials); - } - if (alloc->credentials != credentials) { - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 441, // Wrong credentials - credentials); - } - - uint16_t channel = msg->channel_number; - if (!is_valid_channel(channel)) { - JLOG_WARN("TURN channel 0x%hX is invalid", channel); - return server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, - 400, // Bad request - credentials); - } - - if (!turn_bind_channel(&alloc->map, &msg->peer, msg->transaction_id, channel, BIND_LIFETIME)) { - server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, 500, - credentials); - return -1; - } - - stun_message_t ans; - memset(&ans, 0, sizeof(ans)); - ans.msg_class = STUN_CLASS_RESP_SUCCESS; - ans.msg_method = STUN_METHOD_CHANNEL_BIND; - memcpy(ans.transaction_id, msg->transaction_id, STUN_TRANSACTION_ID_SIZE); - - server_prepare_credentials(server, src, credentials, &ans); - - return server_stun_send(server, src, &ans, credentials->password); -} - -int server_process_turn_send(juice_server_t *server, const stun_message_t *msg, - const addr_record_t *src) { - if (msg->msg_class != STUN_CLASS_INDICATION) - return -1; - - JLOG_DEBUG("Processing STUN Send indication"); - - if (!msg->data) { - JLOG_WARN("Missing data in TURN Send indication"); - return -1; - } - if (!msg->peer.len) { - JLOG_WARN("Missing peer address in TURN Send indication"); - return -1; - } - - server_turn_alloc_t *alloc = find_allocation(server->allocs, server->allocs_count, src, false); - if (!alloc || alloc->state != SERVER_TURN_ALLOC_FULL) { - JLOG_WARN("Allocation mismatch for TURN Send indication"); - return -1; - } - - if (!turn_has_permission(&alloc->map, &msg->peer)) { - JLOG_WARN("No permission for peer address"); - return -1; - } - - JLOG_VERBOSE("Forwarding datagram to peer, size=%zu", msg->data_size); - - int ret = udp_sendto(alloc->sock, msg->data, msg->data_size, &msg->peer); - if (ret < 0 && sockerrno != SEAGAIN && sockerrno != SEWOULDBLOCK) - JLOG_WARN("Forwarding failed, errno=%d", sockerrno); - - return ret; -} - -int server_process_channel_data(juice_server_t *server, char *buf, size_t len, - const addr_record_t *src) { - server_turn_alloc_t *alloc = find_allocation(server->allocs, server->allocs_count, src, false); - if (!alloc || alloc->state != SERVER_TURN_ALLOC_FULL) { - JLOG_WARN("Allocation mismatch for TURN Channel Data"); - return -1; - } - - if (len < sizeof(struct channel_data_header)) { - JLOG_WARN("ChannelData is too short"); - return -1; - } - - const struct channel_data_header *header = (const struct channel_data_header *)buf; - buf += sizeof(struct channel_data_header); - len -= sizeof(struct channel_data_header); - uint16_t channel = ntohs(header->channel_number); - uint16_t length = ntohs(header->length); - JLOG_VERBOSE("Received ChannelData, channel=0x%hX, length=%hu", channel, length); - if (length > len) { - JLOG_WARN("ChannelData has invalid length"); - return -1; - } - len = length; - - addr_record_t record; - if (!turn_find_bound_channel(&alloc->map, channel, &record)) { - JLOG_WARN("Channel 0x%hX is not bound", channel); - return -1; - } - - JLOG_VERBOSE("Forwarding datagram to peer, size=%zu", len); - - int ret = udp_sendto(alloc->sock, buf, len, &record); - if (ret < 0 && sockerrno != SEAGAIN && sockerrno != SEWOULDBLOCK) - JLOG_WARN("Send failed, errno=%d", sockerrno); - - return 0; -} - -#endif // ifndef NO_SERVER diff --git a/thirdparty/libjuice/src/server.h b/thirdparty/libjuice/src/server.h deleted file mode 100644 index a7adf10..0000000 --- a/thirdparty/libjuice/src/server.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * 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 -#include - -#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 diff --git a/thirdparty/libjuice/src/socket.h b/thirdparty/libjuice/src/socket.h deleted file mode 100644 index d126f34..0000000 --- a/thirdparty/libjuice/src/socket.h +++ /dev/null @@ -1,132 +0,0 @@ -/** - * 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 -#include -// -#include -#include - -#ifdef __MINGW32__ -#include -#include -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef __linux__ -#define NO_PMTUDISC -#endif - -#ifdef __ANDROID__ -#define NO_IFADDRS -#else -#include -#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 diff --git a/thirdparty/libjuice/src/stun.c b/thirdparty/libjuice/src/stun.c deleted file mode 100644 index f14b1d3..0000000 --- a/thirdparty/libjuice/src/stun.c +++ /dev/null @@ -1,1236 +0,0 @@ -/** - * 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 "base64.h" -#include "const_time.h" -#include "crc32.h" -#include "juice.h" -#include "log.h" -#include "udp.h" - -#include -#include -#include -#include -#include -#include - -#define STUN_MAGIC 0x2112A442 -#define STUN_FINGERPRINT_XOR 0x5354554E // "STUN" -#define STUN_ATTR_SIZE sizeof(struct stun_attr) - -// STUN_MAX_PASSWORD_LEN > HASH_SHA256_SIZE > HASH_MD5_SIZE -#define MAX_HMAC_KEY_LEN STUN_MAX_PASSWORD_LEN - -#define MAX_HMAC_INPUT_LEN (STUN_MAX_USERNAME_LEN + STUN_MAX_REALM_LEN + STUN_MAX_PASSWORD_LEN + 2) - -#define MAX_USERHASH_INPUT_LEN (STUN_MAX_USERNAME_LEN + STUN_MAX_REALM_LEN + 1) - -#ifndef htonll -#define htonll(x) \ - ((uint64_t)(((uint64_t)htonl((uint32_t)(x))) << 32) | (uint64_t)htonl((uint32_t)((x) >> 32))) -#endif -#ifndef ntohll -#define ntohll(x) htonll(x) -#endif - -static size_t align32(size_t len) { - while (len & 0x03) - ++len; - return len; -} - -static size_t generate_hmac_key(const stun_message_t *msg, const char *password, void *key) { - if (*msg->credentials.realm != '\0') { - // long-term credentials - if (*msg->credentials.username == '\0') - JLOG_WARN("Generating HMAC key for long-term credentials with empty STUN username"); - - char input[MAX_HMAC_INPUT_LEN]; - int input_len = snprintf(input, MAX_HMAC_INPUT_LEN, "%s:%s:%s", msg->credentials.username, - msg->credentials.realm, password ? password : ""); - if (input_len < 0) - return 0; - - if (input_len >= MAX_HMAC_INPUT_LEN) - input_len = MAX_HMAC_INPUT_LEN - 1; - - switch (msg->credentials.password_algorithm) { - case STUN_PASSWORD_ALGORITHM_SHA256: - hash_sha256(input, input_len, key); - return HASH_SHA256_SIZE; - default: - hash_md5(input, input_len, key); - return HASH_MD5_SIZE; - } - } else { - // short-term credentials - int key_len = snprintf((char *)key, MAX_HMAC_KEY_LEN, "%s", password ? password : ""); - if (key_len < 0) - return 0; - - if (key_len >= MAX_HMAC_KEY_LEN) - key_len = MAX_HMAC_KEY_LEN - 1; - - return key_len; - } -} - -static size_t generate_password_algorithms_attr(uint8_t *attr) { - // attr size must be at least STUN_PASSWORD_ALGORITHMS_ATTR_MAX_SIZE - struct stun_value_password_algorithm *pwa = (struct stun_value_password_algorithm *)attr; - pwa->algorithm = htons(STUN_PASSWORD_ALGORITHM_SHA256); - pwa->parameters_length = 0; - ++pwa; - pwa->algorithm = htons(STUN_PASSWORD_ALGORITHM_MD5); - pwa->parameters_length = 0; - ++pwa; - return (uint8_t *)pwa - attr; -} - -int stun_write(void *buf, size_t size, const stun_message_t *msg, const char *password) { - uint8_t *begin = buf; - uint8_t *pos = begin; - uint8_t *end = begin + size; - - JLOG_VERBOSE("Writing STUN message, class=0x%X, method=0x%X", (unsigned int)msg->msg_class, - (unsigned int)msg->msg_method); - - size_t len = - stun_write_header(pos, end - pos, msg->msg_class, msg->msg_method, msg->transaction_id); - if (len <= 0) - goto overflow; - pos += len; - uint8_t *attr_begin = pos; - - if (msg->error_code) { - const char *reason = stun_get_error_reason(msg->error_code); - char buffer[sizeof(struct stun_value_error_code) + STUN_MAX_ERROR_REASON_LEN + 1]; - struct stun_value_error_code *error = (struct stun_value_error_code *)buffer; - memset(error, 0, sizeof(*error)); - error->code_class = (msg->error_code / 100) & 0x07; - error->code_number = msg->error_code % 100; - strcpy((char *)error->reason, reason); - len = stun_write_attr(pos, end - pos, STUN_ATTR_ERROR_CODE, error, - sizeof(struct stun_value_error_code) + strlen(reason)); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->mapped.len) { - JLOG_VERBOSE("Writing XOR mapped address"); - uint8_t value[32]; - uint8_t mask[16]; - *((uint32_t *)mask) = htonl(STUN_MAGIC); - memcpy(mask + 4, msg->transaction_id, 12); - int value_len = stun_write_value_mapped_address( - value, 32, (const struct sockaddr *)&msg->mapped.addr, msg->mapped.len, mask); - if (value_len > 0) { - len = stun_write_attr(pos, end - pos, STUN_ATTR_XOR_MAPPED_ADDRESS, value, value_len); - if (len <= 0) - goto overflow; - pos += len; - } - } - if (msg->priority) { - uint32_t priority = htonl(msg->priority); - len = stun_write_attr(pos, end - pos, STUN_ATTR_PRIORITY, &priority, 4); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->use_candidate) { - len = stun_write_attr(pos, end - pos, STUN_ATTR_USE_CANDIDATE, NULL, 0); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->ice_controlling) { - uint64_t ice_controlling = htonll(msg->ice_controlling); - len = stun_write_attr(pos, end - pos, STUN_ATTR_ICE_CONTROLLING, &ice_controlling, 8); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->ice_controlled) { - uint64_t ice_controlled = htonll(msg->ice_controlled); - len = stun_write_attr(pos, end - pos, STUN_ATTR_ICE_CONTROLLED, &ice_controlled, 8); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->channel_number) { - struct stun_value_channel_number channel_number; - memset(&channel_number, 0, sizeof(channel_number)); - channel_number.channel_number = htons(msg->channel_number); - len = stun_write_attr(pos, end - pos, STUN_ATTR_CHANNEL_NUMBER, &channel_number, - sizeof(channel_number)); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->lifetime_set || msg->lifetime) { - uint32_t lifetime = htonl(msg->lifetime); - len = stun_write_attr(pos, end - pos, STUN_ATTR_LIFETIME, &lifetime, 4); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->peer.len) { - JLOG_VERBOSE("Writing XOR peer address"); - uint8_t value[32]; - uint8_t mask[16]; - *((uint32_t *)mask) = htonl(STUN_MAGIC); - memcpy(mask + 4, msg->transaction_id, 12); - int value_len = stun_write_value_mapped_address( - value, 32, (const struct sockaddr *)&msg->peer.addr, msg->peer.len, mask); - if (value_len > 0) { - len = stun_write_attr(pos, end - pos, STUN_ATTR_XOR_PEER_ADDRESS, value, value_len); - if (len <= 0) - goto overflow; - pos += len; - } - } - if (msg->relayed.len) { - JLOG_VERBOSE("Writing XOR relay address"); - uint8_t value[32]; - uint8_t mask[16]; - *((uint32_t *)mask) = htonl(STUN_MAGIC); - memcpy(mask + 4, msg->transaction_id, 12); - int value_len = stun_write_value_mapped_address( - value, 32, (const struct sockaddr *)&msg->relayed.addr, msg->relayed.len, mask); - if (value_len > 0) { - len = stun_write_attr(pos, end - pos, STUN_ATTR_XOR_RELAYED_ADDRESS, value, value_len); - if (len <= 0) - goto overflow; - pos += len; - } - } - if (msg->data) { - len = stun_write_attr(pos, end - pos, STUN_ATTR_DATA, (const uint8_t *)msg->data, - msg->data_size); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->even_port) { - struct stun_value_even_port even_port; - memset(&even_port, 0, sizeof(even_port)); - if (msg->next_port) - even_port.r |= 0x80; - len = stun_write_attr(pos, end - pos, STUN_ATTR_CHANNEL_NUMBER, &even_port, - sizeof(even_port)); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->requested_transport) { - struct stun_value_requested_transport requested_transport; - memset(&requested_transport, 0, sizeof(requested_transport)); - requested_transport.protocol = 17; - len = stun_write_attr(pos, end - pos, STUN_ATTR_REQUESTED_TRANSPORT, &requested_transport, - sizeof(requested_transport)); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->dont_fragment) { - len = stun_write_attr(pos, end - pos, STUN_ATTR_DONT_FRAGMENT, NULL, 0); - if (len <= 0) - goto overflow; - pos += len; - } - if (msg->reservation_token) { - uint64_t reservation_token = htonll(msg->reservation_token); - len = stun_write_attr(pos, end - pos, STUN_ATTR_RESERVATION_TOKEN, &reservation_token, 8); - if (len <= 0) - goto overflow; - pos += len; - } - - const char *software = "libjuice"; - len = stun_write_attr(pos, end - pos, STUN_ATTR_SOFTWARE, software, strlen(software)); - if (len <= 0) - goto overflow; - pos += len; - - if (msg->msg_class == STUN_CLASS_REQUEST) { - if (msg->credentials.enable_userhash) { - len = stun_write_attr(pos, end - pos, STUN_ATTR_USERHASH, msg->credentials.userhash, - USERHASH_SIZE); - if (len <= 0) - goto overflow; - pos += len; - - } else if (*msg->credentials.username != '\0') { - len = stun_write_attr(pos, end - pos, STUN_ATTR_USERNAME, msg->credentials.username, - strlen(msg->credentials.username)); - if (len <= 0) - goto overflow; - pos += len; - } - } - if (msg->msg_class == STUN_CLASS_REQUEST || - (msg->msg_class == STUN_CLASS_RESP_ERROR && - (msg->error_code == 401 || msg->error_code == 438) // Unauthenticated or Stale Nonce - )) { - if (*msg->credentials.realm != '\0') { - len = stun_write_attr(pos, end - pos, STUN_ATTR_REALM, msg->credentials.realm, - strlen(msg->credentials.realm)); - if (len <= 0) - goto overflow; - pos += len; - } - if (*msg->credentials.nonce != '\0') { - len = stun_write_attr(pos, end - pos, STUN_ATTR_NONCE, msg->credentials.nonce, - strlen(msg->credentials.nonce)); - if (len <= 0) - goto overflow; - pos += len; - - if (msg->credentials.password_algorithm > 0) { - len = stun_write_attr(pos, end - pos, STUN_ATTR_PASSWORD_ALGORITHMS, - msg->credentials.password_algorithms_value, - msg->credentials.password_algorithms_value_size); - if (len <= 0) - goto overflow; - pos += len; - - } else if (msg->msg_class != STUN_CLASS_REQUEST) { - uint8_t pwa_value[STUN_MAX_PASSWORD_ALGORITHMS_VALUE_SIZE]; - size_t pwa_size = generate_password_algorithms_attr(pwa_value); - len = stun_write_attr(pos, end - pos, STUN_ATTR_PASSWORD_ALGORITHMS, pwa_value, - pwa_size); - if (len <= 0) - goto overflow; - pos += len; - } - - if (msg->msg_class == STUN_CLASS_REQUEST && - msg->credentials.password_algorithm != STUN_PASSWORD_ALGORITHM_UNSET) { - struct stun_value_password_algorithm pwa; - pwa.algorithm = htons(msg->credentials.password_algorithm); - len = stun_write_attr(pos, end - pos, STUN_ATTR_PASSWORD_ALGORITHM, &pwa, - sizeof(pwa)); - if (len <= 0) - goto overflow; - pos += len; - } - } - } - if (msg->msg_class != STUN_CLASS_INDICATION && password) { - uint8_t key[MAX_HMAC_KEY_LEN]; - size_t key_len = generate_hmac_key(msg, password, key); - - size_t tmp_length = pos - attr_begin + STUN_ATTR_SIZE + HMAC_SHA1_SIZE; - stun_update_header_length(begin, tmp_length); - - uint8_t hmac[HMAC_SHA1_SIZE]; - hmac_sha1(begin, pos - begin, key, key_len, hmac); - len = stun_write_attr(pos, end - pos, STUN_ATTR_MESSAGE_INTEGRITY, hmac, HMAC_SHA1_SIZE); - if (len <= 0) - goto overflow; - pos += len; - - // According to RFC 8489, the agent must include both MESSAGE-INTEGRITY and - // MESSAGE-INTEGRITY-SHA256. However, this makes legacy agents and servers fail with error - // 420 Unknown Attribute. Therefore, unless the password algorithm SHA-256 is enabled, only - // MESSAGE-INTEGRITY is included in the message for compatibility. - if (msg->credentials.password_algorithm != STUN_PASSWORD_ALGORITHM_UNSET) { - // If the response contains a PASSWORD-ALGORITHMS attribute, all the - // subsequent requests MUST be authenticated using MESSAGE-INTEGRITY- - // SHA256 only. - size_t tmp_length = pos - attr_begin + STUN_ATTR_SIZE + HMAC_SHA256_SIZE; - stun_update_header_length(begin, tmp_length); - - uint8_t hmac[HMAC_SHA256_SIZE]; - hmac_sha256(begin, pos - begin, key, key_len, hmac); - len = stun_write_attr(pos, end - pos, STUN_ATTR_MESSAGE_INTEGRITY_SHA256, hmac, - HMAC_SHA256_SIZE); - if (len <= 0) - goto overflow; - pos += len; - } - } - - size_t length = pos - attr_begin + STUN_ATTR_SIZE + 4; - if (length & 0x03) { - JLOG_ERROR("Written STUN message length is not multiple of 4, length=%zu", length); - return -1; - } - stun_update_header_length(begin, length); - - uint32_t fingerprint = htonl(CRC32(buf, pos - begin) ^ STUN_FINGERPRINT_XOR); - len = stun_write_attr(pos, end - pos, STUN_ATTR_FINGERPRINT, &fingerprint, 4); - if (len <= 0) - goto overflow; - pos += len; - - return (int)(pos - begin); - -overflow: - JLOG_ERROR("Not enough space in buffer for STUN message, size=%zu", size); - return -1; -} - -int stun_write_header(void *buf, size_t size, stun_class_t class, stun_method_t method, - const uint8_t *transaction_id) { - if (size < sizeof(struct stun_header)) - return -1; - - uint16_t type = (uint16_t) class | (uint16_t)method; - - struct stun_header *header = buf; - header->type = htons(type); - header->length = htons(0); - header->magic = htonl(STUN_MAGIC); - memcpy(header->transaction_id, transaction_id, STUN_TRANSACTION_ID_SIZE); - - return sizeof(struct stun_header); -} - -size_t stun_update_header_length(void *buf, size_t length) { - struct stun_header *header = buf; - size_t previous = ntohs(header->length); - header->length = htons((uint16_t)length); - return previous; -} - -int stun_write_attr(void *buf, size_t size, uint16_t type, const void *value, size_t length) { - JLOG_VERBOSE("Writing STUN attribute type 0x%X, length=%zu", (unsigned int)type, length); - - if (size < sizeof(struct stun_attr) + length) - return -1; - - struct stun_attr *attr = buf; - attr->type = htons(type); - attr->length = htons((uint16_t)length); - - if (length > 0) { - memcpy(attr->value, value, length); - - // Pad to align on 4 bytes - while (length & 0x03) - attr->value[length++] = 0; - } - - return (int)(sizeof(struct stun_attr) + length); -} - -int stun_write_value_mapped_address(void *buf, size_t size, const struct sockaddr *addr, - socklen_t addrlen, const uint8_t *mask) { - if (size < sizeof(struct stun_value_mapped_address)) - return -1; - - struct stun_value_mapped_address *value = buf; - value->padding = 0; - switch (addr->sa_family) { - case AF_INET: { - value->family = STUN_ADDRESS_FAMILY_IPV4; - if (size < sizeof(struct stun_value_mapped_address) + 4) - return -1; - if (addrlen < (socklen_t)sizeof(struct sockaddr_in)) - return -1; - JLOG_VERBOSE("Writing IPv4 address"); - const struct sockaddr_in *sin = (const struct sockaddr_in *)addr; - value->port = sin->sin_port ^ *((uint16_t *)mask); - const uint8_t *bytes = (const uint8_t *)&sin->sin_addr; - for (int i = 0; i < 4; ++i) - value->address[i] = bytes[i] ^ mask[i]; - return sizeof(struct stun_value_mapped_address) + 4; - } - case AF_INET6: { - value->family = STUN_ADDRESS_FAMILY_IPV6; - if (size < sizeof(struct stun_value_mapped_address) + 16) - return -1; - if (addrlen < (socklen_t)sizeof(struct sockaddr_in6)) - return -1; - JLOG_VERBOSE("Writing IPv6 address"); - const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)addr; - value->port = sin6->sin6_port ^ *((uint16_t *)mask); - const uint8_t *bytes = (const uint8_t *)&sin6->sin6_addr; - for (int i = 0; i < 16; ++i) - value->address[i] = bytes[i] ^ mask[i]; - return sizeof(struct stun_value_mapped_address) + 16; - } - default: { - JLOG_DEBUG("Unknown address family %u", (unsigned int)addr->sa_family); - return -1; - } - } -} - -bool is_stun_datagram(const void *data, size_t size) { - // RFC 8489: The most significant 2 bits of every STUN message MUST be zeroes. This can be used - // to differentiate STUN packets from other protocols when STUN is multiplexed with other - // protocols on the same port. - if (!size || *((uint8_t *)data) & 0xC0) { - JLOG_VERBOSE("Not a STUN message: first 2 bits are not zeroes"); - return false; - } - - if (size < sizeof(struct stun_header)) { - JLOG_VERBOSE("Not a STUN message: message too short, size=%zu", size); - return false; - } - - const struct stun_header *header = data; - if (ntohl(header->magic) != STUN_MAGIC) { - JLOG_VERBOSE("Not a STUN message: magic number invalid"); - return false; - } - - // RFC 8489: The message length MUST contain the size of the message in bytes, not including the - // 20-byte STUN header. Since all STUN attributes are padded to a multiple of 4 bytes, the last - // 2 bits of this field are always zero. This provides another way to distinguish STUN packets - // from packets of other protocols. - const size_t length = ntohs(header->length); - if (length & 0x03) { - JLOG_VERBOSE("Not a STUN message: invalid length %zu not multiple of 4", length); - return false; - } - if (size != sizeof(struct stun_header) + length) { - JLOG_VERBOSE("Not a STUN message: invalid length %zu while expecting %zu", length, - size - sizeof(struct stun_header)); - return false; - } - - return true; -} - -int stun_read(void *data, size_t size, stun_message_t *msg) { - memset(msg, 0, sizeof(*msg)); - - if (size < sizeof(struct stun_header)) { - JLOG_ERROR("STUN message too short, size=%zu", size); - return -1; - } - - const struct stun_header *header = data; - const size_t length = ntohs(header->length); - if (size < sizeof(struct stun_header) + length) { - JLOG_ERROR("Invalid STUN message length, length=%zu, available=%zu", length, - size - sizeof(struct stun_header)); - return -1; - } - - uint16_t type = ntohs(header->type); - msg->msg_class = (stun_class_t)(type & STUN_CLASS_MASK); - msg->msg_method = (stun_method_t)(type & ~STUN_CLASS_MASK); - memcpy(msg->transaction_id, header->transaction_id, STUN_TRANSACTION_ID_SIZE); - JLOG_VERBOSE("Reading STUN message, class=0x%X, method=0x%X", (unsigned int)msg->msg_class, - (unsigned int)msg->msg_method); - - uint32_t security_bits = 0; - - uint8_t *begin = data; - uint8_t *attr_begin = begin + sizeof(struct stun_header); - uint8_t *end = attr_begin + length; - const uint8_t *pos = attr_begin; - while (pos < end) { - int ret = stun_read_attr(pos, end - pos, msg, begin, attr_begin, &security_bits); - if (ret <= 0) { - JLOG_DEBUG("Reading STUN attribute failed"); - return -1; - } - pos += ret; - } - - JLOG_VERBOSE("Finished reading STUN attributes"); - - stun_credentials_t *credentials = &msg->credentials; - - // RFC 8489: If the response is an error response with an error code of 401 (Unauthenticated) or - // 438 (Stale Nonce), the client MUST test if the NONCE attribute value starts with the "nonce - // cookie". If so and the "nonce cookie" has the STUN Security Feature "Password algorithms" - // bit set to 1 but no PASSWORD-ALGORITHMS attribute is present, then the client MUST NOT retry - // the request with a new transaction. See - // https://www.rfc-editor.org/rfc/rfc8489.html#section-9.2.5 - if (msg->msg_class == STUN_CLASS_RESP_ERROR && - (msg->error_code == 401 || msg->error_code == 438) && - security_bits & STUN_SECURITY_PASSWORD_ALGORITHMS_BIT && - credentials->password_algorithms_value_size == 0) { - JLOG_INFO("STUN Security Feature \"Password algorithms\" bit is set in %u error response " - "but the corresponding attribute is missing", - msg->error_code); - msg->error_code = STUN_ERROR_INTERNAL_VALIDATION_FAILED; // so the agent will give up - } - - // RFC 8489: If the request contains neither the PASSWORD-ALGORITHMS nor the - // PASSWORD-ALGORITHM algorithm, then the request is processed as though - // PASSWORD-ALGORITHM were MD5. - // Otherwise, unless (1) PASSWORD-ALGORITHM and PASSWORD-ALGORITHMS are both - // present, (2) PASSWORD-ALGORITHMS matches the value sent in the response that sent - // this NONCE, and (3) PASSWORD-ALGORITHM matches one of the entries in - // PASSWORD-ALGORITHMS, the server MUST generate an error response with an error code of - // 400 (Bad Request). See https://www.rfc-editor.org/rfc/rfc8489.html#section-9.2.4 - if (!STUN_IS_RESPONSE(msg->msg_class)) { - if (credentials->password_algorithms_value_size == 0 && - credentials->password_algorithm == STUN_PASSWORD_ALGORITHM_UNSET) { - credentials->password_algorithm = STUN_PASSWORD_ALGORITHM_MD5; - - } else if (credentials->password_algorithm == STUN_PASSWORD_ALGORITHM_UNSET) { - JLOG_INFO("No suitable password algorithm in STUN request"); - msg->error_code = STUN_ERROR_INTERNAL_VALIDATION_FAILED; - - } else if (credentials->password_algorithms_value_size == 0) { - JLOG_INFO("Missing password algorithms list in STUN request"); - msg->error_code = STUN_ERROR_INTERNAL_VALIDATION_FAILED; - - } else { - uint8_t pwa_value[STUN_MAX_PASSWORD_ALGORITHMS_VALUE_SIZE]; - size_t pwa_size = generate_password_algorithms_attr(pwa_value); - if (pwa_size != credentials->password_algorithms_value_size || - memcmp(credentials->password_algorithms_value, pwa_value, pwa_size) != 0) { - JLOG_INFO("Password algorithms list is invalid in STUN request"); - msg->error_code = STUN_ERROR_INTERNAL_VALIDATION_FAILED; - } - } - } - - if (security_bits & STUN_SECURITY_USERNAME_ANONYMITY_BIT) { - JLOG_DEBUG("Remote agent supports user anonymity"); - credentials->enable_userhash = true; - } - - return (int)(sizeof(struct stun_header) + length); -} - -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) { - // RFC 8489: When present, the FINGERPRINT attribute MUST be the last attribute in the - // message and thus will appear after MESSAGE-INTEGRITY and MESSAGE-INTEGRITY-SHA256. - if (msg->has_fingerprint) { - JLOG_DEBUG("Invalid STUN attribute after fingerprint"); - return -1; - } - - if (size < sizeof(struct stun_attr)) { - JLOG_VERBOSE("STUN attribute too short"); - return -1; - } - - const struct stun_attr *attr = data; - size_t length = ntohs(attr->length); - stun_attr_type_t type = (stun_attr_type_t)ntohs(attr->type); - JLOG_VERBOSE("Reading attribute 0x%X, length=%zu", (unsigned int)type, length); - if (size < sizeof(struct stun_attr) + length) { - JLOG_DEBUG("STUN attribute length invalid, length=%zu, available=%zu", length, - size - sizeof(struct stun_attr)); - return -1; - } - - // RFC 8489: Note that agents MUST ignore all attributes that follow MESSAGE-INTEGRITY, with - // the exception of the MESSAGE-INTEGRITY-SHA256 and FINGERPRINT attributes. - if (msg->has_integrity && type != STUN_ATTR_MESSAGE_INTEGRITY && - type != STUN_ATTR_MESSAGE_INTEGRITY_SHA256 && type != STUN_ATTR_FINGERPRINT) { - JLOG_DEBUG("Ignoring STUN attribute 0x%X after message integrity", (unsigned int)type); - while (length & 0x03) - ++length; // attributes are aligned on 4 bytes - return (int)(sizeof(struct stun_attr) + length); - } - - switch (type) { - case STUN_ATTR_MAPPED_ADDRESS: { - JLOG_VERBOSE("Reading mapped address"); - uint8_t zero_mask[16] = {0}; - if (stun_read_value_mapped_address(attr->value, length, &msg->mapped, zero_mask) < 0) - return -1; - break; - } - case STUN_ATTR_XOR_MAPPED_ADDRESS: { - JLOG_VERBOSE("Reading XOR mapped address"); - uint8_t mask[16]; - *((uint32_t *)mask) = htonl(STUN_MAGIC); - memcpy(mask + 4, msg->transaction_id, 12); - if (stun_read_value_mapped_address(attr->value, length, &msg->mapped, mask) < 0) - return -1; - break; - } - case STUN_ATTR_ALTERNATE_SERVER: { - JLOG_VERBOSE("Reading alternate server"); - uint8_t zero_mask[16] = {0}; - if (stun_read_value_mapped_address(attr->value, length, &msg->alternate_server, zero_mask) < - 0) - return -1; - break; - } - case STUN_ATTR_ERROR_CODE: { - JLOG_VERBOSE("Reading error code"); - if (length < sizeof(struct stun_value_error_code)) { - JLOG_DEBUG("STUN error code value too short, length=%zu", length); - return -1; - } - const struct stun_value_error_code *error = - (const struct stun_value_error_code *)attr->value; - msg->error_code = (error->code_class & 0x07) * 100 + error->code_number; - - if (msg->error_code == 401 || msg->error_code == 438) { // Unauthenticated or Stale Nonce - JLOG_DEBUG("Got STUN error code %u", msg->error_code); - - } else if (JLOG_INFO_ENABLED) { - size_t reason_length = length - sizeof(struct stun_value_error_code); - if (reason_length >= STUN_MAX_ERROR_REASON_LEN) - reason_length = STUN_MAX_ERROR_REASON_LEN - 1; - - char buffer[STUN_MAX_ERROR_REASON_LEN]; - memcpy(buffer, (const char *)error->reason, reason_length); - buffer[reason_length] = '\0'; - - JLOG_INFO("Got STUN error code %u, reason \"%s\"", msg->error_code, buffer); - } - break; - } - case STUN_ATTR_UNKNOWN_ATTRIBUTES: { - JLOG_VERBOSE("Reading STUN unknown attributes"); - const uint16_t *attributes = (const uint16_t *)attr->value; - for (int i = 0; i < (int)ntohs(attr->length) / 2; ++i) { - stun_attr_type_t type = (stun_attr_type_t)ntohs(attributes[i]); - JLOG_INFO("Got unknown attribute response for attribute 0x%X", (unsigned int)type); - } - break; - } - case STUN_ATTR_USERNAME: { - JLOG_VERBOSE("Reading username"); - if (length + 1 > STUN_MAX_USERNAME_LEN) { - JLOG_WARN("STUN username attribute value too long, length=%zu", length); - return -1; - } - memcpy(msg->credentials.username, (const char *)attr->value, length); - msg->credentials.username[length] = '\0'; - JLOG_VERBOSE("Got username: %s", msg->credentials.username); - break; - } - case STUN_ATTR_MESSAGE_INTEGRITY: { - JLOG_VERBOSE("Reading message integrity"); - if (length != HMAC_SHA1_SIZE) { - JLOG_DEBUG("STUN message integrity length invalid, length=%zu", length); - return -1; - } - msg->has_integrity = true; - break; - } - case STUN_ATTR_MESSAGE_INTEGRITY_SHA256: { - JLOG_VERBOSE("Reading message integrity SHA256"); - if (length != HMAC_SHA256_SIZE) { - JLOG_DEBUG("STUN message integrity SHA256 length invalid, length=%zu", length); - return -1; - } - msg->has_integrity = true; - break; - } - case STUN_ATTR_FINGERPRINT: { - JLOG_VERBOSE("Reading fingerprint"); - if (length != 4) { - JLOG_DEBUG("STUN fingerprint length invalid, length=%zu", length); - return -1; - } - size_t tmp_length = (uint8_t *)data - attr_begin + STUN_ATTR_SIZE + 4; - size_t prev_length = stun_update_header_length(begin, tmp_length); - uint32_t expected = CRC32(begin, (uint8_t *)data - begin) ^ STUN_FINGERPRINT_XOR; - stun_update_header_length(begin, prev_length); - - uint32_t fingerprint = ntohl(*((uint32_t *)attr->value)); - if (fingerprint != expected) { - JLOG_ERROR("STUN fingerprint check failed, expected=%lX, actual=%lX", - (unsigned long)expected, (unsigned long)fingerprint); - return -1; - } - JLOG_VERBOSE("STUN fingerprint check succeeded"); - msg->has_fingerprint = true; - break; - } - case STUN_ATTR_REALM: { - JLOG_VERBOSE("Reading realm"); - if (length + 1 > STUN_MAX_REALM_LEN) { - JLOG_WARN("STUN realm attribute value too long, length=%zu", length); - return -1; - } - memcpy(msg->credentials.realm, (const char *)attr->value, length); - msg->credentials.realm[length] = '\0'; - JLOG_VERBOSE("Got realm: %s", msg->credentials.realm); - break; - } - case STUN_ATTR_NONCE: { - JLOG_VERBOSE("Reading nonce"); - if (length + 1 > STUN_MAX_NONCE_LEN) { - JLOG_WARN("STUN nonce attribute value too long, length=%zu", length); - return -1; - } - memcpy(msg->credentials.nonce, (const char *)attr->value, length); - msg->credentials.nonce[length] = '\0'; - JLOG_VERBOSE("Got nonce: %s", msg->credentials.nonce); - - // If the nonce of a response starts with the nonce cookie, decode the Security Feature bits - // See https://www.rfc-editor.org/rfc/rfc8489.html#section-9.2 - if (STUN_IS_RESPONSE(msg->msg_class) && - strlen(msg->credentials.nonce) > STUN_NONCE_COOKIE_LEN + 4 && - strncmp(msg->credentials.nonce, STUN_NONCE_COOKIE, STUN_NONCE_COOKIE_LEN) == 0) { - char encoded_security_bits[5]; - memcpy(encoded_security_bits, msg->credentials.nonce + STUN_NONCE_COOKIE_LEN, 4); - encoded_security_bits[4] = '\0'; - - uint8_t bytes[4]; - bytes[0] = 0; - int len = BASE64_DECODE(encoded_security_bits, bytes + 1, 3); - if (len == 3) { - *security_bits = ntohl(*((uint32_t *)bytes)); - JLOG_VERBOSE("Nonce has cookie, Security Feature bits are 0x%lX", - (unsigned long)*security_bits); - } else { - JLOG_WARN("Nonce has cookie, but the encoded Security Feature bits field \"%s\" is " - "invalid", - encoded_security_bits); - security_bits = 0; - } - } else if (msg->msg_class == STUN_CLASS_RESP_ERROR) { - JLOG_DEBUG("Remote agent does not support RFC 8489"); - } - break; - } - case STUN_ATTR_PASSWORD_ALGORITHM: { - JLOG_VERBOSE("Reading password algorithm"); - if (length < sizeof(struct stun_value_password_algorithm)) { - JLOG_WARN("STUN password algorithm value too short, length=%zu", length); - return -1; - } - if (!STUN_IS_RESPONSE(msg->msg_class)) { - const struct stun_value_password_algorithm *pwa = - (const struct stun_value_password_algorithm *)attr->value; - stun_password_algorithm_t algorithm = ntohs(pwa->algorithm); - if (algorithm == STUN_PASSWORD_ALGORITHM_MD5 || - algorithm == STUN_PASSWORD_ALGORITHM_SHA256) - msg->credentials.password_algorithm = algorithm; - else - JLOG_WARN("Unknown password algorithm 0x%hX", algorithm); - } else { - JLOG_WARN("Found password algorithm in response, ignoring"); - } - break; - } - case STUN_ATTR_PASSWORD_ALGORITHMS: { - JLOG_VERBOSE("Reading password algorithms list"); - if (length < sizeof(struct stun_value_password_algorithm)) { - JLOG_WARN("STUN password algorithms list too short, length=%zu", length); - return -1; - } - if (length > STUN_MAX_PASSWORD_ALGORITHMS_VALUE_SIZE) { - JLOG_WARN("STUN password algorithms list too long, length=%zu", length); - return -1; - } - - memcpy(msg->credentials.password_algorithms_value, attr->value, length); - msg->credentials.password_algorithms_value_size = length; - - if (!STUN_IS_RESPONSE(msg->msg_class)) { - const uint8_t *pos = attr->value; - const uint8_t *end = pos + length; - while (pos < end) { - if ((size_t)(end - pos) < sizeof(struct stun_value_password_algorithm)) { - JLOG_WARN("STUN password algorithms list truncated, available=%zu", end - pos); - return -1; - } - const struct stun_value_password_algorithm *pwa = - (const struct stun_value_password_algorithm *)pos; - stun_password_algorithm_t algorithm = ntohs(pwa->algorithm); - size_t parameters_length = ntohs(pwa->parameters_length); - size_t padded_length = align32(parameters_length); - - pos += sizeof(struct stun_value_password_algorithm); - - if ((size_t)(end - pos) < padded_length) { - JLOG_WARN( - "STUN password algorithm parameters too long, length=%zu, padded=%zu, " - "available=%zu", - parameters_length, padded_length, end - pos); - return -1; - } - - pos += padded_length; - - if (algorithm == STUN_PASSWORD_ALGORITHM_MD5 || - algorithm == STUN_PASSWORD_ALGORITHM_SHA256) { - msg->credentials.password_algorithm = algorithm; - break; - } - - JLOG_DEBUG("Unknown password algorithm 0x%hX", algorithm); - } - } - break; - } - case STUN_ATTR_USERHASH: { - JLOG_VERBOSE("Reading user hash"); - if (length != USERHASH_SIZE) { - JLOG_WARN("STUN user hash value too long, length=%zu", length); - return -1; - } - memcpy(msg->credentials.userhash, attr->value, USERHASH_SIZE); - msg->credentials.enable_userhash = true; - break; - } - case STUN_ATTR_SOFTWARE: { - JLOG_VERBOSE("Reading software"); - if (length + 1 > STUN_MAX_SOFTWARE_LEN) { - JLOG_WARN("STUN software attribute value too long, length=%zu", length); - return -1; - } - char buffer[STUN_MAX_SOFTWARE_LEN]; - memcpy(buffer, (const char *)attr->value, length); - buffer[length] = '\0'; - JLOG_VERBOSE("Remote agent is \"%s\"", buffer); - break; - } - case STUN_ATTR_PRIORITY: { - JLOG_VERBOSE("Reading priority"); - if (length != 4) { - JLOG_DEBUG("STUN priority length invalid, length=%zu", length); - return -1; - } - msg->priority = ntohl(*((uint32_t *)attr->value)); - JLOG_VERBOSE("Got priority: %lu", (unsigned long)msg->priority); - break; - } - case STUN_ATTR_USE_CANDIDATE: { - JLOG_VERBOSE("Found use candidate flag"); - msg->use_candidate = true; - break; - } - case STUN_ATTR_ICE_CONTROLLING: { - JLOG_VERBOSE("Found ICE controlling attribute"); - if (length != 8) { - JLOG_DEBUG("STUN ICE controlling attribute length invalid, length=%zu", length); - return -1; - } - msg->ice_controlling = ntohll(*((uint64_t *)attr->value)); - break; - } - case STUN_ATTR_ICE_CONTROLLED: { - JLOG_VERBOSE("Found ICE controlled attribute"); - if (length != 8) { - JLOG_DEBUG("STUN ICE controlled attribute length invalid, length=%zu", length); - return -1; - } - msg->ice_controlled = ntohll(*((uint64_t *)attr->value)); - break; - } - case STUN_ATTR_CHANNEL_NUMBER: { - JLOG_VERBOSE("Reading channel number attribute"); - if (length < sizeof(struct stun_value_channel_number)) { - JLOG_DEBUG("STUN channel number attribute value too short, length=%zu", length); - return -1; - } - const struct stun_value_channel_number *channel_number = - (const struct stun_value_channel_number *)attr->value; - msg->channel_number = ntohs(channel_number->channel_number); - break; - } - case STUN_ATTR_LIFETIME: { - JLOG_VERBOSE("Reading lifetime attribute"); - if (length != 4) { - JLOG_DEBUG("STUN lifetime attribute length invalid, length=%zu", length); - return -1; - } - msg->lifetime = ntohl(*((uint32_t *)attr->value)); - msg->lifetime_set = true; - break; - } - case STUN_ATTR_XOR_PEER_ADDRESS: { - JLOG_VERBOSE("Reading XOR peer address"); - uint8_t mask[16]; - *((uint32_t *)mask) = htonl(STUN_MAGIC); - memcpy(mask + 4, msg->transaction_id, 12); - if (stun_read_value_mapped_address(attr->value, length, &msg->peer, mask) < 0) - return -1; - break; - } - case STUN_ATTR_XOR_RELAYED_ADDRESS: { - JLOG_VERBOSE("Reading XOR relayed address"); - uint8_t mask[16]; - *((uint32_t *)mask) = htonl(STUN_MAGIC); - memcpy(mask + 4, msg->transaction_id, 12); - if (stun_read_value_mapped_address(attr->value, length, &msg->relayed, mask) < 0) - return -1; - break; - } - case STUN_ATTR_DATA: { - JLOG_VERBOSE("Found data"); - msg->data = (const char *)attr->value; - msg->data_size = length; - break; - } - case STUN_ATTR_EVEN_PORT: { - JLOG_VERBOSE("Found even port attribute"); - if (length < 1) { - JLOG_DEBUG("STUN even port attribute length invalid, length=%zu", length); - return -1; - } - msg->even_port = true; - msg->next_port = ((struct stun_value_even_port *)attr->value)->r & 0x80; - break; - } - case STUN_ATTR_REQUESTED_TRANSPORT: { - JLOG_VERBOSE("Found requested transport attribute"); - if (length < sizeof(struct stun_value_requested_transport)) { - JLOG_DEBUG("STUN requested transport attribute length invalid, length=%zu", length); - return -1; - } - const struct stun_value_requested_transport *requested_transport = - (const struct stun_value_requested_transport *)attr->value; - if (requested_transport->protocol != 17) { // UDP - JLOG_WARN("Unexpected requested transport protocol: %d", - (int)requested_transport->protocol); - return -1; - } - msg->requested_transport = true; - break; - } - case STUN_ATTR_DONT_FRAGMENT: { - JLOG_VERBOSE("Found don't fragment attribute"); - msg->dont_fragment = true; - break; - } - case STUN_ATTR_RESERVATION_TOKEN: { - JLOG_VERBOSE("Found reservation token"); - if (length != 8) { - JLOG_DEBUG("STUN reservation token length invalid, length=%zu", length); - return -1; - } - msg->reservation_token = ntohll(*((uint64_t *)attr->value)); - break; - } - default: { - // Ignore - if (STUN_IS_OPTIONAL_ATTR(type)) - JLOG_DEBUG("Ignoring unknown optional STUN attribute type 0x%X", (unsigned int)type); - else - JLOG_WARN("Unknown STUN attribute type 0x%X, ignoring", (unsigned int)type); - break; - } - } - return (int)(sizeof(struct stun_attr) + align32(length)); -} - -int stun_read_value_mapped_address(const void *data, size_t size, addr_record_t *mapped, - const uint8_t *mask) { - size_t len = sizeof(struct stun_value_mapped_address); - if (size < len) { - JLOG_VERBOSE("STUN mapped address value too short, size=%zu", size); - return -1; - } - const struct stun_value_mapped_address *value = data; - stun_address_family_t family = (stun_address_family_t)value->family; - switch (family) { - case STUN_ADDRESS_FAMILY_IPV4: { - len += 4; - if (size < len) { - JLOG_DEBUG("IPv4 mapped address value too short, size=%zu", size); - return -1; - } - JLOG_VERBOSE("Reading IPv4 address"); - mapped->len = sizeof(struct sockaddr_in); - struct sockaddr_in *sin = (struct sockaddr_in *)&mapped->addr; - sin->sin_family = AF_INET; - sin->sin_port = value->port ^ *((uint16_t *)mask); - uint8_t *bytes = (uint8_t *)&sin->sin_addr; - for (int i = 0; i < 4; ++i) - bytes[i] = value->address[i] ^ mask[i]; - break; - } - case STUN_ADDRESS_FAMILY_IPV6: { - len += 16; - if (size < len) { - JLOG_DEBUG("IPv6 mapped address value too short, size=%zu", size); - return -1; - } - JLOG_VERBOSE("Reading IPv6 address"); - mapped->len = sizeof(struct sockaddr_in6); - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&mapped->addr; - sin6->sin6_family = AF_INET6; - sin6->sin6_port = value->port ^ *((uint16_t *)mask); - uint8_t *bytes = (uint8_t *)&sin6->sin6_addr; - for (int i = 0; i < 16; ++i) - bytes[i] = value->address[i] ^ mask[i]; - break; - } - default: { - JLOG_DEBUG("Unknown STUN address family 0x%X", (unsigned int)family); - len = size; - break; - } - } - return (int)len; -} - -bool stun_check_integrity(void *buf, size_t size, const stun_message_t *msg, const char *password) { - if (!msg->has_integrity) - return false; - - const struct stun_header *header = buf; - const size_t length = ntohs(header->length); - if (size < sizeof(struct stun_header) + length) - return false; - - uint8_t key[MAX_HMAC_KEY_LEN]; - size_t key_len = generate_hmac_key(msg, password, key); - - bool success = false; - uint8_t *begin = buf; - const uint8_t *attr_begin = begin + sizeof(struct stun_header); - const uint8_t *end = attr_begin + length; - const uint8_t *pos = attr_begin; - while (pos < end) { - const struct stun_attr *attr = (const struct stun_attr *)pos; - size_t attr_length = ntohs(attr->length); - if (size < sizeof(struct stun_attr) + attr_length) - return false; - - stun_attr_type_t type = (stun_attr_type_t)ntohs(attr->type); - switch (type) { - case STUN_ATTR_MESSAGE_INTEGRITY: { - if (attr_length != HMAC_SHA1_SIZE) - return false; - - size_t tmp_length = pos - attr_begin + STUN_ATTR_SIZE + HMAC_SHA1_SIZE; - size_t prev_length = stun_update_header_length(begin, tmp_length); - uint8_t hmac[HMAC_SHA1_SIZE]; - hmac_sha1(begin, pos - begin, key, key_len, hmac); - stun_update_header_length(begin, prev_length); - - const uint8_t *expected_hmac = attr->value; - if (const_time_memcmp(hmac, expected_hmac, HMAC_SHA1_SIZE) != 0) { - JLOG_DEBUG("STUN message integrity SHA1 check failed"); - return false; - } - - success = true; - break; - } - case STUN_ATTR_MESSAGE_INTEGRITY_SHA256: { - if (attr_length != HMAC_SHA256_SIZE) - return false; - - size_t tmp_length = pos - attr_begin + STUN_ATTR_SIZE + HMAC_SHA256_SIZE; - size_t prev_length = stun_update_header_length(begin, tmp_length); - uint8_t hmac[HMAC_SHA256_SIZE]; - hmac_sha256(begin, pos - begin, key, key_len, hmac); - stun_update_header_length(begin, prev_length); - - const uint8_t *expected_hmac = attr->value; - if (const_time_memcmp(hmac, expected_hmac, HMAC_SHA256_SIZE) != 0) { - JLOG_DEBUG("STUN message integrity SHA256 check failed"); - return false; - } - - success = true; - break; - } - default: - // Ignore - break; - } - - pos += sizeof(struct stun_attr) + align32(attr_length); - } - - if (!success) - return false; - - JLOG_VERBOSE("STUN message integrity check succeeded"); - return true; -} - -void stun_prepend_nonce_cookie(char *nonce) { - // RFC 8489: To indicate that it supports this specification, a server MUST prepend the - // NONCE attribute value with the character string composed of "obMatJos2" concatenated with - // the (4-character) base64 [RFC4648] encoding of the 24-bit STUN Security Features See - // https://www.rfc-editor.org/rfc/rfc8489.html#section-9.2 - char copy[STUN_MAX_NONCE_LEN]; - strcpy(copy, nonce); - - char encoded_security_bits[5]; - uint32_t security_bits = - htonl(STUN_SECURITY_PASSWORD_ALGORITHMS_BIT | STUN_SECURITY_USERNAME_ANONYMITY_BIT); - BASE64_ENCODE((uint8_t *)&security_bits + 1, 3, encoded_security_bits, 5); - - snprintf(nonce, STUN_MAX_NONCE_LEN, "%s%s%.*s", STUN_NONCE_COOKIE, encoded_security_bits, - STUN_MAX_NONCE_LEN - (STUN_NONCE_COOKIE_LEN + 5), copy); -} - -void stun_compute_userhash(const char *username, const char *realm, uint8_t *out) { - char input[MAX_USERHASH_INPUT_LEN]; - int input_len = snprintf(input, MAX_USERHASH_INPUT_LEN, "%s:%s", username, realm); - if (input_len < 0) - return; - - if (input_len >= MAX_USERHASH_INPUT_LEN) - input_len = MAX_USERHASH_INPUT_LEN - 1; - - hash_sha256(input, input_len, out); -} - -void stun_process_credentials(const stun_credentials_t *credentials, stun_credentials_t *dst) { - char username[STUN_MAX_USERNAME_LEN]; - strcpy(username, dst->username); - *dst = *credentials; - strcpy(dst->username, username); - - if (credentials->enable_userhash) - stun_compute_userhash(username, credentials->realm, dst->userhash); -} - -const char *stun_get_error_reason(unsigned int code) { - switch (code) { - case 0: - return ""; - case 300: - return "Try Alternate"; - case 400: - return "Bad Request"; - case 401: - return "Unauthenticated"; - case 403: - return "Forbidden"; - case 420: - return "Unknown Attribute"; - case 437: - return "Allocation Mismatch"; - case 438: - return "Stale Nonce"; - case 440: - return "Address Family not Supported"; - case 441: - return "Wrong credentials"; - case 442: - return "Unsupported Transport Protocol"; - case 443: - return "Peer Address Family Mismatch"; - case 486: - return "Allocation Quota Reached"; - case 500: - return "Server Error"; - case 508: - return "Insufficient Capacity"; - default: - return "Error"; - } -} - -JUICE_EXPORT bool _juice_is_stun_datagram(const void *data, size_t size) { - return is_stun_datagram(data, size); -} - -JUICE_EXPORT int _juice_stun_read(void *data, size_t size, stun_message_t *msg) { - return stun_read(data, size, msg); -} - -JUICE_EXPORT bool _juice_stun_check_integrity(void *buf, size_t size, const stun_message_t *msg, - const char *password) { - return stun_check_integrity(buf, size, msg, password); -} diff --git a/thirdparty/libjuice/src/stun.h b/thirdparty/libjuice/src/stun.h deleted file mode 100644 index b3aeade..0000000 --- a/thirdparty/libjuice/src/stun.h +++ /dev/null @@ -1,376 +0,0 @@ -/** - * 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 -#include - -#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 diff --git a/thirdparty/libjuice/src/thread.h b/thirdparty/libjuice/src/thread.h deleted file mode 100644 index 6ea89e6..0000000 --- a/thirdparty/libjuice/src/thread.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * 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 - -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 - -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 -#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 - diff --git a/thirdparty/libjuice/src/timestamp.c b/thirdparty/libjuice/src/timestamp.c deleted file mode 100644 index 2a7cc98..0000000 --- a/thirdparty/libjuice/src/timestamp.c +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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 -#else -#include - -// clock_gettime() is not implemented on older versions of OS X (< 10.12) -#if defined(__APPLE__) && !defined(CLOCK_MONOTONIC) -#include -#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 -} diff --git a/thirdparty/libjuice/src/timestamp.h b/thirdparty/libjuice/src/timestamp.h deleted file mode 100644 index fabc8bb..0000000 --- a/thirdparty/libjuice/src/timestamp.h +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 -#include - -typedef int64_t timestamp_t; -typedef timestamp_t timediff_t; - -timestamp_t current_timestamp(); - -#endif diff --git a/thirdparty/libjuice/src/turn.c b/thirdparty/libjuice/src/turn.c deleted file mode 100644 index 5f4abdb..0000000 --- a/thirdparty/libjuice/src/turn.c +++ /dev/null @@ -1,495 +0,0 @@ -/** - * 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 - -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; -} diff --git a/thirdparty/libjuice/src/turn.h b/thirdparty/libjuice/src/turn.h deleted file mode 100644 index c4e9fc6..0000000 --- a/thirdparty/libjuice/src/turn.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * 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 - -#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 diff --git a/thirdparty/libjuice/src/udp.c b/thirdparty/libjuice/src/udp.c deleted file mode 100644 index 7c7582e..0000000 --- a/thirdparty/libjuice/src/udp.c +++ /dev/null @@ -1,604 +0,0 @@ -/** - * 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 -#include -#include -#include - -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; -} diff --git a/thirdparty/libjuice/src/udp.h b/thirdparty/libjuice/src/udp.h deleted file mode 100644 index c318eec..0000000 --- a/thirdparty/libjuice/src/udp.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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 - -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 diff --git a/thirdparty/libjuice/test/base64.c b/thirdparty/libjuice/test/base64.c deleted file mode 100644 index 1525c96..0000000 --- a/thirdparty/libjuice/test/base64.c +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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 -#include - -#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; -} diff --git a/thirdparty/libjuice/test/bind.c b/thirdparty/libjuice/test/bind.c deleted file mode 100644 index 4bd2b62..0000000 --- a/thirdparty/libjuice/test/bind.c +++ /dev/null @@ -1,225 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifdef _WIN32 -#include -static void sleep(unsigned int secs) { Sleep(secs * 1000); } -#else -#include // 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); -} diff --git a/thirdparty/libjuice/test/conflict.c b/thirdparty/libjuice/test/conflict.c deleted file mode 100644 index 7b34180..0000000 --- a/thirdparty/libjuice/test/conflict.c +++ /dev/null @@ -1,203 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifdef _WIN32 -#include -static void sleep(unsigned int secs) { Sleep(secs * 1000); } -#else -#include // 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); -} diff --git a/thirdparty/libjuice/test/connectivity.c b/thirdparty/libjuice/test/connectivity.c deleted file mode 100644 index 53778cd..0000000 --- a/thirdparty/libjuice/test/connectivity.c +++ /dev/null @@ -1,228 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifdef _WIN32 -#include -static void sleep(unsigned int secs) { Sleep(secs * 1000); } -#else -#include // 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); -} diff --git a/thirdparty/libjuice/test/crc32.c b/thirdparty/libjuice/test/crc32.c deleted file mode 100644 index bf24d7f..0000000 --- a/thirdparty/libjuice/test/crc32.c +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 -#include - -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; -} diff --git a/thirdparty/libjuice/test/gathering.c b/thirdparty/libjuice/test/gathering.c deleted file mode 100644 index 6cfddfd..0000000 --- a/thirdparty/libjuice/test/gathering.c +++ /dev/null @@ -1,95 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifdef _WIN32 -#include -static void sleep(unsigned int secs) { Sleep(secs * 1000); } -#else -#include // 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; -} diff --git a/thirdparty/libjuice/test/main.c b/thirdparty/libjuice/test/main.c deleted file mode 100644 index 5caeba7..0000000 --- a/thirdparty/libjuice/test/main.c +++ /dev/null @@ -1,110 +0,0 @@ -/** - * 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 - -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; -} - diff --git a/thirdparty/libjuice/test/mux.c b/thirdparty/libjuice/test/mux.c deleted file mode 100644 index 4262d5b..0000000 --- a/thirdparty/libjuice/test/mux.c +++ /dev/null @@ -1,226 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifdef _WIN32 -#include -static void sleep(unsigned int secs) { Sleep(secs * 1000); } -#else -#include // 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); -} diff --git a/thirdparty/libjuice/test/notrickle.c b/thirdparty/libjuice/test/notrickle.c deleted file mode 100644 index 42d466d..0000000 --- a/thirdparty/libjuice/test/notrickle.c +++ /dev/null @@ -1,201 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifdef _WIN32 -#include -static void sleep(unsigned int secs) { Sleep(secs * 1000); } -#else -#include // 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); -} diff --git a/thirdparty/libjuice/test/server.c b/thirdparty/libjuice/test/server.c deleted file mode 100644 index b40852d..0000000 --- a/thirdparty/libjuice/test/server.c +++ /dev/null @@ -1,274 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifdef _WIN32 -#include -static void sleep(unsigned int secs) { Sleep(secs * 1000); } -#else -#include // 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 diff --git a/thirdparty/libjuice/test/stun.c b/thirdparty/libjuice/test/stun.c deleted file mode 100644 index 361da0b..0000000 --- a/thirdparty/libjuice/test/stun.c +++ /dev/null @@ -1,155 +0,0 @@ -/** - * 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 -#include - -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 "" 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; -} diff --git a/thirdparty/libjuice/test/thread.c b/thirdparty/libjuice/test/thread.c deleted file mode 100644 index f185914..0000000 --- a/thirdparty/libjuice/test/thread.c +++ /dev/null @@ -1,220 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifdef _WIN32 -#include -static void sleep(unsigned int secs) { Sleep(secs * 1000); } -#else -#include // 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); -} diff --git a/thirdparty/libjuice/test/turn.c b/thirdparty/libjuice/test/turn.c deleted file mode 100644 index da700be..0000000 --- a/thirdparty/libjuice/test/turn.c +++ /dev/null @@ -1,241 +0,0 @@ -/** - * 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 -#include -#include -#include - -#ifdef _WIN32 -#include -static void sleep(unsigned int secs) { Sleep(secs * 1000); } -#else -#include // 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); -} diff --git a/thirdparty/libjuice/xmake.lua b/thirdparty/libjuice/xmake.lua deleted file mode 100644 index 4339343..0000000 --- a/thirdparty/libjuice/xmake.lua +++ /dev/null @@ -1,12 +0,0 @@ -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() \ No newline at end of file diff --git a/thirdparty/xmake.lua b/thirdparty/xmake.lua index 7b1cc7d..e69de29 100644 --- a/thirdparty/xmake.lua +++ b/thirdparty/xmake.lua @@ -1 +0,0 @@ -includes("libjuice") \ No newline at end of file diff --git a/xmake.lua b/xmake.lua index 43c3eb9..08a1bcd 100644 --- a/xmake.lua +++ b/xmake.lua @@ -8,7 +8,6 @@ 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}) if is_os("windows") then add_requires("vcpkg::ffmpeg 5.1.2", {configs = {shared = false}}) @@ -23,7 +22,6 @@ elseif is_os("macosx") then add_requires("brew::libnice", "brew::glib") end -add_defines("JUICE_STATIC") add_defines("ASIO_STANDALONE", "ASIO_HAS_STD_TYPE_TRAITS", "ASIO_HAS_STD_SHARED_PTR", "ASIO_HAS_STD_ADDRESSOF", "ASIO_HAS_STD_ATOMIC", "ASIO_HAS_STD_CHRONO", "ASIO_HAS_CSTDINT", "ASIO_HAS_STD_ARRAY", "ASIO_HAS_STD_SYSTEM_ERROR") @@ -89,25 +87,25 @@ target("rtp") target("ice") set_kind("static") add_deps("log", "common", "ws") - add_packages("asio", "nlohmann_json", "libjuice") - add_files("src/ice/libnice/*.cpp") - add_includedirs("src/ws", "src/ice/libnice", {public = true}) + add_packages("asio", "nlohmann_json") + add_files("src/ice/*.cpp") + add_includedirs("src/ws", "src/ice", {public = true}) if is_os("windows") then - add_includedirs("E:/SourceCode/vcpkg/installed/x64-windows-static/include/glib-2.0", {public = true}) - add_includedirs("E:/SourceCode/vcpkg/installed/x64-windows-static/lib/glib-2.0/include", {public = true}) - add_linkdirs("E:/SourceCode/vcpkg/installed/x64-windows-static/lib") - add_links("nice", "glib-2.0", "gio-2.0", "gmodule-2.0", "gobject-2.0", "gthread-2.0", + add_includedirs(path.join(os.getenv("VCPKG_ROOT"), "installed/x64-windows-static/include/glib-2.0"), {public = true}) + add_includedirs(path.join(os.getenv("VCPKG_ROOT"), "installed/x64-windows-static/lib/glib-2.0/include"), {public = true}) + add_links("nice", "glib-2.0", "gio-2.0", "gmodule-2.0", "gobject-2.0", "pcre2-8", "pcre2-16", "pcre2-32", "pcre2-posix", "zlib", "ffi", "libcrypto", "libssl", "intl", "iconv", "charset", "bz2", "Shell32", "Advapi32", "Dnsapi", "Shlwapi", "Iphlpapi") elseif is_os("macosx") then add_packages("glib", "libnice") - add_includedirs("/usr/local/Cellar/glib/2.78.0/include/glib-2.0", {public = true}) - add_includedirs("/usr/local/Cellar/glib/2.78.0/lib/glib-2.0/include", {public = true}) - add_includedirs("/usr/local/Cellar/glib/2.78.0/include", {public = true}) - add_includedirs("/usr/local/Cellar/libnice/0.1.21/include", {public = true}) - add_linkdirs("/usr/local/Cellar/libnice/0.1.21/lib") - add_linkdirs("/usr/local/Cellar/glib/2.78.0/lib") + add_includedirs(path.join("$(shell brew --cellar)", "glib/2.78.0/include/glib-2.0"), {public = true}) + add_includedirs(path.join("$(shell brew --cellar)", "glib/2.78.0/lib/glib-2.0/include"), {public = true}) + add_includedirs(path.join("$(shell brew --cellar)", "glib/2.78.0/lib/glib-2.0/include"), {public = true}) + add_includedirs(path.join("$(shell brew --cellar)", "glib/2.78.0/include"), {public = true}) + add_includedirs(path.join("$(shell brew --cellar)", "libnice/0.1.21/include"), {public = true}) + add_linkdirs(path.join("$(shell brew --cellar)", "glib/2.78.0/lib")) + add_linkdirs(path.join("$(shell brew --cellar)", "libnice/0.1.21/lib")) add_links("nice", "glib-2.0", "gio-2.0", "gobject-2.0") end