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

support juice_bind_stun to reflect stun requests from unbound client. #248

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

xicilion
Copy link
Contributor

In order to implement WebRTC Direct PeerB, we need to capture STUN requests from unknown clients and return the client's information. Once PeerB receives this information, it will construct the client's SDP and establish a connection with the client on its own.

@xicilion
Copy link
Contributor Author

xicilion commented Jun 7, 2024

change argument at callback function.

src/conn.c Outdated
@@ -247,3 +247,30 @@ int conn_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size) {

return get_mode_entry(agent)->get_addrs_func(agent, records, size);
}

int juice_bind_stun(const char *bind_address, int local_port, juice_cb_stun_binding_t cb)
Copy link

@achingbrain achingbrain Jun 13, 2024

Choose a reason for hiding this comment

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

Is it possible to unbind from the address to allow closing all open sockets?

Would you just call this again with NULL as the cb argument?

@xicilion
Copy link
Contributor Author

This should be okay, but I need to do some testing to make sure the socket will be closed automatically. I'll add it.

@xicilion
Copy link
Contributor Author

It turned out to be a little more complicated than I expected, but I did it.

@achingbrain
Copy link

Awesome!

Is the new juice_unbind_stun() function going to unbind all listeners and ports?

I'm thinking of the case where a user specifies multiple listeners running on different ports - they should be stoppable independently of each other.

include/juice/juice.h Outdated Show resolved Hide resolved
@xicilion
Copy link
Contributor Author

xicilion commented Jun 14, 2024

I discussed this with @paullouisageneau . Currently, Juice actually only supports one port for mux. After the first mux connects to the specified port, all other mux connections will use this port. So Juice will only have one mux socket at a time.

#247

@achingbrain
Copy link

So Juice will only have one mux socket at a time

This will prevent people doing things like opening multiple WebRTC-Direct listeners or running more than one js-libp2p node in the same process since they'll try to use the same UDP port for incoming connections.

These aren't very common deployment patterns, though it's not unheard of, and it's done quite often while running unit/integration/interop tests.

It would be good to fix this, though probably not in this PR.

achingbrain added a commit to achingbrain/libdatachannel that referenced this pull request Jun 14, 2024
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
@xicilion
Copy link
Contributor Author

It would be good to fix this

I also hope to achieve this refactoring, but that might be a big project. In the end, it's basically a combination of con_mux and conn_poll.

@achingbrain
Copy link

@paullouisageneau do you have any feedback on this PR or the functionality it adds?

include/juice/juice.h Outdated Show resolved Hide resolved
src/conn.c Show resolved Hide resolved
src/conn_mux.c Outdated Show resolved Hide resolved
include/juice/juice.h Outdated Show resolved Hide resolved
include/juice/juice.h Outdated Show resolved Hide resolved
src/conn_mux.c Outdated Show resolved Hide resolved
src/conn_mux.c Outdated Show resolved Hide resolved
include/juice/juice.h Outdated Show resolved Hide resolved
@paullouisageneau
Copy link
Owner

This will prevent people doing things like opening multiple WebRTC-Direct listeners or running more than one js-libp2p node in the same process since they'll try to use the same UDP port for incoming connections.

These aren't very common deployment patterns, though it's not unheard of, and it's done quite often while running unit/integration/interop tests.

It would be good to fix this, though probably not in this PR.

This is not just a fix, as it affects how the API for this feature is designed. I think it needs to be accounted for, if not implemented, sooner than later. You have to design the interface in accordance now because you can't break compatibility later. This is native library packaged in a few distributions and releasing a new major version is complicated.

@xicilion
Copy link
Contributor Author

xicilion commented Jul 3, 2024

@paullouisageneau Thank you very much for your feedback, I will make the changes according to your suggestions as soon as possible.

src/conn_mux.c Outdated Show resolved Hide resolved
@xicilion
Copy link
Contributor Author

Do you have any more suggestions for this pull request, @paullouisageneau?

