diff --git a/lib/client.c b/lib/client.c index d55d7f512..84f837962 100644 --- a/lib/client.c +++ b/lib/client.c @@ -148,125 +148,12 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi, #ifdef LWS_OPENSSL_SUPPORT /* we can retry this... just cook the SSL BIO the first time */ - if (wsi->use_ssl && !wsi->ssl) { -#if defined(CYASSL_SNI_HOST_NAME) || defined(WOLFSSL_SNI_HOST_NAME) || defined(SSL_CTRL_SET_TLSEXT_HOSTNAME) - const char *hostname = lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_HOST); -#endif - - wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx); -#ifndef USE_WOLFSSL - SSL_set_mode(wsi->ssl, - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); -#endif - /* - * use server name indication (SNI), if supported, - * when establishing connection - */ -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL -#ifdef CYASSL_SNI_HOST_NAME - CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, - hostname, strlen(hostname)); -#endif -#else -#ifdef WOLFSSL_SNI_HOST_NAME - wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, - hostname, strlen(hostname)); -#endif -#endif -#else -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - SSL_set_tlsext_host_name(wsi->ssl, hostname); -#endif -#endif - -#ifdef USE_WOLFSSL - /* - * wolfSSL/CyaSSL does certificate verification differently - * from OpenSSL. - * If we should ignore the certificate, we need to set - * this before SSL_new and SSL_connect is called. - * Otherwise the connect will simply fail with error - * code -155 - */ -#ifdef USE_OLD_CYASSL - if (wsi->use_ssl == 2) - CyaSSL_set_verify(wsi->ssl, - SSL_VERIFY_NONE, NULL); -#else - if (wsi->use_ssl == 2) - wolfSSL_set_verify(wsi->ssl, - SSL_VERIFY_NONE, NULL); -#endif -#endif /* USE_WOLFSSL */ - - wsi->client_bio = - BIO_new_socket(wsi->sock, BIO_NOCLOSE); - SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio); - -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL - CyaSSL_set_using_nonblock(wsi->ssl, 1); -#else - wolfSSL_set_using_nonblock(wsi->ssl, 1); -#endif -#else - BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */ -#endif - - SSL_set_ex_data(wsi->ssl, - openssl_websocket_private_data_index, - context); - } + if (wsi->use_ssl && !wsi->ssl) + lws_ssl_client_bio_create(wsi); if (wsi->use_ssl) { - lws_latency_pre(context, wsi); - n = SSL_connect(wsi->ssl); - lws_latency(context, wsi, - "SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0); - - if (n < 0) { - n = SSL_get_error(wsi->ssl, n); - - if (n == SSL_ERROR_WANT_READ) - goto some_wait; - - if (n == SSL_ERROR_WANT_WRITE) { - /* - * wants us to retry connect due to - * state of the underlying ssl layer... - * but since it may be stalled on - * blocked write, no incoming data may - * arrive to trigger the retry. - * Force (possibly many times if the SSL - * state persists in returning the - * condition code, but other sockets - * are getting serviced inbetweentimes) - * us to get called back when writable. - */ - lwsl_info("%s: WANT_WRITE... retrying\n", __func__); - lws_callback_on_writable(wsi); -some_wait: - wsi->mode = LWSCM_WSCL_WAITING_SSL; - - return 0; /* no error */ - } - n = -1; - } - - if (n <= 0) { - /* - * retry if new data comes until we - * run into the connection timeout or win - */ - n = ERR_get_error(); - if (n != SSL_ERROR_NONE) { - lwsl_err("SSL connect error %lu: %s\n", - n, ERR_error_string(n, sb)); - return 0; - } - } + if (!lws_ssl_client_connect1(wsi)) + return 0; } else wsi->ssl = NULL; @@ -275,78 +162,8 @@ some_wait: case LWSCM_WSCL_WAITING_SSL: if (wsi->use_ssl) { - if (wsi->mode == LWSCM_WSCL_WAITING_SSL) { - lws_latency_pre(context, wsi); - n = SSL_connect(wsi->ssl); - lws_latency(context, wsi, - "SSL_connect LWSCM_WSCL_WAITING_SSL", - n, n > 0); - - if (n < 0) { - n = SSL_get_error(wsi->ssl, n); - - if (n == SSL_ERROR_WANT_READ) - goto some_wait; - - if (n == SSL_ERROR_WANT_WRITE) { - /* - * wants us to retry connect due to - * state of the underlying ssl layer... - * but since it may be stalled on - * blocked write, no incoming data may - * arrive to trigger the retry. - * Force (possibly many times if the SSL - * state persists in returning the - * condition code, but other sockets - * are getting serviced inbetweentimes) - * us to get called back when writable. - */ - lwsl_info("SSL_connect WANT_WRITE... retrying\n"); - lws_callback_on_writable(wsi); - - goto some_wait; - } - n = -1; - } - - if (n <= 0) { - /* - * retry if new data comes until we - * run into the connection timeout or win - */ - n = ERR_get_error(); - if (n != SSL_ERROR_NONE) { - lwsl_err("SSL connect error %lu: %s\n", - n, ERR_error_string(n, sb)); - return 0; - } - } - } - - #ifndef USE_WOLFSSL - /* - * See comment above about wolfSSL certificate - * verification - */ - lws_latency_pre(context, wsi); - n = SSL_get_verify_result(wsi->ssl); - lws_latency(context, wsi, - "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", - n, n > 0); - - if (n != X509_V_OK) { - if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || - n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && wsi->use_ssl == 2) { - lwsl_notice("accepting self-signed certificate\n"); - } 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; - } - } -#endif /* USE_WOLFSSL */ + if (!lws_ssl_client_connect2(wsi)) + return 0; } else wsi->ssl = NULL; #endif diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 125f89270..54994b0f0 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -1511,6 +1511,12 @@ LWS_EXTERN void lws_ssl_context_destroy(struct lws_context *context); LWS_VISIBLE void lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_bio_create(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect1(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect2(struct lws *wsi); #ifndef LWS_NO_SERVER LWS_EXTERN int lws_context_init_server_ssl(struct lws_context_creation_info *info, diff --git a/lib/ssl.c b/lib/ssl.c index f00524c38..3835d874d 100644 --- a/lib/ssl.c +++ b/lib/ssl.c @@ -471,6 +471,223 @@ lws_decode_ssl_error(void) #ifndef LWS_NO_CLIENT +int +lws_ssl_client_bio_create(struct lws *wsi) +{ + struct lws_context *context = wsi->context; +#if defined(CYASSL_SNI_HOST_NAME) || defined(WOLFSSL_SNI_HOST_NAME) || defined(SSL_CTRL_SET_TLSEXT_HOSTNAME) + const char *hostname = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST); +#endif + + wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx); +#ifndef USE_WOLFSSL + SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#endif + /* + * use server name indication (SNI), if supported, + * when establishing connection + */ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#ifdef CYASSL_SNI_HOST_NAME + CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname)); +#endif +#else +#ifdef WOLFSSL_SNI_HOST_NAME + wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname)); +#endif +#endif +#else +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + SSL_set_tlsext_host_name(wsi->ssl, hostname); +#endif +#endif + +#ifdef USE_WOLFSSL + /* + * wolfSSL/CyaSSL does certificate verification differently + * from OpenSSL. + * If we should ignore the certificate, we need to set + * this before SSL_new and SSL_connect is called. + * Otherwise the connect will simply fail with error code -155 + */ +#ifdef USE_OLD_CYASSL + if (wsi->use_ssl == 2) + CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); +#else + if (wsi->use_ssl == 2) + wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); +#endif +#endif /* USE_WOLFSSL */ + + wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE); + SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio); + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + CyaSSL_set_using_nonblock(wsi->ssl, 1); +#else + wolfSSL_set_using_nonblock(wsi->ssl, 1); +#endif +#else + BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */ +#endif + + SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, + context); + + return 0; +} + +int +lws_ssl_client_connect1(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + int n; + + lws_latency_pre(context, wsi); + n = SSL_connect(wsi->ssl); + lws_latency(context, wsi, + "SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0); + + if (n < 0) { + n = SSL_get_error(wsi->ssl, n); + + if (n == SSL_ERROR_WANT_READ) + goto some_wait; + + if (n == SSL_ERROR_WANT_WRITE) { + /* + * wants us to retry connect due to + * state of the underlying ssl layer... + * but since it may be stalled on + * blocked write, no incoming data may + * arrive to trigger the retry. + * Force (possibly many times if the SSL + * state persists in returning the + * condition code, but other sockets + * are getting serviced inbetweentimes) + * us to get called back when writable. + */ + lwsl_info("%s: WANT_WRITE... retrying\n", __func__); + lws_callback_on_writable(wsi); +some_wait: + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + n = -1; + } + + if (n <= 0) { + /* + * retry if new data comes until we + * run into the connection timeout or win + */ + n = ERR_get_error(); + if (n != SSL_ERROR_NONE) { + lwsl_err("SSL connect error %lu: %s\n", + n, ERR_error_string(n, sb)); + return 0; + } + } + + return 1; +} + +int +lws_ssl_client_connect2(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + int n; + + if (wsi->mode == LWSCM_WSCL_WAITING_SSL) { + lws_latency_pre(context, wsi); + n = SSL_connect(wsi->ssl); + lws_latency(context, wsi, + "SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0); + + if (n < 0) { + n = SSL_get_error(wsi->ssl, n); + + if (n == SSL_ERROR_WANT_READ) { + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + + if (n == SSL_ERROR_WANT_WRITE) { + /* + * wants us to retry connect due to + * state of the underlying ssl layer... + * but since it may be stalled on + * blocked write, no incoming data may + * arrive to trigger the retry. + * Force (possibly many times if the SSL + * state persists in returning the + * condition code, but other sockets + * are getting serviced inbetweentimes) + * us to get called back when writable. + */ + lwsl_info("SSL_connect WANT_WRITE... retrying\n"); + lws_callback_on_writable(wsi); + + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + n = -1; + } + + if (n <= 0) { + /* + * retry if new data comes until we + * run into the connection timeout or win + */ + n = ERR_get_error(); + if (n != SSL_ERROR_NONE) { + lwsl_err("SSL connect error %lu: %s\n", + n, ERR_error_string(n, sb)); + return 0; + } + } + } + +#ifndef USE_WOLFSSL + /* + * See comment above about wolfSSL certificate + * verification + */ + lws_latency_pre(context, wsi); + n = SSL_get_verify_result(wsi->ssl); + lws_latency(context, wsi, + "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", + n, n > 0); + + if (n != X509_V_OK) { + if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || + n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && wsi->use_ssl == 2) { + lwsl_notice("accepting self-signed certificate\n"); + } 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; + } + } +#endif /* USE_WOLFSSL */ + + return 1; +} + + int lws_context_init_client_ssl(struct lws_context_creation_info *info, struct lws_vhost *vhost) {