mirror of
https://github.com/rustdesk/hbb_common.git
synced 2026-10-20 23:47:20 +00:00
webrtc: decode TURN userinfo before handing it to the ICE agent
`Url` returns the username and password percent-encoded, and they went into the ICE server configuration as-is, so a password containing `@`, `/`, `?`, `#`, `%` or a space (written encoded, the only way the URL can carry it) failed the long-term credential check with a 401 and TURN never produced a relay candidate. Decode both, as the web client does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXJJGEGdgu26wgCppvUXdZ
This commit is contained in:
1 parent
846daf86f0
commit
00f0a720b5
2 files changed
+18
-2
No files matched your search
@@ -52,6 +52,7 @@ thiserror = "1.0"
|
||||
httparse = "1.10"
|
||||
base64 = "0.22"
|
||||
url = "2.5"
|
||||
percent-encoding = "2"
|
||||
sha2 = "0.10"
|
||||
whoami = "1.5"
|
||||
|
||||
|
||||
+17
-2
@@ -41,6 +41,7 @@ use webrtc::peer_connection::RTCPeerConnection;
|
||||
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
|
||||
use base64::Engine;
|
||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||
use percent_encoding::percent_decode_str;
|
||||
use tokio::sync::{mpsc, watch, Mutex, Semaphore};
|
||||
use tokio::time::{timeout, timeout_at, Instant};
|
||||
use url::Url;
|
||||
@@ -395,8 +396,14 @@ impl WebRTCStream {
|
||||
if host.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let username = u.username().to_string();
|
||||
let credential = u.password().unwrap_or_default().to_string();
|
||||
// `Url` hands userinfo back percent-encoded; the TURN server expects the value,
|
||||
// so `p%40ss` must reach it as `p@ss` for the long-term credential to verify.
|
||||
let username = percent_decode_str(u.username())
|
||||
.decode_utf8_lossy()
|
||||
.into_owned();
|
||||
let credential = percent_decode_str(u.password().unwrap_or_default())
|
||||
.decode_utf8_lossy()
|
||||
.into_owned();
|
||||
// A TURN server without credentials is not merely useless: webrtc-rs validates every
|
||||
// configured server in `new_peer_connection`, so one credential-less entry makes EVERY
|
||||
// peer connection fail — including plain non-relay ones that never wanted TURN. The
|
||||
@@ -1468,6 +1475,14 @@ mod tests {
|
||||
"turn:example.com:3478"
|
||||
);
|
||||
|
||||
// Userinfo arrives percent-encoded from the URL parser; the TURN server wants the
|
||||
// value, so a password written `p%40ss` authenticates as `p@ss`.
|
||||
let encoded =
|
||||
WebRTCStream::get_ice_server_from_url("turn://us%20er:p%40s%2Fs%25@example.com")
|
||||
.expect("encoded credentials are usable");
|
||||
assert_eq!(encoded.username, "us er");
|
||||
assert_eq!(encoded.credential, "p@s/s%");
|
||||
|
||||
// TURN without both halves of the credential is dropped rather than passed on:
|
||||
// webrtc-rs validates every configured server when the peer connection is built, so
|
||||
// one such entry fails EVERY connection, including those that never wanted TURN.
|
||||
|
||||
Reference in new issue
Block a user