MEDIUM: connection: add error reporting for the SSL

Get a bit more info in the logs when client-side SSL handshakes fail.
diff --git a/include/proto/connection.h b/include/proto/connection.h
index 8a40575..ddfb89e 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -476,7 +476,16 @@
 	case CO_ER_PRX_NOT_HDR:   return "Received something which does not look like a PROXY protocol header";
 	case CO_ER_PRX_BAD_HDR:   return "Received an invalid PROXY protocol header";
 	case CO_ER_PRX_BAD_PROTO: return "Received an unhandled protocol in the PROXY protocol header";
+	case CO_ER_SSL_EMPTY:     return "Connection closed during SSL handshake";
+	case CO_ER_SSL_ABORT:     return "Connection error during SSL handshake";
 	case CO_ER_SSL_TIMEOUT:   return "Timeout during SSL handshake";
+	case CO_ER_SSL_TOO_MANY:  return "Too many SSL connections";
+	case CO_ER_SSL_NO_MEM:    return "Out of memory when initializing an SSL connection";
+	case CO_ER_SSL_RENEG:     return "Rejected a client-initiated SSL renegociation attempt";
+	case CO_ER_SSL_CA_FAIL:   return "SSL client CA chain cannot be verified";
+	case CO_ER_SSL_CRT_FAIL:  return "SSL client certificate not trusted";
+	case CO_ER_SSL_HANDSHAKE: return "SSL handshake failure";
+	case CO_ER_SSL_NO_TARGET: return "Attempt to use SSL on an unknownn target (internal error)";
 	}
 	return NULL;
 }
diff --git a/include/types/connection.h b/include/types/connection.h
index f9bedcd..85ea48c 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -154,7 +154,16 @@
 	CO_ER_PRX_BAD_HDR,      /* bad PROXY protocol header */
 	CO_ER_PRX_BAD_PROTO,    /* unsupported protocol in PROXY header */
 
+	CO_ER_SSL_EMPTY,        /* client closed during SSL handshake */
+	CO_ER_SSL_ABORT,        /* client abort during SSL handshake */
 	CO_ER_SSL_TIMEOUT,      /* timeout during SSL handshake */
+	CO_ER_SSL_TOO_MANY,     /* too many SSL connections */
+	CO_ER_SSL_NO_MEM,       /* no more memory to allocate an SSL connection */
+	CO_ER_SSL_RENEG,        /* forbidden client renegociation */
+	CO_ER_SSL_CA_FAIL,      /* client cert verification failed in the CA chain */
+	CO_ER_SSL_CRT_FAIL,     /* client cert verification failed on the certificate */
+	CO_ER_SSL_HANDSHAKE,    /* SSL error during handshake */
+	CO_ER_SSL_NO_TARGET,    /* unkonwn target (not client nor server) */
 };
 
 /* xprt_ops describes transport-layer operations for a connection. They
diff --git a/src/session.c b/src/session.c
index c22459b..e535035 100644
--- a/src/session.c
+++ b/src/session.c
@@ -286,7 +286,8 @@
 	if (log && (s->fe->options & PR_O_NULLNOLOG)) {
 		/* with "option dontlognull", we don't log connections with no transfer */
 		if (!conn->err_code ||
-		    conn->err_code == CO_ER_PRX_EMPTY || conn->err_code == CO_ER_PRX_ABORT)
+		    conn->err_code == CO_ER_PRX_EMPTY || conn->err_code == CO_ER_PRX_ABORT ||
+		    conn->err_code == CO_ER_SSL_EMPTY || conn->err_code == CO_ER_SSL_ABORT)
 			log = 0;
 	}
 
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index be324c0..35c7bd9 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -93,8 +93,10 @@
 
 	if (where & SSL_CB_HANDSHAKE_START) {
 		/* Disable renegotiation (CVE-2009-3555) */
-		if (conn->flags & CO_FL_CONNECTED)
+		if (conn->flags & CO_FL_CONNECTED) {
 			conn->flags |= CO_FL_ERROR;
+			conn->err_code = CO_ER_SSL_RENEG;
+		}
 	}
 }
 
@@ -128,6 +130,7 @@
 		if (objt_listener(conn->target)->bind_conf->ca_ignerr & (1ULL << err))
 			return 1;
 
