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 set_allow_tcp_listen(false);
86 P2PPortAllocator::~P2PPortAllocator() {
89 cricket::PortAllocatorSession
* P2PPortAllocator::CreateSessionInternal(
90 const std::string
& content_name
,
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
);
98 P2PPortAllocatorSession::P2PPortAllocatorSession(
99 P2PPortAllocator
* allocator
,
100 const std::string
& content_name
,
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),
111 relay_ssltcp_port_(0),
112 pending_relay_requests_(0) {
115 P2PPortAllocatorSession::~P2PPortAllocatorSession() {
118 void 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.";
128 relay_session_response_
.append(data
, data
+ data_length
);
131 void P2PPortAllocatorSession::didFinishLoading(
132 WebURLLoader
* loader
, double finish_time
,
133 int64_t total_encoded_data_length
) {
134 ParseRelayResponse();
137 void P2PPortAllocatorSession::didFail(blink::WebURLLoader
* loader
,
138 const blink::WebURLError
& error
) {
139 DCHECK_EQ(loader
, relay_session_request_
.get());
140 DCHECK_NE(error
.reason
, 0);
142 LOG(ERROR
) << "Relay session request failed.";
144 // Retry the request.
145 AllocateLegacyRelaySession();
148 void P2PPortAllocatorSession::GetPortConfigurations() {
149 if (allocator_
->config_
.legacy_relay
) {
150 AllocateLegacyRelaySession();
155 void P2PPortAllocatorSession::AllocateLegacyRelaySession() {
156 if (allocator_
->config_
.relays
.empty())
158 // If we are using legacy relay, we will have only one entry in relay server
160 P2PPortAllocator::Config::RelayServerConfig relay_config
=
161 allocator_
->config_
.relays
[0];
163 if (relay_session_attempts_
> kRelaySessionRetries
)
165 relay_session_attempts_
++;
167 relay_session_response_
.clear();
169 WebURLLoaderOptions options
;
170 options
.allowCredentials
= false;
172 options
.crossOriginRequestPolicy
=
173 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl
;
175 relay_session_request_
.reset(
176 allocator_
->web_frame_
->createAssociatedURLLoader(options
));
177 if (!relay_session_request_
) {
178 LOG(ERROR
) << "Failed to create URL loader.";
182 std::string url
= "https://" + relay_config
.server_address
+
183 kCreateRelaySessionURL
+
184 "?username=" + net::EscapeUrlEncodedData(username(), true) +
185 "&password=" + net::EscapeUrlEncodedData(password(), true);
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"));
202 relay_session_request_
->loadAsynchronously(request
, this);
205 void P2PPortAllocatorSession::ParseRelayResponse() {
206 std::vector
<std::pair
<std::string
, std::string
> > value_pairs
;
207 if (!base::SplitStringIntoKeyValuePairs(relay_session_response_
, '=', '\n',
209 LOG(ERROR
) << "Received invalid response from relay server";
216 relay_ssltcp_port_
= 0;
218 for (std::vector
<std::pair
<std::string
, std::string
> >::iterator
219 it
= value_pairs
.begin();
220 it
!= value_pairs
.end(); ++it
) {
223 base::TrimWhitespaceASCII(it
->first
, base::TRIM_ALL
, &key
);
224 base::TrimWhitespaceASCII(it
->second
, base::TRIM_ALL
, &value
);
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.";
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.";
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
;
244 } else if (key
== "relay.udp_port") {
245 if (!ParsePortNumber(value
, &relay_udp_port_
))
247 } else if (key
== "relay.tcp_port") {
248 if (!ParsePortNumber(value
, &relay_tcp_port_
))
250 } else if (key
== "relay.ssltcp_port") {
251 if (!ParsePortNumber(value
, &relay_ssltcp_port_
))
259 void 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());
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(),
272 DLOG(WARNING
) << "Ignoring TURN server "
273 << config
.relays
[i
].server_address
<< ". "
274 << "Reason= Incorrect "
275 << config
.relays
[i
].transport_type
276 << " transport parameter.";
280 relay_server
.ports
.push_back(cricket::ProtocolAddress(
281 talk_base::SocketAddress(config
.relays
[i
].server_address
,
282 config
.relays
[i
].port
),
284 config
.relays
[i
].secure
));
285 relay_server
.credentials
= credentials
;
286 port_config
->AddRelay(relay_server
);
288 ConfigReady(port_config
);
291 } // namespace content