/** * 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. int rc = htp_tx_res_process_body_data(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; }
/** * 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. int rc = htp_tx_res_process_body_data(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; }
htp_status_t htp_tx_state_response_complete(htp_tx_t *tx) { if (tx->connp->out_tx->progress != HTP_RESPONSE_COMPLETE) { tx->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(tx, NULL, 0); } // Run hook RESPONSE_COMPLETE. return htp_hook_run_all(tx->connp->cfg->hook_response_complete, tx->connp); } 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) { int rc = htp_tx_res_process_body_data(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 (;;) { // 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; }