TEST(BstrTest, CmpCNocase) { bstr *p1; p1 = bstr_dup_c("arfarf"); EXPECT_EQ(0, bstr_cmp_c_nocase(p1, "arfarf")); EXPECT_EQ(0, bstr_cmp_c_nocase(p1, "arfARF")); EXPECT_EQ(1, bstr_cmp_c_nocase(p1, "ArF")); EXPECT_EQ(-1, bstr_cmp_c_nocase(p1, "Not equal")); bstr_free(p1); }
void *htp_table_get_c(const htp_table_t *table, const char *ckey) { if ((table == NULL)||(ckey == NULL)) return NULL; // Iterate through the list, comparing // keys with the parameter, return data if found. for (size_t i = 0, n = htp_list_size(table->list); i < n; i += 2) { bstr *key_candidate = htp_list_get(table->list, i); void *element = htp_list_get(table->list, i + 1); if (bstr_cmp_c_nocase(key_candidate, ckey) == 0) { return element; } } return NULL; }
/** * Generic response header line(s) processor, which assembles folded lines * into a single buffer before invoking the parsing function. * * @param[in] connp * @param[in] data * @param[in] len * @return HTP status */ htp_status_t htp_process_response_header_generic(htp_connp_t *connp, unsigned char *data, size_t len) { // Create a new header structure. htp_header_t *h = calloc(1, sizeof (htp_header_t)); if (h == NULL) return HTP_ERROR; if (htp_parse_response_header_generic(connp, h, data, len) != HTP_OK) { free(h); return HTP_ERROR; } #ifdef HTP_DEBUG fprint_bstr(stderr, "Header name", h->name); fprint_bstr(stderr, "Header value", h->value); #endif // Do we already have a header with the same name? htp_header_t *h_existing = htp_table_get(connp->out_tx->response_headers, h->name); if (h_existing != NULL) { // Keep track of repeated same-name headers. h_existing->flags |= HTP_FIELD_REPEATED; // Having multiple C-L headers is against the RFC but many // browsers ignore the subsequent headers if the values are the same. if (bstr_cmp_c_nocase(h->name, "Content-Length") == 0) { // Don't use string comparison here because we want to // ignore small formatting differences. int64_t existing_cl, new_cl; existing_cl = htp_parse_content_length(h_existing->value); new_cl = htp_parse_content_length(h->value); if ((existing_cl == -1) || (new_cl == -1) || (existing_cl != new_cl)) { // Ambiguous response C-L value. htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Ambiguous response C-L value"); bstr_free(h->name); bstr_free(h->value); free(h); return HTP_ERROR; } // Ignoring the new C-L header that has the same value as the previous ones. } else { // Add to the existing header. bstr *new_value = bstr_expand(h_existing->value, bstr_len(h_existing->value) + 2 + bstr_len(h->value)); if (new_value == NULL) { bstr_free(h->name); bstr_free(h->value); free(h); return HTP_ERROR; } h_existing->value = new_value; bstr_add_mem_noex(h_existing->value, (unsigned char *) ", ", 2); bstr_add_noex(h_existing->value, h->value); } // The new header structure is no longer needed. bstr_free(h->name); bstr_free(h->value); free(h); } else { // Add as a new header. if (htp_table_add(connp->out_tx->response_headers, h->name, h) != HTP_OK) { bstr_free(h->name); bstr_free(h->value); free(h); return HTP_ERROR; } } 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_nocase(ce->value, "gzip") == 0) || (bstr_cmp_c_nocase(ce->value, "x-gzip") == 0)) { tx->response_content_encoding = HTP_COMPRESSION_GZIP; } else if ((bstr_cmp_c_nocase(ce->value, "deflate") == 0) || (bstr_cmp_c_nocase(ce->value, "x-deflate") == 0)) { tx->response_content_encoding = HTP_COMPRESSION_DEFLATE; } else if (bstr_cmp_c_nocase(ce->value, "inflate") != 0) { htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Unknown response content encoding"); } } // 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_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; }