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 "content/renderer/p2p/host_address_request.h" 14 #include "jingle/glue/utils.h" 15 #include "net/base/escape.h" 16 #include "net/base/ip_endpoint.h" 17 #include "third_party/WebKit/public/platform/WebURLError.h" 18 #include "third_party/WebKit/public/platform/WebURLLoader.h" 19 #include "third_party/WebKit/public/platform/WebURLRequest.h" 20 #include "third_party/WebKit/public/platform/WebURLResponse.h" 21 #include "third_party/WebKit/public/web/WebFrame.h" 22 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h" 23 24 using blink::WebString; 25 using blink::WebURL; 26 using blink::WebURLLoader; 27 using blink::WebURLLoaderOptions; 28 using blink::WebURLRequest; 29 using blink::WebURLResponse; 30 31 namespace content { 32 33 namespace { 34 35 // URL used to create a relay session. 36 const char kCreateRelaySessionURL[] = "/create_session"; 37 38 // Number of times we will try to request relay session. 39 const int kRelaySessionRetries = 3; 40 41 // Manimum relay server size we would try to parse. 42 const int kMaximumRelayResponseSize = 102400; 43 44 bool ParsePortNumber( 45 const std::string& string, int* value) { 46 if (!base::StringToInt(string, value) || *value <= 0 || *value >= 65536) { 47 LOG(ERROR) << "Received invalid port number from relay server: " << string; 48 return false; 49 } 50 return true; 51 } 52 53 } // namespace 54 55 P2PPortAllocator::Config::Config() 56 : stun_server_port(0), 57 legacy_relay(true), 58 disable_tcp_transport(false) { 59 } 60 61 P2PPortAllocator::Config::~Config() { 62 } 63 64 P2PPortAllocator::Config::RelayServerConfig::RelayServerConfig() 65 : port(0) { 66 } 67 68 P2PPortAllocator::Config::RelayServerConfig::~RelayServerConfig() { 69 } 70 71 P2PPortAllocator::P2PPortAllocator( 72 blink::WebFrame* web_frame, 73 P2PSocketDispatcher* socket_dispatcher, 74 talk_base::NetworkManager* network_manager, 75 talk_base::PacketSocketFactory* socket_factory, 76 const Config& config) 77 : cricket::BasicPortAllocator(network_manager, socket_factory), 78 web_frame_(web_frame), 79 socket_dispatcher_(socket_dispatcher), 80 config_(config) { 81 uint32 flags = 0; 82 if (config_.disable_tcp_transport) 83 flags |= cricket::PORTALLOCATOR_DISABLE_TCP; 84 set_flags(flags); 85 // TODO(ronghuawu): crbug/138185 add ourselves to the firewall list in browser 86 // process and then remove below line. 87 if (!CommandLine::ForCurrentProcess()->HasSwitch( 88 switches::kEnableWebRtcTcpServerSocket)) { 89 set_allow_tcp_listen(false); 90 } 91 } 92 93 P2PPortAllocator::~P2PPortAllocator() { 94 } 95 96 cricket::PortAllocatorSession* P2PPortAllocator::CreateSessionInternal( 97 const std::string& content_name, 98 int component, 99 const std::string& ice_username_fragment, 100 const std::string& ice_password) { 101 return new P2PPortAllocatorSession( 102 this, content_name, component, ice_username_fragment, ice_password); 103 } 104 105 P2PPortAllocatorSession::RelayServer::RelayServer() { 106 } 107 108 P2PPortAllocatorSession::RelayServer::~RelayServer() { 109 } 110 111 P2PPortAllocatorSession::P2PPortAllocatorSession( 112 P2PPortAllocator* allocator, 113 const std::string& content_name, 114 int component, 115 const std::string& ice_username_fragment, 116 const std::string& ice_password) 117 : cricket::BasicPortAllocatorSession( 118 allocator, content_name, component, 119 ice_username_fragment, ice_password), 120 allocator_(allocator), 121 relay_session_attempts_(0), 122 relay_udp_port_(0), 123 relay_tcp_port_(0), 124 relay_ssltcp_port_(0), 125 pending_relay_requests_(0) { 126 } 127 128 P2PPortAllocatorSession::~P2PPortAllocatorSession() { 129 if (stun_address_request_.get()) 130 stun_address_request_->Cancel(); 131 132 for (size_t i = 0; i < relay_info_.size(); ++i) { 133 if (relay_info_[i].relay_address_request.get()) 134 relay_info_[i].relay_address_request->Cancel(); 135 } 136 } 137 138 void P2PPortAllocatorSession::didReceiveData( 139 WebURLLoader* loader, const char* data, 140 int data_length, int encoded_data_length) { 141 DCHECK_EQ(loader, relay_session_request_.get()); 142 if (static_cast<int>(relay_session_response_.size()) + data_length > 143 kMaximumRelayResponseSize) { 144 LOG(ERROR) << "Response received from the server is too big."; 145 loader->cancel(); 146 return; 147 } 148 relay_session_response_.append(data, data + data_length); 149 } 150 151 void P2PPortAllocatorSession::didFinishLoading(WebURLLoader* loader, 152 double finish_time) { 153 ParseRelayResponse(); 154 } 155 156 void P2PPortAllocatorSession::didFail(blink::WebURLLoader* loader, 157 const blink::WebURLError& error) { 158 DCHECK_EQ(loader, relay_session_request_.get()); 159 DCHECK_NE(error.reason, 0); 160 161 LOG(ERROR) << "Relay session request failed."; 162 163 // Retry the request. 164 AllocateLegacyRelaySession(); 165 } 166 167 void P2PPortAllocatorSession::GetPortConfigurations() { 168 // Resolve Stun and Relay server addresses. 169 if (!allocator_->config_.stun_server.empty() && 170 stun_server_address_.IsNil()) { 171 ResolveStunServerAddress(); 172 } else { 173 AddConfig(); 174 } 175 176 if (allocator_->config_.legacy_relay) { 177 AllocateLegacyRelaySession(); 178 } else { 179 ResolveRelayServerAddresses(); 180 } 181 } 182 183 void P2PPortAllocatorSession::ResolveStunServerAddress() { 184 if (stun_address_request_.get()) 185 return; 186 187 stun_address_request_ = 188 new P2PHostAddressRequest(allocator_->socket_dispatcher_); 189 stun_address_request_->Request(allocator_->config_.stun_server, base::Bind( 190 &P2PPortAllocatorSession::OnStunServerAddress, 191 base::Unretained(this))); 192 } 193 194 void P2PPortAllocatorSession::OnStunServerAddress( 195 const net::IPAddressNumber& address) { 196 if (address.empty()) { 197 LOG(ERROR) << "Failed to resolve STUN server address " 198 << allocator_->config_.stun_server; 199 // Allocating local ports on stun failure. 200 AddConfig(); 201 return; 202 } 203 204 if (!jingle_glue::IPEndPointToSocketAddress( 205 net::IPEndPoint(address, allocator_->config_.stun_server_port), 206 &stun_server_address_)) { 207 return; 208 } 209 AddConfig(); 210 } 211 212 void P2PPortAllocatorSession::ResolveRelayServerAddresses() { 213 for (size_t i = 0; i < allocator_->config_.relays.size(); ++i) { 214 scoped_refptr<P2PHostAddressRequest> relay_request = 215 new P2PHostAddressRequest(allocator_->socket_dispatcher_); 216 relay_request->Request( 217 allocator_->config_.relays[i].server_address, 218 base::Bind(&P2PPortAllocatorSession::OnRelayServerAddressResolved, 219 base::Unretained(this), i)); 220 // Copy relay configuration from alloctor and keeping it in a map. 221 RelayServer relay; 222 relay.config = allocator_->config_.relays[i]; 223 relay.relay_address_request = relay_request; 224 relay_info_.push_back(relay); 225 ++pending_relay_requests_; 226 } 227 } 228 229 void P2PPortAllocatorSession::OnRelayServerAddressResolved( 230 size_t index, const net::IPAddressNumber& address) { 231 // Let's first decrement the pending requests count. 232 --pending_relay_requests_; 233 if (index > relay_info_.size()) { 234 NOTREACHED(); 235 return; 236 } 237 238 if (address.empty()) { 239 LOG(ERROR) << "Failed to resolve Relay server address " 240 << relay_info_.at(index).config.server_address; 241 } else { 242 // Getting relay server info for which this resolved address belongs. 243 RelayServer& relay_server = relay_info_.at(index); 244 245 talk_base::SocketAddress socket_address; 246 if (!jingle_glue::IPEndPointToSocketAddress( 247 net::IPEndPoint(address, relay_server.config.port), 248 &socket_address)) { 249 NOTREACHED(); 250 } 251 relay_server.resolved_relay_address = socket_address; 252 } 253 254 if (!pending_relay_requests_) 255 AddConfig(); 256 } 257 258 void P2PPortAllocatorSession::AllocateLegacyRelaySession() { 259 if (allocator_->config_.relays.empty()) 260 return; 261 // If we are using legacy relay, we will have only one entry in relay server 262 // list. 263 P2PPortAllocator::Config::RelayServerConfig relay_config = 264 allocator_->config_.relays[0]; 265 266 if (relay_session_attempts_ > kRelaySessionRetries) 267 return; 268 relay_session_attempts_++; 269 270 relay_session_response_.clear(); 271 272 WebURLLoaderOptions options; 273 options.allowCredentials = false; 274 275 options.crossOriginRequestPolicy = 276 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; 277 278 relay_session_request_.reset( 279 allocator_->web_frame_->createAssociatedURLLoader(options)); 280 if (!relay_session_request_) { 281 LOG(ERROR) << "Failed to create URL loader."; 282 return; 283 } 284 285 std::string url = "https://" + relay_config.server_address + 286 kCreateRelaySessionURL + 287 "?username=" + net::EscapeUrlEncodedData(username(), true) + 288 "&password=" + net::EscapeUrlEncodedData(password(), true); 289 290 WebURLRequest request; 291 request.initialize(); 292 request.setURL(WebURL(GURL(url))); 293 request.setAllowStoredCredentials(false); 294 request.setCachePolicy(WebURLRequest::ReloadIgnoringCacheData); 295 request.setHTTPMethod("GET"); 296 request.addHTTPHeaderField( 297 WebString::fromUTF8("X-Talk-Google-Relay-Auth"), 298 WebString::fromUTF8(relay_config.password)); 299 request.addHTTPHeaderField( 300 WebString::fromUTF8("X-Google-Relay-Auth"), 301 WebString::fromUTF8(relay_config.username)); 302 request.addHTTPHeaderField(WebString::fromUTF8("X-Stream-Type"), 303 WebString::fromUTF8("chromoting")); 304 305 relay_session_request_->loadAsynchronously(request, this); 306 } 307 308 void P2PPortAllocatorSession::ParseRelayResponse() { 309 std::vector<std::pair<std::string, std::string> > value_pairs; 310 if (!base::SplitStringIntoKeyValuePairs(relay_session_response_, '=', '\n', 311 &value_pairs)) { 312 LOG(ERROR) << "Received invalid response from relay server"; 313 return; 314 } 315 316 relay_ip_.Clear(); 317 relay_udp_port_ = 0; 318 relay_tcp_port_ = 0; 319 relay_ssltcp_port_ = 0; 320 321 for (std::vector<std::pair<std::string, std::string> >::iterator 322 it = value_pairs.begin(); 323 it != value_pairs.end(); ++it) { 324 std::string key; 325 std::string value; 326 TrimWhitespaceASCII(it->first, TRIM_ALL, &key); 327 TrimWhitespaceASCII(it->second, TRIM_ALL, &value); 328 329 if (key == "username") { 330 if (value != username()) { 331 LOG(ERROR) << "When creating relay session received user name " 332 " that was different from the value specified in the query."; 333 return; 334 } 335 } else if (key == "password") { 336 if (value != password()) { 337 LOG(ERROR) << "When creating relay session received password " 338 "that was different from the value specified in the query."; 339 return; 340 } 341 } else if (key == "relay.ip") { 342 relay_ip_.SetIP(value); 343 if (relay_ip_.ip() == 0) { 344 LOG(ERROR) << "Received unresolved relay server address: " << value; 345 return; 346 } 347 } else if (key == "relay.udp_port") { 348 if (!ParsePortNumber(value, &relay_udp_port_)) 349 return; 350 } else if (key == "relay.tcp_port") { 351 if (!ParsePortNumber(value, &relay_tcp_port_)) 352 return; 353 } else if (key == "relay.ssltcp_port") { 354 if (!ParsePortNumber(value, &relay_ssltcp_port_)) 355 return; 356 } 357 } 358 359 AddConfig(); 360 } 361 362 void P2PPortAllocatorSession::AddConfig() { 363 cricket::PortConfiguration* port_config = new cricket::PortConfiguration( 364 stun_server_address_, std::string(), std::string()); 365 366 if (!pending_relay_requests_) { 367 // Push all resolved addresses and transport port type to allocator. 368 for (size_t i = 0; i < relay_info_.size(); ++i) { 369 if (relay_info_[i].resolved_relay_address.IsNil()) 370 continue; 371 372 RelayServer relay_info = relay_info_[i]; 373 cricket::RelayCredentials credentials(relay_info.config.username, 374 relay_info.config.password); 375 cricket::RelayServerConfig relay_server(cricket::RELAY_TURN); 376 cricket::ProtocolType protocol; 377 if (!cricket::StringToProto(relay_info.config.transport_type.c_str(), 378 &protocol)) { 379 DLOG(WARNING) << "Ignoring TURN server " 380 << relay_info.config.server_address << ". " 381 << "Reason= Incorrect " 382 << relay_info.config.transport_type 383 << " transport parameter."; 384 continue; 385 } 386 387 relay_server.ports.push_back(cricket::ProtocolAddress( 388 relay_info.resolved_relay_address, 389 protocol, 390 relay_info.config.secure)); 391 relay_server.credentials = credentials; 392 port_config->AddRelay(relay_server); 393 } 394 } 395 ConfigReady(port_config); 396 } 397 398 } // namespace content 399