summaryrefslogtreecommitdiff
path: root/louloulibs/network/tcp_socket_handler.hpp
blob: 600405d797f7ec262b673bc969c4495230e4542a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
97
98
99
100
101
102
103
104
105
106
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
183
184
185
186
187
188
189
190
191
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#pragma once

#include "louloulibs.h"

#include <network/socket_handler.hpp>
#include <network/resolver.hpp>

#include <network/credentials_manager.hpp>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <chrono>
#include <vector>
#include <memory>
#include <string>
#include <list>

#ifdef BOTAN_FOUND

# include <botan/types.h>
# include <botan/botan.h>
# include <botan/tls_session_manager.h>

class BiboumiTLSPolicy: public Botan::TLS::Policy
{
public:
# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,33)
  bool use_ecc_point_compression() const override
  {
    return true;
  }
  bool require_cert_revocation_info() const override
  {
    return false;
  }
# endif
};

# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32)
#  define BOTAN_TLS_CALLBACKS_OVERRIDE override final
# else
#  define BOTAN_TLS_CALLBACKS_OVERRIDE
# endif
#endif

/**
 * Does all the read/write, buffering etc. With optional tls.
 * But doesn’t do any connect() or accept() or anything else.
 */
class TCPSocketHandler: public SocketHandler
#ifdef BOTAN_FOUND
# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32)
    ,public Botan::TLS::Callbacks
# endif
#endif
{
protected:
  ~TCPSocketHandler();
public:
  explicit TCPSocketHandler(std::shared_ptr<Poller> poller);
  TCPSocketHandler(const TCPSocketHandler&) = delete;
  TCPSocketHandler(TCPSocketHandler&&) = delete;
  TCPSocketHandler& operator=(const TCPSocketHandler&) = delete;
  TCPSocketHandler& operator=(TCPSocketHandler&&) = delete;

  /**
   * 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
   */
  virtual void close();
  /**
   * 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.
   *
   * The function should call consume_in_buffer, with the size that was consumed by the
   * “parsing”, and thus to be removed from the input buffer.
   */
  virtual void parse_in_buffer(const size_t size) = 0;
#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
  bool is_using_tls() const;

private:
  /**
   * 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);

 protected:
  virtual bool is_connecting() const = 0;
#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(const std::string& address, const std::string& port);
 private:
  /**
   * 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_record_received(uint64_t rec_no, const Botan::byte* data, size_t size) BOTAN_TLS_CALLBACKS_OVERRIDE;
  /**
   * 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_emit_data(const Botan::byte* data, size_t size) BOTAN_TLS_CALLBACKS_OVERRIDE;
  /**
   * 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(Botan::TLS::Alert alert) BOTAN_TLS_CALLBACKS_OVERRIDE;
  /**
   * 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_session_established(const Botan::TLS::Session& session) BOTAN_TLS_CALLBACKS_OVERRIDE;

#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,34)
  void tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& cert_chain,
                             const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
                             const std::vector<Botan::Certificate_Store*>& trusted_roots,
                             Botan::Usage_Type usage,
                             const std::string& hostname,
                             const Botan::TLS::Policy& policy) BOTAN_TLS_CALLBACKS_OVERRIDE;
#endif
  /**
   * 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.
   */
  std::vector<std::string> out_buf;
protected:
  /**
   * Whether we are using TLS on this connection or not.
   */
  bool use_tls;
  /**
   * 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;
  /**
   * Remove the given “size” first bytes from our in_buf.
   */
  void consume_in_buffer(const std::size_t size);
  /**
   * 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;
  /**
   * Called when we detect a disconnection from the remote host.
   */
  virtual void on_connection_close(const std::string&) {}
  virtual void on_connection_failed(const std::string&) {}

#ifdef BOTAN_FOUND
protected:
  BasicCredentialsManager credential_manager;
private:
  /**
   * We use a unique_ptr because we may not want to create the object at
   * 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
   * 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::vector<Botan::byte> pre_buf;
#endif // BOTAN_FOUND
};