Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add listener for unhandled STUN requests with ICE UDP mux #1211

4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
11 changes: 0 additions & 11 deletions include/rtc/global.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> Cleanup();

struct IceUdpMuxRequest {
std::string localUfrag;
std::string remoteUfrag;
std::string remoteHost;
uint16_t remotePort;
};

RTC_CPP_EXPORT typedef std::function<void(IceUdpMuxRequest request)> IceUdpMuxCallback;

RTC_CPP_EXPORT void ListenIceUdpMux (int port, IceUdpMuxCallback *callback, std::optional<std::string> bindAddress = nullptr);

struct SctpSettings {
// For the following settings, not set means optimized default
optional<size_t> recvBufferSize; // in bytes
Expand Down
47 changes: 47 additions & 0 deletions include/rtc/iceudpmuxlistener.hpp
Original file line number Diff line number Diff line change
@@ -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<impl::IceUdpMuxListener> {
public:
IceUdpMuxListener(uint16_t port, optional<string> bindAddress = nullopt);
~IceUdpMuxListener();

void stop();

uint16_t port() const;

void OnUnhandledStunRequest(std::function<void(IceUdpMuxRequest)> callback);

private:
using CheshireCat<impl::IceUdpMuxListener>::impl;
};

} // namespace rtc

#endif
1 change: 1 addition & 0 deletions include/rtc/rtc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "datachannel.hpp"
#include "peerconnection.hpp"
#include "track.hpp"
#include "iceudpmuxlistener.hpp"

#if RTC_ENABLE_WEBSOCKET

Expand Down
54 changes: 1 addition & 53 deletions src/global.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@
#include "impl/init.hpp"

#include <mutex>
#include <map>

#if !USE_NICE
#include <juice/juice.h>
#endif

namespace {

Expand Down Expand Up @@ -93,54 +88,7 @@ std::shared_future<void> 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<std::string> 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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RTC_CPP_EXPORT has been removed here, not sure if that was intentional.

switch (level) {
case LogLevel::Fatal:
out << "fatal";
Expand Down
29 changes: 29 additions & 0 deletions src/iceudpmuxlistener.cpp
Original file line number Diff line number Diff line change
@@ -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<string> bindAddress)
: CheshireCat<impl::IceUdpMuxListener>(port, std::move(bindAddress)) {}

IceUdpMuxListener::~IceUdpMuxListener() {}

void IceUdpMuxListener::stop() { impl()->stop(); }

uint16_t IceUdpMuxListener::port() const { return impl()->port; }

void IceUdpMuxListener::OnUnhandledStunRequest(std::function<void(IceUdpMuxRequest)> callback) {
impl()->unhandledStunRequestCallback = callback;
}

} // namespace rtc
62 changes: 62 additions & 0 deletions src/impl/iceudpmuxlistener.cpp
Original file line number Diff line number Diff line change
@@ -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<IceUdpMuxListener *>(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<string> 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
45 changes: 45 additions & 0 deletions src/impl/iceudpmuxlistener.hpp
Original file line number Diff line number Diff line change
@@ -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 <juice/juice.h>
#endif

#include <atomic>

namespace rtc::impl {

struct IceUdpMuxListener final {
IceUdpMuxListener(uint16_t port, optional<string> bindAddress = nullopt);
~IceUdpMuxListener();

void stop();

const uint16_t port;
synchronized_callback<IceUdpMuxRequest> unhandledStunRequestCallback;

private:
#if !USE_NICE
static void UnhandledStunRequestCallback(const juice_mux_binding_request *info, void *user_ptr);
#endif

std::atomic<bool> mStopped;
};

}

#endif

Loading