-
Notifications
You must be signed in to change notification settings - Fork 49
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
Fix panic on STUN binding failure while multiplexing #488
Conversation
Which panic are you hitting? |
This one, because it is a STUN response we hit the |
I think the correct fix would be: --- a/src/ice/agent.rs
+++ b/src/ice/agent.rs
@@ -799,6 +799,9 @@ impl IceAgent {
trace!("Message rejected, transaction ID does not belong to any of our candidate pairs");
return false;
}
+ } else {
+ trace!("Message reject, it is not a succesful binding response");
+ return false;
}
let (_, password) = self.stun_credentials(!message.is_response()); |
That patch would end up rejecting everything that's not a successful binding response, since all other message types, including things like binding requests, would fall through to that I believe the effect you intended was to just reject all non-success binding responses? That would also fix the panic, although arguably (Aside: |
Taking a step back.
|
15efbc4
to
cfd034b
Compare
I think we have our bases covered here then - STUN requests get handled as before, and now both STUN success and failure gets accepted if it corresponds to a STUN request from us (or rejected before the credentials check if not). |
I still don't like this change. What kind of responses are we talking about that we suddenly accept? What about middleboxes? We should be very specific with what we accept. Enumerate the cases. |
I believe this enumeration is exhaustive:
This PR was aimed at fixing the ( Considering the confusion that seems to exist around the exact behavior of |
Agree. I also think this function should default to |
Okay, I'll take a stab at doing that tomorrow. |
cfd034b
to
008d9e8
Compare
let method = message.method(); | ||
let class = message.class(); | ||
match (method, class) { | ||
(StunMethod::Binding, StunClass::Request | StunClass::Indication) => { |
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.
RFC 5389 indicates that requests and indications should be validated in a similar manner.
(StunMethod::Binding, StunClass::Unknown) => { | ||
// Without a known class, it's impossible to know how to validate the message | ||
trace!("Message rejected, unknown STUN class"); | ||
false | ||
} | ||
(StunMethod::Unknown, _) => { | ||
// Without a known method, it's impossible to know how to validate the message | ||
trace!("Message rejected, unknown STUN method"); | ||
false | ||
} |
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.
These are behavior changes from my previous enumeration. Since processing is so method and class-dependent, not knowing either means we can't really know how to even verify that this message is valid, or for this instance.
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.
Looks great!
Thank you for doing this!
Small question
} | ||
} | ||
(StunMethod::Binding, StunClass::Success | StunClass::Failure) => { | ||
let belongs_to_a_candidate_pair = self |
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.
Maybe we should check nominated pair here first, to make a less compute intensive happy path?
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.
Ah, yes, you're right! We even talked about that in the other PR, I guess it got lost amongst the other discussion happening there. Taking a second look, the discussion in the other PR was for Rtc::accepts()
, not IceAgent::accepts_message()
, and that fast path is indeed being used there.
I'll do that.
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 looked deeper into the code, and while adding this fast path is possible, it's surprisingly risky and would be better left to a separate PR, if we deem it necessary at all.
The issue is that our nominated pair IceAgent.nominated_send
is not an index into the pairs array, but rather a PairId
. Resolving that to an index would require a linear scan of the pairs array anyway, negating any benefits of checking it first. Storing an index instead would be very difficult because we sort pairs by priority and prune failed pairs often, making the bookkeeping very error-prone.
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.
Alright! Thanks for checking!
An unrelated bug I had in my application was causing STUN to fail, and a
Class::Failure
message to come back. Because like in the chat example, I'm still using a single multiplexed UDP socket, this message first went to theRtc::accepts()
of a separate client that had not negotiated, which caused it to panic with "Remote ICE Credentials".This is the same underlying panic as in #461, but this time the logic was allowed to fall through to the
stun_credentials()
call because we were only doing the preceding candidate-pair test forClass::Success
responses, notClass::Failure
.I wanted to add a test for this, but with the current field visibility on
StunMessage
, I couldn't create a failure message in a test. I wasn't sure if adding a helper toimpl StunMessage
was appropriate.