MEDIUM: ssl: allow to register callbacks for SSL/TLS protocol messages

This patch adds the ability to register callbacks for SSL/TLS protocol
messages by using the function ssl_sock_register_msg_callback().

All registered callback functions will be called when observing received
or sent SSL/TLS protocol messages.
diff --git a/include/proto/ssl_sock.h b/include/proto/ssl_sock.h
index 5e3f603..d029394 100644
--- a/include/proto/ssl_sock.h
+++ b/include/proto/ssl_sock.h
@@ -103,6 +103,12 @@
 
 #define sh_ssl_sess_tree_lookup(k)     (struct sh_ssl_sess_hdr *)ebmb_lookup(sh_ssl_sess_tree, \
                                                                     (k), SSL_MAX_SSL_SESSION_ID_LENGTH);
+
+/* Registers the function <func> in order to be called on SSL/TLS protocol
+ * message processing.
+ */
+int ssl_sock_register_msg_callback(ssl_sock_msg_callback_func func);
+
 #endif /* USE_OPENSSL */
 #endif /* _PROTO_SSL_SOCK_H */
 
diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h
index dbfa3d7..d71924f 100644
--- a/include/types/ssl_sock.h
+++ b/include/types/ssl_sock.h
@@ -31,6 +31,8 @@
 #include <common/mini-clist.h>
 #include <common/openssl-compat.h>
 
+struct connection;
+
 struct pkey_info {
 	uint8_t sig;          /* TLSEXT_signature_[rsa,ecdsa,...] */
 	uint16_t bits;        /* key size in bits */
@@ -202,6 +204,18 @@
 	char *path;
 };
 
+typedef void (*ssl_sock_msg_callback_func)(struct connection *conn,
+	int write_p, int version, int content_type,
+	const void *buf, size_t len, SSL *ssl);
+
+/* This structure contains a function pointer <func> that is called
+ * when observing received or sent SSL/TLS protocol messages, such as
+ * handshake messages or other events that can occur during processing.
+ */
+struct ssl_sock_msg_callback {
+	ssl_sock_msg_callback_func func;
+	struct list list;    /* list of registered callbacks */
+};
 
 #endif /* USE_OPENSSL */
 #endif /* _TYPES_SSL_SOCK_H */
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 16c7227..4a4ca9b 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -629,6 +629,46 @@
 #define sh_ssl_sess_tree_lookup(k)	(struct sh_ssl_sess_hdr *)ebmb_lookup(sh_ssl_sess_tree, \
 								     (k), SSL_MAX_SSL_SESSION_ID_LENGTH);
 
+/* List head of all registered SSL/TLS protocol message callbacks. */
+struct list ssl_sock_msg_callbacks = LIST_HEAD_INIT(ssl_sock_msg_callbacks);
+
+/* Registers the function <func> in order to be called on SSL/TLS protocol
+ * message processing. It will return 0 if the function <func> is not set
+ * or if it fails to allocate memory.
+ */
+int ssl_sock_register_msg_callback(ssl_sock_msg_callback_func func)
+{
+	struct ssl_sock_msg_callback *cbk;
+
+	if (!func)
+		return 0;
+
+	cbk = calloc(1, sizeof(*cbk));
+	if (!cbk) {
+		ha_alert("out of memory in ssl_sock_register_msg_callback().\n");
+		return 0;
+	}
+
+	cbk->func = func;
+
+	LIST_ADDQ(&ssl_sock_msg_callbacks, &cbk->list);
+
+	return 1;
+}
+
+/* Used to free all SSL/TLS protocol message callbacks that were
+ * registered by using ssl_sock_register_msg_callback().
+ */
+static void ssl_sock_unregister_msg_callbacks(void)
+{
+	struct ssl_sock_msg_callback *cbk, *cbkback;
+
+	list_for_each_entry_safe(cbk, cbkback, &ssl_sock_msg_callbacks, list) {
+		LIST_DEL(&cbk->list);
+		free(cbk);
+	}
+}
+
 /*
  * This function gives the detail of the SSL error. It is used only
  * if the debug mode and the verbose mode are activated. It dump all
@@ -1887,11 +1927,13 @@
 /* Callback is called for ssl protocol analyse */
 void ssl_sock_msgcbk(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg)
 {
+	struct connection *conn = SSL_get_ex_data(ssl, ssl_app_data_index);
+	struct ssl_sock_msg_callback *cbk;
+
 #ifdef TLS1_RT_HEARTBEAT
 	/* test heartbeat received (write_p is set to 0
 	   for a received record) */
 	if ((content_type == TLS1_RT_HEARTBEAT) && (write_p == 0)) {
-		struct connection *conn = SSL_get_ex_data(ssl, ssl_app_data_index);
 		struct ssl_sock_ctx *ctx = conn->xprt_ctx;
 		const unsigned char *p = buf;
 		unsigned int payload;
@@ -1928,6 +1970,13 @@
 #endif
 	if (global_ssl.capture_cipherlist > 0)
 		ssl_sock_parse_clienthello(write_p, version, content_type, buf, len, ssl);
+
+	/* Try to call all callback functions that were registered by using
+	 * ssl_sock_register_msg_callback().
+	 */
+	list_for_each_entry(cbk, &ssl_sock_msg_callbacks, list) {
+		cbk->func(conn, write_p, version, content_type, buf, len, ssl);
+	}
 }
 
 #if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
@@ -13100,6 +13149,11 @@
 	BIO_meth_set_gets(ha_meth, ha_ssl_gets);
 
 	HA_SPIN_INIT(&ckch_lock);
+
+	/* Try to free all callbacks that were registered by using
+	 * ssl_sock_register_msg_callback().
+	 */
+	hap_register_post_deinit(ssl_sock_unregister_msg_callbacks);
 }
 
 /* Compute and register the version string */