1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/renderer/p2p/port_allocator.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_split.h" 11 #include "base/strings/string_util.h" 12 #include "content/public/common/content_switches.h" 13 #include "net/base/escape.h" 14 #include "net/base/ip_endpoint.h" 15 #include "third_party/WebKit/public/platform/WebURLError.h" 16 #include "third_party/WebKit/public/platform/WebURLLoader.h" 17 #include "third_party/WebKit/public/platform/WebURLRequest.h" 18 #include "third_party/WebKit/public/platform/WebURLResponse.h" 19 #include "third_party/WebKit/public/web/WebFrame.h" 20 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h" 21 22 using blink::WebString; 23 using blink::WebURL; 24 using blink::WebURLLoader; 25 using blink::WebURLLoaderOptions; 26 using blink::WebURLRequest; 27 using blink::WebURLResponse; 28 29 namespace content { 30 31 namespace { 32 33 // URL used to create a relay session. 34 const char kCreateRelaySessionURL[] = "/create_session"; 35 36 // Number of times we will try to request relay session. 37 const int kRelaySessionRetries = 3; 38 39 // Manimum relay server size we would try to parse. 40 const int kMaximumRelayResponseSize = 102400; 41 42 bool ParsePortNumber( 43 const std::string& string, int* value) { 44 if (!base::StringToInt(string, value) || *value <= 0 || *value >= 65536) { 45 LOG(ERROR) << "Received invalid port number from relay server: " << string; 46 return false; 47 } 48 return true; 49 } 50 51 } // namespace 52 53 P2PPortAllocator::Config::Config() 54 : legacy_relay(true), 55 disable_tcp_transport(false) { 56 } 57 58 P2PPortAllocator::Config::~Config() { 59 } 60 61 P2PPortAllocator::Config::RelayServerConfig::RelayServerConfig() 62 : port(0) { 63 } 64 65 P2PPortAllocator::Config::RelayServerConfig::~RelayServerConfig() { 66 } 67 68 P2PPortAllocator::P2PPortAllocator( 69 blink::WebFrame* web_frame, 70 P2PSocketDispatcher* socket_dispatcher, 71 rtc::NetworkManager* network_manager, 72 rtc::PacketSocketFactory* socket_factory, 73 const Config& config) 74 : cricket::BasicPortAllocator(network_manager, socket_factory), 75 web_frame_(web_frame), 76 socket_dispatcher_(socket_dispatcher), 77 config_(config) { 78 uint32 flags = 0; 79 if (config_.disable_tcp_transport) 80 flags |= cricket::PORTALLOCATOR_DISABLE_TCP; 81 set_flags(flags); 82 set_allow_tcp_listen(false); 83 } 84 85 P2PPortAllocator::~P2PPortAllocator() { 86 } 87 88 cricket::PortAllocatorSession* P2PPortAllocator::CreateSessionInternal( 89 const std::string& content_name, 90 int component, 91 const std::string& ice_username_fragment, 92 const std::string& ice_password) { 93 return new P2PPortAllocatorSession( 94 this, content_name, component, ice_username_fragment, ice_password); 95 } 96 97 P2PPortAllocatorSession::P2PPortAllocatorSession( 98 P2PPortAllocator* allocator, 99 const std::string& content_name, 100 int component, 101 const std::string& ice_username_fragment, 102 const std::string& ice_password) 103 : cricket::BasicPortAllocatorSession( 104 allocator, content_name, component, 105 ice_username_fragment, ice_password), 106 allocator_(allocator), 107 relay_session_attempts_(0), 108 relay_udp_port_(0), 109 relay_tcp_port_(0), 110 relay_ssltcp_port_(0), 111 pending_relay_requests_(0) { 112 } 113 114 P2PPortAllocatorSession::~P2PPortAllocatorSession() { 115 } 116 117 void P2PPortAllocatorSession::didReceiveData( 118 WebURLLoader* loader, const char* data, 119 int data_length, int encoded_data_length) { 120 DCHECK_EQ(loader, relay_session_request_.get()); 121 if (static_cast<int>(relay_session_response_.size()) + data_length > 122 kMaximumRelayResponseSize) { 123 LOG(ERROR) << "Response received from the server is too big."; 124 loader->cancel(); 125 return; 126 } 127 relay_session_response_.append(data, data + data_length); 128 } 129 130 void P2PPortAllocatorSession::didFinishLoading( 131 WebURLLoader* loader, double finish_time, 132 int64_t total_encoded_data_length) { 133 ParseRelayResponse(); 134 } 135 136 void P2PPortAllocatorSession::didFail(blink::WebURLLoader* loader, 137 const blink::WebURLError& error) { 138 DCHECK_EQ(loader, relay_session_request_.get()); 139 DCHECK_NE(error.reason, 0); 140 141 LOG(ERROR) << "Relay session request failed."; 142 143 // Retry the request. 144 AllocateLegacyRelaySession(); 145 } 146 147 void P2PPortAllocatorSession::GetPortConfigurations() { 148 if (allocator_->config_.legacy_relay) { 149 AllocateLegacyRelaySession(); 150 } 151 AddConfig(); 152 } 153 154 void P2PPortAllocatorSession::AllocateLegacyRelaySession() { 155 if (allocator_->config_.relays.empty()) 156 return; 157 // If we are using legacy relay, we will have only one entry in relay server 158 // list. 159 P2PPortAllocator::Config::RelayServerConfig relay_config = 160 allocator_->config_.relays[0]; 161 162 if (relay_session_attempts_ > kRelaySessionRetries) 163 return; 164 relay_session_attempts_++; 165 166 relay_session_response_.clear(); 167 168 WebURLLoaderOptions options; 169 options.allowCredentials = false; 170 171 options.crossOriginRequestPolicy = 172 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; 173 174 relay_session_request_.reset( 175 allocator_->web_frame_->createAssociatedURLLoader(options)); 176 if (!relay_session_request_) { 177 LOG(ERROR) << "Failed to create URL loader."; 178 return; 179 } 180 181 std::string url = "https://" + relay_config.server_address + 182 kCreateRelaySessionURL + 183 "?username=" + net::EscapeUrlEncodedData(username(), true) + 184 "&password=" + net::EscapeUrlEncodedData(password(), true); 185 186 WebURLRequest request; 187 request.initialize(); 188 request.setURL(WebURL(GURL(url))); 189 request.setAllowStoredCredentials(false); 190 request.setCachePolicy(WebURLRequest::ReloadIgnoringCacheData); 191 request.setHTTPMethod("GET"); 192 request.addHTTPHeaderField( 193 WebString::fromUTF8("X-Talk-Google-Relay-Auth"), 194 WebString::fromUTF8(relay_config.password)); 195 request.addHTTPHeaderField( 196 WebString::fromUTF8("X-Google-Relay-Auth"), 197 WebString::fromUTF8(relay_config.username)); 198 request.addHTTPHeaderField(WebString::fromUTF8("X-Stream-Type"), 199 WebString::fromUTF8("chromoting")); 200 201 relay_session_request_->loadAsynchronously(request, this); 202 } 203 204 void P2PPortAllocatorSession::ParseRelayResponse() { 205 base::StringPairs value_pairs; 206 if (!base::SplitStringIntoKeyValuePairs(relay_session_response_, '=', '\n', 207 &value_pairs)) { 208 LOG(ERROR) << "Received invalid response from relay server"; 209 return; 210 } 211 212 relay_ip_.Clear(); 213 relay_udp_port_ = 0; 214 relay_tcp_port_ = 0; 215 relay_ssltcp_port_ = 0; 216 217 for (base::StringPairs::iterator it = value_pairs.begin(); 218 it != value_pairs.end(); ++it) { 219 std::string key; 220 std::string value; 221 base::TrimWhitespaceASCII(it->first, base::TRIM_ALL, &key); 222 base::TrimWhitespaceASCII(it->second, base::TRIM_ALL, &value); 223 224 if (key == "username") { 225 if (value != username()) { 226 LOG(ERROR) << "When creating relay session received user name " 227 " that was different from the value specified in the query."; 228 return; 229 } 230 } else if (key == "password") { 231 if (value != password()) { 232 LOG(ERROR) << "When creating relay session received password " 233 "that was different from the value specified in the query."; 234 return; 235 } 236 } else if (key == "relay.ip") { 237 relay_ip_.SetIP(value); 238 if (relay_ip_.ip() == 0) { 239 LOG(ERROR) << "Received unresolved relay server address: " << value; 240 return; 241 } 242 } else if (key == "relay.udp_port") { 243 if (!ParsePortNumber(value, &relay_udp_port_)) 244 return; 245 } else if (key == "relay.tcp_port") { 246 if (!ParsePortNumber(value, &relay_tcp_port_)) 247 return; 248 } else if (key == "relay.ssltcp_port") { 249 if (!ParsePortNumber(value, &relay_ssltcp_port_)) 250 return; 251 } 252 } 253 254 AddConfig(); 255 } 256 257 void P2PPortAllocatorSession::AddConfig() { 258 const P2PPortAllocator::Config& config = allocator_->config_; 259 cricket::PortConfiguration* port_config = new cricket::PortConfiguration( 260 config.stun_servers, std::string(), std::string()); 261 262 for (size_t i = 0; i < config.relays.size(); ++i) { 263 cricket::RelayCredentials credentials(config.relays[i].username, 264 config.relays[i].password); 265 cricket::RelayServerConfig relay_server(cricket::RELAY_TURN); 266 cricket::ProtocolType protocol; 267 if (!cricket::StringToProto(config.relays[i].transport_type.c_str(), 268 &protocol)) { 269 DLOG(WARNING) << "Ignoring TURN server " 270 << config.relays[i].server_address << ". " 271 << "Reason= Incorrect " 272 << config.relays[i].transport_type 273 << " transport parameter."; 274 continue; 275 } 276 277 relay_server.ports.push_back(cricket::ProtocolAddress( 278 rtc::SocketAddress(config.relays[i].server_address, 279 config.relays[i].port), 280 protocol, 281 config.relays[i].secure)); 282 relay_server.credentials = credentials; 283 port_config->AddRelay(relay_server); 284 } 285 ConfigReady(port_config); 286 } 287 288 } // namespace content 289