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()
55 disable_tcp_transport(false) {
58 P2PPortAllocator::Config::~Config() {
61 P2PPortAllocator::Config::RelayServerConfig::RelayServerConfig()
65 P2PPortAllocator::Config::RelayServerConfig::~RelayServerConfig() {
68 P2PPortAllocator::P2PPortAllocator(
69 blink::WebFrame
* web_frame
,
70 P2PSocketDispatcher
* socket_dispatcher
,
71 rtc::NetworkManager
* network_manager
,
72 rtc::PacketSocketFactory
* socket_factory
,
74 : cricket::BasicPortAllocator(network_manager
, socket_factory
),
75 web_frame_(web_frame
),
76 socket_dispatcher_(socket_dispatcher
),
79 if (config_
.disable_tcp_transport
)
80 flags
|= cricket::PORTALLOCATOR_DISABLE_TCP
;
82 set_allow_tcp_listen(false);
85 P2PPortAllocator::~P2PPortAllocator() {
88 cricket::PortAllocatorSession
* P2PPortAllocator::CreateSessionInternal(
89 const std::string
& content_name
,
91 const std::string
& ice_username_fragment
,
92 const std::string
& ice_password
) {
93 return new P2PPortAllocatorSession(
94 this, content_name
, component
, ice_username_fragment
, ice_password
);
97 P2PPortAllocatorSession::P2PPortAllocatorSession(
98 P2PPortAllocator
* allocator
,
99 const std::string
& content_name
,
101 const std::string
& ice_username_fragment
,
102 const std::string
& ice_password
)
103 : cricket::BasicPortAllocatorSession(
104 allocator
, content_name
, component
,
105 ice_username_fragment
, ice_password
),
106 allocator_(allocator
),
107 relay_session_attempts_(0),
110 relay_ssltcp_port_(0),
111 pending_relay_requests_(0) {
114 P2PPortAllocatorSession::~P2PPortAllocatorSession() {
117 void P2PPortAllocatorSession::didReceiveData(
118 WebURLLoader
* loader
, const char* data
,
119 int data_length
, int encoded_data_length
) {
120 DCHECK_EQ(loader
, relay_session_request_
.get());
121 if (static_cast<int>(relay_session_response_
.size()) + data_length
>
122 kMaximumRelayResponseSize
) {
123 LOG(ERROR
) << "Response received from the server is too big.";
127 relay_session_response_
.append(data
, data
+ data_length
);
130 void P2PPortAllocatorSession::didFinishLoading(
131 WebURLLoader
* loader
, double finish_time
,
132 int64_t total_encoded_data_length
) {
133 ParseRelayResponse();
136 void P2PPortAllocatorSession::didFail(blink::WebURLLoader
* loader
,
137 const blink::WebURLError
& error
) {
138 DCHECK_EQ(loader
, relay_session_request_
.get());
139 DCHECK_NE(error
.reason
, 0);
141 LOG(ERROR
) << "Relay session request failed.";
143 // Retry the request.
144 AllocateLegacyRelaySession();
147 void P2PPortAllocatorSession::GetPortConfigurations() {
148 if (allocator_
->config_
.legacy_relay
) {
149 AllocateLegacyRelaySession();
154 void P2PPortAllocatorSession::AllocateLegacyRelaySession() {
155 if (allocator_
->config_
.relays
.empty())
157 // If we are using legacy relay, we will have only one entry in relay server
159 P2PPortAllocator::Config::RelayServerConfig relay_config
=
160 allocator_
->config_
.relays
[0];
162 if (relay_session_attempts_
> kRelaySessionRetries
)
164 relay_session_attempts_
++;
166 relay_session_response_
.clear();
168 WebURLLoaderOptions options
;
169 options
.allowCredentials
= false;
171 options
.crossOriginRequestPolicy
=
172 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl
;
174 relay_session_request_
.reset(
175 allocator_
->web_frame_
->createAssociatedURLLoader(options
));
176 if (!relay_session_request_
) {
177 LOG(ERROR
) << "Failed to create URL loader.";
181 std::string url
= "https://" + relay_config
.server_address
+
182 kCreateRelaySessionURL
+
183 "?username=" + net::EscapeUrlEncodedData(username(), true) +
184 "&password=" + net::EscapeUrlEncodedData(password(), true);
186 WebURLRequest request
;
187 request
.initialize();
188 request
.setURL(WebURL(GURL(url
)));
189 request
.setAllowStoredCredentials(false);
190 request
.setCachePolicy(WebURLRequest::ReloadIgnoringCacheData
);
191 request
.setHTTPMethod("GET");
192 request
.addHTTPHeaderField(
193 WebString::fromUTF8("X-Talk-Google-Relay-Auth"),
194 WebString::fromUTF8(relay_config
.password
));
195 request
.addHTTPHeaderField(
196 WebString::fromUTF8("X-Google-Relay-Auth"),
197 WebString::fromUTF8(relay_config
.username
));
198 request
.addHTTPHeaderField(WebString::fromUTF8("X-Stream-Type"),
199 WebString::fromUTF8("chromoting"));
201 relay_session_request_
->loadAsynchronously(request
, this);
204 void P2PPortAllocatorSession::ParseRelayResponse() {
205 std::vector
<std::pair
<std::string
, std::string
> > value_pairs
;
206 if (!base::SplitStringIntoKeyValuePairs(relay_session_response_
, '=', '\n',
208 LOG(ERROR
) << "Received invalid response from relay server";
215 relay_ssltcp_port_
= 0;
217 for (std::vector
<std::pair
<std::string
, std::string
> >::iterator
218 it
= value_pairs
.begin();
219 it
!= value_pairs
.end(); ++it
) {
222 base::TrimWhitespaceASCII(it
->first
, base::TRIM_ALL
, &key
);
223 base::TrimWhitespaceASCII(it
->second
, base::TRIM_ALL
, &value
);
225 if (key
== "username") {
226 if (value
!= username()) {
227 LOG(ERROR
) << "When creating relay session received user name "
228 " that was different from the value specified in the query.";
231 } else if (key
== "password") {
232 if (value
!= password()) {
233 LOG(ERROR
) << "When creating relay session received password "
234 "that was different from the value specified in the query.";
237 } else if (key
== "relay.ip") {
238 relay_ip_
.SetIP(value
);
239 if (relay_ip_
.ip() == 0) {
240 LOG(ERROR
) << "Received unresolved relay server address: " << value
;
243 } else if (key
== "relay.udp_port") {
244 if (!ParsePortNumber(value
, &relay_udp_port_
))
246 } else if (key
== "relay.tcp_port") {
247 if (!ParsePortNumber(value
, &relay_tcp_port_
))
249 } else if (key
== "relay.ssltcp_port") {
250 if (!ParsePortNumber(value
, &relay_ssltcp_port_
))
258 void P2PPortAllocatorSession::AddConfig() {
259 const P2PPortAllocator::Config
& config
= allocator_
->config_
;
260 cricket::PortConfiguration
* port_config
= new cricket::PortConfiguration(
261 config
.stun_servers
, std::string(), std::string());
263 for (size_t i
= 0; i
< config
.relays
.size(); ++i
) {
264 cricket::RelayCredentials
credentials(config
.relays
[i
].username
,
265 config
.relays
[i
].password
);
266 cricket::RelayServerConfig
relay_server(cricket::RELAY_TURN
);
267 cricket::ProtocolType protocol
;
268 if (!cricket::StringToProto(config
.relays
[i
].transport_type
.c_str(),
270 DLOG(WARNING
) << "Ignoring TURN server "
271 << config
.relays
[i
].server_address
<< ". "
272 << "Reason= Incorrect "
273 << config
.relays
[i
].transport_type
274 << " transport parameter.";
278 relay_server
.ports
.push_back(cricket::ProtocolAddress(
279 rtc::SocketAddress(config
.relays
[i
].server_address
,
280 config
.relays
[i
].port
),
282 config
.relays
[i
].secure
));
283 relay_server
.credentials
= credentials
;
284 port_config
->AddRelay(relay_server
);
286 ConfigReady(port_config
);
289 } // namespace content