+ return 1;
+ }
+ assert(to_read >= len);
+ }
+
+ if (to_read != 0)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Not enough request body has been read: expected %d more bytes", to_read);
+ return 1;
+ }
+
+ log_error(LOG_LEVEL_CONNECT, "The last %d bytes of the request body have been read",
+ csp->expected_client_content_length);
+ return 0;
+}
+
+
+/*********************************************************************
+ *
+ * Function : receive_and_send_encrypted_post_data
+ *
+ * Description : Reads remaining request body 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 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;
+ const char *to_send;
+ size_t to_send_len;
+ int filter_client_body = csp->expected_client_content_length != 0 &&
+ client_body_filters_enabled(csp->action) && can_filter_request_body(csp);
+
+ if (filter_client_body)
+ {
+ if (read_https_request_body(csp))
+ {
+ return 1;
+ }
+ to_send_len = csp->expected_client_content_length;
+ to_send = execute_client_body_filters(csp, &to_send_len);
+ if (to_send == NULL)
+ {
+ /* just flush client_iob */
+ filter_client_body = FALSE;
+ }
+ else if (to_send_len != csp->expected_client_content_length &&
+ update_client_headers(csp, to_send_len))
+ {
+ log_error(LOG_LEVEL_HEADER, "Error updating client headers");
+ return 1;
+ }
+ csp->expected_client_content_length = 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 (filter_client_body)
+ {
+ ret = ssl_send_data(&(csp->ssl_server_attr), (const unsigned char *)to_send, to_send_len);
+ freez(to_send);
+ if (ret < 0)
+ {
+ log_error(LOG_LEVEL_CONNECT, "Failed sending filtered request body to: %s",
+ csp->http->hostport);
+ 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 || 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
+ *
+ * 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 == 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;
+
+ 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)
+ {
+ /*
+ * 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;
+ 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
+ * and set the port to 443.
+ */
+ freez(csp->http->hostport);
+ csp->http->hostport = strdup_or_die(csp->http->host);
+ csp->http->port = 443;
+ /*
+ * 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
+ *
+ * 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;
+
+ assert(csp->ssl_with_client_is_opened);
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)
+ {
+ csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ }
+#endif
+ err = receive_encrypted_request(csp);
+ if (err != JB_ERR_OK)
+ {
+ if (csp->client_iob->cur == NULL ||
+ csp->client_iob->cur == csp->client_iob->eod)
+ {
+ /*
+ * We did not receive any data, most likely because the
+ * client is done. Don't log this as a parse failure.
+ */
+ return JB_ERR_PARSE;
+ }
+ /* 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_CLIENT_TAGS
+ /* XXX: If the headers were enlisted sooner, passing csp would do. */
+ if (csp->client_address == NULL)
+ {
+ set_client_address(csp, headers);
+ get_tag_list_for_client(csp->client_tags, csp->client_address);
+ }
+#endif
+
+#ifdef FEATURE_TOGGLE
+ if ((csp->flags & CSP_FLAG_TOGGLED_ON) != 0)
+#endif
+ {
+ /*
+ * Determine the actions for this request after
+ * clearing the ones from the previous one.
+ */
+ free_current_action(csp->action);
+ 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;
+ }
+
+ if ((NULL == csp->https_headers->first->str)
+ || (strcmp(csp->http->cmd, csp->https_headers->first->str) &&
+ (JB_ERR_OK != change_encrypted_request_destination(csp))))
+ {
+ ssl_send_data_delayed(&(csp->ssl_client_attr),
+ (const unsigned char *)MESSED_UP_REQUEST_RESPONSE,
+ strlen(MESSED_UP_REQUEST_RESPONSE), get_write_delay(csp));
+ log_error(LOG_LEVEL_ERROR,
+ "Invalid request line after applying header filters.");
+ /* XXX: Use correct size */
+ log_error(LOG_LEVEL_CLF,
+ "%s - - [%T] \"Invalid request generated\" 400 0", csp->ip_addr_str);
+
+ 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;
+
+}
+
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+/*********************************************************************
+ *
+ * Function : continue_https_chat
+ *
+ * Description : Behaves similar to chat() but only deals with
+ * https-inspected requests that arrive on an already
+ * established connection. The first request is always
+ * served by chat() which is a lot more complex as it
+ * has to deal with forwarding settings and connection
+ * failures etc.
+ *
+ * If a connection to the server has already been
+ * opened it is reused unless the request is blocked
+ * or the forwarder changed.
+ *
+ * If a connection to the server has not yet been
+ * opened (because the previous request was crunched),
+ * or the forwarder changed, the connection is dropped
+ * so that the client retries on a fresh one.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Nothing.
+ *
+ *********************************************************************/
+static void continue_https_chat(struct client_state *csp)
+{
+ const struct forward_spec *fwd;
+
+ if (JB_ERR_OK != process_encrypted_request(csp))
+ {
+ return;
+ }
+
+ csp->requests_received_total++;
+
+ /*
+ * We have an encrypted request. Check if one of the crunchers wants it.
+ */
+ if (crunch_response_triggered(csp, crunchers_all))
+ {
+ /*
+ * Yes. The client got the crunch response and we're done here.
+ */
+ return;
+ }
+ if (csp->ssl_with_server_is_opened == 0)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Dropping the client connection on socket %d. "
+ "The server connection has not been established yet.",
+ csp->cfd);
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ return;
+ }
+ assert(csp->server_connection.sfd != JB_INVALID_SOCKET);
+
+ fwd = forward_url(csp, csp->http);
+ if (!connection_destination_matches(&csp->server_connection, csp->http, fwd))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Dropping the client connection on socket %d with "
+ "server socket %d connected to %s. The forwarder has changed.",
+ csp->cfd, csp->server_connection.sfd, csp->server_connection.host);
+ csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+ return;
+ }
+
+ log_error(LOG_LEVEL_CONNECT,
+ "Reusing server socket %d connected to %s. Requests already sent: %u.",
+ csp->server_connection.sfd, csp->server_connection.host,
+ csp->server_connection.requests_sent_total);
+
+ if (send_https_request(csp))
+ {
+ /*
+ * Most likely the server connection timed out. We can't easily
+ * create a new one so simply drop the client connection without a
+ * error response to let the client retry.
+ */
+ log_error(LOG_LEVEL_CONNECT,
+ "Dropping client connection on socket %d. "
+ "Forwarding the encrypted client request failed.",
+ csp->cfd);
+ return;
+ }
+ csp->server_connection.requests_sent_total++;
+ handle_established_connection(csp);
+ freez(csp->receive_buffer);
+}
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+#endif
+
+
+/*********************************************************************
+ *
+ * Function : handle_established_connection
+ *
+ * Description : Shuffle data between client and server once the
+ * connection has been established.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Nothing.
+ *
+ *********************************************************************/
+static void handle_established_connection(struct client_state *csp)
+{
+ char *hdr;
+ char *p;
+ int n;
+#ifdef HAVE_POLL
+ struct pollfd poll_fds[2];
+#else
+ fd_set rfds;
+ jb_socket maxfd;
+ struct timeval timeout;
+#endif
+ int server_body;
+ int ms_iis5_hack = 0;
+ unsigned long long byte_count = 0;
+ struct http_request *http;
+ long len = 0; /* for buffer sizes (and negative error codes) */
+ int buffer_and_filter_content = 0;
+ unsigned int write_delay;
+#ifdef FEATURE_HTTPS_INSPECTION
+ int ret = 0;
+ int use_ssl_tunnel = 0;
+ csp->dont_verify_certificate = 0;
+
+ if (csp->http->ssl && !(csp->action->flags & ACTION_HTTPS_INSPECTION))
+ {
+ /* Pass encrypted content without filtering. */
+ use_ssl_tunnel = 1;
+ }
+#endif
+
+ /* Skeleton for HTTP response, if we should intercept the request */
+ struct http_response *rsp;
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ int watch_client_socket;
+#endif
+
+ csp->receive_buffer_size = csp->config->receive_buffer_size;
+ csp->receive_buffer = zalloc(csp->receive_buffer_size + 1);
+ if (csp->receive_buffer == NULL)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Out of memory. Failed to allocate the receive buffer.");
+ rsp = cgi_error_memory();
+ send_crunch_response(csp, rsp);
+ return;
+ }
+
+ http = csp->http;
+
+#ifndef HAVE_POLL
+ maxfd = (csp->cfd > csp->server_connection.sfd) ?
+ csp->cfd : csp->server_connection.sfd;
+#endif
+
+ /* pass data between the client and server
+ * until one or the other shuts down the connection.
+ */
+
+ server_body = 0;
+
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ watch_client_socket = 0 == (csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING);
+#endif
+ write_delay = get_write_delay(csp);
+
+ for (;;)
+ {
+#ifndef HAVE_POLL
+ FD_ZERO(&rfds);
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (!watch_client_socket)
+ {
+ maxfd = csp->server_connection.sfd;
+ }
+ else
+#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
+ {
+ FD_SET(csp->cfd, &rfds);