/** * Generic request line parser. * * @param connp * @return HTP_OK or HTP_ERROR */ int htp_parse_request_line_generic(htp_connp_t *connp) { htp_tx_t *tx = connp->in_tx; unsigned char *data = (unsigned char *)bstr_ptr(tx->request_line); size_t len = bstr_len(tx->request_line); size_t pos = 0; // The request method starts at the beginning of the // line and ends with the first whitespace character. while ((pos < len) && (!htp_is_space(data[pos]))) { pos++; } // No, we don't care if the method is empty. tx->request_method = bstr_memdup((char *)data, pos); if (tx->request_method == NULL) { return HTP_ERROR; } tx->request_method_number = htp_convert_method_to_number(tx->request_method); // Ignore whitespace after request method. The RFC allows // for only one SP, but then suggests any number of SP and HT // should be permitted. while ((pos < len) && (isspace(data[pos]))) { pos++; } size_t start = pos; // The URI ends with the first whitespace. while ((pos < len) && (!htp_is_space(data[pos]))) { pos++; } tx->request_uri = bstr_memdup((char *)data + start, pos - start); if (tx->request_uri == NULL) { return HTP_ERROR; } // Ignore whitespace after URI while ((pos < len) && (htp_is_space(data[pos]))) { pos++; } // Is there protocol information available? if (pos == len) { // No, this looks like a HTTP/0.9 request. tx->protocol_is_simple = 1; return HTP_OK; } // The protocol information spreads until the end of the line. tx->request_protocol = bstr_memdup((char *)data + pos, len - pos); if (tx->request_protocol == NULL) { return HTP_ERROR; } tx->request_protocol_number = htp_parse_protocol(tx->request_protocol); return HTP_OK; }
/** * Determines whether inbound parsing needs to continue or stop. In * case the data appears to be plain text HTTP, we try to continue. * * @param[in] connp * @return HTP_OK if the parser can resume parsing, HTP_DATA_BUFFER if * we need more data. */ htp_status_t htp_connp_REQ_CONNECT_PROBE_DATA(htp_connp_t *connp) { for (;;) {//;i < max_read; i++) { IN_PEEK_NEXT(connp); // Have we reached the end of the line? For some reason // we can't test after IN_COPY_BYTE_OR_RETURN */ if (connp->in_next_byte == LF || connp->in_next_byte == 0x00) break; IN_COPY_BYTE_OR_RETURN(connp); } unsigned char *data; size_t len; if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { fprintf(stderr, "htp_connp_req_consolidate_data fail"); return HTP_ERROR; } #ifdef HTP_DEBUG fprint_raw_data(stderr, "PROBING", data, len); #endif size_t pos = 0; size_t mstart = 0; // skip past leading whitespace. IIS allows this while ((pos < len) && htp_is_space(data[pos])) pos++; if (pos) mstart = pos; // The request method starts at the beginning of the // line and ends with the first whitespace character. while ((pos < len) && (!htp_is_space(data[pos]))) pos++; int methodi = HTP_M_UNKNOWN; bstr *method = bstr_dup_mem(data + mstart, pos - mstart); if (method) { methodi = htp_convert_method_to_number(method); bstr_free(method); } if (methodi != HTP_M_UNKNOWN) { #ifdef HTP_DEBUG fprint_raw_data(stderr, "htp_connp_REQ_CONNECT_PROBE_DATA: tunnel contains plain text HTTP", data, len); #endif connp->in_state = htp_connp_REQ_IDLE; } else { #ifdef HTP_DEBUG fprint_raw_data(stderr, "htp_connp_REQ_CONNECT_PROBE_DATA: tunnel is not HTTP", data, len); #endif connp->in_status = HTP_STREAM_TUNNEL; connp->out_status = HTP_STREAM_TUNNEL; } // not calling htp_connp_req_clear_buffer, we're not consuming the data return HTP_OK; }
TEST(UtilTest, Space) { EXPECT_EQ(0, htp_is_space('a')); EXPECT_EQ(1, htp_is_space(' ')); EXPECT_EQ(1, htp_is_space('\f')); EXPECT_EQ(1, htp_is_space('\n')); EXPECT_EQ(1, htp_is_space('\r')); EXPECT_EQ(1, htp_is_space('\t')); EXPECT_EQ(1, htp_is_space('\v')); }
/** * Determines presence (and encoding) of a response body. * * @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_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 == HTP_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 = HTP_STREAM_TUNNEL; connp->out_status = HTP_STREAM_TUNNEL; connp->out_state = htp_connp_RES_FINALIZE; return HTP_OK; } else { // This is a failed CONNECT stream, which means that // we can unblock request parsing connp->in_status = HTP_STREAM_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 seen so far. htp_header_t *h = NULL; for (int i = 0, n = htp_table_size(connp->out_tx->response_headers); i < n; i++) { h = htp_table_get_index(connp->out_tx->response_headers, i, NULL); bstr_free(h->name); bstr_free(h->value); free(h); } htp_table_clear(connp->out_tx->response_headers); // Expecting to see another response line next. connp->out_state = htp_connp_RES_LINE; connp->out_tx->progress = HTP_RESPONSE_LINE; connp->out_tx->seen_100continue++; return HTP_OK; } // 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 == HTP_M_HEAD)) { // There's no response body connp->out_tx->response_transfer_coding = HTP_CODING_NO_BODY; connp->out_state = htp_connp_RES_FINALIZE; } else { // We have a response body htp_header_t *ct = htp_table_get_c(connp->out_tx->response_headers, "content-type"); htp_header_t *cl = htp_table_get_c(connp->out_tx->response_headers, "content-length"); htp_header_t *te = htp_table_get_c(connp->out_tx->response_headers, "transfer-encoding"); if (ct != NULL) { connp->out_tx->response_content_type = bstr_dup_lower(ct->value); if (connp->out_tx->response_content_type == NULL) return HTP_ERROR; // Ignore parameters unsigned char *data = bstr_ptr(connp->out_tx->response_content_type); size_t len = bstr_len(ct->value); size_t newlen = 0; while (newlen < len) { // TODO Some platforms may do things differently here. if (htp_is_space(data[newlen]) || (data[newlen] == ';')) { bstr_adjust_len(connp->out_tx->response_content_type, newlen); break; } newlen++; } } // 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 = HTP_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; } connp->out_state = htp_connp_RES_BODY_CHUNKED_LENGTH; connp->out_tx->progress = HTP_RESPONSE_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 = HTP_CODING_IDENTITY; // Check for multiple C-L headers if (cl->flags & HTP_FIELD_REPEATED) { connp->out_tx->flags |= HTP_REQUEST_SMUGGLING; } // Get body length connp->out_tx->response_content_length = htp_parse_content_length(cl->value); if (connp->out_tx->response_content_length < 0) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Invalid C-L field in response: %d", connp->out_tx->response_content_length); return HTP_ERROR; } else { connp->out_content_length = connp->out_tx->response_content_length; connp->out_body_data_left = connp->out_content_length; if (connp->out_content_length != 0) { connp->out_state = htp_connp_RES_BODY_IDENTITY_CL_KNOWN; connp->out_tx->progress = HTP_RESPONSE_BODY; } else { connp->out_state = htp_connp_RES_FINALIZE; } } } 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. 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_STREAM_CLOSE; connp->out_tx->response_transfer_coding = HTP_CODING_IDENTITY; connp->out_tx->progress = HTP_RESPONSE_BODY; connp->out_body_data_left = -1; } } // 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 int rc = htp_tx_state_response_headers(connp->out_tx); if (rc != HTP_OK) return rc; return HTP_OK; }
htp_status_t htp_parse_request_line_generic_ex(htp_connp_t *connp, int nul_terminates) { htp_tx_t *tx = connp->in_tx; unsigned char *data = bstr_ptr(tx->request_line); size_t len = bstr_len(tx->request_line); size_t pos = 0; size_t mstart = 0; size_t start; size_t bad_delim; if (nul_terminates) { // The line ends with the first NUL byte. size_t newlen = 0; while ((pos < len) && (data[pos] != '\0')) { pos++; newlen++; } // Start again, with the new length. len = newlen; pos = 0; } // skip past leading whitespace. IIS allows this while ((pos < len) && htp_is_space(data[pos])) pos++; if (pos) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: leading whitespace"); mstart = pos; if (connp->cfg->requestline_leading_whitespace_unwanted != HTP_UNWANTED_IGNORE) { // reset mstart so that we copy the whitespace into the method mstart = 0; // set expected response code to this anomaly tx->response_status_expected_number = connp->cfg->requestline_leading_whitespace_unwanted; } } // The request method starts at the beginning of the // line and ends with the first whitespace character. while ((pos < len) && (!htp_is_space(data[pos]))) pos++; // No, we don't care if the method is empty. tx->request_method = bstr_dup_mem(data + mstart, pos - mstart); if (tx->request_method == NULL) return HTP_ERROR; #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, bstr_ptr(tx->request_method), bstr_len(tx->request_method)); #endif tx->request_method_number = htp_convert_method_to_number(tx->request_method); bad_delim = 0; // Ignore whitespace after request method. The RFC allows // for only one SP, but then suggests any number of SP and HT // should be permitted. Apache uses isspace(), which is even // more permitting, so that's what we use here. while ((pos < len) && (isspace(data[pos]))) { if (!bad_delim && data[pos] != 0x20) { bad_delim++; } pos++; } if (bad_delim) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: non-compliant delimiter between Method and URI"); } // Is there anything after the request method? if (pos == len) { // No, this looks like a HTTP/0.9 request. tx->is_protocol_0_9 = 1; tx->request_protocol_number = HTP_PROTOCOL_0_9; return HTP_OK; } start = pos; bad_delim = 0; // The URI ends with the first whitespace. while ((pos < len) && (data[pos] != 0x20)) { if (!bad_delim && htp_is_space(data[pos])) { bad_delim++; } pos++; } /* if we've seen some 'bad' delimiters, we retry with those */ if (bad_delim && pos == len) { // special case: even though RFC's allow only SP (0x20), many // implementations allow other delimiters, like tab or other // characters that isspace() accepts. pos = start; while ((pos < len) && (!htp_is_space(data[pos]))) pos++; } if (bad_delim) { // warn regardless if we've seen non-compliant chars htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: URI contains non-compliant delimiter"); } tx->request_uri = bstr_dup_mem(data + start, pos - start); if (tx->request_uri == NULL) return HTP_ERROR; #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, bstr_ptr(tx->request_uri), bstr_len(tx->request_uri)); #endif // Ignore whitespace after URI. while ((pos < len) && (htp_is_space(data[pos]))) pos++; // Is there protocol information available? if (pos == len) { // No, this looks like a HTTP/0.9 request. tx->is_protocol_0_9 = 1; tx->request_protocol_number = HTP_PROTOCOL_0_9; return HTP_OK; } // The protocol information continues until the end of the line. tx->request_protocol = bstr_dup_mem(data + pos, len - pos); if (tx->request_protocol == NULL) return HTP_ERROR; tx->request_protocol_number = htp_parse_protocol(tx->request_protocol); #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, bstr_ptr(tx->request_protocol), bstr_len(tx->request_protocol)); #endif return HTP_OK; }
/** * Parse request line as Apache 2.2 does. * * @param connp * @return HTP_OK or HTP_ERROR */ int htp_parse_request_line_apache_2_2(htp_connp_t *connp) { htp_tx_t *tx = connp->in_tx; unsigned char *data = (unsigned char *) bstr_ptr(tx->request_line); size_t len = bstr_len(tx->request_line); size_t pos = 0; // In this implementation we assume the // line ends with the first NUL byte. if (tx->request_line_nul_offset != -1) { len = tx->request_line_nul_offset - 1; } // The request method starts at the beginning of the // line and ends with the first whitespace character. while ((pos < len) && (!htp_is_space(data[pos]))) { pos++; } // No, we don't care if the method is empty. tx->request_method = bstr_memdup((char *) data, pos); #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, (unsigned char *)bstr_ptr(tx->request_method), bstr_len(tx->request_method)); #endif tx->request_method_number = htp_convert_method_to_number(tx->request_method); // Ignore whitespace after request method. The RFC allows // for only one SP, but then suggests any number of SP and HT // should be permitted. Apache uses isspace(), which is even // more permitting, so that's what we use here. while ((pos < len) && (isspace(data[pos]))) { pos++; } size_t start = pos; // The URI ends with the first whitespace. while ((pos < len) && (!htp_is_space(data[pos]))) { pos++; } tx->request_uri = bstr_memdup((char *) data + start, pos - start); if (tx->request_uri == NULL) { return HTP_ERROR; } #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, (unsigned char *)bstr_ptr(tx->request_uri), bstr_len(tx->request_uri)); #endif // Ignore whitespace after URI while ((pos < len) && (htp_is_space(data[pos]))) { pos++; } // Is there protocol information available? if (pos == len) { // No, this looks like a HTTP/0.9 request. tx->protocol_is_simple = 1; return HTP_OK; } // The protocol information spreads until the end of the line. tx->request_protocol = bstr_memdup((char *) data + pos, len - pos); tx->request_protocol_number = htp_parse_protocol(tx->request_protocol); #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, (unsigned char *)bstr_ptr(tx->request_protocol), bstr_len(tx->request_protocol)); #endif return HTP_OK; }
/** * Generic response line parser. * * @param[in] connp * @return HTP status */ htp_status_t htp_parse_response_line_generic(htp_connp_t *connp) { htp_tx_t *tx = connp->out_tx; unsigned char *data = bstr_ptr(tx->response_line); size_t len = bstr_len(tx->response_line); size_t pos = 0; tx->response_protocol = NULL; tx->response_protocol_number = HTP_PROTOCOL_INVALID; tx->response_status = NULL; tx->response_status_number = HTP_STATUS_INVALID; tx->response_message = NULL; // Ignore whitespace at the beginning of the line. while ((pos < len) && (htp_is_space(data[pos]))) pos++; size_t start = pos; // Find the end of the protocol string. while ((pos < len) && (!htp_is_space(data[pos]))) pos++; if (pos - start == 0) return HTP_OK; tx->response_protocol = bstr_dup_mem(data + start, pos - start); if (tx->response_protocol == NULL) return HTP_ERROR; tx->response_protocol_number = htp_parse_protocol(tx->response_protocol); #ifdef HTP_DEBUG fprint_raw_data(stderr, "Response protocol", bstr_ptr(tx->response_protocol), bstr_len(tx->response_protocol)); fprintf(stderr, "Response protocol number: %d\n", tx->response_protocol_number); #endif // Ignore whitespace after the response protocol. while ((pos < len) && (htp_is_space(data[pos]))) pos++; if (pos == len) return HTP_OK; start = pos; // Find the next whitespace character. while ((pos < len) && (!htp_is_space(data[pos]))) pos++; if (pos - start == 0) return HTP_OK; tx->response_status = bstr_dup_mem(data + start, pos - start); if (tx->response_status == NULL) return HTP_ERROR; tx->response_status_number = htp_parse_status(tx->response_status); #ifdef HTP_DEBUG fprint_raw_data(stderr, "Response status (as text)", bstr_ptr(tx->response_status), bstr_len(tx->response_status)); fprintf(stderr, "Response status number: %d\n", tx->response_status_number); #endif // Ignore whitespace that follows the status code. while ((pos < len) && (isspace(data[pos]))) pos++; if (pos == len) return HTP_OK; // Assume the message stretches until the end of the line. tx->response_message = bstr_dup_mem(data + pos, len - pos); if (tx->response_message == NULL) return HTP_ERROR; #ifdef HTP_DEBUG fprint_raw_data(stderr, "Response status message", bstr_ptr(tx->response_message), bstr_len(tx->response_message)); #endif return HTP_OK; }
/** * Determines presence (and encoding) of a request body. * * @param connp * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed. */ int htp_connp_REQ_BODY_DETERMINE(htp_connp_t *connp) { htp_header_t *cl = table_get_c(connp->in_tx->request_headers, "content-length"); htp_header_t *te = table_get_c(connp->in_tx->request_headers, "transfer-encoding"); // Check for the Transfer-Encoding header, which // would indicate a chunked request body if (te != NULL) { // Make sure it contains "chunked" only if (bstr_cmp_c(te->value, "chunked") != 0) { // Invalid T-E header value htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Invalid T-E value in request"); } // Chunked encoding is a HTTP/1.1 feature. Check // that some other protocol is not used. The flag will // also be set if the protocol could not be parsed. // // TODO IIS 7.0, for example, would ignore the T-E header when it // it is used with a protocol below HTTP 1.1. if (connp->in_tx->request_protocol_number < HTTP_1_1) { connp->in_tx->flags |= HTP_INVALID_CHUNKING; // TODO Log } // If the T-E header is present we are going to use it. connp->in_tx->request_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->in_tx->flags |= HTP_REQUEST_SMUGGLING; // TODO Log } connp->in_state = htp_connp_REQ_BODY_CHUNKED_LENGTH; connp->in_tx->progress = TX_PROGRESS_REQ_BODY; } else // Next check for the presence of the Content-Length header if (cl != NULL) { // It seems that we have a request body. connp->in_tx->request_transfer_coding = IDENTITY; // Check for a folded C-L header if (cl->flags & HTP_FIELD_FOLDED) { connp->in_tx->flags |= HTP_REQUEST_SMUGGLING; // TODO Log } // Check for multiple C-L headers if (cl->flags & HTP_FIELD_REPEATED) { connp->in_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 request"); return HTP_ERROR; } else { connp->in_content_length = i; connp->in_body_data_left = connp->in_content_length; if (connp->in_content_length != 0) { connp->in_state = htp_connp_REQ_BODY_IDENTITY; connp->in_tx->progress = TX_PROGRESS_REQ_BODY; } else { connp->in_state = htp_connp_REQ_IDLE; connp->in_tx->progress = TX_PROGRESS_WAIT; } } } else { // This request does not have a body, which // means that we're done with it connp->in_state = htp_connp_REQ_IDLE; connp->in_tx->progress = TX_PROGRESS_WAIT; } // Check for PUT requests, which we need to treat as file uploads if (connp->in_tx->request_method_number == M_PUT) { if (connp->in_tx->connp->in_tx->request_transfer_coding != 0) { // Prepare to treat PUT request body as a file connp->put_file = calloc(1, sizeof (htp_file_t)); if (connp->put_file == NULL) return HTP_ERROR; connp->put_file->source = HTP_FILE_PUT; } else { // TODO Warn about PUT request without a body } return HTP_OK; } // Host resolution htp_header_t *h = table_get_c(connp->in_tx->request_headers, "host"); if (h == NULL) { // No host information in the headers // HTTP/1.1 requires host information in the headers if (connp->in_tx->request_protocol_number >= HTTP_1_1) { connp->in_tx->flags |= HTP_HOST_MISSING; htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Host information in request headers required by HTTP/1.1"); } } else { // Host information available in the headers // Is there host information in the URI? if (connp->in_tx->parsed_uri->hostname == NULL) { // There is no host information in the URI. Place the // hostname from the headers into the parsed_uri structure. htp_replace_hostname(connp, connp->in_tx->parsed_uri, h->value); } else if (bstr_cmp_nocase(h->value, connp->in_tx->parsed_uri->hostname) != 0) { // The host information is different in the // headers and the URI. The HTTP RFC states that // we should ignore the headers copy. connp->in_tx->flags |= HTP_AMBIGUOUS_HOST; htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Host information ambiguous"); } } // Parse Content-Type htp_header_t *ct = table_get_c(connp->in_tx->request_headers, "content-type"); if (ct != NULL) { connp->in_tx->request_content_type = bstr_dup_lower(ct->value); if (connp->in_tx->request_content_type == NULL) { return HTP_ERROR; } // Ignore parameters char *data = bstr_ptr(connp->in_tx->request_content_type); size_t len = bstr_len(ct->value); size_t newlen = 0; while (newlen < len) { // TODO Some platforms may do things differently here if (htp_is_space(data[newlen]) || (data[newlen] == ';')) { bstr_util_adjust_len(connp->in_tx->request_content_type, newlen); break; } newlen++; } } // Parse cookies if (connp->cfg->parse_request_cookies) { htp_parse_cookies_v0(connp); } // Parse authentication information if (connp->cfg->parse_request_http_authentication) { htp_parse_authorization(connp); } // Run hook REQUEST_HEADERS int rc = hook_run_all(connp->cfg->hook_request_headers, connp); if (rc != HOOK_OK) { switch (rc) { case HOOK_STOP: return HTP_STOP; case HOOK_ERROR: case HOOK_DECLINED: default: htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request headers callback returned error (%d)", rc); return HTP_ERROR; } } return HTP_OK; }
/** * Generic response line parser. * * @param[in] connp * @return HTP status */ int htp_parse_response_line_generic(htp_connp_t *connp) { htp_tx_t *tx = connp->out_tx; unsigned char *data = (unsigned char *) bstr_ptr(tx->response_line); size_t len = bstr_len(tx->response_line); size_t pos = 0; // Ignore whitespace at the beginning of the line while ((pos < len) && (htp_is_space(data[pos]))) { pos++; } size_t start = pos; // Find the end of the protocol string while ((pos < len) && (!htp_is_space(data[pos]))) { pos++; } tx->response_protocol = bstr_dup_mem(data + start, pos - start); if (tx->response_protocol == NULL) { return HTP_ERROR; } tx->response_protocol_number = htp_parse_protocol(tx->response_protocol); #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, (unsigned char *) bstr_ptr(tx->response_protocol), bstr_len(tx->response_protocol)); #endif // Ignore whitespace after response protocol // TODO Why use both isspace (below) and htp_is_space (above)? while ((pos < len) && (isspace(data[pos]))) { pos++; } start = pos; // Find the next whitespace character while ((pos < len) && (!htp_is_space(data[pos]))) { pos++; } tx->response_status = bstr_dup_mem(data + start, pos - start); if (tx->response_status == NULL) { return HTP_ERROR; } tx->response_status_number = htp_parse_status(tx->response_status); #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, (unsigned char *) bstr_ptr(tx->response_status), bstr_len(tx->response_status)); #endif // Ignore whitespace that follows while ((pos < len) && (isspace(data[pos]))) { pos++; } tx->response_message = bstr_dup_mem(data + pos, len - pos); if (tx->response_message == NULL) { return HTP_ERROR; } #ifdef HTP_DEBUG fprint_raw_data(stderr, __FUNCTION__, (unsigned char *) bstr_ptr(tx->response_message), bstr_len(tx->response_message)); #endif return HTP_OK; }