tcp_socket_handler.hpp 8.48 KB
Newer Older
1 2 3
#ifndef SOCKET_HANDLER_INCLUDED
# define SOCKET_HANDLER_INCLUDED

4 5
#include "louloulibs.h"

6
#include <network/socket_handler.hpp>
7
#include <network/resolver.hpp>
8

9 10
#include <network/credentials_manager.hpp>

11 12 13 14 15
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

louiz’'s avatar
louiz’ committed
16
#include <vector>
17 18 19 20 21 22 23 24 25 26 27 28 29
#include <memory>
#include <string>
#include <list>

/**
 * An interface, with a series of callbacks that should be implemented in
 * subclasses that deal with a socket. These callbacks are called on various events
 * (read/write/timeout, etc) when they are notified to a poller
 * (select/poll/epoll etc)
 */
class TCPSocketHandler: public SocketHandler
{
protected:
30
  ~TCPSocketHandler();
31 32
public:
  explicit TCPSocketHandler(std::shared_ptr<Poller> poller);
louiz’'s avatar
louiz’ committed
33 34 35 36 37
  TCPSocketHandler(const TCPSocketHandler&) = delete;
  TCPSocketHandler(TCPSocketHandler&&) = delete;
  TCPSocketHandler& operator=(const TCPSocketHandler&) = delete;
  TCPSocketHandler& operator=(TCPSocketHandler&&) = delete;

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
  /**
   * Connect to the remote server, and call on_connected() if this
   * succeeds. If tls is true, we set use_tls to true and will also call
   * start_tls() when the connection succeeds.
   */
  void connect(const std::string& address, const std::string& port, const bool tls);
  void connect() override final;
  /**
   * Reads raw data from the socket. And pass it to parse_in_buffer()
   * If we are using TLS on this connection, we call tls_recv()
   */
  void on_recv() override final;
  /**
   * Write as much data from out_buf as possible, in the socket.
   */
  void on_send() override final;
  /**
   * Add the given data to out_buf and tell our poller that we want to be
   * notified when a send event is ready.
   *
   * This can be overriden if we want to modify the data before sending
   * it. For example if we want to encrypt it.
   */
  void send_data(std::string&& data);
  /**
   * Watch the socket for send events, if our out buffer is not empty.
   */
  void send_pending_data();
  /**
   * Close the connection, remove us from the poller
   */
  void close();
  /**
   * Called by a TimedEvent, when the connection did not succeed or fail
   * after a given time.
   */
  void on_connection_timeout();
  /**
   * Called when the connection is successful.
   */
  virtual void on_connected() = 0;
  /**
   * Called when the connection fails. Not when it is closed later, just at
   * the connect() call.
   */
  virtual void on_connection_failed(const std::string& reason) = 0;
  /**
   * Called when we detect a disconnection from the remote host.
   */
  virtual void on_connection_close(const std::string& error) = 0;
  /**
   * Handle/consume (some of) the data received so far.  The data to handle
   * may be in the in_buf buffer, or somewhere else, depending on what
   * get_receive_buffer() returned.  If some data is used from in_buf, it
   * should be truncated, only the unused data should be left untouched.
   *
   * The size argument is the size of the last chunk of data that was added to the buffer.
   */
  virtual void parse_in_buffer(const size_t size) = 0;
97 98 99 100 101 102 103 104 105 106
#ifdef BOTAN_FOUND
  /**
   * Tell whether the credential manager should cancel the connection when the
   * certificate is invalid.
   */
  virtual bool abort_on_invalid_cert() const
  {
    return true;
  }
#endif
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
  bool is_connected() const override final;
  bool is_connecting() const;

private:
  /**
   * Initialize the socket with the parameters contained in the given
   * addrinfo structure.
   */
  void init_socket(const struct addrinfo* rp);
  /**
   * Reads from the socket into the provided buffer.  If an error occurs
   * (read returns <= 0), the handling of the error is done here (close the
   * connection, log a message, etc).
   *
   * Returns the value returned by ::recv(), so the buffer should not be
   * used if it’s not positive.
   */
  ssize_t do_recv(void* recv_buf, const size_t buf_size);
  /**
   * Reads data from the socket and calls parse_in_buffer with it.
   */
  void plain_recv();
  /**
   * Mark the given data as ready to be sent, as-is, on the socket, as soon
   * as we can.
   */
  void raw_send(std::string&& data);

#ifdef BOTAN_FOUND
  /**
   * Create the TLS::Client object, with all the callbacks etc. This must be
   * called only when we know we are able to send TLS-encrypted data over
   * the socket.
   */
  void start_tls();
  /**
   * An additional step to pass the data into our tls object to decrypt it
   * before passing it to parse_in_buffer.
   */
  void tls_recv();
  /**
   * Pass the data to the tls object in order to encrypt it. The tls object
   * will then call raw_send as a callback whenever data as been encrypted
   * and can be sent on the socket.
   */
  void tls_send(std::string&& data);
  /**
   * Called by the tls object that some data has been decrypt. We call
   * parse_in_buffer() to handle that unencrypted data.
   */
  void tls_data_cb(const Botan::byte* data, size_t size);
  /**
   * Called by the tls object to indicate that some data has been encrypted
   * and is now ready to be sent on the socket as is.
   */
  void tls_output_fn(const Botan::byte* data, size_t size);
  /**
   * Called by the tls object to indicate that a TLS alert has been
   * received. We don’t use it, we just log some message, at the moment.
   */
  void tls_alert_cb(Botan::TLS::Alert alert, const Botan::byte*, size_t);
  /**
   * Called by the tls object at the end of the TLS handshake. We don't do
   * anything here appart from logging the TLS session information.
   */
  bool tls_handshake_cb(const Botan::TLS::Session& session);
  /**
   * Called whenever the tls session goes from inactive to active. This
   * means that the handshake has just been successfully done, and we can
   * now proceed to send any available data into our tls object.
   */
  void on_tls_activated();
#endif // BOTAN_FOUND
  /**
   * Where data is added, when we want to send something to the client.
   */
louiz’'s avatar
louiz’ committed
183
  std::vector<std::string> out_buf;
184
  /**
185 186 187 188 189 190 191
   * DNS resolver
   */
  Resolver resolver;
  /**
   * Keep the details of the addrinfo returned by the resolver that
   * triggered a EINPROGRESS error when connect()ing to it, to reuse it
   * directly when connect() is called again.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
   */
  struct addrinfo addrinfo;
  struct sockaddr_in6 ai_addr;
  socklen_t ai_addrlen;

protected:
  /**
   * Where data read from the socket is added until we can extract a full
   * and meaningful “message” from it.
   *
   * TODO: something more efficient than a string.
   */
  std::string in_buf;
  /**
   * Whether we are using TLS on this connection or not.
   */
  bool use_tls;
  /**
   * Provide a buffer in which data can be directly received. This can be
   * used to avoid copying data into in_buf before using it. If no buffer
   * needs to be provided, nullptr is returned (the default implementation
   * does that), in that case our internal in_buf will be used to save the
   * data until it can be used by parse_in_buffer().
   */
  virtual void* get_receive_buffer(const size_t size) const;
  /**
   * Hostname we are connected/connecting to
   */
  std::string address;
  /**
   * Port we are connected/connecting to
   */
  std::string port;

