From 6d122623efe0b18110c460aed5d4ce4fa2d7b8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=B6ller?= Date: Fri, 3 Jun 2022 13:51:21 +0200 Subject: [PATCH] Refactor ParseIceServerUrl to use absl::string_view Bug: webrtc:13579 Change-Id: Icbcee2348dab7aae1c43ff073480f2a1ede8041f Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/264545 Reviewed-by: Harald Alvestrand Commit-Queue: Niels Moller Cr-Commit-Position: refs/heads/main@{#37117} --- pc/ice_server_parsing.cc | 165 +++++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 76 deletions(-) diff --git a/pc/ice_server_parsing.cc b/pc/ice_server_parsing.cc index 29127c5c91..8052cd809a 100644 --- a/pc/ice_server_parsing.cc +++ b/pc/ice_server_parsing.cc @@ -14,6 +14,7 @@ #include // For std::isdigit. #include +#include #include "p2p/base/port_interface.h" #include "rtc_base/arraysize.h" @@ -22,18 +23,20 @@ #include "rtc_base/logging.h" #include "rtc_base/socket_address.h" #include "rtc_base/string_encode.h" +#include "rtc_base/string_to_number.h" namespace webrtc { +namespace { // Number of tokens must be preset when TURN uri has transport param. -static const size_t kTurnTransportTokensNum = 2; +const size_t kTurnTransportTokensNum = 2; // The default stun port. -static const int kDefaultStunPort = 3478; -static const int kDefaultStunTlsPort = 5349; -static const char kTransport[] = "transport"; +const int kDefaultStunPort = 3478; +const int kDefaultStunTlsPort = 5349; +const char kTransport[] = "transport"; // Allowed characters in hostname per RFC 3986 Appendix A "reg-name" -static const char kRegNameCharacters[] = +const char kRegNameCharacters[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" @@ -42,18 +45,19 @@ static const char kRegNameCharacters[] = "!$&'()*+,;="; // sub-delims // NOTE: Must be in the same order as the ServiceType enum. -static const char* kValidIceServiceTypes[] = {"stun", "stuns", "turn", "turns"}; +const char* kValidIceServiceTypes[] = {"stun", "stuns", "turn", "turns"}; // NOTE: A loop below assumes that the first value of this enum is 0 and all // other values are incremental. -enum ServiceType { +enum class ServiceType { STUN = 0, // Indicates a STUN server. STUNS, // Indicates a STUN server used with a TLS session. TURN, // Indicates a TURN server TURNS, // Indicates a TURN server used with a TLS session. INVALID, // Unknown. }; -static_assert(INVALID == arraysize(kValidIceServiceTypes), +static_assert(static_cast(ServiceType::INVALID) == + arraysize(kValidIceServiceTypes), "kValidIceServiceTypes must have as many strings as ServiceType " "has values."); @@ -63,40 +67,36 @@ static_assert(INVALID == arraysize(kValidIceServiceTypes), // scheme = "stun" / "stuns" / "turn" / "turns" // host = IP-literal / IPv4address / reg-name // port = *DIGIT -static bool GetServiceTypeAndHostnameFromUri(const std::string& in_str, - ServiceType* service_type, - std::string* hostname) { - const std::string::size_type colonpos = in_str.find(':'); - if (colonpos == std::string::npos) { + +// Return tuple is service_type, host, with service_type == ServiceType::INVALID +// on failure. +std::tuple GetServiceTypeAndHostnameFromUri( + absl::string_view in_str) { + const auto colonpos = in_str.find(':'); + if (colonpos == absl::string_view::npos) { RTC_LOG(LS_WARNING) << "Missing ':' in ICE URI: " << in_str; - return false; + return {ServiceType::INVALID, ""}; } if ((colonpos + 1) == in_str.length()) { RTC_LOG(LS_WARNING) << "Empty hostname in ICE URI: " << in_str; - return false; + return {ServiceType::INVALID, ""}; } - *service_type = INVALID; for (size_t i = 0; i < arraysize(kValidIceServiceTypes); ++i) { if (in_str.compare(0, colonpos, kValidIceServiceTypes[i]) == 0) { - *service_type = static_cast(i); - break; + return {static_cast(i), in_str.substr(colonpos + 1)}; } } - if (*service_type == INVALID) { - return false; - } - *hostname = in_str.substr(colonpos + 1, std::string::npos); - return true; + return {ServiceType::INVALID, ""}; } -static bool ParsePort(const std::string& in_str, int* port) { - // Make sure port only contains digits. FromString doesn't check this. +absl::optional ParsePort(absl::string_view in_str) { + // Make sure port only contains digits. StringToNumber doesn't check this. for (const char& c : in_str) { if (!std::isdigit(static_cast(c))) { return false; } } - return rtc::FromString(in_str, port); + return rtc::StringToNumber(in_str); } // This method parses IPv6 and IPv4 literal strings, along with hostnames in @@ -104,49 +104,60 @@ static bool ParsePort(const std::string& in_str, int* port) { // Consider following formats as correct. // `hostname:port`, |[IPV6 address]:port|, |IPv4 address|:port, // `hostname`, |[IPv6 address]|, |IPv4 address|. -static bool ParseHostnameAndPortFromString(const std::string& in_str, - std::string* host, - int* port) { - RTC_DCHECK(host->empty()); + +// Return tuple is success, host, port. +std::tuple ParseHostnameAndPortFromString( + absl::string_view in_str, + int default_port) { + if (in_str.empty()) { + return {false, "", 0}; + } + absl::string_view host; + int port = default_port; + if (in_str.at(0) == '[') { // IP_literal syntax - std::string::size_type closebracket = in_str.rfind(']'); - if (closebracket != std::string::npos) { - std::string::size_type colonpos = in_str.find(':', closebracket); - if (std::string::npos != colonpos) { - if (!ParsePort(in_str.substr(closebracket + 2, std::string::npos), - port)) { - return false; - } - } - *host = in_str.substr(1, closebracket - 1); - } else { - return false; + auto closebracket = in_str.rfind(']'); + if (closebracket == absl::string_view::npos) { + return {false, "", 0}; } + auto colonpos = in_str.find(':', closebracket); + if (absl::string_view::npos != colonpos) { + if (absl::optional opt_port = + ParsePort(in_str.substr(closebracket + 2))) { + port = *opt_port; + } else { + return {false, "", 0}; + } + } + host = in_str.substr(1, closebracket - 1); } else { // IPv4address or reg-name syntax - std::string::size_type colonpos = in_str.find(':'); - if (std::string::npos != colonpos) { - if (!ParsePort(in_str.substr(colonpos + 1, std::string::npos), port)) { - return false; + auto colonpos = in_str.find(':'); + if (absl::string_view::npos != colonpos) { + if (absl::optional opt_port = + ParsePort(in_str.substr(colonpos + 1))) { + port = *opt_port; + } else { + return {false, "", 0}; } - *host = in_str.substr(0, colonpos); + host = in_str.substr(0, colonpos); } else { - *host = in_str; + host = in_str; } // RFC 3986 section 3.2.2 and Appendix A - "reg-name" syntax - if (host->find_first_not_of(kRegNameCharacters) != std::string::npos) { - return false; + if (host.find_first_not_of(kRegNameCharacters) != absl::string_view::npos) { + return {false, "", 0}; } } - return !host->empty(); + return {!host.empty(), host, port}; } // Adds a STUN or TURN server to the appropriate list, // by parsing `url` and using the username/password in `server`. -static RTCErrorType ParseIceServerUrl( +RTCErrorType ParseIceServerUrl( const PeerConnectionInterface::IceServer& server, - const std::string& url, + absl::string_view url, cricket::ServerAddresses* stun_servers, std::vector* turn_servers) { // RFC 7064 @@ -166,26 +177,25 @@ static RTCErrorType ParseIceServerUrl( RTC_DCHECK(stun_servers != nullptr); RTC_DCHECK(turn_servers != nullptr); - std::vector tokens; cricket::ProtocolType turn_transport_type = cricket::PROTO_UDP; RTC_DCHECK(!url.empty()); - rtc::split(url, '?', &tokens); - std::string uri_without_transport = tokens[0]; + std::vector tokens = rtc::split(url, '?'); + absl::string_view uri_without_transport = tokens[0]; // Let's look into transport= param, if it exists. if (tokens.size() == kTurnTransportTokensNum) { // ?transport= is present. - std::string uri_transport_param = tokens[1]; - rtc::split(uri_transport_param, '=', &tokens); - if (tokens[0] != kTransport) { + std::vector transport_tokens = + rtc::split(tokens[1], '='); + if (transport_tokens[0] != kTransport) { RTC_LOG(LS_WARNING) << "Invalid transport parameter key."; return RTCErrorType::SYNTAX_ERROR; } - if (tokens.size() < 2) { + if (transport_tokens.size() < 2) { RTC_LOG(LS_WARNING) << "Transport parameter missing value."; return RTCErrorType::SYNTAX_ERROR; } absl::optional proto = - cricket::StringToProto(tokens[1]); + cricket::StringToProto(transport_tokens[1]); if (!proto || (*proto != cricket::PROTO_UDP && *proto != cricket::PROTO_TCP)) { RTC_LOG(LS_WARNING) << "Transport parameter should always be udp or tcp."; @@ -194,10 +204,9 @@ static RTCErrorType ParseIceServerUrl( turn_transport_type = *proto; } - std::string hoststring; - ServiceType service_type; - if (!GetServiceTypeAndHostnameFromUri(uri_without_transport, &service_type, - &hoststring)) { + auto [service_type, hoststring] = + GetServiceTypeAndHostnameFromUri(uri_without_transport); + if (service_type == ServiceType::INVALID) { RTC_LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: " << url; return RTCErrorType::SYNTAX_ERROR; } @@ -205,20 +214,22 @@ static RTCErrorType ParseIceServerUrl( // GetServiceTypeAndHostnameFromUri should never give an empty hoststring RTC_DCHECK(!hoststring.empty()); - int port = kDefaultStunPort; - if (service_type == TURNS) { - port = kDefaultStunTlsPort; + int default_port = kDefaultStunPort; + if (service_type == ServiceType::TURNS) { + default_port = kDefaultStunTlsPort; turn_transport_type = cricket::PROTO_TLS; } - if (hoststring.find('@') != std::string::npos) { + if (hoststring.find('@') != absl::string_view::npos) { RTC_LOG(LS_WARNING) << "Invalid url: " << uri_without_transport; RTC_LOG(LS_WARNING) << "Note that user-info@ in turn:-urls is long-deprecated."; return RTCErrorType::SYNTAX_ERROR; } - std::string address; - if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) { + + auto [success, address, port] = + ParseHostnameAndPortFromString(hoststring, default_port); + if (!success) { RTC_LOG(LS_WARNING) << "Invalid hostname format: " << uri_without_transport; return RTCErrorType::SYNTAX_ERROR; } @@ -229,12 +240,12 @@ static RTCErrorType ParseIceServerUrl( } switch (service_type) { - case STUN: - case STUNS: + case ServiceType::STUN: + case ServiceType::STUNS: stun_servers->insert(rtc::SocketAddress(address, port)); break; - case TURN: - case TURNS: { + case ServiceType::TURN: + case ServiceType::TURNS: { if (server.username.empty() || server.password.empty()) { // The WebRTC spec requires throwing an InvalidAccessError when username // or credential are ommitted; this is the native equivalent. @@ -244,7 +255,7 @@ static RTCErrorType ParseIceServerUrl( // If the hostname field is not empty, then the server address must be // the resolved IP for that host, the hostname is needed later for TLS // handshake (SNI and Certificate verification). - const std::string& hostname = + absl::string_view hostname = server.hostname.empty() ? address : server.hostname; rtc::SocketAddress socket_address(hostname, port); if (!server.hostname.empty()) { @@ -282,6 +293,8 @@ static RTCErrorType ParseIceServerUrl( return RTCErrorType::NONE; } +} // namespace + RTCErrorType ParseIceServers( const PeerConnectionInterface::IceServers& servers, cricket::ServerAddresses* stun_servers,