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