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 "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"
22 using blink::WebString;
24 using blink::WebURLLoader;
25 using blink::WebURLLoaderOptions;
26 using blink::WebURLRequest;
27 using blink::WebURLResponse;
33 // URL used to create a relay session.
34 const char kCreateRelaySessionURL[] = "/create_session";
36 // Number of times we will try to request relay session.
37 const int kRelaySessionRetries = 3;
39 // Manimum relay server size we would try to parse.
40 const int kMaximumRelayResponseSize = 102400;
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;
53 P2PPortAllocator::Config::Config()
54 : stun_server_port(0),
56 disable_tcp_transport(false) {
59 P2PPortAllocator::Config::~Config() {
62 P2PPortAllocator::Config::RelayServerConfig::RelayServerConfig()
66 P2PPortAllocator::Config::RelayServerConfig::~RelayServerConfig() {
69 P2PPortAllocator::P2PPortAllocator(
70 blink::WebFrame* web_frame,
71 P2PSocketDispatcher* socket_dispatcher,
72 talk_base::NetworkManager* network_manager,
73 talk_base::PacketSocketFactory* socket_factory,
75 : cricket::BasicPortAllocator(network_manager, socket_factory),
76 web_frame_(web_frame),
77 socket_dispatcher_(socket_dispatcher),
80 if (config_.disable_tcp_transport)
81 flags |= cricket::PORTALLOCATOR_DISABLE_TCP;
83 // TODO(ronghuawu): crbug/138185 add ourselves to the firewall list in browser
84 // process and then remove below line.
85 if (!CommandLine::ForCurrentProcess()->HasSwitch(
86 switches::kEnableWebRtcTcpServerSocket)) {
87 set_allow_tcp_listen(false);
91 P2PPortAllocator::~P2PPortAllocator() {
94 cricket::PortAllocatorSession* P2PPortAllocator::CreateSessionInternal(
95 const std::string& content_name,
97 const std::string& ice_username_fragment,
98 const std::string& ice_password) {
99 return new P2PPortAllocatorSession(
100 this, content_name, component, ice_username_fragment, ice_password);
103 P2PPortAllocatorSession::P2PPortAllocatorSession(
104 P2PPortAllocator* allocator,
105 const std::string& content_name,
107 const std::string& ice_username_fragment,
108 const std::string& ice_password)
109 : cricket::BasicPortAllocatorSession(
110 allocator, content_name, component,
111 ice_username_fragment, ice_password),
112 allocator_(allocator),
113 relay_session_attempts_(0),
116 relay_ssltcp_port_(0),
117 pending_relay_requests_(0) {
120 P2PPortAllocatorSession::~P2PPortAllocatorSession() {
123 void P2PPortAllocatorSession::didReceiveData(
124 WebURLLoader* loader, const char* data,
125 int data_length, int encoded_data_length) {
126 DCHECK_EQ(loader, relay_session_request_.get());
127 if (static_cast<int>(relay_session_response_.size()) + data_length >
128 kMaximumRelayResponseSize) {
129 LOG(ERROR) << "Response received from the server is too big.";
133 relay_session_response_.append(data, data + data_length);
136 void P2PPortAllocatorSession::didFinishLoading(
137 WebURLLoader* loader, double finish_time,
138 int64_t total_encoded_data_length) {
139 ParseRelayResponse();
142 void P2PPortAllocatorSession::didFail(blink::WebURLLoader* loader,
143 const blink::WebURLError& error) {
144 DCHECK_EQ(loader, relay_session_request_.get());
145 DCHECK_NE(error.reason, 0);
147 LOG(ERROR) << "Relay session request failed.";
149 // Retry the request.
150 AllocateLegacyRelaySession();
153 void P2PPortAllocatorSession::GetPortConfigurations() {
154 if (allocator_->config_.legacy_relay) {
155 AllocateLegacyRelaySession();
160 void P2PPortAllocatorSession::AllocateLegacyRelaySession() {
161 if (allocator_->config_.relays.empty())
163 // If we are using legacy relay, we will have only one entry in relay server
165 P2PPortAllocator::Config::RelayServerConfig relay_config =
166 allocator_->config_.relays[0];
168 if (relay_session_attempts_ > kRelaySessionRetries)
170 relay_session_attempts_++;
172 relay_session_response_.clear();
174 WebURLLoaderOptions options;
175 options.allowCredentials = false;
177 options.crossOriginRequestPolicy =
178 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
180 relay_session_request_.reset(
181 allocator_->web_frame_->createAssociatedURLLoader(options));
182 if (!relay_session_request_) {
183 LOG(ERROR) << "Failed to create URL loader.";
187 std::string url = "https://" + relay_config.server_address +
188 kCreateRelaySessionURL +
189 "?username=" + net::EscapeUrlEncodedData(username(), true) +
190 "&password=" + net::EscapeUrlEncodedData(password(), true);
192 WebURLRequest request;
193 request.initialize();
194 request.setURL(WebURL(GURL(url)));
195 request.setAllowStoredCredentials(false);
196 request.setCachePolicy(WebURLRequest::ReloadIgnoringCacheData);
197 request.setHTTPMethod("GET");
198 request.addHTTPHeaderField(
199 WebString::fromUTF8("X-Talk-Google-Relay-Auth"),
200 WebString::fromUTF8(relay_config.password));
201 request.addHTTPHeaderField(
202 WebString::fromUTF8("X-Google-Relay-Auth"),
203 WebString::fromUTF8(relay_config.username));
204 request.addHTTPHeaderField(WebString::fromUTF8("X-Stream-Type"),
205 WebString::fromUTF8("chromoting"));
207 relay_session_request_->loadAsynchronously(request, this);
210 void P2PPortAllocatorSession::ParseRelayResponse() {
211 std::vector<std::pair<std::string, std::string> > value_pairs;
212 if (!base::SplitStringIntoKeyValuePairs(relay_session_response_, '=', '\n',
214 LOG(ERROR) << "Received invalid response from relay server";
221 relay_ssltcp_port_ = 0;
223 for (std::vector<std::pair<std::string, std::string> >::iterator
224 it = value_pairs.begin();
225 it != value_pairs.end(); ++it) {
228 TrimWhitespaceASCII(it->first, TRIM_ALL, &key);
229 TrimWhitespaceASCII(it->second, TRIM_ALL, &value);
231 if (key == "username") {
232 if (value != username()) {
233 LOG(ERROR) << "When creating relay session received user name "
234 " that was different from the value specified in the query.";
237 } else if (key == "password") {
238 if (value != password()) {
239 LOG(ERROR) << "When creating relay session received password "
240 "that was different from the value specified in the query.";
243 } else if (key == "relay.ip") {
244 relay_ip_.SetIP(value);
245 if (relay_ip_.ip() == 0) {
246 LOG(ERROR) << "Received unresolved relay server address: " << value;
249 } else if (key == "relay.udp_port") {
250 if (!ParsePortNumber(value, &relay_udp_port_))
252 } else if (key == "relay.tcp_port") {
253 if (!ParsePortNumber(value, &relay_tcp_port_))
255 } else if (key == "relay.ssltcp_port") {
256 if (!ParsePortNumber(value, &relay_ssltcp_port_))
264 void P2PPortAllocatorSession::AddConfig() {
265 const P2PPortAllocator::Config& config = allocator_->config_;
266 cricket::PortConfiguration* port_config = new cricket::PortConfiguration(
267 talk_base::SocketAddress(config.stun_server, config.stun_server_port),
268 std::string(), std::string());
270 for (size_t i = 0; i < config.relays.size(); ++i) {
271 cricket::RelayCredentials credentials(config.relays[i].username,
272 config.relays[i].password);
273 cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
274 cricket::ProtocolType protocol;
275 if (!cricket::StringToProto(config.relays[i].transport_type.c_str(),
277 DLOG(WARNING) << "Ignoring TURN server "
278 << config.relays[i].server_address << ". "
279 << "Reason= Incorrect "
280 << config.relays[i].transport_type
281 << " transport parameter.";
285 relay_server.ports.push_back(cricket::ProtocolAddress(
286 talk_base::SocketAddress(config.relays[i].server_address,
287 config.relays[i].port),
289 config.relays[i].secure));
290 relay_server.credentials = credentials;
291 port_config->AddRelay(relay_server);
293 ConfigReady(port_config);
296 } // namespace content