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/plugin_globals.h"
16#include "ppapi/proxy/ppapi_messages.h"
17#include "ppapi/shared_impl/socket_option_data.h"
18#include "ppapi/thunk/enter.h"
19#include "ppapi/thunk/resource_creation_api.h"
20
21namespace ppapi {
22namespace proxy {
23
24const int32_t UDPSocketResourceBase::kMaxReadSize = 128 * 1024;
25const int32_t UDPSocketResourceBase::kMaxWriteSize = 128 * 1024;
26const int32_t UDPSocketResourceBase::kMaxSendBufferSize =
27    1024 * UDPSocketResourceBase::kMaxWriteSize;
28const int32_t UDPSocketResourceBase::kMaxReceiveBufferSize =
29    1024 * UDPSocketResourceBase::kMaxReadSize;
30const size_t UDPSocketResourceBase::kPluginReceiveBufferSlots = 32u;
31
32UDPSocketResourceBase::UDPSocketResourceBase(Connection connection,
33                                             PP_Instance instance,
34                                             bool private_api)
35    : PluginResource(connection, instance),
36      private_api_(private_api),
37      bound_(false),
38      closed_(false),
39      read_buffer_(NULL),
40      bytes_to_read_(-1),
41      recvfrom_addr_resource_(NULL) {
42  recvfrom_addr_.size = 0;
43  memset(recvfrom_addr_.data, 0,
44         arraysize(recvfrom_addr_.data) * sizeof(*recvfrom_addr_.data));
45  bound_addr_.size = 0;
46  memset(bound_addr_.data, 0,
47         arraysize(bound_addr_.data) * sizeof(*bound_addr_.data));
48
49  if (private_api)
50    SendCreate(BROWSER, PpapiHostMsg_UDPSocket_CreatePrivate());
51  else
52    SendCreate(BROWSER, PpapiHostMsg_UDPSocket_Create());
53
54  PluginGlobals::Get()->resource_reply_thread_registrar()->HandleOnIOThread(
55      PpapiPluginMsg_UDPSocket_PushRecvResult::ID);
56}
57
58UDPSocketResourceBase::~UDPSocketResourceBase() {
59}
60
61int32_t UDPSocketResourceBase::SetOptionImpl(
62    PP_UDPSocket_Option name,
63    const PP_Var& value,
64    scoped_refptr<TrackedCallback> callback) {
65  if (closed_)
66    return PP_ERROR_FAILED;
67
68  SocketOptionData option_data;
69  switch (name) {
70    case PP_UDPSOCKET_OPTION_ADDRESS_REUSE:
71    case PP_UDPSOCKET_OPTION_BROADCAST: {
72      if (bound_)
73        return PP_ERROR_FAILED;
74      if (value.type != PP_VARTYPE_BOOL)
75        return PP_ERROR_BADARGUMENT;
76      option_data.SetBool(PP_ToBool(value.value.as_bool));
77      break;
78    }
79    case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE:
80    case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
81      if (!bound_)
82        return PP_ERROR_FAILED;
83      if (value.type != PP_VARTYPE_INT32)
84        return PP_ERROR_BADARGUMENT;
85      option_data.SetInt32(value.value.as_int);
86      break;
87    }
88    default: {
89      NOTREACHED();
90      return PP_ERROR_BADARGUMENT;
91    }
92  }
93
94  Call<PpapiPluginMsg_UDPSocket_SetOptionReply>(
95      BROWSER,
96      PpapiHostMsg_UDPSocket_SetOption(name, option_data),
97      base::Bind(&UDPSocketResourceBase::OnPluginMsgSetOptionReply,
98                 base::Unretained(this),
99                 callback),
100      callback);
101  return PP_OK_COMPLETIONPENDING;
102}
103
104int32_t UDPSocketResourceBase::BindImpl(
105    const PP_NetAddress_Private* addr,
106    scoped_refptr<TrackedCallback> callback) {
107  if (!addr)
108    return PP_ERROR_BADARGUMENT;
109  if (bound_ || closed_)
110    return PP_ERROR_FAILED;
111  if (TrackedCallback::IsPending(bind_callback_))
112    return PP_ERROR_INPROGRESS;
113
114  bind_callback_ = callback;
115
116  // Send the request, the browser will call us back via BindReply.
117  Call<PpapiPluginMsg_UDPSocket_BindReply>(
118      BROWSER,
119      PpapiHostMsg_UDPSocket_Bind(*addr),
120      base::Bind(&UDPSocketResourceBase::OnPluginMsgBindReply,
121                 base::Unretained(this)),
122      callback);
123  return PP_OK_COMPLETIONPENDING;
124}
125
126PP_Bool UDPSocketResourceBase::GetBoundAddressImpl(
127    PP_NetAddress_Private* addr) {
128  if (!addr || !bound_ || closed_)
129    return PP_FALSE;
130
131  *addr = bound_addr_;
132  return PP_TRUE;
133}
134
135int32_t UDPSocketResourceBase::RecvFromImpl(
136    char* buffer,
137    int32_t num_bytes,
138    PP_Resource* addr,
139    scoped_refptr<TrackedCallback> callback) {
140  if (!buffer || num_bytes <= 0)
141    return PP_ERROR_BADARGUMENT;
142  if (!bound_)
143    return PP_ERROR_FAILED;
144  if (TrackedCallback::IsPending(recvfrom_callback_))
145    return PP_ERROR_INPROGRESS;
146
147  if (recv_buffers_.empty()) {
148    read_buffer_ = buffer;
149    bytes_to_read_ = std::min(num_bytes, kMaxReadSize);
150    recvfrom_addr_resource_ = addr;
151    recvfrom_callback_ = callback;
152
153    return PP_OK_COMPLETIONPENDING;
154  } else {
155    RecvBuffer& front = recv_buffers_.front();
156
157    if (num_bytes < static_cast<int32_t>(front.data.size()))
158      return PP_ERROR_MESSAGE_TOO_BIG;
159
160    int32_t result = SetRecvFromOutput(front.result, front.data, front.addr,
161                                       buffer, num_bytes, addr);
162
163    recv_buffers_.pop();
164    Post(BROWSER, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
165
166    return result;
167  }
168}
169
170PP_Bool UDPSocketResourceBase::GetRecvFromAddressImpl(
171    PP_NetAddress_Private* addr) {
172  if (!addr)
173    return PP_FALSE;
174  *addr = recvfrom_addr_;
175  return PP_TRUE;
176}
177
178int32_t UDPSocketResourceBase::SendToImpl(
179    const char* buffer,
180    int32_t num_bytes,
181    const PP_NetAddress_Private* addr,
182    scoped_refptr<TrackedCallback> callback) {
183  if (!buffer || num_bytes <= 0 || !addr)
184    return PP_ERROR_BADARGUMENT;
185  if (!bound_)
186    return PP_ERROR_FAILED;
187  if (TrackedCallback::IsPending(sendto_callback_))
188    return PP_ERROR_INPROGRESS;
189
190  if (num_bytes > kMaxWriteSize)
191    num_bytes = kMaxWriteSize;
192
193  sendto_callback_ = callback;
194
195  // Send the request, the browser will call us back via SendToReply.
196  Call<PpapiPluginMsg_UDPSocket_SendToReply>(
197      BROWSER,
198      PpapiHostMsg_UDPSocket_SendTo(std::string(buffer, num_bytes), *addr),
199      base::Bind(&UDPSocketResourceBase::OnPluginMsgSendToReply,
200                 base::Unretained(this)),
201      callback);
202  return PP_OK_COMPLETIONPENDING;
203}
204
205void UDPSocketResourceBase::CloseImpl() {
206  if(closed_)
207    return;
208
209  bound_ = false;
210  closed_ = true;
211
212  Post(BROWSER, PpapiHostMsg_UDPSocket_Close());
213
214  PostAbortIfNecessary(&bind_callback_);
215  PostAbortIfNecessary(&recvfrom_callback_);
216  PostAbortIfNecessary(&sendto_callback_);
217
218  read_buffer_ = NULL;
219  bytes_to_read_ = -1;
220}
221
222void UDPSocketResourceBase::OnReplyReceived(
223    const ResourceMessageReplyParams& params,
224    const IPC::Message& msg) {
225  PPAPI_BEGIN_MESSAGE_MAP(UDPSocketResourceBase, msg)
226    PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
227        PpapiPluginMsg_UDPSocket_PushRecvResult,
228        OnPluginMsgPushRecvResult)
229    PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
230        PluginResource::OnReplyReceived(params, msg))
231  PPAPI_END_MESSAGE_MAP()
232}
233
234void UDPSocketResourceBase::PostAbortIfNecessary(
235    scoped_refptr<TrackedCallback>* callback) {
236  if (TrackedCallback::IsPending(*callback))
237    (*callback)->PostAbort();
238}
239
240void UDPSocketResourceBase::OnPluginMsgSetOptionReply(
241    scoped_refptr<TrackedCallback> callback,
242    const ResourceMessageReplyParams& params) {
243  if (TrackedCallback::IsPending(callback))
244    RunCallback(callback, params.result());
245}
246
247void UDPSocketResourceBase::OnPluginMsgBindReply(
248    const ResourceMessageReplyParams& params,
249    const PP_NetAddress_Private& bound_addr) {
250  // It is possible that |bind_callback_| is pending while |closed_| is true:
251  // CloseImpl() has been called, but a BindReply came earlier than the task to
252  // abort |bind_callback_|. We don't want to update |bound_| or |bound_addr_|
253  // in that case.
254  if (!TrackedCallback::IsPending(bind_callback_) || closed_)
255    return;
256
257  if (params.result() == PP_OK)
258    bound_ = true;
259  bound_addr_ = bound_addr;
260  RunCallback(bind_callback_, params.result());
261}
262
263void UDPSocketResourceBase::OnPluginMsgPushRecvResult(
264    const ResourceMessageReplyParams& params,
265    int32_t result,
266    const std::string& data,
267    const PP_NetAddress_Private& addr) {
268  // TODO(yzshen): Support passing in a non-const string ref, so that we can
269  // eliminate one copy when storing the data in the buffer.
270
271  DCHECK_LT(recv_buffers_.size(), kPluginReceiveBufferSlots);
272
273  if (!TrackedCallback::IsPending(recvfrom_callback_) || !read_buffer_) {
274    recv_buffers_.push(RecvBuffer());
275    RecvBuffer& back = recv_buffers_.back();
276    back.result = result;
277    back.data = data;
278    back.addr = addr;
279
280    return;
281  }
282
283  DCHECK_EQ(recv_buffers_.size(), 0u);
284
285  if (bytes_to_read_ < static_cast<int32_t>(data.size())) {
286    recv_buffers_.push(RecvBuffer());
287    RecvBuffer& back = recv_buffers_.back();
288    back.result = result;
289    back.data = data;
290    back.addr = addr;
291
292    result = PP_ERROR_MESSAGE_TOO_BIG;
293  } else {
294    result = SetRecvFromOutput(result, data, addr, read_buffer_, bytes_to_read_,
295                               recvfrom_addr_resource_);
296    Post(BROWSER, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
297  }
298
299  read_buffer_ = NULL;
300  bytes_to_read_ = -1;
301  recvfrom_addr_resource_ = NULL;
302
303  RunCallback(recvfrom_callback_, result);
304}
305
306void UDPSocketResourceBase::OnPluginMsgSendToReply(
307    const ResourceMessageReplyParams& params,
308    int32_t bytes_written) {
309  if (!TrackedCallback::IsPending(sendto_callback_))
310    return;
311
312  if (params.result() == PP_OK)
313    RunCallback(sendto_callback_, bytes_written);
314  else
315    RunCallback(sendto_callback_, params.result());
316}
317
318void UDPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback,
319                                        int32_t pp_result) {
320  callback->Run(ConvertNetworkAPIErrorForCompatibility(pp_result,
321                                                       private_api_));
322}
323
324int32_t UDPSocketResourceBase::SetRecvFromOutput(
325    int32_t browser_result,
326    const std::string& data,
327    const PP_NetAddress_Private& addr,
328    char* output_buffer,
329    int32_t num_bytes,
330    PP_Resource* output_addr) {
331  DCHECK_GE(num_bytes, static_cast<int32_t>(data.size()));
332
333  int32_t result = browser_result;
334  if (result == PP_OK && output_addr) {
335    thunk::EnterResourceCreationNoLock enter(pp_instance());
336    if (enter.succeeded()) {
337      *output_addr = enter.functions()->CreateNetAddressFromNetAddressPrivate(
338          pp_instance(), addr);
339    } else {
340      result = PP_ERROR_FAILED;
341    }
342  }
343
344  if (result == PP_OK && !data.empty())
345    memcpy(output_buffer, data.c_str(), data.size());
346
347  recvfrom_addr_ = addr;
348
349  return result == PP_OK ? static_cast<int32_t>(data.size()) : result;
350}
351
352}  // namespace proxy
353}  // namespace ppapi
354