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