Browse Source

Merge pull request #1211 from achingbrain/feat/expose-callback-for-unknown-stun-ufrag

feat: add listener for unhandled STUN requests with ICE UDP mux
Paul-Louis Ageneau 4 months ago
parent
commit
7dc38e179b

+ 4 - 0
CMakeLists.txt

@@ -66,6 +66,7 @@ set(LIBDATACHANNEL_SOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/datachannel.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/datachannel.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/description.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/mediahandler.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/global.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/global.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/message.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/configuration.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/datachannel.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/datachannel.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/description.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/mediahandler.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpreceivingsession.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpreceivingsession.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.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/dtlssrtptransport.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.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/init.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/logcounter.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/dtlssrtptransport.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/dtlstransport.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/icetransport.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/init.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/internals.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/internals.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/peerconnection.hpp

+ 1 - 1
deps/libjuice

@@ -1 +1 @@
-Subproject commit 8d1a99a0683a811876c03a73ff764a92774027ad
+Subproject commit 77daa8befd828046169adbb012d8de928bfdcdd4

+ 47 - 0
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<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 - 0
include/rtc/rtc.hpp

@@ -16,6 +16,7 @@
 #include "datachannel.hpp"
 #include "datachannel.hpp"
 #include "peerconnection.hpp"
 #include "peerconnection.hpp"
 #include "track.hpp"
 #include "track.hpp"
+#include "iceudpmuxlistener.hpp"
 
 
 #if RTC_ENABLE_WEBSOCKET
 #if RTC_ENABLE_WEBSOCKET
 
 

+ 1 - 1
src/global.cpp

@@ -88,7 +88,7 @@ std::shared_future<void> Cleanup() { return impl::Init::Instance().cleanup(); }
 
 
 void SetSctpSettings(SctpSettings s) { impl::Init::Instance().setSctpSettings(std::move(s)); }
 void SetSctpSettings(SctpSettings s) { impl::Init::Instance().setSctpSettings(std::move(s)); }
 
 
-RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, LogLevel level) {
+std::ostream &operator<<(std::ostream &out, LogLevel level) {
 	switch (level) {
 	switch (level) {
 	case LogLevel::Fatal:
 	case LogLevel::Fatal:
 		out << "fatal";
 		out << "fatal";

+ 29 - 0
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<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 - 0
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<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, [[maybe_unused]] 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 - 0
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 <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
+