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