int lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, union lws_tls_cert_info_results *buf, size_t len) { int rc = 0; X509 *x509; wsi = lws_get_network_wsi(wsi); x509 = SSL_get_peer_certificate(wsi->tls.ssl); if (!x509) { lwsl_debug("no peer cert\n"); return -1; } switch (type) { case LWS_TLS_CERT_INFO_VERIFIED: buf->verified = SSL_get_verify_result(wsi->tls.ssl) == X509_V_OK; break; default: rc = lws_tls_openssl_cert_info(x509, type, buf, len); } X509_free(x509); return rc; }
LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi) { struct lws *wsi_eff = wsi; #if defined(LWS_WITH_HTTP2) wsi_eff = lws_get_network_wsi(wsi); #endif /* the fact we checked implies we avoided back-to-back writes */ wsi_eff->could_have_pending = 0; /* treat the fact we got a truncated send pending as if we're choked */ if (wsi_eff->trunc_len) return 1; return (int)wsi_eff->sock_send_blocking; }
LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi) { struct lws *wsi_eff; #if defined(LWS_WITH_HTTP2) wsi_eff = lws_get_network_wsi(wsi); #else wsi_eff = wsi; #endif /* the fact we checked implies we avoided back-to-back writes */ wsi_eff->could_have_pending = 0; /* treat the fact we got a truncated send pending as if we're choked */ if (lws_has_buffered_out(wsi_eff) #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) ||wsi->http.comp_ctx.buflist_comp || wsi->http.comp_ctx.may_have_more #endif ) return 1; return (int)wsi_eff->sock_send_blocking; }
LWS_VISIBLE int lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, union lws_tls_cert_info_results *buf, size_t len) { mbedtls_x509_crt *x509; wsi = lws_get_network_wsi(wsi); x509 = ssl_get_peer_mbedtls_x509_crt(wsi->tls.ssl); if (!x509) return -1; switch (type) { case LWS_TLS_CERT_INFO_VERIFIED: buf->verified = SSL_get_verify_result(wsi->tls.ssl) == X509_V_OK; return 0; default: return lws_tls_mbedtls_cert_info(x509, type, buf, len); } return -1; }
/* *check the socket choked or not */ LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi) { struct lws *wsi_eff = wsi; fd_set writefds; struct timeval tv = { 0, 0 }; #if defined(LWS_WITH_HTTP2) wsi_eff = lws_get_network_wsi(wsi); #endif int n; /* treat the fact we got a truncated send pending as if we're choked */ if (wsi_eff->trunc_len) return 1; FD_ZERO(&writefds); FD_SET(wsi_eff->desc.sockfd, &writefds); n = select(wsi_eff->desc.sockfd + 1, NULL, &writefds, NULL, &tv); if (n < 0) return 1; /* choked */ return !n; /* n = 0 = not writable = choked */ }
LWS_VISIBLE LWS_EXTERN int lws_cgi_write_split_stdout_headers(struct lws *wsi) { int n, m, cmd; unsigned char buf[LWS_PRE + 4096], *start = &buf[LWS_PRE], *p = start, *end = &buf[sizeof(buf) - 1 - LWS_PRE], *name, *value = NULL; char c, hrs; if (!wsi->http.cgi) return -1; while (wsi->hdr_state != LHCS_PAYLOAD) { /* * We have to separate header / finalize and payload chunks, * since they need to be handled separately */ switch (wsi->hdr_state) { case LHCS_RESPONSE: lwsl_debug("LHCS_RESPONSE: issuing response %d\n", wsi->http.cgi->response_code); if (lws_add_http_header_status(wsi, wsi->http.cgi->response_code, &p, end)) return 1; if (!wsi->http.cgi->explicitly_chunked && !wsi->http.cgi->content_length && lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING, (unsigned char *)"chunked", 7, &p, end)) return 1; if (!(wsi->http2_substream)) if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION, (unsigned char *)"close", 5, &p, end)) return 1; n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_NO_FIN); /* * so we have a bunch of http/1 style ascii headers * starting from wsi->http.cgi->headers_buf through * wsi->http.cgi->headers_pos. These are OK for http/1 * connections, but they're no good for http/2 conns. * * Let's redo them at headers_pos forward using the * correct coding for http/1 or http/2 */ if (!wsi->http2_substream) goto post_hpack_recode; p = wsi->http.cgi->headers_start; wsi->http.cgi->headers_start = wsi->http.cgi->headers_pos; wsi->http.cgi->headers_dumped = wsi->http.cgi->headers_start; hrs = HR_NAME; name = buf; while (p < wsi->http.cgi->headers_start) { switch (hrs) { case HR_NAME: /* * in http/2 upper-case header names * are illegal. So convert to lower- * case. */ if (name - buf > 64) return -1; if (*p != ':') { if (*p >= 'A' && *p <= 'Z') *name++ = (*p++) + ('a' - 'A'); else *name++ = *p++; } else { p++; *name++ = '\0'; value = name; hrs = HR_WHITESPACE; } break; case HR_WHITESPACE: if (*p == ' ') { p++; break; } hrs = HR_ARG; /* fallthru */ case HR_ARG: if (name > end - 64) return -1; if (*p != '\x0a' && *p != '\x0d') { *name++ = *p++; break; } hrs = HR_CRLF; /* fallthru */ case HR_CRLF: if ((*p != '\x0a' && *p != '\x0d') || p + 1 == wsi->http.cgi->headers_start) { *name = '\0'; if ((strcmp((const char *)buf, "transfer-encoding") )) { lwsl_debug("+ %s: %s\n", buf, value); if ( lws_add_http_header_by_name(wsi, buf, (unsigned char *)value, name - value, (unsigned char **)&wsi->http.cgi->headers_pos, (unsigned char *)wsi->http.cgi->headers_end)) return 1; hrs = HR_NAME; name = buf; break; } } p++; break; } } post_hpack_recode: /* finalize cached headers before dumping them */ if (lws_finalize_http_header(wsi, (unsigned char **)&wsi->http.cgi->headers_pos, (unsigned char *)wsi->http.cgi->headers_end)) { lwsl_notice("finalize failed\n"); return -1; } wsi->hdr_state = LHCS_DUMP_HEADERS; wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS; lws_callback_on_writable(wsi); /* back to the loop for writeability again */ return 0; case LHCS_DUMP_HEADERS: n = wsi->http.cgi->headers_pos - wsi->http.cgi->headers_dumped; if (n > 512) n = 512; lwsl_debug("LHCS_DUMP_HEADERS: %d\n", n); cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION; if (wsi->http.cgi->headers_dumped + n != wsi->http.cgi->headers_pos) { lwsl_notice("adding no fin flag\n"); cmd |= LWS_WRITE_NO_FIN; } m = lws_write(wsi, (unsigned char *)wsi->http.cgi->headers_dumped, n, cmd); if (m < 0) { lwsl_debug("%s: write says %d\n", __func__, m); return -1; } wsi->http.cgi->headers_dumped += n; if (wsi->http.cgi->headers_dumped == wsi->http.cgi->headers_pos) { wsi->hdr_state = LHCS_PAYLOAD; lws_free_set_NULL(wsi->http.cgi->headers_buf); lwsl_debug("freed cgi headers\n"); } else { wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS; lws_callback_on_writable(wsi); } /* writeability becomes uncertain now we wrote * something, we must return to the event loop */ return 0; } if (!wsi->http.cgi->headers_buf) { /* if we don't already have a headers buf, cook one */ n = 2048; if (wsi->http2_substream) n = 4096; wsi->http.cgi->headers_buf = lws_malloc(n + LWS_PRE, "cgi hdr buf"); if (!wsi->http.cgi->headers_buf) { lwsl_err("OOM\n"); return -1; } lwsl_debug("allocated cgi hdrs\n"); wsi->http.cgi->headers_start = wsi->http.cgi->headers_buf + LWS_PRE; wsi->http.cgi->headers_pos = wsi->http.cgi->headers_start; wsi->http.cgi->headers_dumped = wsi->http.cgi->headers_pos; wsi->http.cgi->headers_end = wsi->http.cgi->headers_buf + n - 1; for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) { wsi->http.cgi->match[n] = 0; wsi->http.cgi->lp = 0; } } n = lws_get_socket_fd(wsi->http.cgi->stdwsi[LWS_STDOUT]); if (n < 0) return -1; n = read(n, &c, 1); if (n < 0) { if (errno != EAGAIN) { lwsl_debug("%s: read says %d\n", __func__, n); return -1; } else n = 0; if (wsi->http.cgi->headers_pos >= wsi->http.cgi->headers_end - 4) { lwsl_notice("CGI hdrs > buf size\n"); return -1; } } if (!n) goto agin; lwsl_debug("-- 0x%02X %c %d %d\n", (unsigned char)c, c, wsi->http.cgi->match[1], wsi->hdr_state); if (!c) return -1; switch (wsi->hdr_state) { case LCHS_HEADER: hdr: for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) { /* * significant headers with * numeric decimal payloads */ if (!significant_hdr[n][wsi->http.cgi->match[n]] && (c >= '0' && c <= '9') && wsi->http.cgi->lp < (int)sizeof(wsi->http.cgi->l) - 1) { wsi->http.cgi->l[wsi->http.cgi->lp++] = c; wsi->http.cgi->l[wsi->http.cgi->lp] = '\0'; switch (n) { case SIGNIFICANT_HDR_CONTENT_LENGTH: wsi->http.cgi->content_length = atoll(wsi->http.cgi->l); break; case SIGNIFICANT_HDR_STATUS: wsi->http.cgi->response_code = atol(wsi->http.cgi->l); lwsl_debug("Status set to %d\n", wsi->http.cgi->response_code); break; default: break; } } /* hits up to the NUL are sticky until next hdr */ if (significant_hdr[n][wsi->http.cgi->match[n]]) { if (tolower(c) == significant_hdr[n][wsi->http.cgi->match[n]]) wsi->http.cgi->match[n]++; else wsi->http.cgi->match[n] = 0; } } /* some cgi only send us \x0a for EOL */ if (c == '\x0a') { wsi->hdr_state = LCHS_SINGLE_0A; *wsi->http.cgi->headers_pos++ = '\x0d'; } *wsi->http.cgi->headers_pos++ = c; if (c == '\x0d') wsi->hdr_state = LCHS_LF1; if (wsi->hdr_state != LCHS_HEADER && !significant_hdr[SIGNIFICANT_HDR_TRANSFER_ENCODING] [wsi->http.cgi->match[ SIGNIFICANT_HDR_TRANSFER_ENCODING]]) { lwsl_info("cgi produced chunked\n"); wsi->http.cgi->explicitly_chunked = 1; } /* presence of Location: mandates 302 retcode */ if (wsi->hdr_state != LCHS_HEADER && !significant_hdr[SIGNIFICANT_HDR_LOCATION][ wsi->http.cgi->match[SIGNIFICANT_HDR_LOCATION]]) { lwsl_debug("CGI: Location hdr seen\n"); wsi->http.cgi->response_code = 302; } break; case LCHS_LF1: *wsi->http.cgi->headers_pos++ = c; if (c == '\x0a') { wsi->hdr_state = LCHS_CR2; break; } /* we got \r[^\n]... it's unreasonable */ lwsl_debug("%s: funny CRLF 0x%02X\n", __func__, (unsigned char)c); return -1; case LCHS_CR2: if (c == '\x0d') { /* drop the \x0d */ wsi->hdr_state = LCHS_LF2; break; } wsi->hdr_state = LCHS_HEADER; for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) wsi->http.cgi->match[n] = 0; wsi->http.cgi->lp = 0; goto hdr; case LCHS_LF2: case LCHS_SINGLE_0A: m = wsi->hdr_state; if (c == '\x0a') { lwsl_debug("Content-Length: %lld\n", (unsigned long long) wsi->http.cgi->content_length); wsi->hdr_state = LHCS_RESPONSE; /* * drop the \0xa ... finalize * will add it if needed (HTTP/1) */ break; } if (m == LCHS_LF2) /* we got \r\n\r[^\n]... unreasonable */ return -1; /* we got \x0anext header, it's reasonable */ *wsi->http.cgi->headers_pos++ = c; wsi->hdr_state = LCHS_HEADER; for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) wsi->http.cgi->match[n] = 0; wsi->http.cgi->lp = 0; break; case LHCS_PAYLOAD: break; } agin: /* ran out of input, ended the hdrs, or filled up the hdrs buf */ if (!n || wsi->hdr_state == LHCS_PAYLOAD) return 0; } /* payload processing */ m = !wsi->http.cgi->implied_chunked && !wsi->http2_substream && !wsi->http.cgi->explicitly_chunked && !wsi->http.cgi->content_length; n = lws_get_socket_fd(wsi->http.cgi->stdwsi[LWS_STDOUT]); if (n < 0) return -1; if (m) { uint8_t term[LWS_PRE + 6]; lwsl_info("%s: zero chunk\n", __func__); memcpy(term + LWS_PRE, (uint8_t *)"0\x0d\x0a\x0d\x0a", 5); if (lws_write(wsi, term + LWS_PRE, 5, LWS_WRITE_HTTP_FINAL) != 5) return -1; wsi->http.cgi->cgi_transaction_over = 1; return 0; } n = read(n, start, sizeof(buf) - LWS_PRE); if (n < 0 && errno != EAGAIN) { lwsl_debug("%s: stdout read says %d\n", __func__, n); return -1; } if (n > 0) { /* if (!wsi->http2_substream && m) { char chdr[LWS_HTTP_CHUNK_HDR_SIZE]; m = lws_snprintf(chdr, LWS_HTTP_CHUNK_HDR_SIZE - 3, "%X\x0d\x0a", n); memmove(start + m, start, n); memcpy(start, chdr, m); memcpy(start + m + n, "\x0d\x0a", 2); n += m + 2; } */ #if defined(LWS_WITH_HTTP2) if (wsi->http2_substream) { struct lws *nwsi = lws_get_network_wsi(wsi); __lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31); if (!nwsi->immortal_substream_count) __lws_set_timeout(nwsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31); } #endif cmd = LWS_WRITE_HTTP; if (wsi->http.cgi->content_length_seen + n == wsi->http.cgi->content_length) cmd = LWS_WRITE_HTTP_FINAL; m = lws_write(wsi, (unsigned char *)start, n, cmd); //lwsl_notice("write %d\n", m); if (m < 0) { lwsl_debug("%s: stdout write says %d\n", __func__, m); return -1; } wsi->http.cgi->content_length_seen += n; } else { if (wsi->cgi_stdout_zero_length) { lwsl_debug("%s: stdout is POLLHUP'd\n", __func__); if (wsi->http2_substream) m = lws_write(wsi, (unsigned char *)start, 0, LWS_WRITE_HTTP_FINAL); else return -1; return 1; } wsi->cgi_stdout_zero_length = 1; } return 0; }