| // Copyright 2014 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 "mojo/services/network/udp_socket_impl.h" | 
 |  | 
 | #include <string.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <limits> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/memory/scoped_ptr.h" | 
 | #include "base/stl_util.h" | 
 | #include "mojo/services/network/net_adapters.h" | 
 | #include "mojo/services/network/net_address_type_converters.h" | 
 | #include "net/base/io_buffer.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/base/rand_callback.h" | 
 | #include "net/udp/datagram_socket.h" | 
 |  | 
 | namespace mojo { | 
 |  | 
 | namespace { | 
 |  | 
 | const int kMaxReadSize = 128 * 1024; | 
 | const size_t kMaxWriteSize = 128 * 1024; | 
 | const size_t kMaxPendingSendRequestsUpperbound = 128; | 
 | const size_t kDefaultMaxPendingSendRequests = 32; | 
 |  | 
 | }  // namespace | 
 |  | 
 | UDPSocketImpl::PendingSendRequest::PendingSendRequest() {} | 
 |  | 
 | UDPSocketImpl::PendingSendRequest::~PendingSendRequest() {} | 
 |  | 
 | UDPSocketImpl::UDPSocketImpl(InterfaceRequest<UDPSocket> request) | 
 |     : binding_(this, request.Pass()), | 
 |       socket_(net::DatagramSocket::DEFAULT_BIND, | 
 |               net::RandIntCallback(), | 
 |               nullptr, | 
 |               net::NetLog::Source()), | 
 |       state_(NOT_BOUND_OR_CONNECTED), | 
 |       allow_address_reuse_(false), | 
 |       remaining_recv_slots_(0), | 
 |       max_pending_send_requests_(kDefaultMaxPendingSendRequests) { | 
 | } | 
 |  | 
 | UDPSocketImpl::~UDPSocketImpl() { | 
 |   STLDeleteElements(&pending_send_requests_); | 
 | } | 
 |  | 
 | void UDPSocketImpl::AllowAddressReuse( | 
 |     const Callback<void(NetworkErrorPtr)>& callback) { | 
 |   if (IsBoundOrConnected()) { | 
 |     callback.Run(MakeNetworkError(net::ERR_FAILED)); | 
 |     return; | 
 |   } | 
 |  | 
 |   allow_address_reuse_ = true; | 
 |   callback.Run(MakeNetworkError(net::OK)); | 
 | } | 
 |  | 
 | void UDPSocketImpl::Bind( | 
 |     NetAddressPtr addr, | 
 |     const Callback<void(NetworkErrorPtr, | 
 |                         NetAddressPtr, | 
 |                         InterfaceRequest<UDPSocketReceiver>)>& callback) { | 
 |   int net_result = net::OK; | 
 |   bool opened = false; | 
 |  | 
 |   do { | 
 |     if (IsBoundOrConnected()) { | 
 |       net_result = net::ERR_FAILED; | 
 |       break; | 
 |     } | 
 |  | 
 |     net::IPEndPoint ip_end_point = addr.To<net::IPEndPoint>(); | 
 |     if (ip_end_point.GetFamily() == net::ADDRESS_FAMILY_UNSPECIFIED) { | 
 |       net_result = net::ERR_ADDRESS_INVALID; | 
 |       break; | 
 |     } | 
 |  | 
 |     net_result = socket_.Open(ip_end_point.GetFamily()); | 
 |     if (net_result != net::OK) | 
 |       break; | 
 |     opened = true; | 
 |  | 
 |     if (allow_address_reuse_) { | 
 |       net_result = socket_.AllowAddressReuse(); | 
 |       if (net_result != net::OK) | 
 |         break; | 
 |     } | 
 |  | 
 |     net_result = socket_.Bind(ip_end_point); | 
 |     if (net_result != net::OK) | 
 |       break; | 
 |  | 
 |     net::IPEndPoint bound_ip_end_point; | 
 |     net_result = socket_.GetLocalAddress(&bound_ip_end_point); | 
 |     if (net_result != net::OK) | 
 |       break; | 
 |  | 
 |     state_ = BOUND; | 
 |     callback.Run(MakeNetworkError(net_result), | 
 |                  NetAddress::From(bound_ip_end_point), GetProxy(&receiver_)); | 
 |  | 
 |     if (remaining_recv_slots_ > 0) { | 
 |       DCHECK(!recvfrom_buffer_.get()); | 
 |       DoRecvFrom(); | 
 |     } | 
 |     return; | 
 |   } while (false); | 
 |  | 
 |   DCHECK(net_result != net::OK); | 
 |   if (opened) | 
 |     socket_.Close(); | 
 |   callback.Run(MakeNetworkError(net_result), nullptr, nullptr); | 
 | } | 
 |  | 
 | void UDPSocketImpl::Connect( | 
 |     NetAddressPtr remote_addr, | 
 |     const Callback<void(NetworkErrorPtr, | 
 |                         NetAddressPtr, | 
 |                         InterfaceRequest<UDPSocketReceiver>)>& callback) { | 
 |   int net_result = net::OK; | 
 |   bool opened = false; | 
 |  | 
 |   do { | 
 |     if (IsBoundOrConnected()) { | 
 |       net_result = net::ERR_FAILED; | 
 |       break; | 
 |     } | 
 |  | 
 |     net::IPEndPoint ip_end_point = remote_addr.To<net::IPEndPoint>(); | 
 |     if (ip_end_point.GetFamily() == net::ADDRESS_FAMILY_UNSPECIFIED) { | 
 |       net_result = net::ERR_ADDRESS_INVALID; | 
 |       break; | 
 |     } | 
 |  | 
 |     net_result = socket_.Open(ip_end_point.GetFamily()); | 
 |     if (net_result != net::OK) | 
 |       break; | 
 |     opened = true; | 
 |  | 
 |     net_result = socket_.Connect(ip_end_point); | 
 |     if (net_result != net::OK) | 
 |       break; | 
 |  | 
 |     net::IPEndPoint local_ip_end_point; | 
 |     net_result = socket_.GetLocalAddress(&local_ip_end_point); | 
 |     if (net_result != net::OK) | 
 |       break; | 
 |  | 
 |     state_ = CONNECTED; | 
 |     callback.Run(MakeNetworkError(net_result), | 
 |                  NetAddress::From(local_ip_end_point), GetProxy(&receiver_)); | 
 |  | 
 |     if (remaining_recv_slots_ > 0) { | 
 |       DCHECK(!recvfrom_buffer_.get()); | 
 |       DoRecvFrom(); | 
 |     } | 
 |     return; | 
 |   } while (false); | 
 |  | 
 |   DCHECK(net_result != net::OK); | 
 |   if (opened) | 
 |     socket_.Close(); | 
 |   callback.Run(MakeNetworkError(net_result), nullptr, nullptr); | 
 | } | 
 |  | 
 | void UDPSocketImpl::SetSendBufferSize( | 
 |     uint32_t size, | 
 |     const Callback<void(NetworkErrorPtr)>& callback) { | 
 |   if (!IsBoundOrConnected()) { | 
 |     callback.Run(MakeNetworkError(net::ERR_FAILED)); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (size > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) | 
 |     size = std::numeric_limits<int32_t>::max(); | 
 |  | 
 |   int net_result = socket_.SetSendBufferSize(static_cast<int32_t>(size)); | 
 |   callback.Run(MakeNetworkError(net_result)); | 
 | } | 
 |  | 
 | void UDPSocketImpl::SetReceiveBufferSize( | 
 |     uint32_t size, | 
 |     const Callback<void(NetworkErrorPtr)>& callback) { | 
 |   if (!IsBoundOrConnected()) { | 
 |     callback.Run(MakeNetworkError(net::ERR_FAILED)); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (size > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) | 
 |     size = std::numeric_limits<int32_t>::max(); | 
 |  | 
 |   int net_result = socket_.SetReceiveBufferSize(static_cast<int32_t>(size)); | 
 |   callback.Run(MakeNetworkError(net_result)); | 
 | } | 
 |  | 
 | void UDPSocketImpl::NegotiateMaxPendingSendRequests( | 
 |     uint32_t requested_size, | 
 |     const Callback<void(uint32_t)>& callback) { | 
 |   if (requested_size != 0) { | 
 |     max_pending_send_requests_ = | 
 |       std::min(kMaxPendingSendRequestsUpperbound, | 
 |                static_cast<size_t>(requested_size)); | 
 |   } | 
 |   callback.Run(static_cast<uint32_t>(max_pending_send_requests_)); | 
 |  | 
 |   if (pending_send_requests_.size() > max_pending_send_requests_) { | 
 |     std::deque<PendingSendRequest*> discarded_requests( | 
 |         pending_send_requests_.begin() + max_pending_send_requests_, | 
 |         pending_send_requests_.end()); | 
 |     pending_send_requests_.resize(max_pending_send_requests_); | 
 |     for (auto& discarded_request : discarded_requests) { | 
 |       discarded_request->callback.Run( | 
 |           MakeNetworkError(net::ERR_INSUFFICIENT_RESOURCES)); | 
 |       delete discarded_request; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void UDPSocketImpl::ReceiveMore(uint32_t datagram_number) { | 
 |   if (!receiver_) | 
 |     return; | 
 |   if (datagram_number == 0) | 
 |     return; | 
 |   if (std::numeric_limits<size_t>::max() - remaining_recv_slots_ < | 
 |           datagram_number) { | 
 |     return; | 
 |   } | 
 |  | 
 |   remaining_recv_slots_ += datagram_number; | 
 |  | 
 |   if (IsBoundOrConnected() && !recvfrom_buffer_.get()) { | 
 |     DCHECK_EQ(datagram_number, remaining_recv_slots_); | 
 |     DoRecvFrom(); | 
 |   } | 
 | } | 
 |  | 
 | void UDPSocketImpl::SendTo(NetAddressPtr dest_addr, | 
 |                            Array<uint8_t> data, | 
 |                            const Callback<void(NetworkErrorPtr)>& callback) { | 
 |   if (!IsBoundOrConnected()) { | 
 |     callback.Run(MakeNetworkError(net::ERR_FAILED)); | 
 |     return; | 
 |   } | 
 |   if (state_ == BOUND && !dest_addr) { | 
 |     callback.Run(MakeNetworkError(net::ERR_INVALID_ARGUMENT)); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (sendto_buffer_.get()) { | 
 |     if (pending_send_requests_.size() >= max_pending_send_requests_) { | 
 |       callback.Run(MakeNetworkError(net::ERR_INSUFFICIENT_RESOURCES)); | 
 |       return; | 
 |     } | 
 |  | 
 |     PendingSendRequest* request = new PendingSendRequest; | 
 |     request->addr = dest_addr.Pass(); | 
 |     request->data = data.Pass(); | 
 |     request->callback = callback; | 
 |     pending_send_requests_.push_back(request); | 
 |     return; | 
 |   } | 
 |  | 
 |   DCHECK_EQ(0u, pending_send_requests_.size()); | 
 |  | 
 |   DoSendTo(dest_addr.Pass(), data.Pass(), callback); | 
 | } | 
 |  | 
 | void UDPSocketImpl::DoRecvFrom() { | 
 |   DCHECK(IsBoundOrConnected()); | 
 |   DCHECK(receiver_); | 
 |   DCHECK(!recvfrom_buffer_.get()); | 
 |   DCHECK_GT(remaining_recv_slots_, 0u); | 
 |  | 
 |   recvfrom_buffer_ = new net::IOBuffer(kMaxReadSize); | 
 |  | 
 |   // It is safe to use base::Unretained(this) 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(), | 
 |       kMaxReadSize, | 
 |       state_ == BOUND ? &recvfrom_address_ : nullptr, | 
 |       base::Bind(&UDPSocketImpl::OnRecvFromCompleted, base::Unretained(this))); | 
 |   if (net_result != net::ERR_IO_PENDING) | 
 |     OnRecvFromCompleted(net_result); | 
 | } | 
 |  | 
 | void UDPSocketImpl::DoSendTo(NetAddressPtr addr, | 
 |                              Array<uint8_t> data, | 
 |                              const Callback<void(NetworkErrorPtr)>& callback) { | 
 |   DCHECK(IsBoundOrConnected()); | 
 |   DCHECK(!sendto_buffer_.get()); | 
 |  | 
 |   if (data.size() > kMaxWriteSize) { | 
 |     callback.Run(MakeNetworkError(net::ERR_INVALID_ARGUMENT)); | 
 |     return; | 
 |   } | 
 |   sendto_buffer_ = new net::IOBufferWithSize(static_cast<int>(data.size())); | 
 |   if (data.size() > 0) | 
 |     memcpy(sendto_buffer_->data(), &data.storage()[0], data.size()); | 
 |  | 
 |   int net_result = net::OK; | 
 |   if (addr) { | 
 |     net::IPEndPoint ip_end_point = addr.To<net::IPEndPoint>(); | 
 |     if (ip_end_point.GetFamily() == net::ADDRESS_FAMILY_UNSPECIFIED) { | 
 |       callback.Run(MakeNetworkError(net::ERR_ADDRESS_INVALID)); | 
 |       return; | 
 |     } | 
 |  | 
 |     // It is safe to use base::Unretained(this) because |socket_| is owned by | 
 |     // this object. If this object gets destroyed (and so does |socket_|), the | 
 |     // callback won't be called. | 
 |     net_result = socket_.SendTo(sendto_buffer_.get(), sendto_buffer_->size(), | 
 |                                 ip_end_point, | 
 |                                 base::Bind(&UDPSocketImpl::OnSendToCompleted, | 
 |                                            base::Unretained(this), callback)); | 
 |   } else { | 
 |     DCHECK(state_ == CONNECTED); | 
 |     net_result = socket_.Write(sendto_buffer_.get(), sendto_buffer_->size(), | 
 |                                base::Bind(&UDPSocketImpl::OnSendToCompleted, | 
 |                                           base::Unretained(this), callback)); | 
 |   } | 
 |   if (net_result != net::ERR_IO_PENDING) | 
 |     OnSendToCompleted(callback, net_result); | 
 | } | 
 |  | 
 | void UDPSocketImpl::OnRecvFromCompleted(int net_result) { | 
 |   DCHECK(recvfrom_buffer_.get()); | 
 |  | 
 |   NetAddressPtr net_address; | 
 |   Array<uint8_t> array; | 
 |   if (net_result >= 0) { | 
 |     if (state_ == BOUND) | 
 |       net_address = NetAddress::From(recvfrom_address_); | 
 |  | 
 |     std::vector<uint8_t> data(net_result); | 
 |     if (net_result > 0) | 
 |       memcpy(&data[0], recvfrom_buffer_->data(), net_result); | 
 |  | 
 |     array.Swap(&data); | 
 |   } | 
 |   recvfrom_buffer_ = nullptr; | 
 |  | 
 |   receiver_->OnReceived(MakeNetworkError(net_result), net_address.Pass(), | 
 |                         array.Pass()); | 
 |   DCHECK_GT(remaining_recv_slots_, 0u); | 
 |   remaining_recv_slots_--; | 
 |   if (remaining_recv_slots_ > 0) | 
 |     DoRecvFrom(); | 
 | } | 
 |  | 
 | void UDPSocketImpl::OnSendToCompleted( | 
 |     const Callback<void(NetworkErrorPtr)>& callback, | 
 |     int net_result) { | 
 |   DCHECK(sendto_buffer_.get()); | 
 |  | 
 |   sendto_buffer_ = nullptr; | 
 |  | 
 |   callback.Run(MakeNetworkError(net_result)); | 
 |  | 
 |   if (pending_send_requests_.empty()) | 
 |     return; | 
 |  | 
 |   scoped_ptr<PendingSendRequest> request(pending_send_requests_.front()); | 
 |   pending_send_requests_.pop_front(); | 
 |  | 
 |   DoSendTo(request->addr.Pass(), request->data.Pass(), request->callback); | 
 | } | 
 |  | 
 | }  // namespace mojo |