Upstream version 10.39.225.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     : legacy_relay(true),
55       disable_tcp_transport(false) {
56 }
57
58 P2PPortAllocator::Config::~Config() {
59 }
60
61 P2PPortAllocator::Config::RelayServerConfig::RelayServerConfig()
62     : port(0) {
63 }
64
65 P2PPortAllocator::Config::RelayServerConfig::~RelayServerConfig() {
66 }
67
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
85 P2PPortAllocator::~P2PPortAllocator() {
86 }
87
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
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
114 P2PPortAllocatorSession::~P2PPortAllocatorSession() {
115 }
116
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
130 void P2PPortAllocatorSession::didFinishLoading(
131     WebURLLoader* loader, double finish_time,
132     int64_t total_encoded_data_length) {
133   ParseRelayResponse();
134 }
135
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
147 void P2PPortAllocatorSession::GetPortConfigurations() {
148   if (allocator_->config_.legacy_relay) {
149     AllocateLegacyRelaySession();
150   }
151   AddConfig();
152 }
153
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
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
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