REORG: shctx: move ssl functions to ssl_sock.c

Move the ssl callback functions of the ssl shared session cache to
ssl_sock.c. The shctx functions still needs to be separated of the ssl
tree and data.
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 24e4f21..71bcbe3 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -3718,6 +3718,191 @@
 	return cfgerr;
 }
 
+/* SSL context callbacks */
+
+/* SSL callback used on new session creation */
+int shctx_new_cb(SSL *ssl, SSL_SESSION *sess)
+{
+	unsigned char encsess[SHSESS_MAX_DATA_LEN];           /* encoded session  */
+	unsigned char encid[SSL_MAX_SSL_SESSION_ID_LENGTH];   /* encoded id */
+	unsigned char *p;
+	int data_len;
+	unsigned int sid_length, sid_ctx_length;
+	const unsigned char *sid_data;
+	const unsigned char *sid_ctx_data;
+
+	/* Session id is already stored in to key and session id is known
+	 * so we dont store it to keep size.
+	 */
+
+	sid_data = SSL_SESSION_get_id(sess, &sid_length);
+	sid_ctx_data = SSL_SESSION_get0_id_context(sess, &sid_ctx_length);
+	SSL_SESSION_set1_id(sess, sid_data, 0);
+	SSL_SESSION_set1_id_context(sess, sid_ctx_data, 0);
+
+	/* check if buffer is large enough for the ASN1 encoded session */
+	data_len = i2d_SSL_SESSION(sess, NULL);
+	if (data_len > SHSESS_MAX_DATA_LEN)
+		goto err;
+
+	p = encsess;
+
+	/* process ASN1 session encoding before the lock */
+	i2d_SSL_SESSION(sess, &p);
+
+	memcpy(encid, sid_data, sid_length);
+	if (sid_length < SSL_MAX_SSL_SESSION_ID_LENGTH)
+		memset(encid + sid_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH-sid_length);
+
+	shared_context_lock(ssl_shctx);
+
+	/* store to cache */
+	shsess_store(ssl_shctx, encid, encsess, data_len);
+
+	shared_context_unlock(ssl_shctx);
+
+err:
+	/* reset original length values */
+	SSL_SESSION_set1_id(sess, sid_data, sid_length);
+	SSL_SESSION_set1_id_context(sess, sid_ctx_data, sid_ctx_length);
+
+	return 0; /* do not increment session reference count */
+}
+
+/* SSL callback used on lookup an existing session cause none found in internal cache */
+SSL_SESSION *shctx_get_cb(SSL *ssl, __OPENSSL_110_CONST__ unsigned char *key, int key_len, int *do_copy)
+{
+	struct shared_session *shsess;
+	unsigned char data[SHSESS_MAX_DATA_LEN], *p;
+	unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH];
+	int data_len;
+	SSL_SESSION *sess;
+
+	global.shctx_lookups++;
+
+	/* allow the session to be freed automatically by openssl */
+	*do_copy = 0;
+
+	/* tree key is zeros padded sessionid */
+	if (key_len < SSL_MAX_SSL_SESSION_ID_LENGTH) {
+		memcpy(tmpkey, key, key_len);
+		memset(tmpkey + key_len, 0, SSL_MAX_SSL_SESSION_ID_LENGTH - key_len);
+		key = tmpkey;
+	}
+
+	/* lock cache */
+	shared_context_lock(ssl_shctx);
+
+	/* lookup for session */
+	shsess = shsess_tree_lookup(ssl_shctx, key);
+	if (!shsess) {
+		/* no session found: unlock cache and exit */
+		shared_context_unlock(ssl_shctx);
+		global.shctx_misses++;
+		return NULL;
+	}
+
+	data_len = ((struct shared_block *)shsess)->data_len;
+	if (data_len <= sizeof(shsess->data)) {
+		/* Session stored on single block */
+		memcpy(data, shsess->data, data_len);
+		shblock_set_active(ssl_shctx, (struct shared_block *)shsess);
+	}
+	else {
+		/* Session stored on multiple blocks */
+		struct shared_block *block;
+
+		memcpy(data, shsess->data, sizeof(shsess->data));
+		p = data + sizeof(shsess->data);
+		block = ((struct shared_block *)shsess)->n;
+		shblock_set_active(ssl_shctx, (struct shared_block *)shsess);
+		while (1) {
+			/* Retrieve data from next block */
+			struct shared_block *next;
+
+			if (block->data_len <= sizeof(block->data.data)) {
+				/* This is the last block */
+				memcpy(p, block->data.data, block->data_len);
+				p += block->data_len;
+				shblock_set_active(ssl_shctx, block);
+				break;
+			}
+			/* Intermediate block */
+			memcpy(p, block->data.data, sizeof(block->data.data));
+			p += sizeof(block->data.data);
+			next = block->n;
+			shblock_set_active(ssl_shctx, block);
+			block = next;
+		}
+	}
+
+	shared_context_unlock(ssl_shctx);
+
+	/* decode ASN1 session */
+	p = data;
+	sess = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, data_len);
+	/* Reset session id and session id contenxt */
+	if (sess) {
+		SSL_SESSION_set1_id(sess, key, key_len);
+		SSL_SESSION_set1_id_context(sess, (const unsigned char *)SHCTX_APPNAME, strlen(SHCTX_APPNAME));
+	}
+
+	return sess;
+}
+
+/* SSL callback used to signal session is no more used in internal cache */
+void shctx_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess)
+{
+	struct shared_session *shsess;
+	unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH];
+	unsigned int sid_length;
+	const unsigned char *sid_data;
+	(void)ctx;
+
+	sid_data = SSL_SESSION_get_id(sess, &sid_length);
+	/* tree key is zeros padded sessionid */
+	if (sid_length < SSL_MAX_SSL_SESSION_ID_LENGTH) {
+		memcpy(tmpkey, sid_data, sid_length);
+		memset(tmpkey+sid_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH - sid_length);
+		sid_data = tmpkey;
+	}
+
+	shared_context_lock(ssl_shctx);
+
+	/* lookup for session */
+	shsess = shsess_tree_lookup(ssl_shctx, sid_data);
+	if (shsess) {
+		/* free session */
+		shsess_tree_delete(shsess);
+		shsess_free(ssl_shctx, shsess);
+	}
+
+	/* unlock cache */
+	shared_context_unlock(ssl_shctx);
+}
+
+/* Set session cache mode to server and disable openssl internal cache.
+ * Set shared cache callbacks on an ssl context.
+ * Shared context MUST be firstly initialized */
+void shared_context_set_cache(SSL_CTX *ctx)
+{
+	SSL_CTX_set_session_id_context(ctx, (const unsigned char *)SHCTX_APPNAME, strlen(SHCTX_APPNAME));
+
+	if (!ssl_shctx) {
+		SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+		return;
+	}
+
+	SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER |
+	                                    SSL_SESS_CACHE_NO_INTERNAL |
+	                                    SSL_SESS_CACHE_NO_AUTO_CLEAR);
+
+	/* Set callbacks */
+	SSL_CTX_sess_set_new_cb(ctx, shctx_new_cb);
+	SSL_CTX_sess_set_get_cb(ctx, shctx_get_cb);
+	SSL_CTX_sess_set_remove_cb(ctx, shctx_remove_cb);
+}
+
 int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf, SSL_CTX *ctx)
 {
 	struct proxy *curproxy = bind_conf->frontend;