diff --git a/libhtp/htp/htp.h b/libhtp/htp/htp.h index 5916f2aa39..765aeddf3b 100644 --- a/libhtp/htp/htp.h +++ b/libhtp/htp/htp.h @@ -41,7 +41,7 @@ typedef struct htp_urldecoder_t htp_urldecoder_t; // -- Defines ------------------------------------------------------------------------------------- -#define HTP_BASE_VERSION_TEXT "0.2.6" +#define HTP_BASE_VERSION_TEXT "0.2.7" #define HTP_ERROR -1 #define HTP_OK 0 @@ -884,6 +884,16 @@ struct htp_tx_t { /** Parsed response headers. */ table_t *response_headers; + /** Contains raw response headers. This field is generated on demand, use + * htp_tx_get_response_headers_raw() to get it. + */ + bstr *response_headers_raw; + + /** How many response header lines have been included in the raw + * buffer (above). + */ + size_t response_headers_raw_lines; + /** The actual message length (the length _after_ transformations * have been applied). This field will change as a request body is being * received, with the final value available once the entire body has @@ -1129,6 +1139,7 @@ char *htp_tx_progress_as_string(htp_tx_t *tx); bstr *htp_unparse_uri_noencode(htp_uri_t *uri); bstr *htp_tx_get_request_headers_raw(htp_tx_t *tx); +bstr *htp_tx_get_response_headers_raw(htp_tx_t *tx); #endif /* _HTP_H */ diff --git a/libhtp/htp/htp_response_generic.c b/libhtp/htp/htp_response_generic.c index b1ca6fa175..13992956a7 100644 --- a/libhtp/htp/htp_response_generic.c +++ b/libhtp/htp/htp_response_generic.c @@ -107,7 +107,7 @@ int htp_parse_response_header_generic(htp_connp_t *connp, htp_header_t *h, char if (!(connp->out_tx->flags & HTP_FIELD_UNPARSEABLE)) { connp->out_tx->flags |= HTP_FIELD_UNPARSEABLE; // Only log once per transaction - htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request field invalid: colon missing"); + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Response field invalid: colon missing"); } return HTP_ERROR; @@ -120,7 +120,7 @@ int htp_parse_response_header_generic(htp_connp_t *connp, htp_header_t *h, char if (!(connp->out_tx->flags & HTP_FIELD_INVALID)) { connp->out_tx->flags |= HTP_FIELD_INVALID; // Only log once per transaction - htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request field invalid: empty name"); + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Response field invalid: empty name"); } } @@ -136,7 +136,7 @@ int htp_parse_response_header_generic(htp_connp_t *connp, htp_header_t *h, char if (!(connp->out_tx->flags & HTP_FIELD_INVALID)) { connp->out_tx->flags |= HTP_FIELD_INVALID; - htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request field invalid: LWS after name"); + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Response field invalid: LWS after name"); } } @@ -173,7 +173,7 @@ int htp_parse_response_header_generic(htp_connp_t *connp, htp_header_t *h, char if (!(connp->out_tx->flags & HTP_FIELD_INVALID)) { connp->out_tx->flags |= HTP_FIELD_INVALID; - htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request header name is not a token"); + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Response header name is not a token"); } break; diff --git a/libhtp/htp/htp_util.c b/libhtp/htp/htp_util.c index 540029f434..a5d6dc6642 100644 --- a/libhtp/htp/htp_util.c +++ b/libhtp/htp/htp_util.c @@ -1878,3 +1878,65 @@ bstr *htp_tx_get_request_headers_raw(htp_tx_t *tx) { return tx->request_headers_raw; } + +/** + * Construct a bstr that contains the raw response headers. + * + * @param tx + * @return + */ +bstr *htp_tx_generate_response_headers_raw(htp_tx_t *tx) { + bstr *response_headers_raw = NULL; + size_t i, len = 0; + + for (i = 0; i < list_size(tx->response_header_lines); i++) { + htp_header_line_t *hl = list_get(tx->response_header_lines, i); + len += bstr_len(hl->line); + } + + response_headers_raw = bstr_alloc(len); + if (response_headers_raw == NULL) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Failed to allocate bstring of %d bytes", len); + return NULL; + } + + for (i = 0; i < list_size(tx->response_header_lines); i++) { + htp_header_line_t *hl = list_get(tx->response_header_lines, i); + bstr_add_str_noex(response_headers_raw, hl->line); + } + + return response_headers_raw; +} + +/** + * Get a bstr that contains the raw response headers. This method will always + * return an up-to-date buffer, containing the last known headers. Thus, if + * it is called once after RESPONSE_HEADERS phase it will return one buffer, but + * it may return a different buffer if called after RESPONSE_TRAILERS phase (but + * only if the response actually contains trailer headers). Do not retain the + * bstr pointer, as the buffer may change. If there are no changes to the + * response header structure, only one buffer will be contstructed and used. (Multiple + * invocations of this method will not cause multiple buffers to be created.) + * + * @param tx + * @return + */ +bstr *htp_tx_get_response_headers_raw(htp_tx_t *tx) { + // Check that we are not called too early + if (tx->progress < TX_PROGRESS_RES_HEADERS) return NULL; + + if (tx->response_headers_raw == NULL) { + tx->response_headers_raw = htp_tx_generate_response_headers_raw(tx); + tx->response_headers_raw_lines = list_size(tx->response_header_lines); + } else { + // Check that the buffer we have is not obsolete + if (tx->response_headers_raw_lines < list_size(tx->response_header_lines)) { + // Rebuild raw buffer + bstr_free(tx->response_headers_raw); + tx->response_headers_raw = htp_tx_generate_response_headers_raw(tx); + tx->response_headers_raw_lines = list_size(tx->response_header_lines); + } + } + + return tx->response_headers_raw; +}