summaryrefslogtreecommitdiff
path: root/src/network/socket_handler.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/socket_handler.hpp')
-rw-r--r--src/network/socket_handler.hpp186
1 files changed, 156 insertions, 30 deletions
diff --git a/src/network/socket_handler.hpp b/src/network/socket_handler.hpp
index 1be3db1..02311c4 100644
--- a/src/network/socket_handler.hpp
+++ b/src/network/socket_handler.hpp
@@ -1,6 +1,8 @@
#ifndef SOCKET_HANDLER_INCLUDED
# define SOCKET_HANDLER_INCLUDED
+#include <logger/logger.hpp>
+
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
@@ -10,6 +12,26 @@
#include <string>
#include <list>
+#include "config.h"
+
+#ifdef BOTAN_FOUND
+# include <botan/botan.h>
+# include <botan/tls_client.h>
+
+/**
+ * A very simple credential manager that accepts any certificate.
+ */
+class Permissive_Credentials_Manager: public Botan::Credentials_Manager
+{
+public:
+ void verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector<Botan::X509_Certificate>&)
+ { // TODO: Offer the admin to disallow connection on untrusted
+ // certificates
+ log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname);
+ }
+};
+#endif // BOTAN_FOUND
+
typedef int socket_t;
class Poller;
@@ -24,21 +46,19 @@ class SocketHandler
{
protected:
~SocketHandler() {}
+
public:
explicit SocketHandler(std::shared_ptr<Poller> poller);
/**
- * Initialize the socket with the parameters contained in the given
- * addrinfo structure.
- */
- void init_socket(const struct addrinfo* rp);
- /**
- * Connect to the remote server, and call on_connected() if this succeeds
+ * 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);
+ void connect(const std::string& address, const std::string& port, const bool tls);
void connect();
/**
- * Reads data in our in_buf and the call parse_in_buf, for the implementor
- * to handle the data received so far.
+ * 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();
/**
@@ -48,6 +68,9 @@ public:
/**
* 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);
/**
@@ -87,30 +110,95 @@ public:
bool is_connected() const;
bool is_connecting() const;
-protected:
+private:
/**
- * 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
- * can provided, nullptr is returned (the default implementation does
- * that).
+ * Initialize the socket with the parameters contained in the given
+ * addrinfo structure.
*/
- virtual void* get_receive_buffer(const size_t size) const;
+ void init_socket(const struct addrinfo* rp);
/**
- * The handled socket.
+ * 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.
*/
- socket_t socket;
+ ssize_t do_recv(void* recv_buf, const size_t buf_size);
/**
- * 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.
+ * Reads data from the socket and calls parse_in_buffer with it.
*/
- std::string in_buf;
+ 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
+ /**
+ * The handled socket.
+ */
+ socket_t socket;
/**
* Where data is added, when we want to send something to the client.
*/
std::list<std::string> out_buf;
/**
+ * Keep the details of the addrinfo the triggered a EINPROGRESS error when
+ * connect()ing to it, to reuse it directly when connect() is called
+ * again.
+ */
+ struct addrinfo addrinfo;
+ struct sockaddr ai_addr;
+ socklen_t ai_addrlen;
+
+protected:
+ /**
* A pointer to the poller that manages us, because we need to communicate
* with it, sometimes (for example to tell it that he now needs to watch
* write events for us). Do not ever try to delete it.
@@ -120,6 +208,25 @@ protected:
*/
std::shared_ptr<Poller> poller;
/**
+ * 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;
@@ -127,14 +234,6 @@ protected:
* Port we are connected/connecting to
*/
std::string port;
- /**
- * Keep the details of the addrinfo the triggered a EINPROGRESS error when
- * connect()ing to it, to reuse it directly when connect() is called
- * again.
- */
- struct addrinfo addrinfo;
- struct sockaddr ai_addr;
- socklen_t ai_addrlen;
bool connected;
bool connecting;
@@ -144,6 +243,33 @@ private:
SocketHandler(SocketHandler&&) = delete;
SocketHandler& operator=(const SocketHandler&) = delete;
SocketHandler& operator=(SocketHandler&&) = delete;
+
+#ifdef BOTAN_FOUND
+ /**
+ * Botan stuff to manipulate a TLS session.
+ */
+ Botan::AutoSeeded_RNG rng;
+ Permissive_Credentials_Manager credential_manager;
+ Botan::TLS::Policy policy;
+ Botan::TLS::Session_Manager_In_Memory session_manager;
+ /**
+ * 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 as
+ * soon and calls the output_fn callback with it as soon as it is
+ * created. Therefore, we do not want to create it if do not intend to do
+ * 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::string pre_buf;
+#endif // BOTAN_FOUND
};
#endif // SOCKET_HANDLER_INCLUDED