Example #1
0
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;
}
Example #2
0
    virtual void SetUp() {
        home = getenv("srcdir");
        if (home == NULL) {
            fprintf(stderr, "This program needs environment variable 'srcdir' set.");
            exit(EXIT_FAILURE);
        }

        cfg = htp_config_create();
        htp_config_set_server_personality(cfg, HTP_SERVER_APACHE_2);

        connp = htp_connp_create(cfg);
        tx = htp_connp_tx_create(connp);
        htp_tx_set_user_data(tx, &output);

        decompressor = htp_gzip_decompressor_create(connp, HTP_COMPRESSION_GZIP);
        decompressor->callback = GUnzip_decompressor_callback;

        o_boxing_wizards = bstr_dup_c("The five boxing wizards jump quickly.");
        output = NULL;
    }
Example #3
0
/**
 * Determines presence (and encoding) of a response body.
 *
 * @param connp
 * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed.
 */
int htp_connp_RES_BODY_DETERMINE(htp_connp_t *connp) {
    // If the request uses the CONNECT method, then not only are we
    // to assume there's no body, but we need to ignore all
    // subsequent data in the stream.
    if (connp->out_tx->request_method_number == M_CONNECT) {
        if ((connp->out_tx->response_status_number >= 200)
            && (connp->out_tx->response_status_number <= 299))
        {
            // This is a successful CONNECT stream, which means
            // we need to switch into tunnelling mode.
            connp->in_status = STREAM_STATE_TUNNEL;
            connp->out_status = STREAM_STATE_TUNNEL;
            connp->out_state = htp_connp_RES_IDLE;
            connp->out_tx->progress = TX_PROGRESS_DONE;
            return HTP_OK;
        } else {
            // This is a failed CONNECT stream, which means that
            // we can unblock request parsing
            connp->in_status = STREAM_STATE_DATA;
            
            // We are going to continue processing this transaction,
            // adding a note for ourselves to stop at the end (because
            // we don't want to see the beginning of a new transaction).
            connp->out_data_other_at_tx_end = 1;
        }       
    }

    // Check for an interim "100 Continue"
    // response. Ignore it if found, and revert back to RES_FIRST_LINE.
    if (connp->out_tx->response_status_number == 100) {
        if (connp->out_tx->seen_100continue != 0) {
            htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Already seen 100-Continue");
            return HTP_ERROR;
        }

        // Ignore any response headers set
        table_clear(connp->out_tx->response_headers);

        connp->out_state = htp_connp_RES_LINE;
        connp->out_tx->progress = TX_PROGRESS_RES_LINE;
        connp->out_tx->seen_100continue++;

        return HTP_OK;
    }

    // Check for compression
    if (connp->cfg->response_decompression_enabled) {
        htp_header_t *ce = table_get_c(connp->out_tx->response_headers, "content-encoding");
        if (ce != NULL) {
            if ((bstr_cmp_c(ce->value, "gzip") == 0) || (bstr_cmp_c(ce->value, "x-gzip") == 0)) {
                connp->out_tx->response_content_encoding = COMPRESSION_GZIP;
            } else if ((bstr_cmp_c(ce->value, "deflate") == 0) || (bstr_cmp_c(ce->value, "x-deflate") == 0)) {
                connp->out_tx->response_content_encoding = COMPRESSION_DEFLATE;
            }

            if (connp->out_tx->response_content_encoding != COMPRESSION_NONE) {
                connp->out_decompressor = (htp_decompressor_t *) htp_gzip_decompressor_create(connp,
                    connp->out_tx->response_content_encoding);
                if (connp->out_decompressor != NULL) {
                    connp->out_decompressor->callback = htp_connp_RES_BODY_DECOMPRESSOR_CALLBACK;
                } else {
                    // No need to do anything; the error will have already
                    // been reported by the failed decompressor.
                }
            }
        }
    }

    // 1. Any response message which MUST NOT include a message-body
    //  (such as the 1xx, 204, and 304 responses and any response to a HEAD
    //  request) is always terminated by the first empty line after the
    //  header fields, regardless of the entity-header fields present in the
    //  message.
    if (((connp->out_tx->response_status_number >= 100) && (connp->out_tx->response_status_number <= 199))
        || (connp->out_tx->response_status_number == 204) || (connp->out_tx->response_status_number == 304)
        || (connp->out_tx->request_method_number == M_HEAD)) {
        // There's no response body        
        connp->out_state = htp_connp_RES_IDLE;
    } else {
        // We have a response body

        htp_header_t *cl = table_get_c(connp->out_tx->response_headers, "content-length");
        htp_header_t *te = table_get_c(connp->out_tx->response_headers, "transfer-encoding");

        // 2. If a Transfer-Encoding header field (section 14.40) is present and
        //   indicates that the "chunked" transfer coding has been applied, then
        //   the length is defined by the chunked encoding (section 3.6).
        if ((te != NULL) && (bstr_cmp_c(te->value, "chunked") == 0)) {
            // If the T-E header is present we are going to use it.
            connp->out_tx->response_transfer_coding = CHUNKED;

            // We are still going to check for the presence of C-L
            if (cl != NULL) {
                // This is a violation of the RFC
                connp->out_tx->flags |= HTP_REQUEST_SMUGGLING;
                // TODO
            }

            connp->out_state = htp_connp_RES_BODY_CHUNKED_LENGTH;
            connp->out_tx->progress = TX_PROGRESS_RES_BODY;
        }// 3. If a Content-Length header field (section 14.14) is present, its
            //   value in bytes represents the length of the message-body.
        else if (cl != NULL) {
            // We know the exact length
            connp->out_tx->response_transfer_coding = IDENTITY;

            // Check for multiple C-L headers
            if (cl->flags & HTP_FIELD_REPEATED) {
                connp->out_tx->flags |= HTP_REQUEST_SMUGGLING;
                // TODO Log
            }

            // Get body length
            int i = htp_parse_content_length(cl->value);
            if (i < 0) {
                htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Invalid C-L field in response: %d", i);
                return HTP_ERROR;
            } else {
                connp->out_content_length = i;
                connp->out_body_data_left = connp->out_content_length;

                if (connp->out_content_length != 0) {
                    connp->out_state = htp_connp_RES_BODY_IDENTITY;
                    connp->out_tx->progress = TX_PROGRESS_RES_BODY;
                } else {
                    connp->out_state = htp_connp_RES_IDLE;
                    connp->out_tx->progress = TX_PROGRESS_DONE;
                }
            }
        } else {
            // 4. If the message uses the media type "multipart/byteranges", which is
            //   self-delimiting, then that defines the length. This media type MUST
            //   NOT be used unless the sender knows that the recipient can parse it;
            //   the presence in a request of a Range header with multiple byte-range
            //   specifiers implies that the client can parse multipart/byteranges
            //   responses.
            htp_header_t *ct = table_get_c(connp->out_tx->response_headers, "content-type");
            if (ct != NULL) {
                // TODO Handle multipart/byteranges

                if (bstr_index_of_c_nocase(ct->value, "multipart/byteranges") != -1) {
                    htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
                        "C-T multipart/byteranges in responses not supported");
                    return HTP_ERROR;
                }
            }

            // 5. By the server closing the connection. (Closing the connection
            //   cannot be used to indicate the end of a request body, since that
            //   would leave no possibility for the server to send back a response.)
            connp->out_state = htp_connp_RES_BODY_IDENTITY;
            connp->out_tx->progress = TX_PROGRESS_RES_BODY;
        }
    }

    // NOTE We do not need to check for short-style HTTP/0.9 requests here because
    //      that is done earlier, before response line parsing begins

    // Run hook RESPONSE_HEADERS_COMPLETE
    int rc = hook_run_all(connp->cfg->hook_response_headers, connp);
    if (rc != HOOK_OK) {
        htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0,
            "Response headers callback returned error (%d)", rc);

        return HTP_ERROR;
    }

    return HTP_OK;
}