Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / renderer / p2p / port_allocator.cc
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
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
53 P2PPortAllocator::Config::Config()
54     : stun_server_port(0),
55       legacy_relay(true),
56       disable_tcp_transport(false) {
57 }
58
59 P2PPortAllocator::Config::~Config() {
60 }
61
62 P2PPortAllocator::Config::RelayServerConfig::RelayServerConfig()
63     : port(0) {
64 }
65
66 P2PPortAllocator::Config::RelayServerConfig::~RelayServerConfig() {
67 }
68
69 P2PPortAllocator::P2PPortAllocator(
70     blink::WebFrame* web_frame,
71     P2PSocketDispatcher* socket_dispatcher,
72     talk_base::NetworkManager* network_manager,
73     talk_base::PacketSocketFactory* socket_factory,
74     const Config& config)
75     : cricket::BasicPortAllocator(network_manager, socket_factory),
76       web_frame_(web_frame),
77       socket_dispatcher_(socket_dispatcher),
78       config_(config) {
79   uint32 flags = 0;
80   if (config_.disable_tcp_transport)
81     flags |= cricket::PORTALLOCATOR_DISABLE_TCP;
82   set_flags(flags);
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);
88   }
89 }
90
91 P2PPortAllocator::~P2PPortAllocator() {
92 }
93
94 cricket::PortAllocatorSession* P2PPortAllocator::CreateSessionInternal(
95     const std::string& content_name,
96     int component,
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);
101 }
102
103 P2PPortAllocatorSession::P2PPortAllocatorSession(
104     P2PPortAllocator* allocator,
105     const std::string& content_name,
106     int component,
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),
114       relay_udp_port_(0),
115       relay_tcp_port_(0),
116       relay_ssltcp_port_(0),
117       pending_relay_requests_(0) {
118 }
119
120 P2PPortAllocatorSession::~P2PPortAllocatorSession() {
121 }
122
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.";
130     loader->cancel();
131     return;
132   }
133   relay_session_response_.append(data, data + data_length);
134 }
135
136 void P2PPortAllocatorSession::didFinishLoading(
137     WebURLLoader* loader, double finish_time,
138     int64_t total_encoded_data_length) {
139   ParseRelayResponse();
140 }
141
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);
146
147   LOG(ERROR) << "Relay session request failed.";
148
149   // Retry the request.
150   AllocateLegacyRelaySession();
151 }
152
153 void P2PPortAllocatorSession::GetPortConfigurations() {
154   if (allocator_->config_.legacy_relay) {
155     AllocateLegacyRelaySession();
156   }
157   AddConfig();
158 }
159
160 void P2PPortAllocatorSession::AllocateLegacyRelaySession() {
161   if (allocator_->config_.relays.empty())
162     return;
163   // If we are using legacy relay, we will have only one entry in relay server
164   // list.
165   P2PPortAllocator::Config::RelayServerConfig relay_config =
166       allocator_->config_.relays[0];
167
168   if (relay_session_attempts_ > kRelaySessionRetries)
169     return;
170   relay_session_attempts_++;
171
172   relay_session_response_.clear();
173
174   WebURLLoaderOptions options;
175   options.allowCredentials = false;
176
177   options.crossOriginRequestPolicy =
178       WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
179
180   relay_session_request_.reset(
181       allocator_->web_frame_->createAssociatedURLLoader(options));
182   if (!relay_session_request_) {
183     LOG(ERROR) << "Failed to create URL loader.";
184     return;
185   }
186
187   std::string url = "https://" + relay_config.server_address +
188       kCreateRelaySessionURL +
189       "?username=" + net::EscapeUrlEncodedData(username(), true) +
190       "&password=" + net::EscapeUrlEncodedData(password(), true);
191
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"));
206
207   relay_session_request_->loadAsynchronously(request, this);
208 }
209
210 void P2PPortAllocatorSession::ParseRelayResponse() {
211   std::vector<std::pair<std::string, std::string> > value_pairs;
212   if (!base::SplitStringIntoKeyValuePairs(relay_session_response_, '=', '\n',
213                                           &value_pairs)) {
214     LOG(ERROR) << "Received invalid response from relay server";
215     return;
216   }
217
218   relay_ip_.Clear();
219   relay_udp_port_ = 0;
220   relay_tcp_port_ = 0;
221   relay_ssltcp_port_ = 0;
222
223   for (std::vector<std::pair<std::string, std::string> >::iterator
224            it = value_pairs.begin();
225        it != value_pairs.end(); ++it) {
226     std::string key;
227     std::string value;
228     TrimWhitespaceASCII(it->first, TRIM_ALL, &key);
229     TrimWhitespaceASCII(it->second, TRIM_ALL, &value);
230
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.";
235         return;
236       }
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.";
241         return;
242       }
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;
247         return;
248       }
249     } else if (key == "relay.udp_port") {
250       if (!ParsePortNumber(value, &relay_udp_port_))
251         return;
252     } else if (key == "relay.tcp_port") {
253       if (!ParsePortNumber(value, &relay_tcp_port_))
254         return;
255     } else if (key == "relay.ssltcp_port") {
256       if (!ParsePortNumber(value, &relay_ssltcp_port_))
257         return;
258     }
259   }
260
261   AddConfig();
262 }
263
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());
269
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(),
276                                 &protocol)) {
277       DLOG(WARNING) << "Ignoring TURN server "
278                     << config.relays[i].server_address << ". "
279                     << "Reason= Incorrect "
280                     << config.relays[i].transport_type
281                     << " transport parameter.";
282       continue;
283     }
284
285     relay_server.ports.push_back(cricket::ProtocolAddress(
286         talk_base::SocketAddress(config.relays[i].server_address,
287                                  config.relays[i].port),
288         protocol,
289         config.relays[i].secure));
290     relay_server.credentials = credentials;
291     port_config->AddRelay(relay_server);
292   }
293   ConfigReady(port_config);
294 }
295
296 }  // namespace content