• 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_packet_socket_factory.h"
6 
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "net/base/io_buffer.h"
10 #include "ppapi/cpp/net_address.h"
11 #include "ppapi/cpp/udp_socket.h"
12 #include "ppapi/utility/completion_callback_factory.h"
13 #include "remoting/client/plugin/pepper_util.h"
14 #include "third_party/libjingle/source/talk/base/asyncpacketsocket.h"
15 
16 namespace remoting {
17 
18 namespace {
19 
20 // Size of the buffer to allocate for RecvFrom().
21 const int kReceiveBufferSize = 65536;
22 
23 // Maximum amount of data in the send buffers. This is necessary to
24 // prevent out-of-memory crashes if the caller sends data faster than
25 // Pepper's UDP API can handle it. This maximum should never be
26 // reached under normal conditions.
27 const int kMaxSendBufferSize = 256 * 1024;
28 
29 class UdpPacketSocket : public talk_base::AsyncPacketSocket {
30  public:
31   explicit UdpPacketSocket(const pp::InstanceHandle& instance);
32   virtual ~UdpPacketSocket();
33 
34   // |min_port| and |max_port| are set to zero if the port number
35   // should be assigned by the OS.
36   bool Init(const talk_base::SocketAddress& local_address,
37             int min_port,
38             int max_port);
39 
40   // talk_base::AsyncPacketSocket interface.
41   virtual talk_base::SocketAddress GetLocalAddress() const OVERRIDE;
42   virtual talk_base::SocketAddress GetRemoteAddress() const OVERRIDE;
43   virtual int Send(const void* data, size_t data_size,
44                    talk_base::DiffServCodePoint dscp) OVERRIDE;
45   virtual int SendTo(const void* data,
46                      size_t data_size,
47                      const talk_base::SocketAddress& address,
48                      talk_base::DiffServCodePoint dscp) OVERRIDE;
49   virtual int Close() OVERRIDE;
50   virtual State GetState() const OVERRIDE;
51   virtual int GetOption(talk_base::Socket::Option opt, int* value) OVERRIDE;
52   virtual int SetOption(talk_base::Socket::Option opt, int value) OVERRIDE;
53   virtual int GetError() const OVERRIDE;
54   virtual void SetError(int error) OVERRIDE;
55 
56  private:
57   struct PendingPacket {
58     PendingPacket(const void* buffer,
59                   int buffer_size,
60                   const pp::NetAddress& address);
61 
62     scoped_refptr<net::IOBufferWithSize> data;
63     pp::NetAddress address;
64   };
65 
66   void OnBindCompleted(int error);
67 
68   void DoSend();
69   void OnSendCompleted(int result);
70 
71   void DoRead();
72   void OnReadCompleted(int result, pp::NetAddress address);
73   void HandleReadResult(int result, pp::NetAddress address);
74 
75   pp::InstanceHandle instance_;
76 
77   pp::UDPSocket socket_;
78 
79   State state_;
80   int error_;
81 
82   talk_base::SocketAddress local_address_;
83 
84   // Used to scan ports when necessary. Both values are set to 0 when
85   // the port number is assigned by OS.
86   uint16_t min_port_;
87   uint16_t max_port_;
88 
89   std::vector<char> receive_buffer_;
90 
91   bool send_pending_;
92   std::list<PendingPacket> send_queue_;
93   int send_queue_size_;
94 
95   pp::CompletionCallbackFactory<UdpPacketSocket> callback_factory_;
96 
97   DISALLOW_COPY_AND_ASSIGN(UdpPacketSocket);
98 };
99 
PendingPacket(const void * buffer,int buffer_size,const pp::NetAddress & address)100 UdpPacketSocket::PendingPacket::PendingPacket(
101     const void* buffer,
102     int buffer_size,
103     const pp::NetAddress& address)
104     : data(new net::IOBufferWithSize(buffer_size)),
105       address(address) {
106   memcpy(data->data(), buffer, buffer_size);
107 }
108 
UdpPacketSocket(const pp::InstanceHandle & instance)109 UdpPacketSocket::UdpPacketSocket(const pp::InstanceHandle& instance)
110     : instance_(instance),
111       socket_(instance),
112       state_(STATE_CLOSED),
113       error_(0),
114       min_port_(0),
115       max_port_(0),
116       send_pending_(false),
117       send_queue_size_(0),
118       callback_factory_(this) {
119 }
120 
~UdpPacketSocket()121 UdpPacketSocket::~UdpPacketSocket() {
122   Close();
123 }
124 
Init(const talk_base::SocketAddress & local_address,int min_port,int max_port)125 bool UdpPacketSocket::Init(const talk_base::SocketAddress& local_address,
126                            int min_port,
127                            int max_port) {
128   if (socket_.is_null()) {
129     return false;
130   }
131 
132   local_address_ = local_address;
133   max_port_ = max_port;
134   min_port_ = min_port;
135 
136   pp::NetAddress pp_local_address;
137   if (!SocketAddressToPpNetAddressWithPort(
138           instance_, local_address_, &pp_local_address, min_port_)) {
139     return false;
140   }
141 
142   pp::CompletionCallback callback =
143       callback_factory_.NewCallback(&UdpPacketSocket::OnBindCompleted);
144   int result = socket_.Bind(pp_local_address, callback);
145   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
146   state_ = STATE_BINDING;
147 
148   return true;
149 }
150 
OnBindCompleted(int result)151 void UdpPacketSocket::OnBindCompleted(int result) {
152   DCHECK(state_ == STATE_BINDING || state_ == STATE_CLOSED);
153 
154   if (result == PP_ERROR_ABORTED) {
155     // Socket is being destroyed while binding.
156     return;
157   }
158 
159   if (result == PP_OK) {
160     pp::NetAddress address = socket_.GetBoundAddress();
161     PpNetAddressToSocketAddress(address, &local_address_);
162     state_ = STATE_BOUND;
163     SignalAddressReady(this, local_address_);
164     DoRead();
165     return;
166   }
167 
168   if (min_port_ < max_port_) {
169     // Try to bind to the next available port.
170     ++min_port_;
171     pp::NetAddress pp_local_address;
172     if (SocketAddressToPpNetAddressWithPort(
173             instance_, local_address_, &pp_local_address, min_port_)) {
174       pp::CompletionCallback callback =
175           callback_factory_.NewCallback(&UdpPacketSocket::OnBindCompleted);
176       int result = socket_.Bind(pp_local_address, callback);
177       DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
178     }
179   } else {
180     LOG(ERROR) << "Failed to bind UDP socket: " << result;
181   }
182 }
183 
GetLocalAddress() const184 talk_base::SocketAddress UdpPacketSocket::GetLocalAddress() const {
185   DCHECK_EQ(state_, STATE_BOUND);
186   return local_address_;
187 }
188 
GetRemoteAddress() const189 talk_base::SocketAddress UdpPacketSocket::GetRemoteAddress() const {
190   // UDP sockets are not connected - this method should never be called.
191   NOTREACHED();
192   return talk_base::SocketAddress();
193 }
194 
Send(const void * data,size_t data_size,talk_base::DiffServCodePoint dscp)195 int UdpPacketSocket::Send(const void* data, size_t data_size,
196                           talk_base::DiffServCodePoint dscp) {
197   // UDP sockets are not connected - this method should never be called.
198   NOTREACHED();
199   return EWOULDBLOCK;
200 }
201 
SendTo(const void * data,size_t data_size,const talk_base::SocketAddress & address,talk_base::DiffServCodePoint dscp)202 int UdpPacketSocket::SendTo(const void* data,
203                             size_t data_size,
204                             const talk_base::SocketAddress& address,
205                             talk_base::DiffServCodePoint dscp) {
206   if (state_ != STATE_BOUND) {
207     // TODO(sergeyu): StunPort may try to send stun request before we
208     // are bound. Fix that problem and change this to DCHECK.
209     return EINVAL;
210   }
211 
212   if (error_ != 0) {
213     return error_;
214   }
215 
216   pp::NetAddress pp_address;
217   if (!SocketAddressToPpNetAddress(instance_, address, &pp_address)) {
218     return EINVAL;
219   }
220 
221   if (send_queue_size_ >= kMaxSendBufferSize) {
222     return EWOULDBLOCK;
223   }
224 
225   send_queue_.push_back(PendingPacket(data, data_size, pp_address));
226   send_queue_size_ += data_size;
227   DoSend();
228   return data_size;
229 }
230 
Close()231 int UdpPacketSocket::Close() {
232   state_ = STATE_CLOSED;
233   socket_.Close();
234   return 0;
235 }
236 
GetState() const237 talk_base::AsyncPacketSocket::State UdpPacketSocket::GetState() const {
238   return state_;
239 }
240 
GetOption(talk_base::Socket::Option opt,int * value)241 int UdpPacketSocket::GetOption(talk_base::Socket::Option opt, int* value) {
242   // Options are not supported for Pepper UDP sockets.
243   return -1;
244 }
245 
SetOption(talk_base::Socket::Option opt,int value)246 int UdpPacketSocket::SetOption(talk_base::Socket::Option opt, int value) {
247   // Options are not supported for Pepper UDP sockets.
248   return -1;
249 }
250 
GetError() const251 int UdpPacketSocket::GetError() const {
252   return error_;
253 }
254 
SetError(int error)255 void UdpPacketSocket::SetError(int error) {
256   error_ = error;
257 }
258 
DoSend()259 void UdpPacketSocket::DoSend() {
260   if (send_pending_ || send_queue_.empty())
261     return;
262 
263   pp::CompletionCallback callback =
264       callback_factory_.NewCallback(&UdpPacketSocket::OnSendCompleted);
265   int result = socket_.SendTo(
266       send_queue_.front().data->data(), send_queue_.front().data->size(),
267       send_queue_.front().address,
268       callback);
269   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
270   send_pending_ = true;
271 }
272 
OnSendCompleted(int result)273 void UdpPacketSocket::OnSendCompleted(int result) {
274   if (result == PP_ERROR_ABORTED) {
275     // Send is aborted when the socket is being destroyed.
276     // |send_queue_| may be already destroyed, it's not safe to access
277     // it here.
278     return;
279   }
280 
281   send_pending_ = false;
282 
283   if (result < 0) {
284     LOG(ERROR) << "Send failed on a UDP socket: " << result;
285 
286     // OS (e.g. OSX) may return EHOSTUNREACH when the peer has the
287     // same subnet address as the local host but connected to a
288     // different network. That error must be ingored because the
289     // socket may still be useful for other ICE canidadates (e.g. for
290     // STUN candidates with a different address). Unfortunately pepper
291     // interface currently returns PP_ERROR_FAILED for any error (see
292     // crbug.com/136406). It's not possible to distinguish that case
293     // from other errors and so we have to ingore all of them. This
294     // behavior matchers the libjingle's AsyncUDPSocket used by the
295     // host.
296     //
297     // TODO(sergeyu): Once implementation of the Pepper UDP interface
298     // is fixed, uncomment the code below, but ignore
299     // host-unreacheable error.
300 
301     // error_ = EINVAL;
302     // return;
303   }
304 
305   send_queue_size_ -= send_queue_.front().data->size();
306   send_queue_.pop_front();
307   DoSend();
308 }
309 
DoRead()310 void UdpPacketSocket::DoRead() {
311   receive_buffer_.resize(kReceiveBufferSize);
312   pp::CompletionCallbackWithOutput<pp::NetAddress> callback =
313       callback_factory_.NewCallbackWithOutput(
314           &UdpPacketSocket::OnReadCompleted);
315   int result =
316       socket_.RecvFrom(&receive_buffer_[0], receive_buffer_.size(), callback);
317   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
318 }
319 
OnReadCompleted(int result,pp::NetAddress address)320 void UdpPacketSocket::OnReadCompleted(int result, pp::NetAddress address) {
321   HandleReadResult(result, address);
322   if (result > 0) {
323     DoRead();
324   }
325 }
326 
HandleReadResult(int result,pp::NetAddress address)327   void UdpPacketSocket::HandleReadResult(int result, pp::NetAddress address) {
328   if (result > 0) {
329     talk_base::SocketAddress socket_address;
330     PpNetAddressToSocketAddress(address, &socket_address);
331     SignalReadPacket(this, &receive_buffer_[0], result, socket_address,
332                      talk_base::CreatePacketTime(0));
333   } else if (result != PP_ERROR_ABORTED) {
334     LOG(ERROR) << "Received error when reading from UDP socket: " << result;
335   }
336 }
337 
338 }  // namespace
339 
PepperPacketSocketFactory(const pp::InstanceHandle & instance)340 PepperPacketSocketFactory::PepperPacketSocketFactory(
341     const pp::InstanceHandle& instance)
342     : pp_instance_(instance) {
343 }
344 
~PepperPacketSocketFactory()345 PepperPacketSocketFactory::~PepperPacketSocketFactory() {
346 }
347 
CreateUdpSocket(const talk_base::SocketAddress & local_address,int min_port,int max_port)348 talk_base::AsyncPacketSocket* PepperPacketSocketFactory::CreateUdpSocket(
349       const talk_base::SocketAddress& local_address,
350       int min_port,
351       int max_port) {
352   scoped_ptr<UdpPacketSocket> result(new UdpPacketSocket(pp_instance_));
353   if (!result->Init(local_address, min_port, max_port))
354     return NULL;
355   return result.release();
356 }
357 
CreateServerTcpSocket(const talk_base::SocketAddress & local_address,int min_port,int max_port,int opts)358 talk_base::AsyncPacketSocket* PepperPacketSocketFactory::CreateServerTcpSocket(
359     const talk_base::SocketAddress& local_address,
360     int min_port,
361     int max_port,
362     int opts) {
363   // We don't use TCP sockets for remoting connections.
364   NOTREACHED();
365   return NULL;
366 }
367 
CreateClientTcpSocket(const talk_base::SocketAddress & local_address,const talk_base::SocketAddress & remote_address,const talk_base::ProxyInfo & proxy_info,const std::string & user_agent,int opts)368 talk_base::AsyncPacketSocket* PepperPacketSocketFactory::CreateClientTcpSocket(
369       const talk_base::SocketAddress& local_address,
370       const talk_base::SocketAddress& remote_address,
371       const talk_base::ProxyInfo& proxy_info,
372       const std::string& user_agent,
373       int opts) {
374   // We don't use TCP sockets for remoting connections.
375   NOTREACHED();
376   return NULL;
377 }
378 
379 talk_base::AsyncResolverInterface*
CreateAsyncResolver()380 PepperPacketSocketFactory::CreateAsyncResolver() {
381   NOTREACHED();
382   return NULL;
383 }
384 
385 }  // namespace remoting
386