-
-
Notifications
You must be signed in to change notification settings - Fork 376
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 callback for unhandled STUN requests #1211
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
+13 −0 | include/juice/juice.h | |
+8 −4 | src/agent.c | |
+67 −1 | src/conn.c | |
+2 −0 | src/conn.h | |
+34 −8 | src/conn_mux.c | |
+1 −0 | src/conn_mux.h | |
+1 −0 | src/conn_poll.c | |
+1 −0 | src/conn_thread.c | |
+9 −11 | src/juice.c |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,22 @@ RTC_CPP_EXPORT void InitLogger(LogLevel level, LogCallback callback = nullptr); | |
RTC_CPP_EXPORT void Preload(); | ||
RTC_CPP_EXPORT std::shared_future<void> Cleanup(); | ||
|
||
struct UnhandledStunRequest { | ||
std::string localUfrag; | ||
std::string remoteUfrag; | ||
std::string remoteHost; | ||
uint16_t remotePort; | ||
}; | ||
|
||
RTC_CPP_EXPORT typedef std::function<void(UnhandledStunRequest request, void* userPtr)> UnhandledStunRequestCallback; | ||
|
||
struct UnhandledStunRequestHandler { | ||
UnhandledStunRequestCallback callback; | ||
void *userPtr; | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you don't need this since the user pointer is useless in C++. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed! |
||
|
||
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 | ||
optional<size_t> recvBufferSize; // in bytes | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the contrary, the C callback should have a user pointer as there is no way to pass context otherwise. It should be stored by capturing it in the C++ callback. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I think this def might be left over from a previous attempt. I've removed it. |
||
|
||
RTC_C_EXPORT void rtcOnUnhandledStunRequest(const char *host, int port, rtcUnhandledStunRequestCallbackFunc callback); | ||
|
||
// Log | ||
|
||
// NULL cb on the first call will log to stdout | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,11 @@ | |
#include "impl/init.hpp" | ||
|
||
#include <mutex> | ||
#include <map> | ||
|
||
#if !USE_NICE | ||
#include <juice/juice.h> | ||
#endif | ||
|
||
namespace { | ||
|
||
|
@@ -88,6 +93,61 @@ std::shared_future<void> Cleanup() { return impl::Init::Instance().cleanup(); } | |
|
||
void SetSctpSettings(SctpSettings s) { impl::Init::Instance().setSctpSettings(std::move(s)); } | ||
|
||
std::map<int, UnhandledStunRequestHandler *> unboundStunCallbacks; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should callbacks be specified by port or by bind address? Just asking because it needs to be consistent with libjuice. I think port makes sense, since the user is typically going to set the same There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The libjuice PR uses Since the juice_mux_listen("0.0.0.0", port, &cb, NULL);
juice_mux_listen("::", port, &cb, NULL);
Actually I'm not sure that's correct, passing |
||
|
||
#if !USE_NICE | ||
|
||
void InvokeUnhandledStunRequestCallback (const juice_mux_binding_request *info, void *user_ptr) { | ||
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, [[maybe_unused]] void *userPtr) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name should reflect that this listens only on mux mode, and that it attempts to listen to a specific port, you could rename it to
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed! |
||
#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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This causes a race condition: If the callback is called in parallel it will cause a use after free. Also, if no callback is set for the port it will throw an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've removed the map - |
||
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"); | ||
} | ||
|
||
return; | ||
} | ||
|
||
PLOG_DEBUG << "Adding listener for unhandled STUN requests"; | ||
|
||
UnhandledStunRequestHandler *handler = (UnhandledStunRequestHandler*)calloc(1, sizeof(UnhandledStunRequestHandler)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't use C-style functions in C++. Here, you should use a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed! |
||
handler->callback = std::move(callback); | ||
handler->userPtr = userPtr; | ||
|
||
unboundStunCallbacks[port] = handler; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There should be a mutex protecting the map otherwise the function is not thread-safe. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've removed the map. |
||
|
||
if (juice_mux_listen(host.c_str(), port, &InvokeUnhandledStunRequestCallback, handler) < 0) { | ||
throw std::invalid_argument("Could not add listener for unhandled STUN requests"); | ||
} | ||
#endif | ||
} | ||
|
||
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, LogLevel level) { | ||
switch (level) { | ||
case LogLevel::Fatal: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An
std::function
should not have a user pointer as it has its own context. The user can use bind, lambda capture, or a callable object.Additionally, all callbacks in the library should be implemented with
synchronized_callback
to prevent race conditions between calling and setting/resetting.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed the user pointer part.
I'm not sure I follow about
synchronized_callback
- where it's used elsewhere here a callback has been passed in that's stored as a class member. That member can get overwritten so I can see why we need to synchronize on it's access.Here we're passing a reference to a function to
libjuice
and all state is passed along with the function invocation - do we still needsynchronized_callback
?