+		conn->err_code = CO_ER_SSL_CA_FAIL;
 		return 0;
 	}
 
@@ -138,6 +141,7 @@
 	if (objt_listener(conn->target)->bind_conf->crt_ignerr & (1ULL << err))
 		return 1;
 
+	conn->err_code = CO_ER_SSL_CRT_FAIL;
 	return 0;
 }
 
@@ -800,16 +804,20 @@
 	if (conn->xprt_ctx)
 		return 0;
 
-	if (global.maxsslconn && sslconns >= global.maxsslconn)
+	if (global.maxsslconn && sslconns >= global.maxsslconn) {
+		conn->err_code = CO_ER_SSL_TOO_MANY;
 		return -1;
+	}
 
 	/* If it is in client mode initiate SSL session
 	   in connect state otherwise accept state */
 	if (objt_server(conn->target)) {
 		/* Alloc a new SSL session ctx */
 		conn->xprt_ctx = SSL_new(objt_server(conn->target)->ssl_ctx.ctx);
-		if (!conn->xprt_ctx)
+		if (!conn->xprt_ctx) {
+			conn->err_code = CO_ER_SSL_NO_MEM;
 			return -1;
+		}
 
 		SSL_set_connect_state(conn->xprt_ctx);
 		if (objt_server(conn->target)->ssl_ctx.reused_sess)
@@ -827,8 +835,10 @@
 	else if (objt_listener(conn->target)) {
 		/* Alloc a new SSL session ctx */
 		conn->xprt_ctx = SSL_new(objt_listener(conn->target)->bind_conf->default_ctx);
-		if (!conn->xprt_ctx)
+		if (!conn->xprt_ctx) {
+			conn->err_code = CO_ER_SSL_NO_MEM;
 			return -1;
+		}
 
 		SSL_set_accept_state(conn->xprt_ctx);
 
@@ -845,6 +855,7 @@
 		return 0;
 	}
 	/* don't know how to handle such a target */
+	conn->err_code = CO_ER_SSL_NO_TARGET;
 	return -1;
 }
 
@@ -900,6 +911,15 @@
 				/* if errno is null, then connection was successfully established */
 				if (!errno && conn->flags & CO_FL_WAIT_L4_CONN)
 					conn->flags &= ~CO_FL_WAIT_L4_CONN;
+				if (!conn->err_code) {
+					if (!((SSL *)conn->xprt_ctx)->packet_length)
+						if (!errno)
+							conn->err_code = CO_ER_SSL_EMPTY;
+						else
+							conn->err_code = CO_ER_SSL_ABORT;
+					else
+						conn->err_code = CO_ER_SSL_HANDSHAKE;
+				}
 				goto out_error;
 			}
 			else {
@@ -910,6 +930,8 @@
 				 * data to avoid this as much as possible.
 				 */
 				ret = recv(conn->t.sock.fd, trash.str, trash.size, MSG_NOSIGNAL|MSG_DONTWAIT);
+				if (!conn->err_code)
+					conn->err_code = CO_ER_SSL_HANDSHAKE;
 				goto out_error;
 			}
 		}
@@ -940,6 +962,14 @@
 			/* if errno is null, then connection was successfully established */
 			if (!errno && conn->flags & CO_FL_WAIT_L4_CONN)
 				conn->flags &= ~CO_FL_WAIT_L4_CONN;
+
+			if (!((SSL *)conn->xprt_ctx)->packet_length)
+				if (!errno)
+					conn->err_code = CO_ER_SSL_EMPTY;
+				else
+					conn->err_code = CO_ER_SSL_ABORT;
+			else
+				conn->err_code = CO_ER_SSL_HANDSHAKE;
 			goto out_error;
 		}
 		else {
@@ -950,6 +980,8 @@
 			 * data to avoid this as much as possible.
 			 */
 			ret = recv(conn->t.sock.fd, trash.str, trash.size, MSG_NOSIGNAL|MSG_DONTWAIT);
+			if (!conn->err_code)
+				conn->err_code = CO_ER_SSL_HANDSHAKE;
 			goto out_error;
 		}
 	}
@@ -980,6 +1012,8 @@
 
 	/* Fail on all other handshake errors */
 	conn->flags |= CO_FL_ERROR;
+	if (!conn->err_code)
+		conn->err_code = CO_ER_SSL_HANDSHAKE;
 	return 0;
 }