root/chrome/browser/extensions/api/cast_channel/cast_socket.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


// Copyright 2013 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 CHROME_BROWSER_EXTENSIONS_API_CAST_CHANNEL_CAST_SOCKET_H_
#define CHROME_BROWSER_EXTENSIONS_API_CAST_CHANNEL_CAST_SOCKET_H_

#include <queue>
#include <string>

#include "base/basictypes.h"
#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "chrome/common/extensions/api/cast_channel.h"
#include "extensions/browser/api/api_resource.h"
#include "extensions/browser/api/api_resource_manager.h"
#include "net/base/completion_callback.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_log.h"
#include "url/gurl.h"

namespace net {
class AddressList;
class CertVerifier;
class SSLClientSocket;
class StreamSocket;
class TCPClientSocket;
class TransportSecurityState;
}

namespace extensions {
namespace api {
namespace cast_channel {

class CastMessage;

// Size (in bytes) of the largest allowed message payload on the wire (without
// the header).
extern const uint32 kMaxMessageSize;

// Size (in bytes) of the message header.
extern const uint32 kMessageHeaderSize;

// This class implements a channel between Chrome and a Cast device using a TCP
// socket. The channel may be unauthenticated (cast://) or authenticated
// (casts://). All CastSocket objects must be used only on the IO thread.
//
// NOTE: Not called "CastChannel" to reduce confusion with the generated API
// code.
class CastSocket : public ApiResource,
                   public base::SupportsWeakPtr<CastSocket> {
 public:
  // Object to be informed of incoming messages and errors.
  class Delegate {
   public:
    // An error occurred on the channel.
    // It is fine to delete the socket in this callback.
    virtual void OnError(const CastSocket* socket, ChannelError error) = 0;
    // A message was received on the channel.
    // Do NOT delete the socket in this callback.
    virtual void OnMessage(const CastSocket* socket,
                           const MessageInfo& message) = 0;

   protected:
    virtual ~Delegate() {}
  };

  // Creates a new CastSocket to |url|. |owner_extension_id| is the id of the
  // extension that opened the socket.
  CastSocket(const std::string& owner_extension_id,
             const GURL& url,
             CastSocket::Delegate* delegate,
             net::NetLog* net_log);
  virtual ~CastSocket();

  // The URL for the channel.
  const GURL& url() const;

  // Whether to perform receiver authentication.
  bool auth_required() const { return auth_required_; }

  // Channel id for the ApiResourceManager.
  int id() const { return channel_id_; }

  // Sets the channel id.
  void set_id(int channel_id) { channel_id_ = channel_id; }

  // Returns the state of the channel.
  ReadyState ready_state() const { return ready_state_; }

  // Returns the last error that occurred on this channel, or
  // CHANNEL_ERROR_NONE if no error has occurred.
  ChannelError error_state() const { return error_state_; }

  // Connects the channel to the peer. If successful, the channel will be in
  // READY_STATE_OPEN.
  // It is fine to delete the CastSocket object in |callback|.
  virtual void Connect(const net::CompletionCallback& callback);

  // Sends a message over a connected channel. The channel must be in
  // READY_STATE_OPEN.
  //
  // Note that if an error occurs the following happens:
  // 1. Completion callbacks for all pending writes are invoked with error.
  // 2. Delegate::OnError is called once.
  // 3. Castsocket is closed.
  //
  // DO NOT delete the CastSocket object in write completion callback.
  // But it is fine to delete the socket in Delegate::OnError
  virtual void SendMessage(const MessageInfo& message,
                           const net::CompletionCallback& callback);

  // Closes the channel. On completion, the channel will be in
  // READY_STATE_CLOSED.
  // It is fine to delete the CastSocket object in |callback|.
  virtual void Close(const net::CompletionCallback& callback);

  // Fills |channel_info| with the status of this channel.
  virtual void FillChannelInfo(ChannelInfo* channel_info) const;

 private:
  friend class ApiResourceManager<CastSocket>;
  friend class CastSocketTest;

  static const char* service_name() { return "CastSocketManager"; }

  // Internal connection states.
  enum ConnectionState {
    CONN_STATE_NONE,
    CONN_STATE_TCP_CONNECT,
    CONN_STATE_TCP_CONNECT_COMPLETE,
    CONN_STATE_SSL_CONNECT,
    CONN_STATE_SSL_CONNECT_COMPLETE,
    CONN_STATE_AUTH_CHALLENGE_SEND,
    CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE,
    CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE,
  };

  // Internal write states.
  enum WriteState {
    WRITE_STATE_NONE,
    WRITE_STATE_WRITE,
    WRITE_STATE_WRITE_COMPLETE,
    WRITE_STATE_DO_CALLBACK,
    WRITE_STATE_ERROR,
  };

  // Internal read states.
  enum ReadState {
    READ_STATE_NONE,
    READ_STATE_READ,
    READ_STATE_READ_COMPLETE,
    READ_STATE_DO_CALLBACK,
    READ_STATE_ERROR,
  };

  // Creates an instance of TCPClientSocket.
  virtual scoped_ptr<net::TCPClientSocket> CreateTcpSocket();
  // Creates an instance of SSLClientSocket with the given underlying |socket|.
  virtual scoped_ptr<net::SSLClientSocket> CreateSslSocket(
      scoped_ptr<net::StreamSocket> socket);
  // Returns IPEndPoint for the URL to connect to.
  const net::IPEndPoint& ip_endpoint() const { return ip_endpoint_; }
  // Extracts peer certificate from SSLClientSocket instance when the socket
  // is in cert error state.
  // Returns whether certificate is successfully extracted.
  virtual bool ExtractPeerCert(std::string* cert);
  // 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();

  /////////////////////////////////////////////////////////////////////////////
  // 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);
  /////////////////////////////////////////////////////////////////////////////

  /////////////////////////////////////////////////////////////////////////////
  // Following methods work together to implement write flow.
  //
  // Main method that performs write flow state transitions.
  void DoWriteLoop(int result);
  // Each of the below Do* method is executed in the corresponding
  // write state. For example when write state is WRITE_STATE_WRITE_COMPLETE
  // DowriteComplete is called, and so on.
  int DoWrite();
  int DoWriteComplete(int result);
  int DoWriteCallback();
  int DoWriteError(int result);
  /////////////////////////////////////////////////////////////////////////////

  /////////////////////////////////////////////////////////////////////////////
  // Following methods work together to implement read flow.
  //
  // Main method that performs write flow state transitions.
  void DoReadLoop(int result);
  // Each of the below Do* method is executed in the corresponding
  // write state. For example when write state is READ_STATE_READ_COMPLETE
  // DoReadComplete is called, and so on.
  int DoRead();
  int DoReadComplete(int result);
  int DoReadCallback();
  int DoReadError(int result);
  /////////////////////////////////////////////////////////////////////////////

  // Runs the external connection callback and resets it.
  void DoConnectCallback(int result);
  // Verifies that the URL is a valid cast:// or casts:// URL and sets url_ to
  // the result.
  bool ParseChannelUrl(const GURL& url);
  // Adds |message| to the write queue and starts the write loop if needed.
  void SendCastMessageInternal(const CastMessage& message,
                               const net::CompletionCallback& callback);
  void PostTaskToStartConnectLoop(int result);
  void PostTaskToStartReadLoop();
  void StartReadLoop();
  // Parses the contents of header_read_buffer_ and sets current_message_size_
  // to the size of the body of the message.
  bool ProcessHeader();
  // Parses the contents of body_read_buffer_ and sets current_message_ to
  // the message received.
  bool ProcessBody();
  // Closes socket, updating the error state and signaling the delegate that
  // |error| has occurred.
  void CloseWithError(ChannelError error);
  // Serializes the content of message_proto (with a header) to |message_data|.
  static bool Serialize(const CastMessage& message_proto,
                        std::string* message_data);

  virtual bool CalledOnValidThread() const;

  base::ThreadChecker thread_checker_;

  // The id of the channel.
  int channel_id_;

  // The URL of the peer (cast:// or casts://).
  GURL url_;
  // Delegate to inform of incoming messages and errors.
  Delegate* delegate_;
  // True if receiver authentication should be performed.
  bool auth_required_;
  // The IP endpoint of the peer.
  net::IPEndPoint ip_endpoint_;

  // IOBuffer for reading the message header.
  scoped_refptr<net::GrowableIOBuffer> header_read_buffer_;
  // IOBuffer for reading the message body.
  scoped_refptr<net::GrowableIOBuffer> body_read_buffer_;
  // IOBuffer to currently read into.
  scoped_refptr<net::GrowableIOBuffer> current_read_buffer_;
  // The number of bytes in the current message body.
  uint32 current_message_size_;
  // Last message received on the socket.
  scoped_ptr<CastMessage> current_message_;

  // The NetLog for this service.
  net::NetLog* net_log_;
  // The NetLog source for this service.
  net::NetLog::Source net_log_source_;

  // CertVerifier is owned by us but should be deleted AFTER SSLClientSocket
  // since in some cases the destructor of SSLClientSocket may call a method
  // to cancel a cert verification request.
  scoped_ptr<net::CertVerifier> cert_verifier_;
  scoped_ptr<net::TransportSecurityState> transport_security_state_;

  // Owned ptr to the underlying TCP socket.
  scoped_ptr<net::TCPClientSocket> tcp_socket_;
  // Owned ptr to the underlying SSL socket.
  scoped_ptr<net::SSLClientSocket> socket_;
  // Certificate of the peer. This field may be empty if the peer
  // certificate is not yet fetched.
  std::string peer_cert_;
  // Reply received from the receiver to a challenge request.
  scoped_ptr<CastMessage> challenge_reply_;

  // Callback invoked when the socket is connected.
  net::CompletionCallback connect_callback_;

  // 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_;

  // Message header struct. If fields are added, be sure to update
  // kMessageHeaderSize in the .cc.
  struct MessageHeader {
    MessageHeader();
    // Sets the message size.
    void SetMessageSize(size_t message_size);
    // Prepends this header to |str|.
    void PrependToString(std::string* str);
    // Reads |header| from the beginning of |buffer|.
    static void ReadFromIOBuffer(net::GrowableIOBuffer* buffer,
                                 MessageHeader* header);
    std::string ToString();
    // The size of the following protocol message in bytes, in host byte order.
    uint32 message_size;
  };

  // Holds a message to be written to the socket. |callback| is invoked when the
  // message is fully written or an error occurrs.
  struct WriteRequest {
    explicit WriteRequest(const net::CompletionCallback& callback);
    ~WriteRequest();
    // Sets the content of the request by serializing |message| into |io_buffer|
    // and prepending the header.  Must only be called once.
    bool SetContent(const CastMessage& message_proto);

    net::CompletionCallback callback;
    scoped_refptr<net::DrainableIOBuffer> io_buffer;
  };
  // Queue of pending writes. The message at the front of the queue is the one
  // being written.
  std::queue<WriteRequest> write_queue_;

  FRIEND_TEST_ALL_PREFIXES(CastSocketTest, TestCastURLs);
  FRIEND_TEST_ALL_PREFIXES(CastSocketTest, TestRead);
  FRIEND_TEST_ALL_PREFIXES(CastSocketTest, TestReadMany);
  FRIEND_TEST_ALL_PREFIXES(CastSocketTest, TestFullSecureConnectionFlowAsync);
  DISALLOW_COPY_AND_ASSIGN(CastSocket);
};

}  // namespace cast_channel
}  // namespace api
}  // namespace extensions

#endif  // CHROME_BROWSER_EXTENSIONS_API_CAST_CHANNEL_CAST_SOCKET_H_

/* [<][>][^][v][top][bottom][index][help] */