From f1fd882d572dfb916c2fcd105155fb4a100964da Mon Sep 17 00:00:00 2001
From: Andy Green <andy@warmcat.com>
Date: Tue, 3 May 2016 07:26:10 +0800
Subject: [PATCH] client fix reaction to tls failure

https://github.com/warmcat/libwebsockets/issues/508

Signed-off-by: Andy Green <andy@warmcat.com>
---
 README.test-apps.md         | 10 ++++++++++
 lib/client-handshake.c      | 13 ++++++++++++-
 lib/client.c                | 13 +++++++++++--
 lib/libwebsockets.c         |  5 +++--
 lib/private-libwebsockets.h |  2 ++
 lib/service.c               |  5 +++++
 lib/ssl-client.c            | 10 +++++-----
 lib/ssl.c                   | 36 +++++++++++++++++++++---------------
 test-server/test-client.c   | 10 ++++++++--
 9 files changed, 77 insertions(+), 27 deletions(-)

diff --git a/README.test-apps.md b/README.test-apps.md
index 221adc7a..3b76052a 100644
--- a/README.test-apps.md
+++ b/README.test-apps.md
@@ -78,6 +78,16 @@ same time as drawing random circles in the mirror protocol;
 if you connect to the test server using a browser at the
 same time you will be able to see the circles being drawn.
 
+The test client supports SSL too, use
+
+```bash
+$ libwebsockets-test-client localhost --ssl -s
+```
+
+the -s tells it to accept the default selfsigned cert from the server,
+otherwise it will strictly fail the connection if there is no CA cert to
+validate the server's certificate.
+
 
 Choosing between test server variations
 ---------------------------------------
diff --git a/lib/client-handshake.c b/lib/client-handshake.c
index c7e220ee..0135df50 100644
--- a/lib/client-handshake.c
+++ b/lib/client-handshake.c
@@ -112,8 +112,10 @@ lws_client_connect_2(struct lws *wsi)
 		ai.ai_socktype = SOCK_STREAM;
 		ai.ai_flags = AI_CANONNAME;
 
-		if (getaddrinfo(ads, NULL, &ai, &result))
+		if (getaddrinfo(ads, NULL, &ai, &result)) {
+			lwsl_err("getaddrinfo failed\n");
 			goto oom4;
+		}
 
 		res = result;
 		while (!p && res) {
@@ -127,6 +129,7 @@ lws_client_connect_2(struct lws *wsi)
 		}
 
 		if (!p) {
+			lwsl_err("Couldn't identify address\n");
 			freeaddrinfo(result);
 			goto oom4;
 		}
@@ -170,6 +173,9 @@ lws_client_connect_2(struct lws *wsi)
 		 * handling as oom4 does.  We have to run the whole close flow.
 		 */
 
+		if (!wsi->protocol)
+			wsi->protocol = &wsi->vhost->protocols[0];
+
 		wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE,
 					wsi->user_space, NULL, 0);
 
@@ -278,6 +284,11 @@ lws_client_connect_2(struct lws *wsi)
 oom4:
 	/* we're closing, losing some rx is OK */
 	wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
+	//lwsl_err("%d\n", wsi->mode);
+	if (wsi->mode == LWSCM_HTTP_CLIENT)
+		wsi->vhost->protocols[0].callback(wsi,
+			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+			wsi->user_space, NULL, 0);
 	/* take care that we might be inserted in fds already */
 	if (wsi->position_in_fds_table != -1)
 		goto failed;
diff --git a/lib/client.c b/lib/client.c
index 15579426..63f9f808 100644
--- a/lib/client.c
+++ b/lib/client.c
@@ -152,8 +152,11 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
 			lws_ssl_client_bio_create(wsi);
 
 		if (wsi->use_ssl) {
-			if (!lws_ssl_client_connect1(wsi))
+			n = lws_ssl_client_connect1(wsi);
+			if (!n)
 				return 0;
+			if (n < 0)
+				goto bail3;
 		} else
 			wsi->ssl = NULL;
 
@@ -162,8 +165,11 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
 	case LWSCM_WSCL_WAITING_SSL:
 
 		if (wsi->use_ssl) {
-			if (!lws_ssl_client_connect2(wsi))
+			n = lws_ssl_client_connect2(wsi);
+			if (!n)
 				return 0;
+			if (n < 0)
+				goto bail3;
 		} else
 			wsi->ssl = NULL;
 #endif
@@ -279,6 +285,9 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
 
 bail3:
 		lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
