| // Copyright (c) 2012 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 "chrome/browser/extensions/api/socket/udp_socket.h" |
| |
| #include <algorithm> |
| |
| #include "chrome/browser/extensions/api/api_resource.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/net_errors.h" |
| #include "net/udp/datagram_socket.h" |
| #include "net/udp/udp_client_socket.h" |
| |
| namespace extensions { |
| |
| static base::LazyInstance<ProfileKeyedAPIFactory< |
| ApiResourceManager<ResumableUDPSocket> > > |
| g_factory = LAZY_INSTANCE_INITIALIZER; |
| |
| // static |
| template <> |
| ProfileKeyedAPIFactory<ApiResourceManager<ResumableUDPSocket> >* |
| ApiResourceManager<ResumableUDPSocket>::GetFactoryInstance() { |
| return &g_factory.Get(); |
| } |
| |
| UDPSocket::UDPSocket(const std::string& owner_extension_id) |
| : Socket(owner_extension_id), |
| socket_(net::DatagramSocket::DEFAULT_BIND, |
| net::RandIntCallback(), |
| NULL, |
| net::NetLog::Source()) { |
| } |
| |
| UDPSocket::~UDPSocket() { |
| if (is_connected_) { |
| Disconnect(); |
| } |
| } |
| |
| void UDPSocket::Connect(const std::string& address, |
| int port, |
| const CompletionCallback& callback) { |
| int result = net::ERR_CONNECTION_FAILED; |
| do { |
| if (is_connected_) |
| break; |
| |
| net::IPEndPoint ip_end_point; |
| if (!StringAndPortToIPEndPoint(address, port, &ip_end_point)) { |
| result = net::ERR_ADDRESS_INVALID; |
| break; |
| } |
| |
| result = socket_.Connect(ip_end_point); |
| is_connected_ = (result == net::OK); |
| } while (false); |
| |
| callback.Run(result); |
| } |
| |
| int UDPSocket::Bind(const std::string& address, int port) { |
| net::IPEndPoint ip_end_point; |
| if (!StringAndPortToIPEndPoint(address, port, &ip_end_point)) |
| return net::ERR_INVALID_ARGUMENT; |
| |
| return socket_.Bind(ip_end_point); |
| } |
| |
| void UDPSocket::Disconnect() { |
| is_connected_ = false; |
| socket_.Close(); |
| } |
| |
| void UDPSocket::Read(int count, |
| const ReadCompletionCallback& callback) { |
| DCHECK(!callback.is_null()); |
| |
| if (!read_callback_.is_null()) { |
| callback.Run(net::ERR_IO_PENDING, NULL); |
| return; |
| } else { |
| read_callback_ = callback; |
| } |
| |
| int result = net::ERR_FAILED; |
| scoped_refptr<net::IOBuffer> io_buffer; |
| do { |
| if (count < 0) { |
| result = net::ERR_INVALID_ARGUMENT; |
| break; |
| } |
| |
| if (!socket_.is_connected()) { |
| result = net::ERR_SOCKET_NOT_CONNECTED; |
| break; |
| } |
| |
| io_buffer = new net::IOBuffer(count); |
| result = socket_.Read(io_buffer.get(), count, |
| base::Bind(&UDPSocket::OnReadComplete, base::Unretained(this), |
| io_buffer)); |
| } while (false); |
| |
| if (result != net::ERR_IO_PENDING) |
| OnReadComplete(io_buffer, result); |
| } |
| |
| int UDPSocket::WriteImpl(net::IOBuffer* io_buffer, |
| int io_buffer_size, |
| const net::CompletionCallback& callback) { |
| if (!socket_.is_connected()) |
| return net::ERR_SOCKET_NOT_CONNECTED; |
| else |
| return socket_.Write(io_buffer, io_buffer_size, callback); |
| } |
| |
| void UDPSocket::RecvFrom(int count, |
| const RecvFromCompletionCallback& callback) { |
| DCHECK(!callback.is_null()); |
| |
| if (!recv_from_callback_.is_null()) { |
| callback.Run(net::ERR_IO_PENDING, NULL, std::string(), 0); |
| return; |
| } else { |
| recv_from_callback_ = callback; |
| } |
| |
| int result = net::ERR_FAILED; |
| scoped_refptr<net::IOBuffer> io_buffer; |
| scoped_refptr<IPEndPoint> address; |
| do { |
| if (count < 0) { |
| result = net::ERR_INVALID_ARGUMENT; |
| break; |
| } |
| |
| if (!socket_.is_connected()) { |
| result = net::ERR_SOCKET_NOT_CONNECTED; |
| break; |
| } |
| |
| io_buffer = new net::IOBuffer(count); |
| address = new IPEndPoint(); |
| result = socket_.RecvFrom( |
| io_buffer.get(), |
| count, |
| &address->data, |
| base::Bind(&UDPSocket::OnRecvFromComplete, |
| base::Unretained(this), |
| io_buffer, |
| address)); |
| } while (false); |
| |
| if (result != net::ERR_IO_PENDING) |
| OnRecvFromComplete(io_buffer, address, result); |
| } |
| |
| void UDPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer, |
| int byte_count, |
| const std::string& address, |
| int port, |
| const CompletionCallback& callback) { |
| DCHECK(!callback.is_null()); |
| |
| if (!send_to_callback_.is_null()) { |
| // TODO(penghuang): Put requests in a pending queue to support multiple |
| // sendTo calls. |
| callback.Run(net::ERR_IO_PENDING); |
| return; |
| } else { |
| send_to_callback_ = callback; |
| } |
| |
| int result = net::ERR_FAILED; |
| do { |
| net::IPEndPoint ip_end_point; |
| if (!StringAndPortToIPEndPoint(address, port, &ip_end_point)) { |
| result = net::ERR_ADDRESS_INVALID; |
| break; |
| } |
| |
| if (!socket_.is_connected()) { |
| result = net::ERR_SOCKET_NOT_CONNECTED; |
| break; |
| } |
| |
| result = socket_.SendTo( |
| io_buffer.get(), |
| byte_count, |
| ip_end_point, |
| base::Bind(&UDPSocket::OnSendToComplete, base::Unretained(this))); |
| } while (false); |
| |
| if (result != net::ERR_IO_PENDING) |
| OnSendToComplete(result); |
| } |
| |
| bool UDPSocket::IsConnected() { |
| return is_connected_; |
| } |
| |
| bool UDPSocket::GetPeerAddress(net::IPEndPoint* address) { |
| return !socket_.GetPeerAddress(address); |
| } |
| |
| bool UDPSocket::GetLocalAddress(net::IPEndPoint* address) { |
| return !socket_.GetLocalAddress(address); |
| } |
| |
| Socket::SocketType UDPSocket::GetSocketType() const { |
| return Socket::TYPE_UDP; |
| } |
| |
| void UDPSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, |
| int result) { |
| DCHECK(!read_callback_.is_null()); |
| read_callback_.Run(result, io_buffer); |
| read_callback_.Reset(); |
| } |
| |
| void UDPSocket::OnRecvFromComplete(scoped_refptr<net::IOBuffer> io_buffer, |
| scoped_refptr<IPEndPoint> address, |
| int result) { |
| DCHECK(!recv_from_callback_.is_null()); |
| std::string ip; |
| int port = 0; |
| if (result > 0 && address.get()) { |
| IPEndPointToStringAndPort(address->data, &ip, &port); |
| } |
| recv_from_callback_.Run(result, io_buffer, ip, port); |
| recv_from_callback_.Reset(); |
| } |
| |
| void UDPSocket::OnSendToComplete(int result) { |
| DCHECK(!send_to_callback_.is_null()); |
| send_to_callback_.Run(result); |
| send_to_callback_.Reset(); |
| } |
| |
| int UDPSocket::JoinGroup(const std::string& address) { |
| net::IPAddressNumber ip; |
| if (!net::ParseIPLiteralToNumber(address, &ip)) |
| return net::ERR_ADDRESS_INVALID; |
| |
| std::string normalized_address = net::IPAddressToString(ip); |
| std::vector<std::string>::iterator find_result = |
| std::find(multicast_groups_.begin(), |
| multicast_groups_.end(), |
| normalized_address); |
| if (find_result != multicast_groups_.end()) |
| return net::ERR_ADDRESS_INVALID; |
| |
| int rv = socket_.JoinGroup(ip); |
| if (rv == 0) |
| multicast_groups_.push_back(normalized_address); |
| return rv; |
| } |
| |
| int UDPSocket::LeaveGroup(const std::string& address) { |
| net::IPAddressNumber ip; |
| if (!net::ParseIPLiteralToNumber(address, &ip)) |
| return net::ERR_ADDRESS_INVALID; |
| |
| std::string normalized_address = net::IPAddressToString(ip); |
| std::vector<std::string>::iterator find_result = |
| std::find(multicast_groups_.begin(), |
| multicast_groups_.end(), |
| normalized_address); |
| if (find_result == multicast_groups_.end()) |
| return net::ERR_ADDRESS_INVALID; |
| |
| int rv = socket_.LeaveGroup(ip); |
| if (rv == 0) |
| multicast_groups_.erase(find_result); |
| return rv; |
| } |
| |
| int UDPSocket::SetMulticastTimeToLive(int ttl) { |
| return socket_.SetMulticastTimeToLive(ttl); |
| } |
| |
| int UDPSocket::SetMulticastLoopbackMode(bool loopback) { |
| return socket_.SetMulticastLoopbackMode(loopback); |
| } |
| |
| const std::vector<std::string>& UDPSocket::GetJoinedGroups() const { |
| return multicast_groups_; |
| } |
| |
| ResumableUDPSocket::ResumableUDPSocket(const std::string& owner_extension_id) |
| : UDPSocket(owner_extension_id), |
| persistent_(false), |
| buffer_size_(0) { |
| } |
| |
| const std::string& ResumableUDPSocket::name() const { |
| return name_; |
| } |
| |
| void ResumableUDPSocket::set_name(const std::string& name) { |
| name_ = name; |
| } |
| |
| bool ResumableUDPSocket::persistent() const { |
| return persistent_; |
| } |
| |
| void ResumableUDPSocket::set_persistent(bool persistent) { |
| persistent_ = persistent; |
| } |
| |
| int ResumableUDPSocket::buffer_size() const { |
| return buffer_size_; |
| } |
| |
| void ResumableUDPSocket::set_buffer_size(int buffer_size) { |
| buffer_size_ = buffer_size; |
| } |
| |
| } // namespace extensions |