| // 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 "content/renderer/p2p/ipc_socket_factory.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <list> |
| |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/threading/non_thread_safe.h" |
| #include "base/threading/thread_checker.h" |
| #include "base/trace_event/trace_event.h" |
| #include "content/renderer/media/webrtc_logging.h" |
| #include "content/renderer/p2p/host_address_request.h" |
| #include "content/renderer/p2p/socket_client_delegate.h" |
| #include "content/renderer/p2p/socket_client_impl.h" |
| #include "content/renderer/p2p/socket_dispatcher.h" |
| #include "jingle/glue/utils.h" |
| #include "net/base/ip_address.h" |
| #include "third_party/webrtc/base/asyncpacketsocket.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| const int kDefaultNonSetOptionValue = -1; |
| |
| bool IsTcpClientSocket(P2PSocketType type) { |
| return (type == P2P_SOCKET_STUN_TCP_CLIENT) || |
| (type == P2P_SOCKET_TCP_CLIENT) || |
| (type == P2P_SOCKET_STUN_SSLTCP_CLIENT) || |
| (type == P2P_SOCKET_SSLTCP_CLIENT) || |
| (type == P2P_SOCKET_TLS_CLIENT) || |
| (type == P2P_SOCKET_STUN_TLS_CLIENT); |
| } |
| |
| bool JingleSocketOptionToP2PSocketOption(rtc::Socket::Option option, |
| P2PSocketOption* ipc_option) { |
| switch (option) { |
| case rtc::Socket::OPT_RCVBUF: |
| *ipc_option = P2P_SOCKET_OPT_RCVBUF; |
| break; |
| case rtc::Socket::OPT_SNDBUF: |
| *ipc_option = P2P_SOCKET_OPT_SNDBUF; |
| break; |
| case rtc::Socket::OPT_DSCP: |
| *ipc_option = P2P_SOCKET_OPT_DSCP; |
| break; |
| case rtc::Socket::OPT_DONTFRAGMENT: |
| case rtc::Socket::OPT_NODELAY: |
| case rtc::Socket::OPT_IPV6_V6ONLY: |
| case rtc::Socket::OPT_RTP_SENDTIME_EXTN_ID: |
| return false; // Not supported by the chrome sockets. |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| return true; |
| } |
| |
| // TODO(miu): This needs tuning. http://crbug.com/237960 |
| // http://crbug.com/427555 |
| const size_t kMaximumInFlightBytes = 64 * 1024; // 64 KB |
| |
| // IpcPacketSocket implements rtc::AsyncPacketSocket interface |
| // using P2PSocketClient that works over IPC-channel. It must be used |
| // on the thread it was created. |
| class IpcPacketSocket : public rtc::AsyncPacketSocket, |
| public P2PSocketClientDelegate { |
| public: |
| IpcPacketSocket(); |
| ~IpcPacketSocket() override; |
| |
| // Struct to track information when a packet is received by this socket for |
| // send. The information tracked here will be used to match with the |
| // P2PSendPacketMetrics from the underneath system socket. |
| struct InFlightPacketRecord { |
| InFlightPacketRecord(uint64_t packet_id, size_t packet_size) |
| : packet_id(packet_id), packet_size(packet_size) {} |
| |
| uint64_t packet_id; |
| size_t packet_size; |
| }; |
| |
| typedef std::list<InFlightPacketRecord> InFlightPacketList; |
| |
| // Always takes ownership of client even if initialization fails. |
| bool Init(P2PSocketType type, |
| P2PSocketClientImpl* client, |
| const rtc::SocketAddress& local_address, |
| uint16_t min_port, |
| uint16_t max_port, |
| const rtc::SocketAddress& remote_address); |
| |
| // rtc::AsyncPacketSocket interface. |
| rtc::SocketAddress GetLocalAddress() const override; |
| rtc::SocketAddress GetRemoteAddress() const override; |
| int Send(const void* pv, |
| size_t cb, |
| const rtc::PacketOptions& options) override; |
| int SendTo(const void* pv, |
| size_t cb, |
| const rtc::SocketAddress& addr, |
| const rtc::PacketOptions& options) override; |
| int Close() override; |
| State GetState() const override; |
| int GetOption(rtc::Socket::Option option, int* value) override; |
| int SetOption(rtc::Socket::Option option, int value) override; |
| int GetError() const override; |
| void SetError(int error) override; |
| |
| // P2PSocketClientDelegate implementation. |
| void OnOpen(const net::IPEndPoint& local_address, |
| const net::IPEndPoint& remote_address) override; |
| void OnIncomingTcpConnection(const net::IPEndPoint& address, |
| P2PSocketClient* client) override; |
| void OnSendComplete(const P2PSendPacketMetrics& send_metrics) override; |
| void OnError() override; |
| void OnDataReceived(const net::IPEndPoint& address, |
| const std::vector<char>& data, |
| const base::TimeTicks& timestamp) override; |
| |
| private: |
| enum InternalState { |
| IS_UNINITIALIZED, |
| IS_OPENING, |
| IS_OPEN, |
| IS_CLOSED, |
| IS_ERROR, |
| }; |
| |
| // Increment the counter for consecutive bytes discarded as socket is running |
| // out of buffer. |
| void IncrementDiscardCounters(size_t bytes_discarded); |
| |
| // Update trace of send throttling internal state. This should be called |
| // immediately after any changes to |send_bytes_available_| and/or |
| // |in_flight_packet_records_|. |
| void TraceSendThrottlingState() const; |
| |
| void InitAcceptedTcp(P2PSocketClient* client, |
| const rtc::SocketAddress& local_address, |
| const rtc::SocketAddress& remote_address); |
| |
| int DoSetOption(P2PSocketOption option, int value); |
| |
| // Allow a finch experiment to control the initial value of |
| // send_bytes_available_; |
| void AdjustUdpSendBufferSize(); |
| |
| P2PSocketType type_; |
| |
| // Used to verify that a method runs on the thread that created this socket. |
| base::ThreadChecker thread_checker_; |
| |
| // Corresponding P2P socket client. |
| scoped_refptr<P2PSocketClient> client_; |
| |
| // Local address is allocated by the browser process, and the |
| // renderer side doesn't know the address until it receives OnOpen() |
| // event from the browser. |
| rtc::SocketAddress local_address_; |
| |
| // Remote address for client TCP connections. |
| rtc::SocketAddress remote_address_; |
| |
| // Current state of the object. |
| InternalState state_; |
| |
| // Track the number of bytes allowed to be sent non-blocking. This is used to |
| // throttle the sending of packets to the browser process. For each packet |
| // sent, the value is decreased. As callbacks to OnSendComplete() (as IPCs |
| // from the browser process) are made, the value is increased back. This |
| // allows short bursts of high-rate sending without dropping packets, but |
| // quickly restricts the client to a sustainable steady-state rate. |
| size_t send_bytes_available_; |
| |
| // Used to detect when browser doesn't send SendComplete message for some |
| // packets. In normal case, the first packet should be the one that we're |
| // going to receive the next completion signal. |
| InFlightPacketList in_flight_packet_records_; |
| |
| // Set to true once EWOULDBLOCK was returned from Send(). Indicates that the |
| // caller expects SignalWritable notification. |
| bool writable_signal_expected_; |
| |
| // Current error code. Valid when state_ == IS_ERROR. |
| int error_; |
| int options_[P2P_SOCKET_OPT_MAX]; |
| |
| // Track the maximum and current consecutive bytes discarded due to not enough |
| // send_bytes_available_. |
| size_t max_discard_bytes_sequence_; |
| size_t current_discard_bytes_sequence_; |
| |
| // Track the total number of packets and the number of packets discarded. |
| size_t packets_discarded_; |
| size_t total_packets_; |
| |
| DISALLOW_COPY_AND_ASSIGN(IpcPacketSocket); |
| }; |
| |
| // Simple wrapper around P2PAsyncAddressResolver. The main purpose of this |
| // class is to send SignalDone, after OnDone callback from |
| // P2PAsyncAddressResolver. Libjingle sig slots are not thread safe. In case |
| // of MT sig slots clients must call disconnect. This class is to make sure |
| // we destruct from the same thread on which is created. |
| class AsyncAddressResolverImpl : public base::NonThreadSafe, |
| public rtc::AsyncResolverInterface { |
| public: |
| AsyncAddressResolverImpl(P2PSocketDispatcher* dispatcher); |
| ~AsyncAddressResolverImpl() override; |
| |
| // rtc::AsyncResolverInterface interface. |
| void Start(const rtc::SocketAddress& addr) override; |
| bool GetResolvedAddress(int family, rtc::SocketAddress* addr) const override; |
| int GetError() const override; |
| void Destroy(bool wait) override; |
| |
| private: |
| virtual void OnAddressResolved(const net::IPAddressList& addresses); |
| |
| scoped_refptr<P2PAsyncAddressResolver> resolver_; |
| int port_; // Port number in |addr| from Start() method. |
| std::vector<rtc::IPAddress> addresses_; // Resolved addresses. |
| }; |
| |
| IpcPacketSocket::IpcPacketSocket() |
| : type_(P2P_SOCKET_UDP), |
| state_(IS_UNINITIALIZED), |
| send_bytes_available_(kMaximumInFlightBytes), |
| writable_signal_expected_(false), |
| error_(0), |
| max_discard_bytes_sequence_(0), |
| current_discard_bytes_sequence_(0), |
| packets_discarded_(0), |
| total_packets_(0) { |
| static_assert(kMaximumInFlightBytes > 0, "would send at zero rate"); |
| std::fill_n(options_, static_cast<int> (P2P_SOCKET_OPT_MAX), |
| kDefaultNonSetOptionValue); |
| } |
| |
| IpcPacketSocket::~IpcPacketSocket() { |
| if (state_ == IS_OPENING || state_ == IS_OPEN || |
| state_ == IS_ERROR) { |
| Close(); |
| } |
| |
| UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.ApplicationMaxConsecutiveBytesDiscard.v2", |
| max_discard_bytes_sequence_, 1, 1000000, 200); |
| if (total_packets_ > 0) { |
| UMA_HISTOGRAM_PERCENTAGE("WebRTC.ApplicationPercentPacketsDiscarded", |
| (packets_discarded_ * 100) / total_packets_); |
| } |
| } |
| |
| void IpcPacketSocket::TraceSendThrottlingState() const { |
| TRACE_COUNTER_ID1("p2p", "P2PSendBytesAvailable", local_address_.port(), |
| send_bytes_available_); |
| TRACE_COUNTER_ID1("p2p", "P2PSendPacketsInFlight", local_address_.port(), |
| in_flight_packet_records_.size()); |
| } |
| |
| void IpcPacketSocket::IncrementDiscardCounters(size_t bytes_discarded) { |
| current_discard_bytes_sequence_ += bytes_discarded; |
| packets_discarded_++; |
| |
| if (current_discard_bytes_sequence_ > max_discard_bytes_sequence_) { |
| max_discard_bytes_sequence_ = current_discard_bytes_sequence_; |
| } |
| } |
| |
| void IpcPacketSocket::AdjustUdpSendBufferSize() { |
| DCHECK_EQ(type_, P2P_SOCKET_UDP); |
| unsigned int send_buffer_size = 0; |
| |
| base::StringToUint( |
| base::FieldTrialList::FindFullName("WebRTC-ApplicationUDPSendSocketSize"), |
| &send_buffer_size); |
| |
| if (send_buffer_size > 0) { |
| send_bytes_available_ = send_buffer_size; |
| } |
| } |
| |
| bool IpcPacketSocket::Init(P2PSocketType type, |
| P2PSocketClientImpl* client, |
| const rtc::SocketAddress& local_address, |
| uint16_t min_port, |
| uint16_t max_port, |
| const rtc::SocketAddress& remote_address) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK_EQ(state_, IS_UNINITIALIZED); |
| |
| type_ = type; |
| client_ = client; |
| local_address_ = local_address; |
| remote_address_ = remote_address; |
| state_ = IS_OPENING; |
| |
| net::IPEndPoint local_endpoint; |
| if (!jingle_glue::SocketAddressToIPEndPoint( |
| local_address, &local_endpoint)) { |
| return false; |
| } |
| |
| if (type_ == P2P_SOCKET_UDP) { |
| AdjustUdpSendBufferSize(); |
| } |
| |
| net::IPEndPoint remote_endpoint; |
| if (!remote_address.IsNil()) { |
| DCHECK(IsTcpClientSocket(type_)); |
| |
| if (remote_address.IsUnresolvedIP()) { |
| remote_endpoint = |
| net::IPEndPoint(net::IPAddress(), remote_address.port()); |
| } else { |
| if (!jingle_glue::SocketAddressToIPEndPoint(remote_address, |
| &remote_endpoint)) { |
| return false; |
| } |
| } |
| } |
| |
| // We need to send both resolved and unresolved address in Init. Unresolved |
| // address will be used in case of TLS for certificate hostname matching. |
| // Certificate will be tied to domain name not to IP address. |
| P2PHostAndIPEndPoint remote_info(remote_address.hostname(), remote_endpoint); |
| |
| client->Init(type, local_endpoint, min_port, max_port, remote_info, this); |
| |
| return true; |
| } |
| |
| void IpcPacketSocket::InitAcceptedTcp( |
| P2PSocketClient* client, |
| const rtc::SocketAddress& local_address, |
| const rtc::SocketAddress& remote_address) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK_EQ(state_, IS_UNINITIALIZED); |
| |
| client_ = client; |
| local_address_ = local_address; |
| remote_address_ = remote_address; |
| state_ = IS_OPEN; |
| TraceSendThrottlingState(); |
| client_->SetDelegate(this); |
| } |
| |
| // rtc::AsyncPacketSocket interface. |
| rtc::SocketAddress IpcPacketSocket::GetLocalAddress() const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return local_address_; |
| } |
| |
| rtc::SocketAddress IpcPacketSocket::GetRemoteAddress() const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return remote_address_; |
| } |
| |
| int IpcPacketSocket::Send(const void *data, size_t data_size, |
| const rtc::PacketOptions& options) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return SendTo(data, data_size, remote_address_, options); |
| } |
| |
| int IpcPacketSocket::SendTo(const void *data, size_t data_size, |
| const rtc::SocketAddress& address, |
| const rtc::PacketOptions& options) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| switch (state_) { |
| case IS_UNINITIALIZED: |
| NOTREACHED(); |
| error_ = EWOULDBLOCK; |
| return -1; |
| case IS_OPENING: |
| error_ = EWOULDBLOCK; |
| return -1; |
| case IS_CLOSED: |
| error_ = ENOTCONN; |
| return -1; |
| case IS_ERROR: |
| return -1; |
| case IS_OPEN: |
| // Continue sending the packet. |
| break; |
| } |
| |
| if (data_size == 0) { |
| NOTREACHED(); |
| return 0; |
| } |
| |
| total_packets_++; |
| |
| if (data_size > send_bytes_available_) { |
| TRACE_EVENT_INSTANT1("p2p", "MaxPendingBytesWouldBlock", |
| TRACE_EVENT_SCOPE_THREAD, |
| "id", |
| client_->GetSocketID()); |
| if (!writable_signal_expected_) { |
| WebRtcLogMessage(base::StringPrintf( |
| "IpcPacketSocket: sending is blocked. %d packets_in_flight.", |
| static_cast<int>(in_flight_packet_records_.size()))); |
| |
| writable_signal_expected_ = true; |
| } |
| |
| error_ = EWOULDBLOCK; |
| IncrementDiscardCounters(data_size); |
| return -1; |
| } else { |
| current_discard_bytes_sequence_ = 0; |
| } |
| |
| net::IPEndPoint address_chrome; |
| if (address.IsUnresolvedIP()) { |
| address_chrome = net::IPEndPoint(net::IPAddress(), address.port()); |
| } else { |
| if (!jingle_glue::SocketAddressToIPEndPoint(address, &address_chrome)) { |
| LOG(WARNING) << "Failed to convert remote address to IPEndPoint: address=" |
| << address.ipaddr().ToSensitiveString() |
| << ", remote_address_=" |
| << remote_address_.ipaddr().ToSensitiveString(); |
| NOTREACHED(); |
| error_ = EINVAL; |
| return -1; |
| } |
| } |
| |
| send_bytes_available_ -= data_size; |
| |
| const char* data_char = reinterpret_cast<const char*>(data); |
| std::vector<char> data_vector(data_char, data_char + data_size); |
| uint64_t packet_id = client_->Send(address_chrome, data_vector, options); |
| |
| // Ensure packet_id is not 0. It can't be the case according to |
| // P2PSocketClientImpl::Send(). |
| DCHECK_NE(packet_id, 0uL); |
| |
| in_flight_packet_records_.push_back( |
| InFlightPacketRecord(packet_id, data_size)); |
| TraceSendThrottlingState(); |
| |
| // Fake successful send. The caller ignores result anyway. |
| return data_size; |
| } |
| |
| int IpcPacketSocket::Close() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| client_->Close(); |
| state_ = IS_CLOSED; |
| |
| return 0; |
| } |
| |
| rtc::AsyncPacketSocket::State IpcPacketSocket::GetState() const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| switch (state_) { |
| case IS_UNINITIALIZED: |
| NOTREACHED(); |
| return STATE_CLOSED; |
| |
| case IS_OPENING: |
| return STATE_BINDING; |
| |
| case IS_OPEN: |
| if (IsTcpClientSocket(type_)) { |
| return STATE_CONNECTED; |
| } else { |
| return STATE_BOUND; |
| } |
| |
| case IS_CLOSED: |
| case IS_ERROR: |
| return STATE_CLOSED; |
| } |
| |
| NOTREACHED(); |
| return STATE_CLOSED; |
| } |
| |
| int IpcPacketSocket::GetOption(rtc::Socket::Option option, int* value) { |
| P2PSocketOption p2p_socket_option = P2P_SOCKET_OPT_MAX; |
| if (!JingleSocketOptionToP2PSocketOption(option, &p2p_socket_option)) { |
| // unsupported option. |
| return -1; |
| } |
| |
| *value = options_[p2p_socket_option]; |
| return 0; |
| } |
| |
| int IpcPacketSocket::SetOption(rtc::Socket::Option option, int value) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| P2PSocketOption p2p_socket_option = P2P_SOCKET_OPT_MAX; |
| if (!JingleSocketOptionToP2PSocketOption(option, &p2p_socket_option)) { |
| // Option is not supported. |
| return -1; |
| } |
| |
| options_[p2p_socket_option] = value; |
| |
| if (state_ == IS_OPEN) { |
| // Options will be applied when state becomes IS_OPEN in OnOpen. |
| return DoSetOption(p2p_socket_option, value); |
| } |
| return 0; |
| } |
| |
| int IpcPacketSocket::DoSetOption(P2PSocketOption option, int value) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK_EQ(state_, IS_OPEN); |
| |
| client_->SetOption(option, value); |
| return 0; |
| } |
| |
| int IpcPacketSocket::GetError() const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return error_; |
| } |
| |
| void IpcPacketSocket::SetError(int error) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| error_ = error; |
| } |
| |
| void IpcPacketSocket::OnOpen(const net::IPEndPoint& local_address, |
| const net::IPEndPoint& remote_address) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| if (!jingle_glue::IPEndPointToSocketAddress(local_address, &local_address_)) { |
| // Always expect correct IPv4 address to be allocated. |
| NOTREACHED(); |
| OnError(); |
| return; |
| } |
| |
| state_ = IS_OPEN; |
| TraceSendThrottlingState(); |
| |
| // Set all pending options if any. |
| for (int i = 0; i < P2P_SOCKET_OPT_MAX; ++i) { |
| if (options_[i] != kDefaultNonSetOptionValue) |
| DoSetOption(static_cast<P2PSocketOption> (i), options_[i]); |
| } |
| |
| SignalAddressReady(this, local_address_); |
| if (IsTcpClientSocket(type_)) { |
| // If remote address is unresolved, set resolved remote IP address received |
| // in the callback. This address will be used while sending the packets |
| // over the network. |
| if (remote_address_.IsUnresolvedIP()) { |
| rtc::SocketAddress jingle_socket_address; |
| // |remote_address| could be unresolved if the connection is behind a |
| // proxy. |
| if (!remote_address.address().empty() && |
| jingle_glue::IPEndPointToSocketAddress( |
| remote_address, &jingle_socket_address)) { |
| // Set only the IP address. |
| remote_address_.SetResolvedIP(jingle_socket_address.ipaddr()); |
| } |
| } |
| |
| // SignalConnect after updating the |remote_address_| so that the listener |
| // can get the resolved remote address. |
| SignalConnect(this); |
| } |
| } |
| |
| void IpcPacketSocket::OnIncomingTcpConnection( |
| const net::IPEndPoint& address, |
| P2PSocketClient* client) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| std::unique_ptr<IpcPacketSocket> socket(new IpcPacketSocket()); |
| |
| rtc::SocketAddress remote_address; |
| if (!jingle_glue::IPEndPointToSocketAddress(address, &remote_address)) { |
| // Always expect correct IPv4 address to be allocated. |
| NOTREACHED(); |
| } |
| socket->InitAcceptedTcp(client, local_address_, remote_address); |
| SignalNewConnection(this, socket.release()); |
| } |
| |
| void IpcPacketSocket::OnSendComplete(const P2PSendPacketMetrics& send_metrics) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| CHECK(!in_flight_packet_records_.empty()); |
| |
| const InFlightPacketRecord& record = in_flight_packet_records_.front(); |
| |
| // Tracking is not turned on for TCP so it's always 0. For UDP, this will |
| // cause a crash when the packet ids don't match. |
| CHECK(send_metrics.packet_id == 0 || |
| record.packet_id == send_metrics.packet_id); |
| |
| send_bytes_available_ += record.packet_size; |
| |
| DCHECK_LE(send_bytes_available_, kMaximumInFlightBytes); |
| |
| in_flight_packet_records_.pop_front(); |
| TraceSendThrottlingState(); |
| |
| int64_t send_time_ms = -1; |
| if (send_metrics.rtc_packet_id >= 0) { |
| send_time_ms = (send_metrics.send_time - base::TimeTicks::UnixEpoch()) |
| .InMilliseconds(); |
| } |
| SignalSentPacket(this, rtc::SentPacket(send_metrics.rtc_packet_id, |
| send_time_ms)); |
| |
| if (writable_signal_expected_ && send_bytes_available_ > 0) { |
| WebRtcLogMessage(base::StringPrintf( |
| "IpcPacketSocket: sending is unblocked. %d packets in flight.", |
| static_cast<int>(in_flight_packet_records_.size()))); |
| |
| SignalReadyToSend(this); |
| writable_signal_expected_ = false; |
| } |
| } |
| |
| void IpcPacketSocket::OnError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| bool was_closed = (state_ == IS_ERROR || state_ == IS_CLOSED); |
| state_ = IS_ERROR; |
| error_ = ECONNABORTED; |
| if (!was_closed) { |
| SignalClose(this, 0); |
| } |
| } |
| |
| void IpcPacketSocket::OnDataReceived(const net::IPEndPoint& address, |
| const std::vector<char>& data, |
| const base::TimeTicks& timestamp) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| rtc::SocketAddress address_lj; |
| |
| if (address.address().empty()) { |
| DCHECK(IsTcpClientSocket(type_)); |
| // |address| could be empty for TCP connections behind a proxy. |
| address_lj = remote_address_; |
| } else { |
| if (!jingle_glue::IPEndPointToSocketAddress(address, &address_lj)) { |
| // We should always be able to convert address here because we |
| // don't expect IPv6 address on IPv4 connections. |
| NOTREACHED(); |
| return; |
| } |
| } |
| |
| rtc::PacketTime packet_time(timestamp.ToInternalValue(), 0); |
| SignalReadPacket(this, &data[0], data.size(), address_lj, |
| packet_time); |
| } |
| |
| AsyncAddressResolverImpl::AsyncAddressResolverImpl( |
| P2PSocketDispatcher* dispatcher) |
| : resolver_(new P2PAsyncAddressResolver(dispatcher)) { |
| } |
| |
| AsyncAddressResolverImpl::~AsyncAddressResolverImpl() { |
| } |
| |
| void AsyncAddressResolverImpl::Start(const rtc::SocketAddress& addr) { |
| DCHECK(CalledOnValidThread()); |
| // Copy port number from |addr|. |port_| must be copied |
| // when resolved address is returned in GetResolvedAddress. |
| port_ = addr.port(); |
| |
| resolver_->Start(addr, base::Bind( |
| &AsyncAddressResolverImpl::OnAddressResolved, |
| base::Unretained(this))); |
| } |
| |
| bool AsyncAddressResolverImpl::GetResolvedAddress( |
| int family, rtc::SocketAddress* addr) const { |
| DCHECK(CalledOnValidThread()); |
| |
| if (addresses_.empty()) |
| return false; |
| |
| for (size_t i = 0; i < addresses_.size(); ++i) { |
| if (family == addresses_[i].family()) { |
| addr->SetResolvedIP(addresses_[i]); |
| addr->SetPort(port_); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| int AsyncAddressResolverImpl::GetError() const { |
| DCHECK(CalledOnValidThread()); |
| return addresses_.empty() ? -1 : 0; |
| } |
| |
| void AsyncAddressResolverImpl::Destroy(bool wait) { |
| DCHECK(CalledOnValidThread()); |
| resolver_->Cancel(); |
| // Libjingle doesn't need this object any more and it's not going to delete |
| // it explicitly. |
| delete this; |
| } |
| |
| void AsyncAddressResolverImpl::OnAddressResolved( |
| const net::IPAddressList& addresses) { |
| DCHECK(CalledOnValidThread()); |
| for (size_t i = 0; i < addresses.size(); ++i) { |
| rtc::SocketAddress socket_address; |
| if (!jingle_glue::IPEndPointToSocketAddress( |
| net::IPEndPoint(addresses[i], 0), &socket_address)) { |
| NOTREACHED(); |
| } |
| addresses_.push_back(socket_address.ipaddr()); |
| } |
| SignalDone(this); |
| } |
| |
| } // namespace |
| |
| IpcPacketSocketFactory::IpcPacketSocketFactory( |
| P2PSocketDispatcher* socket_dispatcher) |
| : socket_dispatcher_(socket_dispatcher) { |
| } |
| |
| IpcPacketSocketFactory::~IpcPacketSocketFactory() { |
| } |
| |
| rtc::AsyncPacketSocket* IpcPacketSocketFactory::CreateUdpSocket( |
| const rtc::SocketAddress& local_address, |
| uint16_t min_port, |
| uint16_t max_port) { |
| P2PSocketClientImpl* socket_client = |
| new P2PSocketClientImpl(socket_dispatcher_); |
| std::unique_ptr<IpcPacketSocket> socket(new IpcPacketSocket()); |
| if (!socket->Init(P2P_SOCKET_UDP, socket_client, local_address, min_port, |
| max_port, rtc::SocketAddress())) { |
| return nullptr; |
| } |
| return socket.release(); |
| } |
| |
| rtc::AsyncPacketSocket* IpcPacketSocketFactory::CreateServerTcpSocket( |
| const rtc::SocketAddress& local_address, |
| uint16_t min_port, |
| uint16_t max_port, |
| int opts) { |
| // TODO(sergeyu): Implement SSL support. |
| if (opts & rtc::PacketSocketFactory::OPT_SSLTCP) |
| return NULL; |
| |
| P2PSocketType type = (opts & rtc::PacketSocketFactory::OPT_STUN) ? |
| P2P_SOCKET_STUN_TCP_SERVER : P2P_SOCKET_TCP_SERVER; |
| P2PSocketClientImpl* socket_client = |
| new P2PSocketClientImpl(socket_dispatcher_); |
| std::unique_ptr<IpcPacketSocket> socket(new IpcPacketSocket()); |
| if (!socket->Init(type, socket_client, local_address, min_port, max_port, |
| rtc::SocketAddress())) { |
| return NULL; |
| } |
| return socket.release(); |
| } |
| |
| rtc::AsyncPacketSocket* IpcPacketSocketFactory::CreateClientTcpSocket( |
| const rtc::SocketAddress& local_address, |
| const rtc::SocketAddress& remote_address, |
| const rtc::ProxyInfo& proxy_info, |
| const std::string& user_agent, int opts) { |
| P2PSocketType type; |
| if (opts & rtc::PacketSocketFactory::OPT_SSLTCP) { |
| type = (opts & rtc::PacketSocketFactory::OPT_STUN) ? |
| P2P_SOCKET_STUN_SSLTCP_CLIENT : P2P_SOCKET_SSLTCP_CLIENT; |
| } else if (opts & rtc::PacketSocketFactory::OPT_TLS) { |
| type = (opts & rtc::PacketSocketFactory::OPT_STUN) ? |
| P2P_SOCKET_STUN_TLS_CLIENT : P2P_SOCKET_TLS_CLIENT; |
| } else { |
| type = (opts & rtc::PacketSocketFactory::OPT_STUN) ? |
| P2P_SOCKET_STUN_TCP_CLIENT : P2P_SOCKET_TCP_CLIENT; |
| } |
| P2PSocketClientImpl* socket_client = |
| new P2PSocketClientImpl(socket_dispatcher_); |
| std::unique_ptr<IpcPacketSocket> socket(new IpcPacketSocket()); |
| if (!socket->Init(type, socket_client, local_address, 0, 0, remote_address)) |
| return NULL; |
| return socket.release(); |
| } |
| |
| rtc::AsyncResolverInterface* |
| IpcPacketSocketFactory::CreateAsyncResolver() { |
| std::unique_ptr<AsyncAddressResolverImpl> resolver( |
| new AsyncAddressResolverImpl(socket_dispatcher_)); |
| return resolver.release(); |
| } |
| |
| } // namespace content |