+ * 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 ((content_length_known && csp->expected_client_content_length != 0) ||
+ (is_ssl_pending(&(csp->ssl_client_attr)) ||
+ data_is_available(csp->cfd, csp->config->socket_timeout)))
+ {
+ 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,
+ "Prepared to read up to %d bytes of encrypted request body 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 request body",
+ 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 request body");
+
+ 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;
+ }
+
+ /* XXX: Client body isn't sent if there's pipelined data? */
+ 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 || csp->expected_client_content_length != 0)
+ {
+ if (csp->expected_client_content_length != 0)
+ {
+ if (csp->expected_client_content_length < flushed)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Flushed %ld 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 %ld 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 %ld bytes of request body", flushed);
+ }
+ }
+
+ log_error(LOG_LEVEL_CONNECT, "Encrypted request sent");
+
+ return 0;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : receive_encrypted_request_headers
+ *
+ * Description : Receives the encrypted request headers when
+ * https-inspecting.
+ *
+ * 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_headers(struct client_state *csp)
+{
+ char buf[BUFFER_SIZE];
+ int len;
+ char *p;
+
+ do
+ {
+ log_error(LOG_LEVEL_HEADER, "Waiting for encrypted client 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 == 0)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Socket %d closed while waiting for client headers", csp->cfd);
+ return JB_ERR_PARSE;
+ }
+ 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 : change_encrypted_request_destination
+ *
+ * Description : Parse a (rewritten) request line from an encrypted
+ * request and regenerate the http request data.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Forwards the parse_http_request() return code.
+ * Terminates in case of memory problems.
+ *
+ *********************************************************************/
+static jb_err change_encrypted_request_destination(struct client_state *csp)
+{
+ jb_err err;
+ char *original_host = csp->http->host;
+ int original_port = csp->http->port;
+
+ log_error(LOG_LEVEL_REDIRECTS, "Rewrite detected: %s",
+ csp->https_headers->first->str);
+ csp->http->host = NULL;
+ free_http_request(csp->http);
+ err = parse_http_request(csp->https_headers->first->str, csp->http);
+ if (JB_ERR_OK != err)
+ {
+ log_error(LOG_LEVEL_ERROR, "Couldn't parse rewritten request: %s.",
+ jb_err_to_string(err));
+ freez(original_host);
+ return err;
+ }
+
+ if (csp->http->host == NULL)
+ {
+ char port_string[10];
+ /*
+ * The rewritten request line did not specify a host
+ * which means we can use the original host specified
+ * by the client.
+ */
+ csp->http->host = original_host;
+ csp->http->port = original_port;
+ log_error(LOG_LEVEL_REDIRECTS, "Keeping the original host: %s",
+ csp->http->host);
+ /*
+ * If the rewritten request line didn't contain a host
+ * it also didn't contain a port so we can reuse the host
+ * port.
+ */
+ freez(csp->http->hostport);
+ csp->http->hostport = strdup_or_die(csp->http->host);
+ snprintf(port_string, sizeof(port_string), ":%d", original_port);
+ err = string_append(&csp->http->hostport, port_string);
+ if (err != JB_ERR_OK)
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to rebuild hostport: %s.",
+ jb_err_to_string(err));
+ return err;
+ }
+
+ /*
+ * While the request line didn't mention it,
+ * we're https-inspecting and want to speak TLS
+ * with the server.
+ */
+ csp->http->server_ssl = 1;
+ csp->http->ssl = 1;
+ }
+ else
+ {
+ /* The rewrite filter added a host so we can ditch the original */
+ freez(original_host);
+ csp->http->server_ssl = csp->http->ssl;
+ }
+
+ csp->http->client_ssl = 1;
+
+ freez(csp->https_headers->first->str);
+ build_request_line(csp, NULL, &csp->https_headers->first->str);
+
+ if (!server_use_ssl(csp))
+ {
+ log_error(LOG_LEVEL_REDIRECTS,
+ "Rewritten request line results in downgrade to http");
+ /*
+ * Replace the unencryptd headers received with the
+ * CONNECT request with the ones we received securely.
+ */
+ destroy_list(csp->headers);
+ csp->headers->first = csp->https_headers->first;
+ csp->headers->last = csp->https_headers->last;
+ csp->https_headers->first = NULL;
+ csp->https_headers->last = NULL;
+ }
+
+ return JB_ERR_OK;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : process_encrypted_request_headers
+ *
+ * Description : Receives and parses the encrypted headers send
+ * by the client when https-inspecting.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : JB_ERR_OK on success,