• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "remoting/client/plugin/pepper_port_allocator.h"
6 
7 #include "base/bind.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "net/base/net_util.h"
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/cpp/host_resolver.h"
12 #include "ppapi/cpp/net_address.h"
13 #include "ppapi/cpp/url_loader.h"
14 #include "ppapi/cpp/url_request_info.h"
15 #include "ppapi/cpp/url_response_info.h"
16 #include "ppapi/utility/completion_callback_factory.h"
17 #include "remoting/client/plugin/pepper_network_manager.h"
18 #include "remoting/client/plugin/pepper_packet_socket_factory.h"
19 #include "remoting/client/plugin/pepper_util.h"
20 
21 namespace remoting {
22 
23 namespace {
24 
25 // Read buffer we allocate per read when reading response from
26 // URLLoader. Normally the response from URL loader is smaller than 1kB.
27 const int kReadSize = 1024;
28 
29 class PepperPortAllocatorSession
30     : public cricket::HttpPortAllocatorSessionBase {
31  public:
32   PepperPortAllocatorSession(
33       cricket::HttpPortAllocatorBase* allocator,
34       const std::string& content_name,
35       int component,
36       const std::string& ice_username_fragment,
37       const std::string& ice_password,
38       const std::vector<talk_base::SocketAddress>& stun_hosts,
39       const std::vector<std::string>& relay_hosts,
40       const std::string& relay_token,
41       const pp::InstanceHandle& instance);
42   virtual ~PepperPortAllocatorSession();
43 
44   // cricket::HttpPortAllocatorBase overrides.
45   virtual void ConfigReady(cricket::PortConfiguration* config) OVERRIDE;
46   virtual void GetPortConfigurations() OVERRIDE;
47   virtual void SendSessionRequest(const std::string& host, int port) OVERRIDE;
48 
49  private:
50   void ResolveStunServerAddress();
51   void OnStunAddressResolved(int32_t result);
52 
53   void OnUrlOpened(int32_t result);
54   void ReadResponseBody();
55   void OnResponseBodyRead(int32_t result);
56 
57   pp::InstanceHandle instance_;
58 
59   pp::HostResolver stun_address_resolver_;
60   talk_base::SocketAddress stun_address_;
61   int stun_port_;
62 
63   scoped_ptr<pp::URLLoader> relay_url_loader_;
64   std::vector<char> relay_response_body_;
65   bool relay_response_received_;
66 
67   pp::CompletionCallbackFactory<PepperPortAllocatorSession> callback_factory_;
68 
69   DISALLOW_COPY_AND_ASSIGN(PepperPortAllocatorSession);
70 };
71 
PepperPortAllocatorSession(cricket::HttpPortAllocatorBase * allocator,const std::string & content_name,int component,const std::string & ice_username_fragment,const std::string & ice_password,const std::vector<talk_base::SocketAddress> & stun_hosts,const std::vector<std::string> & relay_hosts,const std::string & relay_token,const pp::InstanceHandle & instance)72 PepperPortAllocatorSession::PepperPortAllocatorSession(
73     cricket::HttpPortAllocatorBase* allocator,
74     const std::string& content_name,
75     int component,
76     const std::string& ice_username_fragment,
77     const std::string& ice_password,
78     const std::vector<talk_base::SocketAddress>& stun_hosts,
79     const std::vector<std::string>& relay_hosts,
80     const std::string& relay_token,
81     const pp::InstanceHandle& instance)
82     : HttpPortAllocatorSessionBase(allocator,
83                                    content_name,
84                                    component,
85                                    ice_username_fragment,
86                                    ice_password,
87                                    stun_hosts,
88                                    relay_hosts,
89                                    relay_token,
90                                    std::string()),
91       instance_(instance),
92       stun_address_resolver_(instance_),
93       stun_port_(0),
94       relay_response_received_(false),
95       callback_factory_(this) {
96   if (stun_hosts.size() > 0) {
97     stun_address_ = stun_hosts[0];
98   }
99 }
100 
~PepperPortAllocatorSession()101 PepperPortAllocatorSession::~PepperPortAllocatorSession() {
102 }
103 
ConfigReady(cricket::PortConfiguration * config)104 void PepperPortAllocatorSession::ConfigReady(
105     cricket::PortConfiguration* config) {
106   if (config->stun_address.IsUnresolved()) {
107     // Make sure that the address that we pass to ConfigReady() is
108     // always resolved.
109     if (stun_address_.IsUnresolved()) {
110       config->stun_address.Clear();
111     } else {
112       config->stun_address = stun_address_;
113     }
114   }
115 
116   // Filter out non-UDP relay ports, so that we don't try using TCP.
117   for (cricket::PortConfiguration::RelayList::iterator relay =
118            config->relays.begin(); relay != config->relays.end(); ++relay) {
119     cricket::PortList filtered_ports;
120     for (cricket::PortList::iterator port =
121              relay->ports.begin(); port != relay->ports.end(); ++port) {
122       if (port->proto == cricket::PROTO_UDP) {
123         filtered_ports.push_back(*port);
124       }
125     }
126     relay->ports = filtered_ports;
127   }
128   cricket::BasicPortAllocatorSession::ConfigReady(config);
129 }
130 
GetPortConfigurations()131 void PepperPortAllocatorSession::GetPortConfigurations() {
132   // Add an empty configuration synchronously, so a local connection
133   // can be started immediately.
134   ConfigReady(new cricket::PortConfiguration(
135       talk_base::SocketAddress(), std::string(), std::string()));
136 
137   ResolveStunServerAddress();
138   TryCreateRelaySession();
139 }
140 
ResolveStunServerAddress()141 void PepperPortAllocatorSession::ResolveStunServerAddress() {
142   if (stun_address_.IsNil()) {
143     return;
144   }
145 
146   if (!stun_address_.IsUnresolved()) {
147     return;
148   }
149 
150   std::string hostname = stun_address_.hostname();
151   uint16 port = stun_address_.port();
152 
153   PP_HostResolver_Hint hint;
154   hint.flags = 0;
155   hint.family = PP_NETADDRESS_FAMILY_IPV4;
156   pp::CompletionCallback callback = callback_factory_.NewCallback(
157       &PepperPortAllocatorSession::OnStunAddressResolved);
158   int result = stun_address_resolver_.Resolve(hostname.c_str(),
159                                               port,
160                                               hint,
161                                               callback);
162   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
163 }
164 
OnStunAddressResolved(int32_t result)165 void PepperPortAllocatorSession::OnStunAddressResolved(int32_t result) {
166   if (result < 0) {
167     LOG(ERROR) << "Failed to resolve stun address "
168                << stun_address_.hostname() << ": " << result;
169     return;
170   }
171 
172   if (!stun_address_resolver_.GetNetAddressCount()) {
173     LOG(WARNING) << "Received 0 addresses for stun server "
174                << stun_address_.hostname();
175     return;
176   }
177 
178   pp::NetAddress address = stun_address_resolver_.GetNetAddress(0);
179   if (address.is_null()) {
180     LOG(ERROR) << "Failed to get address for STUN server "
181                << stun_address_.hostname();
182     return;
183   }
184 
185   PpNetAddressToSocketAddress(address, &stun_address_);
186   DCHECK(!stun_address_.IsUnresolved());
187 
188   if (relay_response_received_) {
189     // If we've finished reading the response, then resubmit it to
190     // HttpPortAllocatorSessionBase. This is necessary because STUN
191     // and Relay parameters are stored together in PortConfiguration
192     // and ReceiveSessionResponse() doesn't save relay session
193     // configuration for the case we resolve STUN address later. This
194     // method invokes overriden ConfigReady() which then submits
195     // resolved |stun_address_|.
196     //
197     // TODO(sergeyu): Refactor HttpPortAllocatorSessionBase to fix this.
198     ReceiveSessionResponse(std::string(relay_response_body_.begin(),
199                                        relay_response_body_.end()));
200   } else {
201     ConfigReady(new cricket::PortConfiguration(
202         stun_address_, std::string(), std::string()));
203   }
204 }
205 
SendSessionRequest(const std::string & host,int port)206 void PepperPortAllocatorSession::SendSessionRequest(
207     const std::string& host,
208     int port) {
209   relay_url_loader_.reset(new pp::URLLoader(instance_));
210   pp::URLRequestInfo request_info(instance_);
211   std::string url = "https://" + host + ":" + base::IntToString(port) +
212       GetSessionRequestUrl() + "&sn=1";
213   request_info.SetURL(url);
214   request_info.SetMethod("GET");
215   std::stringstream headers;
216   headers << "X-Talk-Google-Relay-Auth: " << relay_token() << "\n\r";
217   headers << "X-Google-Relay-Auth: " << relay_token() << "\n\r";
218   headers << "X-Stream-Type: " << "chromoting" << "\n\r";
219   request_info.SetHeaders(headers.str());
220 
221   pp::CompletionCallback callback =
222       callback_factory_.NewCallback(&PepperPortAllocatorSession::OnUrlOpened);
223   int result = relay_url_loader_->Open(request_info, callback);
224   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
225 }
226 
OnUrlOpened(int32_t result)227 void PepperPortAllocatorSession::OnUrlOpened(int32_t result) {
228   if (result == PP_ERROR_ABORTED) {
229     return;
230   }
231 
232   if (result < 0) {
233     LOG(WARNING) << "URLLoader failed: " << result;
234     // Retry creating session.
235     TryCreateRelaySession();
236     return;
237   }
238 
239   pp::URLResponseInfo response = relay_url_loader_->GetResponseInfo();
240   DCHECK(!response.is_null());
241   if (response.GetStatusCode() != 200) {
242     LOG(WARNING) << "Received HTTP status code " << response.GetStatusCode();
243     // Retry creating session.
244     TryCreateRelaySession();
245     return;
246   }
247 
248   relay_response_body_.clear();
249   ReadResponseBody();
250 }
251 
ReadResponseBody()252 void PepperPortAllocatorSession::ReadResponseBody() {
253   int pos = relay_response_body_.size();
254   relay_response_body_.resize(pos + kReadSize);
255   pp::CompletionCallback callback = callback_factory_.NewCallback(
256       &PepperPortAllocatorSession::OnResponseBodyRead);
257   int result = relay_url_loader_->ReadResponseBody(&relay_response_body_[pos],
258                                                    kReadSize,
259                                                    callback);
260   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
261 }
262 
OnResponseBodyRead(int32_t result)263 void PepperPortAllocatorSession::OnResponseBodyRead(int32_t result) {
264   if (result == PP_ERROR_ABORTED) {
265     return;
266   }
267 
268   if (result < 0) {
269     LOG(WARNING) << "Failed to read HTTP response body when "
270         "creating relay session: " << result;
271     // Retry creating session.
272     TryCreateRelaySession();
273     return;
274   }
275 
276   // Resize the buffer in case we've read less than was requested.
277   CHECK_LE(result, kReadSize);
278   CHECK_GE(static_cast<int>(relay_response_body_.size()), kReadSize);
279   relay_response_body_.resize(relay_response_body_.size() - kReadSize + result);
280 
281   if (result == 0) {
282     relay_response_received_ = true;
283     ReceiveSessionResponse(std::string(relay_response_body_.begin(),
284                                        relay_response_body_.end()));
285     return;
286   }
287 
288   ReadResponseBody();
289 }
290 
291 }  // namespace
292 
293 // static
Create(const pp::InstanceHandle & instance)294 scoped_ptr<PepperPortAllocator> PepperPortAllocator::Create(
295     const pp::InstanceHandle& instance) {
296   scoped_ptr<talk_base::NetworkManager> network_manager(
297       new PepperNetworkManager(instance));
298   scoped_ptr<talk_base::PacketSocketFactory> socket_factory(
299       new PepperPacketSocketFactory(instance));
300   scoped_ptr<PepperPortAllocator> result(new PepperPortAllocator(
301       instance, network_manager.Pass(), socket_factory.Pass()));
302   return result.Pass();
303 }
304 
PepperPortAllocator(const pp::InstanceHandle & instance,scoped_ptr<talk_base::NetworkManager> network_manager,scoped_ptr<talk_base::PacketSocketFactory> socket_factory)305 PepperPortAllocator::PepperPortAllocator(
306     const pp::InstanceHandle& instance,
307     scoped_ptr<talk_base::NetworkManager> network_manager,
308     scoped_ptr<talk_base::PacketSocketFactory> socket_factory)
309     : HttpPortAllocatorBase(network_manager.get(),
310                             socket_factory.get(),
311                             std::string()),
312       instance_(instance),
313       network_manager_(network_manager.Pass()),
314       socket_factory_(socket_factory.Pass()) {
315   // TCP transport is disabled becase PseudoTCP works poorly over
316   // it. ENABLE_SHARED_UFRAG flag is specified so that the same
317   // username fragment is shared between all candidates for this
318   // channel.
319   set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
320             cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG|
321             cricket::PORTALLOCATOR_ENABLE_IPV6);
322 }
323 
~PepperPortAllocator()324 PepperPortAllocator::~PepperPortAllocator() {
325 }
326 
CreateSessionInternal(const std::string & content_name,int component,const std::string & ice_username_fragment,const std::string & ice_password)327 cricket::PortAllocatorSession* PepperPortAllocator::CreateSessionInternal(
328     const std::string& content_name,
329     int component,
330     const std::string& ice_username_fragment,
331     const std::string& ice_password) {
332    return new PepperPortAllocatorSession(
333        this, content_name, component, ice_username_fragment, ice_password,
334        stun_hosts(), relay_hosts(), relay_token(), instance_);
335 }
336 
337 }  // namespace remoting
338