  bool connected;
  bool connecting;

229 230
  bool hostname_resolution_failed;

louiz’'s avatar
louiz’ committed
231 232 233 234 235 236
  /**
   * Address to bind the socket to, before calling connect().
   * If empty, it’s equivalent to binding to INADDR_ANY.
   */
  std::string bind_addr;

237
private:
238 239 240 241 242
  /**
   * Display the resolved IP, just for information purpose.
   */
  void display_resolved_ip(struct addrinfo* rp) const;

243 244 245 246 247 248 249
#ifdef BOTAN_FOUND
  /**
   * Botan stuff to manipulate a TLS session.
   */
  static Botan::AutoSeeded_RNG rng;
  static Botan::TLS::Policy policy;
  static Botan::TLS::Session_Manager_In_Memory session_manager;
250
protected:
louiz’'s avatar
louiz’ committed
251
  BasicCredentialsManager credential_manager;
252
private:
253 254
  /**
   * We use a unique_ptr because we may not want to create the object at
louiz’'s avatar
louiz’ committed
255 256 257 258 259
   * all. The Botan::TLS::Client object generates a handshake message and
   * calls the output_fn callback with it as soon as it is created.
   * Therefore, we do not want to create it if we do not intend to send any
   * TLS-encrypted message. We create the object only when needed (for
   * example after we have negociated a TLS session using a STARTTLS
260 261 262 263 264 265 266 267 268 269 270 271 272 273
   * message, or stuf like that).
   *
   * See start_tls for the method where this object is created.
   */
  std::unique_ptr<Botan::TLS::Client> tls;
  /**
   * An additional buffer to keep data that the user wants to send, but
   * cannot because the handshake is not done.
   */
  std::string pre_buf;
#endif // BOTAN_FOUND
};

#endif // SOCKET_HANDLER_INCLUDED
274