@achingbrain
Copy link

@xicilion this PR still only allows a single muxed port per process, or am I reading it wrong?

@paullouisageneau requested it support multiple ports - #248 (comment)

@xicilion
Copy link
Contributor Author

To support multiple ports, we'll need to almost rewrite the implementation of conn_mux.c. Currently, juice_mux_listen and juice_mux_stop_listen are already compatible with supporting multiple ports in the future. I think this time we can start by only adding the listen feature.

@@ -295,6 +297,25 @@ static juice_agent_t *lookup_agent(conn_registry_t *registry, char *buf, size_t
}
}

if (registry->cb_mux_incoming) {
JLOG_DEBUG("Found STUN request with unknown ICE ufrag");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I found an issue here.

When connecting to the server using a browser and the server closes the peer connection, the browser will continue to send STUN messages, and the incoming event will be triggered again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the RFC document:

[7.1.2](https://www.rfc-editor.org/rfc/rfc8445.html#section-7.1.2).  USE-CANDIDATE

   The controlling agent MUST include the USE-CANDIDATE attribute in
   order to nominate a candidate pair ([Section 8.1.1](https://www.rfc-editor.org/rfc/rfc8445.html#section-8.1.1)).  The controlled
   agent MUST NOT include the USE-CANDIDATE attribute in a Binding
   request.

An agent must not include the USE-CANDIDATE attribute when sending a binding request. I tested this in practice, the binding request message received has use_candidate=0. After the connection is closed, the browser continues to send stun messages with use_candidate=1.

I will proceed to test further based on this strategy.

Copy link
Contributor Author

@xicilion xicilion Jul 31, 2024

Choose a reason for hiding this comment

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

2024-07-31 23:04:44.369 VERB  [387904] [rtc::impl::IceTransport::LogCallback@372] juice: conn.c:125: 0 connection left
2024-07-31 23:04:44.369 VERB  [387904] [rtc::impl::IceTransport::LogCallback@372] juice: agent.c:192: Destroyed agent
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:449: Leaving poll
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:408: Receiving datagram
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:417: No more datagrams to receive
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:447: Entering poll for 60000 ms
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:449: Leaving poll
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:408: Receiving datagram
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:363: Demultiplexing incoming datagram from 127.0.0.1:54664
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:255: Looking up agent from address
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:485: Not a STUN message: magic number invalid
2024-07-31 23:04:44.369 INFO  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:266: Got non-STUN message from unknown source address
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:368: Agent not found for incoming datagram, dropping
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:408: Receiving datagram
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:417: No more datagrams to receive
2024-07-31 23:04:44.369 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:447: Entering poll for 60000 ms
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:449: Leaving poll
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:408: Receiving datagram
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:363: Demultiplexing incoming datagram from 127.0.0.1:54664
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:255: Looking up agent from address
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:270: Looking up agent from STUN message content
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:528: Reading STUN message, class=0x0, method=0x1
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x6, length=67
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:700: Reading username
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:707: Got username: libp2p+webrtc+v1/5fhhLR3XJZHejuox:libp2p+webrtc+v1/5fhhLR3XJZHejuox
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0xC057, length=4
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:1014: Ignoring unknown optional STUN attribute type 0xC057
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x802A, length=8
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:906: Found ICE controlling attribute
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x25, length=0
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:901: Found use candidate flag
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x24, length=4
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:891: Reading priority
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:897: Got priority: 1845501695
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x8, length=20
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:711: Reading message integrity
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x8028, length=4
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:729: Reading fingerprint
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:745: STUN fingerprint check succeeded
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:545: Finished reading STUN attributes
2024-07-31 23:04:44.553 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:368: Agent not found for incoming datagram, dropping

===========================================================================

2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:408: Receiving datagram
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:363: Demultiplexing incoming datagram from 127.0.0.1:54664
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:255: Looking up agent from address
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:270: Looking up agent from STUN message content
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:528: Reading STUN message, class=0x0, method=0x1
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x6, length=67
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:700: Reading username
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:707: Got username: libp2p+webrtc+v1/5fhhLR3XJZHejuox:libp2p+webrtc+v1/5fhhLR3XJZHejuox
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0xC057, length=4
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:1014: Ignoring unknown optional STUN attribute type 0xC057
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x802A, length=8
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:906: Found ICE controlling attribute
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x24, length=4
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:891: Reading priority
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:897: Got priority: 1845501695
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x8, length=20
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:711: Reading message integrity
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:622: Reading attribute 0x8028, length=4
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:729: Reading fingerprint
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:745: STUN fingerprint check succeeded
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: stun.c:545: Finished reading STUN attributes
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:307: Found STUN request with unknown ICE ufrag
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:368: Agent not found for incoming datagram, dropping
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:408: Receiving datagram
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:417: No more datagrams to receive
2024-07-31 23:04:49.570 VERB  [387845] [rtc::impl::IceTransport::LogCallback@372] juice: conn_mux.c:447: Entering poll for 60000 ms

After successfully intercepting dozens of messages, the browser still stubbornly sent a binding request message with USE-CANDIDATE set to 0.

Copy link
Contributor Author

@xicilion xicilion Jul 31, 2024

Choose a reason for hiding this comment

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

I examined numerous attributes in the STUN message, but did not identify any additional characteristics that could be utilized to detect the binding request after the connection is terminated.

@paullouisageneau need your help, do you have any suggestions?

Copy link
Owner

Choose a reason for hiding this comment

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

An agent must not include the USE-CANDIDATE attribute when sending a binding request.

I think you misunderstood what USE-CANDIDATE means. The RFC says "The controlled agent MUST NOT include the USE-CANDIDATE attribute in a Binding request". Only the controlling agent includes it to nominate a candidate pair (i.e. to tell the controlled agent which pair to use), otherwise it's not present.

I examined numerous attributes in the STUN message, but did not identify any additional characteristics that could be utilized to detect the binding request after the connection is terminated.

To my knowledge there is no way to know the connection state from STUN attributes, since the ICE layer is unaware of upper layers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I have already canceled this commit. I cached the information of the just closed RTCPeerConnection at the business layer for a while, and blocked the same request from being accepted.

@SgtPooki
Copy link

Is anything else needed here? I'm looking forward to seeing this completed so we can unblock libp2p/js-libp2p#2583

Comment on lines +33 to +34
juice_cb_mux_incoming_t cb_mux_incoming;
void *mux_incoming_user_ptr;
Copy link
Owner

Choose a reason for hiding this comment

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

Since they are specific to the mux implementation, they should be moved to struct registry_impl in conn_mux.c instead, and you can then drop the mux_ prefix in the names.

@@ -247,3 +247,59 @@ int conn_get_addrs(juice_agent_t *agent, addr_record_t *records, size_t size) {

return get_mode_entry(agent)->get_addrs_func(agent, records, size);
}

int juice_mux_stop_listen(const char *bind_address, int local_port) {
Copy link
Owner

Choose a reason for hiding this comment

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

If juice_mux_stop_listen() now has the same bind_address and local_port parameters as juice_mux_listen(), why not simply call juice_mux_listen(bind_address, local_port, NULL) to stop listening? It would also permit multiple ports in the future, while being less clumsy that those two useless parameters.

@@ -64,6 +64,16 @@ typedef void (*juice_cb_gathering_done_t)(juice_agent_t *agent, void *user_ptr);
typedef void (*juice_cb_recv_t)(juice_agent_t *agent, const char *data, size_t size,
void *user_ptr);

typedef struct juice_mux_incoming {
Copy link
Owner

Choose a reason for hiding this comment

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

Since this does not actually represent incoming connections in the end (for instance in the case of closed connections), I think it should be renamed for clarity. For instance juice_mux_binding_request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants