-
-
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?
feat: add callback for unhandled STUN requests #1211
Conversation
When implementing this I hadn't realised that libjuice will only ever allow one UDP socket to be used so unregistering the handler takes a host/port pair in order to unregister the handler only for that pair. I hope this can be worked around as it's quite common to create two listeners during a test run and they'll not be able to co-exist in the same process like this. We could, for example invalidate the module cache when loading the native part of
Anyway, if you'd prefer a separate function without the host/port to remove the handler please shout. It may not need the |
This should not happen. In order to avoid such hidden errors, I added a check in |
The Mux mode is bound to a unique socket, which has no problem at all in a normal WebRTC application, after all, you can't even set the port in the browser. But in the Peer B application of WebRTC Direct, it is restricted by this. One process can only bind to one port, which greatly limits its usage. |
4de4a6d
to
af8f6c8
Compare
Calls the functions added to libjuice in paullouisageneau/libjuice#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 paullouisageneau#1166
41e2380
to
311fea0
Compare
Needs the changes in paullouisageneau/libjuice#292 before this can be merged. |
src/global.cpp
Outdated
|
||
#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 comment
The 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 ListenIceUdpMux()
for instance, since the feature is called enableIceUdpMux
in rtc::Configuration
.
host
should also be optional to allow any address (it would pass NULL to libjuice), and you could remame it to bindAddress
as it is the name in the configuration. If it's not part of the mapping, I think it should be moved at the end and made an optional parameter.
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!
src/global.cpp
Outdated
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 comment
The 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 std::out_of_range
exception which is confusing for the user so you should use find
instead.
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.
I've removed the map - ListenIceUdpMux
now accepts a pointer to a callback and the caller of ListenIceUdpMux
is responsible for it's memory management.
include/rtc/global.hpp
Outdated
uint16_t remotePort; | ||
}; | ||
|
||
RTC_CPP_EXPORT typedef std::function<void(UnhandledStunRequest request, void* userPtr)> UnhandledStunRequestCallback; |
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 need synchronized_callback
?
include/rtc/global.hpp
Outdated
struct UnhandledStunRequestHandler { | ||
UnhandledStunRequestCallback callback; | ||
void *userPtr; | ||
}; |
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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!
include/rtc/rtc.h
Outdated
uint16_t port; | ||
} rtcUnhandledStunRequest; | ||
|
||
typedef void(RTC_API *rtcUnhandledStunRequestCallbackFunc)(rtcUnhandledStunRequest request); |
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.
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 comment
The 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.
src/global.cpp
Outdated
|
||
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 comment
The 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 unique_ptr
or directly set the object as map value. However, this should not be necessary anymore if you remove the user pointer.
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!
src/global.cpp
Outdated
handler->callback = std::move(callback); | ||
handler->userPtr = userPtr; | ||
|
||
unboundStunCallbacks[port] = handler; |
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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I've removed the map.
src/global.cpp
Outdated
@@ -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 comment
The 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 bindAddress
everytime, and the any address is quite tedious to handle.
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.
The libjuice PR uses ${host}:${port}
as the key.
Since the conn_mux.c
uses udp_socket_config_t
to bind a UDP port, I wanted to make sure the user could explicitly bind both IPv4 and IPv6 addresses with:
juice_mux_listen("0.0.0.0", port, &cb, NULL);
juice_mux_listen("::", port, &cb, NULL);
I could be misreading it but I think these lines mean you need to pass one address family or the other. For example passing NULL
as the bind_address
will not cause it to bind to both.
Actually I'm not sure that's correct, passing NULL
seems to bind all IPv4 and IPv6 interfaces.
2dca4c7
to
4e0a5d8
Compare
Calls the functions added to libjuice in paullouisageneau/libjuice#248 (this needs to be merged & released before CI will pass here).
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.Refs #1166