/** * Configures the data receiver hook. If there is a previous hook, it will be finalized and cleared. * * @param[in] connp * @param[in] data_receiver_hook * @return HTP_OK, or a value returned from a callback. */ static htp_status_t htp_connp_res_receiver_set(htp_connp_t *connp, htp_hook_t *data_receiver_hook) { htp_connp_res_receiver_finalize_clear(connp); connp->out_data_receiver_hook = data_receiver_hook; connp->out_current_receiver_offset = connp->out_current_read_offset; return HTP_OK; }
htp_status_t htp_tx_state_response_headers(htp_tx_t *tx) { if (tx == NULL) return HTP_ERROR; // Check for compression. // Determine content encoding. tx->response_content_encoding = HTP_COMPRESSION_NONE; htp_header_t *ce = htp_table_get_c(tx->response_headers, "content-encoding"); if (ce != NULL) { if ((bstr_cmp_c(ce->value, "gzip") == 0) || (bstr_cmp_c(ce->value, "x-gzip") == 0)) { tx->response_content_encoding = HTP_COMPRESSION_GZIP; } else if ((bstr_cmp_c(ce->value, "deflate") == 0) || (bstr_cmp_c(ce->value, "x-deflate") == 0)) { tx->response_content_encoding = HTP_COMPRESSION_DEFLATE; } } // Configure decompression, if enabled in the configuration. if (tx->connp->cfg->response_decompression_enabled) { tx->response_content_encoding_processing = tx->response_content_encoding; } else { tx->response_content_encoding_processing = HTP_COMPRESSION_NONE; } // Finalize sending raw header data. htp_status_t rc = htp_connp_res_receiver_finalize_clear(tx->connp); if (rc != HTP_OK) return rc; // Run hook RESPONSE_HEADERS. rc = htp_hook_run_all(tx->connp->cfg->hook_response_headers, tx); if (rc != HTP_OK) return rc; // Initialize the decompression engine as necessary. We can deal with three // scenarios: // // 1. Decompression is enabled, compression indicated in headers, and we decompress. // // 2. As above, but the user disables decompression by setting response_content_encoding // to COMPRESSION_NONE. // // 3. Decompression is disabled and we do not attempt to enable it, but the user // forces decompression by setting response_content_encoding to one of the // supported algorithms. if ((tx->response_content_encoding_processing == HTP_COMPRESSION_GZIP) || (tx->response_content_encoding_processing == HTP_COMPRESSION_DEFLATE)) { if (tx->connp->out_decompressor != NULL) { tx->connp->out_decompressor->destroy(tx->connp->out_decompressor); tx->connp->out_decompressor = NULL; } tx->connp->out_decompressor = (htp_decompressor_t *) htp_gzip_decompressor_create(tx->connp, tx->response_content_encoding_processing); if (tx->connp->out_decompressor == NULL) return HTP_ERROR; tx->connp->out_decompressor->callback = htp_tx_res_process_body_data_decompressor_callback; } else if (tx->response_content_encoding_processing != HTP_COMPRESSION_NONE) { return HTP_ERROR; } return HTP_OK; }
/** * Parses response headers. * * @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_HEADERS(htp_connp_t *connp) { for (;;) { 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 // Should we terminate headers? if (htp_connp_is_line_terminator(connp, data, len)) { // Parse previous header, if any. if (connp->out_header != NULL) { if (connp->cfg->process_response_header(connp, bstr_ptr(connp->out_header), bstr_len(connp->out_header)) != HTP_OK) return HTP_ERROR; bstr_free(connp->out_header); connp->out_header = NULL; } htp_connp_res_clear_buffer(connp); // We've seen all response headers. if (connp->out_tx->progress == HTP_RESPONSE_HEADERS) { // Response headers. // The next step is to determine if this response has a body. connp->out_state = htp_connp_RES_BODY_DETERMINE; } else { // Response trailer. // Finalize sending raw trailer data. htp_status_t rc = htp_connp_res_receiver_finalize_clear(connp); if (rc != HTP_OK) return rc; // Run hook response_TRAILER. rc = htp_hook_run_all(connp->cfg->hook_response_trailer, connp); if (rc != HTP_OK) return rc; // The next step is to finalize this response. connp->out_state = htp_connp_RES_FINALIZE; } return HTP_OK; } htp_chomp(data, &len); // Check for header folding. if (htp_connp_is_line_folded(data, len) == 0) { // New header line. // Parse previous header, if any. if (connp->out_header != NULL) { if (connp->cfg->process_response_header(connp, bstr_ptr(connp->out_header), bstr_len(connp->out_header)) != HTP_OK) return HTP_ERROR; bstr_free(connp->out_header); connp->out_header = NULL; } OUT_PEEK_NEXT(connp); if (htp_is_folding_char(connp->out_next_byte) == 0) { // Because we know this header is not folded, we can process the buffer straight away. if (connp->cfg->process_response_header(connp, data, len) != HTP_OK) return HTP_ERROR; } else { // Keep the partial header data for parsing later. connp->out_header = bstr_dup_mem(data, len); if (connp->out_header == NULL) return HTP_ERROR; } } else { // Folding; check that there's a previous header line to add to. if (connp->out_header == NULL) { // Invalid folding. // Warn only once per transaction. if (!(connp->out_tx->flags & HTP_INVALID_FOLDING)) { connp->out_tx->flags |= HTP_INVALID_FOLDING; htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid response field folding"); } // Keep the header data for parsing later. connp->out_header = bstr_dup_mem(data, len); if (connp->out_header == NULL) return HTP_ERROR; } else { // Add to the existing header. bstr *new_out_header = bstr_add_mem(connp->out_header, data, len); if (new_out_header == NULL) return HTP_ERROR; connp->out_header = new_out_header; } } htp_connp_res_clear_buffer(connp); } } return HTP_ERROR; }