/** * Processes an identity response body of known length. * * @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_BODY_IDENTITY_CL_KNOWN(htp_connp_t *connp) { size_t bytes_to_consume; // Determine how many bytes we can consume. if (connp->out_current_len - connp->out_current_read_offset >= connp->out_body_data_left) { bytes_to_consume = connp->out_body_data_left; } else { bytes_to_consume = connp->out_current_len - connp->out_current_read_offset; } if (bytes_to_consume == 0) return HTP_DATA; // Consume the data. htp_status_t rc = htp_tx_res_process_body_data_ex(connp->out_tx, connp->out_current_data + connp->out_current_read_offset, bytes_to_consume); if (rc != HTP_OK) return rc; // Adjust the counters. connp->out_current_read_offset += bytes_to_consume; connp->out_current_consume_offset += bytes_to_consume; connp->out_stream_offset += bytes_to_consume; connp->out_body_data_left -= bytes_to_consume; // Have we seen the entire response body? if (connp->out_body_data_left == 0) { connp->out_state = htp_connp_RES_FINALIZE; return HTP_OK; } return HTP_DATA; }
/** * Processes a chunk of data. * * @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_BODY_CHUNKED_DATA(htp_connp_t *connp) { size_t bytes_to_consume; // Determine how many bytes we can consume. if (connp->out_current_len - connp->out_current_read_offset >= connp->out_chunked_length) { bytes_to_consume = connp->out_chunked_length; } else { bytes_to_consume = connp->out_current_len - connp->out_current_read_offset; } if (bytes_to_consume == 0) return HTP_DATA; // Consume the data. htp_status_t rc = htp_tx_res_process_body_data_ex(connp->out_tx, connp->out_current_data + connp->out_current_read_offset, bytes_to_consume); if (rc != HTP_OK) return rc; // Adjust the counters. connp->out_current_read_offset += bytes_to_consume; connp->out_current_consume_offset += bytes_to_consume; connp->out_stream_offset += bytes_to_consume; connp->out_chunked_length -= bytes_to_consume; // Have we seen the entire chunk? if (connp->out_chunked_length == 0) { connp->out_state = htp_connp_RES_BODY_CHUNKED_DATA_END; return HTP_OK; } return HTP_DATA; }
htp_status_t htp_tx_state_response_complete_ex(htp_tx_t *tx, int hybrid_mode) { if (tx == NULL) return HTP_ERROR; if (tx->response_progress != HTP_RESPONSE_COMPLETE) { tx->response_progress = HTP_RESPONSE_COMPLETE; // Run the last RESPONSE_BODY_DATA HOOK, but only if there was a response body present. if (tx->response_transfer_coding != HTP_CODING_NO_BODY) { htp_tx_res_process_body_data_ex(tx, NULL, 0); } // Run hook RESPONSE_COMPLETE. htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_complete, tx); if (rc != HTP_OK) return rc; } if (!hybrid_mode) { // Check if the inbound parser is waiting on us. If it is, that means that // there might be request data that the inbound parser hasn't consumed yet. // If we don't stop parsing we might encounter a response without a request, // which is why we want to return straight away before processing any data. // // This situation will occur any time the parser needs to see the server // respond to a particular situation before it can decide how to proceed. For // example, when a CONNECT is sent, different paths are used when it is accepted // and when it is not accepted. // // It is not enough to check only in_status here. Because of pipelining, it's possible // that many inbound transactions have been processed, and that the parser is // waiting on a response that we have not seen yet. if ((tx->connp->in_status == HTP_STREAM_DATA_OTHER) && (tx->connp->in_tx == tx->connp->out_tx)) { return HTP_DATA_OTHER; } // Do we have a signal to yield to inbound processing at // the end of the next transaction? if (tx->connp->out_data_other_at_tx_end) { // We do. Let's yield then. tx->connp->out_data_other_at_tx_end = 0; return HTP_DATA_OTHER; } } // Make a copy of the connection parser pointer, so that // we don't have to reference it via tx, which may be destroyed later. htp_connp_t *connp = tx->connp; // Finalize the transaction. This may call may destroy the transaction, if auto-destroy is enabled. htp_status_t rc = htp_tx_finalize(tx); if (rc != HTP_OK) return rc; // Disconnect transaction from the parser. connp->out_tx = NULL; connp->out_state = htp_connp_RES_IDLE; return HTP_OK; }
/** * Processes identity response body of unknown length. In this case, we assume the * response body consumes all data until the end of the stream. * * @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_BODY_IDENTITY_STREAM_CLOSE(htp_connp_t *connp) { // Consume all data from the input buffer. size_t bytes_to_consume = connp->out_current_len - connp->out_current_read_offset; if (bytes_to_consume != 0) { htp_status_t rc = htp_tx_res_process_body_data_ex(connp->out_tx, connp->out_current_data + connp->out_current_read_offset, bytes_to_consume); if (rc != HTP_OK) return rc; // Adjust the counters. connp->out_current_read_offset += bytes_to_consume; connp->out_current_consume_offset += bytes_to_consume; connp->out_stream_offset += bytes_to_consume; } // Have we seen the entire response body? if (connp->out_status == HTP_STREAM_CLOSED) { connp->out_state = htp_connp_RES_FINALIZE; return HTP_OK; } return HTP_DATA; }
/** * 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 (;;) { // Don't try to get more data if the stream is closed. If we do, we'll return, asking for more data. if (connp->out_status != HTP_STREAM_CLOSED) { // Get one byte OUT_COPY_BYTE_OR_RETURN(connp); } // Have we reached the end of the line? We treat stream closure as end of line in // order to handle the case when the first line of the response is actually response body // (and we wish it processed as such). if ((connp->out_next_byte == LF)||(connp->out_status == HTP_STREAM_CLOSED)) { unsigned char *data; size_t len; if (htp_connp_res_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->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; htp_status_t rc = htp_tx_res_process_body_data_ex(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->response_progress = HTP_RESPONSE_BODY; connp->out_state = htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE; connp->out_body_data_left = -1; return HTP_OK; } htp_status_t 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->response_progress = HTP_RESPONSE_HEADERS; return HTP_OK; } } return HTP_ERROR; }
htp_status_t htp_tx_res_process_body_data(htp_tx_t *tx, const void *data, size_t len) { if ((tx == NULL) || (data == NULL)) return HTP_ERROR; if (len == 0) return HTP_OK; return htp_tx_res_process_body_data_ex(tx, data, len); }