text: // Copyright 2013 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h"
  
  #include <cstring>
  #include <utility>
  
  #include "base/compiler_specific.h"
  #include "base/logging.h"
  #include "base/metrics/histogram_macros.h"
  #include "build/build_config.h"
  #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
  #include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
  #include "content/public/browser/browser_thread.h"
  #include "content/public/browser/content_browser_client.h"
  #include "content/public/common/process_type.h"
  #include "content/public/common/socket_permission_request.h"
  #include "ipc/ipc_message_macros.h"
  #include "net/base/io_buffer.h"
  #include "net/base/net_errors.h"
  #include "net/base/rand_callback.h"
  #include "net/udp/udp_socket.h"
  #include "ppapi/c/pp_errors.h"
  #include "ppapi/c/private/ppb_net_address_private.h"
  #include "ppapi/host/dispatch_host_message.h"
  #include "ppapi/host/error_conversion.h"
  #include "ppapi/host/host_message_context.h"
  #include "ppapi/host/ppapi_host.h"
  #include "ppapi/host/resource_host.h"
  #include "ppapi/proxy/ppapi_messages.h"
  #include "ppapi/proxy/udp_socket_filter.h"
  #include "ppapi/proxy/udp_socket_resource_base.h"
  #include "ppapi/shared_impl/private/net_address_private_impl.h"
  #include "ppapi/shared_impl/socket_option_data.h"
  
  #if defined(OS_CHROMEOS)
  #include "chromeos/network/firewall_hole.h"
  #endif  // defined(OS_CHROMEOS)
  
  using ppapi::NetAddressPrivateImpl;
  using ppapi::host::NetErrorToPepperError;
  using ppapi::proxy::UDPSocketFilter;
  using ppapi::proxy::UDPSocketResourceBase;
  
  namespace {
  
  size_t g_num_instances = 0;
  
  }  // namespace
  
  namespace content {
  
  PepperUDPSocketMessageFilter::PendingSend::PendingSend(
      const net::IPAddressNumber& address,
      int port,
      const scoped_refptr<net::IOBufferWithSize>& buffer,
      const ppapi::host::ReplyMessageContext& context)
      : address(address), port(port), buffer(buffer), context(context) {
  }
  
  PepperUDPSocketMessageFilter::PendingSend::~PendingSend() {
  }
  
  PepperUDPSocketMessageFilter::PepperUDPSocketMessageFilter(
      BrowserPpapiHostImpl* host,
      PP_Instance instance,
      bool private_api)
      : socket_options_(0),
        rcvbuf_size_(0),
        sndbuf_size_(0),
        multicast_ttl_(0),
        can_use_multicast_(PP_ERROR_FAILED),
        closed_(false),
        remaining_recv_slots_(UDPSocketFilter::kPluginReceiveBufferSlots),
        external_plugin_(host->external_plugin()),
        private_api_(private_api),
        render_process_id_(0),
        render_frame_id_(0),
        is_potentially_secure_plugin_context_(
            host->IsPotentiallySecurePluginContext(instance)) {
    ++g_num_instances;
    DCHECK(host);
  
    if (!host->GetRenderFrameIDsForInstance(
            instance, &render_process_id_, &render_frame_id_)) {
      NOTREACHED();
    }
  }
  
  PepperUDPSocketMessageFilter::~PepperUDPSocketMessageFilter() {
    Close();
    --g_num_instances;
  }
  
  // static
  size_t PepperUDPSocketMessageFilter::GetNumInstances() {
    return g_num_instances;
  }
  
  scoped_refptr<base::TaskRunner>
  PepperUDPSocketMessageFilter::OverrideTaskRunnerForMessage(
      const IPC::Message& message) {
    switch (message.type()) {
      case PpapiHostMsg_UDPSocket_SetOption::ID:
      case PpapiHostMsg_UDPSocket_Close::ID:
      case PpapiHostMsg_UDPSocket_RecvSlotAvailable::ID:
        return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
      case PpapiHostMsg_UDPSocket_Bind::ID:
      case PpapiHostMsg_UDPSocket_SendTo::ID:
      case PpapiHostMsg_UDPSocket_JoinGroup::ID:
      case PpapiHostMsg_UDPSocket_LeaveGroup::ID:
        return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
    }
    return NULL;
  }
  
  int32_t PepperUDPSocketMessageFilter::OnResourceMessageReceived(
      const IPC::Message& msg,
      ppapi::host::HostMessageContext* context) {
    PPAPI_BEGIN_MESSAGE_MAP(PepperUDPSocketMessageFilter, msg)
      PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_SetOption,
                                        OnMsgSetOption)
      PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_Bind, OnMsgBind)
      PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_SendTo,
                                        OnMsgSendTo)
      PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_UDPSocket_Close,
                                          OnMsgClose)
      PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
          PpapiHostMsg_UDPSocket_RecvSlotAvailable, OnMsgRecvSlotAvailable)
      PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_JoinGroup,
                                        OnMsgJoinGroup)
      PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_LeaveGroup,
                                        OnMsgLeaveGroup)
    PPAPI_END_MESSAGE_MAP()
    return PP_ERROR_FAILED;
  }
  
  int32_t PepperUDPSocketMessageFilter::OnMsgSetOption(
      const ppapi::host::HostMessageContext* context,
      PP_UDPSocket_Option name,
      const ppapi::SocketOptionData& value) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
  
    if (closed_)
      return PP_ERROR_FAILED;
  
    switch (name) {
      case PP_UDPSOCKET_OPTION_ADDRESS_REUSE: {
        if (socket_.get()) {
          // AllowReuseAddress is only effective before Bind().
          // Note that this limitation originally comes from Windows, but
          // PPAPI tries to provide platform independent APIs.
          return PP_ERROR_FAILED;
        }
  
        bool boolean_value = false;
        if (!value.GetBool(&boolean_value))
          return PP_ERROR_BADARGUMENT;
  
        if (boolean_value) {
          socket_options_ |= SOCKET_OPTION_ADDRESS_REUSE;
        } else {
          socket_options_ &= ~SOCKET_OPTION_ADDRESS_REUSE;
        }
        return PP_OK;
      }
      case PP_UDPSOCKET_OPTION_BROADCAST: {
        bool boolean_value = false;
        if (!value.GetBool(&boolean_value))
          return PP_ERROR_BADARGUMENT;
  
        // If the socket is already bound, proxy the value to UDPSocket.
        if (socket_.get())
          return NetErrorToPepperError(socket_->SetBroadcast(boolean_value));
  
        // UDPSocket instance is not yet created, so remember the value here.
        if (boolean_value) {
          socket_options_ |= SOCKET_OPTION_BROADCAST;
        } else {
          socket_options_ &= ~SOCKET_OPTION_BROADCAST;
        }
        return PP_OK;
      }
      case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE: {
        int32_t integer_value = 0;
        if (!value.GetInt32(&integer_value) || integer_value <= 0 ||
            integer_value > UDPSocketResourceBase::kMaxSendBufferSize)
          return PP_ERROR_BADARGUMENT;
  
        // If the socket is already bound, proxy the value to UDPSocket.
        if (socket_.get()) {
          return NetErrorToPepperError(
              socket_->SetSendBufferSize(integer_value));
        }
  
        // UDPSocket instance is not yet created, so remember the value here.
        socket_options_ |= SOCKET_OPTION_SNDBUF_SIZE;
        sndbuf_size_ = integer_value;
        return PP_OK;
      }
      case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
        int32_t integer_value = 0;
        if (!value.GetInt32(&integer_value) || integer_value <= 0 ||
            integer_value > UDPSocketFilter::kMaxReceiveBufferSize)
          return PP_ERROR_BADARGUMENT;
  
        // If the socket is already bound, proxy the value to UDPSocket.
        if (socket_.get()) {
          return NetErrorToPepperError(
              socket_->SetReceiveBufferSize(integer_value));
        }
  
        // UDPSocket instance is not yet created, so remember the value here.
        socket_options_ |= SOCKET_OPTION_RCVBUF_SIZE;
        rcvbuf_size_ = integer_value;
        return PP_OK;
      }
      case PP_UDPSOCKET_OPTION_MULTICAST_LOOP: {
        bool boolean_value = false;
        if (!value.GetBool(&boolean_value))
          return PP_ERROR_BADARGUMENT;
  
        // If the socket is already bound, proxy the value to UDPSocket.
        if (socket_) {
          if (can_use_multicast_ != PP_OK)
            return can_use_multicast_;
  
          return NetErrorToPepperError(
              socket_->SetMulticastLoopbackMode(boolean_value));
        }
  
        // UDPSocket instance is not yet created, so remember the value here.
        if (boolean_value) {
          socket_options_ |= SOCKET_OPTION_MULTICAST_LOOP;
        } else {
          socket_options_ &= ~SOCKET_OPTION_MULTICAST_LOOP;
        }
        return PP_OK;
      }
      case PP_UDPSOCKET_OPTION_MULTICAST_TTL: {
        int32_t integer_value = 0;
        if (!value.GetInt32(&integer_value) ||
            integer_value < 0 || integer_value > 255)
          return PP_ERROR_BADARGUMENT;
  
        // If the socket is already bound, proxy the value to UDPSocket.
        if (socket_) {
          if (can_use_multicast_ != PP_OK)
            return can_use_multicast_;
  
          return NetErrorToPepperError(
              socket_->SetMulticastTimeToLive(integer_value));
        }
  
        // UDPSocket instance is not yet created, so remember the value here.
        socket_options_ |= SOCKET_OPTION_MULTICAST_TTL;
        multicast_ttl_ = integer_value;
        return PP_OK;
      }
      default: {
        NOTREACHED();
        return PP_ERROR_BADARGUMENT;
      }
    }
  }
  
  int32_t PepperUDPSocketMessageFilter::OnMsgBind(
      const ppapi::host::HostMessageContext* context,
      const PP_NetAddress_Private& addr) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    DCHECK(context);
  
    // Check for permissions to use multicast APIS. This check must be done while
    // on the UI thread, so we cache the value here to be used later on.
    PP_NetAddress_Private any_addr;
    NetAddressPrivateImpl::GetAnyAddress(PP_FALSE, &any_addr);
    can_use_multicast_ = CanUseMulticastAPI(any_addr);
  
    SocketPermissionRequest request =
        pepper_socket_utils::CreateSocketPermissionRequest(
            SocketPermissionRequest::UDP_BIND, addr);
    if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
                                               private_api_,
                                               &request,
                                               render_process_id_,
                                               render_frame_id_)) {
      return PP_ERROR_NOACCESS;
    }
  
    BrowserThread::PostTask(BrowserThread::IO,
                            FROM_HERE,
                            base::Bind(&PepperUDPSocketMessageFilter::DoBind,
                                       this,
                                       context->MakeReplyMessageContext(),
                                       addr));
    return PP_OK_COMPLETIONPENDING;
  }
  
  int32_t PepperUDPSocketMessageFilter::OnMsgSendTo(
      const ppapi::host::HostMessageContext* context,
      const std::string& data,
      const PP_NetAddress_Private& addr) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    DCHECK(context);
  
    SocketPermissionRequest request =
        pepper_socket_utils::CreateSocketPermissionRequest(
            SocketPermissionRequest::UDP_SEND_TO, addr);
    if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
                                               private_api_,
                                               &request,
                                               render_process_id_,
                                               render_frame_id_)) {
      return PP_ERROR_NOACCESS;
    }
  
    BrowserThread::PostTask(BrowserThread::IO,
                            FROM_HERE,
                            base::Bind(&PepperUDPSocketMessageFilter::DoSendTo,
                                       this,
                                       context->MakeReplyMessageContext(),
                                       data,
                                       addr));
    return PP_OK_COMPLETIONPENDING;
  }
  
  int32_t PepperUDPSocketMessageFilter::OnMsgClose(
      const ppapi::host::HostMessageContext* context) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
    Close();
    return PP_OK;
  }
  
  int32_t PepperUDPSocketMessageFilter::OnMsgRecvSlotAvailable(
      const ppapi::host::HostMessageContext* context) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
  
    if (remaining_recv_slots_ < UDPSocketFilter::kPluginReceiveBufferSlots) {
      remaining_recv_slots_++;
    }
  
    if (!recvfrom_buffer_.get() && !closed_ && socket_.get()) {
      DCHECK_EQ(1u, remaining_recv_slots_);
      DoRecvFrom();
    }
  
    return PP_OK;
  }
  
  int32_t PepperUDPSocketMessageFilter::OnMsgJoinGroup(
      const ppapi::host::HostMessageContext* context,
      const PP_NetAddress_Private& addr) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
  
    int32_t ret = CanUseMulticastAPI(addr);
    if (ret != PP_OK)
      return ret;
  
    if (!socket_)
      return PP_ERROR_FAILED;
  
    net::IPAddressNumber group;
    uint16_t port;
  
    if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &group, &port))
      return PP_ERROR_ADDRESS_INVALID;
  
    return NetErrorToPepperError(socket_->JoinGroup(group));
  }
  
  int32_t PepperUDPSocketMessageFilter::OnMsgLeaveGroup(
      const ppapi::host::HostMessageContext* context,
      const PP_NetAddress_Private& addr) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
  
    int32_t ret = CanUseMulticastAPI(addr);
    if (ret != PP_OK)
      return ret;
  
    if (!socket_)
      return PP_ERROR_FAILED;
  
    net::IPAddressNumber group;
    uint16_t port;
  
    if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &group, &port))
      return PP_ERROR_ADDRESS_INVALID;
  
    return NetErrorToPepperError(socket_->LeaveGroup(group));
  }
  
  void PepperUDPSocketMessageFilter::DoBind(
      const ppapi::host::ReplyMessageContext& context,
      const PP_NetAddress_Private& addr) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
  
    if (closed_ || socket_.get()) {
      SendBindError(context, PP_ERROR_FAILED);
      return;
    }
  
    scoped_ptr<net::UDPSocket> socket(new net::UDPSocket(
        net::DatagramSocket::DEFAULT_BIND, net::RandIntCallback(),
        NULL, net::NetLog::Source()));
  
    net::IPAddressNumber address;
    uint16_t port;
    if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port)) {
      SendBindError(context, PP_ERROR_ADDRESS_INVALID);
      return;
    }
    net::IPEndPoint end_point(address, port);
    {
      int net_result = socket->Open(end_point.GetFamily());
      if (net_result != net::OK) {
        SendBindError(context, NetErrorToPepperError(net_result));
        return;
      }
    }
  
    if (socket_options_ & SOCKET_OPTION_ADDRESS_REUSE) {
      int net_result = socket->AllowAddressReuse();
      if (net_result != net::OK) {
        SendBindError(context, NetErrorToPepperError(net_result));
        return;
      }
    }
    if (socket_options_ & SOCKET_OPTION_BROADCAST) {
      int net_result = socket->SetBroadcast(true);
      if (net_result != net::OK) {
        SendBindError(context, NetErrorToPepperError(net_result));
        return;
      }
    }
    if (socket_options_ & SOCKET_OPTION_SNDBUF_SIZE) {
      int net_result = socket->SetSendBufferSize(sndbuf_size_);
      if (net_result != net::OK) {
        SendBindError(context, NetErrorToPepperError(net_result));
        return;
      }
    }
    if (socket_options_ & SOCKET_OPTION_RCVBUF_SIZE) {
      int net_result = socket->SetReceiveBufferSize(rcvbuf_size_);
      if (net_result != net::OK) {
        SendBindError(context, NetErrorToPepperError(net_result));
        return;
      }
    }
    if (socket_options_ & SOCKET_OPTION_MULTICAST_LOOP) {
      if (can_use_multicast_ != PP_OK) {
        SendBindError(context, NetErrorToPepperError(can_use_multicast_));
        return;
      }
  
      int net_result = socket->SetMulticastLoopbackMode(true);
      if (net_result != net::OK) {
        SendBindError(context, NetErrorToPepperError(net_result));
        return;
      }
    }
    if (socket_options_ & SOCKET_OPTION_MULTICAST_TTL) {
      if (can_use_multicast_ != PP_OK) {
        SendBindError(context, NetErrorToPepperError(can_use_multicast_));
        return;
      }
  
      int net_result = socket->SetMulticastTimeToLive(multicast_ttl_);
      if (net_result != net::OK) {
        SendBindError(context, NetErrorToPepperError(net_result));
        return;
      }
    }
  
    {
      int net_result = socket->Bind(end_point);
      if (net_result != net::OK) {
        SendBindError(context, NetErrorToPepperError(net_result));
        return;
      }
    }
  
    net::IPEndPoint bound_address;
    {
      int net_result = socket->GetLocalAddress(&bound_address);
      if (net_result != net::OK) {
        SendBindError(context, NetErrorToPepperError(net_result));
        return;
      }
    }
  
    PP_NetAddress_Private net_address = NetAddressPrivateImpl::kInvalidNetAddress;
    if (!NetAddressPrivateImpl::IPEndPointToNetAddress(
            bound_address.address(), bound_address.port(), &net_address)) {
      SendBindError(context, PP_ERROR_ADDRESS_INVALID);
      return;
    }
  
  #if defined(OS_CHROMEOS)
    OpenFirewallHole(
        bound_address,
        base::Bind(&PepperUDPSocketMessageFilter::OnBindComplete, this,
                   base::Passed(&socket), context, net_address));
  #else
    OnBindComplete(std::move(socket), context, net_address);
  #endif
  }
  
  void PepperUDPSocketMessageFilter::OnBindComplete(
      scoped_ptr<net::UDPSocket> socket,
      const ppapi::host::ReplyMessageContext& context,
      const PP_NetAddress_Private& net_address) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
    socket_.swap(socket);
    SendBindReply(context, PP_OK, net_address);
  
    DoRecvFrom();
  }
  
  #if defined(OS_CHROMEOS)
  void PepperUDPSocketMessageFilter::OpenFirewallHole(
      const net::IPEndPoint& local_address,
      base::Closure bind_complete) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
    pepper_socket_utils::FirewallHoleOpenCallback callback = base::Bind(
        &PepperUDPSocketMessageFilter::OnFirewallHoleOpened, this, bind_complete);
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                            base::Bind(&pepper_socket_utils::OpenUDPFirewallHole,
                                       local_address, callback));
  }
  
  void PepperUDPSocketMessageFilter::OnFirewallHoleOpened(
      base::Closure bind_complete,
      scoped_ptr<chromeos::FirewallHole> hole) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
  
    LOG_IF(WARNING, !hole.get()) << "Firewall hole could not be opened.";
    firewall_hole_.reset(hole.release());
  
    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, bind_complete);
  }
  #endif  // defined(OS_CHROMEOS)
  
  void PepperUDPSocketMessageFilter::DoRecvFrom() {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
    DCHECK(!closed_);
    DCHECK(socket_.get());
    DCHECK(!recvfrom_buffer_.get());
    DCHECK_GT(remaining_recv_slots_, 0u);
  
    recvfrom_buffer_ = new net::IOBuffer(UDPSocketFilter::kMaxReadSize);
  
    // Use base::Unretained(this), so that the lifespan of this object doesn't
    // have to last until the callback is called.
    // It is safe to do so because |socket_| is owned by this object. If this
    // object gets destroyed (and so does |socket_|), the callback won't be
    // called.
    int net_result = socket_->RecvFrom(
        recvfrom_buffer_.get(), UDPSocketFilter::kMaxReadSize, &recvfrom_address_,
        base::Bind(&PepperUDPSocketMessageFilter::OnRecvFromCompleted,
                   base::Unretained(this)));
    if (net_result != net::ERR_IO_PENDING)
      OnRecvFromCompleted(net_result);
  }
  
  void PepperUDPSocketMessageFilter::DoSendTo(
      const ppapi::host::ReplyMessageContext& context,
      const std::string& data,
      const PP_NetAddress_Private& addr) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
    DCHECK(socket_.get());
  
    if (closed_ || !socket_.get()) {
      SendSendToError(context, PP_ERROR_FAILED);
      return;
    }
  
    size_t num_bytes = data.size();
    if (num_bytes == 0 ||
        num_bytes > static_cast<size_t>(UDPSocketResourceBase::kMaxWriteSize)) {
      // Size of |data| is checked on the plugin side.
      NOTREACHED();
      SendSendToError(context, PP_ERROR_BADARGUMENT);
      return;
    }
  
    net::IPAddressNumber address;
    uint16_t port;
    if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port)) {
      SendSendToError(context, PP_ERROR_ADDRESS_INVALID);
      return;
    }
  
    scoped_refptr<net::IOBufferWithSize> buffer(
        new net::IOBufferWithSize(num_bytes));
    memcpy(buffer->data(), data.data(), num_bytes);
  
    // Make sure a malicious plugin can't queue up an unlimited number of buffers.
    size_t num_pending_sends = pending_sends_.size();
    if (num_pending_sends == UDPSocketResourceBase::kPluginSendBufferSlots) {
      SendSendToError(context, PP_ERROR_FAILED);
      return;
    }
  
    pending_sends_.push(PendingSend(address, port, buffer, context));
    // If there are other sends pending, we can't start yet.
    if (num_pending_sends)
      return;
    int net_result = StartPendingSend();
    if (net_result != net::ERR_IO_PENDING)
      FinishPendingSend(net_result);
  }
  
  int PepperUDPSocketMessageFilter::StartPendingSend() {
    DCHECK(!pending_sends_.empty());
    const PendingSend& pending_send = pending_sends_.front();
    // See OnMsgRecvFrom() for the reason why we use base::Unretained(this)
    // when calling |socket_| methods.
    int net_result = socket_->SendTo(
        pending_send.buffer.get(),
        pending_send.buffer->size(),
        net::IPEndPoint(pending_send.address, pending_send.port),
        base::Bind(&PepperUDPSocketMessageFilter::OnSendToCompleted,
                   base::Unretained(this)));
    return net_result;
  }
  
  void PepperUDPSocketMessageFilter::Close() {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
    if (socket_.get() && !closed_)
      socket_->Close();
    closed_ = true;
  }
  
  void PepperUDPSocketMessageFilter::OnRecvFromCompleted(int net_result) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
    DCHECK(recvfrom_buffer_.get());
  
    int32_t pp_result = NetErrorToPepperError(net_result);
  
    // Convert IPEndPoint we get back from RecvFrom to a PP_NetAddress_Private
    // to send back.
    PP_NetAddress_Private addr = NetAddressPrivateImpl::kInvalidNetAddress;
    if (pp_result >= 0 &&
        !NetAddressPrivateImpl::IPEndPointToNetAddress(
            recvfrom_address_.address(), recvfrom_address_.port(), &addr)) {
      pp_result = PP_ERROR_ADDRESS_INVALID;
    }
  
    if (pp_result >= 0) {
      SendRecvFromResult(PP_OK, std::string(recvfrom_buffer_->data(), pp_result),
                         addr);
    } else {
      SendRecvFromError(pp_result);
    }
  
    recvfrom_buffer_ = NULL;
  
    DCHECK_GT(remaining_recv_slots_, 0u);
    remaining_recv_slots_--;
  
    if (remaining_recv_slots_ > 0 && !closed_ && socket_.get())
      DoRecvFrom();
  }
  
  void PepperUDPSocketMessageFilter::OnSendToCompleted(int net_result) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
    FinishPendingSend(net_result);
  
    // Start pending sends until none are left or a send doesn't complete.
    while (!pending_sends_.empty()) {
      net_result = StartPendingSend();
      if (net_result == net::ERR_IO_PENDING)
        break;
      FinishPendingSend(net_result);
    }
  }
  
  void PepperUDPSocketMessageFilter::FinishPendingSend(int net_result) {
    DCHECK(!pending_sends_.empty());
    const PendingSend& pending_send = pending_sends_.front();
    int32_t pp_result = NetErrorToPepperError(net_result);
    if (pp_result < 0)
      SendSendToError(pending_send.context, pp_result);
    else
      SendSendToReply(pending_send.context, PP_OK, pp_result);
  
    pending_sends_.pop();
  }
  
  void PepperUDPSocketMessageFilter::SendBindReply(
      const ppapi::host::ReplyMessageContext& context,
      int32_t result,
      const PP_NetAddress_Private& addr) {
    UMA_HISTOGRAM_BOOLEAN("Pepper.PluginContextSecurity.UDPBind",
                          is_potentially_secure_plugin_context_);
  
    ppapi::host::ReplyMessageContext reply_context(context);
    reply_context.params.set_result(result);
    SendReply(reply_context, PpapiPluginMsg_UDPSocket_BindReply(addr));
  }
  
  void PepperUDPSocketMessageFilter::SendRecvFromResult(
      int32_t result,
      const std::string& data,
      const PP_NetAddress_Private& addr) {
    if (resource_host()) {
      resource_host()->host()->SendUnsolicitedReply(
          resource_host()->pp_resource(),
          PpapiPluginMsg_UDPSocket_PushRecvResult(result, data, addr));
    }
  }
  
  void PepperUDPSocketMessageFilter::SendSendToReply(
      const ppapi::host::ReplyMessageContext& context,
      int32_t result,
      int32_t bytes_written) {
    ppapi::host::ReplyMessageContext reply_context(context);
    reply_context.params.set_result(result);
    SendReply(reply_context, PpapiPluginMsg_UDPSocket_SendToReply(bytes_written));
  }
  
  void PepperUDPSocketMessageFilter::SendBindError(
      const ppapi::host::ReplyMessageContext& context,
      int32_t result) {
    SendBindReply(context, result, NetAddressPrivateImpl::kInvalidNetAddress);
  }
  
  void PepperUDPSocketMessageFilter::SendRecvFromError(
      int32_t result) {
    SendRecvFromResult(result, std::string(),
                       NetAddressPrivateImpl::kInvalidNetAddress);
  }
  
  void PepperUDPSocketMessageFilter::SendSendToError(
      const ppapi::host::ReplyMessageContext& context,
      int32_t result) {
    SendSendToReply(context, result, 0);
  }
  
  int32_t PepperUDPSocketMessageFilter::CanUseMulticastAPI(
      const PP_NetAddress_Private& addr) {
    // Check for plugin permissions.
    SocketPermissionRequest request =
        pepper_socket_utils::CreateSocketPermissionRequest(
            SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, addr);
    if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
                                               private_api_,
                                               &request,
                                               render_process_id_,
                                               render_frame_id_)) {
      return PP_ERROR_NOACCESS;
    }
  
    return PP_OK;
  }
  
  }  // namespace content
  