| // 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. |
| |
| #ifndef COMPONENTS_CAST_CHANNEL_CAST_SOCKET_H_ |
| #define COMPONENTS_CAST_CHANNEL_CAST_SOCKET_H_ |
| |
| #include <stdint.h> |
| |
| #include <queue> |
| #include <string> |
| |
| #include "base/cancelable_callback.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list.h" |
| #include "base/threading/thread_checker.h" |
| #include "base/timer/timer.h" |
| #include "components/cast_channel/cast_auth_util.h" |
| #include "components/cast_channel/cast_channel_enum.h" |
| #include "components/cast_channel/cast_socket.h" |
| #include "components/cast_channel/cast_transport.h" |
| #include "net/base/completion_once_callback.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/log/net_log_source.h" |
| #include "services/network/public/mojom/network_context.mojom.h" |
| #include "services/network/public/mojom/tls_socket.mojom.h" |
| |
| namespace net { |
| class X509Certificate; |
| } |
| |
| namespace cast_channel { |
| class CastMessage; |
| class Logger; |
| class MojoDataPump; |
| struct LastError; |
| |
| // Cast device capabilities. |
| enum CastDeviceCapability : int { |
| NONE = 0, |
| VIDEO_OUT = 1 << 0, |
| VIDEO_IN = 1 << 1, |
| AUDIO_OUT = 1 << 2, |
| AUDIO_IN = 1 << 3, |
| DEV_MODE = 1 << 4, |
| MULTIZONE_GROUP = 1 << 5 |
| }; |
| |
| // Public interface of the CastSocket class. |
| class CastSocket { |
| public: |
| // Invoked when CastSocket opens. |
| // |socket|: raw pointer of opened socket (this pointer). Guaranteed to be |
| // valid in callback function. Do not pass |socket| around. |
| using OnOpenCallback = base::OnceCallback<void(CastSocket* socket)>; |
| |
| class Observer { |
| public: |
| virtual ~Observer() {} |
| |
| // Invoked when an error occurs on |socket|. |
| virtual void OnError(const CastSocket& socket, |
| ChannelError error_state) = 0; |
| |
| // Invoked when |socket| receives a message. |
| virtual void OnMessage(const CastSocket& socket, |
| const CastMessage& message) = 0; |
| |
| virtual void OnReadyStateChanged(const CastSocket& socket); |
| }; |
| |
| virtual ~CastSocket() {} |
| |
| // Used by BrowserContextKeyedAPIFactory. |
| static const char* service_name() { return "CastSocketImplManager"; } |
| |
| // Connects the channel to the peer. If successful, the channel will be in |
| // READY_STATE_OPEN. DO NOT delete the CastSocket object in |callback|. |
| // Instead use Close(). |
| // |callback| will be invoked with any ChannelError that occurred, or |
| // CHANNEL_ERROR_NONE if successful. |
| // If the CastSocket is destroyed while the connection is pending, |callback| |
| // will be invoked with CHANNEL_ERROR_UNKNOWN. In this case, invoking |
| // |callback| must not result in any re-entrancy behavior. |
| virtual void Connect(OnOpenCallback callback) = 0; |
| |
| // Closes the channel if not already closed. On completion, the channel will |
| // be in READY_STATE_CLOSED. |
| // |
| // It is fine to delete this object in |callback|. |
| virtual void Close(net::CompletionOnceCallback callback) = 0; |
| |
| // The IP endpoint for the destination of the channel. |
| virtual const net::IPEndPoint& ip_endpoint() const = 0; |
| |
| // Channel id generated by the CastChannelService. |
| virtual int id() const = 0; |
| |
| // Sets the channel id generated by CastChannelService. |
| virtual void set_id(int id) = 0; |
| |
| // The ready state of the channel. |
| virtual ReadyState ready_state() const = 0; |
| |
| // Returns the last error that occurred on this channel, or |
| // CHANNEL_ERROR_NONE if no error has occurred. |
| virtual ChannelError error_state() const = 0; |
| |
| // True when keep-alive signaling is handled for this socket. |
| virtual bool keep_alive() const = 0; |
| |
| // Whether the channel is audio only as identified by the device |
| // certificate during channel authentication. |
| virtual bool audio_only() const = 0; |
| |
| // Marks a socket as invalid due to an error, and sends an OnError |
| // event to |delegate_|. |
| // The OnError event receipient is responsible for closing the socket in the |
| // event of an error. |
| // Setting the error state does not close the socket if it is open. |
| virtual void SetErrorState(ChannelError error_state) = 0; |
| |
| // Returns a pointer to the socket's message transport layer. Can be used to |
| // send and receive CastMessages over the socket. |
| virtual CastTransport* transport() const = 0; |
| |
| // Registers |observer| with the socket to receive messages and error events. |
| virtual void AddObserver(Observer* observer) = 0; |
| |
| // Unregisters |observer|. |
| virtual void RemoveObserver(Observer* observer) = 0; |
| }; |
| |
| // Holds parameters necessary to open a Cast channel (CastSocket) to a Cast |
| // device. |
| struct CastSocketOpenParams { |
| // IP endpoint of the Cast device. |
| net::IPEndPoint ip_endpoint; |
| |
| // Connection timeout interval. If this value is not set, Cast socket will not |
| // report CONNECT_TIMEOUT error and may hang when connecting to a Cast device. |
| base::TimeDelta connect_timeout; |
| |
| // Amount of idle time to wait before disconnecting. Cast socket will ping |
| // Cast device periodically at |ping_interval| to check liveness. If it does |
| // not receive response in |liveness_timeout|, it reports PING_TIMEOUT error. |
| // |liveness_timeout| should always be larger than or equal to |
| // |ping_interval|. |
| // If this value is not set, there is not periodic ping and Cast socket is |
| // always assumed alive. |
| base::TimeDelta liveness_timeout; |
| |
| // Amount of idle time to wait before pinging the Cast device. See comments |
| // for |liveness_timeout|. |
| base::TimeDelta ping_interval; |
| |
| // A bit vector representing the capabilities of the sink. The values are |
| // defined in components/cast_channel/cast_socket.h. |
| uint64_t device_capabilities; |
| |
| CastSocketOpenParams(const net::IPEndPoint& ip_endpoint, |
| base::TimeDelta connect_timeout); |
| CastSocketOpenParams(const net::IPEndPoint& ip_endpoint, |
| base::TimeDelta connect_timeout, |
| base::TimeDelta liveness_timeout, |
| base::TimeDelta ping_interval, |
| uint64_t device_capabilities); |
| }; |
| |
| // This class implements a channel between Chrome and a Cast device using a TCP |
| // socket with SSL. The channel may authenticate that the receiver is a genuine |
| // Cast device. All CastSocketImpl objects must be used only on the IO thread. |
| // |
| // NOTE: Not called "CastChannel" to reduce confusion with the generated API |
| // code. |
| class CastSocketImpl : public CastSocket { |
| public: |
| using NetworkContextGetter = |
| base::RepeatingCallback<network::mojom::NetworkContext*()>; |
| CastSocketImpl(NetworkContextGetter network_context_getter, |
| const CastSocketOpenParams& open_params, |
| const scoped_refptr<Logger>& logger); |
| |
| CastSocketImpl(NetworkContextGetter network_context_getter, |
| const CastSocketOpenParams& open_params, |
| const scoped_refptr<Logger>& logger, |
| const AuthContext& auth_context); |
| |
| // Ensures that the socket is closed. |
| ~CastSocketImpl() override; |
| |
| // CastSocket interface. |
| void Connect(OnOpenCallback callback) override; |
| CastTransport* transport() const override; |
| void Close(net::CompletionOnceCallback callback) override; |
| const net::IPEndPoint& ip_endpoint() const override; |
| int id() const override; |
| void set_id(int channel_id) override; |
| ReadyState ready_state() const override; |
| ChannelError error_state() const override; |
| bool keep_alive() const override; |
| bool audio_only() const override; |
| void AddObserver(Observer* observer) override; |
| void RemoveObserver(Observer* observer) override; |
| |
| static net::NetworkTrafficAnnotationTag GetNetworkTrafficAnnotationTag(); |
| |
| protected: |
| // CastTransport::Delegate methods for receiving handshake messages. |
| class AuthTransportDelegate : public CastTransport::Delegate { |
| public: |
| explicit AuthTransportDelegate(CastSocketImpl* socket); |
| |
| // Gets the error state of the channel. |
| // Returns CHANNEL_ERROR_NONE if no errors are present. |
| ChannelError error_state() const; |
| |
| // Gets recorded error details. |
| LastError last_error() const; |
| |
| // CastTransport::Delegate interface. |
| void OnError(ChannelError error_state) override; |
| void OnMessage(const CastMessage& message) override; |
| void Start() override; |
| |
| private: |
| CastSocketImpl* socket_; |
| ChannelError error_state_; |
| LastError last_error_; |
| }; |
| |
| // CastTransport::Delegate methods to receive normal messages and errors. |
| class CastSocketMessageDelegate : public CastTransport::Delegate { |
| public: |
| CastSocketMessageDelegate(CastSocketImpl* socket); |
| ~CastSocketMessageDelegate() override; |
| |
| // CastTransport::Delegate implementation. |
| void OnError(ChannelError error_state) override; |
| void OnMessage(const CastMessage& message) override; |
| void Start() override; |
| |
| private: |
| CastSocketImpl* const socket_; |
| DISALLOW_COPY_AND_ASSIGN(CastSocketMessageDelegate); |
| }; |
| |
| // Replaces the internally-constructed transport object with one provided |
| // by the caller (e.g. a mock). |
| void SetTransportForTesting(std::unique_ptr<CastTransport> transport); |
| |
| void SetPeerCertForTesting(scoped_refptr<net::X509Certificate> peer_cert); |
| |
| // Verifies whether the socket complies with cast channel policy. |
| // Audio only channel policy mandates that a device declaring a video out |
| // capability must not have a certificate with audio only policy. |
| bool VerifyChannelPolicy(const AuthResult& result); |
| |
| void Connect(); |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(MockCastSocketTest, TestObservers); |
| friend class AuthTransportDelegate; |
| |
| void SetErrorState(ChannelError error_state) override; |
| |
| // Frees resources and cancels pending callbacks. |ready_state_| will be set |
| // READY_STATE_CLOSED on completion. A no-op if |ready_state_| is already |
| // READY_STATE_CLOSED. |
| void CloseInternal(); |
| |
| // Verifies whether the challenge reply received from the peer is valid: |
| // 1. Signature in the reply is valid. |
| // 2. Certificate is rooted to a trusted CA. |
| virtual bool VerifyChallengeReply(); |
| |
| // Invoked by a cancelable closure when connection setup time |
| // exceeds the interval specified at |connect_timeout|. |
| void OnConnectTimeout(); |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // Following methods work together to implement the following flow: |
| // 1. Create a new TCP socket and connect to it |
| // 2. Create a new SSL socket and try connecting to it |
| // 3. If connection fails due to invalid cert authority, then extract the |
| // peer certificate from the error. |
| // 4. Whitelist the peer certificate and try #1 and #2 again. |
| // 5. If SSL socket is connected successfully, and if protocol is casts:// |
| // then issue an auth challenge request. |
| // 6. Validate the auth challenge response. |
| // |
| // Main method that performs connection state transitions. |
| void DoConnectLoop(int result); |
| // Each of the below Do* method is executed in the corresponding |
| // connection state. For example when connection state is TCP_CONNECT |
| // DoTcpConnect is called, and so on. |
| int DoTcpConnect(); |
| int DoTcpConnectComplete(int result); |
| int DoSslConnect(); |
| int DoSslConnectComplete(int result); |
| int DoAuthChallengeSend(); |
| int DoAuthChallengeSendComplete(int result); |
| int DoAuthChallengeReplyComplete(int result); |
| |
| // Callback from network::mojom::NetworkContext::CreateTCPConnectedSocket. |
| void OnConnect(int result, |
| const base::Optional<net::IPEndPoint>& local_addr, |
| const base::Optional<net::IPEndPoint>& peer_addr, |
| mojo::ScopedDataPipeConsumerHandle receive_stream, |
| mojo::ScopedDataPipeProducerHandle send_stream); |
| void OnUpgradeToTLS(int result, |
| mojo::ScopedDataPipeConsumerHandle receive_stream, |
| mojo::ScopedDataPipeProducerHandle send_stream, |
| const base::Optional<net::SSLInfo>& ssl_info); |
| ///////////////////////////////////////////////////////////////////////////// |
| |
| // Resets the cancellable callback used for async invocations of |
| // DoConnectLoop. |
| void ResetConnectLoopCallback(); |
| |
| // Posts a task to invoke |connect_loop_callback_| with |result| on the |
| // current message loop. |
| void PostTaskToStartConnectLoop(int result); |
| |
| // Runs the external connection callback and resets it. |
| void DoConnectCallback(); |
| |
| virtual base::OneShotTimer* GetTimer(); |
| |
| void SetConnectState(ConnectionState connect_state); |
| void SetReadyState(ReadyState ready_state); |
| |
| THREAD_CHECKER(thread_checker_); |
| |
| // The id of the channel. |
| int channel_id_; |
| |
| // Cast socket related settings. |
| CastSocketOpenParams open_params_; |
| |
| // Shared logging object, used to log CastSocket events for diagnostics. |
| scoped_refptr<Logger> logger_; |
| |
| NetworkContextGetter network_context_getter_; |
| |
| // Owned ptr to the underlying TCP socket. |
| network::mojom::TCPConnectedSocketPtr tcp_socket_; |
| |
| // Owned ptr to the underlying SSL socket. |
| network::mojom::TLSClientSocketPtr socket_; |
| |
| // Helper class to write to the SSL socket. |
| std::unique_ptr<MojoDataPump> mojo_data_pump_; |
| |
| // Certificate of the peer. This field may be empty if the peer |
| // certificate is not yet fetched. |
| scoped_refptr<net::X509Certificate> peer_cert_; |
| |
| // The challenge context for the current connection. |
| const AuthContext auth_context_; |
| |
| // Reply received from the receiver to a challenge request. |
| std::unique_ptr<CastMessage> challenge_reply_; |
| |
| // Callbacks invoked when the socket is connected or fails to connect. |
| std::vector<OnOpenCallback> connect_callbacks_; |
| |
| // Callback invoked by |connect_timeout_timer_| to cancel the connection. |
| base::CancelableClosure connect_timeout_callback_; |
| |
| // Timer invoked when the connection has timed out. |
| std::unique_ptr<base::OneShotTimer> connect_timeout_timer_; |
| |
| // Set when a timeout is triggered and the connection process has |
| // canceled. |
| bool is_canceled_; |
| |
| // Whether the channel is audio only as identified by the device |
| // certificate during channel authentication. |
| bool audio_only_; |
| |
| // Connection flow state machine state. |
| ConnectionState connect_state_; |
| |
| // Write flow state machine state. |
| WriteState write_state_; |
| |
| // Read flow state machine state. |
| ReadState read_state_; |
| |
| // The last error encountered by the channel. |
| ChannelError error_state_; |
| |
| // The current status of the channel. |
| ReadyState ready_state_; |
| |
| // Callback which, when invoked, will re-enter the connection state machine. |
| // Oustanding callbacks will be cancelled when |this| is destroyed. |
| // The callback signature is based on net::CompletionCallback, which passes |
| // operation result codes as byte counts in the success case, or as |
| // net::Error enum values for error cases. |
| base::CancelableCallback<void(int)> connect_loop_callback_; |
| |
| // Cast message formatting and parsing layer. |
| std::unique_ptr<CastTransport> transport_; |
| |
| // Caller's message read and error handling delegate. |
| std::unique_ptr<CastTransport::Delegate> delegate_; |
| |
| // Raw pointer to the auth handshake delegate. Used to get detailed error |
| // information. |
| AuthTransportDelegate* auth_delegate_; |
| |
| // List of socket observers. |
| base::ObserverList<Observer>::Unchecked observers_; |
| |
| base::WeakPtrFactory<CastSocketImpl> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CastSocketImpl); |
| }; |
| } // namespace cast_channel |
| |
| #endif // COMPONENTS_CAST_CHANNEL_CAST_SOCKET_H_ |