Hello community, here is the log from the commit of package libhtp for openSUSE:Leap:15.2 checked in at 2020-05-01 15:08:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/libhtp (Old) and /work/SRC/openSUSE:Leap:15.2/.libhtp.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libhtp" Fri May 1 15:08:07 2020 rev:2 rq:799454 version:0.5.33 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/libhtp/libhtp.changes 2020-02-21 23:50:16.788677141 +0100 +++ /work/SRC/openSUSE:Leap:15.2/.libhtp.new.2738/libhtp.changes 2020-05-01 15:08:13.082684403 +0200 @@ -1,0 +2,8 @@ +Wed Apr 29 18:33:00 UTC 2020 - Martin Hauke <[email protected]> + +- Update to version 0.5.33 + * compression bomb protection + * memory handling issue found by Oss-Fuzz + * improve handling of anomalies in traffic + +------------------------------------------------------------------- Old: ---- libhtp-0.5.32.tar.gz New: ---- libhtp-0.5.33.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libhtp.spec ++++++ --- /var/tmp/diff_new_pack.rjMQHK/_old 2020-05-01 15:08:13.734685820 +0200 +++ /var/tmp/diff_new_pack.rjMQHK/_new 2020-05-01 15:08:13.734685820 +0200 @@ -1,7 +1,7 @@ # # spec file for package libhtp # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %define sover 2 %define lname %{name}%{sover} Name: libhtp -Version: 0.5.32 +Version: 0.5.33 Release: 0 Summary: HTTP normalizer and parser License: BSD-3-Clause ++++++ libhtp-0.5.32.tar.gz -> libhtp-0.5.33.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/ChangeLog new/libhtp-0.5.33/ChangeLog --- old/libhtp-0.5.32/ChangeLog 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/ChangeLog 2020-04-27 15:30:04.000000000 +0200 @@ -1,3 +1,12 @@ +0.5.33 (27 April 2020) +---------------------- + +- compression bomb protection + +- memory handling issue found by Oss-Fuzz + +- improve handling of anomalies in traffic + 0.5.32 (13 December 2019) -------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/VERSION new/libhtp-0.5.33/VERSION --- old/libhtp-0.5.32/VERSION 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/VERSION 2020-04-27 15:30:04.000000000 +0200 @@ -1,2 +1,2 @@ # This file is intended to be sourced by sh -PKG_VERSION=0.5.32 +PKG_VERSION=0.5.33 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/configure.ac new/libhtp-0.5.33/configure.ac --- old/libhtp-0.5.32/configure.ac 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/configure.ac 2020-04-27 15:30:04.000000000 +0200 @@ -155,6 +155,24 @@ sinclude(m4/lib-prefix.m4) AM_ICONV +# iconvctl is not standard, it is defined only in GNU libiconv +AC_MSG_CHECKING(for iconvctl) +TMPLIBS="${LIBS}" +LIBS="${LIBS} ${LIBICONV}" + +AC_TRY_LINK([#include <stdlib.h> + #include <iconv.h>], + [int iconv_param = 0; + iconv_t cd = iconv_open("",""); + iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, &iconv_param); + iconv_close(cd);], + [ac_cv_func_iconvctl=yes]) +AC_MSG_RESULT($ac_cv_func_iconvctl) +if test "$ac_cv_func_iconvctl" == yes; then + AC_DEFINE(HAVE_ICONVCTL,1,"Define to 1 if you have the `iconvctl' function.") +fi +LIBS="${TMPLIBS}" + dnl ----------------------------------------------- dnl Check and enable the GCC opts we want to use. dnl We may need to add more checks diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_config.c new/libhtp-0.5.33/htp/htp_config.c --- old/libhtp-0.5.32/htp/htp_config.c 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_config.c 2020-04-27 15:30:04.000000000 +0200 @@ -160,6 +160,7 @@ cfg->response_decompression_layer_limit = 2; // 2 layers seem fairly common cfg->lzma_memlimit = HTP_LZMA_MEMLIMIT; cfg->compression_bomb_limit = HTP_COMPRESSION_BOMB_LIMIT; + cfg->compression_time_limit = HTP_COMPRESSION_TIME_LIMIT_USEC; // Default settings for URL-encoded data. @@ -523,6 +524,16 @@ } } +void htp_config_set_compression_time_limit(htp_cfg_t *cfg, size_t useclimit) { + if (cfg == NULL) return; + // max limit is one second + if (useclimit >= 1000000) { + cfg->compression_time_limit = 1000000; + } else { + cfg->compression_time_limit = useclimit; + } +} + void htp_config_set_log_level(htp_cfg_t *cfg, enum htp_log_level_t log_level) { if (cfg == NULL) return; cfg->log_level = log_level; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_config.h new/libhtp-0.5.33/htp/htp_config.h --- old/libhtp-0.5.32/htp/htp_config.h 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_config.h 2020-04-27 15:30:04.000000000 +0200 @@ -443,6 +443,14 @@ void htp_config_set_compression_bomb_limit(htp_cfg_t *cfg, size_t bomblimit); /** + * Configures the maximum compression bomb time LibHTP will decompress. + * + * @param[in] cfg + * @param[in] useclimit + */ +void htp_config_set_compression_time_limit(htp_cfg_t *cfg, size_t useclimit); + +/** * Configures the desired log level. * * @param[in] cfg diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_config_private.h new/libhtp-0.5.33/htp/htp_config_private.h --- old/libhtp-0.5.32/htp/htp_config_private.h 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_config_private.h 2020-04-27 15:30:04.000000000 +0200 @@ -348,6 +348,9 @@ /** max output size for a compression bomb. */ int32_t compression_bomb_limit; + + /** max time for a decompression bomb. */ + int32_t compression_time_limit; }; #ifdef __cplusplus diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_decompressors.h new/libhtp-0.5.33/htp/htp_decompressors.h --- old/libhtp-0.5.32/htp/htp_decompressors.h 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_decompressors.h 2020-04-27 15:30:04.000000000 +0200 @@ -59,6 +59,9 @@ htp_status_t (*callback)(htp_tx_data_t *); void (*destroy)(htp_decompressor_t *); struct htp_decompressor_t *next; + struct timeval time_before; + int32_t time_spent; + uint32_t nb_callbacks; }; struct htp_decompressor_gzip_t { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_private.h new/libhtp-0.5.33/htp/htp_private.h --- old/libhtp-0.5.32/htp/htp_private.h 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_private.h 2020-04-27 15:30:04.000000000 +0200 @@ -83,6 +83,10 @@ //deflate max ratio is about 1000 #define HTP_COMPRESSION_BOMB_RATIO 2048 #define HTP_COMPRESSION_BOMB_LIMIT 1048576 +// 0.1 second +#define HTP_COMPRESSION_TIME_LIMIT_USEC 100000 +// test time for compression every 256 callbacks +#define HTP_COMPRESSION_TIME_FREQ_TEST 256 #define HTP_FIELD_LIMIT_HARD 18000 #define HTP_FIELD_LIMIT_SOFT 9000 @@ -157,7 +161,7 @@ int htp_connp_is_line_folded(unsigned char *data, size_t len); int htp_is_folding_char(int c); -int htp_connp_is_line_terminator(htp_connp_t *connp, unsigned char *data, size_t len); +int htp_connp_is_line_terminator(htp_connp_t *connp, unsigned char *data, size_t len, int next_no_lf); int htp_connp_is_line_ignorable(htp_connp_t *connp, unsigned char *data, size_t len); int htp_parse_uri(bstr *input, htp_uri_t **uri); @@ -167,8 +171,6 @@ int htp_parse_uri_hostport(htp_connp_t *connp, bstr *input, htp_uri_t *uri); int htp_normalize_parsed_uri(htp_tx_t *tx, htp_uri_t *parsed_uri_incomplete, htp_uri_t *parsed_uri); bstr *htp_normalize_hostname_inplace(bstr *input); -void htp_replace_hostname(htp_connp_t *connp, htp_uri_t *parsed_uri, bstr *hostname); -int htp_is_uri_unreserved(unsigned char c); int htp_decode_path_inplace(htp_tx_t *tx, bstr *path); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_request.c new/libhtp-0.5.33/htp/htp_request.c --- old/libhtp-0.5.32/htp/htp_request.c 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_request.c 2020-04-27 15:30:04.000000000 +0200 @@ -647,7 +647,7 @@ #endif // Should we terminate headers? - if (htp_connp_is_line_terminator(connp, data, len)) { + if (htp_connp_is_line_terminator(connp, data, len, 0)) { // Parse previous header, if any. if (connp->in_header != NULL) { if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header), @@ -816,6 +816,10 @@ htp_status_t htp_connp_REQ_LINE(htp_connp_t *connp) { for (;;) { // Get one byte + IN_PEEK_NEXT(connp); + if (connp->in_status == HTP_STREAM_CLOSED && connp->in_next_byte == -1) { + return htp_connp_REQ_LINE_complete(connp); + } IN_COPY_BYTE_OR_RETURN(connp); // Have we reached the end of the line? @@ -869,7 +873,12 @@ while ((pos < len) && (!htp_is_space(data[pos]))) pos++; - if (pos > mstart) { + if (pos <= mstart) { + //empty whitespace line + htp_status_t rc = htp_tx_req_process_body_data_ex(connp->in_tx, data, len); + htp_connp_req_clear_buffer(connp); + return rc; + } else { int methodi = HTP_M_UNKNOWN; bstr *method = bstr_dup_mem(data + mstart, pos - mstart); if (method) { @@ -882,9 +891,8 @@ htp_status_t rc = htp_tx_req_process_body_data_ex(connp->in_tx, data, len); htp_connp_req_clear_buffer(connp); return rc; - } + } // else continue } - //else //unread last end of line so that REQ_LINE works if (connp->in_current_read_offset < (int64_t)len) { connp->in_current_read_offset=0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_response.c new/libhtp-0.5.33/htp/htp_response.c --- old/libhtp-0.5.32/htp/htp_response.c 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_response.c 2020-04-27 15:30:04.000000000 +0200 @@ -826,8 +826,13 @@ fprint_raw_data(stderr, __func__, data, len); #endif + int next_no_lf = 0; + if (connp->out_current_read_offset < connp->out_current_len && + connp->out_current_data[connp->out_current_read_offset] != LF) { + next_no_lf = 1; + } // Should we terminate headers? - if (htp_connp_is_line_terminator(connp, data, len)) { + if (htp_connp_is_line_terminator(connp, data, len, next_no_lf)) { // Parse previous header, if any. if (connp->out_header != NULL) { if (connp->cfg->process_response_header(connp, bstr_ptr(connp->out_header), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_transaction.c new/libhtp-0.5.33/htp/htp_transaction.c --- old/libhtp-0.5.32/htp/htp_transaction.c 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_transaction.c 2020-04-27 15:30:04.000000000 +0200 @@ -776,6 +776,19 @@ htp_connp_destroy_decompressors(tx->connp); } +static htp_status_t htp_timer_track(int32_t *time_spent, struct timeval * after, struct timeval *before) { + if (after->tv_sec < before->tv_sec) { + return HTP_ERROR; + } else if (after->tv_sec == before->tv_sec) { + if (after->tv_usec < before->tv_usec) { + return HTP_ERROR; + } + *time_spent += after->tv_usec - before->tv_usec; + } else { + *time_spent += (after->tv_sec - before->tv_sec) * 1000000 + after->tv_usec - before->tv_usec; + } + return HTP_OK; +} static htp_status_t htp_tx_res_process_body_data_decompressor_callback(htp_tx_data_t *d) { if (d == NULL) return HTP_ERROR; @@ -789,6 +802,23 @@ // Invoke all callbacks. htp_status_t rc = htp_res_run_hook_body_data(d->tx->connp, d); if (rc != HTP_OK) return HTP_ERROR; + d->tx->connp->out_decompressor->nb_callbacks++; + if (d->tx->connp->out_decompressor->nb_callbacks % HTP_COMPRESSION_TIME_FREQ_TEST == 0) { + struct timeval after; + gettimeofday(&after, NULL); + // sanity check for race condition if system time changed + if ( htp_timer_track(&d->tx->connp->out_decompressor->time_spent, &after, &d->tx->connp->out_decompressor->time_before) == HTP_OK) { + // updates last tracked time + d->tx->connp->out_decompressor->time_before = after; + if (d->tx->connp->out_decompressor->time_spent > d->tx->connp->cfg->compression_time_limit ) { + htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "Compression bomb: spent %"PRId64" us decompressing", + d->tx->connp->out_decompressor->time_spent); + return HTP_ERROR; + } + } + + } if (d->tx->response_entity_len > d->tx->connp->cfg->compression_bomb_limit && d->tx->response_entity_len > HTP_COMPRESSION_BOMB_RATIO * d->tx->response_message_len) { htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, @@ -834,8 +864,21 @@ if (tx->connp->out_decompressor == NULL || tx->connp->out_decompressor->decompress == NULL) return HTP_ERROR; + struct timeval after; + gettimeofday(&tx->connp->out_decompressor->time_before, NULL); // Send data buffer to the decompressor. + tx->connp->out_decompressor->nb_callbacks=0; tx->connp->out_decompressor->decompress(tx->connp->out_decompressor, &d); + gettimeofday(&after, NULL); + // sanity check for race condition if system time changed + if ( htp_timer_track(&tx->connp->out_decompressor->time_spent, &after, &tx->connp->out_decompressor->time_before) == HTP_OK) { + if ( tx->connp->out_decompressor->time_spent > tx->connp->cfg->compression_time_limit ) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "Compression bomb: spent %"PRId64" us decompressing", + tx->connp->out_decompressor->time_spent); + return HTP_ERROR; + } + } if (data == NULL) { // Shut down the decompressor, if we used one. @@ -1350,10 +1393,6 @@ * or a overly long request */ if (tx->request_method == HTP_M_UNKNOWN && tx->request_uri == NULL && tx->connp->in_state == htp_connp_REQ_LINE) { htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line incomplete"); - - if (htp_connp_REQ_LINE_complete(tx->connp) != HTP_OK) { - return HTP_ERROR; - } } return HTP_OK; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_transcoder.c new/libhtp-0.5.33/htp/htp_transcoder.c --- old/libhtp-0.5.32/htp/htp_transcoder.c 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_transcoder.c 2020-04-27 15:30:04.000000000 +0200 @@ -64,7 +64,7 @@ return HTP_ERROR; } - #if (_LIBICONV_VERSION >= 0x0108) + #if (_LIBICONV_VERSION >= 0x0108 && HAVE_ICONVCTL) int iconv_param = 0; iconvctl(cd, ICONV_SET_TRANSLITERATE, &iconv_param); iconv_param = 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libhtp-0.5.32/htp/htp_util.c new/libhtp-0.5.33/htp/htp_util.c --- old/libhtp-0.5.32/htp/htp_util.c 2019-12-13 10:30:17.000000000 +0100 +++ new/libhtp-0.5.33/htp/htp_util.c 2020-04-27 15:30:04.000000000 +0200 @@ -464,7 +464,7 @@ * @param[in] len * @return 0 or 1 */ -int htp_connp_is_line_terminator(htp_connp_t *connp, unsigned char *data, size_t len) { +int htp_connp_is_line_terminator(htp_connp_t *connp, unsigned char *data, size_t len, int next_no_lf) { // Is this the end of request headers? switch (connp->cfg->server_personality) { case HTP_SERVER_IIS_5_1: @@ -481,10 +481,7 @@ } // Only space is terminator if terminator does not follow right away if (len == 2 && htp_is_lws(data[0]) && data[1] == LF) { - if (connp->out_current_read_offset < connp->out_current_len && - connp->out_current_data[connp->out_current_read_offset] != LF) { - return 1; - } + return next_no_lf; } break; } @@ -501,7 +498,7 @@ * @return 0 or 1 */ int htp_connp_is_line_ignorable(htp_connp_t *connp, unsigned char *data, size_t len) { - return htp_connp_is_line_terminator(connp, data, len); + return htp_connp_is_line_terminator(connp, data, len, 0); } static htp_status_t htp_parse_port(unsigned char *data, size_t len, int *port, int *invalid) { @@ -1860,136 +1857,6 @@ return hostname; } -#if 0 - -/** - * Replace the URI in the structure with the one provided as the parameter - * to this function (which will typically be supplied in a Host header). - * - * @param[in] connp - * @param[in] parsed_uri - * @param[in] hostname - */ -void htp_replace_hostname(htp_connp_t *connp, htp_uri_t *parsed_uri, bstr *hostname) { - if (hostname == NULL) return; - - bstr *new_hostname = NULL; - - int colon = bstr_chr(hostname, ':'); - if (colon == -1) { - // Hostname alone (no port information) - new_hostname = bstr_dup(hostname); - if (new_hostname == NULL) return; - htp_normalize_hostname_inplace(new_hostname); - - if (parsed_uri->hostname != NULL) bstr_free(parsed_uri->hostname); - parsed_uri->hostname = new_hostname; - } else { - // Hostname and port - new_hostname = bstr_dup_ex(hostname, 0, colon); - if (new_hostname == NULL) return; - // TODO Handle whitespace around hostname - htp_normalize_hostname_inplace(new_hostname); - - if (parsed_uri->hostname != NULL) bstr_free(parsed_uri->hostname); - parsed_uri->hostname = new_hostname; - parsed_uri->port_number = 0; - - // Port - int port = htp_parse_positive_integer_whitespace((unsigned char *) bstr_ptr(hostname) + colon + 1, - bstr_len(hostname) - colon - 1, 10); - if (port < 0) { - // Failed to parse port - htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Invalid server port information in request"); - } else if ((port > 0) && (port < 65536)) { - // Valid port - if ((connp->conn->server_port != 0) && (port != connp->conn->server_port)) { - // Port was specified in connection and is different from the TCP port - htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request server port=%d number differs from the actual TCP port=%d", port, connp->conn->server_port); - } else { - parsed_uri->port_number = port; - } - } - } -} - -/** - * Is URI character reserved? - * - * @param[in] c - * @return 1 if it is, 0 if it isn't - */ -int htp_is_uri_unreserved(unsigned char c) { - if (((c >= 0x41) && (c <= 0x5a)) || - ((c >= 0x61) && (c <= 0x7a)) || - ((c >= 0x30) && (c <= 0x39)) || - (c == 0x2d) || (c == 0x2e) || - (c == 0x5f) || (c == 0x7e)) { - return 1; - } else { - return 0; - } -} - -/** - * Decode a URL-encoded string, leaving the reserved - * characters and invalid encodings alone. - * - * @param[in] s - */ -void htp_uriencoding_normalize_inplace(bstr *s) { - if (s == NULL) return; - - unsigned char *data = bstr_ptr(s); - if (data == NULL) return; - size_t len = bstr_len(s); - - size_t rpos = 0; - size_t wpos = 0; - - while (rpos < len) { - if (data[rpos] == '%') { - if (rpos + 2 < len) { - if (isxdigit(data[rpos + 1]) && (isxdigit(data[rpos + 2]))) { - unsigned char c = x2c(&data[rpos + 1]); - - if (htp_is_uri_unreserved(c)) { - // Leave reserved characters encoded, but convert - // the hexadecimal digits to uppercase - data[wpos++] = data[rpos++]; - data[wpos++] = toupper(data[rpos++]); - data[wpos++] = toupper(data[rpos++]); - } else { - // Decode unreserved character - data[wpos++] = c; - rpos += 3; - } - } else { - // Invalid URL encoding: invalid hex digits - - // Copy over what's there - data[wpos++] = data[rpos++]; - data[wpos++] = toupper(data[rpos++]); - data[wpos++] = toupper(data[rpos++]); - } - } else { - // Invalid URL encoding: string too short - - // Copy over what's there - data[wpos++] = data[rpos++]; - while (rpos < len) { - data[wpos++] = toupper(data[rpos++]); - } - } - } else { - data[wpos++] = data[rpos++]; - } - } - - bstr_adjust_len(s, wpos); -} -#endif - /** * Normalize URL path. This function implements the remove dot segments algorithm * specified in RFC 3986, section 5.2.4. @@ -2360,21 +2227,6 @@ if (uri->query != NULL) { bstr_add_c_noex(r, "?"); bstr_add_noex(r, uri->query); - - /* - bstr *query = bstr_dup(uri->query); - if (query == NULL) { - bstr_free(r); - return NULL; - } - - htp_uriencoding_normalize_inplace(query); - - bstr_add_c_noex(r, "?"); - bstr_add_noex(r, query); - - bstr_free(query); - */ } if (uri->fragment != NULL) {
