From 311fea07d54a7d3cbd24876e52db83a63e46a0a3 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Fri, 14 Jun 2024 13:27:04 +0100 Subject: [PATCH 1/6] feat: add callback for unhandled STUN requests Calls the functions added to libjuice in https://github.com/paullouisageneau/libjuice/pull/248 Exports a `OnUnhandledStunRequest` function that can be passed a callback that will be invoked when an incoming STUN message is received that has no corresponding agent for the ICE ufrag. Closes #1166 --- deps/libjuice | 2 +- include/rtc/global.hpp | 11 +++++++++ include/rtc/rtc.h | 14 +++++++++++ src/global.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/deps/libjuice b/deps/libjuice index ac7d25205..2fb91a3c9 160000 --- a/deps/libjuice +++ b/deps/libjuice @@ -1 +1 @@ -Subproject commit ac7d2520527e8d4e74386a0e400da927aaf865c6 +Subproject commit 2fb91a3c90b9e5bde2f3cb0ecb4f53b9af027eca diff --git a/include/rtc/global.hpp b/include/rtc/global.hpp index 84317f4dd..37f0c0236 100644 --- a/include/rtc/global.hpp +++ b/include/rtc/global.hpp @@ -34,6 +34,17 @@ RTC_CPP_EXPORT void InitLogger(LogLevel level, LogCallback callback = nullptr); RTC_CPP_EXPORT void Preload(); RTC_CPP_EXPORT std::shared_future Cleanup(); +struct UnhandledStunRequest { + optional localUfrag; + optional remoteUfrag; + std::string address; + uint16_t port; +}; + +RTC_CPP_EXPORT typedef std::function UnhandledStunRequestCallback; + +RTC_CPP_EXPORT void OnUnhandledStunRequest(std::string host, int port, UnhandledStunRequestCallback callback = nullptr); + struct SctpSettings { // For the following settings, not set means optimized default optional recvBufferSize; // in bytes diff --git a/include/rtc/rtc.h b/include/rtc/rtc.h index 2fc70c90c..349687c6e 100644 --- a/include/rtc/rtc.h +++ b/include/rtc/rtc.h @@ -172,6 +172,20 @@ typedef void(RTC_API *rtcAvailableCallbackFunc)(int id, void *ptr); typedef void(RTC_API *rtcPliHandlerCallbackFunc)(int tr, void *ptr); typedef void(RTC_API *rtcRembHandlerCallbackFunc)(int tr, unsigned int bitrate, void *ptr); +// Handle STUN requests with unexpected ufrags + +typedef struct { + const char * ufrag; + const char * pwd; + uint8_t family; + const char * address; + uint16_t port; +} rtcUnhandledStunRequest; + +typedef void(RTC_API *rtcUnhandledStunRequestCallbackFunc)(rtcUnhandledStunRequest request); + +RTC_C_EXPORT void rtcOnUnhandledStunRequest(const char *host, int port, rtcUnhandledStunRequestCallbackFunc callback); + // Log // NULL cb on the first call will log to stdout diff --git a/src/global.cpp b/src/global.cpp index 959053749..81902a4fb 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -19,6 +19,11 @@ #include "impl/init.hpp" #include +#include + +#if !USE_NICE +#include +#endif namespace { @@ -88,6 +93,54 @@ std::shared_future Cleanup() { return impl::Init::Instance().cleanup(); } void SetSctpSettings(SctpSettings s) { impl::Init::Instance().setSctpSettings(std::move(s)); } +#if !USE_NICE + +UnhandledStunRequestCallback unboundStunCallback; + +void InvokeUnhandledStunRequestCallback (const juice_mux_binding_request *info, void *user_ptr) { + PLOG_DEBUG << "Invoking Unbind STUN listener"; + auto callback = static_cast(user_ptr); + + (*callback)({ + std::string(info->local_ufrag), + std::string(info->remote_ufrag), + std::string(info->address), + info->port + }); +} + +#endif + +void OnUnhandledStunRequest ([[maybe_unused]] std::string host, [[maybe_unused]] int port, [[maybe_unused]] UnhandledStunRequestCallback callback) { + #if USE_NICE + PLOG_WARNING << "BindStunListener is not supported with libnice, please use libjuice"; + #else + if (callback == NULL) { + PLOG_DEBUG << "Removing unhandled STUN request listener"; + + // call with NULL callback to unbind + if (juice_mux_listen(host.c_str(), port, NULL, NULL) < 0) { + throw std::runtime_error("Could not unbind STUN listener"); + } + unboundStunCallback = NULL; + + return; + } + + PLOG_DEBUG << "Adding listener for unhandled STUN requests"; + + if (unboundStunCallback != NULL) { + throw std::runtime_error("Unhandled STUN request handler already present"); + } + + unboundStunCallback = std::move(callback); + + if (juice_mux_listen(host.c_str(), port, &InvokeUnhandledStunRequestCallback, &unboundStunCallback) < 0) { + throw std::invalid_argument("Could add listener for unhandled STUN requests"); + } + #endif +} + RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, LogLevel level) { switch (level) { case LogLevel::Fatal: From 9157c0c90696de154eb9cb884ab45cae89a83fe9 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Thu, 16 Jan 2025 19:10:30 +0100 Subject: [PATCH 2/6] fix: support multiple callbacks for different ports --- include/rtc/global.hpp | 17 ++++++++++------ src/global.cpp | 45 ++++++++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/include/rtc/global.hpp b/include/rtc/global.hpp index 37f0c0236..7792e1a43 100644 --- a/include/rtc/global.hpp +++ b/include/rtc/global.hpp @@ -35,15 +35,20 @@ RTC_CPP_EXPORT void Preload(); RTC_CPP_EXPORT std::shared_future Cleanup(); struct UnhandledStunRequest { - optional localUfrag; - optional remoteUfrag; - std::string address; - uint16_t port; + std::string localUfrag; + std::string remoteUfrag; + std::string remoteHost; + uint16_t remotePort; }; -RTC_CPP_EXPORT typedef std::function UnhandledStunRequestCallback; +RTC_CPP_EXPORT typedef std::function UnhandledStunRequestCallback; -RTC_CPP_EXPORT void OnUnhandledStunRequest(std::string host, int port, UnhandledStunRequestCallback callback = nullptr); +struct UnhandledStunRequestHandler { + UnhandledStunRequestCallback callback; + void *userPtr; +}; + +RTC_CPP_EXPORT void OnUnhandledStunRequest(std::string host, int port, UnhandledStunRequestCallback callback = nullptr, void *userPtr = nullptr); struct SctpSettings { // For the following settings, not set means optimized default diff --git a/src/global.cpp b/src/global.cpp index 81902a4fb..f92fab711 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -93,50 +93,57 @@ std::shared_future Cleanup() { return impl::Init::Instance().cleanup(); } void SetSctpSettings(SctpSettings s) { impl::Init::Instance().setSctpSettings(std::move(s)); } -#if !USE_NICE +std::map unboundStunCallbacks; -UnhandledStunRequestCallback unboundStunCallback; +#if !USE_NICE void InvokeUnhandledStunRequestCallback (const juice_mux_binding_request *info, void *user_ptr) { - PLOG_DEBUG << "Invoking Unbind STUN listener"; - auto callback = static_cast(user_ptr); - - (*callback)({ - std::string(info->local_ufrag), - std::string(info->remote_ufrag), - std::string(info->address), - info->port - }); + PLOG_DEBUG << "Invoking unhandled STUN request callback"; + + UnhandledStunRequestHandler *handler = (struct UnhandledStunRequestHandler*)user_ptr; + + if (handler->callback) { + handler->callback({ + std::string(info->local_ufrag), + std::string(info->remote_ufrag), + std::string(info->address), + info->port + }, handler->userPtr); + } else { + PLOG_DEBUG << "No unhandled STUN request callback configured for port " << info->port; + } } #endif -void OnUnhandledStunRequest ([[maybe_unused]] std::string host, [[maybe_unused]] int port, [[maybe_unused]] UnhandledStunRequestCallback callback) { +void OnUnhandledStunRequest ([[maybe_unused]] std::string host, [[maybe_unused]] int port, [[maybe_unused]] UnhandledStunRequestCallback callback, [[maybe_unused]] void *userPtr) { #if USE_NICE PLOG_WARNING << "BindStunListener is not supported with libnice, please use libjuice"; #else if (callback == NULL) { PLOG_DEBUG << "Removing unhandled STUN request listener"; + free(unboundStunCallbacks.at(port)); + unboundStunCallbacks.erase(port); + // call with NULL callback to unbind if (juice_mux_listen(host.c_str(), port, NULL, NULL) < 0) { throw std::runtime_error("Could not unbind STUN listener"); } - unboundStunCallback = NULL; return; } PLOG_DEBUG << "Adding listener for unhandled STUN requests"; - if (unboundStunCallback != NULL) { - throw std::runtime_error("Unhandled STUN request handler already present"); - } + UnhandledStunRequestHandler *handler = (UnhandledStunRequestHandler*)calloc(1, sizeof(UnhandledStunRequestHandler)); + handler->callback = std::move(callback); + handler->userPtr = userPtr; - unboundStunCallback = std::move(callback); + unboundStunCallbacks[port] = handler; - if (juice_mux_listen(host.c_str(), port, &InvokeUnhandledStunRequestCallback, &unboundStunCallback) < 0) { - throw std::invalid_argument("Could add listener for unhandled STUN requests"); + if (juice_mux_listen(host.c_str(), port, &InvokeUnhandledStunRequestCallback, handler) < 0) { + throw std::invalid_argument("Could not add listener for unhandled STUN requests"); } #endif } From 4e0a5d80dab57268758748813ba2141fa768c5c4 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Fri, 17 Jan 2025 17:00:53 +0100 Subject: [PATCH 3/6] chore: pr comments --- include/rtc/global.hpp | 11 +++------- include/rtc/rtc.h | 14 ------------- src/global.cpp | 46 +++++++++++++++++------------------------- 3 files changed, 22 insertions(+), 49 deletions(-) diff --git a/include/rtc/global.hpp b/include/rtc/global.hpp index 7792e1a43..249d1e77e 100644 --- a/include/rtc/global.hpp +++ b/include/rtc/global.hpp @@ -34,21 +34,16 @@ RTC_CPP_EXPORT void InitLogger(LogLevel level, LogCallback callback = nullptr); RTC_CPP_EXPORT void Preload(); RTC_CPP_EXPORT std::shared_future Cleanup(); -struct UnhandledStunRequest { +struct IceUdpMuxRequest { std::string localUfrag; std::string remoteUfrag; std::string remoteHost; uint16_t remotePort; }; -RTC_CPP_EXPORT typedef std::function UnhandledStunRequestCallback; +RTC_CPP_EXPORT typedef std::function IceUdpMuxCallback; -struct UnhandledStunRequestHandler { - UnhandledStunRequestCallback callback; - void *userPtr; -}; - -RTC_CPP_EXPORT void OnUnhandledStunRequest(std::string host, int port, UnhandledStunRequestCallback callback = nullptr, void *userPtr = nullptr); +RTC_CPP_EXPORT void ListenIceUdpMux (int port, IceUdpMuxCallback *callback, std::optional bindAddress = nullptr); struct SctpSettings { // For the following settings, not set means optimized default diff --git a/include/rtc/rtc.h b/include/rtc/rtc.h index 349687c6e..2fc70c90c 100644 --- a/include/rtc/rtc.h +++ b/include/rtc/rtc.h @@ -172,20 +172,6 @@ typedef void(RTC_API *rtcAvailableCallbackFunc)(int id, void *ptr); typedef void(RTC_API *rtcPliHandlerCallbackFunc)(int tr, void *ptr); typedef void(RTC_API *rtcRembHandlerCallbackFunc)(int tr, unsigned int bitrate, void *ptr); -// Handle STUN requests with unexpected ufrags - -typedef struct { - const char * ufrag; - const char * pwd; - uint8_t family; - const char * address; - uint16_t port; -} rtcUnhandledStunRequest; - -typedef void(RTC_API *rtcUnhandledStunRequestCallbackFunc)(rtcUnhandledStunRequest request); - -RTC_C_EXPORT void rtcOnUnhandledStunRequest(const char *host, int port, rtcUnhandledStunRequestCallbackFunc callback); - // Log // NULL cb on the first call will log to stdout diff --git a/src/global.cpp b/src/global.cpp index f92fab711..5cf2873f3 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -93,57 +93,49 @@ std::shared_future Cleanup() { return impl::Init::Instance().cleanup(); } void SetSctpSettings(SctpSettings s) { impl::Init::Instance().setSctpSettings(std::move(s)); } -std::map unboundStunCallbacks; - #if !USE_NICE -void InvokeUnhandledStunRequestCallback (const juice_mux_binding_request *info, void *user_ptr) { - PLOG_DEBUG << "Invoking unhandled STUN request callback"; +void InvokeIceUdpMuxCallback (const juice_mux_binding_request *info, void *user_ptr) { + PLOG_DEBUG << "Invoking ICE UDP mux callback"; - UnhandledStunRequestHandler *handler = (struct UnhandledStunRequestHandler*)user_ptr; + IceUdpMuxCallback *callback = (IceUdpMuxCallback*)user_ptr; - if (handler->callback) { - handler->callback({ + if (callback) { + (*callback)({ std::string(info->local_ufrag), std::string(info->remote_ufrag), std::string(info->address), info->port - }, handler->userPtr); + }); } else { - PLOG_DEBUG << "No unhandled STUN request callback configured for port " << info->port; + PLOG_DEBUG << "No ICE UDP mux callback configured for port " << info->port; } } #endif -void OnUnhandledStunRequest ([[maybe_unused]] std::string host, [[maybe_unused]] int port, [[maybe_unused]] UnhandledStunRequestCallback callback, [[maybe_unused]] void *userPtr) { +void ListenIceUdpMux ([[maybe_unused]] int port, [[maybe_unused]] IceUdpMuxCallback *callback, [[maybe_unused]] std::optional bindAddress) { #if USE_NICE - PLOG_WARNING << "BindStunListener is not supported with libnice, please use libjuice"; + PLOG_WARNING << "ListenIceUdpMux is not supported with libnice, please use libjuice"; #else - if (callback == NULL) { - PLOG_DEBUG << "Removing unhandled STUN request listener"; + // NULL host binds to all available addresses + const char *host = bindAddress.has_value() ? bindAddress.value().c_str() : NULL; - free(unboundStunCallbacks.at(port)); - unboundStunCallbacks.erase(port); + if (callback == NULL) { + PLOG_DEBUG << "Removing ICE UDP mux callback for port " << port; - // call with NULL callback to unbind - if (juice_mux_listen(host.c_str(), port, NULL, NULL) < 0) { - throw std::runtime_error("Could not unbind STUN listener"); + // call with NULL callback to remove the listener for the host/port + if (juice_mux_listen(host, port, NULL, NULL) < 0) { + throw std::runtime_error("Could not remove ICE UDP mux callback"); } return; } - PLOG_DEBUG << "Adding listener for unhandled STUN requests"; - - UnhandledStunRequestHandler *handler = (UnhandledStunRequestHandler*)calloc(1, sizeof(UnhandledStunRequestHandler)); - handler->callback = std::move(callback); - handler->userPtr = userPtr; - - unboundStunCallbacks[port] = handler; + PLOG_DEBUG << "Adding ICE UDP mux callback for port " << port; - if (juice_mux_listen(host.c_str(), port, &InvokeUnhandledStunRequestCallback, handler) < 0) { - throw std::invalid_argument("Could not add listener for unhandled STUN requests"); + if (juice_mux_listen(host, port, &InvokeIceUdpMuxCallback, callback) < 0) { + throw std::invalid_argument("Could not add ICE UDP mux callback"); } #endif } From fcfcc4f6850b53ca3afe279a624b06cb9646fe90 Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Thu, 23 Jan 2025 14:33:39 +0100 Subject: [PATCH 4/6] Refactor API into an IceUdpMuxListener object --- CMakeLists.txt | 4 ++ include/rtc/global.hpp | 11 ------ include/rtc/iceudpmuxlistener.hpp | 47 +++++++++++++++++++++++ include/rtc/rtc.hpp | 1 + src/global.cpp | 54 +-------------------------- src/iceudpmuxlistener.cpp | 29 +++++++++++++++ src/impl/iceudpmuxlistener.cpp | 62 +++++++++++++++++++++++++++++++ src/impl/iceudpmuxlistener.hpp | 45 ++++++++++++++++++++++ 8 files changed, 189 insertions(+), 64 deletions(-) create mode 100644 include/rtc/iceudpmuxlistener.hpp create mode 100644 src/iceudpmuxlistener.cpp create mode 100644 src/impl/iceudpmuxlistener.cpp create mode 100644 src/impl/iceudpmuxlistener.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fe08cc435..90bcce490 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,7 @@ set(LIBDATACHANNEL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/datachannel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/description.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/iceudpmuxlistener.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/global.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/message.cpp @@ -99,6 +100,7 @@ set(LIBDATACHANNEL_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/configuration.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/datachannel.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/description.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/iceudpmuxlistener.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediahandler.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpreceivingsession.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.hpp @@ -139,6 +141,7 @@ set(LIBDATACHANNEL_IMPL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/iceudpmuxlistener.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/init.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.cpp @@ -171,6 +174,7 @@ set(LIBDATACHANNEL_IMPL_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlssrtptransport.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/iceudpmuxlistener.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/init.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/internals.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.hpp diff --git a/include/rtc/global.hpp b/include/rtc/global.hpp index 249d1e77e..84317f4dd 100644 --- a/include/rtc/global.hpp +++ b/include/rtc/global.hpp @@ -34,17 +34,6 @@ RTC_CPP_EXPORT void InitLogger(LogLevel level, LogCallback callback = nullptr); RTC_CPP_EXPORT void Preload(); RTC_CPP_EXPORT std::shared_future Cleanup(); -struct IceUdpMuxRequest { - std::string localUfrag; - std::string remoteUfrag; - std::string remoteHost; - uint16_t remotePort; -}; - -RTC_CPP_EXPORT typedef std::function IceUdpMuxCallback; - -RTC_CPP_EXPORT void ListenIceUdpMux (int port, IceUdpMuxCallback *callback, std::optional bindAddress = nullptr); - struct SctpSettings { // For the following settings, not set means optimized default optional recvBufferSize; // in bytes diff --git a/include/rtc/iceudpmuxlistener.hpp b/include/rtc/iceudpmuxlistener.hpp new file mode 100644 index 000000000..df72c4df6 --- /dev/null +++ b/include/rtc/iceudpmuxlistener.hpp @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2025 Alex Potsides + * Copyright (c) 2025 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 RTC_ICE_UDP_MUX_LISTENER_H +#define RTC_ICE_UDP_MUX_LISTENER_H + +#include "common.hpp" + +namespace rtc { + +namespace impl { + +struct IceUdpMuxListener; + +} // namespace impl + +struct IceUdpMuxRequest { // TODO change name + string localUfrag; + string remoteUfrag; + string remoteAddress; + uint16_t remotePort; +}; + +class RTC_CPP_EXPORT IceUdpMuxListener final : private CheshireCat { +public: + IceUdpMuxListener(uint16_t port, optional bindAddress = nullopt); + ~IceUdpMuxListener(); + + void stop(); + + uint16_t port() const; + + void OnUnhandledStunRequest(std::function callback); + +private: + using CheshireCat::impl; +}; + +} // namespace rtc + +#endif diff --git a/include/rtc/rtc.hpp b/include/rtc/rtc.hpp index 74a9c9e8b..4efae29dd 100644 --- a/include/rtc/rtc.hpp +++ b/include/rtc/rtc.hpp @@ -16,6 +16,7 @@ #include "datachannel.hpp" #include "peerconnection.hpp" #include "track.hpp" +#include "iceudpmuxlistener.hpp" #if RTC_ENABLE_WEBSOCKET diff --git a/src/global.cpp b/src/global.cpp index 5cf2873f3..a7ed576f2 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -19,11 +19,6 @@ #include "impl/init.hpp" #include -#include - -#if !USE_NICE -#include -#endif namespace { @@ -93,54 +88,7 @@ std::shared_future Cleanup() { return impl::Init::Instance().cleanup(); } void SetSctpSettings(SctpSettings s) { impl::Init::Instance().setSctpSettings(std::move(s)); } -#if !USE_NICE - -void InvokeIceUdpMuxCallback (const juice_mux_binding_request *info, void *user_ptr) { - PLOG_DEBUG << "Invoking ICE UDP mux callback"; - - IceUdpMuxCallback *callback = (IceUdpMuxCallback*)user_ptr; - - if (callback) { - (*callback)({ - std::string(info->local_ufrag), - std::string(info->remote_ufrag), - std::string(info->address), - info->port - }); - } else { - PLOG_DEBUG << "No ICE UDP mux callback configured for port " << info->port; - } -} - -#endif - -void ListenIceUdpMux ([[maybe_unused]] int port, [[maybe_unused]] IceUdpMuxCallback *callback, [[maybe_unused]] std::optional bindAddress) { - #if USE_NICE - PLOG_WARNING << "ListenIceUdpMux is not supported with libnice, please use libjuice"; - #else - // NULL host binds to all available addresses - const char *host = bindAddress.has_value() ? bindAddress.value().c_str() : NULL; - - if (callback == NULL) { - PLOG_DEBUG << "Removing ICE UDP mux callback for port " << port; - - // call with NULL callback to remove the listener for the host/port - if (juice_mux_listen(host, port, NULL, NULL) < 0) { - throw std::runtime_error("Could not remove ICE UDP mux callback"); - } - - return; - } - - PLOG_DEBUG << "Adding ICE UDP mux callback for port " << port; - - if (juice_mux_listen(host, port, &InvokeIceUdpMuxCallback, callback) < 0) { - throw std::invalid_argument("Could not add ICE UDP mux callback"); - } - #endif -} - -RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, LogLevel level) { +std::ostream &operator<<(std::ostream &out, LogLevel level) { switch (level) { case LogLevel::Fatal: out << "fatal"; diff --git a/src/iceudpmuxlistener.cpp b/src/iceudpmuxlistener.cpp new file mode 100644 index 000000000..59c056241 --- /dev/null +++ b/src/iceudpmuxlistener.cpp @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2025 Alex Potsides + * Copyright (c) 2025 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 "iceudpmuxlistener.hpp" + +#include "impl/iceudpmuxlistener.hpp" + +namespace rtc { + +IceUdpMuxListener::IceUdpMuxListener(uint16_t port, optional bindAddress) + : CheshireCat(port, std::move(bindAddress)) {} + +IceUdpMuxListener::~IceUdpMuxListener() {} + +void IceUdpMuxListener::stop() { impl()->stop(); } + +uint16_t IceUdpMuxListener::port() const { return impl()->port; } + +void IceUdpMuxListener::OnUnhandledStunRequest(std::function callback) { + impl()->unhandledStunRequestCallback = callback; +} + +} // namespace rtc diff --git a/src/impl/iceudpmuxlistener.cpp b/src/impl/iceudpmuxlistener.cpp new file mode 100644 index 000000000..f68045831 --- /dev/null +++ b/src/impl/iceudpmuxlistener.cpp @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2025 Alex Potsides + * Copyright (c) 2025 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 "iceudpmuxlistener.hpp" +#include "internals.hpp" + +namespace rtc::impl { + +#if !USE_NICE +void IceUdpMuxListener::UnhandledStunRequestCallback(const juice_mux_binding_request *info, + void *user_ptr) { + auto listener = static_cast(user_ptr); + if (!listener) + return; + + IceUdpMuxRequest request; + request.localUfrag = info->local_ufrag; + request.remoteUfrag = info->remote_ufrag; + request.remoteAddress = info->address; + request.remotePort = info->port; + listener->unhandledStunRequestCallback(std::move(request)); +} +#endif + +IceUdpMuxListener::IceUdpMuxListener(uint16_t port, optional bindAddress) : port(port) { + PLOG_VERBOSE << "Creating IceUdpMuxListener"; + +#if !USE_NICE + PLOG_DEBUG << "Registering ICE UDP mux listener for port " << port; + if (juice_mux_listen(bindAddress ? bindAddress->c_str() : NULL, port, + IceUdpMuxListener::UnhandledStunRequestCallback, this) < 0) { + throw std::runtime_error("Failed to register ICE UDP mux listener"); + } +#else + PLOG_WARNING << "ICE UDP mux is not available with libnice"; +#endif +} + +IceUdpMuxListener::~IceUdpMuxListener() { + PLOG_VERBOSE << "Destroying IceUdpMuxListener"; + stop(); +} + +void IceUdpMuxListener::stop() { + if (mStopped.exchange(true)) + return; + +#if !USE_NICE + PLOG_DEBUG << "Unregistering ICE UDP mux listener for port " << port; + if (juice_mux_listen(NULL, port, NULL, NULL) < 0) { + PLOG_ERROR << "Failed to unregister ICE UDP mux listener"; + } +#endif +} + +} // namespace rtc::impl diff --git a/src/impl/iceudpmuxlistener.hpp b/src/impl/iceudpmuxlistener.hpp new file mode 100644 index 000000000..03ca6f6f5 --- /dev/null +++ b/src/impl/iceudpmuxlistener.hpp @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2025 Alex Potsides + * Copyright (c) 2025 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 RTC_IMPL_ICE_UDP_MUX_LISTENER_H +#define RTC_IMPL_ICE_UDP_MUX_LISTENER_H + +#include "common.hpp" + +#include "rtc/iceudpmuxlistener.hpp" + +#if !USE_NICE +#include +#endif + +#include + +namespace rtc::impl { + +struct IceUdpMuxListener final { + IceUdpMuxListener(uint16_t port, optional bindAddress = nullopt); + ~IceUdpMuxListener(); + + void stop(); + + const uint16_t port; + synchronized_callback unhandledStunRequestCallback; + +private: +#if !USE_NICE + static void UnhandledStunRequestCallback(const juice_mux_binding_request *info, void *user_ptr); +#endif + + std::atomic mStopped; +}; + +} + +#endif + From b8e534816f33f57df26868e0c21378662b0862af Mon Sep 17 00:00:00 2001 From: Paul-Louis Ageneau Date: Thu, 23 Jan 2025 14:47:17 +0100 Subject: [PATCH 5/6] Fix warning with libnice --- src/impl/iceudpmuxlistener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl/iceudpmuxlistener.cpp b/src/impl/iceudpmuxlistener.cpp index f68045831..0ee8c0f83 100644 --- a/src/impl/iceudpmuxlistener.cpp +++ b/src/impl/iceudpmuxlistener.cpp @@ -28,7 +28,7 @@ void IceUdpMuxListener::UnhandledStunRequestCallback(const juice_mux_binding_req } #endif -IceUdpMuxListener::IceUdpMuxListener(uint16_t port, optional bindAddress) : port(port) { +IceUdpMuxListener::IceUdpMuxListener(uint16_t port, [[maybe_unused]] optional bindAddress) : port(port) { PLOG_VERBOSE << "Creating IceUdpMuxListener"; #if !USE_NICE From d15c2cdc78247296ad2e130566fa782656495d8d Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Thu, 6 Feb 2025 15:19:11 +0100 Subject: [PATCH 6/6] chore: whitespace --- src/impl/iceudpmuxlistener.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl/iceudpmuxlistener.hpp b/src/impl/iceudpmuxlistener.hpp index 03ca6f6f5..f58a2f57a 100644 --- a/src/impl/iceudpmuxlistener.hpp +++ b/src/impl/iceudpmuxlistener.hpp @@ -24,7 +24,7 @@ namespace rtc::impl { struct IceUdpMuxListener final { IceUdpMuxListener(uint16_t port, optional bindAddress = nullopt); - ~IceUdpMuxListener(); + ~IceUdpMuxListener(); void stop();