+#ifdef FEATURE_HTTPS_INSPECTION
+/*********************************************************************
+ *
+ * Function : receive_and_send_encrypted_post_data
+ *
+ * Description : Reads remaining POST data from the client and sends
+ * it to the server.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : 0 on success, anything else is an error.
+ *
+ *********************************************************************/
+static int receive_and_send_encrypted_post_data(struct client_state *csp)
+{
+ int content_length_known = csp->expected_client_content_length != 0;
+
+ while (is_ssl_pending(&(csp->ssl_client_attr))
+ || (content_length_known && csp->expected_client_content_length != 0))
+ {
+ unsigned char buf[BUFFER_SIZE];
+ int len;
+ int max_bytes_to_read = sizeof(buf);
+
+ if (content_length_known && csp->expected_client_content_length < sizeof(buf))
+ {
+ max_bytes_to_read = (int)csp->expected_client_content_length;
+ }
+ log_error(LOG_LEVEL_CONNECT,
+ "Waiting for up to %d bytes of POST data from the client.",
+ max_bytes_to_read);
+ len = ssl_recv_data(&(csp->ssl_client_attr), buf,
+ (unsigned)max_bytes_to_read);
+ if (len == -1)
+ {
+ return 1;
+ }
+ if (len == 0)
+ {
+ /* XXX: Does this actually happen? */
+ break;
+ }
+ log_error(LOG_LEVEL_CONNECT, "Forwarding %d bytes of encrypted POST data",
+ len);
+ len = ssl_send_data(&(csp->ssl_server_attr), buf, (size_t)len);
+ if (len == -1)
+ {
+ return 1;
+ }
+ if (csp->expected_client_content_length != 0)
+ {
+ if (csp->expected_client_content_length >= len)
+ {
+ csp->expected_client_content_length -= (unsigned)len;
+ }
+ if (csp->expected_client_content_length == 0)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Forwarded the last %d bytes", len);
+ break;
+ }
+ }
+ }
+
+ log_error(LOG_LEVEL_CONNECT, "Done forwarding encrypted POST data");
+
+ return 0;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : send_https_request
+ *
+ * Description : Sends the HTTP headers from the client request
+ * and all the body data that has already been received.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : 0 on success, anything else is an error.
+ *
+ *********************************************************************/
+static int send_https_request(struct client_state *csp)
+{
+ char *hdr;
+ int ret;
+ long flushed = 0;
+
+ hdr = list_to_text(csp->https_headers);
+ if (hdr == NULL)
+ {
+ /* FIXME Should handle error properly */
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header");
+ }
+ list_remove_all(csp->https_headers);
+
+ /*
+ * Write the client's (modified) header to the server
+ * (along with anything else that may be in the buffer)
+ */
+ ret = ssl_send_data(&(csp->ssl_server_attr),
+ (const unsigned char *)hdr, strlen(hdr));
+ freez(hdr);
+
+ if (ret < 0)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Failed sending encrypted request headers to: %s: %E",
+ csp->http->hostport);
+ mark_server_socket_tainted(csp);
+ return 1;
+ }
+
+ if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0)
+ && ((flushed = ssl_flush_socket(&(csp->ssl_server_attr),
+ csp->client_iob)) < 0))
+ {
+ log_error(LOG_LEVEL_CONNECT, "Failed sending request body to: %s: %E",
+ csp->http->hostport);
+ return 1;
+ }
+ if (flushed != 0)
+ {
+ if (csp->expected_client_content_length != 0)
+ {
+ if (csp->expected_client_content_length < flushed)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Flushed %d bytes of request body while only expecting %llu",
+ flushed, csp->expected_client_content_length);
+ csp->expected_client_content_length = 0;
+ }
+ else
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Flushed %d bytes of request body while expecting %llu",
+ flushed, csp->expected_client_content_length);
+ csp->expected_client_content_length -= (unsigned)flushed;
+ if (receive_and_send_encrypted_post_data(csp))
+ {
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Flushed %d bytes of request body", flushed);
+ }
+ }
+
+ log_error(LOG_LEVEL_CONNECT, "Encrypted request sent");
+
+ return 0;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : receive_encrypted_request
+ *
+ * Description : Receives an encrypted request.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : JB_ERR_OK on success,
+ * JB_ERR_PARSE or JB_ERR_MEMORY otherwise
+ *
+ *********************************************************************/
+static jb_err receive_encrypted_request(struct client_state *csp)
+{
+ char buf[BUFFER_SIZE];
+ int len;
+ char *p;
+
+ do
+ {
+ log_error(LOG_LEVEL_HEADER, "Reading encrypted headers");
+ if (!is_ssl_pending(&(csp->ssl_client_attr)) &&
+ !data_is_available(csp->cfd, csp->config->socket_timeout))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Socket %d timed out while waiting for client headers", csp->cfd);
+ return JB_ERR_PARSE;
+ }
+ len = ssl_recv_data(&(csp->ssl_client_attr),
+ (unsigned char *)buf, sizeof(buf));
+ if (len == -1)
+ {
+ return JB_ERR_PARSE;
+ }
+ if (add_to_iob(csp->client_iob, csp->config->buffer_limit, buf, len))
+ {
+ return JB_ERR_MEMORY;
+ }
+ p = strstr(csp->client_iob->cur, "\r\n\r\n");
+ } while (p == NULL);
+
+ log_error(LOG_LEVEL_HEADER, "Encrypted headers received completely");
+
+ return JB_ERR_OK;
+}
+
+
+/*********************************************************************
+ *
+ * Function : process_encrypted_request
+ *
+ * Description : Receives and parses an encrypted request.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : JB_ERR_OK on success,
+ * JB_ERR_PARSE or JB_ERR_MEMORY otherwise
+ *
+ *********************************************************************/
+static jb_err process_encrypted_request(struct client_state *csp)
+{
+ char *p;
+ char *request_line;
+ jb_err err;
+ /* Temporary copy of the client's headers before they get enlisted in csp->https_headers */
+ struct list header_list;
+ struct list *headers = &header_list;
+
+ err = receive_encrypted_request(csp);
+ if (err != JB_ERR_OK)
+ {
+ /* XXX: Also used for JB_ERR_MEMORY */
+ log_error(LOG_LEVEL_ERROR, "Failed to receive encrypted request: %s",
+ jb_err_to_string(err));
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ return err;
+ }
+
+ /* We don't need get_request_line() because the whole HTTP head is buffered. */
+ request_line = get_header(csp->client_iob);
+ if (request_line == NULL)
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to get the encrypted request line");
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ return JB_ERR_PARSE;
+ }
+ assert(*request_line != '\0');
+
+ if (client_protocol_is_unsupported(csp, request_line))
+ {
+ /*
+ * If the protocol is unsupported we're done here.
+ * client_protocol_is_unsupported() took care of sending
+ * the error response and logging the error message.
+ */
+ return JB_ERR_PARSE;
+ }
+
+#ifdef FEATURE_FORCE_LOAD
+ if (force_required(csp, request_line))
+ {
+ csp->flags |= CSP_FLAG_FORCED;
+ }
+#endif /* def FEATURE_FORCE_LOAD */
+
+ free_http_request(csp->http);
+
+ err = parse_http_request(request_line, csp->http);
+ /* XXX: Restore ssl setting. This is ugly */
+ csp->http->client_ssl = 1;
+ csp->http->server_ssl = 1;
+
+ freez(request_line);
+ if (JB_ERR_OK != err)
+ {
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ /* XXX: Use correct size */
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request\" 400 0", csp->ip_addr_str);
+ log_error(LOG_LEVEL_ERROR,
+ "Couldn't parse request line received from %s: %s",
+ csp->ip_addr_str, jb_err_to_string(err));
+
+ free_http_request(csp->http);
+ return JB_ERR_PARSE;
+ }
+
+ /* Parse the rest of the client's headers. */
+ init_list(headers);
+ for (;;)
+ {
+ p = get_header(csp->client_iob);
+
+ if (p == NULL)
+ {
+ /* There are no additional headers to read. */
+ break;
+ }
+ enlist(headers, p);
+ freez(p);
+ }
+
+ if (JB_ERR_OK != get_destination_from_https_headers(headers, csp->http))
+ {
+ /*
+ * Our attempts to get the request destination
+ * elsewhere failed.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "Failed to get the encrypted request destination");
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ return JB_ERR_PARSE;
+ }
+
+ /* Split the domain we just got for pattern matching */
+ init_domain_components(csp->http);
+
+#ifdef FEATURE_TOGGLE
+ if ((csp->flags & CSP_FLAG_TOGGLED_ON) != 0)
+#endif
+ {
+ /* Determine the actions for this URL */
+ get_url_actions(csp, csp->http);
+ }
+
+ enlist(csp->https_headers, csp->http->cmd);
+
+ /* Append the previously read headers */
+ err = list_append_list_unique(csp->https_headers, headers);
+ destroy_list(headers);
+ if (JB_ERR_OK != err)
+ {
+ /* XXX: Send error message */
+ return err;
+ }
+
+ /* XXX: Work around crash */
+ csp->error_message = NULL;
+
+ /* XXX: Why do this here? */
+ csp->http->ssl = 1;
+
+ err = sed_https(csp);
+ if (JB_ERR_OK != err)
+ {
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+ log_error(LOG_LEVEL_ERROR, "Failed to parse client request from %s.",
+ csp->ip_addr_str);
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0",
+ csp->ip_addr_str, csp->http->cmd);
+ return JB_ERR_PARSE;
+ }
+
+ log_error(LOG_LEVEL_HEADER, "Encrypted request processed");
+ log_applied_actions(csp->action);
+ log_error(LOG_LEVEL_REQUEST, "https://%s%s", csp->http->hostport,
+ csp->http->path);
+
+ return err;
+
+}
+
+/*********************************************************************
+ *
+ * Function : cgi_page_requested
+ *
+ * Description : Checks if a request is for an internal CGI page.
+ *
+ * Parameters :
+ * 1 : host = The host requested by the client.
+ *
+ * Returns : 1 if a CGI page has been requested, 0 otherwise
+ *
+ *********************************************************************/
+static int cgi_page_requested(const char *host)
+{
+ if ((0 == strcmpic(host, CGI_SITE_1_HOST))
+ || (0 == strcmpic(host, CGI_SITE_1_HOST "."))
+ || (0 == strcmpic(host, CGI_SITE_2_HOST))
+ || (0 == strcmpic(host, CGI_SITE_2_HOST ".")))
+ {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+#endif
+
+