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.
5 #include "content/renderer/p2p/port_allocator.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"
24 using WebKit::WebString;
26 using WebKit::WebURLLoader;
27 using WebKit::WebURLLoaderOptions;
28 using WebKit::WebURLRequest;
29 using WebKit::WebURLResponse;
35 // URL used to create a relay session.
36 const char kCreateRelaySessionURL[] = "/create_session";
38 // Number of times we will try to request relay session.
39 const int kRelaySessionRetries = 3;
41 // Manimum relay server size we would try to parse.
42 const int kMaximumRelayResponseSize = 102400;
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;
55 P2PPortAllocator::Config::Config()
56 : stun_server_port(0),
58 disable_tcp_transport(false) {
61 P2PPortAllocator::Config::~Config() {
64 P2PPortAllocator::Config::RelayServerConfig::RelayServerConfig()
68 P2PPortAllocator::Config::RelayServerConfig::~RelayServerConfig() {
71 P2PPortAllocator::P2PPortAllocator(
72 WebKit::WebFrame* web_frame,
73 P2PSocketDispatcher* socket_dispatcher,
74 talk_base::NetworkManager* network_manager,
75 talk_base::PacketSocketFactory* socket_factory,
77 : cricket::BasicPortAllocator(network_manager, socket_factory),
78 web_frame_(web_frame),
79 socket_dispatcher_(socket_dispatcher),
82 if (config_.disable_tcp_transport)
83 flags |= cricket::PORTALLOCATOR_DISABLE_TCP;
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);
93 P2PPortAllocator::~P2PPortAllocator() {
96 cricket::PortAllocatorSession* P2PPortAllocator::CreateSessionInternal(
97 const std::string& content_name,
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);
105 P2PPortAllocatorSession::RelayServer::RelayServer() {
108 P2PPortAllocatorSession::RelayServer::~RelayServer() {
111 P2PPortAllocatorSession::P2PPortAllocatorSession(
112 P2PPortAllocator* allocator,
113 const std::string& content_name,
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),
124 relay_ssltcp_port_(0),
125 pending_relay_requests_(0) {
128 P2PPortAllocatorSession::~P2PPortAllocatorSession() {
129 if (stun_address_request_.get())
130 stun_address_request_->Cancel();
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();
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.";
148 relay_session_response_.append(data, data + data_length);
151 void P2PPortAllocatorSession::didFinishLoading(WebURLLoader* loader,
152 double finish_time) {
153 ParseRelayResponse();
156 void P2PPortAllocatorSession::didFail(WebKit::WebURLLoader* loader,
157 const WebKit::WebURLError& error) {
158 DCHECK_EQ(loader, relay_session_request_.get());
159 DCHECK_NE(error.reason, 0);
161 LOG(ERROR) << "Relay session request failed.";
163 // Retry the request.
164 AllocateLegacyRelaySession();
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();
176 if (allocator_->config_.legacy_relay) {
177 AllocateLegacyRelaySession();
179 ResolveRelayServerAddresses();
183 void P2PPortAllocatorSession::ResolveStunServerAddress() {
184 if (stun_address_request_.get())
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)));
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.
204 if (!jingle_glue::IPEndPointToSocketAddress(
205 net::IPEndPoint(address, allocator_->config_.stun_server_port),
206 &stun_server_address_)) {
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.
222 relay.config = allocator_->config_.relays[i];
223 relay.relay_address_request = relay_request;
224 relay_info_.push_back(relay);
225 ++pending_relay_requests_;
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()) {
238 if (address.empty()) {
239 LOG(ERROR) << "Failed to resolve Relay server address "
240 << relay_info_.at(index).config.server_address;
242 // Getting relay server info for which this resolved address belongs.
243 RelayServer& relay_server = relay_info_.at(index);
245 talk_base::SocketAddress socket_address;
246 if (!jingle_glue::IPEndPointToSocketAddress(
247 net::IPEndPoint(address, relay_server.config.port),
251 relay_server.resolved_relay_address = socket_address;
254 if (!pending_relay_requests_)
258 void P2PPortAllocatorSession::AllocateLegacyRelaySession() {
259 if (allocator_->config_.relays.empty())
261 // If we are using legacy relay, we will have only one entry in relay server
263 P2PPortAllocator::Config::RelayServerConfig relay_config =
264 allocator_->config_.relays[0];
266 if (relay_session_attempts_ > kRelaySessionRetries)
268 relay_session_attempts_++;
270 relay_session_response_.clear();
272 WebURLLoaderOptions options;
273 options.allowCredentials = false;
275 options.crossOriginRequestPolicy =
276 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
278 relay_session_request_.reset(
279 allocator_->web_frame_->createAssociatedURLLoader(options));
280 if (!relay_session_request_) {
281 LOG(ERROR) << "Failed to create URL loader.";
285 std::string url = "https://" + relay_config.server_address +
286 kCreateRelaySessionURL +
287 "?username=" + net::EscapeUrlEncodedData(username(), true) +
288 "&password=" + net::EscapeUrlEncodedData(password(), true);
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"));
305 relay_session_request_->loadAsynchronously(request, this);
308 void P2PPortAllocatorSession::ParseRelayResponse() {
309 std::vector<std::pair<std::string, std::string> > value_pairs;
310 if (!base::SplitStringIntoKeyValuePairs(relay_session_response_, '=', '\n',
312 LOG(ERROR) << "Received invalid response from relay server";
319 relay_ssltcp_port_ = 0;
321 for (std::vector<std::pair<std::string, std::string> >::iterator
322 it = value_pairs.begin();
323 it != value_pairs.end(); ++it) {
326 TrimWhitespaceASCII(it->first, TRIM_ALL, &key);
327 TrimWhitespaceASCII(it->second, TRIM_ALL, &value);
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.";
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.";
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;
347 } else if (key == "relay.udp_port") {
348 if (!ParsePortNumber(value, &relay_udp_port_))
350 } else if (key == "relay.tcp_port") {
351 if (!ParsePortNumber(value, &relay_tcp_port_))
353 } else if (key == "relay.ssltcp_port") {
354 if (!ParsePortNumber(value, &relay_ssltcp_port_))
362 void P2PPortAllocatorSession::AddConfig() {
363 cricket::PortConfiguration* port_config = new cricket::PortConfiguration(
364 stun_server_address_, std::string(), std::string());
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())
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(),
379 DLOG(WARNING) << "Ignoring TURN server "
380 << relay_info.config.server_address << ". "
381 << "Reason= Incorrect "
382 << relay_info.config.transport_type
383 << " transport parameter.";
387 relay_server.ports.push_back(cricket::ProtocolAddress(
388 relay_info.resolved_relay_address,
390 relay_info.config.secure));
391 relay_server.credentials = credentials;
392 port_config->AddRelay(relay_server);
395 ConfigReady(port_config);
398 } // namespace content