+		wsi->vhost->protocols[0].callback(wsi,
+			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+					wsi->user_space, NULL, 0);
 		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
 		return -1;
 
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index b34430bf..60da78ef 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -380,12 +380,13 @@ just_kill_connection:
 	 * the actual close.
 	 */
 	if (wsi->state != LWSS_SHUTDOWN &&
+	    wsi->state != LWSS_CLIENT_UNCONNECTED &&
 	    reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
 	    !wsi->socket_is_permanently_unusable) {
-		lwsl_info("%s: shutting down connection: %p (sock %d)\n", __func__, wsi, wsi->sock);
+		lwsl_info("%s: shutting down connection: %p (sock %d, state %d)\n", __func__, wsi, wsi->sock, wsi->state);
 		n = shutdown(wsi->sock, SHUT_WR);
 		if (n)
-			lwsl_debug("closing: shutdown ret %d\n", LWS_ERRNO);
+			lwsl_debug("closing: shutdown (state %d) ret %d\n", wsi->state, LWS_ERRNO);
 
 // This causes problems with disconnection when the events are half closing connection
 // FD_READ | FD_CLOSE (33)
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index df2bbf64..7e131a62 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -1583,6 +1583,8 @@ LWS_EXTERN int
 lws_ssl_client_connect1(struct lws *wsi);
 LWS_EXTERN int
 lws_ssl_client_connect2(struct lws *wsi);
+LWS_EXTERN void
+lws_ssl_elaborate_error(void);
 #ifndef LWS_NO_SERVER
 LWS_EXTERN int
 lws_context_init_server_ssl(struct lws_context_creation_info *info,
diff --git a/lib/service.c b/lib/service.c
index a838c7c4..fcafd99d 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -343,6 +343,11 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec)
 		 * cleanup like flush partials.
 		 */
 		wsi->socket_is_permanently_unusable = 1;
+		if (wsi->mode == LWSCM_WSCL_WAITING_SSL)
+			wsi->vhost->protocols[0].callback(wsi,
+				LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+				wsi->user_space, NULL, 0);
+
 		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
 
 		return 1;
diff --git a/lib/ssl-client.c b/lib/ssl-client.c
index 14fc99c0..10fe444a 100644
--- a/lib/ssl-client.c
+++ b/lib/ssl-client.c
@@ -167,7 +167,7 @@ some_wait:
 			char *sb = p;
 			lwsl_err("SSL connect error %lu: %s\n",
 				n, ERR_error_string(n, sb));
-			return 0;
+			return -1;
 		}
 #endif
 #endif
@@ -248,7 +248,7 @@ lws_ssl_client_connect2(struct lws *wsi)
 			if (n != SSL_ERROR_NONE) {
 				lwsl_err("SSL connect error %lu: %s\n",
 					 n, ERR_error_string(n, sb));
-				return 0;
+				return -1;
 			}
 #endif
 #endif
@@ -277,9 +277,9 @@ lws_ssl_client_connect2(struct lws *wsi)
 		} else {
 			lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n",
 				 n, ERR_error_string(n, sb));
-			lws_close_free_wsi(wsi,
-				LWS_CLOSE_STATUS_NOSTATUS);
-			return 0;
+			lws_ssl_elaborate_error();
+			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+			return -1;
 		}
 	}
 #endif /* USE_WOLFSSL */
diff --git a/lib/ssl.c b/lib/ssl.c
index 2be1a0a2..0a028bb1 100644
--- a/lib/ssl.c
+++ b/lib/ssl.c
@@ -70,6 +70,26 @@ int lws_ssl_get_error(struct lws *wsi, int n)
 #endif
 }
 
+void
+lws_ssl_elaborate_error(void)
+{
+#if defined(LWS_USE_POLARSSL)
+#else
+#if defined(LWS_USE_MBEDTLS)
+#else
+
+	char buf[256];
+	u_long err;
+
+	while ((err = ERR_get_error()) != 0) {
+		ERR_error_string_n(err, buf, sizeof(buf));
+		lwsl_err("*** %s\n", buf);
+	}
+#endif
+#endif
+}
+
+
 #if defined(LWS_USE_POLARSSL)
 #else
 #if defined(LWS_USE_MBEDTLS)
@@ -612,21 +632,7 @@ go_again:
 		lwsl_err("SSL_accept failed skt %u: %s\n",
 			   wsi->sock, ERR_error_string(m, NULL));
 
-#if defined(LWS_USE_POLARSSL)
-#else
-#if defined(LWS_USE_MBEDTLS)
-#else
-		{
-		   char buf[256];
-		   u_long err;
-
-		   while ((err = ERR_get_error()) != 0) {
-		      ERR_error_string_n(err, buf, sizeof(buf));
-		      lwsl_err("*** %s\n", buf);
-		   }
-		}
-#endif
-#endif
+		lws_ssl_elaborate_error();
 		goto fail;
 
 accepted:
diff --git a/test-server/test-client.c b/test-server/test-client.c
index 7d667611..688c513e 100644
--- a/test-server/test-client.c
+++ b/test-server/test-client.c
@@ -17,7 +17,7 @@
  * may be proprietary.  So unlike the library itself, they are licensed
  * Public Domain.
  */
- 
+
 #include "lws_config.h"
 
 #include <stdio.h>
@@ -433,7 +433,8 @@ int main(int argc, char **argv)
 	if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
 		use_ssl = 0;
 	if (!strcmp(prot, "https") || !strcmp(prot, "wss"))
-		use_ssl = 1;
+		if (!use_ssl)
+			use_ssl = 1;
 
 	/*
 	 * create the websockets context.  This tracks open connections and
@@ -476,6 +477,11 @@ int main(int argc, char **argv)
 #endif
 #endif
 #endif
+
+		if (use_ssl == 1)
+			lwsl_notice(" Cert must validate correctly (use -s to allow selfsigned)\n");
+		else
+			lwsl_notice(" Selfsigned certs allowed\n");
 	}
 
 	context = lws_create_context(&info);
-- 
GitLab