+ len = 0;
+ }
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (csp->flags & CSP_FLAG_CHUNKED)
+ {
+ if ((len >= 5) && !memcmp(csp->receive_buffer+len-5, "0\r\n\r\n", 5))
+ {
+ /* XXX: this is a temporary hack */
+ log_error(LOG_LEVEL_CONNECT,
+ "Looks like we reached the end of the last chunk. "
+ "We better stop reading.");
+ csp->expected_content_length = byte_count + (unsigned long long)len;
+ csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET;
+ }
+ }
+ reading_done:
+#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+
+ /*
+ * This is guaranteed by allocating with zalloc_or_die()
+ * and never (intentionally) writing to the last byte.
+ *
+ * csp->receive_buffer_size is the size of the part of the
+ * buffer we intentionally write to, but we actually
+ * allocated csp->receive_buffer_size+1 bytes so the assertion
+ * stays within the allocated range.
+ */
+ assert(csp->receive_buffer[csp->receive_buffer_size] == '\0');
+
+ /*
+ * Add a trailing zero to let be able to use string operations.
+ * XXX: do we still need this with filter_popups gone?
+ */
+ assert(len <= csp->receive_buffer_size);
+ csp->receive_buffer[len] = '\0';
+
+ /*
+ * Normally, this would indicate that we've read
+ * as much as the server has sent us and we can
+ * close the client connection. However, Microsoft
+ * in its wisdom has released IIS/5 with a bug that
+ * prevents it from sending the trailing \r\n in
+ * a 302 redirect header (and possibly other headers).
+ * To work around this if we've haven't parsed
+ * a full header we'll append a trailing \r\n
+ * and see if this now generates a valid one.
+ *
+ * This hack shouldn't have any impacts. If we've
+ * already transmitted the header or if this is a
+ * SSL connection, then we won't bother with this
+ * hack. So we only work on partially received
+ * headers. If we append a \r\n and this still
+ * doesn't generate a valid header, then we won't
+ * transmit anything to the client.
+ */
+ if (len == 0)
+ {
+
+ if (server_body || (http->ssl
+#ifdef FEATURE_HTTPS_INSPECTION
+ && use_ssl_tunnel
+#endif
+ ))
+ {
+ /*
+ * If we have been buffering up the document,
+ * now is the time to apply content modification
+ * and send the result to the client.
+ */
+ if (buffer_and_filter_content)
+ {
+ p = execute_content_filters(csp);
+ /*
+ * If content filtering fails, use the original
+ * buffer and length.
+ * (see p != NULL ? p : csp->iob->cur below)
+ */
+ if (NULL == p)
+ {
+ csp->content_length = (size_t)(csp->iob->eod - csp->iob->cur);
+ }
+#ifdef FEATURE_COMPRESSION
+ else if ((csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE)
+ && (csp->content_length > LOWER_LENGTH_LIMIT_FOR_COMPRESSION))
+ {
+ char *compressed_content = compress_buffer(p,
+ (size_t *)&csp->content_length, csp->config->compression_level);
+ if (compressed_content != NULL)
+ {
+ freez(p);
+ p = compressed_content;
+ csp->flags |= CSP_FLAG_BUFFERED_CONTENT_DEFLATED;
+ }
+ }
+#endif
+
+ if (JB_ERR_OK != update_server_headers(csp))
+ {
+ log_error(LOG_LEVEL_FATAL,
+ "Failed to update server headers. after filtering.");
+ }
+
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /* FIXME Should handle error properly */
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
+ }
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *)hdr, strlen(hdr)) < 0)
+ || (ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *) ((p != NULL) ? p : csp->iob->cur),
+ csp->content_length) < 0))
+ {
+ log_error(LOG_LEVEL_ERROR, "write modified content to "
+ "client over TLS/SSL failed");
+ freez(hdr);
+ freez(p);
+ mark_server_socket_tainted(csp);
+ close_client_and_server_ssl_connections(csp);
+ return;
+ }
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ if (write_socket_delayed(csp->cfd, hdr, strlen(hdr), write_delay)
+ || write_socket_delayed(csp->cfd, ((p != NULL) ? p : csp->iob->cur),
+ (size_t)csp->content_length, write_delay))
+ {
+ log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E");
+ freez(hdr);
+ freez(p);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ }
+
+ freez(hdr);
+ freez(p);
+ }
+
+ break; /* "game over, man" */
+ }
+
+ /*
+ * This is NOT the body, so
+ * Let's pretend the server just sent us a blank line.
+ */
+ snprintf(csp->receive_buffer, csp->receive_buffer_size, "\r\n");
+ len = (int)strlen(csp->receive_buffer);
+
+ /*
+ * Now, let the normal header parsing algorithm below do its
+ * job. If it fails, we'll exit instead of continuing.
+ */
+
+ ms_iis5_hack = 1;
+ }
+
+ /*
+ * If we're in the body of the server document, just write it to
+ * the client, unless we need to buffer the body for later
+ * content-filtering.
+ */
+ if (server_body || (http->ssl
+#ifdef FEATURE_HTTPS_INSPECTION
+ && use_ssl_tunnel
+#endif
+ ))
+ {
+ if (buffer_and_filter_content)
+ {
+ /*
+ * If there is no memory left for buffering the content, or the buffer limit
+ * has been reached, switch to non-filtering mode, i.e. make & write the
+ * header, flush the iob and buf, and get out of the way.
+ */
+ if (add_to_iob(csp->iob, csp->config->buffer_limit, csp->receive_buffer, len))
+ {
+ size_t hdrlen;
+ long flushed;
+
+ log_error(LOG_LEVEL_INFO,
+ "Flushing header and buffers. Stepping back from filtering.");
+
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /*
+ * Memory is too tight to even generate the header.
+ * Send our static "Out-of-memory" page.
+ */
+ log_error(LOG_LEVEL_ERROR, "Out of memory while trying to flush.");
+ rsp = cgi_error_memory();
+ send_crunch_response(csp, rsp);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ hdrlen = strlen(hdr);
+
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *)hdr, hdrlen) < 0)
+ || ((flushed = ssl_flush_socket(&(csp->mbedtls_client_attr.ssl),
+ csp->iob)) < 0)
+ || (ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *)csp->receive_buffer, (size_t)len) < 0))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Flush header and buffers to client failed");
+ freez(hdr);
+ mark_server_socket_tainted(csp);
+ close_client_and_server_ssl_connections(csp);
+ return;
+ }
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ if (write_socket_delayed(csp->cfd, hdr, hdrlen, write_delay)
+ || ((flushed = flush_iob(csp->cfd, csp->iob, write_delay)) < 0)
+ || write_socket_delayed(csp->cfd, csp->receive_buffer, (size_t)len,
+ write_delay))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Flush header and buffers to client failed: %E");
+ freez(hdr);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ }
+
+ /*
+ * Reset the byte_count to the amount of bytes
+ * we just flushed. len will be added a few lines below,
+ * hdrlen doesn't matter for LOG_LEVEL_CLF.
+ */
+ byte_count = (unsigned long long)flushed;
+ freez(hdr);
+ buffer_and_filter_content = 0;
+ server_body = 1;
+ }
+ }
+ else
+ {
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ ret = ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *)csp->receive_buffer, (size_t)len);
+ if (ret < 0)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Sending data to client failed");
+ mark_server_socket_tainted(csp);
+ close_client_and_server_ssl_connections(csp);
+ return;
+ }
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ if (write_socket_delayed(csp->cfd, csp->receive_buffer,
+ (size_t)len, write_delay))
+ {
+ log_error(LOG_LEVEL_ERROR, "write to client failed: %E");
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ }
+ }
+ byte_count += (unsigned long long)len;
+ continue;
+ }
+ else
+ {
+ /*
+ * We're still looking for the end of the server's header.
+ * Buffer up the data we just read. If that fails, there's
+ * little we can do but send our static out-of-memory page.
+ */
+ if (add_to_iob(csp->iob, csp->config->buffer_limit, csp->receive_buffer, len))
+ {
+ log_error(LOG_LEVEL_ERROR, "Out of memory while looking for end of server headers.");
+ rsp = cgi_error_memory();
+ send_crunch_response(csp, rsp);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+
+ /* Convert iob into something sed() can digest */
+ if (JB_ERR_PARSE == get_server_headers(csp))
+ {
+ if (ms_iis5_hack)
+ {
+ /*
+ * Well, we tried our MS IIS/5 hack and it didn't work.
+ * The header is incomplete and there isn't anything
+ * we can do about it.
+ */
+ log_error(LOG_LEVEL_ERROR, "Invalid server headers. "
+ "Applying the MS IIS5 hack didn't help.");
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE));
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ write_socket_delayed(csp->cfd,
+ INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
+ }
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ else
+ {
+ /*
+ * Since we have to wait for more from the server before
+ * we can parse the headers we just continue here.
+ */
+ log_error(LOG_LEVEL_CONNECT,
+ "Continuing buffering server headers from socket %d. "
+ "Bytes most recently read: %d.", csp->cfd, len);
+ continue;
+ }
+ }
+ else
+ {
+ /*
+ * Account for the content bytes we
+ * might have gotten with the headers.
+ */
+ assert(csp->iob->eod >= csp->iob->cur);
+ byte_count = (unsigned long long)(csp->iob->eod - csp->iob->cur);
+ }
+
+ /* Did we actually get anything? */
+ if (NULL == csp->headers->first)
+ {
+ if ((csp->flags & CSP_FLAG_REUSED_CLIENT_CONNECTION))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "No server or forwarder response received on socket %d. "
+ "Closing client socket %d without sending data.",
+ csp->server_connection.sfd, csp->cfd);
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+ }
+ else
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "No server or forwarder response received on socket %d.",
+ csp->server_connection.sfd);
+ send_crunch_response(csp, error_response(csp, "no-server-data"));
+ }
+ free_http_request(http);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+
+ if (!csp->headers->first->str)
+ {
+ log_error(LOG_LEVEL_ERROR, "header search: csp->headers->first->str == NULL, assert will be called");
+ }
+ assert(csp->headers->first->str);
+
+ if (strncmpic(csp->headers->first->str, "HTTP", 4) &&
+ strncmpic(csp->headers->first->str, "ICY", 3))
+ {
+ /*
+ * It doesn't look like a HTTP (or Shoutcast) response:
+ * tell the client and log the problem.
+ */
+ if (strlen(csp->headers->first->str) > 30)
+ {
+ csp->headers->first->str[30] = '\0';
+ }
+ log_error(LOG_LEVEL_ERROR,
+ "Invalid server or forwarder response. Starts with: %s",
+ csp->headers->first->str);
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE));
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
+ }
+ free_http_request(http);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+
+ /*
+ * We have now received the entire server header,
+ * filter it and send the result to the client
+ */
+ if (JB_ERR_OK != sed(csp, FILTER_SERVER_HEADERS))
+ {
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE));
+ }
+ else
+#endif
+ {
+ write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
+ }
+ free_http_request(http);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /* FIXME Should handle error properly */
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
+ }
+
+ if ((csp->flags & CSP_FLAG_CHUNKED)
+ && !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET)
+ && ((csp->iob->eod - csp->iob->cur) >= 5)
+ && !memcmp(csp->iob->eod-5, "0\r\n\r\n", 5))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Looks like we got the last chunk together with "
+ "the server headers. We better stop reading.");
+ byte_count = (unsigned long long)(csp->iob->eod - csp->iob->cur);
+ csp->expected_content_length = byte_count;
+ csp->flags |= CSP_FLAG_CONTENT_LENGTH_SET;
+ }
+
+ csp->server_connection.response_received = time(NULL);
+
+ if (crunch_response_triggered(csp, crunchers_light))
+ {
+ /*
+ * One of the tags created by a server-header
+ * tagger triggered a crunch. We already
+ * delivered the crunch response to the client
+ * and are done here after cleaning up.
+ */
+ freez(hdr);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+
+ /* Buffer and pcrs filter this if appropriate. */
+ buffer_and_filter_content = content_requires_filtering(csp);
+
+ if (!buffer_and_filter_content)
+ {
+ /*
+ * Write the server's (modified) header to
+ * the client (along with anything else that
+ * may be in the buffer). Use standard or secured
+ * connection.
+ */
+#ifdef FEATURE_HTTPS_INSPECTION
+ if (client_use_ssl(csp))
+ {
+ if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *)hdr, strlen(hdr)) < 0)
+ || (len = ssl_flush_socket(&(csp->mbedtls_client_attr.ssl),
+ csp->iob) < 0))
+ {
+ log_error(LOG_LEVEL_CONNECT, "Write header to client failed");
+
+ /*
+ * The write failed, so don't bother mentioning it
+ * to the client... it probably can't hear us anyway.
+ */
+ freez(hdr);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ if (write_socket_delayed(csp->cfd, hdr, strlen(hdr), write_delay)
+ || ((len = flush_iob(csp->cfd, csp->iob, write_delay)) < 0))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "write header to client failed");
+ /*
+ * The write failed, so don't bother mentioning it
+ * to the client... it probably can't hear us anyway.
+ */
+ freez(hdr);
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }
+ }
+ }
+
+ /* we're finished with the server's header */
+
+ freez(hdr);
+ server_body = 1;
+
+ /*
+ * If this was a MS IIS/5 hack then it means the server
+ * has already closed the connection. Nothing more to read.
+ * Time to bail.
+ */
+ if (ms_iis5_hack)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Closed server connection detected. "
+ "Applying the MS IIS5 hack didn't help.");
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+#ifdef FEATURE_HTTPS_INSPECTION
+ /*
+ * Sending data with standard or secured connection (HTTP/HTTPS)
+ */
+ if (client_use_ssl(csp))
+ {
+ ssl_send_data(&(csp->mbedtls_client_attr.ssl),
+ (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE));
+ }
+ else
+#endif /* def FEATURE_HTTPS_INSPECTION */
+ {
+ write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE,
+ strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay);
+ }
+ mark_server_socket_tainted(csp);
+#ifdef FEATURE_HTTPS_INSPECTION
+ close_client_and_server_ssl_connections(csp);
+#endif
+ return;
+ }