-
-
Notifications
You must be signed in to change notification settings - Fork 391
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
311fea0
9157c0c
4e0a5d8
fcfcc4f
b8e5348
d15c2cd
b47bae7
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 |
---|---|---|
|
@@ -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 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.
Correct, |
||
|
||
#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! 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 guess the only problem with this is the invocation to unbind a listener with a host now looks a bit awkward, certainly in js: listenIceUdpMux(port, null, host) How about passing a |
||
#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 - 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. To be honest, making the memory management unsafe and delegating it to the user is a complete footgun, and even if the user manages lifetimes correctly, simply changing the function on user side would create a race condition. More importantly, it prevents from using lambdas, which removes at lot of the appeal of callbacks: rtc::ListenIceUdpMux(port, [](IceUdpMuxRequest request) {
// Do something
}); |
||
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)); | ||
paullouisageneau marked this conversation as resolved.
Show resolved
Hide resolved
|
||
handler->callback = std::move(callback); | ||
handler->userPtr = userPtr; | ||
|
||
unboundStunCallbacks[port] = handler; | ||
paullouisageneau marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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
?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.
You are right that it's not needed if you store only a pointer to an external function object, since the concern is pushed to the user. However, I don't think it's a good idea, as stated in #1211 (comment).