TEST(BstrTest, AddNoex) { bstr *p1; bstr *p2; bstr *p3; p1 = bstr_alloc(10); p1 = bstr_add_c(p1, "12345"); p2 = bstr_dup_c("abcdef"); p3 = bstr_add_noex(p1,p2); EXPECT_EQ(p1,p3); EXPECT_EQ(0,bstr_cmp_c(p3,"12345abcde")); bstr_free(p1); bstr_free(p2); }
/** * Extract one request header. A header can span multiple lines, in * which case they will be folded into one before parsing is attempted. * * @param[in] connp * @param[in] data * @param[in] len * @return HTP_OK or HTP_ERROR */ htp_status_t htp_process_request_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; // Now try to parse the header. if (htp_parse_request_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->in_tx->request_headers, h->name); if (h_existing != NULL) { // TODO Do we want to have a list of the headers that are // allowed to be combined in this way? // 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, ", ", 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); // Keep track of repeated same-name headers. h_existing->flags |= HTP_FIELD_REPEATED; } else { // Add as a new header. htp_table_add(connp->in_tx->request_headers, h->name, h); } return HTP_OK; }
bstr *bstr_builder_to_str(const bstr_builder_t *bb) { size_t len = 0; // Determine the size of the string for (size_t i = 0, n = htp_list_size(bb->pieces); i < n; i++) { bstr *b = htp_list_get(bb->pieces, i); len += bstr_len(b); } // Allocate string bstr *bnew = bstr_alloc(len); if (bnew == NULL) return NULL; // Determine the size of the string for (size_t i = 0, n = htp_list_size(bb->pieces); i < n; i++) { bstr *b = htp_list_get(bb->pieces, i); bstr_add_noex(bnew, b); } return bnew; }
/** * \brief Generates the normalized uri. * * Libhtp doesn't recreate the whole normalized uri and save it. * That duty has now been passed to us. A lot of this code has been * copied from libhtp. * * Keep an eye out on the tx->parsed_uri struct and how the parameters * in it are generated, just in case some modifications are made to * them in the future. * * \param uri_include_all boolean to indicate if scheme, username/password, hostname and port should be part of the buffer */ bstr *SCHTPGenerateNormalizedUri(htp_tx_t *tx, htp_uri_t *uri, int uri_include_all) { if (uri == NULL) return NULL; // On the first pass determine the length of the final string size_t len = 0; if (uri_include_all) { if (uri->scheme != NULL) { len += bstr_len(uri->scheme); len += 3; // "://" } if ((uri->username != NULL) || (uri->password != NULL)) { if (uri->username != NULL) { len += bstr_len(uri->username); } len += 1; // ":" if (uri->password != NULL) { len += bstr_len(uri->password); } len += 1; // "@" } if (uri->hostname != NULL) { len += bstr_len(uri->hostname); } if (uri->port != NULL) { len += 1; // ":" len += bstr_len(uri->port); } } if (uri->path != NULL) { len += bstr_len(uri->path); } if (uri->query != NULL) { len += 1; // "?" len += bstr_len(uri->query); } if (uri->fragment != NULL) { len += 1; // "#" len += bstr_len(uri->fragment); } // On the second pass construct the string /* FIXME in memcap */ bstr *r = bstr_alloc(len); if (r == NULL) { return NULL; } if (uri_include_all) { if (uri->scheme != NULL) { bstr_add_noex(r, uri->scheme); bstr_add_c_noex(r, "://"); } if ((uri->username != NULL) || (uri->password != NULL)) { if (uri->username != NULL) { bstr_add_noex(r, uri->username); } bstr_add_c_noex(r, ":"); if (uri->password != NULL) { bstr_add_noex(r, uri->password); } bstr_add_c_noex(r, "@"); } if (uri->hostname != NULL) { bstr_add_noex(r, uri->hostname); } if (uri->port != NULL) { bstr_add_c_noex(r, ":"); bstr_add_noex(r, uri->port); } } if (uri->path != NULL) { bstr_add_noex(r, uri->path); } if (uri->query != NULL) { bstr *query = bstr_dup(uri->query); if (query) { uint64_t flags = 0; htp_urldecode_inplace(tx->cfg, HTP_DECODER_URLENCODED, query, &flags); bstr_add_c_noex(r, "?"); bstr_add_noex(r, query); bstr_free(query); } } if (uri->fragment != NULL) { bstr_add_c_noex(r, "#"); bstr_add_noex(r, uri->fragment); } return r; }
/** * 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; }
/** * Parses one part header. * * @param data * @param len * @param Success indication */ int htp_mpartp_parse_header(htp_mpart_part_t *part, unsigned char *data, size_t len) { size_t name_start, name_end; size_t value_start, value_end; name_start = 0; // Look for the colon size_t colon_pos = 0; while ((colon_pos < len) && (data[colon_pos] != ':')) colon_pos++; if (colon_pos == len) { // Missing colon // TODO Error message return -1; } if (colon_pos == 0) { // Empty header name // TODO Error message } name_end = colon_pos; // Ignore LWS after field-name size_t prev = name_end; while ((prev > name_start) && (htp_is_lws(data[prev - 1]))) { prev--; name_end--; // LWS after field name // TODO Error message } // Value value_start = colon_pos; // Go over the colon if (value_start < len) { value_start++; } // Ignore LWS before field-content while ((value_start < len) && (htp_is_lws(data[value_start]))) { value_start++; } // Look for the end of field-content value_end = value_start; while (value_end < len) value_end++; // Ignore LWS after field-content prev = value_end - 1; while ((prev > value_start) && (htp_is_lws(data[prev]))) { prev--; value_end--; } // Check that the header name is a token size_t i = name_start; while (i < name_end) { if (!htp_is_token(data[i])) { // Request field is not a token // TODO Error message break; } i++; } // Now extract the name and the value htp_header_t *h = calloc(1, sizeof (htp_header_t)); if (h == NULL) return -1; h->name = bstr_dup_mem((char *) data + name_start, name_end - name_start); h->value = bstr_dup_mem((char *) data + value_start, value_end - value_start); // Check if the header already exists htp_header_t * h_existing = table_get(part->headers, h->name); if (h_existing != NULL) { // Add to 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 -1; } h_existing->value = new_value; bstr_add_mem_noex(h_existing->value, ", ", 2); bstr_add_noex(h_existing->value, h->value); // The header is no longer needed bstr_free(&h->name); bstr_free(&h->value); free(h); // Keep track of same-name headers h_existing->flags |= HTP_FIELD_REPEATED; } else { // Add as a new header table_add(part->headers, h->name, h); } return 1; }
/** * Extract one request header. A header can span multiple lines, in * which case they will be folded into one before parsing is attempted. * * @param[in] connp * @return HTP_OK or HTP_ERROR */ int htp_process_request_header_generic(htp_connp_t *connp) { bstr *tempstr = NULL; unsigned char *data = NULL; size_t len = 0; // Create new header structure htp_header_t *h = calloc(1, sizeof (htp_header_t)); if (h == NULL) { return HTP_ERROR; } // Ensure we have the necessary header data in a single buffer if (connp->in_header_line_index + 1 == connp->in_header_line_counter) { // Single line htp_header_line_t *hl = htp_list_get(connp->in_tx->request_header_lines, connp->in_header_line_index); if (hl == NULL) { // Internal error free(h); return HTP_ERROR; } data = (unsigned char *)bstr_ptr(hl->line); len = bstr_len(hl->line); hl->header = h; } else { // Multiple lines (folded) int i = 0; for (i = connp->in_header_line_index; i < connp->in_header_line_counter; i++) { htp_header_line_t *hl = htp_list_get(connp->in_tx->request_header_lines, i); len += bstr_len(hl->line); } tempstr = bstr_alloc(len); if (tempstr == NULL) { free(h); return HTP_ERROR; } for (i = connp->in_header_line_index; i < connp->in_header_line_counter; i++) { htp_header_line_t *hl = htp_list_get(connp->in_tx->request_header_lines, i); unsigned char *line = bstr_ptr(hl->line); size_t llen = bstr_len(hl->line); htp_chomp((unsigned char *)line, &llen); bstr_add_mem_noex(tempstr, line, llen); hl->header = h; if (i != connp->in_header_line_index) { hl->flags |= HTP_FIELD_FOLDED; } } h->flags |= HTP_FIELD_FOLDED; data = (unsigned char *)bstr_ptr(tempstr); len = bstr_len(tempstr); } // Now try to parse the header if (htp_parse_request_header_generic(connp, h, data, len) != HTP_OK) { bstr_free(tempstr); free(h); return HTP_ERROR; } // Do we already have a header with the same name? htp_header_t *h_existing = htp_table_get(connp->in_tx->request_headers, h->name); if (h_existing != NULL) { // repeated header int i = 0; // TODO Do we want to keep a list of the headers that are // allowed to be combined in this way? // Add to 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); bstr_free(tempstr); 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); // replace the header references in all lines for (i = connp->in_header_line_index; i < connp->in_header_line_counter; i++) { htp_header_line_t *hl = htp_list_get(connp->in_tx->request_header_lines, i); hl->header = h_existing; } // The header fields are no longer needed bstr_free(h->name); bstr_free(h->value); free(h); // Keep track of same-name headers h_existing->flags |= HTP_FIELD_REPEATED; } else { // Add as a new header htp_table_add(connp->in_tx->request_headers, h->name, h); } bstr_free(tempstr); return HTP_OK; }