Home | History | Annotate | Download | only in proxy
      1 // Copyright 2013 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 "ppapi/proxy/udp_socket_resource_base.h"
      6 
      7 #include <algorithm>
      8 #include <cstring>
      9 
     10 #include "base/logging.h"
     11 #include "ppapi/c/pp_bool.h"
     12 #include "ppapi/c/pp_completion_callback.h"
     13 #include "ppapi/c/pp_errors.h"
     14 #include "ppapi/proxy/error_conversion.h"
     15 #include "ppapi/proxy/ppapi_messages.h"
     16 #include "ppapi/shared_impl/socket_option_data.h"
     17 #include "ppapi/thunk/enter.h"
     18 #include "ppapi/thunk/resource_creation_api.h"
     19 
     20 namespace ppapi {
     21 namespace proxy {
     22 
     23 const int32_t UDPSocketResourceBase::kMaxReadSize = 1024 * 1024;
     24 const int32_t UDPSocketResourceBase::kMaxWriteSize = 1024 * 1024;
     25 const int32_t UDPSocketResourceBase::kMaxSendBufferSize =
     26     1024 * UDPSocketResourceBase::kMaxWriteSize;
     27 const int32_t UDPSocketResourceBase::kMaxReceiveBufferSize =
     28     1024 * UDPSocketResourceBase::kMaxReadSize;
     29 
     30 
     31 UDPSocketResourceBase::UDPSocketResourceBase(Connection connection,
     32                                              PP_Instance instance,
     33                                              bool private_api)
     34     : PluginResource(connection, instance),
     35       private_api_(private_api),
     36       bound_(false),
     37       closed_(false),
     38       read_buffer_(NULL),
     39       bytes_to_read_(-1) {
     40   recvfrom_addr_.size = 0;
     41   memset(recvfrom_addr_.data, 0,
     42          arraysize(recvfrom_addr_.data) * sizeof(*recvfrom_addr_.data));
     43   bound_addr_.size = 0;
     44   memset(bound_addr_.data, 0,
     45          arraysize(bound_addr_.data) * sizeof(*bound_addr_.data));
     46 
     47   if (private_api)
     48     SendCreate(BROWSER, PpapiHostMsg_UDPSocket_CreatePrivate());
     49   else
     50     SendCreate(BROWSER, PpapiHostMsg_UDPSocket_Create());
     51 }
     52 
     53 UDPSocketResourceBase::~UDPSocketResourceBase() {
     54 }
     55 
     56 int32_t UDPSocketResourceBase::SetOptionImpl(
     57     PP_UDPSocket_Option name,
     58     const PP_Var& value,
     59     scoped_refptr<TrackedCallback> callback) {
     60   if (closed_)
     61     return PP_ERROR_FAILED;
     62 
     63   SocketOptionData option_data;
     64   switch (name) {
     65     case PP_UDPSOCKET_OPTION_ADDRESS_REUSE:
     66     case PP_UDPSOCKET_OPTION_BROADCAST: {
     67       if (bound_)
     68         return PP_ERROR_FAILED;
     69       if (value.type != PP_VARTYPE_BOOL)
     70         return PP_ERROR_BADARGUMENT;
     71       option_data.SetBool(PP_ToBool(value.value.as_bool));
     72       break;
     73     }
     74     case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE:
     75     case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
     76       if (!bound_)
     77         return PP_ERROR_FAILED;
     78       if (value.type != PP_VARTYPE_INT32)
     79         return PP_ERROR_BADARGUMENT;
     80       option_data.SetInt32(value.value.as_int);
     81       break;
     82     }
     83     default: {
     84       NOTREACHED();
     85       return PP_ERROR_BADARGUMENT;
     86     }
     87   }
     88 
     89   Call<PpapiPluginMsg_UDPSocket_SetOptionReply>(
     90       BROWSER,
     91       PpapiHostMsg_UDPSocket_SetOption(name, option_data),
     92       base::Bind(&UDPSocketResourceBase::OnPluginMsgSetOptionReply,
     93                  base::Unretained(this),
     94                  callback),
     95       callback);
     96   return PP_OK_COMPLETIONPENDING;
     97 }
     98 
     99 int32_t UDPSocketResourceBase::BindImpl(
    100     const PP_NetAddress_Private* addr,
    101     scoped_refptr<TrackedCallback> callback) {
    102   if (!addr)
    103     return PP_ERROR_BADARGUMENT;
    104   if (bound_ || closed_)
    105     return PP_ERROR_FAILED;
    106   if (TrackedCallback::IsPending(bind_callback_))
    107     return PP_ERROR_INPROGRESS;
    108 
    109   bind_callback_ = callback;
    110 
    111   // Send the request, the browser will call us back via BindReply.
    112   Call<PpapiPluginMsg_UDPSocket_BindReply>(
    113       BROWSER,
    114       PpapiHostMsg_UDPSocket_Bind(*addr),
    115       base::Bind(&UDPSocketResourceBase::OnPluginMsgBindReply,
    116                  base::Unretained(this)),
    117       callback);
    118   return PP_OK_COMPLETIONPENDING;
    119 }
    120 
    121 PP_Bool UDPSocketResourceBase::GetBoundAddressImpl(
    122     PP_NetAddress_Private* addr) {
    123   if (!addr || !bound_ || closed_)
    124     return PP_FALSE;
    125 
    126   *addr = bound_addr_;
    127   return PP_TRUE;
    128 }
    129 
    130 int32_t UDPSocketResourceBase::RecvFromImpl(
    131     char* buffer,
    132     int32_t num_bytes,
    133     PP_Resource* addr,
    134     scoped_refptr<TrackedCallback> callback) {
    135   if (!buffer || num_bytes <= 0)
    136     return PP_ERROR_BADARGUMENT;
    137   if (!bound_)
    138     return PP_ERROR_FAILED;
    139   if (TrackedCallback::IsPending(recvfrom_callback_))
    140     return PP_ERROR_INPROGRESS;
    141 
    142   read_buffer_ = buffer;
    143   bytes_to_read_ = std::min(num_bytes, kMaxReadSize);
    144   recvfrom_callback_ = callback;
    145 
    146   // Send the request, the browser will call us back via RecvFromReply.
    147   Call<PpapiPluginMsg_UDPSocket_RecvFromReply>(
    148       BROWSER,
    149       PpapiHostMsg_UDPSocket_RecvFrom(bytes_to_read_),
    150       base::Bind(&UDPSocketResourceBase::OnPluginMsgRecvFromReply,
    151                  base::Unretained(this), addr),
    152       callback);
    153   return PP_OK_COMPLETIONPENDING;
    154 }
    155 
    156 PP_Bool UDPSocketResourceBase::GetRecvFromAddressImpl(
    157     PP_NetAddress_Private* addr) {
    158   if (!addr)
    159     return PP_FALSE;
    160   *addr = recvfrom_addr_;
    161   return PP_TRUE;
    162 }
    163 
    164 int32_t UDPSocketResourceBase::SendToImpl(
    165     const char* buffer,
    166     int32_t num_bytes,
    167     const PP_NetAddress_Private* addr,
    168     scoped_refptr<TrackedCallback> callback) {
    169   if (!buffer || num_bytes <= 0 || !addr)
    170     return PP_ERROR_BADARGUMENT;
    171   if (!bound_)
    172     return PP_ERROR_FAILED;
    173   if (TrackedCallback::IsPending(sendto_callback_))
    174     return PP_ERROR_INPROGRESS;
    175 
    176   if (num_bytes > kMaxWriteSize)
    177     num_bytes = kMaxWriteSize;
    178 
    179   sendto_callback_ = callback;
    180 
    181   // Send the request, the browser will call us back via SendToReply.
    182   Call<PpapiPluginMsg_UDPSocket_SendToReply>(
    183       BROWSER,
    184       PpapiHostMsg_UDPSocket_SendTo(std::string(buffer, num_bytes), *addr),
    185       base::Bind(&UDPSocketResourceBase::OnPluginMsgSendToReply,
    186                  base::Unretained(this)),
    187       callback);
    188   return PP_OK_COMPLETIONPENDING;
    189 }
    190 
    191 void UDPSocketResourceBase::CloseImpl() {
    192   if(closed_)
    193     return;
    194 
    195   bound_ = false;
    196   closed_ = true;
    197 
    198   Post(BROWSER, PpapiHostMsg_UDPSocket_Close());
    199 
    200   PostAbortIfNecessary(&bind_callback_);
    201   PostAbortIfNecessary(&recvfrom_callback_);
    202   PostAbortIfNecessary(&sendto_callback_);
    203 
    204   read_buffer_ = NULL;
    205   bytes_to_read_ = -1;
    206 }
    207 
    208 void UDPSocketResourceBase::PostAbortIfNecessary(
    209     scoped_refptr<TrackedCallback>* callback) {
    210   if (TrackedCallback::IsPending(*callback))
    211     (*callback)->PostAbort();
    212 }
    213 
    214 void UDPSocketResourceBase::OnPluginMsgSetOptionReply(
    215     scoped_refptr<TrackedCallback> callback,
    216     const ResourceMessageReplyParams& params) {
    217   if (TrackedCallback::IsPending(callback))
    218     RunCallback(callback, params.result());
    219 }
    220 
    221 void UDPSocketResourceBase::OnPluginMsgBindReply(
    222     const ResourceMessageReplyParams& params,
    223     const PP_NetAddress_Private& bound_addr) {
    224   // It is possible that |bind_callback_| is pending while |closed_| is true:
    225   // CloseImpl() has been called, but a BindReply came earlier than the task to
    226   // abort |bind_callback_|. We don't want to update |bound_| or |bound_addr_|
    227   // in that case.
    228   if (!TrackedCallback::IsPending(bind_callback_) || closed_)
    229     return;
    230 
    231   if (params.result() == PP_OK)
    232     bound_ = true;
    233   bound_addr_ = bound_addr;
    234   RunCallback(bind_callback_, params.result());
    235 }
    236 
    237 void UDPSocketResourceBase::OnPluginMsgRecvFromReply(
    238     PP_Resource* output_addr,
    239     const ResourceMessageReplyParams& params,
    240     const std::string& data,
    241     const PP_NetAddress_Private& addr) {
    242   // It is possible that |recvfrom_callback_| is pending while |read_buffer_| is
    243   // NULL: CloseImpl() has been called, but a RecvFromReply came earlier than
    244   // the task to abort |recvfrom_callback_|. We shouldn't access the buffer in
    245   // that case. The user may have released it.
    246   if (!TrackedCallback::IsPending(recvfrom_callback_) || !read_buffer_)
    247     return;
    248 
    249   int32_t result = params.result();
    250   if (result == PP_OK && output_addr) {
    251     thunk::EnterResourceCreationNoLock enter(pp_instance());
    252     if (enter.succeeded()) {
    253       *output_addr = enter.functions()->CreateNetAddressFromNetAddressPrivate(
    254           pp_instance(), addr);
    255     } else {
    256       result = PP_ERROR_FAILED;
    257     }
    258   }
    259 
    260   if (result == PP_OK) {
    261     CHECK_LE(static_cast<int32_t>(data.size()), bytes_to_read_);
    262     if (!data.empty())
    263       memcpy(read_buffer_, data.c_str(), data.size());
    264   }
    265 
    266   read_buffer_ = NULL;
    267   bytes_to_read_ = -1;
    268   recvfrom_addr_ = addr;
    269 
    270   if (result == PP_OK)
    271     RunCallback(recvfrom_callback_, static_cast<int32_t>(data.size()));
    272   else
    273     RunCallback(recvfrom_callback_, result);
    274 }
    275 
    276 void UDPSocketResourceBase::OnPluginMsgSendToReply(
    277     const ResourceMessageReplyParams& params,
    278     int32_t bytes_written) {
    279   if (!TrackedCallback::IsPending(sendto_callback_))
    280     return;
    281 
    282   if (params.result() == PP_OK)
    283     RunCallback(sendto_callback_, bytes_written);
    284   else
    285     RunCallback(sendto_callback_, params.result());
    286 }
    287 
    288 void UDPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback,
    289                                         int32_t pp_result) {
    290   callback->Run(ConvertNetworkAPIErrorForCompatibility(pp_result,
    291                                                        private_api_));
    292 }
    293 
    294 }  // namespace proxy
    295 }  // namespace ppapi
    296