udp_socket_resource_base.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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
19namespace ppapi {
20namespace proxy {
21
22namespace {
23
24int32_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
39const int32_t UDPSocketResourceBase::kMaxReadSize = 1024 * 1024;
40const int32_t UDPSocketResourceBase::kMaxWriteSize = 1024 * 1024;
41const int32_t UDPSocketResourceBase::kMaxSendBufferSize =
42    1024 * UDPSocketResourceBase::kMaxWriteSize;
43const int32_t UDPSocketResourceBase::kMaxReceiveBufferSize =
44    1024 * UDPSocketResourceBase::kMaxReadSize;
45
46
47UDPSocketResourceBase::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
69UDPSocketResourceBase::~UDPSocketResourceBase() {
70}
71
72int32_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
114int32_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
135PP_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
144int32_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
169PP_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
177int32_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
203void 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
220void UDPSocketResourceBase::PostAbortIfNecessary(
221    scoped_refptr<TrackedCallback>* callback) {
222  if (TrackedCallback::IsPending(*callback))
223    (*callback)->PostAbort();
224}
225
226void 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
233void 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
249void 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
288void 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