int lws_client_socket_service(struct lws_context *context, struct lws *wsi, struct lws_pollfd *pollfd) { struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; char *p = (char *)&pt->serv_buf[0]; char *sb = p; unsigned char c; int n, len; switch (wsi->mode) { case LWSCM_WSCL_WAITING_CONNECT: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c */ if (!lws_client_connect_2(wsi)) { /* closed */ lwsl_client("closed\n"); return -1; } /* either still pending connection, or changed mode */ return 0; case LWSCM_WSCL_WAITING_PROXY_REPLY: /* handle proxy hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_warn("Proxy connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } n = recv(wsi->sock, sb, LWS_MAX_SOCKET_IO_BUF, 0); if (n < 0) { if (LWS_ERRNO == LWS_EAGAIN) { lwsl_debug("Proxy read returned EAGAIN... retrying\n"); return 0; } lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR reading from proxy socket\n"); return 0; } pt->serv_buf[13] = '\0'; if (strcmp(sb, "HTTP/1.0 200 ") && strcmp(sb, "HTTP/1.1 200 ")) { lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR proxy: %s\n", sb); return 0; } /* clear his proxy connection timeout */ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); /* fallthru */ case LWSCM_WSCL_ISSUE_HANDSHAKE: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c * * take care of our lws_callback_on_writable * happening at a time when there's no real connection yet */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) return -1; lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); #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(context->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) { 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; } } } else wsi->ssl = NULL; /* fallthru */ 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 */ } else wsi->ssl = NULL; #endif wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2; lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, AWAITING_TIMEOUT); /* fallthru */ case LWSCM_WSCL_ISSUE_HANDSHAKE2: p = lws_generate_client_handshake(wsi, p); if (p == NULL) { lwsl_err("Failed to generate handshake for client\n"); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } /* send our request to the server */ lws_latency_pre(context, wsi); n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb); lws_latency(context, wsi, "send lws_issue_raw", n, n == p - sb); switch (n) { case LWS_SSL_CAPABLE_ERROR: lwsl_debug("ERROR writing to client socket\n"); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; case LWS_SSL_CAPABLE_MORE_SERVICE: lws_callback_on_writable(wsi); break; } wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; wsi->u.hdr.lextable_pos = 0; wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY; lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, AWAITING_TIMEOUT); break; case LWSCM_WSCL_WAITING_SERVER_REPLY: /* handle server hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_debug("Server connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); goto bail3; } if (!(pollfd->revents & LWS_POLLIN)) break; /* interpret the server response */ /* * HTTP/1.1 101 Switching Protocols * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== * Sec-WebSocket-Protocol: chat */ /* * we have to take some care here to only take from the * socket bytewise. The browser may (and has been seen to * in the case that onopen() performs websocket traffic) * coalesce both handshake response and websocket traffic * in one packet, since at that point the connection is * definitively ready from browser pov. */ len = 1; while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE && len > 0) { n = lws_ssl_capable_read(wsi, &c, 1); lws_latency(context, wsi, "send lws_issue_raw", n, n == 1); switch (n) { case 0: case LWS_SSL_CAPABLE_ERROR: goto bail3; case LWS_SSL_CAPABLE_MORE_SERVICE: return 0; } if (lws_parse(wsi, c)) { lwsl_warn("problems parsing header\n"); goto bail3; } } /* * hs may also be coming in multiple packets, there is a 5-sec * libwebsocket timeout still active here too, so if parsing did * not complete just wait for next packet coming in this state */ if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) break; /* * otherwise deal with the handshake. If there's any * packet traffic already arrived we'll trigger poll() again * right away and deal with it that way */ return lws_client_interpret_server_handshake(wsi); bail3: lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n"); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return -1; case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: lwsl_ext("LWSCM_WSCL_WAITING_EXTENSION_CONNECT\n"); break; case LWSCM_WSCL_PENDING_CANDIDATE_CHILD: lwsl_ext("LWSCM_WSCL_PENDING_CANDIDATE_CHILD\n"); break; default: break; } return 0; }
int lws_client_socket_service(struct lws_context *context, struct lws *wsi, struct lws_pollfd *pollfd) { struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; char *p = (char *)&pt->serv_buf[0]; const char *cce = NULL; unsigned char c; char *sb = p; int n = 0, len = 0; #if defined(LWS_WITH_SOCKS5) char conn_mode = 0, pending_timeout = 0; #endif switch (wsi->mode) { case LWSCM_WSCL_WAITING_CONNECT: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c */ if (!lws_client_connect_2(wsi)) { /* closed */ lwsl_client("closed\n"); return -1; } /* either still pending connection, or changed mode */ return 0; #if defined(LWS_WITH_SOCKS5) /* SOCKS Greeting Reply */ case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY: /* handle proxy hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_warn("SOCKS connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); if (n < 0) { if (LWS_ERRNO == LWS_EAGAIN) { lwsl_debug("SOCKS read returned EAGAIN..." "retrying\n"); return 0; } lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR reading from SOCKS socket\n"); return 0; } /* processing greeting reply */ if (pt->serv_buf[0] == SOCKS_VERSION_5 && pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) { lwsl_client("%s\n", "SOCKS greeting reply received " "- No Authentication Method"); socks_generate_msg(wsi, SOCKS_MSG_CONNECT, (size_t *)&len); conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; lwsl_client("%s\n", "Sending SOCKS connect command"); } else if (pt->serv_buf[0] == SOCKS_VERSION_5 && pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) { lwsl_client("%s\n", "SOCKS greeting reply received " "- User Name Password Method"); socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD, (size_t *)&len); conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY; pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY; lwsl_client("%s\n", "Sending SOCKS user/password"); } else { lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR SOCKS greeting reply failed, method " "code: %d\n", pt->serv_buf[1]); return 0; } n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, MSG_NOSIGNAL); if (n < 0) { lwsl_debug("ERROR writing socks command to socks proxy " "socket\n"); return 0; } lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT); wsi->mode = conn_mode; break; /* SOCKS auth Reply */ case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY: /* handle proxy hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_warn("SOCKS connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); if (n < 0) { if (LWS_ERRNO == LWS_EAGAIN) { lwsl_debug("SOCKS read returned EAGAIN... " "retrying\n"); return 0; } lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR reading from socks socket\n"); return 0; } /* processing auth reply */ if (pt->serv_buf[0] == SOCKS_SUBNEGOTIATION_VERSION_1 && pt->serv_buf[1] == SOCKS_SUBNEGOTIATION_STATUS_SUCCESS) { lwsl_client("%s\n", "SOCKS password reply recieved - " "successful"); socks_generate_msg(wsi, SOCKS_MSG_CONNECT, (size_t *)&len); conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; lwsl_client("%s\n", "Sending SOCKS connect command"); } else { lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR : SOCKS user/password reply failed, " "error code: %d\n", pt->serv_buf[1]); return 0; } n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, MSG_NOSIGNAL); if (n < 0) { lwsl_debug("ERROR writing connect command to SOCKS " "socket\n"); return 0; } lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT); wsi->mode = conn_mode; break; /* SOCKS connect command Reply */ case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY: /* handle proxy hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_warn("SOCKS connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); if (n < 0) { if (LWS_ERRNO == LWS_EAGAIN) { lwsl_debug("SOCKS read returned EAGAIN... " "retrying\n"); return 0; } lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR reading from socks socket\n"); return 0; } /* processing connect reply */ if (pt->serv_buf[0] == SOCKS_VERSION_5 && pt->serv_buf[1] == SOCKS_REQUEST_REPLY_SUCCESS) { lwsl_client("%s\n", "SOCKS connect reply recieved - " "successful"); } else { lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR SOCKS connect reply failed, error " "code: %d\n", pt->serv_buf[1]); return 0; } /* free stash since we are done with it */ lws_free_set_NULL(wsi->u.hdr.stash); if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, wsi->vhost->socks_proxy_address)) goto bail3; wsi->c_port = wsi->vhost->socks_proxy_port; /* clear his proxy connection timeout */ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); goto start_ws_hanshake; #endif case LWSCM_WSCL_WAITING_PROXY_REPLY: /* handle proxy hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_warn("Proxy connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); if (n < 0) { if (LWS_ERRNO == LWS_EAGAIN) { lwsl_debug("Proxy read returned EAGAIN... retrying\n"); return 0; } lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR reading from proxy socket\n"); return 0; } pt->serv_buf[13] = '\0'; if (strcmp(sb, "HTTP/1.0 200 ") && strcmp(sb, "HTTP/1.1 200 ")) { lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR proxy: %s\n", sb); return 0; } /* clear his proxy connection timeout */ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); /* fallthru */ case LWSCM_WSCL_ISSUE_HANDSHAKE: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c * * take care of our lws_callback_on_writable * happening at a time when there's no real connection yet */ #if defined(LWS_WITH_SOCKS5) start_ws_hanshake: #endif if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) return -1; #ifdef LWS_OPENSSL_SUPPORT /* we can retry this... just cook the SSL BIO the first time */ if (wsi->use_ssl && !wsi->ssl) { if (lws_ssl_client_bio_create(wsi)) return -1; } if (wsi->use_ssl) { n = lws_ssl_client_connect1(wsi); if (!n) return 0; if (n < 0) { cce = "lws_ssl_client_connect1 failed"; goto bail3; } } else wsi->ssl = NULL; /* fallthru */ case LWSCM_WSCL_WAITING_SSL: if (wsi->use_ssl) { n = lws_ssl_client_connect2(wsi); if (!n) return 0; if (n < 0) { cce = "lws_ssl_client_connect2 failed"; goto bail3; } } else wsi->ssl = NULL; #endif wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2; lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, context->timeout_secs); /* fallthru */ case LWSCM_WSCL_ISSUE_HANDSHAKE2: p = lws_generate_client_handshake(wsi, p); if (p == NULL) { if (wsi->mode == LWSCM_RAW) return 0; lwsl_err("Failed to generate handshake for client\n"); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } /* send our request to the server */ lws_latency_pre(context, wsi); n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb); lws_latency(context, wsi, "send lws_issue_raw", n, n == p - sb); switch (n) { case LWS_SSL_CAPABLE_ERROR: lwsl_debug("ERROR writing to client socket\n"); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; case LWS_SSL_CAPABLE_MORE_SERVICE: lws_callback_on_writable(wsi); break; } if (wsi->client_http_body_pending) { wsi->mode = LWSCM_WSCL_ISSUE_HTTP_BODY; lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, context->timeout_secs); /* user code must ask for writable callback */ break; } goto client_http_body_sent; case LWSCM_WSCL_ISSUE_HTTP_BODY: if (wsi->client_http_body_pending) { lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, context->timeout_secs); /* user code must ask for writable callback */ break; } client_http_body_sent: wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; wsi->u.hdr.lextable_pos = 0; wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY; lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, context->timeout_secs); break; case LWSCM_WSCL_WAITING_SERVER_REPLY: /* handle server hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_debug("Server connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); cce = "Peer hung up"; goto bail3; } if (!(pollfd->revents & LWS_POLLIN)) break; /* interpret the server response */ /* * HTTP/1.1 101 Switching Protocols * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== * Sec-WebSocket-Protocol: chat */ /* * we have to take some care here to only take from the * socket bytewise. The browser may (and has been seen to * in the case that onopen() performs websocket traffic) * coalesce both handshake response and websocket traffic * in one packet, since at that point the connection is * definitively ready from browser pov. */ len = 1; while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE && len > 0) { n = lws_ssl_capable_read(wsi, &c, 1); lws_latency(context, wsi, "send lws_issue_raw", n, n == 1); switch (n) { case 0: case LWS_SSL_CAPABLE_ERROR: cce = "read failed"; goto bail3; case LWS_SSL_CAPABLE_MORE_SERVICE: return 0; } if (lws_parse(wsi, c)) { lwsl_warn("problems parsing header\n"); goto bail3; } } /* * hs may also be coming in multiple packets, there is a 5-sec * libwebsocket timeout still active here too, so if parsing did * not complete just wait for next packet coming in this state */ if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) break; /* * otherwise deal with the handshake. If there's any * packet traffic already arrived we'll trigger poll() again * right away and deal with it that way */ return lws_client_interpret_server_handshake(wsi); bail3: lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n"); if (cce) lwsl_info("reason: %s\n", cce); wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR, wsi->user_space, (void *)cce, cce ? strlen(cce) : 0); wsi->already_did_cce = 1; lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return -1; case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: lwsl_ext("LWSCM_WSCL_WAITING_EXTENSION_CONNECT\n"); break; case LWSCM_WSCL_PENDING_CANDIDATE_CHILD: lwsl_ext("LWSCM_WSCL_PENDING_CANDIDATE_CHILD\n"); break; default: break; } return 0; }
int lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd, struct lws *wsi_conn) { struct lws_context *context = wsi->context; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; char *p = (char *)&pt->serv_buf[0]; struct lws *w; #if defined(LWS_WITH_TLS) char ebuf[128]; #endif const char *cce = NULL; #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) ssize_t len = 0; unsigned char c; #endif char *sb = p; int n = 0; #if defined(LWS_WITH_SOCKS5) char conn_mode = 0, pending_timeout = 0; #endif if ((pollfd->revents & LWS_POLLOUT) && wsi->keepalive_active && wsi->dll_client_transaction_queue_head.next) { struct lws *wfound = NULL; lwsl_debug("%s: pollout HANDSHAKE2\n", __func__); /* * We have a transaction queued that wants to pipeline. * * We have to allow it to send headers strictly in the order * that it was queued, ie, tail-first. */ lws_vhost_lock(wsi->vhost); lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, wsi->dll_client_transaction_queue_head.next) { struct lws *w = lws_container_of(d, struct lws, dll_client_transaction_queue); lwsl_debug("%s: %p states 0x%x\n", __func__, w, w->wsistate); if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2) wfound = w; } lws_end_foreach_dll_safe(d, d1); if (wfound) { /* * pollfd has the master sockfd in it... we * need to use that in HANDSHAKE2 to understand * which wsi to actually write on */ lws_client_socket_service(wfound, pollfd, wsi); lws_callback_on_writable(wsi); } else lwsl_debug("%s: didn't find anything in txn q in HS2\n", __func__); lws_vhost_unlock(wsi->vhost); return 0; } switch (lwsi_state(wsi)) { case LRS_WAITING_CONNECT: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c */ if (!lws_client_connect_2(wsi)) { /* closed */ lwsl_client("closed\n"); return -1; } /* either still pending connection, or changed mode */ return 0; #if defined(LWS_WITH_SOCKS5) /* SOCKS Greeting Reply */ case LRS_WAITING_SOCKS_GREETING_REPLY: case LRS_WAITING_SOCKS_AUTH_REPLY: case LRS_WAITING_SOCKS_CONNECT_REPLY: /* handle proxy hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_warn("SOCKS connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); goto bail3; } n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); if (n < 0) { if (LWS_ERRNO == LWS_EAGAIN) { lwsl_debug("SOCKS read EAGAIN, retrying\n"); return 0; } lwsl_err("ERROR reading from SOCKS socket\n"); goto bail3; } switch (lwsi_state(wsi)) { case LRS_WAITING_SOCKS_GREETING_REPLY: if (pt->serv_buf[0] != SOCKS_VERSION_5) goto socks_reply_fail; if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) { lwsl_client("SOCKS GR: No Auth Method\n"); socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len); conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY; pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; goto socks_send; } if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) { lwsl_client("SOCKS GR: User/Pw Method\n"); socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD, &len); conn_mode = LRS_WAITING_SOCKS_AUTH_REPLY; pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY; goto socks_send; } goto socks_reply_fail; case LRS_WAITING_SOCKS_AUTH_REPLY: if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 || pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS) goto socks_reply_fail; lwsl_client("SOCKS password OK, sending connect\n"); socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len); conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY; pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; socks_send: n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, MSG_NOSIGNAL); if (n < 0) { lwsl_debug("ERROR writing to socks proxy\n"); goto bail3; } lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT); lwsi_set_state(wsi, conn_mode); break; socks_reply_fail: lwsl_notice("socks reply: v%d, err %d\n", pt->serv_buf[0], pt->serv_buf[1]); goto bail3; case LRS_WAITING_SOCKS_CONNECT_REPLY: if (pt->serv_buf[0] != SOCKS_VERSION_5 || pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS) goto socks_reply_fail; lwsl_client("socks connect OK\n"); /* free stash since we are done with it */ lws_client_stash_destroy(wsi); if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, wsi->vhost->socks_proxy_address)) goto bail3; wsi->c_port = wsi->vhost->socks_proxy_port; /* clear his proxy connection timeout */ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); goto start_ws_handshake; } break; #endif case LRS_WAITING_PROXY_REPLY: /* handle proxy hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_warn("Proxy connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); goto bail3; } n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); if (n < 0) { if (LWS_ERRNO == LWS_EAGAIN) { lwsl_debug("Proxy read EAGAIN... retrying\n"); return 0; } lwsl_err("ERROR reading from proxy socket\n"); goto bail3; } pt->serv_buf[13] = '\0'; if (strcmp(sb, "HTTP/1.0 200 ") && strcmp(sb, "HTTP/1.1 200 ")) { lwsl_err("ERROR proxy: %s\n", sb); goto bail3; } /* clear his proxy connection timeout */ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); /* fallthru */ case LRS_H1C_ISSUE_HANDSHAKE: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c * * take care of our lws_callback_on_writable * happening at a time when there's no real connection yet */ #if defined(LWS_WITH_SOCKS5) start_ws_handshake: #endif if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) return -1; #if defined(LWS_WITH_TLS) /* we can retry this... just cook the SSL BIO the first time */ if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !wsi->tls.ssl && lws_ssl_client_bio_create(wsi) < 0) { cce = "bio_create failed"; goto bail3; } if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { n = lws_ssl_client_connect1(wsi); if (!n) return 0; if (n < 0) { cce = "lws_ssl_client_connect1 failed"; goto bail3; } } else wsi->tls.ssl = NULL; /* fallthru */ case LRS_WAITING_SSL: if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf)); if (!n) return 0; if (n < 0) { cce = ebuf; goto bail3; } } else wsi->tls.ssl = NULL; #endif #if defined (LWS_WITH_HTTP2) if (wsi->client_h2_alpn) { /* * We connected to the server and set up tls, and * negotiated "h2". * * So this is it, we are an h2 master client connection * now, not an h1 client connection. */ lws_tls_server_conn_alpn(wsi); /* send the H2 preface to legitimize the connection */ if (lws_h2_issue_preface(wsi)) { cce = "error sending h2 preface"; goto bail3; } break; } #endif lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, context->timeout_secs); /* fallthru */ case LRS_H1C_ISSUE_HANDSHAKE2: p = lws_generate_client_handshake(wsi, p); if (p == NULL) { if (wsi->role_ops == &role_ops_raw_skt || wsi->role_ops == &role_ops_raw_file) return 0; lwsl_err("Failed to generate handshake for client\n"); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "chs"); return 0; } /* send our request to the server */ lws_latency_pre(context, wsi); w = _lws_client_wsi_master(wsi); lwsl_info("%s: HANDSHAKE2: %p: sending headers on %p (wsistate 0x%x 0x%x)\n", __func__, wsi, w, wsi->wsistate, w->wsistate); n = lws_ssl_capable_write(w, (unsigned char *)sb, (int)(p - sb)); lws_latency(context, wsi, "send lws_issue_raw", n, n == p - sb); switch (n) { case LWS_SSL_CAPABLE_ERROR: lwsl_debug("ERROR writing to client socket\n"); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cws"); return 0; case LWS_SSL_CAPABLE_MORE_SERVICE: lws_callback_on_writable(wsi); break; } if (wsi->client_http_body_pending) { lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY); lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, context->timeout_secs); /* user code must ask for writable callback */ break; } lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); wsi->hdr_parsing_completed = 0; if (lwsi_state(w) == LRS_IDLING) { lwsi_set_state(w, LRS_WAITING_SERVER_REPLY); w->hdr_parsing_completed = 0; #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) w->http.ah->parser_state = WSI_TOKEN_NAME_PART; w->http.ah->lextable_pos = 0; /* If we're (re)starting on headers, need other implied init */ wsi->http.ah->ues = URIES_IDLE; #endif } lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, wsi->context->timeout_secs); lws_callback_on_writable(w); goto client_http_body_sent; case LRS_ISSUE_HTTP_BODY: if (wsi->client_http_body_pending) { //lws_set_timeout(wsi, // PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, // context->timeout_secs); /* user code must ask for writable callback */ break; } client_http_body_sent: #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) /* prepare ourselves to do the parsing */ wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART; wsi->http.ah->lextable_pos = 0; #endif lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, context->timeout_secs); break; case LRS_WAITING_SERVER_REPLY: /* * handle server hanging up on us... * but if there is POLLIN waiting, handle that first */ if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) == LWS_POLLHUP) { lwsl_debug("Server connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); cce = "Peer hung up"; goto bail3; } if (!(pollfd->revents & LWS_POLLIN)) break; #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) /* interpret the server response * * HTTP/1.1 101 Switching Protocols * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== * Sec-WebSocket-Protocol: chat * * we have to take some care here to only take from the * socket bytewise. The browser may (and has been seen to * in the case that onopen() performs websocket traffic) * coalesce both handshake response and websocket traffic * in one packet, since at that point the connection is * definitively ready from browser pov. */ len = 1; while (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE && len > 0) { int plen = 1; n = lws_ssl_capable_read(wsi, &c, 1); lws_latency(context, wsi, "send lws_issue_raw", n, n == 1); switch (n) { case 0: case LWS_SSL_CAPABLE_ERROR: cce = "read failed"; goto bail3; case LWS_SSL_CAPABLE_MORE_SERVICE: return 0; } if (lws_parse(wsi, &c, &plen)) { lwsl_warn("problems parsing header\n"); goto bail3; } } /* * hs may also be coming in multiple packets, there is a 5-sec * libwebsocket timeout still active here too, so if parsing did * not complete just wait for next packet coming in this state */ if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE) break; #endif /* * otherwise deal with the handshake. If there's any * packet traffic already arrived we'll trigger poll() again * right away and deal with it that way */ return lws_client_interpret_server_handshake(wsi); bail3: lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n"); if (cce) lwsl_info("reason: %s\n", cce); wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR, wsi->user_space, (void *)cce, cce ? strlen(cce) : 0); wsi->already_did_cce = 1; lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3"); return -1; default: break; } return 0; }
int lws_client_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct pollfd *pollfd) { int n; char *p = (char *)&context->service_buffer[0]; int len; char c; switch (wsi->mode) { case LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c */ if (__libwebsocket_client_connect_2(context, wsi) == NULL) { /* closed */ lwsl_client("closed\n"); return -1; } /* either still pending connection, or changed mode */ return 0; case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY: /* handle proxy hung up on us */ if (pollfd->revents & (POLLERR | POLLHUP)) { lwsl_warn("Proxy connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } n = recv(wsi->sock, context->service_buffer, sizeof(context->service_buffer), 0); if (n < 0) { libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR reading from proxy socket\n"); return 0; } context->service_buffer[13] = '\0'; if (strcmp((char *)context->service_buffer, "HTTP/1.0 200 ")) { libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR proxy: %s\n", context->service_buffer); return 0; } /* clear his proxy connection timeout */ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); /* fallthru */ case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c */ #ifdef LWS_OPENSSL_SUPPORT /* * take care of our libwebsocket_callback_on_writable * happening at a time when there's no real connection yet */ pollfd->events &= ~POLLOUT; /* external POLL support via protocol 0 */ context->protocols[0].callback(context, wsi, LWS_CALLBACK_CLEAR_MODE_POLL_FD, wsi->user_space, (void *)(long)wsi->sock, POLLOUT); /* we can retry this... just cook the SSL BIO the first time */ if (wsi->use_ssl && !wsi->ssl) { wsi->ssl = SSL_new(context->ssl_client_ctx); #ifdef USE_CYASSL /* * 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 */ if (wsi->use_ssl == 2) CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); #endif /* USE_CYASSL */ wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE); SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio); #ifdef USE_CYASSL CyaSSL_set_using_nonblock(wsi->ssl, 1); #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) { lws_latency_pre(context, wsi); n = SSL_connect(wsi->ssl); lws_latency(context, wsi, "SSL_connect LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE", n, n > 0); if (n < 0) { n = SSL_get_error(wsi->ssl, n); if (n == SSL_ERROR_WANT_READ || 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_... retrying\n"); libwebsocket_callback_on_writable( context, wsi); return 0; /* no error */ } n = -1; } if (n <= 0) { /* * retry if new data comes until we * run into the connection timeout or win */ lwsl_err("SSL connect error %lu: %s\n", ERR_get_error(), ERR_error_string(ERR_get_error(), (char *)context->service_buffer)); return 0; } #ifndef USE_CYASSL /* * See comment above about CyaSSL 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) && ( n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || wsi->use_ssl != 2)) { lwsl_err( "server's cert didn't look good %d\n", n); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } #endif /* USE_CYASSL */ } else wsi->ssl = NULL; #endif p = libwebsockets_generate_client_handshake(context, wsi, p); if (p == NULL) { lwsl_err("Failed to generate handshake for client\n"); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } /* send our request to the server */ lws_latency_pre(context, wsi); #ifdef LWS_OPENSSL_SUPPORT if (wsi->use_ssl) n = SSL_write(wsi->ssl, context->service_buffer, p - (char *)context->service_buffer); else #endif n = send(wsi->sock, context->service_buffer, p - (char *)context->service_buffer, MSG_NOSIGNAL); lws_latency(context, wsi, "send or SSL_write LWS_CONNMODE...HANDSHAKE", n, n >= 0); if (n < 0) { lwsl_debug("ERROR writing to client socket\n"); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; wsi->u.hdr.lextable_pos = 0; wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY; libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, AWAITING_TIMEOUT); break; case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY: /* handle server hung up on us */ if (pollfd->revents & (POLLERR | POLLHUP)) { lwsl_debug("Server connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); goto bail3; } if (!(pollfd->revents & POLLIN)) break; /* interpret the server response */ /* * HTTP/1.1 101 Switching Protocols * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== * Sec-WebSocket-Protocol: chat */ /* * we have to take some care here to only take from the * socket bytewise. The browser may (and has been seen to * in the case that onopen() performs websocket traffic) * coalesce both handshake response and websocket traffic * in one packet, since at that point the connection is * definitively ready from browser pov. */ len = 1; while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE && len > 0) { #ifdef LWS_OPENSSL_SUPPORT if (wsi->use_ssl) { len = SSL_read(wsi->ssl, &c, 1); if (len < 0) { n = SSL_get_error(wsi->ssl, len); if (n == SSL_ERROR_WANT_READ || n == SSL_ERROR_WANT_WRITE) return 0; } } else #endif len = recv(wsi->sock, &c, 1, 0); if (len < 0) { lwsl_warn("error on parsing recv\n"); goto bail3; } if (libwebsocket_parse(wsi, c)) { lwsl_warn("problems parsing header\n"); goto bail3; } } /* * hs may also be coming in multiple packets, there is a 5-sec * libwebsocket timeout still active here too, so if parsing did * not complete just wait for next packet coming in this state */ if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) break; /* * otherwise deal with the handshake. If there's any * packet traffic already arrived we'll trigger poll() again * right away and deal with it that way */ return lws_client_interpret_server_handshake(context, wsi); bail3: lwsl_info( "closing connection at LWS_CONNMODE...SERVER_REPLY\n"); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return -1; case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT: lwsl_ext("LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT\n"); break; case LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD: lwsl_ext("LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD\n"); break; default: break; } return 0; }