/** * Parses request line. * * @param[in] connp * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. */ htp_status_t htp_connp_REQ_LINE(htp_connp_t *connp) { for (;;) { // Get one byte IN_COPY_BYTE_OR_RETURN(connp); // Have we reached the end of the line? if (connp->in_next_byte == LF) { unsigned char *data; size_t len; htp_connp_req_consolidate_data(connp, &data, &len); #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, data, len); #endif // Is this a line that should be ignored? if (htp_connp_is_line_ignorable(connp, data, len)) { // We have an empty/whitespace line, which we'll note, ignore and move on. connp->in_tx->request_ignored_lines++; // TODO How many empty lines are we willing to accept? htp_connp_req_clear_buffer(connp); return HTP_OK; } // Process request line. htp_chomp(data, &len); connp->in_tx->request_line = bstr_dup_mem(data, len); if (connp->in_tx->request_line == NULL) return HTP_ERROR; if (connp->cfg->parse_request_line(connp) != HTP_OK) return HTP_ERROR; // Finalize request line parsing. if (htp_tx_state_request_line(connp->in_tx) != HTP_OK) return HTP_ERROR; htp_connp_req_clear_buffer(connp); return HTP_OK; } } return HTP_ERROR; }
/** * Parse the request line. * * @param[in] connp * @returns HTP_OK on succesful parse, HTP_ERROR on error. */ htp_status_t htp_connp_REQ_LINE_complete(htp_connp_t *connp) { unsigned char *data; size_t len; if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { return HTP_ERROR; } #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, data, len); #endif // Is this a line that should be ignored? if (htp_connp_is_line_ignorable(connp, data, len)) { // We have an empty/whitespace line, which we'll note, ignore and move on. connp->in_tx->request_ignored_lines++; htp_connp_req_clear_buffer(connp); return HTP_OK; } // Process request line. htp_chomp(data, &len); connp->in_tx->request_line = bstr_dup_mem(data, len); if (connp->in_tx->request_line == NULL) return HTP_ERROR; if (connp->cfg->parse_request_line(connp) != HTP_OK) return HTP_ERROR; // Finalize request line parsing. if (htp_tx_state_request_line(connp->in_tx) != HTP_OK) return HTP_ERROR; htp_connp_req_clear_buffer(connp); return HTP_OK; }
/** * Parses response line. * * @param[in] connp * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. */ htp_status_t htp_connp_RES_LINE(htp_connp_t *connp) { for (;;) { // Get one byte OUT_COPY_BYTE_OR_RETURN(connp); // Have we reached the end of the line? if (connp->out_next_byte == LF) { unsigned char *data; size_t len; htp_connp_res_consolidate_data(connp, &data, &len); #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, data, len); #endif // Is this a line that should be ignored? if (htp_connp_is_line_ignorable(connp, data, len)) { // We have an empty/whitespace line, which we'll note, ignore and move on connp->out_tx->response_ignored_lines++; // TODO How many lines are we willing to accept? // Start again htp_connp_res_clear_buffer(connp); return HTP_OK; } // Deallocate previous response line allocations, which we would have on a 100 response. if (connp->out_tx->response_line != NULL) { bstr_free(connp->out_tx->response_line); connp->out_tx->response_line = NULL; } if (connp->out_tx->response_protocol != NULL) { bstr_free(connp->out_tx->response_protocol); connp->out_tx->response_protocol = NULL; } if (connp->out_tx->response_status != NULL) { bstr_free(connp->out_tx->response_status); connp->out_tx->response_status = NULL; } if (connp->out_tx->response_message != NULL) { bstr_free(connp->out_tx->response_message); connp->out_tx->response_message = NULL; } // Process response line. int chomp_result = htp_chomp(data, &len); connp->out_tx->response_line = bstr_dup_mem(data, len); if (connp->out_tx->response_line == NULL) return HTP_ERROR; if (connp->cfg->parse_response_line(connp) != HTP_OK) return HTP_ERROR; // If the response line is invalid, determine if it _looks_ like // a response line. If it does not look like a line, process the // data as a response body because that is what browsers do. if (htp_treat_response_line_as_body(connp->out_tx)) { connp->out_tx->response_content_encoding_processing = HTP_COMPRESSION_NONE; int rc = htp_tx_res_process_body_data(connp->out_tx, data, len + chomp_result); if (rc != HTP_OK) return rc; // Continue to process response body. Because we don't have // any headers to parse, we assume the body continues until // the end of the stream. connp->out_tx->response_transfer_coding = HTP_CODING_IDENTITY; connp->out_tx->progress = HTP_RESPONSE_BODY; connp->out_state = htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE; connp->out_body_data_left = -1; return HTP_OK; } int rc = htp_tx_state_response_line(connp->out_tx); if (rc != HTP_OK) return rc; htp_connp_res_clear_buffer(connp); // Move on to the next phase. connp->out_state = htp_connp_RES_HEADERS; connp->out_tx->progress = HTP_RESPONSE_HEADERS; return HTP_OK; } } return HTP_ERROR; }
/** * Parses request line. * * @param connp * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed. */ int htp_connp_REQ_LINE(htp_connp_t *connp) { for (;;) { // Get one byte IN_COPY_BYTE_OR_RETURN(connp); // Keep track of NUL bytes if (connp->in_next_byte == 0) { // Remember how many NULs there were connp->in_tx->request_line_nul++; // Store the offset of the first NUL byte if (connp->in_tx->request_line_nul_offset == -1) { connp->in_tx->request_line_nul_offset = connp->in_line_len; } } // Have we reached the end of the line? if (connp->in_next_byte == LF) { #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, connp->in_line, connp->in_line_len); #endif // Is this a line that should be ignored? if (htp_connp_is_line_ignorable(connp, connp->in_line, connp->in_line_len)) { // We have an empty/whitespace line, which we'll note, ignore and move on connp->in_tx->request_ignored_lines++; // TODO How many empty lines are we willing to accept? // Start again connp->in_line_len = 0; return HTP_OK; } // Process request line connp->in_tx->request_line_raw = bstr_dup_mem((char *) connp->in_line, connp->in_line_len); if (connp->in_tx->request_line_raw == NULL) { return HTP_ERROR; } /// @todo Would be nice to reference request_line_raw data htp_chomp(connp->in_line, &connp->in_line_len); connp->in_tx->request_line = bstr_dup_ex(connp->in_tx->request_line_raw, 0, connp->in_line_len); if (connp->in_tx->request_line == NULL) { return HTP_ERROR; } // Parse request line if (connp->cfg->parse_request_line(connp) != HTP_OK) { // Note: downstream responsible for error logging return HTP_ERROR; } if (connp->in_tx->request_method_number == M_CONNECT) { // Parse authority if (htp_parse_authority(connp, connp->in_tx->request_uri, &(connp->in_tx->parsed_uri_incomplete)) != HTP_OK) { // Note: downstream responsible for error logging return HTP_ERROR; } } else { // Parse the request URI if (htp_parse_uri(connp->in_tx->request_uri, &(connp->in_tx->parsed_uri_incomplete)) != HTP_OK) { // Note: downstream responsible for error logging return HTP_ERROR; } // Keep the original URI components, but // create a copy which we can normalize and use internally if (htp_normalize_parsed_uri(connp, connp->in_tx->parsed_uri_incomplete, connp->in_tx->parsed_uri)) { // Note: downstream responsible for error logging return HTP_ERROR; } // Run hook REQUEST_URI_NORMALIZE int rc = hook_run_all(connp->cfg->hook_request_uri_normalize, connp); if (rc != HOOK_OK) { switch (rc) { case HOOK_STOP: return HTP_STOP; case HOOK_ERROR: case HOOK_DECLINED: default: htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request headers callback returned error (%d)", rc); return HTP_ERROR; } } // Now is a good time to generate request_uri_normalized, before we finalize // parsed_uri (and lose the information which parts were provided in the request and // which parts we added). if (connp->cfg->generate_request_uri_normalized) { connp->in_tx->request_uri_normalized = htp_unparse_uri_noencode(connp->in_tx->parsed_uri); if (connp->in_tx->request_uri_normalized == NULL) { // There's no sense in logging anything on a memory allocation failure return HTP_ERROR; } #ifdef HTP_DEBUG fprint_raw_data(stderr, "request_uri_normalized", (unsigned char *) bstr_ptr(connp->in_tx->request_uri_normalized), bstr_len(connp->in_tx->request_uri_normalized)); #endif } // Finalize parsed_uri // Scheme if (connp->in_tx->parsed_uri->scheme != NULL) { if (bstr_cmp_c(connp->in_tx->parsed_uri->scheme, "http") != 0) { // TODO Invalid scheme } } else { connp->in_tx->parsed_uri->scheme = bstr_dup_c("http"); if (connp->in_tx->parsed_uri->scheme == NULL) { return HTP_ERROR; } } // Port if (connp->in_tx->parsed_uri->port != NULL) { if (connp->in_tx->parsed_uri->port_number != -1) { // Check that the port in the URI is the same // as the port on which the client is talking // to the server if (connp->conn->use_local_port) { if (connp->in_tx->parsed_uri->port_number != connp->conn->local_port) { // Incorrect port; use the real port instead connp->in_tx->parsed_uri->port_number = connp->conn->local_port; // TODO Log } } else { connp->in_tx->parsed_uri->port_number = connp->conn->remote_port; } } else { // Invalid port; use the real port instead if (connp->conn->use_local_port) { connp->in_tx->parsed_uri->port_number = connp->conn->local_port; } else { connp->in_tx->parsed_uri->port_number = connp->conn->remote_port; } // TODO Log } } else { if (connp->conn->use_local_port) { connp->in_tx->parsed_uri->port_number = connp->conn->local_port; } else { connp->in_tx->parsed_uri->port_number = connp->conn->remote_port; } } // Path if (connp->in_tx->parsed_uri->path == NULL) { connp->in_tx->parsed_uri->path = bstr_dup_c("/"); if (connp->in_tx->parsed_uri->path == NULL) { return HTP_ERROR; } } } // Run hook REQUEST_LINE int rc = hook_run_all(connp->cfg->hook_request_line, connp); if (rc != HOOK_OK) { switch (rc) { case HOOK_STOP: return HTP_STOP; case HOOK_ERROR: case HOOK_DECLINED: default: htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request headers callback returned error (%d)", rc); return HTP_ERROR; } } // Clean up. connp->in_line_len = 0; // Move on to the next phase. connp->in_state = htp_connp_REQ_PROTOCOL; return HTP_OK; } } }
/** * Parses response line. * * @param connp * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed. */ int htp_connp_RES_LINE(htp_connp_t * connp) { for (;;) { // Get one byte OUT_COPY_BYTE_OR_RETURN(connp); // Have we reached the end of the line? if (connp->out_next_byte == LF) { #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, connp->out_line, connp->out_line_len); #endif // Is this a line that should be ignored? if (htp_connp_is_line_ignorable(connp, connp->out_line, connp->out_line_len)) { // We have an empty/whitespace line, which we'll note, ignore and move on connp->out_tx->response_ignored_lines++; // TODO How many lines are we willing to accept? // Start again connp->out_line_len = 0; return HTP_OK; } // Process response line // Deallocate previous response line allocations, which we would have on a 100 response // TODO Consider moving elsewhere; no need to make these checks on every response if (connp->out_tx->response_line != NULL) { bstr_free(&connp->out_tx->response_line); } if (connp->out_tx->response_protocol != NULL) { bstr_free(&connp->out_tx->response_protocol); } if (connp->out_tx->response_status != NULL) { bstr_free(&connp->out_tx->response_status); } if (connp->out_tx->response_message != NULL) { bstr_free(&connp->out_tx->response_message); } connp->out_tx->response_line_raw = bstr_dup_mem((char *) connp->out_line, connp->out_line_len); if (connp->out_tx->response_line_raw == NULL) { return HTP_ERROR; } /// @todo Would be nice to reference response_line_raw data int chomp_result = htp_chomp(connp->out_line, &connp->out_line_len); connp->out_tx->response_line = bstr_dup_ex(connp->out_tx->response_line_raw, 0, connp->out_line_len); if (connp->out_tx->response_line == NULL) { return HTP_ERROR; } // Parse response line if (connp->cfg->parse_response_line(connp) != HTP_OK) { // Note: downstream responsible for error logging return HTP_ERROR; } // Is the response line valid? if ((connp->out_tx->response_protocol_number < 0) || (connp->out_tx->response_status_number < 0) || (connp->out_tx->response_status_number < HTP_VALID_STATUS_MIN) || (connp->out_tx->response_status_number > HTP_VALID_STATUS_MAX)) { // Response line is invalid htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid response line"); connp->out_tx->flags |= HTP_STATUS_LINE_INVALID; } // Even when the response line is invalid, determine if it looks like // a response line (which is what browsers do). if (htp_resembles_response_line(connp->out_tx) == 0) { // Process this line as response body data htp_tx_data_t d; d.tx = connp->out_tx; d.data = connp->out_line; d.len = connp->out_line_len + chomp_result; connp->out_tx->response_message_len += d.len; connp->out_tx->response_entity_len += d.len; int rc = htp_res_run_hook_body_data(connp, &d); if (rc != HOOK_OK) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Response body data callback returned error (%d)", rc); return HTP_ERROR; } // Continue to process response body connp->out_tx->response_transfer_coding = IDENTITY; connp->out_state = htp_connp_RES_BODY_IDENTITY; connp->out_tx->progress = TX_PROGRESS_RES_BODY; return HTP_OK; } // Run hook RESPONSE_LINE int rc = hook_run_all(connp->cfg->hook_response_line, connp); if (rc != HOOK_OK) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Response line callback returned error (%d)", rc); return HTP_ERROR; } // Clean up. connp->out_line_len = 0; // Move on to the next phase. connp->out_state = htp_connp_RES_HEADERS; connp->out_tx->progress = TX_PROGRESS_RES_HEADERS; return HTP_OK; } } }