BUG/MINOR: checks: don't consider errno and use conn->err_code

The last fix on checks (02b0f58: BUG/MEDIUM: checks: fix a long-standing
issue with reporting connection errors) tried to isolate error codes
retrieved from the socket in order to report appropriate messages. The
only thing is that we must not pre-initialize err to errno since we're
not in I/O context anymore and errno will be the one of the last syscall
(whatever it was). However we can complete the message with more info
from the transport layer (eg: SSL can inform us we were in a handshake).

Also add a catch-all case for CO_FL_ERROR when the connection was
established. No check currently seem to leave this case open, but better
catch it because it's hard to find all possible cases.

Error handling in checks is complex because some stuff must be done in
the central task (mandatory at least for timeouts) and other stuff is
done closer to the data.

Since checks have their own buffers now, we could move everything to
the main task and only keep the low-level I/O for sending/retrieving
data to/from this buffer. It would also avoid sending logs from the
I/O context!
diff --git a/src/checks.c b/src/checks.c
index 564eab4..8b823bc 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -1467,31 +1467,58 @@
 		 * which can happen on connect timeout or error.
 		 */
 		if (s->check.result == SRV_CHK_UNKNOWN) {
-			int skerr, err = errno;
+			int skerr, err;
 			socklen_t lskerr = sizeof(skerr);
+			const char *err_msg;
 
+			err = 0;
 			if (conn->ctrl) {
 				if (getsockopt(conn->t.sock.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0 && skerr)
 					err = skerr;
+				/* EAGAIN is normal on a timeout */
+				if (err == EAGAIN)
+					err = 0;
 			}
 
-			/* eagain is normal on a timeout */
-			if (err == EAGAIN)
-				err = 0;
+			/* we'll try to build a meaningful error message
+			 * depending on the context of the error possibly
+			 * present in conn->err_type, and the socket error
+			 * possibly collected above.
+			 */
+			if (conn->err_code) {
+				if (err)
+					chunk_printf(&trash, "%s (%s)", conn_err_code_str(conn), strerror(err));
+				else
+					chunk_printf(&trash, "%s", conn_err_code_str(conn));
+				err_msg = trash.str;
+			}
+			else {
+				if (err) {
+					chunk_printf(&trash, "%s", strerror(err));
+					err_msg = trash.str;
+				}
+				else {
+					err_msg = NULL;
+				}
+			}
 
 			if ((conn->flags & (CO_FL_CONNECTED|CO_FL_WAIT_L4_CONN)) == CO_FL_WAIT_L4_CONN) {
 				/* L4 not established (yet) */
 				if (conn->flags & CO_FL_ERROR)
-					set_server_check_status(check, HCHK_STATUS_L4CON, err ? strerror(err) : NULL);
+					set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
 				else if (expired)
-					set_server_check_status(check, HCHK_STATUS_L4TOUT, NULL);
+					set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
 			}
 			else if ((conn->flags & (CO_FL_CONNECTED|CO_FL_WAIT_L6_CONN)) == CO_FL_WAIT_L6_CONN) {
 				/* L6 not established (yet) */
 				if (conn->flags & CO_FL_ERROR)
-					set_server_check_status(check, HCHK_STATUS_L6RSP, err ? strerror(err) : NULL);
+					set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
 				else if (expired)
-					set_server_check_status(check, HCHK_STATUS_L6TOUT, NULL);
+					set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
+			}
+			else if (conn->flags & CO_FL_ERROR) {
+				/* I/O error after connection was established and before we could diagnose */
+				set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
 			}
 			else if (!check->type) {
 				/* good connection is enough for pure TCP check */