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