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; }
/* * notice this returns number of bytes consumed, or -1 */ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len) { struct lws_context *context = lws_get_context(wsi); struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; size_t real_len = len; unsigned int n; // lwsl_hexdump_err(buf, len); /* * Detect if we got called twice without going through the * event loop to handle pending. This would be caused by either * back-to-back writes in one WRITABLE (illegal) or calling lws_write() * from outside the WRITABLE callback (illegal). */ if (wsi->could_have_pending) { lwsl_hexdump_level(LLL_ERR, buf, len); lwsl_err("** %p: vh: %s, prot: %s, role %s: " "Illegal back-to-back write of %lu detected...\n", wsi, wsi->vhost->name, wsi->protocol->name, wsi->role_ops->name, (unsigned long)len); // assert(0); return -1; } lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1); if (!len) return 0; /* just ignore sends after we cleared the truncation buffer */ if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE && !wsi->trunc_len) return (int)len; if (wsi->trunc_len && (buf < wsi->trunc_alloc || buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) { lwsl_hexdump_level(LLL_ERR, buf, len); lwsl_err("** %p: vh: %s, prot: %s, Sending new %lu, pending truncated ...\n" " It's illegal to do an lws_write outside of\n" " the writable callback: fix your code\n", wsi, wsi->vhost->name, wsi->protocol->name, (unsigned long)len); assert(0); return -1; } if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd)) lwsl_warn("** error invalid sock but expected to send\n"); /* limit sending */ if (wsi->protocol->tx_packet_size) n = (int)wsi->protocol->tx_packet_size; else { n = (int)wsi->protocol->rx_buffer_size; if (!n) n = context->pt_serv_buf_size; } n += LWS_PRE + 4; if (n > len) n = (int)len; /* nope, send it on the socket directly */ lws_latency_pre(context, wsi); n = lws_ssl_capable_write(wsi, buf, n); lws_latency(context, wsi, "send lws_issue_raw", n, n == len); /* something got written, it can have been truncated now */ wsi->could_have_pending = 1; switch (n) { case LWS_SSL_CAPABLE_ERROR: /* we're going to close, let close know sends aren't possible */ wsi->socket_is_permanently_unusable = 1; return -1; case LWS_SSL_CAPABLE_MORE_SERVICE: /* * nothing got sent, not fatal. Retry the whole thing later, * ie, implying treat it was a truncated send so it gets * retried */ n = 0; break; } /* * we were already handling a truncated send? */ if (wsi->trunc_len) { lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len); wsi->trunc_offset += n; wsi->trunc_len -= n; if (!wsi->trunc_len) { lwsl_info("** %p partial send completed\n", wsi); /* done with it, but don't free it */ n = (int)real_len; if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) { lwsl_info("** %p signalling to close now\n", wsi); return -1; /* retry closing now */ } } /* always callback on writeable */ lws_callback_on_writable(wsi); return n; } if ((unsigned int)n == real_len) /* what we just sent went out cleanly */ return n; /* * Newly truncated send. Buffer the remainder (it will get * first priority next time the socket is writable). */ lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n, (unsigned long)real_len); lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1); lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n); /* * - if we still have a suitable malloc lying around, use it * - or, if too small, reallocate it * - or, if no buffer, create it */ if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) { lws_free(wsi->trunc_alloc); wsi->trunc_alloc_len = (unsigned int)(real_len - n); wsi->trunc_alloc = lws_malloc(real_len - n, "truncated send alloc"); if (!wsi->trunc_alloc) { lwsl_err("truncated send: unable to malloc %lu\n", (unsigned long)(real_len - n)); return -1; } } wsi->trunc_offset = 0; wsi->trunc_len = (unsigned int)(real_len - n); memcpy(wsi->trunc_alloc, buf + n, real_len - n); #if !defined(LWS_WITH_ESP32) if (lws_wsi_is_udp(wsi)) { /* stash original destination for fulfilling UDP partials */ wsi->udp->sa_pending = wsi->udp->sa; wsi->udp->salen_pending = wsi->udp->salen; } #endif /* since something buffered, force it to get another chance to send */ lws_callback_on_writable(wsi); return (int)real_len; }
int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len) { struct libwebsocket_context *context = wsi->protocol->owning_server; int n; size_t real_len = len; int m; if (!len) return 0; /* just ignore sends after we cleared the truncation buffer */ if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE && !wsi->truncated_send_len) return len; if (wsi->truncated_send_len && (buf < wsi->truncated_send_malloc || buf > (wsi->truncated_send_malloc + wsi->truncated_send_len + wsi->truncated_send_offset))) { lwsl_err("****** %x Sending new, pending truncated ...\n", wsi); assert(0); } m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_PACKET_TX_DO_SEND, &buf, len); if (m < 0) return -1; if (m) /* handled */ { n = m; goto handle_truncated_send; } if (wsi->sock < 0) lwsl_warn("** error invalid sock but expected to send\n"); /* * nope, send it on the socket directly */ lws_latency_pre(context, wsi); n = lws_ssl_capable_write(wsi, buf, len); lws_latency(context, wsi, "send lws_issue_raw", n, n == len); switch (n) { case LWS_SSL_CAPABLE_ERROR: /* we're going to close, let close know sends aren't possible */ wsi->socket_is_permanently_unusable = 1; return -1; case LWS_SSL_CAPABLE_MORE_SERVICE: /* nothing got sent, not fatal, retry the whole thing later */ n = 0; break; } handle_truncated_send: /* * we were already handling a truncated send? */ if (wsi->truncated_send_len) { lwsl_info("***** %x partial send moved on by %d (vs %d)\n", wsi, n, real_len); wsi->truncated_send_offset += n; wsi->truncated_send_len -= n; if (!wsi->truncated_send_len) { lwsl_info("***** %x partial send completed\n", wsi); /* done with it, but don't free it */ n = real_len; if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) { lwsl_info("***** %x signalling to close now\n", wsi); return -1; /* retry closing now */ } } /* always callback on writeable */ libwebsocket_callback_on_writable( wsi->protocol->owning_server, wsi); return n; } if (n == real_len) /* what we just sent went out cleanly */ return n; if (n && wsi->u.ws.clean_buffer) /* * This buffer unaffected by extension rewriting. * It means the user code is expected to deal with * partial sends. (lws knows the header was already * sent, so on next send will just resume sending * payload) */ return n; /* * Newly truncated send. Buffer the remainder (it will get * first priority next time the socket is writable) */ lwsl_info("***** %x new partial sent %d from %d total\n", wsi, n, real_len); /* * - if we still have a suitable malloc lying around, use it * - or, if too small, reallocate it * - or, if no buffer, create it */ if (!wsi->truncated_send_malloc || real_len - n > wsi->truncated_send_allocation) { lws_free(wsi->truncated_send_malloc); wsi->truncated_send_allocation = real_len - n; wsi->truncated_send_malloc = lws_malloc(real_len - n); if (!wsi->truncated_send_malloc) { lwsl_err("truncated send: unable to malloc %d\n", real_len - n); return -1; } } wsi->truncated_send_offset = 0; wsi->truncated_send_len = real_len - n; memcpy(wsi->truncated_send_malloc, buf + n, real_len - n); /* since something buffered, force it to get another chance to send */ libwebsocket_callback_on_writable(wsi->protocol->owning_server, wsi); return real_len; }
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 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_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len) { struct libwebsocket_context *context = wsi->protocol->owning_server; int n; size_t real_len = len; int m; if (!len) return 0; if (wsi->truncated_send_len && (buf < wsi->truncated_send_malloc || buf > (wsi->truncated_send_malloc + wsi->truncated_send_len + wsi->truncated_send_offset))) { lwsl_err("****** %x Sending new, pending truncated ...\n", wsi); assert(0); } m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_PACKET_TX_DO_SEND, &buf, len); if (m < 0) return -1; if (m) /* handled */ { n = m; goto handle_truncated_send; } if (wsi->sock < 0) lwsl_warn("** error invalid sock but expected to send\n"); /* * nope, send it on the socket directly */ lws_latency_pre(context, wsi); n = lws_ssl_capable_write(wsi, buf, len); lws_latency(context, wsi, "send lws_issue_raw", n, n == len); switch (n) { case LWS_SSL_CAPABLE_ERROR: return -1; case LWS_SSL_CAPABLE_MORE_SERVICE: n = 0; goto handle_truncated_send; } handle_truncated_send: /* * already handling a truncated send? */ if (wsi->truncated_send_len) { lwsl_info("***** %x partial send moved on by %d (vs %d)\n", wsi, n, real_len); wsi->truncated_send_offset += n; wsi->truncated_send_len -= n; if (!wsi->truncated_send_len) { lwsl_info("***** %x partial send completed\n", wsi); /* done with it, but don't free it */ n = real_len; } else libwebsocket_callback_on_writable( wsi->protocol->owning_server, wsi); return n; } if (n < real_len) { if (n && wsi->u.ws.clean_buffer) /* * This buffer unaffected by extension rewriting. * It means the user code is expected to deal with * partial sends. (lws knows the header was already * sent, so on next send will just resume sending * payload) */ return n; /* * Newly truncated send. Buffer the remainder (it will get * first priority next time the socket is writable) */ lwsl_info("***** %x new partial sent %d from %d total\n", wsi, n, real_len); /* * - if we still have a suitable malloc lying around, use it * - or, if too small, reallocate it * - or, if no buffer, create it */ if (!wsi->truncated_send_malloc || real_len - n > wsi->truncated_send_allocation) { if (wsi->truncated_send_malloc) free(wsi->truncated_send_malloc); wsi->truncated_send_allocation = real_len - n; wsi->truncated_send_malloc = malloc(real_len - n); if (!wsi->truncated_send_malloc) { lwsl_err( "truncated send: unable to malloc %d\n", real_len - n); return -1; } } wsi->truncated_send_offset = 0; wsi->truncated_send_len = real_len - n; memcpy(wsi->truncated_send_malloc, buf + n, real_len - n); libwebsocket_callback_on_writable( wsi->protocol->owning_server, wsi); return real_len; } return n; }