LWS_VISIBLE int lws_server_socket_service_ssl(struct libwebsocket_context *context, struct libwebsocket **pwsi, struct libwebsocket *new_wsi, int accept_fd, struct libwebsocket_pollfd *pollfd) { int n, m; struct libwebsocket *wsi = *pwsi; #ifndef USE_CYASSL BIO *bio; #endif if (!LWS_SSL_ENABLED(context)) return 0; switch (wsi->mode) { case LWS_CONNMODE_SERVER_LISTENER: if (!new_wsi) { lwsl_err("no new_wsi\n"); return 0; } new_wsi->ssl = SSL_new(context->ssl_ctx); if (new_wsi->ssl == NULL) { lwsl_err("SSL_new failed: %s\n", ERR_error_string(SSL_get_error( new_wsi->ssl, 0), NULL)); libwebsockets_decode_ssl_error(); lws_free(new_wsi); compatible_close(accept_fd); break; } SSL_set_ex_data(new_wsi->ssl, openssl_websocket_private_data_index, context); SSL_set_fd(new_wsi->ssl, accept_fd); #ifdef USE_CYASSL CyaSSL_set_using_nonblock(new_wsi->ssl, 1); #else SSL_set_mode(new_wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); bio = SSL_get_rbio(new_wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); bio = SSL_get_wbio(new_wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); #endif /* * we are not accepted yet, but we need to enter ourselves * as a live connection. That way we can retry when more * pieces come if we're not sorted yet */ *pwsi = new_wsi; wsi = *pwsi; wsi->mode = LWS_CONNMODE_SSL_ACK_PENDING; insert_wsi_socket_into_fds(context, wsi); libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, AWAITING_TIMEOUT); lwsl_info("inserted SSL accept into fds, trying SSL_accept\n"); /* fallthru */ case LWS_CONNMODE_SSL_ACK_PENDING: if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) goto fail; lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE); lws_latency_pre(context, wsi); n = recv(wsi->sock, context->service_buffer, sizeof(context->service_buffer), MSG_PEEK); /* * optionally allow non-SSL connect on SSL listening socket * This is disabled by default, if enabled it goes around any * SSL-level access control (eg, client-side certs) so leave * it disabled unless you know it's not a problem for you */ if (context->allow_non_ssl_on_ssl_port && n >= 1 && context->service_buffer[0] >= ' ') { /* * TLS content-type for Handshake is 0x16 * TLS content-type for ChangeCipherSpec Record is 0x14 * * A non-ssl session will start with the HTTP method in * ASCII. If we see it's not a legit SSL handshake * kill the SSL for this connection and try to handle * as a HTTP connection upgrade directly. */ wsi->use_ssl = 0; SSL_shutdown(wsi->ssl); SSL_free(wsi->ssl); wsi->ssl = NULL; goto accepted; } /* normal SSL connection processing path */ n = SSL_accept(wsi->ssl); lws_latency(context, wsi, "SSL_accept LWS_CONNMODE_SSL_ACK_PENDING\n", n, n == 1); if (n == 1) goto accepted; m = SSL_get_error(wsi->ssl, n); lwsl_debug("SSL_accept failed %d / %s\n", m, ERR_error_string(m, NULL)); if (m == SSL_ERROR_WANT_READ) { if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) goto fail; lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_READ); lwsl_info("SSL_ERROR_WANT_READ\n"); break; } if (m == SSL_ERROR_WANT_WRITE) { if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) goto fail; lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_WRITE); break; } lwsl_debug("SSL_accept failed skt %u: %s\n", pollfd->fd, ERR_error_string(m, NULL)); goto fail; accepted: /* OK, we are accepted... give him some time to negotiate */ libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, AWAITING_TIMEOUT); wsi->mode = LWS_CONNMODE_HTTP_SERVING; lws_http2_configure_if_upgraded(wsi); lwsl_debug("accepted new SSL conn\n"); break; } return 0; fail: return 1; }
int lws_server_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct pollfd *pollfd) { struct libwebsocket *new_wsi; int accept_fd; socklen_t clilen; struct sockaddr_in cli_addr; int n; ssize_t len; #ifdef LWS_OPENSSL_SUPPORT int m; #ifndef USE_CYASSL BIO *bio; #endif #endif switch (wsi->mode) { case LWS_CONNMODE_HTTP_SERVING: case LWS_CONNMODE_HTTP_SERVING_ACCEPTED: /* handle http headers coming in */ /* pending truncated sends have uber priority */ if (wsi->truncated_send_malloc) { if (pollfd->revents & POLLOUT) lws_issue_raw(wsi, wsi->truncated_send_malloc + wsi->truncated_send_offset, wsi->truncated_send_len); /* * we can't afford to allow input processing send * something new, so spin around he event loop until * he doesn't have any partials */ break; } /* any incoming data ready? */ if (pollfd->revents & POLLIN) { #ifdef LWS_OPENSSL_SUPPORT if (wsi->ssl) len = SSL_read(wsi->ssl, context->service_buffer, sizeof(context->service_buffer)); else #endif len = recv(pollfd->fd, context->service_buffer, sizeof(context->service_buffer), 0); if (len < 0) { lwsl_debug("Socket read returned %d\n", len); if (errno != EINTR && errno != EAGAIN) libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } if (!len) { lwsl_info("lws_server_skt_srv: read 0 len\n"); /* lwsl_info(" state=%d\n", wsi->state); */ if (!wsi->hdr_parsing_completed) free(wsi->u.hdr.ah); libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } /* hm this may want to send (via HTTP callback for example) */ n = libwebsocket_read(context, wsi, context->service_buffer, len); if (n < 0) /* we closed wsi */ return 0; /* hum he may have used up the writability above */ break; } /* this handles POLLOUT for http serving fragments */ if (!(pollfd->revents & POLLOUT)) break; /* one shot */ lws_change_pollfd(wsi, POLLOUT, 0); if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow( wsi->protocol->callback, wsi->protocol->owning_server, wsi, LWS_CALLBACK_HTTP_WRITEABLE, wsi->user_space, NULL, 0); if (n < 0) libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; } /* nonzero for completion or error */ if (libwebsockets_serve_http_file_fragment(context, wsi)) libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; case LWS_CONNMODE_SERVER_LISTENER: /* pollin means a client has connected to us then */ if (!(pollfd->revents & POLLIN)) break; /* listen socket got an unencrypted connection... */ clilen = sizeof(cli_addr); lws_latency_pre(context, wsi); accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, &clilen); lws_latency(context, wsi, "unencrypted accept LWS_CONNMODE_SERVER_LISTENER", accept_fd, accept_fd >= 0); if (accept_fd < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { lwsl_debug("accept asks to try again\n"); break; } lwsl_warn("ERROR on accept: %s\n", strerror(errno)); break; } lws_set_socket_options(context, accept_fd); /* * look at who we connected to and give user code a chance * to reject based on client IP. There's no protocol selected * yet so we issue this to protocols[0] */ if ((context->protocols[0].callback)(context, wsi, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, NULL, (void *)(long)accept_fd, 0)) { lwsl_debug("Callback denied network connection\n"); compatible_close(accept_fd); break; } new_wsi = libwebsocket_create_new_server_wsi(context); if (new_wsi == NULL) { compatible_close(accept_fd); break; } new_wsi->sock = accept_fd; #ifdef LWS_OPENSSL_SUPPORT new_wsi->ssl = NULL; if (!context->use_ssl) { #endif lwsl_debug("accepted new conn port %u on fd=%d\n", ntohs(cli_addr.sin_port), accept_fd); insert_wsi_socket_into_fds(context, new_wsi); break; #ifdef LWS_OPENSSL_SUPPORT } new_wsi->ssl = SSL_new(context->ssl_ctx); if (new_wsi->ssl == NULL) { lwsl_err("SSL_new failed: %s\n", ERR_error_string(SSL_get_error( new_wsi->ssl, 0), NULL)); libwebsockets_decode_ssl_error(); free(new_wsi); compatible_close(accept_fd); break; } SSL_set_ex_data(new_wsi->ssl, openssl_websocket_private_data_index, context); SSL_set_fd(new_wsi->ssl, accept_fd); #ifdef USE_CYASSL CyaSSL_set_using_nonblock(new_wsi->ssl, 1); #else bio = SSL_get_rbio(new_wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); bio = SSL_get_wbio(new_wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); #endif /* * we are not accepted yet, but we need to enter ourselves * as a live connection. That way we can retry when more * pieces come if we're not sorted yet */ wsi = new_wsi; wsi->mode = LWS_CONNMODE_SSL_ACK_PENDING; insert_wsi_socket_into_fds(context, wsi); libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, AWAITING_TIMEOUT); lwsl_info("inserted SSL accept into fds, trying SSL_accept\n"); /* fallthru */ case LWS_CONNMODE_SSL_ACK_PENDING: lws_change_pollfd(wsi, POLLOUT, 0); lws_latency_pre(context, wsi); n = recv(wsi->sock, context->service_buffer, sizeof(context->service_buffer), MSG_PEEK); /* * optionally allow non-SSL connect on SSL listening socket * This is disabled by default, if enabled it goes around any * SSL-level access control (eg, client-side certs) so leave * it disabled unless you know it's not a problem for you */ if (context->allow_non_ssl_on_ssl_port && n >= 1 && context->service_buffer[0] >= ' ') { /* * TLS content-type for Handshake is 0x16 * TLS content-type for ChangeCipherSpec Record is 0x14 * * A non-ssl session will start with the HTTP method in * ASCII. If we see it's not a legit SSL handshake * kill the SSL for this connection and try to handle * as a HTTP connection upgrade directly. */ wsi->use_ssl = 0; SSL_shutdown(wsi->ssl); SSL_free(wsi->ssl); wsi->ssl = NULL; goto accepted; } /* normal SSL connection processing path */ n = SSL_accept(wsi->ssl); lws_latency(context, wsi, "SSL_accept LWS_CONNMODE_SSL_ACK_PENDING\n", n, n == 1); if (n != 1) { m = SSL_get_error(wsi->ssl, n); lwsl_debug("SSL_accept failed %d / %s\n", m, ERR_error_string(m, NULL)); if (m == SSL_ERROR_WANT_READ) { lws_change_pollfd(wsi, 0, POLLIN); lwsl_info("SSL_ERROR_WANT_READ\n"); break; } if (m == SSL_ERROR_WANT_WRITE) { lws_change_pollfd(wsi, 0, POLLOUT); break; } lwsl_debug("SSL_accept failed skt %u: %s\n", pollfd->fd, ERR_error_string(m, NULL)); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; } accepted: /* OK, we are accepted */ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); wsi->mode = LWS_CONNMODE_HTTP_SERVING; lwsl_debug("accepted new SSL conn\n"); break; #endif default: break; } return 0; }