port_allocator.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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 24using WebKit::WebString; 25using WebKit::WebURL; 26using WebKit::WebURLLoader; 27using WebKit::WebURLLoaderOptions; 28using WebKit::WebURLRequest; 29using WebKit::WebURLResponse; 30 31namespace content { 32 33namespace { 34 35// URL used to create a relay session. 36const char kCreateRelaySessionURL[] = "/create_session"; 37 38// Number of times we will try to request relay session. 39const int kRelaySessionRetries = 3; 40 41// Manimum relay server size we would try to parse. 42const int kMaximumRelayResponseSize = 102400; 43 44bool 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 55P2PPortAllocator::Config::Config() 56 : stun_server_port(0), 57 relay_server_port(0), 58 legacy_relay(true), 59 disable_tcp_transport(false) { 60} 61 62P2PPortAllocator::Config::~Config() { 63} 64 65P2PPortAllocator::P2PPortAllocator( 66 WebKit::WebFrame* web_frame, 67 P2PSocketDispatcher* socket_dispatcher, 68 talk_base::NetworkManager* network_manager, 69 talk_base::PacketSocketFactory* socket_factory, 70 const Config& config) 71 : cricket::BasicPortAllocator(network_manager, socket_factory), 72 web_frame_(web_frame), 73 socket_dispatcher_(socket_dispatcher), 74 config_(config) { 75 uint32 flags = 0; 76 if (config_.disable_tcp_transport) 77 flags |= cricket::PORTALLOCATOR_DISABLE_TCP; 78 set_flags(flags); 79 // TODO(ronghuawu): crbug/138185 add ourselves to the firewall list in browser 80 // process and then remove below line. 81 if (!CommandLine::ForCurrentProcess()->HasSwitch( 82 switches::kEnableWebRtcTcpServerSocket)) { 83 set_allow_tcp_listen(false); 84 } 85} 86 87P2PPortAllocator::~P2PPortAllocator() { 88} 89 90cricket::PortAllocatorSession* P2PPortAllocator::CreateSessionInternal( 91 const std::string& content_name, 92 int component, 93 const std::string& ice_username_fragment, 94 const std::string& ice_password) { 95 return new P2PPortAllocatorSession( 96 this, content_name, component, ice_username_fragment, ice_password); 97} 98 99P2PPortAllocatorSession::P2PPortAllocatorSession( 100 P2PPortAllocator* allocator, 101 const std::string& content_name, 102 int component, 103 const std::string& ice_username_fragment, 104 const std::string& ice_password) 105 : cricket::BasicPortAllocatorSession( 106 allocator, content_name, component, 107 ice_username_fragment, ice_password), 108 allocator_(allocator), 109 relay_session_attempts_(0), 110 relay_udp_port_(0), 111 relay_tcp_port_(0), 112 relay_ssltcp_port_(0) { 113} 114 115P2PPortAllocatorSession::~P2PPortAllocatorSession() { 116 if (stun_address_request_.get()) 117 stun_address_request_->Cancel(); 118} 119 120void P2PPortAllocatorSession::didReceiveData( 121 WebURLLoader* loader, const char* data, 122 int data_length, int encoded_data_length) { 123 DCHECK_EQ(loader, relay_session_request_.get()); 124 if (static_cast<int>(relay_session_response_.size()) + data_length > 125 kMaximumRelayResponseSize) { 126 LOG(ERROR) << "Response received from the server is too big."; 127 loader->cancel(); 128 return; 129 } 130 relay_session_response_.append(data, data + data_length); 131} 132 133void P2PPortAllocatorSession::didFinishLoading(WebURLLoader* loader, 134 double finish_time) { 135 ParseRelayResponse(); 136} 137 138void P2PPortAllocatorSession::didFail(WebKit::WebURLLoader* loader, 139 const WebKit::WebURLError& error) { 140 DCHECK_EQ(loader, relay_session_request_.get()); 141 DCHECK_NE(error.reason, 0); 142 143 LOG(ERROR) << "Relay session request failed."; 144 145 // Retry the request. 146 AllocateLegacyRelaySession(); 147} 148 149void P2PPortAllocatorSession::GetPortConfigurations() { 150 if (!allocator_->config_.stun_server.empty() && 151 stun_server_address_.IsNil()) { 152 ResolveStunServerAddress(); 153 } else { 154 AddConfig(); 155 } 156 157 if (allocator_->config_.legacy_relay) { 158 AllocateLegacyRelaySession(); 159 } 160} 161 162void P2PPortAllocatorSession::ResolveStunServerAddress() { 163 if (stun_address_request_.get()) 164 return; 165 166 stun_address_request_ = 167 new P2PHostAddressRequest(allocator_->socket_dispatcher_); 168 stun_address_request_->Request(allocator_->config_.stun_server, base::Bind( 169 &P2PPortAllocatorSession::OnStunServerAddress, 170 base::Unretained(this))); 171} 172 173void P2PPortAllocatorSession::OnStunServerAddress( 174 const net::IPAddressNumber& address) { 175 if (address.empty()) { 176 LOG(ERROR) << "Failed to resolve STUN server address " 177 << allocator_->config_.stun_server; 178 // Allocating local ports on stun failure. 179 AddConfig(); 180 return; 181 } 182 183 if (!jingle_glue::IPEndPointToSocketAddress( 184 net::IPEndPoint(address, allocator_->config_.stun_server_port), 185 &stun_server_address_)) { 186 return; 187 } 188 189 AddConfig(); 190} 191 192void P2PPortAllocatorSession::AllocateLegacyRelaySession() { 193 if (allocator_->config_.relay_server.empty()) 194 return; 195 196 if (relay_session_attempts_ > kRelaySessionRetries) 197 return; 198 relay_session_attempts_++; 199 200 relay_session_response_.clear(); 201 202 WebURLLoaderOptions options; 203 options.allowCredentials = false; 204 205 options.crossOriginRequestPolicy = 206 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; 207 208 relay_session_request_.reset( 209 allocator_->web_frame_->createAssociatedURLLoader(options)); 210 if (!relay_session_request_) { 211 LOG(ERROR) << "Failed to create URL loader."; 212 return; 213 } 214 215 std::string url = "https://" + allocator_->config_.relay_server + 216 kCreateRelaySessionURL + 217 "?username=" + net::EscapeUrlEncodedData(username(), true) + 218 "&password=" + net::EscapeUrlEncodedData(password(), true); 219 220 WebURLRequest request; 221 request.initialize(); 222 request.setURL(WebURL(GURL(url))); 223 request.setAllowStoredCredentials(false); 224 request.setCachePolicy(WebURLRequest::ReloadIgnoringCacheData); 225 request.setHTTPMethod("GET"); 226 request.addHTTPHeaderField( 227 WebString::fromUTF8("X-Talk-Google-Relay-Auth"), 228 WebString::fromUTF8(allocator_->config_.relay_password)); 229 request.addHTTPHeaderField( 230 WebString::fromUTF8("X-Google-Relay-Auth"), 231 WebString::fromUTF8(allocator_->config_.relay_password)); 232 request.addHTTPHeaderField(WebString::fromUTF8("X-Stream-Type"), 233 WebString::fromUTF8("chromoting")); 234 235 relay_session_request_->loadAsynchronously(request, this); 236} 237 238void P2PPortAllocatorSession::ParseRelayResponse() { 239 std::vector<std::pair<std::string, std::string> > value_pairs; 240 if (!base::SplitStringIntoKeyValuePairs(relay_session_response_, '=', '\n', 241 &value_pairs)) { 242 LOG(ERROR) << "Received invalid response from relay server"; 243 return; 244 } 245 246 relay_ip_.Clear(); 247 relay_udp_port_ = 0; 248 relay_tcp_port_ = 0; 249 relay_ssltcp_port_ = 0; 250 251 for (std::vector<std::pair<std::string, std::string> >::iterator 252 it = value_pairs.begin(); 253 it != value_pairs.end(); ++it) { 254 std::string key; 255 std::string value; 256 TrimWhitespaceASCII(it->first, TRIM_ALL, &key); 257 TrimWhitespaceASCII(it->second, TRIM_ALL, &value); 258 259 if (key == "username") { 260 if (value != username()) { 261 LOG(ERROR) << "When creating relay session received user name " 262 " that was different from the value specified in the query."; 263 return; 264 } 265 } else if (key == "password") { 266 if (value != password()) { 267 LOG(ERROR) << "When creating relay session received password " 268 "that was different from the value specified in the query."; 269 return; 270 } 271 } else if (key == "relay.ip") { 272 relay_ip_.SetIP(value); 273 if (relay_ip_.ip() == 0) { 274 LOG(ERROR) << "Received unresolved relay server address: " << value; 275 return; 276 } 277 } else if (key == "relay.udp_port") { 278 if (!ParsePortNumber(value, &relay_udp_port_)) 279 return; 280 } else if (key == "relay.tcp_port") { 281 if (!ParsePortNumber(value, &relay_tcp_port_)) 282 return; 283 } else if (key == "relay.ssltcp_port") { 284 if (!ParsePortNumber(value, &relay_ssltcp_port_)) 285 return; 286 } 287 } 288 289 AddConfig(); 290} 291 292void P2PPortAllocatorSession::AddConfig() { 293 cricket::PortConfiguration* config = new cricket::PortConfiguration( 294 stun_server_address_, std::string(), std::string()); 295 296 if (allocator_->config_.legacy_relay) { 297 // Passing empty credentials for legacy google relay. 298 cricket::RelayServerConfig gturn_config(cricket::RELAY_GTURN); 299 if (relay_ip_.ip() != 0) { 300 if (relay_udp_port_ > 0) { 301 talk_base::SocketAddress address(relay_ip_.ip(), relay_udp_port_); 302 gturn_config.ports.push_back(cricket::ProtocolAddress( 303 address, cricket::PROTO_UDP)); 304 } 305 if (relay_tcp_port_ > 0 && !allocator_->config_.disable_tcp_transport) { 306 talk_base::SocketAddress address(relay_ip_.ip(), relay_tcp_port_); 307 gturn_config.ports.push_back(cricket::ProtocolAddress( 308 address, cricket::PROTO_TCP)); 309 } 310 if (relay_ssltcp_port_ > 0 && 311 !allocator_->config_.disable_tcp_transport) { 312 talk_base::SocketAddress address(relay_ip_.ip(), relay_ssltcp_port_); 313 gturn_config.ports.push_back(cricket::ProtocolAddress( 314 address, cricket::PROTO_SSLTCP)); 315 } 316 if (!gturn_config.ports.empty()) { 317 config->AddRelay(gturn_config); 318 } 319 } 320 } else { 321 if (!(allocator_->config_.relay_username.empty() || 322 allocator_->config_.relay_server.empty())) { 323 // Adding TURN related information to config. 324 // As per TURN RFC, same turn server should be used for stun as well. 325 // Configuration should have same address for both stun and turn. 326 DCHECK_EQ(allocator_->config_.stun_server, 327 allocator_->config_.relay_server); 328 cricket::RelayServerConfig turn_config(cricket::RELAY_TURN); 329 cricket::RelayCredentials credentials( 330 allocator_->config_.relay_username, 331 allocator_->config_.relay_password); 332 turn_config.credentials = credentials; 333 334 cricket::ProtocolType protocol; 335 if (cricket::StringToProto( 336 allocator_->config_.relay_transport_type.c_str(), &protocol)) { 337 turn_config.ports.push_back(cricket::ProtocolAddress( 338 stun_server_address_, protocol)); 339 config->AddRelay(turn_config); 340 } else { 341 DLOG(WARNING) << "Ignoring TURN server " 342 << allocator_->config_.relay_server << ". " 343 << "Reason= Incorrect " 344 << allocator_->config_.relay_transport_type 345 << " transport parameter."; 346 } 347 } 348 } 349 ConfigReady(config); 350} 351 352} // namespace content 353